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