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
AlcSiz(Ulong vlen)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
XClpSiz(Ulong vlen)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
get_vol_size(int vblen)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
write_fork(hfsfile * hfp,long tot)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
make_mac_volume(struct directory * dpnt,int start_extent)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
copy_to_mac_vol(hfsvol * vol,struct directory * node)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
set_dir_info(hfsvol * vol,struct directory * de)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 */
729