From 9a7300d973a192193857c595997f292a14c14197 Mon Sep 17 00:00:00 2001 From: Davide Italiano Date: Thu, 2 Apr 2015 05:56:00 -0400 Subject: [PATCH 1/5] btrfs: RENAME_EXCHANGE semantic for renameat2() --- fs/btrfs/inode.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 611b66d73e80ba..84426c92a87771 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9110,6 +9110,190 @@ static int btrfs_getattr(struct vfsmount *mnt, return 0; } +static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root = BTRFS_I(old_dir)->root; + struct btrfs_root *dest = BTRFS_I(new_dir)->root; + struct inode *new_inode = new_dentry->d_inode; + struct inode *old_inode = old_dentry->d_inode; + struct timespec ctime = CURRENT_TIME; + u64 old_ino = btrfs_ino(old_inode); + u64 new_ino = btrfs_ino(new_inode); + u64 old_idx = 0; + u64 new_idx = 0; + u64 root_objectid; + int ret; + + /* we only allow rename subvolume link between subvolumes */ + if (old_ino != BTRFS_FIRST_FREE_OBJECTID && root != dest) + return -EXDEV; + + /* close the racy window with snapshot create/destroy ioctl */ + if (old_ino == BTRFS_FIRST_FREE_OBJECTID) + down_read(&root->fs_info->subvol_sem); + if (new_ino == BTRFS_FIRST_FREE_OBJECTID) + down_read(&dest->fs_info->subvol_sem); + + /* + * We want to reserve the absolute worst case amount of items. So if + * both inodes are subvols and we need to unlink them then that would + * require 4 item modifications, but if they are both normal inodes it + * would require 5 item modifications, so we'll assume their normal + * inodes. So 5 * 2 is 10, plus 2 for the new links, so 12 total items + * should cover the worst case number of items we'll modify. + */ + trans = btrfs_start_transaction(root, 12); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out_notrans; + } + + /* + * We need to find a free sequence number both in the source and + * in the destination directory for the exchange. + */ + ret = btrfs_set_inode_index(new_dir, &old_idx); + if (ret) + goto out_fail; + ret = btrfs_set_inode_index(old_dir, &new_idx); + if (ret) + goto out_fail; + + BTRFS_I(old_inode)->dir_index = 0ULL; + BTRFS_I(new_inode)->dir_index = 0ULL; + + /* Reference for the source. */ + if (unlikely(old_ino == BTRFS_FIRST_FREE_OBJECTID)) { + /* force full log commit if subvolume involved. */ + btrfs_set_log_full_commit(root->fs_info, trans); + } else { + ret = btrfs_insert_inode_ref(trans, dest, + new_dentry->d_name.name, + new_dentry->d_name.len, + old_ino, + btrfs_ino(new_dir), old_idx); + if (ret) + goto out_fail; + btrfs_pin_log_trans(root); + } + + /* And now for the dest. */ + if (unlikely(new_ino == BTRFS_FIRST_FREE_OBJECTID)) { + /* force full log commit if subvolume involved. */ + btrfs_set_log_full_commit(dest->fs_info, trans); + } else { + ret = btrfs_insert_inode_ref(trans, root, + old_dentry->d_name.name, + old_dentry->d_name.len, + new_ino, + btrfs_ino(old_dir), new_idx); + if (ret) + goto out_fail; + btrfs_pin_log_trans(dest); + } + + /* + * Update i-node version and ctime/mtime. + */ + inode_inc_iversion(old_dir); + inode_inc_iversion(new_dir); + inode_inc_iversion(old_inode); + inode_inc_iversion(new_inode); + old_dir->i_ctime = old_dir->i_mtime = ctime; + new_dir->i_ctime = new_dir->i_mtime = ctime; + old_inode->i_ctime = ctime; + new_inode->i_ctime = ctime; + + if (old_dentry->d_parent != new_dentry->d_parent) { + btrfs_record_unlink_dir(trans, old_dir, old_inode, 1); + btrfs_record_unlink_dir(trans, new_dir, new_inode, 1); + } + + /* src is a subvolume */ + if (unlikely(old_ino == BTRFS_FIRST_FREE_OBJECTID)) { + root_objectid = BTRFS_I(old_inode)->root->root_key.objectid; + ret = btrfs_unlink_subvol(trans, root, old_dir, + root_objectid, + old_dentry->d_name.name, + old_dentry->d_name.len); + } else { /* src is a inode */ + ret = __btrfs_unlink_inode(trans, root, old_dir, + old_dentry->d_inode, + old_dentry->d_name.name, + old_dentry->d_name.len); + if (!ret) + ret = btrfs_update_inode(trans, root, old_inode); + } + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + /* dest is a subvolume */ + if (unlikely(new_ino == BTRFS_FIRST_FREE_OBJECTID)) { + root_objectid = BTRFS_I(new_inode)->root->root_key.objectid; + ret = btrfs_unlink_subvol(trans, dest, new_dir, + root_objectid, + new_dentry->d_name.name, + new_dentry->d_name.len); + } else { /* dest is an inode */ + ret = __btrfs_unlink_inode(trans, dest, new_dir, + new_dentry->d_inode, + new_dentry->d_name.name, + new_dentry->d_name.len); + if (!ret) + ret = btrfs_update_inode(trans, dest, new_inode); + } + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + ret = btrfs_add_link(trans, new_dir, old_inode, + new_dentry->d_name.name, + new_dentry->d_name.len, 0, old_idx); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + ret = btrfs_add_link(trans, old_dir, new_inode, + old_dentry->d_name.name, + old_dentry->d_name.len, 0, new_idx); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + if (old_inode->i_nlink == 1) + BTRFS_I(old_inode)->dir_index = old_idx; + if (new_inode->i_nlink == 1) + BTRFS_I(new_inode)->dir_index = new_idx; + + if (old_ino != BTRFS_FIRST_FREE_OBJECTID) { + struct dentry *parent = new_dentry->d_parent; + btrfs_log_new_name(trans, old_inode, old_dir, parent); + btrfs_end_log_trans(root); + } + if (new_ino != BTRFS_FIRST_FREE_OBJECTID) { + struct dentry *parent = old_dentry->d_parent; + btrfs_log_new_name(trans, new_inode, new_dir, parent); + btrfs_end_log_trans(dest); + } + +out_fail: + btrfs_end_transaction(trans, root); +out_notrans: + /* Racy window with snapshot create/destroy ioctl */ + if (new_ino == BTRFS_FIRST_FREE_OBJECTID) + up_read(&dest->fs_info->subvol_sem); + if (old_ino == BTRFS_FIRST_FREE_OBJECTID) + up_read(&root->fs_info->subvol_sem); + return 0; +} + static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { @@ -9294,8 +9478,12 @@ static int btrfs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - if (flags & ~RENAME_NOREPLACE) + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) return -EINVAL; + + if (flags & RENAME_EXCHANGE) + return btrfs_rename_exchange(old_dir, old_dentry, new_dir, + new_dentry); return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry); } From a6c3c98c8f144f5f326431ecc3650f20e1c34377 Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Wed, 20 Jan 2016 12:23:00 -0500 Subject: [PATCH 2/5] btrfs: add support for RENAME_WHITEOUT semantic --- fs/btrfs/inode.c | 58 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 84426c92a87771..657e61257c83b0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9295,16 +9295,20 @@ static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentr } static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct btrfs_trans_handle *trans; struct btrfs_root *root = BTRFS_I(old_dir)->root; struct btrfs_root *dest = BTRFS_I(new_dir)->root; struct inode *new_inode = d_inode(new_dentry); struct inode *old_inode = d_inode(old_dentry); + struct inode *whiteout_inode = NULL; struct timespec ctime = CURRENT_TIME; u64 index = 0; + u64 whiteout_index; u64 root_objectid; + u64 whiteout_objectid; int ret; u64 old_ino = btrfs_ino(old_inode); @@ -9357,7 +9361,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, * We want to reserve the absolute worst case amount of items. So if * both inodes are subvols and we need to unlink them then that would * require 4 item modifications, but if they are both normal inodes it - * would require 5 item modifications, so we'll assume their normal + * would require 5 item modifications, so we'll assume they are normal * inodes. So 5 * 2 is 10, plus 1 for the new link, so 11 total items * should cover the worst case number of items we'll modify. */ @@ -9424,6 +9428,43 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_fail; } + /* the old inode is freed, create whiteout in its place */ + if (flags & RENAME_WHITEOUT) { + ret = btrfs_find_free_ino(root, &whiteout_objectid); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + whiteout_inode = btrfs_new_inode(trans, root, old_dir, + old_dentry->d_name.name, old_dentry->d_name.len, + btrfs_ino(old_dir), whiteout_objectid, + S_IFCHR | S_IRWXUGO, &whiteout_index); + + if (IS_ERR(whiteout_inode)) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + whiteout_inode->i_op = &btrfs_special_inode_operations; + init_special_inode(whiteout_inode, whiteout_inode->i_mode, + MKDEV(0, 0)); + + ret = btrfs_init_inode_security(trans, whiteout_inode, old_dir, + &old_dentry->d_name); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + ret = btrfs_add_nondir(trans, old_dir, old_dentry, whiteout_inode, + 0, whiteout_index); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + } + if (new_inode) { inode_inc_iversion(new_inode); new_inode->i_ctime = CURRENT_TIME; @@ -9465,6 +9506,13 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, btrfs_log_new_name(trans, old_inode, old_dir, parent); btrfs_end_log_trans(root); } + + /* finalize whiteout creation */ + if (flags & RENAME_WHITEOUT) { + btrfs_update_inode(trans, root, whiteout_inode); + unlock_new_inode(whiteout_inode); + d_instantiate(old_dentry, whiteout_inode); + } out_fail: btrfs_end_transaction(trans, root); out_notrans: @@ -9478,14 +9526,14 @@ static int btrfs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; - + if (flags & RENAME_EXCHANGE) return btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); - return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry); + return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } static void btrfs_run_delalloc_work(struct btrfs_work *work) From 7a21697da1a06df365f559a0b037d25c61809b8f Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Wed, 20 Jan 2016 16:42:08 -0500 Subject: [PATCH 3/5] btrfs_rename(): join all whiteout creation logic together --- fs/btrfs/inode.c | 76 +++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 657e61257c83b0..a1912d70cf7409 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9428,43 +9428,6 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_fail; } - /* the old inode is freed, create whiteout in its place */ - if (flags & RENAME_WHITEOUT) { - ret = btrfs_find_free_ino(root, &whiteout_objectid); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto out_fail; - } - - whiteout_inode = btrfs_new_inode(trans, root, old_dir, - old_dentry->d_name.name, old_dentry->d_name.len, - btrfs_ino(old_dir), whiteout_objectid, - S_IFCHR | S_IRWXUGO, &whiteout_index); - - if (IS_ERR(whiteout_inode)) { - btrfs_abort_transaction(trans, root, ret); - goto out_fail; - } - - whiteout_inode->i_op = &btrfs_special_inode_operations; - init_special_inode(whiteout_inode, whiteout_inode->i_mode, - MKDEV(0, 0)); - - ret = btrfs_init_inode_security(trans, whiteout_inode, old_dir, - &old_dentry->d_name); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto out_fail; - } - - ret = btrfs_add_nondir(trans, old_dir, old_dentry, whiteout_inode, - 0, whiteout_index); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto out_fail; - } - } - if (new_inode) { inode_inc_iversion(new_inode); new_inode->i_ctime = CURRENT_TIME; @@ -9506,11 +9469,46 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, btrfs_log_new_name(trans, old_inode, old_dir, parent); btrfs_end_log_trans(root); } - - /* finalize whiteout creation */ + + /* the old dentry is freed, recreate it as a whiteout */ if (flags & RENAME_WHITEOUT) { + ret = btrfs_find_free_ino(root, &whiteout_objectid); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + whiteout_inode = btrfs_new_inode(trans, root, old_dir, + old_dentry->d_name.name, old_dentry->d_name.len, + btrfs_ino(old_dir), whiteout_objectid, + S_IFCHR | WHITEOUT_MODE, &whiteout_index); + + if (IS_ERR(whiteout_inode)) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + whiteout_inode->i_op = &btrfs_special_inode_operations; + init_special_inode(whiteout_inode, whiteout_inode->i_mode, + WHITEOUT_DEV); + + ret = btrfs_init_inode_security(trans, whiteout_inode, old_dir, + &old_dentry->d_name); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + + ret = btrfs_add_nondir(trans, old_dir, old_dentry, whiteout_inode, + 0, whiteout_index); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + btrfs_update_inode(trans, root, whiteout_inode); unlock_new_inode(whiteout_inode); + dentry_unlink_inode(old_dentry); d_instantiate(old_dentry, whiteout_inode); } out_fail: From 44ea213a0a3189467477cd4992e706872bb165ad Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Thu, 21 Jan 2016 08:40:12 -0500 Subject: [PATCH 4/5] btrfs_rename(): improve error handling in whiteout creation --- fs/btrfs/inode.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a1912d70cf7409..5de7fa71eb9e69 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9470,7 +9470,6 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, btrfs_end_log_trans(root); } - /* the old dentry is freed, recreate it as a whiteout */ if (flags & RENAME_WHITEOUT) { ret = btrfs_find_free_ino(root, &whiteout_objectid); if (ret) { @@ -9484,6 +9483,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout_index); if (IS_ERR(whiteout_inode)) { + ret = PTR_ERR(whiteout_inode); btrfs_abort_transaction(trans, root, ret); goto out_fail; } @@ -9506,10 +9506,14 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_fail; } - btrfs_update_inode(trans, root, whiteout_inode); + ret = btrfs_update_inode(trans, root, whiteout_inode); + if ( ret ) { + btrfs_abort_transaction(trans, root, ret); + goto out_fail; + } + unlock_new_inode(whiteout_inode); - dentry_unlink_inode(old_dentry); - d_instantiate(old_dentry, whiteout_inode); + iput(whiteout_inode); } out_fail: btrfs_end_transaction(trans, root); From 8e574c79bc4215e5ff8ec517421e53c42a8c35e5 Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Thu, 21 Jan 2016 09:00:43 -0500 Subject: [PATCH 5/5] fs/btrfs/inode.c: Style guide conformance --- fs/btrfs/inode.c | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5de7fa71eb9e69..a1eba96fa28452 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9110,8 +9110,10 @@ static int btrfs_getattr(struct vfsmount *mnt, return 0; } -static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) +static int btrfs_rename_exchange(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry) { struct btrfs_trans_handle *trans; struct btrfs_root *root = BTRFS_I(old_dir)->root; @@ -9119,6 +9121,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentr struct inode *new_inode = new_dentry->d_inode; struct inode *old_inode = old_dentry->d_inode; struct timespec ctime = CURRENT_TIME; + struct dentry *parent; u64 old_ino = btrfs_ino(old_inode); u64 new_ino = btrfs_ino(new_inode); u64 old_idx = 0; @@ -9146,9 +9149,9 @@ static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentr */ trans = btrfs_start_transaction(root, 12); if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - goto out_notrans; - } + ret = PTR_ERR(trans); + goto out_notrans; + } /* * We need to find a free sequence number both in the source and @@ -9273,12 +9276,12 @@ static int btrfs_rename_exchange(struct inode *old_dir, struct dentry *old_dentr BTRFS_I(new_inode)->dir_index = new_idx; if (old_ino != BTRFS_FIRST_FREE_OBJECTID) { - struct dentry *parent = new_dentry->d_parent; + parent = new_dentry->d_parent; btrfs_log_new_name(trans, old_inode, old_dir, parent); btrfs_end_log_trans(root); } if (new_ino != BTRFS_FIRST_FREE_OBJECTID) { - struct dentry *parent = old_dentry->d_parent; + parent = old_dentry->d_parent; btrfs_log_new_name(trans, new_inode, new_dir, parent); btrfs_end_log_trans(dest); } @@ -9367,9 +9370,9 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ trans = btrfs_start_transaction(root, 11); if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - goto out_notrans; - } + ret = PTR_ERR(trans); + goto out_notrans; + } if (dest != root) btrfs_record_root_in_trans(trans, dest); @@ -9469,7 +9472,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, btrfs_log_new_name(trans, old_inode, old_dir, parent); btrfs_end_log_trans(root); } - + if (flags & RENAME_WHITEOUT) { ret = btrfs_find_free_ino(root, &whiteout_objectid); if (ret) { @@ -9478,9 +9481,12 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, } whiteout_inode = btrfs_new_inode(trans, root, old_dir, - old_dentry->d_name.name, old_dentry->d_name.len, - btrfs_ino(old_dir), whiteout_objectid, - S_IFCHR | WHITEOUT_MODE, &whiteout_index); + old_dentry->d_name.name, + old_dentry->d_name.len, + btrfs_ino(old_dir), + whiteout_objectid, + S_IFCHR | WHITEOUT_MODE, + &whiteout_index); if (IS_ERR(whiteout_inode)) { ret = PTR_ERR(whiteout_inode); @@ -9499,19 +9505,19 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_fail; } - ret = btrfs_add_nondir(trans, old_dir, old_dentry, whiteout_inode, - 0, whiteout_index); + ret = btrfs_add_nondir(trans, old_dir, old_dentry, + whiteout_inode, 0, whiteout_index); if (ret) { btrfs_abort_transaction(trans, root, ret); goto out_fail; } - + ret = btrfs_update_inode(trans, root, whiteout_inode); - if ( ret ) { + if (ret) { btrfs_abort_transaction(trans, root, ret); goto out_fail; } - + unlock_new_inode(whiteout_inode); iput(whiteout_inode); } @@ -10120,7 +10126,7 @@ static const struct inode_operations btrfs_dir_inode_operations = { .get_acl = btrfs_get_acl, .set_acl = btrfs_set_acl, .update_time = btrfs_update_time, - .tmpfile = btrfs_tmpfile, + .tmpfile = btrfs_tmpfile, }; static const struct inode_operations btrfs_dir_ro_inode_operations = { .lookup = btrfs_lookup, @@ -10138,7 +10144,7 @@ static const struct file_operations btrfs_dir_file_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = btrfs_ioctl, #endif - .release = btrfs_release_file, + .release = btrfs_release_file, .fsync = btrfs_sync_file, };