* [PATCH] ext4: fix same-dir rename when inline data directory overflows
@ 2014-08-25 19:40 Darrick J. Wong
2014-08-26 2:49 ` Theodore Ts'o
0 siblings, 1 reply; 2+ messages in thread
From: Darrick J. Wong @ 2014-08-25 19:40 UTC (permalink / raw)
To: Theodore Ts'o, Zheng Liu; +Cc: linux-ext4
When performing a same-directory rename, it's possible that adding or
setting the new directory entry will cause the directory to overflow
the inline data area, which causes the directory to be converted to an
extent-based directory. Under this circumstance it is necessary to
re-read the directory when deleting the old dirent because the "old
directory" context still points to i_block in the inode table, which
is now an extent tree root! The delete fails with an FS error, and
the subsequent fsck complains about incorrect link counts and
hardlinked directories.
Test case (originally found with flat_dir_test in the metadata_csum
test program):
# mkfs.ext4 -O inline_data /dev/sda
# mount /dev/sda /mnt
# mkdir /mnt/x
# touch /mnt/x/changelog.gz /mnt/x/copyright /mnt/x/README.Debian
# sync
# for i in /mnt/x/*; do mv $i $i.longer; done
# ls -la /mnt/x/
total 0
-rw-r--r-- 1 root root 0 Aug 25 12:03 changelog.gz.longer
-rw-r--r-- 1 root root 0 Aug 25 12:03 copyright
-rw-r--r-- 1 root root 0 Aug 25 12:03 copyright.longer
-rw-r--r-- 1 root root 0 Aug 25 12:03 README.Debian.longer
(Hey! Why are there four files now??)
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/ext4/namei.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b147a67..f1bbe1c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3128,7 +3128,8 @@ static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
return retval;
}
-static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
+static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
+ int force_reread)
{
int retval;
/*
@@ -3140,7 +3141,8 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
ent->de->name_len != ent->dentry->d_name.len ||
strncmp(ent->de->name, ent->dentry->d_name.name,
- ent->de->name_len)) {
+ ent->de->name_len) ||
+ force_reread) {
retval = ext4_find_delete_entry(handle, ent->dir,
&ent->dentry->d_name);
} else {
@@ -3191,6 +3193,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
.dentry = new_dentry,
.inode = new_dentry->d_inode,
};
+ int force_reread;
int retval;
dquot_initialize(old.dir);
@@ -3246,6 +3249,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
if (retval)
goto end_rename;
}
+ /*
+ * If we're renaming a file within an inline_data dir and adding or
+ * setting the new dirent causes a conversion from inline_data to
+ * extents/blockmap, we need to force the dirent delete code to
+ * re-read the directory, or else we end up trying to delete a dirent
+ * from what is now the extent tree root (or a block map).
+ */
+ force_reread = (new.dir->i_ino == old.dir->i_ino &&
+ ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
if (!new.bh) {
retval = ext4_add_entry(handle, new.dentry, old.inode);
if (retval)
@@ -3256,6 +3268,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
if (retval)
goto end_rename;
}
+ if (force_reread)
+ force_reread = !ext4_test_inode_flag(new.dir,
+ EXT4_INODE_INLINE_DATA);
/*
* Like most other Unix systems, set the ctime for inodes on a
@@ -3267,7 +3282,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
/*
* ok, that's it
*/
- ext4_rename_delete(handle, &old);
+ ext4_rename_delete(handle, &old, force_reread);
if (new.inode) {
ext4_dec_count(handle, new.inode);
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] ext4: fix same-dir rename when inline data directory overflows
2014-08-25 19:40 [PATCH] ext4: fix same-dir rename when inline data directory overflows Darrick J. Wong
@ 2014-08-26 2:49 ` Theodore Ts'o
0 siblings, 0 replies; 2+ messages in thread
From: Theodore Ts'o @ 2014-08-26 2:49 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Zheng Liu, linux-ext4
On Mon, Aug 25, 2014 at 12:40:31PM -0700, Darrick J. Wong wrote:
> When performing a same-directory rename, it's possible that adding or
> setting the new directory entry will cause the directory to overflow
> the inline data area, which causes the directory to be converted to an
> extent-based directory. Under this circumstance it is necessary to
> re-read the directory when deleting the old dirent because the "old
> directory" context still points to i_block in the inode table, which
> is now an extent tree root! The delete fails with an FS error, and
> the subsequent fsck complains about incorrect link counts and
> hardlinked directories.
>
> Test case (originally found with flat_dir_test in the metadata_csum
> test program):
>
> # mkfs.ext4 -O inline_data /dev/sda
> # mount /dev/sda /mnt
> # mkdir /mnt/x
> # touch /mnt/x/changelog.gz /mnt/x/copyright /mnt/x/README.Debian
> # sync
> # for i in /mnt/x/*; do mv $i $i.longer; done
> # ls -la /mnt/x/
> total 0
> -rw-r--r-- 1 root root 0 Aug 25 12:03 changelog.gz.longer
> -rw-r--r-- 1 root root 0 Aug 25 12:03 copyright
> -rw-r--r-- 1 root root 0 Aug 25 12:03 copyright.longer
> -rw-r--r-- 1 root root 0 Aug 25 12:03 README.Debian.longer
>
> (Hey! Why are there four files now??)
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Thanks, applied.
- Ted
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-08-26 2:49 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-08-25 19:40 [PATCH] ext4: fix same-dir rename when inline data directory overflows Darrick J. Wong
2014-08-26 2:49 ` Theodore Ts'o
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).