patch genisoimage multi extent
[platform/upstream/cdrkit.git] / genisoimage / boot.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* @(#)boot.c   1.13 04/02/22 Copyright 1999-2003 J. Schilling */
14 /*
15  *      Support for generic boot (sector 0..16)
16  *      and to boot Sun sparc and Sun x86 systems.
17  *
18  *      Copyright (c) 1999-2003 J. Schilling
19  */
20 /*
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License version 2
23  * as published by the Free Software Foundation.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License along with
31  * this program; see the file COPYING.  If not, write to the Free Software
32  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33  */
34
35 #include <mconfig.h>
36 #include "genisoimage.h"
37 #include <fctldefs.h>
38 #include <utypes.h>
39 #include <intcvt.h>
40 #include <schily.h>
41 #include "sunlabel.h"
42
43 extern  int     use_sunx86boot;
44
45 static struct sun_label cd_label;
46 static struct x86_label sx86_label;
47 static struct pc_part   fdisk_part;
48 static char     *boot_files[NDKMAP];    /* Change this for > 8 x86 parts */
49
50 static  void    init_sparc_label(void);
51 static  void    init_sunx86_label(void);
52 void    sparc_boot_label(char *label);
53 void    sunx86_boot_label(char *label);
54 void    scan_sparc_boot(char *files);
55 void    scan_sunx86_boot(char *files);
56 int     make_sun_label(void);
57 int     make_sunx86_label(void);
58 static  void    dup_sun_label(int part);
59 static  int     sunboot_write(FILE *outfile);
60 static  int     sunlabel_size(int starting_extent);
61 static  int     sunlabel_write(FILE * outfile);
62 static  int     genboot_size(int starting_extent);
63 static  int     genboot_write(FILE * outfile);
64
65 /*
66  * Set the virtual geometry in the disk label.
67  * If we like to make the geometry variable, we may change
68  * dkl_ncyl and dkl_pcyl later.
69  */
70 static void
71 init_sparc_label()
72 {
73         i_to_4_byte(cd_label.dkl_vtoc.v_version, V_VERSION);
74         i_to_2_byte(cd_label.dkl_vtoc.v_nparts, NDKMAP);
75         i_to_4_byte(cd_label.dkl_vtoc.v_sanity, VTOC_SANE);
76
77         i_to_2_byte(cd_label.dkl_rpm, CD_RPM);
78         i_to_2_byte(cd_label.dkl_pcyl, CD_PCYL);
79         i_to_2_byte(cd_label.dkl_apc, CD_APC);
80         i_to_2_byte(cd_label.dkl_intrlv, CD_INTRLV);
81         i_to_2_byte(cd_label.dkl_ncyl, CD_NCYL);
82         i_to_2_byte(cd_label.dkl_acyl, CD_ACYL);
83         i_to_2_byte(cd_label.dkl_nhead, CD_NHEAD);
84         i_to_2_byte(cd_label.dkl_nsect, CD_NSECT);
85
86         cd_label.dkl_magic[0] = DKL_MAGIC_0;
87         cd_label.dkl_magic[1] = DKL_MAGIC_1;
88 }
89
90 static void
91 init_sunx86_label()
92 {
93         li_to_4_byte(sx86_label.dkl_vtoc.v_sanity, VTOC_SANE);
94         li_to_4_byte(sx86_label.dkl_vtoc.v_version, V_VERSION);
95         li_to_2_byte(sx86_label.dkl_vtoc.v_sectorsz, 512);
96         li_to_2_byte(sx86_label.dkl_vtoc.v_nparts, NX86MAP);
97
98         li_to_4_byte(sx86_label.dkl_pcyl, CD_PCYL);
99         li_to_4_byte(sx86_label.dkl_ncyl, CD_NCYL);
100         li_to_2_byte(sx86_label.dkl_acyl, CD_ACYL);
101         li_to_2_byte(sx86_label.dkl_bcyl, 0);
102
103         li_to_4_byte(sx86_label.dkl_nhead, CD_NHEAD);
104         li_to_4_byte(sx86_label.dkl_nsect, CD_NSECT);
105         li_to_2_byte(sx86_label.dkl_intrlv, CD_INTRLV);
106         li_to_2_byte(sx86_label.dkl_skew, 0);
107         li_to_2_byte(sx86_label.dkl_apc, CD_APC);
108         li_to_2_byte(sx86_label.dkl_rpm, CD_RPM);
109
110         li_to_2_byte(sx86_label.dkl_write_reinstruct, 0);
111         li_to_2_byte(sx86_label.dkl_read_reinstruct, 0);
112
113         li_to_2_byte(sx86_label.dkl_magic, DKL_MAGIC);
114 }
115
116 /*
117  * For command line parser: set ASCII label.
118  */
119 void
120 sparc_boot_label(char *label)
121 {
122         strncpy(cd_label.dkl_ascilabel, label, 127);
123         cd_label.dkl_ascilabel[127] = '\0';
124 }
125
126 void
127 sunx86_boot_label(char *label)
128 {
129         strncpy(sx86_label.dkl_vtoc.v_asciilabel, label, 127);
130         sx86_label.dkl_vtoc.v_asciilabel[127] = '\0';
131 }
132
133 /*
134  * Parse the command line argument for boot images.
135  */
136 void
137 scan_sparc_boot(char *files)
138 {
139         char            *p;
140         int             i = 1;
141         struct stat     statbuf;
142         int             status;
143
144         init_sparc_label();
145
146         do {
147                 if (i >= NDKMAP)
148                         comerrno(EX_BAD, "Too many boot partitions.\n");
149                 boot_files[i++] = files;
150                 if ((p = strchr(files, ',')) != NULL)
151                         *p++ = '\0';
152                 files = p;
153         } while (p);
154
155         i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_tag,  V_USR);
156         i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
157         for (i = 0; i < NDKMAP; i++) {
158                 p = boot_files[i];
159                 if (p == NULL || *p == '\0')
160                         continue;
161                 if (strcmp(p, "...") == '\0')
162                         break;
163
164                 status = stat_filter(p, &statbuf);
165                 if (status < 0 || access(p, R_OK) < 0)
166                         comerr("Cannot access '%s'.\n", p);
167
168                 i_to_4_byte(cd_label.dkl_map[i].dkl_nblk,
169                         roundup(statbuf.st_size, CD_CYLSIZE)/512);
170
171                 i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag,  V_ROOT);
172                 i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
173         }
174 }
175
176 void
177 scan_sunx86_boot(char *files)
178 {
179         char            *p;
180         int             i = 0;
181         struct stat     statbuf;
182         int             status;
183
184         init_sunx86_label();
185
186         do {
187                 if (i >= NDKMAP)
188                         comerrno(EX_BAD, "Too many boot partitions.\n");
189                 boot_files[i++] = files;
190                 if ((p = strchr(files, ',')) != NULL)
191                         *p++ = '\0';
192                 files = p;
193         } while (p);
194
195         li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_tag,  V_ROOT);  /* UFS */
196         li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_flag, V_RONLY);
197         li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_tag,  V_USR);   /* ISO */
198         li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_flag, V_RONLY);
199         li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_tag,  0);      /* ALL */
200         li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_flag, 0);
201         for (i = 0; i < NDKMAP; i++) {
202                 p = boot_files[i];
203                 if (p == NULL || *p == '\0')
204                         continue;
205                 if (i == 1 || i == 2) {
206                         comerrno(EX_BAD,
207                         "Partition %d may not have a filename.\n", i);
208                 }
209
210                 status = stat_filter(p, &statbuf);
211                 if (status < 0 || access(p, R_OK) < 0)
212                         comerr("Cannot access '%s'.\n", p);
213
214                 li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size,
215                         roundup(statbuf.st_size, CD_CYLSIZE)/512);
216
217                 if (i > 2) {
218                         li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_tag,  V_USR);
219                         li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
220                 }
221         }
222 }
223
224 /*
225  * Finish the Sun disk label and compute the size of the additional data.
226  */
227 int
228 make_sun_label()
229 {
230         int     last;
231         int     cyl = 0;
232         int     nblk;
233         int     bsize;
234         int     i;
235         char    *p;
236
237         /*
238          * Compute the size of the padding for the iso9660 image
239          * to allow the next partition to start on a cylinder boundary.
240          */
241         last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
242
243         i_to_4_byte(cd_label.dkl_map[0].dkl_nblk, last*4);
244         bsize = 0;
245         for (i = 0; i < NDKMAP; i++) {
246                 p = boot_files[i];
247                 if (p != NULL && strcmp(p, "...") == '\0') {
248                         dup_sun_label(i);
249                         break;
250                 }
251                 if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
252                         continue;
253
254                 i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
255                 cyl += nblk / (CD_CYLSIZE/512);
256                 if (i > 0)
257                         bsize += nblk;
258         }
259         bsize /= 4;
260         return (last-last_extent+bsize);
261 }
262
263 /*
264  * A typical Solaris boot/install CD from a Sun CD set looks
265  * this way:
266  *
267  * UFS  Part 0 tag 2 flag 10 start 3839 size 1314560
268  * ISO  Part 1 tag 4 flag 10 start    0 size    3839
269  * ALL  Part 2 tag 0 flag  0 start    0 size 1318400
270  */
271 int
272 make_sunx86_label()
273 {
274         int     last;
275         int     cyl = 0;
276         int     nblk;
277         int     bsize;
278         int     i;
279         int     partoff = 1;    /* The offset of the Solaris 0x82 partition */
280         char    *p;
281
282         /*
283          * Compute the size of the padding for the iso9660 image
284          * to allow the next partition to start on a cylinder boundary.
285          */
286         last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE));
287
288         li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4);
289
290         /*
291          * Note that the Solaris fdisk partition with fdisk signature 0x82
292          * is created at fixed offset 1 sector == 512 Bytes by this
293          * implementation.
294          * We need subtract this partition offset from all absolute
295          * partition offsets in order to get offsets relative to the
296          * Solaris primary partition.
297          */
298         bsize = 0;
299         for (i = 0; i < NDKMAP; i++) {
300                 if (i == 2)             /* Never include the whole disk in */
301                         continue;       /* size/offset computations        */
302                 p = boot_files[i];
303
304                 if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
305                         continue;
306
307                 li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_start,
308                                                 cyl*(CD_CYLSIZE/512)-partoff);
309                 cyl += nblk / (CD_CYLSIZE/512);
310                 if (i == 0 || i > 2)
311                         bsize += nblk;
312         }
313         li_to_4_byte(sx86_label.dkl_vtoc.v_part[0].p_start, last*4-partoff);
314         li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_start, 0);
315         li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4-partoff);
316         li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_start, 0);
317         li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_size, last*4+bsize);
318
319         fdisk_part.part[0].pr_status = STATUS_ACTIVE;
320         fdisk_part.part[0].pr_type   = TYPE_SOLARIS;
321         li_to_4_byte(fdisk_part.part[0].pr_partoff, partoff);
322         li_to_4_byte(fdisk_part.part[0].pr_nsect, last*4+bsize-partoff);
323         fdisk_part.magic[0] = 0x55;
324         fdisk_part.magic[1] = 0xAA;
325
326         bsize /= 4;
327         return (last-last_extent+bsize);
328 }
329
330 /*
331  * Duplicate a partition of the Sun disk label until all partitions are filled up.
332  */
333 static void
334 dup_sun_label(int part)
335 {
336         int     cyl;
337         int     nblk;
338         int     i;
339
340
341         if (part < 1 || part >= NDKMAP)
342                 part = 1;
343         cyl = a_to_4_byte(cd_label.dkl_map[part-1].dkl_cylno);
344         nblk = a_to_4_byte(cd_label.dkl_map[part-1].dkl_nblk);
345
346         for (i = part; i < NDKMAP; i++) {
347                 i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl);
348                 i_to_4_byte(cd_label.dkl_map[i].dkl_nblk, nblk);
349
350                 i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag,  V_ROOT);
351                 i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY);
352         }
353 }
354
355 /*
356  * Write out Sun boot partitions.
357  */
358 static int
359 sunboot_write(FILE *outfile)
360 {
361         char    buffer[SECTOR_SIZE];
362         int     i;
363         int     n;
364         int     nblk;
365         int     amt;
366         int     f;
367         char    *p;
368
369         memset(buffer, 0, sizeof (buffer));
370
371         /*
372          * Write padding to the iso9660 image to allow the
373          * boot partitions to start on a cylinder boundary.
374          */
375         amt = roundup(last_extent_written, (CD_CYLSIZE/SECTOR_SIZE)) - last_extent_written;
376         for (n = 0; n < amt; n++) {
377                 jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
378                 xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
379                 last_extent_written++;
380         }
381         if (use_sunx86boot)
382                 i = 0;
383         else
384                 i = 1;
385         for (; i < NDKMAP; i++) {
386                 if (use_sunx86boot && (i == 1 || i == 2))
387                         continue;
388                 p = boot_files[i];
389                 if (p == NULL || *p == '\0')
390                         continue;
391                 if (p != NULL && strcmp(p, "...") == '\0')
392                         break;
393                 if (use_sunx86boot) {
394                         if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0)
395                                 continue;
396                 } else {
397                         if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0)
398                                 continue;
399                 }
400                 if ((f = open(boot_files[i], O_RDONLY| O_BINARY)) < 0)
401                         comerr("Cannot open '%s'.\n", boot_files[i]);
402
403                 amt = nblk / 4;
404                 for (n = 0; n < amt; n++) {
405                         memset(buffer, 0, sizeof (buffer));
406                         if (read(f, buffer, SECTOR_SIZE) < 0)
407                                 comerr("Read error on '%s'.\n", boot_files[i]);
408                         jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
409                         xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
410                         last_extent_written++;
411                 }
412                 close(f);
413         }
414         fprintf(stderr, "Total extents including %s boot = %u\n",
415                                 use_sunx86boot ? "Solaris x86":"sparc",
416                                 last_extent_written - session_start);
417         return (0);
418 }
419
420 /*
421  * Do size management for the Sun disk label that is located in the first
422  * sector of a disk.
423  */
424 static int
425 sunlabel_size(int starting_extent)
426 {
427         if (last_extent != session_start)
428                 comerrno(EX_BAD, "Cannot create sparc boot on offset != 0.\n");
429         last_extent++;
430         return (0);
431 }
432
433 /*
434  * Cumpute the checksum and write a Sun disk label to the first sector
435  * of the disk.
436  * If the -generic-boot option has been specified too, overlay the
437  * Sun disk label on the firs 512 bytes of the generic boot code.
438  */
439 static int
440 sunlabel_write(FILE *outfile)
441 {
442                 char    buffer[SECTOR_SIZE];
443         register char   *p;
444         register short  count = (512/2) - 1;
445                 int     f;
446
447         memset(buffer, 0, sizeof (buffer));
448         if (genboot_image) {
449                 if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
450                         comerr("Cannot open '%s'.\n", genboot_image);
451
452                 if (read(f, buffer, SECTOR_SIZE) < 0)
453                         comerr("Read error on '%s'.\n", genboot_image);
454                 close(f);
455         }
456
457         if (use_sunx86boot) {
458                 if (sx86_label.dkl_vtoc.v_asciilabel[0] == '\0')
459                         strcpy(sx86_label.dkl_vtoc.v_asciilabel, CD_X86LABEL);
460
461                 p = (char *)&sx86_label;
462                 sx86_label.dkl_cksum[0] = 0;
463                 sx86_label.dkl_cksum[1] = 0;
464                 while (count-- > 0) {
465                         sx86_label.dkl_cksum[0] ^= *p++;
466                         sx86_label.dkl_cksum[1] ^= *p++;
467                 }
468                 memcpy(&buffer[0x1BE], &fdisk_part.part, 512-0x1BE);
469                 memcpy(&buffer[1024], &sx86_label, 512);
470         } else {
471                 /*
472                  * If we don't already have a Sun disk label text
473                  * set up the default.
474                  */
475                 if (cd_label.dkl_ascilabel[0] == '\0')
476                         strcpy(cd_label.dkl_ascilabel, CD_DEFLABEL);
477
478                 p = (char *)&cd_label;
479                 cd_label.dkl_cksum[0] = 0;
480                 cd_label.dkl_cksum[1] = 0;
481                 while (count--) {
482                         cd_label.dkl_cksum[0] ^= *p++;
483                         cd_label.dkl_cksum[1] ^= *p++;
484                 }
485                 memcpy(buffer, &cd_label, 512);
486         }
487
488         jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
489         xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
490         last_extent_written++;
491         return (0);
492 }
493
494 /*
495  * Do size management for the generic boot code on sectors 0..16.
496  */
497 static int
498 genboot_size(int starting_extent)
499 {
500         if (last_extent > (session_start + 1))
501                 comerrno(EX_BAD, "Cannot create generic boot on offset != 0.\n");
502         last_extent = session_start + 16;
503         return (0);
504 }
505
506 /*
507  * Write the generic boot code to sectors 0..16.
508  * If there is a Sun disk label, start writing at sector 1.
509  */
510 static int
511 genboot_write(FILE *outfile)
512 {
513         char    buffer[SECTOR_SIZE];
514         int     i;
515         int     f;
516
517         if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0)
518                 comerr("Cannot open '%s'.\n", genboot_image);
519
520         for (i = 0; i < 16; i++) {
521                 memset(buffer, 0, sizeof (buffer));
522                 if (read(f, buffer, SECTOR_SIZE) < 0)
523                         comerr("Read error on '%s'.\n", genboot_image);
524
525                 if (i != 0 || last_extent_written == session_start) {
526                         jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
527                         xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
528                         last_extent_written++;
529                 }
530         }
531         close(f);
532         return (0);
533 }
534
535 struct output_fragment sunboot_desc     = {NULL, NULL,          NULL,   sunboot_write,  "Sun Boot" };
536 struct output_fragment sunlabel_desc    = {NULL, sunlabel_size, NULL,   sunlabel_write, "Sun Disk Label" };
537 struct output_fragment genboot_desc     = {NULL, genboot_size,  NULL,   genboot_write,  "Generic Boot" };