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; | ||
3155 | struct ext4_dir_entry_2 *old_de2; | ||
3156 | |||
3157 | old_bh2 = ext4_find_entry(old.dir, &old.dentry->d_name, | ||
3158 | &old_de2, NULL); | ||
3159 | if (old_bh2) { | ||
3160 | retval = ext4_delete_entry(handle, old.dir, | ||
3161 | old_de2, old_bh2); | ||
3162 | brelse(old_bh2); | ||
3163 | } | ||
3164 | } | ||
3165 | if (retval) { | ||
3166 | ext4_warning(old.dir->i_sb, | ||
3167 | "Deleting old file (%lu), %d, error=%d", | ||
3168 | old.dir->i_ino, old.dir->i_nlink, retval); | ||
3169 | } | ||
3170 | 3237 | ||
3171 | if (new.inode) { | 3238 | if (new.inode) { |
3172 | ext4_dec_count(handle, new.inode); | 3239 | ext4_dec_count(handle, new.inode); |
@@ -3175,24 +3242,10 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3175 | old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir); | 3242 | old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir); |
3176 | ext4_update_dx_flag(old.dir); | 3243 | ext4_update_dx_flag(old.dir); |
3177 | if (old.dir_bh) { | 3244 | if (old.dir_bh) { |
3178 | old.parent_de->inode = cpu_to_le32(new.dir->i_ino); | 3245 | retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino); |
3179 | BUFFER_TRACE(old.dir_bh, "call ext4_handle_dirty_metadata"); | 3246 | if (retval) |
3180 | if (!old.dir_inlined) { | ||
3181 | if (is_dx(old.inode)) { | ||
3182 | retval = ext4_handle_dirty_dx_node(handle, | ||
3183 | old.inode, | ||
3184 | old.dir_bh); | ||
3185 | } else { | ||
3186 | retval = ext4_handle_dirty_dirent_node(handle, | ||
3187 | old.inode, old.dir_bh); | ||
3188 | } | ||
3189 | } else { | ||
3190 | retval = ext4_mark_inode_dirty(handle, old.inode); | ||
3191 | } | ||
3192 | if (retval) { | ||
3193 | ext4_std_error(old.dir->i_sb, retval); | ||
3194 | goto end_rename; | 3247 | goto end_rename; |
3195 | } | 3248 | |
3196 | ext4_dec_count(handle, old.dir); | 3249 | ext4_dec_count(handle, old.dir); |
3197 | if (new.inode) { | 3250 | if (new.inode) { |
3198 | /* checked empty_dir above, can't have another parent, | 3251 | /* checked empty_dir above, can't have another parent, |