1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <sys/types.h>  // Include something that will define __GLIBC__.
6
7// The entire file is wrapped in this #if. We do this so this .cc file can be
8// compiled, even on a non-glibc build.
9#if defined(__native_client__) && defined(__GLIBC__)
10
11#include "nacl_io/kernel_wrap.h"
12
13#include <alloca.h>
14#include <assert.h>
15#include <dirent.h>
16#include <errno.h>
17#include <irt.h>
18#include <irt_syscalls.h>
19#include <nacl_stat.h>
20#include <string.h>
21#include <sys/stat.h>
22#include <sys/time.h>
23
24#include "nacl_io/kernel_intercept.h"
25#include "nacl_io/kernel_wrap_real.h"
26#include "nacl_io/osmman.h"
27
28
29namespace {
30
31void stat_to_nacl_stat(const struct stat* buf, nacl_abi_stat* nacl_buf) {
32  memset(nacl_buf, 0, sizeof(struct nacl_abi_stat));
33  nacl_buf->nacl_abi_st_dev = buf->st_dev;
34  nacl_buf->nacl_abi_st_ino = buf->st_ino;
35  nacl_buf->nacl_abi_st_mode = buf->st_mode;
36  nacl_buf->nacl_abi_st_nlink = buf->st_nlink;
37  nacl_buf->nacl_abi_st_uid = buf->st_uid;
38  nacl_buf->nacl_abi_st_gid = buf->st_gid;
39  nacl_buf->nacl_abi_st_rdev = buf->st_rdev;
40  nacl_buf->nacl_abi_st_size = buf->st_size;
41  nacl_buf->nacl_abi_st_blksize = buf->st_blksize;
42  nacl_buf->nacl_abi_st_blocks = buf->st_blocks;
43  nacl_buf->nacl_abi_st_atime = buf->st_atime;
44  nacl_buf->nacl_abi_st_mtime = buf->st_mtime;
45  nacl_buf->nacl_abi_st_ctime = buf->st_ctime;
46}
47
48void nacl_stat_to_stat(const nacl_abi_stat* nacl_buf, struct stat* buf) {
49  memset(buf, 0, sizeof(struct stat));
50  buf->st_dev = nacl_buf->nacl_abi_st_dev;
51  buf->st_ino = nacl_buf->nacl_abi_st_ino;
52  buf->st_mode = nacl_buf->nacl_abi_st_mode;
53  buf->st_nlink = nacl_buf->nacl_abi_st_nlink;
54  buf->st_uid = nacl_buf->nacl_abi_st_uid;
55  buf->st_gid = nacl_buf->nacl_abi_st_gid;
56  buf->st_rdev = nacl_buf->nacl_abi_st_rdev;
57  buf->st_size = nacl_buf->nacl_abi_st_size ;
58  buf->st_blksize = nacl_buf->nacl_abi_st_blksize;
59  buf->st_blocks = nacl_buf->nacl_abi_st_blocks;
60  buf->st_atime = nacl_buf->nacl_abi_st_atime;
61  buf->st_mtime = nacl_buf->nacl_abi_st_mtime;
62  buf->st_ctime = nacl_buf->nacl_abi_st_ctime;
63}
64
65}  // namespace
66
67// From native_client/src/trusted/service_runtime/include/sys/dirent.h
68
69#ifndef nacl_abi___ino_t_defined
70#define nacl_abi___ino_t_defined
71typedef int64_t nacl_abi___ino_t;
72typedef nacl_abi___ino_t nacl_abi_ino_t;
73#endif
74
75#ifndef nacl_abi___off_t_defined
76#define nacl_abi___off_t_defined
77typedef int64_t nacl_abi__off_t;
78typedef nacl_abi__off_t nacl_abi_off_t;
79#endif
80
81/* We need a way to define the maximum size of a name. */
82#ifndef MAXNAMLEN
83# ifdef NAME_MAX
84#  define MAXNAMLEN NAME_MAX
85# else
86#  define MAXNAMLEN 255
87# endif
88#endif
89
90struct nacl_abi_dirent {
91  nacl_abi_ino_t nacl_abi_d_ino;
92  nacl_abi_off_t nacl_abi_d_off;
93  uint16_t       nacl_abi_d_reclen;
94  char           nacl_abi_d_name[MAXNAMLEN + 1];
95};
96
97static const int d_name_shift = offsetof (dirent, d_name) -
98  offsetof (struct nacl_abi_dirent, nacl_abi_d_name);
99
100EXTERN_C_BEGIN
101
102// Macro to get the REAL function pointer
103#define REAL(name) __nacl_irt_##name##_real
104
105// Macro to get the WRAP function
106#define WRAP(name) __nacl_irt_##name##_wrap
107
108// Declare REAL function pointer.
109#define DECLARE_REAL_PTR(name) \
110  typeof(__nacl_irt_##name) REAL(name);
111
112// Assign the REAL function pointer.
113#define ASSIGN_REAL_PTR(name) \
114  assert(__nacl_irt_##name != NULL); \
115  REAL(name) = __nacl_irt_##name;
116
117// Switch IRT's pointer to the REAL pointer
118#define USE_REAL(name) \
119  __nacl_irt_##name = (typeof(__nacl_irt_##name)) REAL(name)
120
121// Switch IRT's pointer to the WRAP function
122#define USE_WRAP(name) \
123  __nacl_irt_##name = (typeof(__nacl_irt_##name)) WRAP(name)
124
125
126#define EXPAND_SYMBOL_LIST_OPERATION(OP) \
127  OP(chdir); \
128  OP(close); \
129  OP(dup); \
130  OP(dup2);  \
131  OP(fstat); \
132  OP(getcwd);  \
133  OP(getdents);  \
134  OP(mkdir); \
135  OP(open); \
136  OP(poll);\
137  OP(read); \
138  OP(rmdir); \
139  OP(seek); \
140  OP(stat); \
141  OP(select); \
142  OP(write); \
143  OP(mmap); \
144  OP(munmap); \
145  OP(open_resource);
146
147EXPAND_SYMBOL_LIST_OPERATION(DECLARE_REAL_PTR);
148
149int WRAP(chdir) (const char* pathname) {
150  return (ki_chdir(pathname)) ? errno : 0;
151}
152
153int WRAP(close)(int fd) {
154  return (ki_close(fd) < 0) ? errno : 0;
155}
156
157int WRAP(dup)(int fd, int* newfd) NOTHROW {
158  *newfd = ki_dup(fd);
159  return (*newfd < 0) ? errno : 0;
160}
161
162int WRAP(dup2)(int fd, int newfd) NOTHROW {
163  return (ki_dup2(fd, newfd) < 0) ? errno : 0;
164}
165
166int WRAP(fstat)(int fd, struct nacl_abi_stat *nacl_buf) {
167  struct stat buf;
168  memset(&buf, 0, sizeof(struct stat));
169  int res = ki_fstat(fd, &buf);
170  if (res < 0)
171    return errno;
172  stat_to_nacl_stat(&buf, nacl_buf);
173  return 0;
174}
175
176char* WRAP(getcwd)(char* buf, size_t size) {
177  return ki_getcwd(buf, size);
178}
179
180int WRAP(getdents)(int fd, dirent* nacl_buf, size_t nacl_count, size_t *nread) {
181  int nacl_offset = 0;
182  // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s).
183  // nacl_abi_dirent(s) are smaller than dirent(s), so nacl_count bytes buffer
184  // is enough
185  char* buf = (char*)alloca(nacl_count);
186  int offset = 0;
187  int count;
188
189  count = ki_getdents(fd, buf, nacl_count);
190  if (count < 0)
191    return errno;
192
193  while (offset < count) {
194    dirent* d = (dirent*)(buf + offset);
195    nacl_abi_dirent* nacl_d = (nacl_abi_dirent*)((char*)nacl_buf + nacl_offset);
196    nacl_d->nacl_abi_d_ino = d->d_ino;
197    nacl_d->nacl_abi_d_off = d->d_off;
198    nacl_d->nacl_abi_d_reclen = d->d_reclen - d_name_shift;
199    size_t d_name_len = d->d_reclen - offsetof(dirent, d_name);
200    memcpy(nacl_d->nacl_abi_d_name, d->d_name, d_name_len);
201
202    offset += d->d_reclen;
203    nacl_offset += nacl_d->nacl_abi_d_reclen;
204  }
205
206  *nread = nacl_offset;
207  return 0;
208}
209
210int WRAP(mkdir)(const char* pathname, mode_t mode) {
211  return (ki_mkdir(pathname, mode)) ? errno : 0;
212}
213
214int WRAP(mmap)(void** addr, size_t length, int prot, int flags, int fd,
215               off_t offset) {
216  if (flags & MAP_ANONYMOUS)
217    return REAL(mmap)(addr, length, prot, flags, fd, offset);
218
219  *addr = ki_mmap(*addr, length, prot, flags, fd, offset);
220  return *addr == (void*)-1 ? errno : 0;
221}
222
223int WRAP(munmap)(void* addr, size_t length) {
224  // Always let the real munmap run on the address range. It is not an error if
225  // there are no mapped pages in that range.
226  ki_munmap(addr, length);
227  return REAL(munmap)(addr, length);
228}
229
230int WRAP(open)(const char* pathname, int oflag, mode_t cmode, int* newfd) {
231  *newfd = ki_open(pathname, oflag);
232  return (*newfd < 0) ? errno : 0;
233}
234
235int WRAP(open_resource)(const char* file, int* fd) {
236  *fd = ki_open_resource(file);
237  return (*fd < 0) ? errno : 0;
238}
239
240int WRAP(poll)(struct pollfd *fds, nfds_t nfds, int timeout, int* count) {
241  *count = ki_poll(fds, nfds, timeout);
242  return (*count < 0) ? errno : 0;
243
244}
245
246int WRAP(read)(int fd, void *buf, size_t count, size_t *nread) {
247  ssize_t signed_nread = ki_read(fd, buf, count);
248  *nread = static_cast<size_t>(signed_nread);
249  return (signed_nread < 0) ? errno : 0;
250}
251
252int WRAP(rmdir)(const char* pathname) {
253  return (ki_rmdir(pathname) < 0) ? errno : 0;
254}
255
256int WRAP(seek)(int fd, off_t offset, int whence, off_t* new_offset) {
257  *new_offset = ki_lseek(fd, offset, whence);
258  return (*new_offset < 0) ? errno : 0;
259}
260
261int WRAP(select)(int nfds, fd_set* readfds, fd_set* writefds,
262                 fd_set* exceptfds, struct timeval* timeout, int* count) {
263  *count = ki_select(nfds, readfds, writefds, exceptfds, timeout);
264  return (*count < 0) ? errno : 0;
265}
266
267int WRAP(stat)(const char *pathname, struct nacl_abi_stat *nacl_buf) {
268  struct stat buf;
269  memset(&buf, 0, sizeof(struct stat));
270  int res = ki_stat(pathname, &buf);
271  if (res < 0)
272    return errno;
273  stat_to_nacl_stat(&buf, nacl_buf);
274  return 0;
275}
276
277int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) {
278  ssize_t signed_nwrote = ki_write(fd, buf, count);
279  *nwrote = static_cast<size_t>(signed_nwrote);
280  return (signed_nwrote < 0) ? errno : 0;
281}
282
283static void assign_real_pointers() {
284  static bool assigned = false;
285  if (!assigned) {
286    EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR)
287    assigned = true;
288  }
289}
290
291#define CHECK_REAL(func) \
292  if (!REAL(func)) \
293    assign_real_pointers();
294
295// "real" functions, i.e. the unwrapped original functions.
296
297int _real_close(int fd) {
298  CHECK_REAL(close);
299  return REAL(close)(fd);
300}
301
302int _real_fstat(int fd, struct stat* buf) {
303  struct nacl_abi_stat st;
304  CHECK_REAL(fstat);
305  int err = REAL(fstat)(fd, &st);
306  if (err) {
307    errno = err;
308    return -1;
309  }
310
311  nacl_stat_to_stat(&st, buf);
312  return 0;
313}
314
315int _real_getdents(int fd, void* buf, size_t count, size_t* nread) {
316  // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s).
317  // See WRAP(getdents) above.
318  char* nacl_buf = (char*)alloca(count);
319  size_t offset = 0;
320  size_t nacl_offset = 0;
321  size_t nacl_nread;
322  CHECK_REAL(getdents);
323  int err = REAL(getdents)(fd, (dirent*)nacl_buf, count, &nacl_nread);
324  if (err)
325    return err;
326
327  while (nacl_offset < nacl_nread) {
328    dirent* d = (dirent*)((char*)buf + offset);
329    nacl_abi_dirent* nacl_d = (nacl_abi_dirent*)(nacl_buf + nacl_offset);
330    d->d_ino = nacl_d->nacl_abi_d_ino;
331    d->d_off = nacl_d->nacl_abi_d_off;
332    d->d_reclen = nacl_d->nacl_abi_d_reclen + d_name_shift;
333    size_t d_name_len = nacl_d->nacl_abi_d_reclen -
334        offsetof(nacl_abi_dirent, nacl_abi_d_name);
335    memcpy(d->d_name, nacl_d->nacl_abi_d_name, d_name_len);
336
337    offset += d->d_reclen;
338    offset += nacl_d->nacl_abi_d_reclen;
339  }
340
341  *nread = offset;
342  return 0;
343}
344
345int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) {
346  CHECK_REAL(seek);
347  return REAL(seek)(fd, offset, whence, new_offset);
348}
349
350int _real_mkdir(const char* pathname, mode_t mode) {
351  CHECK_REAL(mkdir);
352  return REAL(mkdir)(pathname, mode);
353}
354
355int _real_mmap(void** addr, size_t length, int prot, int flags, int fd,
356               off_t offset) {
357  CHECK_REAL(mmap);
358  return REAL(mmap)(addr, length, prot, flags, fd, offset);
359}
360
361int _real_munmap(void* addr, size_t length) {
362  CHECK_REAL(munmap);
363  return REAL(munmap)(addr, length);
364}
365
366int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) {
367  CHECK_REAL(open);
368  return REAL(open)(pathname, oflag, cmode, newfd);
369}
370
371int _real_open_resource(const char* file, int* fd) {
372  CHECK_REAL(open_resource);
373  return REAL(open_resource)(file, fd);
374}
375
376int _real_read(int fd, void *buf, size_t count, size_t *nread) {
377  CHECK_REAL(read);
378  return REAL(read)(fd, buf, count, nread);
379}
380
381int _real_rmdir(const char* pathname) {
382  CHECK_REAL(rmdir);
383  return REAL(rmdir)(pathname);
384}
385
386int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) {
387  CHECK_REAL(write);
388  return REAL(write)(fd, buf, count, nwrote);
389}
390
391static bool s_wrapped = false;
392void kernel_wrap_init() {
393  if (!s_wrapped) {
394    assign_real_pointers();
395    EXPAND_SYMBOL_LIST_OPERATION(USE_WRAP)
396    s_wrapped = true;
397  }
398}
399
400void kernel_wrap_uninit() {
401  if (s_wrapped) {
402    EXPAND_SYMBOL_LIST_OPERATION(USE_REAL)
403    s_wrapped = false;
404  }
405}
406
407EXTERN_C_END
408
409#endif  // defined(__native_client__) && defined(__GLIBC__)
410