mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 17:47:59 -06:00
330 lines
9.7 KiB
C++
330 lines
9.7 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
WSLASession.cpp
|
|
|
|
Abstract:
|
|
|
|
This file contains the implementation of the WSLASession COM class.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "WSLASession.h"
|
|
#include "WSLAUserSession.h"
|
|
#include "WSLAContainer.h"
|
|
#include "ServiceProcessLauncher.h"
|
|
#include "WslCoreFilesystem.h"
|
|
|
|
using wsl::windows::service::wsla::WSLASession;
|
|
using wsl::windows::service::wsla::WSLAVirtualMachine;
|
|
|
|
WSLASession::WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl) :
|
|
m_sessionSettings(Settings), m_userSession(&userSessionImpl), m_displayName(Settings.DisplayName)
|
|
{
|
|
WSL_LOG("SessionCreated", TraceLoggingValue(m_displayName.c_str(), "DisplayName"));
|
|
|
|
m_virtualMachine = wil::MakeOrThrow<WSLAVirtualMachine>(CreateVmSettings(Settings), userSessionImpl.GetUserSid());
|
|
|
|
if (Settings.TerminationCallback != nullptr)
|
|
{
|
|
m_virtualMachine->RegisterCallback(Settings.TerminationCallback);
|
|
}
|
|
|
|
m_virtualMachine->Start();
|
|
|
|
ConfigureStorage(Settings);
|
|
|
|
// Launch the init script.
|
|
// TODO: Replace with something more robust once the final VHD is ready.
|
|
try
|
|
{
|
|
ServiceProcessLauncher launcher{"/bin/sh", {"/bin/sh", "-c", "/etc/lsw-init.sh"}};
|
|
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
|
|
|
|
THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "Init script failed: %hs", launcher.FormatResult(result).c_str());
|
|
}
|
|
catch (...)
|
|
{
|
|
// Ignore issues launching the init script with custom root VHD's, for convenience.
|
|
// TODO: Remove once the final VHD is ready.
|
|
if (Settings.RootVhdOverride == nullptr)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
WSLAVirtualMachine::Settings WSLASession::CreateVmSettings(const WSLA_SESSION_SETTINGS& Settings)
|
|
{
|
|
WSLAVirtualMachine::Settings vmSettings{};
|
|
vmSettings.CpuCount = Settings.CpuCount;
|
|
vmSettings.MemoryMb = Settings.MemoryMb;
|
|
vmSettings.NetworkingMode = Settings.NetworkingMode;
|
|
vmSettings.BootTimeoutMs = Settings.BootTimeoutMs;
|
|
vmSettings.FeatureFlags = static_cast<WSLAFeatureFlags>(Settings.FeatureFlags);
|
|
vmSettings.DisplayName = Settings.DisplayName;
|
|
|
|
if (Settings.RootVhdOverride != nullptr)
|
|
{
|
|
THROW_HR_IF(E_INVALIDARG, Settings.RootVhdTypeOverride == nullptr);
|
|
|
|
vmSettings.RootVhd = Settings.RootVhdOverride;
|
|
vmSettings.RootVhdType = Settings.RootVhdTypeOverride;
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef WSLA_TEST_DISTRO_PATH
|
|
|
|
vmSettings.RootVhd = TEXT(WSLA_TEST_DISTRO_PATH);
|
|
|
|
#else
|
|
vmSettings.RootVhd = std::filesystem::path(common::wslutil::GetMsiPackagePath().value()) / L"wslarootfs.vhd";
|
|
|
|
#endif
|
|
|
|
vmSettings.RootVhdType = "squashfs";
|
|
}
|
|
|
|
if (Settings.DmesgOutput != 0)
|
|
{
|
|
vmSettings.DmesgHandle.reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(Settings.DmesgOutput)));
|
|
}
|
|
|
|
return vmSettings;
|
|
}
|
|
|
|
WSLASession::~WSLASession()
|
|
{
|
|
WSL_LOG("SessionTerminated", TraceLoggingValue(m_displayName.c_str(), "DisplayName"));
|
|
|
|
std::lock_guard lock{m_lock};
|
|
|
|
if (m_virtualMachine)
|
|
{
|
|
m_virtualMachine->OnSessionTerminated();
|
|
m_virtualMachine.Reset();
|
|
}
|
|
|
|
if (m_userSession != nullptr)
|
|
{
|
|
m_userSession->OnSessionTerminated(this);
|
|
}
|
|
}
|
|
|
|
void WSLASession::ConfigureStorage(const WSLA_SESSION_SETTINGS& Settings)
|
|
{
|
|
if (Settings.StoragePath == nullptr)
|
|
{
|
|
// If no storage path is specified, use a tmpfs for convenience.
|
|
m_virtualMachine->Mount("", "/root", "tmpfs", "", 0);
|
|
return;
|
|
}
|
|
|
|
std::filesystem::path storagePath{Settings.StoragePath};
|
|
THROW_HR_IF_MSG(E_INVALIDARG, !storagePath.is_absolute(), "Storage path is not absolute: %ls", storagePath.c_str());
|
|
|
|
m_storageVhdPath = storagePath / "storage.vhdx";
|
|
|
|
std::string diskDevice;
|
|
std::optional<ULONG> diskLun{};
|
|
bool vhdCreated = false;
|
|
|
|
auto deleteVhdOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
|
|
if (vhdCreated)
|
|
{
|
|
if (diskLun.has_value())
|
|
{
|
|
m_virtualMachine->DetachDisk(diskLun.value());
|
|
}
|
|
|
|
auto runAsUser = wil::CoImpersonateClient();
|
|
LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(m_storageVhdPath.c_str()));
|
|
}
|
|
});
|
|
|
|
auto result =
|
|
wil::ResultFromException([&]() { diskDevice = m_virtualMachine->AttachDisk(m_storageVhdPath.c_str(), false).second; });
|
|
|
|
if (FAILED(result))
|
|
{
|
|
THROW_HR_IF_MSG(
|
|
result,
|
|
result != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) && result != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
|
|
"Failed to attach vhd: %ls",
|
|
m_storageVhdPath.c_str());
|
|
|
|
// If the VHD wasn't found, create it.
|
|
WSL_LOG("CreateStorageVhd", TraceLoggingValue(m_storageVhdPath.c_str(), "StorageVhdPath"));
|
|
|
|
auto runAsUser = wil::CoImpersonateClient();
|
|
|
|
std::filesystem::create_directories(storagePath);
|
|
wsl::core::filesystem::CreateVhd(
|
|
m_storageVhdPath.c_str(), Settings.MaximumStorageSizeMb * _1MB, m_userSession->GetUserSid(), false, false);
|
|
vhdCreated = true;
|
|
|
|
// Then attach the new disk.
|
|
std::tie(diskLun, diskDevice) = m_virtualMachine->AttachDisk(m_storageVhdPath.c_str(), false);
|
|
|
|
// Then format it.
|
|
Ext4Format(diskDevice);
|
|
}
|
|
|
|
// Mount the device to /root.
|
|
m_virtualMachine->Mount(diskDevice.c_str(), "/root", "ext4", "", 0);
|
|
|
|
deleteVhdOnFailure.release();
|
|
}
|
|
|
|
HRESULT WSLASession::GetDisplayName(LPWSTR* DisplayName)
|
|
{
|
|
*DisplayName = wil::make_unique_string<wil::unique_cotaskmem_string>(m_displayName.c_str()).release();
|
|
return S_OK;
|
|
}
|
|
|
|
const std::wstring& WSLASession::DisplayName() const
|
|
{
|
|
return m_displayName;
|
|
}
|
|
|
|
HRESULT WSLASession::PullImage(LPCWSTR Image, const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, IProgressCallback* ProgressCallback)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT WSLASession::ImportImage(ULONG Handle, LPCWSTR Image, IProgressCallback* ProgressCallback)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT WSLASession::ListImages(WSLA_IMAGE_INFORMATION** Images, ULONG* Count)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT WSLASession::DeleteImage(LPCWSTR Image)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT WSLASession::CreateContainer(const WSLA_CONTAINER_OPTIONS* containerOptions, IWSLAContainer** Container)
|
|
try
|
|
{
|
|
RETURN_HR_IF_NULL(E_POINTER, containerOptions);
|
|
|
|
std::lock_guard lock{m_lock};
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
|
|
|
THROW_HR_IF(E_INVALIDARG, strlen(containerOptions->Name) > WSLA_MAX_CONTAINER_NAME_LENGTH);
|
|
THROW_HR_IF(E_INVALIDARG, strlen(containerOptions->Image) > WSLA_MAX_IMAGE_NAME_LENGTH);
|
|
|
|
// TODO: Log entrance into the function.
|
|
auto container = WSLAContainer::Create(*containerOptions, *m_virtualMachine.Get());
|
|
|
|
m_containers.Add(container.Get());
|
|
|
|
THROW_IF_FAILED(container.CopyTo(__uuidof(IWSLAContainer), (void**)Container));
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN();
|
|
|
|
HRESULT WSLASession::OpenContainer(LPCWSTR Name, IWSLAContainer** Container)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT WSLASession::ListContainers(WSLA_CONTAINER** Images, ULONG* Count)
|
|
{
|
|
auto lockedElements = m_containers.Get();
|
|
|
|
auto output = wil::make_unique_cotaskmem<WSLA_CONTAINER[]>(lockedElements.elements.size());
|
|
size_t index = 0;
|
|
for (const auto &e: lockedElements.elements)
|
|
{
|
|
e->GetImage(output[index].Image);
|
|
e->GetName(output[index].Name);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
HRESULT WSLASession::GetVirtualMachine(IWSLAVirtualMachine** VirtualMachine)
|
|
{
|
|
std::lock_guard lock{m_lock};
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
|
|
|
THROW_IF_FAILED(m_virtualMachine->QueryInterface(__uuidof(IWSLAVirtualMachine), (void**)VirtualMachine));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WSLASession::CreateRootNamespaceProcess(const WSLA_PROCESS_OPTIONS* Options, IWSLAProcess** Process, int* Errno)
|
|
try
|
|
{
|
|
if (Errno != nullptr)
|
|
{
|
|
*Errno = -1; // Make sure not to return 0 if something fails.
|
|
}
|
|
|
|
std::lock_guard lock{m_lock};
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
|
|
|
return m_virtualMachine->CreateLinuxProcess(Options, Process, Errno);
|
|
}
|
|
CATCH_RETURN();
|
|
|
|
void WSLASession::Ext4Format(const std::string& Device)
|
|
{
|
|
constexpr auto mkfsPath = "/usr/sbin/mkfs.ext4";
|
|
ServiceProcessLauncher launcher(mkfsPath, {mkfsPath, Device});
|
|
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
|
|
|
|
THROW_HR_IF_MSG(E_FAIL, result.Code != 0, "%hs", launcher.FormatResult(result).c_str());
|
|
}
|
|
|
|
HRESULT WSLASession::FormatVirtualDisk(LPCWSTR Path)
|
|
try
|
|
{
|
|
THROW_HR_IF_MSG(E_INVALIDARG, !std::filesystem::path(Path).is_absolute(), "FormatVirtualDisk called with a relative path: %ls", Path);
|
|
|
|
std::lock_guard lock{m_lock};
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
|
|
|
// Attach the disk to the VM (AttachDisk() performs the access check for the VHD file).
|
|
auto [lun, device] = m_virtualMachine->AttachDisk(Path, false);
|
|
|
|
// N.B. DetachDisk calls sync() before detaching.
|
|
auto detachDisk = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this, lun]() { m_virtualMachine->DetachDisk(lun); });
|
|
|
|
// Format it to ext4.
|
|
Ext4Format(device);
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN();
|
|
|
|
void WSLASession::OnUserSessionTerminating()
|
|
{
|
|
std::lock_guard lock{m_lock};
|
|
WI_ASSERT(m_userSession != nullptr);
|
|
|
|
m_userSession = nullptr;
|
|
m_virtualMachine.Reset();
|
|
}
|
|
|
|
HRESULT WSLASession::Shutdown(ULONG Timeout)
|
|
try
|
|
{
|
|
std::lock_guard lock{m_lock};
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
|
|
|
|
THROW_IF_FAILED(m_virtualMachine->Shutdown(Timeout));
|
|
|
|
m_virtualMachine.Reset();
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN(); |