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