| From 900c9981680067573671ecc5cbfa7c5770be3a40 Mon Sep 17 00:00:00 2001 |
| From: Liu Bo <bo.li.liu@oracle.com> |
| Date: Thu, 25 Jan 2018 11:02:56 -0700 |
| Subject: Btrfs: fix unexpected -EEXIST when creating new inode |
| |
| From: Liu Bo <bo.li.liu@oracle.com> |
| |
| commit 900c9981680067573671ecc5cbfa7c5770be3a40 upstream. |
| |
| The highest objectid, which is assigned to new inode, is decided at |
| the time of initializing fs roots. However, in cases where log replay |
| gets processed, the btree which fs root owns might be changed, so we |
| have to search it again for the highest objectid, otherwise creating |
| new inode would end up with -EEXIST. |
| |
| cc: <stable@vger.kernel.org> v4.4-rc6+ |
| Fixes: f32e48e92596 ("Btrfs: Initialize btrfs_root->highest_objectid when loading tree root and subvolume roots") |
| Signed-off-by: Liu Bo <bo.li.liu@oracle.com> |
| Reviewed-by: Josef Bacik <jbacik@fb.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/tree-log.c | 18 ++++++++++++++++++ |
| 1 file changed, 18 insertions(+) |
| |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -26,6 +26,7 @@ |
| #include "print-tree.h" |
| #include "backref.h" |
| #include "hash.h" |
| +#include "inode-map.h" |
| |
| /* magic values for the inode_only field in btrfs_log_inode: |
| * |
| @@ -5523,6 +5524,23 @@ again: |
| path); |
| } |
| |
| + if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) { |
| + struct btrfs_root *root = wc.replay_dest; |
| + |
| + btrfs_release_path(path); |
| + |
| + /* |
| + * We have just replayed everything, and the highest |
| + * objectid of fs roots probably has changed in case |
| + * some inode_item's got replayed. |
| + * |
| + * root->objectid_mutex is not acquired as log replay |
| + * could only happen during mount. |
| + */ |
| + ret = btrfs_find_highest_objectid(root, |
| + &root->highest_objectid); |
| + } |
| + |
| key.offset = found_key.offset - 1; |
| wc.replay_dest->log_root = NULL; |
| free_extent_buffer(log->node); |