diff options
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 124 |
1 files changed, 106 insertions, 18 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index c10e4c70f02d..20d3300bd268 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -3521,7 +3521,40 @@ out: | |||
3521 | } | 3521 | } |
3522 | 3522 | ||
3523 | /* | 3523 | /* |
3524 | * Check if ino ino1 is an ancestor of inode ino2 in the given root. | 3524 | * Check if inode ino2, or any of its ancestors, is inode ino1. |
3525 | * Return 1 if true, 0 if false and < 0 on error. | ||
3526 | */ | ||
3527 | static int check_ino_in_path(struct btrfs_root *root, | ||
3528 | const u64 ino1, | ||
3529 | const u64 ino1_gen, | ||
3530 | const u64 ino2, | ||
3531 | const u64 ino2_gen, | ||
3532 | struct fs_path *fs_path) | ||
3533 | { | ||
3534 | u64 ino = ino2; | ||
3535 | |||
3536 | if (ino1 == ino2) | ||
3537 | return ino1_gen == ino2_gen; | ||
3538 | |||
3539 | while (ino > BTRFS_FIRST_FREE_OBJECTID) { | ||
3540 | u64 parent; | ||
3541 | u64 parent_gen; | ||
3542 | int ret; | ||
3543 | |||
3544 | fs_path_reset(fs_path); | ||
3545 | ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path); | ||
3546 | if (ret < 0) | ||
3547 | return ret; | ||
3548 | if (parent == ino1) | ||
3549 | return parent_gen == ino1_gen; | ||
3550 | ino = parent; | ||
3551 | } | ||
3552 | return 0; | ||
3553 | } | ||
3554 | |||
3555 | /* | ||
3556 | * Check if ino ino1 is an ancestor of inode ino2 in the given root for any | ||
3557 | * possible path (in case ino2 is not a directory and has multiple hard links). | ||
3525 | * Return 1 if true, 0 if false and < 0 on error. | 3558 | * Return 1 if true, 0 if false and < 0 on error. |
3526 | */ | 3559 | */ |
3527 | static int is_ancestor(struct btrfs_root *root, | 3560 | static int is_ancestor(struct btrfs_root *root, |
@@ -3530,36 +3563,91 @@ static int is_ancestor(struct btrfs_root *root, | |||
3530 | const u64 ino2, | 3563 | const u64 ino2, |
3531 | struct fs_path *fs_path) | 3564 | struct fs_path *fs_path) |
3532 | { | 3565 | { |
3533 | u64 ino = ino2; | 3566 | bool free_fs_path = false; |
3534 | bool free_path = false; | ||
3535 | int ret = 0; | 3567 | int ret = 0; |
3568 | struct btrfs_path *path = NULL; | ||
3569 | struct btrfs_key key; | ||
3536 | 3570 | ||
3537 | if (!fs_path) { | 3571 | if (!fs_path) { |
3538 | fs_path = fs_path_alloc(); | 3572 | fs_path = fs_path_alloc(); |
3539 | if (!fs_path) | 3573 | if (!fs_path) |
3540 | return -ENOMEM; | 3574 | return -ENOMEM; |
3541 | free_path = true; | 3575 | free_fs_path = true; |
3542 | } | 3576 | } |
3543 | 3577 | ||
3544 | while (ino > BTRFS_FIRST_FREE_OBJECTID) { | 3578 | path = alloc_path_for_send(); |
3545 | u64 parent; | 3579 | if (!path) { |
3546 | u64 parent_gen; | 3580 | ret = -ENOMEM; |
3581 | goto out; | ||
3582 | } | ||
3547 | 3583 | ||
3548 | fs_path_reset(fs_path); | 3584 | key.objectid = ino2; |
3549 | ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path); | 3585 | key.type = BTRFS_INODE_REF_KEY; |
3550 | if (ret < 0) { | 3586 | key.offset = 0; |
3551 | if (ret == -ENOENT && ino == ino2) | 3587 | |
3552 | ret = 0; | 3588 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); |
3553 | goto out; | 3589 | if (ret < 0) |
3590 | goto out; | ||
3591 | |||
3592 | while (true) { | ||
3593 | struct extent_buffer *leaf = path->nodes[0]; | ||
3594 | int slot = path->slots[0]; | ||
3595 | u32 cur_offset = 0; | ||
3596 | u32 item_size; | ||
3597 | |||
3598 | if (slot >= btrfs_header_nritems(leaf)) { | ||
3599 | ret = btrfs_next_leaf(root, path); | ||
3600 | if (ret < 0) | ||
3601 | goto out; | ||
3602 | if (ret > 0) | ||
3603 | break; | ||
3604 | continue; | ||
3554 | } | 3605 | } |
3555 | if (parent == ino1) { | 3606 | |
3556 | ret = parent_gen == ino1_gen ? 1 : 0; | 3607 | btrfs_item_key_to_cpu(leaf, &key, slot); |
3557 | goto out; | 3608 | if (key.objectid != ino2) |
3609 | break; | ||
3610 | if (key.type != BTRFS_INODE_REF_KEY && | ||
3611 | key.type != BTRFS_INODE_EXTREF_KEY) | ||
3612 | break; | ||
3613 | |||
3614 | item_size = btrfs_item_size_nr(leaf, slot); | ||
3615 | while (cur_offset < item_size) { | ||
3616 | u64 parent; | ||
3617 | u64 parent_gen; | ||
3618 | |||
3619 | if (key.type == BTRFS_INODE_EXTREF_KEY) { | ||
3620 | unsigned long ptr; | ||
3621 | struct btrfs_inode_extref *extref; | ||
3622 | |||
3623 | ptr = btrfs_item_ptr_offset(leaf, slot); | ||
3624 | extref = (struct btrfs_inode_extref *) | ||
3625 | (ptr + cur_offset); | ||
3626 | parent = btrfs_inode_extref_parent(leaf, | ||
3627 | extref); | ||
3628 | cur_offset += sizeof(*extref); | ||
3629 | cur_offset += btrfs_inode_extref_name_len(leaf, | ||
3630 | extref); | ||
3631 | } else { | ||
3632 | parent = key.offset; | ||
3633 | cur_offset = item_size; | ||
3634 | } | ||
3635 | |||
3636 | ret = get_inode_info(root, parent, NULL, &parent_gen, | ||
3637 | NULL, NULL, NULL, NULL); | ||
3638 | if (ret < 0) | ||
3639 | goto out; | ||
3640 | ret = check_ino_in_path(root, ino1, ino1_gen, | ||
3641 | parent, parent_gen, fs_path); | ||
3642 | if (ret) | ||
3643 | goto out; | ||
3558 | } | 3644 | } |
3559 | ino = parent; | 3645 | path->slots[0]++; |
3560 | } | 3646 | } |
3647 | ret = 0; | ||
3561 | out: | 3648 | out: |
3562 | if (free_path) | 3649 | btrfs_free_path(path); |
3650 | if (free_fs_path) | ||
3563 | fs_path_free(fs_path); | 3651 | fs_path_free(fs_path); |
3564 | return ret; | 3652 | return ret; |
3565 | } | 3653 | } |