xref: /reactos/sdk/tools/isohybrid/isohybrid.c (revision 84ccccab)
1 /*
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
4  * disk.
5  *
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.
8  *
9  * Copyright (C) 2010 P J P <pj.pandit@yahoo.co.in>
10  *
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
14  * version.
15  *
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
19  * more details.
20  *
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>.
23  *
24  */
25 
26 #define _FILE_OFFSET_BITS 64
27 //#include <err.h>
28 #include <time.h>
29 #include <ctype.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 //#include <alloca.h>
33 //#include <getopt.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <string.h>
37 //#include <unistd.h>
38 #include <sys/stat.h>
39 //#include <inttypes.h>
40 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
41 #include <uuid/uuid.h>
42 #endif
43 
44 #include "isohybrid.h"
45 #include "reactos_support_code.h"
46 
47 char *prog = NULL;
48 extern int opterr, optind;
49 struct stat isostat;
50 unsigned int padding = 0;
51 
52 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
53 uuid_t disk_uuid, part_uuid, iso_uuid;
54 #endif
55 
56 uint8_t mode = 0;
57 enum { VERBOSE = 1 , EFI = 2 , MAC = 4};
58 
59 /* user options */
60 uint16_t head = 64;             /* 1 <= head <= 256 */
61 uint8_t sector = 32;            /* 1 <= sector <= 63  */
62 
63 uint8_t entry = 0;              /* partition number: 1 <= entry <= 4 */
64 uint8_t offset = 0;             /* partition offset: 0 <= offset <= 64 */
65 uint16_t type = 0x17;           /* partition type: 0 <= type <= 255 */
66 uint32_t id = 0;                /* MBR: 0 <= id <= 0xFFFFFFFF(4294967296) */
67 
68 uint8_t hd0 = 0;                /* 0 <= hd0 <= 2 */
69 uint8_t partok = 0;             /* 0 <= partok <= 1 */
70 
71 char mbr_template_path[1024] = {0};   /* Path to MBR template */
72 
73 uint16_t ve[16];
74 uint32_t catoffset = 0;
75 uint32_t c = 0, cc = 0, cs = 0;
76 
77 uint32_t psize = 0, isosize = 0;
78 
79 /* boot catalogue parameters */
80 uint32_t de_lba = 0;
81 uint16_t de_seg = 0, de_count = 0, de_mbz2 = 0;
82 uint8_t de_boot = 0, de_media = 0, de_sys = 0, de_mbz1 = 0;
83 uint32_t efi_lba = 0, mac_lba = 0;
84 uint16_t efi_count = 0, mac_count = 0;
85 uint8_t efi_boot = 0, efi_media = 0, efi_sys = 0;
86 
87 int apm_parts = 3;
88 
89 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
90 uint8_t afp_header[] = { 0x45, 0x52, 0x08, 0x00, 0x00, 0x00, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
91 
92 uuid_t efi_system_partition = {0xC1, 0x2A, 0x73, 0x28, 0xF8, 0x1F, 0x11, 0xD2, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B};
93 uuid_t basic_partition = {0xEB,0xD0,0xA0,0xA2,0xB9,0xE5,0x44,0x33,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7};
94 uuid_t hfs_partition = {0x48, 0x46, 0x53, 0x00, 0x00, 0x00, 0x11, 0xAA, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC};
95 #endif
96 
97 uint32_t crc_tab[256] =
98 {
99     0, 0x77073096, 0xEE0E612C, 0x990951BA,
100     0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
101     0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
102     0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
103     0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
104     0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
105     0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
106     0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
107     0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
108     0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
109     0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
110     0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
111     0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
112     0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
113     0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
114     0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
115     0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
116     0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
117     0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
118     0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
119     0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
120     0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
121     0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
122     0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
123     0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
124     0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
125     0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
126     0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
127     0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
128     0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
129     0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
130     0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
131     0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
132     0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
133     0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
134     0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
135     0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
136     0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
137     0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
138     0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
139     0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
140     0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
141     0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
142     0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
143     0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
144     0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
145     0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
146     0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
147     0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
148     0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
149     0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
150     0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
151     0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
152     0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
153     0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
154     0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
155     0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
156     0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
157     0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
158     0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
159     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
160     0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
161     0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
162     0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
163 };
164 
165 struct iso_primary_descriptor {
166     uint8_t ignore [80];
167     uint32_t size;
168     uint8_t ignore2 [44];
169     uint16_t block_size;
170 };
171 
172 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
173 struct gpt_header {
174     uint64_t signature;
175     uint32_t revision;
176     uint32_t headerSize;
177     uint32_t headerCRC;
178     uint32_t reserved;
179     uint64_t currentLBA;
180     uint64_t backupLBA;
181     uint64_t firstUsableLBA;
182     uint64_t lastUsableLBA;
183     uuid_t diskGUID;
184     uint64_t partitionEntriesLBA;
185     uint32_t numParts;
186     uint32_t sizeOfPartitionEntries;
187     uint32_t partitionEntriesCRC;
188     uint8_t reserved2[420];
189 };
190 
191 struct gpt_part_header {
192     uuid_t partTypeGUID;
193     uuid_t partGUID;
194     uint64_t firstLBA;
195     uint64_t lastLBA;
196     uint64_t attributes;
197     uint16_t name[36];
198 };
199 
200 #define APM_OFFSET 2048
201 
202 struct apple_part_header {
203     uint16_t        signature;      /* expected to be MAC_PARTITION_MAGIC */
204     uint16_t        res1;
205     uint32_t        map_count;      /* # blocks in partition map */
206     uint32_t        start_block;    /* absolute starting block # of partition */
207     uint32_t        block_count;    /* number of blocks in partition */
208     char            name[32];       /* partition name */
209     char            type[32];       /* string type description */
210     uint32_t        data_start;     /* rel block # of first data block */
211     uint32_t        data_count;     /* number of data blocks */
212     uint32_t        status;         /* partition status bits */
213     uint32_t        boot_start;
214     uint32_t        boot_count;
215     uint32_t        boot_load;
216     uint32_t        boot_load2;
217     uint32_t        boot_entry;
218     uint32_t        boot_entry2;
219     uint32_t        boot_cksum;
220     char            processor[16];  /* Contains 680x0, x=0,2,3,4; or empty */
221     uint32_t        driver_sig;
222     char            _padding[372];
223 };
224 #endif
225 
226 
227 void
228 usage(void)
229 {
230     printf("Usage: %s [OPTIONS] <boot.iso>\n", prog);
231 }
232 
233 
234 void
235 printh(void)
236 {
237 #define FMT "%-20s %s\n"
238 
239     usage();
240 
241     printf("\n");
242     printf("Options:\n");
243     printf(FMT, "   -h <X>", "Number of geometry heads (default 64)");
244     printf(FMT, "   -s <X>", "Number of geometry sectors (default 32)");
245     printf(FMT, "   -e --entry", "Specify partition entry number (1-4)");
246     printf(FMT, "   -o --offset", "Specify partition offset (default 0)");
247     printf(FMT, "   -t --type", "Specify partition type (default 0x17)");
248     printf(FMT, "   -i --id", "Specify MBR ID (default random)");
249 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
250     printf(FMT, "   -u --uefi", "Build EFI bootable image");
251     printf(FMT, "   -m --mac", "Add AFP table support");
252 #endif
253     printf(FMT, "   -b --mbr <PATH>", "Load MBR from PATH");
254 
255     printf("\n");
256     printf(FMT, "   --forcehd0", "Assume we are loaded as disk ID 0");
257     printf(FMT, "   --ctrlhd0", "Assume disk ID 0 if the Ctrl key is pressed");
258     printf(FMT, "   --partok", "Allow booting from within a partition");
259 
260     printf("\n");
261     printf(FMT, "   -? --help", "Display this help");
262     printf(FMT, "   -v --verbose", "Display verbose output");
263     printf(FMT, "   -V --version", "Display version information");
264 
265     printf("\n");
266     printf("Report bugs to <pj.pandit@yahoo.co.in>\n");
267 }
268 
269 
270 int
271 check_option(int argc, char *argv[])
272 {
273     char *err = NULL;
274     int n = 0, ind = 0;
275 
276     const char optstr[] = ":h:s:e:o:t:i:b:umfcp?vV";
277     struct option lopt[] = \
278     {
279         { "entry", required_argument, NULL, 'e' },
280         { "offset", required_argument, NULL, 'o' },
281         { "type", required_argument, NULL, 't' },
282         { "id", required_argument, NULL, 'i' },
283 
284         { "forcehd0", no_argument, NULL, 'f' },
285         { "ctrlhd0", no_argument, NULL, 'c' },
286         { "partok", no_argument, NULL, 'p'},
287 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
288 	{ "uefi", no_argument, NULL, 'u'},
289 	{ "mac", no_argument, NULL, 'm'},
290 #endif
291         { "mbr", required_argument, NULL, 'b' },
292 
293         { "help", no_argument, NULL, '?' },
294         { "verbose", no_argument, NULL, 'v' },
295         { "version", no_argument, NULL, 'V' },
296 
297         { 0, 0, 0, 0 }
298     };
299 
300     opterr = mode = 0;
301     while ((n = getopt_long_only(argc, argv, optstr, lopt, &ind)) != -1)
302     {
303         switch (n)
304         {
305         case 'h':
306             head = strtoul(optarg, &err, 0);
307             if (head < 1 || head > 256)
308                 errx(1, "invalid head: `%s', 1 <= head <= 256", optarg);
309             break;
310 
311         case 's':
312             sector = strtoul(optarg, &err, 0);
313             if (sector < 1 || sector > 63)
314                 errx(1, "invalid sector: `%s', 1 <= sector <= 63", optarg);
315             break;
316 
317         case 'e':
318             entry = strtoul(optarg, &err, 0);
319             if (entry < 1 || entry > 4)
320                 errx(1, "invalid entry: `%s', 1 <= entry <= 4", optarg);
321 	    if (mode & MAC || mode & EFI)
322 		errx(1, "setting an entry is unsupported with EFI or Mac");
323             break;
324 
325         case 'o':
326             offset = strtoul(optarg, &err, 0);
327             if (*err || offset > 64)
328                 errx(1, "invalid offset: `%s', 0 <= offset <= 64", optarg);
329             break;
330 
331         case 't':
332             type = strtoul(optarg, &err, 0);
333             if (*err || type > 255)
334                 errx(1, "invalid type: `%s', 0 <= type <= 255", optarg);
335             break;
336 
337         case 'i':
338             id = strtoul(optarg, &err, 0);
339             if (*err)
340                 errx(1, "invalid id: `%s'", optarg);
341             break;
342 
343         case 'f':
344             hd0 = 1;
345             break;
346 
347         case 'c':
348             hd0 = 2;
349             break;
350 
351         case 'p':
352             partok = 1;
353             break;
354 
355 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
356 	case 'u':
357 	    mode |= EFI;
358 	    if (entry)
359 		errx(1, "setting an entry is unsupported with EFI or Mac");
360 	    break;
361 
362 	case 'm':
363 	    mode |= MAC;
364 	    if (entry)
365 		errx(1, "setting an entry is unsupported with EFI or Mac");
366 	    break;
367 #endif
368 
369 	case 'b':
370             if (strlen(optarg) >= sizeof(mbr_template_path))
371                 errx(1, "--mbr : Path too long");
372             strcpy(mbr_template_path, optarg);
373             break;
374 
375         case 'v':
376             mode |= VERBOSE;
377             break;
378 
379         case 'V':
380             printf("%s version %s\n", prog, VERSION);
381             exit(0);
382 
383         case ':':
384             errx(1, "option `-%c' takes an argument", optopt);
385 
386         default:
387         case '?':
388             if (optopt)
389                 errx(1, "invalid option `-%c', see --help", optopt);
390 
391             printh();
392             exit(0);
393         }
394     }
395 
396     return optind;
397 }
398 
399 uint16_t
400 bendian_short(const uint16_t s)
401 {
402     uint16_t r = 1;
403 
404     if (!*(uint8_t *)&r)
405         return s;
406 
407     r = (s & 0x00FF) << 8 | (s & 0xFF00) >> 8;
408 
409     return r;
410 }
411 
412 
413 uint32_t
414 bendian_int(const uint32_t s)
415 {
416     uint32_t r = 1;
417 
418     if (!*(uint8_t *)&r)
419         return s;
420 
421     r = (s & 0x000000FF) << 24 | (s & 0xFF000000) >> 24
422         | (s & 0x0000FF00) << 8 | (s & 0x00FF0000) >> 8;
423 
424     return r;
425 }
426 
427 uint16_t
428 lendian_short(const uint16_t s)
429 {
430     uint16_t r = 1;
431 
432     if (*(uint8_t *)&r)
433         return s;
434 
435     r = (s & 0x00FF) << 8 | (s & 0xFF00) >> 8;
436 
437     return r;
438 }
439 
440 
441 uint32_t
442 lendian_int(const uint32_t s)
443 {
444     uint32_t r = 1;
445 
446     if (*(uint8_t *)&r)
447         return s;
448 
449     r = (s & 0x000000FF) << 24 | (s & 0xFF000000) >> 24
450         | (s & 0x0000FF00) << 8 | (s & 0x00FF0000) >> 8;
451 
452     return r;
453 }
454 
455 uint64_t
456 lendian_64(const uint64_t s)
457 {
458 	uint64_t r = 1;
459 
460 	if (*(uint8_t *)&r)
461 		return s;
462 
463        r = (s & 0x00000000000000FFull) << 56 | (s & 0xFF00000000000000ull) >> 56
464             | (s & 0x000000000000FF00ull) << 40 | (s & 0x00FF000000000000ull) >> 40
465             | (s & 0x0000000000FF0000ull) << 24 | (s & 0x0000FF0000000000ull) >> 24
466             | (s & 0x00000000FF000000ull) << 8 | (s & 0x000000FF00000000ull) >> 8;
467 
468 	return r;
469 }
470 
471 
472 int
473 check_banner(const uint8_t *buf)
474 {
475     static const char banner[] = "\0CD001\1EL TORITO SPECIFICATION\0\0\0\0" \
476         "\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" \
477         "\0\0\0\0\0";
478 
479     if (!buf || memcmp(buf, banner, sizeof(banner) - 1))
480         return 1;
481 
482     buf += sizeof(banner) - 1;
483     memcpy(&catoffset, buf, sizeof(catoffset));
484 
485     catoffset = lendian_int(catoffset);
486 
487     return 0;
488 }
489 
490 
491 int
492 check_catalogue(const uint8_t *buf)
493 {
494     int i = 0;
495 
496     for (i = 0, cs = 0; i < 16; i++)
497     {
498         ve[i] = 0;
499         memcpy(&ve[i], buf, sizeof(ve[i]));
500 
501         ve[i] = lendian_short(ve[i]);
502 
503         buf += 2;
504         cs += ve[i];
505 
506         if (mode & VERBOSE)
507             printf("ve[%d]: %d, cs: %d\n", i, ve[i], cs);
508     }
509     if ((ve[0] != 0x0001) || (ve[15] != 0xAA55) || (cs & 0xFFFF))
510         return 1;
511 
512     return 0;
513 }
514 
515 
516 int
517 read_catalogue(const uint8_t *buf)
518 {
519     memcpy(&de_boot, buf++, 1);
520     memcpy(&de_media, buf++, 1);
521 
522     memcpy(&de_seg, buf, 2);
523     de_seg = lendian_short(de_seg);
524     buf += 2;
525 
526     memcpy(&de_sys, buf++, 1);
527     memcpy(&de_mbz1, buf++, 1);
528 
529     memcpy(&de_count, buf, 2);
530     de_count = lendian_short(de_count);
531     buf += 2;
532 
533     memcpy(&de_lba, buf, 4);
534     de_lba = lendian_int(de_lba);
535     buf += 4;
536 
537     memcpy(&de_mbz2, buf, 2);
538     de_mbz2 = lendian_short(de_mbz2);
539     buf += 2;
540 
541     if (de_boot != 0x88 || de_media != 0
542         || (de_seg != 0 && de_seg != 0x7C0) || de_count != 4)
543         return 1;
544 
545     return 0;
546 }
547 
548 
549 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
550 int
551 read_efi_section(const uint8_t *buf)
552 {
553 	unsigned char header_indicator;
554 	unsigned char platform_id;
555 	short count;
556 
557 	memcpy(&header_indicator, buf++, 1);
558 	memcpy(&platform_id, buf++, 1);
559 
560 	memcpy(&count, buf, 2);
561 	count = lendian_short(count);
562 	buf += 2;
563 
564 	if (platform_id == 0xef)
565 		return 0;
566 
567 	return 1;
568 }
569 
570 int
571 read_efi_catalogue(const uint8_t *buf, uint16_t *count, uint32_t *lba)
572 {
573     buf += 6;
574 
575     memcpy(count, buf, 2);
576     *count = lendian_short(*count);
577     buf += 2;
578 
579     memcpy(lba, buf, 4);
580     *lba = lendian_int(*lba);
581     buf += 6;
582 
583     return 0;
584 }
585 #endif
586 
587 
588 void
589 display_catalogue(void)
590 {
591     printf("de_boot: %hhu\n", de_boot);
592     printf("de_media: %hhu\n", de_media);
593     printf("de_seg: %hu\n", de_seg);
594     printf("de_sys: %hhu\n", de_sys);
595     printf("de_mbz1: %hhu\n", de_mbz1);
596     printf("de_count: %hu\n", de_count);
597     printf("de_lba: %u\n", de_lba);
598     printf("de_mbz2: %hu\n", de_mbz2);
599 }
600 
601 
602 void
603 read_mbr_template(char *path, uint8_t *mbr)
604 {
605     FILE *fp;
606     int ret;
607 
608     fp = fopen(path, "rb");
609     if (fp == NULL)
610         err(1, "could not open MBR template file `%s'", path);
611     clearerr(fp);
612     ret = fread(mbr, 1, MBRSIZE, fp);
613     if (ferror(fp) || ret != MBRSIZE)
614         err(1, "error while reading MBR template file `%s'", path);
615     fclose(fp);
616 }
617 
618 
619 int
620 initialise_mbr(uint8_t *mbr)
621 {
622     int i = 0;
623     uint32_t tmp = 0;
624     uint8_t ptype = 0, *rbm = mbr;
625     uint8_t bhead = 0, bsect = 0, bcyle = 0;
626     uint8_t ehead = 0, esect = 0, ecyle = 0;
627 
628 #ifndef ISOHYBRID_C_STANDALONE
629     extern unsigned char isohdpfx[][MBRSIZE];
630 #endif
631 
632     if (mbr_template_path[0]) {
633         read_mbr_template(mbr_template_path, mbr);
634     } else {
635 
636 #ifdef ISOHYBRID_C_STANDALONE
637 
638         err(1, "This is a standalone binary. You must specify --mbr. E.g with /usr/lib/syslinux/isohdpfx.bin");
639 
640 #else
641 
642         memcpy(mbr, &isohdpfx[hd0 + 3 * partok], MBRSIZE);
643 
644 #endif /* ! ISOHYBRID_C_STANDALONE */
645 
646     }
647 
648 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
649     if (mode & MAC) {
650 	memcpy(mbr, afp_header, sizeof(afp_header));
651     }
652 #endif
653 
654     if (!entry)
655 	entry = 1;
656 
657 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
658     if (mode & EFI)
659 	type = 0;
660 #endif
661 
662     mbr += MBRSIZE;                                 /* offset 432 */
663 
664     tmp = lendian_int(de_lba * 4);
665     memcpy(mbr, &tmp, sizeof(tmp));
666     mbr += sizeof(tmp);                             /* offset 436 */
667 
668     tmp = 0;
669     memcpy(mbr, &tmp, sizeof(tmp));
670     mbr += sizeof(tmp);                             /* offset 440 */
671 
672     tmp = lendian_int(id);
673     memcpy(mbr, &tmp, sizeof(tmp));
674     mbr += sizeof(tmp);                             /* offset 444 */
675 
676     mbr[0] = '\0';
677     mbr[1] = '\0';
678     mbr += 2;                                       /* offset 446 */
679 
680     ptype = type;
681     psize = c * head * sector - offset;
682 
683     bhead = (offset / sector) % head;
684     bsect = (offset % sector) + 1;
685     bcyle = offset / (head * sector);
686 
687     bsect += (bcyle & 0x300) >> 2;
688     bcyle  &= 0xFF;
689 
690     ehead = head - 1;
691     esect = sector + (((cc - 1) & 0x300) >> 2);
692     ecyle = (cc - 1) & 0xFF;
693 
694     for (i = 1; i <= 4; i++)
695     {
696         memset(mbr, 0, 16);
697         if (i == entry)
698         {
699             mbr[0] = 0x80;
700             mbr[1] = bhead;
701             mbr[2] = bsect;
702             mbr[3] = bcyle;
703             mbr[4] = ptype;
704             mbr[5] = ehead;
705             mbr[6] = esect;
706             mbr[7] = ecyle;
707 
708             tmp = lendian_int(offset);
709             memcpy(&mbr[8], &tmp, sizeof(tmp));
710 
711             tmp = lendian_int(psize);
712             memcpy(&mbr[12], &tmp, sizeof(tmp));
713         }
714 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
715         if (i == 2 && (mode & EFI))
716         {
717             mbr[0] = 0x0;
718             mbr[1] = 0xfe;
719             mbr[2] = 0xff;
720             mbr[3] = 0xff;
721             mbr[4] = 0xef;
722             mbr[5] = 0xfe;
723             mbr[6] = 0xff;
724             mbr[7] = 0xff;
725 
726             tmp = lendian_int(efi_lba * 4);
727             memcpy(&mbr[8], &tmp, sizeof(tmp));
728 
729             tmp = lendian_int(efi_count);
730             memcpy(&mbr[12], &tmp, sizeof(tmp));
731         }
732         if (i == 3 && (mode & MAC))
733         {
734             mbr[0] = 0x0;
735             mbr[1] = 0xfe;
736             mbr[2] = 0xff;
737             mbr[3] = 0xff;
738             mbr[4] = 0x0;
739             mbr[5] = 0xfe;
740             mbr[6] = 0xff;
741             mbr[7] = 0xff;
742 
743             tmp = lendian_int(mac_lba * 4);
744             memcpy(&mbr[8], &tmp, sizeof(tmp));
745 
746             tmp = lendian_int(mac_count);
747             memcpy(&mbr[12], &tmp, sizeof(tmp));
748         }
749 #endif
750         mbr += 16;
751     }
752     mbr[0] = 0x55;
753     mbr[1] = 0xAA;
754     mbr += 2;
755 
756     return mbr - rbm;
757 }
758 
759 void
760 display_mbr(const uint8_t *mbr, size_t len)
761 {
762     unsigned char c = 0;
763     unsigned int i = 0, j = 0;
764 
765     printf("sizeof(MBR): %zu bytes\n", len);
766     for (i = 0; i < len; i++)
767     {
768         if (!(i % 16))
769             printf("%04d ", i);
770 
771         if (!(i % 8))
772             printf(" ");
773 
774         c = mbr[i];
775         printf("%02x ", c);
776 
777         if (!((i + 1) % 16))
778         {
779             printf(" |");
780             for (; j <= i; j++)
781                 printf("%c", isprint(mbr[j]) ? mbr[j] : '.');
782             printf("|\n");
783         }
784     }
785 }
786 
787 
788 uint32_t chksum_crc32 (unsigned char *block, unsigned int length)
789 {
790 	register unsigned long crc;
791 	unsigned long i;
792 
793 	crc = 0xFFFFFFFF;
794 	for (i = 0; i < length; i++)
795 	{
796 		crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
797 	}
798 	return (crc ^ 0xFFFFFFFF);
799 }
800 
801 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
802 void
803 reverse_uuid(uuid_t uuid)
804 {
805 	uint8_t t, *p = (uint8_t *)uuid;
806 
807 	t = p[0]; p[0] = p[3]; p[3] = t;
808 	t = p[1]; p[1] = p[2]; p[2] = t;
809 	t = p[4]; p[4] = p[5]; p[5] = t;
810 	t = p[6]; p[6] = p[7]; p[7] = t;
811 }
812 #endif
813 
814 static uint16_t *
815 ascii_to_utf16le(uint16_t *dst, const char *src)
816 {
817     uint8_t *p = (uint8_t *)dst;
818     char c;
819 
820     do {
821 	c = *src++;
822 	*p++ = c;
823 	*p++ = 0;
824     } while (c);
825 
826     return (uint16_t *)p;
827 }
828 
829 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
830 void
831 initialise_gpt(uint8_t *gpt, uint32_t current, uint32_t alternate, int primary)
832 {
833     struct gpt_header *header = (struct gpt_header *)gpt;
834     struct gpt_part_header *part;
835     int hole = 0;
836     int gptsize = 128 / 4 + 2;
837 
838     if (mac_lba) {
839 	/* 2048 bytes per partition, plus round to 2048 boundary */
840 	hole = (apm_parts * 4) + 2;
841     }
842 
843     if (primary) {
844 	uuid_generate(disk_uuid);
845 	reverse_uuid(disk_uuid);
846     }
847 
848     header->signature = lendian_64(0x5452415020494645ull);
849     header->revision = lendian_int(0x010000);
850     header->headerSize = lendian_int(0x5c);
851     header->currentLBA = lendian_64(current);
852     header->backupLBA = lendian_64(alternate);
853     header->firstUsableLBA = lendian_64(gptsize + hole);
854     header->lastUsableLBA = lendian_64((isostat.st_size + padding)/512 -
855 				       gptsize);
856     if (primary)
857 	header->partitionEntriesLBA = lendian_64(0x02 + hole);
858     else
859 	header->partitionEntriesLBA = lendian_64(current - (128 / 4));
860     header->numParts = lendian_int(0x80);
861     header->sizeOfPartitionEntries = lendian_int(0x80);
862     memcpy(header->diskGUID, disk_uuid, sizeof(uuid_t));
863 
864     if (primary)
865 	gpt += sizeof(struct gpt_header) + hole * 512;
866     else
867 	gpt -= header->sizeOfPartitionEntries * header->numParts;
868 
869     part = (struct gpt_part_header *)gpt;
870     if (primary) {
871 	uuid_generate(part_uuid);
872 	uuid_generate(iso_uuid);
873 	reverse_uuid(part_uuid);
874 	reverse_uuid(iso_uuid);
875     }
876 
877     memcpy(part->partGUID, iso_uuid, sizeof(uuid_t));
878     memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
879     part->firstLBA = lendian_64(0);
880     part->lastLBA = lendian_64(psize - 1);
881     ascii_to_utf16le(part->name, "ISOHybrid ISO");
882 
883     gpt += sizeof(struct gpt_part_header);
884     part++;
885 
886     memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
887     memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
888     part->firstLBA = lendian_64(efi_lba * 4);
889     part->lastLBA = lendian_64(part->firstLBA + efi_count - 1);
890     ascii_to_utf16le(part->name, "ISOHybrid");
891 
892     gpt += sizeof(struct gpt_part_header);
893 
894     if (mac_lba) {
895 	gpt += sizeof(struct gpt_part_header);
896 
897 	part++;
898 
899 	memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
900 	memcpy(part->partTypeGUID, hfs_partition, sizeof(uuid_t));
901 	part->firstLBA = lendian_64(mac_lba * 4);
902 	part->lastLBA = lendian_64(part->firstLBA + mac_count - 1);
903 	ascii_to_utf16le(part->name, "ISOHybrid");
904 
905 	part--;
906     }
907 
908     part--;
909 
910     header->partitionEntriesCRC = lendian_int (chksum_crc32((uint8_t *)part,
911 			   header->numParts * header->sizeOfPartitionEntries));
912 
913     header->headerCRC = lendian_int(chksum_crc32((uint8_t *)header,
914 						 header->headerSize));
915 }
916 
917 void
918 initialise_apm(uint8_t *gpt, uint32_t start)
919 {
920     struct apple_part_header *part = (struct apple_part_header *)gpt;
921 
922     part->signature = bendian_short(0x504d);
923     part->map_count = bendian_int(apm_parts);
924     part->start_block = bendian_int(1);
925     part->block_count = bendian_int(4);
926     strcpy(part->name, "Apple");
927     strcpy(part->type, "Apple_partition_map");
928     part->data_start = bendian_int(0);
929     part->data_count = bendian_int(10);
930     part->status = bendian_int(0x03);
931 
932     part = (struct apple_part_header *)(gpt + 2048);
933 
934     part->signature = bendian_short(0x504d);
935     part->map_count = bendian_int(3);
936     part->start_block = bendian_int(efi_lba);
937     part->block_count = bendian_int(efi_count / 4);
938     strcpy(part->name, "EFI");
939     strcpy(part->type, "Apple_HFS");
940     part->data_start = bendian_int(0);
941     part->data_count = bendian_int(efi_count / 4);
942     part->status = bendian_int(0x33);
943 
944     part = (struct apple_part_header *)(gpt + 4096);
945 
946     if (mac_lba)
947     {
948 	part->signature = bendian_short(0x504d);
949 	part->map_count = bendian_int(3);
950 	part->start_block = bendian_int(mac_lba);
951 	part->block_count = bendian_int(mac_count / 4);
952 	strcpy(part->name, "EFI");
953 	strcpy(part->type, "Apple_HFS");
954 	part->data_start = bendian_int(0);
955 	part->data_count = bendian_int(mac_count / 4);
956 	part->status = bendian_int(0x33);
957     } else {
958 	part->signature = bendian_short(0x504d);
959 	part->map_count = bendian_int(3);
960 	part->start_block = bendian_int((start/2048) + 10);
961 	part->block_count = bendian_int(efi_lba - start/2048 - 10);
962 	strcpy(part->name, "ISO");
963 	strcpy(part->type, "Apple_Free");
964 	part->data_start = bendian_int(0);
965 	part->data_count = bendian_int(efi_lba - start/2048 - 10);
966 	part->status = bendian_int(0x01);
967     }
968 }
969 #endif
970 
971 int
972 main(int argc, char *argv[])
973 {
974     int i = 0;
975     FILE *fp = NULL;
976     uint8_t *buf = NULL, *bufz = NULL;
977     int cylsize = 0, frac = 0;
978     size_t orig_gpt_size, free_space, gpt_size;
979     struct iso_primary_descriptor descriptor;
980 
981     prog = strcpy(alloca(strlen(argv[0]) + 1), argv[0]);
982     i = check_option(argc, argv);
983     argc -= i;
984     argv += i;
985 
986     if (!argc)
987     {
988         usage();
989         return 1;
990     }
991 
992 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
993     if ((mode & EFI) && offset)
994 	errx(1, "%s: --offset is invalid with UEFI images\n", argv[0]);
995 #endif
996 
997     srand(time(NULL) << (getppid() << getpid()));
998 
999     if (!(fp = fopen(argv[0], "rb+")))
1000         err(1, "could not open file `%s'", argv[0]);
1001 
1002     if (fseeko(fp, (off_t) (16 << 11), SEEK_SET))
1003         err(1, "%s: seek error - 0", argv[0]);
1004 
1005     if (fread(&descriptor, sizeof(char), sizeof(descriptor), fp) != sizeof(descriptor))
1006         err(1, "%s: read error - 0", argv[0]);
1007 
1008     if (fseeko(fp, (off_t) 17 * 2048, SEEK_SET))
1009         err(1, "%s: seek error - 1", argv[0]);
1010 
1011     bufz = buf = calloc(BUFSIZE, sizeof(char));
1012     if (fread(buf, sizeof(char), BUFSIZE, fp) != BUFSIZE)
1013         err(1, "%s", argv[0]);
1014 
1015     if (check_banner(buf))
1016         errx(1, "%s: could not find boot record", argv[0]);
1017 
1018     if (mode & VERBOSE)
1019         printf("catalogue offset: %d\n", catoffset);
1020 
1021     if (fseeko(fp, ((off_t) catoffset) * 2048, SEEK_SET))
1022         err(1, "%s: seek error - 2", argv[0]);
1023 
1024     buf = bufz;
1025     memset(buf, 0, BUFSIZE);
1026     if (fread(buf, sizeof(char), BUFSIZE, fp) != BUFSIZE)
1027         err(1, "%s", argv[0]);
1028 
1029     if (check_catalogue(buf))
1030         errx(1, "%s: invalid boot catalogue", argv[0]);
1031 
1032     buf += sizeof(ve);
1033     if (read_catalogue(buf))
1034         errx(1, "%s: unexpected boot catalogue parameters", argv[0]);
1035 
1036     if (mode & VERBOSE)
1037         display_catalogue();
1038 
1039     buf += 32;
1040 
1041 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
1042     if (mode & EFI)
1043     {
1044 	if (!read_efi_section(buf)) {
1045 	    buf += 32;
1046 	    if (!read_efi_catalogue(buf, &efi_count, &efi_lba) && efi_lba) {
1047 		offset = 0;
1048 	    } else {
1049 		errx(1, "%s: invalid efi catalogue", argv[0]);
1050 	    }
1051 	} else {
1052 	    errx(1, "%s: unable to find efi image", argv[0]);
1053 	}
1054     }
1055 #endif
1056 
1057     buf += 32;
1058 
1059 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
1060     if (mode & MAC)
1061     {
1062 	if (!read_efi_section(buf)) {
1063 	    buf += 32;
1064 	    if (!read_efi_catalogue(buf, &mac_count, &mac_lba) && mac_lba) {
1065 		offset = 0;
1066 	    } else {
1067 		errx(1, "%s: invalid efi catalogue", argv[0]);
1068 	    }
1069 	} else {
1070 	    errx(1, "%s: unable to find mac efi image", argv[0]);
1071 	}
1072     }
1073 #endif
1074 
1075     if (fseeko(fp, (((off_t) de_lba) * 2048 + 0x40), SEEK_SET))
1076         err(1, "%s: seek error - 3", argv[0]);
1077 
1078     buf = bufz;
1079     memset(buf, 0, BUFSIZE);
1080     if (fread(buf, sizeof(char), 4, fp) != 4)
1081         err(1, "%s", argv[0]);
1082 
1083     if (memcmp(buf, "\xFB\xC0\x78\x70", 4))
1084         errx(1, "%s: boot loader does not have an isolinux.bin hybrid " \
1085                  "signature. Note that isolinux-debug.bin does not support " \
1086                  "hybrid booting", argv[0]);
1087 
1088     if (stat(argv[0], &isostat))
1089         err(1, "%s", argv[0]);
1090 
1091     isosize = lendian_int(descriptor.size) * lendian_short(descriptor.block_size);
1092     free_space = isostat.st_size - isosize;
1093 
1094     cylsize = head * sector * 512;
1095     frac = isostat.st_size % cylsize;
1096     padding = (frac > 0) ? cylsize - frac : 0;
1097 
1098     if (mode & VERBOSE)
1099         printf("imgsize: %zu, padding: %d\n", (size_t)isostat.st_size, padding);
1100 
1101     cc = c = ( isostat.st_size + padding) / cylsize;
1102     if (c > 1024)
1103     {
1104         warnx("Warning: more than 1024 cylinders: %d", c);
1105         warnx("Not all BIOSes will be able to boot this device");
1106         cc = 1024;
1107     }
1108 
1109     if (!id)
1110     {
1111         if (fseeko(fp, (off_t) 440, SEEK_SET))
1112             err(1, "%s: seek error - 4", argv[0]);
1113 
1114 	if (fread(&id, 1, 4, fp) != 4)
1115 	    err(1, "%s: read error", argv[0]);
1116 
1117         id = lendian_int(id);
1118         if (!id)
1119         {
1120             if (mode & VERBOSE)
1121                 printf("random ");
1122             id = rand();
1123         }
1124     }
1125     if (mode & VERBOSE)
1126         printf("id: %u\n", id);
1127 
1128     buf = bufz;
1129     memset(buf, 0, BUFSIZE);
1130     i = initialise_mbr(buf);
1131 
1132     if (mode & VERBOSE)
1133         display_mbr(buf, i);
1134 
1135     if (fseeko(fp, (off_t) 0, SEEK_SET))
1136         err(1, "%s: seek error - 5", argv[0]);
1137 
1138     if (fwrite(buf, sizeof(char), i, fp) != (size_t)i)
1139         err(1, "%s: write error - 1", argv[0]);
1140 
1141 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
1142     if (efi_lba) {
1143 	reverse_uuid(basic_partition);
1144 	reverse_uuid(hfs_partition);
1145 
1146 	/* 512 byte header, 128 entries of 128 bytes */
1147 	orig_gpt_size = gpt_size = 512 + (128 * 128);
1148 
1149 	/* Leave space for the APM if necessary */
1150 	if (mac_lba)
1151 	    gpt_size += (4 * 2048);
1152 
1153 	buf = calloc(gpt_size, sizeof(char));
1154 	memset(buf, 0, gpt_size);
1155 
1156 	/*
1157 	 * We need to ensure that we have enough space for the secondary GPT.
1158 	 * Unlike the primary, this doesn't need a hole for the APM. We still
1159 	 * want to be 1MB aligned so just bump the padding by a megabyte.
1160 	 */
1161 	if (free_space < orig_gpt_size && padding < orig_gpt_size) {
1162 	    padding += 1024 * 1024;
1163 	}
1164 
1165 	/*
1166 	 * Determine the size of the ISO filesystem. This will define the size
1167 	 * of the partition that covers it.
1168 	 */
1169 	psize = isosize / 512;
1170 
1171 	/*
1172 	 * Primary GPT starts at sector 1, secondary GPT starts at 1 sector
1173 	 * before the end of the image
1174 	 */
1175 	initialise_gpt(buf, 1, (isostat.st_size + padding - 512) / 512, 1);
1176 
1177 	if (fseeko(fp, (off_t) 512, SEEK_SET))
1178 	    err(1, "%s: seek error - 6", argv[0]);
1179 
1180 	if (fwrite(buf, sizeof(char), gpt_size, fp) != (size_t)gpt_size)
1181 	    err(1, "%s: write error - 2", argv[0]);
1182     }
1183 
1184     if (mac_lba)
1185     {
1186 	/* Apple partition entries filling 2048 bytes each */
1187 	int apm_size = apm_parts * 2048;
1188 
1189 	buf = realloc(buf, apm_size);
1190 	memset(buf, 0, apm_size);
1191 
1192 	initialise_apm(buf, APM_OFFSET);
1193 
1194 	fseeko(fp, (off_t) APM_OFFSET, SEEK_SET);
1195 	fwrite(buf, sizeof(char), apm_size, fp);
1196     }
1197 #endif
1198 
1199     if (padding)
1200     {
1201         if (fsync(fileno(fp)))
1202             err(1, "%s: could not synchronise", argv[0]);
1203 
1204         if (ftruncate(fileno(fp), isostat.st_size + padding))
1205             err(1, "%s: could not add padding bytes", argv[0]);
1206     }
1207 
1208 #ifdef REACTOS_ISOHYBRID_EFI_MAC_SUPPORT
1209     if (efi_lba) {
1210 	buf = realloc(buf, orig_gpt_size);
1211 	memset(buf, 0, orig_gpt_size);
1212 
1213 	buf += orig_gpt_size - sizeof(struct gpt_header);
1214 
1215 	initialise_gpt(buf, (isostat.st_size + padding - 512) / 512, 1, 0);
1216 
1217 	/* Shift back far enough to write the 128 GPT entries */
1218 	buf -= 128 * sizeof(struct gpt_part_header);
1219 
1220 	/*
1221 	 * Seek far enough back that the gpt header is 512 bytes before the
1222 	 * end of the image
1223 	 */
1224 
1225 	if (fseeko(fp, (isostat.st_size + padding) - orig_gpt_size, SEEK_SET))
1226 	    err(1, "%s: seek error - 8", argv[0]);
1227 
1228 	if (fwrite(buf, sizeof(char), orig_gpt_size, fp) != orig_gpt_size)
1229 	    err(1, "%s: write error - 4", argv[0]);
1230     }
1231 #endif
1232 
1233     free(buf);
1234     fclose(fp);
1235 
1236     return 0;
1237 }
1238