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