1 /* 2 * hfsutils - tools for reading and writing Macintosh HFS volumes 3 * Copyright (C) 1996, 1997 Robert Leslie 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 /* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */ 21 22 # include <stdlib.h> 23 # include <unistd.h> 24 # include <fcntl.h> 25 # include <errno.h> 26 # include <string.h> 27 # include <time.h> 28 # include <ctype.h> 29 # include <sys/stat.h> 30 31 # include "internal.h" 32 # include "data.h" 33 # include "block.h" 34 # include "low.h" 35 # include "file.h" 36 # include "btree.h" 37 # include "node.h" 38 # include "record.h" 39 # include "volume.h" 40 41 char *hfs_error = "no error"; /* static error string */ 42 43 hfsvol *hfs_mounts; /* linked list of mounted volumes */ 44 hfsvol *hfs_curvol; /* current volume */ 45 46 /* High-Level Volume Routines ============================================== */ 47 48 /* 49 * NAME: hfs->mount() 50 * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) 51 */ 52 #ifdef APPLE_HYB 53 hfsvol *hfs_mount(hce_mem *hce, int pnum, int flags) 54 #else 55 hfsvol *hfs_mount(char *path, int pnum, int flags) 56 #endif /* APPLE_HYB */ 57 { 58 struct stat dev; 59 hfsvol *vol = 0; 60 61 #ifndef APPLE_HYB 62 /* see if the volume is already mounted */ 63 64 if (stat(path, &dev) >= 0) 65 { 66 struct stat mdev; 67 hfsvol *check; 68 69 for (check = hfs_mounts; check; check = check->next) 70 { 71 if (fstat(check->fd, &mdev) >= 0 && 72 mdev.st_dev == dev.st_dev && 73 mdev.st_ino == dev.st_ino && 74 (check->pnum == 0 || check->pnum == pnum)) 75 { 76 /* verify compatible read/write mode */ 77 78 if (((check->flags & HFS_READONLY) && 79 ! (flags & O_WRONLY)) || 80 (! (check->flags & HFS_READONLY) && 81 (flags & (O_WRONLY | O_RDWR)))) 82 { 83 vol = check; 84 break; 85 } 86 } 87 } 88 } 89 #endif /* APPLE_HYB */ 90 if (vol == 0) 91 { 92 vol = ALLOC(hfsvol, 1); 93 if (vol == 0) 94 { 95 ERROR(ENOMEM, 0); 96 return 0; 97 } 98 99 vol->flags = 0; 100 vol->pnum = pnum; 101 vol->vstart = 0; 102 vol->vlen = 0; 103 vol->lpa = 0; 104 vol->vbm = 0; 105 vol->cwd = HFS_CNID_ROOTDIR; 106 107 vol->refs = 0; 108 vol->files = 0; 109 vol->dirs = 0; 110 vol->prev = 0; 111 vol->next = 0; 112 113 vol->ext.map = 0; 114 vol->ext.mapsz = 0; 115 vol->ext.flags = 0; 116 vol->ext.compare = r_compareextkeys; 117 118 vol->cat.map = 0; 119 vol->cat.mapsz = 0; 120 vol->cat.flags = 0; 121 vol->cat.compare = r_comparecatkeys; 122 123 /* open and lock the device */ 124 125 #ifdef APPLE_HYB 126 vol->fd = 3; /* any +ve number will do? */ 127 vol->hce = hce; /* store the extra with the vol info */ 128 #else 129 if (flags & (O_WRONLY | O_RDWR)) 130 { 131 vol->fd = open(path, O_RDWR); 132 if (vol->fd >= 0 && l_lockvol(vol) < 0) 133 { 134 close(vol->fd); 135 vol->fd = -2; 136 } 137 } 138 139 if (! (flags & (O_WRONLY | O_RDWR)) || 140 (vol->fd < 0 && 141 (errno == EROFS || errno == EACCES || errno == EAGAIN) && 142 (flags & O_RDWR))) 143 { 144 vol->flags |= HFS_READONLY; 145 vol->fd = open(path, O_RDONLY); 146 if (vol->fd >= 0 && l_lockvol(vol) < 0) 147 { 148 close(vol->fd); 149 vol->fd = -2; 150 } 151 } 152 153 if (vol->fd < 0) 154 { 155 if (vol->fd != -2) 156 ERROR(errno, "error opening device"); 157 158 v_destruct(vol); 159 160 return 0; 161 } 162 #endif /* APPLE_HYB */ 163 164 /* find out what kind of media this is and read the MDB */ 165 166 if (l_readblock0(vol) < 0 || 167 l_readmdb(vol) < 0) 168 { 169 #ifndef APPLE_HYB 170 close(vol->fd); 171 v_destruct(vol); 172 #endif /* APPLE_HYB */ 173 return 0; 174 } 175 176 /* verify this is an HFS volume */ 177 178 if (vol->mdb.drSigWord != 0x4244) 179 { 180 #ifndef APPLE_HYB 181 close(vol->fd); 182 #endif /* APPLE_HYB */ 183 v_destruct(vol); 184 185 ERROR(EINVAL, "not a Macintosh HFS volume"); 186 return 0; 187 } 188 189 /* do minimal consistency checks */ 190 191 if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) 192 { 193 #ifndef APPLE_HYB 194 close(vol->fd); 195 #endif /* APPLE_HYB */ 196 v_destruct(vol); 197 198 ERROR(EINVAL, "bad volume allocation block size"); 199 return 0; 200 } 201 202 if (vol->vlen == 0) 203 vol->vlen = vol->mdb.drAlBlSt + 204 vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2; 205 206 /* read the volume bitmap and extents/catalog B*-tree headers */ 207 208 if (l_readvbm(vol) < 0 || 209 bt_readhdr(&vol->ext) < 0 || 210 bt_readhdr(&vol->cat) < 0) 211 { 212 #ifndef APPLE_HYB 213 close(vol->fd); 214 #endif /* APPLE_HYB */ 215 v_destruct(vol); 216 return 0; 217 } 218 219 if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) 220 { 221 /* volume was not cleanly unmounted; scavenge free-space */ 222 223 if (v_scavenge(vol) < 0) 224 { 225 #ifndef APPLE_HYB 226 close(vol->fd); 227 #endif /* APPLE_HYB */ 228 v_destruct(vol); 229 return 0; 230 } 231 } 232 233 if (vol->flags & HFS_READONLY) 234 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; 235 else 236 vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; 237 238 vol->prev = 0; 239 vol->next = hfs_mounts; 240 241 if (hfs_mounts) 242 hfs_mounts->prev = vol; 243 244 hfs_mounts = vol; 245 } 246 247 ++vol->refs; 248 249 return hfs_curvol = vol; 250 } 251 252 /* 253 * NAME: hfs->flush() 254 * DESCRIPTION: flush all pending changes to an HFS volume 255 */ 256 int hfs_flush(hfsvol *vol) 257 { 258 hfsfile *file; 259 260 if (v_getvol(&vol) < 0) 261 return -1; 262 263 for (file = vol->files; file; file = file->next) 264 { 265 if (f_flush(file) < 0) 266 return -1; 267 } 268 269 if (v_flush(vol, 0) < 0) 270 return -1; 271 272 return 0; 273 } 274 275 /* 276 * NAME: hfs->flushall() 277 * DESCRIPTION: flush all pending changes to all mounted HFS volumes 278 */ 279 void hfs_flushall(void) 280 { 281 hfsvol *vol; 282 283 for (vol = hfs_mounts; vol; vol = vol->next) 284 hfs_flush(vol); 285 } 286 287 /* 288 * NAME: hfs->umount() 289 * DESCRIPTION: close an HFS volume 290 */ 291 #ifdef APPLE_HYB 292 /* extra argument used to alter the position of the extents/catalog files */ 293 int hfs_umount(hfsvol *vol, long end) 294 #else 295 int hfs_umount(hfsvol *vol) 296 #endif /* APPLE_HYB */ 297 { 298 int result = 0; 299 300 if (v_getvol(&vol) < 0) 301 return -1; 302 303 if (--vol->refs) 304 return v_flush(vol, 0); 305 306 /* close all open files and directories */ 307 308 while (vol->files) 309 #ifdef APPLE_HYB 310 hfs_close(vol->files, 0, 0); 311 #else 312 hfs_close(vol->files); 313 #endif /* APPLE_HYB */ 314 315 while (vol->dirs) 316 hfs_closedir(vol->dirs); 317 318 #ifdef APPLE_HYB 319 if (end) 320 { 321 /* move extents and catalog to end of volume ... */ 322 long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096; 323 324 /* we are adding this "files" to the end of the ISO volume, 325 so calculate this address in HFS speak ... */ 326 /* end -= vol->mdb.drAlBlSt; */ 327 end -= (vol->mdb.drAlBlSt + vol->hce->hfs_map_size); 328 end /= vol->lpa; 329 330 /* catalog file ... */ 331 vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end; 332 vol->mdb.drXTExtRec[0].xdrStABN = end; 333 334 /* move postition to start of extents file */ 335 end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN; 336 337 /* extents file ... */ 338 vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end; 339 vol->mdb.drCTExtRec[0].xdrStABN = end; 340 341 /* the volume bitmap is wrong as we have "moved" files 342 about - simple just set the whole lot (it's a readonly volume 343 anyway!) */ 344 memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ); 345 346 /* set the free blocks to zero */ 347 vol->mdb.drFreeBks = 0; 348 349 /* flag changes for flushing later */ 350 vol->flags |= HFS_UPDATE_VBM; 351 vol->flags |= HFS_UPDATE_MDB; 352 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; 353 vol->ext.flags |= HFS_UPDATE_BTHDR; 354 vol->cat.flags |= HFS_UPDATE_BTHDR; 355 } 356 #endif /* APPLE_HYB */ 357 358 if (v_flush(vol, 1) < 0) 359 result = -1; 360 361 #ifndef APPLE_HYB 362 if (close(vol->fd) < 0 && result == 0) 363 { 364 ERROR(errno, "error closing device"); 365 result = -1; 366 } 367 #endif /* APPLE_HYB */ 368 369 if (vol->prev) 370 vol->prev->next = vol->next; 371 if (vol->next) 372 vol->next->prev = vol->prev; 373 374 if (vol == hfs_mounts) 375 hfs_mounts = vol->next; 376 if (vol == hfs_curvol) 377 hfs_curvol = 0; 378 379 v_destruct(vol); 380 381 return result; 382 } 383 384 /* 385 * NAME: hfs->umountall() 386 * DESCRIPTION: unmount all mounted volumes 387 */ 388 void hfs_umountall(void) 389 { 390 while (hfs_mounts) 391 #ifdef APPLE_HYB 392 continue; 393 #else 394 hfs_umount(hfs_mounts); 395 #endif /* APPLE_HYB */ 396 } 397 398 /* 399 * NAME: hfs->getvol() 400 * DESCRIPTION: return a pointer to a mounted volume 401 */ 402 hfsvol *hfs_getvol(char *name) 403 { 404 hfsvol *vol; 405 406 if (name == 0) 407 return hfs_curvol; 408 409 for (vol = hfs_mounts; vol; vol = vol->next) 410 { 411 if (d_relstring(name, vol->mdb.drVN) == 0) 412 return vol; 413 } 414 415 return 0; 416 } 417 418 /* 419 * NAME: hfs->setvol() 420 * DESCRIPTION: change the current volume 421 */ 422 void hfs_setvol(hfsvol *vol) 423 { 424 hfs_curvol = vol; 425 } 426 427 /* 428 * NAME: hfs->vstat() 429 * DESCRIPTION: return volume statistics 430 */ 431 int hfs_vstat(hfsvol *vol, hfsvolent *ent) 432 { 433 if (v_getvol(&vol) < 0) 434 return -1; 435 436 strcpy(ent->name, vol->mdb.drVN); 437 438 ent->flags = (vol->flags & HFS_READONLY) ? HFS_ISLOCKED : 0; 439 ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; 440 ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; 441 ent->crdate = d_toutime(vol->mdb.drCrDate); 442 ent->mddate = d_toutime(vol->mdb.drLsMod); 443 444 return 0; 445 } 446 447 /* 448 * NAME: hfs->format() 449 * DESCRIPTION: write a new filesystem 450 */ 451 #ifdef APPLE_HYB 452 int hfs_format(hce_mem *hce, int pnum, char *vname) 453 #else 454 int hfs_format(char *path, int pnum, char *vname) 455 #endif /* APPLE_HYB */ 456 { 457 hfsvol vol; 458 btree *ext = &vol.ext; 459 btree *cat = &vol.cat; 460 unsigned int vbmsz; 461 int i, result = 0; 462 block vbm[16]; 463 char *map; 464 465 if (strchr(vname, ':')) 466 { 467 ERROR(EINVAL, "volume name may not contain colons"); 468 return -1; 469 } 470 471 i = strlen(vname); 472 if (i < 1 || i > HFS_MAX_VLEN) 473 { 474 ERROR(EINVAL, "volume name must be 1-27 chars"); 475 return -1; 476 } 477 478 vol.flags = 0; 479 vol.pnum = pnum; 480 vol.vstart = 0; 481 vol.vlen = 0; 482 vol.lpa = 0; 483 vol.vbm = vbm; 484 vol.cwd = HFS_CNID_ROOTDIR; 485 486 vol.refs = 0; 487 vol.files = 0; 488 vol.dirs = 0; 489 vol.prev = 0; 490 vol.next = 0; 491 492 #ifndef APPLE_HYB 493 vol.fd = open(path, O_RDWR); 494 if (vol.fd < 0) 495 { 496 ERROR(errno, "error opening device for writing"); 497 return -1; 498 } 499 500 if (l_lockvol(&vol) < 0) 501 { 502 close(vol.fd); 503 return -1; 504 } 505 #endif /* APPLE_HYB */ 506 if (pnum > 0) 507 { 508 if (l_readpm(&vol) < 0) 509 { 510 close(vol.fd); 511 return -1; 512 } 513 } 514 else /* determine size of entire device */ 515 { 516 #ifdef APPLE_HYB 517 vol.vlen = hce->hfs_vol_size/HFS_BLOCKSZ; 518 #else 519 unsigned long low, high, mid; 520 block b; 521 522 for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2) 523 low = high; 524 525 while (low < high - 1) 526 { 527 mid = (low + high) / 2; 528 529 if (b_readlb(&vol, mid, &b) < 0) 530 high = mid; 531 else 532 low = mid; 533 } 534 535 vol.vlen = low + 1; 536 #endif /* APPLE_HYB */ 537 } 538 539 if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ) 540 { 541 #ifndef APPLE_HYB 542 close(vol.fd); 543 #endif /* APPLE_HYB */ 544 545 ERROR(EINVAL, "volume size must be >= 800K"); 546 return -1; 547 } 548 549 /* initialize volume geometry */ 550 551 #ifdef APPLE_HYB 552 /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated 553 earlier */ 554 vol.lpa = hce->Csize/HFS_BLOCKSZ; 555 #else 556 vol.lpa = 1 + vol.vlen / 65536; 557 #endif /* APPLE_HYB */ 558 559 vbmsz = (vol.vlen / vol.lpa + 4095) / 4096; 560 561 vol.mdb.drSigWord = 0x4244; 562 vol.mdb.drCrDate = d_tomtime(time(0)); 563 vol.mdb.drLsMod = vol.mdb.drCrDate; 564 vol.mdb.drAtrb = 0; 565 vol.mdb.drNmFls = 0; 566 vol.mdb.drVBMSt = 3; 567 vol.mdb.drAllocPtr = 0; 568 vol.mdb.drNmAlBlks = (vol.vlen - 5 - vbmsz) / vol.lpa; 569 vol.mdb.drAlBlkSiz = vol.lpa * HFS_BLOCKSZ; 570 vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz * 4; 571 vol.mdb.drAlBlSt = 3 + vbmsz; 572 #ifdef APPLE_HYB 573 /* round up start block to a muliple of lpa - important later */ 574 /*vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + vol.lpa - 1) / vol.lpa) * vol.lpa; 575 */ 576 /* take in accout alignment of files wrt HFS volume start i.e we want 577 drAlBlSt plus hfs_map_size to me a multiple of lpa */ 578 vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + hce->hfs_map_size + vol.lpa - 1) / vol.lpa) * vol.lpa; 579 vol.mdb.drAlBlSt -= hce->hfs_map_size; 580 #endif /* APPLE_HYB */ 581 vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */ 582 vol.mdb.drFreeBks = vol.mdb.drNmAlBlks; 583 584 strcpy(vol.mdb.drVN, vname); 585 586 vol.mdb.drVolBkUp = 0; 587 vol.mdb.drVSeqNum = 0; 588 vol.mdb.drWrCnt = 0; 589 vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz; 590 #ifdef APPLE_HYB 591 /* adjust size of extents/catalog upwards as we may have rounded up 592 allocation size */ 593 i = 1 + vol.vlen / 65536; 594 595 vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i; 596 597 /* round up to lpa size */ 598 vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) / 599 vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz; 600 601 /* ignore above, use what we have already calculated ... */ 602 vol.mdb.drXTClpSiz = hce->XTCsize; 603 604 /* make Catalog file CTC (default twice) as big - prevents further allocation 605 later which we don't want - this seems to work OK ... */ 606 /*vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * CTC; */ 607 vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * hce->ctc_size; 608 609 /* we want to put things at the end of the volume later, so we'll 610 cheat here ... shouldn't matter, as we only need the volume read 611 only anyway (we won't be adding files later!) - leave some extra 612 space for the alternative MDB (in the last allocation block) */ 613 614 vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1; 615 #else 616 vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; 617 #endif /* APPLE_HYB */ 618 vol.mdb.drNmRtDirs = 0; 619 vol.mdb.drFilCnt = 0; 620 vol.mdb.drDirCnt = -1; /* incremented when root folder is created */ 621 622 for (i = 0; i < 8; ++i) 623 vol.mdb.drFndrInfo[i] = 0; 624 625 vol.mdb.drVCSize = 0; 626 vol.mdb.drVBMCSize = 0; 627 vol.mdb.drCtlCSize = 0; 628 629 vol.mdb.drXTFlSize = 0; 630 vol.mdb.drCTFlSize = 0; 631 632 for (i = 0; i < 3; ++i) 633 { 634 vol.mdb.drXTExtRec[i].xdrStABN = 0; 635 vol.mdb.drXTExtRec[i].xdrNumABlks = 0; 636 637 vol.mdb.drCTExtRec[i].xdrStABN = 0; 638 vol.mdb.drCTExtRec[i].xdrNumABlks = 0; 639 } 640 641 /* initialize volume bitmap */ 642 643 memset(vol.vbm, 0, sizeof(vbm)); 644 645 #ifdef APPLE_HYB 646 /* We don't want to write anything out at the moment, so we allocate 647 memory to hold the HFS "header" info and extents/catalog files. 648 Any reads/writes from/to these parts of the volume are trapped and 649 stored in memory. */ 650 651 /* blocks up to the first unallocated block == HFS "header" info 652 This will be placed in the first 32kb of the ISO volume later */ 653 hce->hfs_hdr_size = vol.mdb.drAlBlSt; 654 655 /* size of the extents and catalog files. This will be added 656 to the end of the ISO volume later */ 657 hce->hfs_ce_size = vol.mdb.drXTClpSiz + vol.mdb.drCTClpSiz; 658 659 /* we also allocate space for the Desktop file and the alternative 660 MDB while we're here */ 661 FREE(hce->hfs_ce); 662 hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz 663 + vol.mdb.drAlBlkSiz)); 664 665 /* allocate memory for the map and hdr */ 666 FREE(hce->hfs_map); 667 hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size) 668 *HFS_BLOCKSZ)); 669 670 if (hce->hfs_ce == 0 || hce->hfs_map == 0) 671 { 672 ERROR(ENOMEM, 0); 673 result = -1; 674 } 675 676 /* hfs_hdr is immediately after the hfs_map */ 677 hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ; 678 679 /* size needed in HFS_BLOCKSZ blocks for later use */ 680 hce->hfs_ce_size /= HFS_BLOCKSZ; 681 682 /* note size of Desktop file */ 683 hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ; 684 685 /* total size of catalog/extents and desktop */ 686 hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size; 687 688 /* alternative MDB in the last alocation block */ 689 hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ; 690 691 /* add the MDB to the total size */ 692 hce->hfs_tot_size += vol.lpa; 693 694 /* store this info in the volume info */ 695 vol.hce = hce; 696 697 #endif /* APPLE_HYB */ 698 699 /* create extents overflow file */ 700 701 ext->f.vol = &vol; 702 ext->f.parid = 0; 703 strcpy(ext->f.name, "extents overflow"); 704 705 ext->f.cat.cdrType = cdrFilRec; 706 /* ext->f.cat.cdrResrv2 */ 707 ext->f.cat.u.fil.filFlags = 0; 708 ext->f.cat.u.fil.filTyp = 0; 709 /* ext->f.cat.u.fil.filUsrWds */ 710 ext->f.cat.u.fil.filFlNum = HFS_CNID_EXT; 711 ext->f.cat.u.fil.filStBlk = 0; 712 ext->f.cat.u.fil.filLgLen = 0; 713 ext->f.cat.u.fil.filPyLen = 0; 714 ext->f.cat.u.fil.filRStBlk = 0; 715 ext->f.cat.u.fil.filRLgLen = 0; 716 ext->f.cat.u.fil.filRPyLen = 0; 717 ext->f.cat.u.fil.filCrDat = vol.mdb.drCrDate; 718 ext->f.cat.u.fil.filMdDat = vol.mdb.drLsMod; 719 ext->f.cat.u.fil.filBkDat = 0; 720 /* ext->f.cat.u.fil.filFndrInfo */ 721 ext->f.cat.u.fil.filClpSize = 0; 722 723 for (i = 0; i < 3; ++i) 724 { 725 ext->f.cat.u.fil.filExtRec[i].xdrStABN = 0; 726 ext->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0; 727 728 ext->f.cat.u.fil.filRExtRec[i].xdrStABN = 0; 729 ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0; 730 } 731 /* ext->f.cat.u.fil.filResrv */ 732 f_selectfork(&ext->f, 0); 733 734 ext->f.clump = vol.mdb.drXTClpSiz; 735 ext->f.flags = 0; 736 737 ext->f.prev = ext->f.next = 0; 738 739 n_init(&ext->hdrnd, ext, ndHdrNode, 0); 740 741 ext->hdrnd.nnum = 0; 742 ext->hdrnd.nd.ndNRecs = 3; 743 ext->hdrnd.roff[1] = 0x078; 744 ext->hdrnd.roff[2] = 0x0f8; 745 ext->hdrnd.roff[3] = 0x1f8; 746 747 memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128); 748 749 ext->hdr.bthDepth = 0; 750 ext->hdr.bthRoot = 0; 751 ext->hdr.bthNRecs = 0; 752 ext->hdr.bthFNode = 0; 753 ext->hdr.bthLNode = 0; 754 ext->hdr.bthNodeSize = HFS_BLOCKSZ; 755 ext->hdr.bthKeyLen = 0x07; 756 ext->hdr.bthNNodes = 0; 757 ext->hdr.bthFree = 0; 758 for (i = 0; i < 76; ++i) 759 ext->hdr.bthResv[i] = 0; 760 761 map = ALLOC(char, HFS_MAP1SZ); 762 if (map == 0) 763 { 764 if (result == 0) 765 { 766 ERROR(ENOMEM, 0); 767 result = -1; 768 } 769 } 770 else 771 { 772 memset(map, 0, HFS_MAP1SZ); 773 BMSET(map, 0); 774 } 775 776 ext->map = map; 777 ext->mapsz = HFS_MAP1SZ; 778 ext->flags = HFS_UPDATE_BTHDR; 779 ext->compare = r_compareextkeys; 780 781 if (result == 0 && bt_space(ext, 1) < 0) 782 result = -1; 783 784 --ext->hdr.bthFree; 785 786 /* create catalog file */ 787 788 cat->f.vol = &vol; 789 cat->f.parid = 0; 790 strcpy(cat->f.name, "catalog"); 791 792 cat->f.cat.cdrType = cdrFilRec; 793 /* cat->f.cat.cdrResrv2 */ 794 cat->f.cat.u.fil.filFlags = 0; 795 cat->f.cat.u.fil.filTyp = 0; 796 /* cat->f.cat.u.fil.filUsrWds */ 797 cat->f.cat.u.fil.filFlNum = HFS_CNID_CAT; 798 cat->f.cat.u.fil.filStBlk = 0; 799 cat->f.cat.u.fil.filLgLen = 0; 800 cat->f.cat.u.fil.filPyLen = 0; 801 cat->f.cat.u.fil.filRStBlk = 0; 802 cat->f.cat.u.fil.filRLgLen = 0; 803 cat->f.cat.u.fil.filRPyLen = 0; 804 cat->f.cat.u.fil.filCrDat = vol.mdb.drCrDate; 805 cat->f.cat.u.fil.filMdDat = vol.mdb.drLsMod; 806 cat->f.cat.u.fil.filBkDat = 0; 807 /* cat->f.cat.u.fil.filFndrInfo */ 808 cat->f.cat.u.fil.filClpSize = 0; 809 810 for (i = 0; i < 3; ++i) 811 { 812 cat->f.cat.u.fil.filExtRec[i].xdrStABN = 0; 813 cat->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0; 814 815 cat->f.cat.u.fil.filRExtRec[i].xdrStABN = 0; 816 cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0; 817 } 818 /* cat->f.cat.u.fil.filResrv */ 819 f_selectfork(&cat->f, 0); 820 821 cat->f.clump = vol.mdb.drCTClpSiz; 822 cat->f.flags = 0; 823 824 cat->f.prev = cat->f.next = 0; 825 826 n_init(&cat->hdrnd, cat, ndHdrNode, 0); 827 828 cat->hdrnd.nnum = 0; 829 cat->hdrnd.nd.ndNRecs = 3; 830 cat->hdrnd.roff[1] = 0x078; 831 cat->hdrnd.roff[2] = 0x0f8; 832 cat->hdrnd.roff[3] = 0x1f8; 833 834 memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128); 835 836 cat->hdr.bthDepth = 0; 837 cat->hdr.bthRoot = 0; 838 cat->hdr.bthNRecs = 0; 839 cat->hdr.bthFNode = 0; 840 cat->hdr.bthLNode = 0; 841 cat->hdr.bthNodeSize = HFS_BLOCKSZ; 842 cat->hdr.bthKeyLen = 0x25; 843 cat->hdr.bthNNodes = 0; 844 cat->hdr.bthFree = 0; 845 for (i = 0; i < 76; ++i) 846 cat->hdr.bthResv[i] = 0; 847 848 map = ALLOC(char, HFS_MAP1SZ); 849 if (map == 0) 850 { 851 if (result == 0) 852 { 853 ERROR(ENOMEM, 0); 854 result = -1; 855 } 856 } 857 else 858 { 859 memset(map, 0, HFS_MAP1SZ); 860 BMSET(map, 0); 861 } 862 863 cat->map = map; 864 cat->mapsz = HFS_MAP1SZ; 865 cat->flags = HFS_UPDATE_BTHDR; 866 cat->compare = r_comparecatkeys; 867 868 if (result == 0 && bt_space(cat, 1) < 0) 869 result = -1; 870 871 --cat->hdr.bthFree; 872 873 /* create root folder */ 874 875 if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0) 876 result = -1; 877 878 vol.mdb.drNxtCNID = 16; 879 880 /* finish up */ 881 882 if (result == 0) 883 { 884 block b; 885 886 /* write boot blocks */ 887 888 memset(&b, 0, sizeof(b)); 889 b_writelb(&vol, 0, &b); 890 b_writelb(&vol, 1, &b); 891 892 /* flush other disk state */ 893 894 vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM; 895 896 if (v_flush(&vol, 1) < 0) 897 result = -1; 898 } 899 #ifndef APPLE_HYB 900 if (close(vol.fd) < 0 && result == 0) 901 { 902 ERROR(errno, "error closing device"); 903 result = -1; 904 } 905 #endif /* APPLE_HYB */ 906 FREE(vol.ext.map); 907 FREE(vol.cat.map); 908 909 return result; 910 } 911 912 /* High-Level Directory Routines =========================================== */ 913 914 /* 915 * NAME: hfs->chdir() 916 * DESCRIPTION: change current HFS directory 917 */ 918 int hfs_chdir(hfsvol *vol, char *path) 919 { 920 CatDataRec data; 921 922 if (v_getvol(&vol) < 0 || 923 v_resolve(&vol, path, &data, 0, 0, 0) <= 0) 924 return -1; 925 926 if (data.cdrType != cdrDirRec) 927 { 928 ERROR(ENOTDIR, 0); 929 return -1; 930 } 931 932 vol->cwd = data.u.dir.dirDirID; 933 934 return 0; 935 } 936 937 /* 938 * NAME: hfs->getcwd() 939 * DESCRIPTION: return the current working directory ID 940 */ 941 long hfs_getcwd(hfsvol *vol) 942 { 943 if (v_getvol(&vol) < 0) 944 return 0; 945 946 return vol->cwd; 947 } 948 949 /* 950 * NAME: hfs->setcwd() 951 * DESCRIPTION: set the current working directory ID 952 */ 953 int hfs_setcwd(hfsvol *vol, long id) 954 { 955 if (v_getvol(&vol) < 0) 956 return -1; 957 958 if (id == vol->cwd) 959 return 0; 960 961 /* make sure the directory exists */ 962 963 if (v_getdthread(vol, id, 0, 0) <= 0) 964 return -1; 965 966 vol->cwd = id; 967 968 return 0; 969 } 970 971 /* 972 * NAME: hfs->dirinfo() 973 * DESCRIPTION: given a directory ID, return its (name and) parent ID 974 */ 975 int hfs_dirinfo(hfsvol *vol, long *id, char *name) 976 { 977 CatDataRec thread; 978 979 if (v_getvol(&vol) < 0 || 980 v_getdthread(vol, *id, &thread, 0) <= 0) 981 return -1; 982 983 *id = thread.u.dthd.thdParID; 984 985 if (name) 986 strcpy(name, thread.u.dthd.thdCName); 987 988 return 0; 989 } 990 991 /* 992 * NAME: hfs->opendir() 993 * DESCRIPTION: prepare to read the contents of a directory 994 */ 995 hfsdir *hfs_opendir(hfsvol *vol, char *path) 996 { 997 hfsdir *dir; 998 CatKeyRec key; 999 CatDataRec data; 1000 unsigned char pkey[HFS_CATKEYLEN]; 1001 1002 if (v_getvol(&vol) < 0) 1003 return 0; 1004 1005 dir = ALLOC(hfsdir, 1); 1006 if (dir == 0) 1007 { 1008 ERROR(ENOMEM, 0); 1009 return 0; 1010 } 1011 1012 dir->vol = vol; 1013 1014 if (*path == 0) 1015 { 1016 /* meta-directory containing root dirs from all mounted volumes */ 1017 1018 dir->dirid = 0; 1019 dir->vptr = hfs_mounts; 1020 } 1021 else 1022 { 1023 if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) 1024 { 1025 FREE(dir); 1026 return 0; 1027 } 1028 1029 if (data.cdrType != cdrDirRec) 1030 { 1031 FREE(dir); 1032 ERROR(ENOTDIR, 0); 1033 return 0; 1034 } 1035 1036 dir->dirid = data.u.dir.dirDirID; 1037 dir->vptr = 0; 1038 1039 r_makecatkey(&key, dir->dirid, ""); 1040 r_packcatkey(&key, pkey, 0); 1041 1042 if (bt_search(&vol->cat, pkey, &dir->n) <= 0) 1043 { 1044 FREE(dir); 1045 return 0; 1046 } 1047 } 1048 1049 dir->prev = 0; 1050 dir->next = vol->dirs; 1051 1052 if (vol->dirs) 1053 vol->dirs->prev = dir; 1054 1055 vol->dirs = dir; 1056 1057 return dir; 1058 } 1059 1060 /* 1061 * NAME: hfs->readdir() 1062 * DESCRIPTION: return the next entry in the directory 1063 */ 1064 int hfs_readdir(hfsdir *dir, hfsdirent *ent) 1065 { 1066 CatKeyRec key; 1067 CatDataRec data; 1068 unsigned char *ptr; 1069 1070 if (dir->dirid == 0) 1071 { 1072 hfsvol *vol; 1073 char cname[HFS_MAX_FLEN + 1]; 1074 1075 for (vol = hfs_mounts; vol; vol = vol->next) 1076 { 1077 if (vol == dir->vptr) 1078 break; 1079 } 1080 1081 if (vol == 0) 1082 { 1083 ERROR(ENOENT, "no more entries"); 1084 return -1; 1085 } 1086 1087 if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 || 1088 v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, 1089 &data, cname, 0) < 0) 1090 return -1; 1091 1092 r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); 1093 1094 dir->vptr = vol->next; 1095 1096 return 0; 1097 } 1098 1099 if (dir->n.rnum == -1) 1100 { 1101 ERROR(ENOENT, "no more entries"); 1102 return -1; 1103 } 1104 1105 while (1) 1106 { 1107 ++dir->n.rnum; 1108 1109 while (dir->n.rnum >= dir->n.nd.ndNRecs) 1110 { 1111 dir->n.nnum = dir->n.nd.ndFLink; 1112 if (dir->n.nnum == 0) 1113 { 1114 dir->n.rnum = -1; 1115 ERROR(ENOENT, "no more entries"); 1116 return -1; 1117 } 1118 1119 if (bt_getnode(&dir->n) < 0) 1120 { 1121 dir->n.rnum = -1; 1122 return -1; 1123 } 1124 1125 dir->n.rnum = 0; 1126 } 1127 1128 ptr = HFS_NODEREC(dir->n, dir->n.rnum); 1129 1130 r_unpackcatkey(ptr, &key); 1131 1132 if (key.ckrParID != dir->dirid) 1133 { 1134 dir->n.rnum = -1; 1135 ERROR(ENOENT, "no more entries"); 1136 return -1; 1137 } 1138 1139 r_unpackcatdata(HFS_RECDATA(ptr), &data); 1140 1141 switch (data.cdrType) 1142 { 1143 case cdrDirRec: 1144 case cdrFilRec: 1145 r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); 1146 return 0; 1147 1148 case cdrThdRec: 1149 case cdrFThdRec: 1150 break; 1151 1152 default: 1153 dir->n.rnum = -1; 1154 1155 ERROR(EIO, "unexpected directory entry found"); 1156 return -1; 1157 } 1158 } 1159 } 1160 1161 /* 1162 * NAME: hfs->closedir() 1163 * DESCRIPTION: stop reading a directory 1164 */ 1165 int hfs_closedir(hfsdir *dir) 1166 { 1167 hfsvol *vol = dir->vol; 1168 1169 if (dir->prev) 1170 dir->prev->next = dir->next; 1171 if (dir->next) 1172 dir->next->prev = dir->prev; 1173 if (dir == vol->dirs) 1174 vol->dirs = dir->next; 1175 1176 FREE(dir); 1177 1178 return 0; 1179 } 1180 1181 /* High-Level File Routines ================================================ */ 1182 1183 /* 1184 * NAME: hfs->open() 1185 * DESCRIPTION: prepare a file for I/O 1186 */ 1187 hfsfile *hfs_open(hfsvol *vol, char *path) 1188 { 1189 hfsfile *file; 1190 1191 if (v_getvol(&vol) < 0) 1192 return 0; 1193 1194 file = ALLOC(hfsfile, 1); 1195 if (file == 0) 1196 { 1197 ERROR(ENOMEM, 0); 1198 return 0; 1199 } 1200 1201 if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) 1202 { 1203 FREE(file); 1204 return 0; 1205 } 1206 1207 if (file->cat.cdrType != cdrFilRec) 1208 { 1209 FREE(file); 1210 ERROR(EISDIR, 0); 1211 return 0; 1212 } 1213 1214 file->vol = vol; 1215 file->clump = file->cat.u.fil.filClpSize; 1216 file->flags = 0; 1217 1218 f_selectfork(file, 0); 1219 1220 file->prev = 0; 1221 file->next = vol->files; 1222 1223 if (vol->files) 1224 vol->files->prev = file; 1225 1226 vol->files = file; 1227 1228 return file; 1229 } 1230 1231 /* 1232 * NAME: hfs->setfork() 1233 * DESCRIPTION: select file fork for I/O operations 1234 */ 1235 int hfs_setfork(hfsfile *file, int fork) 1236 { 1237 int result = 0; 1238 1239 if (! (file->vol->flags & HFS_READONLY) && 1240 f_trunc(file) < 0) 1241 result = -1; 1242 1243 f_selectfork(file, fork); 1244 1245 return result; 1246 } 1247 1248 /* 1249 * NAME: hfs->getfork() 1250 * DESCRIPTION: return the current fork for I/O operations 1251 */ 1252 int hfs_getfork(hfsfile *file) 1253 { 1254 return file->fork != fkData; 1255 } 1256 1257 /* 1258 * NAME: hfs->read() 1259 * DESCRIPTION: read from an open file 1260 */ 1261 long hfs_read(hfsfile *file, void *buf, unsigned long len) 1262 { 1263 unsigned long *lglen, count; 1264 unsigned char *ptr = buf; 1265 1266 f_getptrs(file, &lglen, 0, 0); 1267 1268 if (file->pos + len > *lglen) 1269 len = *lglen - file->pos; 1270 1271 count = len; 1272 while (count) 1273 { 1274 block b; 1275 unsigned long bnum, offs, chunk; 1276 1277 bnum = file->pos / HFS_BLOCKSZ; 1278 offs = file->pos % HFS_BLOCKSZ; 1279 1280 chunk = HFS_BLOCKSZ - offs; 1281 if (chunk > count) 1282 chunk = count; 1283 1284 if (f_getblock(file, bnum, &b) < 0) 1285 return -1; 1286 1287 memcpy(ptr, b + offs, chunk); 1288 ptr += chunk; 1289 1290 file->pos += chunk; 1291 count -= chunk; 1292 } 1293 1294 return len; 1295 } 1296 1297 /* 1298 * NAME: hfs->write() 1299 * DESCRIPTION: write to an open file 1300 */ 1301 long hfs_write(hfsfile *file, void *buf, unsigned long len) 1302 { 1303 unsigned long *lglen, *pylen, count; 1304 unsigned char *ptr = buf; 1305 1306 if (file->vol->flags & HFS_READONLY) 1307 { 1308 ERROR(EROFS, 0); 1309 return -1; 1310 } 1311 1312 f_getptrs(file, &lglen, &pylen, 0); 1313 1314 count = len; 1315 1316 /* set flag to update (at least) the modification time */ 1317 1318 if (count) 1319 { 1320 file->cat.u.fil.filMdDat = d_tomtime(time(0)); 1321 file->flags |= HFS_UPDATE_CATREC; 1322 } 1323 1324 while (count) 1325 { 1326 block b; 1327 unsigned long bnum, offs, chunk; 1328 1329 bnum = file->pos / HFS_BLOCKSZ; 1330 offs = file->pos % HFS_BLOCKSZ; 1331 1332 chunk = HFS_BLOCKSZ - offs; 1333 if (chunk > count) 1334 chunk = count; 1335 1336 if (file->pos + chunk > *pylen) 1337 { 1338 if (bt_space(&file->vol->ext, 1) < 0 || 1339 f_alloc(file) < 0) 1340 return -1; 1341 } 1342 #ifndef APPLE_HYB 1343 /* Ignore this part as we are always writing new files to an empty disk 1344 i.e. offs will always be 0 */ 1345 1346 if (offs > 0 || chunk < HFS_BLOCKSZ) 1347 { 1348 if (f_getblock(file, bnum, &b) < 0) 1349 return -1; 1350 } 1351 #endif /* APPLE_HYB */ 1352 memcpy(b + offs, ptr, chunk); 1353 ptr += chunk; 1354 1355 if (f_putblock(file, bnum, &b) < 0) 1356 return -1; 1357 1358 file->pos += chunk; 1359 count -= chunk; 1360 1361 if (file->pos > *lglen) 1362 *lglen = file->pos; 1363 } 1364 1365 return len; 1366 } 1367 1368 /* 1369 * NAME: hfs->truncate() 1370 * DESCRIPTION: truncate an open file 1371 */ 1372 int hfs_truncate(hfsfile *file, unsigned long len) 1373 { 1374 unsigned long *lglen; 1375 1376 f_getptrs(file, &lglen, 0, 0); 1377 1378 if (*lglen > len) 1379 { 1380 if (file->vol->flags & HFS_READONLY) 1381 { 1382 ERROR(EROFS, 0); 1383 return -1; 1384 } 1385 1386 *lglen = len; 1387 1388 file->cat.u.fil.filMdDat = d_tomtime(time(0)); 1389 file->flags |= HFS_UPDATE_CATREC; 1390 1391 if (file->pos > len) 1392 file->pos = len; 1393 } 1394 1395 return 0; 1396 } 1397 1398 /* 1399 * NAME: hfs->lseek() 1400 * DESCRIPTION: change file seek pointer 1401 */ 1402 long hfs_lseek(hfsfile *file, long offset, int from) 1403 { 1404 unsigned long *lglen; 1405 long newpos; 1406 1407 f_getptrs(file, &lglen, 0, 0); 1408 1409 switch (from) 1410 { 1411 case SEEK_SET: 1412 newpos = offset; 1413 break; 1414 1415 case SEEK_CUR: 1416 newpos = file->pos + offset; 1417 break; 1418 1419 case SEEK_END: 1420 newpos = *lglen + offset; 1421 break; 1422 1423 default: 1424 ERROR(EINVAL, 0); 1425 return -1; 1426 } 1427 1428 if (newpos < 0) 1429 newpos = 0; 1430 else if (newpos > *lglen) 1431 newpos = *lglen; 1432 1433 file->pos = newpos; 1434 1435 return newpos; 1436 } 1437 1438 /* 1439 * NAME: hfs->close() 1440 * DESCRIPTION: close a file 1441 */ 1442 #ifdef APPLE_HYB 1443 /* extra args are used to set the start of the forks in the ISO volume */ 1444 int hfs_close(hfsfile *file, long dext, long rext) 1445 { 1446 int offset; 1447 #else 1448 int hfs_close(hfsfile *file) 1449 { 1450 #endif /* APPLE_HYB */ 1451 hfsvol *vol = file->vol; 1452 int result = 0; 1453 1454 if (f_trunc(file) < 0 || 1455 f_flush(file) < 0) 1456 result = -1; 1457 1458 #ifdef APPLE_HYB 1459 /* "start" of file is relative to the first available block */ 1460 offset = vol->hce->hfs_hdr_size + vol->hce->hfs_map_size; 1461 /* update the "real" starting extent and re-flush the file */ 1462 if (dext) 1463 file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa; 1464 1465 if (rext) 1466 file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa; 1467 1468 if (dext || rext) 1469 file->flags |= HFS_UPDATE_CATREC; 1470 1471 if (f_flush(file) < 0) 1472 result = -1; 1473 #endif /*APPLE_HYB */ 1474 1475 if (file->prev) 1476 file->prev->next = file->next; 1477 if (file->next) 1478 file->next->prev = file->prev; 1479 if (file == vol->files) 1480 vol->files = file->next; 1481 1482 FREE(file); 1483 1484 return result; 1485 } 1486 1487 /* High-Level Catalog Routines ============================================= */ 1488 1489 /* 1490 * NAME: hfs->stat() 1491 * DESCRIPTION: return catalog information for an arbitrary path 1492 */ 1493 int hfs_stat(hfsvol *vol, char *path, hfsdirent *ent) 1494 { 1495 CatDataRec data; 1496 long parid; 1497 char name[HFS_MAX_FLEN + 1]; 1498 1499 if (v_getvol(&vol) < 0 || 1500 v_resolve(&vol, path, &data, &parid, name, 0) <= 0) 1501 return -1; 1502 1503 r_unpackdirent(parid, name, &data, ent); 1504 1505 return 0; 1506 } 1507 1508 /* 1509 * NAME: hfs->fstat() 1510 * DESCRIPTION: return catalog information for an open file 1511 */ 1512 int hfs_fstat(hfsfile *file, hfsdirent *ent) 1513 { 1514 r_unpackdirent(file->parid, file->name, &file->cat, ent); 1515 1516 return 0; 1517 } 1518 1519 /* 1520 * NAME: hfs->setattr() 1521 * DESCRIPTION: change a file's attributes 1522 */ 1523 int hfs_setattr(hfsvol *vol, char *path, hfsdirent *ent) 1524 { 1525 CatDataRec data; 1526 node n; 1527 1528 if (v_getvol(&vol) < 0 || 1529 v_resolve(&vol, path, &data, 0, 0, &n) <= 0) 1530 return -1; 1531 1532 if (vol->flags & HFS_READONLY) 1533 { 1534 ERROR(EROFS, 0); 1535 return -1; 1536 } 1537 1538 r_packdirent(&data, ent); 1539 1540 if (v_putcatrec(&data, &n) < 0) 1541 return -1; 1542 1543 return 0; 1544 } 1545 1546 /* 1547 * NAME: hfs->fsetattr() 1548 * DESCRIPTION: change an open file's attributes 1549 */ 1550 int hfs_fsetattr(hfsfile *file, hfsdirent *ent) 1551 { 1552 if (file->vol->flags & HFS_READONLY) 1553 { 1554 ERROR(EROFS, 0); 1555 return -1; 1556 } 1557 1558 r_packdirent(&file->cat, ent); 1559 1560 file->flags |= HFS_UPDATE_CATREC; 1561 1562 return 0; 1563 } 1564 1565 /* 1566 * NAME: hfs->mkdir() 1567 * DESCRIPTION: create a new directory 1568 */ 1569 int hfs_mkdir(hfsvol *vol, char *path) 1570 { 1571 CatDataRec data; 1572 long parid; 1573 char name[HFS_MAX_FLEN + 1]; 1574 int found; 1575 1576 if (v_getvol(&vol) < 0) 1577 return -1; 1578 1579 found = v_resolve(&vol, path, &data, &parid, name, 0); 1580 if (found < 0 || parid == 0) 1581 return -1; 1582 else if (found) 1583 { 1584 ERROR(EEXIST, 0); 1585 return -1; 1586 } 1587 1588 if (parid == HFS_CNID_ROOTPAR) 1589 { 1590 ERROR(EINVAL, 0); 1591 return -1; 1592 } 1593 1594 if (vol->flags & HFS_READONLY) 1595 { 1596 ERROR(EROFS, 0); 1597 return -1; 1598 } 1599 1600 if (v_newfolder(vol, parid, name) < 0) 1601 return -1; 1602 1603 return 0; 1604 } 1605 1606 /* 1607 * NAME: hfs->rmdir() 1608 * DESCRIPTION: delete an empty directory 1609 */ 1610 int hfs_rmdir(hfsvol *vol, char *path) 1611 { 1612 CatKeyRec key; 1613 CatDataRec data; 1614 long parid; 1615 char name[HFS_MAX_FLEN + 1]; 1616 unsigned char pkey[HFS_CATKEYLEN]; 1617 1618 if (v_getvol(&vol) < 0 || 1619 v_resolve(&vol, path, &data, &parid, name, 0) <= 0) 1620 return -1; 1621 1622 if (data.cdrType != cdrDirRec) 1623 { 1624 ERROR(ENOTDIR, 0); 1625 return -1; 1626 } 1627 1628 if (data.u.dir.dirVal != 0) 1629 { 1630 ERROR(ENOTEMPTY, 0); 1631 return -1; 1632 } 1633 1634 if (parid == HFS_CNID_ROOTPAR) 1635 { 1636 ERROR(EINVAL, 0); 1637 return -1; 1638 } 1639 1640 if (vol->flags & HFS_READONLY) 1641 { 1642 ERROR(EROFS, 0); 1643 return -1; 1644 } 1645 1646 /* delete directory record */ 1647 1648 r_makecatkey(&key, parid, name); 1649 r_packcatkey(&key, pkey, 0); 1650 1651 if (bt_delete(&vol->cat, pkey) < 0) 1652 return -1; 1653 1654 /* delete thread record */ 1655 1656 r_makecatkey(&key, data.u.dir.dirDirID, ""); 1657 r_packcatkey(&key, pkey, 0); 1658 1659 if (bt_delete(&vol->cat, pkey) < 0 || 1660 v_adjvalence(vol, parid, 1, -1) < 0) 1661 return -1; 1662 1663 return 0; 1664 } 1665 1666 /* 1667 * NAME: hfs->create() 1668 * DESCRIPTION: create a new file 1669 */ 1670 int hfs_create(hfsvol *vol, char *path, char *type, char *creator) 1671 { 1672 CatKeyRec key; 1673 CatDataRec data; 1674 long id, parid; 1675 char name[HFS_MAX_FLEN + 1]; 1676 unsigned char record[HFS_CATRECMAXLEN]; 1677 int found, i, reclen; 1678 1679 if (v_getvol(&vol) < 0) 1680 return -1; 1681 1682 found = v_resolve(&vol, path, &data, &parid, name, 0); 1683 if (found < 0 || parid == 0) 1684 return -1; 1685 else if (found) 1686 { 1687 ERROR(EEXIST, 0); 1688 return -1; 1689 } 1690 1691 if (parid == HFS_CNID_ROOTPAR) 1692 { 1693 ERROR(EINVAL, 0); 1694 return -1; 1695 } 1696 1697 if (vol->flags & HFS_READONLY) 1698 { 1699 ERROR(EROFS, 0); 1700 return -1; 1701 } 1702 1703 /* create file `name' in parent `parid' */ 1704 1705 if (bt_space(&vol->cat, 1) < 0) 1706 return -1; 1707 1708 id = vol->mdb.drNxtCNID++; 1709 vol->flags |= HFS_UPDATE_MDB; 1710 1711 /* create file record */ 1712 1713 data.cdrType = cdrFilRec; 1714 data.cdrResrv2 = 0; 1715 1716 data.u.fil.filFlags = 0; 1717 data.u.fil.filTyp = 0; 1718 1719 memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds)); 1720 1721 data.u.fil.filUsrWds.fdType = d_getl((unsigned char *) type); 1722 data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator); 1723 1724 data.u.fil.filFlNum = id; 1725 data.u.fil.filStBlk = 0; 1726 data.u.fil.filLgLen = 0; 1727 data.u.fil.filPyLen = 0; 1728 data.u.fil.filRStBlk = 0; 1729 data.u.fil.filRLgLen = 0; 1730 data.u.fil.filRPyLen = 0; 1731 data.u.fil.filCrDat = d_tomtime(time(0)); 1732 data.u.fil.filMdDat = data.u.fil.filCrDat; 1733 data.u.fil.filBkDat = 0; 1734 1735 memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo)); 1736 1737 data.u.fil.filClpSize = 0; 1738 1739 for (i = 0; i < 3; ++i) 1740 { 1741 data.u.fil.filExtRec[i].xdrStABN = 0; 1742 data.u.fil.filExtRec[i].xdrNumABlks = 0; 1743 1744 data.u.fil.filRExtRec[i].xdrStABN = 0; 1745 data.u.fil.filRExtRec[i].xdrNumABlks = 0; 1746 } 1747 1748 data.u.fil.filResrv = 0; 1749 1750 r_makecatkey(&key, parid, name); 1751 r_packcatkey(&key, record, &reclen); 1752 r_packcatdata(&data, HFS_RECDATA(record), &reclen); 1753 1754 if (bt_insert(&vol->cat, record, reclen) < 0 || 1755 v_adjvalence(vol, parid, 0, 1) < 0) 1756 return -1; 1757 1758 return 0; 1759 } 1760 1761 /* 1762 * NAME: hfs->delete() 1763 * DESCRIPTION: remove both forks of a file 1764 */ 1765 int hfs_delete(hfsvol *vol, char *path) 1766 { 1767 hfsfile file; 1768 CatKeyRec key; 1769 unsigned char pkey[HFS_CATKEYLEN]; 1770 int found; 1771 1772 if (v_getvol(&vol) < 0 || 1773 v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) 1774 return -1; 1775 1776 if (file.cat.cdrType != cdrFilRec) 1777 { 1778 ERROR(EISDIR, 0); 1779 return -1; 1780 } 1781 1782 if (file.parid == HFS_CNID_ROOTPAR) 1783 { 1784 ERROR(EINVAL, 0); 1785 return -1; 1786 } 1787 1788 if (vol->flags & HFS_READONLY) 1789 { 1790 ERROR(EROFS, 0); 1791 return -1; 1792 } 1793 1794 /* free disk blocks */ 1795 1796 file.vol = vol; 1797 file.flags = 0; 1798 1799 file.cat.u.fil.filLgLen = 0; 1800 file.cat.u.fil.filRLgLen = 0; 1801 1802 f_selectfork(&file, 0); 1803 if (f_trunc(&file) < 0) 1804 return -1; 1805 1806 f_selectfork(&file, 1); 1807 if (f_trunc(&file) < 0) 1808 return -1; 1809 1810 /* delete file record */ 1811 1812 r_makecatkey(&key, file.parid, file.name); 1813 r_packcatkey(&key, pkey, 0); 1814 1815 if (bt_delete(&vol->cat, pkey) < 0 || 1816 v_adjvalence(vol, file.parid, 0, -1) < 0) 1817 return -1; 1818 1819 /* delete file thread, if any */ 1820 1821 found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); 1822 if (found < 0) 1823 return -1; 1824 1825 if (found) 1826 { 1827 r_makecatkey(&key, file.cat.u.fil.filFlNum, ""); 1828 r_packcatkey(&key, pkey, 0); 1829 1830 if (bt_delete(&vol->cat, pkey) < 0) 1831 return -1; 1832 } 1833 1834 return 0; 1835 } 1836 1837 /* 1838 * NAME: hfs->rename() 1839 * DESCRIPTION: change the name of and/or move a file or directory 1840 */ 1841 int hfs_rename(hfsvol *vol, char *srcpath, char *dstpath) 1842 { 1843 hfsvol *srcvol; 1844 CatDataRec src, dst; 1845 long srcid, dstid; 1846 CatKeyRec key; 1847 char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1]; 1848 unsigned char record[HFS_CATRECMAXLEN]; 1849 int found, isdir, moving, reclen; 1850 node n; 1851 1852 if (v_getvol(&vol) < 0 || 1853 v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0) 1854 return -1; 1855 1856 isdir = (src.cdrType == cdrDirRec); 1857 srcvol = vol; 1858 1859 found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0); 1860 if (found < 0) 1861 return -1; 1862 1863 if (vol != srcvol) 1864 { 1865 ERROR(EINVAL, "can't move across volumes"); 1866 return -1; 1867 } 1868 1869 if (dstid == 0) 1870 { 1871 ERROR(ENOENT, "bad destination path"); 1872 return -1; 1873 } 1874 1875 if (found && 1876 dst.cdrType == cdrDirRec && 1877 dst.u.dir.dirDirID != src.u.dir.dirDirID) 1878 { 1879 dstid = dst.u.dir.dirDirID; 1880 strcpy(dstname, srcname); 1881 1882 found = v_catsearch(vol, dstid, dstname, 0, 0, 0); 1883 if (found < 0) 1884 return -1; 1885 } 1886 1887 moving = (srcid != dstid); 1888 1889 if (found) 1890 { 1891 char *ptr; 1892 1893 ptr = strrchr(dstpath, ':'); 1894 if (ptr == 0) 1895 ptr = dstpath; 1896 else 1897 ++ptr; 1898 1899 if (*ptr) 1900 strcpy(dstname, ptr); 1901 1902 if (! moving && strcmp(srcname, dstname) == 0) 1903 return 0; /* source and destination are the same */ 1904 1905 if (moving || d_relstring(srcname, dstname)) 1906 { 1907 ERROR(EEXIST, "can't use destination name"); 1908 return -1; 1909 } 1910 } 1911 1912 /* can't move anything into the root directory's parent */ 1913 1914 if (moving && dstid == HFS_CNID_ROOTPAR) 1915 { 1916 ERROR(EINVAL, "can't move above root directory"); 1917 return -1; 1918 } 1919 1920 if (moving && isdir) 1921 { 1922 long id; 1923 1924 /* can't move root directory anywhere */ 1925 1926 if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR) 1927 { 1928 ERROR(EINVAL, "can't move root directory"); 1929 return -1; 1930 } 1931 1932 /* make sure we aren't trying to move a directory inside itself */ 1933 1934 for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID) 1935 { 1936 if (id == src.u.dir.dirDirID) 1937 { 1938 ERROR(EINVAL, "can't move directory inside itself"); 1939 return -1; 1940 } 1941 1942 if (v_getdthread(vol, id, &dst, 0) <= 0) 1943 return -1; 1944 } 1945 } 1946 1947 if (vol->flags & HFS_READONLY) 1948 { 1949 ERROR(EROFS, 0); 1950 return -1; 1951 } 1952 1953 /* change volume name */ 1954 1955 if (dstid == HFS_CNID_ROOTPAR) 1956 { 1957 if (strlen(dstname) > HFS_MAX_VLEN) 1958 { 1959 ERROR(ENAMETOOLONG, 0); 1960 return -1; 1961 } 1962 1963 strcpy(vol->mdb.drVN, dstname); 1964 vol->flags |= HFS_UPDATE_MDB; 1965 } 1966 1967 /* remove source record */ 1968 1969 r_makecatkey(&key, srcid, srcname); 1970 r_packcatkey(&key, record, 0); 1971 1972 if (bt_delete(&vol->cat, record) < 0) 1973 return -1; 1974 1975 /* insert destination record */ 1976 1977 r_makecatkey(&key, dstid, dstname); 1978 r_packcatkey(&key, record, &reclen); 1979 r_packcatdata(&src, HFS_RECDATA(record), &reclen); 1980 1981 if (bt_insert(&vol->cat, record, reclen) < 0) 1982 return -1; 1983 1984 /* update thread record */ 1985 1986 if (isdir) 1987 { 1988 if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0) 1989 return -1; 1990 1991 dst.u.dthd.thdParID = dstid; 1992 strcpy(dst.u.dthd.thdCName, dstname); 1993 1994 if (v_putcatrec(&dst, &n) < 0) 1995 return -1; 1996 } 1997 else 1998 { 1999 found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n); 2000 if (found < 0) 2001 return -1; 2002 2003 if (found) 2004 { 2005 dst.u.fthd.fthdParID = dstid; 2006 strcpy(dst.u.fthd.fthdCName, dstname); 2007 2008 if (v_putcatrec(&dst, &n) < 0) 2009 return -1; 2010 } 2011 } 2012 2013 /* update directory valences */ 2014 2015 if (moving) 2016 { 2017 if (v_adjvalence(vol, srcid, isdir, -1) < 0 || 2018 v_adjvalence(vol, dstid, isdir, 1) < 0) 2019 return -1; 2020 } 2021 2022 return 0; 2023 } 2024 #ifdef APPLE_HYB 2025 /* 2026 * NAME: hfs->hfs_get_drAllocPtr() 2027 * DESCRIPTION: get the current start of next allocation search 2028 */ 2029 unsigned short 2030 hfs_get_drAllocPtr(hfsfile *file) 2031 { 2032 return(file->vol->mdb.drAllocPtr); 2033 } 2034 2035 /* 2036 * NAME: hfs->hfs_set_drAllocPtr() 2037 * DESCRIPTION: set the current start of next allocation search 2038 */ 2039 int 2040 hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size) 2041 { 2042 hfsvol *vol = file->vol; 2043 int result = 0; 2044 2045 /* truncate the current fork */ 2046 if (f_trunc(file) < 0 || 2047 f_flush(file) < 0) 2048 result = -1; 2049 2050 /* convert the fork size into allocation blocks */ 2051 size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz; 2052 2053 /* set the start of next allocation search to be after this fork */ 2054 vol->mdb.drAllocPtr = drAllocPtr + size; 2055 2056 vol->flags |= HFS_UPDATE_MDB; 2057 2058 return result; 2059 } 2060 2061 /* 2062 * NAME: hfs->vsetbless() 2063 * DESCRIPTION: set blessed folder 2064 * 2065 * adapted from vsetattr() from v3.2.6 2066 */ 2067 void 2068 hfs_vsetbless(hfsvol *vol, unsigned long cnid) 2069 { 2070 vol->mdb.drFndrInfo[0] = cnid; 2071 2072 vol->flags |= HFS_UPDATE_MDB; 2073 } 2074 #endif /* APPLE_HYB */ 2075