xref: /netbsd-src/sbin/gpt/show.c (revision 87b4c5e3d17fee9e9f68698e14f5b63497fe22d7)
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 
27 #if HAVE_NBTOOL_CONFIG_H
28 #include "nbtool_config.h"
29 #endif
30 
31 #include <sys/cdefs.h>
32 #ifdef __FBSDID
33 __FBSDID("$FreeBSD: src/sbin/gpt/show.c,v 1.14 2006/06/22 22:22:32 marcel Exp $");
34 #endif
35 #ifdef __RCSID
36 __RCSID("$NetBSD: show.c,v 1.46 2024/11/04 18:36:16 christos Exp $");
37 #endif
38 
39 #include <sys/bootblock.h>
40 #include <sys/types.h>
41 
42 #include <err.h>
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 
50 #include "map.h"
51 #include "gpt.h"
52 #include "gpt_private.h"
53 
54 static int cmd_show(gpt_t, int, char *[]);
55 
56 static const char *showhelp[] = {
57 	"[-aglux] [-i index]",
58 };
59 
60 #define SHOW_UUID  1
61 #define SHOW_GUID  2
62 #define SHOW_LABEL 4
63 #define SHOW_ALL   8
64 #define SHOW_HEX   16
65 
66 struct gpt_cmd c_show = {
67 	"show",
68 	cmd_show,
69 	showhelp, __arraycount(showhelp),
70 	GPT_READONLY,
71 };
72 
73 #define usage() gpt_usage(NULL, &c_show)
74 
75 static const char *
76 get_mbr_sig(char *b, size_t blen, const uint8_t *bp)
77 {
78 	gpt_uuid_t uuid;
79 
80 	/*
81 	 * MBR partitions have a 4 byte signature in the MBR.  Table
82 	 * 10.54 of UEFI Spec 2.10 Errata A states how this is to be
83 	 * formatted as a GUID.
84 	 *
85 	 * XXX: I thought I had seen more on this elsewhere, but I
86 	 * can't seem to find it now.  In particular, the endianness
87 	 * of this quanity is not clear in the above.
88 	 *
89 	 * XXX: The location and size of the MBR signature should be
90 	 * in 'struct mbr,' e.g.:
91 	 *
92 	 * struct mbr {
93 	 *	uint8_t		mbr_code[440];
94 	 *	uint32_t	mbr_disc_sig;
95 	 *	uint16_t	mbr_unknown;
96 	 *	struct mbr_part	mbr_part[4];
97 	 *	uint16_t	mbr_sig;
98 	 * };
99 	 *
100 	 * For now, we just hardcode it.  Ugh!
101 	 */
102 	memset(uuid, 0, sizeof(uuid));
103 	memcpy(uuid, bp + 440, 4);
104 	gpt_uuid_snprintf(b, blen, "%d", uuid);
105 	return b;
106 }
107 
108 static const char *
109 get_gpt_hdr_guid(char *b, size_t blen, struct gpt_hdr *hdr)
110 {
111 	gpt_uuid_snprintf(b, blen, "%d", hdr->hdr_guid);
112 	return b;
113 }
114 
115 static void
116 print_part_type(int map_type, int flags, void *map_data, off_t map_start)
117 {
118 	off_t start;
119 	map_t p;
120 	struct mbr *mbr;
121 	struct gpt_ent *ent;
122 	unsigned int i;
123 	char buf[128], *b = buf;
124 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
125 
126 	switch (map_type) {
127 	case MAP_TYPE_UNUSED:
128 		printf("Unused");
129 		break;
130 	case MAP_TYPE_MBR:
131 		if (map_start != 0)
132 			printf("Extended ");
133 		printf("MBR");
134 		if (map_start == 0 && flags & SHOW_GUID)
135 			printf(" - %s",
136 			    get_mbr_sig(buf, sizeof(buf), map_data));
137 		break;
138 	case MAP_TYPE_PRI_GPT_HDR:
139 		printf("Pri GPT header");
140 		if (flags & SHOW_GUID)
141 			printf(" - %s",
142 			    get_gpt_hdr_guid(buf, sizeof(buf), map_data));
143 		break;
144 	case MAP_TYPE_SEC_GPT_HDR:
145 		printf("Sec GPT header");
146 		if (flags & SHOW_GUID)
147 			printf(" - %s",
148 			    get_gpt_hdr_guid(buf, sizeof(buf), map_data));
149 		break;
150 	case MAP_TYPE_PRI_GPT_TBL:
151 		printf("Pri GPT table");
152 		break;
153 	case MAP_TYPE_SEC_GPT_TBL:
154 		printf("Sec GPT table");
155 		break;
156 	case MAP_TYPE_MBR_PART:
157 		p = map_data;
158 		if (p->map_start != 0)
159 			printf("Extended ");
160 		printf("MBR part ");
161 		mbr = p->map_data;
162 		for (i = 0; i < 4; i++) {
163 			start = le16toh(mbr->mbr_part[i].part_start_hi);
164 			start = (start << 16) +
165 			    le16toh(mbr->mbr_part[i].part_start_lo);
166 			if (map_start == p->map_start + start)
167 				break;
168 		}
169 		if (i == 4) {
170 			/* wasn't there */
171 			printf("[partition not found?]");
172 		} else {
173 			printf("%d%s", mbr->mbr_part[i].part_typ,
174 			    mbr->mbr_part[i].part_flag == 0x80 ?
175 			    " (active)" : "");
176 		}
177 		break;
178 	case MAP_TYPE_GPT_PART:
179 		printf("GPT part ");
180 		ent = map_data;
181 		if (flags & SHOW_LABEL) {
182 			utf16_to_utf8(ent->ent_name,
183 			    __arraycount(ent->ent_name), utfbuf,
184 			    __arraycount(utfbuf));
185 			b = (char *)utfbuf;
186 		} else if (flags & SHOW_GUID) {
187 			gpt_uuid_snprintf( buf, sizeof(buf), "%d",
188 			    ent->ent_guid);
189 		} else if (flags & SHOW_UUID) {
190 			gpt_uuid_snprintf(buf, sizeof(buf),
191 			    "%d", ent->ent_type);
192 		} else {
193 			gpt_uuid_snprintf(buf, sizeof(buf), "%ls",
194 			    ent->ent_type);
195 		}
196 		printf("- %s", b);
197 		break;
198 	case MAP_TYPE_PMBR:
199 		printf("PMBR");
200 		mbr = map_data;
201 		if (mbr->mbr_part[0].part_typ == MBR_PTYPE_PMBR &&
202 		    mbr->mbr_part[0].part_flag == 0x80)
203 			    printf(" (active)");
204 		break;
205 	default:
206 		printf("Unknown %#x", map_type);
207 		break;
208 	}
209 }
210 
211 static int
212 show(gpt_t gpt, int xshow)
213 {
214 	map_t m;
215 
216 	printf("  %*s", gpt->lbawidth, "start");
217 	printf("  %*s", gpt->lbawidth, "size");
218 	printf("  index  contents\n");
219 
220 	m = map_first(gpt);
221 	while (m != NULL) {
222 #define FMT (xshow & SHOW_HEX) ? "  %*jx" : "  %*ju"
223 		printf(FMT, gpt->lbawidth, (uintmax_t)m->map_start);
224 		printf(FMT, gpt->lbawidth, (uintmax_t)m->map_size);
225 		putchar(' ');
226 		putchar(' ');
227 		if (m->map_index > 0)
228 			printf("%5d", m->map_index);
229 		else
230 			printf("     ");
231 		putchar(' ');
232 		putchar(' ');
233 		print_part_type(m->map_type, xshow, m->map_data, m->map_start);
234 		putchar('\n');
235 		m = m->map_next;
236 	}
237 	return 0;
238 }
239 
240 static void
241 gpt_show_sec_num(const char *prompt, int64_t secsize, off_t num)
242 {
243 #ifdef HN_AUTOSCALE
244 	char human_num[5];
245 	if (humanize_number(human_num, sizeof(human_num),
246 	    (int64_t)num*secsize,
247 	    "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
248 		human_num[0] = '\0';
249 #endif
250 	printf("%s: %" PRIu64, prompt, (uint64_t)num);
251 #ifdef HN_AUTOSCALE
252 	if (human_num[0] != '\0')
253 		printf(" (%s)", human_num);
254 #endif
255 	printf("\n");
256 }
257 
258 static int
259 show_one(gpt_t gpt, unsigned int entry)
260 {
261 	map_t m;
262 	struct gpt_ent *ent;
263 	char s1[128], s2[128];
264 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
265 
266 	for (m = map_first(gpt); m != NULL; m = m->map_next)
267 		if (entry == m->map_index)
268 			break;
269 	if (m == NULL) {
270 		gpt_warnx(gpt, "Could not find index %d", entry);
271 		return -1;
272 	}
273 	ent = m->map_data;
274 
275 	printf("Details for index %d:\n", entry);
276 	gpt_show_sec_num("Start", gpt->secsz, m->map_start);
277 	gpt_show_sec_num("Size", gpt->secsz, m->map_size);
278 
279 	gpt_uuid_snprintf(s1, sizeof(s1), "%s", ent->ent_type);
280 	gpt_uuid_snprintf(s2, sizeof(s2), "%d", ent->ent_type);
281 	if (strcmp(s1, s2) == 0)
282 		strlcpy(s1, "unknown", sizeof(s1));
283 	printf("Type: %s (%s)\n", s1, s2);
284 
285 	gpt_uuid_snprintf(s2, sizeof(s1), "%d", ent->ent_guid);
286 	printf("GUID: %s\n", s2);
287 
288 	utf16_to_utf8(ent->ent_name, __arraycount(ent->ent_name), utfbuf,
289 	    __arraycount(utfbuf));
290 	printf("Label: %s\n", (char *)utfbuf);
291 
292 	printf("Attributes: ");
293 	if (ent->ent_attr == 0) {
294 		printf("None\n");
295 	} else  {
296 		char buf[1024];
297 		printf("%s\n", gpt_attr_list(buf, sizeof(buf), ent->ent_attr));
298 	}
299 
300 	return 0;
301 }
302 
303 static int
304 show_all(gpt_t gpt, int xshow)
305 {
306 	map_t m;
307 	struct gpt_ent *ent;
308 	char s1[128], s2[128];
309 #ifdef HN_AUTOSCALE
310 	char human_num[8];
311 #endif
312 	uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
313 #define PFX "                                 "
314 
315 	printf("  %*s", gpt->lbawidth, "start");
316 	printf("  %*s", gpt->lbawidth, "size");
317 	printf("  index  contents\n");
318 
319 	m = map_first(gpt);
320 	while (m != NULL) {
321 		printf(FMT, gpt->lbawidth, (uintmax_t)m->map_start);
322 		printf(FMT, gpt->lbawidth, (uintmax_t)m->map_size);
323 		putchar(' ');
324 		putchar(' ');
325 		if (m->map_index > 0) {
326 			printf("%5d  ", m->map_index);
327 			print_part_type(m->map_type, 0, m->map_data,
328 			    m->map_start);
329 			putchar('\n');
330 
331 			ent = m->map_data;
332 
333 			gpt_uuid_snprintf(s1, sizeof(s1), "%s", ent->ent_type);
334 			gpt_uuid_snprintf(s2, sizeof(s2), "%d", ent->ent_type);
335 			if (strcmp(s1, s2) == 0)
336 				strlcpy(s1, "unknown", sizeof(s1));
337 			printf(PFX "Type: %s\n", s1);
338 			if (m->map_type == MAP_TYPE_MBR_PART) {
339 				static uint8_t unused_uuid[sizeof(gpt_uuid_t)];
340 				/*
341 				 * MBR part partitions don't have
342 				 * GUIDs, so don't create a bogus one!
343 				 *
344 				 * We could get the TypeID from the
345 				 * partition type (the one byte OSType
346 				 * field in the partition structure),
347 				 * perhaps borrowing info from fdisk.
348 				 * However, some OSTypes have multiple
349 				 * OSes assigned to them and many may
350 				 * not have official UUIDs.
351 				 *
352 				 * Should we even print anything for
353 				 * these, in particular the GUID?
354 				 */
355 				gpt_uuid_snprintf(s2, sizeof(s2), "%d",
356 				    unused_uuid);
357 				printf(PFX "TypeID: %s\n", s2);	/* XXX: show this? */
358 				printf(PFX "GUID: %s\n", s2);	/* XXX: show this? */
359 			}
360 			else {
361 				printf(PFX "TypeID: %s\n", s2);
362 				gpt_uuid_snprintf(s2, sizeof(s2), "%d", ent->ent_guid);
363 				printf(PFX "GUID: %s\n", s2);
364 			}
365 
366 			printf(PFX "Size: ");
367 #ifdef HN_AUTOSCALE
368 			if (humanize_number(human_num, sizeof(human_num),
369 			    (int64_t)(m->map_size * gpt->secsz),
370 			    "", HN_AUTOSCALE, HN_B) < 0) {
371 #endif
372 				printf("%ju",
373 				    (int64_t)(m->map_size * gpt->secsz));
374 #ifdef HN_AUTOSCALE
375 			} else {
376 				printf("%s", human_num);
377 			}
378 #endif
379 			putchar('\n');
380 
381 			utf16_to_utf8(ent->ent_name,
382 			    __arraycount(ent->ent_name), utfbuf,
383 			    __arraycount(utfbuf));
384 			printf(PFX "Label: %s\n", (char *)utfbuf);
385 
386 			printf(PFX "Attributes: ");
387 			if (ent->ent_attr == 0) {
388 				printf("None\n");
389 			} else  {
390 				char buf[1024];
391 
392 				printf("%s\n", gpt_attr_list(buf, sizeof(buf),
393 				    ent->ent_attr));
394 			}
395 		} else {
396 			printf("       ");
397 			print_part_type(m->map_type, 0, m->map_data,
398 			    m->map_start);
399 			putchar('\n');
400 
401 			switch (m->map_type) {
402 			case MAP_TYPE_PRI_GPT_HDR:
403 			case MAP_TYPE_SEC_GPT_HDR:
404 				printf(PFX "GUID: %s\n",
405 				    get_gpt_hdr_guid(s1, sizeof(s1),
406 				    m->map_data));
407 				break;
408 			case MAP_TYPE_MBR:
409 				printf(PFX "GUID: %s\n",
410 				    get_mbr_sig(s1, sizeof(s1), m->map_data));
411 				break;
412 			default:
413 				break;
414 			}
415 		}
416 		m = m->map_next;
417 	}
418 	return 0;
419 }
420 
421 static int
422 cmd_show(gpt_t gpt, int argc, char *argv[])
423 {
424 	int ch;
425 	int xshow = 0;
426 	unsigned int entry = 0;
427 	off_t start = 0;
428 	map_t m;
429 
430 	while ((ch = getopt(argc, argv, "gi:b:luax")) != -1) {
431 		switch(ch) {
432 		case 'a':
433 			xshow |= SHOW_ALL;
434 			break;
435 		case 'g':
436 			xshow |= SHOW_GUID;
437 			break;
438 		case 'i':
439 			if (gpt_uint_get(gpt, &entry) == -1)
440 				return usage();
441 			break;
442 		case 'b':
443 			if (gpt_human_get(gpt, &start) == -1)
444 				return usage();
445 			break;
446 		case 'l':
447 			xshow |= SHOW_LABEL;
448 			break;
449 		case 'u':
450 			xshow |= SHOW_UUID;
451 			break;
452 		case 'x':
453 			xshow |= SHOW_HEX;
454 			break;
455 		default:
456 			return usage();
457 		}
458 	}
459 
460 	if (argc != optind)
461 		return usage();
462 
463 	if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) == NULL)
464 		printf("GPT not found, displaying data from MBR.\n\n");
465 
466 	if (xshow & SHOW_ALL)
467 		return show_all(gpt, xshow);
468 
469 	if (start > 0) {
470 		for (m = map_first(gpt); m != NULL; m = m->map_next) {
471 			if (m->map_type != MAP_TYPE_GPT_PART ||
472 			    m->map_index < 1)
473 				continue;
474 			if (start != m->map_start)
475 				continue;
476 			entry = m->map_index;
477 			break;
478 		}
479 	}
480 
481 	return entry > 0 ? show_one(gpt, entry) : show(gpt, xshow);
482 }
483