+++ /dev/null
-From 0d836392cadd5535f4184d46d901a82eb276ed62 Mon Sep 17 00:00:00 2001
-From: Filipe Manana <fdmanana@suse.com>
-Date: Fri, 20 Jul 2018 10:59:06 +0100
-Subject: Btrfs: fix mount failure after fsync due to hard link recreation
-
-From: Filipe Manana <fdmanana@suse.com>
-
-commit 0d836392cadd5535f4184d46d901a82eb276ed62 upstream.
-
-If we end up with logging an inode reference item which has the same name
-but different index from the one we have persisted, we end up failing when
-replaying the log with an errno value of -EEXIST. The error comes from
-btrfs_add_link(), which is called from add_inode_ref(), when we are
-replaying an inode reference item.
-
-Example scenario where this happens:
-
- $ mkfs.btrfs -f /dev/sdb
- $ mount /dev/sdb /mnt
-
- $ touch /mnt/foo
- $ ln /mnt/foo /mnt/bar
-
- $ sync
-
- # Rename the first hard link (foo) to a new name and rename the second
- # hard link (bar) to the old name of the first hard link (foo).
- $ mv /mnt/foo /mnt/qwerty
- $ mv /mnt/bar /mnt/foo
-
- # Create a new file, in the same parent directory, with the old name of
- # the second hard link (bar) and fsync this new file.
- # We do this instead of calling fsync on foo/qwerty because if we did
- # that the fsync resulted in a full transaction commit, not triggering
- # the problem.
- $ touch /mnt/bar
- $ xfs_io -c "fsync" /mnt/bar
-
- <power fail>
-
- $ mount /dev/sdb /mnt
- mount: mount /dev/sdb on /mnt failed: File exists
-
-So fix this by checking if a conflicting inode reference exists (same
-name, same parent but different index), removing it (and the associated
-dir index entries from the parent inode) if it exists, before attempting
-to add the new reference.
-
-A test case for fstests follows soon.
-
-CC: stable@vger.kernel.org # 4.4+
-Signed-off-by: Filipe Manana <fdmanana@suse.com>
-Signed-off-by: David Sterba <dsterba@suse.com>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-
----
- fs/btrfs/tree-log.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 66 insertions(+)
-
---- a/fs/btrfs/tree-log.c
-+++ b/fs/btrfs/tree-log.c
-@@ -1188,6 +1188,46 @@ static int ref_get_fields(struct extent_
- return 0;
- }
-
-+static int btrfs_inode_ref_exists(struct inode *inode, struct inode *dir,
-+ const u8 ref_type, const char *name,
-+ const int namelen)
-+{
-+ struct btrfs_key key;
-+ struct btrfs_path *path;
-+ const u64 parent_id = btrfs_ino(BTRFS_I(dir));
-+ int ret;
-+
-+ path = btrfs_alloc_path();
-+ if (!path)
-+ return -ENOMEM;
-+
-+ key.objectid = btrfs_ino(BTRFS_I(inode));
-+ key.type = ref_type;
-+ if (key.type == BTRFS_INODE_REF_KEY)
-+ key.offset = parent_id;
-+ else
-+ key.offset = btrfs_extref_hash(parent_id, name, namelen);
-+
-+ ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
-+ if (ret < 0)
-+ goto out;
-+ if (ret > 0) {
-+ ret = 0;
-+ goto out;
-+ }
-+ if (key.type == BTRFS_INODE_EXTREF_KEY)
-+ ret = btrfs_find_name_in_ext_backref(path->nodes[0],
-+ path->slots[0], parent_id,
-+ name, namelen, NULL);
-+ else
-+ ret = btrfs_find_name_in_backref(path->nodes[0], path->slots[0],
-+ name, namelen, NULL);
-+
-+out:
-+ btrfs_free_path(path);
-+ return ret;
-+}
-+
- /*
- * 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.
-@@ -1295,6 +1335,32 @@ static noinline int add_inode_ref(struct
- }
- }
-
-+ /*
-+ * If a reference item already exists for this inode
-+ * with the same parent and name, but different index,
-+ * drop it and the corresponding directory index entries
-+ * from the parent before adding the new reference item
-+ * and dir index entries, otherwise we would fail with
-+ * -EEXIST returned from btrfs_add_link() below.
-+ */
-+ ret = btrfs_inode_ref_exists(inode, dir, key->type,
-+ name, namelen);
-+ if (ret > 0) {
-+ ret = btrfs_unlink_inode(trans, root,
-+ BTRFS_I(dir),
-+ BTRFS_I(inode),
-+ name, namelen);
-+ /*
-+ * If we dropped the link count to 0, bump it so
-+ * that later the iput() on the inode will not
-+ * free it. We will fixup the link count later.
-+ */
-+ if (!ret && inode->i_nlink == 0)
-+ inc_nlink(inode);
-+ }
-+ if (ret < 0)
-+ goto out;
-+
- /* insert our name */
- ret = btrfs_add_link(trans, dir, inode, name, namelen,
- 0, ref_index);
smb3-don-t-request-leases-in-symlink-creation-and-query.patch
smb3-fill-in-statfs-fsid-and-correct-namelen.patch
btrfs-don-t-leak-ret-from-do_chunk_alloc.patch
-btrfs-fix-mount-failure-after-fsync-due-to-hard-link-recreation.patch
+++ /dev/null
-From 0d836392cadd5535f4184d46d901a82eb276ed62 Mon Sep 17 00:00:00 2001
-From: Filipe Manana <fdmanana@suse.com>
-Date: Fri, 20 Jul 2018 10:59:06 +0100
-Subject: Btrfs: fix mount failure after fsync due to hard link recreation
-
-From: Filipe Manana <fdmanana@suse.com>
-
-commit 0d836392cadd5535f4184d46d901a82eb276ed62 upstream.
-
-If we end up with logging an inode reference item which has the same name
-but different index from the one we have persisted, we end up failing when
-replaying the log with an errno value of -EEXIST. The error comes from
-btrfs_add_link(), which is called from add_inode_ref(), when we are
-replaying an inode reference item.
-
-Example scenario where this happens:
-
- $ mkfs.btrfs -f /dev/sdb
- $ mount /dev/sdb /mnt
-
- $ touch /mnt/foo
- $ ln /mnt/foo /mnt/bar
-
- $ sync
-
- # Rename the first hard link (foo) to a new name and rename the second
- # hard link (bar) to the old name of the first hard link (foo).
- $ mv /mnt/foo /mnt/qwerty
- $ mv /mnt/bar /mnt/foo
-
- # Create a new file, in the same parent directory, with the old name of
- # the second hard link (bar) and fsync this new file.
- # We do this instead of calling fsync on foo/qwerty because if we did
- # that the fsync resulted in a full transaction commit, not triggering
- # the problem.
- $ touch /mnt/bar
- $ xfs_io -c "fsync" /mnt/bar
-
- <power fail>
-
- $ mount /dev/sdb /mnt
- mount: mount /dev/sdb on /mnt failed: File exists
-
-So fix this by checking if a conflicting inode reference exists (same
-name, same parent but different index), removing it (and the associated
-dir index entries from the parent inode) if it exists, before attempting
-to add the new reference.
-
-A test case for fstests follows soon.
-
-CC: stable@vger.kernel.org # 4.4+
-Signed-off-by: Filipe Manana <fdmanana@suse.com>
-Signed-off-by: David Sterba <dsterba@suse.com>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-
----
- fs/btrfs/tree-log.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 66 insertions(+)
-
---- a/fs/btrfs/tree-log.c
-+++ b/fs/btrfs/tree-log.c
-@@ -1206,6 +1206,46 @@ static int ref_get_fields(struct extent_
- return 0;
- }
-
-+static int btrfs_inode_ref_exists(struct inode *inode, struct inode *dir,
-+ const u8 ref_type, const char *name,
-+ const int namelen)
-+{
-+ struct btrfs_key key;
-+ struct btrfs_path *path;
-+ const u64 parent_id = btrfs_ino(BTRFS_I(dir));
-+ int ret;
-+
-+ path = btrfs_alloc_path();
-+ if (!path)
-+ return -ENOMEM;
-+
-+ key.objectid = btrfs_ino(BTRFS_I(inode));
-+ key.type = ref_type;
-+ if (key.type == BTRFS_INODE_REF_KEY)
-+ key.offset = parent_id;
-+ else
-+ key.offset = btrfs_extref_hash(parent_id, name, namelen);
-+
-+ ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
-+ if (ret < 0)
-+ goto out;
-+ if (ret > 0) {
-+ ret = 0;
-+ goto out;
-+ }
-+ if (key.type == BTRFS_INODE_EXTREF_KEY)
-+ ret = btrfs_find_name_in_ext_backref(path->nodes[0],
-+ path->slots[0], parent_id,
-+ name, namelen, NULL);
-+ else
-+ ret = btrfs_find_name_in_backref(path->nodes[0], path->slots[0],
-+ name, namelen, NULL);
-+
-+out:
-+ btrfs_free_path(path);
-+ return ret;
-+}
-+
- /*
- * 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.
-@@ -1313,6 +1353,32 @@ static noinline int add_inode_ref(struct
- }
- }
-
-+ /*
-+ * If a reference item already exists for this inode
-+ * with the same parent and name, but different index,
-+ * drop it and the corresponding directory index entries
-+ * from the parent before adding the new reference item
-+ * and dir index entries, otherwise we would fail with
-+ * -EEXIST returned from btrfs_add_link() below.
-+ */
-+ ret = btrfs_inode_ref_exists(inode, dir, key->type,
-+ name, namelen);
-+ if (ret > 0) {
-+ ret = btrfs_unlink_inode(trans, root,
-+ BTRFS_I(dir),
-+ BTRFS_I(inode),
-+ name, namelen);
-+ /*
-+ * If we dropped the link count to 0, bump it so
-+ * that later the iput() on the inode will not
-+ * free it. We will fixup the link count later.
-+ */
-+ if (!ret && inode->i_nlink == 0)
-+ inc_nlink(inode);
-+ }
-+ if (ret < 0)
-+ goto out;
-+
- /* insert our name */
- ret = btrfs_add_link(trans, dir, inode, name, namelen,
- 0, ref_index);
smb3-don-t-request-leases-in-symlink-creation-and-query.patch
smb3-fill-in-statfs-fsid-and-correct-namelen.patch
btrfs-don-t-leak-ret-from-do_chunk_alloc.patch
-btrfs-fix-mount-failure-after-fsync-due-to-hard-link-recreation.patch