diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-10-23 18:14:37 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-10-23 18:14:37 -0400 |
commit | cd808deced431b66b5fa4e5c193cb7ec0059eaff (patch) | |
tree | da59bbb6e074330931f3a1c02b430371cba4aca1 /fs/ext4/namei.c | |
parent | 0d7a855526dd672e114aff2ac22b60fc6f155b08 (diff) |
ext4: support RENAME_WHITEOUT
Add whiteout support to ext4_rename(). A whiteout inode (chrdev/0,0) is
created before the rename takes place. The whiteout inode is added to the
old entry instead of deleting it.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/ext4/namei.c')
-rw-r--r-- | fs/ext4/namei.c | 95 |
1 files changed, 78 insertions, 17 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 603e4ebbd0ac..aba86e8ef1ef 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -3190,6 +3190,39 @@ static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent) | |||
3190 | } | 3190 | } |
3191 | } | 3191 | } |
3192 | 3192 | ||
3193 | static struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent, | ||
3194 | int credits, handle_t **h) | ||
3195 | { | ||
3196 | struct inode *wh; | ||
3197 | handle_t *handle; | ||
3198 | int retries = 0; | ||
3199 | |||
3200 | /* | ||
3201 | * for inode block, sb block, group summaries, | ||
3202 | * and inode bitmap | ||
3203 | */ | ||
3204 | credits += (EXT4_MAXQUOTAS_TRANS_BLOCKS(ent->dir->i_sb) + | ||
3205 | EXT4_XATTR_TRANS_BLOCKS + 4); | ||
3206 | retry: | ||
3207 | wh = ext4_new_inode_start_handle(ent->dir, S_IFCHR | WHITEOUT_MODE, | ||
3208 | &ent->dentry->d_name, 0, NULL, | ||
3209 | EXT4_HT_DIR, credits); | ||
3210 | |||
3211 | handle = ext4_journal_current_handle(); | ||
3212 | if (IS_ERR(wh)) { | ||
3213 | if (handle) | ||
3214 | ext4_journal_stop(handle); | ||
3215 | if (PTR_ERR(wh) == -ENOSPC && | ||
3216 | ext4_should_retry_alloc(ent->dir->i_sb, &retries)) | ||
3217 | goto retry; | ||
3218 | } else { | ||
3219 | *h = handle; | ||
3220 | init_special_inode(wh, wh->i_mode, WHITEOUT_DEV); | ||
3221 | wh->i_op = &ext4_special_inode_operations; | ||
3222 | } | ||
3223 | return wh; | ||
3224 | } | ||
3225 | |||
3193 | /* | 3226 | /* |
3194 | * Anybody can rename anything with this: the permission checks are left to the | 3227 | * Anybody can rename anything with this: the permission checks are left to the |
3195 | * higher-level routines. | 3228 | * higher-level routines. |
@@ -3199,7 +3232,8 @@ static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent) | |||
3199 | * This comes from rename(const char *oldpath, const char *newpath) | 3232 | * This comes from rename(const char *oldpath, const char *newpath) |
3200 | */ | 3233 | */ |
3201 | static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | 3234 | static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, |
3202 | struct inode *new_dir, struct dentry *new_dentry) | 3235 | struct inode *new_dir, struct dentry *new_dentry, |
3236 | unsigned int flags) | ||
3203 | { | 3237 | { |
3204 | handle_t *handle = NULL; | 3238 | handle_t *handle = NULL; |
3205 | struct ext4_renament old = { | 3239 | struct ext4_renament old = { |
@@ -3214,6 +3248,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3214 | }; | 3248 | }; |
3215 | int force_reread; | 3249 | int force_reread; |
3216 | int retval; | 3250 | int retval; |
3251 | struct inode *whiteout = NULL; | ||
3252 | int credits; | ||
3253 | u8 old_file_type; | ||
3217 | 3254 | ||
3218 | dquot_initialize(old.dir); | 3255 | dquot_initialize(old.dir); |
3219 | dquot_initialize(new.dir); | 3256 | dquot_initialize(new.dir); |
@@ -3252,11 +3289,17 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3252 | if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC)) | 3289 | if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC)) |
3253 | ext4_alloc_da_blocks(old.inode); | 3290 | ext4_alloc_da_blocks(old.inode); |
3254 | 3291 | ||
3255 | handle = ext4_journal_start(old.dir, EXT4_HT_DIR, | 3292 | credits = (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) + |
3256 | (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) + | 3293 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2); |
3257 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2)); | 3294 | if (!(flags & RENAME_WHITEOUT)) { |
3258 | if (IS_ERR(handle)) | 3295 | handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits); |
3259 | return PTR_ERR(handle); | 3296 | if (IS_ERR(handle)) |
3297 | return PTR_ERR(handle); | ||
3298 | } else { | ||
3299 | whiteout = ext4_whiteout_for_rename(&old, credits, &handle); | ||
3300 | if (IS_ERR(whiteout)) | ||
3301 | return PTR_ERR(whiteout); | ||
3302 | } | ||
3260 | 3303 | ||
3261 | if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) | 3304 | if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) |
3262 | ext4_handle_sync(handle); | 3305 | ext4_handle_sync(handle); |
@@ -3284,13 +3327,26 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3284 | */ | 3327 | */ |
3285 | force_reread = (new.dir->i_ino == old.dir->i_ino && | 3328 | force_reread = (new.dir->i_ino == old.dir->i_ino && |
3286 | ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA)); | 3329 | ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA)); |
3330 | |||
3331 | old_file_type = old.de->file_type; | ||
3332 | if (whiteout) { | ||
3333 | /* | ||
3334 | * Do this before adding a new entry, so the old entry is sure | ||
3335 | * to be still pointing to the valid old entry. | ||
3336 | */ | ||
3337 | retval = ext4_setent(handle, &old, whiteout->i_ino, | ||
3338 | EXT4_FT_CHRDEV); | ||
3339 | if (retval) | ||
3340 | goto end_rename; | ||
3341 | ext4_mark_inode_dirty(handle, whiteout); | ||
3342 | } | ||
3287 | if (!new.bh) { | 3343 | if (!new.bh) { |
3288 | retval = ext4_add_entry(handle, new.dentry, old.inode); | 3344 | retval = ext4_add_entry(handle, new.dentry, old.inode); |
3289 | if (retval) | 3345 | if (retval) |
3290 | goto end_rename; | 3346 | goto end_rename; |
3291 | } else { | 3347 | } else { |
3292 | retval = ext4_setent(handle, &new, | 3348 | retval = ext4_setent(handle, &new, |
3293 | old.inode->i_ino, old.de->file_type); | 3349 | old.inode->i_ino, old_file_type); |
3294 | if (retval) | 3350 | if (retval) |
3295 | goto end_rename; | 3351 | goto end_rename; |
3296 | } | 3352 | } |
@@ -3305,10 +3361,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3305 | old.inode->i_ctime = ext4_current_time(old.inode); | 3361 | old.inode->i_ctime = ext4_current_time(old.inode); |
3306 | ext4_mark_inode_dirty(handle, old.inode); | 3362 | ext4_mark_inode_dirty(handle, old.inode); |
3307 | 3363 | ||
3308 | /* | 3364 | if (!whiteout) { |
3309 | * ok, that's it | 3365 | /* |
3310 | */ | 3366 | * ok, that's it |
3311 | ext4_rename_delete(handle, &old, force_reread); | 3367 | */ |
3368 | ext4_rename_delete(handle, &old, force_reread); | ||
3369 | } | ||
3312 | 3370 | ||
3313 | if (new.inode) { | 3371 | if (new.inode) { |
3314 | ext4_dec_count(handle, new.inode); | 3372 | ext4_dec_count(handle, new.inode); |
@@ -3344,6 +3402,12 @@ end_rename: | |||
3344 | brelse(old.dir_bh); | 3402 | brelse(old.dir_bh); |
3345 | brelse(old.bh); | 3403 | brelse(old.bh); |
3346 | brelse(new.bh); | 3404 | brelse(new.bh); |
3405 | if (whiteout) { | ||
3406 | if (retval) | ||
3407 | drop_nlink(whiteout); | ||
3408 | unlock_new_inode(whiteout); | ||
3409 | iput(whiteout); | ||
3410 | } | ||
3347 | if (handle) | 3411 | if (handle) |
3348 | ext4_journal_stop(handle); | 3412 | ext4_journal_stop(handle); |
3349 | return retval; | 3413 | return retval; |
@@ -3476,18 +3540,15 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry, | |||
3476 | struct inode *new_dir, struct dentry *new_dentry, | 3540 | struct inode *new_dir, struct dentry *new_dentry, |
3477 | unsigned int flags) | 3541 | unsigned int flags) |
3478 | { | 3542 | { |
3479 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) | 3543 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) |
3480 | return -EINVAL; | 3544 | return -EINVAL; |
3481 | 3545 | ||
3482 | if (flags & RENAME_EXCHANGE) { | 3546 | if (flags & RENAME_EXCHANGE) { |
3483 | return ext4_cross_rename(old_dir, old_dentry, | 3547 | return ext4_cross_rename(old_dir, old_dentry, |
3484 | new_dir, new_dentry); | 3548 | new_dir, new_dentry); |
3485 | } | 3549 | } |
3486 | /* | 3550 | |
3487 | * Existence checking was done by the VFS, otherwise "RENAME_NOREPLACE" | 3551 | return ext4_rename(old_dir, old_dentry, new_dir, new_dentry, flags); |
3488 | * is equivalent to regular rename. | ||
3489 | */ | ||
3490 | return ext4_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
3491 | } | 3552 | } |
3492 | 3553 | ||
3493 | /* | 3554 | /* |