gpt.c revision 1.86 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.86 2023/12/11 12:45:22 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
499 mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
500
501 gpt->fd = opendisk(dev, mode, gpt->device_name,
502 sizeof(gpt->device_name), 0);
503 if (gpt->fd == -1) {
504 strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
505 gpt_warn(gpt, "Cannot open");
506 goto close;
507 }
508
509 if (fstat(gpt->fd, &gpt->sb) == -1) {
510 gpt_warn(gpt, "Cannot stat");
511 goto close;
512 }
513
514 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
515 if (gpt->secsz == 0) {
516 #ifdef DIOCGSECTORSIZE
517 if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
518 gpt_warn(gpt, "Cannot get sector size");
519 goto close;
520 }
521 #endif
522 if (gpt->secsz == 0) {
523 gpt_warnx(gpt, "Sector size can't be 0");
524 goto close;
525 }
526 }
527 if (gpt->mediasz == 0) {
528 #ifdef DIOCGMEDIASIZE
529 if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
530 gpt_warn(gpt, "Cannot get media size");
531 goto close;
532 }
533 #endif
534 if (gpt->mediasz == 0) {
535 gpt_warnx(gpt, "Media size can't be 0");
536 goto close;
537 }
538 }
539 } else {
540 gpt->flags |= GPT_FILE;
541 if (gpt->secsz == 0)
542 gpt->secsz = 512; /* Fixed size for files. */
543 if (gpt->mediasz == 0) {
544 if (gpt->sb.st_size % gpt->secsz) {
545 gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz);
546 errno = EINVAL;
547 goto close;
548 }
549 gpt->mediasz = gpt->sb.st_size;
550 }
551 gpt->flags |= GPT_NOSYNC;
552 }
553
554 /*
555 * We require an absolute minimum of 6 sectors. One for the MBR,
556 * 2 for the GPT header, 2 for the GPT table and one to hold some
557 * user data. Let's catch this extreme border case here so that
558 * we don't have to worry about it later.
559 */
560 devsz = gpt->mediasz / gpt->secsz;
561 if (devsz < 6) {
562 gpt_warnx(gpt, "Need 6 sectors, we have %ju",
563 (uintmax_t)devsz);
564 goto close;
565 }
566
567 if (gpt->verbose) {
568 gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
569 (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
570 }
571
572 if (map_init(gpt, devsz) == -1)
573 goto close;
574
575 index = 1;
576 if (gpt_mbr(gpt, 0LL, &index, 0U) == -1)
577 goto close;
578 if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
579 goto close;
580
581 if (found) {
582 struct map *map;
583 struct gpt_hdr *hdr;
584 uint64_t lba;
585
586 /*
587 * read secondary GPT from position stored in primary header
588 * when possible
589 */
590 map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
591 hdr = map ? map->map_data : NULL;
592 lba = le64toh(hdr->hdr_lba_alt);
593 if (hdr && lba > 0 && lba < (uint64_t)devsz) {
594 if (gpt_gpt(gpt, (off_t)lba, found) == -1)
595 goto close;
596 }
597 } else {
598 if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
599 goto close;
600 }
601
602 return gpt;
603
604 close:
605 if (gpt->fd != -1)
606 close(gpt->fd);
607 free(gpt);
608 if (!(flags & GPT_QUIET))
609 gpt_warn(gpt, "No GPT found");
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