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