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