diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-01 11:08:43 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-01 11:08:43 -0400 |
commit | da1ce0670c14d8380e423a3239e562a1dc15fa9e (patch) | |
tree | 146ea4ac0fbf5550db8e65a59ddc7c668b68db76 /fs/dcache.c | |
parent | 4fd699ae3fbca2ac760137e1d26f98a105f59f05 (diff) |
vfs: add cross-rename
If flags contain RENAME_EXCHANGE then exchange source and destination files.
There's no restriction on the type of the files; e.g. a directory can be
exchanged with a symlink.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 50 |
1 files changed, 41 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index ca02c13a84aa..66cba5a8a346 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -2483,12 +2483,14 @@ static void switch_names(struct dentry *dentry, struct dentry *target) | |||
2483 | dentry->d_name.name = dentry->d_iname; | 2483 | dentry->d_name.name = dentry->d_iname; |
2484 | } else { | 2484 | } else { |
2485 | /* | 2485 | /* |
2486 | * Both are internal. Just copy target to dentry | 2486 | * Both are internal. |
2487 | */ | 2487 | */ |
2488 | memcpy(dentry->d_iname, target->d_name.name, | 2488 | unsigned int i; |
2489 | target->d_name.len + 1); | 2489 | BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long))); |
2490 | dentry->d_name.len = target->d_name.len; | 2490 | for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) { |
2491 | return; | 2491 | swap(((long *) &dentry->d_iname)[i], |
2492 | ((long *) &target->d_iname)[i]); | ||
2493 | } | ||
2492 | } | 2494 | } |
2493 | } | 2495 | } |
2494 | swap(dentry->d_name.len, target->d_name.len); | 2496 | swap(dentry->d_name.len, target->d_name.len); |
@@ -2545,13 +2547,15 @@ static void dentry_unlock_parents_for_move(struct dentry *dentry, | |||
2545 | * __d_move - move a dentry | 2547 | * __d_move - move a dentry |
2546 | * @dentry: entry to move | 2548 | * @dentry: entry to move |
2547 | * @target: new dentry | 2549 | * @target: new dentry |
2550 | * @exchange: exchange the two dentries | ||
2548 | * | 2551 | * |
2549 | * Update the dcache to reflect the move of a file name. Negative | 2552 | * Update the dcache to reflect the move of a file name. Negative |
2550 | * dcache entries should not be moved in this way. Caller must hold | 2553 | * dcache entries should not be moved in this way. Caller must hold |
2551 | * rename_lock, the i_mutex of the source and target directories, | 2554 | * rename_lock, the i_mutex of the source and target directories, |
2552 | * and the sb->s_vfs_rename_mutex if they differ. See lock_rename(). | 2555 | * and the sb->s_vfs_rename_mutex if they differ. See lock_rename(). |
2553 | */ | 2556 | */ |
2554 | static void __d_move(struct dentry * dentry, struct dentry * target) | 2557 | static void __d_move(struct dentry *dentry, struct dentry *target, |
2558 | bool exchange) | ||
2555 | { | 2559 | { |
2556 | if (!dentry->d_inode) | 2560 | if (!dentry->d_inode) |
2557 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); | 2561 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); |
@@ -2573,8 +2577,15 @@ static void __d_move(struct dentry * dentry, struct dentry * target) | |||
2573 | __d_drop(dentry); | 2577 | __d_drop(dentry); |
2574 | __d_rehash(dentry, d_hash(target->d_parent, target->d_name.hash)); | 2578 | __d_rehash(dentry, d_hash(target->d_parent, target->d_name.hash)); |
2575 | 2579 | ||
2576 | /* Unhash the target: dput() will then get rid of it */ | 2580 | /* |
2581 | * Unhash the target (d_delete() is not usable here). If exchanging | ||
2582 | * the two dentries, then rehash onto the other's hash queue. | ||
2583 | */ | ||
2577 | __d_drop(target); | 2584 | __d_drop(target); |
2585 | if (exchange) { | ||
2586 | __d_rehash(target, | ||
2587 | d_hash(dentry->d_parent, dentry->d_name.hash)); | ||
2588 | } | ||
2578 | 2589 | ||
2579 | list_del(&dentry->d_u.d_child); | 2590 | list_del(&dentry->d_u.d_child); |
2580 | list_del(&target->d_u.d_child); | 2591 | list_del(&target->d_u.d_child); |
@@ -2601,6 +2612,8 @@ static void __d_move(struct dentry * dentry, struct dentry * target) | |||
2601 | write_seqcount_end(&dentry->d_seq); | 2612 | write_seqcount_end(&dentry->d_seq); |
2602 | 2613 | ||
2603 | dentry_unlock_parents_for_move(dentry, target); | 2614 | dentry_unlock_parents_for_move(dentry, target); |
2615 | if (exchange) | ||
2616 | fsnotify_d_move(target); | ||
2604 | spin_unlock(&target->d_lock); | 2617 | spin_unlock(&target->d_lock); |
2605 | fsnotify_d_move(dentry); | 2618 | fsnotify_d_move(dentry); |
2606 | spin_unlock(&dentry->d_lock); | 2619 | spin_unlock(&dentry->d_lock); |
@@ -2618,11 +2631,30 @@ static void __d_move(struct dentry * dentry, struct dentry * target) | |||
2618 | void d_move(struct dentry *dentry, struct dentry *target) | 2631 | void d_move(struct dentry *dentry, struct dentry *target) |
2619 | { | 2632 | { |
2620 | write_seqlock(&rename_lock); | 2633 | write_seqlock(&rename_lock); |
2621 | __d_move(dentry, target); | 2634 | __d_move(dentry, target, false); |
2622 | write_sequnlock(&rename_lock); | 2635 | write_sequnlock(&rename_lock); |
2623 | } | 2636 | } |
2624 | EXPORT_SYMBOL(d_move); | 2637 | EXPORT_SYMBOL(d_move); |
2625 | 2638 | ||
2639 | /* | ||
2640 | * d_exchange - exchange two dentries | ||
2641 | * @dentry1: first dentry | ||
2642 | * @dentry2: second dentry | ||
2643 | */ | ||
2644 | void d_exchange(struct dentry *dentry1, struct dentry *dentry2) | ||
2645 | { | ||
2646 | write_seqlock(&rename_lock); | ||
2647 | |||
2648 | WARN_ON(!dentry1->d_inode); | ||
2649 | WARN_ON(!dentry2->d_inode); | ||
2650 | WARN_ON(IS_ROOT(dentry1)); | ||
2651 | WARN_ON(IS_ROOT(dentry2)); | ||
2652 | |||
2653 | __d_move(dentry1, dentry2, true); | ||
2654 | |||
2655 | write_sequnlock(&rename_lock); | ||
2656 | } | ||
2657 | |||
2626 | /** | 2658 | /** |
2627 | * d_ancestor - search for an ancestor | 2659 | * d_ancestor - search for an ancestor |
2628 | * @p1: ancestor dentry | 2660 | * @p1: ancestor dentry |
@@ -2670,7 +2702,7 @@ static struct dentry *__d_unalias(struct inode *inode, | |||
2670 | m2 = &alias->d_parent->d_inode->i_mutex; | 2702 | m2 = &alias->d_parent->d_inode->i_mutex; |
2671 | out_unalias: | 2703 | out_unalias: |
2672 | if (likely(!d_mountpoint(alias))) { | 2704 | if (likely(!d_mountpoint(alias))) { |
2673 | __d_move(alias, dentry); | 2705 | __d_move(alias, dentry, false); |
2674 | ret = alias; | 2706 | ret = alias; |
2675 | } | 2707 | } |
2676 | out_err: | 2708 | out_err: |