patch genisoimage multi extent
[platform/upstream/cdrkit.git] / genisoimage / volume.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 /* @(#)volume.c 1.14 04/07/09 joerg, Copyright 1997, 1998, 1999, 2000 James Pearson */
14 /*
15  *      Copyright (c) 1997, 1998, 1999, 2000 James Pearson
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 2, or (at your option)
20  * any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; see the file COPYING.  If not, write to
29  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
30  */
31
32 /*
33  *      volume.c: prepare HFS volume for mkhybrid
34  *
35  *      James Pearson 17/7/97
36  *      modified JCP 29/7/97 to improve allocation sizes to cut
37  *      down on wasted space. Now uses the HFS "allocation" size rounded
38  *      up to the nearest 2048 bytes. Savings can be significant with
39  *      a large volume containing lots of smallish files.
40  *
41  *      Updated for v1.12 - now uses the built in RELOCATED_DIRECTORY
42  *      flag for finding the real directory location JCP 8/1/97
43  */
44
45 #ifdef APPLE_HYB
46
47 #include <mconfig.h>
48 #include "genisoimage.h"
49 #include <errno.h>
50
51 #define HFS_MIN_SIZE    1600    /* 800k == 1600 HFS blocks */
52
53 static hfsvol  *vol_save = 0;   /* used to "destroy" an HFS volume */
54
55 static  int     AlcSiz(Ulong);
56 static  int     XClpSiz(Ulong);
57 static  int     get_vol_size(int);
58 int     write_fork(hfsfile * hfp, long tot);
59 int     make_mac_volume(struct directory *, int);
60 static  int     copy_to_mac_vol(hfsvol *, struct directory *);
61 static void     set_dir_info(hfsvol *, struct directory *);
62
63 /*
64  *      AlcSiz: find allocation size for given volume size
65  */
66 static int
67 AlcSiz(Ulong vlen)
68 {
69         int     lpa,
70                 drAlBlkSiz;
71
72         /* code extracted from hfs_format() */
73         lpa = 1 + vlen / 65536;
74         drAlBlkSiz = lpa * HFS_BLOCKSZ;
75
76         /*
77          * now set our "allocation size" to the allocation block rounded up to
78          * the nearest SECTOR_SIZE (2048 bytes)
79          */
80         drAlBlkSiz = ROUND_UP(drAlBlkSiz, SECTOR_SIZE);
81
82         return (drAlBlkSiz);
83 }
84
85 /*
86  *      XClpSiz: find the default size of the catalog/extent file
87  */
88 static int
89 XClpSiz(Ulong vlen)
90 {
91         int     olpa,
92                 lpa,
93                 drNmAlBlks,
94                 drAlBlkSiz;
95         int     vbmsz,
96                 drXTClpSiz;
97
98         /* code extracted from hfs_format() */
99
100         /* get the lpa from our calculated allocation block size */
101         drAlBlkSiz = AlcSiz(vlen);
102         lpa = drAlBlkSiz / HFS_BLOCKSZ;
103
104         vbmsz = (vlen / lpa + 4095) / 4096;
105         drNmAlBlks = (vlen - 5 - vbmsz) / lpa;
106         drXTClpSiz = drNmAlBlks / 128 * drAlBlkSiz;
107
108         /* override the drXTClpSiz size for large volumes */
109         if (drXTClpSiz > hce->max_XTCsize) {
110                 drXTClpSiz = hce->max_XTCsize;
111         } else {
112                 /*
113                  * make allowances because we have possibly rounded up the
114                  * allocation size get the "original" lpa "
115                  */
116                 olpa = 1 + vlen / 65536;
117
118                 /* adjust size upwards */
119                 drXTClpSiz = ((Ullong)drXTClpSiz * lpa) / olpa;
120         }
121
122         /* round up to the nearest allocation size */
123         drXTClpSiz = ROUND_UP(drXTClpSiz, drAlBlkSiz);
124
125         return (drXTClpSiz);
126 }
127
128 /*
129  *      get_vol_size: get the size of the volume including the extent/catalog
130  */
131 static int
132 get_vol_size(int vblen)
133 {
134         int     drXTClpSiz;
135         int     drAlBlkSiz;
136         int     new_vblen;
137
138         /*
139          * try to estimate a "volume size" based on the code in hfs_format
140          * - we need the size of the catalog/extents and Desktop files included
141          * in the volume, as we add this to the end of the ISO volume
142          */
143         drXTClpSiz = XClpSiz(vblen);
144         drAlBlkSiz = AlcSiz(vblen);
145
146         /*
147          * catalog file is set at CTC times (default twice) the extents
148          * file size - hence the (ctc_size + 1) below. The Desktop starts of
149          * the same size as the "clump size" == 4 x drAlBlkSiz,
150          * plus a spare drAlBlkSiz for the alternative MDB
151          */
152         new_vblen = vblen +
153             ((hce->ctc_size + 1) * drXTClpSiz + 5 * drAlBlkSiz) / HFS_BLOCKSZ;
154
155         return (new_vblen);
156 }
157
158 /*
159  *      write_fork: "write" file data to the volume
160  *
161  *      This is used to update the HFS file internal structures
162  *      but no data is actually written (it's trapped deep down in
163  *      libhfs).
164  */
165 int
166 write_fork(hfsfile *hfp, long tot)
167 {
168         char            blk[HFS_BLOCKSZ];
169         unsigned short  start;
170         long            len;
171
172         len = tot;
173         /* we need to know where this fork starts */
174         start = hfs_get_drAllocPtr(hfp);
175
176         /* loop through the data a block at a time */
177         while (len >= HFS_BLOCKSZ) {
178                 if (hfs_write(hfp, blk, HFS_BLOCKSZ) < 0)
179                         return (-1);
180                 len -= HFS_BLOCKSZ;
181         }
182         /* write out anything left */
183         if (len)
184                 if (hfs_write(hfp, blk, len) < 0)
185                         return (-1);
186
187         /*
188          * set the start of the allocation search to be immediately after
189          * this fork
190          */
191         hfs_set_drAllocPtr(hfp, start, tot);
192
193         return (0);
194 }
195
196 /*
197  *      make_mac_volume: "create" an HFS volume using the ISO data
198  *
199  *      The HFS volume structures are set up (but no data is written yet).
200  *
201  *      ISO volumes have a allocation size of 2048 bytes - regardless
202  *      of the size of the volume. HFS allocation size is depends on volume
203  *      size, so we may have to update the ISO structures to add in any
204  *      padding.
205  */
206 int
207 make_mac_volume(struct directory  *dpnt, int start_extent)
208 {
209         char    vol_name[HFS_MAX_VLEN + 1];     /* Mac volume name */
210         hfsvol  *vol;                   /* Mac volume */
211         int     vblen;                  /* vol length (HFS blocks) */
212         int     Csize,
213                 lastCsize;              /* allocation sizes */
214         int     ret = 0;                /* return value */
215         int     loop = 1;
216
217         /* umount volume if we have had a previous attempt */
218         if (vol_save)
219                 if (hfs_umount(vol_save, 0, hfs_lock) < 0)
220                         return (-1);
221
222         /* set the default clump size to the ISO block size */
223         Csize = lastCsize = SECTOR_SIZE;
224
225         if (verbose > 1)
226                 fprintf(stderr, "Creating HFS Volume info\n");
227
228         /* name or copy ISO volume name to Mac Volume name */
229         strncpy(vol_name, hfs_volume_id ? hfs_volume_id : volume_id,
230                                                                 HFS_MAX_VLEN);
231         vol_name[HFS_MAX_VLEN] = '\0';
232
233         /* get initial size of HFS volume (size of current ISO volume) */
234         vblen = (last_extent - session_start) * HFS_BLK_CONV;
235
236         /* make sure volume is at least 800k */
237         if (vblen < HFS_MIN_SIZE)
238                 vblen += insert_padding_file(HFS_MIN_SIZE - vblen);
239
240         /*
241          * add on size of extents/catalog file, but this may mean the
242          * allocation size will change, so loop round until the
243          * allocation size doesn't change
244          */
245         while (loop) {
246                 hce->XTCsize = XClpSiz(vblen);
247                 vblen = get_vol_size(vblen);
248                 Csize = AlcSiz(vblen);
249
250                 if (Csize == lastCsize) {
251                         /* allocation size hasn't changed, so carry on */
252                         loop = 0;
253                 } else {
254                         /*
255                          * allocation size has changed, so update
256                          * ISO volume size
257                          */
258                         if ((vblen = get_adj_size(Csize)) < 0) {
259                                 sprintf(hce->error,
260                                         "too many files for HFS volume");
261                                 return (-1);
262                         }
263                         vblen +=
264                                 ROUND_UP((start_extent - session_start) *
265                                                 HFS_BLK_CONV, Csize);
266                         lastCsize = Csize;
267                 }
268         }
269
270         /* take off the label/map size */
271         vblen -= hce->hfs_map_size;
272
273         hce->hfs_vol_size = vblen;
274
275         /* set the default allocation size for libhfs */
276         hce->Csize = Csize;
277
278         /* format and mount the "volume" */
279         if (hfs_format(hce, 0, vol_name) < 0) {
280                 sprintf(hce->error, "can't HFS format %s", vol_name);
281                 return (-1);
282         }
283         /*
284          * update the ISO structures with new start extents and any
285          * padding required
286          */
287         if (Csize != SECTOR_SIZE) {
288                 last_extent = adj_size(Csize, start_extent,
289                                         hce->hfs_hdr_size + hce->hfs_map_size);
290                 adj_size_other(dpnt);
291         }
292         if ((vol = hfs_mount(hce, 0, 0)) == 0) {
293                 sprintf(hce->error, "can't HFS mount %s", vol_name);
294                 return (-1);
295         }
296         /* save the volume for possible later use */
297         vol_save = vol;
298
299         /*
300          * Recursively "copy" the files to the volume
301          * - we need to know the first allocation block in the volume as
302          * starting blocks of files are relative to this.
303          */
304         ret = copy_to_mac_vol(vol, dpnt);
305         if (ret < 0)
306                 return (ret);
307
308         /*
309          * make the Desktop files - I *think* this stops the Mac rebuilding the
310          * desktop when the CD is mounted on a Mac These will be ignored if they
311          * already exist
312          */
313         if (create_dt)
314                 ret = make_desktop(vol,
315                                 (last_extent - session_start) * HFS_BLK_CONV);
316         if (ret < 0)
317                 return (ret);
318
319         /* close the volume */
320         if (hfs_flush(vol) < 0)
321                 return (-1);
322
323         /* unmount and set the start blocks for the catalog/extents files */
324         if (hfs_umount(vol, (last_extent - session_start) * HFS_BLK_CONV, hfs_lock) < 0)
325                 return (-1);
326
327         return (Csize);
328 }
329
330 #define TEN 10  /* well, it is! */
331 #define LCHAR "_"
332
333 /*
334  *      copy_to_mac_vol: copy all files in a directory to corresponding
335  *                       Mac folder.
336  *
337  *      Files are copied recursively to corresponding folders on the Mac
338  *      volume. The caller routine needs to do a hfs_chdir before calling this
339  *      routine.
340  */
341 static int
342 copy_to_mac_vol(hfsvol *vol, struct directory *node)
343 {
344         struct directory_entry  *s_entry;       /* ISO directory entry */
345         struct directory_entry  *s_entry1;      /* tmp ISO directory entry */
346         struct directory        *dpnt;          /* ISO directory */
347
348         hfsfile                 *hfp;           /* HFS file */
349         hfsdirent               *ent;           /* HFS file entities */
350         long                    id;             /* current HFS folder */
351         long                    dext,
352                                 rext;           /* real data/rsrc start blk */
353         int                     ret;            /* result code */
354         int                     new_name;       /* HFS file has modified name */
355
356         int                     tens;
357         int                     digits;
358         int                     i;
359
360         /* store the current HFS directory ID */
361         if ((id = hfs_getcwd(vol)) == 0)
362                 return (-1);
363
364         if (verbose > 1)
365                 fprintf(stderr, "HFS scanning %s\n", node->whole_name);
366
367         /* loop through the ISO directory entries and process files */
368         for (s_entry = node->contents; s_entry; s_entry = s_entry->next) {
369                 /* ignore directory and associated (rsrc) files */
370                 if (s_entry->isorec.flags[0] & (ISO_DIRECTORY|ISO_ASSOCIATED))
371                         continue;
372
373                 /* ignore any non-Mac type file */
374                 if (!s_entry->hfs_ent)
375                         continue;
376
377                 /*
378                  * ignore if from a previous session
379                  * - should be trapped above
380                  */
381                 if (s_entry->starting_block < session_start)
382                         continue;
383
384 #ifdef DEBUG
385                 fprintf(stderr, " Name = %s", s_entry->whole_name);
386                 fprintf(stderr, "   Startb =  %d\n", s_entry->starting_block);
387 #endif  /* DEBUG */
388
389                 ent = s_entry->hfs_ent;
390
391                 /* create file */
392                 i = HFS_MAX_FLEN - strlen(ent->name);
393                 new_name = 0;
394                 tens = TEN;
395                 digits = 1;
396
397                 while (1) {
398                         /*
399                          * try to open file - if it exists,
400                          * then append '_' to the name and try again
401                          */
402                         errno = 0;
403                         if ((hfs_create(vol, ent->name, ent->u.file.type,
404                                                 ent->u.file.creator)) < 0) {
405                                 if (errno != EEXIST) {
406                                         /*
407                                          * not an "exist" error, or we can't
408                                          * append as the filename is already
409                                          * HFS_MAX_FLEN chars
410                                          */
411                                         sprintf(hce->error,
412                                                 "can't HFS create file %s",
413                                                 s_entry->whole_name);
414                                         return (-1);
415                                 } else if (i == 0) {
416                                         /*
417                                          * File name at max HFS length
418                                          * - make unique name
419                                          */
420                                         if (!new_name)
421                                                 new_name++;
422
423                                         sprintf(ent->name +
424                                                 HFS_MAX_FLEN - digits - 1,
425                                                 "%s%d", LCHAR, new_name);
426                                         new_name++;
427                                         if (new_name == tens) {
428                                                 tens *= TEN;
429                                                 digits++;
430                                         }
431                                 } else {
432                                         /* append '_' to get new name */
433                                         strcat(ent->name, LCHAR);
434                                         i--;
435                                         new_name = 1;
436                                 }
437                         } else
438                                 break;
439                 }
440
441                 /* warn that we have a new name */
442                 if (new_name && verbose > 0) {
443                         fprintf(stderr, "Using HFS name: %s for %s\n",
444                                 ent->name,
445                                 s_entry->whole_name);
446                 }
447                 /* open file */
448                 if ((hfp = hfs_open(vol, ent->name)) == 0) {
449                         sprintf(hce->error, "can't HFS open %s",
450                                 s_entry->whole_name);
451                         return (-1);
452                 }
453                 /* if it has a data fork, then "write" it out */
454                 if (ent->u.file.dsize)
455                         write_fork(hfp, ent->u.file.dsize);
456
457                 /* if it has a resource fork, set the fork and "write" it out */
458                 if (ent->u.file.rsize) {
459                         if ((hfs_setfork(hfp, 1)) < 0)
460                                 return (-1);
461                         write_fork(hfp, ent->u.file.rsize);
462                 }
463
464                 /* make file invisible if ISO9660 hidden */
465                 if (s_entry->de_flags & HIDDEN_FILE)
466                         ent->fdflags |= HFS_FNDR_ISINVISIBLE;
467
468                 /* update any HFS file attributes */
469                 if ((hfs_fsetattr(hfp, ent)) < 0) {
470                         sprintf(hce->error, "can't HFS set attributes %s",
471                                 s_entry->whole_name);
472                         return (-1);
473                 }
474                 /*
475                  * get the ISO starting block of data fork (may be zero)
476                  * and convert to the equivalent HFS block
477                  */
478                 if (ent->u.file.dsize) {
479                         dext = (s_entry->starting_block - session_start) *
480                                                                 HFS_BLK_CONV;
481                 } else {
482                         dext = 0;
483                 }
484
485                 /*
486                  * if the file has a resource fork (associated file),
487                  * get it's ISO starting block and convert as above
488                  */
489                 if (s_entry->assoc && ent->u.file.rsize) {
490                         rext =
491                             (s_entry->assoc->starting_block - session_start) *
492                                                                 HFS_BLK_CONV;
493                 } else {
494                         rext = 0;
495                 }
496
497                 /* close the file and update the starting blocks */
498                 if (hfs_close(hfp, dext, rext) < 0) {
499                         sprintf(hce->error, "can't HFS close file %s",
500                                 s_entry->whole_name);
501                         return (-1);
502                 }
503         }
504
505         /* set folder info and custom icon (if it exists) */
506         set_dir_info(vol, node);
507
508         /*
509          * process sub-directories  - have a slight problem here,
510          * if the directory had been relocated, then we need to find the
511          * real directory - we do this by first finding the
512          * real directory_entry, and then finding it's directory info
513          */
514
515         /* following code taken from joliet.c */
516         for (s_entry = node->contents; s_entry; s_entry = s_entry->next) {
517                 if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) {
518                         /*
519                          * if the directory has been reloacted, then search the
520                          * relocated directory for the real entry
521                          */
522                         for (s_entry1 = reloc_dir->contents; s_entry1;
523                                                 s_entry1 = s_entry1->next) {
524                                 if (s_entry1->parent_rec == s_entry)
525                                         break;
526                         }
527
528                         /* have a problem - can't find the real directory */
529                         if (s_entry1 == NULL) {
530                                 sprintf(hce->error,
531                                         "can't locate relocated directory %s",
532                                         s_entry->whole_name);
533                                 return (-1);
534                         }
535                 } else
536                         s_entry1 = s_entry;
537
538                 /* now have the correct entry - now find the actual directory */
539                 if ((s_entry1->isorec.flags[0] & ISO_DIRECTORY) &&
540                                 strcmp(s_entry1->name, ".") &&
541                                 strcmp(s_entry1->name, "..")) {
542                         if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
543                                 dpnt = reloc_dir->subdir;
544                         else
545                                 dpnt = node->subdir;
546
547                         while (1) {
548                                 if (dpnt->self == s_entry1)
549                                         break;
550                                 dpnt = dpnt->next;
551                                 if (!dpnt) {
552                                         sprintf(hce->error,
553                                             "can't find directory location %s",
554                                                         s_entry1->whole_name);
555                                         return (-1);
556                                 }
557                         }
558                         /*
559                          * now have the correct directory
560                          * - so do the HFS stuff
561                          */
562                         ent = dpnt->hfs_ent;
563
564                         /*
565                          * if we don't have hfs entries, then this is a "deep"
566                          * directory - this will be processed later
567                          */
568                         if (!ent)
569                                 continue;
570
571                         /* make sub-folder */
572                         i = HFS_MAX_FLEN - strlen(ent->name);
573                         new_name = 0;
574                         tens = TEN;
575                         digits = 1;
576
577                         while (1) {
578                                 /*
579                                  * try to create new directory
580                                  * - if it exists, then append '_' to the name
581                                  * and try again
582                                  */
583                                 errno = 0;
584                                 if (hfs_mkdir(vol, ent->name) < 0) {
585                                         if (errno != EEXIST) {
586                                                 /*
587                                                  * not an "exist" error,
588                                                  * or we can't append as the
589                                                  * filename is already
590                                                  * HFS_MAX_FLEN chars
591                                                  */
592                                                 sprintf(hce->error,
593                                                     "can't HFS create folder %s",
594                                                         s_entry->whole_name);
595                                                 return (-1);
596                                         } else if (i == 0) {
597                                                 /*
598                                                  * File name at max HFS length
599                                                  * - make unique name
600                                                  */
601                                                 if (!new_name)
602                                                         new_name++;
603
604                                                 sprintf(ent->name +
605                                                     HFS_MAX_FLEN - digits - 1,
606                                                     "%s%d", LCHAR, new_name);
607                                                 new_name++;
608                                                 if (new_name == tens) {
609                                                         tens *= TEN;
610                                                         digits++;
611                                                 }
612                                         } else {
613                                                 /* append '_' to get new name */
614                                                 strcat(ent->name, LCHAR);
615                                                 i--;
616                                                 new_name = 1;
617                                         }
618                                 } else
619                                         break;
620                         }
621
622                         /* warn that we have a new name */
623                         if (new_name && verbose > 0) {
624                                 fprintf(stderr, "Using HFS name: %s for %s\n",
625                                                         ent->name,
626                                                         s_entry->whole_name);
627                         }
628                         /* see if we need to "bless" this folder */
629                         if (hfs_bless && strcmp(s_entry->whole_name, hfs_bless)
630                                         == 0) {
631                                 hfs_stat(vol, ent->name, ent);
632                                 hfs_vsetbless(vol, ent->cnid);
633                                 if (verbose > 0) {
634                                         fprintf(stderr, "Blessing %s (%s)\n",
635                                                         ent->name,
636                                                         s_entry->whole_name);
637                                 }
638                                 /* stop any further checks */
639                                 hfs_bless = NULL;
640                         }
641                         /* change to sub-folder */
642                         if (hfs_chdir(vol, ent->name) < 0)
643                                 return (-1);
644
645                         /* recursively copy files ... */
646                         ret = copy_to_mac_vol(vol, dpnt);
647                         if (ret < 0)
648                                 return (ret);
649
650                         /* change back to this folder */
651                         if (hfs_setcwd(vol, id) < 0)
652                                 return (-1);
653                 }
654         }
655
656         return (0);
657 }
658
659 /*
660  *      set_dir_info:   Set directory info for a file - also use a custom
661  *                      Icon - if it exists.
662  *
663  *      Sets folder' layout (window layout, view, scroll bars etc)
664  *
665  *      Set the 'HFS_FNDR_HASCUSTOMICON' bit of the folder flags
666  *      if a file called 'Icon\r' exists in the folder
667  *
668  *      Also makes sure the Icon file is invisible
669  *      Don't worry if any of this fails ...
670  *
671  *      Thanks to Rob Leslie <rob@mars.org> for how to do this.
672  */
673
674 #define ICON    "Icon"
675
676 static void
677 set_dir_info(hfsvol *vol, struct directory *de)
678 {
679         hfsdirent       *ent = de->hfs_ent;
680         hfsdirent       ent1;
681         char            name[HFS_MAX_FLEN + 1];
682         unsigned short  flags = 0;
683
684         memset(&ent1, 0, sizeof (hfsdirent));
685
686         sprintf(name, "%s\r", ICON);
687
688         /* get the attributes for the Icon file */
689         if (hfs_stat(vol, name, &ent1) == 0) {
690
691                 /* make sure it is invisible */
692                 ent1.fdflags |= HFS_FNDR_ISINVISIBLE;
693
694                 /* set the new attributes for the Icon file */
695                 hfs_setattr(vol, name, &ent1);
696
697                 /* flag the folder as having a custom icon */
698                 flags |= HFS_FNDR_HASCUSTOMICON;
699         }
700
701         /* make the current folder invisible if ISO9660 hidden */
702         if (de->self->de_flags & HIDDEN_FILE) {
703                 flags |= HFS_FNDR_ISINVISIBLE;
704         }
705
706         /* may not have an hfs_ent for this directory */
707         if (ent == NULL) {
708                 ent = &ent1;
709                 memset(ent, 0, sizeof (hfsdirent));
710
711                 /* get the attributes for the folder */
712                 if (hfs_stat(vol, ":", ent) < 0)
713                         return;
714         }
715
716         /* set HFS_FNDR_HASCUSTOMICON/HFS_FNDR_ISINVISIBLE if needed */
717         ent->fdflags |= flags;
718
719         /* set the new attributes for the folder */
720         if (hfs_setattr(vol, ":", ent) < 0) {
721                 /*
722                  * Only needed if we add things after this if statement.
723                  */
724 /*              return;*/
725         }
726 }
727
728 #endif  /* APPLE_HYB */