static int hfs_releasepage(struct page *page, int mask) { struct inode *inode = page->mapping->host; struct super_block *sb = inode->i_sb; struct hfs_btree *tree; struct hfs_bnode *node; u32 nidx; int i, res = 1; switch (inode->i_ino) { case HFS_EXT_CNID: tree = HFS_SB(sb)->ext_tree; break; case HFS_CAT_CNID: tree = HFS_SB(sb)->cat_tree; break; default: BUG(); return 0; } if (tree->node_size >= PAGE_CACHE_SIZE) { nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT); spin_lock(&tree->hash_lock); node = hfs_bnode_findhash(tree, nidx); if (!node) ; else if (atomic_read(&node->refcnt)) res = 0; if (res && node) { hfs_bnode_unhash(node); hfs_bnode_free(node); } spin_unlock(&tree->hash_lock); } else {
/* * 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; }
/* * bdelete() * * Delete the given record from a B-tree. */ static int bdelete(struct hfs_brec *brec) { struct hfs_btree *tree = brec->tree; struct hfs_belem *belem = brec->bottom; struct hfs_belem *parent = (belem-1); struct hfs_bnode *bnode; hfs_u32 left_node, right_node; struct hfs_bnode_ref left, right; int left_space, right_space, min_space; int fix_right_key; int fix_key; while ((belem > brec->top) && (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) { bnode = belem->bnr.bn; fix_key = belem->flags & HFS_BPATH_FIRST; fix_right_key = 0; bdelete_nonempty(brec, belem); if (bnode->node == tree->root->node) { del_root(&belem->bnr); --brec->bottom; goto done; } /* check for btree corruption which could lead to deadlock */ left_node = bnode->ndBLink; right_node = bnode->ndFLink; if ((left_node && hfs_bnode_in_brec(left_node, brec)) || (right_node && hfs_bnode_in_brec(right_node, brec)) || (left_node == right_node)) { hfs_warn("hfs_bdelete: corrupt btree\n"); hfs_brec_relse(brec, NULL); return -EIO; } /* grab the left neighbor if it exists */ if (left_node) { hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV); left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE); if (!left.bn) { hfs_warn("hfs_bdelete: unable to read left " "neighbor.\n"); hfs_brec_relse(brec, NULL); return -EIO; } hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE); if (parent->record != 1) { left_space = bnode_freespace(left.bn); } else { left_space = NO_SPACE; } } else { left.bn = NULL; left_space = NO_SPACE; } /* grab the right neighbor if it exists */ if (right_node) { right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE); if (!right.bn) { hfs_warn("hfs_bdelete: unable to read right " "neighbor.\n"); hfs_bnode_relse(&left); hfs_brec_relse(brec, NULL); return -EIO; } if (parent->record < parent->bnr.bn->ndNRecs) { right_space = bnode_freespace(right.bn); } else { right_space = NO_SPACE; } } else { right.bn = NULL; right_space = NO_SPACE; } if (left_space < right_space) { min_space = left_space; } else { min_space = right_space; } if (min_space == NO_SPACE) { hfs_warn("hfs_bdelete: no siblings?\n"); hfs_brec_relse(brec, NULL); return -EIO; } if (bnode->ndNRecs == 0) { delete_empty_bnode(left_node, &left, &belem->bnr, right_node, &right); } else if (min_space + bnode_freespace(bnode) >= FULL) { if ((right_space == NO_SPACE) || ((right_space == min_space) && (left_space != NO_SPACE))) { hfs_bnode_shift_left(left.bn, bnode, bnode->ndNRecs); } else { hfs_bnode_shift_right(bnode, right.bn, 1); fix_right_key = 1; } delete_empty_bnode(left_node, &left, &belem->bnr, right_node, &right); } else if (min_space == right_space) { balance(bnode, right.bn); fix_right_key = 1; } else { balance(left.bn, bnode); fix_key = 1; } if (fix_right_key) { hfs_bnode_update_key(brec, belem, right.bn, 1); } hfs_bnode_relse(&left); hfs_bnode_relse(&right); if (bnode->ndNRecs) { if (fix_key) { hfs_bnode_update_key(brec, belem, bnode, 0); } goto done; } hfs_bnode_free(&belem->bnr); --brec->bottom; belem = parent; --parent; } if (belem < brec->top) { hfs_warn("hfs_bdelete: Missing parent.\n"); hfs_brec_relse(brec, NULL); return -EIO; } bdelete_nonempty(brec, belem); done: hfs_brec_relse(brec, NULL); return 0; }