gpt.c revision 1.70 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.70 2017/02/16 03:32:17 christos 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 void
125 utf16_to_utf8(const uint16_t *s16, uint8_t *s8, size_t s8len)
126 {
127 size_t s8idx, s16idx, s16len;
128 uint32_t utfchar;
129 unsigned int c;
130
131 s16len = 0;
132 while (s16[s16len++] != 0)
133 continue;
134 s8idx = s16idx = 0;
135 while (s16idx < s16len) {
136 utfchar = le16toh(s16[s16idx++]);
137 if ((utfchar & 0xf800) == 0xd800) {
138 c = le16toh(s16[s16idx]);
139 if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
140 utfchar = 0xfffd;
141 else
142 s16idx++;
143 }
144 if (utfchar < 0x80) {
145 if (s8idx + 1 >= s8len)
146 break;
147 s8[s8idx++] = (uint8_t)utfchar;
148 } else if (utfchar < 0x800) {
149 if (s8idx + 2 >= s8len)
150 break;
151 s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
152 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
153 } else if (utfchar < 0x10000) {
154 if (s8idx + 3 >= s8len)
155 break;
156 s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
157 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
158 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
159 } else if (utfchar < 0x200000) {
160 if (s8idx + 4 >= s8len)
161 break;
162 s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
163 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
164 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
165 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
166 }
167 }
168 s8[s8idx] = 0;
169 }
170
171 void
172 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
173 {
174 size_t s16idx, s8idx, s8len;
175 uint32_t utfchar = 0;
176 unsigned int c, utfbytes;
177
178 s8len = 0;
179 while (s8[s8len++] != 0)
180 ;
181 s8idx = s16idx = 0;
182 utfbytes = 0;
183 do {
184 c = s8[s8idx++];
185 if ((c & 0xc0) != 0x80) {
186 /* Initial characters. */
187 if (utfbytes != 0) {
188 /* Incomplete encoding. */
189 s16[s16idx++] = htole16(0xfffd);
190 if (s16idx == s16len) {
191 s16[--s16idx] = 0;
192 return;
193 }
194 }
195 if ((c & 0xf8) == 0xf0) {
196 utfchar = c & 0x07;
197 utfbytes = 3;
198 } else if ((c & 0xf0) == 0xe0) {
199 utfchar = c & 0x0f;
200 utfbytes = 2;
201 } else if ((c & 0xe0) == 0xc0) {
202 utfchar = c & 0x1f;
203 utfbytes = 1;
204 } else {
205 utfchar = c & 0x7f;
206 utfbytes = 0;
207 }
208 } else {
209 /* Followup characters. */
210 if (utfbytes > 0) {
211 utfchar = (utfchar << 6) + (c & 0x3f);
212 utfbytes--;
213 } else if (utfbytes == 0)
214 utfbytes = (u_int)~0;
215 }
216 if (utfbytes == 0) {
217 if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
218 utfchar = 0xfffd;
219 if (utfchar >= 0x10000) {
220 s16[s16idx++] = htole16((uint16_t)
221 (0xd800 | ((utfchar>>10) - 0x40)));
222 s16[s16idx++] = htole16((uint16_t)
223 (0xdc00 | (utfchar & 0x3ff)));
224 } else
225 s16[s16idx++] = htole16((uint16_t)utfchar);
226 if (s16idx == s16len) {
227 s16[--s16idx] = 0;
228 return;
229 }
230 }
231 } while (c != 0);
232 }
233
234 void *
235 gpt_read(gpt_t gpt, off_t lba, size_t count)
236 {
237 off_t ofs;
238 void *buf;
239
240 count *= gpt->secsz;
241 buf = malloc(count);
242 if (buf == NULL)
243 return NULL;
244
245 ofs = lba * gpt->secsz;
246 if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
247 read(gpt->fd, buf, count) == (ssize_t)count)
248 return buf;
249
250 free(buf);
251 return NULL;
252 }
253
254 int
255 gpt_write(gpt_t gpt, map_t map)
256 {
257 off_t ofs;
258 size_t count;
259
260 count = (size_t)(map->map_size * gpt->secsz);
261 ofs = map->map_start * gpt->secsz;
262 if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
263 write(gpt->fd, map->map_data, count) != (ssize_t)count)
264 return -1;
265 gpt->flags |= GPT_MODIFIED;
266 return 0;
267 }
268
269 static int
270 gpt_mbr(gpt_t gpt, off_t lba)
271 {
272 struct mbr *mbr;
273 map_t m, p;
274 off_t size, start;
275 unsigned int i, pmbr;
276
277 mbr = gpt_read(gpt, lba, 1);
278 if (mbr == NULL) {
279 gpt_warn(gpt, "Read failed");
280 return -1;
281 }
282
283 if (mbr->mbr_sig != htole16(MBR_SIG)) {
284 if (gpt->verbose)
285 gpt_msg(gpt,
286 "MBR not found at sector %ju", (uintmax_t)lba);
287 free(mbr);
288 return 0;
289 }
290
291 /*
292 * Differentiate between a regular MBR and a PMBR. This is more
293 * convenient in general. A PMBR is one with a single partition
294 * of type 0xee.
295 */
296 pmbr = 0;
297 for (i = 0; i < 4; i++) {
298 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
299 continue;
300 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
301 pmbr++;
302 else
303 break;
304 }
305 if (pmbr && i == 4 && lba == 0) {
306 if (pmbr != 1)
307 gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
308 (uintmax_t)lba);
309 else if (gpt->verbose > 1)
310 gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
311 p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
312 goto out;
313 }
314 if (pmbr)
315 gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
316 else if (gpt->verbose > 1)
317 gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
318
319 p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
320 if (p == NULL)
321 goto out;
322
323 for (i = 0; i < 4; i++) {
324 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
325 mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
326 continue;
327 start = le16toh(mbr->mbr_part[i].part_start_hi);
328 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
329 size = le16toh(mbr->mbr_part[i].part_size_hi);
330 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
331 if (start == 0 && size == 0) {
332 gpt_warnx(gpt, "Malformed MBR at sector %ju",
333 (uintmax_t)lba);
334 continue;
335 }
336 /* start is relative to the offset of the MBR itself. */
337 start += lba;
338 if (gpt->verbose > 2)
339 gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
340 "size=%ju", mbr->mbr_part[i].part_flag,
341 mbr->mbr_part[i].part_typ,
342 (uintmax_t)start, (uintmax_t)size);
343 if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
344 m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
345 if (m == NULL)
346 return -1;
347 m->map_index = i + 1;
348 } else {
349 if (gpt_mbr(gpt, start) == -1)
350 return -1;
351 }
352 }
353 return 0;
354 out:
355 if (p == NULL) {
356 free(mbr);
357 return -1;
358 }
359 return 0;
360 }
361
362 int
363 gpt_gpt(gpt_t gpt, off_t lba, int found)
364 {
365 off_t size;
366 struct gpt_ent *ent;
367 struct gpt_hdr *hdr;
368 char *p;
369 map_t m;
370 size_t blocks, tblsz;
371 unsigned int i;
372 uint32_t crc;
373
374 hdr = gpt_read(gpt, lba, 1);
375 if (hdr == NULL)
376 return -1;
377
378 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
379 goto fail_hdr;
380
381 crc = le32toh(hdr->hdr_crc_self);
382 hdr->hdr_crc_self = 0;
383 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
384 if (gpt->verbose)
385 gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
386 (uintmax_t)lba);
387 goto fail_hdr;
388 }
389
390 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
391 blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
392
393 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
394 p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
395 if (p == NULL) {
396 if (found) {
397 if (gpt->verbose)
398 gpt_msg(gpt,
399 "Cannot read LBA table at sector %ju",
400 (uintmax_t)le64toh(hdr->hdr_lba_table));
401 return -1;
402 }
403 goto fail_hdr;
404 }
405
406 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
407 if (gpt->verbose)
408 gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
409 (uintmax_t)le64toh(hdr->hdr_lba_table));
410 goto fail_ent;
411 }
412
413 if (gpt->verbose > 1)
414 gpt_msg(gpt, "%s GPT at sector %ju",
415 (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
416
417 m = map_add(gpt, lba, 1, (lba == 1)
418 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
419 if (m == NULL)
420 return (-1);
421
422 m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
423 (off_t)blocks,
424 lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
425 if (m == NULL)
426 return (-1);
427
428 if (lba != 1)
429 return (1);
430
431 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
432 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
433 if (gpt_uuid_is_nil(ent->ent_type))
434 continue;
435
436 size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
437 le64toh((uint64_t)ent->ent_lba_start) + 1LL);
438 if (gpt->verbose > 2) {
439 char buf[128];
440 gpt_uuid_snprintf(buf, sizeof(buf), "%s",
441 ent->ent_type);
442 gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
443 "size=%ju", buf,
444 (uintmax_t)le64toh(ent->ent_lba_start),
445 (uintmax_t)size);
446 }
447 m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
448 size, MAP_TYPE_GPT_PART, ent, 0);
449 if (m == NULL)
450 return (-1);
451 m->map_index = i + 1;
452 }
453 return (1);
454
455 fail_ent:
456 free(p);
457
458 fail_hdr:
459 free(hdr);
460 return (0);
461 }
462
463 gpt_t
464 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
465 time_t timestamp)
466 {
467 int mode, found;
468 off_t devsz;
469 gpt_t gpt;
470
471
472 if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
473 if (!(flags & GPT_QUIET))
474 warn("Cannot allocate `%s'", dev);
475 return NULL;
476 }
477 gpt->flags = flags;
478 gpt->verbose = verbose;
479 gpt->mediasz = mediasz;
480 gpt->secsz = secsz;
481 gpt->timestamp = timestamp;
482
483 mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
484
485 gpt->fd = opendisk(dev, mode, gpt->device_name,
486 sizeof(gpt->device_name), 0);
487 if (gpt->fd == -1) {
488 strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
489 gpt_warn(gpt, "Cannot open");
490 goto close;
491 }
492
493 if (fstat(gpt->fd, &gpt->sb) == -1) {
494 gpt_warn(gpt, "Cannot stat");
495 goto close;
496 }
497
498 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
499 if (gpt->secsz == 0) {
500 #ifdef DIOCGSECTORSIZE
501 if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
502 gpt_warn(gpt, "Cannot get sector size");
503 goto close;
504 }
505 #endif
506 if (gpt->secsz == 0) {
507 gpt_warnx(gpt, "Sector size can't be 0");
508 goto close;
509 }
510 }
511 if (gpt->mediasz == 0) {
512 #ifdef DIOCGMEDIASIZE
513 if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
514 gpt_warn(gpt, "Cannot get media size");
515 goto close;
516 }
517 #endif
518 if (gpt->mediasz == 0) {
519 gpt_warnx(gpt, "Media size can't be 0");
520 goto close;
521 }
522 }
523 } else {
524 gpt->flags |= GPT_FILE;
525 if (gpt->secsz == 0)
526 gpt->secsz = 512; /* Fixed size for files. */
527 if (gpt->mediasz == 0) {
528 if (gpt->sb.st_size % gpt->secsz) {
529 errno = EINVAL;
530 goto close;
531 }
532 gpt->mediasz = gpt->sb.st_size;
533 }
534 gpt->flags |= GPT_NOSYNC;
535 }
536
537 /*
538 * We require an absolute minimum of 6 sectors. One for the MBR,
539 * 2 for the GPT header, 2 for the GPT table and one to hold some
540 * user data. Let's catch this extreme border case here so that
541 * we don't have to worry about it later.
542 */
543 devsz = gpt->mediasz / gpt->secsz;
544 if (devsz < 6) {
545 gpt_warnx(gpt, "Need 6 sectors, we have %ju",
546 (uintmax_t)devsz);
547 goto close;
548 }
549
550 if (gpt->verbose) {
551 gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
552 (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
553 }
554
555 if (map_init(gpt, devsz) == -1)
556 goto close;
557
558 if (gpt_mbr(gpt, 0LL) == -1)
559 goto close;
560 if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
561 goto close;
562 if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
563 goto close;
564
565 return gpt;
566
567 close:
568 if (gpt->fd != -1)
569 close(gpt->fd);
570 free(gpt);
571 return NULL;
572 }
573
574 void
575 gpt_close(gpt_t gpt)
576 {
577
578 if (!(gpt->flags & GPT_MODIFIED))
579 goto out;
580
581 if (!(gpt->flags & GPT_NOSYNC)) {
582 #ifdef DIOCMWEDGES
583 int bits;
584 if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
585 gpt_warn(gpt, "Can't update wedge information");
586 else
587 goto out;
588 #endif
589 }
590 if (!(gpt->flags & GPT_FILE))
591 gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
592 " for the changes to take effect\n", gpt->device_name);
593
594 out:
595 close(gpt->fd);
596 }
597
598 __printflike(2, 0)
599 static void
600 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
601 {
602 if (gpt && (gpt->flags & GPT_QUIET))
603 return;
604 fprintf(stderr, "%s: ", getprogname());
605 if (gpt)
606 fprintf(stderr, "%s: ", gpt->device_name);
607 vfprintf(stderr, fmt, ap);
608 if (e)
609 fprintf(stderr, " (%s)\n", e);
610 else
611 fputc('\n', stderr);
612 }
613
614 void
615 gpt_warnx(gpt_t gpt, const char *fmt, ...)
616 {
617 va_list ap;
618
619 va_start(ap, fmt);
620 gpt_vwarnx(gpt, fmt, ap, NULL);
621 va_end(ap);
622 }
623
624 void
625 gpt_warn(gpt_t gpt, const char *fmt, ...)
626 {
627 va_list ap;
628
629 va_start(ap, fmt);
630 gpt_vwarnx(gpt, fmt, ap, strerror(errno));
631 va_end(ap);
632 }
633
634 void
635 gpt_msg(gpt_t gpt, const char *fmt, ...)
636 {
637 va_list ap;
638
639 if (gpt && (gpt->flags & GPT_QUIET))
640 return;
641 if (gpt)
642 printf("%s: ", gpt->device_name);
643 va_start(ap, fmt);
644 vprintf(fmt, ap);
645 va_end(ap);
646 printf("\n");
647 }
648
649 struct gpt_hdr *
650 gpt_hdr(gpt_t gpt)
651 {
652 gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
653 if (gpt->gpt == NULL) {
654 gpt_warnx(gpt, "No primary GPT header; run create or recover");
655 return NULL;
656 }
657
658 gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
659 if (gpt->tpg == NULL) {
660 gpt_warnx(gpt, "No secondary GPT header; run recover");
661 return NULL;
662 }
663
664 gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
665 gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
666 if (gpt->tbl == NULL || gpt->lbt == NULL) {
667 gpt_warnx(gpt, "Corrupt maps, run recover");
668 return NULL;
669 }
670
671 return gpt->gpt->map_data;
672 }
673
674 int
675 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
676 {
677 struct gpt_hdr *hdr = map->map_data;
678
679 hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
680 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
681 hdr->hdr_crc_self = 0;
682 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
683
684 if (gpt_write(gpt, map) == -1) {
685 gpt_warn(gpt, "Error writing crc map");
686 return -1;
687 }
688
689 if (gpt_write(gpt, tbl) == -1) {
690 gpt_warn(gpt, "Error writing crc table");
691 return -1;
692 }
693
694 return 0;
695 }
696
697 int
698 gpt_write_primary(gpt_t gpt)
699 {
700 return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
701 }
702
703
704 int
705 gpt_write_backup(gpt_t gpt)
706 {
707 return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
708 }
709
710 void
711 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
712 {
713 part->part_flag = active ? 0x80 : 0;
714 part->part_shd = 0x00;
715 part->part_ssect = 0x02;
716 part->part_scyl = 0x00;
717 part->part_typ = MBR_PTYPE_PMBR;
718 part->part_ehd = 0xfe;
719 part->part_esect = 0xff;
720 part->part_ecyl = 0xff;
721 part->part_start_lo = htole16(1);
722 if (last > 0xffffffff) {
723 part->part_size_lo = htole16(0xffff);
724 part->part_size_hi = htole16(0xffff);
725 } else {
726 part->part_size_lo = htole16((uint16_t)last);
727 part->part_size_hi = htole16((uint16_t)(last >> 16));
728 }
729 }
730
731 struct gpt_ent *
732 gpt_ent(map_t map, map_t tbl, unsigned int i)
733 {
734 struct gpt_hdr *hdr = map->map_data;
735 return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
736 }
737
738 struct gpt_ent *
739 gpt_ent_primary(gpt_t gpt, unsigned int i)
740 {
741 return gpt_ent(gpt->gpt, gpt->tbl, i);
742 }
743
744 struct gpt_ent *
745 gpt_ent_backup(gpt_t gpt, unsigned int i)
746 {
747 return gpt_ent(gpt->tpg, gpt->lbt, i);
748 }
749
750 int
751 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
752 {
753 const char **a = cmd->help;
754 size_t hlen = cmd->hlen;
755 size_t i;
756
757 if (prefix == NULL) {
758 const char *pname = getprogname();
759 const char *d1, *d2, *d = " <device>";
760 int len = (int)strlen(pname);
761 if (strcmp(pname, "gpt") == 0) {
762 d1 = "";
763 d2 = d;
764 } else {
765 d2 = "";
766 d1 = d;
767 }
768 fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
769 d1, cmd->name, a[0], d2);
770 for (i = 1; i < hlen; i++) {
771 fprintf(stderr,
772 " %*s%s %s %s%s\n", len, "",
773 d1, cmd->name, a[i], d2);
774 }
775 } else {
776 for (i = 0; i < hlen; i++)
777 fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
778 }
779 return -1;
780 }
781
782 off_t
783 gpt_last(gpt_t gpt)
784 {
785 return gpt->mediasz / gpt->secsz - 1LL;
786 }
787
788 off_t
789 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
790 {
791 off_t blocks;
792 map_t map;
793 struct gpt_hdr *hdr;
794 struct gpt_ent *ent;
795 unsigned int i;
796 void *p;
797
798 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
799 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
800 gpt_warnx(gpt, "Device already contains a GPT, "
801 "destroy it first");
802 return -1;
803 }
804
805 /* Get the amount of free space after the MBR */
806 blocks = map_free(gpt, 1LL, 0LL);
807 if (blocks == 0LL) {
808 gpt_warnx(gpt, "No room for the GPT header");
809 return -1;
810 }
811
812 /* Don't create more than parts entries. */
813 if ((uint64_t)(blocks - 1) * gpt->secsz >
814 parts * sizeof(struct gpt_ent)) {
815 blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
816 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
817 blocks++;
818 blocks++; /* Don't forget the header itself */
819 }
820
821 /* Never cross the median of the device. */
822 if ((blocks + 1LL) > ((last + 1LL) >> 1))
823 blocks = ((last + 1LL) >> 1) - 1LL;
824
825 /*
826 * Get the amount of free space at the end of the device and
827 * calculate the size for the GPT structures.
828 */
829 map = map_last(gpt);
830 if (map->map_type != MAP_TYPE_UNUSED) {
831 gpt_warnx(gpt, "No room for the backup header");
832 return -1;
833 }
834
835 if (map->map_size < blocks)
836 blocks = map->map_size;
837 if (blocks == 1LL) {
838 gpt_warnx(gpt, "No room for the GPT table");
839 return -1;
840 }
841
842 blocks--; /* Number of blocks in the GPT table. */
843
844 if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
845 return -1;
846
847 if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
848 gpt_warnx(gpt, "Can't allocate the primary GPT table");
849 return -1;
850 }
851 if ((gpt->tbl = map_add(gpt, 2LL, blocks,
852 MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
853 free(p);
854 gpt_warnx(gpt, "Can't add the primary GPT table");
855 return -1;
856 }
857
858 hdr = gpt->gpt->map_data;
859 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
860
861 /*
862 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
863 * contains padding we must not include in the size.
864 */
865 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
866 hdr->hdr_size = htole32(GPT_HDR_SIZE);
867 hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
868 hdr->hdr_lba_alt = htole64((uint64_t)last);
869 hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
870 hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
871 if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
872 return -1;
873 hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
874 hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
875 sizeof(struct gpt_ent)));
876 if (le32toh(hdr->hdr_entries) > parts)
877 hdr->hdr_entries = htole32(parts);
878 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
879
880 ent = gpt->tbl->map_data;
881 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
882 if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
883 return -1;
884 }
885
886 /*
887 * Create backup GPT if the user didn't suppress it.
888 */
889 if (primary_only)
890 return last;
891
892 if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
893 return -1;
894
895 if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
896 MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
897 gpt_warnx(gpt, "Can't add the secondary GPT table");
898 return -1;
899 }
900
901 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
902
903 hdr = gpt->tpg->map_data;
904 hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
905 hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
906 hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
907 return last;
908 }
909
910 static int
911 gpt_size_get(gpt_t gpt, off_t *size)
912 {
913 off_t sectors;
914 int64_t human_num;
915 char *p;
916
917 if (*size > 0)
918 return -1;
919 sectors = strtoll(optarg, &p, 10);
920 if (sectors < 1)
921 return -1;
922 if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
923 *size = sectors * gpt->secsz;
924 return 0;
925 }
926 if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
927 *size = sectors;
928 return 0;
929 }
930 if (dehumanize_number(optarg, &human_num) < 0)
931 return -1;
932 *size = human_num;
933 return 0;
934 }
935
936 int
937 gpt_human_get(gpt_t gpt, off_t *human)
938 {
939 int64_t human_num;
940
941 if (*human > 0) {
942 gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
943 optarg);
944 return -1;
945 }
946 if (dehumanize_number(optarg, &human_num) < 0) {
947 gpt_warn(gpt, "Bad number `%s'", optarg);
948 return -1;
949 }
950 *human = human_num;
951 if (*human < 1) {
952 gpt_warn(gpt, "Number `%s' < 1", optarg);
953 return -1;
954 }
955 return 0;
956 }
957
958 int
959 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
960 {
961 switch (ch) {
962 case 'a':
963 if (find->all > 0) {
964 gpt_warn(gpt, "-a is already set");
965 return -1;
966 }
967 find->all = 1;
968 break;
969 case 'b':
970 if (gpt_human_get(gpt, &find->block) == -1)
971 return -1;
972 break;
973 case 'i':
974 if (gpt_uint_get(gpt, &find->entry) == -1)
975 return -1;
976 break;
977 case 'L':
978 if (gpt_name_get(gpt, &find->label) == -1)
979 return -1;
980 break;
981 case 's':
982 if (gpt_size_get(gpt, &find->size) == -1)
983 return -1;
984 break;
985 case 't':
986 if (!gpt_uuid_is_nil(find->type))
987 return -1;
988 if (gpt_uuid_parse(optarg, find->type) != 0)
989 return -1;
990 break;
991 default:
992 gpt_warn(gpt, "Unknown find option `%c'", ch);
993 return -1;
994 }
995 return 0;
996 }
997
998 int
999 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1000 void (*cfn)(struct gpt_ent *, void *), void *v)
1001 {
1002 map_t m;
1003 struct gpt_hdr *hdr;
1004 struct gpt_ent *ent;
1005 unsigned int i;
1006 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1007
1008 if (!find->all ^
1009 (find->block > 0 || find->entry > 0 || find->label != NULL
1010 || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1011 return -1;
1012
1013 if ((hdr = gpt_hdr(gpt)) == NULL)
1014 return -1;
1015
1016 /* Relabel all matching entries in the map. */
1017 for (m = map_first(gpt); m != NULL; m = m->map_next) {
1018 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1019 continue;
1020 if (find->entry > 0 && find->entry != m->map_index)
1021 continue;
1022 if (find->block > 0 && find->block != m->map_start)
1023 continue;
1024 if (find->size > 0 && find->size != m->map_size)
1025 continue;
1026
1027 i = m->map_index - 1;
1028
1029 ent = gpt_ent_primary(gpt, i);
1030 if (find->label != NULL) {
1031 utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf));
1032 if (strcmp((char *)find->label, (char *)utfbuf) == 0)
1033 continue;
1034 }
1035
1036 if (!gpt_uuid_is_nil(find->type) &&
1037 !gpt_uuid_equal(find->type, ent->ent_type))
1038 continue;
1039
1040 /* Change the primary entry. */
1041 (*cfn)(ent, v);
1042
1043 if (gpt_write_primary(gpt) == -1)
1044 return -1;
1045
1046 ent = gpt_ent_backup(gpt, i);
1047 /* Change the secondary entry. */
1048 (*cfn)(ent, v);
1049
1050 if (gpt_write_backup(gpt) == -1)
1051 return -1;
1052
1053 gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1054 }
1055 return 0;
1056 }
1057
1058 int
1059 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1060 {
1061 switch (ch) {
1062 case 'a':
1063 if (gpt_human_get(gpt, alignment) == -1)
1064 return -1;
1065 return 0;
1066 case 'i':
1067 if (gpt_uint_get(gpt, entry) == -1)
1068 return -1;
1069 return 0;
1070 case 's':
1071 if (gpt_size_get(gpt, size) == -1)
1072 return -1;
1073 return 0;
1074 default:
1075 gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1076 return -1;
1077 }
1078 }
1079
1080 off_t
1081 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1082 {
1083 if (entry == 0) {
1084 gpt_warnx(gpt, "Entry not specified");
1085 return -1;
1086 }
1087 if (alignment % gpt->secsz != 0) {
1088 gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1089 "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1090 return -1;
1091 }
1092
1093 if (size % gpt->secsz != 0) {
1094 gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1095 "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1096 return -1;
1097 }
1098 if (size > 0)
1099 return size / gpt->secsz;
1100 return 0;
1101 }
1102
1103 static const struct nvd {
1104 const char *name;
1105 uint64_t mask;
1106 const char *description;
1107 } gpt_attr[] = {
1108 {
1109 "biosboot",
1110 GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1111 "Legacy BIOS boot partition",
1112 },
1113 {
1114 "bootme",
1115 GPT_ENT_ATTR_BOOTME,
1116 "Bootable partition",
1117 },
1118 {
1119 "bootfailed",
1120 GPT_ENT_ATTR_BOOTFAILED,
1121 "Partition that marked bootonce failed to boot",
1122 },
1123 {
1124 "bootonce",
1125 GPT_ENT_ATTR_BOOTONCE,
1126 "Attempt to boot this partition only once",
1127 },
1128 {
1129 "noblockio",
1130 GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1131 "UEFI won't recognize file system for block I/O",
1132 },
1133 {
1134 "required",
1135 GPT_ENT_ATTR_REQUIRED_PARTITION,
1136 "Partition required for platform to function",
1137 },
1138 };
1139
1140 int
1141 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1142 {
1143 size_t i;
1144 int rv = 0;
1145 char *ptr;
1146
1147 *attributes = 0;
1148
1149 for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1150 for (i = 0; i < __arraycount(gpt_attr); i++)
1151 if (strcmp(gpt_attr[i].name, ptr) == 0)
1152 break;
1153 if (i == __arraycount(gpt_attr)) {
1154 gpt_warnx(gpt, "Unregognized attribute `%s'", ptr);
1155 rv = -1;
1156 } else
1157 *attributes |= gpt_attr[i].mask;
1158 }
1159 return rv;
1160 }
1161
1162 void
1163 gpt_attr_help(const char *prefix)
1164 {
1165 size_t i;
1166
1167 for (i = 0; i < __arraycount(gpt_attr); i++)
1168 printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1169 gpt_attr[i].description);
1170 }
1171
1172 const char *
1173 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1174 {
1175 size_t i;
1176 strlcpy(buf, "", len);
1177
1178 for (i = 0; i < __arraycount(gpt_attr); i++)
1179 if (attributes & gpt_attr[i].mask) {
1180 strlcat(buf, buf[0] ? ", " : "", len);
1181 strlcat(buf, gpt_attr[i].name, len);
1182 }
1183 return buf;
1184 }
1185
1186 int
1187 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1188 {
1189 struct gpt_hdr *hdr;
1190 struct gpt_ent *ent;
1191 unsigned int i;
1192
1193 if (entry == 0 || (set == 0 && clr == 0)) {
1194 gpt_warnx(gpt, "Nothing to set");
1195 return -1;
1196 }
1197
1198 if ((hdr = gpt_hdr(gpt)) == NULL)
1199 return -1;
1200
1201 if (entry > le32toh(hdr->hdr_entries)) {
1202 gpt_warnx(gpt, "Index %u out of range (%u max)",
1203 entry, le32toh(hdr->hdr_entries));
1204 return -1;
1205 }
1206
1207 i = entry - 1;
1208 ent = gpt_ent_primary(gpt, i);
1209 if (gpt_uuid_is_nil(ent->ent_type)) {
1210 gpt_warnx(gpt, "Entry at index %u is unused", entry);
1211 return -1;
1212 }
1213
1214 ent->ent_attr &= ~clr;
1215 ent->ent_attr |= set;
1216
1217 if (gpt_write_primary(gpt) == -1)
1218 return -1;
1219
1220 ent = gpt_ent_backup(gpt, i);
1221 ent->ent_attr &= ~clr;
1222 ent->ent_attr |= set;
1223
1224 if (gpt_write_backup(gpt) == -1)
1225 return -1;
1226 gpt_msg(gpt, "Partition %d attributes updated", entry);
1227 return 0;
1228 }
1229
1230 int
1231 gpt_uint_get(gpt_t gpt, u_int *entry)
1232 {
1233 char *p;
1234 if (*entry > 0)
1235 return -1;
1236 *entry = (u_int)strtoul(optarg, &p, 10);
1237 if (*p != 0 || *entry < 1) {
1238 gpt_warn(gpt, "Bad number `%s'", optarg);
1239 return -1;
1240 }
1241 return 0;
1242 }
1243 int
1244 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1245 {
1246 if (!gpt_uuid_is_nil(*uuid))
1247 return -1;
1248 if (gpt_uuid_parse(optarg, *uuid) != 0) {
1249 gpt_warn(gpt, "Can't parse uuid");
1250 return -1;
1251 }
1252 return 0;
1253 }
1254
1255 int
1256 gpt_name_get(gpt_t gpt, void *v)
1257 {
1258 char **name = v;
1259 if (*name != NULL)
1260 return -1;
1261 *name = strdup(optarg);
1262 if (*name == NULL) {
1263 gpt_warn(gpt, "Can't copy string");
1264 return -1;
1265 }
1266 return 0;
1267 }
1268
1269 void
1270 gpt_show_num(const char *prompt, uintmax_t num)
1271 {
1272 #ifdef HN_AUTOSCALE
1273 char human_num[5];
1274 if (humanize_number(human_num, 5, (int64_t)num ,
1275 "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1276 human_num[0] = '\0';
1277 #endif
1278 printf("%s: %ju", prompt, num);
1279 #ifdef HN_AUTOSCALE
1280 if (human_num[0] != '\0')
1281 printf(" (%s)", human_num);
1282 #endif
1283 printf("\n");
1284 }
1285
1286 int
1287 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1288 {
1289 void *p;
1290 map_t *t;
1291 const char *msg;
1292
1293 switch (type) {
1294 case MAP_TYPE_PRI_GPT_HDR:
1295 t = &gpt->gpt;
1296 msg = "primary";
1297 break;
1298 case MAP_TYPE_SEC_GPT_HDR:
1299 t = &gpt->tpg;
1300 msg = "secondary";
1301 break;
1302 default:
1303 gpt_warnx(gpt, "Unknown GPT header type %d", type);
1304 return -1;
1305 }
1306
1307 if ((p = calloc(1, gpt->secsz)) == NULL) {
1308 gpt_warn(gpt, "Error allocating %s GPT header", msg);
1309 return -1;
1310 }
1311
1312 *t = map_add(gpt, loc, 1LL, type, p, 1);
1313 if (*t == NULL) {
1314 gpt_warn(gpt, "Error adding %s GPT header", msg);
1315 free(p);
1316 return -1;
1317 }
1318 return 0;
1319 }
1320