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