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. Directory descriptor / Handle abstraction.
9 */
10 #include "native_client/src/include/portability.h"
11 #include <windows.h>
12 #include <io.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <share.h>
17 #include <stddef.h>
18
19 #include "native_client/src/include/nacl_platform.h"
20 #include "native_client/src/shared/platform/nacl_check.h"
21 #include "native_client/src/shared/platform/nacl_host_desc.h"
22 #include "native_client/src/shared/platform/nacl_host_dir.h"
23 #include "native_client/src/shared/platform/nacl_log.h"
24 #include "native_client/src/shared/platform/nacl_sync.h"
25 #include "native_client/src/shared/platform/nacl_sync_checked.h"
26 #include "native_client/src/shared/platform/win/xlate_system_error.h"
27
28 #include "native_client/src/trusted/service_runtime/internal_errno.h"
29
30 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
31 #include "native_client/src/trusted/service_runtime/include/sys/dirent.h"
32 #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
33 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
34 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
35
36
37 #define SSIZE_T_MAX ((ssize_t) ((~(size_t) 0) >> 1))
38
39
NaClHostDirInit(struct NaClHostDir * d)40 static int NaClHostDirInit(struct NaClHostDir *d) {
41 int retval;
42
43 d->handle = FindFirstFile(d->pattern, &d->find_data);
44 d->off = 0;
45 d->done = 0;
46
47 if (INVALID_HANDLE_VALUE != d->handle) {
48 retval = 0;
49 } else {
50 int win_error = GetLastError();
51 NaClLog(LOG_ERROR, "NaClHostDirInit: failed: %d\n", win_error);
52 if (ERROR_NO_MORE_FILES == win_error) {
53 d->done = 1;
54 retval = 0;
55 } else if (ERROR_PATH_NOT_FOUND == win_error) {
56 retval = -NACL_ABI_ENOTDIR;
57 } else {
58 /* TODO(sehr): fix the errno handling */
59 retval = -NaClXlateSystemError(win_error);
60 }
61 }
62 return retval;
63 }
64
NaClHostDirOpen(struct NaClHostDir * d,char * path)65 int NaClHostDirOpen(struct NaClHostDir *d,
66 char *path) {
67 int err;
68 int retval;
69 const char *prefix = path;
70
71 if (NULL == d) {
72 NaClLog(LOG_FATAL, "NaClHostDirOpen: 'this' is NULL\n");
73 }
74
75 if (strcmp(path, "/") == 0 || strcmp(path, "\\") == 0) {
76 prefix = "";
77 }
78
79 /**
80 * "path" is an 8-bit char string. Convert to UTF-16 here.
81 * Using Microsoft "Secure CRT" snprintf. Passing _TRUNCATE
82 * instructs the runtime to truncate and return -1 if the
83 * buffer is too small, rather than the default behavior of
84 * dumping core.
85 *
86 * NOTE: %hs specifies a single-byte-char string.
87 */
88 err = _snwprintf_s(d->pattern,
89 NACL_ARRAY_SIZE(d->pattern),
90 _TRUNCATE, L"%hs\\*.*", prefix);
91 if (err < 0) {
92 return -NACL_ABI_EOVERFLOW;
93 }
94 if (!NaClMutexCtor(&d->mu)) {
95 return -NACL_ABI_ENOMEM;
96 }
97
98 retval = NaClHostDirInit(d);
99 if (0 != retval) {
100 NaClMutexDtor(&d->mu);
101 }
102 return retval;
103 }
104
NaClHostDirGetdents(struct NaClHostDir * d,void * buf,size_t len)105 ssize_t NaClHostDirGetdents(struct NaClHostDir *d,
106 void *buf,
107 size_t len) {
108 struct nacl_abi_dirent volatile *p;
109 size_t i;
110 ssize_t retval;
111
112 if (NULL == d) {
113 NaClLog(LOG_FATAL, "NaClHostDirGetdents: 'this' is NULL\n");
114 }
115 NaClLog(3, "NaClHostDirGetdents(0x%08x, %u):\n", buf, len);
116 if (len > SSIZE_T_MAX) {
117 NaClLog(3, "Clamping to len SSIZE_T_MAX\n");
118 len = SSIZE_T_MAX;
119 }
120
121 NaClXMutexLock(&d->mu);
122
123 p = (struct nacl_abi_dirent *) buf;
124 i = 0;
125
126 /*
127 * d->off is currently the record number, assuming that FindNextFile
128 * output order is deterministic and consistent.
129 */
130 while (1) {
131 /**
132 * The FIND_DATA structure contains the filename as a UTF-16
133 * string of length MAX_PATH. This may or may not convert into an
134 * 8-bit char string of similar length. The safe thing to do here
135 * is to assume the worst case: every UTF-16 character expands to
136 * a four-byte MBCS sequence. This should default to CP_ACP (ANSI
137 * code page).
138 *
139 * TODO(bsy,sehr): consider using WideCharToMultiByte (and
140 * MultiByteToWideChar before invoking _s_open_s in
141 * NaClHostDescOpen) with CP_UTF8 to always present UTF8 to
142 * untrusted application code. NB: MB_ERR_INVALID_CHARS is needed
143 * since otherwise we have an issue with silently dropping invalid
144 * Unicode code points that can cause file name aliasing.
145 *
146 * http://code.google.com/p/nativeclient/issues/detail?id=2725
147 *
148 * NB: Keep in mind that MAX_PATH is an API limitation, not a
149 * limitation of the underlying filesystem.
150 */
151 char name_mbcs[(MAX_PATH * 4) + 1];
152 size_t name_length;
153 size_t rec_length;
154 uint16_t nacl_abi_rec_length;
155 int err;
156
157 /* Handle case where NaClHostDirRewind() failed. */
158 if (d->handle == INVALID_HANDLE_VALUE) {
159 retval = -NACL_ABI_ENOENT;
160 goto done;
161 }
162
163 if (d->done) {
164 retval = 0;
165 goto done;
166 }
167
168 err = _snprintf_s(name_mbcs,
169 _countof(name_mbcs),
170 _TRUNCATE,
171 "%ws", d->find_data.cFileName);
172 if (err < 0) {
173 retval = -NACL_ABI_EOVERFLOW;
174 goto done;
175 }
176 name_length = strlen(name_mbcs) + 1;
177 rec_length = (offsetof(struct nacl_abi_dirent, nacl_abi_d_name)
178 + (name_length + 3))
179 & ~3;
180
181 /* Check for overflow in record length */
182 nacl_abi_rec_length = (uint16_t) rec_length;
183 if (rec_length > (size_t) nacl_abi_rec_length) {
184 /*
185 * Can there be file names that are longer than 64K? Windows
186 * API docs say that 1023 is the maximum file name length, so
187 * with a 4x expansion we should only get 4092 + 1 or 4093
188 * bytes. But this may be filesystem dependent....
189 */
190 retval = -NACL_ABI_EOVERFLOW;
191 goto done;
192 }
193
194 CHECK(rec_length <= SSIZE_T_MAX - i);
195 /*
196 * Should never happen, since len is clamped to SSIZE_T_MAX.
197 */
198
199 if (i + rec_length >= len) {
200 /*
201 * Insufficent buffer space! Check if any entries have been
202 * copied...
203 */
204 if (0 == i) {
205 retval = (ssize_t) -NACL_ABI_EINVAL;
206 } else {
207 retval = (ssize_t) i;
208 }
209 goto done;
210 }
211
212 p = (struct nacl_abi_dirent volatile *) (((char *) buf) + i);
213 p->nacl_abi_d_ino = NACL_FAKE_INODE_NUM; /* windows doesn't do inodes */
214 p->nacl_abi_d_off = d->off;
215 p->nacl_abi_d_reclen = nacl_abi_rec_length;
216 memcpy((char *) p->nacl_abi_d_name, name_mbcs, name_length);
217 i += nacl_abi_rec_length;
218 ++d->off;
219
220 if (!FindNextFile(d->handle, &d->find_data)) {
221 int win_err = GetLastError();
222 if (win_err == ERROR_NO_MORE_FILES) {
223 d->done = 1;
224 retval = (ssize_t) i;
225 goto done;
226 } else {
227 retval = -NaClXlateSystemError(win_err);
228 goto done;
229 }
230 }
231 }
232 done:
233 NaClXMutexUnlock(&d->mu);
234 return retval;
235 }
236
NaClHostDirRewind(struct NaClHostDir * d)237 int NaClHostDirRewind(struct NaClHostDir *d) {
238 int retval;
239 if (NULL == d) {
240 NaClLog(LOG_FATAL, "NaClHostDirRewind: 'this' is NULL\n");
241 }
242
243 NaClXMutexLock(&d->mu);
244
245 /* Close the handle and reopen it at the beginning. */
246 if (!FindClose(d->handle)) {
247 /*
248 * It's not clear why FindClose() would fail. Abort because we
249 * don't want to leave d->handle in an undefined state.
250 */
251 NaClLog(LOG_FATAL, "NaClHostDirRewind(): FindClose() failed\n");
252 }
253
254 retval = NaClHostDirInit(d);
255 if (retval != 0) {
256 /*
257 * If FindFirstFile fails for some reason mark the handle as invalid so that
258 * future calls to NaClHostDirGetdents can report the error.
259 */
260 d->handle = INVALID_HANDLE_VALUE;
261 }
262
263 NaClXMutexUnlock(&d->mu);
264 return retval;
265 }
266
NaClHostDirClose(struct NaClHostDir * d)267 int NaClHostDirClose(struct NaClHostDir *d) {
268 if (NULL == d) {
269 NaClLog(LOG_FATAL, "NaClHostDirClose: 'this' is NULL\n");
270 }
271 NaClMutexDtor(&d->mu);
272 if (!FindClose(d->handle)) {
273 return -NaClXlateSystemError(GetLastError());
274 }
275 return 0;
276 }
277
NaClHostDirFchdir(struct NaClHostDir * d)278 int NaClHostDirFchdir(struct NaClHostDir *d) {
279 NaClLog(1, "NaClHostDirFchdir Not yet implemented.\n");
280
281 return -NACL_ABI_ENOSYS;
282 }
283
NaClHostDirFchmod(struct NaClHostDir * d,int mode)284 int NaClHostDirFchmod(struct NaClHostDir *d, int mode) {
285 NaClLog(1, "NaClHostDirFchmod Not yet implemented.\n");
286
287 return -NACL_ABI_ENOSYS;
288 }
289
NaClHostDirFsync(struct NaClHostDir * d)290 int NaClHostDirFsync(struct NaClHostDir *d) {
291 DWORD err;
292
293 if (!FlushFileBuffers(d->handle)) {
294 err = GetLastError();
295 return -NaClXlateSystemError(err);
296 }
297
298 return 0;
299 }
300
NaClHostDirFdatasync(struct NaClHostDir * d)301 int NaClHostDirFdatasync(struct NaClHostDir *d) {
302 DWORD err;
303
304 if (!FlushFileBuffers(d->handle)) {
305 err = GetLastError();
306 return -NaClXlateSystemError(err);
307 }
308
309 return 0;
310 }
311