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 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 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 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 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 */ 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 */ 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