11590Srgrimes/*-
21590Srgrimes * Copyright (c) 2018 iXsystems, Inc.
31590Srgrimes * All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes *
141590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241590Srgrimes * SUCH DAMAGE.
251590Srgrimes */
261590Srgrimes
271590Srgrimes#include <sys/cdefs.h>
281590Srgrimes#include <err.h>
291590Srgrimes#include <getopt.h>
3087675Smarkm#include <libgen.h>
3187675Smarkm#include <stdbool.h>
3287675Smarkm#include <stdio.h>
3387675Smarkm
341590Srgrimes#include "cd9660.h"
3528695Scharnier#include "cd9660_eltorito.h"
361590Srgrimes
371590Srgrimes#include "etdump.h"
3887675Smarkm
391590Srgrimesconst char *
401590Srgrimessystem_id_string(u_char system_id)
4187675Smarkm{
4228695Scharnier
431590Srgrimes	switch (system_id) {
441590Srgrimes	case ET_SYS_X86:
451590Srgrimes		return ("i386");
461590Srgrimes	case ET_SYS_PPC:
471590Srgrimes		return ("powerpc");
481590Srgrimes	case ET_SYS_MAC:
491590Srgrimes		return ("mac");
501590Srgrimes	case ET_SYS_EFI:
511590Srgrimes		return ("efi");
521590Srgrimes	default:
5328695Scharnier		return ("invalid");
5428695Scharnier	}
5573255Simp}
5625777Sache
571590Srgrimesconst char *
581590Srgrimesmedia_type_string(u_char media_type)
591590Srgrimes{
601590Srgrimes
611590Srgrimes	switch (media_type) {
6229434Sache	case ET_MEDIA_NOEM:
631590Srgrimes		return ("no emulation");
64202200Sed	case ET_MEDIA_12FDD:
65233269Sglebius		return ("1.2MB FDD");
66233269Sglebius	case ET_MEDIA_144FDD:
671590Srgrimes		return ("1.44MB FDD");
6883242Sdd	case ET_MEDIA_288FDD:
6983242Sdd		return ("2.88MB FDD");
7073255Simp	case ET_MEDIA_HDD:
7173255Simp		return ("HDD");
721590Srgrimes	default:
73227200Sed		return ("invalid");
7473255Simp	}
7573255Simp}
7673255Simp
7773255Simpstatic int
78227200Sedread_sector(FILE *iso, daddr_t sector, char *buffer)
79227200Sed{
80227200Sed
811590Srgrimes	if (fseek(iso, sector * ISO_DEFAULT_BLOCK_SIZE, SEEK_SET) != 0) {
82155875Scognet		return (errno);
83200156Sed	}
84155875Scognet	if (fread(buffer, ISO_DEFAULT_BLOCK_SIZE, 1, iso) != 1) {
85155875Scognet		return (errno);
86155875Scognet	}
87155875Scognet	return (0);
88200156Sed}
89155875Scognet
90155875Scognetstatic bool
91155875Scognetboot_catalog_valid(char *entry)
92155875Scognet{
93155875Scognet	boot_catalog_validation_entry *ve;
94155875Scognet	int16_t		checksum, sum;
951590Srgrimes	unsigned char	*csptr;
9673255Simp	size_t		i;
971590Srgrimes
981590Srgrimes	ve = (boot_catalog_validation_entry *)entry;
99200156Sed
10073255Simp	checksum = isonum_721(ve->checksum);
10183082Sru	cd9660_721(0, ve->checksum);
10273255Simp	csptr = (unsigned char *)ve;
10373255Simp
10483242Sdd	for (i = sum = 0; i < sizeof(*ve); i += 2) {
10583242Sdd		sum += (int16_t)csptr[i];
10673255Simp		sum += 256 * (int16_t)csptr[i + 1];
1071590Srgrimes	}
10825777Sache	if (sum + checksum != 0) {
10925777Sache		return (false);
11073255Simp	}
1111590Srgrimes
1121590Srgrimes	cd9660_721(checksum, ve->checksum);
1131590Srgrimes	return (true);
1141590Srgrimes}
1151590Srgrimes
1161590Srgrimesstatic int
11773255Simpdump_section(char *buffer, size_t bufsize, size_t offset, FILE *outfile,
11873255Simp    const char *filename, struct outputter *outputter)
11973255Simp{
12073255Simp	boot_catalog_section_header *sh;
12173255Simp	u_char platform_id;
12273255Simp	int i;
12373255Simp	size_t entry_offset;
1241590Srgrimes	boot_catalog_section_entry *entry;
1251590Srgrimes
12628695Scharnier	if (offset + sizeof(boot_catalog_section_header) > bufsize)
1271590Srgrimes		errx(1, "%s: section header out of bounds", filename);
1281590Srgrimes	sh = (boot_catalog_section_header *)&buffer[offset];
1291590Srgrimes	if (outputter->output_section != NULL) {
1301590Srgrimes		outputter->output_section(outfile, filename, sh);
13128695Scharnier	}
1321590Srgrimes
13373255Simp	platform_id = sh->platform_id[0];
13473255Simp
13573320Simp	if (outputter->output_entry != NULL) {
13673255Simp		for (i = 1; i <= (int)sh->num_section_entries[0]; i++) {
13773320Simp			entry_offset = offset + i * ET_BOOT_ENTRY_SIZE;
13873320Simp			if (entry_offset + sizeof(boot_catalog_section_entry) >
13973255Simp			    bufsize)
14073255Simp				errx(1, "%s: section entry out of bounds",
1411590Srgrimes				    filename);
1421590Srgrimes			entry =
1431590Srgrimes			    (boot_catalog_section_entry *)&buffer[entry_offset];
1441590Srgrimes			outputter->output_entry(outfile, filename, entry,
1451590Srgrimes			    platform_id, false);
146200156Sed		}
147200156Sed	}
1481590Srgrimes
149200156Sed	return (1 + (int)sh->num_section_entries[0]);
150155875Scognet}
15173255Simp
15283082Srustatic void
153200156Seddump_eltorito(FILE *iso, const char *filename, FILE *outfile,
15473255Simp    struct outputter *outputter)
15573255Simp{
15673255Simp	char buffer[ISO_DEFAULT_BLOCK_SIZE], *entry;
15787675Smarkm	boot_volume_descriptor *bvd;
15873255Simp	daddr_t boot_catalog;
15973255Simp	size_t offset;
16073255Simp	int entry_count;
16183082Sru
16283082Sru	if (read_sector(iso, 17, buffer) != 0)
163200156Sed		err(1, "failed to read from image");
16483082Sru
16583082Sru	bvd = (boot_volume_descriptor *)buffer;
16683082Sru	if (memcmp(bvd->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5) != 0)
16783082Sru		warnx("%s: not a valid ISO", filename);
16883082Sru	if (bvd->boot_record_indicator[0] != ISO_VOLUME_DESCRIPTOR_BOOT ||
16973255Simp	    memcmp(bvd->boot_system_identifier, ET_ID, 23) != 0)
17073255Simp		warnx("%s: not an El Torito bootable ISO", filename);
17173255Simp
17273255Simp	boot_catalog = isonum_731(bvd->boot_catalog_pointer);
173200156Sed
17428695Scharnier	if (read_sector(iso, boot_catalog, buffer) != 0)
1751590Srgrimes		err(1, "failed to read from image");
1761590Srgrimes
1771590Srgrimes	entry = buffer;
1781590Srgrimes	offset = 0;
17928695Scharnier
180201224Sed	if (!boot_catalog_valid(entry))
18128695Scharnier		warnx("%s: boot catalog checksum is invalid", filename);
18273320Simp
18328695Scharnier	if (outputter->output_image != NULL)
18428695Scharnier		outputter->output_image(outfile, filename, bvd);
18528695Scharnier
1861590Srgrimes	offset += ET_BOOT_ENTRY_SIZE;
18773255Simp	entry = &buffer[offset];
1881590Srgrimes	if (outputter->output_entry != NULL)
18973255Simp		outputter->output_entry(outfile, filename,
190233269Sglebius		    (boot_catalog_section_entry *)entry, 0, true);
1911590Srgrimes
1921590Srgrimes	offset += ET_BOOT_ENTRY_SIZE;
1931590Srgrimes
19429434Sache	while (offset < ISO_DEFAULT_BLOCK_SIZE) {
1951590Srgrimes		entry = &buffer[offset];
1961590Srgrimes
197233269Sglebius		if ((uint8_t)entry[0] != ET_SECTION_HEADER_MORE &&
198233269Sglebius		    (uint8_t)entry[0] != ET_SECTION_HEADER_LAST)
19987675Smarkm			break;
20069231Skris
20176367Skris		entry_count = dump_section(buffer, sizeof(buffer), offset,
2021590Srgrimes		    outfile, filename, outputter);
20369231Skris
20469231Skris		offset += entry_count * ET_BOOT_ENTRY_SIZE;
20569231Skris	}
2061590Srgrimes}
2071590Srgrimes
2081590Srgrimesstatic void
20969231Skrisusage(const char *progname)
21069231Skris{
21166557Sn_hibma	char *path;
21266557Sn_hibma
2131590Srgrimes	path = strdup(progname);
2141590Srgrimes
2151590Srgrimes	fprintf(stderr, "usage: %s [-f format] [-o filename] filename [...]\n",
2161590Srgrimes	    basename(path));
2171590Srgrimes	fprintf(stderr, "\tsupported output formats: shell, text\n");
2181590Srgrimes	exit(1);
2191590Srgrimes}
2201590Srgrimes
2211590Srgrimesint
2221590Srgrimesmain(int argc, char **argv)
2231590Srgrimes{
2241590Srgrimes	int ch, i;
2251590Srgrimes	FILE *outfile, *iso;
226233269Sglebius	struct outputter *outputter;
227233269Sglebius
228233269Sglebius	outfile = stdout;
2291590Srgrimes	outputter = output_text;
230233269Sglebius
231233269Sglebius	static struct option longopts[] = {
232233269Sglebius		{ "format",	required_argument,	NULL,	'f' },
23363909Sasmodai		{ "output",	required_argument,	NULL,	'o' },
234233269Sglebius		{ NULL,		0,			NULL,	0 },
2351590Srgrimes	};
236233269Sglebius
2371590Srgrimes	while ((ch = getopt_long(argc, argv, "f:o:", longopts, NULL)) != -1) {
23876367Skris		switch (ch) {
23976367Skris		case 'f':
24076367Skris			if (strcmp(optarg, "shell") == 0)
241233269Sglebius				outputter = output_shell;
24276367Skris			else if (strcmp(optarg, "text") == 0)
243241848Seadler				outputter = output_text;
244241848Seadler			else
24576367Skris				usage(argv[0]);
246223940Sobrien			break;
247233269Sglebius		case 'o':
248233269Sglebius			if (strcmp(optarg, "-") == 0) {
249233269Sglebius				outfile = stdout;
250233269Sglebius			} else if ((outfile = fopen(optarg, "w")) == NULL) {
25150776Sdbaker				err(1, "unable to open %s for output", optarg);
252175346Sdas			}
253233269Sglebius			break;
2541590Srgrimes		default:
255233269Sglebius			usage(argv[0]);
256233269Sglebius		}
257233269Sglebius	}
258175346Sdas
259175346Sdas	argc -= optind;
260175346Sdas	argv += optind;
261233269Sglebius
262233269Sglebius	for (i = 0; i < argc; i++) {
2631590Srgrimes		if (strcmp(argv[i], "-") == 0) {
264175346Sdas			iso = stdin;
265233269Sglebius		} else {
266233269Sglebius			iso = fopen(argv[i], "r");
267233269Sglebius			if (iso == NULL)
268233269Sglebius				err(1, "could not open %s", argv[i]);
269233269Sglebius		}
270233269Sglebius		dump_eltorito(iso, argv[i], outfile, outputter);
27129434Sache	}
272233269Sglebius}
273233269Sglebius