gpt.c revision 1.77 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.77 2019/01/27 13:16:05 martin 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
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 return -1;
391
392 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
393 goto fail_hdr;
394
395 crc = le32toh(hdr->hdr_crc_self);
396 hdr->hdr_crc_self = 0;
397 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
398 if (gpt->verbose)
399 gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
400 (uintmax_t)lba);
401 goto fail_hdr;
402 }
403
404 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
405 blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
406
407 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
408 p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
409 if (p == NULL) {
410 if (found) {
411 if (gpt->verbose)
412 gpt_msg(gpt,
413 "Cannot read LBA table at sector %ju",
414 (uintmax_t)le64toh(hdr->hdr_lba_table));
415 return -1;
416 }
417 goto fail_hdr;
418 }
419
420 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
421 if (gpt->verbose)
422 gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
423 (uintmax_t)le64toh(hdr->hdr_lba_table));
424 goto fail_ent;
425 }
426
427 if (gpt->verbose > 1)
428 gpt_msg(gpt, "%s GPT at sector %ju",
429 (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
430
431 m = map_add(gpt, lba, 1, (lba == 1)
432 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
433 if (m == NULL)
434 return (-1);
435
436 m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
437 (off_t)blocks,
438 lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
439 if (m == NULL)
440 return (-1);
441
442 if (lba != 1)
443 return (1);
444
445 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
446 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
447 if (gpt_uuid_is_nil(ent->ent_type))
448 continue;
449
450 size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
451 le64toh((uint64_t)ent->ent_lba_start) + 1LL);
452 if (gpt->verbose > 2) {
453 char buf[128];
454 gpt_uuid_snprintf(buf, sizeof(buf), "%s",
455 ent->ent_type);
456 gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
457 "size=%ju", buf,
458 (uintmax_t)le64toh(ent->ent_lba_start),
459 (uintmax_t)size);
460 }
461 m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
462 size, MAP_TYPE_GPT_PART, ent, 0);
463 if (m == NULL)
464 return (-1);
465 m->map_index = i + 1;
466 }
467 return (1);
468
469 fail_ent:
470 free(p);
471
472 fail_hdr:
473 free(hdr);
474 return (0);
475 }
476
477 gpt_t
478 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
479 time_t timestamp)
480 {
481 int mode, found;
482 off_t devsz;
483 gpt_t gpt;
484 unsigned int index;
485
486 if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
487 if (!(flags & GPT_QUIET))
488 warn("Cannot allocate `%s'", dev);
489 return NULL;
490 }
491 gpt->flags = flags;
492 gpt->verbose = verbose;
493 gpt->mediasz = mediasz;
494 gpt->secsz = secsz;
495 gpt->timestamp = timestamp;
496
497 mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
498
499 gpt->fd = opendisk(dev, mode, gpt->device_name,
500 sizeof(gpt->device_name), 0);
501 if (gpt->fd == -1) {
502 strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
503 gpt_warn(gpt, "Cannot open");
504 goto close;
505 }
506
507 if (fstat(gpt->fd, &gpt->sb) == -1) {
508 gpt_warn(gpt, "Cannot stat");
509 goto close;
510 }
511
512 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
513 if (gpt->secsz == 0) {
514 #ifdef DIOCGSECTORSIZE
515 if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
516 gpt_warn(gpt, "Cannot get sector size");
517 goto close;
518 }
519 #endif
520 if (gpt->secsz == 0) {
521 gpt_warnx(gpt, "Sector size can't be 0");
522 goto close;
523 }
524 }
525 if (gpt->mediasz == 0) {
526 #ifdef DIOCGMEDIASIZE
527 if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
528 gpt_warn(gpt, "Cannot get media size");
529 goto close;
530 }
531 #endif
532 if (gpt->mediasz == 0) {
533 gpt_warnx(gpt, "Media size can't be 0");
534 goto close;
535 }
536 }
537 } else {
538 gpt->flags |= GPT_FILE;
539 if (gpt->secsz == 0)
540 gpt->secsz = 512; /* Fixed size for files. */
541 if (gpt->mediasz == 0) {
542 if (gpt->sb.st_size % gpt->secsz) {
543 errno = EINVAL;
544 goto close;
545 }
546 gpt->mediasz = gpt->sb.st_size;
547 }
548 gpt->flags |= GPT_NOSYNC;
549 }
550
551 /*
552 * We require an absolute minimum of 6 sectors. One for the MBR,
553 * 2 for the GPT header, 2 for the GPT table and one to hold some
554 * user data. Let's catch this extreme border case here so that
555 * we don't have to worry about it later.
556 */
557 devsz = gpt->mediasz / gpt->secsz;
558 if (devsz < 6) {
559 gpt_warnx(gpt, "Need 6 sectors, we have %ju",
560 (uintmax_t)devsz);
561 goto close;
562 }
563
564 if (gpt->verbose) {
565 gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
566 (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
567 }
568
569 if (map_init(gpt, devsz) == -1)
570 goto close;
571
572 index = 1;
573 if (gpt_mbr(gpt, 0LL, &index, 0U) == -1)
574 goto close;
575 if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
576 goto close;
577 if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
578 goto close;
579
580 return gpt;
581
582 close:
583 if (gpt->fd != -1)
584 close(gpt->fd);
585 free(gpt);
586 return NULL;
587 }
588
589 void
590 gpt_close(gpt_t gpt)
591 {
592
593 if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
594 goto out;
595
596 if (!(gpt->flags & GPT_NOSYNC)) {
597 #ifdef DIOCMWEDGES
598 int bits;
599 if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
600 gpt_warn(gpt, "Can't update wedge information");
601 else
602 goto out;
603 #endif
604 }
605 if (!(gpt->flags & GPT_FILE))
606 gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
607 " for the changes to take effect\n", gpt->device_name);
608
609 out:
610 close(gpt->fd);
611 }
612
613 __printflike(2, 0)
614 static void
615 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
616 {
617 if (gpt && (gpt->flags & GPT_QUIET))
618 return;
619 fprintf(stderr, "%s: ", getprogname());
620 if (gpt)
621 fprintf(stderr, "%s: ", gpt->device_name);
622 vfprintf(stderr, fmt, ap);
623 if (e)
624 fprintf(stderr, " (%s)\n", e);
625 else
626 fputc('\n', stderr);
627 }
628
629 void
630 gpt_warnx(gpt_t gpt, const char *fmt, ...)
631 {
632 va_list ap;
633
634 va_start(ap, fmt);
635 gpt_vwarnx(gpt, fmt, ap, NULL);
636 va_end(ap);
637 }
638
639 void
640 gpt_warn(gpt_t gpt, const char *fmt, ...)
641 {
642 va_list ap;
643
644 va_start(ap, fmt);
645 gpt_vwarnx(gpt, fmt, ap, strerror(errno));
646 va_end(ap);
647 }
648
649 void
650 gpt_msg(gpt_t gpt, const char *fmt, ...)
651 {
652 va_list ap;
653
654 if (gpt && (gpt->flags & GPT_QUIET))
655 return;
656 if (gpt)
657 printf("%s: ", gpt->device_name);
658 va_start(ap, fmt);
659 vprintf(fmt, ap);
660 va_end(ap);
661 printf("\n");
662 }
663
664 struct gpt_hdr *
665 gpt_hdr(gpt_t gpt)
666 {
667 gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
668 if (gpt->gpt == NULL) {
669 gpt_warnx(gpt, "No primary GPT header; run create or recover");
670 return NULL;
671 }
672
673 gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
674 if (gpt->tpg == NULL) {
675 gpt_warnx(gpt, "No secondary GPT header; run recover");
676 return NULL;
677 }
678
679 gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
680 gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
681 if (gpt->tbl == NULL || gpt->lbt == NULL) {
682 gpt_warnx(gpt, "Corrupt maps, run recover");
683 return NULL;
684 }
685
686 return gpt->gpt->map_data;
687 }
688
689 int
690 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
691 {
692 struct gpt_hdr *hdr = map->map_data;
693
694 hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
695 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
696 hdr->hdr_crc_self = 0;
697 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
698
699 if (gpt_write(gpt, map) == -1) {
700 gpt_warn(gpt, "Error writing crc map");
701 return -1;
702 }
703
704 if (gpt_write(gpt, tbl) == -1) {
705 gpt_warn(gpt, "Error writing crc table");
706 return -1;
707 }
708
709 return 0;
710 }
711
712 int
713 gpt_write_primary(gpt_t gpt)
714 {
715 return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
716 }
717
718
719 int
720 gpt_write_backup(gpt_t gpt)
721 {
722 return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
723 }
724
725 void
726 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
727 {
728 part->part_flag = active ? 0x80 : 0;
729 part->part_shd = 0x00;
730 part->part_ssect = 0x02;
731 part->part_scyl = 0x00;
732 part->part_typ = MBR_PTYPE_PMBR;
733 part->part_ehd = 0xfe;
734 part->part_esect = 0xff;
735 part->part_ecyl = 0xff;
736 part->part_start_lo = htole16(1);
737 if (last > 0xffffffff) {
738 part->part_size_lo = htole16(0xffff);
739 part->part_size_hi = htole16(0xffff);
740 } else {
741 part->part_size_lo = htole16((uint16_t)last);
742 part->part_size_hi = htole16((uint16_t)(last >> 16));
743 }
744 }
745
746 struct gpt_ent *
747 gpt_ent(map_t map, map_t tbl, unsigned int i)
748 {
749 struct gpt_hdr *hdr = map->map_data;
750 return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
751 }
752
753 struct gpt_ent *
754 gpt_ent_primary(gpt_t gpt, unsigned int i)
755 {
756 return gpt_ent(gpt->gpt, gpt->tbl, i);
757 }
758
759 struct gpt_ent *
760 gpt_ent_backup(gpt_t gpt, unsigned int i)
761 {
762 return gpt_ent(gpt->tpg, gpt->lbt, i);
763 }
764
765 int
766 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
767 {
768 const char **a = cmd->help;
769 size_t hlen = cmd->hlen;
770 size_t i;
771
772 if (prefix == NULL) {
773 const char *pname = getprogname();
774 const char *d1, *d2, *d = " <device>";
775 int len = (int)strlen(pname);
776 if (strcmp(pname, "gpt") == 0) {
777 d1 = "";
778 d2 = d;
779 } else {
780 d2 = "";
781 d1 = d;
782 }
783 fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
784 d1, cmd->name, a[0], d2);
785 for (i = 1; i < hlen; i++) {
786 fprintf(stderr,
787 " %*s%s %s %s%s\n", len, "",
788 d1, cmd->name, a[i], d2);
789 }
790 } else {
791 for (i = 0; i < hlen; i++)
792 fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
793 }
794 return -1;
795 }
796
797 off_t
798 gpt_last(gpt_t gpt)
799 {
800 return gpt->mediasz / gpt->secsz - 1LL;
801 }
802
803 off_t
804 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
805 {
806 off_t blocks;
807 map_t map;
808 struct gpt_hdr *hdr;
809 struct gpt_ent *ent;
810 unsigned int i;
811 void *p;
812
813 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
814 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
815 gpt_warnx(gpt, "Device already contains a GPT, "
816 "destroy it first");
817 return -1;
818 }
819
820 /* Get the amount of free space after the MBR */
821 blocks = map_free(gpt, 1LL, 0LL);
822 if (blocks == 0LL) {
823 gpt_warnx(gpt, "No room for the GPT header");
824 return -1;
825 }
826
827 /* Don't create more than parts entries. */
828 if ((uint64_t)(blocks - 1) * gpt->secsz >
829 parts * sizeof(struct gpt_ent)) {
830 blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
831 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
832 blocks++;
833 blocks++; /* Don't forget the header itself */
834 }
835
836 /* Never cross the median of the device. */
837 if ((blocks + 1LL) > ((last + 1LL) >> 1))
838 blocks = ((last + 1LL) >> 1) - 1LL;
839
840 /*
841 * Get the amount of free space at the end of the device and
842 * calculate the size for the GPT structures.
843 */
844 map = map_last(gpt);
845 if (map->map_type != MAP_TYPE_UNUSED) {
846 gpt_warnx(gpt, "No room for the backup header");
847 return -1;
848 }
849
850 if (map->map_size < blocks)
851 blocks = map->map_size;
852 if (blocks == 1LL) {
853 gpt_warnx(gpt, "No room for the GPT table");
854 return -1;
855 }
856
857 blocks--; /* Number of blocks in the GPT table. */
858
859 if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
860 return -1;
861
862 if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
863 gpt_warnx(gpt, "Can't allocate the primary GPT table");
864 return -1;
865 }
866 if ((gpt->tbl = map_add(gpt, 2LL, blocks,
867 MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
868 free(p);
869 gpt_warnx(gpt, "Can't add the primary GPT table");
870 return -1;
871 }
872
873 hdr = gpt->gpt->map_data;
874 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
875
876 /*
877 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
878 * contains padding we must not include in the size.
879 */
880 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
881 hdr->hdr_size = htole32(GPT_HDR_SIZE);
882 hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
883 hdr->hdr_lba_alt = htole64((uint64_t)last);
884 hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
885 hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
886 if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
887 return -1;
888 hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
889 hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
890 sizeof(struct gpt_ent)));
891 if (le32toh(hdr->hdr_entries) > parts)
892 hdr->hdr_entries = htole32(parts);
893 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
894
895 ent = gpt->tbl->map_data;
896 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
897 if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
898 return -1;
899 }
900
901 /*
902 * Create backup GPT if the user didn't suppress it.
903 */
904 if (primary_only)
905 return last;
906
907 if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
908 return -1;
909
910 if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
911 MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
912 gpt_warnx(gpt, "Can't add the secondary GPT table");
913 return -1;
914 }
915
916 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
917
918 hdr = gpt->tpg->map_data;
919 hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
920 hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
921 hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
922 return last;
923 }
924
925 static int
926 gpt_size_get(gpt_t gpt, off_t *size)
927 {
928 off_t sectors;
929 int64_t human_num;
930 char *p;
931
932 if (*size > 0)
933 return -1;
934 sectors = strtoll(optarg, &p, 10);
935 if (sectors < 1)
936 return -1;
937 if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
938 *size = sectors * gpt->secsz;
939 return 0;
940 }
941 if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
942 *size = sectors;
943 return 0;
944 }
945 if (dehumanize_number(optarg, &human_num) < 0)
946 return -1;
947 *size = human_num;
948 return 0;
949 }
950
951 int
952 gpt_human_get(gpt_t gpt, off_t *human)
953 {
954 int64_t human_num;
955
956 if (*human > 0) {
957 gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
958 optarg);
959 return -1;
960 }
961 if (dehumanize_number(optarg, &human_num) < 0) {
962 gpt_warn(gpt, "Bad number `%s'", optarg);
963 return -1;
964 }
965 *human = human_num;
966 if (*human < 1) {
967 gpt_warn(gpt, "Number `%s' < 1", optarg);
968 return -1;
969 }
970 return 0;
971 }
972
973 int
974 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
975 {
976 switch (ch) {
977 case 'a':
978 if (find->all > 0) {
979 gpt_warn(gpt, "-a is already set");
980 return -1;
981 }
982 find->all = 1;
983 break;
984 case 'b':
985 if (gpt_human_get(gpt, &find->block) == -1)
986 return -1;
987 break;
988 case 'i':
989 if (gpt_uint_get(gpt, &find->entry) == -1)
990 return -1;
991 break;
992 case 'L':
993 if (gpt_name_get(gpt, &find->label) == -1)
994 return -1;
995 break;
996 case 's':
997 if (gpt_size_get(gpt, &find->size) == -1)
998 return -1;
999 break;
1000 case 't':
1001 if (!gpt_uuid_is_nil(find->type))
1002 return -1;
1003 if (gpt_uuid_parse(optarg, find->type) != 0)
1004 return -1;
1005 break;
1006 default:
1007 gpt_warn(gpt, "Unknown find option `%c'", ch);
1008 return -1;
1009 }
1010 return 0;
1011 }
1012
1013 int
1014 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1015 void (*cfn)(struct gpt_ent *, void *), void *v)
1016 {
1017 map_t m;
1018 struct gpt_hdr *hdr;
1019 struct gpt_ent *ent;
1020 unsigned int i;
1021 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1022
1023 if (!find->all ^
1024 (find->block > 0 || find->entry > 0 || find->label != NULL
1025 || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1026 return -1;
1027
1028 if ((hdr = gpt_hdr(gpt)) == NULL)
1029 return -1;
1030
1031 /* Relabel all matching entries in the map. */
1032 for (m = map_first(gpt); m != NULL; m = m->map_next) {
1033 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1034 continue;
1035 if (find->entry > 0 && find->entry != m->map_index)
1036 continue;
1037 if (find->block > 0 && find->block != m->map_start)
1038 continue;
1039 if (find->size > 0 && find->size != m->map_size)
1040 continue;
1041
1042 i = m->map_index - 1;
1043
1044 ent = gpt_ent_primary(gpt, i);
1045 if (find->label != NULL) {
1046 utf16_to_utf8(ent->ent_name,
1047 __arraycount(ent->ent_name),
1048 utfbuf, __arraycount(utfbuf));
1049 if (strcmp((char *)find->label, (char *)utfbuf) != 0)
1050 continue;
1051 }
1052
1053 if (!gpt_uuid_is_nil(find->type) &&
1054 !gpt_uuid_equal(find->type, ent->ent_type))
1055 continue;
1056
1057 /* Change the primary entry. */
1058 (*cfn)(ent, v);
1059
1060 if (gpt_write_primary(gpt) == -1)
1061 return -1;
1062
1063 ent = gpt_ent_backup(gpt, i);
1064 /* Change the secondary entry. */
1065 (*cfn)(ent, v);
1066
1067 if (gpt_write_backup(gpt) == -1)
1068 return -1;
1069
1070 gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1071 }
1072 return 0;
1073 }
1074
1075 int
1076 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1077 {
1078 switch (ch) {
1079 case 'a':
1080 if (gpt_human_get(gpt, alignment) == -1)
1081 return -1;
1082 return 0;
1083 case 'i':
1084 if (gpt_uint_get(gpt, entry) == -1)
1085 return -1;
1086 return 0;
1087 case 's':
1088 if (gpt_size_get(gpt, size) == -1)
1089 return -1;
1090 return 0;
1091 default:
1092 gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1093 return -1;
1094 }
1095 }
1096
1097 off_t
1098 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1099 {
1100 if (entry == 0) {
1101 gpt_warnx(gpt, "Entry not specified");
1102 return -1;
1103 }
1104 if (alignment % gpt->secsz != 0) {
1105 gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1106 "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1107 return -1;
1108 }
1109
1110 if (size % gpt->secsz != 0) {
1111 gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1112 "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1113 return -1;
1114 }
1115 if (size > 0)
1116 return size / gpt->secsz;
1117 return 0;
1118 }
1119
1120 static const struct nvd {
1121 const char *name;
1122 uint64_t mask;
1123 const char *description;
1124 } gpt_attr[] = {
1125 {
1126 "biosboot",
1127 GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1128 "Legacy BIOS boot partition",
1129 },
1130 {
1131 "bootme",
1132 GPT_ENT_ATTR_BOOTME,
1133 "Bootable partition",
1134 },
1135 {
1136 "bootfailed",
1137 GPT_ENT_ATTR_BOOTFAILED,
1138 "Partition that marked bootonce failed to boot",
1139 },
1140 {
1141 "bootonce",
1142 GPT_ENT_ATTR_BOOTONCE,
1143 "Attempt to boot this partition only once",
1144 },
1145 {
1146 "noblockio",
1147 GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1148 "UEFI won't recognize file system for block I/O",
1149 },
1150 {
1151 "required",
1152 GPT_ENT_ATTR_REQUIRED_PARTITION,
1153 "Partition required for platform to function",
1154 },
1155 };
1156
1157 int
1158 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1159 {
1160 size_t i;
1161 int rv = 0;
1162 char *ptr;
1163
1164 *attributes = 0;
1165
1166 for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1167 for (i = 0; i < __arraycount(gpt_attr); i++)
1168 if (strcmp(gpt_attr[i].name, ptr) == 0)
1169 break;
1170 if (i == __arraycount(gpt_attr)) {
1171 gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
1172 rv = -1;
1173 } else
1174 *attributes |= gpt_attr[i].mask;
1175 }
1176 return rv;
1177 }
1178
1179 void
1180 gpt_attr_help(const char *prefix)
1181 {
1182 size_t i;
1183
1184 for (i = 0; i < __arraycount(gpt_attr); i++)
1185 printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1186 gpt_attr[i].description);
1187 }
1188
1189 const char *
1190 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1191 {
1192 size_t i;
1193 strlcpy(buf, "", len);
1194
1195 for (i = 0; i < __arraycount(gpt_attr); i++)
1196 if (attributes & gpt_attr[i].mask) {
1197 strlcat(buf, buf[0] ? ", " : "", len);
1198 strlcat(buf, gpt_attr[i].name, len);
1199 }
1200 return buf;
1201 }
1202
1203 int
1204 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1205 {
1206 struct gpt_hdr *hdr;
1207 struct gpt_ent *ent;
1208 unsigned int i;
1209
1210 if (entry == 0 || (set == 0 && clr == 0)) {
1211 gpt_warnx(gpt, "Nothing to set");
1212 return -1;
1213 }
1214
1215 if ((hdr = gpt_hdr(gpt)) == NULL)
1216 return -1;
1217
1218 if (entry > le32toh(hdr->hdr_entries)) {
1219 gpt_warnx(gpt, "Index %u out of range (%u max)",
1220 entry, le32toh(hdr->hdr_entries));
1221 return -1;
1222 }
1223
1224 i = entry - 1;
1225 ent = gpt_ent_primary(gpt, i);
1226 if (gpt_uuid_is_nil(ent->ent_type)) {
1227 gpt_warnx(gpt, "Entry at index %u is unused", entry);
1228 return -1;
1229 }
1230
1231 ent->ent_attr &= ~clr;
1232 ent->ent_attr |= set;
1233
1234 if (gpt_write_primary(gpt) == -1)
1235 return -1;
1236
1237 ent = gpt_ent_backup(gpt, i);
1238 ent->ent_attr &= ~clr;
1239 ent->ent_attr |= set;
1240
1241 if (gpt_write_backup(gpt) == -1)
1242 return -1;
1243 gpt_msg(gpt, "Partition %d attributes updated", entry);
1244 return 0;
1245 }
1246
1247 int
1248 gpt_uint_get(gpt_t gpt, u_int *entry)
1249 {
1250 char *p;
1251 if (*entry > 0)
1252 return -1;
1253 *entry = (u_int)strtoul(optarg, &p, 10);
1254 if (*p != 0 || *entry < 1) {
1255 gpt_warn(gpt, "Bad number `%s'", optarg);
1256 return -1;
1257 }
1258 return 0;
1259 }
1260 int
1261 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1262 {
1263 if (!gpt_uuid_is_nil(*uuid))
1264 return -1;
1265 if (gpt_uuid_parse(optarg, *uuid) != 0) {
1266 gpt_warn(gpt, "Can't parse uuid");
1267 return -1;
1268 }
1269 return 0;
1270 }
1271
1272 int
1273 gpt_name_get(gpt_t gpt, void *v)
1274 {
1275 char **name = v;
1276 if (*name != NULL)
1277 return -1;
1278 *name = strdup(optarg);
1279 if (*name == NULL) {
1280 gpt_warn(gpt, "Can't copy string");
1281 return -1;
1282 }
1283 return 0;
1284 }
1285
1286 void
1287 gpt_show_num(const char *prompt, uintmax_t num)
1288 {
1289 #ifdef HN_AUTOSCALE
1290 char human_num[5];
1291 if (humanize_number(human_num, 5, (int64_t)num ,
1292 "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1293 human_num[0] = '\0';
1294 #endif
1295 printf("%s: %ju", prompt, num);
1296 #ifdef HN_AUTOSCALE
1297 if (human_num[0] != '\0')
1298 printf(" (%s)", human_num);
1299 #endif
1300 printf("\n");
1301 }
1302
1303 int
1304 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1305 {
1306 void *p;
1307 map_t *t;
1308 const char *msg;
1309
1310 switch (type) {
1311 case MAP_TYPE_PRI_GPT_HDR:
1312 t = &gpt->gpt;
1313 msg = "primary";
1314 break;
1315 case MAP_TYPE_SEC_GPT_HDR:
1316 t = &gpt->tpg;
1317 msg = "secondary";
1318 break;
1319 default:
1320 gpt_warnx(gpt, "Unknown GPT header type %d", type);
1321 return -1;
1322 }
1323
1324 if ((p = calloc(1, gpt->secsz)) == NULL) {
1325 gpt_warn(gpt, "Error allocating %s GPT header", msg);
1326 return -1;
1327 }
1328
1329 *t = map_add(gpt, loc, 1LL, type, p, 1);
1330 if (*t == NULL) {
1331 gpt_warn(gpt, "Error adding %s GPT header", msg);
1332 free(p);
1333 return -1;
1334 }
1335 return 0;
1336 }
1337