|
| 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) : |
| 8 | + m_machineId(machineId), m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId)) |
| 9 | +{ |
| 10 | +} |
| 11 | + |
| 12 | +_Requires_lock_not_held_(m_lock) |
| 13 | +GUID GuestDeviceManager::AddGuestDevice( |
| 14 | + _In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken) |
| 15 | +{ |
| 16 | + auto guestDeviceLock = m_lock.lock_exclusive(); |
| 17 | + return AddHdvShareWithOptions(DeviceId, ImplementationClsid, AccessName, Options, Path, Flags, UserToken); |
| 18 | +} |
| 19 | + |
| 20 | +_Requires_lock_held_(m_lock) |
| 21 | +GUID GuestDeviceManager::AddHdvShareWithOptions( |
| 22 | + _In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken) |
| 23 | +{ |
| 24 | + wil::com_ptr<IPlan9FileSystem> server; |
| 25 | + |
| 26 | + // Options are appended to the name with a semi-colon separator. |
| 27 | + // "name;key1=value1;key2=value2" |
| 28 | + // The AddSharePath implementation is responsible for separating them out and interpreting them. |
| 29 | + std::wstring nameWithOptions{AccessName}; |
| 30 | + if (ARGUMENT_PRESENT(Options)) |
| 31 | + { |
| 32 | + nameWithOptions += L";"; |
| 33 | + nameWithOptions += Options; |
| 34 | + } |
| 35 | + |
| 36 | + { |
| 37 | + auto revert = wil::impersonate_token(UserToken); |
| 38 | + |
| 39 | + server = GetRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag); |
| 40 | + if (!server) |
| 41 | + { |
| 42 | + server = wil::CoCreateInstance<IPlan9FileSystem>(ImplementationClsid, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA)); |
| 43 | + AddRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag.c_str(), server); |
| 44 | + } |
| 45 | + |
| 46 | + THROW_IF_FAILED(server->AddSharePath(nameWithOptions.c_str(), Path, Flags)); |
| 47 | + } |
| 48 | + |
| 49 | + // This requires more privileges than the user may have, so impersonation is disabled. |
| 50 | + return AddNewDevice(DeviceId, server, AccessName); |
| 51 | +} |
| 52 | + |
| 53 | +GUID GuestDeviceManager::AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag) |
| 54 | +{ |
| 55 | + THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport); |
| 56 | + return m_deviceHostSupport->AddNewDevice(deviceId, server, tag); |
| 57 | +} |
| 58 | + |
| 59 | +void GuestDeviceManager::AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server) |
| 60 | +{ |
| 61 | + THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport); |
| 62 | + m_deviceHostSupport->AddRemoteFileSystem(clsid, tag, server); |
| 63 | +} |
| 64 | + |
| 65 | +void GuestDeviceManager::AddSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken) |
| 66 | +{ |
| 67 | + auto guestDeviceLock = m_lock.lock_exclusive(); |
| 68 | + auto objectLifetime = CreateSectionObjectRoot(Path, UserToken); |
| 69 | + |
| 70 | + // For virtiofs hdv, the flags parameter has been overloaded. Flags are placed in the lower |
| 71 | + // 16 bits, while the shared memory size in megabytes are placed in the upper 16 bits. |
| 72 | + static constexpr auto VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT = 16; |
| 73 | + UINT32 flags = (SizeMb << VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT); |
| 74 | + WI_SetFlag(flags, VIRTIO_FS_FLAGS_TYPE_SECTIONS); |
| 75 | + (void)AddHdvShareWithOptions(VIRTIO_VIRTIOFS_DEVICE_ID, ImplementationClsid, Tag, {}, objectLifetime.Path.c_str(), flags, UserToken); |
| 76 | + m_objectDirectories.emplace_back(std::move(objectLifetime)); |
| 77 | +} |
| 78 | + |
| 79 | +GuestDeviceManager::DirectoryObjectLifetime GuestDeviceManager::CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const |
| 80 | +{ |
| 81 | + auto revert = wil::impersonate_token(UserToken); |
| 82 | + DWORD sessionId; |
| 83 | + DWORD bytesWritten; |
| 84 | + THROW_LAST_ERROR_IF(!GetTokenInformation(GetCurrentThreadToken(), TokenSessionId, &sessionId, sizeof(sessionId), &bytesWritten)); |
| 85 | + |
| 86 | + // /Sessions/1/BaseNamedObjects/WSL/<VM ID>/<Relative Path> |
| 87 | + std::wstringstream sectionPathBuilder; |
| 88 | + sectionPathBuilder << L"\\Sessions\\" << sessionId << L"\\BaseNamedObjects" << L"\\WSL\\" << m_machineId << L"\\" << RelativeRootPath; |
| 89 | + auto sectionPath = sectionPathBuilder.str(); |
| 90 | + |
| 91 | + UNICODE_STRING ntPath{}; |
| 92 | + OBJECT_ATTRIBUTES attributes{}; |
| 93 | + attributes.Length = sizeof(OBJECT_ATTRIBUTES); |
| 94 | + attributes.ObjectName = &ntPath; |
| 95 | + std::vector<wil::unique_handle> directoryHierarchy; |
| 96 | + auto remainingPath = std::wstring_view(sectionPath.data(), sectionPath.length()); |
| 97 | + while (remainingPath.length() > 0) |
| 98 | + { |
| 99 | + // Find the next path substring, ignoring the root path backslash. |
| 100 | + auto nextDir = remainingPath; |
| 101 | + const auto separatorPos = nextDir.find(L"\\", remainingPath[0] == L'\\' ? 1 : 0); |
| 102 | + if (separatorPos != std::wstring_view::npos) |
| 103 | + { |
| 104 | + nextDir = nextDir.substr(0, separatorPos); |
| 105 | + remainingPath = remainingPath.substr(separatorPos + 1, std::wstring_view::npos); |
| 106 | + |
| 107 | + // Skip concurrent backslashes. |
| 108 | + while (remainingPath.length() > 0 && remainingPath[0] == L'\\') |
| 109 | + { |
| 110 | + remainingPath = remainingPath.substr(1, std::wstring_view::npos); |
| 111 | + } |
| 112 | + } |
| 113 | + else |
| 114 | + { |
| 115 | + remainingPath = remainingPath.substr(remainingPath.length(), std::wstring_view::npos); |
| 116 | + } |
| 117 | + |
| 118 | + attributes.RootDirectory = directoryHierarchy.size() > 0 ? directoryHierarchy.back().get() : nullptr; |
| 119 | + ntPath.Buffer = const_cast<PWCH>(nextDir.data()); |
| 120 | + ntPath.Length = sizeof(WCHAR) * gsl::narrow_cast<USHORT>(nextDir.length()); |
| 121 | + ntPath.MaximumLength = ntPath.Length; |
| 122 | + wil::unique_handle nextHandle; |
| 123 | + NTSTATUS status = ZwCreateDirectoryObject(&nextHandle, DIRECTORY_ALL_ACCESS, &attributes); |
| 124 | + if (status == STATUS_OBJECT_NAME_COLLISION) |
| 125 | + { |
| 126 | + status = NtOpenDirectoryObject(&nextHandle, MAXIMUM_ALLOWED, &attributes); |
| 127 | + } |
| 128 | + THROW_IF_NTSTATUS_FAILED(status); |
| 129 | + directoryHierarchy.emplace_back(std::move(nextHandle)); |
| 130 | + } |
| 131 | + |
| 132 | + return {std::move(sectionPath), std::move(directoryHierarchy)}; |
| 133 | +} |
| 134 | + |
| 135 | +wil::com_ptr<IPlan9FileSystem> GuestDeviceManager::GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag) |
| 136 | +{ |
| 137 | + THROW_HR_IF(E_NOT_VALID_STATE, !m_deviceHostSupport); |
| 138 | + return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag); |
| 139 | +} |
| 140 | + |
| 141 | +void GuestDeviceManager::Shutdown() |
| 142 | +try |
| 143 | +{ |
| 144 | + if (m_deviceHostSupport) |
| 145 | + { |
| 146 | + m_deviceHostSupport->Shutdown(); |
| 147 | + } |
| 148 | +} |
| 149 | +CATCH_LOG() |
0 commit comments