diff options
| -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); |
