linux-old/fs/hfs/bdelete.c
<<
>>
Prefs
   1/*
   2 * linux/fs/hfs/bdelete.c
   3 *
   4 * Copyright (C) 1995-1997  Paul H. Hargrove
   5 * This file may be distributed under the terms of the GNU General Public License.
   6 *
   7 * This file contains the code to delete records in a B-tree.
   8 *
   9 * "XXX" in a comment is a note to myself to consider changing something.
  10 *
  11 * In function preconditions the term "valid" applied to a pointer to
  12 * a structure means that the pointer is non-NULL and the structure it
  13 * points to has all fields initialized to consistent values.
  14 */
  15
  16#include "hfs_btree.h"
  17
  18/*================ Variable-like macros ================*/
  19
  20#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
  21#define NO_SPACE (HFS_SECTOR_SIZE+1)
  22
  23/*================ File-local functions ================*/
  24
  25/*
  26 * bdelete_nonempty()
  27 *
  28 * Description:
  29 *   Deletes a record from a given bnode without regard to it becoming empty.
  30 * Input Variable(s):
  31 *   struct hfs_brec* brec: pointer to the brec for the deletion
  32 *   struct hfs_belem* belem: which node in 'brec' to delete from
  33 * Output Variable(s):
  34 *   NONE
  35 * Returns:
  36 *   void
  37 * Preconditions:
  38 *   'brec' points to a valid (struct hfs_brec).
  39 *   'belem' points to a valid (struct hfs_belem) in 'brec'.
  40 * Postconditions:
  41 *   The record has been inserted in the position indicated by 'brec'.
  42 */
  43static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem)
  44{
  45        int i, rec, nrecs, tomove;
  46        hfs_u16 size;
  47        hfs_u8 *start;
  48        struct hfs_bnode *bnode = belem->bnr.bn;
  49
  50        rec = belem->record;
  51        nrecs = bnode->ndNRecs;
  52        size = bnode_rsize(bnode, rec);
  53        tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1);
  54        
  55        /* adjust the record table */
  56        for (i = rec+1; i <= nrecs; ++i) {
  57                hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i));
  58        }
  59
  60        /* move it down */
  61        start = bnode_key(bnode, rec);
  62        memmove(start, start + size, tomove);
  63
  64        /* update record count */
  65        --bnode->ndNRecs;
  66}
  67
  68/*
  69 * del_root()
  70 *
  71 * Description:
  72 *   Delete the current root bnode.
  73 * Input Variable(s):
  74 *   struct hfs_bnode_ref *root: reference to the root bnode
  75 * Output Variable(s):
  76 *   NONE
  77 * Returns:
  78 *   int: 0 on success, error code on failure
  79 * Preconditions:
  80 *   'root' refers to the root bnode with HFS_LOCK_WRITE access.
  81 *   None of 'root's children are held with HFS_LOCK_WRITE access.
  82 * Postconditions:
  83 *   The current 'root' node is removed from the tree and the depth
  84 *    of the tree is reduced by one.
  85 *   If 'root' is an index node with exactly one child, then that
  86 *    child becomes the new root of the tree.
  87 *   If 'root' is an empty leaf node the tree becomes empty.
  88 *   Upon return access to 'root' is relinquished.
  89 */
  90static int del_root(struct hfs_bnode_ref *root)
  91{
  92        struct hfs_btree *tree = root->bn->tree;
  93        struct hfs_bnode_ref child;
  94        hfs_u32 node;
  95
  96        if (root->bn->ndNRecs > 1) {
  97                return 0;
  98        } else if (root->bn->ndNRecs == 0) {
  99                /* tree is empty */
 100                tree->bthRoot = 0;
 101                tree->root = NULL;
 102                tree->bthRoot = 0;
 103                tree->bthFNode = 0;
 104                tree->bthLNode = 0;
 105                --tree->bthDepth;
 106                tree->dirt = 1;
 107                if (tree->bthDepth) {
 108                        hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
 109                                 tree->bthDepth);
 110                        goto bail;
 111                }
 112                return hfs_bnode_free(root);
 113        } else if (root->bn->ndType == ndIndxNode) {
 114                /* tree is non-empty */
 115                node = hfs_get_hl(bkey_record(bnode_datastart(root->bn)));
 116
 117                child = hfs_bnode_find(tree, node, HFS_LOCK_READ);
 118                if (!child.bn) {
 119                        hfs_warn("hfs_bdelete: can't read child node.\n");
 120                        goto bail;
 121                }
 122                        
 123                child.bn->sticky = HFS_STICKY;
 124                if (child.bn->next) {
 125                        child.bn->next->prev = child.bn->prev;
 126                }
 127                if (child.bn->prev) {
 128                        child.bn->prev->next = child.bn->next;
 129                }
 130                if (bhash(tree, child.bn->node) == child.bn) {
 131                        bhash(tree, child.bn->node) = child.bn->next;
 132                }
 133                child.bn->next = NULL;
 134                child.bn->prev = NULL;
 135
 136                tree->bthRoot = child.bn->node;
 137                tree->root = child.bn;
 138
 139                /* re-assign bthFNode and bthLNode if the new root is
 140                   a leaf node. */
 141                if (child.bn->ndType == ndLeafNode) {
 142                        tree->bthFNode = node;
 143                        tree->bthLNode = node;
 144                }
 145                hfs_bnode_relse(&child);
 146
 147                tree->bthRoot = node;
 148                --tree->bthDepth;
 149                tree->dirt = 1;
 150                if (!tree->bthDepth) {
 151                        hfs_warn("hfs_bdelete: non-empty tree with "
 152                                 "bthDepth == 0\n");
 153                        goto bail;
 154                }
 155                return hfs_bnode_free(root);    /* marks tree dirty */
 156        }
 157        hfs_bnode_relse(root);
 158        return 0;
 159
 160bail:
 161        hfs_bnode_relse(root);
 162        return -EIO;
 163}
 164
 165
 166/*
 167 * delete_empty_bnode()
 168 *
 169 * Description:
 170 *   Removes an empty non-root bnode from between 'left' and 'right'
 171 * Input Variable(s):
 172 *   hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid
 173 *   struct hfs_bnode_ref *left: reference to the left neighbor of the
 174 *    bnode to remove, or invalid if no such neighbor exists.
 175 *   struct hfs_bnode_ref *center: reference to the bnode to remove
 176 *   hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid
 177 *   struct hfs_bnode_ref *right: reference to the right neighbor of the
 178 *    bnode to remove, or invalid if no such neighbor exists.
 179 * Output Variable(s):
 180 *   NONE
 181 * Returns:
 182 *   void
 183 * Preconditions:
 184 *   'left_node' is as described above.
 185 *   'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
 186 *    access and referring to the left neighbor of 'center' if such a
 187 *    neighbor exists, or invalid if no such neighbor exists.
 188 *   'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
 189 *    access and referring to the bnode to delete.
 190 *   'right_node' is as described above.
 191 *   'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
 192 *    access and referring to the right neighbor of 'center' if such a
 193 *    neighbor exists, or invalid if no such neighbor exists.
 194 * Postconditions:
 195 *   If 'left' is valid its 'ndFLink' field becomes 'right_node'.
 196 *   If 'right' is valid its 'ndBLink' field becomes 'left_node'.
 197 *   If 'center' was the first leaf node then the tree's 'bthFNode'
 198 *    field becomes 'right_node' 
 199 *   If 'center' was the last leaf node then the tree's 'bthLNode'
 200 *    field becomes 'left_node' 
 201 *   'center' is NOT freed and access to the nodes is NOT relinquished.
 202 */
 203static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left,
 204                               struct hfs_bnode_ref *center,
 205                               hfs_u32 right_node, struct hfs_bnode_ref *right)
 206{
 207        struct hfs_bnode *bnode = center->bn;
 208
 209        if (left_node) {
 210                left->bn->ndFLink = right_node;
 211        } else if (bnode->ndType == ndLeafNode) {
 212                bnode->tree->bthFNode = right_node;
 213                bnode->tree->dirt = 1;
 214        }
 215
 216        if (right_node) {
 217                right->bn->ndBLink = left_node;
 218        } else if (bnode->ndType == ndLeafNode) {
 219                bnode->tree->bthLNode = left_node;
 220                bnode->tree->dirt = 1;
 221        }
 222}
 223
 224/*
 225 * balance()
 226 *
 227 * Description:
 228 *   Attempt to equalize space usage in neighboring bnodes.
 229 * Input Variable(s):
 230 *   struct hfs_bnode *left: the left bnode.
 231 *   struct hfs_bnode *right: the right bnode.
 232 * Output Variable(s):
 233 *   NONE
 234 * Returns:
 235 *   void
 236 * Preconditions:
 237 *   'left' and 'right' point to valid (struct hfs_bnode)s obtained
 238 *    with HFS_LOCK_WRITE access, and are neighbors.
 239 * Postconditions:
 240 *   Records are shifted either left or right to make the space usage
 241 *   nearly equal.  When exact equality is not possible the break
 242 *   point is chosen to reduce data movement.
 243 *   The key corresponding to 'right' in its parent is NOT updated.
 244 */
 245static void balance(struct hfs_bnode *left, struct hfs_bnode *right)
 246{
 247        int index, left_free, right_free, half;
 248
 249        left_free = bnode_freespace(left);
 250        right_free = bnode_freespace(right);
 251        half = (left_free + right_free)/2;
 252
 253        if (left_free < right_free) {
 254                /* shift right to balance */
 255                index = left->ndNRecs + 1;
 256                while (right_free >= half) {
 257                        --index;
 258                        right_free -= bnode_rsize(left,index)+sizeof(hfs_u16);
 259                }
 260                if (index < left->ndNRecs) {
 261#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
 262                        hfs_warn("shifting %d of %d recs right to balance: ",
 263                               left->ndNRecs - index, left->ndNRecs);
 264#endif
 265                        hfs_bnode_shift_right(left, right, index+1);
 266#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
 267                        hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
 268#endif
 269                }
 270        } else {
 271                /* shift left to balance */
 272                index = 0;
 273                while (left_free >= half) {
 274                        ++index;
 275                        left_free -= bnode_rsize(right,index)+sizeof(hfs_u16);
 276                }
 277                if (index > 1) {
 278#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
 279                        hfs_warn("shifting %d of %d recs left to balance: ",
 280                               index-1, right->ndNRecs);
 281#endif
 282                        hfs_bnode_shift_left(left, right, index-1);
 283#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
 284                        hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
 285#endif
 286                }
 287        }
 288}
 289
 290/*
 291 * bdelete()
 292 *
 293 * Delete the given record from a B-tree.
 294 */
 295static int bdelete(struct hfs_brec *brec)
 296{
 297        struct hfs_btree *tree = brec->tree;
 298        struct hfs_belem *belem = brec->bottom;
 299        struct hfs_belem *parent = (belem-1);
 300        struct hfs_bnode *bnode;
 301        hfs_u32 left_node, right_node;
 302        struct hfs_bnode_ref left, right;
 303        int left_space, right_space, min_space;
 304        int fix_right_key;
 305        int fix_key;
 306        
 307        while ((belem > brec->top) &&
 308               (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) {
 309                bnode = belem->bnr.bn;
 310                fix_key = belem->flags & HFS_BPATH_FIRST;
 311                fix_right_key = 0;
 312
 313                bdelete_nonempty(brec, belem);
 314
 315                if (bnode->node == tree->root->node) {
 316                        del_root(&belem->bnr);
 317                        --brec->bottom;
 318                        goto done;
 319                }
 320
 321                /* check for btree corruption which could lead to deadlock */
 322                left_node = bnode->ndBLink;
 323                right_node = bnode->ndFLink;
 324                if ((left_node && hfs_bnode_in_brec(left_node, brec)) ||
 325                    (right_node && hfs_bnode_in_brec(right_node, brec)) ||
 326                    (left_node == right_node)) {
 327                        hfs_warn("hfs_bdelete: corrupt btree\n");
 328                        hfs_brec_relse(brec, NULL);
 329                        return -EIO;
 330                }
 331
 332                /* grab the left neighbor if it exists */
 333                if (left_node) {
 334                        hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV);
 335                        left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE);
 336                        if (!left.bn) {
 337                                hfs_warn("hfs_bdelete: unable to read left "
 338                                         "neighbor.\n");
 339                                hfs_brec_relse(brec, NULL);
 340                                return -EIO;
 341                        }
 342                        hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE);
 343                        if (parent->record != 1) {
 344                                left_space = bnode_freespace(left.bn);
 345                        } else {
 346                                left_space = NO_SPACE;
 347                        }
 348                } else {
 349                        left.bn = NULL;
 350                        left_space = NO_SPACE;
 351                }
 352
 353                /* grab the right neighbor if it exists */
 354                if (right_node) {
 355                        right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE);
 356                        if (!right.bn) {
 357                                hfs_warn("hfs_bdelete: unable to read right "
 358                                         "neighbor.\n");
 359                                hfs_bnode_relse(&left);
 360                                hfs_brec_relse(brec, NULL);
 361                                return -EIO;
 362                        }
 363                        if (parent->record < parent->bnr.bn->ndNRecs) {
 364                                right_space = bnode_freespace(right.bn);
 365                        } else {
 366                                right_space = NO_SPACE;
 367                        }
 368                } else {
 369                        right.bn = NULL;
 370                        right_space = NO_SPACE;
 371                }
 372
 373                if (left_space < right_space) {
 374                        min_space = left_space;
 375                } else {
 376                        min_space = right_space;
 377                }
 378
 379                if (min_space == NO_SPACE) {
 380                        hfs_warn("hfs_bdelete: no siblings?\n");
 381                        hfs_brec_relse(brec, NULL);
 382                        return -EIO;
 383                }
 384
 385                if (bnode->ndNRecs == 0) {
 386                        delete_empty_bnode(left_node, &left, &belem->bnr,
 387                                           right_node, &right);
 388                } else if (min_space + bnode_freespace(bnode) >= FULL) {
 389                        if ((right_space == NO_SPACE) ||
 390                            ((right_space == min_space) &&
 391                             (left_space != NO_SPACE))) {
 392                                hfs_bnode_shift_left(left.bn, bnode,
 393                                                     bnode->ndNRecs);
 394                        } else {
 395                                hfs_bnode_shift_right(bnode, right.bn, 1);
 396                                fix_right_key = 1;
 397                        }
 398                        delete_empty_bnode(left_node, &left, &belem->bnr,
 399                                           right_node, &right);
 400                } else if (min_space == right_space) {
 401                        balance(bnode, right.bn);
 402                        fix_right_key = 1;
 403                } else {
 404                        balance(left.bn, bnode);
 405                        fix_key = 1;
 406                }
 407
 408                if (fix_right_key) {
 409                        hfs_bnode_update_key(brec, belem, right.bn, 1);
 410                }
 411
 412                hfs_bnode_relse(&left);
 413                hfs_bnode_relse(&right);
 414
 415                if (bnode->ndNRecs) {
 416                        if (fix_key) {
 417                                hfs_bnode_update_key(brec, belem, bnode, 0);
 418                        }
 419                        goto done;
 420                }
 421
 422                hfs_bnode_free(&belem->bnr);
 423                --brec->bottom;
 424                belem = parent;
 425                --parent;
 426        }
 427
 428        if (belem < brec->top) {
 429                hfs_warn("hfs_bdelete: Missing parent.\n");
 430                hfs_brec_relse(brec, NULL);
 431                return -EIO;
 432        }
 433
 434        bdelete_nonempty(brec, belem);
 435
 436done:
 437        hfs_brec_relse(brec, NULL);
 438        return 0;
 439}
 440
 441/*================ Global functions ================*/
 442
 443/*
 444 * hfs_bdelete()
 445 *
 446 * Delete the requested record from a B-tree.
 447 */
 448int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key)
 449{ 
 450        struct hfs_belem *belem;
 451        struct hfs_bnode *bnode;
 452        struct hfs_brec brec;
 453        int retval;
 454
 455        if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) {
 456                hfs_warn("hfs_bdelete: invalid arguments.\n");
 457                return -EINVAL;
 458        }
 459
 460        retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE);
 461        if (!retval) {
 462                belem = brec.bottom;
 463                bnode = belem->bnr.bn;
 464
 465                belem->flags = 0;
 466                if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) -
 467                     bnode_rsize(bnode, belem->record)) < FULL/2) {
 468                        belem->flags |= HFS_BPATH_UNDERFLOW;
 469                }
 470                if (belem->record == 1) {
 471                        belem->flags |= HFS_BPATH_FIRST;
 472                }
 473
 474                if (!belem->flags) {
 475                        hfs_brec_lock(&brec, brec.bottom);
 476                } else {
 477                        hfs_brec_lock(&brec, NULL);
 478                }
 479
 480                retval = bdelete(&brec);
 481                if (!retval) {
 482                        --brec.tree->bthNRecs;
 483                        brec.tree->dirt = 1;
 484                }
 485                hfs_brec_relse(&brec, NULL);
 486        }
 487        return retval;
 488}
 489
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.