gpt.c revision 1.87 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.87 2023/12/13 06:51:57 mrg Exp $");
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <sys/bootblock.h>
46
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <paths.h>
51 #include <stddef.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <ctype.h>
58
59 #include "map.h"
60 #include "gpt.h"
61 #include "gpt_private.h"
62
63 static uint32_t crc32_tab[] = {
64 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
65 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
66 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
67 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
68 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
69 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
70 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
71 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
72 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
73 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
74 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
75 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
76 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
77 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
78 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
79 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
80 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
81 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
82 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
83 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
84 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
85 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
86 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
87 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
88 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
89 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
90 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
91 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
92 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
93 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
94 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
95 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
96 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
97 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
98 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
99 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
100 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
101 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
102 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
103 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
104 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
105 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
106 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
107 };
108
109 uint32_t
110 crc32(const void *buf, size_t size)
111 {
112 const uint8_t *p;
113 uint32_t crc;
114
115 p = buf;
116 crc = ~0U;
117
118 while (size--)
119 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
120
121 return crc ^ ~0U;
122 }
123
124 /*
125 * Produce a NUL-terminated utf-8 string from the non-NUL-terminated
126 * utf16 string.
127 */
128 void
129 utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len)
130 {
131 size_t s8idx, s16idx;
132 uint32_t utfchar;
133 unsigned int c;
134
135 for (s16idx = 0; s16idx < s16len; s16idx++)
136 if (s16[s16idx] == 0)
137 break;
138
139 s16len = s16idx;
140 s8idx = s16idx = 0;
141 while (s16idx < s16len) {
142 utfchar = le16toh(s16[s16idx++]);
143 if ((utfchar & 0xf800) == 0xd800) {
144 c = le16toh(s16[s16idx]);
145 if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
146 utfchar = 0xfffd;
147 else
148 s16idx++;
149 }
150 if (utfchar < 0x80) {
151 if (s8idx + 1 >= s8len)
152 break;
153 s8[s8idx++] = (uint8_t)utfchar;
154 } else if (utfchar < 0x800) {
155 if (s8idx + 2 >= s8len)
156 break;
157 s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
158 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
159 } else if (utfchar < 0x10000) {
160 if (s8idx + 3 >= s8len)
161 break;
162 s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
163 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
164 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
165 } else if (utfchar < 0x200000) {
166 if (s8idx + 4 >= s8len)
167 break;
168 s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
169 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
170 s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
171 s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
172 }
173 }
174 s8[s8idx] = 0;
175 }
176
177 /*
178 * Produce a non-NUL-terminated utf-16 string from the NUL-terminated
179 * utf8 string.
180 */
181 void
182 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
183 {
184 size_t s16idx, s8idx, s8len;
185 uint32_t utfchar = 0;
186 unsigned int c, utfbytes;
187
188 s8len = 0;
189 while (s8[s8len++] != 0)
190 ;
191 s8idx = s16idx = 0;
192 utfbytes = 0;
193 do {
194 c = s8[s8idx++];
195 if ((c & 0xc0) != 0x80) {
196 /* Initial characters. */
197 if (utfbytes != 0) {
198 /* Incomplete encoding. */
199 s16[s16idx++] = htole16(0xfffd);
200 if (s16idx == s16len) {
201 s16[--s16idx] = 0;
202 return;
203 }
204 }
205 if ((c & 0xf8) == 0xf0) {
206 utfchar = c & 0x07;
207 utfbytes = 3;
208 } else if ((c & 0xf0) == 0xe0) {
209 utfchar = c & 0x0f;
210 utfbytes = 2;
211 } else if ((c & 0xe0) == 0xc0) {
212 utfchar = c & 0x1f;
213 utfbytes = 1;
214 } else {
215 utfchar = c & 0x7f;
216 utfbytes = 0;
217 }
218 } else {
219 /* Followup characters. */
220 if (utfbytes > 0) {
221 utfchar = (utfchar << 6) + (c & 0x3f);
222 utfbytes--;
223 } else if (utfbytes == 0)
224 utfbytes = (u_int)~0;
225 }
226 if (utfbytes == 0) {
227 if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
228 utfchar = 0xfffd;
229 if (utfchar >= 0x10000) {
230 s16[s16idx++] = htole16((uint16_t)
231 (0xd800 | ((utfchar>>10) - 0x40)));
232 s16[s16idx++] = htole16((uint16_t)
233 (0xdc00 | (utfchar & 0x3ff)));
234 } else
235 s16[s16idx++] = htole16((uint16_t)utfchar);
236 if (s16idx == s16len) {
237 return;
238 }
239 }
240 } while (c != 0);
241
242 while (s16idx < s16len)
243 s16[s16idx++] = 0;
244 }
245
246 void *
247 gpt_read(gpt_t gpt, off_t lba, size_t count)
248 {
249 off_t ofs;
250 void *buf;
251
252 count *= gpt->secsz;
253 buf = malloc(count);
254 if (buf == NULL)
255 return NULL;
256
257 ofs = lba * gpt->secsz;
258 if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
259 read(gpt->fd, buf, count) == (ssize_t)count)
260 return buf;
261
262 free(buf);
263 return NULL;
264 }
265
266 int
267 gpt_write(gpt_t gpt, map_t map)
268 {
269 off_t ofs;
270 size_t count;
271
272 count = (size_t)(map->map_size * gpt->secsz);
273 ofs = map->map_start * gpt->secsz;
274 if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
275 write(gpt->fd, map->map_data, count) != (ssize_t)count)
276 return -1;
277 gpt->flags |= GPT_MODIFIED;
278 return 0;
279 }
280
281 static int
282 gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset)
283 {
284 struct mbr *mbr;
285 map_t m, p;
286 off_t size, start;
287 unsigned int i, pmbr;
288
289 mbr = gpt_read(gpt, lba, 1);
290 if (mbr == NULL) {
291 gpt_warn(gpt, "Read failed");
292 return -1;
293 }
294
295 if (mbr->mbr_sig != htole16(MBR_SIG)) {
296 if (gpt->verbose)
297 gpt_msg(gpt,
298 "MBR not found at sector %ju", (uintmax_t)lba);
299 free(mbr);
300 return 0;
301 }
302
303 /*
304 * Differentiate between a regular MBR and a PMBR. This is more
305 * convenient in general. A PMBR is one with a single partition
306 * of type 0xee.
307 */
308 pmbr = 0;
309 for (i = 0; i < 4; i++) {
310 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
311 continue;
312 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
313 pmbr++;
314 else if ((gpt->flags & GPT_HYBRID) == 0)
315 break;
316 }
317 if (pmbr && i == 4 && lba == 0) {
318 if (pmbr != 1)
319 gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
320 (uintmax_t)lba);
321 else if (gpt->verbose > 1)
322 gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
323 p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
324 goto out;
325 }
326 if (pmbr)
327 gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
328 else if (gpt->verbose > 1)
329 gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
330
331 p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
332 if (p == NULL)
333 goto out;
334
335 for (i = 0; i < 4; i++) {
336 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
337 mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
338 continue;
339 start = le16toh(mbr->mbr_part[i].part_start_hi);
340 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
341 size = le16toh(mbr->mbr_part[i].part_size_hi);
342 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
343 if (start == 0 && size == 0) {
344 gpt_warnx(gpt, "Malformed MBR at sector %ju",
345 (uintmax_t)lba);
346 continue;
347 }
348 if (gpt->verbose > 2)
349 gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
350 "size=%ju", mbr->mbr_part[i].part_flag,
351 mbr->mbr_part[i].part_typ,
352 (uintmax_t)start, (uintmax_t)size);
353 if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) {
354 start += lba;
355 m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
356 if (m == NULL)
357 return -1;
358 m->map_index = *next_index;
359 (*next_index)++;
360 } else {
361 start += ext_offset;
362 if (gpt_mbr(gpt, start, next_index,
363 ext_offset ? ext_offset : start) == -1)
364 return -1;
365 }
366 }
367 return 0;
368 out:
369 if (p == NULL) {
370 free(mbr);
371 return -1;
372 }
373 return 0;
374 }
375
376 int
377 gpt_gpt(gpt_t gpt, off_t lba, int found)
378 {
379 off_t size;
380 struct gpt_ent *ent;
381 struct gpt_hdr *hdr;
382 char *p;
383 map_t m;
384 size_t blocks, tblsz;
385 unsigned int i;
386 uint32_t crc;
387
388 hdr = gpt_read(gpt, lba, 1);
389 if (hdr == NULL) {
390 gpt_warn(gpt, "Read failed");
391 return -1;
392 }
393
394 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
395 goto fail_hdr;
396
397 crc = le32toh(hdr->hdr_crc_self);
398 hdr->hdr_crc_self = 0;
399 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
400 if (gpt->verbose)
401 gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
402 (uintmax_t)lba);
403 goto fail_hdr;
404 }
405
406 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
407 blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
408
409 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
410 p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
411 if (p == NULL) {
412 if (found) {
413 if (gpt->verbose)
414 gpt_msg(gpt,
415 "Cannot read LBA table at sector %ju",
416 (uintmax_t)le64toh(hdr->hdr_lba_table));
417 return -1;
418 }
419 goto fail_hdr;
420 }
421
422 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
423 if (gpt->verbose)
424 gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
425 (uintmax_t)le64toh(hdr->hdr_lba_table));
426 goto fail_ent;
427 }
428
429 if (gpt->verbose > 1)
430 gpt_msg(gpt, "%s GPT at sector %ju",
431 (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
432
433 m = map_add(gpt, lba, 1, (lba == 1)
434 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
435 if (m == NULL)
436 return (-1);
437
438 m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
439 (off_t)blocks,
440 lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
441 if (m == NULL)
442 return (-1);
443
444 if (lba != 1)
445 return (1);
446
447 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
448 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
449 if (gpt_uuid_is_nil(ent->ent_type))
450 continue;
451
452 size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
453 le64toh((uint64_t)ent->ent_lba_start) + 1LL);
454 if (gpt->verbose > 2) {
455 char buf[128];
456 gpt_uuid_snprintf(buf, sizeof(buf), "%s",
457 ent->ent_type);
458 gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
459 "size=%ju", buf,
460 (uintmax_t)le64toh(ent->ent_lba_start),
461 (uintmax_t)size);
462 }
463 m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
464 size, MAP_TYPE_GPT_PART, ent, 0);
465 if (m == NULL)
466 return (-1);
467 m->map_index = i + 1;
468 }
469 return (1);
470
471 fail_ent:
472 free(p);
473
474 fail_hdr:
475 free(hdr);
476 return (0);
477 }
478
479 gpt_t
480 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
481 time_t timestamp)
482 {
483 int mode, found;
484 off_t devsz;
485 gpt_t gpt;
486 unsigned int index;
487
488 if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
489 if (!(flags & GPT_QUIET))
490 warn("Cannot allocate `%s'", dev);
491 return NULL;
492 }
493 gpt->flags = flags;
494 gpt->verbose = verbose;
495 gpt->mediasz = mediasz;
496 gpt->secsz = secsz;
497 gpt->timestamp = timestamp;
498
499 mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
500
501 gpt->fd = opendisk(dev, mode, gpt->device_name,
502 sizeof(gpt->device_name), 0);
503 if (gpt->fd == -1) {
504 strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
505 gpt_warn(gpt, "Cannot open");
506 goto close;
507 }
508
509 if (fstat(gpt->fd, &gpt->sb) == -1) {
510 gpt_warn(gpt, "Cannot stat");
511 goto close;
512 }
513
514 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
515 if (gpt->secsz == 0) {
516 #ifdef DIOCGSECTORSIZE
517 if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
518 gpt_warn(gpt, "Cannot get sector size");
519 goto close;
520 }
521 #endif
522 if (gpt->secsz == 0) {
523 gpt_warnx(gpt, "Sector size can't be 0");
524 goto close;
525 }
526 }
527 if (gpt->mediasz == 0) {
528 #ifdef DIOCGMEDIASIZE
529 if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
530 gpt_warn(gpt, "Cannot get media size");
531 goto close;
532 }
533 #endif
534 if (gpt->mediasz == 0) {
535 gpt_warnx(gpt, "Media size can't be 0");
536 goto close;
537 }
538 }
539 } else {
540 gpt->flags |= GPT_FILE;
541 if (gpt->secsz == 0)
542 gpt->secsz = 512; /* Fixed size for files. */
543 if (gpt->mediasz == 0) {
544 if (gpt->sb.st_size % gpt->secsz) {
545 gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz);
546 errno = EINVAL;
547 goto close;
548 }
549 gpt->mediasz = gpt->sb.st_size;
550 }
551 gpt->flags |= GPT_NOSYNC;
552 }
553
554 /*
555 * We require an absolute minimum of 6 sectors. One for the MBR,
556 * 2 for the GPT header, 2 for the GPT table and one to hold some
557 * user data. Let's catch this extreme border case here so that
558 * we don't have to worry about it later.
559 */
560 devsz = gpt->mediasz / gpt->secsz;
561 if (devsz < 6) {
562 gpt_warnx(gpt, "Need 6 sectors, we have %ju",
563 (uintmax_t)devsz);
564 goto close;
565 }
566
567 if (gpt->verbose) {
568 gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
569 (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
570 }
571
572 if (map_init(gpt, devsz) == -1)
573 goto close;
574
575 index = 1;
576 if (gpt_mbr(gpt, 0LL, &index, 0U) == -1)
577 goto close;
578 if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
579 goto close;
580
581 if (found) {
582 struct map *map;
583 struct gpt_hdr *hdr;
584 uint64_t lba;
585
586 /*
587 * read secondary GPT from position stored in primary header
588 * when possible
589 */
590 map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
591 hdr = map ? map->map_data : NULL;
592 lba = le64toh(hdr->hdr_lba_alt);
593 if (hdr && lba > 0 && lba < (uint64_t)devsz) {
594 if (gpt_gpt(gpt, (off_t)lba, found) == -1)
595 goto close;
596 }
597 } else {
598 if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
599 goto close;
600 }
601
602 return gpt;
603
604 close:
605 if (gpt->fd != -1)
606 close(gpt->fd);
607 gpt_warn(gpt, "No GPT found");
608 free(gpt);
609 return NULL;
610 }
611
612 void
613 gpt_close(gpt_t gpt)
614 {
615
616 if (gpt == NULL)
617 return;
618
619 if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
620 goto out;
621
622 if (!(gpt->flags & GPT_NOSYNC)) {
623 #ifdef DIOCMWEDGES
624 int bits;
625 if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
626 gpt_warn(gpt, "Can't update wedge information");
627 else
628 goto out;
629 #endif
630 }
631 if (!(gpt->flags & GPT_FILE))
632 gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
633 " for the changes to take effect\n", gpt->device_name);
634
635 out:
636 close(gpt->fd);
637 }
638
639 __printflike(2, 0)
640 static void
641 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
642 {
643 if (gpt && (gpt->flags & GPT_QUIET))
644 return;
645 fprintf(stderr, "%s: ", getprogname());
646 if (gpt)
647 fprintf(stderr, "%s: ", gpt->device_name);
648 vfprintf(stderr, fmt, ap);
649 if (e)
650 fprintf(stderr, " (%s)\n", e);
651 else
652 fputc('\n', stderr);
653 }
654
655 void
656 gpt_warnx(gpt_t gpt, const char *fmt, ...)
657 {
658 va_list ap;
659
660 va_start(ap, fmt);
661 gpt_vwarnx(gpt, fmt, ap, NULL);
662 va_end(ap);
663 }
664
665 void
666 gpt_warn(gpt_t gpt, const char *fmt, ...)
667 {
668 va_list ap;
669
670 va_start(ap, fmt);
671 gpt_vwarnx(gpt, fmt, ap, strerror(errno));
672 va_end(ap);
673 }
674
675 void
676 gpt_msg(gpt_t gpt, const char *fmt, ...)
677 {
678 va_list ap;
679
680 if (gpt && (gpt->flags & GPT_QUIET))
681 return;
682 if (gpt)
683 printf("%s: ", gpt->device_name);
684 va_start(ap, fmt);
685 vprintf(fmt, ap);
686 va_end(ap);
687 printf("\n");
688 }
689
690 struct gpt_hdr *
691 gpt_hdr(gpt_t gpt)
692 {
693 gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
694 if (gpt->gpt == NULL) {
695 gpt_warnx(gpt, "No primary GPT header; run create or recover");
696 return NULL;
697 }
698
699 gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
700 if (gpt->tpg == NULL) {
701 gpt_warnx(gpt, "No secondary GPT header; run recover");
702 return NULL;
703 }
704
705 gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
706 gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
707 if (gpt->tbl == NULL || gpt->lbt == NULL) {
708 gpt_warnx(gpt, "Corrupt maps, run recover");
709 return NULL;
710 }
711
712 return gpt->gpt->map_data;
713 }
714
715 int
716 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
717 {
718 struct gpt_hdr *hdr = map->map_data;
719
720 hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
721 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
722 hdr->hdr_crc_self = 0;
723 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
724
725 if (gpt_write(gpt, map) == -1) {
726 gpt_warn(gpt, "Error writing crc map");
727 return -1;
728 }
729
730 if (gpt_write(gpt, tbl) == -1) {
731 gpt_warn(gpt, "Error writing crc table");
732 return -1;
733 }
734
735 return 0;
736 }
737
738 int
739 gpt_write_primary(gpt_t gpt)
740 {
741 return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
742 }
743
744
745 int
746 gpt_write_backup(gpt_t gpt)
747 {
748 return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
749 }
750
751 void
752 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
753 {
754 part->part_flag = active ? 0x80 : 0;
755 part->part_shd = 0x00;
756 part->part_ssect = 0x02;
757 part->part_scyl = 0x00;
758 part->part_typ = MBR_PTYPE_PMBR;
759 part->part_ehd = 0xfe;
760 part->part_esect = 0xff;
761 part->part_ecyl = 0xff;
762 part->part_start_lo = htole16(1);
763 if (last > 0xffffffff) {
764 part->part_size_lo = htole16(0xffff);
765 part->part_size_hi = htole16(0xffff);
766 } else {
767 part->part_size_lo = htole16((uint16_t)last);
768 part->part_size_hi = htole16((uint16_t)(last >> 16));
769 }
770 }
771
772 struct gpt_ent *
773 gpt_ent(map_t map, map_t tbl, unsigned int i)
774 {
775 struct gpt_hdr *hdr = map->map_data;
776 return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
777 }
778
779 struct gpt_ent *
780 gpt_ent_primary(gpt_t gpt, unsigned int i)
781 {
782 return gpt_ent(gpt->gpt, gpt->tbl, i);
783 }
784
785 struct gpt_ent *
786 gpt_ent_backup(gpt_t gpt, unsigned int i)
787 {
788 return gpt_ent(gpt->tpg, gpt->lbt, i);
789 }
790
791 int
792 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
793 {
794 const char **a = cmd->help;
795 size_t hlen = cmd->hlen;
796 size_t i;
797
798 if (prefix == NULL) {
799 const char *pname = getprogname();
800 const char *d1, *d2, *d = " <device>";
801 int len = (int)strlen(pname);
802 if (strcmp(pname, "gpt") == 0) {
803 d1 = "";
804 d2 = d;
805 } else {
806 d2 = "";
807 d1 = d;
808 }
809 fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
810 d1, cmd->name, a[0], d2);
811 for (i = 1; i < hlen; i++) {
812 fprintf(stderr,
813 " %*s%s %s %s%s\n", len, "",
814 d1, cmd->name, a[i], d2);
815 }
816 } else {
817 for (i = 0; i < hlen; i++)
818 fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
819 }
820 return -1;
821 }
822
823 off_t
824 gpt_last(gpt_t gpt)
825 {
826 return gpt->mediasz / gpt->secsz - 1LL;
827 }
828
829 off_t
830 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
831 {
832 off_t blocks;
833 map_t map;
834 struct gpt_hdr *hdr;
835 struct gpt_ent *ent;
836 unsigned int i;
837 void *p;
838
839 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
840 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
841 gpt_warnx(gpt, "Device already contains a GPT, "
842 "destroy it first");
843 return -1;
844 }
845
846 /* Get the amount of free space after the MBR */
847 blocks = map_free(gpt, 1LL, 0LL);
848 if (blocks == 0LL) {
849 gpt_warnx(gpt, "No room for the GPT header");
850 return -1;
851 }
852
853 /* Don't create more than parts entries. */
854 if ((uint64_t)(blocks - 1) * gpt->secsz >
855 parts * sizeof(struct gpt_ent)) {
856 blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
857 if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
858 blocks++;
859 blocks++; /* Don't forget the header itself */
860 }
861
862 /* Never cross the median of the device. */
863 if ((blocks + 1LL) > ((last + 1LL) >> 1))
864 blocks = ((last + 1LL) >> 1) - 1LL;
865
866 /*
867 * Get the amount of free space at the end of the device and
868 * calculate the size for the GPT structures.
869 */
870 map = map_last(gpt);
871 if (map->map_type != MAP_TYPE_UNUSED) {
872 gpt_warnx(gpt, "No room for the backup header");
873 return -1;
874 }
875
876 if (map->map_size < blocks)
877 blocks = map->map_size;
878 if (blocks == 1LL) {
879 gpt_warnx(gpt, "No room for the GPT table");
880 return -1;
881 }
882
883 blocks--; /* Number of blocks in the GPT table. */
884
885 if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
886 return -1;
887
888 if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
889 gpt_warnx(gpt, "Can't allocate the primary GPT table");
890 return -1;
891 }
892 if ((gpt->tbl = map_add(gpt, 2LL, blocks,
893 MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
894 free(p);
895 gpt_warnx(gpt, "Can't add the primary GPT table");
896 return -1;
897 }
898
899 hdr = gpt->gpt->map_data;
900 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
901
902 /*
903 * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
904 * contains padding we must not include in the size.
905 */
906 hdr->hdr_revision = htole32(GPT_HDR_REVISION);
907 hdr->hdr_size = htole32(GPT_HDR_SIZE);
908 hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
909 hdr->hdr_lba_alt = htole64((uint64_t)last);
910 hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
911 hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
912 if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
913 return -1;
914 hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
915 hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
916 sizeof(struct gpt_ent)));
917 if (le32toh(hdr->hdr_entries) > parts)
918 hdr->hdr_entries = htole32(parts);
919 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
920
921 ent = gpt->tbl->map_data;
922 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
923 if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
924 return -1;
925 }
926
927 /*
928 * Create backup GPT if the user didn't suppress it.
929 */
930 if (primary_only)
931 return last;
932
933 if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
934 return -1;
935
936 if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
937 MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
938 gpt_warnx(gpt, "Can't add the secondary GPT table");
939 return -1;
940 }
941
942 memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
943
944 hdr = gpt->tpg->map_data;
945 hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
946 hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
947 hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
948 return last;
949 }
950
951 static int
952 gpt_size_get(gpt_t gpt, off_t *size)
953 {
954 off_t sectors;
955 int64_t human_num;
956 char *p;
957
958 if (*size > 0)
959 return -1;
960 sectors = strtoll(optarg, &p, 10);
961 if (sectors < 1)
962 return -1;
963 if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
964 *size = sectors * gpt->secsz;
965 return 0;
966 }
967 if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
968 *size = sectors;
969 return 0;
970 }
971 if (dehumanize_number(optarg, &human_num) < 0)
972 return -1;
973 *size = human_num;
974 return 0;
975 }
976
977 int
978 gpt_human_get(gpt_t gpt, off_t *human)
979 {
980 int64_t human_num;
981
982 if (*human > 0) {
983 gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
984 optarg);
985 return -1;
986 }
987 if (dehumanize_number(optarg, &human_num) < 0) {
988 gpt_warn(gpt, "Bad number `%s'", optarg);
989 return -1;
990 }
991 *human = human_num;
992 if (*human < 1) {
993 gpt_warn(gpt, "Number `%s' < 1", optarg);
994 return -1;
995 }
996 return 0;
997 }
998
999 int
1000 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
1001 {
1002 switch (ch) {
1003 case 'a':
1004 if (find->all > 0) {
1005 gpt_warn(gpt, "-a is already set");
1006 return -1;
1007 }
1008 find->all = 1;
1009 break;
1010 case 'b':
1011 if (gpt_human_get(gpt, &find->block) == -1)
1012 return -1;
1013 break;
1014 case 'i':
1015 if (gpt_uint_get(gpt, &find->entry) == -1)
1016 return -1;
1017 break;
1018 case 'L':
1019 if (gpt_name_get(gpt, &find->label) == -1)
1020 return -1;
1021 break;
1022 case 's':
1023 if (gpt_size_get(gpt, &find->size) == -1)
1024 return -1;
1025 break;
1026 case 't':
1027 if (!gpt_uuid_is_nil(find->type))
1028 return -1;
1029 if (gpt_uuid_parse(optarg, find->type) != 0)
1030 return -1;
1031 break;
1032 default:
1033 gpt_warn(gpt, "Unknown find option `%c'", ch);
1034 return -1;
1035 }
1036 return 0;
1037 }
1038
1039 int
1040 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1041 void (*cfn)(struct gpt_ent *, void *, int), void *v)
1042 {
1043 map_t m;
1044 struct gpt_hdr *hdr;
1045 struct gpt_ent *ent;
1046 unsigned int i;
1047 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1048
1049 if (!find->all ^
1050 (find->block > 0 || find->entry > 0 || find->label != NULL
1051 || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1052 return -1;
1053
1054 if ((hdr = gpt_hdr(gpt)) == NULL)
1055 return -1;
1056
1057 /* Relabel all matching entries in the map. */
1058 for (m = map_first(gpt); m != NULL; m = m->map_next) {
1059 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1060 continue;
1061 if (find->entry > 0 && find->entry != m->map_index)
1062 continue;
1063 if (find->block > 0 && find->block != m->map_start)
1064 continue;
1065 if (find->size > 0 && find->size != m->map_size)
1066 continue;
1067
1068 i = m->map_index - 1;
1069
1070 ent = gpt_ent_primary(gpt, i);
1071 if (find->label != NULL) {
1072 utf16_to_utf8(ent->ent_name,
1073 __arraycount(ent->ent_name),
1074 utfbuf, __arraycount(utfbuf));
1075 if (strcmp((char *)find->label, (char *)utfbuf) != 0)
1076 continue;
1077 }
1078
1079 if (!gpt_uuid_is_nil(find->type) &&
1080 !gpt_uuid_equal(find->type, ent->ent_type))
1081 continue;
1082
1083 /* Change the primary entry. */
1084 (*cfn)(ent, v, 0);
1085
1086 if (gpt_write_primary(gpt) == -1)
1087 return -1;
1088
1089 ent = gpt_ent_backup(gpt, i);
1090 /* Change the secondary entry. */
1091 (*cfn)(ent, v, 1);
1092
1093 if (gpt_write_backup(gpt) == -1)
1094 return -1;
1095
1096 gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1097 }
1098 return 0;
1099 }
1100
1101 int
1102 gpt_change_hdr(gpt_t gpt, const struct gpt_find *find,
1103 void (*cfn)(struct gpt_hdr *, void *, int), void *v)
1104 {
1105 struct gpt_hdr *hdr;
1106
1107 if ((hdr = gpt_hdr(gpt)) == NULL)
1108 return -1;
1109
1110 /* Change the primary header. */
1111 (*cfn)(hdr, v, 0);
1112
1113 if (gpt_write_primary(gpt) == -1)
1114 return -1;
1115
1116 hdr = gpt->tpg->map_data;
1117 /* Change the secondary header. */
1118 (*cfn)(hdr, v, 1);
1119
1120 if (gpt_write_backup(gpt) == -1)
1121 return -1;
1122
1123 gpt_msg(gpt, "Header %s", find->msg);
1124
1125 return 0;
1126 }
1127
1128 int
1129 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1130 {
1131 switch (ch) {
1132 case 'a':
1133 if (gpt_human_get(gpt, alignment) == -1)
1134 return -1;
1135 return 0;
1136 case 'i':
1137 if (gpt_uint_get(gpt, entry) == -1)
1138 return -1;
1139 return 0;
1140 case 's':
1141 if (gpt_size_get(gpt, size) == -1)
1142 return -1;
1143 return 0;
1144 default:
1145 gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1146 return -1;
1147 }
1148 }
1149
1150 off_t
1151 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1152 {
1153 if (entry == 0) {
1154 gpt_warnx(gpt, "Entry not specified");
1155 return -1;
1156 }
1157 if (alignment % gpt->secsz != 0) {
1158 gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1159 "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1160 return -1;
1161 }
1162
1163 if (size % gpt->secsz != 0) {
1164 gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1165 "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1166 return -1;
1167 }
1168 if (size > 0)
1169 return size / gpt->secsz;
1170 return 0;
1171 }
1172
1173 static const struct nvd {
1174 const char *name;
1175 uint64_t mask;
1176 const char *description;
1177 } gpt_attr[] = {
1178 {
1179 "biosboot",
1180 GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1181 "Legacy BIOS boot partition",
1182 },
1183 {
1184 "bootme",
1185 GPT_ENT_ATTR_BOOTME,
1186 "Bootable partition",
1187 },
1188 {
1189 "bootfailed",
1190 GPT_ENT_ATTR_BOOTFAILED,
1191 "Partition that marked bootonce failed to boot",
1192 },
1193 {
1194 "bootonce",
1195 GPT_ENT_ATTR_BOOTONCE,
1196 "Attempt to boot this partition only once",
1197 },
1198 {
1199 "noblockio",
1200 GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1201 "UEFI won't recognize file system for block I/O",
1202 },
1203 {
1204 "required",
1205 GPT_ENT_ATTR_REQUIRED_PARTITION,
1206 "Partition required for platform to function",
1207 },
1208 };
1209
1210 int
1211 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1212 {
1213 size_t i;
1214 int rv = 0;
1215 char *ptr;
1216
1217 *attributes = 0;
1218
1219 for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1220 for (i = 0; i < __arraycount(gpt_attr); i++)
1221 if (strcmp(gpt_attr[i].name, ptr) == 0)
1222 break;
1223 if (i == __arraycount(gpt_attr)) {
1224 gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
1225 rv = -1;
1226 } else
1227 *attributes |= gpt_attr[i].mask;
1228 }
1229 return rv;
1230 }
1231
1232 void
1233 gpt_attr_help(const char *prefix)
1234 {
1235 size_t i;
1236
1237 for (i = 0; i < __arraycount(gpt_attr); i++)
1238 printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1239 gpt_attr[i].description);
1240 }
1241
1242 const char *
1243 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1244 {
1245 size_t i;
1246 /*
1247 * a uint64_t (attributes) has at most 16 hex digits
1248 * in its representation, add 2 for "0x", and 2 more
1249 * for surrounding [ ], plus one for a trailing \0,
1250 * and we need 21 bytes, round that up to 24
1251 */
1252 char xbuf[24];
1253
1254 strlcpy(buf, "", len);
1255
1256 for (i = 0; i < __arraycount(gpt_attr); i++) {
1257 /*
1258 * if the attribute is specified in one of bits
1259 * 48..63, it should depend upon the defining
1260 * partition type for that attribute. Currently
1261 * we have no idea what that is, so...
1262 *
1263 * Also note that for some partition types, these
1264 * fields are not a single bit boolean, but several
1265 * bits to form a numeric value. That we could handle.
1266 */
1267
1268 if (attributes & gpt_attr[i].mask) {
1269 strlcat(buf, buf[0] ? ", " : "", len);
1270 strlcat(buf, gpt_attr[i].name, len);
1271 #if 0
1272 /*
1273 * there are none currently defined, so this is untestable
1274 * (it does build however).
1275 */
1276 if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) {
1277 /* This only happens in bits 46..63 */
1278
1279 /*
1280 * xbuf is big enough for "=65535\0"
1281 * which is the biggest possible value
1282 */
1283 snprintf(xbuf, sizeof xbuf, "=%ju",
1284 (uintmax_t) (
1285 (attributes & gpt_attr[i].mask) >>
1286 (ffs((int)(gpt_attr[i].mask >> 48)) + 47)
1287 ));
1288
1289 strlcat(buf, xbuf, len);
1290 }
1291 #endif
1292 attributes &=~ gpt_attr[i].mask;
1293 }
1294 }
1295
1296 if (attributes != 0) {
1297 snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes);
1298 strlcat(buf, buf[0] ? ", " : "", len);
1299 strlcat(buf, xbuf, len);
1300 }
1301
1302 return buf;
1303 }
1304
1305 int
1306 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1307 {
1308 struct gpt_hdr *hdr;
1309 struct gpt_ent *ent;
1310 unsigned int i;
1311
1312 if (entry == 0 || (set == 0 && clr == 0)) {
1313 gpt_warnx(gpt, "Nothing to set");
1314 return -1;
1315 }
1316
1317 if ((hdr = gpt_hdr(gpt)) == NULL)
1318 return -1;
1319
1320 if (entry > le32toh(hdr->hdr_entries)) {
1321 gpt_warnx(gpt, "Index %u out of range (%u max)",
1322 entry, le32toh(hdr->hdr_entries));
1323 return -1;
1324 }
1325
1326 i = entry - 1;
1327 ent = gpt_ent_primary(gpt, i);
1328 if (gpt_uuid_is_nil(ent->ent_type)) {
1329 gpt_warnx(gpt, "Entry at index %u is unused", entry);
1330 return -1;
1331 }
1332
1333 ent->ent_attr &= ~clr;
1334 ent->ent_attr |= set;
1335
1336 if (gpt_write_primary(gpt) == -1)
1337 return -1;
1338
1339 ent = gpt_ent_backup(gpt, i);
1340 ent->ent_attr &= ~clr;
1341 ent->ent_attr |= set;
1342
1343 if (gpt_write_backup(gpt) == -1)
1344 return -1;
1345 gpt_msg(gpt, "Partition %d attributes updated", entry);
1346 return 0;
1347 }
1348
1349 int
1350 gpt_uint_get(gpt_t gpt, u_int *entry)
1351 {
1352 char *p;
1353 if (*entry > 0)
1354 return -1;
1355 *entry = (u_int)strtoul(optarg, &p, 10);
1356 if (*p != 0 || *entry < 1) {
1357 gpt_warn(gpt, "Bad number `%s'", optarg);
1358 return -1;
1359 }
1360 return 0;
1361 }
1362 int
1363 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1364 {
1365 if (!gpt_uuid_is_nil(*uuid))
1366 return -1;
1367 if (gpt_uuid_parse(optarg, *uuid) != 0) {
1368 gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg);
1369 return -1;
1370 }
1371 return 0;
1372 }
1373
1374 int
1375 gpt_name_get(gpt_t gpt, void *v)
1376 {
1377 char **name = v;
1378 if (*name != NULL)
1379 return -1;
1380 *name = strdup(optarg);
1381 if (*name == NULL) {
1382 gpt_warn(gpt, "Can't copy string");
1383 return -1;
1384 }
1385 return 0;
1386 }
1387
1388 void
1389 gpt_show_num(const char *prompt, uintmax_t num)
1390 {
1391 #ifdef HN_AUTOSCALE
1392 char human_num[5];
1393 if (humanize_number(human_num, 5, (int64_t)num ,
1394 "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1395 human_num[0] = '\0';
1396 #endif
1397 printf("%s: %ju", prompt, num);
1398 #ifdef HN_AUTOSCALE
1399 if (human_num[0] != '\0')
1400 printf(" (%s)", human_num);
1401 #endif
1402 printf("\n");
1403 }
1404
1405 int
1406 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1407 {
1408 void *p;
1409 map_t *t;
1410 const char *msg;
1411
1412 switch (type) {
1413 case MAP_TYPE_PRI_GPT_HDR:
1414 t = &gpt->gpt;
1415 msg = "primary";
1416 break;
1417 case MAP_TYPE_SEC_GPT_HDR:
1418 t = &gpt->tpg;
1419 msg = "secondary";
1420 break;
1421 default:
1422 gpt_warnx(gpt, "Unknown GPT header type %d", type);
1423 return -1;
1424 }
1425
1426 if ((p = calloc(1, gpt->secsz)) == NULL) {
1427 gpt_warn(gpt, "Error allocating %s GPT header", msg);
1428 return -1;
1429 }
1430
1431 *t = map_add(gpt, loc, 1LL, type, p, 1);
1432 if (*t == NULL) {
1433 gpt_warn(gpt, "Error adding %s GPT header", msg);
1434 free(p);
1435 return -1;
1436 }
1437 return 0;
1438 }
1439