The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/hfs/binsert.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * linux/fs/hfs/binsert.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 insert 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 /*================ File-local functions ================*/
   19 
   20 /* btree locking functions */
   21 static inline void hfs_btree_lock(struct hfs_btree *tree)
   22 {
   23   while (tree->lock) 
   24     hfs_sleep_on(&tree->wait);
   25   tree->lock = 1;
   26 }
   27 
   28 static inline void hfs_btree_unlock(struct hfs_btree *tree)
   29 {
   30   tree->lock = 0;
   31   hfs_wake_up(&tree->wait);
   32 }
   33 
   34 /*
   35  * binsert_nonfull()
   36  *
   37  * Description:
   38  *   Inserts a record in a given bnode known to have sufficient space.
   39  * Input Variable(s):
   40  *   struct hfs_brec* brec: pointer to the brec for the insertion
   41  *   struct hfs_belem* belem: the element in the search path to insert in
   42  *   struct hfs_bkey* key: pointer to the key for the record to insert
   43  *   void* data: pointer to the record to insert
   44  *   hfs_u16 keysize: size of the key to insert
   45  *   hfs_u16 datasize: size of the record to insert
   46  * Output Variable(s):
   47  *   NONE
   48  * Returns:
   49  *   NONE
   50  * Preconditions:
   51  *   'brec' points to a valid (struct hfs_brec).
   52  *   'belem' points to a valid (struct hfs_belem) in 'brec', the node
   53  *    of which has enough free space to insert 'key' and 'data'.
   54  *   'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize'
   55  *    which, in sorted order, belongs at the location indicated by 'brec'.
   56  *   'data' is non-NULL an points to appropriate data of length 'datasize'
   57  * Postconditions:
   58  *   The record has been inserted in the position indicated by 'brec'.
   59  */
   60 static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem,
   61                             const struct hfs_bkey *key, const void *data,
   62                             hfs_u8 keysize, hfs_u16 datasize)
   63 {
   64         int i, rec, nrecs, size, tomove;
   65         hfs_u8 *start;
   66         struct hfs_bnode *bnode = belem->bnr.bn;
   67 
   68         rec = ++(belem->record);
   69         size = ROUND(keysize+1) + datasize;
   70         nrecs = bnode->ndNRecs + 1;
   71         tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec);
   72         
   73         /* adjust the record table */
   74         for (i = nrecs; i >= rec; --i) {
   75                 hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1));
   76         }
   77 
   78         /* make room */
   79         start = bnode_key(bnode, rec);
   80         memmove(start + size, start, tomove);
   81 
   82         /* copy in the key and the data*/
   83         *start = keysize;
   84         keysize = ROUND(keysize+1);
   85         memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1);
   86         memcpy(start + keysize, data, datasize);
   87 
   88         /* update record count */
   89         ++bnode->ndNRecs;
   90 }
   91 
   92 /*
   93  * add_root()
   94  *
   95  * Description:
   96  *   Adds a new root to a B*-tree, increasing its height.
   97  * Input Variable(s):
   98  *   struct hfs_btree *tree: the tree to add a new root to
   99  *   struct hfs_bnode *left: the new root's first child or NULL
  100  *   struct hfs_bnode *right: the new root's second child or NULL
  101  * Output Variable(s):
  102  *   NONE
  103  * Returns:
  104  *   void
  105  * Preconditions:
  106  *   'tree' points to a valid (struct hfs_btree).
  107  *   'left' and 'right' point to valid (struct hfs_bnode)s, which
  108  *    resulted from splitting the old root node, or are both NULL
  109  *    if there was no root node before.
  110  * Postconditions:
  111  *   Upon success a new root node is added to 'tree' with either
  112  *    two children ('left' and 'right') or none.
  113  */
  114 static void add_root(struct hfs_btree *tree,
  115                      struct hfs_bnode *left,
  116                      struct hfs_bnode *right)
  117 {
  118         struct hfs_bnode_ref bnr;
  119         struct hfs_bnode *root;
  120         struct hfs_bkey *key;
  121         int keylen = tree->bthKeyLen;
  122 
  123         if (left && !right) {
  124                 hfs_warn("add_root: LEFT but no RIGHT\n");
  125                 return;
  126         }
  127 
  128         bnr = hfs_bnode_alloc(tree);
  129         if (!(root = bnr.bn)) {
  130                 return;
  131         }
  132 
  133         root->sticky = HFS_STICKY;
  134         tree->root = root;
  135         tree->bthRoot = root->node;
  136         ++tree->bthDepth;
  137 
  138         root->ndNHeight = tree->bthDepth;
  139         root->ndFLink = 0;
  140         root->ndBLink = 0;
  141 
  142         if (!left) {
  143                 /* tree was empty */
  144                 root->ndType  = ndLeafNode;
  145                 root->ndNRecs = 0;
  146 
  147                 tree->bthFNode = root->node;
  148                 tree->bthLNode = root->node;
  149         } else {
  150                 root->ndType  = ndIndxNode;
  151                 root->ndNRecs = 2;
  152 
  153                 hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) +
  154                            sizeof(hfs_u32), RECTBL(root, 2));
  155                 key = bnode_key(root, 1);
  156                 key->KeyLen = keylen;
  157                 memcpy(key->value,
  158                        ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen);
  159                 hfs_put_hl(left->node, bkey_record(key));
  160                 
  161                 hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) +
  162                            2*sizeof(hfs_u32), RECTBL(root, 3));
  163                 key = bnode_key(root, 2);
  164                 key->KeyLen = keylen;
  165                 memcpy(key->value,
  166                        ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen);
  167                 hfs_put_hl(right->node, bkey_record(key));
  168 
  169                 /* the former root (left) is now just a normal node */
  170                 left->sticky = HFS_NOT_STICKY;
  171                 if ((left->next = bhash(tree, left->node))) {
  172                         left->next->prev = left;
  173                 }
  174                 bhash(tree, left->node) = left;
  175         }
  176         hfs_bnode_relse(&bnr);
  177         tree->dirt = 1;
  178 }
  179 
  180 /*
  181  * insert_empty_bnode()
  182  *
  183  * Description:
  184  *   Adds an empty node to the right of 'left'.
  185  * Input Variable(s):
  186  *   struct hfs_btree *tree: the tree to add a node to
  187  *   struct hfs_bnode *left: the node to add a node after
  188  * Output Variable(s):
  189  *   NONE
  190  * Returns:
  191  *   struct hfs_bnode_ref *: reference to the new bnode.
  192  * Preconditions:
  193  *   'tree' points to a valid (struct hfs_btree) with at least 1 free node.
  194  *   'left' points to a valid (struct hfs_bnode) belonging to 'tree'.
  195  * Postconditions:
  196  *   If NULL is returned then 'tree' and 'left' are unchanged.
  197  *   Otherwise a node with 0 records is inserted in the tree to the right
  198  *   of the node 'left'.  The 'ndFLink' of 'left' and the 'ndBLink' of
  199  *   the former right-neighbor of 'left' (if one existed) point to the
  200  *   new node.  If 'left' had no right neighbor and is a leaf node the
  201  *   the 'bthLNode' of 'tree' points to the new node.  The free-count and
  202  *   bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies
  203  *   the required node.
  204  */
  205 static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree,
  206                                                struct hfs_bnode *left)
  207 {
  208         struct hfs_bnode_ref retval;
  209         struct hfs_bnode_ref right;
  210 
  211         retval = hfs_bnode_alloc(tree);
  212         if (!retval.bn) {
  213                 hfs_warn("hfs_binsert: out of bnodes?.\n");
  214                 goto done;
  215         }
  216         retval.bn->sticky = HFS_NOT_STICKY;
  217         if ((retval.bn->next = bhash(tree, retval.bn->node))) {
  218                 retval.bn->next->prev = retval.bn;
  219         }
  220         bhash(tree, retval.bn->node) = retval.bn;
  221 
  222         if (left->ndFLink) {
  223                 right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE);
  224                 if (!right.bn) {
  225                         hfs_warn("hfs_binsert: corrupt btree.\n");
  226                         hfs_bnode_bitop(tree, retval.bn->node, 0);
  227                         hfs_bnode_relse(&retval);
  228                         goto done;
  229                 }
  230                 right.bn->ndBLink = retval.bn->node;
  231                 hfs_bnode_relse(&right);
  232         } else if (left->ndType == ndLeafNode) {
  233                 tree->bthLNode = retval.bn->node;
  234                 tree->dirt = 1;
  235         }
  236 
  237         retval.bn->ndFLink   = left->ndFLink;
  238         retval.bn->ndBLink   = left->node;
  239         retval.bn->ndType    = left->ndType;
  240         retval.bn->ndNHeight = left->ndNHeight;
  241         retval.bn->ndNRecs   = 0;
  242 
  243         left->ndFLink = retval.bn->node;
  244 
  245  done:
  246         return retval;
  247 }
  248 
  249 /*
  250  * split()
  251  *
  252  * Description:
  253  *   Splits an over full node during insertion.
  254  *   Picks the split point that results in the most-nearly equal
  255  *   space usage in the new and old nodes.
  256  * Input Variable(s):
  257  *   struct hfs_belem *elem: the over full node.
  258  *   int size: the number of bytes to be used by the new record and its key.
  259  * Output Variable(s):
  260  *   struct hfs_belem *elem: changed to indicate where the new record
  261  *    should be inserted.
  262  * Returns:
  263  *   struct hfs_bnode_ref: reference to the new bnode.
  264  * Preconditions:
  265  *   'elem' points to a valid path element corresponding to the over full node.
  266  *   'size' is positive.
  267  * Postconditions:
  268  *   The records in the node corresponding to 'elem' are redistributed across
  269  *   the old and new nodes so that after inserting the new record, the space
  270  *   usage in these two nodes is as equal as possible.
  271  *   'elem' is updated so that a call to binsert_nonfull() will insert the
  272  *   new record in the correct location.
  273  */
  274 static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size)
  275 {
  276         struct hfs_bnode *bnode = elem->bnr.bn;
  277         int nrecs, cutoff, index, tmp, used, in_right;
  278         struct hfs_bnode_ref right;
  279 
  280         right = insert_empty_bnode(bnode->tree, bnode);
  281         if (right.bn) {
  282                 nrecs = bnode->ndNRecs;
  283                 cutoff = (size + bnode_end(bnode) -
  284                               sizeof(struct NodeDescriptor) +
  285                               (nrecs+1)*sizeof(hfs_u16))/2;
  286                 used = 0;
  287                 in_right = 1;
  288                 /* note that this only works because records sizes are even */
  289                 for (index=1; index <= elem->record; ++index) {
  290                         tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
  291                         used += tmp;
  292                         if (used > cutoff) {
  293                                 goto found;
  294                         }
  295                         used += tmp;
  296                 }
  297                 tmp = (size + sizeof(hfs_u16))/2;
  298                 used += tmp;
  299                 if (used > cutoff) {
  300                         goto found;
  301                 }
  302                 in_right = 0;
  303                 used += tmp;
  304                 for (; index <= nrecs; ++index) {
  305                         tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
  306                         used += tmp;
  307                         if (used > cutoff) {
  308                                 goto found;
  309                         }
  310                         used += tmp;
  311                 }
  312                 /* couldn't find the split point! */
  313                 hfs_bnode_relse(&right);
  314         }
  315         return right;
  316 
  317 found:
  318         if (in_right) {
  319                 elem->bnr = right;
  320                 elem->record -= index-1;
  321         }
  322         hfs_bnode_shift_right(bnode, right.bn, index);
  323 
  324         return right;
  325 }
  326 
  327 /*
  328  * binsert()
  329  *
  330  * Description:
  331  *   Inserts a record in a tree known to have enough room, even if the
  332  *   insertion requires the splitting of nodes.
  333  * Input Variable(s):
  334  *    struct hfs_brec *brec: partial path to the node to insert in
  335  *    const struct hfs_bkey *key: key for the new record
  336  *    const void *data: data for the new record
  337  *    hfs_u8 keysize: size of the key
  338  *    hfs_u16 datasize: size of the data
  339  *    int reserve: number of nodes reserved in case of splits
  340  * Output Variable(s):
  341  *    *brec = NULL
  342  * Returns:
  343  *    int: 0 on success, error code on failure
  344  * Preconditions:
  345  *    'brec' points to a valid (struct hfs_brec) corresponding to a
  346  *     record in a leaf node, after which a record is to be inserted,
  347  *     or to "record 0" of the leaf node if the record is to be inserted
  348  *     before all existing records in the node.  The (struct hfs_brec)
  349  *     includes all ancestors of the leaf node that are needed to
  350  *     complete the insertion including the parents of any nodes that
  351  *     will be split.
  352  *    'key' points to a valid (struct hfs_bkey) which is appropriate
  353  *     to this tree, and which belongs at the insertion point.
  354  *    'data' points data appropriate for the indicated node.
  355  *    'keysize' gives the size in bytes of the key.
  356  *    'datasize' gives the size in bytes of the data.
  357  *    'reserve' gives the number of nodes that have been reserved in the
  358  *     tree to allow for splitting of nodes.
  359  * Postconditions:
  360  *    All 'reserve'd nodes have been either used or released.
  361  *    *brec = NULL
  362  *    On success the key and data have been inserted at the indicated
  363  *    location in the tree, all appropriate fields of the in-core data
  364  *    structures have been changed and updated versions of the on-disk
  365  *    data structures have been scheduled for write-back to disk.
  366  *    On failure the B*-tree is probably invalid both on disk and in-core.
  367  *
  368  *    XXX: Some attempt at repair might be made in the event of failure,
  369  *    or the fs should be remounted read-only so things don't get worse.
  370  */
  371 static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key,
  372                    const void *data, hfs_u8 keysize, hfs_u16 datasize,
  373                    int reserve)
  374 {
  375         struct hfs_bnode_ref left, right, other;
  376         struct hfs_btree *tree = brec->tree;
  377         struct hfs_belem *belem = brec->bottom;
  378         int tmpsize = 1 + tree->bthKeyLen;
  379         struct hfs_bkey *tmpkey = hfs_malloc(tmpsize);
  380         hfs_u32 node;
  381         
  382         while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) {
  383                 left = belem->bnr;
  384                 if (left.bn->ndFLink &&
  385                     hfs_bnode_in_brec(left.bn->ndFLink, brec)) {
  386                         hfs_warn("hfs_binsert: corrupt btree\n");
  387                         tree->reserved -= reserve;
  388                         hfs_free(tmpkey, tmpsize);
  389                         return -EIO;
  390                 }
  391                         
  392                 right = split(belem, ROUND(keysize+1) + ROUND(datasize));
  393                 --reserve;
  394                 --tree->reserved;
  395                 if (!right.bn) {
  396                         hfs_warn("hfs_binsert: unable to split node!\n");
  397                         tree->reserved -= reserve;
  398                         hfs_free(tmpkey, tmpsize);
  399                         return -ENOSPC;
  400                 }
  401                 binsert_nonfull(brec, belem, key, data, keysize, datasize);
  402         
  403                 if (belem->bnr.bn == left.bn) {
  404                         other = right;
  405                         if (belem->record == 1) {
  406                                 hfs_bnode_update_key(brec, belem, left.bn, 0);
  407                         }
  408                 } else {
  409                         other = left;
  410                 }
  411 
  412                 if (left.bn->node == tree->root->node) {
  413                         add_root(tree, left.bn, right.bn);
  414                         hfs_bnode_relse(&other);
  415                         goto done;
  416                 }
  417 
  418                 data = &node;
  419                 datasize = sizeof(node);
  420                 node = htonl(right.bn->node);
  421                 key = tmpkey;
  422                 keysize = tree->bthKeyLen;
  423                 memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1);
  424                 hfs_bnode_relse(&other);
  425                 
  426                 --belem;
  427         }
  428 
  429         if (belem < brec->top) {
  430                 hfs_warn("hfs_binsert: Missing parent.\n");
  431                 tree->reserved -= reserve;
  432                 hfs_free(tmpkey, tmpsize);
  433                 return -EIO;
  434         }
  435 
  436         binsert_nonfull(brec, belem, key, data, keysize, datasize);
  437 
  438 done:
  439         tree->reserved -= reserve;
  440         hfs_free(tmpkey, tmpsize);
  441         return 0;
  442 }
  443 
  444 /*================ Global functions ================*/
  445 
  446 /*
  447  * hfs_binsert()
  448  *
  449  * Description:
  450  *   This function inserts a new record into a b-tree.
  451  * Input Variable(s):
  452  *   struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in
  453  *   struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert
  454  *   void *data: pointer to the data to associate with 'key' in the b-tree
  455  *   unsigned int datasize: the size of the data
  456  * Output Variable(s):
  457  *   NONE
  458  * Returns:
  459  *   int: 0 on success, error code on failure
  460  * Preconditions:
  461  *   'tree' points to a valid (struct hfs_btree)
  462  *   'key' points to a valid (struct hfs_bkey)
  463  *   'data' points to valid memory of length 'datasize'
  464  * Postconditions:
  465  *   If zero is returned then the record has been inserted in the
  466  *    indicated location updating all in-core data structures and
  467  *    scheduling all on-disk data structures for write-back.
  468  */
  469 int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key,
  470                 const void *data, hfs_u16 datasize)
  471 { 
  472         struct hfs_brec brec;
  473         struct hfs_belem *belem;
  474         int err, reserve, retval;
  475         hfs_u8 keysize;
  476 
  477         if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) {
  478                 hfs_warn("hfs_binsert: invalid arguments.\n");
  479                 return -EINVAL;
  480         }
  481 
  482         if (key->KeyLen > tree->bthKeyLen) {
  483                 hfs_warn("hfs_binsert: oversized key\n");
  484                 return -EINVAL;
  485         }
  486 
  487 restart:
  488         if (!tree->bthNRecs) {
  489                 /* create the root bnode */
  490                 add_root(tree, NULL, NULL);
  491                 if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) {
  492                         hfs_warn("hfs_binsert: failed to create root.\n");
  493                         return -ENOSPC;
  494                 }
  495         } else {
  496                 err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT);
  497                 if (err < 0) {
  498                         hfs_warn("hfs_binsert: hfs_brec_find failed.\n");
  499                         return err;
  500                 } else if (err == 0) {
  501                         hfs_brec_relse(&brec, NULL);
  502                         return -EEXIST;
  503                 }
  504         }
  505 
  506         keysize = key->KeyLen;
  507         datasize = ROUND(datasize);
  508         belem = brec.bottom;
  509         belem->flags = 0;
  510         if (bnode_freespace(belem->bnr.bn) <
  511                             (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) {
  512                 belem->flags |= HFS_BPATH_OVERFLOW;
  513         }
  514         if (belem->record == 0) {
  515                 belem->flags |= HFS_BPATH_FIRST;
  516         }
  517 
  518         if (!belem->flags) {
  519                 hfs_brec_lock(&brec, brec.bottom);
  520                 reserve = 0;
  521         } else {
  522                 reserve = brec.bottom - brec.top;
  523                 if (brec.top == 0) {
  524                         ++reserve;
  525                 }
  526                 /* make certain we have enough nodes to proceed */
  527                 if ((tree->bthFree - tree->reserved) < reserve) {
  528                         hfs_brec_relse(&brec, NULL);
  529                         hfs_btree_lock(tree);
  530                         if ((tree->bthFree - tree->reserved) < reserve) {
  531                                 hfs_btree_extend(tree);
  532                         }
  533                         hfs_btree_unlock(tree);
  534                         if ((tree->bthFree - tree->reserved) < reserve) {
  535                                 return -ENOSPC;
  536                         } else {
  537                                 goto restart;
  538                         }
  539                 }
  540                 tree->reserved += reserve;
  541                 hfs_brec_lock(&brec, NULL);
  542         }
  543 
  544         retval = binsert(&brec, key, data, keysize, datasize, reserve);
  545         hfs_brec_relse(&brec, NULL);
  546         if (!retval) {
  547                 ++tree->bthNRecs;
  548                 tree->dirt = 1;
  549         }
  550         return retval;
  551 }

Cache object: 1e1a283032381ca3937d008ae74effc2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.