aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-04-01 11:08:44 -0400
committerMiklos Szeredi <mszeredi@suse.cz>2014-04-01 11:08:44 -0400
commitbd42998a6bcb9b1708dac9ca9876e3d304c16f3d (patch)
tree4e76a14bd33ad0499ed75e2aa13cc4516539707b /fs/ext4
parentbd1af145b99311242673b32dff4599ce614352be (diff)
ext4: add cross rename support
Implement RENAME_EXCHANGE flag in renameat2 syscall. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Reviewed-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/namei.c139
1 files changed, 138 insertions, 1 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 75f1bde43dcc..1cb84f78909e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3004,6 +3004,8 @@ struct ext4_renament {
3004 struct inode *dir; 3004 struct inode *dir;
3005 struct dentry *dentry; 3005 struct dentry *dentry;
3006 struct inode *inode; 3006 struct inode *inode;
3007 bool is_dir;
3008 int dir_nlink_delta;
3007 3009
3008 /* entry for "dentry" */ 3010 /* entry for "dentry" */
3009 struct buffer_head *bh; 3011 struct buffer_head *bh;
@@ -3135,6 +3137,17 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
3135 } 3137 }
3136} 3138}
3137 3139
3140static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
3141{
3142 if (ent->dir_nlink_delta) {
3143 if (ent->dir_nlink_delta == -1)
3144 ext4_dec_count(handle, ent->dir);
3145 else
3146 ext4_inc_count(handle, ent->dir);
3147 ext4_mark_inode_dirty(handle, ent->dir);
3148 }
3149}
3150
3138/* 3151/*
3139 * Anybody can rename anything with this: the permission checks are left to the 3152 * Anybody can rename anything with this: the permission checks are left to the
3140 * higher-level routines. 3153 * higher-level routines.
@@ -3274,13 +3287,137 @@ end_rename:
3274 return retval; 3287 return retval;
3275} 3288}
3276 3289
3290static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
3291 struct inode *new_dir, struct dentry *new_dentry)
3292{
3293 handle_t *handle = NULL;
3294 struct ext4_renament old = {
3295 .dir = old_dir,
3296 .dentry = old_dentry,
3297 .inode = old_dentry->d_inode,
3298 };
3299 struct ext4_renament new = {
3300 .dir = new_dir,
3301 .dentry = new_dentry,
3302 .inode = new_dentry->d_inode,
3303 };
3304 u8 new_file_type;
3305 int retval;
3306
3307 dquot_initialize(old.dir);
3308 dquot_initialize(new.dir);
3309
3310 old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
3311 &old.de, &old.inlined);
3312 /*
3313 * Check for inode number is _not_ due to possible IO errors.
3314 * We might rmdir the source, keep it as pwd of some process
3315 * and merrily kill the link to whatever was created under the
3316 * same name. Goodbye sticky bit ;-<
3317 */
3318 retval = -ENOENT;
3319 if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
3320 goto end_rename;
3321
3322 new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
3323 &new.de, &new.inlined);
3324
3325 /* RENAME_EXCHANGE case: old *and* new must both exist */
3326 if (!new.bh || le32_to_cpu(new.de->inode) != new.inode->i_ino)
3327 goto end_rename;
3328
3329 handle = ext4_journal_start(old.dir, EXT4_HT_DIR,
3330 (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
3331 2 * EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
3332 if (IS_ERR(handle))
3333 return PTR_ERR(handle);
3334
3335 if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
3336 ext4_handle_sync(handle);
3337
3338 if (S_ISDIR(old.inode->i_mode)) {
3339 old.is_dir = true;
3340 retval = ext4_rename_dir_prepare(handle, &old);
3341 if (retval)
3342 goto end_rename;
3343 }
3344 if (S_ISDIR(new.inode->i_mode)) {
3345 new.is_dir = true;
3346 retval = ext4_rename_dir_prepare(handle, &new);
3347 if (retval)
3348 goto end_rename;
3349 }
3350
3351 /*
3352 * Other than the special case of overwriting a directory, parents'
3353 * nlink only needs to be modified if this is a cross directory rename.
3354 */
3355 if (old.dir != new.dir && old.is_dir != new.is_dir) {
3356 old.dir_nlink_delta = old.is_dir ? -1 : 1;
3357 new.dir_nlink_delta = -old.dir_nlink_delta;
3358 retval = -EMLINK;
3359 if ((old.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(old.dir)) ||
3360 (new.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(new.dir)))
3361 goto end_rename;
3362 }
3363
3364 new_file_type = new.de->file_type;
3365 retval = ext4_setent(handle, &new, old.inode->i_ino, old.de->file_type);
3366 if (retval)
3367 goto end_rename;
3368
3369 retval = ext4_setent(handle, &old, new.inode->i_ino, new_file_type);
3370 if (retval)
3371 goto end_rename;
3372
3373 /*
3374 * Like most other Unix systems, set the ctime for inodes on a
3375 * rename.
3376 */
3377 old.inode->i_ctime = ext4_current_time(old.inode);
3378 new.inode->i_ctime = ext4_current_time(new.inode);
3379 ext4_mark_inode_dirty(handle, old.inode);
3380 ext4_mark_inode_dirty(handle, new.inode);
3381
3382 if (old.dir_bh) {
3383 retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
3384 if (retval)
3385 goto end_rename;
3386 }
3387 if (new.dir_bh) {
3388 retval = ext4_rename_dir_finish(handle, &new, old.dir->i_ino);
3389 if (retval)
3390 goto end_rename;
3391 }
3392 ext4_update_dir_count(handle, &old);
3393 ext4_update_dir_count(handle, &new);
3394 retval = 0;
3395
3396end_rename:
3397 brelse(old.dir_bh);
3398 brelse(new.dir_bh);
3399 brelse(old.bh);
3400 brelse(new.bh);
3401 if (handle)
3402 ext4_journal_stop(handle);
3403 return retval;
3404}
3405
3277static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry, 3406static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
3278 struct inode *new_dir, struct dentry *new_dentry, 3407 struct inode *new_dir, struct dentry *new_dentry,
3279 unsigned int flags) 3408 unsigned int flags)
3280{ 3409{
3281 if (flags & ~RENAME_NOREPLACE) 3410 if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
3282 return -EINVAL; 3411 return -EINVAL;
3283 3412
3413 if (flags & RENAME_EXCHANGE) {
3414 return ext4_cross_rename(old_dir, old_dentry,
3415 new_dir, new_dentry);
3416 }
3417 /*
3418 * Existence checking was done by the VFS, otherwise "RENAME_NOREPLACE"
3419 * is equivalent to regular rename.
3420 */
3284 return ext4_rename(old_dir, old_dentry, new_dir, new_dentry); 3421 return ext4_rename(old_dir, old_dentry, new_dir, new_dentry);
3285} 3422}
3286 3423