gpt.c revision 1.31 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.31 2014/09/29 20:28:57 christos Exp $");
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/disk.h>
44 #include <sys/stat.h>
45 #include <sys/ioctl.h>
46 #include <sys/bootblock.h>
47
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <paths.h>
52 #include <stddef.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <util.h>
58 #include <ctype.h>
59 #include <prop/proplib.h>
60 #include <sys/drvctlio.h>
61
62 #include "map.h"
63 #include "gpt.h"
64
65 char device_path[MAXPATHLEN];
66 const char *device_arg;
67 char *device_name;
68
69 off_t mediasz;
70
71 u_int parts;
72 u_int secsz;
73
74 int readonly, verbose;
75
76 static uint32_t crc32_tab[] = {
77 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
78 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
79 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
80 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
81 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
82 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
83 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
84 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
85 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
86 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
87 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
88 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
89 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
90 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
91 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
92 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
93 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
94 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
95 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
96 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
97 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
98 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
99 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
100 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
101 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
102 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
103 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
104 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
105 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
106 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
107 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
108 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
109 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
110 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
111 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
112 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
113 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
114 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
115 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
116 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
117 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
118 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
119 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
120 };
121
122 uint32_t
123 crc32(const void *buf, size_t size)
124 {
125 const uint8_t *p;
126 uint32_t crc;
127
128 p = buf;
129 crc = ~0U;
130
131 while (size--)
132 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
133
134 return crc ^ ~0U;
135 }
136
137 uint8_t *
138 utf16_to_utf8(uint16_t *s16)
139 {
140 static uint8_t *s8 = NULL;
141 static size_t s8len = 0;
142 size_t s8idx, s16idx, s16len;
143 uint32_t utfchar;
144 unsigned int c;
145
146 s16len = 0;
147 while (s16[s16len++] != 0)
148 ;
149 if (s8len < s16len * 3) {
150 if (s8 != NULL)
151 free(s8);
152 s8len = s16len * 3;
153 s8 = calloc(s16len, 3);
154 }
155 s8idx = s16idx = 0;
156 while (s16idx < s16len) {
157 utfchar = le16toh(s16[s16idx++]);
158 if ((utfchar & 0xf800) == 0xd800) {
159 c = le16toh(s16[s16idx]);
160 if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
161 utfchar = 0xfffd;
162 else
163 s16idx++;
164 }
165 if (utfchar < 0x80) {
166 s8[s8idx++] = utfchar;
167 } else if (utfchar < 0x800) {
168 s8[s8idx++] = 0xc0 | (utfchar >> 6);
169 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
170 } else if (utfchar < 0x10000) {
171 s8[s8idx++] = 0xe0 | (utfchar >> 12);
172 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
173 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
174 } else if (utfchar < 0x200000) {
175 s8[s8idx++] = 0xf0 | (utfchar >> 18);
176 s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
177 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
178 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
179 }
180 }
181 return (s8);
182 }
183
184 void
185 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
186 {
187 size_t s16idx, s8idx, s8len;
188 uint32_t utfchar = 0;
189 unsigned int c, utfbytes;
190
191 s8len = 0;
192 while (s8[s8len++] != 0)
193 ;
194 s8idx = s16idx = 0;
195 utfbytes = 0;
196 do {
197 c = s8[s8idx++];
198 if ((c & 0xc0) != 0x80) {
199 /* Initial characters. */
200 if (utfbytes != 0) {
201 /* Incomplete encoding. */
202 s16[s16idx++] = htole16(0xfffd);
203 if (s16idx == s16len) {
204 s16[--s16idx] = 0;
205 return;
206 }
207 }
208 if ((c & 0xf8) == 0xf0) {
209 utfchar = c & 0x07;
210 utfbytes = 3;
211 } else if ((c & 0xf0) == 0xe0) {
212 utfchar = c & 0x0f;
213 utfbytes = 2;
214 } else if ((c & 0xe0) == 0xc0) {
215 utfchar = c & 0x1f;
216 utfbytes = 1;
217 } else {
218 utfchar = c & 0x7f;
219 utfbytes = 0;
220 }
221 } else {
222 /* Followup characters. */
223 if (utfbytes > 0) {
224 utfchar = (utfchar << 6) + (c & 0x3f);
225 utfbytes--;
226 } else if (utfbytes == 0)
227 utfbytes = -1;
228 }
229 if (utfbytes == 0) {
230 if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
231 utfchar = 0xfffd;
232 if (utfchar >= 0x10000) {
233 s16[s16idx++] =
234 htole16(0xd800 | ((utfchar>>10)-0x40));
235 s16[s16idx++] =
236 htole16(0xdc00 | (utfchar & 0x3ff));
237 } else
238 s16[s16idx++] = htole16(utfchar);
239 if (s16idx == s16len) {
240 s16[--s16idx] = 0;
241 return;
242 }
243 }
244 } while (c != 0);
245 }
246
247 int
248 parse_uuid(const char *s, uuid_t *uuid)
249 {
250 uint32_t status;
251
252 uuid_from_string(s, uuid, &status);
253 if (status == uuid_s_ok)
254 return (0);
255
256 switch (*s) {
257 case 'b':
258 if (strcmp(s, "bios") == 0) {
259 static const uuid_t bios = GPT_ENT_TYPE_BIOS;
260 *uuid = bios;
261 return (0);
262 }
263 break;
264 case 'c':
265 if (strcmp(s, "ccd") == 0) {
266 static const uuid_t ccd = GPT_ENT_TYPE_NETBSD_CCD;
267 *uuid = ccd;
268 return (0);
269 } else if (strcmp(s, "cgd") == 0) {
270 static const uuid_t cgd = GPT_ENT_TYPE_NETBSD_CGD;
271 *uuid = cgd;
272 return (0);
273 }
274 break;
275 case 'e':
276 if (strcmp(s, "efi") == 0) {
277 static const uuid_t efi = GPT_ENT_TYPE_EFI;
278 *uuid = efi;
279 return (0);
280 }
281 break;
282 case 'f':
283 if (strcmp(s, "ffs") == 0) {
284 static const uuid_t nb_ffs = GPT_ENT_TYPE_NETBSD_FFS;
285 *uuid = nb_ffs;
286 return (0);
287 }
288 break;
289 case 'h':
290 if (strcmp(s, "hfs") == 0) {
291 static const uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS;
292 *uuid = hfs;
293 return (0);
294 }
295 break;
296 case 'l':
297 if (strcmp(s, "lfs") == 0) {
298 static const uuid_t lfs = GPT_ENT_TYPE_NETBSD_LFS;
299 *uuid = lfs;
300 return (0);
301 } else if (strcmp(s, "linux") == 0) {
302 static const uuid_t lnx = GPT_ENT_TYPE_LINUX_DATA;
303 *uuid = lnx;
304 return (0);
305 }
306 break;
307 case 'r':
308 if (strcmp(s, "raid") == 0) {
309 static const uuid_t raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME;
310 *uuid = raid;
311 return (0);
312 }
313 break;
314 case 's':
315 if (strcmp(s, "swap") == 0) {
316 static const uuid_t sw = GPT_ENT_TYPE_NETBSD_SWAP;
317 *uuid = sw;
318 return (0);
319 }
320 break;
321 case 'u':
322 if (strcmp(s, "ufs") == 0) {
323 static const uuid_t ufs = GPT_ENT_TYPE_NETBSD_FFS;
324 *uuid = ufs;
325 return (0);
326 }
327 break;
328 case 'w':
329 if (strcmp(s, "windows") == 0) {
330 static const uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA;
331 *uuid = win;
332 return (0);
333 }
334 break;
335 }
336 return (EINVAL);
337 }
338
339 void*
340 gpt_read(int fd, off_t lba, size_t count)
341 {
342 off_t ofs;
343 void *buf;
344
345 count *= secsz;
346 buf = malloc(count);
347 if (buf == NULL)
348 return (NULL);
349
350 ofs = lba * secsz;
351 if (lseek(fd, ofs, SEEK_SET) == ofs &&
352 read(fd, buf, count) == (ssize_t)count)
353 return (buf);
354
355 free(buf);
356 return (NULL);
357 }
358
359 int
360 gpt_write(int fd, map_t *map)
361 {
362 off_t ofs;
363 size_t count;
364
365 count = map->map_size * secsz;
366 ofs = map->map_start * secsz;
367 if (lseek(fd, ofs, SEEK_SET) == ofs &&
368 write(fd, map->map_data, count) == (ssize_t)count)
369 return (0);
370 return (-1);
371 }
372
373 static int
374 gpt_mbr(int fd, off_t lba)
375 {
376 struct mbr *mbr;
377 map_t *m, *p;
378 off_t size, start;
379 unsigned int i, pmbr;
380
381 mbr = gpt_read(fd, lba, 1);
382 if (mbr == NULL)
383 return (-1);
384
385 if (mbr->mbr_sig != htole16(MBR_SIG)) {
386 if (verbose)
387 warnx("%s: MBR not found at sector %llu", device_name,
388 (long long)lba);
389 free(mbr);
390 return (0);
391 }
392
393 /*
394 * Differentiate between a regular MBR and a PMBR. This is more
395 * convenient in general. A PMBR is one with a single partition
396 * of type 0xee.
397 */
398 pmbr = 0;
399 for (i = 0; i < 4; i++) {
400 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
401 continue;
402 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
403 pmbr++;
404 else
405 break;
406 }
407 if (pmbr && i == 4 && lba == 0) {
408 if (pmbr != 1)
409 warnx("%s: Suspicious PMBR at sector %llu",
410 device_name, (long long)lba);
411 else if (verbose > 1)
412 warnx("%s: PMBR at sector %llu", device_name,
413 (long long)lba);
414 p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
415 return ((p == NULL) ? -1 : 0);
416 }
417 if (pmbr)
418 warnx("%s: Suspicious MBR at sector %llu", device_name,
419 (long long)lba);
420 else if (verbose > 1)
421 warnx("%s: MBR at sector %llu", device_name, (long long)lba);
422
423 p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
424 if (p == NULL)
425 return (-1);
426 for (i = 0; i < 4; i++) {
427 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
428 mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
429 continue;
430 start = le16toh(mbr->mbr_part[i].part_start_hi);
431 start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
432 size = le16toh(mbr->mbr_part[i].part_size_hi);
433 size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
434 if (start == 0 && size == 0) {
435 warnx("%s: Malformed MBR at sector %llu", device_name,
436 (long long)lba);
437 continue;
438 }
439 /* start is relative to the offset of the MBR itself. */
440 start += lba;
441 if (verbose > 2)
442 warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
443 device_name, mbr->mbr_part[i].part_typ,
444 (long long)start, (long long)size);
445 if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
446 m = map_add(start, size, MAP_TYPE_MBR_PART, p);
447 if (m == NULL)
448 return (-1);
449 m->map_index = i + 1;
450 } else {
451 if (gpt_mbr(fd, start) == -1)
452 return (-1);
453 }
454 }
455 return (0);
456 }
457
458 static int
459 drvctl(const char *name, u_int *sector_size, off_t *media_size)
460 {
461 prop_dictionary_t command_dict, args_dict, results_dict, data_dict,
462 disk_info, geometry;
463 prop_string_t string;
464 prop_number_t number;
465 int dfd, res;
466 char *dname, *p;
467
468 if ((dfd = open("/dev/drvctl", O_RDONLY)) == -1) {
469 warn("%s: /dev/drvctl", __func__);
470 return -1;
471 }
472
473 command_dict = prop_dictionary_create();
474 args_dict = prop_dictionary_create();
475
476 string = prop_string_create_cstring_nocopy("get-properties");
477 prop_dictionary_set(command_dict, "drvctl-command", string);
478 prop_object_release(string);
479
480 if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
481 (void)close(dfd);
482 return -1;
483 }
484 for (p = dname; *p; p++)
485 continue;
486 for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
487 continue;
488
489 string = prop_string_create_cstring(dname);
490 free(dname);
491 prop_dictionary_set(args_dict, "device-name", string);
492 prop_object_release(string);
493
494 prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
495 prop_object_release(args_dict);
496
497 res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
498 &results_dict);
499 (void)close(dfd);
500 prop_object_release(command_dict);
501 if (res) {
502 warn("%s: prop_dictionary_sendrecv_ioctl", __func__);
503 errno = res;
504 return -1;
505 }
506
507 number = prop_dictionary_get(results_dict, "drvctl-error");
508 if ((errno = prop_number_integer_value(number)) != 0)
509 return -1;
510
511 data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
512 if (data_dict == NULL)
513 goto out;
514
515 disk_info = prop_dictionary_get(data_dict, "disk-info");
516 if (disk_info == NULL)
517 goto out;
518
519 geometry = prop_dictionary_get(disk_info, "geometry");
520 if (geometry == NULL)
521 goto out;
522
523 number = prop_dictionary_get(geometry, "sector-size");
524 if (number == NULL)
525 goto out;
526
527 *sector_size = prop_number_integer_value(number);
528
529 number = prop_dictionary_get(geometry, "sectors-per-unit");
530 if (number == NULL)
531 goto out;
532
533 *media_size = prop_number_integer_value(number) * *sector_size;
534
535 return 0;
536 out:
537 errno = EINVAL;
538 return -1;
539 }
540
541 int
542 gpt_gpt(int fd, off_t lba, int found)
543 {
544 uuid_t type;
545 off_t size;
546 struct gpt_ent *ent;
547 struct gpt_hdr *hdr;
548 char *p, *s;
549 map_t *m;
550 size_t blocks, tblsz;
551 unsigned int i;
552 uint32_t crc;
553
554 hdr = gpt_read(fd, lba, 1);
555 if (hdr == NULL)
556 return (-1);
557
558 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
559 goto fail_hdr;
560
561 crc = le32toh(hdr->hdr_crc_self);
562 hdr->hdr_crc_self = 0;
563 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
564 if (verbose)
565 warnx("%s: Bad CRC in GPT header at sector %llu",
566 device_name, (long long)lba);
567 goto fail_hdr;
568 }
569
570 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
571 blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
572
573 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
574 p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
575 if (p == NULL) {
576 if (found) {
577 if (verbose)
578 warn("%s: Cannot read LBA table at sector %llu",
579 device_name, (unsigned long long)
580 le64toh(hdr->hdr_lba_table));
581 return (-1);
582 }
583 goto fail_hdr;
584 }
585
586 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
587 if (verbose)
588 warnx("%s: Bad CRC in GPT table at sector %llu",
589 device_name,
590 (long long)le64toh(hdr->hdr_lba_table));
591 goto fail_ent;
592 }
593
594 if (verbose > 1)
595 warnx("%s: %s GPT at sector %llu", device_name,
596 (lba == 1) ? "Pri" : "Sec", (long long)lba);
597
598 m = map_add(lba, 1, (lba == 1)
599 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
600 if (m == NULL)
601 return (-1);
602
603 m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
604 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
605 if (m == NULL)
606 return (-1);
607
608 if (lba != 1)
609 return (1);
610
611 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
612 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
613 if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
614 continue;
615
616 size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
617 1LL;
618 if (verbose > 2) {
619 le_uuid_dec(&ent->ent_type, &type);
620 uuid_to_string(&type, &s, NULL);
621 warnx(
622 "%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
623 (long long)le64toh(ent->ent_lba_start),
624 (long long)size);
625 free(s);
626 }
627 m = map_add(le64toh(ent->ent_lba_start), size,
628 MAP_TYPE_GPT_PART, ent);
629 if (m == NULL)
630 return (-1);
631 m->map_index = i + 1;
632 }
633 return (1);
634
635 fail_ent:
636 free(p);
637
638 fail_hdr:
639 free(hdr);
640 return (0);
641 }
642
643 int
644 gpt_open(const char *dev)
645 {
646 struct stat sb;
647 int fd, mode, found;
648
649 mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
650
651 device_arg = dev;
652 fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
653 if (fd == -1)
654 return -1;
655 if (strncmp(device_path, _PATH_DEV, strlen(_PATH_DEV)) == 0)
656 device_name = device_path + strlen(_PATH_DEV);
657 else
658 device_name = device_path;
659
660 if (fstat(fd, &sb) == -1)
661 goto close;
662
663 if ((sb.st_mode & S_IFMT) != S_IFREG) {
664 #ifdef DIOCGSECTORSIZE
665 if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
666 ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
667 goto close;
668 #endif
669 if (drvctl(device_name, &secsz, &mediasz) == -1)
670 goto close;
671 } else {
672 secsz = 512; /* Fixed size for files. */
673 if (sb.st_size % secsz) {
674 errno = EINVAL;
675 goto close;
676 }
677 mediasz = sb.st_size;
678 }
679
680 /*
681 * We require an absolute minimum of 6 sectors. One for the MBR,
682 * 2 for the GPT header, 2 for the GPT table and one to hold some
683 * user data. Let's catch this extreme border case here so that
684 * we don't have to worry about it later.
685 */
686 if (mediasz / secsz < 6) {
687 errno = ENODEV;
688 goto close;
689 }
690
691 if (verbose)
692 warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
693 device_name, (long long)mediasz, secsz,
694 (long long)(mediasz / secsz));
695
696 map_init(mediasz / secsz);
697
698 if (gpt_mbr(fd, 0LL) == -1)
699 goto close;
700 if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
701 goto close;
702 if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
703 goto close;
704
705 return (fd);
706
707 close:
708 close(fd);
709 return (-1);
710 }
711
712 void
713 gpt_close(int fd)
714 {
715 /* XXX post processing? */
716 close(fd);
717 }
718
719 static struct {
720 int (*fptr)(int, char *[]);
721 const char *name;
722 } cmdsw[] = {
723 { cmd_add, "add" },
724 { cmd_backup, "backup" },
725 { cmd_biosboot, "biosboot" },
726 { cmd_create, "create" },
727 { cmd_destroy, "destroy" },
728 { NULL, "help" },
729 { cmd_label, "label" },
730 { cmd_migrate, "migrate" },
731 { cmd_recover, "recover" },
732 { cmd_remove, "remove" },
733 { NULL, "rename" },
734 { cmd_resize, "resize" },
735 { cmd_resizedisk, "resizedisk" },
736 { cmd_restore, "restore" },
737 { cmd_set, "set" },
738 { cmd_show, "show" },
739 { cmd_type, "type" },
740 { cmd_unset, "unset" },
741 { NULL, "verify" },
742 { NULL, NULL }
743 };
744
745 __dead static void
746 usage(void)
747 {
748 extern const char addmsg1[], addmsg2[], backupmsg[], biosbootmsg[];
749 extern const char createmsg[], destroymsg[], labelmsg1[], labelmsg2[];
750 extern const char labelmsg3[], migratemsg[], recovermsg[], removemsg1[];
751 extern const char removemsg2[], resizemsg[], resizediskmsg[];
752 extern const char restoremsg[], setmsg[], showmsg[], typemsg1[];
753 extern const char typemsg2[], typemsg3[], unsetmsg[];
754
755 fprintf(stderr,
756 "usage: %s %s\n"
757 " %s %s\n"
758 " %s %s\n"
759 " %s %s\n"
760 " %s %s\n"
761 " %s %s\n"
762 " %s %s\n"
763 " %s %s\n"
764 " %*s %s\n"
765 " %s %s\n"
766 " %s %s\n"
767 " %s %s\n"
768 " %s %s\n"
769 " %s %s\n"
770 " %s %s\n"
771 " %s %s\n"
772 " %s %s\n"
773 " %s %s\n"
774 " %s %s\n"
775 " %s %s\n"
776 " %*s %s\n"
777 " %s %s\n",
778 getprogname(), addmsg1,
779 getprogname(), addmsg2,
780 getprogname(), backupmsg,
781 getprogname(), biosbootmsg,
782 getprogname(), createmsg,
783 getprogname(), destroymsg,
784 getprogname(), labelmsg1,
785 getprogname(), labelmsg2,
786 (int)strlen(getprogname()), "", labelmsg3,
787 getprogname(), migratemsg,
788 getprogname(), recovermsg,
789 getprogname(), removemsg1,
790 getprogname(), removemsg2,
791 getprogname(), resizemsg,
792 getprogname(), resizediskmsg,
793 getprogname(), restoremsg,
794 getprogname(), setmsg,
795 getprogname(), showmsg,
796 getprogname(), typemsg1,
797 getprogname(), typemsg2,
798 (int)strlen(getprogname()), "", typemsg3,
799 getprogname(), unsetmsg);
800 exit(1);
801 }
802
803 static void
804 prefix(const char *cmd)
805 {
806 char *pfx;
807 const char *prg;
808
809 prg = getprogname();
810 pfx = malloc(strlen(prg) + strlen(cmd) + 2);
811 /* Don't bother failing. It's not important */
812 if (pfx == NULL)
813 return;
814
815 sprintf(pfx, "%s %s", prg, cmd);
816 setprogname(pfx);
817 }
818
819 int
820 main(int argc, char *argv[])
821 {
822 char *cmd, *p;
823 int ch, i;
824
825 /* Get the generic options */
826 while ((ch = getopt(argc, argv, "p:rv")) != -1) {
827 switch(ch) {
828 case 'p':
829 if (parts > 0)
830 usage();
831 parts = strtoul(optarg, &p, 10);
832 if (*p != 0 || parts < 1)
833 usage();
834 break;
835 case 'r':
836 readonly = 1;
837 break;
838 case 'v':
839 verbose++;
840 break;
841 default:
842 usage();
843 }
844 }
845 if (!parts)
846 parts = 128;
847
848 if (argc == optind)
849 usage();
850
851 cmd = argv[optind++];
852 for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
853
854 if (cmdsw[i].fptr == NULL)
855 errx(1, "unknown command: %s", cmd);
856
857 prefix(cmd);
858 return ((*cmdsw[i].fptr)(argc, argv));
859 }
860