2 * linux/fs/hfs/bdelete.c
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU Public License.
7 * This file contains the code to delete records in a B-tree.
9 * "XXX" in a comment is a note to myself to consider changing something.
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.
16 #include "hfs_btree.h"
18 /*================ Variable-like macros ================*/
20 #define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
21 #define NO_SPACE (HFS_SECTOR_SIZE+1)
23 /*================ File-local functions ================*/
29 * Deletes a record from a given bnode without regard to it becoming empty.
31 * struct hfs_brec* brec: pointer to the brec for the deletion
32 * struct hfs_belem* belem: which node in 'brec' to delete from
38 * 'brec' points to a valid (struct hfs_brec).
39 * 'belem' points to a valid (struct hfs_belem) in 'brec'.
41 * The record has been inserted in the position indicated by 'brec'.
43 static void bdelete_nonempty(struct hfs_brec
*brec
, struct hfs_belem
*belem
)
45 int i
, rec
, nrecs
, tomove
;
48 struct hfs_bnode
*bnode
= belem
->bnr
.bn
;
51 nrecs
= bnode
->ndNRecs
;
52 size
= bnode_rsize(bnode
, rec
);
53 tomove
= bnode_offset(bnode
, nrecs
+1) - bnode_offset(bnode
, rec
+1);
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
));
61 start
= bnode_key(bnode
, rec
);
62 memmove(start
, start
+ size
, tomove
);
64 /* update record count */
72 * Delete the current root bnode.
74 * struct hfs_bnode_ref *root: reference to the root bnode
78 * int: 0 on success, error code on failure
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.
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.
90 static int del_root(struct hfs_bnode_ref
*root
)
92 struct hfs_btree
*tree
= root
->bn
->tree
;
93 struct hfs_bnode_ref child
;
96 if (root
->bn
->ndNRecs
> 1) {
98 } else if (root
->bn
->ndNRecs
== 0) {
107 if (tree
->bthDepth
) {
108 hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
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
)));
117 child
= hfs_bnode_find(tree
, node
, HFS_LOCK_READ
);
119 hfs_warn("hfs_bdelete: can't read child node.\n");
123 child
.bn
->sticky
= HFS_STICKY
;
124 if (child
.bn
->next
) {
125 child
.bn
->next
->prev
= child
.bn
->prev
;
127 if (child
.bn
->prev
) {
128 child
.bn
->prev
->next
= child
.bn
->next
;
130 if (bhash(tree
, child
.bn
->node
) == child
.bn
) {
131 bhash(tree
, child
.bn
->node
) = child
.bn
->next
;
133 child
.bn
->next
= NULL
;
134 child
.bn
->prev
= NULL
;
136 tree
->bthRoot
= child
.bn
->node
;
137 tree
->root
= child
.bn
;
139 /* re-assign bthFNode and bthLNode if the new root is
141 if (child
.bn
->ndType
== ndLeafNode
) {
142 tree
->bthFNode
= node
;
143 tree
->bthLNode
= node
;
145 hfs_bnode_relse(&child
);
147 tree
->bthRoot
= node
;
150 if (!tree
->bthDepth
) {
151 hfs_warn("hfs_bdelete: non-empty tree with "
155 return hfs_bnode_free(root
); /* marks tree dirty */
157 hfs_bnode_relse(root
);
161 hfs_bnode_relse(root
);
167 * delete_empty_bnode()
170 * Removes an empty non-root bnode from between 'left' and 'right'
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):
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.
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.
203 static 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
)
207 struct hfs_bnode
*bnode
= center
->bn
;
210 left
->bn
->ndFLink
= right_node
;
211 } else if (bnode
->ndType
== ndLeafNode
) {
212 bnode
->tree
->bthFNode
= right_node
;
213 bnode
->tree
->dirt
= 1;
217 right
->bn
->ndBLink
= left_node
;
218 } else if (bnode
->ndType
== ndLeafNode
) {
219 bnode
->tree
->bthLNode
= left_node
;
220 bnode
->tree
->dirt
= 1;
228 * Attempt to equalize space usage in neighboring bnodes.
230 * struct hfs_bnode *left: the left bnode.
231 * struct hfs_bnode *right: the right bnode.
232 * Output Variable(s):
237 * 'left' and 'right' point to valid (struct hfs_bnode)s obtained
238 * with HFS_LOCK_WRITE access, and are neighbors.
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.
245 static void balance(struct hfs_bnode
*left
, struct hfs_bnode
*right
)
247 int index
, left_free
, right_free
, half
;
249 left_free
= bnode_freespace(left
);
250 right_free
= bnode_freespace(right
);
251 half
= (left_free
+ right_free
)/2;
253 if (left_free
< right_free
) {
254 /* shift right to balance */
255 index
= left
->ndNRecs
+ 1;
256 while (right_free
>= half
) {
258 right_free
-= bnode_rsize(left
,index
)+sizeof(hfs_u16
);
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
);
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
);
271 /* shift left to balance */
273 while (left_free
>= half
) {
275 left_free
-= bnode_rsize(right
,index
)+sizeof(hfs_u16
);
278 #if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
279 hfs_warn("shifting %d of %d recs left to balance: ",
280 index
-1, right
->ndNRecs
);
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
);
293 * Delete the given record from a B-tree.
295 static int bdelete(struct hfs_brec
*brec
)
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
;
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
;
313 bdelete_nonempty(brec
, belem
);
315 if (bnode
->node
== tree
->root
->node
) {
316 del_root(&belem
->bnr
);
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
);
332 /* grab the left neighbor if it exists */
334 hfs_bnode_lock(&belem
->bnr
, HFS_LOCK_RESRV
);
335 left
= hfs_bnode_find(tree
,left_node
,HFS_LOCK_WRITE
);
337 hfs_warn("hfs_bdelete: unable to read left "
339 hfs_brec_relse(brec
, NULL
);
342 hfs_bnode_lock(&belem
->bnr
, HFS_LOCK_WRITE
);
343 if (parent
->record
!= 1) {
344 left_space
= bnode_freespace(left
.bn
);
346 left_space
= NO_SPACE
;
350 left_space
= NO_SPACE
;
353 /* grab the right neighbor if it exists */
355 right
= hfs_bnode_find(tree
,right_node
,HFS_LOCK_WRITE
);
357 hfs_warn("hfs_bdelete: unable to read right "
359 hfs_bnode_relse(&left
);
360 hfs_brec_relse(brec
, NULL
);
363 if (parent
->record
< parent
->bnr
.bn
->ndNRecs
) {
364 right_space
= bnode_freespace(right
.bn
);
366 right_space
= NO_SPACE
;
370 right_space
= NO_SPACE
;
373 if (left_space
< right_space
) {
374 min_space
= left_space
;
376 min_space
= right_space
;
379 if (min_space
== NO_SPACE
) {
380 hfs_warn("hfs_bdelete: no siblings?\n");
381 hfs_brec_relse(brec
, NULL
);
385 if (bnode
->ndNRecs
== 0) {
386 delete_empty_bnode(left_node
, &left
, &belem
->bnr
,
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
,
395 hfs_bnode_shift_right(bnode
, right
.bn
, 1);
398 delete_empty_bnode(left_node
, &left
, &belem
->bnr
,
400 } else if (min_space
== right_space
) {
401 balance(bnode
, right
.bn
);
404 balance(left
.bn
, bnode
);
409 hfs_bnode_update_key(brec
, belem
, right
.bn
, 1);
412 hfs_bnode_relse(&left
);
413 hfs_bnode_relse(&right
);
415 if (bnode
->ndNRecs
) {
417 hfs_bnode_update_key(brec
, belem
, bnode
, 0);
422 hfs_bnode_free(&belem
->bnr
);
428 if (belem
< brec
->top
) {
429 hfs_warn("hfs_bdelete: Missing parent.\n");
430 hfs_brec_relse(brec
, NULL
);
434 bdelete_nonempty(brec
, belem
);
437 hfs_brec_relse(brec
, NULL
);
441 /*================ Global functions ================*/
446 * Delete the requested record from a B-tree.
448 int hfs_bdelete(struct hfs_btree
*tree
, const struct hfs_bkey
*key
)
450 struct hfs_belem
*belem
;
451 struct hfs_bnode
*bnode
;
452 struct hfs_brec brec
;
455 if (!tree
|| (tree
->magic
!= HFS_BTREE_MAGIC
) || !key
) {
456 hfs_warn("hfs_bdelete: invalid arguments.\n");
460 retval
= hfs_bfind(&brec
, tree
, key
, HFS_BFIND_DELETE
);
463 bnode
= belem
->bnr
.bn
;
466 if ((bnode
->ndNRecs
* sizeof(hfs_u16
) + bnode_end(bnode
) -
467 bnode_rsize(bnode
, belem
->record
)) < FULL
/2) {
468 belem
->flags
|= HFS_BPATH_UNDERFLOW
;
470 if (belem
->record
== 1) {
471 belem
->flags
|= HFS_BPATH_FIRST
;
475 hfs_brec_lock(&brec
, brec
.bottom
);
477 hfs_brec_lock(&brec
, NULL
);
480 retval
= bdelete(&brec
);
482 --brec
.tree
->bthNRecs
;
485 hfs_brec_relse(&brec
, NULL
);