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.
8 * NaCl Service Runtime. I/O Descriptor / Handle abstraction. Memory
9 * mapping using descriptors.
11 #include "native_client/src/include/portability.h"
12 #include "native_client/src/include/portability_io.h"
17 #include <sys/types.h>
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"
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"
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"
42 #define OFFSET_FOR_FILEPOS_LOCK (GG_LONGLONG(0x7000000000000000))
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.
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
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.
75 static void NaClTakeFilePosLock(HANDLE hFile) {
79 memset(&overlap, 0, sizeof overlap);
80 overlap.Offset = (DWORD) OFFSET_FOR_FILEPOS_LOCK;
81 overlap.OffsetHigh = (DWORD) (OFFSET_FOR_FILEPOS_LOCK >> 32);
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.
94 if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK,
96 /* nNumberOfBytesToLockLow= */ 1,
97 /* nNumberOfBytesToLockHigh= */ 0,
100 NaClLog(LOG_FATAL, "NaClTakeFilePosLock: LockFileEx failed, error %u\n",
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
110 static INLINE mode_t NaClMapMode(nacl_abi_mode_t abi_mode) {
112 if (0 != (abi_mode & NACL_ABI_S_IRUSR))
114 if (0 != (abi_mode & NACL_ABI_S_IWUSR))
119 /* Windows doesn't define R_OK or W_OK macros but expects these constants */
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.
130 static INLINE int NaClMapAccessMode(int nacl_mode) {
132 if (nacl_mode == NACL_ABI_F_OK) {
135 if (nacl_mode & NACL_ABI_R_OK)
137 if (nacl_mode & NACL_ABI_W_OK)
143 static void NaClDropFilePosLock(HANDLE hFile) {
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,
152 /* nNumberOfBytesToLockLow= */ 1,
153 /* nNumberOfBytesToLockHigh= */ 0,
155 err = GetLastError();
156 NaClLog(LOG_FATAL, "NaClDropFilePosLock: UnlockFileEx failed, error %u\n",
161 static nacl_off64_t NaClLockAndGetCurrentFilePos(HANDLE hFile) {
162 LARGE_INTEGER to_move;
163 LARGE_INTEGER cur_pos;
166 NaClTakeFilePosLock(hFile);
167 to_move.QuadPart = 0;
168 if (!SetFilePointerEx(hFile, to_move, &cur_pos, FILE_CURRENT)) {
169 err = GetLastError();
171 "NaClLockAndGetCurrentFilePos: SetFilePointerEx failed, error %u\n",
174 return cur_pos.QuadPart;
177 static void NaClSetCurrentFilePosAndUnlock(HANDLE hFile,
179 LARGE_INTEGER to_move;
182 to_move.QuadPart = pos;
183 if (!SetFilePointerEx(hFile, to_move, (LARGE_INTEGER *) NULL, FILE_BEGIN)) {
184 err = GetLastError();
186 "NaClSetCurrentFilePosAndUnlock: SetFilePointerEx failed:"
190 NaClDropFilePosLock(hFile);
194 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
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.
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.
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
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.
237 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
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.
247 (void) _get_errno(&thread_errno);
248 return NaClXlateErrno(thread_errno);
251 static INLINE size_t size_min(size_t a, size_t b) {
252 return (a < b) ? a : b;
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.
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).
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
273 * in parameters are all NACL_ABI_ values or bool (0/1).
275 * accmode may be NACL_ABI_O_RDONLY or NACL_ABI_O_RDWR, but not
276 * NACL_ABI_O_WRONLY (see below).
278 * Caller should check for:
280 * - PROT_NONE -> special case handling,
281 * - NACL_ABI_O_APPEND and PROT_WRITE -> EACCES,
282 * - accmode is O_WRONLY -> EACCES,
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.
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.
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.
306 void NaClflProtectAndDesiredAccessMap(int prot,
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 }
315 DWORD flMappingProtect;
317 DWORD dwDesiredAccess;
319 char const *flMappingProtect_str;
320 char const *flProtect_str;
321 char const *dwDesiredAccess_str;
326 M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
328 M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
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"),
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"),
346 M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
348 M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
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),
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.
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.
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),
381 M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
383 M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
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),
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),
404 M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
406 M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
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),
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),
430 "NaClflProtectAndDesiredAccessMap(prot 0x%x,"
431 " priv 0x%x, accmode 0x%x, ...)\n",
432 prot, is_private, accmode);
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);
441 CHECK(accmode != NACL_ABI_O_WRONLY);
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.
448 ix = ((prot & NACL_ABI_PROT_MASK) +
450 ((accmode & NACL_ABI_O_ACCMODE) << 3));
452 CHECK(ix < NACL_ARRAY_SIZE(table)); /* compiler should elide this */
454 if (NULL != out_flMappingProtect) {
455 *out_flMappingProtect = table[ix].flMappingProtect;
457 if (NULL != out_flProtect) {
458 *out_flProtect = table[ix].flProtect;
460 if (NULL != out_dwDesiredAccess) {
461 *out_dwDesiredAccess = table[ix].dwDesiredAccess;
463 if (NULL != out_msg) {
464 *out_msg = table[ix].err;
467 NaClLog(3, "NaClflProtectAndDesiredAccessMap: %s %s %s\n",
468 table[ix].flMappingProtect_str,
469 table[ix].flProtect_str,
470 table[ix].dwDesiredAccess_str);
474 * Returns flProtect flags for VirtualAlloc'd memory, file based
475 * mappings should always use NaClflProtectAndDesiredAccessMap.
477 DWORD NaClflProtectMap(int prot) {
478 #define M(p) { p, #p }
481 char const *flProtect_str;
489 /* PROT_READ | PROT_WRITE */
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),
505 NaClLog(3, "NaClflProtectMap(prot 0x%x)\n", prot);
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);
512 ix = (prot & NACL_ABI_PROT_MASK);
513 CHECK(ix < NACL_ARRAY_SIZE(table)); /* compiler should elide this */
515 NaClLog(3, "NaClflProtectMap: %s\n", table[ix].flProtect_str);
517 return table[ix].flProtect;
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.
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
539 static DWORD NaClflProtectRemoveExecute(DWORD flProtect) {
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;
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;
562 * TODO(mseaborn): Reduce duplication between this function and
563 * nacl::Map()/NaClMap().
565 uintptr_t NaClHostDescMap(struct NaClHostDesc *d,
566 struct NaClDescEffector *effp,
571 nacl_off64_t offset) {
578 DWORD flMappingProtect;
579 DWORD dwDesiredAccess;
583 DWORD dwMaximumSizeHigh;
584 DWORD dwMaximumSizeLow;
585 uintptr_t map_result;
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");
597 if (NULL != d && -1 == d->d) {
598 NaClLog(LOG_FATAL, "NaClHostDescMap: already closed\n");
600 if ((0 == (flags & NACL_ABI_MAP_SHARED)) ==
601 (0 == (flags & NACL_ABI_MAP_PRIVATE))) {
603 "NaClHostDescMap: exactly one of NACL_ABI_MAP_SHARED"
604 " and NACL_ABI_MAP_PRIVATE must be set.\n");
606 addr = (uintptr_t) start_addr;
607 prot &= NACL_ABI_PROT_MASK;
610 * Check that if FIXED, start_addr is not NULL.
611 * Use platform free address space locator to set start_addr if NULL and
614 if (0 == (flags & NACL_ABI_MAP_FIXED)) {
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.
620 if (!NaClFindAddressSpace(&addr, len)) {
622 "NaClHostDescMap: not fixed, and could not find space\n");
623 return (uintptr_t) -NACL_ABI_ENOMEM;
627 "NaClHostDescMap: NOT FIXED, found space at %"NACL_PRIxPTR"\n",
630 start_addr = (void *) addr;
636 if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
638 * anonymous memory must be free'able later via VirtualFree
640 NaClLog(3, "NaClHostDescMap: anonymous mapping\n");
642 flProtect = NaClflProtectMap(prot & (~PROT_EXEC));
643 NaClLog(3, "NaClHostDescMap: flProtect 0x%x\n", flProtect);
645 for (chunk_offset = 0;
647 chunk_offset += NACL_MAP_PAGESIZE) {
648 uintptr_t chunk_addr = addr + chunk_offset;
650 (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
653 "NaClHostDescMap: VirtualAlloc(0x%08x,,%x,%x)\n",
654 (void *) (addr + chunk_offset),
655 MEM_COMMIT | MEM_RESERVE,
657 map_result = (uintptr_t) VirtualAlloc((void *) chunk_addr,
659 MEM_COMMIT | MEM_RESERVE,
661 if (map_result != addr + chunk_offset) {
663 ("Could not VirtualAlloc anonymous memory at"
664 " addr 0x%08x with prot %x\n"),
665 addr + chunk_offset, flProtect);
668 NaClLog(3, "NaClHostDescMap: (anon) returning 0x%08"NACL_PRIxPTR"\n",
670 return (uintptr_t) start_addr;
674 desc_flags = NACL_ABI_O_RDWR;
676 desc_flags = d->flags;
679 if (0 != (desc_flags & NACL_ABI_O_APPEND) &&
680 0 != (prot & NACL_ABI_PROT_WRITE)) {
681 return (uintptr_t) -NACL_ABI_EACCES;
683 if (NACL_ABI_O_WRONLY == (desc_flags & NACL_ABI_O_ACCMODE)) {
684 return (uintptr_t) -NACL_ABI_EACCES;
686 NaClflProtectAndDesiredAccessMap(prot,
687 0 != (flags & NACL_ABI_MAP_PRIVATE),
688 (desc_flags & NACL_ABI_O_ACCMODE),
693 if (0 == flProtect) {
694 NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
695 return (uintptr_t) -NACL_ABI_EACCES;
698 "NaClHostDescMap: flMappingProtect 0x%x,"
699 " dwDesiredAccess 0x%x, flProtect 0x%x\n",
700 flMappingProtect, dwDesiredAccess, flProtect);
702 hFile = (HANDLE) _get_osfhandle(d->d);
703 dwMaximumSizeLow = 0;
704 dwMaximumSizeHigh = 0;
707 * Ensure consistency of the d->flMappingProtect access.
709 NaClFastMutexLock(&d->mu);
710 if (0 != d->flMappingProtect) {
711 flMappingProtect = d->flMappingProtect;
716 NaClFastMutexUnlock(&d->mu);
719 * Finite retry cycle. We can fallback from PAGE_EXECUTE_WRITECOPY to
720 * PAGE_EXECUTE_READ and from having executable permissions to not having
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?
729 hMap = CreateFileMapping(hFile,
735 if (NULL == hMap && retry_fallback) {
737 * PAGE_EXECUTE_WRITECOPY is not supported on Windows XP so we fallback
738 * to PAGE_EXECUTE_READ.
740 if (PAGE_EXECUTE_WRITECOPY == flMappingProtect) {
742 "NaClHostDescMap: CreateFileMapping failed, retrying with"
743 " PAGE_EXECUTE_READ instead of PAGE_EXECUTE_WRITECOPY\n");
744 flMappingProtect = PAGE_EXECUTE_READ;
747 if (0 == (prot & NACL_ABI_PROT_EXEC) &&
748 NaClflProtectHasExecute(flMappingProtect)) {
750 "NaClHostDescMap: CreateFileMapping failed, retrying without"
751 " execute permission. Original flMappingProtect 0x%x\n",
753 NaClflProtectAndDesiredAccessMap(prot & (~PROT_EXEC),
754 0 != (flags & NACL_ABI_MAP_PRIVATE),
755 (desc_flags & NACL_ABI_O_ACCMODE),
760 if (0 == flProtect) {
761 NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
762 return (uintptr_t) -NACL_ABI_EACCES;
764 flMappingProtect = NaClflProtectRemoveExecute(flMappingProtect);
766 "NaClHostDescMap: fallback flMappingProtect 0x%x,"
767 " dwDesiredAccess 0x%x, flProtect 0x%x\n",
768 flMappingProtect, dwDesiredAccess, flProtect);
772 "NaClHostDescMap: not retrying, since caller explicitly asked"
773 " for NACL_ABI_PROT_EXEC\n");
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
785 NaClFastMutexLock(&d->mu);
786 d->flMappingProtect = flMappingProtect;
787 NaClFastMutexUnlock(&d->mu);
792 DWORD err = GetLastError();
794 "NaClHostDescMap: CreateFileMapping failed: %d\n",
796 return -NaClXlateSystemError(err);
798 NaClLog(3, "NaClHostDescMap: CreateFileMapping got handle %d\n", (int) hMap);
799 NaClLog(3, "NaClHostDescMap: dwDesiredAccess 0x%x\n", dwDesiredAccess);
801 retval = (uintptr_t) -NACL_ABI_EINVAL;
803 for (chunk_offset = 0;
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;
811 (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
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;
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,
829 (void *) chunk_addr);
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();
838 "MapViewOfFileEx failed at 0x%08"NACL_PRIxPTR
839 ", got 0x%08"NACL_PRIxPTR", err %x\n",
844 if (!VirtualProtect((void *) map_result,
845 NaClRoundPage(chunk_size),
848 DWORD err = GetLastError();
850 "VirtualProtect failed at 0x%08x, err %x\n",
852 retval = (uintptr_t) -NaClXlateSystemError(err);
856 retval = (uintptr_t) start_addr;
858 (void) CloseHandle(hMap);
859 NaClLog(3, "NaClHostDescMap: returning %"NACL_PRIxPTR"\n", retval);
863 int NaClHostDescUnmapUnsafe(void *start_addr,
868 addr = (uintptr_t) start_addr;
870 for (off = 0; off < len; off += NACL_MAP_PAGESIZE) {
871 if (!UnmapViewOfFile((void *) (addr + off))) {
873 "NaClHostDescUnmap: UnmapViewOfFile(0x%08x) failed\n",
875 return -NACL_ABI_EINVAL;
881 static void NaClHostDescCtorIntern(struct NaClHostDesc *hd,
884 nacl_host_stat_t stbuf;
888 hd->flMappingProtect = 0;
889 if (_fstat64(posix_d, &stbuf) != 0) {
890 /* inherited non-fstat'able are IPC channels, e.g., for bootstrap channel */
892 "NaClHostDescCtorIntern: could not _fstat64,"
893 " assuming non-seekable\n");
894 hd->protect_filepos = 0;
896 int file_type = stbuf.st_mode & S_IFMT;
898 * Inherited stdio are console handles and are not seekable.
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
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) ==
912 if (!NaClFastMutexCtor(&hd->mu)) {
913 NaClLog(LOG_FATAL, "NaClHostDescCtorIntern: NaClFastMutexCtor failed\n");
917 int NaClHostDescOpen(struct NaClHostDesc *d,
921 DWORD dwDesiredAccess;
922 DWORD dwCreationDisposition;
923 DWORD dwFlagsAndAttributes;
925 int truncate_after_open = 0;
931 NaClLog(LOG_FATAL, "NaClHostDescOpen: 'this' is NULL\n");
935 * Sanitize access flags.
937 if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
938 return -NACL_ABI_EINVAL;
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;
946 case NACL_ABI_O_RDWR:
947 oflags = _O_RDWR | _O_BINARY;
948 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
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;
956 "NaClHostDescOpen: bad access flags 0x%x.\n",
958 return -NACL_ABI_EINVAL;
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.
965 if (0 == (perms & NACL_ABI_S_IWUSR)) {
966 dwFlagsAndAttributes = (FILE_ATTRIBUTE_READONLY |
967 FILE_FLAG_POSIX_SEMANTICS);
969 dwFlagsAndAttributes = (FILE_ATTRIBUTE_NORMAL |
970 FILE_FLAG_POSIX_SEMANTICS);
973 * Postcondition: flags & NACL_ABI_O_ACCMODE is one of the three
976 switch (flags & (NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC)) {
978 dwCreationDisposition = OPEN_EXISTING;
980 case NACL_ABI_O_CREAT:
981 dwCreationDisposition = OPEN_ALWAYS;
983 case NACL_ABI_O_TRUNC:
984 dwCreationDisposition = TRUNCATE_EXISTING;
985 truncate_after_open = 1;
987 case NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC:
988 dwCreationDisposition = OPEN_ALWAYS;
989 truncate_after_open = 1;
991 if (0 != (flags & NACL_ABI_O_APPEND)) {
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);
1001 hFile = CreateFileA(path, dwDesiredAccess,
1002 (FILE_SHARE_DELETE |
1006 dwCreationDisposition,
1007 dwFlagsAndAttributes,
1009 if (INVALID_HANDLE_VALUE == hFile) {
1010 err = GetLastError();
1011 NaClLog(3, "NaClHostDescOpen: CreateFile failed %d\n", err);
1012 return -NaClXlateSystemError(err);
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();
1020 "NaClHostDescOpen: could not truncate file:"
1021 " last error %d.\n",
1023 if (err == ERROR_USER_MAPPED_FILE) {
1025 "NaClHostDescOpen: this is due to an existing mapping"
1026 " of the same file.\n");
1030 fd = _open_osfhandle((intptr_t) hFile, oflags);
1032 * oflags _O_APPEND, _O_RDONLY, and _O_TEXT are meaningful; unclear
1033 * whether _O_RDWR, _O_WRONLY, etc has any effect.
1036 NaClLog(LOG_FATAL, "NaClHostDescOpen failed: err %d\n",
1039 NaClHostDescCtorIntern(d, fd, flags);
1043 int NaClHostDescPosixDup(struct NaClHostDesc *d,
1048 NaClLog(3, "NaClHostDescPosixDup(0x%08x, %d, 0%o)\n",
1049 (uintptr_t) d, posix_d, flags);
1051 NaClLog(LOG_FATAL, "NaClHostDescPosixDup: 'this' is NULL\n");
1054 * Sanitize access flags.
1056 if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1057 return -NACL_ABI_EINVAL;
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:
1066 "NaClHostDescOpen: bad access flags 0x%x.\n",
1068 return -NACL_ABI_EINVAL;
1071 host_desc = _dup(posix_d);
1072 if (-1 == host_desc) {
1075 NaClHostDescCtorIntern(d, host_desc, flags);
1079 int NaClHostDescPosixTake(struct NaClHostDesc *d,
1083 NaClLog(LOG_FATAL, "NaClHostDescPosixTake: 'this' is NULL\n");
1086 * Sanitize access flags.
1088 if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1089 return -NACL_ABI_EINVAL;
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:
1098 "NaClHostDescOpen: bad access flags 0x%x.\n",
1100 return -NACL_ABI_EINVAL;
1102 NaClHostDescCtorIntern(d, posix_d, flags);
1106 ssize_t NaClHostDescRead(struct NaClHostDesc *d,
1109 /* Windows ReadFile only supports DWORD, so we need
1110 * to clamp the length. */
1111 unsigned int actual_len;
1113 DWORD bytes_received;
1116 if (len < UINT_MAX) {
1117 actual_len = (unsigned int) len;
1119 actual_len = UINT_MAX;
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;
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.
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.
1145 fh = (HANDLE) _get_osfhandle(d->d);
1146 CHECK(INVALID_HANDLE_VALUE != fh);
1149 * Ensure that we do not corrupt shared implicit file position.
1151 if (d->protect_filepos) {
1152 NaClTakeFilePosLock(fh);
1154 if (!ReadFile(fh, buf, actual_len, &bytes_received, NULL)) {
1155 err = GetLastError();
1156 if (ERROR_HANDLE_EOF == err) {
1159 NaClLog(4, "NaClHostDescRead: ReadFile error %d\n", err);
1160 bytes_received = -NaClXlateSystemError(err);
1163 if (d->protect_filepos) {
1164 NaClDropFilePosLock(fh);
1167 return bytes_received;
1170 ssize_t NaClHostDescWrite(struct NaClHostDesc *d,
1174 * Windows WriteFile only supports DWORD uint, so we need to clamp
1177 unsigned int actual_len;
1179 DWORD bytes_written;
1182 OVERLAPPED *overlap_ptr = NULL;
1184 if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1185 NaClLog(3, "NaClHostDescWrite: RDONLY file\n");
1186 return -NACL_ABI_EBADF;
1188 if (len < UINT_MAX) {
1189 actual_len = (unsigned int) len;
1191 actual_len = UINT_MAX;
1194 NaClHostDescCheckValidity("NaClHostDescWrite", d);
1196 * See discussion in NaClHostDescRead above wrt why we use WriteFile
1197 * instead of _write below.
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;
1206 fh = (HANDLE) _get_osfhandle(d->d);
1207 CHECK(INVALID_HANDLE_VALUE != fh);
1209 * Ensure that we do not corrupt shared implicit file position.
1211 if (d->protect_filepos) {
1212 NaClTakeFilePosLock(fh);
1214 if (!WriteFile(fh, buf, actual_len, &bytes_written, overlap_ptr)) {
1215 err = GetLastError();
1216 NaClLog(4, "NaClHostDescWrite: WriteFile error %d\n", err);
1218 bytes_written = -NaClXlateSystemError(err);
1220 if (d->protect_filepos) {
1221 NaClDropFilePosLock(fh);
1224 return bytes_written;
1227 nacl_off64_t NaClHostDescSeek(struct NaClHostDesc *d,
1228 nacl_off64_t offset,
1231 nacl_off64_t retval;
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);
1239 retval = _lseeki64(d->d, offset, whence);
1240 if (d->protect_filepos) {
1241 NaClDropFilePosLock(hFile);
1243 return (-1 == retval) ? -errno : retval;
1246 ssize_t NaClHostDescPRead(struct NaClHostDesc *d,
1249 nacl_off64_t offset) {
1252 DWORD bytes_received;
1254 nacl_off64_t orig_pos = 0;
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;
1262 return -NACL_ABI_EINVAL;
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.
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) {
1279 if (d->protect_filepos) {
1280 orig_pos = NaClLockAndGetCurrentFilePos(fh);
1282 if (!ReadFile(fh, buf, (DWORD) len, &bytes_received, &overlap)) {
1283 err = GetLastError();
1284 if (ERROR_HANDLE_EOF == err) {
1286 /* handle as if returned true. */
1288 NaClLog(4, "NaClHostDescPRead: ReadFile failed, error %d\n", err);
1289 NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1290 bytes_received = -NaClXlateSystemError(err);
1293 if (d->protect_filepos) {
1294 NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1296 return bytes_received;
1299 ssize_t NaClHostDescPWrite(struct NaClHostDesc *d,
1302 nacl_off64_t offset) {
1307 nacl_off64_t orig_pos = 0;
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;
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
1321 return -NACL_ABI_EINVAL;
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) {
1331 if (d->protect_filepos) {
1332 orig_pos = NaClLockAndGetCurrentFilePos(fh);
1334 if (!WriteFile(fh, buf, (DWORD) len, &bytes_sent, &overlap)) {
1335 err = GetLastError();
1336 if (ERROR_HANDLE_EOF == err) {
1338 /* handle as if returned true. */
1341 "NaClHostDescPWrite: WriteFile failed, error %d\n", err);
1342 bytes_sent = -NaClXlateSystemError(err);
1345 if (d->protect_filepos) {
1346 NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
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) {
1361 int NaClHostDescIsatty(struct NaClHostDesc *d) {
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;
1370 int NaClHostDescClose(struct NaClHostDesc *d) {
1373 NaClHostDescCheckValidity("NaClHostDescClose", d);
1375 retval = _close(d->d);
1381 NaClFastMutexDtor(&d->mu);
1386 * This is not a host descriptor function, but is closely related to
1387 * fstat and should behave similarly.
1389 int NaClHostDescStat(char const *path, nacl_host_stat_t *nhsp) {
1390 if (NACL_HOST_STAT64(path, nhsp) == -1) {
1397 int NaClHostDescMkdir(const char *path, int mode) {
1398 UNREFERENCED_PARAMETER(mode);
1399 if (_mkdir(path) != 0)
1400 return -NaClXlateErrno(errno);
1404 int NaClHostDescRmdir(const char *path) {
1405 if (_rmdir(path) != 0)
1406 return -NaClXlateErrno(errno);
1410 int NaClHostDescChdir(const char *path) {
1411 if (_chdir(path) != 0)
1412 return -NaClXlateErrno(errno);
1416 int NaClHostDescGetcwd(char *path, size_t len) {
1417 if (_getcwd(path, (int) len) == NULL)
1418 return -NaClXlateErrno(errno);
1422 int NaClHostDescUnlink(const char *path) {
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.
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);
1435 if (_unlink(path) != 0)
1436 return -NaClXlateErrno(errno);
1441 int NaClHostDescTruncate(char const *path, nacl_abi_off_t length) {
1442 LARGE_INTEGER win_length;
1445 HANDLE hfile = CreateFileA(path,
1446 GENERIC_READ | GENERIC_WRITE,
1447 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1450 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS,
1453 if (INVALID_HANDLE_VALUE == hfile) {
1454 err = GetLastError();
1455 NaClLog(3, "NaClHostDescTruncate: CreateFile failed %d\n", err);
1456 return -NaClXlateSystemError(err);
1459 win_length.QuadPart = length;
1460 if (!SetFilePointerEx(hfile, win_length, NULL, FILE_BEGIN)) {
1461 err = GetLastError();
1463 "NaClHostDescTruncate: SetFilePointerEx failed:"
1464 " last error %d.\n", err);
1465 return -NaClXlateSystemError(err);
1468 if (!SetEndOfFile(hfile)) {
1469 err = GetLastError();
1471 "NaClHostDescTruncate: could not truncate file:"
1472 " last error %d.\n", err);
1473 if (err == ERROR_USER_MAPPED_FILE) {
1475 "NaClHostDescTruncate: this is due to an existing"
1476 " mapping of the same file.\n");
1478 return -NaClXlateSystemError(err);
1484 int NaClHostDescLstat(char const *path, nacl_host_stat_t *nhsp) {
1486 * Since symlinks don't exist on windows, stat() and lstat()
1489 return NaClHostDescStat(path, nhsp);
1492 int NaClHostDescLink(const char *oldpath, const char *newpath) {
1494 * Hard linking not implemented for win32
1496 NaClLog(1, "NaClHostDescLink: hard linking not supported on windows.\n");
1497 return -NACL_ABI_ENOSYS;
1500 int NaClHostDescRename(const char *oldpath, const char *newpath) {
1501 if (rename(oldpath, newpath) != 0)
1502 return -NaClXlateErrno(errno);
1506 int NaClHostDescSymlink(const char *oldpath, const char *newpath) {
1508 * Symlinks are not supported on win32.
1510 NaClLog(1, "NaClHostDescSymlink: symbolic links not supported on windows.\n");
1511 return -NACL_ABI_ENOSYS;
1514 int NaClHostDescChmod(const char *path, nacl_abi_mode_t mode) {
1515 if (_chmod(path, NaClMapMode(mode)) != 0)
1516 return -NaClXlateErrno(errno);
1520 int NaClHostDescAccess(const char *path, int amode) {
1521 if (_access(path, NaClMapAccessMode(amode)) != 0)
1522 return -NaClXlateErrno(errno);
1526 int NaClHostDescReadlink(const char *path, char *buf, size_t bufsize) {
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.
1533 "NaClHostDescReadlink: symbolic links not supported on Windows.\n");
1534 return -NACL_ABI_EINVAL;