aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikhail Efremov <sem@altlinux.org>2014-09-24 14:14:33 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-09-27 15:59:39 -0400
commitd2fa4a8476b911782f7e5167db18770222ac40c3 (patch)
treedc69bf88ccf13f002c2cf9e02eb9146408eb66ad
parenta28ddb87cdddb0db57466ba7f59f831002f4340c (diff)
vfs: Don't exchange "short" filenames unconditionally.
Only exchange source and destination filenames if flags contain RENAME_EXCHANGE. In case if executable file was running and replaced by other file /proc/PID/exe should still show correct file name, not the old name of the file by which it was replaced. The scenario when this bug manifests itself was like this: * ALT Linux uses rpm and start-stop-daemon; * during a package upgrade rpm creates a temporary file for an executable to rename it upon successful unpacking; * start-stop-daemon is run subsequently and it obtains the (nonexistant) temporary filename via /proc/PID/exe thus failing to identify the running process. Note that "long" filenames (> DNAiME_INLINE_LEN) are still exchanged without RENAME_EXCHANGE and this behaviour exists long enough (should be fixed too apparently). So this patch is just an interim workaround that restores behavior for "short" names as it was before changes introduced by commit da1ce0670c14 ("vfs: add cross-rename"). See https://lkml.org/lkml/2014/9/7/6 for details. AV: the comments about being more careful with ->d_name.hash than with ->d_name.name are from back in 2.3.40s; they became obsolete by 2.3.60s, when we started to unhash the target instead of swapping hash chain positions followed by d_delete() as we used to do when dcache was first introduced. Acked-by: Miklos Szeredi <mszeredi@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: da1ce0670c14 "vfs: add cross-rename" Signed-off-by: Mikhail Efremov <sem@altlinux.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/dcache.c27
1 files changed, 18 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 7599d35b4ae5..cb25a1a5e307 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2372,7 +2372,8 @@ void dentry_update_name_case(struct dentry *dentry, struct qstr *name)
2372} 2372}
2373EXPORT_SYMBOL(dentry_update_name_case); 2373EXPORT_SYMBOL(dentry_update_name_case);
2374 2374
2375static void switch_names(struct dentry *dentry, struct dentry *target) 2375static void switch_names(struct dentry *dentry, struct dentry *target,
2376 bool exchange)
2376{ 2377{
2377 if (dname_external(target)) { 2378 if (dname_external(target)) {
2378 if (dname_external(dentry)) { 2379 if (dname_external(dentry)) {
@@ -2406,6 +2407,12 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
2406 */ 2407 */
2407 unsigned int i; 2408 unsigned int i;
2408 BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long))); 2409 BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
2410 if (!exchange) {
2411 memcpy(dentry->d_iname, target->d_name.name,
2412 target->d_name.len + 1);
2413 dentry->d_name.hash_len = target->d_name.hash_len;
2414 return;
2415 }
2409 for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) { 2416 for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
2410 swap(((long *) &dentry->d_iname)[i], 2417 swap(((long *) &dentry->d_iname)[i],
2411 ((long *) &target->d_iname)[i]); 2418 ((long *) &target->d_iname)[i]);
@@ -2456,12 +2463,15 @@ static void dentry_unlock_for_move(struct dentry *dentry, struct dentry *target)
2456 * When switching names, the actual string doesn't strictly have to 2463 * When switching names, the actual string doesn't strictly have to
2457 * be preserved in the target - because we're dropping the target 2464 * be preserved in the target - because we're dropping the target
2458 * anyway. As such, we can just do a simple memcpy() to copy over 2465 * anyway. As such, we can just do a simple memcpy() to copy over
2459 * the new name before we switch. 2466 * the new name before we switch, unless we are going to rehash
2460 * 2467 * it. Note that if we *do* unhash the target, we are not allowed
2461 * Note that we have to be a lot more careful about getting the hash 2468 * to rehash it without giving it a new name/hash key - whether
2462 * switched - we have to switch the hash value properly even if it 2469 * we swap or overwrite the names here, resulting name won't match
2463 * then no longer matches the actual (corrupted) string of the target. 2470 * the reality in filesystem; it's only there for d_path() purposes.
2464 * The hash value has to match the hash queue that the dentry is on.. 2471 * Note that all of this is happening under rename_lock, so the
2472 * any hash lookup seeing it in the middle of manipulations will
2473 * be discarded anyway. So we do not care what happens to the hash
2474 * key in that case.
2465 */ 2475 */
2466/* 2476/*
2467 * __d_move - move a dentry 2477 * __d_move - move a dentry
@@ -2507,9 +2517,8 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
2507 d_hash(dentry->d_parent, dentry->d_name.hash)); 2517 d_hash(dentry->d_parent, dentry->d_name.hash));
2508 } 2518 }
2509 2519
2510
2511 /* Switch the names.. */ 2520 /* Switch the names.. */
2512 switch_names(dentry, target); 2521 switch_names(dentry, target, exchange);
2513 2522
2514 /* ... and switch them in the tree */ 2523 /* ... and switch them in the tree */
2515 if (IS_ROOT(dentry)) { 2524 if (IS_ROOT(dentry)) {