xref: /freebsd-src/usr.bin/etdump/etdump.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1158d2fcdSBenno Rice /*-
2158d2fcdSBenno Rice  * Copyright (c) 2018 iXsystems, Inc.
3158d2fcdSBenno Rice  * All rights reserved.
4158d2fcdSBenno Rice  *
5158d2fcdSBenno Rice  * Redistribution and use in source and binary forms, with or without
6158d2fcdSBenno Rice  * modification, are permitted provided that the following conditions
7158d2fcdSBenno Rice  * are met:
8158d2fcdSBenno Rice  * 1. Redistributions of source code must retain the above copyright
9158d2fcdSBenno Rice  *    notice, this list of conditions and the following disclaimer.
10158d2fcdSBenno Rice  * 2. Redistributions in binary form must reproduce the above copyright
11158d2fcdSBenno Rice  *    notice, this list of conditions and the following disclaimer in the
12158d2fcdSBenno Rice  *    documentation and/or other materials provided with the distribution.
13158d2fcdSBenno Rice  *
14158d2fcdSBenno Rice  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15158d2fcdSBenno Rice  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16158d2fcdSBenno Rice  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17158d2fcdSBenno Rice  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18158d2fcdSBenno Rice  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19158d2fcdSBenno Rice  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20158d2fcdSBenno Rice  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21158d2fcdSBenno Rice  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22158d2fcdSBenno Rice  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23158d2fcdSBenno Rice  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24158d2fcdSBenno Rice  * SUCH DAMAGE.
25158d2fcdSBenno Rice  */
26158d2fcdSBenno Rice 
27158d2fcdSBenno Rice #include <sys/cdefs.h>
28158d2fcdSBenno Rice #include <err.h>
29158d2fcdSBenno Rice #include <getopt.h>
30158d2fcdSBenno Rice #include <libgen.h>
31158d2fcdSBenno Rice #include <stdbool.h>
32158d2fcdSBenno Rice #include <stdio.h>
33158d2fcdSBenno Rice 
34158d2fcdSBenno Rice #include "cd9660.h"
35158d2fcdSBenno Rice #include "cd9660_eltorito.h"
36158d2fcdSBenno Rice 
37158d2fcdSBenno Rice #include "etdump.h"
38158d2fcdSBenno Rice 
39158d2fcdSBenno Rice const char *
system_id_string(u_char system_id)40158d2fcdSBenno Rice system_id_string(u_char system_id)
41158d2fcdSBenno Rice {
42158d2fcdSBenno Rice 
43158d2fcdSBenno Rice 	switch (system_id) {
44158d2fcdSBenno Rice 	case ET_SYS_X86:
45158d2fcdSBenno Rice 		return ("i386");
46158d2fcdSBenno Rice 	case ET_SYS_PPC:
47158d2fcdSBenno Rice 		return ("powerpc");
48158d2fcdSBenno Rice 	case ET_SYS_MAC:
49158d2fcdSBenno Rice 		return ("mac");
50158d2fcdSBenno Rice 	case ET_SYS_EFI:
51158d2fcdSBenno Rice 		return ("efi");
52158d2fcdSBenno Rice 	default:
53158d2fcdSBenno Rice 		return ("invalid");
54158d2fcdSBenno Rice 	}
55158d2fcdSBenno Rice }
56158d2fcdSBenno Rice 
57158d2fcdSBenno Rice const char *
media_type_string(u_char media_type)58158d2fcdSBenno Rice media_type_string(u_char media_type)
59158d2fcdSBenno Rice {
60158d2fcdSBenno Rice 
61158d2fcdSBenno Rice 	switch (media_type) {
62158d2fcdSBenno Rice 	case ET_MEDIA_NOEM:
63158d2fcdSBenno Rice 		return ("no emulation");
64158d2fcdSBenno Rice 	case ET_MEDIA_12FDD:
65158d2fcdSBenno Rice 		return ("1.2MB FDD");
66158d2fcdSBenno Rice 	case ET_MEDIA_144FDD:
67158d2fcdSBenno Rice 		return ("1.44MB FDD");
68158d2fcdSBenno Rice 	case ET_MEDIA_288FDD:
69158d2fcdSBenno Rice 		return ("2.88MB FDD");
70158d2fcdSBenno Rice 	case ET_MEDIA_HDD:
71158d2fcdSBenno Rice 		return ("HDD");
72158d2fcdSBenno Rice 	default:
73158d2fcdSBenno Rice 		return ("invalid");
74158d2fcdSBenno Rice 	}
75158d2fcdSBenno Rice }
76158d2fcdSBenno Rice 
77158d2fcdSBenno Rice static int
read_sector(FILE * iso,daddr_t sector,char * buffer)78158d2fcdSBenno Rice read_sector(FILE *iso, daddr_t sector, char *buffer)
79158d2fcdSBenno Rice {
80158d2fcdSBenno Rice 
81695fed77SBenno Rice 	if (fseek(iso, sector * ISO_DEFAULT_BLOCK_SIZE, SEEK_SET) != 0) {
82695fed77SBenno Rice 		return (errno);
83695fed77SBenno Rice 	}
84158d2fcdSBenno Rice 	if (fread(buffer, ISO_DEFAULT_BLOCK_SIZE, 1, iso) != 1) {
85158d2fcdSBenno Rice 		return (errno);
86158d2fcdSBenno Rice 	}
87158d2fcdSBenno Rice 	return (0);
88158d2fcdSBenno Rice }
89158d2fcdSBenno Rice 
90158d2fcdSBenno Rice static bool
boot_catalog_valid(char * entry)91158d2fcdSBenno Rice boot_catalog_valid(char *entry)
92158d2fcdSBenno Rice {
93158d2fcdSBenno Rice 	boot_catalog_validation_entry *ve;
94158d2fcdSBenno Rice 	int16_t		checksum, sum;
95158d2fcdSBenno Rice 	unsigned char	*csptr;
96158d2fcdSBenno Rice 	size_t		i;
97158d2fcdSBenno Rice 
98158d2fcdSBenno Rice 	ve = (boot_catalog_validation_entry *)entry;
99158d2fcdSBenno Rice 
100158d2fcdSBenno Rice 	checksum = isonum_721(ve->checksum);
101158d2fcdSBenno Rice 	cd9660_721(0, ve->checksum);
102158d2fcdSBenno Rice 	csptr = (unsigned char *)ve;
103158d2fcdSBenno Rice 
104158d2fcdSBenno Rice 	for (i = sum = 0; i < sizeof(*ve); i += 2) {
105158d2fcdSBenno Rice 		sum += (int16_t)csptr[i];
106158d2fcdSBenno Rice 		sum += 256 * (int16_t)csptr[i + 1];
107158d2fcdSBenno Rice 	}
108158d2fcdSBenno Rice 	if (sum + checksum != 0) {
109158d2fcdSBenno Rice 		return (false);
110158d2fcdSBenno Rice 	}
111158d2fcdSBenno Rice 
112158d2fcdSBenno Rice 	cd9660_721(checksum, ve->checksum);
113158d2fcdSBenno Rice 	return (true);
114158d2fcdSBenno Rice }
115158d2fcdSBenno Rice 
116158d2fcdSBenno Rice static int
dump_section(char * buffer,size_t bufsize,size_t offset,FILE * outfile,const char * filename,struct outputter * outputter)117ddf77ec3SEd Maste dump_section(char *buffer, size_t bufsize, size_t offset, FILE *outfile,
118ddf77ec3SEd Maste     const char *filename, struct outputter *outputter)
119158d2fcdSBenno Rice {
120158d2fcdSBenno Rice 	boot_catalog_section_header *sh;
121158d2fcdSBenno Rice 	u_char platform_id;
122158d2fcdSBenno Rice 	int i;
123158d2fcdSBenno Rice 	size_t entry_offset;
124158d2fcdSBenno Rice 	boot_catalog_section_entry *entry;
125158d2fcdSBenno Rice 
126ddf77ec3SEd Maste 	if (offset + sizeof(boot_catalog_section_header) > bufsize)
127ddf77ec3SEd Maste 		errx(1, "%s: section header out of bounds", filename);
128158d2fcdSBenno Rice 	sh = (boot_catalog_section_header *)&buffer[offset];
129158d2fcdSBenno Rice 	if (outputter->output_section != NULL) {
130158d2fcdSBenno Rice 		outputter->output_section(outfile, filename, sh);
131158d2fcdSBenno Rice 	}
132158d2fcdSBenno Rice 
133158d2fcdSBenno Rice 	platform_id = sh->platform_id[0];
134158d2fcdSBenno Rice 
135158d2fcdSBenno Rice 	if (outputter->output_entry != NULL) {
136158d2fcdSBenno Rice 		for (i = 1; i <= (int)sh->num_section_entries[0]; i++) {
137158d2fcdSBenno Rice 			entry_offset = offset + i * ET_BOOT_ENTRY_SIZE;
138ddf77ec3SEd Maste 			if (entry_offset + sizeof(boot_catalog_section_entry) >
139ddf77ec3SEd Maste 			    bufsize)
140ddf77ec3SEd Maste 				errx(1, "%s: section entry out of bounds",
141ddf77ec3SEd Maste 				    filename);
142158d2fcdSBenno Rice 			entry =
143158d2fcdSBenno Rice 			    (boot_catalog_section_entry *)&buffer[entry_offset];
144158d2fcdSBenno Rice 			outputter->output_entry(outfile, filename, entry,
145158d2fcdSBenno Rice 			    platform_id, false);
146158d2fcdSBenno Rice 		}
147158d2fcdSBenno Rice 	}
148158d2fcdSBenno Rice 
149158d2fcdSBenno Rice 	return (1 + (int)sh->num_section_entries[0]);
150158d2fcdSBenno Rice }
151158d2fcdSBenno Rice 
152158d2fcdSBenno Rice static void
dump_eltorito(FILE * iso,const char * filename,FILE * outfile,struct outputter * outputter)153158d2fcdSBenno Rice dump_eltorito(FILE *iso, const char *filename, FILE *outfile,
154158d2fcdSBenno Rice     struct outputter *outputter)
155158d2fcdSBenno Rice {
156158d2fcdSBenno Rice 	char buffer[ISO_DEFAULT_BLOCK_SIZE], *entry;
157158d2fcdSBenno Rice 	boot_volume_descriptor *bvd;
158158d2fcdSBenno Rice 	daddr_t boot_catalog;
159158d2fcdSBenno Rice 	size_t offset;
160158d2fcdSBenno Rice 	int entry_count;
161158d2fcdSBenno Rice 
162158d2fcdSBenno Rice 	if (read_sector(iso, 17, buffer) != 0)
163158d2fcdSBenno Rice 		err(1, "failed to read from image");
164158d2fcdSBenno Rice 
165158d2fcdSBenno Rice 	bvd = (boot_volume_descriptor *)buffer;
166158d2fcdSBenno Rice 	if (memcmp(bvd->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5) != 0)
167158d2fcdSBenno Rice 		warnx("%s: not a valid ISO", filename);
168*bfe6a0afSEd Maste 	if (bvd->boot_record_indicator[0] != ISO_VOLUME_DESCRIPTOR_BOOT ||
169*bfe6a0afSEd Maste 	    memcmp(bvd->boot_system_identifier, ET_ID, 23) != 0)
170158d2fcdSBenno Rice 		warnx("%s: not an El Torito bootable ISO", filename);
171158d2fcdSBenno Rice 
172158d2fcdSBenno Rice 	boot_catalog = isonum_731(bvd->boot_catalog_pointer);
173158d2fcdSBenno Rice 
174158d2fcdSBenno Rice 	if (read_sector(iso, boot_catalog, buffer) != 0)
175158d2fcdSBenno Rice 		err(1, "failed to read from image");
176158d2fcdSBenno Rice 
177158d2fcdSBenno Rice 	entry = buffer;
178158d2fcdSBenno Rice 	offset = 0;
179158d2fcdSBenno Rice 
180158d2fcdSBenno Rice 	if (!boot_catalog_valid(entry))
181158d2fcdSBenno Rice 		warnx("%s: boot catalog checksum is invalid", filename);
182158d2fcdSBenno Rice 
183158d2fcdSBenno Rice 	if (outputter->output_image != NULL)
184158d2fcdSBenno Rice 		outputter->output_image(outfile, filename, bvd);
185158d2fcdSBenno Rice 
186158d2fcdSBenno Rice 	offset += ET_BOOT_ENTRY_SIZE;
187158d2fcdSBenno Rice 	entry = &buffer[offset];
188158d2fcdSBenno Rice 	if (outputter->output_entry != NULL)
189158d2fcdSBenno Rice 		outputter->output_entry(outfile, filename,
190158d2fcdSBenno Rice 		    (boot_catalog_section_entry *)entry, 0, true);
191158d2fcdSBenno Rice 
192158d2fcdSBenno Rice 	offset += ET_BOOT_ENTRY_SIZE;
193158d2fcdSBenno Rice 
194158d2fcdSBenno Rice 	while (offset < ISO_DEFAULT_BLOCK_SIZE) {
195158d2fcdSBenno Rice 		entry = &buffer[offset];
196158d2fcdSBenno Rice 
197158d2fcdSBenno Rice 		if ((uint8_t)entry[0] != ET_SECTION_HEADER_MORE &&
198158d2fcdSBenno Rice 		    (uint8_t)entry[0] != ET_SECTION_HEADER_LAST)
199158d2fcdSBenno Rice 			break;
200158d2fcdSBenno Rice 
201ddf77ec3SEd Maste 		entry_count = dump_section(buffer, sizeof(buffer), offset,
202ddf77ec3SEd Maste 		    outfile, filename, outputter);
203158d2fcdSBenno Rice 
204158d2fcdSBenno Rice 		offset += entry_count * ET_BOOT_ENTRY_SIZE;
205158d2fcdSBenno Rice 	}
206158d2fcdSBenno Rice }
207158d2fcdSBenno Rice 
208158d2fcdSBenno Rice static void
usage(const char * progname)209158d2fcdSBenno Rice usage(const char *progname)
210158d2fcdSBenno Rice {
211158d2fcdSBenno Rice 	char *path;
212158d2fcdSBenno Rice 
213158d2fcdSBenno Rice 	path = strdup(progname);
214158d2fcdSBenno Rice 
215158d2fcdSBenno Rice 	fprintf(stderr, "usage: %s [-f format] [-o filename] filename [...]\n",
216158d2fcdSBenno Rice 	    basename(path));
217158d2fcdSBenno Rice 	fprintf(stderr, "\tsupported output formats: shell, text\n");
218158d2fcdSBenno Rice 	exit(1);
219158d2fcdSBenno Rice }
220158d2fcdSBenno Rice 
221158d2fcdSBenno Rice int
main(int argc,char ** argv)222158d2fcdSBenno Rice main(int argc, char **argv)
223158d2fcdSBenno Rice {
224158d2fcdSBenno Rice 	int ch, i;
225158d2fcdSBenno Rice 	FILE *outfile, *iso;
226158d2fcdSBenno Rice 	struct outputter *outputter;
227158d2fcdSBenno Rice 
228158d2fcdSBenno Rice 	outfile = stdout;
229158d2fcdSBenno Rice 	outputter = output_text;
230158d2fcdSBenno Rice 
231158d2fcdSBenno Rice 	static struct option longopts[] = {
232158d2fcdSBenno Rice 		{ "format",	required_argument,	NULL,	'f' },
233158d2fcdSBenno Rice 		{ "output",	required_argument,	NULL,	'o' },
234158d2fcdSBenno Rice 		{ NULL,		0,			NULL,	0 },
235158d2fcdSBenno Rice 	};
236158d2fcdSBenno Rice 
237158d2fcdSBenno Rice 	while ((ch = getopt_long(argc, argv, "f:o:", longopts, NULL)) != -1) {
238158d2fcdSBenno Rice 		switch (ch) {
239158d2fcdSBenno Rice 		case 'f':
240158d2fcdSBenno Rice 			if (strcmp(optarg, "shell") == 0)
241158d2fcdSBenno Rice 				outputter = output_shell;
242158d2fcdSBenno Rice 			else if (strcmp(optarg, "text") == 0)
243158d2fcdSBenno Rice 				outputter = output_text;
244158d2fcdSBenno Rice 			else
245158d2fcdSBenno Rice 				usage(argv[0]);
246158d2fcdSBenno Rice 			break;
247158d2fcdSBenno Rice 		case 'o':
248158d2fcdSBenno Rice 			if (strcmp(optarg, "-") == 0) {
249158d2fcdSBenno Rice 				outfile = stdout;
250158d2fcdSBenno Rice 			} else if ((outfile = fopen(optarg, "w")) == NULL) {
251158d2fcdSBenno Rice 				err(1, "unable to open %s for output", optarg);
252158d2fcdSBenno Rice 			}
253158d2fcdSBenno Rice 			break;
254158d2fcdSBenno Rice 		default:
255158d2fcdSBenno Rice 			usage(argv[0]);
256158d2fcdSBenno Rice 		}
257158d2fcdSBenno Rice 	}
258158d2fcdSBenno Rice 
259158d2fcdSBenno Rice 	argc -= optind;
260158d2fcdSBenno Rice 	argv += optind;
261158d2fcdSBenno Rice 
262158d2fcdSBenno Rice 	for (i = 0; i < argc; i++) {
263158d2fcdSBenno Rice 		if (strcmp(argv[i], "-") == 0) {
264158d2fcdSBenno Rice 			iso = stdin;
265158d2fcdSBenno Rice 		} else {
266158d2fcdSBenno Rice 			iso = fopen(argv[i], "r");
267158d2fcdSBenno Rice 			if (iso == NULL)
268340cebe9SJessica Clarke 				err(1, "could not open %s", argv[i]);
269158d2fcdSBenno Rice 		}
270158d2fcdSBenno Rice 		dump_eltorito(iso, argv[i], outfile, outputter);
271158d2fcdSBenno Rice 	}
272158d2fcdSBenno Rice }
273