/* * parse_new_part_table() * * Parse a new style partition map looking for the * start and length of the 'part'th HFS partition. */ static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, int part, hfs_s32 *size, hfs_s32 *start) { struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf); hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt); int hfs_part = 0; int entry; for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) { if (entry) { /* read the next partition map entry */ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1); if (!hfs_buffer_ok(buf)) { hfs_warn("hfs_fs: unable to " "read partition map.\n"); goto bail; } pm = (struct new_pmap *)hfs_buffer_data(buf); if (hfs_get_ns(pm->pmSig) != htons(HFS_NEW_PMAP_MAGIC)) { hfs_warn("hfs_fs: invalid " "entry in partition map\n"); hfs_buffer_put(buf); goto bail; } } /* look for an HFS partition */ if (!memcmp(pm->pmPartType,"Apple_HFS",9) && ((hfs_part++) == part)) { /* Found it! */ *start = hfs_get_hl(pm->pmPyPartStart); *size = hfs_get_hl(pm->pmPartBlkCnt); } hfs_buffer_put(buf); } return 0; bail: return 1; }
/* * hfs_ext_compare() * * Description: * This is the comparison function used for the extents B-tree. In * comparing extent B-tree entries, the file id is the most * significant field (compared as unsigned ints); the fork type is * the second most significant field (compared as unsigned chars); * and the allocation block number field is the least significant * (compared as unsigned ints). * Input Variable(s): * struct hfs_ext_key *key1: pointer to the first key to compare * struct hfs_ext_key *key2: pointer to the second key to compare * Output Variable(s): * NONE * Returns: * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 * Preconditions: * key1 and key2 point to "valid" (struct hfs_ext_key)s. * Postconditions: * This function has no side-effects */ int hfs_ext_compare(const struct hfs_ext_key *key1, const struct hfs_ext_key *key2) { unsigned int tmp; int retval; tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum); if (tmp != 0) { retval = (int)tmp; } else { tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType; if (tmp != 0) { retval = (int)tmp; } else { retval = (int)(hfs_get_hs(key1->FABN) - hfs_get_hs(key2->FABN)); } } return retval; }
/* * parse_old_part_table() * * Parse a old style partition map looking for the * start and length of the 'part'th HFS partition. */ static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, int part, hfs_s32 *size, hfs_s32 *start) { struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf); struct old_pmap_entry *p = &pm->pdEntry[0]; int hfs_part = 0; while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) { /* look for an HFS partition */ if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) && ((hfs_part++) == part)) { /* Found it! */ *start = hfs_get_hl(p->pdStart); *size = hfs_get_hl(p->pdSize); } ++p; } hfs_buffer_put(buf); return 0; }
/* * hfs_btree_init() * * Description: * Given some vital information from the MDB (HFS superblock), * initializes the fields of a (struct hfs_btree). * Input Variable(s): * struct hfs_mdb *mdb: pointer to the MDB * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree * hfs_u32 tsize: the size, in bytes, of the B-tree * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree * Output Variable(s): * NONE * Returns: * (struct hfs_btree *): pointer to the initialized hfs_btree on success, * or NULL on failure * Preconditions: * 'mdb' points to a "valid" (struct hfs_mdb) * Postconditions: * Assuming the inputs are what they claim to be, no errors occur * reading from disk, and no inconsistencies are noticed in the data * read from disk, the return value is a pointer to a "valid" * (struct hfs_btree). If there are errors reading from disk or * inconsistencies are noticed in the data read from disk, then and * all resources that were allocated are released and NULL is * returned. If the inputs are not what they claim to be or if they * are unnoticed inconsistencies in the data read from disk then the * returned hfs_btree is probably going to lead to errors when it is * used in a non-trivial way. */ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid, hfs_byte_t ext[12], hfs_u32 tsize, hfs_u32 csize) { struct hfs_btree * bt; struct BTHdrRec * th; struct hfs_bnode * tmp; unsigned int next; #if defined(DEBUG_HEADER) || defined(DEBUG_ALL) unsigned char *p, *q; #endif if (!mdb || !ext || !HFS_NEW(bt)) { goto bail3; } bt->magic = HFS_BTREE_MAGIC; bt->sys_mdb = mdb->sys_mdb; bt->reserved = 0; bt->lock = 0; hfs_init_waitqueue(&bt->wait); bt->dirt = 0; memset(bt->cache, 0, sizeof(bt->cache)); #if 0 /* this is a fake entry. so we don't need to initialize it. */ memset(&bt->entry, 0, sizeof(bt->entry)); hfs_init_waitqueue(&bt->entry.wait); INIT_LIST_HEAD(&bt->entry.hash); INIT_LIST_HEAD(&bt->entry.list); #endif bt->entry.mdb = mdb; bt->entry.cnid = cnid; bt->entry.type = HFS_CDR_FIL; bt->entry.u.file.magic = HFS_FILE_MAGIC; bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz) >> HFS_SECTOR_SIZE_BITS; bt->entry.u.file.data_fork.entry = &bt->entry; bt->entry.u.file.data_fork.lsize = tsize; bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS; bt->entry.u.file.data_fork.fork = HFS_FK_DATA; hfs_extent_in(&bt->entry.u.file.data_fork, ext); hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY); if (!hfs_buffer_ok(bt->head.buf)) { goto bail2; } th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) + sizeof(struct NodeDescriptor)); /* read in the bitmap nodes (if any) */ tmp = &bt->head; while ((next = tmp->ndFLink)) { if (!HFS_NEW(tmp->next)) { goto bail2; } hfs_bnode_read(tmp->next, bt, next, HFS_STICKY); if (!hfs_buffer_ok(tmp->next->buf)) { goto bail2; } tmp->next->prev = tmp; tmp = tmp->next; } if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) { hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n"); goto bail2; } if (cnid == htonl(HFS_CAT_CNID)) { bt->compare = (hfs_cmpfn)hfs_cat_compare; } else if (cnid == htonl(HFS_EXT_CNID)) { bt->compare = (hfs_cmpfn)hfs_ext_compare; } else { goto bail2; } bt->bthDepth = hfs_get_hs(th->bthDepth); bt->bthRoot = hfs_get_hl(th->bthRoot); bt->bthNRecs = hfs_get_hl(th->bthNRecs); bt->bthFNode = hfs_get_hl(th->bthFNode); bt->bthLNode = hfs_get_hl(th->bthLNode); bt->bthNNodes = hfs_get_hl(th->bthNNodes); bt->bthFree = hfs_get_hl(th->bthFree); bt->bthKeyLen = hfs_get_hs(th->bthKeyLen); #if defined(DEBUG_HEADER) || defined(DEBUG_ALL) hfs_warn("bthDepth %d\n", bt->bthDepth); hfs_warn("bthRoot %d\n", bt->bthRoot); hfs_warn("bthNRecs %d\n", bt->bthNRecs); hfs_warn("bthFNode %d\n", bt->bthFNode); hfs_warn("bthLNode %d\n", bt->bthLNode); hfs_warn("bthKeyLen %d\n", bt->bthKeyLen); hfs_warn("bthNNodes %d\n", bt->bthNNodes); hfs_warn("bthFree %d\n", bt->bthFree); p = (unsigned char *)hfs_buffer_data(bt->head.buf); q = p + HFS_SECTOR_SIZE; while (p < q) { hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++); } #endif /* Read in the root if it exists. The header always exists, but the root exists only if the tree is non-empty */ if (bt->bthDepth && bt->bthRoot) { if (!HFS_NEW(bt->root)) { goto bail2; } hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY); if (!hfs_buffer_ok(bt->root->buf)) { goto bail1; } } else { bt->root = NULL; } return bt; bail1: hfs_bnode_ditch(bt->root); bail2: hfs_bnode_ditch(&bt->head); HFS_DELETE(bt); bail3: return NULL; }
/* * del_root() * * Description: * Delete the current root bnode. * Input Variable(s): * struct hfs_bnode_ref *root: reference to the root bnode * Output Variable(s): * NONE * Returns: * int: 0 on success, error code on failure * Preconditions: * 'root' refers to the root bnode with HFS_LOCK_WRITE access. * None of 'root's children are held with HFS_LOCK_WRITE access. * Postconditions: * The current 'root' node is removed from the tree and the depth * of the tree is reduced by one. * If 'root' is an index node with exactly one child, then that * child becomes the new root of the tree. * If 'root' is an empty leaf node the tree becomes empty. * Upon return access to 'root' is relinquished. */ static int del_root(struct hfs_bnode_ref *root) { struct hfs_btree *tree = root->bn->tree; struct hfs_bnode_ref child; hfs_u32 node; if (root->bn->ndNRecs > 1) { return 0; } else if (root->bn->ndNRecs == 0) { /* tree is empty */ tree->bthRoot = 0; tree->root = NULL; tree->bthRoot = 0; tree->bthFNode = 0; tree->bthLNode = 0; --tree->bthDepth; tree->dirt = 1; if (tree->bthDepth) { hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n", tree->bthDepth); goto bail; } return hfs_bnode_free(root); } else if (root->bn->ndType == ndIndxNode) { /* tree is non-empty */ node = hfs_get_hl(bkey_record(bnode_datastart(root->bn))); child = hfs_bnode_find(tree, node, HFS_LOCK_READ); if (!child.bn) { hfs_warn("hfs_bdelete: can't read child node.\n"); goto bail; } child.bn->sticky = HFS_STICKY; if (child.bn->next) { child.bn->next->prev = child.bn->prev; } if (child.bn->prev) { child.bn->prev->next = child.bn->next; } if (bhash(tree, child.bn->node) == child.bn) { bhash(tree, child.bn->node) = child.bn->next; } child.bn->next = NULL; child.bn->prev = NULL; tree->bthRoot = child.bn->node; tree->root = child.bn; /* re-assign bthFNode and bthLNode if the new root is a leaf node. */ if (child.bn->ndType == ndLeafNode) { tree->bthFNode = node; tree->bthLNode = node; } hfs_bnode_relse(&child); tree->bthRoot = node; --tree->bthDepth; tree->dirt = 1; if (!tree->bthDepth) { hfs_warn("hfs_bdelete: non-empty tree with " "bthDepth == 0\n"); goto bail; } return hfs_bnode_free(root); /* marks tree dirty */ } hfs_bnode_relse(root); return 0; bail: hfs_bnode_relse(root); return -EIO; }