|
|
@@ -3521,7 +3521,40 @@ out:
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Check if ino ino1 is an ancestor of inode ino2 in the given root.
|
|
|
+ * Check if inode ino2, or any of its ancestors, is inode ino1.
|
|
|
+ * Return 1 if true, 0 if false and < 0 on error.
|
|
|
+ */
|
|
|
+static int check_ino_in_path(struct btrfs_root *root,
|
|
|
+ const u64 ino1,
|
|
|
+ const u64 ino1_gen,
|
|
|
+ const u64 ino2,
|
|
|
+ const u64 ino2_gen,
|
|
|
+ struct fs_path *fs_path)
|
|
|
+{
|
|
|
+ u64 ino = ino2;
|
|
|
+
|
|
|
+ if (ino1 == ino2)
|
|
|
+ return ino1_gen == ino2_gen;
|
|
|
+
|
|
|
+ while (ino > BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ u64 parent;
|
|
|
+ u64 parent_gen;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ fs_path_reset(fs_path);
|
|
|
+ ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ if (parent == ino1)
|
|
|
+ return parent_gen == ino1_gen;
|
|
|
+ ino = parent;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Check if ino ino1 is an ancestor of inode ino2 in the given root for any
|
|
|
+ * possible path (in case ino2 is not a directory and has multiple hard links).
|
|
|
* Return 1 if true, 0 if false and < 0 on error.
|
|
|
*/
|
|
|
static int is_ancestor(struct btrfs_root *root,
|
|
|
@@ -3530,36 +3563,91 @@ static int is_ancestor(struct btrfs_root *root,
|
|
|
const u64 ino2,
|
|
|
struct fs_path *fs_path)
|
|
|
{
|
|
|
- u64 ino = ino2;
|
|
|
- bool free_path = false;
|
|
|
+ bool free_fs_path = false;
|
|
|
int ret = 0;
|
|
|
+ struct btrfs_path *path = NULL;
|
|
|
+ struct btrfs_key key;
|
|
|
|
|
|
if (!fs_path) {
|
|
|
fs_path = fs_path_alloc();
|
|
|
if (!fs_path)
|
|
|
return -ENOMEM;
|
|
|
- free_path = true;
|
|
|
+ free_fs_path = true;
|
|
|
}
|
|
|
|
|
|
- while (ino > BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
- u64 parent;
|
|
|
- u64 parent_gen;
|
|
|
+ path = alloc_path_for_send();
|
|
|
+ if (!path) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- fs_path_reset(fs_path);
|
|
|
- ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path);
|
|
|
- if (ret < 0) {
|
|
|
- if (ret == -ENOENT && ino == ino2)
|
|
|
- ret = 0;
|
|
|
- goto out;
|
|
|
+ key.objectid = ino2;
|
|
|
+ key.type = BTRFS_INODE_REF_KEY;
|
|
|
+ key.offset = 0;
|
|
|
+
|
|
|
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ struct extent_buffer *leaf = path->nodes[0];
|
|
|
+ int slot = path->slots[0];
|
|
|
+ u32 cur_offset = 0;
|
|
|
+ u32 item_size;
|
|
|
+
|
|
|
+ if (slot >= btrfs_header_nritems(leaf)) {
|
|
|
+ ret = btrfs_next_leaf(root, path);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ if (ret > 0)
|
|
|
+ break;
|
|
|
+ continue;
|
|
|
}
|
|
|
- if (parent == ino1) {
|
|
|
- ret = parent_gen == ino1_gen ? 1 : 0;
|
|
|
- goto out;
|
|
|
+
|
|
|
+ btrfs_item_key_to_cpu(leaf, &key, slot);
|
|
|
+ if (key.objectid != ino2)
|
|
|
+ break;
|
|
|
+ if (key.type != BTRFS_INODE_REF_KEY &&
|
|
|
+ key.type != BTRFS_INODE_EXTREF_KEY)
|
|
|
+ break;
|
|
|
+
|
|
|
+ item_size = btrfs_item_size_nr(leaf, slot);
|
|
|
+ while (cur_offset < item_size) {
|
|
|
+ u64 parent;
|
|
|
+ u64 parent_gen;
|
|
|
+
|
|
|
+ if (key.type == BTRFS_INODE_EXTREF_KEY) {
|
|
|
+ unsigned long ptr;
|
|
|
+ struct btrfs_inode_extref *extref;
|
|
|
+
|
|
|
+ ptr = btrfs_item_ptr_offset(leaf, slot);
|
|
|
+ extref = (struct btrfs_inode_extref *)
|
|
|
+ (ptr + cur_offset);
|
|
|
+ parent = btrfs_inode_extref_parent(leaf,
|
|
|
+ extref);
|
|
|
+ cur_offset += sizeof(*extref);
|
|
|
+ cur_offset += btrfs_inode_extref_name_len(leaf,
|
|
|
+ extref);
|
|
|
+ } else {
|
|
|
+ parent = key.offset;
|
|
|
+ cur_offset = item_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = get_inode_info(root, parent, NULL, &parent_gen,
|
|
|
+ NULL, NULL, NULL, NULL);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ ret = check_ino_in_path(root, ino1, ino1_gen,
|
|
|
+ parent, parent_gen, fs_path);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
}
|
|
|
- ino = parent;
|
|
|
+ path->slots[0]++;
|
|
|
}
|
|
|
+ ret = 0;
|
|
|
out:
|
|
|
- if (free_path)
|
|
|
+ btrfs_free_path(path);
|
|
|
+ if (free_fs_path)
|
|
|
fs_path_free(fs_path);
|
|
|
return ret;
|
|
|
}
|