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.15 2011/08/27 17:38:16 joerg 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
62char	device_path[MAXPATHLEN];
63const char *device_arg;
64char	*device_name;
65
66off_t	mediasz;
67
68u_int	parts;
69u_int	secsz;
70
71int	readonly, verbose;
72
73static 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
119uint32_t
120crc32(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
134uint8_t *
135utf16_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
181void
182utf8_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
244void
245le_uuid_dec(void const *buf, uuid_t *uuid)
246{
247	u_char const *p;
248	int i;
249
250	p = buf;
251	uuid->time_low = le32dec(p);
252	uuid->time_mid = le16dec(p + 4);
253	uuid->time_hi_and_version = le16dec(p + 6);
254	uuid->clock_seq_hi_and_reserved = p[8];
255	uuid->clock_seq_low = p[9];
256	for (i = 0; i < _UUID_NODE_LEN; i++)
257		uuid->node[i] = p[10 + i];
258}
259
260void
261le_uuid_enc(void *buf, uuid_t const *uuid)
262{
263	u_char *p;
264	int i;
265
266	p = buf;
267	le32enc(p, uuid->time_low);
268	le16enc(p + 4, uuid->time_mid);
269	le16enc(p + 6, uuid->time_hi_and_version);
270	p[8] = uuid->clock_seq_hi_and_reserved;
271	p[9] = uuid->clock_seq_low;
272	for (i = 0; i < _UUID_NODE_LEN; i++)
273		p[10 + i] = uuid->node[i];
274}
275
276int
277parse_uuid(const char *s, uuid_t *uuid)
278{
279	uint32_t status;
280
281	uuid_from_string(s, uuid, &status);
282	if (status == uuid_s_ok)
283		return (0);
284
285	switch (*s) {
286	case 'b':
287		if (strcmp(s, "bios") == 0) {
288			uuid_t bios = GPT_ENT_TYPE_BIOS;
289			*uuid = bios;
290			return (0);
291		}
292		break;
293	case 'c':
294		if (strcmp(s, "ccd") == 0) {
295			uuid_t ccd = GPT_ENT_TYPE_NETBSD_CCD;
296			*uuid = ccd;
297			return (0);
298		} else if (strcmp(s, "cgd") == 0) {
299			uuid_t cgd = GPT_ENT_TYPE_NETBSD_CGD;
300			*uuid = cgd;
301			return (0);
302		}
303		break;
304	case 'e':
305		if (strcmp(s, "efi") == 0) {
306			uuid_t efi = GPT_ENT_TYPE_EFI;
307			*uuid = efi;
308			return (0);
309		}
310		break;
311	case 'f':
312		if (strcmp(s, "ffs") == 0) {
313			uuid_t nb_ffs = GPT_ENT_TYPE_NETBSD_FFS;
314			*uuid = nb_ffs;
315			return (0);
316		}
317		break;
318	case 'h':
319		if (strcmp(s, "hfs") == 0) {
320			uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS;
321			*uuid = hfs;
322			return (0);
323		}
324		break;
325	case 'l':
326		if (strcmp(s, "lfs") == 0) {
327			uuid_t lfs = GPT_ENT_TYPE_NETBSD_LFS;
328			*uuid = lfs;
329			return (0);
330		} else if (strcmp(s, "linux") == 0) {
331			uuid_t lnx = GPT_ENT_TYPE_MS_BASIC_DATA;
332			*uuid = lnx;
333			return (0);
334		}
335		break;
336	case 'r':
337		if (strcmp(s, "raid") == 0) {
338			uuid_t raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME;
339			*uuid = raid;
340			return (0);
341		}
342		break;
343	case 's':
344		if (strcmp(s, "swap") == 0) {
345			uuid_t sw = GPT_ENT_TYPE_NETBSD_SWAP;
346			*uuid = sw;
347			return (0);
348		}
349		break;
350	case 'u':
351		if (strcmp(s, "ufs") == 0) {
352			uuid_t ufs = GPT_ENT_TYPE_NETBSD_FFS;
353			*uuid = ufs;
354			return (0);
355		}
356		break;
357	case 'w':
358		if (strcmp(s, "windows") == 0) {
359			uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA;
360			*uuid = win;
361			return (0);
362		}
363		break;
364	}
365	return (EINVAL);
366}
367
368void*
369gpt_read(int fd, off_t lba, size_t count)
370{
371	off_t ofs;
372	void *buf;
373
374	count *= secsz;
375	buf = malloc(count);
376	if (buf == NULL)
377		return (NULL);
378
379	ofs = lba * secsz;
380	if (lseek(fd, ofs, SEEK_SET) == ofs &&
381	    read(fd, buf, count) == (ssize_t)count)
382		return (buf);
383
384	free(buf);
385	return (NULL);
386}
387
388int
389gpt_write(int fd, map_t *map)
390{
391	off_t ofs;
392	size_t count;
393
394	count = map->map_size * secsz;
395	ofs = map->map_start * secsz;
396	if (lseek(fd, ofs, SEEK_SET) == ofs &&
397	    write(fd, map->map_data, count) == (ssize_t)count)
398		return (0);
399	return (-1);
400}
401
402static int
403gpt_mbr(int fd, off_t lba)
404{
405	struct mbr *mbr;
406	map_t *m, *p;
407	off_t size, start;
408	unsigned int i, pmbr;
409
410	mbr = gpt_read(fd, lba, 1);
411	if (mbr == NULL)
412		return (-1);
413
414	if (mbr->mbr_sig != htole16(MBR_SIG)) {
415		if (verbose)
416			warnx("%s: MBR not found at sector %llu", device_name,
417			    (long long)lba);
418		free(mbr);
419		return (0);
420	}
421
422	/*
423	 * Differentiate between a regular MBR and a PMBR. This is more
424	 * convenient in general. A PMBR is one with a single partition
425	 * of type 0xee.
426	 */
427	pmbr = 0;
428	for (i = 0; i < 4; i++) {
429		if (mbr->mbr_part[i].part_typ == 0)
430			continue;
431		if (mbr->mbr_part[i].part_typ == 0xee)
432			pmbr++;
433		else
434			break;
435	}
436	if (pmbr && i == 4 && lba == 0) {
437		if (pmbr != 1)
438			warnx("%s: Suspicious PMBR at sector %llu",
439			    device_name, (long long)lba);
440		else if (verbose > 1)
441			warnx("%s: PMBR at sector %llu", device_name,
442			    (long long)lba);
443		p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
444		return ((p == NULL) ? -1 : 0);
445	}
446	if (pmbr)
447		warnx("%s: Suspicious MBR at sector %llu", device_name,
448		    (long long)lba);
449	else if (verbose > 1)
450		warnx("%s: MBR at sector %llu", device_name, (long long)lba);
451
452	p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
453	if (p == NULL)
454		return (-1);
455	for (i = 0; i < 4; i++) {
456		if (mbr->mbr_part[i].part_typ == 0 ||
457		    mbr->mbr_part[i].part_typ == 0xee)
458			continue;
459		start = le16toh(mbr->mbr_part[i].part_start_hi);
460		start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
461		size = le16toh(mbr->mbr_part[i].part_size_hi);
462		size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
463		if (start == 0 && size == 0) {
464			warnx("%s: Malformed MBR at sector %llu", device_name,
465			    (long long)lba);
466			continue;
467		}
468		/* start is relative to the offset of the MBR itself. */
469		start += lba;
470		if (verbose > 2)
471			warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
472			    device_name, mbr->mbr_part[i].part_typ,
473			    (long long)start, (long long)size);
474		if (mbr->mbr_part[i].part_typ != 15) {
475			m = map_add(start, size, MAP_TYPE_MBR_PART, p);
476			if (m == NULL)
477				return (-1);
478			m->map_index = i + 1;
479		} else {
480			if (gpt_mbr(fd, start) == -1)
481				return (-1);
482		}
483	}
484	return (0);
485}
486
487#ifdef __NetBSD__
488static int
489drvctl(const char *name, u_int *sector_size, off_t *media_size)
490{
491	prop_dictionary_t command_dict, args_dict, results_dict, data_dict,
492	    disk_info, geometry;
493	prop_string_t string;
494	prop_number_t number;
495	int dfd, res;
496	char *dname, *p;
497
498	if ((dfd = open("/dev/drvctl", O_RDONLY)) == -1) {
499		warn("%s: /dev/drvctl", __func__);
500		return -1;
501	}
502
503	command_dict = prop_dictionary_create();
504	args_dict = prop_dictionary_create();
505
506	string = prop_string_create_cstring_nocopy("get-properties");
507	prop_dictionary_set(command_dict, "drvctl-command", string);
508	prop_object_release(string);
509
510	if ((dname = strdup(name[0] == 'r' ? name + 1 : name)) == NULL) {
511		(void)close(dfd);
512		return -1;
513	}
514	for (p = dname; *p; p++)
515		continue;
516	for (--p; p >= dname && !isdigit((unsigned char)*p); *p-- = '\0')
517		continue;
518
519	string = prop_string_create_cstring(dname);
520	free(dname);
521	prop_dictionary_set(args_dict, "device-name", string);
522	prop_object_release(string);
523
524	prop_dictionary_set(command_dict, "drvctl-arguments", args_dict);
525	prop_object_release(args_dict);
526
527	res = prop_dictionary_sendrecv_ioctl(command_dict, dfd, DRVCTLCOMMAND,
528	    &results_dict);
529	(void)close(dfd);
530	prop_object_release(command_dict);
531	if (res) {
532		warn("%s: prop_dictionary_sendrecv_ioctl", __func__);
533		errno = res;
534		return -1;
535	}
536
537	number = prop_dictionary_get(results_dict, "drvctl-error");
538	if ((errno = prop_number_integer_value(number)) != 0)
539		return -1;
540
541	data_dict = prop_dictionary_get(results_dict, "drvctl-result-data");
542	if (data_dict == NULL)
543		goto out;
544
545	disk_info = prop_dictionary_get(data_dict, "disk-info");
546	if (disk_info == NULL)
547		goto out;
548
549	geometry = prop_dictionary_get(disk_info, "geometry");
550	if (geometry == NULL)
551		goto out;
552
553	number = prop_dictionary_get(geometry, "sector-size");
554	if (number == NULL)
555		goto out;
556
557	*sector_size = prop_number_integer_value(number);
558
559	number = prop_dictionary_get(geometry, "sectors-per-unit");
560	if (number == NULL)
561		goto out;
562
563	*media_size = prop_number_integer_value(number) * *sector_size;
564
565	return 0;
566out:
567	errno = EINVAL;
568	return -1;
569}
570#endif
571
572static int
573gpt_gpt(int fd, off_t lba, int found)
574{
575	uuid_t type;
576	off_t size;
577	struct gpt_ent *ent;
578	struct gpt_hdr *hdr;
579	char *p, *s;
580	map_t *m;
581	size_t blocks, tblsz;
582	unsigned int i;
583	uint32_t crc;
584
585	hdr = gpt_read(fd, lba, 1);
586	if (hdr == NULL)
587		return (-1);
588
589	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
590		goto fail_hdr;
591
592	crc = le32toh(hdr->hdr_crc_self);
593	hdr->hdr_crc_self = 0;
594	if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
595		if (verbose)
596			warnx("%s: Bad CRC in GPT header at sector %llu",
597			    device_name, (long long)lba);
598		goto fail_hdr;
599	}
600
601	tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
602	blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
603
604	/* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
605	p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
606	if (p == NULL) {
607		if (found) {
608			if (verbose)
609				warn("%s: Cannot read LBA table at sector %llu",
610				    device_name, (unsigned long long)
611				    le64toh(hdr->hdr_lba_table));
612			return (-1);
613		}
614		goto fail_hdr;
615	}
616
617	if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
618		if (verbose)
619			warnx("%s: Bad CRC in GPT table at sector %llu",
620			    device_name,
621			    (long long)le64toh(hdr->hdr_lba_table));
622		goto fail_ent;
623	}
624
625	if (verbose > 1)
626		warnx("%s: %s GPT at sector %llu", device_name,
627		    (lba == 1) ? "Pri" : "Sec", (long long)lba);
628
629	m = map_add(lba, 1, (lba == 1)
630	    ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
631	if (m == NULL)
632		return (-1);
633
634	m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
635	    ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
636	if (m == NULL)
637		return (-1);
638
639	if (lba != 1)
640		return (1);
641
642	for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
643		ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
644		if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
645			continue;
646
647		size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
648		    1LL;
649		if (verbose > 2) {
650			le_uuid_dec(&ent->ent_type, &type);
651			uuid_to_string(&type, &s, NULL);
652			warnx(
653	"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
654			    (long long)le64toh(ent->ent_lba_start),
655			    (long long)size);
656			free(s);
657		}
658		m = map_add(le64toh(ent->ent_lba_start), size,
659		    MAP_TYPE_GPT_PART, ent);
660		if (m == NULL)
661			return (-1);
662		m->map_index = i + 1;
663	}
664	return (1);
665
666 fail_ent:
667	free(p);
668
669 fail_hdr:
670	free(hdr);
671	return (0);
672}
673
674int
675gpt_open(const char *dev)
676{
677	struct stat sb;
678	int fd, mode, found;
679
680	mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
681
682	device_arg = dev;
683#ifdef __FreeBSD__
684	strlcpy(device_path, dev, sizeof(device_path));
685	if ((fd = open(device_path, mode)) != -1)
686		goto found;
687
688	snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev);
689	device_name = device_path + strlen(_PATH_DEV);
690	if ((fd = open(device_path, mode)) != -1)
691		goto found;
692	return (-1);
693 found:
694#endif
695#ifdef __NetBSD__
696	device_name = device_path + strlen(_PATH_DEV);
697	fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
698	if (fd == -1)
699		return -1;
700#endif
701
702	if (fstat(fd, &sb) == -1)
703		goto close;
704
705	if ((sb.st_mode & S_IFMT) != S_IFREG) {
706#ifdef DIOCGSECTORSIZE
707		if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1 ||
708		    ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1)
709			goto close;
710#endif
711#ifdef __NetBSD__
712		if (drvctl(device_name, &secsz, &mediasz) == -1)
713			goto close;
714#endif
715	} else {
716		secsz = 512;	/* Fixed size for files. */
717		if (sb.st_size % secsz) {
718			errno = EINVAL;
719			goto close;
720		}
721		mediasz = sb.st_size;
722	}
723
724	/*
725	 * We require an absolute minimum of 6 sectors. One for the MBR,
726	 * 2 for the GPT header, 2 for the GPT table and one to hold some
727	 * user data. Let's catch this extreme border case here so that
728	 * we don't have to worry about it later.
729	 */
730	if (mediasz / secsz < 6) {
731		errno = ENODEV;
732		goto close;
733	}
734
735	if (verbose)
736		warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
737		    device_name, (long long)mediasz, secsz,
738		    (long long)(mediasz / secsz));
739
740	map_init(mediasz / secsz);
741
742	if (gpt_mbr(fd, 0LL) == -1)
743		goto close;
744	if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
745		goto close;
746	if (gpt_gpt(fd, mediasz / secsz - 1LL, found) == -1)
747		goto close;
748
749	return (fd);
750
751 close:
752	close(fd);
753	return (-1);
754}
755
756void
757gpt_close(int fd)
758{
759	/* XXX post processing? */
760	close(fd);
761}
762
763static struct {
764	int (*fptr)(int, char *[]);
765	const char *name;
766} cmdsw[] = {
767	{ cmd_add, "add" },
768	{ cmd_biosboot, "biosboot" },
769	{ cmd_create, "create" },
770	{ cmd_destroy, "destroy" },
771	{ NULL, "help" },
772	{ cmd_label, "label" },
773	{ cmd_migrate, "migrate" },
774	{ cmd_recover, "recover" },
775	{ cmd_remove, "remove" },
776	{ NULL, "rename" },
777	{ cmd_show, "show" },
778	{ NULL, "verify" },
779	{ NULL, NULL }
780};
781
782__dead static void
783usage(void)
784{
785	extern const char addmsg[], biosbootmsg[], createmsg[], destroymsg[];
786	extern const char labelmsg1[], labelmsg2[], labelmsg3[];
787	extern const char migratemsg[], recovermsg[], removemsg1[];
788	extern const char removemsg2[], showmsg[];
789
790	fprintf(stderr,
791	    "usage: %s %s\n"
792	    "       %s %s\n"
793	    "       %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	    getprogname(), addmsg,
804	    getprogname(), biosbootmsg,
805	    getprogname(), createmsg,
806	    getprogname(), destroymsg,
807	    getprogname(), labelmsg1,
808	    getprogname(), labelmsg2,
809	    (int)strlen(getprogname()), "", labelmsg3,
810	    getprogname(), migratemsg,
811	    getprogname(), recovermsg,
812	    getprogname(), removemsg1,
813	    getprogname(), removemsg2,
814	    getprogname(), showmsg);
815	exit(1);
816}
817
818static void
819prefix(const char *cmd)
820{
821	char *pfx;
822	const char *prg;
823
824	prg = getprogname();
825	pfx = malloc(strlen(prg) + strlen(cmd) + 2);
826	/* Don't bother failing. It's not important */
827	if (pfx == NULL)
828		return;
829
830	sprintf(pfx, "%s %s", prg, cmd);
831	setprogname(pfx);
832}
833
834int
835main(int argc, char *argv[])
836{
837	char *cmd, *p;
838	int ch, i;
839
840	/* Get the generic options */
841	while ((ch = getopt(argc, argv, "p:rv")) != -1) {
842		switch(ch) {
843		case 'p':
844			if (parts > 0)
845				usage();
846			parts = strtoul(optarg, &p, 10);
847			if (*p != 0 || parts < 1)
848				usage();
849			break;
850		case 'r':
851			readonly = 1;
852			break;
853		case 'v':
854			verbose++;
855			break;
856		default:
857			usage();
858		}
859	}
860	if (!parts)
861		parts = 128;
862
863	if (argc == optind)
864		usage();
865
866	cmd = argv[optind++];
867	for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++);
868
869	if (cmdsw[i].fptr == NULL)
870		errx(1, "unknown command: %s", cmd);
871
872	prefix(cmd);
873	return ((*cmdsw[i].fptr)(argc, argv));
874}
875