| 1 | // Copyright (C) Microsoft Corporation. All rights reserved. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | #include "GuestDeviceManager.h" |
| 5 | #include "DeviceHostProxy.h" |
| 6 | |
| 7 | GuestDeviceManager::GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId, bool EnableTelemetry) : |
| 8 | m_machineId(machineId), m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId, EnableTelemetry)) |
| 9 | { |
| 10 | } |
| 11 | |
| 12 | GuestDeviceManager::~GuestDeviceManager() |
| 13 | { |
| 14 | try |
| 15 | { |
| 16 | m_deviceHostSupport->Shutdown(); |
| 17 | } |
| 18 | CATCH_LOG() |
| 19 | } |
| 20 | |
| 21 | _Requires_lock_not_held_(m_lock) |
| 22 | GUID GuestDeviceManager::AddVirtiofsDevice(_In_ PCWSTR Label, _In_opt_ PCWSTR MountOptions, _In_ PCWSTR RootPath, _In_ HANDLE UserToken, VirtioFsShareOptions Options) |
| 23 | { |
| 24 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 25 | return m_deviceHostSupport->AddVirtiofsDevice( |
| 26 | UserToken, Label, RootPath, Options.Kind, Options.SharedMemorySizeMb, MountOptions ? MountOptions : L""); |
| 27 | } |
| 28 | |
| 29 | _Requires_lock_not_held_(m_lock) |
| 30 | void GuestDeviceManager::AddVirtiofsChild(_In_ const GUID& InstanceId, _In_ PCWSTR Name, _In_opt_ PCWSTR MountOptions, _In_ PCWSTR RootPath) |
| 31 | { |
| 32 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 33 | m_deviceHostSupport->AddVirtiofsChild(InstanceId, Name, RootPath, MountOptions ? MountOptions : L""); |
| 34 | } |
| 35 | |
| 36 | _Requires_lock_not_held_(m_lock) |
| 37 | void GuestDeviceManager::RemoveVirtiofsChild(_In_ const GUID& InstanceId, _In_ PCWSTR Name) |
| 38 | { |
| 39 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 40 | m_deviceHostSupport->RemoveVirtiofsChild(InstanceId, Name); |
| 41 | } |
| 42 | |
| 43 | _Requires_lock_not_held_(m_lock) |
| 44 | GUID GuestDeviceManager::AddVirtioPmemDevice(_In_ PCWSTR Path, bool ReadOnly, _In_ HANDLE UserToken) |
| 45 | { |
| 46 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 47 | return m_deviceHostSupport->AddVirtioPmemDevice(UserToken, Path, !ReadOnly); |
| 48 | } |
| 49 | |
| 50 | _Requires_lock_not_held_(m_lock) |
| 51 | GUID GuestDeviceManager::AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag) |
| 52 | { |
| 53 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 54 | return m_deviceHostSupport->AddNewDevice(deviceId, server, tag); |
| 55 | } |
| 56 | |
| 57 | GUID GuestDeviceManager::AddVirtioNetDevice(_In_ PCWSTR Tag, const WslVirtioNetConfig& Config, const std::vector<IpAddress>& Nameservers, _In_ HANDLE UserToken) |
| 58 | { |
| 59 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 60 | THROW_HR_IF(E_INVALIDARG, m_virtioNetDevices.contains(Tag)); |
| 61 | const auto instanceId = m_deviceHostSupport->AddVirtioNetDevice(UserToken, Config, Nameservers); |
| 62 | m_virtioNetDevices.emplace(Tag, instanceId); |
| 63 | return instanceId; |
| 64 | } |
| 65 | |
| 66 | wil::com_ptr<IWslVirtioNetDevice> GuestDeviceManager::GetVirtioNetDevice(_In_ PCWSTR Tag) |
| 67 | { |
| 68 | auto guestDeviceLock = m_lock.lock_shared(); |
| 69 | const auto device = m_virtioNetDevices.find(Tag); |
| 70 | THROW_HR_IF(E_NOT_SET, device == m_virtioNetDevices.end()); |
| 71 | return m_deviceHostSupport->GetVirtioNetDevice(device->second); |
| 72 | } |
| 73 | |
| 74 | void GuestDeviceManager::AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server) |
| 75 | { |
| 76 | m_deviceHostSupport->AddRemoteFileSystem(clsid, tag, server); |
| 77 | } |
| 78 | |
| 79 | void GuestDeviceManager::AddSharedMemoryDevice(_In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken) |
| 80 | { |
| 81 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 82 | auto objectLifetime = CreateSectionObjectRoot(Path, UserToken); |
| 83 | |
| 84 | (void)m_deviceHostSupport->AddVirtiofsDevice( |
| 85 | UserToken, Tag, objectLifetime.Path, VirtiofsShareKind_SectionBacked, SizeMb, L""); |
| 86 | m_objectDirectories.emplace_back(std::move(objectLifetime)); |
| 87 | } |
| 88 | |
| 89 | GuestDeviceManager::DirectoryObjectLifetime GuestDeviceManager::CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const |
| 90 | { |
| 91 | auto revert = wil::impersonate_token(UserToken); |
| 92 | DWORD sessionId; |
| 93 | DWORD bytesWritten; |
| 94 | THROW_LAST_ERROR_IF(!GetTokenInformation(GetCurrentThreadToken(), TokenSessionId, &sessionId, sizeof(sessionId), &bytesWritten)); |
| 95 | |
| 96 | // /Sessions/1/BaseNamedObjects/WSL/<VM ID>/<Relative Path> |
| 97 | std::wstringstream sectionPathBuilder; |
| 98 | sectionPathBuilder << L"\\Sessions\\" << sessionId << L"\\BaseNamedObjects" << L"\\WSL\\" << m_machineId << L"\\" << RelativeRootPath; |
| 99 | auto sectionPath = sectionPathBuilder.str(); |
| 100 | |
| 101 | UNICODE_STRING ntPath{}; |
| 102 | OBJECT_ATTRIBUTES attributes{}; |
| 103 | attributes.Length = sizeof(OBJECT_ATTRIBUTES); |
| 104 | attributes.ObjectName = &ntPath; |
| 105 | std::vector<wil::unique_handle> directoryHierarchy; |
| 106 | auto remainingPath = std::wstring_view(sectionPath.data(), sectionPath.length()); |
| 107 | while (remainingPath.length() > 0) |
| 108 | { |
| 109 | // Find the next path substring, ignoring the root path backslash. |
| 110 | auto nextDir = remainingPath; |
| 111 | const auto separatorPos = nextDir.find(L"\\", remainingPath[0] == L'\\' ? 1 : 0); |
| 112 | if (separatorPos != std::wstring_view::npos) |
| 113 | { |
| 114 | nextDir = nextDir.substr(0, separatorPos); |
| 115 | remainingPath = remainingPath.substr(separatorPos + 1, std::wstring_view::npos); |
| 116 | |
| 117 | // Skip concurrent backslashes. |
| 118 | while (remainingPath.length() > 0 && remainingPath[0] == L'\\') |
| 119 | { |
| 120 | remainingPath = remainingPath.substr(1, std::wstring_view::npos); |
| 121 | } |
| 122 | } |
| 123 | else |
| 124 | { |
| 125 | remainingPath = remainingPath.substr(remainingPath.length(), std::wstring_view::npos); |
| 126 | } |
| 127 | |
| 128 | attributes.RootDirectory = directoryHierarchy.size() > 0 ? directoryHierarchy.back().get() : nullptr; |
| 129 | ntPath.Buffer = const_cast<PWCH>(nextDir.data()); |
| 130 | ntPath.Length = sizeof(WCHAR) * gsl::narrow_cast<USHORT>(nextDir.length()); |
| 131 | ntPath.MaximumLength = ntPath.Length; |
| 132 | wil::unique_handle nextHandle; |
| 133 | NTSTATUS status = ZwCreateDirectoryObject(&nextHandle, DIRECTORY_ALL_ACCESS, &attributes); |
| 134 | if (status == STATUS_OBJECT_NAME_COLLISION) |
| 135 | { |
| 136 | status = NtOpenDirectoryObject(&nextHandle, MAXIMUM_ALLOWED, &attributes); |
| 137 | } |
| 138 | THROW_IF_NTSTATUS_FAILED(status); |
| 139 | directoryHierarchy.emplace_back(std::move(nextHandle)); |
| 140 | } |
| 141 | |
| 142 | return {std::move(sectionPath), std::move(directoryHierarchy)}; |
| 143 | } |
| 144 | |
| 145 | wil::com_ptr<IPlan9FileSystem> GuestDeviceManager::GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag) |
| 146 | { |
| 147 | return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag); |
| 148 | } |
| 149 | |
| 150 | void GuestDeviceManager::SetSwiotlb(UINT64 GpaBase, UINT64 SizeBytes) |
| 151 | { |
| 152 | m_deviceHostSupport->SetSwiotlb(GpaBase, SizeBytes); |
| 153 | } |
| 154 | |
| 155 | _Requires_lock_not_held_(m_lock) |
| 156 | void GuestDeviceManager::RemoveGuestDevice(_In_ const GUID& InstanceId) |
| 157 | { |
| 158 | auto guestDeviceLock = m_lock.lock_exclusive(); |
| 159 | for (auto it = m_virtioNetDevices.begin(); it != m_virtioNetDevices.end(); ++it) |
| 160 | { |
| 161 | if (IsEqualGUID(it->second, InstanceId)) |
| 162 | { |
| 163 | m_virtioNetDevices.erase(it); |
| 164 | break; |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | m_deviceHostSupport->RemoveDevice(InstanceId); |
| 169 | } |