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