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