xref: /freebsd/usr.bin/etdump/etdump.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1 /*-
2  * Copyright (c) 2018 iXsystems, Inc.
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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 #include <err.h>
29 #include <getopt.h>
30 #include <libgen.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 
34 #include "cd9660.h"
35 #include "cd9660_eltorito.h"
36 
37 #include "etdump.h"
38 
39 const char *
40 system_id_string(u_char system_id)
41 {
42 
43 	switch (system_id) {
44 	case ET_SYS_X86:
45 		return ("i386");
46 	case ET_SYS_PPC:
47 		return ("powerpc");
48 	case ET_SYS_MAC:
49 		return ("mac");
50 	case ET_SYS_EFI:
51 		return ("efi");
52 	default:
53 		return ("invalid");
54 	}
55 }
56 
57 const char *
58 media_type_string(u_char media_type)
59 {
60 
61 	switch (media_type) {
62 	case ET_MEDIA_NOEM:
63 		return ("no emulation");
64 	case ET_MEDIA_12FDD:
65 		return ("1.2MB FDD");
66 	case ET_MEDIA_144FDD:
67 		return ("1.44MB FDD");
68 	case ET_MEDIA_288FDD:
69 		return ("2.88MB FDD");
70 	case ET_MEDIA_HDD:
71 		return ("HDD");
72 	default:
73 		return ("invalid");
74 	}
75 }
76 
77 static int
78 read_sector(FILE *iso, daddr_t sector, char *buffer)
79 {
80 
81 	if (fseek(iso, sector * ISO_DEFAULT_BLOCK_SIZE, SEEK_SET) != 0) {
82 		return (errno);
83 	}
84 	if (fread(buffer, ISO_DEFAULT_BLOCK_SIZE, 1, iso) != 1) {
85 		return (errno);
86 	}
87 	return (0);
88 }
89 
90 static bool
91 boot_catalog_valid(char *entry)
92 {
93 	boot_catalog_validation_entry *ve;
94 	int16_t		checksum, sum;
95 	unsigned char	*csptr;
96 	size_t		i;
97 
98 	ve = (boot_catalog_validation_entry *)entry;
99 
100 	checksum = isonum_721(ve->checksum);
101 	cd9660_721(0, ve->checksum);
102 	csptr = (unsigned char *)ve;
103 
104 	for (i = sum = 0; i < sizeof(*ve); i += 2) {
105 		sum += (int16_t)csptr[i];
106 		sum += 256 * (int16_t)csptr[i + 1];
107 	}
108 	if (sum + checksum != 0) {
109 		return (false);
110 	}
111 
112 	cd9660_721(checksum, ve->checksum);
113 	return (true);
114 }
115 
116 static int
117 dump_section(char *buffer, size_t bufsize, size_t offset, FILE *outfile,
118     const char *filename, struct outputter *outputter)
119 {
120 	boot_catalog_section_header *sh;
121 	u_char platform_id;
122 	int i;
123 	size_t entry_offset;
124 	boot_catalog_section_entry *entry;
125 
126 	if (offset + sizeof(boot_catalog_section_header) > bufsize)
127 		errx(1, "%s: section header out of bounds", filename);
128 	sh = (boot_catalog_section_header *)&buffer[offset];
129 	if (outputter->output_section != NULL) {
130 		outputter->output_section(outfile, filename, sh);
131 	}
132 
133 	platform_id = sh->platform_id[0];
134 
135 	if (outputter->output_entry != NULL) {
136 		for (i = 1; i <= (int)sh->num_section_entries[0]; i++) {
137 			entry_offset = offset + i * ET_BOOT_ENTRY_SIZE;
138 			if (entry_offset + sizeof(boot_catalog_section_entry) >
139 			    bufsize)
140 				errx(1, "%s: section entry out of bounds",
141 				    filename);
142 			entry =
143 			    (boot_catalog_section_entry *)&buffer[entry_offset];
144 			outputter->output_entry(outfile, filename, entry,
145 			    platform_id, false);
146 		}
147 	}
148 
149 	return (1 + (int)sh->num_section_entries[0]);
150 }
151 
152 static void
153 dump_eltorito(FILE *iso, const char *filename, FILE *outfile,
154     struct outputter *outputter)
155 {
156 	char buffer[ISO_DEFAULT_BLOCK_SIZE], *entry;
157 	boot_volume_descriptor *bvd;
158 	daddr_t boot_catalog;
159 	size_t offset;
160 	int entry_count;
161 
162 	if (read_sector(iso, 17, buffer) != 0)
163 		err(1, "failed to read from image");
164 
165 	bvd = (boot_volume_descriptor *)buffer;
166 	if (memcmp(bvd->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5) != 0)
167 		warnx("%s: not a valid ISO", filename);
168 	if (bvd->boot_record_indicator[0] != ISO_VOLUME_DESCRIPTOR_BOOT ||
169 	    memcmp(bvd->boot_system_identifier, ET_ID, 23) != 0)
170 		warnx("%s: not an El Torito bootable ISO", filename);
171 
172 	boot_catalog = isonum_731(bvd->boot_catalog_pointer);
173 
174 	if (read_sector(iso, boot_catalog, buffer) != 0)
175 		err(1, "failed to read from image");
176 
177 	entry = buffer;
178 	offset = 0;
179 
180 	if (!boot_catalog_valid(entry))
181 		warnx("%s: boot catalog checksum is invalid", filename);
182 
183 	if (outputter->output_image != NULL)
184 		outputter->output_image(outfile, filename, bvd);
185 
186 	offset += ET_BOOT_ENTRY_SIZE;
187 	entry = &buffer[offset];
188 	if (outputter->output_entry != NULL)
189 		outputter->output_entry(outfile, filename,
190 		    (boot_catalog_section_entry *)entry, 0, true);
191 
192 	offset += ET_BOOT_ENTRY_SIZE;
193 
194 	while (offset < ISO_DEFAULT_BLOCK_SIZE) {
195 		entry = &buffer[offset];
196 
197 		if ((uint8_t)entry[0] != ET_SECTION_HEADER_MORE &&
198 		    (uint8_t)entry[0] != ET_SECTION_HEADER_LAST)
199 			break;
200 
201 		entry_count = dump_section(buffer, sizeof(buffer), offset,
202 		    outfile, filename, outputter);
203 
204 		offset += entry_count * ET_BOOT_ENTRY_SIZE;
205 	}
206 }
207 
208 static void
209 usage(const char *progname)
210 {
211 	char *path;
212 
213 	path = strdup(progname);
214 
215 	fprintf(stderr, "usage: %s [-f format] [-o filename] filename [...]\n",
216 	    basename(path));
217 	fprintf(stderr, "\tsupported output formats: shell, text\n");
218 	exit(1);
219 }
220 
221 int
222 main(int argc, char **argv)
223 {
224 	int ch, i;
225 	FILE *outfile, *iso;
226 	struct outputter *outputter;
227 
228 	outfile = stdout;
229 	outputter = output_text;
230 
231 	static struct option longopts[] = {
232 		{ "format",	required_argument,	NULL,	'f' },
233 		{ "output",	required_argument,	NULL,	'o' },
234 		{ NULL,		0,			NULL,	0 },
235 	};
236 
237 	while ((ch = getopt_long(argc, argv, "f:o:", longopts, NULL)) != -1) {
238 		switch (ch) {
239 		case 'f':
240 			if (strcmp(optarg, "shell") == 0)
241 				outputter = output_shell;
242 			else if (strcmp(optarg, "text") == 0)
243 				outputter = output_text;
244 			else
245 				usage(argv[0]);
246 			break;
247 		case 'o':
248 			if (strcmp(optarg, "-") == 0) {
249 				outfile = stdout;
250 			} else if ((outfile = fopen(optarg, "w")) == NULL) {
251 				err(1, "unable to open %s for output", optarg);
252 			}
253 			break;
254 		default:
255 			usage(argv[0]);
256 		}
257 	}
258 
259 	argc -= optind;
260 	argv += optind;
261 
262 	for (i = 0; i < argc; i++) {
263 		if (strcmp(argv[i], "-") == 0) {
264 			iso = stdin;
265 		} else {
266 			iso = fopen(argv[i], "r");
267 			if (iso == NULL)
268 				err(1, "could not open %s", argv[i]);
269 		}
270 		dump_eltorito(iso, argv[i], outfile, outputter);
271 	}
272 }
273