master
cpp 290 lines 9.22 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssMessagePort.cpp
8
9 Abstract:
10
11 This file contains a wrapper class for LxBus message ports.
12
13 --*/
14
15 #include "precomp.h"
16 #include "LxssMessagePort.h"
17 #include "LxssServerPort.h"
18
19 // Defines.
20
21 #define LAUNCH_PROCESS_DEFAULT_BUFFER_SIZE 1024
22
23 LxssMessagePort::LxssMessagePort(_In_ HANDLE MessagePort) : m_messagePort(MessagePort), m_messageEvent(wil::EventOptions::None)
24 {
25 //
26 // N.B. The class takes ownership of the handle.
27 //
28 }
29
30 LxssMessagePort::LxssMessagePort(_In_ LxssMessagePort&& Source) :
31 m_messagePort(std::move(Source.m_messagePort)),
32 m_messageEvent(std::move(Source.m_messageEvent)),
33 m_serverPort(std::move(Source.m_serverPort))
34 {
35 }
36
37 LxssMessagePort::LxssMessagePort(_In_ std::unique_ptr<LxssMessagePort>&& SourcePointer) :
38 LxssMessagePort(std::move(*SourcePointer.get()))
39 {
40 }
41
42 std::shared_ptr<LxssPort> LxssMessagePort::CreateSessionLeader(_In_ HANDLE ClientProcess)
43 {
44 THROW_HR_IF(E_UNEXPECTED, (!m_serverPort));
45
46 const LXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_DATA Data{HandleToUlong(ClientProcess)};
47
48 const LXBUS_IPC_CONSOLE_ID MarshalId = this->MarshalConsole(&Data);
49 auto ReleaseConsole = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { this->ReleaseConsole(MarshalId); });
50
51 LX_INIT_CREATE_SESSION Message{{LxInitMessageCreateSession, sizeof(Message)}, MarshalId};
52
53 Send(&Message, sizeof(Message));
54 auto LocalMessagePort = m_serverPort->WaitForConnection(c_defaultMessageTimeout);
55 ReleaseConsole.release();
56 return LocalMessagePort;
57 }
58
59 wil::unique_handle LxssMessagePort::CreateUnnamedServer(_Out_ PLXBUS_SERVER_ID ServerId) const
60 {
61 LXBUS_IPC_MESSAGE_CREATE_UNNAMED_SERVER_PARAMETERS Parameters;
62 THROW_IF_NTSTATUS_FAILED(LxBusClientCreateUnnamedServer(m_messagePort.get(), &Parameters));
63
64 *ServerId = Parameters.Output.ServerId;
65 return wil::unique_handle(ULongToHandle(Parameters.Output.ServerPort));
66 }
67
68 void LxssMessagePort::DisconnectConsole(_In_ HANDLE ClientProcess)
69 {
70 LXBUS_IPC_MESSAGE_DISCONNECT_CONSOLE_PARAMETERS Parameters;
71 NTSTATUS Status;
72 Parameters.Input.ConsoleData.ClientProcess = HandleToUlong(ClientProcess);
73 Status = LxBusClientDisconnectConsole(m_messagePort.get(), &Parameters);
74
75 // Console disconnect is expected to fail in two cases:
76 // 1. The instance has been torn down: STATUS_NOT_FOUND
77 // 2. The tty device that had the console reference has already been
78 // closed: STATUS_NO_SUCH_DEVICE
79 if ((Status != STATUS_NOT_FOUND) && (Status != STATUS_NO_SUCH_DEVICE))
80 {
81 THROW_IF_NTSTATUS_FAILED(Status);
82 }
83 }
84
85 wil::cs_leave_scope_exit LxssMessagePort::Lock()
86 {
87 return m_lock.lock();
88 }
89
90 LXBUS_IPC_CONSOLE_ID
91 LxssMessagePort::MarshalConsole(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_DATA ConsoleData) const
92 {
93 LXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_PARAMETERS Parameters;
94 Parameters.Input.ConsoleData = *ConsoleData;
95 THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalConsole(m_messagePort.get(), &Parameters));
96
97 return Parameters.Output.ConsoleId;
98 }
99
100 LXBUS_IPC_FORK_TOKEN_ID
101 LxssMessagePort::MarshalForkToken(_In_ HANDLE TokenHandle) const
102 {
103 LXBUS_IPC_MESSAGE_MARSHAL_FORK_TOKEN_PARAMETERS Parameters;
104 Parameters.Input.TokenHandle = HandleToULong(TokenHandle);
105 THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalForkToken(m_messagePort.get(), &Parameters));
106
107 return Parameters.Output.ForkTokenId;
108 }
109
110 LXBUS_IPC_HANDLE_ID
111 LxssMessagePort::MarshalHandle(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData) const
112 {
113 LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_PARAMETERS Parameters;
114 Parameters.Input.HandleData = *HandleData;
115 THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalHandle(m_messagePort.get(), &Parameters));
116
117 return Parameters.Output.HandleId;
118 }
119
120 LXBUS_IPC_PROCESS_ID
121 LxssMessagePort::MarshalProcess(_In_ HANDLE ProcessHandle, _In_ bool TerminateOnClose) const
122 {
123 LXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS Parameters;
124 Parameters.Input.Process = HandleToULong(ProcessHandle);
125 if (TerminateOnClose)
126 {
127 Parameters.Input.Flags = LXBUS_IPC_MARSHAL_PROCESS_FLAG_TERMINATE_ON_CLOSE;
128 }
129
130 THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalProcess(m_messagePort.get(), &Parameters));
131
132 return Parameters.Output.ProcessId;
133 }
134
135 void LxssMessagePort::Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE, _In_ DWORD Timeout)
136 {
137 IO_STATUS_BLOCK IoStatus;
138 ULONG SizeReceived;
139 const NTSTATUS Status =
140 LxBusClientReceiveMessageAsync(m_messagePort.get(), Buffer, Length, &SizeReceived, &IoStatus, m_messageEvent.get());
141 THROW_IF_NTSTATUS_FAILED(Status);
142
143 if (Status == STATUS_PENDING)
144 {
145 WaitForMessage(&IoStatus, Timeout);
146 }
147 else
148 {
149 WI_ASSERT(Status == STATUS_SUCCESS);
150 }
151
152 THROW_IF_NTSTATUS_FAILED(IoStatus.Status);
153 THROW_HR_IF(E_UNEXPECTED, ((NT_SUCCESS(IoStatus.Status)) && (Length != static_cast<ULONG>(IoStatus.Information))));
154
155 return;
156 }
157
158 std::vector<gsl::byte> LxssMessagePort::Receive(DWORD Timeout)
159 {
160 IO_STATUS_BLOCK IoStatus;
161 std::vector<gsl::byte> Message;
162 ULONG SizeReceived;
163 NTSTATUS Status;
164 Message.resize(LAUNCH_PROCESS_DEFAULT_BUFFER_SIZE);
165 for (;;)
166 {
167 Status = LxBusClientReceiveMessageAsync(
168 m_messagePort.get(), Message.data(), static_cast<ULONG>(Message.size()), &SizeReceived, &IoStatus, m_messageEvent.get());
169
170 if (Status == STATUS_PENDING)
171 {
172 WaitForMessage(&IoStatus, Timeout);
173 Status = IoStatus.Status;
174 SizeReceived = static_cast<ULONG>(IoStatus.Information);
175 }
176
177 //
178 // Grow the buffer if it was not large enough.
179 //
180 // N.B. When a provided buffer is too small, LxBus will write the
181 // required size of the buffer as a SIZE_T into the beginning of
182 // the buffer.
183 //
184
185 if (Status == STATUS_BUFFER_TOO_SMALL)
186 {
187 Message.resize(*((PSIZE_T)Message.data()));
188 }
189 else
190 {
191 break;
192 }
193 }
194
195 THROW_IF_NTSTATUS_FAILED(Status);
196
197 //
198 // Resize the buffer to be the size of the received message.
199 //
200
201 Message.resize(SizeReceived);
202 return Message;
203 }
204
205 void LxssMessagePort::ReleaseConsole(_In_ LXBUS_IPC_CONSOLE_ID ConsoleId) const
206 {
207 LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;
208 Parameters.Input.Id.Console = ConsoleId;
209 Parameters.Input.Type = LxBusIpcReleaseTypeConsole;
210 THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseConsole(m_messagePort.get(), &Parameters));
211 }
212
213 void LxssMessagePort::ReleaseForkToken(_In_ LXBUS_IPC_FORK_TOKEN_ID ForkTokenId) const
214 {
215 LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;
216 Parameters.Input.Id.Token = ForkTokenId;
217 Parameters.Input.Type = LxBusIpcReleaseTypeForkToken;
218 THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseHandle(m_messagePort.get(), &Parameters));
219 }
220
221 void LxssMessagePort::ReleaseHandle(_In_ LXBUS_IPC_HANDLE_ID HandleId) const
222 {
223 LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;
224 Parameters.Input.Id.Handle = HandleId;
225 Parameters.Input.Type = LxBusIpcReleaseTypeHandle;
226 THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseHandle(m_messagePort.get(), &Parameters));
227 }
228
229 void LxssMessagePort::Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length)
230 {
231 IO_STATUS_BLOCK IoStatus;
232 const NTSTATUS Status = LxBusClientSendMessageAsync(m_messagePort.get(), Buffer, Length, &IoStatus, m_messageEvent.get());
233 THROW_IF_NTSTATUS_FAILED(Status);
234
235 if (Status == STATUS_PENDING)
236 {
237 WaitForMessage(&IoStatus);
238 }
239 else
240 {
241 WI_ASSERT(Status == STATUS_SUCCESS);
242 }
243
244 THROW_IF_NTSTATUS_FAILED(IoStatus.Status);
245
246 WI_ASSERT((Status != STATUS_SUCCESS) || (Length == IoStatus.Information));
247 }
248
249 void LxssMessagePort::SetServerPort(_In_ const std::shared_ptr<LxssServerPort>& ServerPort)
250 {
251 m_serverPort = ServerPort;
252 }
253
254 wil::unique_handle LxssMessagePort::UnmarshalProcess(_In_ LXBUS_IPC_PROCESS_ID ProcessId) const
255 {
256 LXBUS_IPC_MESSAGE_UNMARSHAL_PROCESS_PARAMETERS Parameters;
257 Parameters.Input.ProcessId = ProcessId;
258 THROW_IF_NTSTATUS_FAILED(LxBusClientUnmarshalProcess(m_messagePort.get(), &Parameters));
259
260 wil::unique_handle ProcessHandle(ULongToHandle(Parameters.Output.ProcessHandle));
261 return ProcessHandle;
262 }
263
264 wil::unique_handle LxssMessagePort::UnmarshalVfsFile(_In_ LXBUS_IPC_HANDLE_ID VfsFileId) const
265 {
266 LXBUS_IPC_MESSAGE_UNMARSHAL_VFS_FILE_PARAMETERS Parameters;
267 Parameters.Input.VfsFileId = VfsFileId;
268 THROW_IF_NTSTATUS_FAILED(LxBusClientUnmarshalVfsFile(m_messagePort.get(), &Parameters));
269
270 wil::unique_handle ProcessHandle(ULongToHandle(Parameters.Output.Handle));
271 return ProcessHandle;
272 }
273
274 void LxssMessagePort::WaitForMessage(_In_ PIO_STATUS_BLOCK IoStatus, _In_ DWORD Timeout) const
275 {
276 const DWORD WaitStatus = WaitForSingleObject(m_messageEvent.get(), Timeout);
277 if (WaitStatus == WAIT_TIMEOUT)
278 {
279 IO_STATUS_BLOCK IoStatusCancel;
280 const NTSTATUS Status = NtCancelIoFileEx(m_messagePort.get(), IoStatus, &IoStatusCancel);
281
282 WI_ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_NOT_FOUND));
283
284 WI_VERIFY(WaitForSingleObject(m_messageEvent.get(), Timeout) == WAIT_OBJECT_0);
285 }
286 else
287 {
288 WI_ASSERT(WaitStatus == WAIT_OBJECT_0);
289 }
290 }