All of lore.kernel.org
 help / color / mirror / Atom feed
From: Omar Sandoval <osandov@osandov.com>
To: linux-fsdevel@vger.kernel.org, Al Viro <viro@zeniv.linux.org.uk>
Cc: kernel-team@fb.com
Subject: [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE
Date: Tue, 28 Jan 2020 15:19:03 -0800	[thread overview]
Message-ID: <55e3795a385177f13cde7041fe7a5e1644994879.1580251857.git.osandov@fb.com> (raw)
In-Reply-To: <cover.1580251857.git.osandov@fb.com>

From: Omar Sandoval <osandov@fb.com>

The implementation is fairly straightforward and looks a lot like
btrfs_rename(). The only tricky bit is that instead of playing games
with the dcache, we simply drop the dentry for it to be instantiated on
the next lookup. This can be improved in the future.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 fs/btrfs/inode.c | 63 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 59 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8c9a114f48f6..b489671d1b5d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6762,14 +6762,16 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 		      struct dentry *dentry, int flags)
 {
 	struct btrfs_trans_handle *trans = NULL;
+	unsigned int trans_num_items;
 	struct btrfs_root *root = BTRFS_I(dir)->root;
 	struct inode *inode = d_inode(old_dentry);
+	struct inode *new_inode = d_inode(dentry);
 	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
 	u64 index;
 	int err;
 	int drop_inode = 0;
 
-	if (flags)
+	if (flags & ~AT_LINK_REPLACE)
 		return -EINVAL;
 
 	/* do not allow sys_link's with other subvols of the same device */
@@ -6779,17 +6781,50 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	if (inode->i_nlink >= BTRFS_LINK_MAX)
 		return -EMLINK;
 
+	/* check for collisions, even if the name isn't there */
+	err = btrfs_check_dir_item_collision(root, dir->i_ino,
+					     dentry->d_name.name,
+					     dentry->d_name.len);
+	if (err) {
+		if (err == -EEXIST) {
+			if (WARN_ON(!new_inode))
+				return err;
+		} else {
+			return err;
+		}
+	}
+
+	/*
+	 * we're using link to replace one file with another. Start IO on it now
+	 * so we don't add too much work to the end of the transaction
+	 */
+	if (new_inode && S_ISREG(inode->i_mode) && new_inode->i_size)
+		filemap_flush(inode->i_mapping);
+
 	err = btrfs_set_inode_index(BTRFS_I(dir), &index);
 	if (err)
 		goto fail;
 
 	/*
+	 * For the source:
 	 * 2 items for inode and inode ref
 	 * 2 items for dir items
 	 * 1 item for parent inode
 	 * 1 item for orphan item deletion if O_TMPFILE
+	 *
+	 * For the target:
+	 * 1 for the possible orphan item
+	 * 1 for the dir item
+	 * 1 for the dir index
+	 * 1 for the inode ref
+	 * 1 for the inode
 	 */
-	trans = btrfs_start_transaction(root, inode->i_nlink ? 5 : 6);
+	trans_num_items = 5;
+	if (!inode->i_nlink)
+		trans_num_items++;
+	if (new_inode)
+		trans_num_items += 5;
+	trans = btrfs_start_transaction(root, trans_num_items);
 	if (IS_ERR(trans)) {
 		err = PTR_ERR(trans);
 		trans = NULL;
@@ -6801,6 +6836,22 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	inc_nlink(inode);
 	inode_inc_iversion(inode);
 	inode->i_ctime = current_time(inode);
+
+	if (new_inode) {
+		inode_inc_iversion(new_inode);
+		new_inode->i_ctime = current_time(new_inode);
+		err = btrfs_unlink_inode(trans, root, BTRFS_I(dir),
+					 BTRFS_I(new_inode),
+					 dentry->d_name.name,
+					 dentry->d_name.len);
+		if (!err && new_inode->i_nlink == 0)
+			err = btrfs_orphan_add(trans, BTRFS_I(new_inode));
+		if (err) {
+			btrfs_abort_transaction(trans, err);
+			goto fail;
+		}
+	}
+
 	set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
 
 	err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry, BTRFS_I(inode),
@@ -6824,8 +6875,12 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 			if (err)
 				goto fail;
 		}
-		ihold(inode);
-		d_instantiate(dentry, inode);
+		if (new_inode) {
+			d_drop(dentry);
+		} else {
+			ihold(inode);
+			d_instantiate(dentry, inode);
+		}
 		ret = btrfs_log_new_name(trans, BTRFS_I(inode), NULL, parent,
 					 true, NULL);
 		if (ret == BTRFS_NEED_TRANS_COMMIT) {
-- 
2.25.0


WARNING: multiple messages have this Message-ID
From: Omar Sandoval <osandov@osandov.com>
To: linux-fsdevel@vger.kernel.org, Al Viro <viro@zeniv.linux.org.uk>
Cc: kernel-team@fb.com, linux-api@vger.kernel.org,
	David Howells <dhowells@redhat.com>,
	Amir Goldstein <amir73il@gmail.com>,
	Xi Wang <xi@cs.washington.edu>
Subject: [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE
Date: Wed, 29 Jan 2020 00:58:34 -0800	[thread overview]
Message-ID: <55e3795a385177f13cde7041fe7a5e1644994879.1580251857.git.osandov@fb.com> (raw)
Message-ID: <20200129085834.CY28-eJFGiujZw7bXr0c6AMjscFZOJP0oZzyqfXEuXw@z> (raw)
In-Reply-To: <cover.1580251857.git.osandov@fb.com>

From: Omar Sandoval <osandov@fb.com>

The implementation is fairly straightforward and looks a lot like
btrfs_rename(). The only tricky bit is that instead of playing games
with the dcache, we simply drop the dentry for it to be instantiated on
the next lookup. This can be improved in the future.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 fs/btrfs/inode.c | 63 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 59 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8c9a114f48f6..b489671d1b5d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6762,14 +6762,16 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 		      struct dentry *dentry, int flags)
 {
 	struct btrfs_trans_handle *trans = NULL;
+	unsigned int trans_num_items;
 	struct btrfs_root *root = BTRFS_I(dir)->root;
 	struct inode *inode = d_inode(old_dentry);
+	struct inode *new_inode = d_inode(dentry);
 	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
 	u64 index;
 	int err;
 	int drop_inode = 0;
 
-	if (flags)
+	if (flags & ~AT_LINK_REPLACE)
 		return -EINVAL;
 
 	/* do not allow sys_link's with other subvols of the same device */
@@ -6779,17 +6781,50 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	if (inode->i_nlink >= BTRFS_LINK_MAX)
 		return -EMLINK;
 
+	/* check for collisions, even if the name isn't there */
+	err = btrfs_check_dir_item_collision(root, dir->i_ino,
+					     dentry->d_name.name,
+					     dentry->d_name.len);
+	if (err) {
+		if (err == -EEXIST) {
+			if (WARN_ON(!new_inode))
+				return err;
+		} else {
+			return err;
+		}
+	}
+
+	/*
+	 * we're using link to replace one file with another. Start IO on it now
+	 * so we don't add too much work to the end of the transaction
+	 */
+	if (new_inode && S_ISREG(inode->i_mode) && new_inode->i_size)
+		filemap_flush(inode->i_mapping);
+
 	err = btrfs_set_inode_index(BTRFS_I(dir), &index);
 	if (err)
 		goto fail;
 
 	/*
+	 * For the source:
 	 * 2 items for inode and inode ref
 	 * 2 items for dir items
 	 * 1 item for parent inode
 	 * 1 item for orphan item deletion if O_TMPFILE
+	 *
+	 * For the target:
+	 * 1 for the possible orphan item
+	 * 1 for the dir item
+	 * 1 for the dir index
+	 * 1 for the inode ref
+	 * 1 for the inode
 	 */
-	trans = btrfs_start_transaction(root, inode->i_nlink ? 5 : 6);
+	trans_num_items = 5;
+	if (!inode->i_nlink)
+		trans_num_items++;
+	if (new_inode)
+		trans_num_items += 5;
+	trans = btrfs_start_transaction(root, trans_num_items);
 	if (IS_ERR(trans)) {
 		err = PTR_ERR(trans);
 		trans = NULL;
@@ -6801,6 +6836,22 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	inc_nlink(inode);
 	inode_inc_iversion(inode);
 	inode->i_ctime = current_time(inode);
+
+	if (new_inode) {
+		inode_inc_iversion(new_inode);
+		new_inode->i_ctime = current_time(new_inode);
+		err = btrfs_unlink_inode(trans, root, BTRFS_I(dir),
+					 BTRFS_I(new_inode),
+					 dentry->d_name.name,
+					 dentry->d_name.len);
+		if (!err && new_inode->i_nlink == 0)
+			err = btrfs_orphan_add(trans, BTRFS_I(new_inode));
+		if (err) {
+			btrfs_abort_transaction(trans, err);
+			goto fail;
+		}
+	}
+
 	set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags);
 
 	err = btrfs_add_nondir(trans, BTRFS_I(dir), dentry, BTRFS_I(inode),
@@ -6824,8 +6875,12 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 			if (err)
 				goto fail;
 		}
-		ihold(inode);
-		d_instantiate(dentry, inode);
+		if (new_inode) {
+			d_drop(dentry);
+		} else {
+			ihold(inode);
+			d_instantiate(dentry, inode);
+		}
 		ret = btrfs_log_new_name(trans, BTRFS_I(inode), NULL, parent,
 					 true, NULL);
 		if (ret == BTRFS_NEED_TRANS_COMMIT) {
-- 
2.25.0


  parent reply	other threads:[~2020-01-28 23:19 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-28 23:18 [RFC PATCH v4 0/4] fs: add flag to linkat() for replacing destination Omar Sandoval
2020-01-29  8:58 ` Omar Sandoval
2020-01-28 23:18 ` [RFC PATCH xfstests] generic: add smoke test for AT_LINK_REPLACE Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-29  7:02   ` Zorro Lang
2020-02-23 14:46   ` Eryu Guan
2020-01-28 23:18 ` [RFC PATCH man-pages] link.2: Document new AT_LINK_REPLACE flag Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-28 23:18 ` [RFC PATCH xfsprogs] xfs_io: add support for linkat() AT_LINK_REPLACE Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-30  4:42   ` Zorro Lang
2020-01-28 23:19 ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-28 23:19 ` [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-28 23:19 ` [RFC PATCH v4 3/4] Btrfs: fix inode reference count leak in btrfs_link() error path Omar Sandoval
2020-01-29  8:58   ` Omar Sandoval
2020-01-28 23:19 ` Omar Sandoval [this message]
2020-01-29  8:58   ` [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE Omar Sandoval
2020-01-29  8:58 ` [RFC PATCH xfstests] generic: add smoke test for AT_LINK_REPLACE Omar Sandoval
     [not found] ` <cover.1580251857.git.osandov-b10kYP2dOMg@public.gmane.org>
2020-01-29  8:58   ` Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH man-pages] link.2: Document new AT_LINK_REPLACE flag Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH xfsprogs] xfs_io: add support for linkat() AT_LINK_REPLACE Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH v4 0/4] fs: add flag to linkat() for replacing destination Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH v4 2/4] fs: add AT_LINK_REPLACE flag for linkat() which replaces the target Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH v4 3/4] Btrfs: fix inode reference count leak in btrfs_link() error path Omar Sandoval
2020-01-29  8:58   ` [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE Omar Sandoval
2020-01-31 13:48 ` [RFC PATCH v4 1/4] fs: add flags argument to i_op->link() David Howells
2020-01-31 20:24   ` Omar Sandoval
2020-01-31 20:24     ` Omar Sandoval

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=55e3795a385177f13cde7041fe7a5e1644994879.1580251857.git.osandov@fb.com \
    --to=osandov@osandov.com \
    --cc=kernel-team@fb.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --subject='Re: [RFC PATCH v4 4/4] Btrfs: add support for linkat() AT_REPLACE' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.