From ba81b75d6f654dd3a8190fcfdb76f2ef9ee02aba Mon Sep 17 00:00:00 2001 From: kvega005 Date: Thu, 5 Mar 2026 11:09:04 -0800 Subject: [PATCH 1/7] Add hourly upstream sync workflow for feature/wsl-for-apps --- .github/workflows/sync-upstream.yml | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/sync-upstream.yml diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml new file mode 100644 index 0000000000..fb0c13bfca --- /dev/null +++ b/.github/workflows/sync-upstream.yml @@ -0,0 +1,49 @@ +name: Sync fork with upstream + +on: + schedule: + # Hourly during Pacific business hours (8 AM - 6 PM PT = 16:00 - 02:00 UTC next day) + - cron: '0 16-23 * * 1-5' # Mon-Fri 16:00-23:00 UTC (8 AM - 3 PM PT) + - cron: '0 0-1 * * 2-6' # Tue-Sat 00:00-01:00 UTC (4 PM - 5 PM PT prev day) + workflow_dispatch: # Allow manual trigger + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout fork + uses: actions/checkout@v4 + with: + ref: feature/wsl-for-apps + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Add upstream remote + run: git remote add upstream https://github.com/Microsoft/WSL.git + + - name: Fetch upstream + run: git fetch upstream feature/wsl-for-apps + + - name: Check if update needed + id: check + run: | + LOCAL=$(git rev-parse HEAD) + UPSTREAM=$(git rev-parse upstream/feature/wsl-for-apps) + echo "local=$LOCAL" >> "$GITHUB_OUTPUT" + echo "upstream=$UPSTREAM" >> "$GITHUB_OUTPUT" + if [ "$LOCAL" = "$UPSTREAM" ]; then + echo "needs_update=false" >> "$GITHUB_OUTPUT" + echo "Already up to date." + else + echo "needs_update=true" >> "$GITHUB_OUTPUT" + echo "Update needed: $LOCAL -> $UPSTREAM" + fi + + - name: Fast-forward merge + if: steps.check.outputs.needs_update == 'true' + run: | + git merge --ff-only upstream/feature/wsl-for-apps + + - name: Push changes + if: steps.check.outputs.needs_update == 'true' + run: git push origin feature/wsl-for-apps From 50f9ce0c5c8b5b200b670fd3c707cfd1eb7f3f15 Mon Sep 17 00:00:00 2001 From: kvega005 Date: Thu, 5 Mar 2026 11:19:36 -0800 Subject: [PATCH 2/7] Remove upstream sync workflow --- .github/workflows/sync-upstream.yml | 49 ----------------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/sync-upstream.yml diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml deleted file mode 100644 index fb0c13bfca..0000000000 --- a/.github/workflows/sync-upstream.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Sync fork with upstream - -on: - schedule: - # Hourly during Pacific business hours (8 AM - 6 PM PT = 16:00 - 02:00 UTC next day) - - cron: '0 16-23 * * 1-5' # Mon-Fri 16:00-23:00 UTC (8 AM - 3 PM PT) - - cron: '0 0-1 * * 2-6' # Tue-Sat 00:00-01:00 UTC (4 PM - 5 PM PT prev day) - workflow_dispatch: # Allow manual trigger - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Checkout fork - uses: actions/checkout@v4 - with: - ref: feature/wsl-for-apps - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Add upstream remote - run: git remote add upstream https://github.com/Microsoft/WSL.git - - - name: Fetch upstream - run: git fetch upstream feature/wsl-for-apps - - - name: Check if update needed - id: check - run: | - LOCAL=$(git rev-parse HEAD) - UPSTREAM=$(git rev-parse upstream/feature/wsl-for-apps) - echo "local=$LOCAL" >> "$GITHUB_OUTPUT" - echo "upstream=$UPSTREAM" >> "$GITHUB_OUTPUT" - if [ "$LOCAL" = "$UPSTREAM" ]; then - echo "needs_update=false" >> "$GITHUB_OUTPUT" - echo "Already up to date." - else - echo "needs_update=true" >> "$GITHUB_OUTPUT" - echo "Update needed: $LOCAL -> $UPSTREAM" - fi - - - name: Fast-forward merge - if: steps.check.outputs.needs_update == 'true' - run: | - git merge --ff-only upstream/feature/wsl-for-apps - - - name: Push changes - if: steps.check.outputs.needs_update == 'true' - run: git push origin feature/wsl-for-apps From eb224c9b15c6a47b16466add7f59a8b8da8e9a7e Mon Sep 17 00:00:00 2001 From: kvega005 Date: Thu, 18 Jun 2026 17:14:15 -0700 Subject: [PATCH 3/7] Delete lost and found --- localization/strings/en-US/Resources.resw | 4 +++ src/windows/wslcsession/WSLCVhdVolume.cpp | 34 +++++++++++++++++++ .../wslcsession/WSLCVirtualMachine.cpp | 34 +++++++++++++++++++ src/windows/wslcsession/WSLCVirtualMachine.h | 2 ++ .../wslc/e2e/WSLCE2EContainerRunTests.cpp | 22 ++++++++++++ 5 files changed, 96 insertions(+) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 974bf046c8..3a66ae1f24 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3316,6 +3316,10 @@ On first run, creates the file with all settings commented out at their defaults Failed to unmount volume '{}': {} {FixedPlaceholder="{}"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated + + Volume '{}' has a non-empty lost+found directory. It was left in place and the volume may not be seeded with image contents. + {FixedPlaceholder="{}"}{Locked="lost+found"}Command line arguments, file names and string inserts should not be translated + Failed to stop container '{}' after plugin rejection {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated diff --git a/src/windows/wslcsession/WSLCVhdVolume.cpp b/src/windows/wslcsession/WSLCVhdVolume.cpp index d194bd6970..43fc783698 100644 --- a/src/windows/wslcsession/WSLCVhdVolume.cpp +++ b/src/windows/wslcsession/WSLCVhdVolume.cpp @@ -84,6 +84,31 @@ namespace { return name; } + void RemoveLostFoundDirectory(WSLCVirtualMachine& VirtualMachine, const std::string& VolumeName, const std::string& MountPath) + try + { + constexpr auto c_lostFoundDir = "lost+found"; + const auto entries = VirtualMachine.ListDirectory(MountPath); + + // Only remove lost+found if the disk is empty besides that directory. + if (entries.size() != 1 || entries.front() != c_lostFoundDir) + { + return; + } + + try + { + VirtualMachine.RemoveDirectory(std::format("{}/{}", MountPath, c_lostFoundDir)); + } + catch (...) + { + // rmdir only removes an empty directory, so reaching here means the + // lone lost+found captured recovered data. Leave it and warn. + LOG_CAUGHT_EXCEPTION(); + EMIT_USER_WARNING(Localization::MessageWslcVolumeLostFoundNotEmpty(VolumeName)); + } + } + CATCH_LOG(); } // namespace WSLCVhdVolumeImpl::WSLCVhdVolumeImpl( @@ -151,6 +176,13 @@ std::unique_ptr WSLCVhdVolumeImpl::Create( auto mountCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { VirtualMachine.Unmount(virtualMachinePath.c_str()); }); + // mkfs.ext4 always creates a lost+found directory at the filesystem root, + // which makes a freshly formatted volume look non-empty to Docker and + // suppresses the copy-up that seeds image data on first use. Drop it so + // Docker seeds the volume with the image's contents. No-op when the volume + // already contains data. + RemoveLostFoundDirectory(VirtualMachine, name, virtualMachinePath); + WSLCVolumeMetadata metadata; metadata.Driver = WSLCVhdVolumeDriver; metadata.DriverOpts = DriverOpts; @@ -246,6 +278,8 @@ std::unique_ptr WSLCVhdVolumeImpl::Open( VirtualMachine.Mount(device.c_str(), virtualMachinePath.c_str(), "ext4", "", 0); auto mountCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { VirtualMachine.Unmount(virtualMachinePath.c_str()); }); + RemoveLostFoundDirectory(VirtualMachine, Volume.Name, virtualMachinePath); + lun = attachedLun; attached = true; diff --git a/src/windows/wslcsession/WSLCVirtualMachine.cpp b/src/windows/wslcsession/WSLCVirtualMachine.cpp index 77a09e1ad8..3f1814e7e0 100644 --- a/src/windows/wslcsession/WSLCVirtualMachine.cpp +++ b/src/windows/wslcsession/WSLCVirtualMachine.cpp @@ -536,6 +536,40 @@ void WSLCVirtualMachine::Ext4Format(const std::string& Device, std::optional args = {rmdirPath, Path}; + + ServiceProcessLauncher launcher(rmdirPath, args); + auto result = launcher.Launch(*this).WaitAndCaptureOutput(); + + THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str()); +} + +std::vector WSLCVirtualMachine::ListDirectory(const std::string& Path) +{ + constexpr auto lsPath = "/bin/ls"; + + std::vector args = {lsPath, "-A", Path}; + + ServiceProcessLauncher launcher(lsPath, args); + auto result = launcher.Launch(*this).WaitAndCaptureOutput(); + + THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str()); + + auto stdOut = result.Output.find(1); + if (stdOut == result.Output.end()) + { + return {}; + } + + return wsl::shared::string::Split(stdOut->second, '\n'); +} + void WSLCVirtualMachine::Unmount(_In_ const char* Path) { auto [pid, _, subChannel] = Fork(WSLC_FORK::Thread); diff --git a/src/windows/wslcsession/WSLCVirtualMachine.h b/src/windows/wslcsession/WSLCVirtualMachine.h index f6e438c5c0..2a2cccad2d 100644 --- a/src/windows/wslcsession/WSLCVirtualMachine.h +++ b/src/windows/wslcsession/WSLCVirtualMachine.h @@ -159,6 +159,8 @@ class WSLCVirtualMachine void DetachDisk(_In_ ULONG Lun); void Ext4Format(_In_ const std::string& Device, _In_ std::optional Uid = std::nullopt, _In_ std::optional Gid = std::nullopt); void Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags); + void RemoveDirectory(_In_ const std::string& Path); + std::vector ListDirectory(_In_ const std::string& Path); wil::unique_socket ConnectUnixSocket(_In_ const char* Path); std::tuple Fork(enum WSLC_FORK::ForkType Type); diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp index 23dd502e4c..604c2340e5 100644 --- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp @@ -797,6 +797,28 @@ class WSLCE2EContainerRunTests result.Verify({.Stderr = L"", .ExitCode = 0}); } + WSLC_TEST_METHOD(WSLCE2E_Container_Run_Volume_VhdVolume_SeedsImageData) + { + // A freshly formatted VHD volume must be seeded with the image's content + // on first use, just like a guest volume. mkfs.ext4 creates a lost+found + // directory at the volume root; if it isn't removed, Docker treats the + // volume as non-empty and skips the copy-up that seeds image data. + // Mounting the empty volume over a directory the image is guaranteed to + // populate (/etc) exercises that copy-up. + constexpr int volumeSizeBytes = 64 * 1024 * 1024; + auto result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", volumeSizeBytes, WslcVolumeName)); + result.Verify({.Stderr = L"", .ExitCode = 0}); + + result = RunWslc(std::format(L"container run --rm --volume {}:/etc {} ls -A /etc", WslcVolumeName, DebianImage.NameAndTag())); + result.Verify({.Stderr = L"", .ExitCode = 0}); + + // Image content was seeded into the volume... + VERIFY_IS_TRUE(result.StdoutContainsLine(L"passwd"), L"Image's /etc content should be seeded into the fresh VHD volume"); + + // ...and the ext4 lost+found is gone, so it never blocked copy-up. + VERIFY_IS_FALSE(result.StdoutContainsLine(L"lost+found"), L"lost+found should have been removed from the volume root"); + } + WSLC_TEST_METHOD(WSLCE2E_Container_Run_WithLabel_Success) { auto result = RunWslc(std::format( From 94470dd3372826ea28f6c92d8c5e3a20318954ba Mon Sep 17 00:00:00 2001 From: kvega005 Date: Fri, 19 Jun 2026 11:18:30 -0700 Subject: [PATCH 4/7] Move test to wslc tests --- test/windows/WSLCTests.cpp | 41 +++++++++++++++++++ .../wslc/e2e/WSLCE2EContainerRunTests.cpp | 22 ---------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/test/windows/WSLCTests.cpp b/test/windows/WSLCTests.cpp index b0cde34fb2..1c4c486b81 100644 --- a/test/windows/WSLCTests.cpp +++ b/test/windows/WSLCTests.cpp @@ -4003,6 +4003,47 @@ class WSLCTests VERIFY_IS_FALSE(std::filesystem::exists(volumeVhdPath)); } + WSLC_TEST_METHOD(NamedVolumesVhdSeedsImageData) + { + // A freshly formatted VHD volume must be seeded with the image's content + // on first use, just like a guest volume. mkfs.ext4 creates a lost+found + // directory at the volume root; if it isn't removed, Docker treats the + // volume as non-empty and skips the copy-up that seeds image data. + // Mounting the empty volume over a directory the image is guaranteed to + // populate (/etc) exercises that copy-up. + WSLCDriverOption driverOpts[] = {{"SizeBytes", "1073741824"}}; + const std::string volumeName = "wslc-test-named-volume-vhd-seed"; + + LOG_IF_FAILED(m_defaultSession->DeleteVolume(volumeName.c_str())); + + WSLCVolumeOptions volumeOptions{}; + volumeOptions.Name = volumeName.c_str(); + volumeOptions.Driver = "vhd"; + volumeOptions.DriverOpts = driverOpts; + volumeOptions.DriverOptsCount = ARRAYSIZE(driverOpts); + + WSLCVolumeInformation volInfo{}; + VERIFY_SUCCEEDED(m_defaultSession->CreateVolume(&volumeOptions, &volInfo)); + auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteVolume(volumeName.c_str())); }); + + WSLCContainerLauncher launcher("debian:latest", "wslc-vhd-seed-container", {"/bin/sh", "-c", "ls -A /etc"}); + launcher.AddNamedVolume(volumeName, "/etc", false); + + auto container = launcher.Launch(*m_defaultSession); + auto result = container.GetInitProcess().WaitAndCaptureOutput(); + + VERIFY_ARE_EQUAL(0, result.Code); + + // Image content was seeded into the volume... + VERIFY_IS_TRUE( + result.Output[1].find("passwd") != std::string::npos, + L"Image's /etc content should be seeded into the fresh VHD volume"); + + // ...and the ext4 lost+found is gone, so it never blocked copy-up. + VERIFY_IS_TRUE( + result.Output[1].find("lost+found") == std::string::npos, L"lost+found should have been removed from the volume root"); + } + WSLC_TEST_METHOD(NamedVolumesGuest) { ValidateNamedVolumeContract("guest", nullptr, 0); diff --git a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp index 604c2340e5..23dd502e4c 100644 --- a/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp @@ -797,28 +797,6 @@ class WSLCE2EContainerRunTests result.Verify({.Stderr = L"", .ExitCode = 0}); } - WSLC_TEST_METHOD(WSLCE2E_Container_Run_Volume_VhdVolume_SeedsImageData) - { - // A freshly formatted VHD volume must be seeded with the image's content - // on first use, just like a guest volume. mkfs.ext4 creates a lost+found - // directory at the volume root; if it isn't removed, Docker treats the - // volume as non-empty and skips the copy-up that seeds image data. - // Mounting the empty volume over a directory the image is guaranteed to - // populate (/etc) exercises that copy-up. - constexpr int volumeSizeBytes = 64 * 1024 * 1024; - auto result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", volumeSizeBytes, WslcVolumeName)); - result.Verify({.Stderr = L"", .ExitCode = 0}); - - result = RunWslc(std::format(L"container run --rm --volume {}:/etc {} ls -A /etc", WslcVolumeName, DebianImage.NameAndTag())); - result.Verify({.Stderr = L"", .ExitCode = 0}); - - // Image content was seeded into the volume... - VERIFY_IS_TRUE(result.StdoutContainsLine(L"passwd"), L"Image's /etc content should be seeded into the fresh VHD volume"); - - // ...and the ext4 lost+found is gone, so it never blocked copy-up. - VERIFY_IS_FALSE(result.StdoutContainsLine(L"lost+found"), L"lost+found should have been removed from the volume root"); - } - WSLC_TEST_METHOD(WSLCE2E_Container_Run_WithLabel_Success) { auto result = RunWslc(std::format( From 5f6781ec48b9fefa1473ddcd63f1aca28feb6f06 Mon Sep 17 00:00:00 2001 From: kvega005 Date: Fri, 19 Jun 2026 11:29:28 -0700 Subject: [PATCH 5/7] Fix comment --- localization/strings/en-US/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 3a66ae1f24..0549e18874 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3318,7 +3318,7 @@ On first run, creates the file with all settings commented out at their defaults Volume '{}' has a non-empty lost+found directory. It was left in place and the volume may not be seeded with image contents. - {FixedPlaceholder="{}"}{Locked="lost+found"}Command line arguments, file names and string inserts should not be translated + {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated Failed to stop container '{}' after plugin rejection From 083203f98d5213518eafe8012d6f208d0c2ef01b Mon Sep 17 00:00:00 2001 From: kvega005 Date: Mon, 22 Jun 2026 13:36:44 -0700 Subject: [PATCH 6/7] Address feedback --- localization/strings/en-US/Resources.resw | 4 +- src/linux/init/WSLCInit.cpp | 39 ++++++++++++++++++- src/shared/inc/lxinitshared.h | 31 +++++++++++++++ .../wslcsession/WSLCVirtualMachine.cpp | 19 +++------ 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 0549e18874..149c84bd1c 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3317,8 +3317,8 @@ On first run, creates the file with all settings commented out at their defaults {FixedPlaceholder="{}"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated - Volume '{}' has a non-empty lost+found directory. It was left in place and the volume may not be seeded with image contents. - {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated + Volume '{}' has a non-empty 'lost+found' directory. It was left in place and the volume may not be seeded with image contents. + {FixedPlaceholder="{}"}{Locked="lost+found"}Command line arguments, file names and string inserts should not be translated Failed to stop container '{}' after plugin rejection diff --git a/src/linux/init/WSLCInit.cpp b/src/linux/init/WSLCInit.cpp index 6afecc8102..01f189244c 100644 --- a/src/linux/init/WSLCInit.cpp +++ b/src/linux/init/WSLCInit.cpp @@ -189,6 +189,43 @@ void HandleMessageImpl( Transaction.Send(writer.Span()); } +void HandleMessageImpl( + wsl::shared::SocketChannel& Channel, wsl::shared::Transaction& Transaction, const WSLC_LISTDIR& Message, const gsl::span& Buffer) +{ + wsl::shared::MessageWriter writer; + + try + { + const auto* path = wsl::shared::string::FromMessageBuffer(Buffer); + THROW_ERRNO_IF(EINVAL, path == nullptr); + + wil::unique_dir dir{opendir(path)}; + THROW_LAST_ERROR_IF(!dir); + + std::vector entries; + for (dirent64* entry = readdir64(dir.get()); entry != nullptr; entry = readdir64(dir.get())) + { + const std::string_view name{entry->d_name}; + if (name == "." || name == "..") + { + continue; + } + + entries.emplace_back(name); + } + + auto pointers = wsl::shared::string::StringPointersFromArray(entries, false); + writer.WriteStringArray(writer->EntriesIndex, pointers.data(), pointers.size()); + writer->Result = 0; + } + catch (...) + { + writer->Result = wil::ResultFromCaughtException(); + } + + Transaction.Send(writer.Span()); +} + void HandleMessageImpl( wsl::shared::SocketChannel& Channel, wsl::shared::Transaction& Transaction, @@ -952,7 +989,7 @@ void ProcessMessage(wsl::shared::SocketChannel& Channel, wsl::shared::Transactio { try { - HandleMessage( + HandleMessage( Channel, Transaction, Type, Buffer); } catch (...) diff --git a/src/shared/inc/lxinitshared.h b/src/shared/inc/lxinitshared.h index 9c9e3fde9f..9c56464daa 100644 --- a/src/shared/inc/lxinitshared.h +++ b/src/shared/inc/lxinitshared.h @@ -410,6 +410,8 @@ typedef enum _LX_MESSAGE_TYPE LxMessageWSLCUnixConnect, LxMessageWSLCGetGuestCapabilities, LxMessageWSLCGetGuestCapabilitiesResult, + LxMessageWSLCListDir, + LxMessageWSLCListDirResult, } LX_MESSAGE_TYPE, *PLX_MESSAGE_TYPE; @@ -522,6 +524,8 @@ inline auto ToString(LX_MESSAGE_TYPE messageType) X(LxMessageWSLCUnixConnect) X(LxMessageWSLCGetGuestCapabilities) X(LxMessageWSLCGetGuestCapabilitiesResult) + X(LxMessageWSLCListDir) + X(LxMessageWSLCListDirResult) default: return ""; @@ -1585,6 +1589,33 @@ struct WSLC_GET_DISK PRETTY_PRINT(FIELD(Header), FIELD(ScsiLun)); }; +struct WSLC_LISTDIR_RESULT +{ + static inline auto Type = LxMessageWSLCListDirResult; + + DECLARE_MESSAGE_CTOR(WSLC_LISTDIR_RESULT); + + MESSAGE_HEADER Header; + int Result{}; + unsigned int EntriesIndex{}; + char Buffer[]; + + PRETTY_PRINT(FIELD(Header), FIELD(Result), STRING_ARRAY_FIELD(EntriesIndex)); +}; + +struct WSLC_LISTDIR +{ + static inline auto Type = LxMessageWSLCListDir; + using TResponse = WSLC_LISTDIR_RESULT; + + DECLARE_MESSAGE_CTOR(WSLC_LISTDIR); + + MESSAGE_HEADER Header; + char Buffer[]; + + PRETTY_PRINT(FIELD(Header), FIELD(Buffer)); +}; + struct WSLC_MOUNT_RESULT { static inline auto Type = LxMessageWSLCMountResult; diff --git a/src/windows/wslcsession/WSLCVirtualMachine.cpp b/src/windows/wslcsession/WSLCVirtualMachine.cpp index 3f1814e7e0..a360f7d1e4 100644 --- a/src/windows/wslcsession/WSLCVirtualMachine.cpp +++ b/src/windows/wslcsession/WSLCVirtualMachine.cpp @@ -552,22 +552,15 @@ void WSLCVirtualMachine::RemoveDirectory(const std::string& Path) std::vector WSLCVirtualMachine::ListDirectory(const std::string& Path) { - constexpr auto lsPath = "/bin/ls"; - - std::vector args = {lsPath, "-A", Path}; - - ServiceProcessLauncher launcher(lsPath, args); - auto result = launcher.Launch(*this).WaitAndCaptureOutput(); + wsl::shared::MessageWriter message; + message.WriteString(Path); - THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str()); + gsl::span responseSpan; + const auto& response = m_initChannel.Transaction(message.Span(), &responseSpan, m_initChannelTimeout); - auto stdOut = result.Output.find(1); - if (stdOut == result.Output.end()) - { - return {}; - } + THROW_HR_IF_MSG(E_FAIL, response.Result != 0, "Failed to list directory '%hs', init returned: %d", Path.c_str(), response.Result); - return wsl::shared::string::Split(stdOut->second, '\n'); + return wsl::shared::string::ArrayFromSpan(responseSpan, response.EntriesIndex); } void WSLCVirtualMachine::Unmount(_In_ const char* Path) From ee6d710d336974281012aadac0398548d85bda5b Mon Sep 17 00:00:00 2001 From: kvega005 Date: Mon, 22 Jun 2026 14:08:05 -0700 Subject: [PATCH 7/7] Fix Localization --- localization/strings/en-US/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index 149c84bd1c..cd04db8282 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -3318,7 +3318,7 @@ On first run, creates the file with all settings commented out at their defaults Volume '{}' has a non-empty 'lost+found' directory. It was left in place and the volume may not be seeded with image contents. - {FixedPlaceholder="{}"}{Locked="lost+found"}Command line arguments, file names and string inserts should not be translated + {Locked="lost+found"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated Failed to stop container '{}' after plugin rejection