linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Miklos Szeredi <miklos@szeredi.hu>
To: viro@ZenIV.linux.org.uk
Cc: torvalds@linux-foundation.org, linux-fsdevel@vger.kernel.org,
	linux-kernel@vger.kernel.org, hch@infradead.org,
	akpm@linux-foundation.org, dhowells@redhat.com, zab@redhat.com,
	jack@suse.cz, luto@amacapital.net, mszeredi@suse.cz
Subject: [PATCH 11/11] ext4: add cross rename support
Date: Wed,  8 Jan 2014 23:10:15 +0100	[thread overview]
Message-ID: <1389219015-10980-12-git-send-email-miklos@szeredi.hu> (raw)
In-Reply-To: <1389219015-10980-1-git-send-email-miklos@szeredi.hu>

From: Miklos Szeredi <mszeredi@suse.cz>

Implement RENAME_EXCHANGE flag in renameat2 syscall.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
 fs/ext4/namei.c | 121 ++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 87 insertions(+), 34 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 7147d08a43a2..e4513ba7ed99 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3005,6 +3005,8 @@ struct ext4_renament {
 	struct inode *dir;
 	struct dentry *dentry;
 	struct inode *inode;
+	bool is_dir;
+	int dir_nlink_delta;
 
 	/* entry for "dentry" */
 	struct buffer_head *bh;
@@ -3136,6 +3138,14 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
 	}
 }
 
+static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
+{
+	if (ent->dir_nlink_delta == -1)
+		ext4_dec_count(handle, ent->dir);
+	else if (ent->dir_nlink_delta == 1)
+		ext4_inc_count(handle, ent->dir);
+}
+
 /*
  * Anybody can rename anything with this: the permission checks are left to the
  * higher-level routines.
@@ -3161,7 +3171,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	};
 	int retval;
 
-	if (flags & ~RENAME_NOREPLACE)
+	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
 		return -EOPNOTSUPP;
 
 	dquot_initialize(old.dir);
@@ -3169,10 +3179,11 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 	/* Initialize quotas before so that eventual writes go
 	 * in separate transaction */
-	if (new.inode)
+	if (!(flags & RENAME_EXCHANGE) && new.inode)
 		dquot_initialize(new.inode);
 
-	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
+	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
+				 &old.de, &old.inlined);
 	/*
 	 *  Check for inode number is _not_ due to possible IO errors.
 	 *  We might rmdir the source, keep it as pwd of some process
@@ -3185,18 +3196,22 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 	new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
 				 &new.de, &new.inlined);
-	if (new.bh) {
-		if (!new.inode) {
-			brelse(new.bh);
-			new.bh = NULL;
+	if (!(flags & RENAME_EXCHANGE)) {
+		if (new.bh) {
+			if (!new.inode) {
+				brelse(new.bh);
+				new.bh = NULL;
+			}
 		}
+		if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
+			ext4_alloc_da_blocks(old.inode);
+	} else if (!new.bh || le32_to_cpu(new.de->inode) != new.inode->i_ino) {
+		goto end_rename;
 	}
-	if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
-		ext4_alloc_da_blocks(old.inode);
 
 	handle = ext4_journal_start(old.dir, EXT4_HT_DIR,
 		(2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
-		 EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
+		 2 * EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
 	if (IS_ERR(handle))
 		return PTR_ERR(handle);
 
@@ -3204,28 +3219,61 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 		ext4_handle_sync(handle);
 
 	if (S_ISDIR(old.inode->i_mode)) {
-		if (new.inode) {
+		old.is_dir = true;
+		if (!(flags & RENAME_EXCHANGE) && new.inode) {
 			retval = -ENOTEMPTY;
 			if (!empty_dir(new.inode))
 				goto end_rename;
-		} else {
-			retval = -EMLINK;
-			if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
-				goto end_rename;
+
+			/*
+			 * Overwriting a directory needs to decrement nlink of
+			 * old parent (even if not cross directory rename).
+			 */
+			old.dir_nlink_delta = -1;
 		}
 		retval = ext4_rename_dir_prepare(handle, &old);
 		if (retval)
 			goto end_rename;
 	}
+	if (new.inode && S_ISDIR(new.inode->i_mode)) {
+		new.is_dir = true;
+		if (flags & RENAME_EXCHANGE) {
+			retval = ext4_rename_dir_prepare(handle, &new);
+			if (retval)
+				goto end_rename;
+		}
+	}
+
+	/*
+	 * Other than the special case of overwring a directory, parents' nlink
+	 * only needs to be modified if this is a cross directory rename.
+	 */
+	if (old.dir != new.dir && old.is_dir != new.is_dir) {
+		old.dir_nlink_delta = old.is_dir ? -1 : 1;
+		new.dir_nlink_delta = -old.dir_nlink_delta;
+		retval = -EMLINK;
+		if ((old.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(old.dir)) ||
+		    (new.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(new.dir)))
+			goto end_rename;
+	}
+
 	if (!new.bh) {
 		retval = ext4_add_entry(handle, new.dentry, old.inode);
 		if (retval)
 			goto end_rename;
 	} else {
+		u8 new_file_type = new.de->file_type;
 		retval = ext4_setent(handle, &new,
 				     old.inode->i_ino, old.de->file_type);
 		if (retval)
 			goto end_rename;
+
+		if (flags & RENAME_EXCHANGE) {
+			retval = ext4_setent(handle, &old,
+					     new.inode->i_ino, new_file_type);
+			if (retval)
+				goto end_rename;
+		}
 	}
 
 	/*
@@ -3235,35 +3283,40 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	old.inode->i_ctime = ext4_current_time(old.inode);
 	ext4_mark_inode_dirty(handle, old.inode);
 
-	/*
-	 * ok, that's it
-	 */
-	ext4_rename_delete(handle, &old);
+	if (!(flags & RENAME_EXCHANGE)) {
+		/*
+		 * ok, that's it
+		 */
+		ext4_rename_delete(handle, &old);
 
-	if (new.inode) {
-		ext4_dec_count(handle, new.inode);
-		new.inode->i_ctime = ext4_current_time(new.inode);
+		old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir);
+		ext4_update_dx_flag(old.dir);
 	}
-	old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir);
-	ext4_update_dx_flag(old.dir);
 	if (old.dir_bh) {
 		retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
 		if (retval)
 			goto end_rename;
-
-		ext4_dec_count(handle, old.dir);
-		if (new.inode) {
+	}
+	if (new.dir_bh) {
+		retval = ext4_rename_dir_finish(handle, &new, old.dir->i_ino);
+		if (retval)
+			goto end_rename;
+	}
+	ext4_update_dir_count(handle, &old);
+	ext4_update_dir_count(handle, &new);
+	if (!new.inode)
+		ext4_update_dx_flag(new.dir);
+	if (new.dir_nlink_delta || !new.inode)
+		ext4_mark_inode_dirty(handle, new.dir);
+	ext4_mark_inode_dirty(handle, old.dir);
+	if (!(flags & RENAME_EXCHANGE) && new.inode) {
+		ext4_dec_count(handle, new.inode);
+		new.inode->i_ctime = ext4_current_time(new.inode);
+		if (S_ISDIR(old.inode->i_mode)) {
 			/* checked empty_dir above, can't have another parent,
 			 * ext4_dec_count() won't work for many-linked dirs */
 			clear_nlink(new.inode);
-		} else {
-			ext4_inc_count(handle, new.dir);
-			ext4_update_dx_flag(new.dir);
-			ext4_mark_inode_dirty(handle, new.dir);
 		}
-	}
-	ext4_mark_inode_dirty(handle, old.dir);
-	if (new.inode) {
 		ext4_mark_inode_dirty(handle, new.inode);
 		if (!new.inode->i_nlink)
 			ext4_orphan_add(handle, new.inode);
-- 
1.8.1.4


  parent reply	other threads:[~2014-01-08 22:10 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-01-08 22:10 [PATCH 00/11] cross rename v3 Miklos Szeredi
2014-01-08 22:10 ` [PATCH 01/11] vfs: add d_is_dir() Miklos Szeredi
2014-01-08 22:10 ` [PATCH 02/11] vfs: rename: move d_move() up Miklos Szeredi
2014-01-08 22:10 ` [PATCH 03/11] vfs: rename: use common code for dir and non-dir Miklos Szeredi
2014-01-08 22:10 ` [PATCH 04/11] vfs: add renameat2 syscall Miklos Szeredi
2014-01-14 22:11   ` Tetsuo Handa
2014-01-15 10:30     ` Miklos Szeredi
2014-01-15 13:50       ` Miklos Szeredi
2014-01-18 10:40         ` Tetsuo Handa
2014-01-08 22:10 ` [PATCH 05/11] vfs: add RENAME_NOREPLACE flag Miklos Szeredi
2014-01-15 18:19   ` J. Bruce Fields
2014-01-15 18:26     ` Andy Lutomirski
2014-01-15 23:33       ` J. Bruce Fields
2014-01-16 10:45         ` Miklos Szeredi
2014-01-15 18:35     ` Miklos Szeredi
2014-01-15 23:31       ` J. Bruce Fields
2014-01-08 22:10 ` [PATCH 06/11] security: add flags to rename hooks Miklos Szeredi
2014-01-08 22:10 ` [PATCH 07/11] vfs: add cross-rename Miklos Szeredi
2014-01-13  7:52   ` Jan Kara
2014-01-14 10:31     ` Miklos Szeredi
2014-01-14 12:47       ` Jan Kara
2014-01-08 22:10 ` [PATCH 08/11] ext4: rename: create ext4_renament structure for local vars Miklos Szeredi
2014-01-08 22:10 ` [PATCH 09/11] ext4: rename: move EMLINK check up Miklos Szeredi
2014-01-08 22:10 ` [PATCH 10/11] ext4: rename: split out helper functions Miklos Szeredi
2014-01-08 22:10 ` Miklos Szeredi [this message]
2014-01-13 12:25   ` [PATCH 11/11] ext4: add cross rename support Jan Kara
2014-01-14 10:35     ` Miklos Szeredi
2014-01-15 18:23     ` J. Bruce Fields
2014-01-15 18:31       ` Miklos Szeredi
2014-01-16 10:54         ` Miklos Szeredi
2014-01-16 14:48           ` J. Bruce Fields
2014-01-17 10:53           ` Michael Kerrisk (man-pages)
2014-01-17 14:41             ` Miklos Szeredi
     [not found]               ` <20140117144126.GG24171-nYI/l+Q8b4r16c5iV7KQqR1Qg9XOENNVk/YoNI2nt5o@public.gmane.org>
2014-04-19  9:08                 ` Michael Kerrisk (man-pages)
2014-04-19 12:08                   ` Tetsuo Handa
2014-04-23 14:24                     ` Miklos Szeredi
2014-04-24 11:20                       ` [PATCH (for 3.15) 0/5] Fix cross rename race window for LSM Tetsuo Handa
2014-04-24 11:22                         ` [PATCH (for 3.15) 1/5] LSM: Pass the rename flags to each LSM module Tetsuo Handa
2014-04-25 20:49                           ` Casey Schaufler
2014-04-24 11:23                         ` [PATCH (for 3.15) 2/5] SELinux: Handle the rename flags Tetsuo Handa
2014-04-24 11:24                         ` [PATCH (for 3.15) 3/5] AppArmor: " Tetsuo Handa
2014-04-24 11:25                         ` [PATCH (for 3.15) 4/5] TOMOYO: " Tetsuo Handa
2014-04-24 11:26                         ` [PATCH (for 3.15) 5/5] LSM: Remove duplicated rename handling Tetsuo Handa
2014-05-01 11:58                         ` [PATCH (for 3.15) 0/5] Fix cross rename race window for LSM Tetsuo Handa
2014-05-05  5:49                           ` Tetsuo Handa
2014-05-11 15:53                             ` Tetsuo Handa
2014-05-12 13:21                               ` [PATCH (for 3.15) 0/5] Fix cross rename regressions " Tetsuo Handa
2014-05-12 13:22                                 ` [PATCH (for 3.15) 1/5] LSM: Pass the rename flags to each LSM module Tetsuo Handa
2014-05-19 12:19                                   ` John Johansen
2014-05-12 13:23                                 ` [PATCH (for 3.15) 2/5] SELinux: Handle the rename flags Tetsuo Handa
2014-05-12 13:24                                 ` [PATCH (for 3.15) 3/5] AppArmor: " Tetsuo Handa
2014-05-19 12:28                                   ` John Johansen
2014-05-12 13:25                                 ` [PATCH (for 3.15) 4/5] TOMOYO: " Tetsuo Handa
2014-05-12 13:25                                 ` [PATCH (for 3.15) 5/5] LSM: Remove duplicated rename handling Tetsuo Handa
2014-05-19 12:34                                   ` John Johansen
2014-04-23 14:21                   ` [PATCH 11/11] ext4: add cross rename support Miklos Szeredi
     [not found]                     ` <CAJfpegsdUwxHOGxhiLtkMHzB==UGzbj+rAVOJGX4nb6z1Urzpw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-04-23 19:01                       ` Michael Kerrisk (man-pages)
2014-01-17 22:08             ` J. Bruce Fields
2014-01-18  6:49               ` Miklos Szeredi
2014-01-18 16:27                 ` J. Bruce Fields
2014-01-20 11:39                   ` Miklos Szeredi
2014-01-20 11:50                     ` Michael Kerrisk (man-pages)
2014-01-13 12:46 ` [PATCH 00/11] cross rename v3 Tetsuo Handa
2014-01-13 17:08   ` Miklos Szeredi
2014-01-13 22:03     ` Tetsuo Handa
2014-01-14  9:58       ` Miklos Szeredi
2014-01-14 13:03         ` Tetsuo Handa
2014-01-14 20:10           ` John Johansen
2014-01-14 20:53             ` Tetsuo Handa
2014-01-15 10:10               ` Miklos Szeredi
  -- strict thread matches above, loose matches on Subject: below --
2013-11-20 13:01 [PATCH 00/11] cross rename v2 Miklos Szeredi
2013-11-20 13:01 ` [PATCH 11/11] ext4: add cross rename support Miklos Szeredi
2013-11-26  9:51   ` Jan Kara
2013-11-26 13:47     ` Miklos Szeredi

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=1389219015-10980-12-git-send-email-miklos@szeredi.hu \
    --to=miklos@szeredi.hu \
    --cc=akpm@linux-foundation.org \
    --cc=dhowells@redhat.com \
    --cc=hch@infradead.org \
    --cc=jack@suse.cz \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=mszeredi@suse.cz \
    --cc=torvalds@linux-foundation.org \
    --cc=viro@ZenIV.linux.org.uk \
    --cc=zab@redhat.com \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).