bootblock_iso9660.cc Source File

Back to the index.

bootblock_iso9660.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003-2018 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *
28  * ISO9660 CD-ROM "bootblock" handling.
29  *
30  * Despite the name, there is actually no bootblock; instead, the file which
31  * is to be booted is extracted into a temporary file, and started as if it
32  * was given as a normal file argument on the command line.
33  *
34  * TODO: This is really ugly. It's a quick hack. All the magic constants
35  * need to be replaced with real code!
36  *
37  * Instead of the current "could not find" message, it should really
38  * be more helpful, and print out the files found in the current
39  * directory, so that one can more easily choose the correct file.
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 
48 #include "cpu.h"
49 #include "diskimage.h"
50 #include "machine.h"
51 #include "misc.h"
52 
53 
54 /* #define ISO_DEBUG */
55 
56 
57 static void debug_print_volume_id_and_filename(int iso_type,
58  unsigned char *buf, char *filename)
59 {
60  /* Volume ID: */
61  char str[35];
62  int i, ofs = iso_type == 3? 48 : 40;
63 
64  memcpy(str, buf + ofs, sizeof(str));
65  str[32] = '\0'; i = 31;
66 
67  while (i >= 0 && str[i]==' ')
68  str[i--] = '\0';
69  if (str[0])
70  debug("\"%s\"", str);
71  else {
72  /* System ID: */
73  ofs = iso_type == 3? 16 : 8;
74  memcpy(str, buf + ofs, sizeof(str));
75  str[32] = '\0'; i = 31;
76  while (i >= 0 && str[i]==' ')
77  str[i--] = '\0';
78  if (str[0])
79  debug("\"%s\"", str);
80  else
81  debug("(no ID)");
82  }
83 
84  debug(":%s\n", filename);
85 }
86 
87 
88 /*
89  * iso_load_bootblock():
90  *
91  * Try to load a kernel from an ISO 9660 disk image. iso_type is 1 for
92  * "CD001" (standard), 2 for "CDW01" (ECMA), and 3 for "CDROM" (Sierra).
93  *
94  * TODO: This function uses too many magic offsets and so on; it should be
95  * cleaned up some day.
96  *
97  * Returns 1 on success, 0 on failure.
98  */
99 int iso_load_bootblock(struct machine *m, struct cpu *cpu,
100  int disk_id, int disk_type, int iso_type, unsigned char *buf,
101  int *n_loadp, char ***load_namesp)
102 {
103  int filenr, dirlen, res = 0, res2, iadd = DEBUG_INDENTATION, found_dir;
104  uint64_t dirofs, fileofs;
105  ssize_t filelen;
106  unsigned char *dirbuf = NULL, *dp, *match_entry = NULL, *filebuf = NULL;
107  char *p, *filename_orig, *filename, *tmpfname = NULL;
108  char **new_array;
109  const char *tmpdir = getenv("TMPDIR");
110  int tmpfile_handle;
111 
112  if (tmpdir == NULL)
113  tmpdir = DEFAULT_TMP_DIR;
114 
115  CHECK_ALLOCATION(filename = strdup(cpu->machine->boot_kernel_filename));
116  filename_orig = filename;
117 
118  debug("ISO9660 boot:\n");
119  debug_indentation(iadd);
120 
121  debug_print_volume_id_and_filename(iso_type, buf, filename);
122 
123 
124  /*
125  * Hack: If there is something like this at offset 0x9000:
126  *
127  * "MKI Wed Nov 29 17:56:55 2000\n"
128  * "mkisofs 1.13 -r -l -C 0,11702 /home/LC2000-CDR"
129  *
130  * and the disk offset is zero, then it can PROBABLY be assumed that
131  * offset 11702 is better. (This is counted in 2048 byte sectors.)
132  */
133  {
134  unsigned char tmpbuf[0x800];
135  uint64_t base_offset = diskimage_get_baseoffset(m, disk_id, disk_type);
136  res2 = diskimage_access(m, disk_id, disk_type, 0, base_offset + 0x9000, tmpbuf, 0x800);
137  if (!res2) {
138  fatal("Couldn't read MKI part after the ISO header of the disk image. Aborting.\n");
139  goto ret;
140  }
141 
142  int64_t suggestedBaseOffset = 0;
143  tmpbuf[sizeof(tmpbuf)-1] = '\0';
144  char* found = strstr((char*)tmpbuf, "MKI ");
145  if (found != NULL) {
146  found = strstr(found, " -C 0,");
147  if (found != NULL) {
148  suggestedBaseOffset = strtoll(found + 6, NULL, 0);
149  }
150  }
151 
152  int64_t currentBaseOffset = diskimage_get_baseoffset(m, disk_id, disk_type);
153  suggestedBaseOffset *= 2048;
154  if (currentBaseOffset == 0 && suggestedBaseOffset != currentBaseOffset)
155  {
156  debug("NOTE: automagically adjusting to use base offset %lli\n", (long long)suggestedBaseOffset);
157  diskimage_set_baseoffset(m, disk_id, disk_type, suggestedBaseOffset);
158  }
159  }
160 
161 
162  /*
163  * Traverse the directory structure to find the kernel.
164  */
165 
166  dirlen = buf[0x84] + 256*buf[0x85] + 65536*buf[0x86];
167  if (dirlen != buf[0x8b] + 256*buf[0x8a] + 65536*buf[0x89])
168  fatal("WARNING: Root directory length mismatch?\n");
169 
170  dirofs = (int64_t)(buf[0x8c] + (buf[0x8d] << 8) + (buf[0x8e] << 16) +
171  ((uint64_t)buf[0x8f] << 24)) * 2048;
172 
173 #ifdef ISO_DEBUG
174  debug("root = %i bytes at 0x%llx\n", dirlen, (long long)dirofs);
175 #endif
176 
177  CHECK_ALLOCATION(dirbuf = (unsigned char *) malloc(dirlen));
178  res2 = diskimage_access(m, disk_id, disk_type, 0, dirofs, dirbuf,
179  dirlen);
180  if (!res2) {
181  fatal("Couldn't read the disk image. Aborting.\n");
182  goto ret;
183  }
184 
185  found_dir = 1; /* Assume root dir */
186  dp = dirbuf; filenr = 1;
187  p = NULL;
188  while (dp < dirbuf + dirlen) {
189  size_t i, nlen = dp[0];
190  int x = dp[2] + (dp[3] << 8) + (dp[4] << 16) +
191  ((uint64_t)dp[5] << 24);
192  int y = dp[6] + (dp[7] << 8);
193  char direntry[65];
194 
195  dp += 8;
196 
197  /*
198  * As long as there is an \ or / in the filename, then we
199  * have not yet found the directory.
200  */
201  p = strchr(filename, '/');
202  if (p == NULL)
203  p = strchr(filename, '\\');
204 
205 #ifdef ISO_DEBUG
206  debug("%i%s: %i, %i, \"", filenr, filenr == found_dir?
207  " [CURRENT]" : "", x, y);
208 #endif
209  for (i=0; i<nlen && i<sizeof(direntry)-1; i++)
210  if (dp[i]) {
211  direntry[i] = dp[i];
212 #ifdef ISO_DEBUG
213  debug("%c", dp[i]);
214 #endif
215  } else
216  break;
217 #ifdef ISO_DEBUG
218  debug("\"\n");
219 #endif
220  direntry[i] = '\0';
221 
222  /* A directory name match? */
223  if ((p != NULL && strncasecmp(filename, direntry, nlen) == 0
224  && nlen == (size_t)p - (size_t)filename && found_dir == y)
225  || (p == NULL && direntry[0] == '\0') ) {
226  found_dir = filenr;
227  if (p != NULL)
228  filename = p+1;
229  dirofs = 2048 * (int64_t)x;
230  }
231 
232  dp += nlen;
233 
234  /* 16-bit aligned length: */
235  if (nlen & 1)
236  dp ++;
237 
238  filenr ++;
239  }
240 
241  p = strchr(filename, '/');
242  if (p == NULL)
243  p = strchr(filename, '\\');
244 
245  if (p != NULL) {
246  char *blah = filename_orig;
247 
248  fatal("could not find '%s' (1) in /", filename);
249 
250  /* Print the first part of the filename: */
251  while (blah != filename)
252  fatal("%c", *blah++);
253 
254  fatal("\n");
255  goto ret;
256  }
257 
258  /* debug("dirofs = 0x%llx\n", (long long)dirofs); */
259 
260  /* Free the old dirbuf, and allocate a new one: */
261  free(dirbuf);
262  CHECK_ALLOCATION(dirbuf = (unsigned char *) malloc(512));
263 
264  for (;;) {
265  size_t len, i;
266 
267  /* Too close to another sector? Then realign. */
268  if ((dirofs & 2047) + 70 > 2047) {
269  dirofs = (dirofs | 2047) + 1;
270  /* debug("realign dirofs = 0x%llx\n", dirofs); */
271  }
272 
273  // debug("dirofs = %lli\n", (long long)dirofs);
274  res2 = diskimage_access(m, disk_id, disk_type, 0, dirofs,
275  dirbuf, 256);
276  if (!res2) {
277  fatal("Couldn't read the disk image. Aborting.\n");
278  goto ret;
279  }
280 
281  dp = dirbuf;
282  len = dp[0];
283  if (len < 2) {
284  // debug("dir too short\n");
285  break;
286  }
287 
288  /*
289  * TODO: Actually parse the directory entry!
290  *
291  * Haha, this must be rewritten.
292  */
293 
294 #ifdef ISO_DEBUG
295 /* hahahaha */
296 printf("filename = '%s'\n", filename);
297 {
298 size_t j;
299 for (j=32; j<len; j++)
300  printf("%c", dp[j] >= ' ' && dp[j] < 128? dp[j] : '.');
301 printf("\n");
302 }
303 
304 // Hahaha. Yes, it's horrible. (Updated 2011-06-09.)
305 // Yes it is. (2018-06-18.) :-(
306 #endif
307 
308  for (i=32; i<len; i++) {
309  if (i < len - strlen(filename))
310  if (strncmp(filename, (char *)dp + i,
311  strlen(filename)) == 0) {
312  /* The filename was found somewhere
313  in the directory entry. */
314  if (match_entry != NULL) {
315  fatal("TODO: I'm too lazy to"
316  " implement a correct "
317  "directory parser right "
318  "now... (BUG)\n");
319  exit(1);
320  }
321  CHECK_ALLOCATION(match_entry = (unsigned char *)
322  malloc(512));
323  memcpy(match_entry, dp, 512);
324  break;
325  }
326  }
327 
328  if (match_entry != NULL)
329  break;
330 
331  dirofs += len;
332  }
333 
334  if (match_entry == NULL) {
335  char *blah = filename_orig;
336 
337  fatal("could not find '%s' (2) in /", filename);
338 
339  /* Print the first part of the filename: */
340  while (blah != filename)
341  fatal("%c", *blah++);
342 
343  fatal("\n");
344  goto ret;
345  }
346 
347  fileofs = match_entry[2] + (match_entry[3] << 8) +
348  (match_entry[4] << 16) + ((uint64_t)match_entry[5] << 24);
349  filelen = match_entry[10] + (match_entry[11] << 8) +
350  (match_entry[12] << 16) + ((uint64_t)match_entry[13] << 24);
351  fileofs *= 2048;
352 
353  /* debug("filelen=%llx fileofs=%llx\n", (long long)filelen,
354  (long long)fileofs); */
355 
356  CHECK_ALLOCATION(filebuf = (unsigned char *) malloc(filelen));
357 
358  CHECK_ALLOCATION(tmpfname = (char *) malloc(300));
359  snprintf(tmpfname, 300, "%s/gxemul.XXXXXXXXXXXX", tmpdir);
360 
361  res2 = diskimage_access(m, disk_id, disk_type, 0, fileofs, filebuf,
362  filelen);
363  if (!res2) {
364  fatal("could not read the file from the disk image!\n");
365  goto ret;
366  }
367 
368  tmpfile_handle = mkstemp(tmpfname);
369  if (tmpfile_handle < 0) {
370  fatal("could not create %s\n", tmpfname);
371  goto ret;
372  }
373 
374  if (write(tmpfile_handle, filebuf, filelen) != filelen) {
375  fatal("could not write to %s\n", tmpfname);
376  perror("write");
377  goto ret;
378  }
379 
380  close(tmpfile_handle);
381 
382  debug("extracted %lli bytes into %s\n", (long long)filelen, tmpfname);
383 
384  /* Add the temporary filename to the load_namesp array: */
385  (*n_loadp)++;
386  CHECK_ALLOCATION(new_array = (char **) malloc(sizeof(char *) * (*n_loadp)));
387  memcpy(new_array, *load_namesp, sizeof(char *) * (*n_loadp));
388  *load_namesp = new_array;
389 
390  /* This adds a Backspace char in front of the filename; this
391  is a special hack which causes the file to be removed once
392  it has been loaded. */
393  CHECK_ALLOCATION(tmpfname = (char *) realloc(tmpfname, strlen(tmpfname) + 2));
394  memmove(tmpfname + 1, tmpfname, strlen(tmpfname) + 1);
395  tmpfname[0] = 8;
396 
397  (*load_namesp)[*n_loadp - 1] = strdup(tmpfname);
398 
399  res = 1;
400 
401 ret:
402  if (dirbuf != NULL)
403  free(dirbuf);
404 
405  if (filebuf != NULL)
406  free(filebuf);
407 
408  if (match_entry != NULL)
409  free(match_entry);
410 
411  if (tmpfname != NULL)
412  free(tmpfname);
413 
414  free(filename_orig);
415 
416  debug_indentation(-iadd);
417  return res;
418 }
419 
diskimage.h
debug
#define debug
Definition: dev_adb.cc:57
iso_load_bootblock
int iso_load_bootblock(struct machine *m, struct cpu *cpu, int disk_id, int disk_type, int iso_type, unsigned char *buf, int *n_loadp, char ***load_namesp)
Definition: bootblock_iso9660.cc:99
diskimage_access
int diskimage_access(struct machine *machine, int id, int type, int writeflag, off_t offset, unsigned char *buf, size_t len)
Definition: diskimage.cc:617
strlen
void COMBINE() strlen(struct cpu *cpu, struct arm_instr_call *ic, int low_addr)
Definition: cpu_arm_instr.cc:2686
fatal
void fatal(const char *fmt,...)
Definition: main.cc:152
diskimage_get_baseoffset
int64_t diskimage_get_baseoffset(struct machine *machine, int id, int type)
Definition: diskimage.cc:222
misc.h
machine.h
machine
Definition: machine.h:97
cpu.h
cpu::machine
struct machine * machine
Definition: cpu.h:328
machine::boot_kernel_filename
char * boot_kernel_filename
Definition: machine.h:170
diskimage_set_baseoffset
void diskimage_set_baseoffset(struct machine *machine, int id, int type, int64_t offset)
Definition: diskimage.cc:242
DEBUG_INDENTATION
#define DEBUG_INDENTATION
Definition: misc.h:212
DEFAULT_TMP_DIR
#define DEFAULT_TMP_DIR
Definition: misc.h:148
cpu
Definition: cpu.h:326
debug_indentation
void debug_indentation(int diff)
Definition: main.cc:120
CHECK_ALLOCATION
#define CHECK_ALLOCATION(ptr)
Definition: misc.h:239

Generated on Tue Aug 25 2020 19:25:06 for GXemul by doxygen 1.8.18