diff --git a/src/linux/init/LSWInit.cpp b/src/linux/init/LSWInit.cpp index cbc1ff7..79c75f0 100644 --- a/src/linux/init/LSWInit.cpp +++ b/src/linux/init/LSWInit.cpp @@ -427,6 +427,18 @@ void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const LSW_SIGNAL& Me Channel.SendResultMessage(result < 0 ? errno : 0); } +void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const LSW_UNMOUNT& Message, const gsl::span& Buffer) +{ + Channel.SendResultMessage(umount(Message.Buffer) == 0 ? 0 : errno); +} + +void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const LSW_DETACH& Message, const gsl::span& Buffer) +{ + sync(); + + Channel.SendResultMessage(DetachScsiDisk(Message.Lun)); +} + template void HandleMessage(wsl::shared::SocketChannel& Channel, LX_MESSAGE_TYPE Type, const gsl::span& Buffer) { @@ -461,7 +473,7 @@ void ProcessMessage(wsl::shared::SocketChannel& Channel, LX_MESSAGE_TYPE Type, c { try { - HandleMessage( + HandleMessage( Channel, Type, Buffer); } catch (...) diff --git a/src/shared/inc/lxinitshared.h b/src/shared/inc/lxinitshared.h index dee0ea6..7351ab9 100644 --- a/src/shared/inc/lxinitshared.h +++ b/src/shared/inc/lxinitshared.h @@ -384,6 +384,8 @@ typedef enum _LX_MESSAGE_TYPE LxMessageLswMapPort, LxMessageLswConnectRelay, LxMessageLswPortRelay, + LxMessageLswUnmount, + LxMessageLswDetach, } LX_MESSAGE_TYPE, *PLX_MESSAGE_TYPE; @@ -488,6 +490,8 @@ inline auto ToString(LX_MESSAGE_TYPE messageType) X(LxMessageLswMapPort) X(LxMessageLswConnectRelay) X(LxMessageLswPortRelay) + X(LxMessageLswUnmount) + X(LxMessageLswDetach) default: return ""; } @@ -1760,6 +1764,31 @@ struct LSW_PORT_RELAY PRETTY_PRINT(FIELD(Header)); }; +struct LSW_UNMOUNT +{ + static inline auto Type = LxMessageLswUnmount; + using TResponse = RESULT_MESSAGE; + + DECLARE_MESSAGE_CTOR(LSW_UNMOUNT); + MESSAGE_HEADER Header; + + char Buffer[]; + + PRETTY_PRINT(FIELD(Header), FIELD(Buffer)); +}; + +struct LSW_DETACH +{ + static inline auto Type = LxMessageLswDetach; + using TResponse = RESULT_MESSAGE; + + DECLARE_MESSAGE_CTOR(LSW_DETACH); + MESSAGE_HEADER Header; + unsigned int Lun; + + PRETTY_PRINT(FIELD(Header), FIELD(Lun)); +}; + typedef struct _LX_MINI_INIT_IMPORT_RESULT { static inline auto Type = LxMiniInitMessageImportResult; diff --git a/src/windows/lswclient/DllMain.cpp b/src/windows/lswclient/DllMain.cpp index 21645d5..4f40d9d 100644 --- a/src/windows/lswclient/DllMain.cpp +++ b/src/windows/lswclient/DllMain.cpp @@ -111,9 +111,9 @@ CATCH_RETURN(); HRESULT WslAttachDisk(LSWVirtualMachineHandle VirtualMachine, const DiskAttachSettings* Settings, AttachedDiskInformation* AttachedDisk) { wil::unique_cotaskmem_ansistring device; - RETURN_IF_FAILED(reinterpret_cast(VirtualMachine)->AttachDisk(Settings->WindowsPath, Settings->ReadOnly, &device)); + RETURN_IF_FAILED(reinterpret_cast(VirtualMachine) + ->AttachDisk(Settings->WindowsPath, Settings->ReadOnly, &device, &AttachedDisk->ScsiLun)); - // TODO: wire LUN auto deviceSize = strlen(device.get()); WI_VERIFY(deviceSize < sizeof(AttachedDiskInformation::Device)); @@ -285,6 +285,15 @@ try } CATCH_RETURN(); +HRESULT WslUnmount(LSWVirtualMachineHandle VirtualMachine, const char* Path) +{ + return reinterpret_cast(VirtualMachine)->Unmount(Path); +} + +HRESULT WslDetachDisk(LSWVirtualMachineHandle VirtualMachine, ULONG Lun) +{ + return reinterpret_cast(VirtualMachine)->DetachDisk(Lun); +} EXTERN_C BOOL STDAPICALLTYPE DllMain(_In_ HINSTANCE Instance, _In_ DWORD Reason, _In_opt_ LPVOID Reserved) { wil::DLLMain(Instance, Reason, Reserved); diff --git a/src/windows/lswclient/LSWApi.h b/src/windows/lswclient/LSWApi.h index bd48f4e..84268b1 100644 --- a/src/windows/lswclient/LSWApi.h +++ b/src/windows/lswclient/LSWApi.h @@ -179,6 +179,10 @@ HRESULT WslMapPort(LSWVirtualMachineHandle VirtualMachine, const struct PortMapp HRESULT WslUnmapPort(LSWVirtualMachineHandle VirtualMachine, const struct PortMappingSettings* Settings); +HRESULT WslUnmount(LSWVirtualMachineHandle VirtualMachine, const char* Path); + +HRESULT WslDetachDisk(LSWVirtualMachineHandle VirtualMachine, ULONG Lun); + enum WslInstallComponent { WslInstallComponentNone = 0, diff --git a/src/windows/lswclient/lswclient.def b/src/windows/lswclient/lswclient.def index 726ff1a..98115c1 100644 --- a/src/windows/lswclient/lswclient.def +++ b/src/windows/lswclient/lswclient.def @@ -14,6 +14,8 @@ EXPORTS WslLaunchDebugShell WslMapPort WslUnmapPort + WslDetachDisk + WslUnmount WslQueryMissingComponents WslInstallComponents - WslSetPackageUrl \ No newline at end of file + WslSetPackageUrl diff --git a/src/windows/service/exe/LSWVirtualMachine.cpp b/src/windows/service/exe/LSWVirtualMachine.cpp index 3d6f8a3..a03f9f5 100644 --- a/src/windows/service/exe/LSWVirtualMachine.cpp +++ b/src/windows/service/exe/LSWVirtualMachine.cpp @@ -331,7 +331,7 @@ void LSWVirtualMachine::OnExit(_In_ const HCS_EVENT* Event) } } -HRESULT LSWVirtualMachine::AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device) +HRESULT LSWVirtualMachine::AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device, _Out_ ULONG* Lun) try { *Device = nullptr; @@ -345,35 +345,35 @@ try wsl::windows::common::hcs::GrantVmAccess(m_vmIdString.c_str(), Path); } - ULONG lun = 0; - while (m_attachedDisks.find(lun) != m_attachedDisks.end()) + *Lun = 0; + while (m_attachedDisks.find(*Lun) != m_attachedDisks.end()) { - lun++; + (*Lun)++; } bool vhdAdded = false; auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { if (vhdAdded) { - wsl::windows::common::hcs::RemoveScsiDisk(m_computeSystem.get(), lun); + wsl::windows::common::hcs::RemoveScsiDisk(m_computeSystem.get(), *Lun); } wsl::windows::common::hcs::RevokeVmAccess(m_vmIdString.c_str(), Path); }); - wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, lun, ReadOnly); + wsl::windows::common::hcs::AddVhd(m_computeSystem.get(), Path, *Lun, ReadOnly); vhdAdded = true; LSW_GET_DISK message{}; message.Header.MessageSize = sizeof(message); message.Header.MessageType = LSW_GET_DISK::Type; - message.ScsiLun = lun; + message.ScsiLun = *Lun; const auto& response = m_initChannel.Transaction(message); THROW_HR_IF_MSG(E_FAIL, response.Result != 0, "Failed to attach disk, init returned: %lu", response.Result); cleanup.release(); - m_attachedDisks.emplace(lun, AttachedDisk{Path, response.Buffer}); + m_attachedDisks.emplace(*Lun, AttachedDisk{Path, response.Buffer}); *Device = wil::make_unique_ansistring(response.Buffer).release(); }); @@ -431,6 +431,49 @@ try } CATCH_RETURN(); +HRESULT LSWVirtualMachine::Unmount(_In_ const char* Path) +try +{ + auto [pid, _, subChannel] = Fork(LSW_FORK::Thread); + + wsl::shared::MessageWriter message; + message.WriteString(Path); + + const auto& response = subChannel.Transaction(message.Span()); + + // TODO: Return errno to caller + THROW_HR_IF(E_FAIL, response.Result != 0); + + return S_OK; +} +CATCH_RETURN() + +HRESULT LSWVirtualMachine::DetachDisk(_In_ ULONG Lun) +try +{ + std::lock_guard lock{m_lock}; + + // Find the disk + auto it = m_attachedDisks.find(Lun); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), it == m_attachedDisks.end()); + + // Detach it from the guest + LSW_DETACH message; + message.Lun = Lun; + const auto& response = m_initChannel.Transaction(message); + + // TODO: Return errno to caller + THROW_HR_IF(E_FAIL, response.Result != 0); + + // Remove it from the VM + m_attachedDisks.erase(it); + + hcs::RemoveScsiDisk(m_computeSystem.get(), Lun); + + return S_OK; +} +CATCH_RETURN() + std::tuple LSWVirtualMachine::Fork(enum LSW_FORK::ForkType Type) { std::lock_guard lock{m_lock}; diff --git a/src/windows/service/exe/LSWVirtualMachine.h b/src/windows/service/exe/LSWVirtualMachine.h index e8aa0f0..fadf9e9 100644 --- a/src/windows/service/exe/LSWVirtualMachine.h +++ b/src/windows/service/exe/LSWVirtualMachine.h @@ -28,7 +28,7 @@ public: void Start(); - IFACEMETHOD(AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device)) override; + IFACEMETHOD(AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly, _Out_ LPSTR* Device, _Out_ ULONG* Lun)) override; IFACEMETHOD(Mount(_In_ LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags)) override; IFACEMETHOD(CreateLinuxProcess( _In_ const LSW_CREATE_PROCESS_OPTIONS* Options, _In_ ULONG FdCount, _In_ LSW_PROCESS_FD* Fd, _Out_ HANDLE* Handles, _Out_ LSW_CREATE_PROCESS_RESULT* Result)) override; @@ -38,6 +38,8 @@ public: IFACEMETHOD(RegisterCallback(_In_ ITerminationCallback* callback)) override; IFACEMETHOD(GetDebugShellPipe(_Out_ LPWSTR* pipePath)) override; IFACEMETHOD(MapPort(_In_ int Family, _In_ short WindowsPort, _In_ short LinuxPort, _In_ BOOL Remove)) override; + IFACEMETHOD(Unmount(_In_ const char* Path)) override; + IFACEMETHOD(DetachDisk(_In_ ULONG Lun)) override; private: static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context); diff --git a/src/windows/service/inc/wslservice.idl b/src/windows/service/inc/wslservice.idl index 7d7bc65..86a3e2b 100644 --- a/src/windows/service/inc/wslservice.idl +++ b/src/windows/service/inc/wslservice.idl @@ -449,7 +449,7 @@ interface ITerminationCallback : IUnknown ] interface ILSWVirtualMachine : IUnknown { - HRESULT AttachDisk([in] LPCWSTR Path, [in] BOOL ReadOnly, [out] LPSTR* Device); + HRESULT AttachDisk([in] LPCWSTR Path, [in] BOOL ReadOnly, [out] LPSTR* Device, [out] ULONG* Lun); HRESULT Mount([in, unique] LPCSTR Source, [in] LPCSTR Target, [in] LPCSTR Type, [in] LPCSTR Options, [in] ULONG Flags); HRESULT CreateLinuxProcess([in] const LSW_CREATE_PROCESS_OPTIONS* Options, [in] ULONG FdCount, [in, unique, size_is(FdCount)] LSW_PROCESS_FD* Fds, [out, size_is(FdCount)] HVSOCKET_HANDLE* Handles, [out] LSW_CREATE_PROCESS_RESULT* Result); HRESULT WaitPid([in] LONG Pid, [in] ULONGLONG TimeoutMs, [out] ULONG* State, [out] int* Code); @@ -458,6 +458,8 @@ interface ILSWVirtualMachine : IUnknown HRESULT RegisterCallback([in] ITerminationCallback* terminationCallback); HRESULT GetDebugShellPipe([out] LPWSTR* pipePath); HRESULT MapPort([in] int Family, [in] short WindowsPort, [in] short LinuxPort, [in] BOOL Remove); + HRESULT Unmount([in] LPCSTR Path); + HRESULT DetachDisk([in] ULONG Lun); } typedef diff --git a/test/windows/LSWTests.cpp b/test/windows/LSWTests.cpp index 7274c89..bfd322e 100644 --- a/test/windows/LSWTests.cpp +++ b/test/windows/LSWTests.cpp @@ -122,6 +122,64 @@ class LSWTests return vm; } + TEST_METHOD(AttachDetach) + { + WSL2_TEST_ONLY(); + + VirtualMachineSettings settings{}; + settings.CPU.CpuCount = 4; + settings.DisplayName = L"LSW"; + settings.Memory.MemoryMb = 1024; + settings.Options.BootTimeoutMs = 30000; + auto vm = CreateVm(&settings); + +#ifdef WSL_DEV_INSTALL_PATH + + auto vhdPath = std::filesystem::path(WSL_DEV_INSTALL_PATH) / "system.vhd"; +#else + + auto msiPath = wsl::windows::common::wslutil::GetMsiPackagePath(); + VERIFY_IS_TRUE(msiPath.has_value()); + + auto vhdPath = std::filesystem::path(msiPath.value()) / "system.vhd"; + +#endif + + auto blockDeviceExists = [&](ULONG Lun) { + std::string device = std::format("/sys/bus/scsi/devices/0:0:0:{}", Lun); + std::vector cmd{"/usr/bin/test", "-d", device.c_str()}; + return RunCommand(vm.get(), cmd) == 0; + }; + + // Attach the disk. + DiskAttachSettings attachSettings{vhdPath.c_str(), true}; + AttachedDiskInformation attachedDisk{}; + VERIFY_SUCCEEDED(WslAttachDisk(vm.get(), &attachSettings, &attachedDisk)); + VERIFY_IS_TRUE(blockDeviceExists(attachedDisk.ScsiLun)); + + // Mount it to /mnt. + MountSettings mountSettings{attachedDisk.Device, "/mnt", "ext4", "ro"}; + VERIFY_SUCCEEDED(WslMount(vm.get(), &mountSettings)); + + // Validate that the mountpoint is present. + std::vector cmd{"/usr/bin/mountpoint", "/mnt"}; + VERIFY_ARE_EQUAL(RunCommand(vm.get(), cmd), 0L); + + // Unmount /mnt. + VERIFY_SUCCEEDED(WslUnmount(vm.get(), "/mnt")); + VERIFY_ARE_EQUAL(RunCommand(vm.get(), cmd), 32L); + + // Verify that unmount fails now. + VERIFY_ARE_EQUAL(WslUnmount(vm.get(), "/mnt"), E_FAIL); + + // Detach the disk + VERIFY_SUCCEEDED(WslDetachDisk(vm.get(), attachedDisk.ScsiLun)); + VERIFY_IS_FALSE(blockDeviceExists(attachedDisk.ScsiLun)); + + // Verify that disk can't be detached twice + VERIFY_ARE_EQUAL(WslDetachDisk(vm.get(), attachedDisk.ScsiLun), HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); + } + TEST_METHOD(CustomDmesgOutput) { WSL2_TEST_ONLY();