CLI: Mount PR followup & fix two docker parser bugs (#41436)
David Bennett committed
Aug 26, 2026 at 10:51 UTC
5e0564f8807de30c9611d38d26a6712ebfdeeae5
12 files changed
+162
-229
src/windows/common/MountSpecParsing.cpp
+27
-26
@@ -84,11 +84,16 @@ namespace {
84
FieldDefinition{L"destination", Field::Target, Family::General, false, Support::Supported},
85
FieldDefinition{L"readonly", Field::ReadOnly, Family::General, true, Support::Supported},
86
FieldDefinition{L"ro", Field::ReadOnly, Family::General, true, Support::Supported},
87
+ // The WSLC mount transport and Docker request model have no end-to-end consistency setting.
88
FieldDefinition{L"consistency", Field::Consistency, Family::General, false, Support::Unsupported},
89
+ // The WSLC mount transport and Docker request model do not carry Docker bind options.
90
+ // "enabled" requires no non-default bind behavior and is accepted below.
91
FieldDefinition{L"bind-propagation", Field::BindPropagation, Family::Bind, false, Support::Unsupported},
92
FieldDefinition{L"bind-nonrecursive", Field::BindNonRecursive, Family::Bind, true, Support::Unsupported},
93
FieldDefinition{L"bind-recursive", Field::BindRecursive, Family::Bind, false, Support::ValueDependent},
94
+ // The mount pipeline relies on Docker's default volume copy-up behavior and does not carry VolumeOptions.
95
FieldDefinition{L"volume-nocopy", Field::VolumeNoCopy, Family::Volume, true, Support::Unsupported},
96
+ // Inline mounts reference volumes by name; the volume creation API owns labels, drivers, and driver options.
97
FieldDefinition{L"volume-label", Field::VolumeLabel, Family::Volume, false, Support::Unsupported},
98
FieldDefinition{L"volume-driver", Field::VolumeDriver, Family::Volume, false, Support::Unsupported},
99
FieldDefinition{L"volume-opt", Field::VolumeOption, Family::Volume, false, Support::Unsupported},
@@ -173,21 +178,14 @@ namespace {
178
179
std::optional<uint32_t> ParseDockerTmpfsMode(const std::wstring& value)
180
{
176
- if (value.empty() || value.front() == L'-')
177
- {
178
- return std::nullopt;
179
- }
180
-
181
- size_t position = value.front() == L'+' ? 1 : 0;
182
- if (position == value.size())
181
+ if (value.empty())
182
{
183
return std::nullopt;
184
}
185
186
uint64_t result = 0;
188
- for (; position < value.size(); ++position)
187
+ for (const auto digit : value)
188
{
190
- const auto digit = value[position];
189
if (digit < L'0' || digit > L'7')
190
{
191
return std::nullopt;
@@ -253,7 +251,10 @@ Spec ParseDockerMountString(const std::wstring& value)
251
case Family::General:
252
break;
253
case Family::Bind:
256
- mount.HasBindOptions = true;
254
+ if (definition->Id != Field::BindRecursive || keyValue.Value != L"enabled")
255
+ {
256
+ mount.HasBindOptions = true;
257
+ }
258
break;
259
case Family::Volume:
260
mount.HasVolumeOptions = true;
@@ -417,15 +418,15 @@ Spec ParseDockerMountString(const std::wstring& value)
418
Type type;
419
if (mount.Type == L"bind")
420
{
420
- type = Type::Bind;
421
+ type = WSLCMountTypeBind;
422
}
423
else if (mount.Type == L"volume")
424
{
424
- type = Type::Volume;
425
+ type = WSLCMountTypeVolume;
426
}
427
else if (mount.Type == L"tmpfs")
428
{
428
- type = Type::Tmpfs;
429
+ type = WSLCMountTypeTmpfs;
430
}
431
else
432
{
@@ -495,15 +496,16 @@ Spec ParseDockerVolumeString(const std::wstring& value)
496
if (IsValidNamedVolumeName(rawSource))
497
{
498
return {
498
- .MountType = Type::Volume,
499
+ .MountType = WSLCMountTypeVolume,
500
.Source = rawSource,
501
.Target = WideToMultiByte(target),
502
.ReadOnly = readOnly,
503
};
504
}
505
505
- std::wstring source;
506
- if (FAILED(wil::GetFullPathNameW(rawSource.c_str(), source)))
506
+ std::error_code error;
507
+ auto source = wsl::windows::common::filesystem::GetCanonicalPath(rawSource, error);
508
+ if (error)
509
{
510
ThrowParse(Localization::WSLCCLI_VolumeHostPathInvalid(value, rawSource));
511
}
@@ -514,8 +516,8 @@ Spec ParseDockerVolumeString(const std::wstring& value)
516
}
517
518
return {
517
- .MountType = Type::Bind,
518
- .Source = std::move(source),
519
+ .MountType = WSLCMountTypeBind,
520
+ .Source = source.wstring(),
521
.Target = WideToMultiByte(target),
522
.ReadOnly = readOnly,
523
.BindSource = BindSourcePolicy::CreateIfMissing,
@@ -529,7 +531,7 @@ Spec ParseDockerTmpfsString(const std::wstring& value)
531
const auto options = colon == std::wstring::npos ? std::wstring_view{} : std::wstring_view{value}.substr(colon + 1);
532
533
return {
532
- .MountType = Type::Tmpfs,
534
+ .MountType = WSLCMountTypeTmpfs,
535
.Target = WideToMultiByte(target),
536
.TmpfsOptions = WideToMultiByte(std::wstring{options}),
537
};
@@ -547,7 +549,8 @@ void ValidateMountSpec(const Spec& mount)
549
ThrowValidation(Localization::WSLCCLI_MountTargetAbsoluteError());
550
}
551
550
- if (mount.MountType != Type::Tmpfs && (mount.TmpfsSizeBytes.has_value() || mount.TmpfsMode.has_value() || mount.TmpfsOptions.has_value()))
552
+ if (mount.MountType != WSLCMountTypeTmpfs &&
553
+ (mount.TmpfsSizeBytes.has_value() || mount.TmpfsMode.has_value() || mount.TmpfsOptions.has_value()))
554
{
555
ThrowValidation(Localization::WSLCCLI_MountTmpfsOptionsTypeError());
556
}
@@ -559,7 +562,7 @@ void ValidateMountSpec(const Spec& mount)
562
563
switch (mount.MountType)
564
{
562
- case Type::Bind:
565
+ case WSLCMountTypeBind:
566
if (mount.Source.empty())
567
{
568
ThrowValidation(Localization::WSLCCLI_MountSourceRequiredError());
@@ -571,14 +574,14 @@ void ValidateMountSpec(const Spec& mount)
574
}
575
break;
576
574
- case Type::Volume:
577
+ case WSLCMountTypeVolume:
578
if (!mount.Source.empty() && !IsValidNamedVolumeName(mount.Source))
579
{
580
ThrowValidation(Localization::WSLCCLI_MountVolumeSourceInvalidError());
581
}
582
break;
583
581
- case Type::Tmpfs:
584
+ case WSLCMountTypeTmpfs:
585
if (!mount.Source.empty())
586
{
587
ThrowValidation(Localization::WSLCCLI_MountTmpfsSourceUnsupportedError());
@@ -610,7 +613,7 @@ void ValidateMountCollection(std::span<const Spec> mounts)
613
614
std::string FormatTmpfsOptions(const Spec& mount)
615
{
613
- WI_ASSERT(mount.MountType == Type::Tmpfs);
616
+ WI_ASSERT(mount.MountType == WSLCMountTypeTmpfs);
617
618
if (mount.TmpfsOptions.has_value())
619
{
@@ -636,8 +639,6 @@ std::string FormatTmpfsOptions(const Spec& mount)
639
640
std::string NormalizeDestination(std::string destination)
641
{
639
- std::replace(destination.begin(), destination.end(), '\\', '/');
640
-
642
std::vector<std::string> components;
643
size_t start = 0;
644
while (start <= destination.size())
src/windows/common/MountSpecParsing.h
+3
-9
@@ -14,6 +14,7 @@ Abstract:
14
15
#pragma once
16
17
+#include "wslc.h"
18
#include <cstdint>
19
#include <exception>
20
#include <optional>
@@ -24,14 +25,7 @@ Abstract:
25
26
namespace wsl::windows::common::mount {
27
27
-inline constexpr std::string_view c_dockerCliMountGrammarVersion = "25.0.3";
28
-
29
-enum class Type
30
-{
31
- Bind,
32
- Volume,
33
- Tmpfs,
34
-};
28
+using Type = WSLCMountType;
29
30
enum class BindSourcePolicy
31
{
@@ -41,7 +35,7 @@ enum class BindSourcePolicy
35
36
struct Spec
37
{
44
- Type MountType = Type::Volume;
38
+ Type MountType = WSLCMountTypeVolume;
39
std::wstring Source;
40
std::string Target;
41
bool ReadOnly = false;
src/windows/common/WSLCContainerLauncher.cpp
+5
-19
@@ -230,7 +230,7 @@ void WSLCContainerLauncher::AddUlimit(const std::string& Name, std::int64_t Soft
230
void wsl::windows::common::WSLCContainerLauncher::AddVolume(const std::wstring& HostPath, const std::string& ContainerPath, bool ReadOnly)
231
{
232
AddMount({
233
- .MountType = mount::Type::Bind,
233
+ .MountType = WSLCMountTypeBind,
234
.Source = HostPath,
235
.Target = ContainerPath,
236
.ReadOnly = ReadOnly,
@@ -241,7 +241,7 @@ void wsl::windows::common::WSLCContainerLauncher::AddVolume(const std::wstring&
241
void wsl::windows::common::WSLCContainerLauncher::AddNamedVolume(const std::string& Name, const std::string& ContainerPath, bool ReadOnly)
242
{
243
AddMount({
244
- .MountType = mount::Type::Volume,
244
+ .MountType = WSLCMountTypeVolume,
245
.Source = wsl::shared::string::MultiByteToWide(Name),
246
.Target = ContainerPath,
247
.ReadOnly = ReadOnly,
@@ -251,20 +251,7 @@ void wsl::windows::common::WSLCContainerLauncher::AddNamedVolume(const std::stri
251
void wsl::windows::common::WSLCContainerLauncher::AddMount(const mount::Spec& Mount)
252
{
253
WSLCMountSpec mount{};
254
- switch (Mount.MountType)
255
- {
256
- case mount::Type::Bind:
257
- mount.Type = WSLCMountTypeBind;
258
- break;
259
-
260
- case mount::Type::Volume:
261
- mount.Type = WSLCMountTypeVolume;
262
- break;
263
-
264
- case mount::Type::Tmpfs:
265
- mount.Type = WSLCMountTypeTmpfs;
266
- break;
267
- }
254
+ mount.Type = Mount.MountType;
255
256
if (!Mount.Source.empty())
257
{
@@ -273,7 +260,7 @@ void wsl::windows::common::WSLCContainerLauncher::AddMount(const mount::Spec& Mo
260
261
mount.Target = m_mountTargets.emplace_back(Mount.Target).c_str();
262
mount.ReadOnly = Mount.ReadOnly ? TRUE : FALSE;
276
- if (Mount.MountType == mount::Type::Bind && Mount.BindSource == mount::BindSourcePolicy::CreateIfMissing)
263
+ if (Mount.MountType == WSLCMountTypeBind && Mount.BindSource == mount::BindSourcePolicy::CreateIfMissing)
264
{
265
WI_SetFlag(mount.Flags, WSLCMountSpecFlagsCreateSourceIfMissing);
266
}
@@ -292,7 +279,6 @@ void wsl::windows::common::WSLCContainerLauncher::AddMount(const mount::Spec& Mo
279
280
if (Mount.TmpfsOptions.has_value())
281
{
295
- WI_SetFlag(mount.Flags, WSLCMountSpecFlagsTmpfsOptions);
282
mount.TmpfsOptions = m_mountTmpfsOptions.emplace_back(Mount.TmpfsOptions.value()).c_str();
283
}
284
@@ -315,7 +301,7 @@ void wsl::windows::common::WSLCContainerLauncher::AddLabel(const std::string& Ke
301
void wsl::windows::common::WSLCContainerLauncher::AddTmpfs(const std::string& ContainerPath, const std::string& Options)
302
{
303
AddMount({
318
- .MountType = mount::Type::Tmpfs,
304
+ .MountType = WSLCMountTypeTmpfs,
305
.Target = ContainerPath,
306
.TmpfsOptions = Options,
307
});
src/windows/service/inc/wslc.idl
+1
-2
@@ -261,10 +261,9 @@ typedef enum _WSLCMountSpecFlags
261
WSLCMountSpecFlagsTmpfsSize = 1,
262
WSLCMountSpecFlagsTmpfsMode = 2,
263
WSLCMountSpecFlagsCreateSourceIfMissing = 4,
264
- WSLCMountSpecFlagsTmpfsOptions = 8,
264
} WSLCMountSpecFlags;
265
267
-cpp_quote("#define WSLCMountSpecFlagsValid (WSLCMountSpecFlagsTmpfsSize | WSLCMountSpecFlagsTmpfsMode | WSLCMountSpecFlagsCreateSourceIfMissing | WSLCMountSpecFlagsTmpfsOptions)")
266
+cpp_quote("#define WSLCMountSpecFlagsValid (WSLCMountSpecFlagsTmpfsSize | WSLCMountSpecFlagsTmpfsMode | WSLCMountSpecFlagsCreateSourceIfMissing)")
267
cpp_quote("DEFINE_ENUM_FLAG_OPERATORS(WSLCMountSpecFlags);")
268
269
typedef struct _WSLCMountSpec
src/windows/wslc/services/ContainerModel.cpp
+1
-18
@@ -176,7 +176,7 @@ VolumeMount VolumeMount::Parse(const std::wstring& value)
176
volume.m_host = std::move(mountSpec.Source);
177
volume.m_containerPath = std::move(mountSpec.Target);
178
volume.m_isReadOnlyMode = mountSpec.ReadOnly;
179
- volume.m_isNamedVolume = mountSpec.MountType == mount::Type::Volume;
179
+ volume.m_isNamedVolume = mountSpec.MountType == WSLCMountTypeVolume;
180
return volume;
181
}
182
@@ -258,23 +258,6 @@ std::vector<std::wstring> EnvironmentVariable::ParseFile(const std::wstring& fil
258
return envVars;
259
}
260
261
-void ValidateUniqueMountDestinations(const ContainerOptions& options)
262
-{
263
- try
264
- {
265
- mount::ValidateMountCollection(options.Mounts);
266
- }
267
- catch (const mount::MountException& ex)
268
- {
269
- if (ex.Error() == mount::ValidationError::DuplicateDestination)
270
- {
271
- THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::WSLCCLI_DuplicateMountDestinationError(MultiByteToWide(ex.Destination())));
272
- }
273
-
274
- throw;
275
- }
276
-}
277
-
261
CidFile::CidFile(const std::optional<std::wstring>& path)
262
{
263
if (!path.has_value())
src/windows/wslc/services/ContainerModel.h
-2
@@ -311,8 +311,6 @@ private:
311
bool m_isNamedVolume = false;
312
};
313
314
-void ValidateUniqueMountDestinations(const ContainerOptions& options);
315
-
314
class CidFile
315
{
316
public:
src/windows/wslc/tasks/ContainerTasks.cpp
-2
@@ -851,8 +851,6 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
851
options.Mounts.insert(options.Mounts.end(), std::make_move_iterator(tmpfs.begin()), std::make_move_iterator(tmpfs.end()));
852
}
853
854
- ValidateUniqueMountDestinations(options);
855
-
854
for (const auto& label : context.Args.GetAllValues<ArgType::Label>())
855
{
856
options.Labels.push_back(label);
src/windows/wslcsession/WSLCContainer.cpp
+27
-54
@@ -430,15 +430,14 @@ auto MountVolumes(std::vector<WSLCVolumeMount>& volumes, WSLCVirtualMachine& par
430
const auto sourceExists = std::filesystem::exists(volume.HostPath, error);
431
if (error)
432
{
433
- throw wsl::windows::common::mount::MountValidationException(
434
- Localization::MessageWslcBindSourcePathError(volume.HostPath, error.message()));
433
+ THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageWslcBindSourcePathError(volume.HostPath, error.message()));
434
}
435
436
if (!sourceExists)
437
{
438
if (!volume.CreateSourceIfMissing)
439
{
441
- throw wsl::windows::common::mount::MountValidationException(Localization::MessageWslcBindSourcePathNotFound(volume.HostPath));
440
+ THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageWslcBindSourcePathNotFound(volume.HostPath));
441
}
442
443
auto result = wil::CreateDirectoryDeepNoThrow(volume.HostPath.c_str());
@@ -457,18 +456,6 @@ auto MountVolumes(std::vector<WSLCVolumeMount>& volumes, WSLCVirtualMachine& par
456
return std::move(errorCleanup);
457
}
458
460
-auto MountVolumesWithUserError(std::vector<WSLCVolumeMount>& volumes, WSLCVirtualMachine& parentVM)
461
-{
462
- try
463
- {
464
- return MountVolumes(volumes, parentVM);
465
- }
466
- catch (const wsl::windows::common::mount::MountValidationException& ex)
467
- {
468
- THROW_HR_WITH_USER_ERROR(E_INVALIDARG, ex.Reason());
469
- }
470
-}
471
-
459
WSLCContainerState DockerStateToWSLCState(ContainerState state)
460
{
461
// TODO: Handle other states like Paused, Restarting, etc.
@@ -546,6 +533,9 @@ std::map<std::string, std::string> StripInternalLabels(std::optional<std::map<st
533
return StripInternalLabels(std::move(labels).value_or(std::map<std::string, std::string>{}));
534
}
535
536
+// Validate every mount representation as one collection before preparing VM shares or calling Docker.
537
+// Docker handles duplicate destinations differently across Binds, Mounts, and Tmpfs and can create named volumes while processing the request.
538
+// This service-boundary check gives every caller consistent duplicate semantics and keeps invalid requests side-effect free.
539
std::vector<wsl::windows::common::mount::Spec> ConvertAndValidateMounts(const WSLCContainerOptions& containerOptions)
540
{
541
namespace mount = wsl::windows::common::mount;
@@ -565,39 +555,31 @@ std::vector<wsl::windows::common::mount::Spec> ConvertAndValidateMounts(const WS
555
i,
556
value.Flags);
557
568
- mount::Type type;
558
switch (value.Type)
559
{
560
case WSLCMountTypeBind:
572
- type = mount::Type::Bind;
573
- break;
574
-
561
case WSLCMountTypeVolume:
576
- type = mount::Type::Volume;
577
- break;
578
-
562
case WSLCMountTypeTmpfs:
580
- type = mount::Type::Tmpfs;
563
break;
564
565
default:
566
THROW_HR_MSG(E_INVALIDARG, "Mount at index %lu has invalid type: %d", i, value.Type);
567
}
568
569
+ const auto type = value.Type;
570
THROW_HR_IF_MSG(
571
E_INVALIDARG,
589
- type != mount::Type::Bind && WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsCreateSourceIfMissing),
572
+ type != WSLCMountTypeBind && WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsCreateSourceIfMissing),
573
"Mount at index %lu specifies create-source-if-missing for a non-bind mount",
574
i);
575
THROW_HR_IF_MSG(
576
E_INVALIDARG,
594
- type != mount::Type::Tmpfs && WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsOptions),
577
+ type != WSLCMountTypeTmpfs && value.TmpfsOptions != nullptr,
578
"Mount at index %lu specifies tmpfs options for a non-tmpfs mount",
579
i);
580
THROW_HR_IF_MSG(
581
E_INVALIDARG,
599
- WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsOptions) &&
600
- WI_IsAnyFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsSize | WSLCMountSpecFlagsTmpfsMode),
582
+ value.TmpfsOptions != nullptr && WI_IsAnyFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsSize | WSLCMountSpecFlagsTmpfsMode),
583
"Mount at index %lu combines legacy and structured tmpfs options",
584
i);
585
@@ -610,9 +592,7 @@ std::vector<wsl::windows::common::mount::Spec> ConvertAndValidateMounts(const WS
592
: mount::BindSourcePolicy::RequireExisting,
593
.TmpfsSizeBytes = WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsSize) ? std::optional<int64_t>{value.TmpfsSizeBytes} : std::nullopt,
594
.TmpfsMode = WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsMode) ? std::optional<uint32_t>{value.TmpfsMode} : std::nullopt,
613
- .TmpfsOptions = WI_IsFlagSet(value.Flags, WSLCMountSpecFlagsTmpfsOptions)
614
- ? std::optional<std::string>{value.TmpfsOptions != nullptr ? value.TmpfsOptions : ""}
615
- : std::nullopt,
595
+ .TmpfsOptions = value.TmpfsOptions != nullptr ? std::optional<std::string>{value.TmpfsOptions} : std::nullopt,
596
});
597
}
598
@@ -621,7 +601,7 @@ std::vector<wsl::windows::common::mount::Spec> ConvertAndValidateMounts(const WS
601
mount::ValidateMountCollection(mounts);
602
for (const auto& mount : mounts)
603
{
624
- if (mount.MountType == mount::Type::Bind)
604
+ if (mount.MountType == WSLCMountTypeBind)
605
{
606
if (mount.BindSource == mount::BindSourcePolicy::CreateIfMissing)
607
{
@@ -706,35 +686,28 @@ enum class MissingBindSource
686
687
PreparedBindMount PrepareBindMount(const std::wstring& source, const std::string& target, bool readOnly, MissingBindSource missingSource)
688
{
709
- GUID volumeId;
710
- THROW_IF_FAILED(CoCreateGuid(&volumeId));
711
-
712
- auto parentVMPath = std::format("/mnt/{}", wsl::shared::string::GuidToString<char>(volumeId));
689
std::filesystem::path hostPath = source;
690
THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::MessagePathNotAbsolute(source), !hostPath.is_absolute());
691
692
std::wstring sourceFilename;
693
{
694
std::error_code ec;
719
- hostPath = std::filesystem::canonical(hostPath, ec);
720
- if (!ec)
695
+ hostPath = wsl::windows::common::filesystem::GetCanonicalPath(hostPath, ec);
696
+ if (ec)
697
{
722
- if (std::filesystem::is_regular_file(hostPath))
723
- {
724
- sourceFilename = hostPath.filename().wstring();
725
- hostPath = hostPath.parent_path();
726
- }
698
+ THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(ec.value()), Localization::MessageWslcFailedToMountVolume(source, ec.message()));
699
}
728
- else if (ec == std::errc::no_such_file_or_directory)
729
- {
730
- hostPath = source;
731
- }
732
- else
700
+
701
+ if (std::filesystem::is_regular_file(hostPath))
702
{
734
- THROW_HR_WITH_USER_ERROR(E_FAIL, Localization::MessageWslcFailedToMountVolume(source, ec.message()));
703
+ sourceFilename = hostPath.filename().wstring();
704
+ hostPath = hostPath.parent_path();
705
}
706
}
707
708
+ GUID volumeId;
709
+ THROW_IF_FAILED(CoCreateGuid(&volumeId));
710
+ auto parentVMPath = std::format("/mnt/{}", wsl::shared::string::GuidToString<char>(volumeId));
711
auto dockerSource = sourceFilename.empty() ? parentVMPath : std::format("{}/{}", parentVMPath, sourceFilename);
712
return {
713
.Volume =
@@ -1133,7 +1106,7 @@ void WSLCContainerImpl::Start(WSLCContainerStartFlags Flags, const WSLCProcessSt
1106
Localization::MessageWslcVolumeNotAvailable(wsl::shared::string::Join(unavailableVolumes, ',')),
1107
!unavailableVolumes.empty());
1108
1136
- auto volumeCleanup = MountVolumesWithUserError(m_mountedVolumes, m_runtime.Vm());
1109
+ auto volumeCleanup = MountVolumes(m_mountedVolumes, m_runtime.Vm());
1110
1111
auto portCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this]() { UnmapPorts(); });
1112
MapPorts();
@@ -2289,7 +2262,7 @@ std::shared_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
2262
2263
switch (mount.MountType)
2264
{
2292
- case wsl::windows::common::mount::Type::Bind:
2265
+ case WSLCMountTypeBind:
2266
{
2267
// Docker's colon-delimited bind format cannot represent ':' in the target.
2268
const auto missingSource = mount.BindSource == wsl::windows::common::mount::BindSourcePolicy::CreateIfMissing
@@ -2302,12 +2275,12 @@ std::shared_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
2275
break;
2276
}
2277
2305
- case wsl::windows::common::mount::Type::Volume:
2278
+ case WSLCMountTypeVolume:
2279
dockerMount.Source = wsl::shared::string::WideToMultiByte(mount.Source);
2280
dockerMount.Type = "volume";
2281
break;
2282
2310
- case wsl::windows::common::mount::Type::Tmpfs:
2283
+ case WSLCMountTypeTmpfs:
2284
if (mount.TmpfsOptions.has_value())
2285
{
2286
request.HostConfig.Tmpfs[mount.Target] = mount.TmpfsOptions.value();
@@ -2470,7 +2443,7 @@ std::shared_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
2443
// Docker validates structured bind sources during container creation, so their VM paths must exist here.
2444
// Release the temporary shares before returning; Start remounts them for the container lifetime.
2445
auto result = [&]() {
2473
- auto volumeCleanup = MountVolumesWithUserError(volumes, virtualMachine);
2446
+ auto volumeCleanup = MountVolumes(volumes, virtualMachine);
2447
return DockerClient.CreateContainer(request, containerName);
2448
}();
2449
@@ -2534,7 +2507,7 @@ std::shared_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
2507
2508
for (const auto& mount : mounts)
2509
{
2537
- if (mount.MountType == wsl::windows::common::mount::Type::Volume && !mount.Source.empty())
2510
+ if (mount.MountType == WSLCMountTypeVolume && !mount.Source.empty())
2511
{
2512
namedVolumes.emplace_back(wsl::shared::string::WideToMultiByte(mount.Source));
2513
}
test/windows/WSLCTests.cpp
+1
-1
@@ -10317,7 +10317,7 @@ class WSLCTests
10317
10318
VerifyPatternMatch(
10319
wsl::shared::string::WideToMultiByte(comError->Message.get()),
10320
- "Failed to create volume '*test-volume\\subfolder': Access is denied. ");
10320
+ "Failed to create volume '*test-volume\\subfolder': Access is denied.");
10321
}
10322
10323
// Validate that files mounts are correctly recovered when a container is loaded from storage
test/windows/wslc/WSLCCLIArgumentUnitTests.cpp
+3
-3
@@ -416,16 +416,16 @@ class WSLCCLIArgumentUnitTests
416
// mount strings -> mount::Spec
417
{
418
const auto volume = ValidateAndGetCached<ArgType::Volume>(LR"(C:\hostPath:/containerPath)");
419
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Bind), static_cast<int>(volume.MountType));
419
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeBind), static_cast<int>(volume.MountType));
420
VERIFY_ARE_EQUAL(static_cast<int>(mount::BindSourcePolicy::CreateIfMissing), static_cast<int>(volume.BindSource));
421
422
const auto tmpfs = ValidateAndGetCached<ArgType::TMPFS>(L"/tmp:size=64k");
423
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Tmpfs), static_cast<int>(tmpfs.MountType));
423
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeTmpfs), static_cast<int>(tmpfs.MountType));
424
VERIFY_IS_TRUE(tmpfs.TmpfsOptions.has_value());
425
VERIFY_ARE_EQUAL(std::string("size=64k"), tmpfs.TmpfsOptions.value());
426
427
const auto structured = ValidateAndGetCached<ArgType::Mount>(L"type=volume,source=data-volume,target=/data");
428
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Volume), static_cast<int>(structured.MountType));
428
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeVolume), static_cast<int>(structured.MountType));
429
VERIFY_ARE_EQUAL(std::wstring(L"data-volume"), structured.Source);
430
}
431
test/windows/wslc/WSLCCLIMountParserUnitTests.cpp
+93
-92
@@ -62,34 +62,34 @@ namespace {
62
};
63
64
constexpr ValidMountCase c_validMountCases[] = {
65
- {L"type=volume,target=/data", mount::Type::Volume, L"", "/data", false, {}, {}, ""},
66
- {L"source=data-volume,target=/data", mount::Type::Volume, L"data-volume", "/data", false, {}, {}, ""},
65
+ {L"type=volume,target=/data", WSLCMountTypeVolume, L"", "/data", false, {}, {}, ""},
66
+ {L"source=data-volume,target=/data", WSLCMountTypeVolume, L"data-volume", "/data", false, {}, {}, ""},
67
{L"type=volume,source=data-volume,target=/path:voldir",
68
- mount::Type::Volume,
68
+ WSLCMountTypeVolume,
69
L"data-volume",
70
"/path:voldir",
71
false,
72
{},
73
{},
74
""},
75
- {L"TYPE=VOLUME,SOURCE=data-volume,TARGET=/data", mount::Type::Volume, L"data-volume", "/data", false, {}, {}, ""},
76
- {L"type=VoLuMe,source=data-volume,target=/data", mount::Type::Volume, L"data-volume", "/data", false, {}, {}, ""},
77
- {L"type=volume,src=data-volume,dst=/data", mount::Type::Volume, L"data-volume", "/data", false, {}, {}, ""},
78
- {L"type=volume,src=data-volume,destination=/data", mount::Type::Volume, L"data-volume", "/data", false, {}, {}, ""},
79
- {L"type=volume,source=first,source=second,target=/data", mount::Type::Volume, L"second", "/data", false, {}, {}, ""},
75
+ {L"TYPE=VOLUME,SOURCE=data-volume,TARGET=/data", WSLCMountTypeVolume, L"data-volume", "/data", false, {}, {}, ""},
76
+ {L"type=VoLuMe,source=data-volume,target=/data", WSLCMountTypeVolume, L"data-volume", "/data", false, {}, {}, ""},
77
+ {L"type=volume,src=data-volume,dst=/data", WSLCMountTypeVolume, L"data-volume", "/data", false, {}, {}, ""},
78
+ {L"type=volume,src=data-volume,destination=/data", WSLCMountTypeVolume, L"data-volume", "/data", false, {}, {}, ""},
79
+ {L"type=volume,source=first,source=second,target=/data", WSLCMountTypeVolume, L"second", "/data", false, {}, {}, ""},
80
{L"type=volume,source=data-volume,target=/first,target=/second",
81
- mount::Type::Volume,
81
+ WSLCMountTypeVolume,
82
L"data-volume",
83
"/second",
84
false,
85
{},
86
{},
87
""},
88
- {L"type=volume,type=bind,source=C:\\data,target=/data", mount::Type::Bind, L"C:\\data", "/data", false, {}, {}, ""},
89
- {L"type=volume,source=data-volume,target=/data,readonly", mount::Type::Volume, L"data-volume", "/data", true, {}, {}, ""},
90
- {L"type=volume,source=data-volume,target=/data,ro", mount::Type::Volume, L"data-volume", "/data", true, {}, {}, ""},
88
+ {L"type=volume,type=bind,source=C:\\data,target=/data", WSLCMountTypeBind, L"C:\\data", "/data", false, {}, {}, ""},
89
+ {L"type=volume,source=data-volume,target=/data,readonly", WSLCMountTypeVolume, L"data-volume", "/data", true, {}, {}, ""},
90
+ {L"type=volume,source=data-volume,target=/data,ro", WSLCMountTypeVolume, L"data-volume", "/data", true, {}, {}, ""},
91
{L"type=volume,source=data-volume,target=/data,readonly=1",
92
- mount::Type::Volume,
92
+ WSLCMountTypeVolume,
93
L"data-volume",
94
"/data",
95
true,
@@ -97,7 +97,7 @@ namespace {
97
{},
98
""},
99
{L"type=volume,source=data-volume,target=/data,readonly=t",
100
- mount::Type::Volume,
100
+ WSLCMountTypeVolume,
101
L"data-volume",
102
"/data",
103
true,
@@ -105,7 +105,7 @@ namespace {
105
{},
106
""},
107
{L"type=volume,source=data-volume,target=/data,readonly=T",
108
- mount::Type::Volume,
108
+ WSLCMountTypeVolume,
109
L"data-volume",
110
"/data",
111
true,
@@ -113,7 +113,7 @@ namespace {
113
{},
114
""},
115
{L"type=volume,source=data-volume,target=/data,readonly=TRUE",
116
- mount::Type::Volume,
116
+ WSLCMountTypeVolume,
117
L"data-volume",
118
"/data",
119
true,
@@ -121,7 +121,7 @@ namespace {
121
{},
122
""},
123
{L"type=volume,source=data-volume,target=/data,readonly=true",
124
- mount::Type::Volume,
124
+ WSLCMountTypeVolume,
125
L"data-volume",
126
"/data",
127
true,
@@ -129,7 +129,7 @@ namespace {
129
{},
130
""},
131
{L"type=volume,source=data-volume,target=/data,readonly=True",
132
- mount::Type::Volume,
132
+ WSLCMountTypeVolume,
133
L"data-volume",
134
"/data",
135
true,
@@ -137,7 +137,7 @@ namespace {
137
{},
138
""},
139
{L"type=volume,source=data-volume,target=/data,readonly=0",
140
- mount::Type::Volume,
140
+ WSLCMountTypeVolume,
141
L"data-volume",
142
"/data",
143
false,
@@ -145,7 +145,7 @@ namespace {
145
{},
146
""},
147
{L"type=volume,source=data-volume,target=/data,readonly=f",
148
- mount::Type::Volume,
148
+ WSLCMountTypeVolume,
149
L"data-volume",
150
"/data",
151
false,
@@ -153,7 +153,7 @@ namespace {
153
{},
154
""},
155
{L"type=volume,source=data-volume,target=/data,readonly=F",
156
- mount::Type::Volume,
156
+ WSLCMountTypeVolume,
157
L"data-volume",
158
"/data",
159
false,
@@ -161,7 +161,7 @@ namespace {
161
{},
162
""},
163
{L"type=volume,source=data-volume,target=/data,readonly=FALSE",
164
- mount::Type::Volume,
164
+ WSLCMountTypeVolume,
165
L"data-volume",
166
"/data",
167
false,
@@ -169,7 +169,7 @@ namespace {
169
{},
170
""},
171
{L"type=volume,source=data-volume,target=/data,readonly=false",
172
- mount::Type::Volume,
172
+ WSLCMountTypeVolume,
173
L"data-volume",
174
"/data",
175
false,
@@ -177,7 +177,7 @@ namespace {
177
{},
178
""},
179
{L"type=volume,source=data-volume,target=/data,readonly=False",
180
- mount::Type::Volume,
180
+ WSLCMountTypeVolume,
181
L"data-volume",
182
"/data",
183
false,
@@ -185,63 +185,70 @@ namespace {
185
{},
186
""},
187
{L"type=volume,source=data-volume,target=/data,readonly=true,readonly=false",
188
- mount::Type::Volume,
188
+ WSLCMountTypeVolume,
189
L"data-volume",
190
"/data",
191
false,
192
{},
193
{},
194
""},
195
- {L"type=bind,\"source=C:\\mount,a\",target=/data", mount::Type::Bind, L"C:\\mount,a", "/data", false, {}, {}, ""},
195
+ {L"type=volume,source=data-volume,target=/data,bind-recursive=enabled",
196
+ WSLCMountTypeVolume,
197
+ L"data-volume",
198
+ "/data",
199
+ false,
200
+ {},
201
+ {},
202
+ ""},
203
+ {L"type=bind,\"source=C:\\mount,a\",target=/data", WSLCMountTypeBind, L"C:\\mount,a", "/data", false, {}, {}, ""},
204
{L"type=bind,source=C:\\mount with spaces,target=/data",
197
- mount::Type::Bind,
205
+ WSLCMountTypeBind,
206
L"C:\\mount with spaces",
207
"/data",
208
false,
209
{},
210
{},
211
""},
204
- {L"type=bind,source=C:\\mount,target=/path:mntdir", mount::Type::Bind, L"C:\\mount", "/path:mntdir", false, {}, {}, ""},
205
- {L"type=bind,source=C:\\,target=/data", mount::Type::Bind, L"C:\\", "/data", false, {}, {}, ""},
206
- {L"type=bind,source=\\\\server\\share,target=/data", mount::Type::Bind, L"\\\\server\\share", "/data", false, {}, {}, ""},
212
+ {L"type=bind,source=C:\\mount,target=/path:mntdir", WSLCMountTypeBind, L"C:\\mount", "/path:mntdir", false, {}, {}, ""},
213
+ {L"type=bind,source=C:\\,target=/data", WSLCMountTypeBind, L"C:\\", "/data", false, {}, {}, ""},
214
+ {L"type=bind,source=\\\\server\\share,target=/data", WSLCMountTypeBind, L"\\\\server\\share", "/data", false, {}, {}, ""},
215
{L"type=bind,source=C:\\mount,target=/data,bind-recursive=enabled",
208
- mount::Type::Bind,
216
+ WSLCMountTypeBind,
217
L"C:\\mount",
218
"/data",
219
false,
220
{},
221
{},
222
""},
215
- {L"type=volume,source=A_,target=/data", mount::Type::Volume, L"A_", "/data", false, {}, {}, ""},
216
- {L"type=volume,source=data.volume-1,target=/data", mount::Type::Volume, L"data.volume-1", "/data", false, {}, {}, ""},
217
- {L"type=tmpfs,target=/tmp", mount::Type::Tmpfs, L"", "/tmp", false, {}, {}, ""},
218
- {L"type=tmpfs,target=/path:tmpfs", mount::Type::Tmpfs, L"", "/path:tmpfs", false, {}, {}, ""},
219
- {L"type=tmpfs,target=/tmp,readonly", mount::Type::Tmpfs, L"", "/tmp", true, {}, {}, "ro"},
220
- {L"type=tmpfs,target=/tmp,tmpfs-size=0", mount::Type::Tmpfs, L"", "/tmp", false, 0, {}, ""},
221
- {L"type=tmpfs,target=/tmp,tmpfs-size=1", mount::Type::Tmpfs, L"", "/tmp", false, 1, {}, "size=1"},
222
- {L"type=tmpfs,target=/tmp,tmpfs-size=1024", mount::Type::Tmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
223
- {L"type=tmpfs,target=/tmp,tmpfs-size=1536", mount::Type::Tmpfs, L"", "/tmp", false, 1536, {}, "size=1536"},
224
- {L"type=tmpfs,target=/tmp,tmpfs-size=1k", mount::Type::Tmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
225
- {L"type=tmpfs,target=/tmp,tmpfs-size=1KB", mount::Type::Tmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
226
- {L"type=tmpfs,target=/tmp,tmpfs-size=1KiB", mount::Type::Tmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
227
- {L"type=tmpfs,target=/tmp,tmpfs-size=1MB", mount::Type::Tmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
228
- {L"type=tmpfs,target=/tmp,tmpfs-size=1MiB", mount::Type::Tmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
229
- {L"type=tmpfs,target=/tmp,tmpfs-size=1GB", mount::Type::Tmpfs, L"", "/tmp", false, 1LL << 30, {}, "size=1g"},
230
- {L"type=tmpfs,target=/tmp,tmpfs-size=1.5MB", mount::Type::Tmpfs, L"", "/tmp", false, 1536LL << 10, {}, "size=1536k"},
231
- {L"type=tmpfs,target=/tmp,tmpfs-size=+1MB", mount::Type::Tmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
232
- {L"type=tmpfs,target=/tmp,tmpfs-size=1e3", mount::Type::Tmpfs, L"", "/tmp", false, 1000, {}, "size=1000"},
233
- {L"type=tmpfs,target=/tmp,tmpfs-mode=0000", mount::Type::Tmpfs, L"", "/tmp", false, {}, 0, ""},
234
- {L"type=tmpfs,target=/tmp,tmpfs-mode=0700", mount::Type::Tmpfs, L"", "/tmp", false, {}, 0700, "mode=700"},
235
- {L"type=tmpfs,target=/tmp,tmpfs-mode=+0700", mount::Type::Tmpfs, L"", "/tmp", false, {}, 0700, "mode=700"},
223
+ {L"type=volume,source=A_,target=/data", WSLCMountTypeVolume, L"A_", "/data", false, {}, {}, ""},
224
+ {L"type=volume,source=data.volume-1,target=/data", WSLCMountTypeVolume, L"data.volume-1", "/data", false, {}, {}, ""},
225
+ {L"type=tmpfs,target=/tmp", WSLCMountTypeTmpfs, L"", "/tmp", false, {}, {}, ""},
226
+ {L"type=tmpfs,target=/path:tmpfs", WSLCMountTypeTmpfs, L"", "/path:tmpfs", false, {}, {}, ""},
227
+ {L"type=tmpfs,target=/tmp,readonly", WSLCMountTypeTmpfs, L"", "/tmp", true, {}, {}, "ro"},
228
+ {L"type=tmpfs,target=/tmp,tmpfs-size=0", WSLCMountTypeTmpfs, L"", "/tmp", false, 0, {}, ""},
229
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1", WSLCMountTypeTmpfs, L"", "/tmp", false, 1, {}, "size=1"},
230
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1024", WSLCMountTypeTmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
231
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1536", WSLCMountTypeTmpfs, L"", "/tmp", false, 1536, {}, "size=1536"},
232
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1k", WSLCMountTypeTmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
233
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1KB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
234
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1KiB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1024, {}, "size=1k"},
235
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1MB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
236
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1MiB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
237
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1GB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1LL << 30, {}, "size=1g"},
238
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1.5MB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1536LL << 10, {}, "size=1536k"},
239
+ {L"type=tmpfs,target=/tmp,tmpfs-size=+1MB", WSLCMountTypeTmpfs, L"", "/tmp", false, 1LL << 20, {}, "size=1m"},
240
+ {L"type=tmpfs,target=/tmp,tmpfs-size=1e3", WSLCMountTypeTmpfs, L"", "/tmp", false, 1000, {}, "size=1000"},
241
+ {L"type=tmpfs,target=/tmp,tmpfs-mode=0000", WSLCMountTypeTmpfs, L"", "/tmp", false, {}, 0, ""},
242
+ {L"type=tmpfs,target=/tmp,tmpfs-mode=0700", WSLCMountTypeTmpfs, L"", "/tmp", false, {}, 0700, "mode=700"},
243
{L"type=tmpfs,target=/tmp,tmpfs-size=1MB,tmpfs-mode=0700,readonly",
237
- mount::Type::Tmpfs,
244
+ WSLCMountTypeTmpfs,
245
L"",
246
"/tmp",
247
true,
248
1LL << 20,
249
0700,
250
"ro,mode=700,size=1m"},
244
- {L"type=tmpfs,target=/tmp,tmpfs-size=0,tmpfs-mode=0000,readonly=false", mount::Type::Tmpfs, L"", "/tmp", false, 0, 0, ""},
251
+ {L"type=tmpfs,target=/tmp,tmpfs-size=0,tmpfs-mode=0000,readonly=false", WSLCMountTypeTmpfs, L"", "/tmp", false, 0, 0, ""},
252
};
253
254
const InvalidMountCase c_invalidMountCases[] = {
@@ -339,8 +346,6 @@ namespace {
346
Localization::WSLCCLI_MountOptionFamilyMismatchError(L"volume-*", L"bind")},
347
{L"type=volume,source=data-volume,target=/data,bind-propagation=rprivate",
348
Localization::WSLCCLI_MountOptionFamilyMismatchError(L"bind-*", L"volume")},
342
- {L"type=volume,source=data-volume,target=/data,bind-recursive=enabled",
343
- Localization::WSLCCLI_MountOptionFamilyMismatchError(L"bind-*", L"volume")},
349
{L"type=volume,source=data-volume,target=/data,tmpfs-size=1m",
350
Localization::WSLCCLI_MountOptionFamilyMismatchError(L"tmpfs-*", L"volume")},
351
{L"type=tmpfs,target=/tmp,volume-label=a=b", Localization::WSLCCLI_MountOptionFamilyMismatchError(L"volume-*", L"tmpfs")},
@@ -357,6 +362,7 @@ namespace {
362
{L"type=tmpfs,target=/tmp,tmpfs-size=9223372036854775808",
363
Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-size", L"9223372036854775808")},
364
{L"type=tmpfs,target=/tmp,tmpfs-mode=", Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-mode", L"")},
365
+ {L"type=tmpfs,target=/tmp,tmpfs-mode=+0700", Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-mode", L"+0700")},
366
{L"type=tmpfs,target=/tmp,tmpfs-mode=-1", Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-mode", L"-1")},
367
{L"type=tmpfs,target=/tmp,tmpfs-mode=8", Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-mode", L"8")},
368
{L"type=tmpfs,target=/tmp,tmpfs-mode=0899", Localization::WSLCCLI_MountInvalidValueError(L"tmpfs-mode", L"0899")},
@@ -394,7 +400,7 @@ class WSLCCLIMountParserUnitTests
400
VERIFY_ARE_EQUAL(testCase.TmpfsMode.value(), actual.TmpfsMode.value());
401
}
402
397
- const auto actualTmpfsOptions = actual.MountType == mount::Type::Tmpfs ? mount::FormatTmpfsOptions(actual) : std::string{};
403
+ const auto actualTmpfsOptions = actual.MountType == WSLCMountTypeTmpfs ? mount::FormatTmpfsOptions(actual) : std::string{};
404
VERIFY_ARE_EQUAL(std::string(testCase.TmpfsOptions), actualTmpfsOptions);
405
}
406
}
@@ -434,20 +440,36 @@ class WSLCCLIMountParserUnitTests
440
TEST_METHOD(Volume_ValidCases)
441
{
442
const auto bind = mount::ParseDockerVolumeString(LR"(C:\hostPath:/data:ro)");
437
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Bind), static_cast<int>(bind.MountType));
443
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeBind), static_cast<int>(bind.MountType));
444
VERIFY_ARE_EQUAL(std::wstring(LR"(C:\hostPath)"), bind.Source);
445
VERIFY_ARE_EQUAL(std::string("/data"), bind.Target);
446
VERIFY_IS_TRUE(bind.ReadOnly);
447
VERIFY_ARE_EQUAL(static_cast<int>(mount::BindSourcePolicy::CreateIfMissing), static_cast<int>(bind.BindSource));
448
449
+ const auto relativeBind = mount::ParseDockerVolumeString(L".\\mount:/data");
450
+ VERIFY_ARE_EQUAL(std::filesystem::weakly_canonical(std::filesystem::current_path() / L"mount").wstring(), relativeBind.Source);
451
+
452
const auto volume = mount::ParseDockerVolumeString(L"named-volume:/data");
444
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Volume), static_cast<int>(volume.MountType));
453
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeVolume), static_cast<int>(volume.MountType));
454
VERIFY_ARE_EQUAL(std::wstring(L"named-volume"), volume.Source);
455
VERIFY_ARE_EQUAL(std::string("/data"), volume.Target);
456
VERIFY_IS_FALSE(volume.ReadOnly);
457
VERIFY_ARE_EQUAL(static_cast<int>(mount::BindSourcePolicy::RequireExisting), static_cast<int>(volume.BindSource));
458
}
459
460
+ TEST_METHOD(Volume_InvalidHostPath)
461
+ {
462
+ try
463
+ {
464
+ (void)mount::ParseDockerVolumeString(L"::/container:ro");
465
+ VERIFY_FAIL(L"Expected MountParseException for an invalid host path");
466
+ }
467
+ catch (const mount::MountParseException& ex)
468
+ {
469
+ VERIFY_ARE_EQUAL(Localization::WSLCCLI_VolumeHostPathInvalid(L"::/container:ro", L":"), ex.Reason());
470
+ }
471
+ }
472
+
473
TEST_METHOD(Mount_DotRelativeBindSourceUsesCurrentDirectory)
474
{
475
const auto expected = (std::filesystem::current_path() / L"mount").lexically_normal().wstring();
@@ -458,28 +480,28 @@ class WSLCCLIMountParserUnitTests
480
TEST_METHOD(Mount_TypedSpecsAreValidated)
481
{
482
const mount::Spec relativeBind{
461
- .MountType = mount::Type::Bind,
483
+ .MountType = WSLCMountTypeBind,
484
.Source = L"relative",
485
.Target = "/data",
486
};
487
VERIFY_THROWS(mount::ValidateMountSpec(relativeBind), mount::MountValidationException);
488
489
const mount::Spec relativeTarget{
468
- .MountType = mount::Type::Volume,
490
+ .MountType = WSLCMountTypeVolume,
491
.Source = L"data-volume",
492
.Target = "data",
493
};
494
VERIFY_THROWS(mount::ValidateMountSpec(relativeTarget), mount::MountValidationException);
495
496
const mount::Spec tmpfsWithSource{
475
- .MountType = mount::Type::Tmpfs,
497
+ .MountType = WSLCMountTypeTmpfs,
498
.Source = L"data-volume",
499
.Target = "/data",
500
};
501
VERIFY_THROWS(mount::ValidateMountSpec(tmpfsWithSource), mount::MountValidationException);
502
503
const mount::Spec bindWithTmpfsOptions{
482
- .MountType = mount::Type::Bind,
504
+ .MountType = WSLCMountTypeBind,
505
.Source = L"C:\\data",
506
.Target = "/data",
507
.TmpfsSizeBytes = 1024,
@@ -487,14 +509,14 @@ class WSLCCLIMountParserUnitTests
509
VERIFY_THROWS(mount::ValidateMountSpec(bindWithTmpfsOptions), mount::MountValidationException);
510
511
const mount::Spec negativeTmpfsSize{
490
- .MountType = mount::Type::Tmpfs,
512
+ .MountType = WSLCMountTypeTmpfs,
513
.Target = "/data",
514
.TmpfsSizeBytes = -1,
515
};
516
VERIFY_THROWS(mount::ValidateMountSpec(negativeTmpfsSize), mount::MountValidationException);
517
518
const mount::Spec tmpfs{
497
- .MountType = mount::Type::Tmpfs,
519
+ .MountType = WSLCMountTypeTmpfs,
520
.Target = "/data",
521
.TmpfsSizeBytes = 1024,
522
.TmpfsMode = 0700,
@@ -502,8 +524,8 @@ class WSLCCLIMountParserUnitTests
524
VERIFY_NO_THROW(mount::ValidateMountSpec(tmpfs));
525
526
const mount::Spec duplicateMounts[] = {
505
- {.MountType = mount::Type::Tmpfs, .Target = "/data"},
506
- {.MountType = mount::Type::Volume, .Source = L"data-volume", .Target = "/data/"},
527
+ {.MountType = WSLCMountTypeTmpfs, .Target = "/data"},
528
+ {.MountType = WSLCMountTypeVolume, .Source = L"data-volume", .Target = "/data/"},
529
};
530
try
531
{
@@ -515,33 +537,12 @@ class WSLCCLIMountParserUnitTests
537
VERIFY_ARE_EQUAL(static_cast<int>(mount::ValidationError::DuplicateDestination), static_cast<int>(ex.Error()));
538
VERIFY_ARE_EQUAL(std::string("/data"), ex.Destination());
539
}
518
- }
540
520
- TEST_METHOD(Mount_DuplicateDestinationsAreRejected)
521
- {
522
- ContainerOptions options;
523
- options.Mounts = {
524
- mount::ParseDockerTmpfsString(L"/data"),
525
- {.MountType = mount::Type::Volume, .Source = L"data-volume", .Target = "/data/"},
526
- };
527
- VERIFY_THROWS(ValidateUniqueMountDestinations(options), wil::ResultException);
528
-
529
- options.Mounts = {
530
- {.MountType = mount::Type::Tmpfs, .Target = "/data/../cache"},
531
- {.MountType = mount::Type::Volume, .Source = L"data-volume", .Target = "/cache"},
532
- };
533
- VERIFY_THROWS(ValidateUniqueMountDestinations(options), wil::ResultException);
534
- }
535
-
536
- TEST_METHOD(Mount_UniqueDestinationsAreAccepted)
537
- {
538
- ContainerOptions options;
539
- options.Mounts = {
540
- mount::ParseDockerTmpfsString(L"/cache"),
541
- {.MountType = mount::Type::Volume, .Source = L"data-volume", .Target = "/data"},
542
- {.MountType = mount::Type::Bind, .Source = L"C:\\logs", .Target = "/logs"},
541
+ const mount::Spec distinctBackslashMounts[] = {
542
+ {.MountType = WSLCMountTypeTmpfs, .Target = "/data\\cache"},
543
+ {.MountType = WSLCMountTypeVolume, .Source = L"data-volume", .Target = "/data/cache"},
544
};
544
- VERIFY_NO_THROW(ValidateUniqueMountDestinations(options));
545
+ VERIFY_NO_THROW(mount::ValidateMountCollection(distinctBackslashMounts));
546
}
547
};
548
test/windows/wslc/WSLCCLITmpfsParserUnitTests.cpp
+1
-1
@@ -41,7 +41,7 @@ class WSLCCLITmpfsParserUnitTests
41
for (const auto& [input, expectedTarget, expectedOptions] : validTmpfsSpecs)
42
{
43
const auto result = mount::ParseDockerTmpfsString(input);
44
- VERIFY_ARE_EQUAL(static_cast<int>(mount::Type::Tmpfs), static_cast<int>(result.MountType));
44
+ VERIFY_ARE_EQUAL(static_cast<int>(WSLCMountTypeTmpfs), static_cast<int>(result.MountType));
45
VERIFY_ARE_EQUAL(expectedTarget, result.Target);
46
VERIFY_IS_TRUE(result.TmpfsOptions.has_value());
47
VERIFY_ARE_EQUAL(expectedOptions, result.TmpfsOptions.value());