1 /*
2 * Copyright (c) 2011 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 * Note that we avoid using the thread-specific data / thread local
11 * storage access to the "errno" variable, and instead use the raw
12 * system call return interface of small negative numbers as errors.
13 */
14
15 #include <dirent.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <limits.h>
19 #include <linux/types.h>
20 #include <linux/unistd.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28
29 #include "native_client/src/shared/platform/nacl_check.h"
30 #include "native_client/src/shared/platform/nacl_host_desc.h"
31 #include "native_client/src/shared/platform/nacl_host_dir.h"
32 #include "native_client/src/shared/platform/nacl_log.h"
33 #include "native_client/src/shared/platform/nacl_sync.h"
34 #include "native_client/src/shared/platform/nacl_sync_checked.h"
35
36 #include "native_client/src/trusted/service_runtime/nacl_config.h"
37
38 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
39 #include "native_client/src/trusted/service_runtime/include/sys/dirent.h"
40 #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
41 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
42 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
43
44 #ifdef _syscall3
45 _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count)
46
47 int getdents(unsigned int fd, struct dirent* dirp, unsigned int count);
48 #else
49 # include <sys/syscall.h>
getdents(unsigned int fd,struct dirent * dirp,unsigned int count)50 int getdents(unsigned int fd, struct dirent* dirp, unsigned int count) {
51 return syscall(__NR_getdents, fd, dirp, count);
52 }
53 #endif
54
55 struct linux_dirent { /* offsets, ILP32 and LP64 */
56 unsigned long d_ino; /* 0, 0 */
57 unsigned long d_off; /* 4, 8 */
58 unsigned short d_reclen; /* 8, 16 */
59 char d_name[1]; /* 10, 18 */
60 /* actual length is d_reclen - 2 - offsetof(struct linux_dirent, d_name) */
61 /*
62 * char pad; / Zero padding byte
63 * char d_type; / File type (only since Linux 2.6.4; offset is d_reclen - 1)
64 */
65 };
66
67
68 /*
69 * from native_client/src/trusted/service_runtime/include/sys/dirent.h:
70
71 struct nacl_abi_dirent { offsets, NaCl
72 nacl_abi_ino_t nacl_abi_d_ino; 0
73 nacl_abi_off_t nacl_abi_d_off; 8
74 uint16_t nacl_abi_d_reclen; 16
75 char nacl_abi_d_name[NACL_ABI_MAXNAMLEN + 1]; 18
76 };
77
78 * It would be nice if we didn't have to buffer dirent data. Is it
79 * possible, when the untrusted NaCl module invokes getdent, to
80 * determine a (different) buffer size value to use with the host OS
81 * (Linux)?
82 *
83 * Since the actual length is d_reclen, do we know if d_reclen is
84 * padded out to a multiple of the alignment restriction for longs?
85 * Best to assume that either may hold. On x86 where unaligned
86 * accesses are okay, this might be fine; on other architectures where
87 * it isn't, then presumably d_reclen will include the padding, and
88 * d_type (which we don't use) is on the far end of the padding.
89 *
90 * Given a user-supplied buffer of N bytes to hold nacl_abi_dirent
91 * entries, what is the size of the buffer that should be supplied to
92 * the Linux kernel so that we will never end up with more information
93 * than we can copy / return back to the user?
94 *
95 * For LP64, the answer is relatively simple: everything is the same
96 * size, except that the kernel is going to use one more byte for the
97 * d_type entry. Since copying to the nacl_abi_dirent omits that, the
98 * transfer shrinks the space needed.
99 *
100 * For ILP32, the answer is a little harder. The linux_dirent use 8
101 * fewer bytes for the entries before d_name, but one more at the end
102 * for d_type. So, when does the worst case expansion occur? The
103 * number of dirent entries is multiplied by the expansion, so we need
104 * to determine the smallest dirent entry. Assuming single character
105 * file names, a user's buffer of size N can hold int(N/20) dirents.
106 * The linux_dirent, with no alignment pads, but with d_type (we
107 * assume newer kernels only), will take 13 bytes per entry, so we had
108 * better not claim to have more than 13*int(N/20) bytes of space
109 * available to the Linux kernel. (We don't need to check in our
110 * platform qualification check since "older" kernels are pre 2.6.4,
111 * and all reasonable Linux distributions use newer kernels.)
112 *
113 * Suppose the user gave us a buffer of 40 bytes. This is enough for
114 * two dirent structures containing information about files each with
115 * a single character name. So, we invoke Linux's getdents with a 26
116 * byte buffer. What happens if the next actual directory entry's
117 * name is 40-18-1=21 bytes long? It would have fit in the user's
118 * buffer, but the linux_dirent buffer required for that entry is
119 * 10+21+2 bytes in length, or 33 bytes. We supplied linux with a 26
120 * byte buffer, so it should respond with EINVAL since the buffer
121 * space is too small.
122 *
123 * This argues against a simple scheme that avoids buffering.
124 *
125 * We could, when we encounter EINVAL, increase the buffer size used
126 * with the host OS. How do we expand and could that might result in
127 * too much being read in?
128 */
129
NaClHostDirCtor(struct NaClHostDir * d,int dir_desc)130 int NaClHostDirCtor(struct NaClHostDir *d,
131 int dir_desc) {
132 if (!NaClMutexCtor(&d->mu)) {
133 return -NACL_ABI_ENOMEM;
134 }
135 d->fd = dir_desc;
136 d->cur_byte = 0;
137 d->nbytes = 0;
138 NaClLog(3, "NaClHostDirCtor: success.\n");
139 return 0;
140 }
141
NaClHostDirOpen(struct NaClHostDir * d,char * path)142 int NaClHostDirOpen(struct NaClHostDir *d,
143 char *path) {
144 int fd;
145 struct stat stbuf;
146 int rv;
147
148 NaClLog(3, "NaClHostDirOpen(0x%08"NACL_PRIxPTR", %s)\n", (uintptr_t) d, path);
149 if (NULL == d) {
150 NaClLog(LOG_FATAL, "NaClHostDirOpen: 'this' is NULL\n");
151 }
152
153 NaClLog(3, "NaClHostDirOpen: invoking open(%s)\n", path);
154 fd = open(path, O_RDONLY);
155 NaClLog(3, "NaClHostDirOpen: got DIR* %d\n", fd);
156 if (-1 == fd) {
157 NaClLog(LOG_ERROR,
158 "NaClHostDirOpen: open returned -1, errno %d\n", errno);
159 return -NaClXlateErrno(errno);
160 }
161 /* check that it is really a directory */
162 if (-1 == fstat(fd, &stbuf)) {
163 NaClLog(LOG_ERROR,
164 "NaClHostDirOpen: fstat failed?!? errno %d\n", errno);
165 (void) close(fd);
166 return -NaClXlateErrno(errno);
167 }
168 if (!S_ISDIR(stbuf.st_mode)) {
169 (void) close(fd);
170 return -NACL_ABI_ENOTDIR;
171 }
172 rv = NaClHostDirCtor(d, fd);
173 return rv;
174 }
175
176 /*
177 * Copy and translate a single linux_dirent to nacl_abi_dirent.
178 * Returns number of bytes consumed (includes alignment adjustment for
179 * next entry).
180 *
181 * TODO(bsy): add filesystem info argument to specify which
182 * directories are "root" inodes, to rewrite the inode number of '..'
183 * as appropriate.
184 */
NaClCopyDirent(struct NaClHostDir * d,void * buf,size_t len)185 static ssize_t NaClCopyDirent(struct NaClHostDir *d,
186 void *buf,
187 size_t len) {
188 struct linux_dirent *ldp = (struct linux_dirent *) (
189 d->dirent_buf
190 + d->cur_byte);
191 struct nacl_abi_dirent volatile *nadp;
192 size_t adjusted_size;
193
194 /* make sure the buffer is aligned */
195 CHECK(0 == ((sizeof(nacl_abi_ino_t) - 1) & (uintptr_t) buf));
196
197 if (d->cur_byte == d->nbytes) {
198 return 0; /* none available */
199 }
200 CHECK(d->cur_byte < d->nbytes);
201 CHECK(ldp->d_reclen <= d->nbytes - d->cur_byte);
202 /* no partial record transferred. */
203
204 nadp = (struct nacl_abi_dirent volatile *) buf;
205
206 /*
207 * is there enough space? assume Linux is sane, so no ssize_t
208 * overflow in the adjusted_size computation. (NAME_MAX is small.)
209 */
210 CHECK(NAME_MAX < 256);
211 adjusted_size = offsetof(struct nacl_abi_dirent, nacl_abi_d_name)
212 + strlen(ldp->d_name) + 1; /* NUL termination */
213 /* pad for alignment for access to d_ino */
214 adjusted_size = (adjusted_size + (sizeof(nacl_abi_ino_t) - 1))
215 & ~(sizeof(nacl_abi_ino_t) - 1);
216 if (len < adjusted_size) {
217 return -NACL_ABI_EINVAL; /* result buffer is too small */
218 }
219
220 nadp->nacl_abi_d_ino = ldp->d_ino;
221 nadp->nacl_abi_d_off = ldp->d_off;
222 nadp->nacl_abi_d_reclen = adjusted_size;
223 NaClLog(4, "NaClCopyDirent: %s\n", ldp->d_name);
224 strcpy((char *) nadp->nacl_abi_d_name, ldp->d_name);
225 /* NB: some padding bytes may not get overwritten */
226
227 d->cur_byte += ldp->d_reclen;
228
229 NaClLog(4, "NaClCopyDirent: returning %"NACL_PRIuS"\n", adjusted_size);
230 return (ssize_t) adjusted_size;
231 }
232
NaClStreamDirents(struct NaClHostDir * d,void * buf,size_t len)233 static ssize_t NaClStreamDirents(struct NaClHostDir *d,
234 void *buf,
235 size_t len) {
236 ssize_t retval;
237 size_t xferred = 0;
238 ssize_t entry_size;
239
240 NaClXMutexLock(&d->mu);
241 while (len > 0) {
242 NaClLog(4, "NaClStreamDirents: loop, xferred = %"NACL_PRIuS"\n", xferred);
243 entry_size = NaClCopyDirent(d, buf, len);
244 if (0 == entry_size) {
245 CHECK(d->cur_byte == d->nbytes);
246 retval = getdents(d->fd,
247 (struct dirent *) d->dirent_buf,
248 sizeof d->dirent_buf);
249 if (-1 == retval) {
250 if (xferred > 0) {
251 /* next time through, we'll pick up the error again */
252 goto cleanup;
253 } else {
254 xferred = -NaClXlateErrno(errno);
255 goto cleanup;
256 }
257 } else if (0 == retval) {
258 goto cleanup;
259 }
260 d->cur_byte = 0;
261 d->nbytes = retval;
262 } else if (entry_size < 0) {
263 /*
264 * The only error return from NaClCopyDirent is NACL_ABI_EINVAL
265 * due to destinaton buffer too small for the current entry. If
266 * we had copied some entries before, we were successful;
267 * otherwise report that the buffer is too small for the next
268 * directory entry.
269 */
270 if (xferred > 0) {
271 goto cleanup;
272 } else {
273 xferred = entry_size;
274 goto cleanup;
275 }
276 }
277 /* entry_size > 0, maybe copy another */
278 buf = (void *) ((char *) buf + entry_size);
279 CHECK(len >= (size_t) entry_size);
280 len -= entry_size;
281 xferred += entry_size;
282 }
283 /* perfect fit! */
284 cleanup:
285 NaClXMutexUnlock(&d->mu);
286 return xferred;
287 }
288
NaClHostDirGetdents(struct NaClHostDir * d,void * buf,size_t len)289 ssize_t NaClHostDirGetdents(struct NaClHostDir *d,
290 void *buf,
291 size_t len) {
292 int retval;
293
294 if (NULL == d) {
295 NaClLog(LOG_FATAL, "NaClHostDirGetdents: 'this' is NULL\n");
296 }
297 NaClLog(3, "NaClHostDirGetdents(0x%08"NACL_PRIxPTR", %"NACL_PRIuS"):\n",
298 (uintptr_t) buf, len);
299
300 if (0 != ((__alignof__(struct nacl_abi_dirent) - 1) & (uintptr_t) buf)) {
301 retval = -NACL_ABI_EINVAL;
302 goto cleanup;
303 }
304
305 retval = NaClStreamDirents(d, buf, len);
306 cleanup:
307 NaClLog(3, "NaClHostDirGetdents: returned %d\n", retval);
308 return retval;
309 }
310
NaClHostDirRewind(struct NaClHostDir * d)311 int NaClHostDirRewind(struct NaClHostDir *d) {
312 if (NULL == d) {
313 NaClLog(LOG_FATAL, "NaClHostDirRewind: 'this' is NULL\n");
314 }
315 return -NaClXlateErrno(lseek64(d->fd, 0, SEEK_SET));
316 }
317
NaClHostDirClose(struct NaClHostDir * d)318 int NaClHostDirClose(struct NaClHostDir *d) {
319 int retval;
320
321 if (NULL == d) {
322 NaClLog(LOG_FATAL, "NaClHostDirClose: 'this' is NULL\n");
323 }
324 NaClLog(3, "NaClHostDirClose(%d)\n", d->fd);
325 retval = close(d->fd);
326 d->fd = -1;
327 NaClMutexDtor(&d->mu);
328 return (-1 == retval) ? -NaClXlateErrno(errno) : retval;
329 }
330
NaClHostDirFchdir(struct NaClHostDir * d)331 int NaClHostDirFchdir(struct NaClHostDir *d) {
332 if (-1 == fchdir(d->fd)) {
333 return -NaClXlateErrno(errno);
334 }
335 return 0;
336 }
337
NaClHostDirFchmod(struct NaClHostDir * d,int mode)338 int NaClHostDirFchmod(struct NaClHostDir *d, int mode) {
339 if (-1 == fchmod(d->fd, mode)) {
340 return -NaClXlateErrno(errno);
341 }
342 return 0;
343 }
344
NaClHostDirFsync(struct NaClHostDir * d)345 int NaClHostDirFsync(struct NaClHostDir *d) {
346 if (-1 == fsync(d->fd)) {
347 return -NaClXlateErrno(errno);
348 }
349 return 0;
350 }
351
NaClHostDirFdatasync(struct NaClHostDir * d)352 int NaClHostDirFdatasync(struct NaClHostDir *d) {
353 if (-1 == fdatasync(d->fd)) {
354 return -NaClXlateErrno(errno);
355 }
356 return 0;
357 }
358