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