master
h 388 lines 12.1 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 wslutil.h
8
9 Abstract:
10
11 This file contains helper function declarations.
12
13 --*/
14
15 #pragma once
16 #include <functional>
17 #include <span>
18 #include <type_traits>
19 #include "SubProcess.h"
20 #include <winrt/windows.management.deployment.h>
21 #include "JsonUtils.h"
22 #include "wslc.h"
23
24 namespace wsl::windows::common {
25 struct Error;
26
27 namespace io {
28 struct HandleWrapper;
29 }
30
31 struct ErrorStrings
32 {
33 std::wstring Message;
34 std::wstring Code;
35 std::optional<std::wstring> Source;
36 };
37 } // namespace wsl::windows::common
38
39 namespace wsl::windows::common::wslutil {
40
41 // Namespace GUID used for Windows Terminal profile generation.
42 // {BE9372FE-59E1-4876-BDA9-C33C8F2F1AF1}
43 inline constexpr GUID WslTerminalNamespace = {0xbe9372fe, 0x59e1, 0x4876, {0xbd, 0xa9, 0xc3, 0x3c, 0x8f, 0x2f, 0x1a, 0xf1}};
44
45 // Namespace GUID for automatically generated Windows Terminal profiles.
46 // {2bde4a90-d05f-401c-9492-e40884ead1d8}
47 inline constexpr GUID GeneratedProfilesTerminalNamespace = {0x2bde4a90, 0xd05f, 0x401c, {0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8}};
48
49 inline auto c_msixPackageFamilyName = L"MicrosoftCorporationII.WindowsSubsystemForLinux_8wekyb3d8bbwe";
50 inline auto c_githubUrlOverrideRegistryValue = L"GitHubUrlOverride";
51 inline auto c_vhdFileExtension = L".vhd";
52 inline auto c_vhdxFileExtension = L".vhdx";
53 inline constexpr auto c_vmOwner = L"WSL"; // TODO-WSLC: Does this apply to WSLC ?
54
55 struct GitHubReleaseAsset
56 {
57 std::wstring url;
58 uint64_t id{};
59 std::wstring name;
60
61 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GitHubReleaseAsset, url, id, name);
62 };
63
64 struct GitHubRelease
65 {
66 std::wstring name;
67 std::vector<GitHubReleaseAsset> assets;
68 std::wstring created_at;
69
70 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GitHubRelease, name, assets, created_at);
71 };
72
73 struct COMErrorInfo
74 {
75 wil::unique_bstr Message;
76 wil::unique_bstr Source;
77 };
78
79 static_assert(sizeof(WSLCHandle::Handle) == sizeof(HANDLE));
80 static_assert(sizeof(FILE_HANDLE) == sizeof(HANDLE));
81 static_assert(sizeof(PIPE_HANDLE) == sizeof(HANDLE));
82 static_assert(sizeof(SOCKET_HANDLE) == sizeof(HANDLE));
83
84 struct COMOutputHandle : public WSLCHandle
85 {
86 NON_COPYABLE(COMOutputHandle);
87 NON_MOVABLE(COMOutputHandle);
88 COMOutputHandle()
89 {
90 ZeroMemory(&Handle, sizeof(Handle));
91 Type = WSLCHandleTypeUnknown;
92 }
93
94 ~COMOutputHandle()
95 {
96 Reset();
97 }
98
99 void Reset() noexcept
100 {
101 if (!Empty())
102 {
103 if (Type == WSLCHandleTypeSocket)
104 {
105 LOG_LAST_ERROR_IF(closesocket(reinterpret_cast<SOCKET>(Handle.Socket)) == SOCKET_ERROR);
106 }
107 else
108 {
109 LOG_IF_WIN32_BOOL_FALSE(CloseHandle(Handle.File));
110 }
111
112 Handle.File = nullptr;
113 Type = WSLCHandleTypeUnknown;
114 }
115 }
116
117 [[nodiscard]] io::HandleWrapper Release();
118
119 HANDLE Get() const noexcept
120 {
121 return Handle.File;
122 }
123
124 bool Empty() const noexcept
125 {
126 return Handle.File == nullptr || Handle.File == INVALID_HANDLE_VALUE;
127 }
128 };
129
130 struct PruneResult
131 {
132 NON_COPYABLE(PruneResult);
133 WSLCPruneContainersResults result{};
134
135 PruneResult() = default;
136
137 PruneResult(PruneResult&& other)
138 {
139 *this = std::move(other);
140 }
141
142 PruneResult& operator=(PruneResult&& other)
143 {
144 CoTaskMemFree(result.Containers);
145 result.Containers = other.result.Containers;
146 result.ContainersCount = other.result.ContainersCount;
147 result.SpaceReclaimed = other.result.SpaceReclaimed;
148
149 other.result.Containers = nullptr;
150 other.result.ContainersCount = 0;
151 other.result.SpaceReclaimed = 0;
152
153 return *this;
154 }
155
156 ~PruneResult()
157 {
158 CoTaskMemFree(result.Containers);
159 }
160 };
161
162 class StopWatch
163 {
164 NON_COPYABLE(StopWatch);
165 NON_MOVABLE(StopWatch);
166
167 public:
168 StopWatch() = default;
169
170 uint64_t ElapsedMilliseconds() const
171 {
172 return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_startTime).count();
173 }
174
175 private:
176 std::chrono::steady_clock::time_point m_startTime = std::chrono::steady_clock::now();
177 };
178
179 template <typename T>
180 void AssertValidPrintfArg()
181 {
182 static_assert(std::is_fundamental_v<T> || std::is_same_v<wchar_t*, T> || std::is_same_v<char*, T> || std::is_same_v<HRESULT, T>);
183 }
184
185 template <typename TInterface>
186 wil::com_ptr<TInterface> CoGetCallContext();
187
188 void CoInitializeSecurity();
189
190 void ConfigureCrt();
191
192 /// <summary>
193 /// Creates a COM server with user impersonation.
194 /// </summary>
195 template <typename Interface>
196 wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ REFCLSID RefClsId, _In_ HANDLE UserToken)
197 {
198 auto revert = wil::impersonate_token(UserToken);
199 return wil::CoCreateInstance<Interface>(RefClsId, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
200 }
201
202 template <typename Class, typename Interface>
203 wil::com_ptr_t<Interface> CreateComServerAsUser(_In_ HANDLE UserToken)
204 {
205 return CreateComServerAsUser<Interface>(__uuidof(Class), UserToken);
206 }
207
208 std::wstring ConstructPipePath(_In_ std::wstring_view PipeName);
209
210 GUID CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name);
211
212 std::wstring DownloadFile(std::wstring_view Url, std::wstring Filename, bool reportProgress = true);
213
214 std::wstring DownloadFileImpl(std::wstring_view Url, std::wstring Filename, const std::function<void(uint64_t, uint64_t)>& Progress);
215
216 [[nodiscard]] HANDLE DuplicateHandle(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess = std::nullopt, _In_ BOOL InheritHandle = FALSE);
217
218 [[nodiscard]] HANDLE DuplicateHandleFromCallingProcess(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess = {});
219
220 [[nodiscard]] HANDLE DuplicateHandleToCallingProcess(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess = {});
221
222 void EnforceFileLimit(LPCWSTR Folder, size_t limit, const std::function<bool(const std::filesystem::directory_entry&)>& pred);
223
224 std::wstring ErrorCodeToString(HRESULT Error);
225
226 ErrorStrings ErrorToString(const Error& error);
227
228 [[nodiscard]] HANDLE FromCOMInputHandle(WSLCHandle Handle);
229
230 std::filesystem::path GetBasePath();
231
232 std::optional<COMErrorInfo> GetCOMErrorInfo();
233
234 DWORD GetDefaultVersion(void);
235
236 std::wstring GetErrorString(_In_ HRESULT result);
237
238 std::optional<std::pair<std::wstring, GitHubReleaseAsset>> GetGitHubAssetFromRelease(const GitHubRelease& Release);
239
240 std::pair<std::wstring, GitHubReleaseAsset> GetLatestGitHubRelease(_In_ bool preRelease);
241
242 std::pair<std::wstring, GitHubReleaseAsset> GetLatestGitHubRelease(_In_ bool preRelease, _In_ LPCWSTR releases);
243
244 GitHubRelease GetGitHubReleaseByTag(_In_ const std::wstring& Version);
245
246 int GetLogicalProcessorCount();
247
248 std::optional<std::wstring> GetMsiPackagePath();
249
250 std::wstring GetPackageFamilyName(_In_ HANDLE process = GetCurrentProcess());
251
252 std::wstring GetSystemErrorString(_In_ HRESULT result);
253
254 std::wstring GetDebugShellPipeName(_In_ PSID Sid);
255
256 std::optional<std::tuple<uint32_t, uint32_t, uint32_t>> GetInstalledPackageVersion();
257
258 std::vector<BYTE> HashFile(HANDLE File, DWORD Algorithm);
259
260 void InitializeWil();
261
262 bool IsConsoleHandle(HANDLE Handle);
263
264 bool IsInteractiveConsole();
265
266 bool IsRunningInMsix();
267
268 bool IsVhdFile(_In_ const std::filesystem::path& path);
269
270 bool IsVirtualMachinePlatformInstalled();
271
272 std::vector<DWORD> ListRunningProcesses();
273
274 // An immutable container repository reference. Holds the original repository token together with its normalized
275 // registry server and path, following Docker's client-side normalization (e.g. "ubuntu" -> {"docker.io",
276 // "library/ubuntu"}). Construct one with Parse(). Normalization is lossy, so the original Name is retained for
277 // callers that must echo the repository exactly as it was written.
278 struct RepositoryReference
279 {
280 const std::string Name;
281 const std::string Server;
282 const std::string Path;
283
284 // Split and normalize a repository string into its registry server and path.
285 static RepositoryReference Parse(const std::string& repository);
286
287 // The fully-qualified "server/path" form (e.g. "docker.io/library/ubuntu").
288 std::string GetCanonical() const;
289 };
290
291 std::pair<wil::unique_hfile, wil::unique_hfile> OpenAnonymousPipe(DWORD Size, bool ReadPipeOverlapped, bool WritePipeOverlapped);
292
293 wil::unique_handle OpenCallingProcess(_In_ DWORD access);
294
295 void ParseIpv4Address(const char* Address, in_addr& Result);
296
297 void ParseIpv6Address(const char* Address, in_addr6& Result);
298
299 std::tuple<uint32_t, uint32_t, uint32_t> ParseWslPackageVersion(_In_ const std::wstring& Version);
300
301 // A parsed, immutable image reference such as "ubuntu:22.04@sha256:...". Construct one with Parse().
302 struct ImageReference
303 {
304 const RepositoryReference Repository;
305 const std::optional<std::string> Tag;
306 const std::optional<std::string> Digest;
307 const EnumReferenceFormat Format;
308
309 // Parse an image reference string into its components. Throws E_INVALIDARG (with a user-facing error) when the
310 // reference is malformed.
311 static ImageReference Parse(const std::string& input);
312
313 // Collapse the reference to a single tag-or-digest field, where a digest takes precedence over a tag. This matches
314 // how callers that resolve, pull or push a single reference treat the two.
315 std::optional<std::string> TagOrDigest() const
316 {
317 return Digest.has_value() ? Digest : Tag;
318 }
319
320 // The fully-qualified canonical reference, matching the string printed by `docker pull`
321 // (e.g. "ubuntu" -> "docker.io/library/ubuntu:latest").
322 std::string GetCanonical() const;
323 };
324
325 void PrintSystemError(_In_ HRESULT result, _Inout_ FILE* stream = stdout);
326
327 void PrintMessageImpl(_In_ const std::wstring& message, _In_ va_list& args, _Inout_ FILE* stream = stdout);
328
329 void PrintMessageImpl(_In_ const std::wstring& message, _Inout_ FILE* stream = stdout, ...);
330
331 void PrintMessage(_In_ const std::wstring& message, _Inout_ FILE* stream = stdout);
332
333 // This template is used to switch between the varargs and non-vararg versions of PrintMessage().
334 // If no varargs are passed, then the string shouldn't be used as a printf format specifier.
335 template <typename... Args>
336 void PrintMessage(_In_ const std::wstring& message, _Inout_ FILE* const stream = stdout, Args... args)
337 {
338 static_assert(sizeof...(Args) > 0);
339
340 // Validate that all Args are valid printf arguments.
341 (
342 [](auto e) {
343 using T = decltype(e);
344 if constexpr (std::is_pointer_v<T>)
345 {
346 AssertValidPrintfArg<std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>>();
347 }
348 else
349 {
350 AssertValidPrintfArg<std::remove_const_t<T>>();
351 }
352 }(args),
353 ...);
354
355 PrintMessageImpl(message, stream, std::forward<Args>(args)...);
356 }
357
358 // Reads an environment variable. Returns nullopt iff the variable is not defined; an engaged
359 // (possibly empty) string otherwise.
360 std::optional<std::wstring> ReadEnvironmentVariable(_In_ LPCWSTR Name);
361
362 void SetCrtEncoding(int Mode);
363
364 void SetThreadDescription(LPCWSTR Name);
365
366 wil::unique_hlocal_string SidToString(_In_ PSID Sid);
367
368 WSLCHandle ToCOMInputHandle(HANDLE Handle);
369 [[nodiscard]] WSLCHandle ToCOMOutputHandle(HANDLE Handle, DWORD Access);
370 [[nodiscard]] WSLCHandle ToCOMOutputHandle(HANDLE Handle, DWORD Access, WSLCHandleType Type);
371
372 winrt::Windows::Management::Deployment::PackageVolume GetSystemVolume();
373
374 std::string Base64Encode(const std::string& input);
375 std::string Base64Decode(const std::string& encoded);
376
377 // Builds the base64-encoded X-Registry-Auth header value used by Docker APIs
378 // (PullImage, PushImage, etc.) from the given credentials.
379 std::string BuildRegistryAuthHeader(const std::string& username, const std::string& password);
380
381 // Builds the base64-encoded X-Registry-Auth header value from an identity token
382 // returned by Authenticate().
383 std::string BuildRegistryAuthHeader(const std::string& identityToken);
384
385 std::map<std::string, std::string> ParseKeyValuePairs(_In_reads_opt_(count) const KeyValuePair* pairs, ULONG count, _In_opt_ LPCSTR reservedKey = nullptr);
386 std::map<std::string, std::vector<std::string>> ParseKeyMultiValuePairs(_In_reads_opt_(count) const KeyValuePair* pairs, ULONG count);
387
388 } // namespace wsl::windows::common::wslutil