|
@@ -785,76 +785,18 @@ out:
|
|
|
return match;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-/*
|
|
|
- * replay one inode back reference item found in the log tree.
|
|
|
- * eb, slot and key refer to the buffer and key found in the log tree.
|
|
|
- * root is the destination we are replaying into, and path is for temp
|
|
|
- * use by this function. (it should be released on return).
|
|
|
- */
|
|
|
-static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
|
|
|
+static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
|
|
|
struct btrfs_root *root,
|
|
|
- struct btrfs_root *log,
|
|
|
struct btrfs_path *path,
|
|
|
- struct extent_buffer *eb, int slot,
|
|
|
- struct btrfs_key *key)
|
|
|
+ struct btrfs_root *log_root,
|
|
|
+ struct inode *dir, struct inode *inode,
|
|
|
+ struct btrfs_key *key,
|
|
|
+ struct extent_buffer *eb,
|
|
|
+ struct btrfs_inode_ref *ref,
|
|
|
+ char *name, int namelen, int *search_done)
|
|
|
{
|
|
|
- struct btrfs_inode_ref *ref;
|
|
|
- struct btrfs_dir_item *di;
|
|
|
- struct inode *dir;
|
|
|
- struct inode *inode;
|
|
|
- unsigned long ref_ptr;
|
|
|
- unsigned long ref_end;
|
|
|
- char *name;
|
|
|
- int namelen;
|
|
|
int ret;
|
|
|
- int search_done = 0;
|
|
|
-
|
|
|
- /*
|
|
|
- * it is possible that we didn't log all the parent directories
|
|
|
- * for a given inode. If we don't find the dir, just don't
|
|
|
- * copy the back ref in. The link count fixup code will take
|
|
|
- * care of the rest
|
|
|
- */
|
|
|
- dir = read_one_inode(root, key->offset);
|
|
|
- if (!dir)
|
|
|
- return -ENOENT;
|
|
|
-
|
|
|
- inode = read_one_inode(root, key->objectid);
|
|
|
- if (!inode) {
|
|
|
- iput(dir);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- ref_ptr = btrfs_item_ptr_offset(eb, slot);
|
|
|
- ref_end = ref_ptr + btrfs_item_size_nr(eb, slot);
|
|
|
-
|
|
|
-again:
|
|
|
- ref = (struct btrfs_inode_ref *)ref_ptr;
|
|
|
-
|
|
|
- namelen = btrfs_inode_ref_name_len(eb, ref);
|
|
|
- name = kmalloc(namelen, GFP_NOFS);
|
|
|
- BUG_ON(!name);
|
|
|
-
|
|
|
- read_extent_buffer(eb, name, (unsigned long)(ref + 1), namelen);
|
|
|
-
|
|
|
- /* if we already have a perfect match, we're done */
|
|
|
- if (inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
|
|
|
- btrfs_inode_ref_index(eb, ref),
|
|
|
- name, namelen)) {
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * look for a conflicting back reference in the metadata.
|
|
|
- * if we find one we have to unlink that name of the file
|
|
|
- * before we add our new link. Later on, we overwrite any
|
|
|
- * existing back reference, and we don't want to create
|
|
|
- * dangling pointers in the directory.
|
|
|
- */
|
|
|
-
|
|
|
- if (search_done)
|
|
|
- goto insert;
|
|
|
+ struct btrfs_dir_item *di;
|
|
|
|
|
|
ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
|
|
|
if (ret == 0) {
|
|
@@ -869,7 +811,7 @@ again:
|
|
|
* if so, just jump out, we're done
|
|
|
*/
|
|
|
if (key->objectid == key->offset)
|
|
|
- goto out_nowrite;
|
|
|
+ return 1;
|
|
|
|
|
|
/* check all the names in this back reference to see
|
|
|
* if they are in the log. if so, we allow them to stay
|
|
@@ -888,7 +830,7 @@ again:
|
|
|
(unsigned long)(victim_ref + 1),
|
|
|
victim_name_len);
|
|
|
|
|
|
- if (!backref_in_log(log, key, victim_name,
|
|
|
+ if (!backref_in_log(log_root, key, victim_name,
|
|
|
victim_name_len)) {
|
|
|
btrfs_inc_nlink(inode);
|
|
|
btrfs_release_path(path);
|
|
@@ -907,7 +849,7 @@ again:
|
|
|
* NOTE: we have searched root tree and checked the
|
|
|
* coresponding ref, it does not need to check again.
|
|
|
*/
|
|
|
- search_done = 1;
|
|
|
+ *search_done = 1;
|
|
|
}
|
|
|
btrfs_release_path(path);
|
|
|
|
|
@@ -930,25 +872,99 @@ again:
|
|
|
}
|
|
|
btrfs_release_path(path);
|
|
|
|
|
|
-insert:
|
|
|
- /* insert our name */
|
|
|
- ret = btrfs_add_link(trans, dir, inode, name, namelen, 0,
|
|
|
- btrfs_inode_ref_index(eb, ref));
|
|
|
- BUG_ON(ret);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * replay one inode back reference item found in the log tree.
|
|
|
+ * eb, slot and key refer to the buffer and key found in the log tree.
|
|
|
+ * root is the destination we are replaying into, and path is for temp
|
|
|
+ * use by this function. (it should be released on return).
|
|
|
+ */
|
|
|
+static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
|
|
|
+ struct btrfs_root *root,
|
|
|
+ struct btrfs_root *log,
|
|
|
+ struct btrfs_path *path,
|
|
|
+ struct extent_buffer *eb, int slot,
|
|
|
+ struct btrfs_key *key)
|
|
|
+{
|
|
|
+ struct btrfs_inode_ref *ref;
|
|
|
+ struct inode *dir;
|
|
|
+ struct inode *inode;
|
|
|
+ unsigned long ref_ptr;
|
|
|
+ unsigned long ref_end;
|
|
|
+ char *name;
|
|
|
+ int namelen;
|
|
|
+ int ret;
|
|
|
+ int search_done = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * it is possible that we didn't log all the parent directories
|
|
|
+ * for a given inode. If we don't find the dir, just don't
|
|
|
+ * copy the back ref in. The link count fixup code will take
|
|
|
+ * care of the rest
|
|
|
+ */
|
|
|
+ dir = read_one_inode(root, key->offset);
|
|
|
+ if (!dir)
|
|
|
+ return -ENOENT;
|
|
|
|
|
|
- btrfs_update_inode(trans, root, inode);
|
|
|
+ inode = read_one_inode(root, key->objectid);
|
|
|
+ if (!inode) {
|
|
|
+ iput(dir);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
|
|
|
-out:
|
|
|
- ref_ptr = (unsigned long)(ref + 1) + namelen;
|
|
|
- kfree(name);
|
|
|
- if (ref_ptr < ref_end)
|
|
|
- goto again;
|
|
|
+ ref_ptr = btrfs_item_ptr_offset(eb, slot);
|
|
|
+ ref_end = ref_ptr + btrfs_item_size_nr(eb, slot);
|
|
|
+
|
|
|
+ while (ref_ptr < ref_end) {
|
|
|
+ ref = (struct btrfs_inode_ref *)ref_ptr;
|
|
|
+
|
|
|
+ namelen = btrfs_inode_ref_name_len(eb, ref);
|
|
|
+ name = kmalloc(namelen, GFP_NOFS);
|
|
|
+ BUG_ON(!name);
|
|
|
+
|
|
|
+ read_extent_buffer(eb, name, (unsigned long)(ref + 1), namelen);
|
|
|
+
|
|
|
+ /* if we already have a perfect match, we're done */
|
|
|
+ if (!inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
|
|
|
+ btrfs_inode_ref_index(eb, ref),
|
|
|
+ name, namelen)) {
|
|
|
+ /*
|
|
|
+ * look for a conflicting back reference in the
|
|
|
+ * metadata. if we find one we have to unlink that name
|
|
|
+ * of the file before we add our new link. Later on, we
|
|
|
+ * overwrite any existing back reference, and we don't
|
|
|
+ * want to create dangling pointers in the directory.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!search_done) {
|
|
|
+ ret = __add_inode_ref(trans, root, path, log,
|
|
|
+ dir, inode, key, eb, ref,
|
|
|
+ name, namelen,
|
|
|
+ &search_done);
|
|
|
+ if (ret == 1)
|
|
|
+ goto out;
|
|
|
+ BUG_ON(ret);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* insert our name */
|
|
|
+ ret = btrfs_add_link(trans, dir, inode, name, namelen,
|
|
|
+ 0, btrfs_inode_ref_index(eb, ref));
|
|
|
+ BUG_ON(ret);
|
|
|
+
|
|
|
+ btrfs_update_inode(trans, root, inode);
|
|
|
+ }
|
|
|
+
|
|
|
+ ref_ptr = (unsigned long)(ref + 1) + namelen;
|
|
|
+ kfree(name);
|
|
|
+ }
|
|
|
|
|
|
/* finally write the back reference in the inode */
|
|
|
ret = overwrite_item(trans, root, path, eb, slot, key);
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
-out_nowrite:
|
|
|
+out:
|
|
|
btrfs_release_path(path);
|
|
|
iput(dir);
|
|
|
iput(inode);
|