1 /*
2 ** volume.c: prepare HFS volume for mkhybrid
3 **
4 ** James Pearson 17/7/97
5 ** modified JCP 29/7/97 to improve allocation sizes to cut
6 ** down on wasted space. Now uses the HFS "allocation" size rounded
7 ** up to the nearest 2048 bytes. Savings can be significant with
8 ** a large volume containing lots of smallish files.
9 **
10 ** Updated for v1.12 - now uses the built in RELOCATED_DIRECTORY
11 ** flag for finding the real directory location JCP 8/1/97
12 */
13
14 #ifdef APPLE_HYB
15
16 #include "config.h"
17 #include "mkisofs.h"
18 #include "volume.h"
19 #include "write.h"
20 #include <errno.h>
21
22 /* from desktop.c */
23 int make_desktop(hfsvol *, int);
24 /* from libhfs_iso/hfs.c */
25 void hfs_vsetbless(hfsvol *, unsigned long);
26
27 static hfsvol *vol_save = 0; /* used to "destroy" an HFS volume */
28
29 int DECL(copy_to_mac_vol, (hfsvol *, struct directory *));
30
31 /*
32 ** AlcSiz: find allocation size for given volume size
33 */
34 int
AlcSiz(int vlen)35 AlcSiz(int vlen)
36 {
37 int lpa, drAlBlkSiz;
38
39 /* code extracted from hfs_format() */
40 lpa = 1 + vlen / 65536;
41 drAlBlkSiz = lpa * HFS_BLOCKSZ;
42
43 /* now set our "allocation size" to the allocation block rounded
44 up to the nearest SECTOR_SIZE (2048 bytes) */
45 drAlBlkSiz = V_ROUND_UP(drAlBlkSiz, SECTOR_SIZE);
46
47 return(drAlBlkSiz);
48 }
49
50 /*
51 ** XClpSiz: find the default size of the catalog/extent file
52 */
53 int
XClpSiz(int vlen)54 XClpSiz(int vlen)
55 {
56 int olpa, lpa, drNmAlBlks, drAlBlkSiz;
57 int vbmsz, drXTClpSiz;
58
59 /* code extracted from hfs_format() */
60
61 /* get the lpa from our calculated allocation block size */
62 drAlBlkSiz = AlcSiz(vlen);
63 lpa = drAlBlkSiz/HFS_BLOCKSZ;
64
65 vbmsz = (vlen / lpa + 4095) / 4096;
66 drNmAlBlks = (vlen - 5 - vbmsz) / lpa;
67 drXTClpSiz = drNmAlBlks / 128 * drAlBlkSiz;
68
69 /* make allowances because we have possibly rounded up the
70 allocation size */
71
72 /* get the "original" lpa " */
73 olpa = 1 + vlen / 65536;
74
75 /* adjust size upwards */
76 drXTClpSiz = (drXTClpSiz*lpa)/olpa;
77
78 /* round up to the nearest alloaction size */
79 drXTClpSiz = V_ROUND_UP(drXTClpSiz, drAlBlkSiz);
80
81 return(drXTClpSiz);
82 }
83
84 /*
85 ** get_vol_size: get the size of the volume including the extent/catalog
86 */
87 int
get_vol_size(int vblen)88 get_vol_size(int vblen)
89 {
90 int drXTClpSiz, drAlBlkSiz;
91 int new_vblen;
92
93 /* try to estimate a "volume size" based on the code
94 in hfs_format - we need the size of the catalog/extents
95 and Desktop files included in the volume, as we add this
96 to the end of the ISO volume */
97
98 drXTClpSiz = XClpSiz(vblen);
99 drAlBlkSiz = AlcSiz(vblen);
100
101 /* catalog file is set at CTC times (default twice) the extents file
102 size - hence the (ctc_size + 1) below. The Desktop starts of the
103 same size as the "clump size" == 4 x drAlBlkSiz, plus a spare
104 drAlBlkSiz for the alternative MDB */
105
106 new_vblen = vblen + ((hce->ctc_size + 1)*drXTClpSiz + 5*drAlBlkSiz)/HFS_BLOCKSZ;
107
108 return (new_vblen);
109 }
110
111 /*
112 ** write_fork: "write" file data to the volume
113 **
114 ** This is used to update the HFS file internal structures
115 ** but no data is actually written (it's trapped deep down in
116 ** libhfs).
117 */
118 int
write_fork(hfsfile * hfp,long tot)119 write_fork(hfsfile *hfp, long tot)
120 {
121 char blk[HFS_BLOCKSZ];
122 unsigned short start;
123 long len;
124
125 len = tot;
126 /* we need to know where this fork starts */
127 start = hfs_get_drAllocPtr(hfp);
128
129 /* loop through the data a block at a time */
130 while (len >= HFS_BLOCKSZ)
131 {
132 if(hfs_write(hfp, blk, HFS_BLOCKSZ) < 0)
133 return(-1);
134 len -= HFS_BLOCKSZ;
135 }
136 /* write out anything left */
137 if (len)
138 if(hfs_write(hfp, blk, len) < 0)
139 return(-1);
140
141 /* set the start of the allocation search to be immediately
142 after this fork */
143 hfs_set_drAllocPtr(hfp, start, tot);
144
145 return(0);
146 }
147
148 /*
149 ** make_mac_volume: "create" an HFS volume using the ISO data
150 **
151 ** The HFS volume structures are set up (but no data is written yet).
152 **
153 ** ISO volumes have a allocation size of 2048 bytes - regardless
154 ** of the size of the volume. HFS allocation size is depends on volume
155 ** size, so we may have to update the ISO structures to add in any
156 ** padding.
157 */
FDECL2(make_mac_volume,struct directory *,dpnt,int,start_extent)158 int FDECL2(make_mac_volume, struct directory *, dpnt, int, start_extent)
159 {
160 char vol_name[HFS_MAX_VLEN+1]; /* Mac volume name */
161 hfsvol *vol; /* Mac volume */
162 int vlen, vblen; /* vol length (bytes, blocks) */
163 int Csize, lastCsize; /* allocation sizes */
164 int ret = 0; /* return value */
165 int loop = 1;
166
167 /* umount volume if we have had a previous attempt */
168 if (vol_save)
169 if (hfs_umount(vol_save, 0) < 0)
170 return (-1);
171
172 /* set the default clump size to the ISO block size */
173 lastCsize = SECTOR_SIZE;
174
175 if (verbose > 1)
176 fprintf(stderr, "Creating HFS Volume info\n");
177
178 /* name or copy ISO volume name to Mac Volume name */
179 strncpy(vol_name, hfs_volume_id ? hfs_volume_id : volume_id, HFS_MAX_VLEN);
180 vol_name[HFS_MAX_VLEN] = '\0';
181
182 /* get initial size of HFS volume (size of ISO volume) */
183 vblen = last_extent * BLK_CONV;
184
185 /* add on size of extents/catalog file, but this may mean
186 the allocation size will change, so loop round until the allocation
187 size doesn't change */
188 while (loop) {
189 hce->XTCsize = XClpSiz(vblen);
190 vblen = get_vol_size(vblen);
191 Csize = AlcSiz(vblen);
192
193 if (Csize == lastCsize) {
194 /* allocation size hasn't changed, so carry on */
195 loop = 0;
196 }
197 else {
198 /* allocation size has changed, so update ISO volume size */
199 if ((vlen = get_adj_size(Csize)) < 0) {
200 snprintf(hce->error, ERROR_SIZE,
201 "too many files for HFS volume");
202 return (-1);
203 }
204 vlen += V_ROUND_UP(start_extent * SECTOR_SIZE, Csize);
205 vblen = vlen / HFS_BLOCKSZ;
206 lastCsize = Csize;
207 }
208 }
209
210 /* set vlen to size in bytes */
211 /* vlen = hce->hfs_vol_size = vblen * HFS_BLOCKSZ; */
212 /* take off the label/map size */
213 vblen -= hce->hfs_map_size;
214 vlen = hce->hfs_vol_size = vblen * HFS_BLOCKSZ;
215
216 /* set the default allocation size for libhfs */
217 hce->Csize = Csize;
218
219 /* format and mount the "volume" */
220 if (hfs_format(hce, 0, vol_name) < 0)
221 {
222 snprintf(hce->error, ERROR_SIZE, "can't HFS format %s",vol_name);
223 return(-1);
224 }
225
226 /* update the ISO structures with new start extents and any padding
227 required */
228 if (Csize != SECTOR_SIZE) {
229 last_extent = adj_size(Csize, start_extent, hce->hfs_hdr_size + hce->hfs_map_size);
230 adj_size_other(dpnt);
231 }
232
233 if ((vol = hfs_mount(hce, 0, 0)) == 0)
234 {
235 snprintf(hce->error, ERROR_SIZE, "can't HFS mount %s",vol_name);
236 return(-1);
237 }
238
239 /* save the volume for possible later use */
240 vol_save = vol;
241
242 /* Recursively "copy" the files to the volume - we need to
243 know the first allocation block in the volume as starting blocks
244 of files are relative to this.
245 */
246 ret = copy_to_mac_vol(vol, dpnt);
247 if (ret < 0)
248 return(ret);
249
250 /* make the Desktop files - I *think* this stops the Mac
251 rebuilding the desktop when the CD is mounted on a Mac
252 These will be ignored if they already exist */
253 if (create_dt)
254 ret = make_desktop(vol, last_extent*BLK_CONV);
255 if (ret < 0)
256 return(ret);
257
258 /* close the volume */
259 if (hfs_flush(vol) < 0)
260 return(-1);
261
262 /* unmount and set the start blocks for the catalog/extents files */
263 if (hfs_umount(vol, last_extent*BLK_CONV) < 0)
264 return(-1);
265
266 return(Csize);
267 }
268
269 #define TEN 10 /* well, it is! */
270 #define LCHAR "_"
271
272 /* copy_to_mac_vol: copy all files in a directory to corresponding
273 ** Mac folder.
274 **
275 ** Files are copied recursively to corresponding folders on the Mac
276 ** volume. The caller routine needs to do a hfs_chdir before calling this
277 ** routine.
278 */
FDECL2(copy_to_mac_vol,hfsvol *,vol,struct directory *,node)279 int FDECL2(copy_to_mac_vol, hfsvol *, vol, struct directory *, node)
280 {
281 struct directory_entry * s_entry; /* ISO directory entry */
282 struct directory_entry * s_entry1; /* tmp ISO directory entry */
283 struct directory *dpnt; /* ISO directory */
284
285 hfsfile *hfp; /* HFS file */
286 hfsdirent *ent; /* HFS file entities */
287 long id; /* current HFS folder */
288 long dext, rext; /* real data/rsrc start blk */
289 int ret; /* result code */
290 int new_name; /* HFS file has modified name */
291
292 int tens;
293 int digits;
294 int i;
295
296 /* store the current HFS directory ID */
297 if ((id = hfs_getcwd(vol)) == 0)
298 return(-1);
299
300 if (verbose > 1)
301 fprintf(stderr,"HFS scanning %s\n", node->whole_name);
302
303 /* loop through the ISO directory entries and process files */
304 for(s_entry = node->contents; s_entry; s_entry = s_entry->next)
305 {
306 /* ignore directory and associated (rsrc) files */
307 if(s_entry->isorec.flags[0])
308 continue;
309
310 /* ignore any non-Mac type file */
311 if(!s_entry->hfs_ent)
312 continue;
313
314 #ifdef DEBUG
315 fprintf(stderr," Name = %s", s_entry->whole_name);
316 fprintf(stderr," Startb = %d\n", s_entry->starting_block);
317 #endif /* DEBUG */
318
319 ent = s_entry->hfs_ent;
320
321 /* create file */
322 i = HFS_MAX_FLEN - strlen(ent->name);
323 new_name = 0;
324 tens = TEN;
325 digits = 1;
326
327 while (1)
328 {
329 /* try to open file - if it exists, then append '_' to
330 the name and try again */
331 errno = 0;
332 if ((hfs_create(vol, ent->name, ent->type, ent->creator)) < 0)
333 {
334 if (errno != EEXIST )
335 {
336 /* not an "exist" error, or we can't append as
337 the filename is already HFS_MAX_FLEN chars */
338 snprintf(hce->error, ERROR_SIZE,
339 "can't HFS create file %s",
340 s_entry->whole_name);
341 return(-1);
342 }
343 else if (i == 0)
344 {
345 /* File name at max HFS length - make unique name */
346 if (!new_name) new_name++;
347
348 sprintf(ent->name + HFS_MAX_FLEN - digits - 1,
349 "%s%d", LCHAR, new_name);
350 new_name++;
351 if (new_name == tens) {
352 tens *= TEN;
353 digits++;
354 }
355 }
356 else
357 {
358 /* append '_' to get new name */
359 strcat(ent->name, LCHAR);
360 i--;
361 new_name = 1;
362 }
363 }
364 else
365 break;
366 }
367
368 /* warn that we have a new name */
369 if (new_name && verbose > 0)
370 {
371 fprintf(stderr, "Using HFS name: %s for %s\n", ent->name,
372 s_entry->whole_name);
373 }
374
375 /* open file */
376 if ((hfp = hfs_open(vol, ent->name)) == 0)
377 {
378 snprintf(hce->error, ERROR_SIZE, "can't HFS open %s",
379 s_entry->whole_name);
380 return(-1);
381 }
382
383 /* if it has a data fork, then "write" it out */
384 if (ent->dsize)
385 write_fork(hfp, ent->dsize);
386
387 /* if it has a resource fork, set the fork and "write" it out */
388 if (ent->rsize)
389 {
390 if ((hfs_setfork(hfp, 1)) < 0)
391 return(-1);
392 write_fork(hfp, ent->rsize);
393 }
394
395 /* update any HFS file attributes */
396 if ((hfs_fsetattr(hfp, ent)) < 0)
397 {
398 snprintf(hce->error, ERROR_SIZE, "can't HFS set attributes %s",
399 s_entry->whole_name);
400 return(-1);
401 }
402
403 /* get the ISO starting block of data fork (may be zero)
404 and convert to the equivalent HFS block */
405 if (ent->dsize)
406 dext = s_entry->starting_block * BLK_CONV;
407 else
408 dext = 0;
409
410 /* if the file has a resource fork (associated file), get it's
411 ISO starting block and convert as above */
412 if (s_entry->assoc && ent->rsize)
413 rext = s_entry->assoc->starting_block * BLK_CONV;
414 else
415 rext = 0;
416
417 /* close the file and update the starting blocks */
418 if (hfs_close(hfp, dext, rext) < 0)
419 {
420 snprintf(hce->error, ERROR_SIZE, "can't HFS close file %s",
421 s_entry->whole_name);
422 return(-1);
423 }
424 }
425
426 /* process sub-directories - have a slight problem here,
427 if the directory had been relocated, then we need to find
428 the real directory - we do this by first finding the real
429 directory_entry, and then finding it's directory info */
430
431 /* following code taken from joliet.c */
432 for(s_entry=node->contents;s_entry;s_entry=s_entry->next)
433 {
434 if((s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
435 {
436 /* if the directory has been reloacted, then search the
437 relocated directory for the real entry */
438 for(s_entry1=reloc_dir->contents;s_entry1;s_entry1=s_entry1->next)
439 {
440 if(s_entry1->parent_rec == s_entry)
441 break;
442 }
443
444 /* have a problem - can't find the real directory */
445 if(s_entry1 == NULL)
446 {
447 snprintf(hce->error, ERROR_SIZE,
448 "can't locate relocated directory %s",
449 s_entry->whole_name);
450 return(-1);
451 }
452 }
453 else
454 s_entry1 = s_entry;
455
456 /* now have the correct entry - now find the actual directory */
457 if ((s_entry1->isorec.flags[0] & 2) && strcmp(s_entry1->name,".") && strcmp(s_entry1->name,".."))
458 {
459 if((s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
460 dpnt = reloc_dir->subdir;
461 else
462 dpnt = node->subdir;
463
464 while(1)
465 {
466 if (dpnt->self == s_entry1)
467 break;
468 dpnt = dpnt->next;
469 if(!dpnt)
470 {
471 snprintf(hce->error, ERROR_SIZE,
472 "can't find directory location %s",
473 s_entry1->whole_name);
474 return (-1);
475 }
476 }
477 /* now have the correct directory - so do the HFS stuff */
478 ent = dpnt->hfs_ent;
479
480 /* if we don't have hfs entries, then this is a "deep"
481 directory - this will be processed later */
482 if (!ent)
483 continue;
484
485 /* make sub-folder */
486 i = HFS_MAX_FLEN - strlen(ent->name);
487 new_name = 0;
488 tens = TEN;
489 digits = 1;
490
491 while (1)
492 {
493 /* try to create new directory - if it exists, then
494 append '_' to the name and try again */
495 errno = 0;
496 if (hfs_mkdir(vol, ent->name) < 0)
497 {
498 if (errno != EEXIST)
499 {
500 /* not an "exist" error, or we can't append as
501 the filename is already HFS_MAX_FLEN chars */
502 snprintf(hce->error, ERROR_SIZE,
503 "can't HFS create folder %s",
504 s_entry->whole_name);
505 return(-1);
506 }
507 else if (i == 0)
508 {
509 /* File name at max HFS length - make unique name */
510 if (!new_name) new_name++;
511
512 sprintf(ent->name + HFS_MAX_FLEN - digits - 1,
513 "%s%d", LCHAR, new_name);
514 new_name++;
515 if (new_name == tens) {
516 tens *= TEN;
517 digits++;
518 }
519 }
520 else
521 {
522 /* append '_' to get new name */
523 strcat(ent->name, LCHAR);
524 i--;
525 new_name = 1;
526 }
527 }
528 else
529 break;
530 }
531
532 /* warn that we have a new name */
533 if (new_name && verbose > 0)
534 {
535 fprintf(stderr, "Using HFS name: %s for %s\n", ent->name,
536 s_entry->whole_name);
537 }
538
539 /* see if we need to "bless" this folder */
540 if (hfs_bless && strcmp(s_entry->whole_name, hfs_bless) == 0) {
541 hfs_stat(vol, ent->name, ent);
542 hfs_vsetbless(vol, ent->cnid);
543 if (verbose > 0) {
544 fprintf(stderr, "Blessing %s (%s)\n",
545 ent->name, s_entry->whole_name);
546 }
547 /* stop any further checks */
548 hfs_bless = NULL;
549 }
550
551 /* change to sub-folder */
552 if (hfs_chdir(vol, ent->name) < 0)
553 return(-1);
554
555 /* recursively copy files ... */
556 ret = copy_to_mac_vol(vol, dpnt);
557 if (ret < 0)
558 return(ret);
559
560 /* change back to this folder */
561 if (hfs_setcwd(vol, id) < 0)
562 return(-1);
563 }
564 }
565
566 return(0);
567 }
568 #endif /* APPLE_HYB */
569
570