Use canonical path when copying files from containers (#41190)
* Use canonical path when copying files from containers * Cleanup file
Blue committed
Jul 28, 2026 at 20:56 UTC
8ff31106efa98786fd908d6175940b151aa2999e
2 files changed
+35
-1
src/windows/wslc/tasks/ContainerTasks.cpp
+7
-1
@@ -398,7 +398,13 @@ void ContainerCp(CLIExecutionContext& context)
398
auto [containerId, srcPath] = parseContainerPath(source);
399
THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::WSLCCLI_CpInvalidSourceError(), containerId.empty() || srcPath.empty());
400
401
- auto absTarget = std::filesystem::absolute(target);
401
+ // Resolve any symlinks in the target path since tar.exe refuses to extract through a symlink.
402
+ std::error_code canonicalError;
403
+ auto absTarget = std::filesystem::weakly_canonical(std::filesystem::absolute(target), canonicalError);
404
+ if (canonicalError)
405
+ {
406
+ absTarget = std::filesystem::absolute(target); // Fall back to absolute if canonicalization fails.
407
+ }
408
409
// Determine if target is a directory or a file destination.
410
// Treat as directory if: ends with separator, or already exists as a directory.
test/windows/wslc/e2e/WSLCE2EContainerCpTests.cpp
+28
@@ -432,6 +432,34 @@ class WSLCE2EContainerCpTests
432
VERIFY_IS_TRUE(std::filesystem::exists(extractedFile));
433
}
434
435
+ WSLC_TEST_METHOD(WSLCE2E_Container_Cp_ContainerToLocal_ThroughSymlink)
436
+ {
437
+ auto runResult =
438
+ RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
439
+ runResult.Verify({.Stderr = L"", .ExitCode = 0});
440
+
441
+ auto execResult = RunWslc(std::format(L"container exec {} touch /tmp/symfile.txt", WslcContainerName));
442
+ execResult.Verify({.ExitCode = 0});
443
+
444
+ auto linkDir = std::filesystem::current_path() / L"wslc-cp-symlink-link";
445
+ auto cleanup = wil::scope_exit([&] {
446
+ std::error_code ec;
447
+ std::filesystem::remove(linkDir, ec);
448
+ std::filesystem::remove(L"symfile.txt", ec);
449
+ });
450
+
451
+ // Creating a symlink requires Administrator privileges, which the E2E tests already run with.
452
+ THROW_LAST_ERROR_IF(!CreateSymbolicLinkW(linkDir.c_str(), std::filesystem::current_path().c_str(), SYMBOLIC_LINK_FLAG_DIRECTORY));
453
+
454
+ // Copy from the container into the symlink.
455
+ const auto cpResult = RunWslc(std::format(L"container cp {}:/tmp/symfile.txt {}", WslcContainerName, linkDir.wstring()));
456
+ cpResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
457
+
458
+ // The file must land in the real directory (and therefore be visible through the symlink).
459
+ VERIFY_IS_TRUE(std::filesystem::exists(std::filesystem::current_path() / L"symfile.txt"));
460
+ VERIFY_IS_TRUE(std::filesystem::exists(linkDir / L"symfile.txt"));
461
+ }
462
+
463
WSLC_TEST_METHOD(WSLCE2E_Container_Cp_ContainerToLocal_FileDestination)
464
{
465
// When the local target doesn't end with a separator and isn't an existing directory,