Fix file permissions: set executable bit
[nativeclient.git] / intermodule_comm / win / nacl_imc.cc
blobacf2684832801c40cd3287c1fee757dea8ab0e14
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // NaCl inter-module communication primitives.
35 #define NOMINMAX // Disables the generation of the min and max macro in
36 // <windows.h>.
38 #include "native_client/intermodule_comm/nacl_imc.h"
39 #include <ctype.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <windows.h>
43 #include <sys/types.h>
44 #include <algorithm>
45 #include "native_client/include/atomic_ops.h"
47 namespace nacl {
49 namespace {
51 const char kPipePrefix[] = "\\\\.\\pipe\\google-nacl-";
52 const size_t kPipePrefixSize = sizeof kPipePrefix / sizeof kPipePrefix[0];
54 const int kPipePathMax = kPipePrefixSize + kPathMax + 1;
55 const int kOutBufferSize = 4096; // TBD
56 const int kInBufferSize = 4096; // TBD
57 const int kDefaultTimeoutMilliSeconds = 1000;
59 // ControlHeader::command
60 const int kEchoRequest = 0;
61 const int kEchoResponse = 1;
62 const int kMessage = 2;
63 const int kCancel = 3; // Cancels Handle transfer operations
65 struct ControlHeader {
66 int command;
67 DWORD pid;
68 size_t message_length;
69 size_t handle_count;
72 bool GetSocketName(const SocketAddress* address, char* name) {
73 if (address == NULL || !isalpha(address->path[0])) {
74 SetLastError(ERROR_INVALID_PARAMETER);
75 return false;
77 sprintf_s(name, kPipePathMax, "%s%.*s",
78 kPipePrefix, kPathMax, address->path);
79 return true;
82 int ReadAll(HANDLE handle, void* buffer, size_t length) {
83 size_t count = 0;
84 while (count < length) {
85 DWORD len;
86 DWORD chunk = static_cast<DWORD>(
87 ((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
88 if (ReadFile(handle, static_cast<char*>(buffer) + count,
89 chunk, &len, NULL) == FALSE) {
90 return static_cast<int>((0 < count) ? count : -1);
92 count += len;
94 return static_cast<int>(count);
97 int WriteAll(HANDLE handle, const void* buffer, size_t length) {
98 size_t count = 0;
99 while (count < length) {
100 DWORD len;
101 // The following statement is for the 64 bit portability.
102 DWORD chunk = static_cast<DWORD>(
103 ((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
104 if (WriteFile(handle, static_cast<const char*>(buffer) + count,
105 chunk, &len, NULL) == FALSE) {
106 return static_cast<int>((0 < count) ? count : -1);
108 count += len;
110 return static_cast<int>(count);
113 BOOL SkipFile(HANDLE handle, size_t length) {
114 while (0 < length) {
115 char scratch[1024];
116 size_t count = std::min(sizeof scratch, length);
117 if (ReadAll(handle, scratch, count) != count) {
118 return FALSE;
120 length -= count;
122 return TRUE;
125 BOOL SkipHandles(HANDLE handle, size_t count) {
126 while (0 < count) {
127 Handle discard;
128 if (ReadAll(handle, &discard, sizeof discard) != sizeof discard) {
129 return FALSE;
131 CloseHandle(discard);
132 --count;
134 return TRUE;
137 } // namespace
139 bool WouldBlock() {
140 return (GetLastError() == ERROR_PIPE_LISTENING) ? true : false;
143 int GetLastErrorString(char* buffer, size_t length) {
144 DWORD error = GetLastError();
145 return FormatMessageA(
146 FORMAT_MESSAGE_FROM_SYSTEM |
147 FORMAT_MESSAGE_IGNORE_INSERTS,
148 NULL,
149 error,
150 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
151 buffer,
152 static_cast<DWORD>((64 * 1024 < length) ? 64 * 1024 : length),
153 NULL) ? 0 : -1;
156 Handle BoundSocket(const SocketAddress* address) {
157 char name[kPipePathMax];
158 if (!GetSocketName(address, name)) {
159 return kInvalidHandle;
161 // Create a named pipe in nonblocking mode.
162 return CreateNamedPipeA(
163 name,
164 PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
165 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
166 PIPE_UNLIMITED_INSTANCES,
167 kOutBufferSize,
168 kInBufferSize,
169 kDefaultTimeoutMilliSeconds,
170 NULL);
173 int SocketPair(Handle pair[2]) {
174 static AtomicWord socket_pair_count;
176 char name[kPipePathMax];
177 do {
178 sprintf_s(name, kPipePathMax, "%s%u.%lu",
179 kPipePrefix, GetCurrentProcessId(),
180 AtomicIncrement(&socket_pair_count, 1));
181 pair[0] = CreateNamedPipeA(
182 name,
183 PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
184 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
186 kOutBufferSize,
187 kInBufferSize,
188 kDefaultTimeoutMilliSeconds,
189 NULL);
190 if (pair[0] == INVALID_HANDLE_VALUE &&
191 GetLastError() != ERROR_ACCESS_DENIED &&
192 GetLastError() != ERROR_PIPE_BUSY) {
193 return -1;
195 } while (pair[0] == INVALID_HANDLE_VALUE);
196 pair[1] = CreateFileA(name,
197 GENERIC_READ | GENERIC_WRITE,
198 0, // no sharing
199 NULL, // default security attributes
200 OPEN_EXISTING, // opens existing pipe
201 0, // default attributes
202 NULL); // no template file
203 if (pair[1] == INVALID_HANDLE_VALUE) {
204 CloseHandle(pair[0]);
205 return -1;
207 if (ConnectNamedPipe(pair[0], NULL) == FALSE) {
208 DWORD error = GetLastError();
209 if (error != ERROR_PIPE_CONNECTED) {
210 CloseHandle(pair[0]);
211 CloseHandle(pair[1]);
212 return -1;
215 return 0;
218 int Close(Handle handle) {
219 if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
220 return 0;
222 return CloseHandle(handle) ? 0 : -1;
225 int SendDatagram(Handle handle, const MessageHeader* message, int flags) {
226 ControlHeader header = { kEchoRequest, GetCurrentProcessId(), 0, 0 };
227 HANDLE remote_handles[kHandleCountMax];
229 if (kHandleCountMax < message->handle_count) {
230 SetLastError(ERROR_INVALID_PARAMETER);
231 return -1;
233 if (0 < message->handle_count && message->handles) {
234 // TODO: On Windows Vista, we can use GetNamedPipeClientProcessId()
235 // and GetNamedPipeServerProcessId() and probably we can remove
236 // kEchoRequest and kEchoResponse completely.
237 if (WriteAll(handle, &header, sizeof header) != sizeof header ||
238 ReadAll(handle, &header, sizeof header) != sizeof header ||
239 header.command != kEchoResponse) {
240 return -1;
242 HANDLE target = OpenProcess(PROCESS_DUP_HANDLE, FALSE, header.pid);
243 if (target == NULL) {
244 return -1;
246 for (size_t i = 0; i < message->handle_count; ++i) {
247 if (DuplicateHandle(GetCurrentProcess(), message->handles[i],
248 target, &remote_handles[i],
249 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) {
250 // Send the kCancel message to revoke the handles duplicated
251 // so far in the remote peer.
252 header.command = kCancel;
253 header.handle_count = i;
254 if (0 < i) {
255 WriteAll(handle, &header, sizeof header);
256 WriteAll(handle, remote_handles, sizeof(Handle) * i);
258 CloseHandle(target);
259 return -1;
262 CloseHandle(target);
264 header.command = kMessage;
265 header.handle_count = message->handle_count;
266 for (size_t i = 0; i < message->iov_length; ++i) {
267 header.message_length += message->iov[i].length;
269 if (WriteAll(handle, &header, sizeof header) != sizeof header) {
270 return -1;
272 for (size_t i = 0; i < message->iov_length; ++i) {
273 if (WriteAll(handle, message->iov[i].base, message->iov[i].length) !=
274 message->iov[i].length) {
275 return -1;
278 if (0 < message->handle_count && message->handles &&
279 WriteAll(handle,
280 remote_handles,
281 sizeof(Handle) * message->handle_count) !=
282 sizeof(Handle) * message->handle_count) {
283 return -1;
285 return static_cast<int>(header.message_length);
288 int SendDatagramTo(Handle handle, const MessageHeader* message, int flags,
289 const SocketAddress* name) {
290 if (kHandleCountMax < message->handle_count) {
291 SetLastError(ERROR_INVALID_PARAMETER);
292 return -1;
294 char pipe_name[kPipePathMax];
295 if (!GetSocketName(name, pipe_name)) {
296 return -1;
298 for (;;) {
299 handle = CreateFileA(pipe_name,
300 GENERIC_READ | GENERIC_WRITE,
301 0, // no sharing
302 NULL, // default security attributes
303 OPEN_EXISTING, // opens existing pipe
304 0, // default attributes
305 NULL); // no template file
306 if (handle != INVALID_HANDLE_VALUE) {
307 break;
309 if (GetLastError() != ERROR_PIPE_BUSY) {
310 return -1;
312 if (flags & kDontWait) {
313 SetLastError(ERROR_PIPE_LISTENING);
314 return -1;
316 if (!WaitNamedPipeA(pipe_name, NMPWAIT_WAIT_FOREVER)) {
317 return -1;
320 int result = SendDatagram(handle, message, flags);
321 CloseHandle(handle);
322 return result;
325 namespace {
327 int ReceiveDatagram(Handle handle, MessageHeader* message, int flags,
328 bool bound_socket) {
329 ControlHeader header;
330 int result = -1;
331 bool dontPeek = false;
332 Repeat:
333 if ((flags & kDontWait) && !dontPeek) {
334 DWORD len;
335 DWORD total;
336 if (PeekNamedPipe(handle, &header, sizeof header, &len, &total, NULL)) {
337 if (len < sizeof header) {
338 SetLastError(ERROR_PIPE_LISTENING);
339 } else {
340 switch (header.command) {
341 case kEchoRequest:
342 // Send back the process id to the remote peer to duplicate handles.
343 // TODO : It might be better to keep remote pid by the initial
344 // handshake rather than send kEchoRequest each time
345 // before duplicating handles.
346 if (ReadAll(handle, &header, sizeof header) == sizeof header) {
347 header.command = kEchoResponse;
348 header.pid = GetCurrentProcessId();
349 WriteAll(handle, &header, sizeof header);
350 if (bound_socket) {
351 // We must not close this connection.
352 dontPeek = true;
354 goto Repeat;
356 break;
357 case kEchoResponse:
358 SkipFile(handle, sizeof header);
359 goto Repeat;
360 break;
361 case kMessage:
362 if (header.message_length + sizeof header <= total) {
363 if (flags & kDontWait) {
364 flags &= ~kDontWait;
365 goto Repeat;
367 result = static_cast<int>(header.message_length);
368 } else {
369 SetLastError(ERROR_PIPE_LISTENING);
371 break;
372 case kCancel:
373 if (sizeof header + sizeof(Handle) * header.handle_count <= len &&
374 ReadAll(handle, &header, sizeof header) == sizeof header) {
375 SkipHandles(handle, header.handle_count);
376 goto Repeat;
378 break;
379 default:
380 break;
384 } else if (ReadAll(handle, &header, sizeof header) == sizeof header) {
385 dontPeek = false;
386 switch (header.command) {
387 case kEchoRequest:
388 header.command = kEchoResponse;
389 header.pid = GetCurrentProcessId();
390 WriteAll(handle, &header, sizeof header);
391 goto Repeat;
392 break;
393 case kEchoResponse:
394 goto Repeat;
395 break;
396 case kMessage: {
397 size_t total_message_bytes = header.message_length;
398 size_t count = 0;
399 message->flags = 0;
400 for (size_t i = 0;
401 i < message->iov_length && count < header.message_length;
402 ++i) {
403 IOVec* iov = &message->iov[i];
404 size_t len = std::min(iov->length, total_message_bytes);
405 if (ReadAll(handle, iov->base, len) != len) {
406 break;
408 total_message_bytes -= len;
409 count += len;
411 if (count < header.message_length) {
412 if (SkipFile(handle, header.message_length - count) == FALSE) {
413 break;
415 message->flags |= kMessageTruncated;
417 if (0 < message->handle_count && message->handles) {
418 message->handle_count = std::min(message->handle_count,
419 header.handle_count);
420 if (ReadAll(handle, message->handles,
421 message->handle_count * sizeof(Handle)) !=
422 message->handle_count * sizeof(Handle)) {
423 break;
425 } else {
426 message->handle_count = 0;
428 if (message->handle_count < header.handle_count) {
429 if (SkipHandles(handle, header.handle_count - message->handle_count) ==
430 FALSE) {
431 break;
433 message->flags |= kHandlesTruncated;
435 result = static_cast<int>(count);
436 break;
438 case kCancel:
439 SkipHandles(handle, header.handle_count);
440 goto Repeat;
441 break;
442 default:
443 break;
446 return result;
449 } // namespace
451 int ReceiveDatagram(Handle handle, MessageHeader* message, int flags) {
452 // If handle is a bound socket, it is a named pipe in non-blocking mode.
453 // Set is_bound_socket to true if handle has been created by BoundSocket().
454 DWORD state;
455 if (!GetNamedPipeHandleState(handle, &state, NULL, NULL, NULL, NULL, NULL)) {
456 return -1;
459 if (!(state & PIPE_NOWAIT)) {
460 // handle is a connected socket.
461 return ReceiveDatagram(handle, message, flags, false);
464 // handle is a bound socket.
465 for (;;) {
466 if (ConnectNamedPipe(handle, NULL)) {
467 // Note ConnectNamedPipe() for a handle in non-blocking mode returns a
468 // nonzero value just to indicate that the pipe is now available to be
469 // connected.
470 continue;
472 switch (GetLastError()) {
473 case ERROR_PIPE_LISTENING: {
474 if (flags & kDontWait) {
475 return -1;
477 // Set handle to blocking mode
478 DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT;
479 SetNamedPipeHandleState(handle, &mode, NULL, NULL);
480 break;
482 case ERROR_PIPE_CONNECTED: {
483 // Set handle to blocking mode
484 DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT;
485 SetNamedPipeHandleState(handle, &mode, NULL, NULL);
486 int result = ReceiveDatagram(handle, message, flags, true);
487 FlushFileBuffers(handle);
488 // Set handle back to non-blocking mode
489 mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
490 SetNamedPipeHandleState(handle, &mode, NULL, NULL);
491 DisconnectNamedPipe(handle);
492 if (result == -1 && GetLastError() == ERROR_BROKEN_PIPE) {
493 if (flags & kDontWait) {
494 SetLastError(ERROR_PIPE_LISTENING);
495 return result;
497 } else {
498 return result;
500 break;
502 default:
503 return -1;
504 break;
509 } // namespace nacl