summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2019-02-13 07:14:09 -0500
committerDavid Sterba <dsterba@suse.com>2019-02-25 08:13:40 -0500
commita3baaf0d786e22fc86295fda9c58ba0dee07599f (patch)
tree77c19a5196b5abbf79050ed5ad74567d48d7f8b5 /fs/btrfs/tree-log.c
parent6b5fc433a7ad6711052d1aa4be0debc6316b219f (diff)
Btrfs: fix fsync after succession of renames and unlink/rmdir
After a succession of renames operations of different files and unlinking one of them, if we fsync one of the renamed files we can end up with a log that will either fail to replay at mount time or result in a filesystem that is in an inconsistent state. One example scenario: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ mkdir /mnt/testdir $ touch /mnt/testdir/fname1 $ touch /mnt/testdir/fname2 $ sync $ mv /mnt/testdir/fname1 /mnt/testdir/fname3 $ rm -f /mnt/testdir/fname2 $ ln /mnt/testdir/fname3 /mnt/testdir/fname2 $ touch /mnt/testdir/fname1 $ xfs_io -c "fsync" /mnt/testdir/fname1 <power failure> $ mount /dev/sdb /mnt $ umount /mnt $ btrfs check /dev/sdb [1/7] checking root items [2/7] checking extents [3/7] checking free space cache [4/7] checking fs roots root 5 inode 259 errors 2, no orphan item ERROR: errors found in fs roots Opening filesystem to check... Checking filesystem on /dev/sdc UUID: 20e4abb8-5a19-4492-8bb4-6084125c2d0d found 393216 bytes used, error(s) found total csum bytes: 0 total tree bytes: 131072 total fs tree bytes: 32768 total extent tree bytes: 16384 btree space waste bytes: 122986 file data blocks allocated: 262144 referenced 262144 On a kernel without the first patch in this series, titled "[PATCH] Btrfs: fix fsync after succession of renames of different files", we get instead an error when mounting the filesystem due to failure of replaying the log: $ mount /dev/sdb /mnt mount: mount /dev/sdb on /mnt failed: File exists Fix this by logging the parent directory of an inode whenever we find an inode that no longer exists (was unlinked in the current transaction), during the procedure which finds inodes that have old names that collide with new names of other inodes. A test case for fstests follows soon. Reviewed-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c49
1 files changed, 37 insertions, 12 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 70d41f669025..e76345f52beb 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -27,6 +27,7 @@
27#define LOG_INODE_ALL 0 27#define LOG_INODE_ALL 0
28#define LOG_INODE_EXISTS 1 28#define LOG_INODE_EXISTS 1
29#define LOG_OTHER_INODE 2 29#define LOG_OTHER_INODE 2
30#define LOG_OTHER_INODE_ALL 3
30 31
31/* 32/*
32 * directory trouble cases 33 * directory trouble cases
@@ -4777,7 +4778,7 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
4777 const int slot, 4778 const int slot,
4778 const struct btrfs_key *key, 4779 const struct btrfs_key *key,
4779 struct btrfs_inode *inode, 4780 struct btrfs_inode *inode,
4780 u64 *other_ino) 4781 u64 *other_ino, u64 *other_parent)
4781{ 4782{
4782 int ret; 4783 int ret;
4783 struct btrfs_path *search_path; 4784 struct btrfs_path *search_path;
@@ -4843,6 +4844,7 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
4843 if (di_key.objectid != key->objectid) { 4844 if (di_key.objectid != key->objectid) {
4844 ret = 1; 4845 ret = 1;
4845 *other_ino = di_key.objectid; 4846 *other_ino = di_key.objectid;
4847 *other_parent = parent;
4846 } else { 4848 } else {
4847 ret = 0; 4849 ret = 0;
4848 } 4850 }
@@ -4867,6 +4869,7 @@ out:
4867 4869
4868struct btrfs_ino_list { 4870struct btrfs_ino_list {
4869 u64 ino; 4871 u64 ino;
4872 u64 parent;
4870 struct list_head list; 4873 struct list_head list;
4871}; 4874};
4872 4875
@@ -4874,7 +4877,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4874 struct btrfs_root *root, 4877 struct btrfs_root *root,
4875 struct btrfs_path *path, 4878 struct btrfs_path *path,
4876 struct btrfs_log_ctx *ctx, 4879 struct btrfs_log_ctx *ctx,
4877 u64 ino) 4880 u64 ino, u64 parent)
4878{ 4881{
4879 struct btrfs_ino_list *ino_elem; 4882 struct btrfs_ino_list *ino_elem;
4880 LIST_HEAD(inode_list); 4883 LIST_HEAD(inode_list);
@@ -4884,6 +4887,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4884 if (!ino_elem) 4887 if (!ino_elem)
4885 return -ENOMEM; 4888 return -ENOMEM;
4886 ino_elem->ino = ino; 4889 ino_elem->ino = ino;
4890 ino_elem->parent = parent;
4887 list_add_tail(&ino_elem->list, &inode_list); 4891 list_add_tail(&ino_elem->list, &inode_list);
4888 4892
4889 while (!list_empty(&inode_list)) { 4893 while (!list_empty(&inode_list)) {
@@ -4894,6 +4898,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4894 ino_elem = list_first_entry(&inode_list, struct btrfs_ino_list, 4898 ino_elem = list_first_entry(&inode_list, struct btrfs_ino_list,
4895 list); 4899 list);
4896 ino = ino_elem->ino; 4900 ino = ino_elem->ino;
4901 parent = ino_elem->parent;
4897 list_del(&ino_elem->list); 4902 list_del(&ino_elem->list);
4898 kfree(ino_elem); 4903 kfree(ino_elem);
4899 if (ret) 4904 if (ret)
@@ -4907,13 +4912,25 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4907 inode = btrfs_iget(fs_info->sb, &key, root, NULL); 4912 inode = btrfs_iget(fs_info->sb, &key, root, NULL);
4908 /* 4913 /*
4909 * If the other inode that had a conflicting dir entry was 4914 * If the other inode that had a conflicting dir entry was
4910 * deleted in the current transaction, we don't need to do more 4915 * deleted in the current transaction, we need to log its parent
4911 * work nor fallback to a transaction commit. 4916 * directory.
4912 */ 4917 */
4913 if (IS_ERR(inode)) { 4918 if (IS_ERR(inode)) {
4914 ret = PTR_ERR(inode); 4919 ret = PTR_ERR(inode);
4915 if (ret == -ENOENT) 4920 if (ret == -ENOENT) {
4916 ret = 0; 4921 key.objectid = parent;
4922 inode = btrfs_iget(fs_info->sb, &key, root,
4923 NULL);
4924 if (IS_ERR(inode)) {
4925 ret = PTR_ERR(inode);
4926 } else {
4927 ret = btrfs_log_inode(trans, root,
4928 BTRFS_I(inode),
4929 LOG_OTHER_INODE_ALL,
4930 0, LLONG_MAX, ctx);
4931 iput(inode);
4932 }
4933 }
4917 continue; 4934 continue;
4918 } 4935 }
4919 /* 4936 /*
@@ -4943,6 +4960,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4943 struct extent_buffer *leaf = path->nodes[0]; 4960 struct extent_buffer *leaf = path->nodes[0];
4944 int slot = path->slots[0]; 4961 int slot = path->slots[0];
4945 u64 other_ino = 0; 4962 u64 other_ino = 0;
4963 u64 other_parent = 0;
4946 4964
4947 if (slot >= btrfs_header_nritems(leaf)) { 4965 if (slot >= btrfs_header_nritems(leaf)) {
4948 ret = btrfs_next_leaf(root, path); 4966 ret = btrfs_next_leaf(root, path);
@@ -4964,7 +4982,8 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4964 } 4982 }
4965 4983
4966 ret = btrfs_check_ref_name_override(leaf, slot, &key, 4984 ret = btrfs_check_ref_name_override(leaf, slot, &key,
4967 BTRFS_I(inode), &other_ino); 4985 BTRFS_I(inode), &other_ino,
4986 &other_parent);
4968 if (ret < 0) 4987 if (ret < 0)
4969 break; 4988 break;
4970 if (ret > 0) { 4989 if (ret > 0) {
@@ -4974,6 +4993,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
4974 break; 4993 break;
4975 } 4994 }
4976 ino_elem->ino = other_ino; 4995 ino_elem->ino = other_ino;
4996 ino_elem->parent = other_parent;
4977 list_add_tail(&ino_elem->list, &inode_list); 4997 list_add_tail(&ino_elem->list, &inode_list);
4978 ret = 0; 4998 ret = 0;
4979 } 4999 }
@@ -5024,7 +5044,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
5024 u64 logged_isize = 0; 5044 u64 logged_isize = 0;
5025 bool need_log_inode_item = true; 5045 bool need_log_inode_item = true;
5026 bool xattrs_logged = false; 5046 bool xattrs_logged = false;
5027 bool recursive_logging = (inode_only == LOG_OTHER_INODE); 5047 bool recursive_logging = false;
5028 5048
5029 path = btrfs_alloc_path(); 5049 path = btrfs_alloc_path();
5030 if (!path) 5050 if (!path)
@@ -5070,8 +5090,12 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
5070 return ret; 5090 return ret;
5071 } 5091 }
5072 5092
5073 if (inode_only == LOG_OTHER_INODE) { 5093 if (inode_only == LOG_OTHER_INODE || inode_only == LOG_OTHER_INODE_ALL) {
5074 inode_only = LOG_INODE_EXISTS; 5094 recursive_logging = true;
5095 if (inode_only == LOG_OTHER_INODE)
5096 inode_only = LOG_INODE_EXISTS;
5097 else
5098 inode_only = LOG_INODE_ALL;
5075 mutex_lock_nested(&inode->log_mutex, SINGLE_DEPTH_NESTING); 5099 mutex_lock_nested(&inode->log_mutex, SINGLE_DEPTH_NESTING);
5076 } else { 5100 } else {
5077 mutex_lock(&inode->log_mutex); 5101 mutex_lock(&inode->log_mutex);
@@ -5169,10 +5193,11 @@ again:
5169 inode->generation == trans->transid && 5193 inode->generation == trans->transid &&
5170 !recursive_logging) { 5194 !recursive_logging) {
5171 u64 other_ino = 0; 5195 u64 other_ino = 0;
5196 u64 other_parent = 0;
5172 5197
5173 ret = btrfs_check_ref_name_override(path->nodes[0], 5198 ret = btrfs_check_ref_name_override(path->nodes[0],
5174 path->slots[0], &min_key, inode, 5199 path->slots[0], &min_key, inode,
5175 &other_ino); 5200 &other_ino, &other_parent);
5176 if (ret < 0) { 5201 if (ret < 0) {
5177 err = ret; 5202 err = ret;
5178 goto out_unlock; 5203 goto out_unlock;
@@ -5195,7 +5220,7 @@ again:
5195 ins_nr = 0; 5220 ins_nr = 0;
5196 5221
5197 err = log_conflicting_inodes(trans, root, path, 5222 err = log_conflicting_inodes(trans, root, path,
5198 ctx, other_ino); 5223 ctx, other_ino, other_parent);
5199 if (err) 5224 if (err)
5200 goto out_unlock; 5225 goto out_unlock;
5201 btrfs_release_path(path); 5226 btrfs_release_path(path);