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