5b25478f0dbce4dd8253ca8801a9312eb172ff1f
[platform/framework/web/crosswalk.git] / src / native_client / src / shared / platform / win / nacl_host_desc.c
1 /*
2  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 /*
8  * NaCl Service Runtime.  I/O Descriptor / Handle abstraction.  Memory
9  * mapping using descriptors.
10  */
11 #include "native_client/src/include/portability.h"
12 #include "native_client/src/include/portability_io.h"
13
14 #include <windows.h>
15 #include <direct.h>
16 #include <io.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <share.h>
20
21 #include "native_client/src/include/nacl_macros.h"
22 #include "native_client/src/include/nacl_platform.h"
23 #include "native_client/src/shared/platform/nacl_check.h"
24 #include "native_client/src/shared/platform/nacl_find_addrsp.h"
25 #include "native_client/src/shared/platform/nacl_host_desc.h"
26 #include "native_client/src/shared/platform/nacl_log.h"
27 #include "native_client/src/shared/platform/nacl_sync.h"
28 #include "native_client/src/shared/platform/nacl_sync_checked.h"
29 #include "native_client/src/shared/platform/win/xlate_system_error.h"
30 #include "native_client/src/trusted/desc/nacl_desc_effector.h"
31
32 #include "native_client/src/trusted/service_runtime/nacl_config.h"
33 #include "native_client/src/trusted/service_runtime/internal_errno.h"
34 #include "native_client/src/trusted/service_runtime/sel_util-inl.h"
35
36 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
37 #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
38 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
39 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
40 #include "native_client/src/trusted/service_runtime/include/sys/unistd.h"
41
42 #define OFFSET_FOR_FILEPOS_LOCK (GG_LONGLONG(0x7000000000000000))
43
44 /*
45  * By convention, we use locking the byte at OFFSET_FOR_FILEPOS_LOCK
46  * as locking for the implicit file position associated with a file
47  * handle.  According to MSDN, LockFileEx of a byte range that does
48  * not (yet) exist in a file is not an error, which makes sense in
49  * that one might want to have exclusive access to a file region that
50  * is beyond the end of the file before populating it.  We assume that
51  * OFFSET_FOR_FILEPOS_LOCK is large enough that no real file will
52  * actually be that big (even if sparse) and cause problems.
53  *
54  * One drawback of this is that two independent file handles on the
55  * same file will share the same lock.  If this leads to actual
56  * contention issues, we can use the following randomized approach,
57  * ASSUMING that each file handle / posix-level host descriptor is
58  * introduced to NaCl at most once (e.g., no dup'ing and invoking
59  * NaClHostDescPosixTake multiple times): we pick a random offset from
60  * OFFSET_FOR_FILEPOS_LOCK, and make sure we transfer that with the
61  * file handle in the nrd_xfer protocol.  This way, we use a range of
62  * byte offsets for locking files and avoid false contention.  We
63  * would be subject to the birthday paradox, of course, so if we
64  * picked a 16-bit random offset to use, then if a file is opened ~256
65  * times we would start seeing performance issues caused by
66  * contention, which is probably acceptable; a 32-bit nonce would be
67  * plenty.
68  *
69  * On Windows, fcntl is not available.  A very similar function to
70  * lockf, _locking, exists in the Windows CRT.  It does not permit
71  * specification of the start of a region, only size (just like lockf)
72  * -- implicitly from the current position -- which is less than
73  * useful for our purposes.
74  */
75 static void NaClTakeFilePosLock(HANDLE hFile) {
76   OVERLAPPED overlap;
77   DWORD err;
78
79   memset(&overlap, 0, sizeof overlap);
80   overlap.Offset = (DWORD) OFFSET_FOR_FILEPOS_LOCK;
81   overlap.OffsetHigh = (DWORD) (OFFSET_FOR_FILEPOS_LOCK >> 32);
82   /*
83    * LockFileEx should never fail -- untrusted code cannot cause hFile
84    * to become invalid, since all NaClHostDesc objects are wrapped in
85    * NaClDesc objects and all uses of NaClDesc objects take a
86    * reference before use, so a threading race that closes a
87    * descriptor at the untrusted code level will only dereference the
88    * NaClDesc (and make it unavailable to the untrusted code), but the
89    * object will not be destroyed until after the NaClDesc-level
90    * operation (which in turn invokes the NaClHostDesc level
91    * operation) completes.  Only after the operation completes will
92    * the reference to the NaClDesc be drop by the syscall handler.
93    */
94   if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK,
95                   /* dwReserved= */ 0,
96                   /* nNumberOfBytesToLockLow= */ 1,
97                   /* nNumberOfBytesToLockHigh= */ 0,
98                   &overlap)) {
99     err = GetLastError();
100     NaClLog(LOG_FATAL, "NaClTakeFilePosLock: LockFileEx failed, error %u\n",
101             err);
102   }
103 }
104
105 /*
106  * Map our ABI to the host OS's ABI.
107  * Note: there is no X bit equivalent on windows so NACL_ABI_S_IXUSR
108  * is ignored.
109  */
110 static INLINE mode_t NaClMapMode(nacl_abi_mode_t abi_mode) {
111   mode_t m = 0;
112   if (0 != (abi_mode & NACL_ABI_S_IRUSR))
113     m |= _S_IREAD;
114   if (0 != (abi_mode & NACL_ABI_S_IWUSR))
115     m |= _S_IWRITE;
116   return m;
117 }
118
119 /* Windows doesn't define R_OK or W_OK macros but expects these constants */
120 #define WIN_F_OK 0
121 #define WIN_R_OK 4
122 #define WIN_W_OK 2
123
124 /*
125  * Map our ABI to the host OS's ABI.
126  * There is no X_OK (0x1) on win32 so we ignore
127  * NACL_ABI_X_OK and report everything that exists
128  * as being executable.
129  */
130 static INLINE int NaClMapAccessMode(int nacl_mode) {
131   int mode = 0;
132   if (nacl_mode == NACL_ABI_F_OK) {
133     mode = WIN_F_OK;
134   } else {
135     if (nacl_mode & NACL_ABI_R_OK)
136       mode |= WIN_R_OK;
137     if (nacl_mode & NACL_ABI_W_OK)
138       mode |= WIN_W_OK;
139   }
140   return mode;
141 }
142
143 static void NaClDropFilePosLock(HANDLE hFile) {
144   OVERLAPPED overlap;
145   DWORD err;
146
147   memset(&overlap, 0, sizeof overlap);
148   overlap.Offset = (DWORD) OFFSET_FOR_FILEPOS_LOCK;
149   overlap.OffsetHigh = (DWORD) (OFFSET_FOR_FILEPOS_LOCK >> 32);
150   if (!UnlockFileEx(hFile,
151                     /* dwReserved= */ 0,
152                     /* nNumberOfBytesToLockLow= */ 1,
153                     /* nNumberOfBytesToLockHigh= */ 0,
154                     &overlap)) {
155     err = GetLastError();
156     NaClLog(LOG_FATAL, "NaClDropFilePosLock: UnlockFileEx failed, error %u\n",
157             err);
158   }
159 }
160
161 static nacl_off64_t NaClLockAndGetCurrentFilePos(HANDLE hFile) {
162   LARGE_INTEGER to_move;
163   LARGE_INTEGER cur_pos;
164   DWORD err;
165
166   NaClTakeFilePosLock(hFile);
167   to_move.QuadPart = 0;
168   if (!SetFilePointerEx(hFile, to_move, &cur_pos, FILE_CURRENT)) {
169     err = GetLastError();
170     NaClLog(LOG_FATAL,
171             "NaClLockAndGetCurrentFilePos: SetFilePointerEx failed, error %u\n",
172             err);
173   }
174   return cur_pos.QuadPart;
175 }
176
177 static void NaClSetCurrentFilePosAndUnlock(HANDLE hFile,
178                                            nacl_off64_t pos) {
179   LARGE_INTEGER to_move;
180   DWORD err;
181
182   to_move.QuadPart = pos;
183   if (!SetFilePointerEx(hFile, to_move, (LARGE_INTEGER *) NULL, FILE_BEGIN)) {
184     err = GetLastError();
185     NaClLog(LOG_FATAL,
186             "NaClSetCurrentFilePosAndUnlock: SetFilePointerEx failed:"
187             " error %d\n",
188             err);
189   }
190   NaClDropFilePosLock(hFile);
191 }
192
193 /*
194  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
195  *
196  * The implementation of the host descriptor abstractions will
197  * probably change.  In particularly, blocking I/O calls must be
198  * interruptible in order to implement the address-space move
199  * mechanism for mmap error recovery, and the it seems that the only
200  * way that this would be feasible is to do the following: instead of
201  * using the POSIX abstraction layer, do the I/O using Windows file
202  * handles opened for asynchronous operations.  When a potentially
203  * blocking system call (e.g., read or write) is performed, use
204  * overlapped I/O via ReadFile/WriteFile to initiate the I/O operation
205  * in a non-blocking manner, and use a separate event object, so that
206  * the thread can, after initiating the I/O, perform
207  * WaitForMultipleObjects on both I/O completion (event in the
208  * OVERLAPPED structure) and on mmap-generated interrupts.  The event
209  * can be signalled via SetEvent by any other thread that wish to
210  * perform a safe mmap operation.
211  *
212  * When the safe mmap is to occur, all other application threads are
213  * stopped (beware, of course, of the race condition where two threads
214  * try to do mmap), and the remaining running thread performs
215  * VirtualFree and MapViewOfFileEx.  If a thread (from some injected
216  * DLL) puts some memory in the hole created by VirtualFree before the
217  * MapViewOfFileEx runs, then we have to move the entire address space
218  * to avoid allowing the untrusted NaCl app from touching this
219  * innocent thread's memory.
220  *
221  * What this implies is that a mechanism must exist in order for the
222  * mmapping thread to stop all other application threads, and this is
223  * why the blocking syscalls must be interruptible.  When interrupted,
224  * the thread that initiated the I/O must perform CancelIo and check,
225  * via GetOverlappedResult, to see how much have completed, etc, then
226  * put itself into a restartable state -- we might simply return EINTR
227  * if no work has been dnoe and require libc to restart the syscall in
228  * the SysV style, though it should be possible to just restart the
229  * syscall in the BSD style -- and to signal the mmapping thread that
230  * it is ready.
231  *
232  * Alternatively, these interrupted operations can return a private
233  * version of EAGAIN, so that the code calling the host descriptor
234  * (syscall handler) can quiesce the thread and restart the I/O
235  * operation once the address space move is complete.
236  *
237  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
238  */
239
240 /*
241  * TODO(bsy, gregoryd): check that _get_errno is indeed a thread-safe way
242  * to get the error from the last 'syscall' into the posix layer.
243  */
244 int GetErrno(void) {
245   int thread_errno;
246
247   (void) _get_errno(&thread_errno);
248   return NaClXlateErrno(thread_errno);
249 }
250
251 static INLINE size_t size_min(size_t a, size_t b) {
252   return (a < b) ? a : b;
253 }
254
255 /*
256  * The mapping and unmapping code work in 64K chunks rather than a
257  * single large allocation because all of our uses will use 64K
258  * chunks.  Higher level code keeps track of whether memory came from
259  * VirtualAlloc or NaClHostDescMap, and will call the appropriate
260  * deallocation functions.
261  *
262  * NB: if prot is NACL_ABI_PROT_NONE, then the memory should be
263  * deallocated via VirtualFree as if it came from paging file rather
264  * than via a file mapping object representing the paging file (and
265  * thus UnmapViewOfFile).
266  */
267
268 /*
269  * out_flProtect == 0 means error, and the error string can be used
270  * for a logging message (except for the cases that the caller should
271  * be checking for).
272  *
273  * in parameters are all NACL_ABI_ values or bool (0/1).
274  *
275  * accmode may be NACL_ABI_O_RDONLY or NACL_ABI_O_RDWR, but not
276  * NACL_ABI_O_WRONLY (see below).
277  *
278  * Caller should check for:
279  *
280  * - PROT_NONE -> special case handling,
281  * - NACL_ABI_O_APPEND and PROT_WRITE -> EACCES,
282  * - accmode is O_WRONLY -> EACCES,
283  *
284  * The interpretation here is that PROT_EXEC or PROT_WRITE implies
285  * asking for PROT_READ, since most hardware behaves this way.  So if
286  * the descriptor is O_WRONLY, we generally refuse.
287  *
288  * The file mapping object created by CreateFileMapping's flProtect
289  * argument specifies the MAXIMUM protection, and MapViewOfFileEx will
290  * request a lesser level of access.  We should always
291  * CreateFileMapping with a high level of access so that
292  * VirtualProtect (used by mprotect) can be used to turn on write when
293  * the initial mmap had read-only mappings.
294  *
295  * BUG(phosek): Due to Windows XP limitation, in particular the missing
296  * PAGE_EXECUTE_WRITECOPY protection support for file mapping objects,
297  * we cannot mmap r/o file as private, read/write and later make it
298  * executable or vice versa mmap r/o file as private, read/execute and
299  * later make it writable. This is a platform difference, but since
300  * untrusted code is not allowed to mmap files as write/execute, this
301  * difference is invisible to application developers and will therefore
302  * likely remain unresolved as the solution would likely be very
303  * expensive. Furthemore, after dropping the support for Windows XP, this
304  * difference can be easily resolved by updating the flag mapping.
305  */
306 void NaClflProtectAndDesiredAccessMap(int prot,
307                                       int is_private,
308                                       int accmode,
309                                       DWORD *out_flMappingProtect,
310                                       DWORD *out_flProtect,
311                                       DWORD *out_dwDesiredAccess,
312                                       char const **out_msg) {
313 #define M(mp,p,da,err) { mp, p, da, err, #mp, #p, #da }
314   static struct {
315     DWORD flMappingProtect;
316     DWORD flProtect;
317     DWORD dwDesiredAccess;
318     char const *err;
319     char const *flMappingProtect_str;
320     char const *flProtect_str;
321     char const *dwDesiredAccess_str;
322   } table[] = {
323     /* RDONLY */
324     /* shared */
325     /* PROT_NONE */
326     M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
327     /* PROT_READ */
328     M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
329     /* PROT_WRITE */
330     M(0, 0, 0, "file open for read only; no shared write allowed"),
331     /* PROT_READ | PROT_WRITE */
332     M(0, 0, 0, "file open for read only; no shared read/write allowed"),
333     /* PROT_EXEC */
334     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE,
335       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
336     /* PROT_READ | PROT_EXEC */
337     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READ,
338       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
339     /* PROT_WRITE | PROT_EXEC */
340     M(0, 0, 0, "file open for read only; no shared write/exec allowed"),
341     /* PROT_READ | PROT_WRITE | PROT_EXEC */
342     M(0, 0, 0, "file open for read only; no shared read/write/exec allowed"),
343
344     /* is_private */
345     /* PROT_NONE */
346     M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
347     /* PROT_READ */
348     M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
349     /* PROT_WRITE */
350     M(PAGE_EXECUTE_READ, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
351     /* PROT_READ | PROT_WRITE */
352     M(PAGE_EXECUTE_READ, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
353
354     /*
355      * NB: PAGE_EXECUTE_WRITECOPY is not supported on Server 2003 or
356      * XP, which means that the mmap will fail.  In this case we fallback
357      * to PAGE_EXECUTE_READ.
358      *
359      * Even with PAGE_EXECUTE_WRITECOPY, the PROT_WRITE | PROT_EXEC
360      * case where we are asking for FILE_MAP_COPY | FILE_MAP_EXECUTE
361      * seems to always fail, with GetLastError() yielding 87
362      * (ERROR_INVALID_PARAMETER).  This may be due to antivirus.
363      */
364
365     /* PROT_EXEC */
366     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE,
367       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
368     /* PROT_READ | PROT_EXEC */
369     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READ,
370       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
371     /* PROT_WRITE | PROT_EXEC */
372     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY,
373       FILE_MAP_COPY | FILE_MAP_EXECUTE, NULL),
374     /* PROT_READ | PROT_WRITE | PROT_EXEC */
375     M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY,
376       FILE_MAP_COPY | FILE_MAP_EXECUTE, NULL),
377
378     /* RDWR */
379     /* shared */
380     /* PROT_NONE */
381     M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
382     /* PROT_READ */
383     M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
384     /* PROT_WRITE */
385     M(PAGE_EXECUTE_READWRITE, PAGE_READWRITE, FILE_MAP_WRITE, NULL),
386     /* PROT_READ | PROT_WRITE */
387     M(PAGE_EXECUTE_READWRITE, PAGE_READWRITE, FILE_MAP_WRITE, NULL),
388
389     /* PROT_EXEC */
390     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE,
391       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
392     /* PROT_READ | PROT_EXEC */
393     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READ,
394       FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
395     /* PROT_WRITE | PROT_EXEC */
396     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE,
397       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
398     /* PROT_READ | PROT_WRITE | PROT_EXEC */
399     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE,
400       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
401
402     /* is_private */
403     /* PROT_NONE */
404     M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
405     /* PROT_READ */
406     M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
407     /* PROT_WRITE */
408     M(PAGE_EXECUTE_READWRITE, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
409     /* PROT_READ | PROT_WRITE */
410     M(PAGE_EXECUTE_READWRITE, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
411
412     /* PROT_EXEC */
413     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE,
414       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
415     /* PROT_READ | PROT_EXEC */
416     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READ,
417       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
418     /* PROT_WRITE | PROT_EXEC */
419     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY,
420       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
421     /* PROT_READ | PROT_WRITE | PROT_EXEC */
422     M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY,
423       FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
424   };
425 #undef M
426
427   size_t ix;
428
429   NaClLog(3,
430           "NaClflProtectAndDesiredAccessMap(prot 0x%x,"
431           " priv 0x%x, accmode 0x%x, ...)\n",
432           prot, is_private, accmode);
433
434   NACL_COMPILE_TIME_ASSERT(NACL_ABI_O_RDONLY == 0);
435   NACL_COMPILE_TIME_ASSERT(NACL_ABI_O_RDWR == 2);
436   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_NONE == 0);
437   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_READ == 1);
438   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_WRITE == 2);
439   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_EXEC == 4);
440
441   CHECK(accmode != NACL_ABI_O_WRONLY);
442
443   /*
444    * NACL_ABI_O_RDONLY == 0, NACL_ABI_O_RDWR == 2, so multiplying by 8
445    * yields a base separation of 8 for the 16 element subtable indexed
446    * by the NACL_ABI_PROT_{READ|WRITE|EXEC} and is_private values.
447    */
448   ix = ((prot & NACL_ABI_PROT_MASK) +
449         (is_private << 3) +
450         ((accmode & NACL_ABI_O_ACCMODE) << 3));
451
452   CHECK(ix < NACL_ARRAY_SIZE(table));  /* compiler should elide this */
453
454   if (NULL != out_flMappingProtect) {
455     *out_flMappingProtect = table[ix].flMappingProtect;
456   }
457   if (NULL != out_flProtect) {
458     *out_flProtect = table[ix].flProtect;
459   }
460   if (NULL != out_dwDesiredAccess) {
461     *out_dwDesiredAccess = table[ix].dwDesiredAccess;
462   }
463   if (NULL != out_msg) {
464     *out_msg = table[ix].err;
465   }
466
467   NaClLog(3, "NaClflProtectAndDesiredAccessMap: %s %s %s\n",
468           table[ix].flMappingProtect_str,
469           table[ix].flProtect_str,
470           table[ix].dwDesiredAccess_str);
471 }
472
473 /*
474  * Returns flProtect flags for VirtualAlloc'd memory, file based
475  * mappings should always use NaClflProtectAndDesiredAccessMap.
476  */
477 DWORD NaClflProtectMap(int prot) {
478 #define M(p) { p, #p }
479   static struct {
480     DWORD flProtect;
481     char const *flProtect_str;
482   } table[] = {
483     /* PROT_NONE */
484     M(PAGE_NOACCESS),
485     /* PROT_READ */
486     M(PAGE_READONLY),
487     /* PROT_WRITE */
488     M(PAGE_READWRITE),
489     /* PROT_READ | PROT_WRITE */
490     M(PAGE_READWRITE),
491
492     /* PROT_EXEC */
493     M(PAGE_EXECUTE),
494     /* PROT_READ | PROT_EXEC */
495     M(PAGE_EXECUTE_READ),
496     /* PROT_WRITE | PROT_EXEC */
497     M(PAGE_EXECUTE_READWRITE),
498     /* PROT_READ | PROT_WRITE | PROT_EXEC */
499     M(PAGE_EXECUTE_READWRITE),
500   };
501 #undef M
502
503   size_t ix;
504
505   NaClLog(3, "NaClflProtectMap(prot 0x%x)\n", prot);
506
507   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_NONE == 0);
508   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_READ == 1);
509   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_WRITE == 2);
510   NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_EXEC == 4);
511
512   ix = (prot & NACL_ABI_PROT_MASK);
513   CHECK(ix < NACL_ARRAY_SIZE(table));  /* compiler should elide this */
514
515   NaClLog(3, "NaClflProtectMap: %s\n", table[ix].flProtect_str);
516
517   return table[ix].flProtect;
518 }
519
520 /*
521  * Unfortunately, when the descriptor is imported via
522  * NaClHostDescPosixTake or NaClHostDescPosixDup, the underlying file
523  * handle may not have GENERIC_EXECUTE permission associated with it,
524  * unlike the files open using NaClHostDescOpen.  This means that the
525  * CreateFileMapping with flMappingProtect that specifies PAGE_EXECUTE_*
526  * will fail. Since we don't know whether GENERIC_EXECUTE without doing
527  * a query, we instead lazily determine the need by detecting the
528  * CreateFileMapping error and retrying using a fallback
529  * flMappingProtect that does not have EXECUTE rights.  We record this
530  * in the descriptor so that the next time we do not have to try with
531  * the PAGE_EXECUTE_* and have it deterministically fail.
532  *
533  * This function is also used when mapping in anonymous memory.  We
534  * assume that we never map anonymous executable memory -- mmap of
535  * executable data is always from a file, and the page will be
536  * non-writable -- and we ensure that anonymous memory is never
537  * executable.
538  */
539 static DWORD NaClflProtectRemoveExecute(DWORD flProtect) {
540   switch (flProtect) {
541     case PAGE_EXECUTE:
542       return PAGE_NOACCESS;
543     case PAGE_EXECUTE_READ:
544       return PAGE_READONLY;
545     case PAGE_EXECUTE_READWRITE:
546       return PAGE_READWRITE;
547     case PAGE_EXECUTE_WRITECOPY:
548       return PAGE_WRITECOPY;
549   }
550   return flProtect;
551 }
552
553 /* Check if flProtect has executable permission. */
554 static int NaClflProtectHasExecute(DWORD flProtect) {
555   return flProtect == PAGE_EXECUTE ||
556          flProtect == PAGE_EXECUTE_READ ||
557          flProtect == PAGE_EXECUTE_READWRITE ||
558          flProtect == PAGE_EXECUTE_WRITECOPY;
559 }
560
561 /*
562  * TODO(mseaborn): Reduce duplication between this function and
563  * nacl::Map()/NaClMap().
564  */
565 uintptr_t NaClHostDescMap(struct NaClHostDesc *d,
566                           struct NaClDescEffector *effp,
567                           void                *start_addr,
568                           size_t              len,
569                           int                 prot,
570                           int                 flags,
571                           nacl_off64_t        offset) {
572   uintptr_t retval;
573   uintptr_t addr;
574   int       desc_flags;
575   HANDLE    hFile;
576   HANDLE    hMap;
577   int       retry_fallback;
578   DWORD     flMappingProtect;
579   DWORD     dwDesiredAccess;
580   DWORD     flProtect;
581   DWORD     flOldProtect;
582   char const *err_msg;
583   DWORD     dwMaximumSizeHigh;
584   DWORD     dwMaximumSizeLow;
585   uintptr_t map_result;
586   size_t    chunk_offset;
587   size_t    chunk_size;
588
589   NaClLog(4,
590           ("NaClHostDescMap(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR
591            ", 0x%"NACL_PRIxS", 0x%x, 0x%x, 0x%016"NACL_PRIxNACL_OFF64")\n"),
592           (uintptr_t) d, (uintptr_t) start_addr,
593           len, prot, flags, offset);
594   if (NULL == d && 0 == (flags & NACL_ABI_MAP_ANONYMOUS)) {
595     NaClLog(LOG_FATAL, "NaClHostDescMap: 'this' is NULL and not anon map\n");
596   }
597   if (NULL != d && -1 == d->d) {
598     NaClLog(LOG_FATAL, "NaClHostDescMap: already closed\n");
599   }
600   if ((0 == (flags & NACL_ABI_MAP_SHARED)) ==
601       (0 == (flags & NACL_ABI_MAP_PRIVATE))) {
602     NaClLog(LOG_FATAL,
603             "NaClHostDescMap: exactly one of NACL_ABI_MAP_SHARED"
604             " and NACL_ABI_MAP_PRIVATE must be set.\n");
605   }
606   addr = (uintptr_t) start_addr;
607   prot &= NACL_ABI_PROT_MASK;
608
609   /*
610    * Check that if FIXED, start_addr is not NULL.
611    * Use platform free address space locator to set start_addr if NULL and
612    * not FIXED.
613    */
614   if (0 == (flags & NACL_ABI_MAP_FIXED)) {
615     /*
616      * Not fixed, addr is a hint... which we ignore.  We cannot just
617      * let windows pick, since we are mapping memory in chunks of
618      * 64-kB to permit piecewise unmapping.
619      */
620     if (!NaClFindAddressSpace(&addr, len)) {
621       NaClLog(LOG_ERROR,
622               "NaClHostDescMap: not fixed, and could not find space\n");
623       return (uintptr_t) -NACL_ABI_ENOMEM;
624     }
625
626     NaClLog(4,
627             "NaClHostDescMap: NOT FIXED, found space at %"NACL_PRIxPTR"\n",
628             addr);
629
630     start_addr = (void *) addr;
631   }
632
633   flProtect = 0;
634   dwDesiredAccess = 0;
635
636   if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
637     /*
638      * anonymous memory must be free'able later via VirtualFree
639      */
640     NaClLog(3, "NaClHostDescMap: anonymous mapping\n");
641
642     flProtect = NaClflProtectMap(prot & (~PROT_EXEC));
643     NaClLog(3, "NaClHostDescMap: flProtect 0x%x\n", flProtect);
644
645     for (chunk_offset = 0;
646          chunk_offset < len;
647          chunk_offset += NACL_MAP_PAGESIZE) {
648       uintptr_t chunk_addr = addr + chunk_offset;
649
650       (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
651
652       NaClLog(3,
653               "NaClHostDescMap: VirtualAlloc(0x%08x,,%x,%x)\n",
654               (void *) (addr + chunk_offset),
655               MEM_COMMIT | MEM_RESERVE,
656               flProtect);
657       map_result = (uintptr_t) VirtualAlloc((void *) chunk_addr,
658                                             NACL_MAP_PAGESIZE,
659                                             MEM_COMMIT | MEM_RESERVE,
660                                             flProtect);
661       if (map_result != addr + chunk_offset) {
662         NaClLog(LOG_FATAL,
663                 ("Could not VirtualAlloc anonymous memory at"
664                  " addr 0x%08x with prot %x\n"),
665                 addr + chunk_offset, flProtect);
666       }
667     }
668     NaClLog(3, "NaClHostDescMap: (anon) returning 0x%08"NACL_PRIxPTR"\n",
669             start_addr);
670     return (uintptr_t) start_addr;
671   }
672
673   if (NULL == d) {
674     desc_flags = NACL_ABI_O_RDWR;
675   } else {
676     desc_flags = d->flags;
677   }
678
679   if (0 != (desc_flags & NACL_ABI_O_APPEND) &&
680       0 != (prot & NACL_ABI_PROT_WRITE)) {
681     return (uintptr_t) -NACL_ABI_EACCES;
682   }
683   if (NACL_ABI_O_WRONLY == (desc_flags & NACL_ABI_O_ACCMODE)) {
684     return (uintptr_t) -NACL_ABI_EACCES;
685   }
686   NaClflProtectAndDesiredAccessMap(prot,
687                                    0 != (flags & NACL_ABI_MAP_PRIVATE),
688                                    (desc_flags & NACL_ABI_O_ACCMODE),
689                                    &flMappingProtect,
690                                    &flProtect,
691                                    &dwDesiredAccess,
692                                    &err_msg);
693   if (0 == flProtect) {
694     NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
695     return (uintptr_t) -NACL_ABI_EACCES;
696   }
697   NaClLog(3,
698           "NaClHostDescMap: flMappingProtect 0x%x,"
699           " dwDesiredAccess 0x%x, flProtect 0x%x\n",
700           flMappingProtect, dwDesiredAccess, flProtect);
701
702   hFile = (HANDLE) _get_osfhandle(d->d);
703   dwMaximumSizeLow = 0;
704   dwMaximumSizeHigh = 0;
705
706   /*
707    * Ensure consistency of the d->flMappingProtect access.
708    */
709   NaClFastMutexLock(&d->mu);
710   if (0 != d->flMappingProtect) {
711     flMappingProtect = d->flMappingProtect;
712     retry_fallback = 0;
713   } else {
714     retry_fallback = 1;
715   }
716   NaClFastMutexUnlock(&d->mu);
717
718   /*
719    * Finite retry cycle.  We can fallback from PAGE_EXECUTE_WRITECOPY to
720    * PAGE_EXECUTE_READ and from having executable permissions to not having
721    * them.
722    */
723   while (1) {
724     /*
725      * If hFile is INVALID_HANDLE_VALUE, the memory is backed by the
726      * system paging file.  Why does it returns NULL instead of
727      * INVALID_HANDLE_VALUE when there is an error?
728      */
729     hMap = CreateFileMapping(hFile,
730                              NULL,
731                              flMappingProtect,
732                              dwMaximumSizeHigh,
733                              dwMaximumSizeLow,
734                              NULL);
735     if (NULL == hMap && retry_fallback) {
736       /*
737        * PAGE_EXECUTE_WRITECOPY is not supported on Windows XP so we fallback
738        * to PAGE_EXECUTE_READ.
739        */
740       if (PAGE_EXECUTE_WRITECOPY == flMappingProtect) {
741         NaClLog(3,
742                 "NaClHostDescMap: CreateFileMapping failed, retrying with"
743                 " PAGE_EXECUTE_READ instead of PAGE_EXECUTE_WRITECOPY\n");
744         flMappingProtect = PAGE_EXECUTE_READ;
745         continue;
746       }
747       if (0 == (prot & NACL_ABI_PROT_EXEC) &&
748           NaClflProtectHasExecute(flMappingProtect)) {
749         NaClLog(3,
750                 "NaClHostDescMap: CreateFileMapping failed, retrying without"
751                 " execute permission.  Original flMappingProtect 0x%x\n",
752                 flMappingProtect);
753         NaClflProtectAndDesiredAccessMap(prot & (~PROT_EXEC),
754                                          0 != (flags & NACL_ABI_MAP_PRIVATE),
755                                          (desc_flags & NACL_ABI_O_ACCMODE),
756                                          &flMappingProtect,
757                                          &flProtect,
758                                          &dwDesiredAccess,
759                                          &err_msg);
760         if (0 == flProtect) {
761           NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
762           return (uintptr_t) -NACL_ABI_EACCES;
763         }
764         flMappingProtect = NaClflProtectRemoveExecute(flMappingProtect);
765         NaClLog(3,
766                 "NaClHostDescMap: fallback flMappingProtect 0x%x,"
767                 " dwDesiredAccess 0x%x, flProtect 0x%x\n",
768                 flMappingProtect, dwDesiredAccess, flProtect);
769         continue;
770       }
771       NaClLog(3,
772               "NaClHostDescMap: not retrying, since caller explicitly asked"
773               " for NACL_ABI_PROT_EXEC\n");
774       break;
775     }
776     /*
777      * Remember successful flProtect used.  Note that this just
778      * ensures reads of d->flMappingProtect gets a consistent value;
779      * we have a potential race where two threads perform mmap and in
780      * parallel determine the replacement flProtect value.  This is
781      * okay, since those two threads should arrive at the same
782      * replacement value.  This could be replaced with an atomic
783      * word.
784      */
785     NaClFastMutexLock(&d->mu);
786     d->flMappingProtect = flMappingProtect;
787     NaClFastMutexUnlock(&d->mu);
788     break;
789   }
790
791   if (NULL == hMap) {
792     DWORD err = GetLastError();
793     NaClLog(LOG_INFO,
794             "NaClHostDescMap: CreateFileMapping failed: %d\n",
795             err);
796     return -NaClXlateSystemError(err);
797   }
798   NaClLog(3, "NaClHostDescMap: CreateFileMapping got handle %d\n", (int) hMap);
799   NaClLog(3, "NaClHostDescMap: dwDesiredAccess 0x%x\n", dwDesiredAccess);
800
801   retval = (uintptr_t) -NACL_ABI_EINVAL;
802
803   for (chunk_offset = 0;
804        chunk_offset < len;
805        chunk_offset += NACL_MAP_PAGESIZE) {
806     uintptr_t chunk_addr = addr + chunk_offset;
807     nacl_off64_t net_offset;
808     uint32_t net_offset_high;
809     uint32_t net_offset_low;
810
811     (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
812
813     chunk_size = size_min(len - chunk_offset, NACL_MAP_PAGESIZE);
814     /* in case MapViewOfFile cares that we exceed the file size */
815     net_offset = offset + chunk_offset;
816     net_offset_high = (uint32_t) (net_offset >> 32);
817     net_offset_low = (uint32_t) net_offset;
818     NaClLog(4,
819             "NaClHostDescMap: MapViewOfFileEx(hMap=%d, dwDesiredAccess=0x%x,"
820             " net_offset_high = 0x%08x, net_offset_low = 0x%08x,"
821             " chunk_size = 0x%"NACL_PRIxS", chunk_addr = 0x%"NACL_PRIxPTR"\n",
822             hMap, dwDesiredAccess, net_offset_high, net_offset_low,
823             chunk_size, chunk_addr);
824     map_result = (uintptr_t) MapViewOfFileEx(hMap,
825                                              dwDesiredAccess,
826                                              net_offset_high,
827                                              net_offset_low,
828                                              chunk_size,
829                                              (void *) chunk_addr);
830     NaClLog(3,
831             "NaClHostDescMap: map_result %"NACL_PRIxPTR
832             ", chunk_addr %"NACL_PRIxPTR
833             ", addr + chunk_offset %"NACL_PRIxPTR"\n",
834             map_result, chunk_addr, (addr + chunk_offset));
835     if ((addr + chunk_offset) != map_result) {
836       DWORD err = GetLastError();
837       NaClLog(LOG_FATAL,
838               "MapViewOfFileEx failed at 0x%08"NACL_PRIxPTR
839               ", got 0x%08"NACL_PRIxPTR", err %x\n",
840               addr + chunk_offset,
841               map_result,
842               err);
843     }
844     if (!VirtualProtect((void *) map_result,
845                         NaClRoundPage(chunk_size),
846                         flProtect,
847                         &flOldProtect)) {
848         DWORD err = GetLastError();
849         NaClLog(LOG_INFO,
850                 "VirtualProtect failed at 0x%08x, err %x\n",
851                 addr, err);
852         retval = (uintptr_t) -NaClXlateSystemError(err);
853         goto cleanup;
854     }
855   }
856   retval = (uintptr_t) start_addr;
857 cleanup:
858   (void) CloseHandle(hMap);
859   NaClLog(3, "NaClHostDescMap: returning %"NACL_PRIxPTR"\n", retval);
860   return retval;
861 }
862
863 int NaClHostDescUnmapUnsafe(void    *start_addr,
864                             size_t  len) {
865   uintptr_t addr;
866   size_t    off;
867
868   addr = (uintptr_t) start_addr;
869
870   for (off = 0; off < len; off += NACL_MAP_PAGESIZE) {
871     if (!UnmapViewOfFile((void *) (addr + off))) {
872       NaClLog(LOG_ERROR,
873               "NaClHostDescUnmap: UnmapViewOfFile(0x%08x) failed\n",
874               addr + off);
875       return -NACL_ABI_EINVAL;
876     }
877   }
878   return 0;
879 }
880
881 static void NaClHostDescCtorIntern(struct NaClHostDesc *hd,
882                                    int posix_d,
883                                    int flags) {
884   nacl_host_stat_t stbuf;
885
886   hd->d = posix_d;
887   hd->flags = flags;
888   hd->flMappingProtect = 0;
889   if (_fstat64(posix_d, &stbuf) != 0) {
890     /* inherited non-fstat'able are IPC channels, e.g., for bootstrap channel */
891     NaClLog(4,
892             "NaClHostDescCtorIntern: could not _fstat64,"
893             " assuming non-seekable\n");
894     hd->protect_filepos = 0;
895   } else {
896     int file_type = stbuf.st_mode & S_IFMT;
897     /*
898      * Inherited stdio are console handles and are not seekable.
899      *
900      * Posix descriptors (wrapping Windows HANDLES) opened for
901      * O_WRONLY | O_APPEND cannot have byte range locks applied, which
902      * is how the protect_filepos mechanism is implemented.  Luckily,
903      * this is only needed for O_RDWR | O_APPEND or non-append
904      * descriptor.
905      */
906     hd->protect_filepos = (((S_IFREG == file_type) ||
907                            (S_IFDIR == file_type)) &&
908                            !((flags & NACL_ABI_O_APPEND) != 0 &&
909                              (flags & NACL_ABI_O_ACCMODE) ==
910                              NACL_ABI_O_WRONLY));
911   }
912   if (!NaClFastMutexCtor(&hd->mu)) {
913     NaClLog(LOG_FATAL, "NaClHostDescCtorIntern: NaClFastMutexCtor failed\n");
914   }
915 }
916
917 int NaClHostDescOpen(struct NaClHostDesc  *d,
918                      char const           *path,
919                      int                  flags,
920                      int                  perms) {
921   DWORD dwDesiredAccess;
922   DWORD dwCreationDisposition;
923   DWORD dwFlagsAndAttributes;
924   int oflags;
925   int truncate_after_open = 0;
926   HANDLE hFile;
927   DWORD err;
928   int fd;
929
930   if (NULL == d) {
931     NaClLog(LOG_FATAL, "NaClHostDescOpen: 'this' is NULL\n");
932   }
933
934   /*
935    * Sanitize access flags.
936    */
937   if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
938     return -NACL_ABI_EINVAL;
939   }
940
941   switch (flags & NACL_ABI_O_ACCMODE) {
942     case NACL_ABI_O_RDONLY:
943       dwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
944       oflags = _O_RDONLY | _O_BINARY;
945       break;
946     case NACL_ABI_O_RDWR:
947       oflags = _O_RDWR | _O_BINARY;
948       dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
949       break;
950     case NACL_ABI_O_WRONLY:  /* Enforced in the Read call */
951       oflags = _O_WRONLY | _O_BINARY;
952       dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
953       break;
954     default:
955       NaClLog(LOG_ERROR,
956               "NaClHostDescOpen: bad access flags 0x%x.\n",
957               flags);
958       return -NACL_ABI_EINVAL;
959   }
960   /*
961    * Possibly make the file read-only.  The file attribute only
962    * applies if the file is created; if it pre-exists, the attributes
963    * from the file is combined with the FILE_FLAG_* values.
964    */
965   if (0 == (perms & NACL_ABI_S_IWUSR)) {
966     dwFlagsAndAttributes = (FILE_ATTRIBUTE_READONLY |
967                             FILE_FLAG_POSIX_SEMANTICS);
968   } else {
969     dwFlagsAndAttributes = (FILE_ATTRIBUTE_NORMAL |
970                             FILE_FLAG_POSIX_SEMANTICS);
971   }
972   /*
973    * Postcondition: flags & NACL_ABI_O_ACCMODE is one of the three
974    * allowed values.
975    */
976   switch (flags & (NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC)) {
977     case 0:
978       dwCreationDisposition = OPEN_EXISTING;
979       break;
980     case NACL_ABI_O_CREAT:
981       dwCreationDisposition = OPEN_ALWAYS;
982       break;
983     case NACL_ABI_O_TRUNC:
984       dwCreationDisposition = TRUNCATE_EXISTING;
985       truncate_after_open = 1;
986       break;
987     case NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC:
988       dwCreationDisposition = OPEN_ALWAYS;
989       truncate_after_open = 1;
990   }
991   if (0 != (flags & NACL_ABI_O_APPEND)) {
992     oflags |= _O_APPEND;
993   }
994
995   NaClLog(1,
996           "NaClHostDescOpen: CreateFileA(path=%s, desired_access=0x%x,"
997           " share_mode=ALL, security_attributes=NULL, creation_disposition=%d,"
998           " flags_and_attributes=%d, template_file=NULL)\n",
999           path, dwDesiredAccess, dwCreationDisposition, dwFlagsAndAttributes);
1000
1001   hFile = CreateFileA(path, dwDesiredAccess,
1002                       (FILE_SHARE_DELETE |
1003                        FILE_SHARE_READ |
1004                        FILE_SHARE_WRITE),
1005                       NULL,
1006                       dwCreationDisposition,
1007                       dwFlagsAndAttributes,
1008                       NULL);
1009   if (INVALID_HANDLE_VALUE == hFile) {
1010     err = GetLastError();
1011     NaClLog(3, "NaClHostDescOpen: CreateFile failed %d\n", err);
1012     return -NaClXlateSystemError(err);
1013   }
1014   if (truncate_after_open &&
1015       NACL_ABI_O_RDONLY != (flags & NACL_ABI_O_ACCMODE)) {
1016     NaClLog(4, "NaClHostDescOpen: Truncating file\n");
1017     if (!SetEndOfFile(hFile)) {
1018       err = GetLastError();
1019       NaClLog(LOG_ERROR,
1020               "NaClHostDescOpen: could not truncate file:"
1021               " last error %d.\n",
1022               err);
1023       if (err == ERROR_USER_MAPPED_FILE) {
1024         NaClLog(LOG_ERROR,
1025                 "NaClHostDescOpen: this is due to an existing mapping"
1026                 " of the same file.\n");
1027       }
1028     }
1029   }
1030   fd = _open_osfhandle((intptr_t) hFile, oflags);
1031   /*
1032    * oflags _O_APPEND, _O_RDONLY, and _O_TEXT are meaningful; unclear
1033    * whether _O_RDWR, _O_WRONLY, etc has any effect.
1034    */
1035   if (-1 == fd) {
1036     NaClLog(LOG_FATAL, "NaClHostDescOpen failed: err %d\n",
1037             GetLastError());
1038   }
1039   NaClHostDescCtorIntern(d, fd, flags);
1040   return 0;
1041 }
1042
1043 int NaClHostDescPosixDup(struct NaClHostDesc  *d,
1044                          int                  posix_d,
1045                          int                  flags) {
1046   int host_desc;
1047
1048   NaClLog(3, "NaClHostDescPosixDup(0x%08x, %d, 0%o)\n",
1049           (uintptr_t) d, posix_d, flags);
1050   if (NULL == d) {
1051     NaClLog(LOG_FATAL, "NaClHostDescPosixDup: 'this' is NULL\n");
1052   }
1053   /*
1054    * Sanitize access flags.
1055    */
1056   if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1057     return -NACL_ABI_EINVAL;
1058   }
1059   switch (flags & NACL_ABI_O_ACCMODE) {
1060     case NACL_ABI_O_RDONLY:
1061     case NACL_ABI_O_WRONLY:
1062     case NACL_ABI_O_RDWR:
1063       break;
1064     default:
1065       NaClLog(LOG_ERROR,
1066               "NaClHostDescOpen: bad access flags 0x%x.\n",
1067               flags);
1068       return -NACL_ABI_EINVAL;
1069   }
1070
1071   host_desc = _dup(posix_d);
1072   if (-1 == host_desc) {
1073     return -GetErrno();
1074   }
1075   NaClHostDescCtorIntern(d, host_desc, flags);
1076   return 0;
1077 }
1078
1079 int NaClHostDescPosixTake(struct NaClHostDesc *d,
1080                           int                 posix_d,
1081                           int                 flags) {
1082   if (NULL == d) {
1083     NaClLog(LOG_FATAL, "NaClHostDescPosixTake: 'this' is NULL\n");
1084   }
1085   /*
1086    * Sanitize access flags.
1087    */
1088   if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1089     return -NACL_ABI_EINVAL;
1090   }
1091   switch (flags & NACL_ABI_O_ACCMODE) {
1092     case NACL_ABI_O_RDONLY:
1093     case NACL_ABI_O_WRONLY:
1094     case NACL_ABI_O_RDWR:
1095       break;
1096     default:
1097       NaClLog(LOG_ERROR,
1098               "NaClHostDescOpen: bad access flags 0x%x.\n",
1099               flags);
1100       return -NACL_ABI_EINVAL;
1101   }
1102   NaClHostDescCtorIntern(d, posix_d, flags);
1103   return 0;
1104 }
1105
1106 ssize_t NaClHostDescRead(struct NaClHostDesc  *d,
1107                          void                 *buf,
1108                          size_t               len) {
1109   /* Windows ReadFile only supports DWORD, so we need
1110    * to clamp the length. */
1111   unsigned int actual_len;
1112   HANDLE fh;
1113   DWORD bytes_received;
1114   DWORD err;
1115
1116   if (len < UINT_MAX) {
1117     actual_len = (unsigned int) len;
1118   } else {
1119     actual_len = UINT_MAX;
1120   }
1121
1122   NaClHostDescCheckValidity("NaClHostDescRead", d);
1123   if (NACL_ABI_O_WRONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1124     NaClLog(3, "NaClHostDescRead: WRONLY file\n");
1125     return -NACL_ABI_EBADF;
1126   }
1127   /*
1128    * We drop into using Windows ReadFile rather than using _read from
1129    * the POSIX compatibility layer here.  The reason for this is
1130    * because the pread/pwrite implementation uses ReadFile/WriteFile,
1131    * it would be more consistent with the pread/pwrite implementation
1132    * to just also use ReadFile/WriteFile directly here as well.
1133    *
1134    * NB: contrary to the documentation available on MSDN, operations
1135    * on synchronous files with non-NULL LPOVERLAPPED arguments result
1136    * in the *implicit* file position getting updated before the
1137    * ReadFile/WriteFile returning, rather than the Offset/OffsetHigh
1138    * members of the explicit OVERLAPPED structure (!).  In order to
1139    * support mixed read/pread syscall sequences (and similarly mixed
1140    * write/pwrite sequences) we must effectively lock the file
1141    * position from access by other threads and then read/write, so
1142    * that when pread/pwrite mess up the implicit file position
1143    * temporarily, it would not be visible.
1144    */
1145   fh = (HANDLE) _get_osfhandle(d->d);
1146   CHECK(INVALID_HANDLE_VALUE != fh);
1147
1148   /*
1149    * Ensure that we do not corrupt shared implicit file position.
1150    */
1151   if (d->protect_filepos) {
1152     NaClTakeFilePosLock(fh);
1153   }
1154   if (!ReadFile(fh, buf, actual_len, &bytes_received, NULL)) {
1155     err = GetLastError();
1156     if (ERROR_HANDLE_EOF == err) {
1157       bytes_received = 0;
1158     } else {
1159       NaClLog(4, "NaClHostDescRead: ReadFile error %d\n", err);
1160       bytes_received = -NaClXlateSystemError(err);
1161     }
1162   }
1163   if (d->protect_filepos) {
1164     NaClDropFilePosLock(fh);
1165   }
1166
1167   return bytes_received;
1168 }
1169
1170 ssize_t NaClHostDescWrite(struct NaClHostDesc *d,
1171                           void const          *buf,
1172                           size_t              len) {
1173   /*
1174    * Windows WriteFile only supports DWORD uint, so we need to clamp
1175    * the length.
1176    */
1177   unsigned int actual_len;
1178   HANDLE fh;
1179   DWORD bytes_written;
1180   DWORD err;
1181   OVERLAPPED overlap;
1182   OVERLAPPED *overlap_ptr = NULL;
1183
1184   if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1185     NaClLog(3, "NaClHostDescWrite: RDONLY file\n");
1186     return -NACL_ABI_EBADF;
1187   }
1188   if (len < UINT_MAX) {
1189     actual_len = (unsigned int) len;
1190   } else {
1191     actual_len = UINT_MAX;
1192   }
1193
1194   NaClHostDescCheckValidity("NaClHostDescWrite", d);
1195   /*
1196    * See discussion in NaClHostDescRead above wrt why we use WriteFile
1197    * instead of _write below.
1198    */
1199   if (0 != (NACL_ABI_O_APPEND & d->flags)) {
1200     nacl_off64_t offset = GG_LONGLONG(0xffffffffffffffff);
1201     memset(&overlap, 0, sizeof overlap);
1202     overlap.Offset = (DWORD) offset;
1203     overlap.OffsetHigh = (DWORD) (offset >> 32);
1204     overlap_ptr = &overlap;
1205   }
1206   fh = (HANDLE) _get_osfhandle(d->d);
1207   CHECK(INVALID_HANDLE_VALUE != fh);
1208   /*
1209    * Ensure that we do not corrupt shared implicit file position.
1210    */
1211   if (d->protect_filepos) {
1212     NaClTakeFilePosLock(fh);
1213   }
1214   if (!WriteFile(fh, buf, actual_len, &bytes_written, overlap_ptr)) {
1215     err = GetLastError();
1216     NaClLog(4, "NaClHostDescWrite: WriteFile error %d\n", err);
1217
1218     bytes_written = -NaClXlateSystemError(err);
1219   }
1220   if (d->protect_filepos) {
1221     NaClDropFilePosLock(fh);
1222   }
1223
1224   return bytes_written;
1225 }
1226
1227 nacl_off64_t NaClHostDescSeek(struct NaClHostDesc  *d,
1228                               nacl_off64_t         offset,
1229                               int                  whence) {
1230   HANDLE hFile;
1231   nacl_off64_t retval;
1232
1233   NaClHostDescCheckValidity("NaClHostDescSeek", d);
1234   hFile = (HANDLE) _get_osfhandle(d->d);
1235   CHECK(INVALID_HANDLE_VALUE != hFile);
1236   if (d->protect_filepos) {
1237     NaClTakeFilePosLock(hFile);
1238   }
1239   retval = _lseeki64(d->d, offset, whence);
1240   if (d->protect_filepos) {
1241     NaClDropFilePosLock(hFile);
1242   }
1243   return (-1 == retval) ? -errno : retval;
1244 }
1245
1246 ssize_t NaClHostDescPRead(struct NaClHostDesc *d,
1247                           void *buf,
1248                           size_t len,
1249                           nacl_off64_t offset) {
1250   HANDLE fh;
1251   OVERLAPPED overlap;
1252   DWORD bytes_received;
1253   DWORD err;
1254   nacl_off64_t orig_pos = 0;
1255
1256   NaClHostDescCheckValidity("NaClHostDescPRead", d);
1257   if (NACL_ABI_O_WRONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1258     NaClLog(3, "NaClHostDescPRead: WRONLY file\n");
1259     return -NACL_ABI_EBADF;
1260   }
1261   if (offset < 0) {
1262     return -NACL_ABI_EINVAL;
1263   }
1264   /*
1265    * There are reports of driver issues that may require clamping len
1266    * to a megabyte or so, lest ReadFile returns an error with
1267    * GetLastError() returning ERROR_INVALID_PARAMETER, but since we
1268    * do not expect to ever read from / write to anything other than
1269    * filesystem files, we do not clamp.
1270    */
1271   fh = (HANDLE) _get_osfhandle(d->d);
1272   CHECK(INVALID_HANDLE_VALUE != fh);
1273   memset(&overlap, 0, sizeof overlap);
1274   overlap.Offset = (DWORD) offset;
1275   overlap.OffsetHigh = (DWORD) (offset >> 32);
1276   if (len > UINT_MAX) {
1277     len = UINT_MAX;
1278   }
1279   if (d->protect_filepos) {
1280     orig_pos = NaClLockAndGetCurrentFilePos(fh);
1281   }
1282   if (!ReadFile(fh, buf, (DWORD) len, &bytes_received, &overlap)) {
1283     err = GetLastError();
1284     if (ERROR_HANDLE_EOF == err) {
1285       bytes_received = 0;
1286       /* handle as if returned true. */
1287     } else {
1288       NaClLog(4, "NaClHostDescPRead: ReadFile failed, error %d\n", err);
1289       NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1290       bytes_received = -NaClXlateSystemError(err);
1291     }
1292   }
1293   if (d->protect_filepos) {
1294     NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1295   }
1296   return bytes_received;
1297 }
1298
1299 ssize_t NaClHostDescPWrite(struct NaClHostDesc *d,
1300                            void const *buf,
1301                            size_t len,
1302                            nacl_off64_t offset) {
1303   HANDLE fh;
1304   OVERLAPPED overlap;
1305   DWORD bytes_sent;
1306   DWORD err;
1307   nacl_off64_t orig_pos = 0;
1308
1309   NaClHostDescCheckValidity("NaClHostDescPWrite", d);
1310   if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1311     NaClLog(3, "NaClHostDescPWrite: RDONLY file\n");
1312     return -NACL_ABI_EBADF;
1313   }
1314   if (offset < 0) {
1315     /*
1316      * This also avoids the case where having 0xffffffff in both
1317      * overlap.Offset and overlap.OffsetHigh means append to the file.
1318      * In Posix, offset does not permit special meanings being encoded
1319      * like this.
1320      */
1321     return -NACL_ABI_EINVAL;
1322   }
1323   fh = (HANDLE) _get_osfhandle(d->d);
1324   CHECK(INVALID_HANDLE_VALUE != fh);
1325   memset(&overlap, 0, sizeof overlap);
1326   overlap.Offset = (DWORD) offset;
1327   overlap.OffsetHigh = (DWORD) (offset >> 32);
1328   if (len > UINT_MAX) {
1329     len = UINT_MAX;
1330   }
1331   if (d->protect_filepos) {
1332     orig_pos = NaClLockAndGetCurrentFilePos(fh);
1333   }
1334   if (!WriteFile(fh, buf, (DWORD) len, &bytes_sent, &overlap)) {
1335     err = GetLastError();
1336     if (ERROR_HANDLE_EOF == err) {
1337       bytes_sent = 0;
1338       /* handle as if returned true. */
1339     } else {
1340       NaClLog(4,
1341               "NaClHostDescPWrite: WriteFile failed, error %d\n", err);
1342       bytes_sent = -NaClXlateSystemError(err);
1343     }
1344   }
1345   if (d->protect_filepos) {
1346     NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1347   }
1348   return bytes_sent;
1349 }
1350
1351 int NaClHostDescFstat(struct NaClHostDesc   *d,
1352                       nacl_host_stat_t      *nasp) {
1353   NaClHostDescCheckValidity("NaClHostDescFstat", d);
1354   if (NACL_HOST_FSTAT64(d->d, nasp) == -1) {
1355     return -GetErrno();
1356   }
1357
1358   return 0;
1359 }
1360
1361 int NaClHostDescIsatty(struct NaClHostDesc *d) {
1362   int retval;
1363
1364   NaClHostDescCheckValidity("NaClHostDescIsatty", d);
1365   retval = _isatty(d->d);
1366   /* When windows _isatty fails it returns zero, but does not set errno. */
1367   return (0 == retval) ? -NACL_ABI_ENOTTY : 1;
1368 }
1369
1370 int NaClHostDescClose(struct NaClHostDesc *d) {
1371   int retval;
1372
1373   NaClHostDescCheckValidity("NaClHostDescClose", d);
1374   if (-1 != d->d) {
1375     retval = _close(d->d);
1376     if (-1 == retval) {
1377       return -GetErrno();
1378     }
1379     d->d = -1;
1380   }
1381   NaClFastMutexDtor(&d->mu);
1382   return 0;
1383 }
1384
1385 /*
1386  * This is not a host descriptor function, but is closely related to
1387  * fstat and should behave similarly.
1388  */
1389 int NaClHostDescStat(char const *path, nacl_host_stat_t *nhsp) {
1390   if (NACL_HOST_STAT64(path, nhsp) == -1) {
1391     return -GetErrno();
1392   }
1393
1394   return 0;
1395 }
1396
1397 int NaClHostDescMkdir(const char *path, int mode) {
1398   UNREFERENCED_PARAMETER(mode);
1399   if (_mkdir(path) != 0)
1400     return -NaClXlateErrno(errno);
1401   return 0;
1402 }
1403
1404 int NaClHostDescRmdir(const char *path) {
1405   if (_rmdir(path) != 0)
1406     return -NaClXlateErrno(errno);
1407   return 0;
1408 }
1409
1410 int NaClHostDescChdir(const char *path) {
1411   if (_chdir(path) != 0)
1412     return -NaClXlateErrno(errno);
1413   return 0;
1414 }
1415
1416 int NaClHostDescGetcwd(char *path, size_t len) {
1417   if (_getcwd(path, (int) len) == NULL)
1418     return -NaClXlateErrno(errno);
1419   return 0;
1420 }
1421
1422 int NaClHostDescUnlink(const char *path) {
1423   /*
1424    * If the file exists and is not writable we make it writeable
1425    * before calling _unlink() to match the POSIX semantics where
1426    * unlink(2) can remove readonly files.
1427    */
1428   if (_access(path, WIN_F_OK) == 0 && _access(path, WIN_W_OK) != 0) {
1429     if (_chmod(path, _S_IREAD | S_IWRITE) != 0) {
1430       /* If _chmod fails just log it and contine on to call _unlink anyway */
1431       NaClLog(3, "NaClHostDescUnlink: _chmod failed: %d\n", errno);
1432     }
1433   }
1434
1435   if (_unlink(path) != 0)
1436     return -NaClXlateErrno(errno);
1437
1438   return 0;
1439 }
1440
1441 int NaClHostDescTruncate(char const *path, nacl_abi_off_t length) {
1442   LARGE_INTEGER win_length;
1443   DWORD err;
1444
1445   HANDLE hfile = CreateFileA(path,
1446       GENERIC_READ | GENERIC_WRITE,
1447       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1448       NULL,
1449       OPEN_EXISTING,
1450       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS,
1451       NULL);
1452
1453   if (INVALID_HANDLE_VALUE == hfile) {
1454     err = GetLastError();
1455     NaClLog(3, "NaClHostDescTruncate: CreateFile failed %d\n", err);
1456     return -NaClXlateSystemError(err);
1457   }
1458
1459   win_length.QuadPart = length;
1460   if (!SetFilePointerEx(hfile, win_length, NULL, FILE_BEGIN)) {
1461     err = GetLastError();
1462     NaClLog(LOG_ERROR,
1463             "NaClHostDescTruncate: SetFilePointerEx failed:"
1464             " last error %d.\n", err);
1465     return -NaClXlateSystemError(err);
1466   }
1467
1468   if (!SetEndOfFile(hfile)) {
1469     err = GetLastError();
1470     NaClLog(LOG_ERROR,
1471             "NaClHostDescTruncate: could not truncate file:"
1472             " last error %d.\n", err);
1473     if (err == ERROR_USER_MAPPED_FILE) {
1474       NaClLog(LOG_ERROR,
1475               "NaClHostDescTruncate: this is due to an existing"
1476               " mapping of the same file.\n");
1477     }
1478     return -NaClXlateSystemError(err);
1479   }
1480
1481   return 0;
1482 }
1483
1484 int NaClHostDescLstat(char const *path, nacl_host_stat_t *nhsp) {
1485   /*
1486    * Since symlinks don't exist on windows, stat() and lstat()
1487    * are equivalent.
1488    */
1489   return NaClHostDescStat(path, nhsp);
1490 }
1491
1492 int NaClHostDescLink(const char *oldpath, const char *newpath) {
1493   /*
1494    * Hard linking not implemented for win32
1495    */
1496   NaClLog(1, "NaClHostDescLink: hard linking not supported on windows.\n");
1497   return -NACL_ABI_ENOSYS;
1498 }
1499
1500 int NaClHostDescRename(const char *oldpath, const char *newpath) {
1501   if (rename(oldpath, newpath) != 0)
1502     return -NaClXlateErrno(errno);
1503   return 0;
1504 }
1505
1506 int NaClHostDescSymlink(const char *oldpath, const char *newpath) {
1507   /*
1508    * Symlinks are not supported on win32.
1509    */
1510   NaClLog(1, "NaClHostDescSymlink: symbolic links not supported on windows.\n");
1511   return -NACL_ABI_ENOSYS;
1512 }
1513
1514 int NaClHostDescChmod(const char *path, nacl_abi_mode_t mode) {
1515   if (_chmod(path, NaClMapMode(mode)) != 0)
1516     return -NaClXlateErrno(errno);
1517   return 0;
1518 }
1519
1520 int NaClHostDescAccess(const char *path, int amode) {
1521   if (_access(path, NaClMapAccessMode(amode)) != 0)
1522     return -NaClXlateErrno(errno);
1523   return 0;
1524 }
1525
1526 int NaClHostDescReadlink(const char *path, char *buf, size_t bufsize) {
1527   /*
1528    * readlink(2) sets errno to EINVAL when the file in question is
1529    * not a symlink.  Since win32 does not support symlinks we simply
1530    * return EINVAL in all cases here.
1531    */
1532   NaClLog(1,
1533           "NaClHostDescReadlink: symbolic links not supported on Windows.\n");
1534   return -NACL_ABI_EINVAL;
1535 }