diff options
| -rw-r--r-- | fs/ext4/namei.c | 199 |
1 files changed, 126 insertions, 73 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 87a8a6e613ba..75f1bde43dcc 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
| @@ -3016,6 +3016,125 @@ struct ext4_renament { | |||
| 3016 | int dir_inlined; | 3016 | int dir_inlined; |
| 3017 | }; | 3017 | }; |
| 3018 | 3018 | ||
| 3019 | static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent) | ||
| 3020 | { | ||
| 3021 | int retval; | ||
| 3022 | |||
| 3023 | ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode, | ||
| 3024 | &retval, &ent->parent_de, | ||
| 3025 | &ent->dir_inlined); | ||
| 3026 | if (!ent->dir_bh) | ||
| 3027 | return retval; | ||
| 3028 | if (le32_to_cpu(ent->parent_de->inode) != ent->dir->i_ino) | ||
| 3029 | return -EIO; | ||
| 3030 | BUFFER_TRACE(ent->dir_bh, "get_write_access"); | ||
| 3031 | return ext4_journal_get_write_access(handle, ent->dir_bh); | ||
| 3032 | } | ||
| 3033 | |||
| 3034 | static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent, | ||
| 3035 | unsigned dir_ino) | ||
| 3036 | { | ||
| 3037 | int retval; | ||
| 3038 | |||
| 3039 | ent->parent_de->inode = cpu_to_le32(dir_ino); | ||
| 3040 | BUFFER_TRACE(ent->dir_bh, "call ext4_handle_dirty_metadata"); | ||
| 3041 | if (!ent->dir_inlined) { | ||
| 3042 | if (is_dx(ent->inode)) { | ||
| 3043 | retval = ext4_handle_dirty_dx_node(handle, | ||
| 3044 | ent->inode, | ||
| 3045 | ent->dir_bh); | ||
| 3046 | } else { | ||
| 3047 | retval = ext4_handle_dirty_dirent_node(handle, | ||
| 3048 | ent->inode, | ||
| 3049 | ent->dir_bh); | ||
| 3050 | } | ||
| 3051 | } else { | ||
| 3052 | retval = ext4_mark_inode_dirty(handle, ent->inode); | ||
| 3053 | } | ||
| 3054 | if (retval) { | ||
| 3055 | ext4_std_error(ent->dir->i_sb, retval); | ||
| 3056 | return retval; | ||
| 3057 | } | ||
| 3058 | return 0; | ||
| 3059 | } | ||
| 3060 | |||
| 3061 | static int ext4_setent(handle_t *handle, struct ext4_renament *ent, | ||
| 3062 | unsigned ino, unsigned file_type) | ||
| 3063 | { | ||
| 3064 | int retval; | ||
| 3065 | |||
| 3066 | BUFFER_TRACE(ent->bh, "get write access"); | ||
| 3067 | retval = ext4_journal_get_write_access(handle, ent->bh); | ||
| 3068 | if (retval) | ||
| 3069 | return retval; | ||
| 3070 | ent->de->inode = cpu_to_le32(ino); | ||
| 3071 | if (EXT4_HAS_INCOMPAT_FEATURE(ent->dir->i_sb, | ||
| 3072 | EXT4_FEATURE_INCOMPAT_FILETYPE)) | ||
| 3073 | ent->de->file_type = file_type; | ||
| 3074 | ent->dir->i_version++; | ||
| 3075 | ent->dir->i_ctime = ent->dir->i_mtime = | ||
| 3076 | ext4_current_time(ent->dir); | ||
| 3077 | ext4_mark_inode_dirty(handle, ent->dir); | ||
| 3078 | BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata"); | ||
| 3079 | if (!ent->inlined) { | ||
| 3080 | retval = ext4_handle_dirty_dirent_node(handle, | ||
| 3081 | ent->dir, ent->bh); | ||
| 3082 | if (unlikely(retval)) { | ||
| 3083 | ext4_std_error(ent->dir->i_sb, retval); | ||
| 3084 | return retval; | ||
| 3085 | } | ||
| 3086 | } | ||
| 3087 | brelse(ent->bh); | ||
| 3088 | ent->bh = NULL; | ||
| 3089 | |||
| 3090 | return 0; | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | static int ext4_find_delete_entry(handle_t *handle, struct inode *dir, | ||
| 3094 | const struct qstr *d_name) | ||
| 3095 | { | ||
| 3096 | int retval = -ENOENT; | ||
| 3097 | struct buffer_head *bh; | ||
| 3098 | struct ext4_dir_entry_2 *de; | ||
| 3099 | |||
| 3100 | bh = ext4_find_entry(dir, d_name, &de, NULL); | ||
| 3101 | if (bh) { | ||
| 3102 | retval = ext4_delete_entry(handle, dir, de, bh); | ||
| 3103 | brelse(bh); | ||
| 3104 | } | ||
| 3105 | return retval; | ||
| 3106 | } | ||
| 3107 | |||
| 3108 | static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent) | ||
| 3109 | { | ||
| 3110 | int retval; | ||
| 3111 | /* | ||
| 3112 | * ent->de could have moved from under us during htree split, so make | ||
| 3113 | * sure that we are deleting the right entry. We might also be pointing | ||
| 3114 | * to a stale entry in the unused part of ent->bh so just checking inum | ||
| 3115 | * and the name isn't enough. | ||
| 3116 | */ | ||
| 3117 | if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino || | ||
| 3118 | ent->de->name_len != ent->dentry->d_name.len || | ||
| 3119 | strncmp(ent->de->name, ent->dentry->d_name.name, | ||
| 3120 | ent->de->name_len)) { | ||
| 3121 | retval = ext4_find_delete_entry(handle, ent->dir, | ||
| 3122 | &ent->dentry->d_name); | ||
| 3123 | } else { | ||
| 3124 | retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh); | ||
| 3125 | if (retval == -ENOENT) { | ||
| 3126 | retval = ext4_find_delete_entry(handle, ent->dir, | ||
| 3127 | &ent->dentry->d_name); | ||
| 3128 | } | ||
| 3129 | } | ||
| 3130 | |||
| 3131 | if (retval) { | ||
| 3132 | ext4_warning(ent->dir->i_sb, | ||
| 3133 | "Deleting old file (%lu), %d, error=%d", | ||
| 3134 | ent->dir->i_ino, ent->dir->i_nlink, retval); | ||
| 3135 | } | ||
| 3136 | } | ||
| 3137 | |||
| 3019 | /* | 3138 | /* |
| 3020 | * Anybody can rename anything with this: the permission checks are left to the | 3139 | * Anybody can rename anything with this: the permission checks are left to the |
| 3021 | * higher-level routines. | 3140 | * higher-level routines. |
| @@ -3089,16 +3208,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 3089 | if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir)) | 3208 | if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir)) |
| 3090 | goto end_rename; | 3209 | goto end_rename; |
| 3091 | } | 3210 | } |
| 3092 | retval = -EIO; | 3211 | retval = ext4_rename_dir_prepare(handle, &old); |
| 3093 | old.dir_bh = ext4_get_first_dir_block(handle, old.inode, | ||
| 3094 | &retval, &old.parent_de, | ||
| 3095 | &old.dir_inlined); | ||
| 3096 | if (!old.dir_bh) | ||
| 3097 | goto end_rename; | ||
| 3098 | if (le32_to_cpu(old.parent_de->inode) != old.dir->i_ino) | ||
| 3099 | goto end_rename; | ||
| 3100 | BUFFER_TRACE(old.dir_bh, "get_write_access"); | ||
| 3101 | retval = ext4_journal_get_write_access(handle, old.dir_bh); | ||
| 3102 | if (retval) | 3212 | if (retval) |
| 3103 | goto end_rename; | 3213 | goto end_rename; |
| 3104 | } | 3214 | } |
| @@ -3107,29 +3217,10 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 3107 | if (retval) | 3217 | if (retval) |
| 3108 | goto end_rename; | 3218 | goto end_rename; |
| 3109 | } else { | 3219 | } else { |
| 3110 | BUFFER_TRACE(new.bh, "get write access"); | 3220 | retval = ext4_setent(handle, &new, |
| 3111 | retval = ext4_journal_get_write_access(handle, new.bh); | 3221 | old.inode->i_ino, old.de->file_type); |
| 3112 | if (retval) | 3222 | if (retval) |
| 3113 | goto end_rename; | 3223 | goto end_rename; |
| 3114 | new.de->inode = cpu_to_le32(old.inode->i_ino); | ||
| 3115 | if (EXT4_HAS_INCOMPAT_FEATURE(new.dir->i_sb, | ||
| 3116 | EXT4_FEATURE_INCOMPAT_FILETYPE)) | ||
| 3117 | new.de->file_type = old.de->file_type; | ||
| 3118 | new.dir->i_version++; | ||
| 3119 | new.dir->i_ctime = new.dir->i_mtime = | ||
| 3120 | ext4_current_time(new.dir); | ||
| 3121 | ext4_mark_inode_dirty(handle, new.dir); | ||
| 3122 | BUFFER_TRACE(new.bh, "call ext4_handle_dirty_metadata"); | ||
| 3123 | if (!new.inlined) { | ||
| 3124 | retval = ext4_handle_dirty_dirent_node(handle, | ||
| 3125 | new.dir, new.bh); | ||
| 3126 | if (unlikely(retval)) { | ||
| 3127 | ext4_std_error(new.dir->i_sb, retval); | ||
| 3128 | goto end_rename; | ||
| 3129 | } | ||
| 3130 | } | ||
| 3131 | brelse(new.bh); | ||
| 3132 | new.bh = NULL; | ||
| 3133 | } | 3224 | } |
| 3134 | 3225 | ||
| 3135 | /* | 3226 | /* |
| @@ -3142,31 +3233,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 3142 | /* | 3233 | /* |
| 3143 | * ok, that's it | 3234 | * ok, that's it |
| 3144 | */ | 3235 | */ |
| 3145 | if (le32_to_cpu(old.de->inode) != old.inode->i_ino || | 3236 | ext4_rename_delete(handle, &old); |
| 3146 | old.de->name_len != old.dentry->d_name.len || | ||
| 3147 | strncmp(old.de->name, old.dentry->d_name.name, old.de->name_len) || | ||
| 3148 | (retval = ext4_delete_entry(handle, old.dir, | ||
| 3149 | old.de, old.bh)) == -ENOENT) { | ||
| 3150 | /* old.de could have moved from under us during htree split, so | ||
| 3151 | * make sure that we are deleting the right entry. We might | ||
| 3152 | * also be pointing to a stale entry in the unused part of | ||
| 3153 | * old.bh so just checking inum and the name isn't enough. */ | ||
| 3154 | struct buffer_head *old_bh2; | ||
