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