Home | History | Annotate | Line # | Download | only in gpt
      1   1.1  christos /*-
      2   1.1  christos  * Copyright (c) 2002 Marcel Moolenaar
      3   1.1  christos  * All rights reserved.
      4   1.1  christos  *
      5   1.1  christos  * Redistribution and use in source and binary forms, with or without
      6   1.1  christos  * modification, are permitted provided that the following conditions
      7   1.1  christos  * are met:
      8   1.1  christos  *
      9   1.1  christos  * 1. Redistributions of source code must retain the above copyright
     10   1.1  christos  *    notice, this list of conditions and the following disclaimer.
     11   1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     12   1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     13   1.1  christos  *    documentation and/or other materials provided with the distribution.
     14   1.1  christos  *
     15   1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16   1.1  christos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17   1.1  christos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18   1.1  christos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19   1.1  christos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20   1.1  christos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21   1.1  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22   1.1  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23   1.1  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24   1.1  christos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25   1.1  christos  *
     26   1.1  christos  * CRC32 code derived from work by Gary S. Brown.
     27   1.1  christos  */
     28   1.1  christos 
     29  1.31  christos #if HAVE_NBTOOL_CONFIG_H
     30  1.31  christos #include "nbtool_config.h"
     31  1.31  christos #endif
     32  1.31  christos 
     33   1.1  christos #include <sys/cdefs.h>
     34   1.2  christos #ifdef __FBSDID
     35   1.1  christos __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
     36   1.2  christos #endif
     37   1.2  christos #ifdef __RCSID
     38  1.91  christos __RCSID("$NetBSD: gpt.c,v 1.91 2025/02/23 20:47:19 christos Exp $");
     39   1.2  christos #endif
     40   1.1  christos 
     41   1.1  christos #include <sys/param.h>
     42   1.1  christos #include <sys/types.h>
     43   1.1  christos #include <sys/stat.h>
     44   1.2  christos #include <sys/ioctl.h>
     45  1.25  jakllsch #include <sys/bootblock.h>
     46   1.1  christos 
     47   1.1  christos #include <err.h>
     48   1.1  christos #include <errno.h>
     49   1.1  christos #include <fcntl.h>
     50   1.1  christos #include <paths.h>
     51   1.1  christos #include <stddef.h>
     52  1.42  christos #include <stdarg.h>
     53   1.1  christos #include <stdio.h>
     54   1.1  christos #include <stdlib.h>
     55   1.1  christos #include <string.h>
     56   1.1  christos #include <unistd.h>
     57  1.32  christos #include <ctype.h>
     58   1.1  christos 
     59   1.1  christos #include "map.h"
     60   1.1  christos #include "gpt.h"
     61  1.49  christos #include "gpt_private.h"
     62   1.1  christos 
     63   1.1  christos static uint32_t crc32_tab[] = {
     64   1.1  christos 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
     65   1.1  christos 	0xe963a535, 0x9e6495a3,	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
     66   1.1  christos 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
     67   1.1  christos 	0xf3b97148, 0x84be41de,	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
     68   1.1  christos 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,	0x14015c4f, 0x63066cd9,
     69   1.1  christos 	0xfa0f3d63, 0x8d080df5,	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
     70   1.1  christos 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,	0x35b5a8fa, 0x42b2986c,
     71   1.1  christos 	0xdbbbc9d6, 0xacbcf940,	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
     72   1.1  christos 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
     73   1.1  christos 	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
     74   1.1  christos 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,	0x76dc4190, 0x01db7106,
     75   1.1  christos 	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
     76   1.1  christos 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
     77   1.1  christos 	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
     78   1.1  christos 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
     79   1.1  christos 	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
     80   1.1  christos 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
     81   1.1  christos 	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
     82   1.1  christos 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
     83   1.1  christos 	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
     84   1.1  christos 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
     85   1.1  christos 	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
     86   1.1  christos 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
     87   1.1  christos 	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
     88   1.1  christos 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
     89   1.1  christos 	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
     90   1.1  christos 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
     91   1.1  christos 	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
     92   1.1  christos 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
     93   1.1  christos 	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
     94   1.1  christos 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
     95   1.1  christos 	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
     96   1.1  christos 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
     97   1.1  christos 	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
     98   1.1  christos 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
     99   1.1  christos 	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    100   1.1  christos 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    101   1.1  christos 	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    102   1.1  christos 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    103   1.1  christos 	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    104   1.1  christos 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    105   1.1  christos 	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    106   1.1  christos 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
    107   1.1  christos };
    108   1.1  christos 
    109   1.1  christos uint32_t
    110   1.1  christos crc32(const void *buf, size_t size)
    111   1.1  christos {
    112   1.1  christos 	const uint8_t *p;
    113   1.1  christos 	uint32_t crc;
    114   1.1  christos 
    115   1.1  christos 	p = buf;
    116   1.1  christos 	crc = ~0U;
    117   1.1  christos 
    118   1.1  christos 	while (size--)
    119   1.1  christos 		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
    120   1.1  christos 
    121   1.1  christos 	return crc ^ ~0U;
    122   1.1  christos }
    123   1.1  christos 
    124  1.72  christos /*
    125  1.72  christos  * Produce a NUL-terminated utf-8 string from the non-NUL-terminated
    126  1.72  christos  * utf16 string.
    127  1.72  christos  */
    128  1.53  christos void
    129  1.72  christos utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len)
    130   1.1  christos {
    131  1.72  christos 	size_t s8idx, s16idx;
    132   1.1  christos 	uint32_t utfchar;
    133   1.1  christos 	unsigned int c;
    134   1.1  christos 
    135  1.72  christos 	for (s16idx = 0; s16idx < s16len; s16idx++)
    136  1.72  christos 		if (s16[s16idx] == 0)
    137  1.72  christos 			break;
    138  1.72  christos 
    139  1.72  christos 	s16len = s16idx;
    140   1.1  christos 	s8idx = s16idx = 0;
    141   1.1  christos 	while (s16idx < s16len) {
    142   1.1  christos 		utfchar = le16toh(s16[s16idx++]);
    143   1.1  christos 		if ((utfchar & 0xf800) == 0xd800) {
    144   1.1  christos 			c = le16toh(s16[s16idx]);
    145   1.1  christos 			if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
    146   1.1  christos 				utfchar = 0xfffd;
    147   1.1  christos 			else
    148   1.1  christos 				s16idx++;
    149   1.1  christos 		}
    150   1.1  christos 		if (utfchar < 0x80) {
    151  1.53  christos 			if (s8idx + 1 >= s8len)
    152  1.53  christos 				break;
    153  1.59  christos 			s8[s8idx++] = (uint8_t)utfchar;
    154   1.1  christos 		} else if (utfchar < 0x800) {
    155  1.53  christos 			if (s8idx + 2 >= s8len)
    156  1.53  christos 				break;
    157  1.59  christos 			s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
    158  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    159   1.1  christos 		} else if (utfchar < 0x10000) {
    160  1.53  christos 			if (s8idx + 3 >= s8len)
    161  1.53  christos 				break;
    162  1.59  christos 			s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
    163  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    164  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    165   1.1  christos 		} else if (utfchar < 0x200000) {
    166  1.53  christos 			if (s8idx + 4 >= s8len)
    167  1.53  christos 				break;
    168  1.59  christos 			s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
    169  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
    170  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
    171  1.59  christos 			s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
    172   1.1  christos 		}
    173   1.1  christos 	}
    174  1.53  christos 	s8[s8idx] = 0;
    175   1.1  christos }
    176   1.1  christos 
    177  1.72  christos /*
    178  1.72  christos  * Produce a non-NUL-terminated utf-16 string from the NUL-terminated
    179  1.72  christos  * utf8 string.
    180  1.72  christos  */
    181   1.1  christos void
    182   1.1  christos utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
    183   1.1  christos {
    184   1.1  christos 	size_t s16idx, s8idx, s8len;
    185   1.2  christos 	uint32_t utfchar = 0;
    186   1.1  christos 	unsigned int c, utfbytes;
    187   1.1  christos 
    188   1.1  christos 	s8len = 0;
    189   1.1  christos 	while (s8[s8len++] != 0)
    190   1.1  christos 		;
    191   1.1  christos 	s8idx = s16idx = 0;
    192   1.1  christos 	utfbytes = 0;
    193   1.1  christos 	do {
    194   1.1  christos 		c = s8[s8idx++];
    195   1.1  christos 		if ((c & 0xc0) != 0x80) {
    196   1.1  christos 			/* Initial characters. */
    197   1.1  christos 			if (utfbytes != 0) {
    198   1.1  christos 				/* Incomplete encoding. */
    199  1.17      matt 				s16[s16idx++] = htole16(0xfffd);
    200   1.1  christos 				if (s16idx == s16len) {
    201   1.1  christos 					s16[--s16idx] = 0;
    202   1.1  christos 					return;
    203   1.1  christos 				}
    204   1.1  christos 			}
    205   1.1  christos 			if ((c & 0xf8) == 0xf0) {
    206   1.1  christos 				utfchar = c & 0x07;
    207   1.1  christos 				utfbytes = 3;
    208   1.1  christos 			} else if ((c & 0xf0) == 0xe0) {
    209   1.1  christos 				utfchar = c & 0x0f;
    210   1.1  christos 				utfbytes = 2;
    211   1.1  christos 			} else if ((c & 0xe0) == 0xc0) {
    212   1.1  christos 				utfchar = c & 0x1f;
    213   1.1  christos 				utfbytes = 1;
    214   1.1  christos 			} else {
    215   1.1  christos 				utfchar = c & 0x7f;
    216   1.1  christos 				utfbytes = 0;
    217   1.1  christos 			}
    218   1.1  christos 		} else {
    219   1.1  christos 			/* Followup characters. */
    220   1.1  christos 			if (utfbytes > 0) {
    221   1.1  christos 				utfchar = (utfchar << 6) + (c & 0x3f);
    222   1.1  christos 				utfbytes--;
    223   1.1  christos 			} else if (utfbytes == 0)
    224  1.59  christos 				utfbytes = (u_int)~0;
    225   1.1  christos 		}
    226   1.1  christos 		if (utfbytes == 0) {
    227   1.1  christos 			if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
    228  1.17      matt 				utfchar = 0xfffd;
    229   1.1  christos 			if (utfchar >= 0x10000) {
    230  1.59  christos 				s16[s16idx++] = htole16((uint16_t)
    231  1.59  christos 				    (0xd800 | ((utfchar>>10) - 0x40)));
    232  1.59  christos 				s16[s16idx++] = htole16((uint16_t)
    233  1.59  christos 				    (0xdc00 | (utfchar & 0x3ff)));
    234   1.1  christos 			} else
    235  1.59  christos 				s16[s16idx++] = htole16((uint16_t)utfchar);
    236   1.1  christos 			if (s16idx == s16len) {
    237   1.1  christos 				return;
    238   1.1  christos 			}
    239   1.1  christos 		}
    240   1.1  christos 	} while (c != 0);
    241  1.72  christos 
    242  1.72  christos 	while (s16idx < s16len)
    243  1.72  christos 		s16[s16idx++] = 0;
    244   1.1  christos }
    245   1.1  christos 
    246  1.49  christos void *
    247  1.49  christos gpt_read(gpt_t gpt, off_t lba, size_t count)
    248   1.1  christos {
    249   1.1  christos 	off_t ofs;
    250   1.1  christos 	void *buf;
    251   1.1  christos 
    252  1.49  christos 	count *= gpt->secsz;
    253   1.1  christos 	buf = malloc(count);
    254   1.1  christos 	if (buf == NULL)
    255  1.49  christos 		return NULL;
    256   1.1  christos 
    257  1.49  christos 	ofs = lba * gpt->secsz;
    258  1.49  christos 	if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
    259  1.49  christos 	    read(gpt->fd, buf, count) == (ssize_t)count)
    260  1.49  christos 		return buf;
    261   1.1  christos 
    262   1.1  christos 	free(buf);
    263  1.49  christos 	return NULL;
    264   1.1  christos }
    265   1.1  christos 
    266   1.1  christos int
    267  1.49  christos gpt_write(gpt_t gpt, map_t map)
    268   1.1  christos {
    269   1.1  christos 	off_t ofs;
    270   1.1  christos 	size_t count;
    271   1.1  christos 
    272  1.59  christos 	count = (size_t)(map->map_size * gpt->secsz);
    273  1.49  christos 	ofs = map->map_start * gpt->secsz;
    274  1.49  christos 	if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
    275  1.49  christos 	    write(gpt->fd, map->map_data, count) != (ssize_t)count)
    276  1.46  christos 		return -1;
    277  1.49  christos 	gpt->flags |= GPT_MODIFIED;
    278  1.46  christos 	return 0;
    279   1.1  christos }
    280   1.1  christos 
    281   1.1  christos static int
    282  1.77    martin gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset)
    283   1.1  christos {
    284   1.1  christos 	struct mbr *mbr;
    285  1.49  christos 	map_t m, p;
    286   1.1  christos 	off_t size, start;
    287   1.1  christos 	unsigned int i, pmbr;
    288   1.1  christos 
    289  1.49  christos 	mbr = gpt_read(gpt, lba, 1);
    290  1.42  christos 	if (mbr == NULL) {
    291  1.49  christos 		gpt_warn(gpt, "Read failed");
    292  1.49  christos 		return -1;
    293  1.42  christos 	}
    294   1.1  christos 
    295   1.1  christos 	if (mbr->mbr_sig != htole16(MBR_SIG)) {
    296  1.49  christos 		if (gpt->verbose)
    297  1.49  christos 			gpt_msg(gpt,
    298  1.49  christos 			    "MBR not found at sector %ju", (uintmax_t)lba);
    299   1.1  christos 		free(mbr);
    300  1.49  christos 		return 0;
    301   1.1  christos 	}
    302   1.1  christos 
    303   1.1  christos 	/*
    304   1.1  christos 	 * Differentiate between a regular MBR and a PMBR. This is more
    305   1.1  christos 	 * convenient in general. A PMBR is one with a single partition
    306   1.1  christos 	 * of type 0xee.
    307   1.1  christos 	 */
    308   1.1  christos 	pmbr = 0;
    309   1.1  christos 	for (i = 0; i < 4; i++) {
    310  1.25  jakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
    311   1.1  christos 			continue;
    312  1.25  jakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    313   1.1  christos 			pmbr++;
    314  1.82  jmcneill 		else if ((gpt->flags & GPT_HYBRID) == 0)
    315   1.1  christos 			break;
    316   1.1  christos 	}
    317   1.1  christos 	if (pmbr && i == 4 && lba == 0) {
    318  1.49  christos 		if (pmbr != 1)
    319  1.49  christos 			gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
    320  1.49  christos 			    (uintmax_t)lba);
    321  1.49  christos 		else if (gpt->verbose > 1)
    322  1.49  christos 			gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
    323  1.61  christos 		p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
    324  1.49  christos 		goto out;
    325  1.49  christos 	}
    326  1.49  christos 	if (pmbr)
    327  1.49  christos 		gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
    328  1.49  christos 	else if (gpt->verbose > 1)
    329  1.49  christos 		gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
    330   1.1  christos 
    331  1.61  christos 	p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
    332   1.1  christos 	if (p == NULL)
    333  1.49  christos 		goto out;
    334  1.49  christos 
    335   1.1  christos 	for (i = 0; i < 4; i++) {
    336  1.25  jakllsch 		if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
    337  1.25  jakllsch 		    mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
    338   1.1  christos 			continue;
    339   1.1  christos 		start = le16toh(mbr->mbr_part[i].part_start_hi);
    340   1.1  christos 		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
    341   1.1  christos 		size = le16toh(mbr->mbr_part[i].part_size_hi);
    342   1.1  christos 		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
    343   1.1  christos 		if (start == 0 && size == 0) {
    344  1.49  christos 			gpt_warnx(gpt, "Malformed MBR at sector %ju",
    345  1.49  christos 			    (uintmax_t)lba);
    346   1.1  christos 			continue;
    347   1.1  christos 		}
    348  1.49  christos 		if (gpt->verbose > 2)
    349  1.68  christos 			gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
    350  1.68  christos 			    "size=%ju", mbr->mbr_part[i].part_flag,
    351  1.42  christos 			    mbr->mbr_part[i].part_typ,
    352  1.42  christos 			    (uintmax_t)start, (uintmax_t)size);
    353  1.77    martin 		if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) {
    354  1.77    martin 			start += lba;
    355  1.61  christos 			m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
    356   1.1  christos 			if (m == NULL)
    357  1.49  christos 				return -1;
    358  1.77    martin 			m->map_index = *next_index;
    359  1.77    martin 			(*next_index)++;
    360   1.1  christos 		} else {
    361  1.77    martin 			start += ext_offset;
    362  1.77    martin 			if (gpt_mbr(gpt, start, next_index,
    363  1.77    martin 			    ext_offset ? ext_offset : start) == -1)
    364  1.49  christos 				return -1;
    365   1.1  christos 		}
    366   1.1  christos 	}
    367  1.49  christos 	return 0;
    368  1.49  christos out:
    369  1.49  christos 	if (p == NULL) {
    370  1.49  christos 		free(mbr);
    371  1.49  christos 		return -1;
    372  1.49  christos 	}
    373  1.49  christos 	return 0;
    374   1.1  christos }
    375   1.1  christos 
    376  1.29   jnemeth int
    377  1.49  christos gpt_gpt(gpt_t gpt, off_t lba, int found)
    378   1.1  christos {
    379   1.1  christos 	off_t size;
    380   1.1  christos 	struct gpt_ent *ent;
    381   1.1  christos 	struct gpt_hdr *hdr;
    382  1.34  christos 	char *p;
    383  1.49  christos 	map_t m;
    384   1.1  christos 	size_t blocks, tblsz;
    385   1.1  christos 	unsigned int i;
    386   1.1  christos 	uint32_t crc;
    387   1.1  christos 
    388  1.49  christos 	hdr = gpt_read(gpt, lba, 1);
    389  1.86   mlelstv 	if (hdr == NULL) {
    390  1.86   mlelstv 		gpt_warn(gpt, "Read failed");
    391  1.49  christos 		return -1;
    392  1.86   mlelstv 	}
    393   1.1  christos 
    394   1.1  christos 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
    395   1.1  christos 		goto fail_hdr;
    396   1.1  christos 
    397   1.1  christos 	crc = le32toh(hdr->hdr_crc_self);
    398   1.1  christos 	hdr->hdr_crc_self = 0;
    399   1.1  christos 	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
    400  1.49  christos 		if (gpt->verbose)
    401  1.49  christos 			gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
    402  1.49  christos 			    (uintmax_t)lba);
    403   1.1  christos 		goto fail_hdr;
    404   1.1  christos 	}
    405   1.1  christos 
    406   1.1  christos 	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
    407  1.49  christos 	blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
    408   1.1  christos 
    409   1.1  christos 	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
    410  1.59  christos 	p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
    411  1.11  christos 	if (p == NULL) {
    412  1.11  christos 		if (found) {
    413  1.49  christos 			if (gpt->verbose)
    414  1.49  christos 				gpt_msg(gpt,
    415  1.49  christos 				    "Cannot read LBA table at sector %ju",
    416  1.49  christos 				    (uintmax_t)le64toh(hdr->hdr_lba_table));
    417  1.49  christos 			return -1;
    418  1.11  christos 		}
    419  1.11  christos 		goto fail_hdr;
    420  1.11  christos 	}
    421   1.1  christos 
    422   1.1  christos 	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
    423  1.49  christos 		if (gpt->verbose)
    424  1.49  christos 			gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
    425  1.49  christos 			    (uintmax_t)le64toh(hdr->hdr_lba_table));
    426   1.1  christos 		goto fail_ent;
    427   1.1  christos 	}
    428   1.1  christos 
    429  1.49  christos 	if (gpt->verbose > 1)
    430  1.49  christos 		gpt_msg(gpt, "%s GPT at sector %ju",
    431  1.49  christos 		    (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
    432   1.1  christos 
    433  1.49  christos 	m = map_add(gpt, lba, 1, (lba == 1)
    434  1.61  christos 	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
    435   1.1  christos 	if (m == NULL)
    436   1.1  christos 		return (-1);
    437   1.1  christos 
    438  1.59  christos 	m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
    439  1.59  christos 	    (off_t)blocks,
    440  1.61  christos 	    lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
    441   1.1  christos 	if (m == NULL)
    442   1.1  christos 		return (-1);
    443   1.1  christos 
    444   1.1  christos 	if (lba != 1)
    445  1.11  christos 		return (1);
    446   1.1  christos 
    447   1.1  christos 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    448   1.1  christos 		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
    449  1.34  christos 		if (gpt_uuid_is_nil(ent->ent_type))
    450   1.1  christos 			continue;
    451   1.1  christos 
    452  1.59  christos 		size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
    453  1.59  christos 		    le64toh((uint64_t)ent->ent_lba_start) + 1LL);
    454  1.49  christos 		if (gpt->verbose > 2) {
    455  1.34  christos 			char buf[128];
    456  1.34  christos 			gpt_uuid_snprintf(buf, sizeof(buf), "%s",
    457  1.34  christos 			    ent->ent_type);
    458  1.49  christos 			gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
    459  1.49  christos 			    "size=%ju", buf,
    460  1.49  christos 			    (uintmax_t)le64toh(ent->ent_lba_start),
    461  1.49  christos 			    (uintmax_t)size);
    462   1.1  christos 		}
    463  1.59  christos 		m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
    464  1.61  christos 		    size, MAP_TYPE_GPT_PART, ent, 0);
    465   1.1  christos 		if (m == NULL)
    466   1.1  christos 			return (-1);
    467   1.1  christos 		m->map_index = i + 1;
    468   1.1  christos 	}
    469  1.11  christos 	return (1);
    470   1.1  christos 
    471   1.1  christos  fail_ent:
    472   1.1  christos 	free(p);
    473   1.1  christos 
    474   1.1  christos  fail_hdr:
    475   1.1  christos 	free(hdr);
    476   1.1  christos 	return (0);
    477   1.1  christos }
    478   1.1  christos 
    479  1.49  christos gpt_t
    480  1.70  christos gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
    481  1.70  christos     time_t timestamp)
    482   1.1  christos {
    483  1.49  christos 	int mode, found;
    484  1.45  christos 	off_t devsz;
    485  1.49  christos 	gpt_t gpt;
    486  1.91  christos 	unsigned int idx;
    487   1.1  christos 
    488  1.49  christos 	if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
    489  1.55   jnemeth 		if (!(flags & GPT_QUIET))
    490  1.49  christos 			warn("Cannot allocate `%s'", dev);
    491  1.49  christos 		return NULL;
    492  1.49  christos 	}
    493  1.49  christos 	gpt->flags = flags;
    494  1.49  christos 	gpt->verbose = verbose;
    495  1.49  christos 	gpt->mediasz = mediasz;
    496  1.49  christos 	gpt->secsz = secsz;
    497  1.70  christos 	gpt->timestamp = timestamp;
    498  1.90   mlelstv 	gpt->uuidgen = 0;
    499  1.49  christos 
    500  1.49  christos 	mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
    501  1.49  christos 
    502  1.49  christos 	gpt->fd = opendisk(dev, mode, gpt->device_name,
    503  1.49  christos 	    sizeof(gpt->device_name), 0);
    504  1.49  christos 	if (gpt->fd == -1) {
    505  1.49  christos 		strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
    506  1.49  christos 		gpt_warn(gpt, "Cannot open");
    507  1.49  christos 		goto close;
    508  1.42  christos 	}
    509   1.1  christos 
    510  1.49  christos 	if (fstat(gpt->fd, &gpt->sb) == -1) {
    511  1.49  christos 		gpt_warn(gpt, "Cannot stat");
    512   1.1  christos 		goto close;
    513  1.42  christos 	}
    514   1.1  christos 
    515  1.49  christos 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
    516  1.49  christos 		if (gpt->secsz == 0) {
    517  1.43   jnemeth #ifdef DIOCGSECTORSIZE
    518  1.49  christos 			if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
    519  1.49  christos 				gpt_warn(gpt, "Cannot get sector size");
    520  1.44  christos 				goto close;
    521  1.44  christos 			}
    522  1.44  christos #endif
    523  1.49  christos 			if (gpt->secsz == 0) {
    524  1.49  christos 				gpt_warnx(gpt, "Sector size can't be 0");
    525  1.44  christos 				goto close;
    526  1.44  christos 			}
    527  1.42  christos 		}
    528  1.49  christos 		if (gpt->mediasz == 0) {
    529  1.44  christos #ifdef DIOCGMEDIASIZE
    530  1.49  christos 			if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
    531  1.49  christos 				gpt_warn(gpt, "Cannot get media size");
    532  1.44  christos 				goto close;
    533  1.44  christos 			}
    534  1.43   jnemeth #endif
    535  1.49  christos 			if (gpt->mediasz == 0) {
    536  1.49  christos 				gpt_warnx(gpt, "Media size can't be 0");
    537  1.44  christos 				goto close;
    538  1.44  christos 			}
    539  1.44  christos 		}
    540   1.1  christos 	} else {
    541  1.62  christos 		gpt->flags |= GPT_FILE;
    542  1.49  christos 		if (gpt->secsz == 0)
    543  1.49  christos 			gpt->secsz = 512;	/* Fixed size for files. */
    544  1.49  christos 		if (gpt->mediasz == 0) {
    545  1.49  christos 			if (gpt->sb.st_size % gpt->secsz) {
    546  1.86   mlelstv 				gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz);
    547  1.36  christos 				errno = EINVAL;
    548  1.36  christos 				goto close;
    549  1.36  christos 			}
    550  1.49  christos 			gpt->mediasz = gpt->sb.st_size;
    551   1.1  christos 		}
    552  1.56  christos 		gpt->flags |= GPT_NOSYNC;
    553   1.1  christos 	}
    554   1.1  christos 
    555   1.1  christos 	/*
    556   1.1  christos 	 * We require an absolute minimum of 6 sectors. One for the MBR,
    557   1.1  christos 	 * 2 for the GPT header, 2 for the GPT table and one to hold some
    558   1.1  christos 	 * user data. Let's catch this extreme border case here so that
    559   1.1  christos 	 * we don't have to worry about it later.
    560   1.1  christos 	 */
    561  1.49  christos 	devsz = gpt->mediasz / gpt->secsz;
    562  1.45  christos 	if (devsz < 6) {
    563  1.62  christos 		gpt_warnx(gpt, "Need 6 sectors, we have %ju",
    564  1.49  christos 		    (uintmax_t)devsz);
    565   1.1  christos 		goto close;
    566   1.1  christos 	}
    567   1.1  christos 
    568  1.49  christos 	if (gpt->verbose) {
    569  1.49  christos 		gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
    570  1.49  christos 		    (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
    571  1.42  christos 	}
    572   1.1  christos 
    573  1.57  christos 	if (map_init(gpt, devsz) == -1)
    574  1.57  christos 		goto close;
    575   1.1  christos 
    576  1.91  christos 	idx = 1;
    577  1.91  christos 	if (gpt_mbr(gpt, 0LL, &idx, 0U) == -1)
    578   1.1  christos 		goto close;
    579  1.49  christos 	if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
    580   1.1  christos 		goto close;
    581  1.83   mlelstv 
    582  1.83   mlelstv 	if (found) {
    583  1.83   mlelstv 		struct map *map;
    584  1.83   mlelstv 		struct gpt_hdr *hdr;
    585  1.84   mlelstv 		uint64_t lba;
    586  1.83   mlelstv 
    587  1.83   mlelstv 		/*
    588  1.83   mlelstv 		 * read secondary GPT from position stored in primary header
    589  1.83   mlelstv 		 * when possible
    590  1.83   mlelstv 		 */
    591  1.83   mlelstv 		map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    592  1.83   mlelstv 		hdr = map ? map->map_data : NULL;
    593  1.84   mlelstv 		lba = le64toh(hdr->hdr_lba_alt);
    594  1.84   mlelstv 		if (hdr && lba > 0 && lba < (uint64_t)devsz) {
    595  1.84   mlelstv 			if (gpt_gpt(gpt, (off_t)lba, found) == -1)
    596  1.83   mlelstv 				goto close;
    597  1.83   mlelstv 		}
    598  1.83   mlelstv 	} else {
    599  1.83   mlelstv 		if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
    600  1.83   mlelstv 			goto close;
    601  1.83   mlelstv 	}
    602   1.1  christos 
    603  1.49  christos 	return gpt;
    604   1.1  christos 
    605   1.1  christos  close:
    606  1.49  christos 	if (gpt->fd != -1)
    607  1.49  christos 		close(gpt->fd);
    608  1.87       mrg 	gpt_warn(gpt, "No GPT found");
    609  1.49  christos 	free(gpt);
    610  1.49  christos 	return NULL;
    611   1.1  christos }
    612   1.1  christos 
    613   1.1  christos void
    614  1.49  christos gpt_close(gpt_t gpt)
    615   1.1  christos {
    616  1.46  christos 
    617  1.78    martin 	if (gpt == NULL)
    618  1.78    martin 		return;
    619  1.78    martin 
    620  1.75   jnemeth 	if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
    621  1.48  christos 		goto out;
    622  1.48  christos 
    623  1.49  christos 	if (!(gpt->flags & GPT_NOSYNC)) {
    624  1.47  christos #ifdef DIOCMWEDGES
    625  1.47  christos 		int bits;
    626  1.49  christos 		if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
    627  1.49  christos 			gpt_warn(gpt, "Can't update wedge information");
    628  1.48  christos 		else
    629  1.48  christos 			goto out;
    630  1.47  christos #endif
    631  1.47  christos 	}
    632  1.62  christos 	if (!(gpt->flags & GPT_FILE))
    633  1.62  christos 		gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
    634  1.62  christos 		    " for the changes to take effect\n", gpt->device_name);
    635  1.46  christos 
    636  1.48  christos out:
    637  1.49  christos 	close(gpt->fd);
    638   1.1  christos }
    639   1.1  christos 
    640  1.67     joerg __printflike(2, 0)
    641  1.66  christos static void
    642  1.66  christos gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
    643  1.66  christos {
    644  1.66  christos 	if (gpt && (gpt->flags & GPT_QUIET))
    645  1.66  christos 		return;
    646  1.66  christos 	fprintf(stderr, "%s: ", getprogname());
    647  1.66  christos 	if (gpt)
    648  1.66  christos 		fprintf(stderr, "%s: ", gpt->device_name);
    649  1.66  christos 	vfprintf(stderr, fmt, ap);
    650  1.66  christos 	if (e)
    651  1.66  christos 		fprintf(stderr, " (%s)\n", e);
    652  1.66  christos 	else
    653  1.66  christos 		fputc('\n', stderr);
    654  1.66  christos }
    655  1.66  christos 
    656  1.42  christos void
    657  1.49  christos gpt_warnx(gpt_t gpt, const char *fmt, ...)
    658  1.42  christos {
    659  1.42  christos 	va_list ap;
    660  1.48  christos 
    661  1.49  christos 	va_start(ap, fmt);
    662  1.66  christos 	gpt_vwarnx(gpt, fmt, ap, NULL);
    663  1.49  christos 	va_end(ap);
    664  1.49  christos }
    665  1.49  christos 
    666  1.49  christos void
    667  1.49  christos gpt_warn(gpt_t gpt, const char *fmt, ...)
    668  1.49  christos {
    669  1.49  christos 	va_list ap;
    670  1.49  christos 
    671  1.49  christos 	va_start(ap, fmt);
    672  1.66  christos 	gpt_vwarnx(gpt, fmt, ap, strerror(errno));
    673  1.49  christos 	va_end(ap);
    674  1.49  christos }
    675  1.49  christos 
    676  1.49  christos void
    677  1.49  christos gpt_msg(gpt_t gpt, const char *fmt, ...)
    678  1.49  christos {
    679  1.49  christos 	va_list ap;
    680  1.49  christos 
    681  1.66  christos 	if (gpt && (gpt->flags & GPT_QUIET))
    682  1.49  christos 		return;
    683  1.66  christos 	if (gpt)
    684  1.66  christos 		printf("%s: ", gpt->device_name);
    685  1.42  christos 	va_start(ap, fmt);
    686  1.42  christos 	vprintf(fmt, ap);
    687  1.42  christos 	va_end(ap);
    688  1.42  christos 	printf("\n");
    689  1.42  christos }
    690  1.49  christos 
    691  1.49  christos struct gpt_hdr *
    692  1.49  christos gpt_hdr(gpt_t gpt)
    693  1.49  christos {
    694  1.49  christos 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    695  1.49  christos 	if (gpt->gpt == NULL) {
    696  1.49  christos 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    697  1.49  christos 		return NULL;
    698  1.49  christos 	}
    699  1.49  christos 
    700  1.49  christos 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    701  1.49  christos 	if (gpt->tpg == NULL) {
    702  1.49  christos 		gpt_warnx(gpt, "No secondary GPT header; run recover");
    703  1.49  christos 		return NULL;
    704  1.49  christos 	}
    705  1.49  christos 
    706  1.49  christos 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    707  1.49  christos 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    708  1.49  christos 	if (gpt->tbl == NULL || gpt->lbt == NULL) {
    709  1.49  christos 		gpt_warnx(gpt, "Corrupt maps, run recover");
    710  1.49  christos 		return NULL;
    711  1.49  christos 	}
    712  1.49  christos 
    713  1.49  christos 	return gpt->gpt->map_data;
    714  1.49  christos }
    715  1.49  christos 
    716  1.49  christos int
    717  1.49  christos gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
    718  1.49  christos {
    719  1.49  christos 	struct gpt_hdr *hdr = map->map_data;
    720  1.49  christos 
    721  1.49  christos 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
    722  1.49  christos 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
    723  1.49  christos 	hdr->hdr_crc_self = 0;
    724  1.49  christos 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    725  1.49  christos 
    726  1.49  christos 	if (gpt_write(gpt, map) == -1) {
    727  1.49  christos 		gpt_warn(gpt, "Error writing crc map");
    728  1.49  christos 		return -1;
    729  1.49  christos 	}
    730  1.49  christos 
    731  1.49  christos 	if (gpt_write(gpt, tbl) == -1) {
    732  1.49  christos 		gpt_warn(gpt, "Error writing crc table");
    733  1.49  christos 		return -1;
    734  1.49  christos 	}
    735  1.49  christos 
    736  1.49  christos 	return 0;
    737  1.49  christos }
    738  1.49  christos 
    739  1.49  christos int
    740  1.49  christos gpt_write_primary(gpt_t gpt)
    741  1.49  christos {
    742  1.49  christos 	return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
    743  1.49  christos }
    744  1.49  christos 
    745  1.49  christos 
    746  1.49  christos int
    747  1.49  christos gpt_write_backup(gpt_t gpt)
    748  1.49  christos {
    749  1.49  christos 	return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
    750  1.49  christos }
    751  1.49  christos 
    752  1.49  christos void
    753  1.68  christos gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
    754  1.49  christos {
    755  1.68  christos 	part->part_flag = active ? 0x80 : 0;
    756  1.49  christos 	part->part_shd = 0x00;
    757  1.49  christos 	part->part_ssect = 0x02;
    758  1.49  christos 	part->part_scyl = 0x00;
    759  1.49  christos 	part->part_typ = MBR_PTYPE_PMBR;
    760  1.49  christos 	part->part_ehd = 0xfe;
    761  1.49  christos 	part->part_esect = 0xff;
    762  1.49  christos 	part->part_ecyl = 0xff;
    763  1.49  christos 	part->part_start_lo = htole16(1);
    764  1.49  christos 	if (last > 0xffffffff) {
    765  1.49  christos 		part->part_size_lo = htole16(0xffff);
    766  1.49  christos 		part->part_size_hi = htole16(0xffff);
    767  1.49  christos 	} else {
    768  1.59  christos 		part->part_size_lo = htole16((uint16_t)last);
    769  1.91  christos 		part->part_size_hi = htole16((uint16_t)((uint64_t)last >> 16));
    770  1.49  christos 	}
    771  1.49  christos }
    772  1.49  christos 
    773  1.49  christos struct gpt_ent *
    774  1.49  christos gpt_ent(map_t map, map_t tbl, unsigned int i)
    775  1.49  christos {
    776  1.49  christos 	struct gpt_hdr *hdr = map->map_data;
    777  1.49  christos 	return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
    778  1.49  christos }
    779  1.49  christos 
    780  1.49  christos struct gpt_ent *
    781  1.49  christos gpt_ent_primary(gpt_t gpt, unsigned int i)
    782  1.49  christos {
    783  1.49  christos 	return gpt_ent(gpt->gpt, gpt->tbl, i);
    784  1.49  christos }
    785  1.49  christos 
    786  1.49  christos struct gpt_ent *
    787  1.49  christos gpt_ent_backup(gpt_t gpt, unsigned int i)
    788  1.49  christos {
    789  1.49  christos 	return gpt_ent(gpt->tpg, gpt->lbt, i);
    790  1.49  christos }
    791  1.50  christos 
    792  1.50  christos int
    793  1.50  christos gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
    794  1.50  christos {
    795  1.50  christos 	const char **a = cmd->help;
    796  1.50  christos 	size_t hlen = cmd->hlen;
    797  1.50  christos 	size_t i;
    798  1.50  christos 
    799  1.50  christos 	if (prefix == NULL) {
    800  1.50  christos 		const char *pname = getprogname();
    801  1.54  christos 		const char *d1, *d2, *d = " <device>";
    802  1.50  christos 		int len = (int)strlen(pname);
    803  1.54  christos 		if (strcmp(pname, "gpt") == 0) {
    804  1.54  christos 			d1 = "";
    805  1.54  christos 			d2 = d;
    806  1.54  christos 		} else {
    807  1.54  christos 			d2 = "";
    808  1.54  christos 			d1 = d;
    809  1.54  christos 		}
    810  1.54  christos 		fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
    811  1.54  christos 		    d1, cmd->name, a[0], d2);
    812  1.50  christos 		for (i = 1; i < hlen; i++) {
    813  1.50  christos 			fprintf(stderr,
    814  1.54  christos 			    "       %*s%s %s %s%s\n", len, "",
    815  1.54  christos 			    d1, cmd->name, a[i], d2);
    816  1.50  christos 		}
    817  1.50  christos 	} else {
    818  1.50  christos 		for (i = 0; i < hlen; i++)
    819  1.50  christos 		    fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
    820  1.50  christos 	}
    821  1.50  christos 	return -1;
    822  1.50  christos }
    823  1.51  christos 
    824  1.51  christos off_t
    825  1.51  christos gpt_last(gpt_t gpt)
    826  1.51  christos {
    827  1.51  christos 	return gpt->mediasz / gpt->secsz - 1LL;
    828  1.51  christos }
    829  1.51  christos 
    830  1.59  christos off_t
    831  1.51  christos gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
    832  1.51  christos {
    833  1.91  christos 	off_t blocks, lastoff;
    834  1.51  christos 	map_t map;
    835  1.51  christos 	struct gpt_hdr *hdr;
    836  1.51  christos 	struct gpt_ent *ent;
    837  1.51  christos 	unsigned int i;
    838  1.51  christos 	void *p;
    839  1.51  christos 
    840  1.51  christos 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
    841  1.51  christos 	    map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
    842  1.69  christos 		gpt_warnx(gpt, "Device already contains a GPT, "
    843  1.69  christos 		    "destroy it first");
    844  1.51  christos 		return -1;
    845  1.51  christos 	}
    846  1.51  christos 
    847  1.51  christos 	/* Get the amount of free space after the MBR */
    848  1.51  christos 	blocks = map_free(gpt, 1LL, 0LL);
    849  1.51  christos 	if (blocks == 0LL) {
    850  1.51  christos 		gpt_warnx(gpt, "No room for the GPT header");
    851  1.51  christos 		return -1;
    852  1.51  christos 	}
    853  1.51  christos 
    854  1.51  christos 	/* Don't create more than parts entries. */
    855  1.51  christos 	if ((uint64_t)(blocks - 1) * gpt->secsz >
    856  1.51  christos 	    parts * sizeof(struct gpt_ent)) {
    857  1.59  christos 		blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
    858  1.51  christos 		if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
    859  1.51  christos 			blocks++;
    860  1.51  christos 		blocks++;		/* Don't forget the header itself */
    861  1.51  christos 	}
    862  1.51  christos 
    863  1.51  christos 	/* Never cross the median of the device. */
    864  1.91  christos 	lastoff = (off_t)(((uint64_t)last + 1LL) >> 1);
    865  1.91  christos 	if ((blocks + 1LL) > lastoff)
    866  1.91  christos 		blocks = lastoff - 1LL;
    867  1.51  christos 
    868  1.51  christos 	/*
    869  1.51  christos 	 * Get the amount of free space at the end of the device and
    870  1.51  christos 	 * calculate the size for the GPT structures.
    871  1.51  christos 	 */
    872  1.51  christos 	map = map_last(gpt);
    873  1.51  christos 	if (map->map_type != MAP_TYPE_UNUSED) {
    874  1.51  christos 		gpt_warnx(gpt, "No room for the backup header");
    875  1.51  christos 		return -1;
    876  1.51  christos 	}
    877  1.51  christos 
    878  1.51  christos 	if (map->map_size < blocks)
    879  1.51  christos 		blocks = map->map_size;
    880  1.51  christos 	if (blocks == 1LL) {
    881  1.51  christos 		gpt_warnx(gpt, "No room for the GPT table");
    882  1.51  christos 		return -1;
    883  1.51  christos 	}
    884  1.51  christos 
    885  1.51  christos 	blocks--;		/* Number of blocks in the GPT table. */
    886  1.51  christos 
    887  1.63  christos 	if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
    888  1.51  christos 		return -1;
    889  1.51  christos 
    890  1.59  christos 	if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
    891  1.51  christos 		gpt_warnx(gpt, "Can't allocate the primary GPT table");
    892  1.51  christos 		return -1;
    893  1.51  christos 	}
    894  1.51  christos 	if ((gpt->tbl = map_add(gpt, 2LL, blocks,
    895  1.61  christos 	    MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
    896  1.51  christos 		free(p);
    897  1.51  christos 		gpt_warnx(gpt, "Can't add the primary GPT table");
    898  1.51  christos 		return -1;
    899  1.51  christos 	}
    900  1.51  christos 
    901  1.51  christos 	hdr = gpt->gpt->map_data;
    902  1.51  christos 	memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
    903  1.51  christos 
    904  1.51  christos 	/*
    905  1.51  christos 	 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
    906  1.51  christos 	 * contains padding we must not include in the size.
    907  1.51  christos 	 */
    908  1.51  christos 	hdr->hdr_revision = htole32(GPT_HDR_REVISION);
    909  1.51  christos 	hdr->hdr_size = htole32(GPT_HDR_SIZE);
    910  1.59  christos 	hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
    911  1.59  christos 	hdr->hdr_lba_alt = htole64((uint64_t)last);
    912  1.59  christos 	hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
    913  1.59  christos 	hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
    914  1.52  christos 	if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
    915  1.52  christos 		return -1;
    916  1.59  christos 	hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
    917  1.59  christos 	hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
    918  1.59  christos 	    sizeof(struct gpt_ent)));
    919  1.51  christos 	if (le32toh(hdr->hdr_entries) > parts)
    920  1.51  christos 		hdr->hdr_entries = htole32(parts);
    921  1.51  christos 	hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
    922  1.51  christos 
    923  1.51  christos 	ent = gpt->tbl->map_data;
    924  1.51  christos 	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
    925  1.52  christos 		if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
    926  1.52  christos 			return -1;
    927  1.51  christos 	}
    928  1.51  christos 
    929  1.51  christos 	/*
    930  1.51  christos 	 * Create backup GPT if the user didn't suppress it.
    931  1.51  christos 	 */
    932  1.51  christos 	if (primary_only)
    933  1.51  christos 		return last;
    934  1.51  christos 
    935  1.63  christos 	if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    936  1.51  christos 		return -1;
    937  1.51  christos 
    938  1.51  christos 	if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
    939  1.61  christos 	    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
    940  1.51  christos 		gpt_warnx(gpt, "Can't add the secondary GPT table");
    941  1.51  christos 		return -1;
    942  1.51  christos 	}
    943  1.51  christos 
    944  1.51  christos 	memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    945  1.51  christos 
    946  1.51  christos 	hdr = gpt->tpg->map_data;
    947  1.59  christos 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    948  1.59  christos 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    949  1.59  christos 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    950  1.51  christos 	return last;
    951  1.51  christos }
    952  1.51  christos 
    953  1.52  christos static int
    954  1.52  christos gpt_size_get(gpt_t gpt, off_t *size)
    955  1.52  christos {
    956  1.52  christos 	off_t sectors;
    957  1.52  christos 	int64_t human_num;
    958  1.52  christos 	char *p;
    959  1.52  christos 
    960  1.52  christos 	if (*size > 0)
    961  1.52  christos 		return -1;
    962  1.52  christos 	sectors = strtoll(optarg, &p, 10);
    963  1.52  christos 	if (sectors < 1)
    964  1.52  christos 		return -1;
    965  1.52  christos 	if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
    966  1.52  christos 		*size = sectors * gpt->secsz;
    967  1.52  christos 		return 0;
    968  1.52  christos 	}
    969  1.52  christos 	if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
    970  1.52  christos 		*size = sectors;
    971  1.52  christos 		return 0;
    972  1.52  christos 	}
    973  1.52  christos 	if (dehumanize_number(optarg, &human_num) < 0)
    974  1.52  christos 		return -1;
    975  1.52  christos 	*size = human_num;
    976  1.52  christos 	return 0;
    977  1.52  christos }
    978  1.52  christos 
    979  1.51  christos int
    980  1.66  christos gpt_human_get(gpt_t gpt, off_t *human)
    981  1.51  christos {
    982  1.51  christos 	int64_t human_num;
    983  1.51  christos 
    984  1.66  christos 	if (*human > 0) {
    985  1.66  christos 		gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
    986  1.66  christos 		    optarg);
    987  1.52  christos 		return -1;
    988  1.66  christos 	}
    989  1.66  christos 	if (dehumanize_number(optarg, &human_num) < 0) {
    990  1.66  christos 		gpt_warn(gpt, "Bad number `%s'", optarg);
    991  1.52  christos 		return -1;
    992  1.66  christos 	}
    993  1.52  christos 	*human = human_num;
    994  1.66  christos 	if (*human < 1) {
    995  1.66  christos 		gpt_warn(gpt, "Number `%s' < 1", optarg);
    996  1.52  christos 		return -1;
    997  1.66  christos 	}
    998  1.52  christos 	return 0;
    999  1.52  christos }
   1000  1.52  christos 
   1001  1.52  christos int
   1002  1.52  christos gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
   1003  1.52  christos {
   1004  1.51  christos 	switch (ch) {
   1005  1.51  christos 	case 'a':
   1006  1.66  christos 		if (find->all > 0) {
   1007  1.66  christos 			gpt_warn(gpt, "-a is already set");
   1008  1.51  christos 			return -1;
   1009  1.66  christos 		}
   1010  1.51  christos 		find->all = 1;
   1011  1.51  christos 		break;
   1012  1.51  christos 	case 'b':
   1013  1.66  christos 		if (gpt_human_get(gpt, &find->block) == -1)
   1014  1.51  christos 			return -1;
   1015  1.51  christos 		break;
   1016  1.51  christos 	case 'i':
   1017  1.66  christos 		if (gpt_uint_get(gpt, &find->entry) == -1)
   1018  1.51  christos 			return -1;
   1019  1.51  christos 		break;
   1020  1.51  christos 	case 'L':
   1021  1.52  christos 		if (gpt_name_get(gpt, &find->label) == -1)
   1022  1.51  christos 			return -1;
   1023  1.51  christos 		break;
   1024  1.51  christos 	case 's':
   1025  1.52  christos 		if (gpt_size_get(gpt, &find->size) == -1)
   1026  1.51  christos 			return -1;
   1027  1.51  christos 		break;
   1028  1.51  christos 	case 't':
   1029  1.51  christos 		if (!gpt_uuid_is_nil(find->type))
   1030  1.51  christos 			return -1;
   1031  1.51  christos 		if (gpt_uuid_parse(optarg, find->type) != 0)
   1032  1.51  christos 			return -1;
   1033  1.51  christos 		break;
   1034  1.51  christos 	default:
   1035  1.66  christos 		gpt_warn(gpt, "Unknown find option `%c'", ch);
   1036  1.51  christos 		return -1;
   1037  1.51  christos 	}
   1038  1.51  christos 	return 0;
   1039  1.51  christos }
   1040  1.51  christos 
   1041  1.51  christos int
   1042  1.51  christos gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
   1043  1.79   jnemeth     void (*cfn)(struct gpt_ent *, void *, int), void *v)
   1044  1.51  christos {
   1045  1.51  christos 	map_t m;
   1046  1.51  christos 	struct gpt_ent *ent;
   1047  1.51  christos 	unsigned int i;
   1048  1.53  christos 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
   1049  1.51  christos 
   1050  1.51  christos 	if (!find->all ^
   1051  1.51  christos 	    (find->block > 0 || find->entry > 0 || find->label != NULL
   1052  1.51  christos 	    || find->size > 0 || !gpt_uuid_is_nil(find->type)))
   1053  1.51  christos 		return -1;
   1054  1.51  christos 
   1055  1.91  christos 	if (gpt_hdr(gpt) == NULL)
   1056  1.51  christos 		return -1;
   1057  1.51  christos 
   1058  1.51  christos 	/* Relabel all matching entries in the map. */
   1059  1.51  christos 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
   1060  1.51  christos 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
   1061  1.51  christos 			continue;
   1062  1.51  christos 		if (find->entry > 0 && find->entry != m->map_index)
   1063  1.51  christos 			continue;
   1064  1.51  christos 		if (find->block > 0 && find->block != m->map_start)
   1065  1.51  christos 			continue;
   1066  1.51  christos 		if (find->size > 0 && find->size != m->map_size)
   1067  1.51  christos 			continue;
   1068  1.51  christos 
   1069  1.51  christos 		i = m->map_index - 1;
   1070  1.51  christos 
   1071  1.51  christos 		ent = gpt_ent_primary(gpt, i);
   1072  1.53  christos 		if (find->label != NULL) {
   1073  1.73  christos 			utf16_to_utf8(ent->ent_name,
   1074  1.73  christos 			    __arraycount(ent->ent_name),
   1075  1.73  christos 			    utfbuf, __arraycount(utfbuf));
   1076  1.76   mlelstv 			if (strcmp((char *)find->label, (char *)utfbuf) != 0)
   1077  1.51  christos 				continue;
   1078  1.53  christos 		}
   1079  1.51  christos 
   1080  1.51  christos 		if (!gpt_uuid_is_nil(find->type) &&
   1081  1.51  christos 		    !gpt_uuid_equal(find->type, ent->ent_type))
   1082  1.51  christos 			continue;
   1083  1.51  christos 
   1084  1.51  christos 		/* Change the primary entry. */
   1085  1.79   jnemeth 		(*cfn)(ent, v, 0);
   1086  1.51  christos 
   1087  1.51  christos 		if (gpt_write_primary(gpt) == -1)
   1088  1.51  christos 			return -1;
   1089  1.51  christos 
   1090  1.51  christos 		ent = gpt_ent_backup(gpt, i);
   1091  1.51  christos 		/* Change the secondary entry. */
   1092  1.79   jnemeth 		(*cfn)(ent, v, 1);
   1093  1.51  christos 
   1094  1.51  christos 		if (gpt_write_backup(gpt) == -1)
   1095  1.51  christos 			return -1;
   1096  1.51  christos 
   1097  1.51  christos 		gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
   1098  1.51  christos 	}
   1099  1.51  christos 	return 0;
   1100  1.51  christos }
   1101  1.51  christos 
   1102  1.51  christos int
   1103  1.80   jnemeth gpt_change_hdr(gpt_t gpt, const struct gpt_find *find,
   1104  1.80   jnemeth     void (*cfn)(struct gpt_hdr *, void *, int), void *v)
   1105  1.80   jnemeth {
   1106  1.80   jnemeth 	struct gpt_hdr *hdr;
   1107  1.80   jnemeth 
   1108  1.80   jnemeth 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1109  1.80   jnemeth 		return -1;
   1110  1.80   jnemeth 
   1111  1.80   jnemeth 	/* Change the primary header. */
   1112  1.80   jnemeth 	(*cfn)(hdr, v, 0);
   1113  1.80   jnemeth 
   1114  1.80   jnemeth 	if (gpt_write_primary(gpt) == -1)
   1115  1.80   jnemeth 		return -1;
   1116  1.80   jnemeth 
   1117  1.80   jnemeth 	hdr = gpt->tpg->map_data;
   1118  1.80   jnemeth 	/* Change the secondary header. */
   1119  1.80   jnemeth 	(*cfn)(hdr, v, 1);
   1120  1.80   jnemeth 
   1121  1.80   jnemeth 	if (gpt_write_backup(gpt) == -1)
   1122  1.80   jnemeth 		return -1;
   1123  1.80   jnemeth 
   1124  1.80   jnemeth 	gpt_msg(gpt, "Header %s", find->msg);
   1125  1.80   jnemeth 
   1126  1.80   jnemeth 	return 0;
   1127  1.80   jnemeth }
   1128  1.80   jnemeth 
   1129  1.80   jnemeth int
   1130  1.51  christos gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
   1131  1.51  christos {
   1132  1.51  christos 	switch (ch) {
   1133  1.51  christos 	case 'a':
   1134  1.66  christos 		if (gpt_human_get(gpt, alignment) == -1)
   1135  1.51  christos 			return -1;
   1136  1.51  christos 		return 0;
   1137  1.51  christos 	case 'i':
   1138  1.66  christos 		if (gpt_uint_get(gpt, entry) == -1)
   1139  1.51  christos 			return -1;
   1140  1.51  christos 		return 0;
   1141  1.51  christos 	case 's':
   1142  1.52  christos 		if (gpt_size_get(gpt, size) == -1)
   1143  1.51  christos 			return -1;
   1144  1.51  christos 		return 0;
   1145  1.51  christos 	default:
   1146  1.66  christos 		gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
   1147  1.51  christos 		return -1;
   1148  1.51  christos 	}
   1149  1.51  christos }
   1150  1.51  christos 
   1151  1.51  christos off_t
   1152  1.51  christos gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
   1153  1.51  christos {
   1154  1.51  christos 	if (entry == 0) {
   1155  1.51  christos 		gpt_warnx(gpt, "Entry not specified");
   1156  1.51  christos 		return -1;
   1157  1.51  christos 	}
   1158  1.51  christos 	if (alignment % gpt->secsz != 0) {
   1159  1.51  christos 		gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
   1160  1.51  christos 		    "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
   1161  1.51  christos 		return -1;
   1162  1.51  christos 	}
   1163  1.51  christos 
   1164  1.51  christos 	if (size % gpt->secsz != 0) {
   1165  1.51  christos 		gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
   1166  1.51  christos 		    "sector size (%#x)", (uintmax_t)size, gpt->secsz);
   1167  1.51  christos 		return -1;
   1168  1.51  christos 	}
   1169  1.51  christos 	if (size > 0)
   1170  1.51  christos 		return size / gpt->secsz;
   1171  1.51  christos 	return 0;
   1172  1.51  christos }
   1173  1.64  christos 
   1174  1.64  christos static const struct nvd {
   1175  1.64  christos 	const char *name;
   1176  1.64  christos 	uint64_t mask;
   1177  1.64  christos 	const char *description;
   1178  1.64  christos } gpt_attr[] = {
   1179  1.64  christos 	{
   1180  1.64  christos 		"biosboot",
   1181  1.64  christos 		GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
   1182  1.64  christos 		"Legacy BIOS boot partition",
   1183  1.64  christos 	},
   1184  1.64  christos 	{
   1185  1.64  christos 		"bootme",
   1186  1.64  christos 		GPT_ENT_ATTR_BOOTME,
   1187  1.64  christos 		"Bootable partition",
   1188  1.64  christos 	},
   1189  1.64  christos 	{
   1190  1.64  christos 		"bootfailed",
   1191  1.64  christos 		GPT_ENT_ATTR_BOOTFAILED,
   1192  1.64  christos 		"Partition that marked bootonce failed to boot",
   1193  1.64  christos 	},
   1194  1.64  christos 	{
   1195  1.64  christos 		"bootonce",
   1196  1.64  christos 		GPT_ENT_ATTR_BOOTONCE,
   1197  1.64  christos 		"Attempt to boot this partition only once",
   1198  1.64  christos 	},
   1199  1.64  christos 	{
   1200  1.64  christos 		"noblockio",
   1201  1.64  christos 		GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
   1202  1.64  christos 		"UEFI won't recognize file system for block I/O",
   1203  1.64  christos 	},
   1204  1.64  christos 	{
   1205  1.64  christos 		"required",
   1206  1.64  christos 		GPT_ENT_ATTR_REQUIRED_PARTITION,
   1207  1.64  christos 		"Partition required for platform to function",
   1208  1.64  christos 	},
   1209  1.64  christos };
   1210  1.64  christos 
   1211  1.51  christos int
   1212  1.64  christos gpt_attr_get(gpt_t gpt, uint64_t *attributes)
   1213  1.64  christos {
   1214  1.64  christos 	size_t i;
   1215  1.64  christos 	int rv = 0;
   1216  1.64  christos 	char *ptr;
   1217  1.64  christos 
   1218  1.64  christos 	*attributes = 0;
   1219  1.64  christos 
   1220  1.64  christos 	for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
   1221  1.64  christos 		for (i = 0; i < __arraycount(gpt_attr); i++)
   1222  1.64  christos 			if (strcmp(gpt_attr[i].name, ptr) == 0)
   1223  1.64  christos 				break;
   1224  1.64  christos 		if (i == __arraycount(gpt_attr)) {
   1225  1.89       kre 			gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
   1226  1.89       kre 			rv = -1;
   1227  1.64  christos 		} else
   1228  1.64  christos 			*attributes |= gpt_attr[i].mask;
   1229  1.64  christos 	}
   1230  1.64  christos 	return rv;
   1231  1.64  christos }
   1232  1.64  christos 
   1233  1.64  christos void
   1234  1.64  christos gpt_attr_help(const char *prefix)
   1235  1.51  christos {
   1236  1.64  christos 	size_t i;
   1237  1.64  christos 
   1238  1.64  christos 	for (i = 0; i < __arraycount(gpt_attr); i++)
   1239  1.64  christos 		printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
   1240  1.64  christos 		    gpt_attr[i].description);
   1241  1.64  christos }
   1242  1.64  christos 
   1243  1.64  christos const char *
   1244  1.64  christos gpt_attr_list(char *buf, size_t len, uint64_t attributes)
   1245  1.64  christos {
   1246  1.64  christos 	size_t i;
   1247  1.85       kre 	/*
   1248  1.85       kre 	 * a uint64_t (attributes) has at most 16 hex digits
   1249  1.85       kre 	 * in its representation, add 2 for "0x", and 2 more
   1250  1.85       kre 	 * for surrounding [ ], plus one for a trailing \0,
   1251  1.85       kre 	 * and we need 21 bytes, round that up to 24
   1252  1.85       kre 	 */
   1253  1.85       kre 	char xbuf[24];
   1254  1.85       kre 
   1255  1.64  christos 	strlcpy(buf, "", len);
   1256  1.64  christos 
   1257  1.85       kre 	for (i = 0; i < __arraycount(gpt_attr); i++) {
   1258  1.85       kre 		/*
   1259  1.85       kre 		 * if the attribute is specified in one of bits
   1260  1.85       kre 		 * 48..63, it should depend upon the defining
   1261  1.85       kre 		 * partition type for that attribute.   Currently
   1262  1.85       kre 		 * we have no idea what that is, so...
   1263  1.85       kre 		 *
   1264  1.85       kre 		 * Also note that for some partition types, these
   1265  1.85       kre 		 * fields are not a single bit boolean, but several
   1266  1.85       kre 		 * bits to form a numeric value.  That we could handle.
   1267  1.85       kre 		 */
   1268  1.85       kre 
   1269  1.64  christos 		if (attributes & gpt_attr[i].mask) {
   1270  1.65   jnemeth 			strlcat(buf, buf[0] ? ", " : "", len);
   1271  1.64  christos 			strlcat(buf, gpt_attr[i].name, len);
   1272  1.85       kre #if 0
   1273  1.85       kre 	/*
   1274  1.85       kre 	 * there are none currently defined, so this is untestable
   1275  1.85       kre 	 * (it does build however).
   1276  1.85       kre 	 */
   1277  1.85       kre 			if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) {
   1278  1.89       kre 				/* This only happens in bits 46..63 */
   1279  1.85       kre 
   1280  1.85       kre 				/*
   1281  1.85       kre 				 * xbuf is big enough for "=65535\0"
   1282  1.85       kre 				 * which is the biggest possible value
   1283  1.85       kre 				 */
   1284  1.85       kre 				snprintf(xbuf, sizeof xbuf, "=%ju",
   1285  1.85       kre 				    (uintmax_t) (
   1286  1.85       kre 				      (attributes & gpt_attr[i].mask) >>
   1287  1.85       kre 				      (ffs((int)(gpt_attr[i].mask >> 48)) + 47)
   1288  1.85       kre 				    ));
   1289  1.85       kre 
   1290  1.85       kre 				strlcat(buf, xbuf, len);
   1291  1.85       kre 			}
   1292  1.85       kre #endif
   1293  1.85       kre 			attributes &=~ gpt_attr[i].mask;
   1294  1.64  christos 		}
   1295  1.85       kre 	}
   1296  1.85       kre 
   1297  1.85       kre 	if (attributes != 0) {
   1298  1.85       kre 		snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes);
   1299  1.85       kre 		strlcat(buf, buf[0] ? ", " : "", len);
   1300  1.85       kre 		strlcat(buf, xbuf, len);
   1301  1.85       kre 	}
   1302  1.85       kre 
   1303  1.64  christos 	return buf;
   1304  1.51  christos }
   1305  1.64  christos 
   1306  1.51  christos int
   1307  1.51  christos gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
   1308  1.51  christos {
   1309  1.51  christos 	struct gpt_hdr *hdr;
   1310  1.51  christos 	struct gpt_ent *ent;
   1311  1.51  christos 	unsigned int i;
   1312  1.51  christos 
   1313  1.66  christos 	if (entry == 0 || (set == 0 && clr == 0)) {
   1314  1.66  christos 		gpt_warnx(gpt, "Nothing to set");
   1315  1.51  christos 		return -1;
   1316  1.66  christos 	}
   1317  1.51  christos 
   1318  1.51  christos 	if ((hdr = gpt_hdr(gpt)) == NULL)
   1319  1.51  christos 		return -1;
   1320  1.51  christos 
   1321  1.51  christos 	if (entry > le32toh(hdr->hdr_entries)) {
   1322  1.51  christos 		gpt_warnx(gpt, "Index %u out of range (%u max)",
   1323  1.51  christos 		    entry, le32toh(hdr->hdr_entries));
   1324  1.51  christos 		return -1;
   1325  1.51  christos 	}
   1326  1.51  christos 
   1327  1.51  christos 	i = entry - 1;
   1328  1.51  christos 	ent = gpt_ent_primary(gpt, i);
   1329  1.51  christos 	if (gpt_uuid_is_nil(ent->ent_type)) {
   1330  1.51  christos 		gpt_warnx(gpt, "Entry at index %u is unused", entry);
   1331  1.51  christos 		return -1;
   1332  1.51  christos 	}
   1333  1.51  christos 
   1334  1.51  christos 	ent->ent_attr &= ~clr;
   1335  1.51  christos 	ent->ent_attr |= set;
   1336  1.51  christos 
   1337  1.51  christos 	if (gpt_write_primary(gpt) == -1)
   1338  1.51  christos 		return -1;
   1339  1.51  christos 
   1340  1.51  christos 	ent = gpt_ent_backup(gpt, i);
   1341  1.51  christos 	ent->ent_attr &= ~clr;
   1342  1.51  christos 	ent->ent_attr |= set;
   1343  1.51  christos 
   1344  1.51  christos 	if (gpt_write_backup(gpt) == -1)
   1345  1.51  christos 		return -1;
   1346  1.51  christos 	gpt_msg(gpt, "Partition %d attributes updated", entry);
   1347  1.51  christos 	return 0;
   1348  1.51  christos }
   1349  1.51  christos 
   1350  1.51  christos int
   1351  1.66  christos gpt_uint_get(gpt_t gpt, u_int *entry)
   1352  1.51  christos {
   1353  1.51  christos 	char *p;
   1354  1.51  christos 	if (*entry > 0)
   1355  1.51  christos 		return -1;
   1356  1.59  christos 	*entry = (u_int)strtoul(optarg, &p, 10);
   1357  1.66  christos 	if (*p != 0 || *entry < 1) {
   1358  1.66  christos 		gpt_warn(gpt, "Bad number `%s'", optarg);
   1359  1.51  christos 		return -1;
   1360  1.66  christos 	}
   1361  1.51  christos 	return 0;
   1362  1.51  christos }
   1363  1.52  christos int
   1364  1.52  christos gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
   1365  1.52  christos {
   1366  1.52  christos 	if (!gpt_uuid_is_nil(*uuid))
   1367  1.52  christos 		return -1;
   1368  1.52  christos 	if (gpt_uuid_parse(optarg, *uuid) != 0) {
   1369  1.81  christos 		gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg);
   1370  1.52  christos 		return -1;
   1371  1.52  christos 	}
   1372  1.52  christos 	return 0;
   1373  1.52  christos }
   1374  1.52  christos 
   1375  1.52  christos int
   1376  1.52  christos gpt_name_get(gpt_t gpt, void *v)
   1377  1.52  christos {
   1378  1.52  christos 	char **name = v;
   1379  1.52  christos 	if (*name != NULL)
   1380  1.52  christos 		return -1;
   1381  1.52  christos 	*name = strdup(optarg);
   1382  1.52  christos 	if (*name == NULL) {
   1383  1.52  christos 		gpt_warn(gpt, "Can't copy string");
   1384  1.52  christos 		return -1;
   1385  1.52  christos 	}
   1386  1.52  christos 	return 0;
   1387  1.52  christos }
   1388  1.59  christos 
   1389  1.59  christos void
   1390  1.59  christos gpt_show_num(const char *prompt, uintmax_t num)
   1391  1.59  christos {
   1392  1.59  christos #ifdef HN_AUTOSCALE
   1393  1.59  christos 	char human_num[5];
   1394  1.59  christos 	if (humanize_number(human_num, 5, (int64_t)num ,
   1395  1.59  christos 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
   1396  1.59  christos 		human_num[0] = '\0';
   1397  1.59  christos #endif
   1398  1.59  christos 	printf("%s: %ju", prompt, num);
   1399  1.59  christos #ifdef HN_AUTOSCALE
   1400  1.59  christos 	if (human_num[0] != '\0')
   1401  1.60  christos 		printf(" (%s)", human_num);
   1402  1.59  christos #endif
   1403  1.59  christos 	printf("\n");
   1404  1.59  christos }
   1405  1.63  christos 
   1406  1.63  christos int
   1407  1.63  christos gpt_add_hdr(gpt_t gpt, int type, off_t loc)
   1408  1.63  christos {
   1409  1.63  christos 	void *p;
   1410  1.63  christos 	map_t *t;
   1411  1.63  christos 	const char *msg;
   1412  1.63  christos 
   1413  1.63  christos 	switch (type) {
   1414  1.63  christos 	case MAP_TYPE_PRI_GPT_HDR:
   1415  1.63  christos 		t = &gpt->gpt;
   1416  1.63  christos 		msg = "primary";
   1417  1.63  christos 		break;
   1418  1.63  christos 	case MAP_TYPE_SEC_GPT_HDR:
   1419  1.63  christos 		t = &gpt->tpg;
   1420  1.63  christos 		msg = "secondary";
   1421  1.63  christos 		break;
   1422  1.63  christos 	default:
   1423  1.63  christos 		gpt_warnx(gpt, "Unknown GPT header type %d", type);
   1424  1.63  christos 		return -1;
   1425  1.63  christos 	}
   1426  1.63  christos 
   1427  1.63  christos 	if ((p = calloc(1, gpt->secsz)) == NULL) {
   1428  1.63  christos 		gpt_warn(gpt, "Error allocating %s GPT header", msg);
   1429  1.63  christos 		return -1;
   1430  1.63  christos 	}
   1431  1.63  christos 
   1432  1.63  christos 	*t = map_add(gpt, loc, 1LL, type, p, 1);
   1433  1.63  christos 	if (*t == NULL) {
   1434  1.63  christos 		gpt_warn(gpt, "Error adding %s GPT header", msg);
   1435  1.63  christos 		free(p);
   1436  1.63  christos 		return -1;
   1437  1.63  christos 	}
   1438  1.63  christos 	return 0;
   1439  1.63  christos }
   1440