master
cpp 248 lines 7.6 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 DistributionRegistration.cpp
8
9 Abstract:
10
11 This file contains the DistributionRegistration helper class implementation.
12
13 --*/
14
15 #include "precomp.h"
16
17 #include "DistributionRegistration.h"
18
19 using wsl::windows::service::DistributionRegistration;
20 using namespace wsl::windows::common;
21 using namespace registry;
22
23 constexpr auto DefaultDistro = L"DefaultDistribution";
24
25 namespace {
26
27 template <typename T>
28 T ApplyTransform(T&& value, T (*transform)(T))
29 {
30 if (transform == nullptr)
31 {
32 return value;
33 }
34
35 return transform(std::move(value));
36 }
37 } // namespace
38
39 DistributionRegistration DistributionRegistration::Open(HKEY LxssKey, const GUID& Id)
40 {
41 ExecutionContext context(Context::ReadDistroConfig);
42
43 const auto distroGuidString = wsl::shared::string::GuidToString<wchar_t>(Id);
44
45 wil::unique_hkey distroKey;
46 try
47 {
48 distroKey = wsl::windows::common::registry::OpenKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE));
49 }
50 catch (...)
51 {
52 THROW_HR_IF(WSL_E_DISTRO_NOT_FOUND, wil::ResultFromCaughtException() == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
53 throw;
54 }
55
56 return DistributionRegistration(Id, std::move(distroKey));
57 }
58
59 DistributionRegistration DistributionRegistration::Create(
60 HKEY LxssKey, const std::optional<GUID>& Id, LPCWSTR Name, ULONG Version, LPCWSTR BasePath, ULONG Flags, ULONG DefaultUID, LPCWSTR PackageFamilyName, LPCWSTR VhdFileName, bool EnableOobe)
61 {
62 std::wstring distroGuidString;
63 GUID distroId{};
64 wil::unique_hkey distroKey{};
65 if (Id.has_value())
66 {
67 distroId = Id.value();
68 distroGuidString = wsl::shared::string::GuidToString<wchar_t>(distroId);
69 distroKey = wsl::windows::common::registry::CreateKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE));
70 }
71 else
72 {
73 DWORD disposition = 0;
74 do
75 {
76 THROW_IF_FAILED(CoCreateGuid(&distroId));
77
78 distroGuidString = wsl::shared::string::GuidToString<wchar_t>(distroId);
79 distroKey = wsl::windows::common::registry::CreateKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE), &disposition);
80 } while (disposition != REG_CREATED_NEW_KEY);
81 }
82
83 WI_ASSERT(distroKey && !distroGuidString.empty());
84
85 // Set up a scope exit member to delete the key if registration fails.
86 auto cleanup = wil::scope_exit([&] { wsl::windows::common::registry::DeleteKey(LxssKey, distroGuidString.c_str()); });
87
88 DistributionRegistration distribution(distroId, std::move(distroKey));
89
90 distribution.Write(Property::State, LxssDistributionStateInstalling);
91
92 if (Name != nullptr)
93 {
94 distribution.Write(Property::Name, Name);
95 }
96 distribution.Write(Property::Version, Version);
97 distribution.Write(Property::BasePath, BasePath);
98 distribution.Write(Property::Flags, Flags);
99 distribution.Write(Property::DefaultUid, DefaultUID);
100 distribution.Write(Property::RunOOBE, EnableOobe);
101
102 if (ARGUMENT_PRESENT(PackageFamilyName))
103 {
104 WI_ASSERT(wcslen(PackageFamilyName) > 0);
105
106 distribution.Write(Property::PackageFamilyName, PackageFamilyName);
107 }
108
109 if (ARGUMENT_PRESENT(VhdFileName))
110 {
111 distribution.Write(Property::VhdFileName, VhdFileName);
112 }
113
114 // Dismiss the scope exit member so the key is persisted.
115 cleanup.release();
116 return distribution;
117 }
118
119 std::optional<DistributionRegistration> DistributionRegistration::OpenDefault(HKEY LxssKey)
120 {
121 const auto defaultId = wsl::windows::common::registry::ReadOptionalString(LxssKey, nullptr, DefaultDistro);
122 if (!defaultId.has_value())
123 {
124 return {};
125 }
126
127 const auto distroGuid = wsl::shared::string::ToGuid(defaultId.value());
128 if (!distroGuid.has_value())
129 {
130 return {};
131 }
132
133 try
134 {
135 return Open(LxssKey, distroGuid.value());
136 }
137 catch (...)
138 {
139 // If we hit this block, it means that the default distribution value point to a distribution that doesn't exist.
140 // Handle gracefully so this doesn't prevent the user from installing new distros.
141
142 LOG_CAUGHT_EXCEPTION_MSG("Broken default distro. ID: %ls", defaultId->c_str());
143 return {};
144 }
145 }
146
147 DistributionRegistration DistributionRegistration::OpenOrDefault(HKEY LxssKey, const GUID* Id)
148 {
149 if (Id == nullptr)
150 {
151 auto defaultDistribution = OpenDefault(LxssKey);
152 THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, !defaultDistribution.has_value());
153
154 return std::move(defaultDistribution.value());
155 }
156 else
157 {
158 return Open(LxssKey, *Id);
159 }
160 }
161
162 void DistributionRegistration::SetDefault(HKEY LxssKey, const DistributionRegistration& Distro)
163 {
164 wsl::windows::common::registry::WriteString(
165 LxssKey, nullptr, DefaultDistro, wsl::shared::string::GuidToString<wchar_t>(Distro.Id()).c_str());
166 }
167
168 void DistributionRegistration::DeleteDefault(HKEY LxssKey)
169 {
170 wsl::windows::common::registry::DeleteKeyValue(LxssKey, DefaultDistro);
171 }
172
173 DistributionRegistration::DistributionRegistration(const GUID& Id, wil::unique_hkey&& key) : m_id(Id), m_key{std::move(key)}
174 {
175 }
176
177 const GUID& DistributionRegistration::Id() const
178 {
179 return m_id;
180 }
181
182 std::wstring DistributionRegistration::Read(const DistributionPropertyWithDefault<LPCWSTR>& property) const
183 {
184 return ReadString(m_key.get(), nullptr, property.Name, property.DefaultValue);
185 }
186
187 DWORD DistributionRegistration::Read(const DistributionPropertyWithDefault<DWORD>& property) const
188 {
189 return ApplyTransform(ReadDword(m_key.get(), nullptr, property.Name, property.DefaultValue), property.Transform);
190 }
191
192 std::optional<std::wstring> DistributionRegistration::Read(const DistributionProperty<LPCWSTR>& property) const
193 {
194 return ReadOptionalString(m_key.get(), nullptr, property.Name);
195 }
196
197 std::vector<std::string> DistributionRegistration::Read(const DistributionPropertyWithDefault<std::vector<std::string>>& property) const
198 {
199 return wsl::windows::common::registry::ReadStringSet(m_key.get(), nullptr, property.Name, property.DefaultValue);
200 }
201
202 std::wstring DistributionRegistration::Read(const ExpectedProperty<LPCWSTR>& property) const
203 {
204 auto value = Read(static_cast<DistributionProperty<LPCWSTR>>(property));
205 if (!value.has_value())
206 {
207 THROW_HR_WITH_USER_ERROR(
208 E_UNEXPECTED,
209 wsl::shared::Localization::MessageCorruptedDistroRegistration(
210 property.Name, wsl::shared::string::GuidToString<wchar_t>(m_id).c_str()));
211 }
212
213 return value.value();
214 }
215
216 void DistributionRegistration::Write(const DistributionProperty<LPCWSTR>& property, LPCWSTR value) const
217 {
218 return WriteString(m_key.get(), nullptr, property.Name, value);
219 }
220
221 void DistributionRegistration::Write(const DistributionProperty<DWORD>& property, DWORD value) const
222 {
223 return WriteDword(m_key.get(), nullptr, property.Name, value);
224 }
225
226 std::filesystem::path DistributionRegistration::ReadVhdFilePath() const
227 {
228 return std::filesystem::path(Read(Property::BasePath)) / Read(Property::VhdFileName);
229 }
230
231 DWORD
232 DistributionRegistration::ApplyGlobalFlagsOverride(DWORD Flags)
233 {
234 WI_ASSERT(!WI_IsAnyFlagSet(Flags, ~LXSS_DISTRO_FLAGS_ALL));
235
236 DWORD globalFlags =
237 wsl::windows::common::registry::ReadDword(HKEY_LOCAL_MACHINE, LXSS_SERVICE_REGISTRY_PATH, L"DistributionFlags", LXSS_DISTRO_FLAGS_ALL);
238
239 // The VM Mode flag cannot be overridden by global flags.
240 WI_SetFlag(globalFlags, LXSS_DISTRO_FLAGS_VM_MODE);
241 Flags &= (globalFlags & LXSS_DISTRO_FLAGS_ALL);
242 return Flags;
243 }
244
245 void DistributionRegistration::Delete(HKEY LxssKey) const
246 {
247 DeleteKey(LxssKey, wsl::shared::string::GuidToString<wchar_t>(m_id).c_str());
248 }