gpt.c revision 1.79 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.79 2019/06/21 02:14:59 jnemeth 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 == NULL)
594 return;
595
596 if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
597 goto out;
598
599 if (!(gpt->flags & GPT_NOSYNC)) {
600 #ifdef DIOCMWEDGES
601 int bits;
602 if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
603 gpt_warn(gpt, "Can't update wedge information");
604 else
605 goto out;
606 #endif
607 }
608 if (!(gpt->flags & GPT_FILE))
609 gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
610 " for the changes to take effect\n", gpt->device_name);
611
612 out:
613 close(gpt->fd);
614 }
615
616 __printflike(2, 0)
617 static void
618 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
619 {
620 if (gpt && (gpt->flags & GPT_QUIET))
621 return;
622 fprintf(stderr, "%s: ", getprogname());
623 if (gpt)
624 fprintf(stderr, "%s: ", gpt->device_name);
625 vfprintf(stderr, fmt, ap);
626 if (e)
627 fprintf(stderr, " (%s)\n", e);
628 else
629 fputc('\n', stderr);
630 }
631
632 void
633 gpt_warnx(gpt_t gpt, const char *fmt, ...)
634 {
635 va_list ap;
636
637 va_start(ap, fmt);
638 gpt_vwarnx(gpt, fmt, ap, NULL);
639 va_end(ap);
640 }
641
642 void
643 gpt_warn(gpt_t gpt, const char *fmt, ...)
644 {
645 va_list ap;
646
647 va_start(ap, fmt);
648 gpt_vwarnx(gpt, fmt, ap, strerror(errno));
649 va_end(ap);
650 }
651
652 void
653 gpt_msg(gpt_t gpt, const char *fmt, ...)
654 {
655 va_list ap;
656
657 if (gpt && (gpt->flags & GPT_QUIET))
658 return;
659 if (gpt)
660 printf("%s: ", gpt->device_name);
661 va_start(ap, fmt);
662 vprintf(fmt, ap);
663 va_end(ap);
664 printf("\n");
665 }
666
667 struct gpt_hdr *
668 gpt_hdr(gpt_t gpt)
669 {
670 gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
671 if (gpt->gpt == NULL) {
672 gpt_warnx(gpt, "No primary GPT header; run create or recover");
673 return NULL;
674 }
675
676 gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
677 if (gpt->tpg == NULL) {
678 gpt_warnx(gpt, "No secondary GPT header; run recover");
679 return NULL;
680 }
681
682 gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
683 gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
684 if (gpt->tbl == NULL || gpt->lbt == NULL) {
685 gpt_warnx(gpt, "Corrupt maps, run recover");
686 return NULL;
687 }
688
689 return gpt->gpt->map_data;
690 }
691
692 int
693 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
694 {
695 struct gpt_hdr *hdr = map->map_data;
696
697 hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
698 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
699 hdr->hdr_crc_self = 0;
700 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
701
702 if (gpt_write(gpt, map) == -1) {
703 gpt_warn(gpt, "Error writing crc map");
704 return -1;
705 }
706
707 if (gpt_write(gpt, tbl) == -1) {
708 gpt_warn(gpt, "Error writing crc table");
709 return -1;
710 }
711
712 return 0;
713 }
714
715 int
716 gpt_write_primary(gpt_t gpt)
717 {
718 return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
719 }
720
721
722 int
723 gpt_write_backup(gpt_t gpt)
724 {
725 return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
726 }
727
728 void
729 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
730 {
731 part->part_flag = active ? 0x80 : 0;
732 part->part_shd = 0x00;
733 part->part_ssect = 0x02;
734 part->part_scyl = 0x00;
735 part->part_typ = MBR_PTYPE_PMBR;
736 part->part_ehd = 0xfe;
737 part->part_esect = 0xff;
738 part->part_ecyl = 0xff;
739 part->part_start_lo = htole16(1);
740 if (last > 0xffffffff) {
741 part->part_size_lo = htole16(0xffff);
742 part->part_size_hi = htole16(0xffff);
743 } else {
744 part->part_size_lo = htole16((uint16_t)last);
745 part->part_size_hi = htole16((uint16_t)(last >> 16));
746 }
747 }
748
749 struct gpt_ent *
750 gpt_ent(map_t map, map_t tbl, unsigned int i)
751 {
752 struct gpt_hdr *hdr = map->map_data;
753 return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
754 }
755
756 struct gpt_ent *
757 gpt_ent_primary(gpt_t gpt, unsigned int i)
758 {
759 return gpt_ent(gpt->gpt, gpt->tbl, i);
760 }
761
762 struct gpt_ent *
763 gpt_ent_backup(gpt_t gpt, unsigned int i)
764 {
765 return gpt_ent(gpt->tpg, gpt->lbt, i);
766 }
767
768 int
769 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
770 {
771 const char **a = cmd->help;
772 size_t hlen = cmd->hlen;
773 size_t i;
774
775 if (prefix == NULL) {
776 const char *pname = getprogname();
777 const char *d1, *d2, *d = " <device>";
778 int len = (int)strlen(pname);
779 if (strcmp(pname, "gpt") == 0) {
780 d1 = "";
781 d2 = d;
782 } else {
783 d2 = "";
784 d1 = d;
785 }
786 fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
787 d1, cmd->name, a[0], d2);
788 for (i = 1; i < hlen; i++) {
789 fprintf(stderr,
790 " %*s%s %s %s%s\n", len, "",
791 d1, cmd->name, a[i], d2);
792 }
793 } else {
794 for (i = 0; i < hlen; i++)
795 fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
796 }
797 return -1;
798 }
799
800 off_t
801 gpt_last(gpt_t gpt)
802 {
803 return gpt->mediasz / gpt->secsz - 1LL;
804 }
805
806 off_t
807 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
808 {
809 off_t blocks;
810 map_t map;
811 struct gpt_hdr *hdr;
812 struct gpt_ent *ent;
813 unsigned int i;
814 void *p;
815
816 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
817 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
818 gpt_warnx(gpt, "Device already contains a GPT, "
819 "destroy it first");
820 return -1;
821 }
822
823 /* Get the amount of free space after the MBR */
824 blocks = map_free(gpt, 1LL, 0LL);
825 if (blocks == 0LL) {
826 gpt_warnx(gpt, "No room for the GPT header");
827 return -1;
828 }
829
830 /* Don't create more than parts entries. */
831 if ((uint64_t)(blocks - 1) * gpt->secsz >
832 parts * sizeof(struct gpt_ent)) {
833 blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
834 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
835 blocks++;
836 blocks++; /* Don't forget the header itself */
837 }
838
839 /* Never cross the median of the device. */
840 if ((blocks + 1LL) > ((last + 1LL) >> 1))
841 blocks = ((last + 1LL) >> 1) - 1LL;
842
843 /*
844 * Get the amount of free space at the end of the device and
845 * calculate the size for the GPT structures.
846 */
847 map = map_last(gpt);
848 if (map->map_type != MAP_TYPE_UNUSED) {
849 gpt_warnx(gpt, "No room for the backup header");
850 return -1;
851 }
852
853 if (map->map_size < blocks)
854 blocks = map->map_size;
855 if (blocks == 1LL) {
856 gpt_warnx(gpt, "No room for the GPT table");
857 return -1;
858 }
859
860 blocks--; /* Number of blocks in the GPT table. */
861
862 if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
863 return -1;
864
865 if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
866 gpt_warnx(gpt, "Can't allocate the primary GPT table");
867 return -1;
868 }
869 if ((gpt->tbl = map_add(gpt, 2LL, blocks,
870 MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
871 free(p);
872 gpt_warnx(gpt, "Can't add the primary GPT table");
873 return -1;
874 }
875
876 hdr = gpt->gpt->map_data;
877 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
878
879 /*
880 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
881 * contains padding we must not include in the size.
882 */
883 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
884 hdr->hdr_size = htole32(GPT_HDR_SIZE);
885 hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
886 hdr->hdr_lba_alt = htole64((uint64_t)last);
887 hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
888 hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
889 if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
890 return -1;
891 hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
892 hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
893 sizeof(struct gpt_ent)));
894 if (le32toh(hdr->hdr_entries) > parts)
895 hdr->hdr_entries = htole32(parts);
896 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
897
898 ent = gpt->tbl->map_data;
899 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
900 if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
901 return -1;
902 }
903
904 /*
905 * Create backup GPT if the user didn't suppress it.
906 */
907 if (primary_only)
908 return last;
909
910 if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
911 return -1;
912
913 if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
914 MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
915 gpt_warnx(gpt, "Can't add the secondary GPT table");
916 return -1;
917 }
918
919 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
920
921 hdr = gpt->tpg->map_data;
922 hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
923 hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
924 hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
925 return last;
926 }
927
928 static int
929 gpt_size_get(gpt_t gpt, off_t *size)
930 {
931 off_t sectors;
932 int64_t human_num;
933 char *p;
934
935 if (*size > 0)
936 return -1;
937 sectors = strtoll(optarg, &p, 10);
938 if (sectors < 1)
939 return -1;
940 if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
941 *size = sectors * gpt->secsz;
942 return 0;
943 }
944 if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
945 *size = sectors;
946 return 0;
947 }
948 if (dehumanize_number(optarg, &human_num) < 0)
949 return -1;
950 *size = human_num;
951 return 0;
952 }
953
954 int
955 gpt_human_get(gpt_t gpt, off_t *human)
956 {
957 int64_t human_num;
958
959 if (*human > 0) {
960 gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
961 optarg);
962 return -1;
963 }
964 if (dehumanize_number(optarg, &human_num) < 0) {
965 gpt_warn(gpt, "Bad number `%s'", optarg);
966 return -1;
967 }
968 *human = human_num;
969 if (*human < 1) {
970 gpt_warn(gpt, "Number `%s' < 1", optarg);
971 return -1;
972 }
973 return 0;
974 }
975
976 int
977 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
978 {
979 switch (ch) {
980 case 'a':
981 if (find->all > 0) {
982 gpt_warn(gpt, "-a is already set");
983 return -1;
984 }
985 find->all = 1;
986 break;
987 case 'b':
988 if (gpt_human_get(gpt, &find->block) == -1)
989 return -1;
990 break;
991 case 'i':
992 if (gpt_uint_get(gpt, &find->entry) == -1)
993 return -1;
994 break;
995 case 'L':
996 if (gpt_name_get(gpt, &find->label) == -1)
997 return -1;
998 break;
999 case 's':
1000 if (gpt_size_get(gpt, &find->size) == -1)
1001 return -1;
1002 break;
1003 case 't':
1004 if (!gpt_uuid_is_nil(find->type))
1005 return -1;
1006 if (gpt_uuid_parse(optarg, find->type) != 0)
1007 return -1;
1008 break;
1009 default:
1010 gpt_warn(gpt, "Unknown find option `%c'", ch);
1011 return -1;
1012 }
1013 return 0;
1014 }
1015
1016 int
1017 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1018 void (*cfn)(struct gpt_ent *, void *, int), void *v)
1019 {
1020 map_t m;
1021 struct gpt_hdr *hdr;
1022 struct gpt_ent *ent;
1023 unsigned int i;
1024 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1025
1026 if (!find->all ^
1027 (find->block > 0 || find->entry > 0 || find->label != NULL
1028 || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1029 return -1;
1030
1031 if ((hdr = gpt_hdr(gpt)) == NULL)
1032 return -1;
1033
1034 /* Relabel all matching entries in the map. */
1035 for (m = map_first(gpt); m != NULL; m = m->map_next) {
1036 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1037 continue;
1038 if (find->entry > 0 && find->entry != m->map_index)
1039 continue;
1040 if (find->block > 0 && find->block != m->map_start)
1041 continue;
1042 if (find->size > 0 && find->size != m->map_size)
1043 continue;
1044
1045 i = m->map_index - 1;
1046
1047 ent = gpt_ent_primary(gpt, i);
1048 if (find->label != NULL) {
1049 utf16_to_utf8(ent->ent_name,
1050 __arraycount(ent->ent_name),
1051 utfbuf, __arraycount(utfbuf));
1052 if (strcmp((char *)find->label, (char *)utfbuf) != 0)
1053 continue;
1054 }
1055
1056 if (!gpt_uuid_is_nil(find->type) &&
1057 !gpt_uuid_equal(find->type, ent->ent_type))
1058 continue;
1059
1060 /* Change the primary entry. */
1061 (*cfn)(ent, v, 0);
1062
1063 if (gpt_write_primary(gpt) == -1)
1064 return -1;
1065
1066 ent = gpt_ent_backup(gpt, i);
1067 /* Change the secondary entry. */
1068 (*cfn)(ent, v, 1);
1069
1070 if (gpt_write_backup(gpt) == -1)
1071 return -1;
1072
1073 gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1074 }
1075 return 0;
1076 }
1077
1078 int
1079 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1080 {
1081 switch (ch) {
1082 case 'a':
1083 if (gpt_human_get(gpt, alignment) == -1)
1084 return -1;
1085 return 0;
1086 case 'i':
1087 if (gpt_uint_get(gpt, entry) == -1)
1088 return -1;
1089 return 0;
1090 case 's':
1091 if (gpt_size_get(gpt, size) == -1)
1092 return -1;
1093 return 0;
1094 default:
1095 gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1096 return -1;
1097 }
1098 }
1099
1100 off_t
1101 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1102 {
1103 if (entry == 0) {
1104 gpt_warnx(gpt, "Entry not specified");
1105 return -1;
1106 }
1107 if (alignment % gpt->secsz != 0) {
1108 gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1109 "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1110 return -1;
1111 }
1112
1113 if (size % gpt->secsz != 0) {
1114 gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1115 "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1116 return -1;
1117 }
1118 if (size > 0)
1119 return size / gpt->secsz;
1120 return 0;
1121 }
1122
1123 static const struct nvd {
1124 const char *name;
1125 uint64_t mask;
1126 const char *description;
1127 } gpt_attr[] = {
1128 {
1129 "biosboot",
1130 GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1131 "Legacy BIOS boot partition",
1132 },
1133 {
1134 "bootme",
1135 GPT_ENT_ATTR_BOOTME,
1136 "Bootable partition",
1137 },
1138 {
1139 "bootfailed",
1140 GPT_ENT_ATTR_BOOTFAILED,
1141 "Partition that marked bootonce failed to boot",
1142 },
1143 {
1144 "bootonce",
1145 GPT_ENT_ATTR_BOOTONCE,
1146 "Attempt to boot this partition only once",
1147 },
1148 {
1149 "noblockio",
1150 GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1151 "UEFI won't recognize file system for block I/O",
1152 },
1153 {
1154 "required",
1155 GPT_ENT_ATTR_REQUIRED_PARTITION,
1156 "Partition required for platform to function",
1157 },
1158 };
1159
1160 int
1161 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1162 {
1163 size_t i;
1164 int rv = 0;
1165 char *ptr;
1166
1167 *attributes = 0;
1168
1169 for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1170 for (i = 0; i < __arraycount(gpt_attr); i++)
1171 if (strcmp(gpt_attr[i].name, ptr) == 0)
1172 break;
1173 if (i == __arraycount(gpt_attr)) {
1174 gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
1175 rv = -1;
1176 } else
1177 *attributes |= gpt_attr[i].mask;
1178 }
1179 return rv;
1180 }
1181
1182 void
1183 gpt_attr_help(const char *prefix)
1184 {
1185 size_t i;
1186
1187 for (i = 0; i < __arraycount(gpt_attr); i++)
1188 printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1189 gpt_attr[i].description);
1190 }
1191
1192 const char *
1193 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1194 {
1195 size_t i;
1196 strlcpy(buf, "", len);
1197
1198 for (i = 0; i < __arraycount(gpt_attr); i++)
1199 if (attributes & gpt_attr[i].mask) {
1200 strlcat(buf, buf[0] ? ", " : "", len);
1201 strlcat(buf, gpt_attr[i].name, len);
1202 }
1203 return buf;
1204 }
1205
1206 int
1207 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1208 {
1209 struct gpt_hdr *hdr;
1210 struct gpt_ent *ent;
1211 unsigned int i;
1212
1213 if (entry == 0 || (set == 0 && clr == 0)) {
1214 gpt_warnx(gpt, "Nothing to set");
1215 return -1;
1216 }
1217
1218 if ((hdr = gpt_hdr(gpt)) == NULL)
1219 return -1;
1220
1221 if (entry > le32toh(hdr->hdr_entries)) {
1222 gpt_warnx(gpt, "Index %u out of range (%u max)",
1223 entry, le32toh(hdr->hdr_entries));
1224 return -1;
1225 }
1226
1227 i = entry - 1;
1228 ent = gpt_ent_primary(gpt, i);
1229 if (gpt_uuid_is_nil(ent->ent_type)) {
1230 gpt_warnx(gpt, "Entry at index %u is unused", entry);
1231 return -1;
1232 }
1233
1234 ent->ent_attr &= ~clr;
1235 ent->ent_attr |= set;
1236
1237 if (gpt_write_primary(gpt) == -1)
1238 return -1;
1239
1240 ent = gpt_ent_backup(gpt, i);
1241 ent->ent_attr &= ~clr;
1242 ent->ent_attr |= set;
1243
1244 if (gpt_write_backup(gpt) == -1)
1245 return -1;
1246 gpt_msg(gpt, "Partition %d attributes updated", entry);
1247 return 0;
1248 }
1249
1250 int
1251 gpt_uint_get(gpt_t gpt, u_int *entry)
1252 {
1253 char *p;
1254 if (*entry > 0)
1255 return -1;
1256 *entry = (u_int)strtoul(optarg, &p, 10);
1257 if (*p != 0 || *entry < 1) {
1258 gpt_warn(gpt, "Bad number `%s'", optarg);
1259 return -1;
1260 }
1261 return 0;
1262 }
1263 int
1264 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1265 {
1266 if (!gpt_uuid_is_nil(*uuid))
1267 return -1;
1268 if (gpt_uuid_parse(optarg, *uuid) != 0) {
1269 gpt_warn(gpt, "Can't parse uuid");
1270 return -1;
1271 }
1272 return 0;
1273 }
1274
1275 int
1276 gpt_name_get(gpt_t gpt, void *v)
1277 {
1278 char **name = v;
1279 if (*name != NULL)
1280 return -1;
1281 *name = strdup(optarg);
1282 if (*name == NULL) {
1283 gpt_warn(gpt, "Can't copy string");
1284 return -1;
1285 }
1286 return 0;
1287 }
1288
1289 void
1290 gpt_show_num(const char *prompt, uintmax_t num)
1291 {
1292 #ifdef HN_AUTOSCALE
1293 char human_num[5];
1294 if (humanize_number(human_num, 5, (int64_t)num ,
1295 "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1296 human_num[0] = '\0';
1297 #endif
1298 printf("%s: %ju", prompt, num);
1299 #ifdef HN_AUTOSCALE
1300 if (human_num[0] != '\0')
1301 printf(" (%s)", human_num);
1302 #endif
1303 printf("\n");
1304 }
1305
1306 int
1307 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1308 {
1309 void *p;
1310 map_t *t;
1311 const char *msg;
1312
1313 switch (type) {
1314 case MAP_TYPE_PRI_GPT_HDR:
1315 t = &gpt->gpt;
1316 msg = "primary";
1317 break;
1318 case MAP_TYPE_SEC_GPT_HDR:
1319 t = &gpt->tpg;
1320 msg = "secondary";
1321 break;
1322 default:
1323 gpt_warnx(gpt, "Unknown GPT header type %d", type);
1324 return -1;
1325 }
1326
1327 if ((p = calloc(1, gpt->secsz)) == NULL) {
1328 gpt_warn(gpt, "Error allocating %s GPT header", msg);
1329 return -1;
1330 }
1331
1332 *t = map_add(gpt, loc, 1LL, type, p, 1);
1333 if (*t == NULL) {
1334 gpt_warn(gpt, "Error adding %s GPT header", msg);
1335 free(p);
1336 return -1;
1337 }
1338 return 0;
1339 }
1340