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