Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | /* * 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 bnode\n"); 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 key\n"); 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 key\n"); hfs_brec_relse(brec, NULL); return -EIO; } if (ntype != ndLeafNode) { hfs_warn("hfs_bfind: invalid node type %02x in node %d of " "btree %d\n", 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 btree\n"); 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 key\n"); hfs_brec_relse(brec, NULL); return -EIO; } return 0; } |