|
|
1.1 ! root 1: /* ! 2: * libhfs - library for reading and writing Macintosh HFS volumes ! 3: * Copyright (C) 1996-1998 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., 51 Franklin Street - Fifth Floor, Boston, ! 18: * MA 02110-1301, USA. ! 19: * ! 20: * $Id: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $ ! 21: */ ! 22: ! 23: #include "config.h" ! 24: #include "libhfs.h" ! 25: #include "data.h" ! 26: #include "block.h" ! 27: #include "medium.h" ! 28: #include "file.h" ! 29: #include "btree.h" ! 30: #include "node.h" ! 31: #include "record.h" ! 32: #include "volume.h" ! 33: ! 34: const char *hfs_error = "no error"; /* static error string */ ! 35: ! 36: hfsvol *hfs_mounts; /* linked list of mounted volumes */ ! 37: ! 38: static ! 39: hfsvol *curvol; /* current volume */ ! 40: ! 41: ! 42: /* ! 43: * NAME: getvol() ! 44: * DESCRIPTION: validate a volume reference ! 45: */ ! 46: static ! 47: int getvol(hfsvol **vol) ! 48: { ! 49: if (*vol == NULL) ! 50: { ! 51: if (curvol == NULL) ! 52: ERROR(EINVAL, "no volume is current"); ! 53: ! 54: *vol = curvol; ! 55: } ! 56: ! 57: return 0; ! 58: ! 59: fail: ! 60: return -1; ! 61: } ! 62: ! 63: /* High-Level Volume Routines ============================================== */ ! 64: ! 65: /* ! 66: * NAME: hfs->mount() ! 67: * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) ! 68: */ ! 69: hfsvol *hfs_mount( int os_fd, int pnum) ! 70: { ! 71: hfsvol *vol, *check; ! 72: int mode = HFS_MODE_RDONLY; ! 73: ! 74: /* see if the volume is already mounted */ ! 75: for (check = hfs_mounts; check; check = check->next) ! 76: { ! 77: if (check->pnum == pnum && v_same(check, os_fd) == 1) ! 78: { ! 79: vol = check; ! 80: goto done; ! 81: } ! 82: } ! 83: ! 84: vol = ALLOC(hfsvol, 1); ! 85: if (vol == NULL) ! 86: ERROR(ENOMEM, NULL); ! 87: ! 88: v_init(vol, mode); ! 89: ! 90: vol->flags |= HFS_VOL_READONLY; ! 91: if( v_open(vol, os_fd) == -1 ) ! 92: goto fail; ! 93: ! 94: /* mount the volume */ ! 95: ! 96: if (v_geometry(vol, pnum) == -1 || ! 97: v_mount(vol) == -1) ! 98: goto fail; ! 99: ! 100: /* add to linked list of volumes */ ! 101: ! 102: vol->prev = NULL; ! 103: vol->next = hfs_mounts; ! 104: ! 105: if (hfs_mounts) ! 106: hfs_mounts->prev = vol; ! 107: ! 108: hfs_mounts = vol; ! 109: ! 110: done: ! 111: ++vol->refs; ! 112: curvol = vol; ! 113: ! 114: return vol; ! 115: ! 116: fail: ! 117: if (vol) ! 118: { ! 119: v_close(vol); ! 120: FREE(vol); ! 121: } ! 122: ! 123: return NULL; ! 124: } ! 125: ! 126: ! 127: /* ! 128: * NAME: hfs->umount() ! 129: * DESCRIPTION: close an HFS volume ! 130: */ ! 131: int hfs_umount(hfsvol *vol) ! 132: { ! 133: int result = 0; ! 134: ! 135: if (getvol(&vol) == -1) ! 136: goto fail; ! 137: ! 138: if (--vol->refs) ! 139: { ! 140: goto done; ! 141: } ! 142: ! 143: /* close all open files and directories */ ! 144: ! 145: while (vol->files) ! 146: { ! 147: if (hfs_close(vol->files) == -1) ! 148: result = -1; ! 149: } ! 150: ! 151: while (vol->dirs) ! 152: { ! 153: if (hfs_closedir(vol->dirs) == -1) ! 154: result = -1; ! 155: } ! 156: ! 157: /* close medium */ ! 158: ! 159: if (v_close(vol) == -1) ! 160: result = -1; ! 161: ! 162: /* remove from linked list of volumes */ ! 163: ! 164: if (vol->prev) ! 165: vol->prev->next = vol->next; ! 166: if (vol->next) ! 167: vol->next->prev = vol->prev; ! 168: ! 169: if (vol == hfs_mounts) ! 170: hfs_mounts = vol->next; ! 171: if (vol == curvol) ! 172: curvol = NULL; ! 173: ! 174: FREE(vol); ! 175: ! 176: done: ! 177: return result; ! 178: ! 179: fail: ! 180: return -1; ! 181: } ! 182: ! 183: /* ! 184: * NAME: hfs->umountall() ! 185: * DESCRIPTION: unmount all mounted volumes ! 186: */ ! 187: void hfs_umountall(void) ! 188: { ! 189: while (hfs_mounts) ! 190: hfs_umount(hfs_mounts); ! 191: } ! 192: ! 193: /* ! 194: * NAME: hfs->getvol() ! 195: * DESCRIPTION: return a pointer to a mounted volume ! 196: */ ! 197: hfsvol *hfs_getvol(const char *name) ! 198: { ! 199: hfsvol *vol; ! 200: ! 201: if (name == NULL) ! 202: return curvol; ! 203: ! 204: for (vol = hfs_mounts; vol; vol = vol->next) ! 205: { ! 206: if (d_relstring(name, vol->mdb.drVN) == 0) ! 207: return vol; ! 208: } ! 209: ! 210: return NULL; ! 211: } ! 212: ! 213: /* ! 214: * NAME: hfs->setvol() ! 215: * DESCRIPTION: change the current volume ! 216: */ ! 217: void hfs_setvol(hfsvol *vol) ! 218: { ! 219: curvol = vol; ! 220: } ! 221: ! 222: /* ! 223: * NAME: hfs->vstat() ! 224: * DESCRIPTION: return volume statistics ! 225: */ ! 226: int hfs_vstat(hfsvol *vol, hfsvolent *ent) ! 227: { ! 228: if (getvol(&vol) == -1) ! 229: goto fail; ! 230: ! 231: strcpy(ent->name, vol->mdb.drVN); ! 232: ! 233: ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0; ! 234: ! 235: ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; ! 236: ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; ! 237: ! 238: ent->alblocksz = vol->mdb.drAlBlkSiz; ! 239: ent->clumpsz = vol->mdb.drClpSiz; ! 240: ! 241: ent->numfiles = vol->mdb.drFilCnt; ! 242: ent->numdirs = vol->mdb.drDirCnt; ! 243: ! 244: ent->crdate = d_ltime(vol->mdb.drCrDate); ! 245: ent->mddate = d_ltime(vol->mdb.drLsMod); ! 246: ent->bkdate = d_ltime(vol->mdb.drVolBkUp); ! 247: ! 248: ent->blessed = vol->mdb.drFndrInfo[0]; ! 249: ! 250: return 0; ! 251: ! 252: fail: ! 253: return -1; ! 254: } ! 255: ! 256: ! 257: /* High-Level Directory Routines =========================================== */ ! 258: ! 259: /* ! 260: * NAME: hfs->chdir() ! 261: * DESCRIPTION: change current HFS directory ! 262: */ ! 263: int hfs_chdir(hfsvol *vol, const char *path) ! 264: { ! 265: CatDataRec data; ! 266: ! 267: if (getvol(&vol) == -1 || ! 268: v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0) ! 269: goto fail; ! 270: ! 271: if (data.cdrType != cdrDirRec) ! 272: ERROR(ENOTDIR, NULL); ! 273: ! 274: vol->cwd = data.u.dir.dirDirID; ! 275: ! 276: return 0; ! 277: ! 278: fail: ! 279: return -1; ! 280: } ! 281: ! 282: /* ! 283: * NAME: hfs->getcwd() ! 284: * DESCRIPTION: return the current working directory ID ! 285: */ ! 286: unsigned long hfs_getcwd(hfsvol *vol) ! 287: { ! 288: if (getvol(&vol) == -1) ! 289: return 0; ! 290: ! 291: return vol->cwd; ! 292: } ! 293: ! 294: /* ! 295: * NAME: hfs->setcwd() ! 296: * DESCRIPTION: set the current working directory ID ! 297: */ ! 298: int hfs_setcwd(hfsvol *vol, unsigned long id) ! 299: { ! 300: if (getvol(&vol) == -1) ! 301: goto fail; ! 302: ! 303: if (id == vol->cwd) ! 304: goto done; ! 305: ! 306: /* make sure the directory exists */ ! 307: ! 308: if (v_getdthread(vol, id, NULL, NULL) <= 0) ! 309: goto fail; ! 310: ! 311: vol->cwd = id; ! 312: ! 313: done: ! 314: return 0; ! 315: ! 316: fail: ! 317: return -1; ! 318: } ! 319: ! 320: /* ! 321: * NAME: hfs->dirinfo() ! 322: * DESCRIPTION: given a directory ID, return its (name and) parent ID ! 323: */ ! 324: int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name) ! 325: { ! 326: CatDataRec thread; ! 327: ! 328: if (getvol(&vol) == -1 || ! 329: v_getdthread(vol, *id, &thread, NULL) <= 0) ! 330: goto fail; ! 331: ! 332: *id = thread.u.dthd.thdParID; ! 333: ! 334: if (name) ! 335: strcpy(name, thread.u.dthd.thdCName); ! 336: ! 337: return 0; ! 338: ! 339: fail: ! 340: return -1; ! 341: } ! 342: ! 343: /* ! 344: * NAME: hfs->opendir() ! 345: * DESCRIPTION: prepare to read the contents of a directory ! 346: */ ! 347: hfsdir *hfs_opendir(hfsvol *vol, const char *path) ! 348: { ! 349: hfsdir *dir = NULL; ! 350: CatKeyRec key; ! 351: CatDataRec data; ! 352: byte pkey[HFS_CATKEYLEN]; ! 353: ! 354: if (getvol(&vol) == -1) ! 355: goto fail; ! 356: ! 357: dir = ALLOC(hfsdir, 1); ! 358: if (dir == NULL) ! 359: ERROR(ENOMEM, NULL); ! 360: ! 361: dir->vol = vol; ! 362: ! 363: if (*path == 0) ! 364: { ! 365: /* meta-directory containing root dirs from all mounted volumes */ ! 366: ! 367: dir->dirid = 0; ! 368: dir->vptr = hfs_mounts; ! 369: } ! 370: else ! 371: { ! 372: if (v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0) ! 373: goto fail; ! 374: ! 375: if (data.cdrType != cdrDirRec) ! 376: ERROR(ENOTDIR, NULL); ! 377: ! 378: dir->dirid = data.u.dir.dirDirID; ! 379: dir->vptr = NULL; ! 380: ! 381: r_makecatkey(&key, dir->dirid, ""); ! 382: r_packcatkey(&key, pkey, NULL); ! 383: ! 384: if (bt_search(&vol->cat, pkey, &dir->n) <= 0) ! 385: goto fail; ! 386: } ! 387: ! 388: dir->prev = NULL; ! 389: dir->next = vol->dirs; ! 390: ! 391: if (vol->dirs) ! 392: vol->dirs->prev = dir; ! 393: ! 394: vol->dirs = dir; ! 395: ! 396: return dir; ! 397: ! 398: fail: ! 399: FREE(dir); ! 400: return NULL; ! 401: } ! 402: ! 403: /* ! 404: * NAME: hfs->readdir() ! 405: * DESCRIPTION: return the next entry in the directory ! 406: */ ! 407: int hfs_readdir(hfsdir *dir, hfsdirent *ent) ! 408: { ! 409: CatKeyRec key; ! 410: CatDataRec data; ! 411: const byte *ptr; ! 412: ! 413: if (dir->dirid == 0) ! 414: { ! 415: hfsvol *vol; ! 416: char cname[HFS_MAX_FLEN + 1]; ! 417: ! 418: for (vol = hfs_mounts; vol; vol = vol->next) ! 419: { ! 420: if (vol == dir->vptr) ! 421: break; ! 422: } ! 423: ! 424: if (vol == NULL) ! 425: ERROR(ENOENT, "no more entries"); ! 426: ! 427: if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, NULL) <= 0 || ! 428: v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, ! 429: &data, cname, NULL) <= 0) ! 430: goto fail; ! 431: ! 432: r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); ! 433: ! 434: dir->vptr = vol->next; ! 435: ! 436: goto done; ! 437: } ! 438: ! 439: if (dir->n.rnum == -1) ! 440: ERROR(ENOENT, "no more entries"); ! 441: ! 442: while (1) ! 443: { ! 444: ++dir->n.rnum; ! 445: ! 446: while (dir->n.rnum >= dir->n.nd.ndNRecs) ! 447: { ! 448: if (dir->n.nd.ndFLink == 0) ! 449: { ! 450: dir->n.rnum = -1; ! 451: ERROR(ENOENT, "no more entries"); ! 452: } ! 453: ! 454: if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1) ! 455: { ! 456: dir->n.rnum = -1; ! 457: goto fail; ! 458: } ! 459: ! 460: dir->n.rnum = 0; ! 461: } ! 462: ! 463: ptr = HFS_NODEREC(dir->n, dir->n.rnum); ! 464: ! 465: r_unpackcatkey(ptr, &key); ! 466: ! 467: if (key.ckrParID != dir->dirid) ! 468: { ! 469: dir->n.rnum = -1; ! 470: ERROR(ENOENT, "no more entries"); ! 471: } ! 472: ! 473: r_unpackcatdata(HFS_RECDATA(ptr), &data); ! 474: ! 475: switch (data.cdrType) ! 476: { ! 477: case cdrDirRec: ! 478: case cdrFilRec: ! 479: r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); ! 480: goto done; ! 481: ! 482: case cdrThdRec: ! 483: case cdrFThdRec: ! 484: break; ! 485: ! 486: default: ! 487: dir->n.rnum = -1; ! 488: ERROR(EIO, "unexpected directory entry found"); ! 489: } ! 490: } ! 491: ! 492: done: ! 493: return 0; ! 494: ! 495: fail: ! 496: return -1; ! 497: } ! 498: ! 499: /* ! 500: * NAME: hfs->closedir() ! 501: * DESCRIPTION: stop reading a directory ! 502: */ ! 503: int hfs_closedir(hfsdir *dir) ! 504: { ! 505: hfsvol *vol = dir->vol; ! 506: ! 507: if (dir->prev) ! 508: dir->prev->next = dir->next; ! 509: if (dir->next) ! 510: dir->next->prev = dir->prev; ! 511: if (dir == vol->dirs) ! 512: vol->dirs = dir->next; ! 513: ! 514: FREE(dir); ! 515: ! 516: return 0; ! 517: } ! 518: ! 519: /* High-Level File Routines ================================================ */ ! 520: ! 521: /* ! 522: * NAME: hfs->open() ! 523: * DESCRIPTION: prepare a file for I/O ! 524: */ ! 525: hfsfile *hfs_open(hfsvol *vol, const char *path) ! 526: { ! 527: hfsfile *file = NULL; ! 528: ! 529: if (getvol(&vol) == -1) ! 530: goto fail; ! 531: ! 532: file = ALLOC(hfsfile, 1); ! 533: if (file == NULL) ! 534: ERROR(ENOMEM, NULL); ! 535: ! 536: if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0) ! 537: goto fail; ! 538: ! 539: if (file->cat.cdrType != cdrFilRec) ! 540: ERROR(EISDIR, NULL); ! 541: ! 542: /* package file handle for user */ ! 543: ! 544: file->vol = vol; ! 545: file->flags = 0; ! 546: ! 547: f_selectfork(file, fkData); ! 548: ! 549: file->prev = NULL; ! 550: file->next = vol->files; ! 551: ! 552: if (vol->files) ! 553: vol->files->prev = file; ! 554: ! 555: vol->files = file; ! 556: ! 557: return file; ! 558: ! 559: fail: ! 560: FREE(file); ! 561: return NULL; ! 562: } ! 563: ! 564: /* ! 565: * NAME: hfs->setfork() ! 566: * DESCRIPTION: select file fork for I/O operations ! 567: */ ! 568: int hfs_setfork(hfsfile *file, int fork) ! 569: { ! 570: int result = 0; ! 571: ! 572: f_selectfork(file, fork ? fkRsrc : fkData); ! 573: ! 574: return result; ! 575: } ! 576: ! 577: /* ! 578: * NAME: hfs->getfork() ! 579: * DESCRIPTION: return the current fork for I/O operations ! 580: */ ! 581: int hfs_getfork(hfsfile *file) ! 582: { ! 583: return file->fork != fkData; ! 584: } ! 585: ! 586: /* ! 587: * NAME: hfs->read() ! 588: * DESCRIPTION: read from an open file ! 589: */ ! 590: unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len) ! 591: { ! 592: unsigned long *lglen, count; ! 593: byte *ptr = buf; ! 594: ! 595: f_getptrs(file, NULL, &lglen, NULL); ! 596: ! 597: if (file->pos + len > *lglen) ! 598: len = *lglen - file->pos; ! 599: ! 600: count = len; ! 601: while (count) ! 602: { ! 603: unsigned long bnum, offs, chunk; ! 604: ! 605: bnum = file->pos >> HFS_BLOCKSZ_BITS; ! 606: offs = file->pos & (HFS_BLOCKSZ - 1); ! 607: ! 608: chunk = HFS_BLOCKSZ - offs; ! 609: if (chunk > count) ! 610: chunk = count; ! 611: ! 612: if (offs == 0 && chunk == HFS_BLOCKSZ) ! 613: { ! 614: if (f_getblock(file, bnum, (block *) ptr) == -1) ! 615: goto fail; ! 616: } ! 617: else ! 618: { ! 619: block b; ! 620: ! 621: if (f_getblock(file, bnum, &b) == -1) ! 622: goto fail; ! 623: ! 624: memcpy(ptr, b + offs, chunk); ! 625: } ! 626: ! 627: ptr += chunk; ! 628: ! 629: file->pos += chunk; ! 630: count -= chunk; ! 631: } ! 632: ! 633: return len; ! 634: ! 635: fail: ! 636: return -1; ! 637: } ! 638: ! 639: /* ! 640: * NAME: hfs->seek() ! 641: * DESCRIPTION: change file seek pointer ! 642: */ ! 643: unsigned long hfs_seek(hfsfile *file, long offset, int from) ! 644: { ! 645: unsigned long *lglen, newpos; ! 646: ! 647: f_getptrs(file, NULL, &lglen, NULL); ! 648: ! 649: switch (from) ! 650: { ! 651: case HFS_SEEK_SET: ! 652: newpos = (offset < 0) ? 0 : offset; ! 653: break; ! 654: ! 655: case HFS_SEEK_CUR: ! 656: if (offset < 0 && (unsigned long) -offset > file->pos) ! 657: newpos = 0; ! 658: else ! 659: newpos = file->pos + offset; ! 660: break; ! 661: ! 662: case HFS_SEEK_END: ! 663: if (offset < 0 && (unsigned long) -offset > *lglen) ! 664: newpos = 0; ! 665: else ! 666: newpos = *lglen + offset; ! 667: break; ! 668: ! 669: default: ! 670: ERROR(EINVAL, NULL); ! 671: } ! 672: ! 673: if (newpos > *lglen) ! 674: newpos = *lglen; ! 675: ! 676: file->pos = newpos; ! 677: ! 678: return newpos; ! 679: ! 680: fail: ! 681: return -1; ! 682: } ! 683: ! 684: /* ! 685: * NAME: hfs->close() ! 686: * DESCRIPTION: close a file ! 687: */ ! 688: int hfs_close(hfsfile *file) ! 689: { ! 690: hfsvol *vol = file->vol; ! 691: int result = 0; ! 692: ! 693: if (file->prev) ! 694: file->prev->next = file->next; ! 695: if (file->next) ! 696: file->next->prev = file->prev; ! 697: if (file == vol->files) ! 698: vol->files = file->next; ! 699: ! 700: FREE(file); ! 701: ! 702: return result; ! 703: } ! 704: ! 705: /* High-Level Catalog Routines ============================================= */ ! 706: ! 707: /* ! 708: * NAME: hfs->stat() ! 709: * DESCRIPTION: return catalog information for an arbitrary path ! 710: */ ! 711: int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent) ! 712: { ! 713: CatDataRec data; ! 714: unsigned long parid; ! 715: char name[HFS_MAX_FLEN + 1]; ! 716: ! 717: if (getvol(&vol) == -1 || ! 718: v_resolve(&vol, path, &data, &parid, name, NULL) <= 0) ! 719: goto fail; ! 720: ! 721: r_unpackdirent(parid, name, &data, ent); ! 722: ! 723: return 0; ! 724: ! 725: fail: ! 726: return -1; ! 727: } ! 728: ! 729: /* ! 730: * NAME: hfs->fstat() ! 731: * DESCRIPTION: return catalog information for an open file ! 732: */ ! 733: int hfs_fstat(hfsfile *file, hfsdirent *ent) ! 734: { ! 735: r_unpackdirent(file->parid, file->name, &file->cat, ent); ! 736: ! 737: return 0; ! 738: } ! 739: ! 740: /* ! 741: * NAME: hfs->probe() ! 742: * DESCRIPTION: return whether a HFS filesystem is present at the given offset ! 743: */ ! 744: int hfs_probe(int fd, long long offset) ! 745: { ! 746: return v_probe(fd, offset); ! 747: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.