master
cpp 1,838 lines 62.8 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 wslcsdk.cpp
8
9 Abstract:
10
11 This file contains the public WSLC Client SDK api implementations.
12
13 --*/
14 #include "precomp.h"
15
16 #include "wslcsdk.h"
17 #include "WslcsdkPrivate.h"
18 #include "Defaults.h"
19 #include "ProgressCallback.h"
20 #include "CrashDumpCallback.h"
21 #include "install.h"
22 #include "Localization.h"
23 #include "WslInstall.h"
24 #include "wslutil.h"
25 #include "APICompat.h"
26 #include "WindowsUpdateIntegration.h"
27
28 using namespace std::string_view_literals;
29 using namespace wsl::windows::common::wslutil;
30
31 namespace {
32
33 #define WSLC_FLAG_VALUE_ASSERT(_wlsc_name_, _wslc_name_) \
34 static_assert(_wlsc_name_ == _wslc_name_, "Flag values differ: " #_wlsc_name_ " != " #_wslc_name_);
35
36 template <typename Flags>
37 struct FlagsTraits
38 {
39 static_assert(false, "Flags used without traits defined.");
40 };
41
42 template <>
43 struct FlagsTraits<WslcSessionFeatureFlags>
44 {
45 using WslcType = WSLCFeatureFlags;
46 constexpr static WslcSessionFeatureFlags Mask = WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU;
47 WSLC_FLAG_VALUE_ASSERT(WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU, WslcFeatureFlagsGPU);
48 };
49
50 template <>
51 struct FlagsTraits<WslcContainerFlags>
52 {
53 using WslcType = WSLCContainerFlags;
54 constexpr static WslcContainerFlags Mask = WSLC_CONTAINER_FLAG_AUTO_REMOVE | WSLC_CONTAINER_FLAG_ENABLE_GPU;
55 WSLC_FLAG_VALUE_ASSERT(WSLC_CONTAINER_FLAG_AUTO_REMOVE, WSLCContainerFlagsRm);
56 WSLC_FLAG_VALUE_ASSERT(WSLC_CONTAINER_FLAG_ENABLE_GPU, WSLCContainerFlagsGpu);
57 // TODO: WSLC_CONTAINER_FLAG_PRIVILEGED has no associated runtime value
58 };
59
60 template <>
61 struct FlagsTraits<WslcContainerStartFlags>
62 {
63 using WslcType = WSLCContainerStartFlags;
64 constexpr static WslcContainerStartFlags Mask = WSLC_CONTAINER_START_FLAG_ATTACH;
65 WSLC_FLAG_VALUE_ASSERT(WSLC_CONTAINER_START_FLAG_ATTACH, WSLCContainerStartFlagsAttach);
66 };
67
68 template <>
69 struct FlagsTraits<WslcDeleteContainerFlags>
70 {
71 using WslcType = WSLCDeleteFlags;
72 constexpr static WslcDeleteContainerFlags Mask = WSLC_DELETE_CONTAINER_FLAG_FORCE;
73 WSLC_FLAG_VALUE_ASSERT(WSLC_DELETE_CONTAINER_FLAG_FORCE, WSLCDeleteFlagsForce);
74 };
75
76 template <typename Flags>
77 typename FlagsTraits<Flags>::WslcType ConvertFlags(Flags flags)
78 {
79 using traits = FlagsTraits<Flags>;
80 return static_cast<typename traits::WslcType>(flags & traits::Mask);
81 }
82
83 WSLCSignal Convert(WslcSignal signal)
84 {
85 switch (signal)
86 {
87 case WSLC_SIGNAL_NONE:
88 return WSLCSignal::WSLCSignalNone;
89 case WSLC_SIGNAL_SIGHUP:
90 return WSLCSignal::WSLCSignalSIGHUP;
91 case WSLC_SIGNAL_SIGINT:
92 return WSLCSignal::WSLCSignalSIGINT;
93 case WSLC_SIGNAL_SIGQUIT:
94 return WSLCSignal::WSLCSignalSIGQUIT;
95 case WSLC_SIGNAL_SIGKILL:
96 return WSLCSignal::WSLCSignalSIGKILL;
97 case WSLC_SIGNAL_SIGTERM:
98 return WSLCSignal::WSLCSignalSIGTERM;
99 default:
100 THROW_HR_MSG(E_INVALIDARG, "Invalid WslcSignal: %i", signal);
101 }
102 }
103
104 PCSTR Convert(WslcContainerNetworkingMode mode)
105 {
106 switch (mode)
107 {
108 case WSLC_CONTAINER_NETWORKING_MODE_NONE:
109 return "none";
110 case WSLC_CONTAINER_NETWORKING_MODE_BRIDGED:
111 return "bridge";
112 default:
113 THROW_HR_MSG(E_INVALIDARG, "Invalid WslcContainerNetworkingMode: %i", mode);
114 }
115 }
116
117 void ConvertSHA256Hash(const char* hashString, uint8_t sha256[32])
118 {
119 static constexpr std::string_view s_sha256Prefix = "sha256:"sv;
120 static constexpr size_t s_sha256ByteCount = 32;
121
122 THROW_HR_IF_NULL(E_POINTER, sha256);
123
124 if (!hashString)
125 {
126 return;
127 }
128
129 std::string_view hashStringView{hashString};
130 THROW_HR_IF_MSG(
131 E_UNEXPECTED,
132 hashStringView.length() < s_sha256Prefix.length() || hashStringView.substr(0, s_sha256Prefix.length()) != s_sha256Prefix,
133 "Unexpected hash specifier: %hs",
134 hashString);
135
136 auto hashBytes = wsl::windows::common::string::HexToBytes(hashStringView.substr(s_sha256Prefix.length()));
137 THROW_HR_IF_MSG(E_INVALIDARG, hashBytes.size() != s_sha256ByteCount, "SHA256 hash was not 32 bytes: %zu", hashBytes.size());
138 memcpy(sha256, &hashBytes[0], s_sha256ByteCount);
139 }
140
141 // TODO: Replace with a derivation of wsl::windows::common::ExecutionContext when telemetry changes are introduced
142 // This will make usage even easier as we can just use the WIL result macros directly.
143 struct ErrorInfoWrapper
144 {
145 ErrorInfoWrapper(PWSTR* errorMessage) : m_errorMessage(errorMessage)
146 {
147 if (m_errorMessage)
148 {
149 *m_errorMessage = nullptr;
150 }
151 }
152
153 void GetErrorInfoFromCOM()
154 {
155 if (m_errorMessage)
156 {
157 auto errorInfo = wsl::windows::common::wslutil::GetCOMErrorInfo();
158 if (errorInfo)
159 {
160 *m_errorMessage = wil::make_unique_string<wil::unique_cotaskmem_string>(errorInfo->Message.get()).release();
161 }
162 }
163 }
164
165 HRESULT CaptureResult(HRESULT hr)
166 {
167 m_hr = hr;
168 if (FAILED_LOG(m_hr.value()))
169 {
170 GetErrorInfoFromCOM();
171 }
172 return m_hr.value();
173 }
174
175 operator HRESULT() const
176 {
177 THROW_HR_IF(E_UNEXPECTED, !m_hr);
178 return m_hr.value();
179 }
180
181 private:
182 PWSTR* m_errorMessage = nullptr;
183 std::optional<HRESULT> m_hr;
184 };
185
186 void EnsureAbsolutePath(const std::filesystem::path& path, bool containerPath)
187 {
188 THROW_HR_IF(E_INVALIDARG, path.empty());
189
190 if (containerPath)
191 {
192 auto pathString = path.native();
193 // Not allowed to mount to root
194 THROW_HR_IF(E_INVALIDARG, pathString.length() < 2);
195 // Must be absolute
196 THROW_HR_IF(E_INVALIDARG, pathString[0] != L'/');
197 }
198 else
199 {
200 THROW_HR_IF(E_INVALIDARG, path.is_relative());
201 }
202 }
203 static HRESULT InetNtopToHresult(int af, const void* src, char* dst, size_t dstCount)
204 {
205 if (inet_ntop(af, src, dst, dstCount) == nullptr)
206 {
207 return HRESULT_FROM_WIN32(WSAGetLastError());
208 }
209 return S_OK;
210 }
211
212 bool CopyProcessSettingsToRuntime(WSLCCompatProcessOptions& runtimeOptions, const WslcContainerProcessOptionsInternal* initProcessOptions)
213 {
214 if (initProcessOptions)
215 {
216 runtimeOptions.CurrentDirectory = initProcessOptions->workingDirectory;
217 runtimeOptions.CommandLine.Values = initProcessOptions->commandLine;
218 runtimeOptions.CommandLine.Count = initProcessOptions->commandLineCount;
219 runtimeOptions.Environment.Values = initProcessOptions->environment;
220 runtimeOptions.Environment.Count = initProcessOptions->environmentCount;
221
222 // TODO: No user access
223 // containerOptions.InitProcessOptions.Flags;
224 // containerOptions.InitProcessOptions.TtyRows;
225 // containerOptions.InitProcessOptions.TtyColumns;
226 // containerOptions.InitProcessOptions.User;
227
228 return true;
229 }
230 else
231 {
232 return false;
233 }
234 }
235
236 // Normalizes file inputs to HANDLE+length.
237 struct ImageFileResolver
238 {
239 ImageFileResolver(PCWSTR path) : m_fileHandle(INVALID_HANDLE_VALUE)
240 {
241 THROW_HR_IF_NULL(E_POINTER, path);
242
243 wil::unique_handle imageFileHandle{
244 CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
245 THROW_LAST_ERROR_IF(!imageFileHandle);
246
247 LARGE_INTEGER fileSize{};
248 THROW_IF_WIN32_BOOL_FALSE(GetFileSizeEx(imageFileHandle.get(), &fileSize));
249
250 m_fileHandle = std::move(imageFileHandle);
251 m_length = static_cast<ULONGLONG>(fileSize.QuadPart);
252 }
253
254 ImageFileResolver(HANDLE imageContent, uint64_t imageContentLength) : m_fileHandle(imageContent)
255 {
256 THROW_HR_IF(E_INVALIDARG, imageContent == nullptr || imageContent == INVALID_HANDLE_VALUE);
257 THROW_HR_IF(E_INVALIDARG, imageContentLength == 0);
258
259 m_length = imageContentLength;
260 }
261
262 HANDLE Handle() const
263 {
264 return m_fileHandle.Get();
265 }
266
267 ULONGLONG Length() const
268 {
269 return m_length;
270 }
271
272 private:
273 wsl::windows::common::io::HandleWrapper m_fileHandle;
274 ULONGLONG m_length;
275 };
276
277 // TODO: Implement Server SKU specific checks
278 bool NeedsVirtualMachineServicesInstalled()
279 {
280 return !wsl::windows::common::wslutil::IsVirtualMachinePlatformInstalled();
281 }
282
283 #define WSLC_API_MIN_VERSION_SUPPORTED 2, 8, 0
284
285 bool DoesWslRuntimeVersionSupportWslc(const std::optional<std::tuple<uint32_t, uint32_t, uint32_t>>& version)
286 {
287 constexpr auto minimalPackageVersion = std::tuple<uint32_t, uint32_t, uint32_t>{WSLC_API_MIN_VERSION_SUPPORTED};
288 return version.has_value() && version >= minimalPackageVersion;
289 }
290
291 enum class WslRuntimeState
292 {
293 NotInstalled,
294 InstalledWithoutWslcSupport,
295 InstalledWithWslcSupport,
296 };
297
298 WslRuntimeState CheckWslRuntimeState()
299 {
300 auto version = wsl::windows::common::wslutil::GetInstalledPackageVersion();
301
302 if (!version.has_value())
303 {
304 return WslRuntimeState::NotInstalled;
305 }
306
307 return DoesWslRuntimeVersionSupportWslc(version) ? WslRuntimeState::InstalledWithWslcSupport : WslRuntimeState::InstalledWithoutWslcSupport;
308 }
309
310 std::pair<wil::com_ptr<IWSLCCompatSessionManager>, HRESULT> CreateSessionManagerRaw()
311 {
312 wil::com_ptr<IWSLCCompatSessionManager> result;
313 HRESULT hr = CoCreateInstance(__uuidof(WSLCSessionManager), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&result));
314 if (SUCCEEDED(hr))
315 {
316 const WSLCCompatVersion clientVersion{WSL_PACKAGE_VERSION_MAJOR, WSL_PACKAGE_VERSION_MINOR, WSL_PACKAGE_VERSION_REVISION};
317 BOOL isSupported = FALSE;
318 THROW_IF_FAILED(result->IsClientVersionSupported(&clientVersion, &isSupported));
319
320 if (!isSupported)
321 {
322 LOG_HR_MSG(
323 WSLC_E_SDK_UPDATE_NEEDED,
324 "WSLC SDK update required. Current SDK version: %lu.%lu.%lu",
325 WSL_PACKAGE_VERSION_MAJOR,
326 WSL_PACKAGE_VERSION_MINOR,
327 WSL_PACKAGE_VERSION_REVISION);
328
329 return {result, WSLC_E_SDK_UPDATE_NEEDED};
330 }
331 }
332
333 return {result, hr};
334 }
335
336 wil::com_ptr<IWSLCCompatSessionManager> CreateSessionManager()
337 {
338 auto [result, hr] = CreateSessionManagerRaw();
339
340 if (hr == REGDB_E_CLASSNOTREG)
341 {
342 WslRuntimeState currentState = CheckWslRuntimeState();
343 THROW_WIN32_IF_MSG(
344 ERROR_NOT_SUPPORTED,
345 currentState == WslRuntimeState::InstalledWithoutWslcSupport,
346 "The currently installed WSL version does not support WSLC.");
347 THROW_HR_IF_MSG(
348 hr,
349 currentState == WslRuntimeState::InstalledWithWslcSupport,
350 "The WSL install appears to be corrupted; session manager class was not registered.");
351 }
352
353 THROW_IF_FAILED(hr);
354
355 wsl::windows::common::security::ConfigureForCOMImpersonation(result.get());
356
357 return result;
358 }
359
360 } // namespace
361
362 // SESSION DEFINITIONS
363 STDAPI WslcInitSessionSettings(_In_ PCWSTR name, _In_ PCWSTR storagePath, _Out_ WslcSessionSettings* sessionSettings)
364 try
365 {
366 RETURN_HR_IF_NULL(E_POINTER, name);
367 RETURN_HR_IF_NULL(E_POINTER, storagePath);
368
369 auto internalType = CheckAndGetInternalType(sessionSettings);
370
371 *internalType = {};
372
373 internalType->displayName = name;
374 internalType->storagePath = storagePath;
375 internalType->cpuCount = s_DefaultCPUCount;
376 internalType->memoryMb = s_DefaultMemoryMB;
377 internalType->timeoutMS = s_DefaultBootTimeout;
378 internalType->vhdRequirements.sizeBytes = s_DefaultStorageSize;
379
380 return S_OK;
381 }
382 CATCH_RETURN();
383
384 STDAPI WslcSetSessionSettingsCpuCount(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t cpuCount)
385 try
386 {
387 auto internalType = CheckAndGetInternalType(sessionSettings);
388
389 if (cpuCount)
390 {
391 internalType->cpuCount = cpuCount;
392 }
393 else
394 {
395 internalType->cpuCount = s_DefaultCPUCount;
396 }
397
398 return S_OK;
399 }
400 CATCH_RETURN();
401
402 STDAPI WslcSetSessionSettingsMemory(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t memoryMB)
403 try
404 {
405 auto internalType = CheckAndGetInternalType(sessionSettings);
406
407 if (memoryMB)
408 {
409 internalType->memoryMb = memoryMB;
410 }
411 else
412 {
413 internalType->memoryMb = s_DefaultMemoryMB;
414 }
415
416 return S_OK;
417 }
418 CATCH_RETURN();
419
420 STDAPI WslcCreateSession(_In_ WslcSessionSettings* sessionSettings, _Out_ WslcSession* session, _Outptr_opt_result_z_ PWSTR* errorMessage)
421 try
422 {
423 RETURN_HR_IF_NULL(E_POINTER, session);
424 *session = nullptr;
425 ErrorInfoWrapper errorInfoWrapper{errorMessage};
426 auto internalType = CheckAndGetInternalType(sessionSettings);
427
428 wil::com_ptr<IWSLCCompatSessionManager> sessionManager = CreateSessionManager();
429
430 auto result = std::make_unique<WslcSessionImpl>();
431 WSLCCompatSessionSettings runtimeSettings{};
432 runtimeSettings.DisplayName = internalType->displayName;
433 runtimeSettings.StoragePath = internalType->storagePath;
434 runtimeSettings.MaximumStorageSizeMb = internalType->vhdRequirements.sizeBytes / _1MB;
435 runtimeSettings.CpuCount = internalType->cpuCount;
436 runtimeSettings.MemoryMb = internalType->memoryMb;
437 runtimeSettings.BootTimeoutMs = internalType->timeoutMS;
438 runtimeSettings.NetworkingMode = WSLCNetworkingModeConsomme;
439 runtimeSettings.FeatureFlags = ConvertFlags(internalType->featureFlags);
440 WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsVirtioFs);
441 WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsDnsTunneling);
442
443 if (SUCCEEDED(errorInfoWrapper.CaptureResult(
444 sessionManager->CreateSession(&runtimeSettings, WSLCSessionFlagsNone, nullptr, &result->session))))
445 {
446 wsl::windows::common::security::ConfigureForCOMImpersonation(result->session.get());
447 *session = reinterpret_cast<WslcSession>(result.release());
448 }
449
450 return errorInfoWrapper;
451 }
452 CATCH_RETURN();
453
454 STDAPI WslcTerminateSession(_In_ WslcSession session)
455 try
456 {
457 auto internalType = CheckAndGetInternalType(session);
458 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
459
460 RETURN_HR(internalType->session->Terminate());
461 }
462 CATCH_RETURN();
463
464 STDAPI WslcSetSessionSettingsTimeout(_In_ WslcSessionSettings* sessionSettings, _In_ uint32_t timeoutMS)
465 try
466 {
467 auto internalType = CheckAndGetInternalType(sessionSettings);
468
469 if (timeoutMS)
470 {
471 internalType->timeoutMS = timeoutMS;
472 }
473 else
474 {
475 internalType->timeoutMS = s_DefaultBootTimeout;
476 }
477
478 return S_OK;
479 }
480 CATCH_RETURN();
481
482 STDAPI WslcCreateSessionVhdVolume(_In_ WslcSession session, _In_ const WslcVhdRequirements* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
483 try
484 {
485 ErrorInfoWrapper errorInfoWrapper{errorMessage};
486
487 auto internalType = CheckAndGetInternalType(session);
488 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
489 RETURN_HR_IF_NULL(E_POINTER, options);
490
491 RETURN_HR_IF_NULL(E_INVALIDARG, options->name);
492 RETURN_HR_IF(E_INVALIDARG, options->sizeBytes == 0);
493
494 // Reject unknown flag bits so future additions can't be silently ignored.
495 constexpr WslcVhdRequirementsFlags c_knownFlags = WSLC_VHD_REQ_FLAG_OWNER;
496 RETURN_HR_IF(E_INVALIDARG, (options->flags & ~c_knownFlags) != WSLC_VHD_REQ_FLAG_NONE);
497
498 // Hold uid/gid strings at function scope so the c_str() pointers stored
499 // in driverOpts stay valid through CreateVolume.
500 const auto sizeStr = std::to_string(options->sizeBytes);
501 std::string uidStr;
502 std::string gidStr;
503
504 std::vector<WSLCCompatDriverOption> driverOpts;
505 driverOpts.push_back({"SizeBytes", sizeStr.c_str()});
506
507 if (options->type == WSLC_VHD_TYPE_FIXED)
508 {
509 driverOpts.push_back({"Fixed", "true"});
510 }
511 else
512 {
513 RETURN_HR_IF(E_INVALIDARG, options->type != WSLC_VHD_TYPE_DYNAMIC);
514 }
515
516 if (WI_IsFlagSet(options->flags, WSLC_VHD_REQ_FLAG_OWNER))
517 {
518 uidStr = std::to_string(options->uid);
519 gidStr = std::to_string(options->gid);
520 driverOpts.push_back({"Uid", uidStr.c_str()});
521 driverOpts.push_back({"Gid", gidStr.c_str()});
522 }
523
524 WSLCCompatVolumeOptions volumeOptions{};
525 volumeOptions.Name = options->name;
526 volumeOptions.Driver = "vhd";
527 volumeOptions.DriverOpts = driverOpts.data();
528 volumeOptions.DriverOptsCount = static_cast<ULONG>(driverOpts.size());
529
530 WSLCCompatVolumeInformation volumeInfo{};
531 return errorInfoWrapper.CaptureResult(internalType->session->CreateVolume(&volumeOptions, &volumeInfo));
532 }
533 CATCH_RETURN();
534
535 STDAPI WslcDeleteSessionVhdVolume(_In_ WslcSession session, _In_z_ PCSTR name, _Outptr_opt_result_z_ PWSTR* errorMessage)
536 try
537 {
538 ErrorInfoWrapper errorInfoWrapper{errorMessage};
539
540 auto internalType = CheckAndGetInternalType(session);
541 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
542 RETURN_HR_IF_NULL(E_POINTER, name);
543
544 return errorInfoWrapper.CaptureResult(internalType->session->DeleteVolume(name));
545 }
546 CATCH_RETURN();
547
548 STDAPI WslcSetSessionSettingsVhd(_In_ WslcSessionSettings* sessionSettings, _In_opt_ const WslcVhdRequirements* vhdRequirements)
549 try
550 {
551 auto internalType = CheckAndGetInternalType(sessionSettings);
552
553 if (vhdRequirements)
554 {
555 RETURN_HR_IF(E_INVALIDARG, vhdRequirements->sizeBytes == 0);
556 RETURN_HR_IF(E_NOTIMPL, vhdRequirements->type != WSLC_VHD_TYPE_DYNAMIC);
557
558 // Owner is only honored on named volumes; reject here so callers can't
559 // mistakenly believe it applied to the session rootfs VHD.
560 RETURN_HR_IF(E_INVALIDARG, vhdRequirements->flags != WSLC_VHD_REQ_FLAG_NONE);
561
562 internalType->vhdRequirements = *vhdRequirements;
563 }
564 else
565 {
566 internalType->vhdRequirements = {};
567 internalType->vhdRequirements.sizeBytes = s_DefaultStorageSize;
568 }
569
570 return S_OK;
571 }
572 CATCH_RETURN();
573
574 STDAPI WslcSetSessionSettingsFeatureFlags(_In_ WslcSessionSettings* sessionSettings, _In_ WslcSessionFeatureFlags flags)
575 try
576 {
577 auto internalType = CheckAndGetInternalType(sessionSettings);
578
579 internalType->featureFlags = flags;
580
581 return S_OK;
582 }
583 CATCH_RETURN();
584
585 STDAPI WslcGetSessionTerminationEvent(_In_ WslcSession session, _Out_ HANDLE* terminationEvent)
586 try
587 {
588 RETURN_HR_IF_NULL(E_POINTER, terminationEvent);
589 *terminationEvent = nullptr;
590
591 auto internalType = CheckAndGetInternalType(session);
592 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
593
594 RETURN_HR(internalType->session->GetTerminationEvent(terminationEvent));
595 }
596 CATCH_RETURN();
597
598 STDAPI WslcGetSessionTerminationReason(_In_ WslcSession session, _Out_ WslcSessionTerminationReason* reason)
599 try
600 {
601 static_assert(
602 WSLC_SESSION_TERMINATION_REASON_UNKNOWN == WSLCVirtualMachineTerminationReasonUnknown &&
603 WSLC_SESSION_TERMINATION_REASON_SHUTDOWN == WSLCVirtualMachineTerminationReasonShutdown &&
604 WSLC_SESSION_TERMINATION_REASON_CRASHED == WSLCVirtualMachineTerminationReasonCrashed,
605 "Termination reason enum values mismatch.");
606
607 RETURN_HR_IF_NULL(E_POINTER, reason);
608 *reason = WSLC_SESSION_TERMINATION_REASON_UNKNOWN;
609
610 auto internalType = CheckAndGetInternalType(session);
611 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
612
613 WSLCVirtualMachineTerminationReason runtimeReason = WSLCVirtualMachineTerminationReasonUnknown;
614 wil::unique_cotaskmem_string details;
615 RETURN_IF_FAILED(internalType->session->GetTerminationReason(&runtimeReason, &details));
616
617 *reason = static_cast<WslcSessionTerminationReason>(runtimeReason);
618
619 return S_OK;
620 }
621 CATCH_RETURN();
622
623 STDAPI WslcRegisterSessionCrashDumpCallback(
624 _In_ WslcSession session,
625 _In_ WslcSessionCrashDumpCallback crashDumpCallback,
626 _In_opt_ PVOID crashDumpContext,
627 _Out_ WslcCrashDumpSubscription* subscription,
628 _Outptr_opt_result_z_ PWSTR* errorMessage)
629 try
630 {
631 RETURN_HR_IF_NULL(E_POINTER, subscription);
632 *subscription = nullptr;
633 RETURN_HR_IF_NULL(E_INVALIDARG, crashDumpCallback);
634
635 ErrorInfoWrapper errorInfoWrapper{errorMessage};
636 auto internalSession = CheckAndGetInternalType(session);
637 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalSession->session);
638
639 auto result = std::make_unique<WslcCrashDumpSubscriptionImpl>();
640 auto callback = winrt::make_self<CrashDumpCallback>(crashDumpCallback, crashDumpContext);
641 result->callback = callback.get();
642
643 if (SUCCEEDED(errorInfoWrapper.CaptureResult(
644 internalSession->session->RegisterCrashDumpCallback(result->callback.get(), &result->subscription))))
645 {
646 *subscription = reinterpret_cast<WslcCrashDumpSubscription>(result.release());
647 }
648
649 return errorInfoWrapper;
650 }
651 CATCH_RETURN();
652
653 STDAPI WslcReleaseCrashDumpSubscription(_In_ WslcCrashDumpSubscription subscription)
654 try
655 {
656 auto internalType = CheckAndGetInternalTypeUniquePointer(subscription);
657
658 // Release the service-side subscription first so it unregisters cleanly, then drop the shim.
659 internalType->subscription.reset();
660 internalType->callback.reset();
661
662 return S_OK;
663 }
664 CATCH_RETURN();
665
666 STDAPI WslcReleaseSession(_In_ WslcSession session)
667 try
668 {
669 auto internalType = CheckAndGetInternalTypeUniquePointer(session);
670
671 internalType->session.reset();
672
673 return S_OK;
674 }
675 CATCH_RETURN();
676
677 STDAPI WslcReleaseContainer(_In_ WslcContainer container)
678 try
679 {
680 // Reject release attempts originating from the container's own IO thread.
681 {
682 auto* peek = CheckAndGetInternalType(container);
683 auto ioCallback = peek->ioCallbacks.load();
684 RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE), ioCallback && ioCallback->IsOnIOCallbackThread());
685 }
686
687 auto internalType = CheckAndGetInternalTypeUniquePointer(container);
688 auto ioCallback = internalType->ioCallbacks.load();
689 if (ioCallback)
690 {
691 // If the container has an IO callback registered, and the container has exited, wait until the IO callback has processed all IO.
692 try
693 {
694 WSLCContainerState state{};
695 THROW_IF_FAILED(internalType->container->GetState(&state));
696
697 if (state == WslcContainerStateExited || state == WslcContainerStateDeleted)
698 {
699 ioCallback->Complete();
700 }
701 }
702 CATCH_LOG();
703 }
704
705 return S_OK;
706 }
707 CATCH_RETURN();
708
709 STDAPI WslcReleaseProcess(_In_ WslcProcess process)
710 try
711 {
712 // Reject release attempts originating from the process's own IO thread.
713 {
714 auto* peek = CheckAndGetInternalType(process);
715 if (peek->ioCallbacks && peek->ioCallbacks->IsOnIOCallbackThread())
716 {
717 RETURN_HR(HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE_STATE));
718 }
719 }
720
721 auto internalType = CheckAndGetInternalTypeUniquePointer(process);
722 if (internalType->ioCallbacks)
723 {
724 // If the process has an IO callback registered, and the process is exited, wait until the IO callback has processed all IO.
725 // If the process is released while still running, cancel the IO callback so we don't get stuck since the process might still be emitting IO.
726
727 try
728 {
729 WSLCProcessState state{};
730 int exitCode{};
731 THROW_IF_FAILED(internalType->process->GetState(&state, &exitCode));
732
733 if (state == WslcProcessStateExited || state == WslcProcessStateSignalled)
734 {
735 internalType->ioCallbacks->Complete();
736 }
737 }
738 CATCH_LOG();
739 }
740
741 return S_OK;
742 }
743 CATCH_RETURN();
744
745 // CONTAINER DEFINITIONS
746
747 STDAPI WslcInitContainerSettings(_In_ PCSTR imageName, _Out_ WslcContainerSettings* containerSettings)
748 try
749 {
750 auto internalType = CheckAndGetInternalType(containerSettings);
751 RETURN_HR_IF_NULL(E_POINTER, imageName);
752
753 *internalType = {};
754
755 internalType->image = imageName;
756 // Default network configuration to WSLC SDK `0`, which is NONE.
757 internalType->networkMode = "none";
758
759 return S_OK;
760 }
761 CATCH_RETURN();
762
763 STDAPI WslcCreateContainer(_In_ WslcSession session, _In_ const WslcContainerSettings* containerSettings, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage)
764 try
765 {
766 RETURN_HR_IF_NULL(E_POINTER, container);
767 *container = nullptr;
768 ErrorInfoWrapper errorInfoWrapper{errorMessage};
769 auto internalSession = CheckAndGetInternalType(session);
770 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalSession->session);
771 auto internalContainerSettings = CheckAndGetInternalType(containerSettings);
772
773 auto result = std::make_unique<WslcContainerImpl>();
774
775 WSLCCompatContainerOptions containerOptions{};
776 std::unique_ptr<WSLCCompatPortMapping[]> convertedPorts; // this must stay in same scope as containerOptions since containerOptions.Ports is getting a raw pointer to the array owned by convertedPorts.
777
778 containerOptions.Image = internalContainerSettings->image;
779 containerOptions.Name = internalContainerSettings->runtimeName;
780 containerOptions.HostName = internalContainerSettings->HostName;
781 containerOptions.DomainName = internalContainerSettings->DomainName;
782 containerOptions.Flags = ConvertFlags(internalContainerSettings->containerFlags);
783
784 CopyProcessSettingsToRuntime(containerOptions.InitProcessOptions, internalContainerSettings->initProcessOptions);
785
786 std::unique_ptr<WSLCCompatVolume[]> convertedVolumes;
787 if (internalContainerSettings->volumes && internalContainerSettings->volumesCount)
788 {
789 convertedVolumes = std::make_unique<WSLCCompatVolume[]>(internalContainerSettings->volumesCount);
790 for (uint32_t i = 0; i < internalContainerSettings->volumesCount; ++i)
791 {
792 const WslcContainerVolume& internalVolume = internalContainerSettings->volumes[i];
793 WSLCCompatVolume& convertedVolume = convertedVolumes[i];
794
795 convertedVolume.HostPath = internalVolume.windowsPath;
796 convertedVolume.ContainerPath = internalVolume.containerPath;
797 convertedVolume.ReadOnly = internalVolume.readOnly;
798 }
799 containerOptions.Volumes = convertedVolumes.get();
800 containerOptions.VolumesCount = static_cast<ULONG>(internalContainerSettings->volumesCount);
801 }
802
803 std::unique_ptr<WSLCCompatNamedVolume[]> convertedNamedVolumes;
804 if (internalContainerSettings->namedVolumes && internalContainerSettings->namedVolumesCount)
805 {
806 convertedNamedVolumes = std::make_unique<WSLCCompatNamedVolume[]>(internalContainerSettings->namedVolumesCount);
807 for (uint32_t i = 0; i < internalContainerSettings->namedVolumesCount; ++i)
808 {
809 const WslcContainerNamedVolume& internalVolume = internalContainerSettings->namedVolumes[i];
810 WSLCCompatNamedVolume& convertedVolume = convertedNamedVolumes[i];
811
812 convertedVolume.Name = internalVolume.name;
813 convertedVolume.ContainerPath = internalVolume.containerPath;
814 convertedVolume.ReadOnly = internalVolume.readOnly;
815 }
816 containerOptions.NamedVolumes = convertedNamedVolumes.get();
817 containerOptions.NamedVolumesCount = static_cast<ULONG>(internalContainerSettings->namedVolumesCount);
818 }
819
820 if (internalContainerSettings->ports && internalContainerSettings->portsCount)
821 {
822 convertedPorts = std::make_unique<WSLCCompatPortMapping[]>(internalContainerSettings->portsCount);
823 for (uint32_t i = 0; i < internalContainerSettings->portsCount; ++i)
824 {
825 const WslcContainerPortMapping& internalPort = internalContainerSettings->ports[i];
826 WSLCCompatPortMapping& convertedPort = convertedPorts[i];
827
828 convertedPort.HostPort = internalPort.windowsPort;
829 convertedPort.ContainerPort = internalPort.containerPort;
830
831 // TODO: Consider using standard protocol numbers instead of our own enum.
832 switch (internalPort.protocol)
833 {
834 case WSLC_PORT_PROTOCOL_TCP:
835 convertedPort.Protocol = IPPROTO_TCP;
836 break;
837 case WSLC_PORT_PROTOCOL_UDP:
838 convertedPort.Protocol = IPPROTO_UDP;
839 break;
840 default:
841 THROW_HR_MSG(E_INVALIDARG, "Unsupported port protocol: %u", internalPort.protocol);
842 }
843 // Validate IP address if provided and if valid, copy to runtime structure.
844 if (internalPort.windowsAddress != nullptr)
845 {
846 switch (internalPort.windowsAddress->ss_family)
847 {
848 case AF_INET:
849 {
850 const auto* addr4 = reinterpret_cast<const sockaddr_in*>(internalPort.windowsAddress);
851 HRESULT hr = InetNtopToHresult(AF_INET, &addr4->sin_addr, convertedPort.BindingAddress, sizeof(convertedPort.BindingAddress));
852 if (FAILED(hr))
853 {
854 THROW_HR_MSG(hr, "inet_ntop() failed for AF_INET address");
855 }
856 convertedPort.Family = AF_INET;
857 break;
858 }
859
860 case AF_INET6:
861 {
862 const auto* addr6 = reinterpret_cast<const sockaddr_in6*>(internalPort.windowsAddress);
863 HRESULT hr = InetNtopToHresult(AF_INET6, &addr6->sin6_addr, convertedPort.BindingAddress, sizeof(convertedPort.BindingAddress));
864 if (FAILED(hr))
865 {
866 THROW_HR_MSG(hr, "inet_ntop() failed for AF_INET6 address");
867 }
868 convertedPort.Family = AF_INET6;
869 break;
870 }
871
872 default:
873 THROW_HR_MSG(E_INVALIDARG, "Unsupported address family: %d", internalPort.windowsAddress->ss_family);
874 }
875 }
876 else
877 {
878 convertedPort.Family = AF_INET;
879 strcpy_s(convertedPort.BindingAddress, "127.0.0.1");
880 }
881 }
882 containerOptions.Ports = convertedPorts.get(); // Make sure convertedPorts stays in scope for life of containerOptions
883 containerOptions.PortsCount = static_cast<ULONG>(internalContainerSettings->portsCount);
884 }
885
886 // SDK only exposes the network mode (no additional endpoints today).
887 containerOptions.ContainerNetwork.NetworkMode = internalContainerSettings->networkMode;
888
889 // TODO: No user access
890 // containerOptions.Labels;
891 // containerOptions.LabelsCount;
892 // containerOptions.StopSignal;
893 // containerOptions.ShmSize;
894
895 if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalSession->session->CreateContainer(&containerOptions, nullptr, &result->container))))
896 {
897 wsl::windows::common::security::ConfigureForCOMImpersonation(result->container.get());
898
899 if (IOCallback::HasIOCallback(internalContainerSettings->initProcessOptions))
900 {
901 result->ioCallbackOptions = internalContainerSettings->initProcessOptions->ioCallbacks;
902 }
903
904 *container = reinterpret_cast<WslcContainer>(result.release());
905 }
906
907 return errorInfoWrapper;
908 }
909 CATCH_RETURN();
910
911 STDAPI WslcOpenContainer(_In_ WslcSession session, _In_z_ PCSTR nameOrId, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage)
912 try
913 {
914 RETURN_HR_IF_NULL(E_POINTER, container);
915 *container = nullptr;
916 RETURN_HR_IF_NULL(E_POINTER, nameOrId);
917 ErrorInfoWrapper errorInfoWrapper{errorMessage};
918 auto internalSession = CheckAndGetInternalType(session);
919 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalSession->session);
920
921 auto result = std::make_unique<WslcContainerImpl>();
922
923 if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalSession->session->OpenContainer(nameOrId, &result->container))))
924 {
925 wsl::windows::common::security::ConfigureForCOMImpersonation(result->container.get());
926 *container = reinterpret_cast<WslcContainer>(result.release());
927 }
928
929 return errorInfoWrapper;
930 }
931 CATCH_RETURN();
932
933 STDAPI WslcSetContainerInitProcessIOCallbacks(_In_ WslcContainer container, _In_ const WslcProcessCallbacks* callbacks, _In_opt_ PVOID context)
934 try
935 {
936 RETURN_HR_IF_NULL(E_POINTER, callbacks);
937 auto internalType = CheckAndGetInternalType(container);
938
939 internalType->ioCallbackOptions.onStdOut = callbacks->onStdOut;
940 internalType->ioCallbackOptions.onStdErr = callbacks->onStdErr;
941 internalType->ioCallbackOptions.onExit = callbacks->onExit;
942 internalType->ioCallbackOptions.callbackContext = context;
943
944 return S_OK;
945 }
946 CATCH_RETURN();
947
948 STDAPI WslcStartContainer(_In_ WslcContainer container, _In_ WslcContainerStartFlags flags, _Outptr_opt_result_z_ PWSTR* errorMessage)
949 try
950 {
951 ErrorInfoWrapper errorInfoWrapper{errorMessage};
952 auto internalType = CheckAndGetInternalType(container);
953 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
954
955 bool hasIOCallback = IOCallback::HasIOCallback(internalType->ioCallbackOptions);
956 // If callbacks were provided, ATTACH must be used.
957 // TODO: Consider if we should just override flags when callbacks were provided instead.
958 RETURN_HR_IF(E_INVALIDARG, WI_IsFlagClear(flags, WSLC_CONTAINER_START_FLAG_ATTACH) && hasIOCallback);
959
960 if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalType->container->Start(ConvertFlags(flags)))))
961 {
962 if (hasIOCallback)
963 {
964 wil::com_ptr<IWSLCCompatProcess> process;
965 RETURN_IF_FAILED(internalType->container->GetInitProcess(&process));
966 wsl::windows::common::security::ConfigureForCOMImpersonation(process.get());
967 internalType->ioCallbacks = std::make_shared<IOCallback>(process.get(), internalType->ioCallbackOptions);
968 }
969 }
970
971 return errorInfoWrapper;
972 }
973 CATCH_RETURN();
974
975 STDAPI WslcSetContainerSettingsFlags(_In_ WslcContainerSettings* containerSettings, _In_ WslcContainerFlags flags)
976 try
977 {
978 auto internalType = CheckAndGetInternalType(containerSettings);
979
980 internalType->containerFlags = flags;
981
982 return S_OK;
983 }
984 CATCH_RETURN();
985
986 STDAPI WslcSetContainerSettingsName(_In_ WslcContainerSettings* containerSettings, _In_ PCSTR name)
987 try
988 {
989 auto internalType = CheckAndGetInternalType(containerSettings);
990
991 internalType->runtimeName = name;
992
993 return S_OK;
994 }
995 CATCH_RETURN();
996
997 STDAPI WslcSetContainerSettingsHostName(_In_ WslcContainerSettings* containerSettings, _In_ PCSTR hostName)
998 try
999 {
1000 auto internalType = CheckAndGetInternalType(containerSettings);
1001
1002 internalType->HostName = hostName;
1003
1004 return S_OK;
1005 }
1006 CATCH_RETURN();
1007
1008 STDAPI WslcSetContainerSettingsDomainName(_In_ WslcContainerSettings* containerSettings, _In_ PCSTR domainName)
1009 try
1010 {
1011 auto internalType = CheckAndGetInternalType(containerSettings);
1012
1013 internalType->DomainName = domainName;
1014
1015 return S_OK;
1016 }
1017 CATCH_RETURN();
1018
1019 STDAPI WslcSetContainerSettingsInitProcess(_In_ WslcContainerSettings* containerSettings, _In_ WslcProcessSettings* initProcess)
1020 try
1021 {
1022 auto internalType = CheckAndGetInternalType(containerSettings);
1023
1024 internalType->initProcessOptions = GetInternalType(initProcess);
1025
1026 return S_OK;
1027 }
1028 CATCH_RETURN();
1029
1030 STDAPI WslcSetContainerSettingsNetworkingMode(_In_ WslcContainerSettings* containerSettings, _In_ WslcContainerNetworkingMode networkingMode)
1031 try
1032 {
1033 auto internalType = CheckAndGetInternalType(containerSettings);
1034
1035 internalType->networkMode = Convert(networkingMode);
1036
1037 return S_OK;
1038 }
1039 CATCH_RETURN();
1040
1041 STDAPI WslcSetContainerSettingsPortMappings(
1042 _In_ WslcContainerSettings* containerSettings, _In_reads_opt_(portMappingCount) const WslcContainerPortMapping* portMappings, _In_ uint32_t portMappingCount)
1043 try
1044 {
1045 auto internalType = CheckAndGetInternalType(containerSettings);
1046 RETURN_HR_IF(E_INVALIDARG, (portMappings == nullptr && portMappingCount != 0) || (portMappings != nullptr && portMappingCount == 0));
1047
1048 for (uint32_t i = 0; i < portMappingCount; ++i)
1049 {
1050 if (portMappings[i].windowsAddress != nullptr)
1051 {
1052 const auto family = portMappings[i].windowsAddress->ss_family;
1053 RETURN_HR_IF_MSG(
1054 E_INVALIDARG, family != AF_INET && family != AF_INET6, "Unsupported address family: %d at port mapping index %u", family, i);
1055 }
1056 RETURN_HR_IF_MSG(
1057 E_NOTIMPL, portMappings[i].protocol != 0, "Unsupported protocol: %d at port mapping index %u", portMappings[i].protocol, i);
1058 }
1059 internalType->ports = portMappings;
1060 internalType->portsCount = portMappingCount;
1061
1062 return S_OK;
1063 }
1064 CATCH_RETURN();
1065
1066 STDAPI WslcSetContainerSettingsVolumes(
1067 _In_ WslcContainerSettings* containerSettings, _In_reads_opt_(volumeCount) const WslcContainerVolume* volumes, _In_ uint32_t volumeCount)
1068 try
1069 {
1070 auto internalType = CheckAndGetInternalType(containerSettings);
1071 RETURN_HR_IF(E_INVALIDARG, (volumes == nullptr && volumeCount != 0) || (volumes != nullptr && volumeCount == 0));
1072
1073 for (uint32_t i = 0; i < volumeCount; ++i)
1074 {
1075 RETURN_HR_IF_NULL(E_INVALIDARG, volumes[i].windowsPath);
1076 EnsureAbsolutePath(volumes[i].windowsPath, false);
1077 RETURN_HR_IF_NULL(E_INVALIDARG, volumes[i].containerPath);
1078 EnsureAbsolutePath(volumes[i].containerPath, true);
1079 }
1080
1081 internalType->volumes = volumes;
1082 internalType->volumesCount = volumeCount;
1083
1084 return S_OK;
1085 }
1086 CATCH_RETURN();
1087
1088 STDAPI WslcSetContainerSettingsNamedVolumes(
1089 _In_ WslcContainerSettings* containerSettings, _In_reads_opt_(namedVolumeCount) const WslcContainerNamedVolume* namedVolumes, _In_ uint32_t namedVolumeCount)
1090 try
1091 {
1092 auto internalType = CheckAndGetInternalType(containerSettings);
1093 RETURN_HR_IF(E_INVALIDARG, (namedVolumes == nullptr && namedVolumeCount != 0) || (namedVolumes != nullptr && namedVolumeCount == 0));
1094
1095 for (uint32_t i = 0; i < namedVolumeCount; ++i)
1096 {
1097 RETURN_HR_IF_NULL(E_INVALIDARG, namedVolumes[i].name);
1098 RETURN_HR_IF_NULL(E_INVALIDARG, namedVolumes[i].containerPath);
1099 EnsureAbsolutePath(namedVolumes[i].containerPath, true);
1100 }
1101
1102 internalType->namedVolumes = namedVolumes;
1103 internalType->namedVolumesCount = namedVolumeCount;
1104
1105 return S_OK;
1106 }
1107 CATCH_RETURN();
1108
1109 STDAPI WslcCreateContainerProcess(
1110 _In_ WslcContainer container, _In_ WslcProcessSettings* newProcessSettings, _Out_ WslcProcess* newProcess, _Outptr_opt_result_z_ PWSTR* errorMessage)
1111 try
1112 {
1113 RETURN_HR_IF_NULL(E_POINTER, newProcess);
1114 *newProcess = nullptr;
1115 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1116 auto internalContainer = CheckAndGetInternalType(container);
1117 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalContainer->container);
1118 auto internalProcessSettings = CheckAndGetInternalType(newProcessSettings);
1119 RETURN_HR_IF(E_INVALIDARG, internalProcessSettings->commandLine == nullptr || internalProcessSettings->commandLineCount == 0);
1120
1121 WSLCCompatProcessOptions runtimeOptions{};
1122 CopyProcessSettingsToRuntime(runtimeOptions, internalProcessSettings);
1123
1124 auto result = std::make_unique<WslcProcessImpl>();
1125 if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalContainer->container->Exec(&runtimeOptions, &result->process))))
1126 {
1127 wsl::windows::common::security::ConfigureForCOMImpersonation(result->process.get());
1128
1129 if (IOCallback::HasIOCallback(internalProcessSettings))
1130 {
1131 result->ioCallbacks = std::make_shared<IOCallback>(result->process.get(), internalProcessSettings->ioCallbacks);
1132 }
1133
1134 *newProcess = reinterpret_cast<WslcProcess>(result.release());
1135 }
1136
1137 return errorInfoWrapper;
1138 }
1139 CATCH_RETURN();
1140
1141 // GENERAL CONTAINER MANAGEMENT
1142
1143 STDAPI WslcGetContainerID(WslcContainer container, CHAR containerID[WSLC_CONTAINER_ID_BUFFER_SIZE])
1144 try
1145 {
1146 static_assert(WSLC_CONTAINER_ID_BUFFER_SIZE == sizeof(WSLCCompatContainerId), "Container ID lengths differ.");
1147
1148 auto internalType = CheckAndGetInternalType(container);
1149 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1150 RETURN_HR_IF_NULL(E_POINTER, containerID);
1151
1152 return internalType->container->GetId(containerID);
1153 }
1154 CATCH_RETURN();
1155
1156 STDAPI WslcInspectContainer(_In_ WslcContainer container, _Outptr_result_z_ PSTR* inspectData)
1157 try
1158 {
1159 auto internalType = CheckAndGetInternalType(container);
1160 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1161 RETURN_HR_IF_NULL(E_POINTER, inspectData);
1162
1163 *inspectData = nullptr;
1164
1165 wil::unique_cotaskmem_ansistring result;
1166 RETURN_IF_FAILED(internalType->container->Inspect(&result));
1167
1168 *inspectData = result.release();
1169
1170 return S_OK;
1171 }
1172 CATCH_RETURN();
1173
1174 STDAPI WslcGetContainerInitProcess(_In_ WslcContainer container, _Out_ WslcProcess* initProcess)
1175 try
1176 {
1177 RETURN_HR_IF_NULL(E_POINTER, initProcess);
1178 *initProcess = nullptr;
1179 auto internalType = CheckAndGetInternalType(container);
1180 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1181
1182 auto result = std::make_unique<WslcProcessImpl>();
1183
1184 RETURN_IF_FAILED(internalType->container->GetInitProcess(&result->process));
1185
1186 wsl::windows::common::security::ConfigureForCOMImpersonation(result->process.get());
1187
1188 result->ioCallbacks = internalType->ioCallbacks.load();
1189
1190 *initProcess = reinterpret_cast<WslcProcess>(result.release());
1191
1192 return S_OK;
1193 }
1194 CATCH_RETURN();
1195
1196 STDAPI WslcGetContainerState(_In_ WslcContainer container, _Out_ WslcContainerState* state)
1197 try
1198 {
1199 static_assert(
1200 WSLC_CONTAINER_STATE_INVALID == WslcContainerStateInvalid && WSLC_CONTAINER_STATE_CREATED == WslcContainerStateCreated &&
1201 WSLC_CONTAINER_STATE_RUNNING == WslcContainerStateRunning &&
1202 WSLC_CONTAINER_STATE_EXITED == WslcContainerStateExited && WSLC_CONTAINER_STATE_DELETED == WslcContainerStateDeleted,
1203 "Container state enum values mismatch.");
1204
1205 auto internalType = CheckAndGetInternalType(container);
1206 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1207 RETURN_HR_IF_NULL(E_POINTER, state);
1208
1209 *state = WSLC_CONTAINER_STATE_INVALID;
1210
1211 WSLCContainerState runtimeState{};
1212 RETURN_IF_FAILED(internalType->container->GetState(&runtimeState));
1213
1214 *state = static_cast<WslcContainerState>(runtimeState);
1215 return S_OK;
1216 }
1217 CATCH_RETURN();
1218
1219 STDAPI WslcStopContainer(_In_ WslcContainer container, _In_ WslcSignal signal, _In_ uint32_t timeoutSeconds, _Outptr_opt_result_z_ PWSTR* errorMessage)
1220 try
1221 {
1222 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1223 auto internalType = CheckAndGetInternalType(container);
1224 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1225
1226 return errorInfoWrapper.CaptureResult(internalType->container->Stop(Convert(signal), timeoutSeconds));
1227 }
1228 CATCH_RETURN();
1229
1230 STDAPI WslcDeleteContainer(_In_ WslcContainer container, _In_ WslcDeleteContainerFlags flags, _Outptr_opt_result_z_ PWSTR* errorMessage)
1231 try
1232 {
1233 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1234 auto internalType = CheckAndGetInternalType(container);
1235 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->container);
1236
1237 return errorInfoWrapper.CaptureResult(internalType->container->Delete(ConvertFlags(flags)));
1238 }
1239 CATCH_RETURN();
1240
1241 // PROCESS DEFINITIONS
1242
1243 STDAPI WslcInitProcessSettings(_Out_ WslcProcessSettings* processSettings)
1244 try
1245 {
1246 auto internalType = CheckAndGetInternalType(processSettings);
1247
1248 *internalType = {};
1249
1250 return S_OK;
1251 }
1252 CATCH_RETURN();
1253
1254 STDAPI WslcSetProcessSettingsWorkingDirectory(_In_ WslcProcessSettings* processSettings, _In_ PCSTR workingDirectory)
1255 try
1256 {
1257 auto internalType = CheckAndGetInternalType(processSettings);
1258
1259 internalType->workingDirectory = workingDirectory;
1260
1261 return S_OK;
1262 }
1263 CATCH_RETURN();
1264
1265 // OPTIONAL PROCESS SETTINGS
1266
1267 STDAPI WslcSetProcessSettingsCmdLine(_In_ WslcProcessSettings* processSettings, _In_reads_(argc) PCSTR const* argv, size_t argc)
1268 try
1269 {
1270 auto internalType = CheckAndGetInternalType(processSettings);
1271 RETURN_HR_IF(
1272 E_INVALIDARG,
1273 (argv == nullptr && argc != 0) || (argv != nullptr && argc == 0) ||
1274 (argc > static_cast<size_t>(std::numeric_limits<uint32_t>::max())));
1275
1276 internalType->commandLine = argv;
1277 internalType->commandLineCount = static_cast<uint32_t>(argc);
1278
1279 return S_OK;
1280 }
1281 CATCH_RETURN();
1282
1283 STDAPI WslcSetProcessSettingsEnvVariables(_In_ WslcProcessSettings* processSettings, _In_reads_(argc) PCSTR const* key_value, size_t argc)
1284 try
1285 {
1286 auto internalType = CheckAndGetInternalType(processSettings);
1287 RETURN_HR_IF(
1288 E_INVALIDARG,
1289 (key_value == nullptr && argc != 0) || (key_value != nullptr && argc == 0) ||
1290 (argc > static_cast<size_t>(std::numeric_limits<uint32_t>::max())));
1291
1292 internalType->environment = key_value;
1293 internalType->environmentCount = static_cast<uint32_t>(argc);
1294
1295 return S_OK;
1296 }
1297 CATCH_RETURN();
1298
1299 // PROCESS MANAGEMENT
1300
1301 STDAPI WslcGetProcessPid(_In_ WslcProcess process, _Out_ uint32_t* pid)
1302 try
1303 {
1304 auto internalType = CheckAndGetInternalType(process);
1305 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1306 RETURN_HR_IF_NULL(E_POINTER, pid);
1307
1308 *pid = 0;
1309
1310 int runtimePid{};
1311 RETURN_IF_FAILED(internalType->process->GetPid(&runtimePid));
1312
1313 *pid = static_cast<uint32_t>(runtimePid);
1314 return S_OK;
1315 }
1316 CATCH_RETURN();
1317
1318 STDAPI WslcGetProcessExitEvent(_In_ WslcProcess process, _Out_ HANDLE* exitEvent)
1319 try
1320 {
1321 auto internalType = CheckAndGetInternalType(process);
1322 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1323 RETURN_HR_IF_NULL(E_POINTER, exitEvent);
1324
1325 return internalType->process->GetExitEvent(exitEvent);
1326 }
1327 CATCH_RETURN();
1328
1329 // PROCESS RESULT / SIGNALS
1330
1331 STDAPI WslcGetProcessState(_In_ WslcProcess process, _Out_ WslcProcessState* state)
1332 try
1333 {
1334 static_assert(
1335 WSLC_PROCESS_STATE_UNKNOWN == WslcProcessStateUnknown && WSLC_PROCESS_STATE_RUNNING == WslcProcessStateRunning &&
1336 WSLC_PROCESS_STATE_EXITED == WslcProcessStateExited && WSLC_PROCESS_STATE_SIGNALLED == WslcProcessStateSignalled,
1337 "Process state enum values mismatch.");
1338
1339 auto internalType = CheckAndGetInternalType(process);
1340 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1341 RETURN_HR_IF_NULL(E_POINTER, state);
1342
1343 *state = WSLC_PROCESS_STATE_UNKNOWN;
1344
1345 WSLCProcessState runtimeState{};
1346 int exitCode{};
1347 RETURN_IF_FAILED(internalType->process->GetState(&runtimeState, &exitCode));
1348
1349 *state = static_cast<WslcProcessState>(runtimeState);
1350 return S_OK;
1351 }
1352 CATCH_RETURN();
1353
1354 STDAPI WslcGetProcessExitCode(_In_ WslcProcess process, _Out_ PINT32 exitCode)
1355 try
1356 {
1357 auto internalType = CheckAndGetInternalType(process);
1358 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1359 RETURN_HR_IF_NULL(E_POINTER, exitCode);
1360
1361 *exitCode = -1;
1362
1363 WSLCProcessState runtimeState{};
1364 RETURN_IF_FAILED(internalType->process->GetState(&runtimeState, exitCode));
1365 RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), runtimeState != WslcProcessStateExited);
1366 return S_OK;
1367 }
1368 CATCH_RETURN();
1369
1370 STDAPI WslcSignalProcess(_In_ WslcProcess process, _In_ WslcSignal signal)
1371 try
1372 {
1373 auto internalType = CheckAndGetInternalType(process);
1374 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1375
1376 RETURN_HR(internalType->process->Signal(Convert(signal)));
1377 }
1378 CATCH_RETURN();
1379
1380 STDAPI WslcSetProcessSettingsCallbacks(_In_ WslcProcessSettings* processSettings, _In_ const WslcProcessCallbacks* callbacks, _In_opt_ PVOID context)
1381 try
1382 {
1383 auto internalType = CheckAndGetInternalType(processSettings);
1384 RETURN_HR_IF(E_INVALIDARG, callbacks == nullptr && context != nullptr);
1385
1386 static_assert(std::is_trivial_v<WslcProcessCallbacks>, "WslcProcessCallbacks must be trivial.");
1387
1388 WslcProcessCallbacks* internalCallbacks = &internalType->ioCallbacks;
1389
1390 if (callbacks)
1391 {
1392 *internalCallbacks = *callbacks;
1393 internalType->ioCallbacks.callbackContext = context;
1394 }
1395 else
1396 {
1397 *internalCallbacks = {};
1398 }
1399
1400 return S_OK;
1401 }
1402 CATCH_RETURN();
1403
1404 STDAPI WslcGetProcessIOHandle(_In_ WslcProcess process, _In_ WslcProcessIOHandle ioHandle, _Out_ HANDLE* handle)
1405 try
1406 {
1407 auto internalType = CheckAndGetInternalType(process);
1408 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->process);
1409 RETURN_HR_IF_NULL(E_POINTER, handle);
1410
1411 *handle = nullptr;
1412
1413 auto result = IOCallback::GetIOHandle(internalType->process.get(), ioHandle);
1414 *handle = result.release();
1415
1416 return S_OK;
1417 }
1418 CATCH_RETURN();
1419
1420 // IMAGE MANAGEMENT
1421 STDAPI WslcPullSessionImage(_In_ WslcSession session, _In_ const WslcPullImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1422 try
1423 {
1424 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1425 auto internalType = CheckAndGetInternalType(session);
1426 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1427 RETURN_HR_IF_NULL(E_POINTER, options);
1428 RETURN_HR_IF_NULL(E_INVALIDARG, options->uri);
1429
1430 auto progressCallback = ProgressCallback::CreateIf(options);
1431
1432 return errorInfoWrapper.CaptureResult(internalType->session->PullImage(options->uri, options->registryAuth, progressCallback.get(), nullptr));
1433 }
1434 CATCH_RETURN();
1435
1436 static HRESULT WslcImportSessionImageImpl(
1437 WslcSessionImpl* internalSession, PCSTR imageName, const WslcImportImageOptions* options, ErrorInfoWrapper& errorInfoWrapper, const ImageFileResolver& imageFile)
1438 {
1439 auto progressCallback = ProgressCallback::CreateIf(options);
1440 wil::unique_cotaskmem_ansistring imageId;
1441
1442 return errorInfoWrapper.CaptureResult(internalSession->session->ImportImage(
1443 wsl::windows::common::apicompat::Convert(ToCOMInputHandle(imageFile.Handle())),
1444 imageName,
1445 progressCallback.get(),
1446 imageFile.Length(),
1447 nullptr,
1448 &imageId));
1449 }
1450
1451 STDAPI WslcImportSessionImage(
1452 _In_ WslcSession session,
1453 _In_z_ PCSTR imageName,
1454 _In_ HANDLE imageContent,
1455 _In_ uint64_t imageContentLength,
1456 _In_opt_ const WslcImportImageOptions* options,
1457 _Outptr_opt_result_z_ PWSTR* errorMessage)
1458 try
1459 {
1460 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1461 auto internalType = CheckAndGetInternalType(session);
1462 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1463 THROW_HR_IF_NULL(E_POINTER, imageName);
1464 return WslcImportSessionImageImpl(internalType, imageName, options, errorInfoWrapper, {imageContent, imageContentLength});
1465 }
1466 CATCH_RETURN();
1467
1468 STDAPI WslcImportSessionImageFromFile(
1469 _In_ WslcSession session, _In_z_ PCSTR imageName, _In_z_ PCWSTR path, _In_opt_ const WslcImportImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1470 try
1471 {
1472 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1473 auto internalType = CheckAndGetInternalType(session);
1474 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1475 THROW_HR_IF_NULL(E_POINTER, imageName);
1476 return WslcImportSessionImageImpl(internalType, imageName, options, errorInfoWrapper, {path});
1477 }
1478 CATCH_RETURN();
1479
1480 static HRESULT WslcLoadSessionImageImpl(
1481 WslcSessionImpl* internalSession, const WslcLoadImageOptions* options, ErrorInfoWrapper& errorInfoWrapper, const ImageFileResolver& imageFile)
1482 {
1483 auto progressCallback = ProgressCallback::CreateIf(options);
1484
1485 return errorInfoWrapper.CaptureResult(internalSession->session->LoadImage(
1486 wsl::windows::common::apicompat::Convert(ToCOMInputHandle(imageFile.Handle())), progressCallback.get(), imageFile.Length(), nullptr));
1487 }
1488
1489 STDAPI WslcLoadSessionImage(
1490 _In_ WslcSession session,
1491 _In_ HANDLE imageContent,
1492 _In_ uint64_t imageContentLength,
1493 _In_opt_ const WslcLoadImageOptions* options,
1494 _Outptr_opt_result_z_ PWSTR* errorMessage)
1495 try
1496 {
1497 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1498 auto internalType = CheckAndGetInternalType(session);
1499 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1500 return WslcLoadSessionImageImpl(internalType, options, errorInfoWrapper, {imageContent, imageContentLength});
1501 }
1502 CATCH_RETURN();
1503
1504 STDAPI WslcLoadSessionImageFromFile(_In_ WslcSession session, _In_z_ PCWSTR path, _In_opt_ const WslcLoadImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1505 try
1506 {
1507 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1508 auto internalType = CheckAndGetInternalType(session);
1509 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1510 return WslcLoadSessionImageImpl(internalType, options, errorInfoWrapper, {path});
1511 }
1512 CATCH_RETURN();
1513
1514 STDAPI WslcDeleteSessionImage(_In_ WslcSession session, _In_z_ PCSTR nameOrID, _Outptr_opt_result_z_ PWSTR* errorMessage)
1515 try
1516 {
1517 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1518 auto internalType = CheckAndGetInternalType(session);
1519 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1520 RETURN_HR_IF_NULL(E_POINTER, nameOrID);
1521
1522 WSLCCompatDeleteImageOptions options{};
1523 options.Image = nameOrID;
1524 // TODO: Flags? (Force and NoPrune)
1525
1526 wil::unique_cotaskmem_array_ptr<WSLCCompatDeletedImageInformation> deletedImageInformation;
1527
1528 return errorInfoWrapper.CaptureResult(
1529 internalType->session->DeleteImage(&options, &deletedImageInformation, deletedImageInformation.size_address<ULONG>()));
1530 }
1531 CATCH_RETURN();
1532
1533 STDAPI WslcTagSessionImage(_In_ WslcSession session, _In_ const WslcTagImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1534 try
1535 {
1536 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1537 auto internalType = CheckAndGetInternalType(session);
1538 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1539 RETURN_HR_IF_NULL(E_POINTER, options);
1540 RETURN_HR_IF_NULL(E_INVALIDARG, options->image);
1541 RETURN_HR_IF_NULL(E_INVALIDARG, options->repo);
1542 RETURN_HR_IF_NULL(E_INVALIDARG, options->tag);
1543
1544 WSLCCompatTagImageOptions runtimeOptions{};
1545 runtimeOptions.Image = options->image;
1546 runtimeOptions.Repo = options->repo;
1547 runtimeOptions.Tag = options->tag;
1548
1549 return errorInfoWrapper.CaptureResult(internalType->session->TagImage(&runtimeOptions));
1550 }
1551 CATCH_RETURN();
1552
1553 STDAPI WslcPushSessionImage(_In_ WslcSession session, _In_ const WslcPushImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage)
1554 try
1555 {
1556 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1557 auto internalType = CheckAndGetInternalType(session);
1558 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1559 RETURN_HR_IF_NULL(E_POINTER, options);
1560 RETURN_HR_IF_NULL(E_INVALIDARG, options->image);
1561 RETURN_HR_IF_NULL(E_INVALIDARG, options->registryAuth);
1562
1563 auto progressCallback = ProgressCallback::CreateIf(options);
1564
1565 return errorInfoWrapper.CaptureResult(
1566 internalType->session->PushImage(options->image, options->registryAuth, progressCallback.get(), nullptr));
1567 }
1568 CATCH_RETURN();
1569
1570 STDAPI WslcSessionAuthenticate(
1571 _In_ WslcSession session,
1572 _In_z_ PCSTR serverAddress,
1573 _In_z_ PCSTR username,
1574 _In_z_ PCSTR password,
1575 _Outptr_result_z_ PSTR* identityToken,
1576 _Out_opt_ WslcIdentityTokenType* tokenType,
1577 _Outptr_opt_result_z_ PWSTR* errorMessage)
1578 try
1579 {
1580 ErrorInfoWrapper errorInfoWrapper{errorMessage};
1581 auto internalType = CheckAndGetInternalType(session);
1582 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1583 RETURN_HR_IF_NULL(E_POINTER, serverAddress);
1584 RETURN_HR_IF_NULL(E_POINTER, username);
1585 RETURN_HR_IF_NULL(E_POINTER, password);
1586 RETURN_HR_IF_NULL(E_POINTER, identityToken);
1587
1588 *identityToken = nullptr;
1589 if (tokenType != nullptr)
1590 {
1591 *tokenType = WSLC_IDENTITY_TOKEN_TYPE_UNKNOWN;
1592 }
1593
1594 wil::unique_cotaskmem_ansistring rawToken;
1595 auto hr = errorInfoWrapper.CaptureResult(internalType->session->Authenticate(serverAddress, username, password, &rawToken));
1596 if (SUCCEEDED(hr))
1597 {
1598 std::string authHeader;
1599 WslcIdentityTokenType type;
1600
1601 if (rawToken && strlen(rawToken.get()) > 0)
1602 {
1603 authHeader = BuildRegistryAuthHeader(std::string{rawToken.get()});
1604 type = WSLC_IDENTITY_TOKEN_TYPE_TOKEN;
1605 }
1606 else
1607 {
1608 authHeader = BuildRegistryAuthHeader(std::string{username}, std::string{password});
1609 type = WSLC_IDENTITY_TOKEN_TYPE_CREDENTIALS;
1610 }
1611
1612 auto result = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(authHeader.c_str());
1613 *identityToken = result.release();
1614
1615 if (tokenType != nullptr)
1616 {
1617 *tokenType = type;
1618 }
1619 }
1620
1621 return errorInfoWrapper;
1622 }
1623 CATCH_RETURN();
1624
1625 STDAPI WslcListSessionImages(_In_ WslcSession session, _Outptr_result_buffer_(*count) WslcImageInfo** images, _Out_ uint32_t* count)
1626 try
1627 {
1628 static_assert(
1629 sizeof(decltype(WslcImageInfo::name)) == sizeof(decltype(WSLCCompatImageInformation::Image)), "Image name size mismatch.");
1630
1631 RETURN_HR_IF_NULL(E_POINTER, images);
1632 *images = nullptr;
1633 RETURN_HR_IF_NULL(E_POINTER, count);
1634 *count = 0;
1635 auto internalType = CheckAndGetInternalType(session);
1636 RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);
1637
1638 // TODO: Many filtering options are available via WSLC_LIST_IMAGES_OPTIONS
1639
1640 wil::unique_cotaskmem_array_ptr<WSLCCompatImageInformation> imageInformation;
1641
1642 RETURN_IF_FAILED(internalType->session->ListImages(nullptr, &imageInformation, imageInformation.size_address<ULONG>()));
1643
1644 if (imageInformation.size())
1645 {
1646 auto result = wil::make_unique_cotaskmem<WslcImageInfo[]>(imageInformation.size());
1647
1648 for (size_t i = 0; i < imageInformation.size(); ++i)
1649 {
1650 WslcImageInfo& currentResult = result[i];
1651 WSLCCompatImageInformation& currentImage = imageInformation[i];
1652
1653 static_assert(std::is_trivial_v<WslcImageInfo>, "WslcImageInfo must be trivial.");
1654 currentResult = {};
1655
1656 THROW_HR_IF(
1657 E_UNEXPECTED,
1658 memcpy_s(currentResult.name, sizeof(decltype(WslcImageInfo::name)), currentImage.Image, sizeof(decltype(WSLCCompatImageInformation::Image))) !=
1659 0);
1660 ConvertSHA256Hash(currentImage.Hash, currentResult.sha256);
1661 currentResult.sizeBytes = currentImage.Size;
1662 currentResult.createdUnixTime = currentImage.Created;
1663 }
1664
1665 *images = result.release();
1666 *count = static_cast<uint32_t>(imageInformation.size());
1667 }
1668
1669 return S_OK;
1670 }
1671 CATCH_RETURN();
1672
1673 // STORAGE
1674
1675 // INSTALL
1676
1677 STDAPI WslcGetMissingComponents(_Out_ WslcComponentFlags* missingComponents)
1678 try
1679 {
1680 RETURN_HR_IF_NULL(E_POINTER, missingComponents);
1681
1682 *missingComponents = WSLC_COMPONENT_FLAG_NONE;
1683
1684 WslcComponentFlags componentCheck = WSLC_COMPONENT_FLAG_NONE;
1685
1686 WI_SetFlagIf(componentCheck, WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM, NeedsVirtualMachineServicesInstalled());
1687
1688 auto hr = CreateSessionManagerRaw().second;
1689 if (hr == REGDB_E_CLASSNOTREG)
1690 {
1691 WI_SetFlag(componentCheck, WSLC_COMPONENT_FLAG_WSL_PACKAGE);
1692 }
1693 else if (hr == WSLC_E_SDK_UPDATE_NEEDED)
1694 {
1695 WI_SetFlag(componentCheck, WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE);
1696 }
1697 else if (FAILED(hr))
1698 {
1699 THROW_HR(hr);
1700 }
1701
1702 *missingComponents = componentCheck;
1703
1704 return S_OK;
1705 }
1706 CATCH_RETURN();
1707
1708 STDAPI WslcGetVersion(_Out_writes_(1) WslcVersion* version)
1709 try
1710 {
1711 RETURN_HR_IF_NULL(E_POINTER, version);
1712
1713 static_assert(std::is_trivial_v<WslcVersion>, "WslcVersion must be trivial");
1714 *version = {};
1715
1716 wil::com_ptr<IWSLCCompatSessionManager> sessionManager = CreateSessionManager();
1717
1718 WSLCCompatVersion runtimeVersion{};
1719 RETURN_IF_FAILED(sessionManager->GetVersion(&runtimeVersion));
1720
1721 version->major = runtimeVersion.Major;
1722 version->minor = runtimeVersion.Minor;
1723 version->revision = runtimeVersion.Revision;
1724
1725 return S_OK;
1726 }
1727 CATCH_RETURN();
1728
1729 STDAPI WslcInstallWithDependencies(
1730 _In_ WslcComponentFlags components, _In_ WslcInstallOptions options, _In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context)
1731 try
1732 {
1733 // Reject unknown flag bits.
1734 constexpr WslcComponentFlags c_knownComponents =
1735 WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM | WSLC_COMPONENT_FLAG_WSL_PACKAGE | WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE;
1736 RETURN_HR_IF(E_INVALIDARG, (components & ~c_knownComponents) != WSLC_COMPONENT_FLAG_NONE);
1737 constexpr WslcInstallOptions c_knownOptions = WSLC_INSTALL_OPTION_REPAIR;
1738 RETURN_HR_IF(E_INVALIDARG, (options & ~c_knownOptions) != WSLC_INSTALL_OPTION_NONE);
1739
1740 // This API cannot update the SDK that the client is using.
1741 RETURN_HR_IF(WSLC_E_SDK_UPDATE_NEEDED, WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE));
1742
1743 HRESULT result = S_OK;
1744
1745 if (components == WSLC_COMPONENT_FLAG_NONE)
1746 {
1747 return result;
1748 }
1749
1750 // Installing components requires elevation.
1751 RETURN_HR_IF(
1752 HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED),
1753 !wsl::windows::common::security::IsTokenElevated(GetCurrentThreadEffectiveToken()) &&
1754 !wsl::windows::common::security::IsTokenLocalSystem(nullptr));
1755
1756 bool isRepair = WI_IsFlagSet(options, WSLC_INSTALL_OPTION_REPAIR);
1757
1758 if (WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM))
1759 {
1760 if (progressCallback)
1761 {
1762 progressCallback(WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM, 0, 1, context);
1763 }
1764
1765 // No difference between install and repair, just let DISM attempt to enable the feature.
1766 auto exitCode = WslInstall::InstallOptionalComponent(WslInstall::c_optionalFeatureNameVmp, false);
1767 if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
1768 {
1769 result = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
1770 }
1771 else if (exitCode != 0)
1772 {
1773 THROW_HR_WITH_USER_ERROR(
1774 WSL_E_INSTALL_COMPONENT_FAILED,
1775 wsl::shared::Localization::MessageOptionalComponentInstallFailed(WslInstall::c_optionalFeatureNameVmp, exitCode));
1776 }
1777
1778 if (progressCallback)
1779 {
1780 progressCallback(WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM, 1, 1, context);
1781 }
1782 }
1783
1784 if (WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_WSL_PACKAGE))
1785 {
1786 std::function<void(uint32_t)> callback;
1787 if (progressCallback)
1788 {
1789 callback = [progressCallback, context](uint32_t progress) {
1790 progressCallback(WSLC_COMPONENT_FLAG_WSL_PACKAGE, progress, 100, context);
1791 };
1792 }
1793
1794 using WindowsUpdateContext = wsl::windows::common::WindowsUpdateContext;
1795 WindowsUpdateContext wuContext;
1796 wuContext.RunUpdateFlow(
1797 isRepair ? WindowsUpdateContext::UpdateOptions::ResetProductRegistration : WindowsUpdateContext::UpdateOptions::EnsureProductRegistration,
1798 callback);
1799
1800 if (wuContext.GetUpdateCount() == 0)
1801 {
1802 // During the preview period, the package may not be published yet, so fall back to getting it from GH.
1803 // When moving to GA, change this to an error like WSL_E_NO_UPDATE_AVAILABLE or similar.
1804 if (callback)
1805 {
1806 callback(0);
1807 }
1808 // Use pre-release builds and repair semantics (required since this function uses the SDK binary version as a filter).
1809 wsl::windows::common::install::UpdatePackage(true, true, false);
1810 if (callback)
1811 {
1812 callback(100);
1813 }
1814 }
1815 }
1816
1817 return result;
1818 }
1819 CATCH_RETURN();
1820
1821 EXTERN_C BOOL STDAPICALLTYPE DllMain(_In_ HINSTANCE Instance, _In_ DWORD Reason, _In_opt_ LPVOID Reserved)
1822 {
1823 wil::DLLMain(Instance, Reason, Reserved);
1824
1825 switch (Reason)
1826 {
1827 case DLL_PROCESS_ATTACH:
1828 wsl::windows::common::wslutil::InitializeWil();
1829 WslTraceLoggingInitialize(WslcTelemetryProvider, false);
1830 break;
1831
1832 case DLL_PROCESS_DETACH:
1833 WslTraceLoggingUninitialize();
1834 break;
1835 }
1836
1837 return TRUE;
1838 }