|
|
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: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $ ! 21: */ ! 22: ! 23: #include "config.h" ! 24: #include "libhfs.h" ! 25: #include "volume.h" ! 26: #include "data.h" ! 27: #include "block.h" ! 28: #include "low.h" ! 29: #include "medium.h" ! 30: #include "file.h" ! 31: #include "btree.h" ! 32: #include "record.h" ! 33: #include "os.h" ! 34: ! 35: #include "libc/byteorder.h" ! 36: ! 37: /* ! 38: * NAME: vol->init() ! 39: * DESCRIPTION: initialize volume structure ! 40: */ ! 41: void v_init(hfsvol *vol, int flags) ! 42: { ! 43: btree *ext = &vol->ext; ! 44: btree *cat = &vol->cat; ! 45: ! 46: vol->os_fd = 0; ! 47: vol->flags = flags & HFS_VOL_OPT_MASK; ! 48: ! 49: vol->pnum = -1; ! 50: vol->vstart = 0; ! 51: vol->vlen = 0; ! 52: vol->lpa = 0; ! 53: ! 54: vol->cache = NULL; ! 55: ! 56: vol->vbm = NULL; ! 57: vol->vbmsz = 0; ! 58: ! 59: f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow"); ! 60: ! 61: ext->map = NULL; ! 62: ext->mapsz = 0; ! 63: ext->flags = 0; ! 64: ! 65: ext->keyunpack = (keyunpackfunc) r_unpackextkey; ! 66: ext->keycompare = (keycomparefunc) r_compareextkeys; ! 67: ! 68: f_init(&cat->f, vol, HFS_CNID_CAT, "catalog"); ! 69: ! 70: cat->map = NULL; ! 71: cat->mapsz = 0; ! 72: cat->flags = 0; ! 73: ! 74: cat->keyunpack = (keyunpackfunc) r_unpackcatkey; ! 75: cat->keycompare = (keycomparefunc) r_comparecatkeys; ! 76: ! 77: vol->cwd = HFS_CNID_ROOTDIR; ! 78: ! 79: vol->refs = 0; ! 80: vol->files = NULL; ! 81: vol->dirs = NULL; ! 82: ! 83: vol->prev = NULL; ! 84: vol->next = NULL; ! 85: } ! 86: ! 87: /* ! 88: * NAME: vol->open() ! 89: * DESCRIPTION: open volume source and lock against concurrent updates ! 90: */ ! 91: int v_open(hfsvol *vol, int os_fd ) ! 92: { ! 93: if (vol->flags & HFS_VOL_OPEN) ! 94: ERROR(EINVAL, "volume already open"); ! 95: ! 96: vol->flags |= HFS_VOL_OPEN; ! 97: vol->os_fd = os_fd; ! 98: ! 99: /* initialize volume block cache (OK to fail) */ ! 100: ! 101: if (! (vol->flags & HFS_OPT_NOCACHE) && ! 102: b_init(vol) != -1) ! 103: vol->flags |= HFS_VOL_USINGCACHE; ! 104: ! 105: return 0; ! 106: ! 107: fail: ! 108: return -1; ! 109: } ! 110: ! 111: /* ! 112: * NAME: vol->close() ! 113: * DESCRIPTION: close access path to volume source ! 114: */ ! 115: int v_close(hfsvol *vol) ! 116: { ! 117: int result = 0; ! 118: ! 119: if (! (vol->flags & HFS_VOL_OPEN)) ! 120: goto done; ! 121: ! 122: if ((vol->flags & HFS_VOL_USINGCACHE) && ! 123: b_finish(vol) == -1) ! 124: result = -1; ! 125: ! 126: vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE); ! 127: ! 128: /* free dynamically allocated structures */ ! 129: ! 130: FREE(vol->vbm); ! 131: ! 132: vol->vbm = NULL; ! 133: vol->vbmsz = 0; ! 134: ! 135: FREE(vol->ext.map); ! 136: FREE(vol->cat.map); ! 137: ! 138: vol->ext.map = NULL; ! 139: vol->cat.map = NULL; ! 140: ! 141: done: ! 142: return result; ! 143: } ! 144: ! 145: /* ! 146: * NAME: vol->same() ! 147: * DESCRIPTION: return 1 iff path is same as open volume ! 148: */ ! 149: int v_same(hfsvol *vol, int os_fd ) ! 150: { ! 151: return vol->os_fd == os_fd; ! 152: } ! 153: ! 154: /* ! 155: * NAME: vol->geometry() ! 156: * DESCRIPTION: determine volume location and size (possibly in a partition) ! 157: */ ! 158: int v_geometry(hfsvol *vol, int pnum) ! 159: { ! 160: Partition map; ! 161: unsigned long bnum = 0; ! 162: int found; ! 163: ! 164: vol->pnum = pnum; ! 165: ! 166: if (pnum == 0) ! 167: { ! 168: vol->vstart = 0; ! 169: vol->vlen = b_size(vol); ! 170: ! 171: if (vol->vlen == 0) ! 172: goto fail; ! 173: } ! 174: else ! 175: { ! 176: while (pnum--) ! 177: { ! 178: found = m_findpmentry(vol, "Apple_HFS", &map, &bnum); ! 179: if (found == -1 || ! found) ! 180: goto fail; ! 181: } ! 182: ! 183: vol->vstart = map.pmPyPartStart; ! 184: vol->vlen = map.pmPartBlkCnt; ! 185: ! 186: if (map.pmDataCnt) ! 187: { ! 188: if ((unsigned long) map.pmLgDataStart + ! 189: (unsigned long) map.pmDataCnt > vol->vlen) ! 190: ERROR(EINVAL, "partition data overflows partition"); ! 191: ! 192: vol->vstart += (unsigned long) map.pmLgDataStart; ! 193: vol->vlen = map.pmDataCnt; ! 194: } ! 195: ! 196: if (vol->vlen == 0) ! 197: ERROR(EINVAL, "volume partition is empty"); ! 198: } ! 199: ! 200: if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS)) ! 201: ERROR(EINVAL, "volume is smaller than 800K"); ! 202: ! 203: return 0; ! 204: ! 205: fail: ! 206: return -1; ! 207: } ! 208: ! 209: /* ! 210: * NAME: vol->readmdb() ! 211: * DESCRIPTION: load Master Directory Block into memory ! 212: */ ! 213: int v_readmdb(hfsvol *vol) ! 214: { ! 215: if (l_getmdb(vol, &vol->mdb, 0) == -1) ! 216: goto fail; ! 217: ! 218: if (vol->mdb.drSigWord != HFS_SIGWORD) ! 219: { ! 220: if (vol->mdb.drSigWord == HFS_SIGWORD_MFS) ! 221: ERROR(EINVAL, "MFS volume format not supported"); ! 222: else ! 223: ERROR(EINVAL, "not a Macintosh HFS volume"); ! 224: } ! 225: ! 226: if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) ! 227: ERROR(EINVAL, "bad volume allocation block size"); ! 228: ! 229: vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS; ! 230: ! 231: /* extents pseudo-file structs */ ! 232: ! 233: vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN; ! 234: vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize; ! 235: vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize; ! 236: ! 237: vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; ! 238: vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; ! 239: ! 240: memcpy(&vol->ext.f.cat.u.fil.filExtRec, ! 241: &vol->mdb.drXTExtRec, sizeof(ExtDataRec)); ! 242: ! 243: f_selectfork(&vol->ext.f, fkData); ! 244: ! 245: /* catalog pseudo-file structs */ ! 246: ! 247: vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN; ! 248: vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize; ! 249: vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize; ! 250: ! 251: vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; ! 252: vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; ! 253: ! 254: memcpy(&vol->cat.f.cat.u.fil.filExtRec, ! 255: &vol->mdb.drCTExtRec, sizeof(ExtDataRec)); ! 256: ! 257: f_selectfork(&vol->cat.f, fkData); ! 258: ! 259: return 0; ! 260: ! 261: fail: ! 262: return -1; ! 263: } ! 264: ! 265: /* ! 266: * NAME: vol->readvbm() ! 267: * DESCRIPTION: read volume bitmap into memory ! 268: */ ! 269: int v_readvbm(hfsvol *vol) ! 270: { ! 271: unsigned int vbmst = vol->mdb.drVBMSt; ! 272: unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12; ! 273: block *bp; ! 274: ! 275: ASSERT(vol->vbm == 0); ! 276: ! 277: if (vol->mdb.drAlBlSt - vbmst < vbmsz) ! 278: ERROR(EIO, "volume bitmap collides with volume data"); ! 279: ! 280: vol->vbm = ALLOC(block, vbmsz); ! 281: if (vol->vbm == NULL) ! 282: ERROR(ENOMEM, NULL); ! 283: ! 284: vol->vbmsz = vbmsz; ! 285: ! 286: for (bp = vol->vbm; vbmsz--; ++bp) ! 287: { ! 288: if (b_readlb(vol, vbmst++, bp) == -1) ! 289: goto fail; ! 290: } ! 291: ! 292: return 0; ! 293: ! 294: fail: ! 295: FREE(vol->vbm); ! 296: ! 297: vol->vbm = NULL; ! 298: vol->vbmsz = 0; ! 299: ! 300: return -1; ! 301: } ! 302: ! 303: /* ! 304: * NAME: vol->mount() ! 305: * DESCRIPTION: load volume information into memory ! 306: */ ! 307: int v_mount(hfsvol *vol) ! 308: { ! 309: /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */ ! 310: ! 311: if (v_readmdb(vol) == -1 || ! 312: v_readvbm(vol) == -1 || ! 313: bt_readhdr(&vol->ext) == -1 || ! 314: bt_readhdr(&vol->cat) == -1) ! 315: goto fail; ! 316: ! 317: if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED) ! 318: vol->flags |= HFS_VOL_READONLY; ! 319: else if (vol->flags & HFS_VOL_READONLY) ! 320: vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; ! 321: else ! 322: vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; ! 323: ! 324: vol->flags |= HFS_VOL_MOUNTED; ! 325: ! 326: return 0; ! 327: ! 328: fail: ! 329: return -1; ! 330: } ! 331: ! 332: /* ! 333: * NAME: vol->catsearch() ! 334: * DESCRIPTION: search catalog tree ! 335: */ ! 336: int v_catsearch(hfsvol *vol, unsigned long parid, const char *name, ! 337: CatDataRec *data, char *cname, node *np) ! 338: { ! 339: CatKeyRec key; ! 340: byte pkey[HFS_CATKEYLEN]; ! 341: const byte *ptr; ! 342: node n; ! 343: int found; ! 344: ! 345: if (np == NULL) ! 346: np = &n; ! 347: ! 348: r_makecatkey(&key, parid, name); ! 349: r_packcatkey(&key, pkey, NULL); ! 350: ! 351: found = bt_search(&vol->cat, pkey, np); ! 352: if (found <= 0) ! 353: return found; ! 354: ! 355: ptr = HFS_NODEREC(*np, np->rnum); ! 356: ! 357: if (cname) ! 358: { ! 359: r_unpackcatkey(ptr, &key); ! 360: strcpy(cname, key.ckrCName); ! 361: } ! 362: ! 363: if (data) ! 364: r_unpackcatdata(HFS_RECDATA(ptr), data); ! 365: ! 366: return 1; ! 367: } ! 368: ! 369: /* ! 370: * NAME: vol->extsearch() ! 371: * DESCRIPTION: search extents tree ! 372: */ ! 373: int v_extsearch(hfsfile *file, unsigned int fabn, ! 374: ExtDataRec *data, node *np) ! 375: { ! 376: ExtKeyRec key; ! 377: ExtDataRec extsave; ! 378: unsigned int fabnsave; ! 379: byte pkey[HFS_EXTKEYLEN]; ! 380: const byte *ptr; ! 381: node n; ! 382: int found; ! 383: ! 384: if (np == NULL) ! 385: np = &n; ! 386: ! 387: r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn); ! 388: r_packextkey(&key, pkey, NULL); ! 389: ! 390: /* in case bt_search() clobbers these */ ! 391: ! 392: memcpy(&extsave, &file->ext, sizeof(ExtDataRec)); ! 393: fabnsave = file->fabn; ! 394: ! 395: found = bt_search(&file->vol->ext, pkey, np); ! 396: ! 397: memcpy(&file->ext, &extsave, sizeof(ExtDataRec)); ! 398: file->fabn = fabnsave; ! 399: ! 400: if (found <= 0) ! 401: return found; ! 402: ! 403: if (data) ! 404: { ! 405: ptr = HFS_NODEREC(*np, np->rnum); ! 406: r_unpackextdata(HFS_RECDATA(ptr), data); ! 407: } ! 408: ! 409: return 1; ! 410: } ! 411: ! 412: /* ! 413: * NAME: vol->getthread() ! 414: * DESCRIPTION: retrieve catalog thread information for a file or directory ! 415: */ ! 416: int v_getthread(hfsvol *vol, unsigned long id, ! 417: CatDataRec *thread, node *np, int type) ! 418: { ! 419: CatDataRec rec; ! 420: int found; ! 421: ! 422: if (thread == NULL) ! 423: thread = &rec; ! 424: ! 425: found = v_catsearch(vol, id, "", thread, NULL, np); ! 426: if (found == 1 && thread->cdrType != type) ! 427: ERROR(EIO, "bad thread record"); ! 428: ! 429: return found; ! 430: ! 431: fail: ! 432: return -1; ! 433: } ! 434: ! 435: ! 436: /* ! 437: * NAME: vol->resolve() ! 438: * DESCRIPTION: translate a pathname; return catalog information ! 439: */ ! 440: int v_resolve(hfsvol **vol, const char *path, ! 441: CatDataRec *data, unsigned long *parid, char *fname, node *np) ! 442: { ! 443: unsigned long dirid; ! 444: char name[HFS_MAX_FLEN + 1], *nptr; ! 445: int found = 0; ! 446: ! 447: if (*path == 0) ! 448: ERROR(ENOENT, "empty path"); ! 449: ! 450: if (parid) ! 451: *parid = 0; ! 452: ! 453: nptr = strchr(path, ':'); ! 454: ! 455: if (*path == ':' || nptr == NULL) ! 456: { ! 457: dirid = (*vol)->cwd; /* relative path */ ! 458: ! 459: if (*path == ':') ! 460: ++path; ! 461: ! 462: if (*path == 0) ! 463: { ! 464: found = v_getdthread(*vol, dirid, data, NULL); ! 465: if (found == -1) ! 466: goto fail; ! 467: ! 468: if (found) ! 469: { ! 470: if (parid) ! 471: *parid = data->u.dthd.thdParID; ! 472: ! 473: found = v_catsearch(*vol, data->u.dthd.thdParID, ! 474: data->u.dthd.thdCName, data, fname, np); ! 475: if (found == -1) ! 476: goto fail; ! 477: } ! 478: ! 479: goto done; ! 480: } ! 481: } ! 482: else ! 483: { ! 484: hfsvol *check; ! 485: ! 486: dirid = HFS_CNID_ROOTPAR; /* absolute path */ ! 487: ! 488: if (nptr - path > HFS_MAX_VLEN) ! 489: ERROR(ENAMETOOLONG, NULL); ! 490: ! 491: strncpy(name, path, nptr - path); ! 492: name[nptr - path] = 0; ! 493: ! 494: for (check = hfs_mounts; check; check = check->next) ! 495: { ! 496: if (d_relstring(check->mdb.drVN, name) == 0) ! 497: { ! 498: *vol = check; ! 499: break; ! 500: } ! 501: } ! 502: } ! 503: ! 504: while (1) ! 505: { ! 506: while (*path == ':') ! 507: { ! 508: ++path; ! 509: ! 510: found = v_getdthread(*vol, dirid, data, NULL); ! 511: if (found == -1) ! 512: goto fail; ! 513: else if (! found) ! 514: goto done; ! 515: ! 516: dirid = data->u.dthd.thdParID; ! 517: } ! 518: ! 519: if (*path == 0) ! 520: { ! 521: found = v_getdthread(*vol, dirid, data, NULL); ! 522: if (found == -1) ! 523: goto fail; ! 524: ! 525: if (found) ! 526: { ! 527: if (parid) ! 528: *parid = data->u.dthd.thdParID; ! 529: ! 530: found = v_catsearch(*vol, data->u.dthd.thdParID, ! 531: data->u.dthd.thdCName, data, fname, np); ! 532: if (found == -1) ! 533: goto fail; ! 534: } ! 535: ! 536: goto done; ! 537: } ! 538: ! 539: nptr = name; ! 540: while (nptr < name + sizeof(name) - 1 && *path && *path != ':') ! 541: *nptr++ = *path++; ! 542: ! 543: if (*path && *path != ':') ! 544: ERROR(ENAMETOOLONG, NULL); ! 545: ! 546: *nptr = 0; ! 547: if (*path == ':') ! 548: ++path; ! 549: ! 550: if (parid) ! 551: *parid = dirid; ! 552: ! 553: found = v_catsearch(*vol, dirid, name, data, fname, np); ! 554: if (found == -1) ! 555: goto fail; ! 556: ! 557: if (! found) ! 558: { ! 559: if (*path && parid) ! 560: *parid = 0; ! 561: ! 562: if (*path == 0 && fname) ! 563: strcpy(fname, name); ! 564: ! 565: goto done; ! 566: } ! 567: ! 568: switch (data->cdrType) ! 569: { ! 570: case cdrDirRec: ! 571: if (*path == 0) ! 572: goto done; ! 573: ! 574: dirid = data->u.dir.dirDirID; ! 575: break; ! 576: ! 577: case cdrFilRec: ! 578: if (*path == 0) ! 579: goto done; ! 580: ! 581: ERROR(ENOTDIR, "invalid pathname"); ! 582: ! 583: default: ! 584: ERROR(EIO, "unexpected catalog record"); ! 585: } ! 586: } ! 587: ! 588: done: ! 589: return found; ! 590: ! 591: fail: ! 592: return -1; ! 593: } ! 594: ! 595: /* Determine whether the volume is a HFS volume */ ! 596: int ! 597: v_probe(int fd, long long offset) ! 598: { ! 599: MDB *mdb; ! 600: ! 601: mdb = (MDB*)malloc(2 * 512); ! 602: os_seek_offset( fd, 2 * 512 + offset ); ! 603: os_read(fd, mdb, 2, 9); ! 604: ! 605: if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) { ! 606: free(mdb); ! 607: return 0; ! 608: } ! 609: ! 610: free(mdb); ! 611: return -1; ! 612: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.