2 * isohybrid.c: Post process an ISO 9660 image generated with mkisofs or
3 * genisoimage to allow - hybrid booting - as a CD-ROM or as a hard
6 * This is based on the original Perl script written by H. Peter Anvin. The
7 * rewrite in C is to avoid dependency on Perl on a system under installation.
9 * Copyright (C) 2010 P J P <pj.pandit@yahoo.co.in>
11 * isohybrid is a free software; you can redistribute it and/or modify it
12 * under the terms of GNU General Public License as published by Free Software
13 * Foundation; either version 2 of the license, or (at your option) any later
16 * isohybrid is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 * You should have received a copy of the GNU General Public License along
22 * with isohybrid; if not, see: <http://www.gnu.org/licenses>.
40 #include "isohybrid.h"
43 extern int opterr
, optind
;
49 uint16_t head
= 64; /* 1 <= head <= 256 */
50 uint8_t sector
= 32; /* 1 <= sector <= 63 */
52 uint8_t entry
= 1; /* partition number: 1 <= entry <= 4 */
53 uint8_t offset
= 0; /* partition offset: 0 <= offset <= 64 */
54 uint16_t type
= 0x17; /* partition type: 0 <= type <= 255 */
55 uint32_t id
= 0; /* MBR: 0 <= id <= 0xFFFFFFFF(4294967296) */
57 uint8_t hd0
= 0; /* 0 <= hd0 <= 2 */
58 uint8_t partok
= 0; /* 0 <= partok <= 1 */
61 uint32_t catoffset
= 0;
62 uint32_t c
= 0, cc
= 0, cs
= 0;
64 /* boot catalogue parameters */
66 uint16_t de_seg
= 0, de_count
= 0, de_mbz2
= 0;
67 uint8_t de_boot
= 0, de_media
= 0, de_sys
= 0, de_mbz1
= 0;
73 printf("Usage: %s [OPTIONS] <boot.iso>\n", prog
);
80 #define FMT "%-18s %s\n"
86 printf(FMT
, " -h <X>", "Number of default geometry heads");
87 printf(FMT
, " -s <X>", "Number of default geometry sectors");
88 printf(FMT
, " -e --entry", "Specify partition entry number (1-4)");
89 printf(FMT
, " -o --offset", "Specify partition offset (default 0)");
90 printf(FMT
, " -t --type", "Specify partition type (default 0x17)");
91 printf(FMT
, " -i --id", "Specify MBR ID (default random)");
94 printf(FMT
, " --forcehd0", "Assume we are loaded as disk ID 0");
95 printf(FMT
, " --ctrlhd0", "Assume disk ID 0 if the Ctrl key is pressed");
96 printf(FMT
, " --partok", "Allow booting from within a partition");
99 printf(FMT
, " -? --help", "Display this help");
100 printf(FMT
, " -v --verbose", "Display verbose output");
101 printf(FMT
, " -V --version", "Display version information");
104 printf("Report bugs to <pj.pandit@yahoo.co.in>\n");
109 check_option(int argc
, char *argv
[])
113 const char optstr
[] = ":h:s:e:o:t:i:fcp?vV";
114 struct option lopt
[] = \
116 { "entry", required_argument
, NULL
, 'e' },
117 { "offset", required_argument
, NULL
, 'o' },
118 { "type", required_argument
, NULL
, 't' },
119 { "id", required_argument
, NULL
, 'i' },
121 { "forcehd0", no_argument
, NULL
, 'f' },
122 { "ctrlhd0", no_argument
, NULL
, 'c' },
123 { "partok", no_argument
, NULL
, 'p'},
125 { "help", no_argument
, NULL
, '?' },
126 { "verbose", no_argument
, NULL
, 'v' },
127 { "version", no_argument
, NULL
, 'V' },
133 while ((n
= getopt_long_only(argc
, argv
, optstr
, lopt
, &ind
)) != -1)
138 if (!sscanf(optarg
, "%hu", &head
) || head
< 1 || head
> 256)
139 errx(1, "invalid head: `%s', 1 <= head <= 256", optarg
);
143 if (!sscanf(optarg
, "%hhu", §or
) || sector
< 1 || sector
> 63)
144 errx(1, "invalid sector: `%s', 1 <= sector <= 63", optarg
);
148 if (!sscanf(optarg
, "%hhu", &entry
) || entry
< 1 || entry
> 4)
149 errx(1, "invalid entry: `%s', 1 <= entry <= 4", optarg
);
153 if (!sscanf(optarg
, "%hhu", &offset
) || offset
> 64)
154 errx(1, "invalid offset: `%s', 0 <= offset <= 64", optarg
);
158 if (!sscanf(optarg
, "%hu", &type
) || type
> 255)
159 errx(1, "invalid type: `%s', 0 <= type <= 255", optarg
);
163 if (!sscanf(optarg
, "%u", &id
))
164 errx(1, "invalid id: `%s'", optarg
);
184 printf("%s version %s\n", prog
, VERSION
);
188 errx(1, "option `-%c' takes an argument", optopt
);
193 errx(1, "invalid option `-%c', see --help", optopt
);
205 lendian_short(const uint16_t s
)
212 r
= (s
& 0x00FF) << 8 | (s
& 0xFF00) >> 8;
219 lendian_int(const uint32_t s
)
226 r
= (s
& 0x000000FF) << 24 | (s
& 0xFF000000) >> 24
227 | (s
& 0x0000FF00) << 8 | (s
& 0x00FF0000) >> 8;
234 check_banner(const uint8_t *buf
)
236 static const char banner
[] = "\0CD001\1EL TORITO SPECIFICATION\0\0\0\0" \
237 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
240 if (!buf
|| memcmp(buf
, banner
, sizeof(banner
) - 1))
243 buf
+= sizeof(banner
) - 1;
244 memcpy(&catoffset
, buf
, sizeof(catoffset
));
246 catoffset
= lendian_int(catoffset
);
253 check_catalogue(const uint8_t *buf
)
257 for (i
= 0, cs
= 0; i
< 16; i
++)
260 memcpy(&ve
[i
], buf
, sizeof(ve
[i
]));
262 ve
[i
] = lendian_short(ve
[i
]);
268 printf("ve[%d]: %d, cs: %d\n", i
, ve
[i
], cs
);
270 if ((ve
[0] != 0x0001) || (ve
[15] != 0xAA55) || (cs
& 0xFFFF))
278 read_catalogue(const uint8_t *buf
)
280 memcpy(&de_boot
, buf
++, 1);
281 memcpy(&de_media
, buf
++, 1);
283 memcpy(&de_seg
, buf
, 2);
284 de_seg
= lendian_short(de_seg
);
287 memcpy(&de_sys
, buf
++, 1);
288 memcpy(&de_mbz1
, buf
++, 1);
290 memcpy(&de_count
, buf
, 2);
291 de_count
= lendian_short(de_count
);
294 memcpy(&de_lba
, buf
, 4);
295 de_lba
= lendian_int(de_lba
);
298 memcpy(&de_mbz2
, buf
, 2);
299 de_mbz2
= lendian_short(de_mbz2
);
302 if (de_boot
!= 0x88 || de_media
!= 0
303 || (de_seg
!= 0 && de_seg
!= 0x7C0) || de_count
!= 4)
311 display_catalogue(void)
313 printf("de_boot: %hhu\n", de_boot
);
314 printf("de_media: %hhu\n", de_media
);
315 printf("de_seg: %hu\n", de_seg
);
316 printf("de_sys: %hhu\n", de_sys
);
317 printf("de_mbz1: %hhu\n", de_mbz1
);
318 printf("de_count: %hu\n", de_count
);
319 printf("de_lba: %u\n", de_lba
);
320 printf("de_mbz2: %hu\n", de_mbz2
);
325 initialise_mbr(uint8_t *mbr
)
328 uint32_t psize
= 0, tmp
= 0;
329 uint8_t ptype
= 0, *rbm
= mbr
;
330 uint8_t bhead
= 0, bsect
= 0, bcyle
= 0;
331 uint8_t ehead
= 0, esect
= 0, ecyle
= 0;
333 extern unsigned char isohdpfx
[][MBRSIZE
];
335 memcpy(mbr
, &isohdpfx
[hd0
+ 3 * partok
], MBRSIZE
);
336 mbr
+= MBRSIZE
; /* offset 432 */
338 tmp
= lendian_int(de_lba
* 4);
339 memcpy(mbr
, &tmp
, sizeof(tmp
));
340 mbr
+= sizeof(tmp
); /* offset 436 */
343 memcpy(mbr
, &tmp
, sizeof(tmp
));
344 mbr
+= sizeof(tmp
); /* offset 440 */
346 tmp
= lendian_int(id
);
347 memcpy(mbr
, &tmp
, sizeof(tmp
));
348 mbr
+= sizeof(tmp
); /* offset 444 */
352 mbr
+= 2; /* offset 446 */
355 psize
= c
* head
* sector
- offset
;
357 bhead
= (offset
/ sector
) % head
;
358 bsect
= (offset
% sector
) + 1;
359 bcyle
= offset
/ (head
* sector
);
361 bsect
+= (bcyle
& 0x300) >> 2;
365 esect
= sector
+ (((cc
- 1) & 0x300) >> 2);
366 ecyle
= (cc
- 1) & 0xFF;
368 for (i
= 1; i
<= 4; i
++)
382 tmp
= lendian_int(offset
);
383 memcpy(&mbr
[8], &tmp
, sizeof(tmp
));
385 tmp
= lendian_int(psize
);
386 memcpy(&mbr
[12], &tmp
, sizeof(tmp
));
399 display_mbr(const uint8_t *mbr
, size_t len
)
402 unsigned int i
= 0, j
= 0;
404 printf("sizeof(MBR): %zu bytes\n", len
);
405 for (i
= 0; i
< len
; i
++)
420 printf("%c", isprint(mbr
[j
]) ? mbr
[j
] : '.');
428 main(int argc
, char *argv
[])
433 uint8_t *buf
= NULL
, *bufz
= NULL
;
434 int cylsize
= 0, frac
= 0, padding
= 0;
436 prog
= strcpy(alloca(strlen(argv
[0]) + 1), argv
[0]);
437 i
= check_option(argc
, argv
);
446 srand(time(NULL
) << (getppid() << getpid()));
448 if (!(fp
= fopen(argv
[0], "r+")))
449 err(1, "could not open file `%s'", argv
[0]);
450 if (fseek(fp
, 17 * 2048, SEEK_SET
))
451 err(1, "%s: seek error - 1", argv
[0]);
453 bufz
= buf
= calloc(BUFSIZE
, sizeof(char));
454 if (fread(buf
, sizeof(char), BUFSIZE
, fp
) != BUFSIZE
)
455 err(1, "%s", argv
[0]);
457 if (check_banner(buf
))
458 errx(1, "%s: could not find boot record", argv
[0]);
461 printf("catalogue offset: %d\n", catoffset
);
463 if (fseek(fp
, catoffset
* 2048, SEEK_SET
))
464 err(1, "%s: seek error - 2", argv
[0]);
467 memset(buf
, 0, BUFSIZE
);
468 if (fread(buf
, sizeof(char), BUFSIZE
, fp
) != BUFSIZE
)
469 err(1, "%s", argv
[0]);
471 if (check_catalogue(buf
))
472 errx(1, "%s: invalid boot catalogue", argv
[0]);
475 if (read_catalogue(buf
))
476 errx(1, "%s: unexpected boot catalogue parameters", argv
[0]);
481 if (fseek(fp
, (de_lba
* 2048 + 0x40), SEEK_SET
))
482 err(1, "%s: seek error - 3", argv
[0]);
485 memset(buf
, 0, BUFSIZE
);
486 if (fread(buf
, sizeof(char), 4, fp
) != 4)
487 err(1, "%s", argv
[0]);
489 if (memcmp(buf
, "\xFB\xC0\x78\x70", 4))
490 errx(1, "%s: boot loader does not have an isolinux.bin hybrid " \
491 "signature. Note that isolinux-debug.bin does not support " \
492 "hybrid booting", argv
[0]);
494 if (stat(argv
[0], &isostat
))
495 err(1, "%s", argv
[0]);
497 cylsize
= head
* sector
* 512;
498 frac
= isostat
.st_size
% cylsize
;
499 padding
= (frac
> 0) ? cylsize
- frac
: 0;
502 printf("imgsize: %zu, padding: %d\n", (size_t)isostat
.st_size
, padding
);
504 cc
= c
= (isostat
.st_size
+ padding
) / cylsize
;
507 warnx("Warning: more than 1024 cylinders: %d", c
);
508 warnx("Not all BIOSes will be able to boot this device");
514 if (fseek(fp
, 440, SEEK_SET
))
515 err(1, "%s: seek error - 4", argv
[0]);
517 if (fread(&id
, 1, 4, fp
) != 4)
518 err(1, "%s: read error", argv
[0]);
520 id
= lendian_int(id
);
529 printf("id: %u\n", id
);
532 memset(buf
, 0, BUFSIZE
);
533 i
= initialise_mbr(buf
);
538 if (fseek(fp
, 0, SEEK_SET
))
539 err(1, "%s: seek error - 5", argv
[0]);
541 if (fwrite(buf
, sizeof(char), i
, fp
) != (size_t)i
)
542 err(1, "%s: write error - 1", argv
[0]);
546 if (fsync(fileno(fp
)))
547 err(1, "%s: could not synchronise", argv
[0]);
549 if (ftruncate(fileno(fp
), isostat
.st_size
+ padding
))
550 err(1, "%s: could not add padding bytes", argv
[0]);