diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/namei.c | 21 |
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 | ||
3150 | static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent) | 3150 | static 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); |