bfind.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:10k
- /*
- * linux/fs/hfs/bfind.c
- *
- * Copyright (C) 1995, 1996 Paul H. Hargrove
- * This file may be distributed under the terms of the GNU General Public License.
- *
- * This file contains the code to access records in a btree.
- *
- * "XXX" in a comment is a note to myself to consider changing something.
- *
- * In function preconditions the term "valid" applied to a pointer to
- * a structure means that the pointer is non-NULL and the structure it
- * points to has all fields initialized to consistent values.
- */
- #include "hfs_btree.h"
- /*================ Global functions ================*/
- /*
- * hfs_brec_relse()
- *
- * Description:
- * This function releases some of the nodes associated with a brec.
- * Input Variable(s):
- * struct hfs_brec *brec: pointer to the brec to release some nodes from.
- * struct hfs_belem *elem: the last node to release or NULL for all
- * Output Variable(s):
- * NONE
- * Returns:
- * void
- * Preconditions:
- * 'brec' points to a "valid" (struct hfs_brec)
- * Postconditions:
- * All nodes between the indicated node and the beginning of the path
- * are released.
- */
- void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem)
- {
- if (!elem) {
- elem = brec->bottom;
- }
- while (brec->top <= elem) {
- hfs_bnode_relse(&brec->top->bnr);
- ++brec->top;
- }
- }
- /*
- * hfs_bfind()
- *
- * Description:
- * This function has sole responsibility for locating existing
- * records in a B-tree. Given a B-tree and a key it locates the
- * "greatest" record "less than or equal to" the given key. The
- * exact behavior is determined by the bits of the flags variable as
- * follows:
- * ('flags' & HFS_LOCK_MASK):
- * The lock_type argument to be used when calling hfs_bnode_find().
- * HFS_BFIND_EXACT: only accept an exact match, otherwise take the
- * "largest" record less than 'target' as a "match"
- * HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing
- * the "matching" record when it is located
- * HFS_BPATH_FIRST: keep access to internal nodes when accessing their
- * first child.
- * HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed
- * child is too full to insert another pointer record.
- * HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed
- * child is would be less than half full upon removing a pointer record.
- * Input Variable(s):
- * struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold
- * the search results.
- * struct hfs_bkey *target: pointer to the (struct hfs_bkey)
- * to search for
- * int flags: bitwise OR of flags which determine the function's behavior
- * Output Variable(s):
- * 'brec' contains the results of the search on success or is invalid
- * on failure.
- * Returns:
- * int: 0 or 1 on success or an error code on failure:
- * -EINVAL: one of the input variables was NULL.
- * -ENOENT: tree is valid but empty or no "matching" record was located.
- * If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no
- * matching record will give a 'brec' with a 'record' field of zero
- * rather than returning this error.
- * -EIO: an I/O operation or an assertion about the structure of a
- * valid B-tree failed indicating corruption of either the B-tree
- * structure on the disk or one of the in-core structures representing
- * the B-tree.
- * (This could also be returned if a kmalloc() call failed in a
- * subordinate routine that is intended to get the data from the
- * disk or the buffer cache.)
- * Preconditions:
- * 'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field
- * which points to a valid (struct hfs_btree).
- * 'target' is NULL or points to a "valid" (struct hfs_bkey)
- * Postconditions:
- * If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned.
- * If 'brec', 'brec->tree' and 'target' are non-NULL but the tree
- * is empty then -ENOENT is returned.
- * If 'brec', 'brec->tree' and 'target' are non-NULL but the call to
- * hfs_brec_init() fails then '*brec' is NULL and -EIO is returned.
- * If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is
- * non-empty then the tree is searched as follows:
- * If any call to hfs_brec_next() fails or returns a node that is
- * neither an index node nor a leaf node then -EIO is returned to
- * indicate that the B-tree or buffer-cache are corrupted.
- * If every record in the tree is "greater than" the given key
- * and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
- * If every record in the tree is "greater than" the given key
- * and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers
- * to the first leaf node in the tree and has a 'record' field of
- * zero, and 1 is returned.
- * If a "matching" record is located with key "equal to" 'target'
- * then the return value is 0 and 'brec' indicates the record.
- * If a "matching" record is located with key "greater than" 'target'
- * then the behavior is determined as follows:
- * If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned
- * and 'brec' refers to the "matching" record.
- * If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
- * If the return value is non-negative and the HFS_BFIND_LOCK bit of
- * 'flags' is set then hfs_brec_lock() is called on the bottom element
- * of 'brec' before returning.
- */
- int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree,
- const struct hfs_bkey *target, int flags)
- {
- struct hfs_belem *curr;
- struct hfs_bkey *key;
- struct hfs_bnode *bn;
- int result, ntype;
- /* check for invalid arguments */
- if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) {
- return -EINVAL;
- }
- /* check for empty tree */
- if (!tree->root || !tree->bthNRecs) {
- return -ENOENT;
- }
- /* start search at root of tree */
- if (!(curr = hfs_brec_init(brec, tree, flags))) {
- return -EIO;
- }
- /* traverse the tree */
- do {
- bn = curr->bnr.bn;
- if (!curr->record) {
- hfs_warn("hfs_bfind: empty bnoden");
- hfs_brec_relse(brec, NULL);
- return -EIO;
- }
- /* reverse linear search yielding largest key "less
- than or equal to" 'target'.
- It is questionable whether a binary search would be
- significantly faster */
- do {
- key = belem_key(curr);
- if (!key->KeyLen) {
- hfs_warn("hfs_bfind: empty keyn");
- hfs_brec_relse(brec, NULL);
- return -EIO;
- }
- result = (tree->compare)(target, key);
- } while ((result<0) && (--curr->record));
- ntype = bn->ndType;
- /* see if all keys > target */
- if (!curr->record) {
- if (bn->ndBLink) {
- /* at a node other than the left-most at a
- given level it means the parent had an
- incorrect key for this child */
- hfs_brec_relse(brec, NULL);
- hfs_warn("hfs_bfind: corrupted b-tree %d.n",
- (int)ntohl(tree->entry.cnid));
- return -EIO;
- }
- if (flags & HFS_BFIND_EXACT) {
- /* we're not going to find it */
- hfs_brec_relse(brec, NULL);
- return -ENOENT;
- }
- if (ntype == ndIndxNode) {
- /* since we are at the left-most node at
- the current level and looking for the
- predecessor of 'target' keep going down */
- curr->record = 1;
- } else {
- /* we're at first leaf so fall through */
- }
- }
- /* get next node if necessary */
- if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) {
- return -EIO;
- }
- } while (ntype == ndIndxNode);
- if (key->KeyLen > tree->bthKeyLen) {
- hfs_warn("hfs_bfind: oversized keyn");
- hfs_brec_relse(brec, NULL);
- return -EIO;
- }
- if (ntype != ndLeafNode) {
- hfs_warn("hfs_bfind: invalid node type %02x in node %d of "
- "btree %dn", bn->ndType, bn->node,
- (int)ntohl(tree->entry.cnid));
- hfs_brec_relse(brec, NULL);
- return -EIO;
- }
- if ((flags & HFS_BFIND_EXACT) && result) {
- hfs_brec_relse(brec, NULL);
- return -ENOENT;
- }
- if (!(flags & HFS_BPATH_MASK)) {
- hfs_brec_relse(brec, brec->bottom-1);
- }
- if (flags & HFS_BFIND_LOCK) {
- hfs_brec_lock(brec, brec->bottom);
- }
- brec->key = brec_key(brec);
- brec->data = bkey_record(brec->key);
- return result ? 1 : 0;
- }
- /*
- * hfs_bsucc()
- *
- * Description:
- * This function overwrites '*brec' with its successor in the B-tree,
- * obtaining the same type of access.
- * Input Variable(s):
- * struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite
- * with its successor
- * Output Variable(s):
- * struct hfs_brec *brec: address of the successor of the original
- * '*brec' or to invalid data
- * Returns:
- * int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure
- * Preconditions:
- * 'brec' pointers to a "valid" (struct hfs_brec)
- * Postconditions:
- * If the given '*brec' is not "valid" -EINVAL is returned and
- * '*brec' is unchanged.
- * If the given 'brec' is "valid" but has no successor then -ENOENT
- * is returned and '*brec' is invalid.
- * If a call to hfs_bnode_find() is necessary to find the successor,
- * but fails then -EIO is returned and '*brec' is invalid.
- * If none of the three previous conditions prevents finding the
- * successor of '*brec', then 0 is returned, and '*brec' is overwritten
- * with the (struct hfs_brec) for its successor.
- * In the cases when '*brec' is invalid, the old records is freed.
- */
- int hfs_bsucc(struct hfs_brec *brec, int count)
- {
- struct hfs_belem *belem;
- struct hfs_bnode *bn;
- if (!brec || !(belem = brec->bottom) || (belem != brec->top) ||
- !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) ||
- !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) ||
- !hfs_buffer_ok(bn->buf)) {
- hfs_warn("hfs_bsucc: invalid/corrupt arguments.n");
- return -EINVAL;
- }
- while (count) {
- int left = bn->ndNRecs - belem->record;
- if (left < count) {
- struct hfs_bnode_ref old;
- hfs_u32 node;
- /* Advance to next node */
- if (!(node = bn->ndFLink)) {
- hfs_brec_relse(brec, belem);
- return -ENOENT;
- }
- if (node == bn->node) {
- hfs_warn("hfs_bsucc: corrupt btreen");
- hfs_brec_relse(brec, belem);
- return -EIO;
- }
- old = belem->bnr;
- belem->bnr = hfs_bnode_find(brec->tree, node,
- belem->bnr.lock_type);
- hfs_bnode_relse(&old);
- if (!(bn = belem->bnr.bn)) {
- return -EIO;
- }
- belem->record = 1;
- count -= (left + 1);
- } else {
- belem->record += count;
- break;
- }
- }
- brec->key = belem_key(belem);
- brec->data = bkey_record(brec->key);
- if (brec->key->KeyLen > brec->tree->bthKeyLen) {
- hfs_warn("hfs_bsucc: oversized keyn");
- hfs_brec_relse(brec, NULL);
- return -EIO;
- }
- return 0;
- }