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