aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2014-08-27 18:40:09 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-08-28 22:22:29 -0400
commitd80d448c6c5bdd32605b78a60fe8081d82d4da0f (patch)
tree81126dbb0bead4385659eae05916ad922293b80c /fs/ext4
parentdb9ee220361de03ee86388f9ea5e529eaad5323c (diff)
ext4: fix same-dir rename when inline data directory overflows
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> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/namei.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index ae7088b446d1..90a3cdca3f88 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3147,7 +3147,8 @@ static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
3147 return retval; 3147 return retval;
3148} 3148}
3149 3149
3150static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent) 3150static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
3151 int force_reread)
3151{ 3152{
3152 int retval; 3153 int retval;
3153 /* 3154 /*
@@ -3159,7 +3160,8 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
3159 if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino || 3160 if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
3160 ent->de->name_len != ent->dentry->d_name.len || 3161 ent->de->name_len != ent->dentry->d_name.len ||
3161 strncmp(ent->de->name, ent->dentry->d_name.name, 3162 strncmp(ent->de->name, ent->dentry->d_name.name,
3162 ent->de->name_len)) { 3163 ent->de->name_len) ||
3164 force_reread) {
3163 retval = ext4_find_delete_entry(handle, ent->dir, 3165 retval = ext4_find_delete_entry(handle, ent->dir,
3164 &ent->dentry->d_name); 3166 &ent->dentry->d_name);
3165 } else { 3167 } else {
@@ -3210,6 +3212,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3210 .dentry = new_dentry, 3212 .dentry = new_dentry,
3211 .inode = new_dentry->d_inode, 3213 .inode = new_dentry->d_inode,
3212 }; 3214 };
3215 int force_reread;
3213 int retval; 3216 int retval;
3214 3217
3215 dquot_initialize(old.dir); 3218 dquot_initialize(old.dir);
@@ -3271,6 +3274,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3271 if (retval) 3274 if (retval)
3272 goto end_rename; 3275 goto end_rename;
3273 } 3276 }
3277 /*
3278 * If we're renaming a file within an inline_data dir and adding or
3279 * setting the new dirent causes a conversion from inline_data to
3280 * extents/blockmap, we need to force the dirent delete code to
3281 * re-read the directory, or else we end up trying to delete a dirent
3282 * from what is now the extent tree root (or a block map).
3283 */
3284 force_reread = (new.dir->i_ino == old.dir->i_ino &&
3285 ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
3274 if (!new.bh) { 3286 if (!new.bh) {
3275 retval = ext4_add_entry(handle, new.dentry, old.inode); 3287 retval = ext4_add_entry(handle, new.dentry, old.inode);
3276 if (retval) 3288 if (retval)
@@ -3281,6 +3293,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3281 if (retval) 3293 if (retval)
3282 goto end_rename; 3294 goto end_rename;
3283 } 3295 }
3296 if (force_reread)
3297 force_reread = !ext4_test_inode_flag(new.dir,
3298 EXT4_INODE_INLINE_DATA);
3284 3299
3285 /* 3300 /*
3286 * Like most other Unix systems, set the ctime for inodes on a 3301 * Like most other Unix systems, set the ctime for inodes on a
@@ -3292,7 +3307,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3292 /* 3307 /*
3293 * ok, that's it 3308 * ok, that's it
3294 */ 3309 */
3295 ext4_rename_delete(handle, &old); 3310 ext4_rename_delete(handle, &old, force_reread);
3296 3311
3297 if (new.inode) { 3312 if (new.inode) {
3298 ext4_dec_count(handle, new.inode); 3313 ext4_dec_count(handle, new.inode);