Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
13964e3
Added VHD named volumes
kvega005 Mar 5, 2026
6ef50e1
Clean up vhd volume
kvega005 Mar 5, 2026
2df4187
Cleanup
kvega005 Mar 5, 2026
a9756d4
Formatting
kvega005 Mar 5, 2026
292ae11
Add options validation
kvega005 Mar 5, 2026
df67401
Rename
kvega005 Mar 5, 2026
ee20433
Format
kvega005 Mar 5, 2026
b3d4a87
using utf8 instead of wide chars
kvega005 Mar 5, 2026
0fb0aae
Validate volumes
kvega005 Mar 6, 2026
5e982bd
Tidying
kvega005 Mar 6, 2026
202c4f0
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 6, 2026
2e6b2fc
Rename resource
kvega005 Mar 6, 2026
9266c94
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 6, 2026
8cfe992
Undo changes to es-ES
kvega005 Mar 6, 2026
7a927af
Undo changes to es-ES
kvega005 Mar 6, 2026
0c6451e
Fix resources files
kvega005 Mar 6, 2026
c2192ac
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 6, 2026
e53ae4e
Fix resources file (again)
kvega005 Mar 6, 2026
60f2338
Fix idl ordering
kvega005 Mar 6, 2026
9ca824e
Fix rsources file again
kvega005 Mar 6, 2026
edf44ec
fix resources again
kvega005 Mar 6, 2026
9d07982
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into featu…
kvega005 Mar 6, 2026
3c78c2c
Fix formatting
kvega005 Mar 7, 2026
29060ce
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 7, 2026
3208004
Moved named volume handling to docker.
kvega005 Mar 7, 2026
39b3135
Add named volume validation
kvega005 Mar 7, 2026
cb201f3
Fix test case
kvega005 Mar 7, 2026
00bd730
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 7, 2026
cb6666a
Fix
kvega005 Mar 8, 2026
244e32b
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 9, 2026
c0f5dc9
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 9, 2026
4eef68d
Fix Docker Client's CreateVolume
kvega005 Mar 10, 2026
01d2538
Persist volume
kvega005 Mar 11, 2026
0fd8c1b
Refactor
kvega005 Mar 11, 2026
2847a07
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into featu…
kvega005 Mar 11, 2026
9f3be0f
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 11, 2026
d488015
Address copilot comments
kvega005 Mar 11, 2026
9cc850e
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 11, 2026
a4fc17c
Address copilot suggestions
kvega005 Mar 11, 2026
527e5de
Log detach errors
kvega005 Mar 11, 2026
6bfc202
Address copilot comments
kvega005 Mar 11, 2026
182673a
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 11, 2026
4c49838
feat: add named volume persistence and validation for WSL containers
kvega005 Mar 12, 2026
fc27370
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 12, 2026
c46d6e1
Use volumes lock
kvega005 Mar 12, 2026
4c1f69f
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into featu…
kvega005 Mar 12, 2026
83a50f1
Update comment
kvega005 Mar 12, 2026
18fbfe2
Address copilot comments
kvega005 Mar 12, 2026
d6e1cd5
Fix formatting
kvega005 Mar 12, 2026
4c10a75
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 12, 2026
7fcdaa7
Address copilot comments
kvega005 Mar 13, 2026
4347cd8
Fix formatting
kvega005 Mar 13, 2026
ea1aa77
Potential fix for pull request finding
kvega005 Mar 13, 2026
0077d7c
Fix duplicate check
kvega005 Mar 13, 2026
6c16a4c
Merge branch 'feature/wsl-for-apps-named-volumes-merge-20260305-11591…
kvega005 Mar 13, 2026
77c3a50
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 13, 2026
219e55a
Merge branch 'feature/wsl-for-apps' into feature/wsl-for-apps-named-v…
kvega005 Mar 13, 2026
ff24d58
Potential fix for pull request finding
kvega005 Mar 13, 2026
6521f5b
Address PR comments
kvega005 Mar 19, 2026
f273b78
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into featu…
kvega005 Mar 19, 2026
273ac59
Copilot suggstion
kvega005 Mar 20, 2026
b270828
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into featu…
kvega005 Mar 20, 2026
cce9d15
List and inspect named volumes
kvega005 Mar 25, 2026
e9bb339
Add inspect and list volumes test
kvega005 Mar 27, 2026
489b05c
Rename test
kvega005 Mar 27, 2026
58ada44
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into listA…
kvega005 Mar 27, 2026
9d463b1
Fix clang formatting
kvega005 Mar 27, 2026
7d79e35
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into listA…
kvega005 Mar 27, 2026
913c76c
Merge branch 'feature/wsl-for-apps' into listAndInspectNamedVolumes
kvega005 Mar 27, 2026
c8afded
Merge branch 'feature/wsl-for-apps' into listAndInspectNamedVolumes
kvega005 Mar 30, 2026
b3d167a
Merge branch 'feature/wsl-for-apps' into listAndInspectNamedVolumes
kvega005 Mar 30, 2026
db6072a
Merge branch 'feature/wsl-for-apps' into listAndInspectNamedVolumes
kvega005 Mar 31, 2026
9e2dbf8
Merge branch 'feature/wsl-for-apps' into listAndInspectNamedVolumes
kvega005 Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/windows/inc/wslc_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,21 @@ struct InspectImage
InspectImage, Id, RepoTags, RepoDigests, Parent, Comment, Created, Author, Architecture, Os, Size, Metadata, Config);
};

} // namespace wsl::windows::common::wslc_schema
struct InspectVhdVolume
{
std::string HostPath;
uint64_t SizeBytes{};

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectVhdVolume, HostPath, SizeBytes);
};

struct InspectVolume
{
std::string Name;
std::string Type;
std::optional<InspectVhdVolume> VhdVolume;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectVolume, Name, Type, VhdVolume);
};

} // namespace wsl::windows::common::wslc_schema
10 changes: 10 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ cpp_quote("#endif")

#define WSLC_MAX_CONTAINER_NAME_LENGTH 255
#define WSLC_MAX_IMAGE_NAME_LENGTH 255
#define WSLC_MAX_VOLUME_NAME_LENGTH 255
#define WSLC_CONTAINER_ID_LENGTH 64

cpp_quote("#define WSLC_MAX_CONTAINER_NAME_LENGTH 255")
cpp_quote("#define WSLC_MAX_IMAGE_NAME_LENGTH 255")
cpp_quote("#define WSLC_MAX_VOLUME_NAME_LENGTH 255")
cpp_quote("#define WSLC_CONTAINER_ID_LENGTH 64")

typedef
Expand Down Expand Up @@ -482,6 +484,12 @@ typedef struct _WSLCVolumeOptions
[unique] LPCSTR Options;
} WSLCVolumeOptions;

typedef struct _WSLCVolumeInformation
{
char Name[WSLC_MAX_VOLUME_NAME_LENGTH + 1];
char Type[64];
} WSLCVolumeInformation;

typedef struct _WSLCContainerPruneFilter
{
LPCSTR Key;
Expand Down Expand Up @@ -572,6 +580,8 @@ interface IWSLCSession : IUnknown
// Volume management.
HRESULT CreateVolume([in] const WSLCVolumeOptions* Options);
HRESULT DeleteVolume([in] LPCSTR Name);
HRESULT ListVolumes([out, size_is(, *Count)] WSLCVolumeInformation** Volumes, [out] ULONG* Count);
Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.
HRESULT InspectVolume([in] LPCSTR Name, [out] LPSTR* Output);
Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.
}

//
Expand Down
66 changes: 65 additions & 1 deletion src/windows/wslcsession/WSLCSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,7 @@ try

std::lock_guard volumesLock(m_volumesLock);
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), m_volumes.contains(name));
THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::MessageWslcInvalidVolumeType(type), type != "vhd");
THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::MessageWslcInvalidVolumeType(type), type != WSLCVhdVolumeType);

auto volume = WSLCVhdVolumeImpl::Create(*Options, m_storageVhdPath.parent_path(), m_virtualMachine.value(), m_dockerClient.value());
auto [it, inserted] = m_volumes.insert({name, std::move(volume)});
Expand Down Expand Up @@ -1514,6 +1514,70 @@ try
}
CATCH_RETURN();

HRESULT WSLCSession::ListVolumes(WSLCVolumeInformation** Volumes, ULONG* Count)
try
{
COMServiceExecutionContext context;

RETURN_HR_IF_NULL(E_POINTER, Volumes);
RETURN_HR_IF_NULL(E_POINTER, Count);

*Volumes = nullptr;
*Count = 0;

auto lock = m_lock.lock_shared();
std::lock_guard volumesLock(m_volumesLock);

if (m_volumes.empty())
{
return S_OK;
}

auto output = wil::make_unique_cotaskmem<WSLCVolumeInformation[]>(m_volumes.size());

ULONG index = 0;
for (const auto& [name, volume] : m_volumes)
{
Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.
THROW_HR_IF(E_UNEXPECTED, strcpy_s(output[index].Name, name.c_str()) != 0);
Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.
THROW_HR_IF(E_UNEXPECTED, strcpy_s(output[index].Type, WSLCVhdVolumeType) != 0);
index++;
}

*Volumes = output.release();
*Count = index;

return S_OK;
}
CATCH_RETURN();

HRESULT WSLCSession::InspectVolume(LPCSTR Name, LPSTR* Output)
try
{
COMServiceExecutionContext context;

RETURN_HR_IF_NULL(E_POINTER, Name);
RETURN_HR_IF_NULL(E_POINTER, Output);

*Output = nullptr;

std::string name = Name;
Comment thread
kvega005 marked this conversation as resolved.
ValidateName(name.c_str());

Comment thread
kvega005 marked this conversation as resolved.
auto lock = m_lock.lock_shared();
std::lock_guard volumesLock(m_volumesLock);

auto it = m_volumes.find(name);
THROW_HR_WITH_USER_ERROR_IF(WSLC_E_VOLUME_NOT_FOUND, Localization::MessageWslcVolumeNotFound(name), it == m_volumes.end());

const auto& volume = it->second;

std::string json = volume->Inspect();
*Output = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(json.c_str()).release();

return S_OK;
}
CATCH_RETURN();

HRESULT WSLCSession::Terminate()
try
{
Expand Down
2 changes: 2 additions & 0 deletions src/windows/wslcsession/WSLCSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class DECLSPEC_UUID("4877FEFC-4977-4929-A958-9F36AA1892A4") WSLCSession
// Volume management.
IFACEMETHOD(CreateVolume)(_In_ const WSLCVolumeOptions* Options) override;
IFACEMETHOD(DeleteVolume)(_In_ LPCSTR Name) override;
IFACEMETHOD(ListVolumes)(_Out_ WSLCVolumeInformation** Volumes, _Out_ ULONG* Count) override;
IFACEMETHOD(InspectVolume)(_In_ LPCSTR Name, _Out_ LPSTR* Output) override;

IFACEMETHOD(Terminate()) override;

Expand Down
18 changes: 16 additions & 2 deletions src/windows/wslcsession/WSLCVhdVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Module Name:
#include "WSLCVirtualMachine.h"
#include "WSLCVolumeMetadata.h"
#include "WslCoreFilesystem.h"
#include "wslc_schema.h"

using namespace wsl::windows::common;
using wsl::shared::Localization;
Expand All @@ -28,7 +29,7 @@ namespace {
std::string SerializeVhdVolumeMetadata(const std::filesystem::path& HostPath, ULONGLONG SizeBytes)
{
WSLCVolumeMetadata metadata{};
metadata.Type = "vhd";
metadata.Type = WSLCVhdVolumeType;

WSLCVhdVolumeMetadata vhdMetadata{};
vhdMetadata.V1 = WSLCVhdVolumeMetadataV1{HostPath.wstring(), SizeBytes};
Expand Down Expand Up @@ -132,7 +133,7 @@ std::unique_ptr<WSLCVhdVolumeImpl> WSLCVhdVolumeImpl::Open(
THROW_HR_IF(E_INVALIDARG, metadataIt == Volume.Labels.end());

auto metadata = wsl::shared::FromJson<WSLCVolumeMetadata>(metadataIt->second.c_str());
THROW_HR_IF(E_INVALIDARG, metadata.Type != "vhd");
THROW_HR_IF(E_INVALIDARG, metadata.Type != WSLCVhdVolumeType);
THROW_HR_IF(E_INVALIDARG, !metadata.VhdVolumeMetadata.has_value() || !metadata.VhdVolumeMetadata->V1.has_value());

const auto& vhdMetadata = metadata.VhdVolumeMetadata->V1.value();
Expand Down Expand Up @@ -179,6 +180,19 @@ void WSLCVhdVolumeImpl::Delete()
LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(m_hostPath.c_str()));
}

std::string WSLCVhdVolumeImpl::Inspect() const
{
wslc_schema::InspectVolume inspect{};
inspect.Name = m_name;
inspect.Type = WSLCVhdVolumeType;
inspect.VhdVolume = wslc_schema::InspectVhdVolume{
m_hostPath.string(),
Comment thread
kvega005 marked this conversation as resolved.
m_sizeBytes,
};
Comment thread
kvega005 marked this conversation as resolved.

return wsl::shared::ToJson(inspect);
}

void WSLCVhdVolumeImpl::Detach()
try
{
Expand Down
1 change: 1 addition & 0 deletions src/windows/wslcsession/WSLCVhdVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class WSLCVhdVolumeImpl
_In_ const wsl::windows::common::docker_schema::Volume& Volume, _In_ WSLCVirtualMachine& VirtualMachine, _In_ DockerHTTPClient& DockerClient);

void Delete();
std::string Inspect() const;

const std::string& Name() const noexcept
{
Expand Down
3 changes: 3 additions & 0 deletions src/windows/wslcsession/WSLCVolumeMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ namespace wsl::windows::service::wslc {
// Label key used to store WSLC volume metadata in Docker volume labels.
constexpr auto WSLCVolumeMetadataLabel = "com.microsoft.wsl.volume.metadata";

// Volume type identifier for VHD-backed volumes.
constexpr auto WSLCVhdVolumeType = "vhd";

struct WSLCVhdVolumeMetadataV1
{
std::wstring HostPath;
Expand Down
69 changes: 69 additions & 0 deletions test/windows/WSLCTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2993,6 +2993,75 @@ class WSLCTests
validateInvalidOptionsFailure("", WSL_E_INVALID_JSON);
}

TEST_METHOD(ListAndInspectNamedVolumesTest)
{
WSL2_TEST_ONLY();

const std::string volumeName1 = "wsla-test-vol1";
const std::string volumeName2 = "wsla-test-vol2";
Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.

auto cleanup = wil::scope_exit([&]() {
LOG_IF_FAILED(m_defaultSession->DeleteVolume(volumeName1.c_str()));
LOG_IF_FAILED(m_defaultSession->DeleteVolume(volumeName2.c_str()));
});

// Verify empty list is returned when no volumes exist.
wil::unique_cotaskmem_array_ptr<WSLCVolumeInformation> volumes;
VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(volumes.addressof(), volumes.size_address<ULONG>()));
VERIFY_ARE_EQUAL(0u, volumes.size());

Comment thread
kvega005 marked this conversation as resolved.
Comment thread
kvega005 marked this conversation as resolved.
// Create first volume and verify list returns one entry.
WSLCVolumeOptions volumeOptions{};
volumeOptions.Name = volumeName1.c_str();
volumeOptions.Type = "vhd";
volumeOptions.Options = R"({"SizeBytes":"1073741824"})";
VERIFY_SUCCEEDED(m_defaultSession->CreateVolume(&volumeOptions));

Comment thread
kvega005 marked this conversation as resolved.
VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(volumes.addressof(), volumes.size_address<ULONG>()));
VERIFY_ARE_EQUAL(1u, volumes.size());
VERIFY_ARE_EQUAL(std::string(volumes[0].Name), volumeName1);
VERIFY_ARE_EQUAL(std::string(volumes[0].Type), std::string("vhd"));

// Create second volume and verify list returns two entries.
volumeOptions.Name = volumeName2.c_str();
VERIFY_SUCCEEDED(m_defaultSession->CreateVolume(&volumeOptions));

VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(volumes.addressof(), volumes.size_address<ULONG>()));
VERIFY_ARE_EQUAL(2u, volumes.size());

std::set<std::string> names;
for (const auto& v : volumes)
{
names.insert(v.Name);
VERIFY_ARE_EQUAL(std::string(v.Type), std::string("vhd"));
}

VERIFY_IS_TRUE(names.contains(volumeName1));
VERIFY_IS_TRUE(names.contains(volumeName2));

// Verify InspectVolume returns correct details.
wil::unique_cotaskmem_ansistring output;
VERIFY_SUCCEEDED(m_defaultSession->InspectVolume(volumeName1.c_str(), &output));
VERIFY_IS_NOT_NULL(output.get());

auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::InspectVolume>(output.get());
VERIFY_ARE_EQUAL(inspect.Name, volumeName1);
VERIFY_ARE_EQUAL(inspect.Type, std::string("vhd"));
VERIFY_IS_TRUE(inspect.VhdVolume.has_value());
VERIFY_ARE_EQUAL(inspect.VhdVolume->SizeBytes, 1073741824ull);
VERIFY_IS_FALSE(inspect.VhdVolume->HostPath.empty());

// Verify InspectVolume fails for a non-existent volume.
output.reset();
VERIFY_ARE_EQUAL(m_defaultSession->InspectVolume("does-not-exist", &output), WSLC_E_VOLUME_NOT_FOUND);

// Delete first volume and verify list returns one entry.
VERIFY_SUCCEEDED(m_defaultSession->DeleteVolume(volumeName1.c_str()));
VERIFY_SUCCEEDED(m_defaultSession->ListVolumes(volumes.addressof(), volumes.size_address<ULONG>()));
VERIFY_ARE_EQUAL(1u, volumes.size());
VERIFY_ARE_EQUAL(std::string(volumes[0].Name), volumeName2);
}

TEST_METHOD(CreateContainer)
{
WSL2_TEST_ONLY();
Expand Down
Loading