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