diff options
author | Christoph Hellwig <hch@infradead.org> | 2008-10-30 01:58:01 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@sgi.com> | 2008-10-30 01:58:01 -0400 |
commit | 91cca5df9bc85efdabfa645f51d54259ed09f4bf (patch) | |
tree | 29032361e21d1210effb72f4018f2a202d6a9fb0 /fs/xfs/xfs_btree.c | |
parent | d4b3a4b7dd62f2e111d4d0afa9ef3f9b6cd955c0 (diff) |
[XFS] implement generic xfs_btree_delete/delrec
Make the btree delete code generic. Based on a patch from David Chinner
with lots of changes to follow the original btree implementations more
closely. While this loses some of the generic helper routines for
inserting/moving/removing records it also solves some of the one off bugs
in the original code and makes it easier to verify.
SGI-PV: 985583
SGI-Modid: xfs-linux-melb:xfs-kern:32205a
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: Bill O'Donnell <billodo@sgi.com>
Signed-off-by: David Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/xfs_btree.c')
-rw-r--r-- | fs/xfs/xfs_btree.c | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 75a8a7b00dfb..28cc76818343 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c | |||
@@ -3171,3 +3171,596 @@ out0: | |||
3171 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | 3171 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); |
3172 | return 0; | 3172 | return 0; |
3173 | } | 3173 | } |
3174 | |||
3175 | STATIC int | ||
3176 | xfs_btree_dec_cursor( | ||
3177 | struct xfs_btree_cur *cur, | ||
3178 | int level, | ||
3179 | int *stat) | ||
3180 | { | ||
3181 | int error; | ||
3182 | int i; | ||
3183 | |||
3184 | if (level > 0) { | ||
3185 | error = xfs_btree_decrement(cur, level, &i); | ||
3186 | if (error) | ||
3187 | return error; | ||
3188 | } | ||
3189 | |||
3190 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3191 | *stat = 1; | ||
3192 | return 0; | ||
3193 | } | ||
3194 | |||
3195 | /* | ||
3196 | * Single level of the btree record deletion routine. | ||
3197 | * Delete record pointed to by cur/level. | ||
3198 | * Remove the record from its block then rebalance the tree. | ||
3199 | * Return 0 for error, 1 for done, 2 to go on to the next level. | ||
3200 | */ | ||
3201 | STATIC int /* error */ | ||
3202 | xfs_btree_delrec( | ||
3203 | struct xfs_btree_cur *cur, /* btree cursor */ | ||
3204 | int level, /* level removing record from */ | ||
3205 | int *stat) /* fail/done/go-on */ | ||
3206 | { | ||
3207 | struct xfs_btree_block *block; /* btree block */ | ||
3208 | union xfs_btree_ptr cptr; /* current block ptr */ | ||
3209 | struct xfs_buf *bp; /* buffer for block */ | ||
3210 | int error; /* error return value */ | ||
3211 | int i; /* loop counter */ | ||
3212 | union xfs_btree_key key; /* storage for keyp */ | ||
3213 | union xfs_btree_key *keyp = &key; /* passed to the next level */ | ||
3214 | union xfs_btree_ptr lptr; /* left sibling block ptr */ | ||
3215 | struct xfs_buf *lbp; /* left buffer pointer */ | ||
3216 | struct xfs_btree_block *left; /* left btree block */ | ||
3217 | int lrecs = 0; /* left record count */ | ||
3218 | int ptr; /* key/record index */ | ||
3219 | union xfs_btree_ptr rptr; /* right sibling block ptr */ | ||
3220 | struct xfs_buf *rbp; /* right buffer pointer */ | ||
3221 | struct xfs_btree_block *right; /* right btree block */ | ||
3222 | struct xfs_btree_block *rrblock; /* right-right btree block */ | ||
3223 | struct xfs_buf *rrbp; /* right-right buffer pointer */ | ||
3224 | int rrecs = 0; /* right record count */ | ||
3225 | struct xfs_btree_cur *tcur; /* temporary btree cursor */ | ||
3226 | int numrecs; /* temporary numrec count */ | ||
3227 | |||
3228 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); | ||
3229 | XFS_BTREE_TRACE_ARGI(cur, level); | ||
3230 | |||
3231 | tcur = NULL; | ||
3232 | |||
3233 | /* Get the index of the entry being deleted, check for nothing there. */ | ||
3234 | ptr = cur->bc_ptrs[level]; | ||
3235 | if (ptr == 0) { | ||
3236 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3237 | *stat = 0; | ||
3238 | return 0; | ||
3239 | } | ||
3240 | |||
3241 | /* Get the buffer & block containing the record or key/ptr. */ | ||
3242 | block = xfs_btree_get_block(cur, level, &bp); | ||
3243 | numrecs = xfs_btree_get_numrecs(block); | ||
3244 | |||
3245 | #ifdef DEBUG | ||
3246 | error = xfs_btree_check_block(cur, block, level, bp); | ||
3247 | if (error) | ||
3248 | goto error0; | ||
3249 | #endif | ||
3250 | |||
3251 | /* Fail if we're off the end of the block. */ | ||
3252 | if (ptr > numrecs) { | ||
3253 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3254 | *stat = 0; | ||
3255 | return 0; | ||
3256 | } | ||
3257 | |||
3258 | XFS_BTREE_STATS_INC(cur, delrec); | ||
3259 | XFS_BTREE_STATS_ADD(cur, moves, numrecs - ptr); | ||
3260 | |||
3261 | /* Excise the entries being deleted. */ | ||
3262 | if (level > 0) { | ||
3263 | /* It's a nonleaf. operate on keys and ptrs */ | ||
3264 | union xfs_btree_key *lkp; | ||
3265 | union xfs_btree_ptr *lpp; | ||
3266 | |||
3267 | lkp = xfs_btree_key_addr(cur, ptr + 1, block); | ||
3268 | lpp = xfs_btree_ptr_addr(cur, ptr + 1, block); | ||
3269 | |||
3270 | #ifdef DEBUG | ||
3271 | for (i = 0; i < numrecs - ptr; i++) { | ||
3272 | error = xfs_btree_check_ptr(cur, lpp, i, level); | ||
3273 | if (error) | ||
3274 | goto error0; | ||
3275 | } | ||
3276 | #endif | ||
3277 | |||
3278 | if (ptr < numrecs) { | ||
3279 | xfs_btree_shift_keys(cur, lkp, -1, numrecs - ptr); | ||
3280 | xfs_btree_shift_ptrs(cur, lpp, -1, numrecs - ptr); | ||
3281 | xfs_btree_log_keys(cur, bp, ptr, numrecs - 1); | ||
3282 | xfs_btree_log_ptrs(cur, bp, ptr, numrecs - 1); | ||
3283 | } | ||
3284 | |||
3285 | /* | ||
3286 | * If it's the first record in the block, we'll need to pass a | ||
3287 | * key up to the next level (updkey). | ||
3288 | */ | ||
3289 | if (ptr == 1) | ||
3290 | keyp = xfs_btree_key_addr(cur, 1, block); | ||
3291 | } else { | ||
3292 | /* It's a leaf. operate on records */ | ||
3293 | if (ptr < numrecs) { | ||
3294 | xfs_btree_shift_recs(cur, | ||
3295 | xfs_btree_rec_addr(cur, ptr + 1, block), | ||
3296 | -1, numrecs - ptr); | ||
3297 | xfs_btree_log_recs(cur, bp, ptr, numrecs - 1); | ||
3298 | } | ||
3299 | |||
3300 | /* | ||
3301 | * If it's the first record in the block, we'll need a key | ||
3302 | * structure to pass up to the next level (updkey). | ||
3303 | */ | ||
3304 | if (ptr == 1) { | ||
3305 | cur->bc_ops->init_key_from_rec(&key, | ||
3306 | xfs_btree_rec_addr(cur, 1, block)); | ||
3307 | keyp = &key; | ||
3308 | } | ||
3309 | } | ||
3310 | |||
3311 | /* | ||
3312 | * Decrement and log the number of entries in the block. | ||
3313 | */ | ||
3314 | xfs_btree_set_numrecs(block, --numrecs); | ||
3315 | xfs_btree_log_block(cur, bp, XFS_BB_NUMRECS); | ||
3316 | |||
3317 | /* | ||
3318 | * If we are tracking the last record in the tree and | ||
3319 | * we are at the far right edge of the tree, update it. | ||
3320 | */ | ||
3321 | if (xfs_btree_is_lastrec(cur, block, level)) { | ||
3322 | cur->bc_ops->update_lastrec(cur, block, NULL, | ||
3323 | ptr, LASTREC_DELREC); | ||
3324 | } | ||
3325 | |||
3326 | /* | ||
3327 | * We're at the root level. First, shrink the root block in-memory. | ||
3328 | * Try to get rid of the next level down. If we can't then there's | ||
3329 | * nothing left to do. | ||
3330 | */ | ||
3331 | if (level == cur->bc_nlevels - 1) { | ||
3332 | if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) { | ||
3333 | xfs_iroot_realloc(cur->bc_private.b.ip, -1, | ||
3334 | cur->bc_private.b.whichfork); | ||
3335 | |||
3336 | error = xfs_btree_kill_iroot(cur); | ||
3337 | if (error) | ||
3338 | goto error0; | ||
3339 | |||
3340 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3341 | if (error) | ||
3342 | goto error0; | ||
3343 | *stat = 1; | ||
3344 | return 0; | ||
3345 | } | ||
3346 | |||
3347 | /* | ||
3348 | * If this is the root level, and there's only one entry left, | ||
3349 | * and it's NOT the leaf level, then we can get rid of this | ||
3350 | * level. | ||
3351 | */ | ||
3352 | if (numrecs == 1 && level > 0) { | ||
3353 | union xfs_btree_ptr *pp; | ||
3354 | /* | ||
3355 | * pp is still set to the first pointer in the block. | ||
3356 | * Make it the new root of the btree. | ||
3357 | */ | ||
3358 | pp = xfs_btree_ptr_addr(cur, 1, block); | ||
3359 | error = cur->bc_ops->kill_root(cur, bp, level, pp); | ||
3360 | if (error) | ||
3361 | goto error0; | ||
3362 | } else if (level > 0) { | ||
3363 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3364 | if (error) | ||
3365 | goto error0; | ||
3366 | } | ||
3367 | *stat = 1; | ||
3368 | return 0; | ||
3369 | } | ||
3370 | |||
3371 | /* | ||
3372 | * If we deleted the leftmost entry in the block, update the | ||
3373 | * key values above us in the tree. | ||
3374 | */ | ||
3375 | if (ptr == 1) { | ||
3376 | error = xfs_btree_updkey(cur, keyp, level + 1); | ||
3377 | if (error) | ||
3378 | goto error0; | ||
3379 | } | ||
3380 | |||
3381 | /* | ||
3382 | * If the number of records remaining in the block is at least | ||
3383 | * the minimum, we're done. | ||
3384 | */ | ||
3385 | if (numrecs >= cur->bc_ops->get_minrecs(cur, level)) { | ||
3386 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3387 | if (error) | ||
3388 | goto error0; | ||
3389 | return 0; | ||
3390 | } | ||
3391 | |||
3392 | /* | ||
3393 | * Otherwise, we have to move some records around to keep the | ||
3394 | * tree balanced. Look at the left and right sibling blocks to | ||
3395 | * see if we can re-balance by moving only one record. | ||
3396 | */ | ||
3397 | xfs_btree_get_sibling(cur, block, &rptr, XFS_BB_RIGHTSIB); | ||
3398 | xfs_btree_get_sibling(cur, block, &lptr, XFS_BB_LEFTSIB); | ||
3399 | |||
3400 | if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) { | ||
3401 | /* | ||
3402 | * One child of root, need to get a chance to copy its contents | ||
3403 | * into the root and delete it. Can't go up to next level, | ||
3404 | * there's nothing to delete there. | ||
3405 | */ | ||
3406 | if (xfs_btree_ptr_is_null(cur, &rptr) && | ||
3407 | xfs_btree_ptr_is_null(cur, &lptr) && | ||
3408 | level == cur->bc_nlevels - 2) { | ||
3409 | error = xfs_btree_kill_iroot(cur); | ||
3410 | if (!error) | ||
3411 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3412 | if (error) | ||
3413 | goto error0; | ||
3414 | return 0; | ||
3415 | } | ||
3416 | } | ||
3417 | |||
3418 | ASSERT(!xfs_btree_ptr_is_null(cur, &rptr) || | ||
3419 | !xfs_btree_ptr_is_null(cur, &lptr)); | ||
3420 | |||
3421 | /* | ||
3422 | * Duplicate the cursor so our btree manipulations here won't | ||
3423 | * disrupt the next level up. | ||
3424 | */ | ||
3425 | error = xfs_btree_dup_cursor(cur, &tcur); | ||
3426 | if (error) | ||
3427 | goto error0; | ||
3428 | |||
3429 | /* | ||
3430 | * If there's a right sibling, see if it's ok to shift an entry | ||
3431 | * out of it. | ||
3432 | */ | ||
3433 | if (!xfs_btree_ptr_is_null(cur, &rptr)) { | ||
3434 | /* | ||
3435 | * Move the temp cursor to the last entry in the next block. | ||
3436 | * Actually any entry but the first would suffice. | ||
3437 | */ | ||
3438 | i = xfs_btree_lastrec(tcur, level); | ||
3439 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3440 | |||
3441 | error = xfs_btree_increment(tcur, level, &i); | ||
3442 | if (error) | ||
3443 | goto error0; | ||
3444 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3445 | |||
3446 | i = xfs_btree_lastrec(tcur, level); | ||
3447 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3448 | |||
3449 | /* Grab a pointer to the block. */ | ||
3450 | right = xfs_btree_get_block(tcur, level, &rbp); | ||
3451 | #ifdef DEBUG | ||
3452 | error = xfs_btree_check_block(tcur, right, level, rbp); | ||
3453 | if (error) | ||
3454 | goto error0; | ||
3455 | #endif | ||
3456 | /* Grab the current block number, for future use. */ | ||
3457 | xfs_btree_get_sibling(tcur, right, &cptr, XFS_BB_LEFTSIB); | ||
3458 | |||
3459 | /* | ||
3460 | * If right block is full enough so that removing one entry | ||
3461 | * won't make it too empty, and left-shifting an entry out | ||
3462 | * of right to us works, we're done. | ||
3463 | */ | ||
3464 | if (xfs_btree_get_numrecs(right) - 1 >= | ||
3465 | cur->bc_ops->get_minrecs(tcur, level)) { | ||
3466 | error = xfs_btree_lshift(tcur, level, &i); | ||
3467 | if (error) | ||
3468 | goto error0; | ||
3469 | if (i) { | ||
3470 | ASSERT(xfs_btree_get_numrecs(block) >= | ||
3471 | cur->bc_ops->get_minrecs(tcur, level)); | ||
3472 | |||
3473 | xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR); | ||
3474 | tcur = NULL; | ||
3475 | |||
3476 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3477 | if (error) | ||
3478 | goto error0; | ||
3479 | return 0; | ||
3480 | } | ||
3481 | } | ||
3482 | |||
3483 | /* | ||
3484 | * Otherwise, grab the number of records in right for | ||
3485 | * future reference, and fix up the temp cursor to point | ||
3486 | * to our block again (last record). | ||
3487 | */ | ||
3488 | rrecs = xfs_btree_get_numrecs(right); | ||
3489 | if (!xfs_btree_ptr_is_null(cur, &lptr)) { | ||
3490 | i = xfs_btree_firstrec(tcur, level); | ||
3491 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3492 | |||
3493 | error = xfs_btree_decrement(tcur, level, &i); | ||
3494 | if (error) | ||
3495 | goto error0; | ||
3496 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3497 | } | ||
3498 | } | ||
3499 | |||
3500 | /* | ||
3501 | * If there's a left sibling, see if it's ok to shift an entry | ||
3502 | * out of it. | ||
3503 | */ | ||
3504 | if (!xfs_btree_ptr_is_null(cur, &lptr)) { | ||
3505 | /* | ||
3506 | * Move the temp cursor to the first entry in the | ||
3507 | * previous block. | ||
3508 | */ | ||
3509 | i = xfs_btree_firstrec(tcur, level); | ||
3510 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3511 | |||
3512 | error = xfs_btree_decrement(tcur, level, &i); | ||
3513 | if (error) | ||
3514 | goto error0; | ||
3515 | i = xfs_btree_firstrec(tcur, level); | ||
3516 | XFS_WANT_CORRUPTED_GOTO(i == 1, error0); | ||
3517 | |||
3518 | /* Grab a pointer to the block. */ | ||
3519 | left = xfs_btree_get_block(tcur, level, &lbp); | ||
3520 | #ifdef DEBUG | ||
3521 | error = xfs_btree_check_block(cur, left, level, lbp); | ||
3522 | if (error) | ||
3523 | goto error0; | ||
3524 | #endif | ||
3525 | /* Grab the current block number, for future use. */ | ||
3526 | xfs_btree_get_sibling(tcur, left, &cptr, XFS_BB_RIGHTSIB); | ||
3527 | |||
3528 | /* | ||
3529 | * If left block is full enough so that removing one entry | ||
3530 | * won't make it too empty, and right-shifting an entry out | ||
3531 | * of left to us works, we're done. | ||
3532 | */ | ||
3533 | if (xfs_btree_get_numrecs(left) - 1 >= | ||
3534 | cur->bc_ops->get_minrecs(tcur, level)) { | ||
3535 | error = xfs_btree_rshift(tcur, level, &i); | ||
3536 | if (error) | ||
3537 | goto error0; | ||
3538 | if (i) { | ||
3539 | ASSERT(xfs_btree_get_numrecs(block) >= | ||
3540 | cur->bc_ops->get_minrecs(tcur, level)); | ||
3541 | xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR); | ||
3542 | tcur = NULL; | ||
3543 | if (level == 0) | ||
3544 | cur->bc_ptrs[0]++; | ||
3545 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3546 | *stat = 1; | ||
3547 | return 0; | ||
3548 | } | ||
3549 | } | ||
3550 | |||
3551 | /* | ||
3552 | * Otherwise, grab the number of records in right for | ||
3553 | * future reference. | ||
3554 | */ | ||
3555 | lrecs = xfs_btree_get_numrecs(left); | ||
3556 | } | ||
3557 | |||
3558 | /* Delete the temp cursor, we're done with it. */ | ||
3559 | xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR); | ||
3560 | tcur = NULL; | ||
3561 | |||
3562 | /* If here, we need to do a join to keep the tree balanced. */ | ||
3563 | ASSERT(!xfs_btree_ptr_is_null(cur, &cptr)); | ||
3564 | |||
3565 | if (!xfs_btree_ptr_is_null(cur, &lptr) && | ||
3566 | lrecs + xfs_btree_get_numrecs(block) <= | ||
3567 | cur->bc_ops->get_maxrecs(cur, level)) { | ||
3568 | /* | ||
3569 | * Set "right" to be the starting block, | ||
3570 | * "left" to be the left neighbor. | ||
3571 | */ | ||
3572 | rptr = cptr; | ||
3573 | right = block; | ||
3574 | rbp = bp; | ||
3575 | error = xfs_btree_read_buf_block(cur, &lptr, level, | ||
3576 | 0, &left, &lbp); | ||
3577 | if (error) | ||
3578 | goto error0; | ||
3579 | |||
3580 | /* | ||
3581 | * If that won't work, see if we can join with the right neighbor block. | ||
3582 | */ | ||
3583 | } else if (!xfs_btree_ptr_is_null(cur, &rptr) && | ||
3584 | rrecs + xfs_btree_get_numrecs(block) <= | ||
3585 | cur->bc_ops->get_maxrecs(cur, level)) { | ||
3586 | /* | ||
3587 | * Set "left" to be the starting block, | ||
3588 | * "right" to be the right neighbor. | ||
3589 | */ | ||
3590 | lptr = cptr; | ||
3591 | left = block; | ||
3592 | lbp = bp; | ||
3593 | error = xfs_btree_read_buf_block(cur, &rptr, level, | ||
3594 | 0, &right, &rbp); | ||
3595 | if (error) | ||
3596 | goto error0; | ||
3597 | |||
3598 | /* | ||
3599 | * Otherwise, we can't fix the imbalance. | ||
3600 | * Just return. This is probably a logic error, but it's not fatal. | ||
3601 | */ | ||
3602 | } else { | ||
3603 | error = xfs_btree_dec_cursor(cur, level, stat); | ||
3604 | if (error) | ||
3605 | goto error0; | ||
3606 | return 0; | ||
3607 | } | ||
3608 | |||
3609 | rrecs = xfs_btree_get_numrecs(right); | ||
3610 | lrecs = xfs_btree_get_numrecs(left); | ||
3611 | |||
3612 | /* | ||
3613 | * We're now going to join "left" and "right" by moving all the stuff | ||
3614 | * in "right" to "left" and deleting "right". | ||
3615 | */ | ||
3616 | XFS_BTREE_STATS_ADD(cur, moves, rrecs); | ||
3617 | if (level > 0) { | ||
3618 | /* It's a non-leaf. Move keys and pointers. */ | ||
3619 | union xfs_btree_key *lkp; /* left btree key */ | ||
3620 | union xfs_btree_ptr *lpp; /* left address pointer */ | ||
3621 | union xfs_btree_key *rkp; /* right btree key */ | ||
3622 | union xfs_btree_ptr *rpp; /* right address pointer */ | ||
3623 | |||
3624 | lkp = xfs_btree_key_addr(cur, lrecs + 1, left); | ||
3625 | lpp = xfs_btree_ptr_addr(cur, lrecs + 1, left); | ||
3626 | rkp = xfs_btree_key_addr(cur, 1, right); | ||
3627 | rpp = xfs_btree_ptr_addr(cur, 1, right); | ||
3628 | #ifdef DEBUG | ||
3629 | for (i = 1; i < rrecs; i++) { | ||
3630 | error = xfs_btree_check_ptr(cur, rpp, i, level); | ||
3631 | if (error) | ||
3632 | goto error0; | ||
3633 | } | ||
3634 | #endif | ||
3635 | xfs_btree_copy_keys(cur, lkp, rkp, rrecs); | ||
3636 | xfs_btree_copy_ptrs(cur, lpp, rpp, rrecs); | ||
3637 | |||
3638 | xfs_btree_log_keys(cur, lbp, lrecs + 1, lrecs + rrecs); | ||
3639 | xfs_btree_log_ptrs(cur, lbp, lrecs + 1, lrecs + rrecs); | ||
3640 | } else { | ||
3641 | /* It's a leaf. Move records. */ | ||
3642 | union xfs_btree_rec *lrp; /* left record pointer */ | ||
3643 | union xfs_btree_rec *rrp; /* right record pointer */ | ||
3644 | |||
3645 | lrp = xfs_btree_rec_addr(cur, lrecs + 1, left); | ||
3646 | rrp = xfs_btree_rec_addr(cur, 1, right); | ||
3647 | |||
3648 | xfs_btree_copy_recs(cur, lrp, rrp, rrecs); | ||
3649 | xfs_btree_log_recs(cur, lbp, lrecs + 1, lrecs + rrecs); | ||
3650 | } | ||
3651 | |||
3652 | XFS_BTREE_STATS_INC(cur, join); | ||
3653 | |||
3654 | /* | ||
3655 | * Fix up the the number of records and right block pointer in the | ||
3656 | * surviving block, and log it. | ||
3657 | */ | ||
3658 | xfs_btree_set_numrecs(left, lrecs + rrecs); | ||
3659 | xfs_btree_get_sibling(cur, right, &cptr, XFS_BB_RIGHTSIB), | ||
3660 | xfs_btree_set_sibling(cur, left, &cptr, XFS_BB_RIGHTSIB); | ||
3661 | xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB); | ||
3662 | |||
3663 | /* If there is a right sibling, point it to the remaining block. */ | ||
3664 | xfs_btree_get_sibling(cur, left, &cptr, XFS_BB_RIGHTSIB); | ||
3665 | if (!xfs_btree_ptr_is_null(cur, &cptr)) { | ||
3666 | error = xfs_btree_read_buf_block(cur, &cptr, level, | ||
3667 | 0, &rrblock, &rrbp); | ||
3668 | if (error) | ||
3669 | goto error0; | ||
3670 | xfs_btree_set_sibling(cur, rrblock, &lptr, XFS_BB_LEFTSIB); | ||
3671 | xfs_btree_log_block(cur, rrbp, XFS_BB_LEFTSIB); | ||
3672 | } | ||
3673 | |||
3674 | /* Free the deleted block. */ | ||
3675 | error = cur->bc_ops->free_block(cur, rbp); | ||
3676 | if (error) | ||
3677 | goto error0; | ||
3678 | XFS_BTREE_STATS_INC(cur, free); | ||
3679 | |||
3680 | /* | ||
3681 | * If we joined with the left neighbor, set the buffer in the | ||
3682 | * cursor to the left block, and fix up the index. | ||
3683 | */ | ||
3684 | if (bp != lbp) { | ||
3685 | cur->bc_bufs[level] = lbp; | ||
3686 | cur->bc_ptrs[level] += lrecs; | ||
3687 | cur->bc_ra[level] = 0; | ||
3688 | } | ||
3689 | /* | ||
3690 | * If we joined with the right neighbor and there's a level above | ||
3691 | * us, increment the cursor at that level. | ||
3692 | */ | ||
3693 | else if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) || | ||
3694 | (level + 1 < cur->bc_nlevels)) { | ||
3695 | error = xfs_btree_increment(cur, level + 1, &i); | ||
3696 | if (error) | ||
3697 | goto error0; | ||
3698 | } | ||
3699 | |||
3700 | /* | ||
3701 | * Readjust the ptr at this level if it's not a leaf, since it's | ||
3702 | * still pointing at the deletion point, which makes the cursor | ||
3703 | * inconsistent. If this makes the ptr 0, the caller fixes it up. | ||
3704 | * We can't use decrement because it would change the next level up. | ||
3705 | */ | ||
3706 | if (level > 0) | ||
3707 | cur->bc_ptrs[level]--; | ||
3708 | |||
3709 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3710 | /* Return value means the next level up has something to do. */ | ||
3711 | *stat = 2; | ||
3712 | return 0; | ||
3713 | |||
3714 | error0: | ||
3715 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
3716 | if (tcur) | ||
3717 | xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR); | ||
3718 | return error; | ||
3719 | } | ||
3720 | |||
3721 | /* | ||
3722 | * Delete the record pointed to by cur. | ||
3723 | * The cursor refers to the place where the record was (could be inserted) | ||
3724 | * when the operation returns. | ||
3725 | */ | ||
3726 | int /* error */ | ||
3727 | xfs_btree_delete( | ||
3728 | struct xfs_btree_cur *cur, | ||
3729 | int *stat) /* success/failure */ | ||
3730 | { | ||
3731 | int error; /* error return value */ | ||
3732 | int level; | ||
3733 | int i; | ||
3734 | |||
3735 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); | ||
3736 | |||
3737 | /* | ||
3738 | * Go up the tree, starting at leaf level. | ||
3739 | * | ||
3740 | * If 2 is returned then a join was done; go to the next level. | ||
3741 | * Otherwise we are done. | ||
3742 | */ | ||
3743 | for (level = 0, i = 2; i == 2; level++) { | ||
3744 | error = xfs_btree_delrec(cur, level, &i); | ||
3745 | if (error) | ||
3746 | goto error0; | ||
3747 | } | ||
3748 | |||
3749 | if (i == 0) { | ||
3750 | for (level = 1; level < cur->bc_nlevels; level++) { | ||
3751 | if (cur->bc_ptrs[level] == 0) { | ||
3752 | error = xfs_btree_decrement(cur, level, &i); | ||
3753 | if (error) | ||
3754 | goto error0; | ||
3755 | break; | ||
3756 | } | ||
3757 | } | ||
3758 | } | ||
3759 | |||
3760 | XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); | ||
3761 | *stat = i; | ||
3762 | return 0; | ||
3763 | error0: | ||
3764 | XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); | ||
3765 | return error; | ||
3766 | } | ||