blob: 29b0e00740991b550a80b9ccb33c3c0dd8e4f92f [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Syscall and library intercepts.
5 */
6
7/* don't remap open() to open64() */
8#undef _FILE_OFFSET_BITS
9
10#define CREATE_FUNC_STORAGE
11#include "Common.h"
12
13#include <stdlib.h>
14#include <string.h>
15#include <stdarg.h>
16#include <unistd.h>
17#include <fcntl.h>
18#include <sys/uio.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <sys/time.h>
22#include <sys/resource.h>
23#include <utime.h>
24#include <limits.h>
25#include <ftw.h>
26#include <assert.h>
27
28
29#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
30#warning "big"
31#endif
32
33//#define CALLTRACE(format, ...) wsLog(format, ##__VA_ARGS__)
34#define CALLTRACE(format, ...) ((void)0)
35
36//#define CALLTRACEV(format, ...) wsLog(format, ##__VA_ARGS__)
37#define CALLTRACEV(format, ...) ((void)0)
38
39/*
40When opening certain files, we need to simulate the contents. For example,
41we can pretend to open the frame buffer, and respond to ioctl()s by
42returning fake data or telling the front-end to render new data.
43
44We want to intercept specific files in /dev. In some cases we want to
45intercept and reject, e.g. to indicate that a standard Linux device does
46not exist.
47
48Some things we're not going to intercept:
49 /etc/... (e.g. /etc/timezone) -- std. Linux version should be okay
50 /proc/... (e.g. /proc/stat) -- we're showing real pid, so real proc will work
51
52For the device drivers we need to intercept:
53
54 close(), ioctl(), mmap(), open()/open64(), read(), readv(), write(),
55 writev()
56
57May also need stat(). We don't need all fd calls, e.g. fchdir() is
58not likely to succeed on a device driver. The expected uses of mmap()
59shouldn't require intercepting related calls like madvise() -- we will
60provide an actual mapping of the requested size. In some cases we will
61want to return a "real" fd so the app can poll() or select() on it.
62
63
64We also need to consider:
65 getuid/setuid + variations -- fake out multi-user-id stuff
66
67
68We also want to translate filenames, effectively performing a "chroot"
69without all the baggage that comes with it. The mapping looks like:
70
71 /system/... --> $ANDROID_PRODUCT_OUT/system/...
72 /data/... --> $ANDROID_PRODUCT_OUT/data/...
73
74Translating pathnames requires interception of additional system calls,
75substituting a new path. Calls include:
76
77 access(), chdir(), chmod(), chown(), creat(), execve(), getcwd(),
78 lchown(), link(), lstat()/lstat64(), mkdir(), open()/open64(),
79 readlink(), rename(), rmdir(), stat()/stat64(), statfs/statfs64(),
80 symlink(), unlink(), utimes(),
81
82Possibly also mknod(), mount(), umount().
83
84The "at" family, notably openat(), should just work since the root comes
85from an open directory fd.
86
87We also need these libc calls, because LD_LIBRARY_PATH substitutes at
88the libc link level, not the syscall layer:
89
90 execl(), execlp(), execle(), execv(), execvp(), fopen(), ftw(), getwd(),
91 opendir(), dlopen()
92
93It is possible for the cwd to leak out. Some possible leaks:
94 - /proc/[self]/exe
95 - /proc/[self]/cwd
96 - LD_LIBRARY_PATH (which may be awkward to work around)
97
98
99To provide a replacement for the dirent functions -- only required if we
100want to show "fake" directory contents -- we would need:
101
102 closedir(), dirfd() readdir(), rewinddir(), scandir(), seekdir(),
103 telldir()
104
105
106*/
107
108
109/*
110 * ===========================================================================
111 * Filename remapping
112 * ===========================================================================
113 */
114
115/*
116 * If appropriate, rewrite the path to point to a different location.
117 *
118 * Returns either "pathBuf" or "origPath" depending on whether or not we
119 * chose to rewrite the path. "origPath" must be a buffer capable of
120 * holding an extended pathname; for best results use PATH_MAX.
121 */
122static const char* rewritePath(const char* func, char* pathBuf,
123 const char* origPath)
124{
125 /*
126 * Rewrite paths that start with "/system/" or "/data/"
127 */
128 if (origPath[0] != '/')
129 goto skip_rewrite;
130 if (memcmp(origPath+1, "system", 6) == 0 &&
131 (origPath[7] == '/' || origPath[7] == '\0'))
132 goto do_rewrite;
133 if (memcmp(origPath+1, "data", 4) == 0 &&
134 (origPath[5] == '/' || origPath[5] == '\0'))
135 goto do_rewrite;
136
137skip_rewrite:
138 /* check to see if something is side-stepping the rewrite */
139 if (memcmp(origPath, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen) == 0)
140 {
141 wsLog("NOTE: full path used: %s(%s)\n", func, origPath);
142 }
143
144 CALLTRACE("rewrite %s('%s') --> (not rewritten)\n", func, origPath);
145 return origPath;
146
147do_rewrite:
148 memcpy(pathBuf, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen);
149 strcpy(pathBuf + gWrapSim.remapBaseDirLen, origPath);
150 CALLTRACE("rewrite %s('%s') --> '%s'\n", func, origPath, pathBuf);
151 return pathBuf;
152}
153
154/*
155 * This works if the pathname is the first argument to the function, and
156 * the function returns "int".
157 */
158#define PASS_THROUGH_DECL(_fname, _rtype, ...) \
159 _rtype _fname( __VA_ARGS__ )
160#define PASS_THROUGH_BODY(_fname, _patharg, ...) \
161 { \
162 CALLTRACEV("%s\n", __FUNCTION__); \
163 char pathBuf[PATH_MAX]; \
164 return _ws_##_fname(rewritePath(#_fname, pathBuf, _patharg), \
165 ##__VA_ARGS__); \
166 }
167
168
169PASS_THROUGH_DECL(chdir, int, const char* path)
170PASS_THROUGH_BODY(chdir, path)
171
172PASS_THROUGH_DECL(chmod, int, const char* path, mode_t mode)
173PASS_THROUGH_BODY(chmod, path, mode)
174
175PASS_THROUGH_DECL(chown, int, const char* path, uid_t owner, gid_t group)
176PASS_THROUGH_BODY(chown, path, owner, group)
177
178PASS_THROUGH_DECL(creat, int, const char* path, mode_t mode)
179PASS_THROUGH_BODY(creat, path, mode)
180
181PASS_THROUGH_DECL(execve, int, const char* path, char* const argv[],
182 char* const envp[])
183PASS_THROUGH_BODY(execve, path, argv, envp)
184
185PASS_THROUGH_DECL(lchown, int, const char* path, uid_t owner, gid_t group)
186PASS_THROUGH_BODY(lchown, path, owner, group)
187
188PASS_THROUGH_DECL(lstat, int, const char* path, struct stat* buf)
189PASS_THROUGH_BODY(lstat, path, buf)
190
191PASS_THROUGH_DECL(lstat64, int, const char* path, struct stat* buf)
192PASS_THROUGH_BODY(lstat64, path, buf)
193
194PASS_THROUGH_DECL(mkdir, int, const char* path, mode_t mode)
195PASS_THROUGH_BODY(mkdir, path, mode)
196
197PASS_THROUGH_DECL(readlink, ssize_t, const char* path, char* buf, size_t bufsiz)
198PASS_THROUGH_BODY(readlink, path, buf, bufsiz)
199
200PASS_THROUGH_DECL(rmdir, int, const char* path)
201PASS_THROUGH_BODY(rmdir, path)
202
203PASS_THROUGH_DECL(stat, int, const char* path, struct stat* buf)
204PASS_THROUGH_BODY(stat, path, buf)
205
206PASS_THROUGH_DECL(stat64, int, const char* path, struct stat* buf)
207PASS_THROUGH_BODY(stat64, path, buf)
208
209PASS_THROUGH_DECL(statfs, int, const char* path, struct statfs* buf)
210PASS_THROUGH_BODY(statfs, path, buf)
211
212PASS_THROUGH_DECL(statfs64, int, const char* path, struct statfs* buf)
213PASS_THROUGH_BODY(statfs64, path, buf)
214
215PASS_THROUGH_DECL(unlink, int, const char* path)
216PASS_THROUGH_BODY(unlink, path)
217
218PASS_THROUGH_DECL(utime, int, const char* path, const struct utimbuf* buf)
219PASS_THROUGH_BODY(utime, path, buf)
220
221PASS_THROUGH_DECL(utimes, int, const char* path, const struct timeval times[2])
222PASS_THROUGH_BODY(utimes, path, times)
223
224
225PASS_THROUGH_DECL(fopen, FILE*, const char* path, const char* mode)
226PASS_THROUGH_BODY(fopen, path, mode)
227
228PASS_THROUGH_DECL(fopen64, FILE*, const char* path, const char* mode)
229PASS_THROUGH_BODY(fopen64, path, mode)
230
231PASS_THROUGH_DECL(freopen, FILE*, const char* path, const char* mode,
232 FILE* stream)
233PASS_THROUGH_BODY(freopen, path, mode, stream)
234
235PASS_THROUGH_DECL(ftw, int, const char* dirpath,
236 int (*fn) (const char* fpath, const struct stat* sb, int typeflag),
237 int nopenfd)
238PASS_THROUGH_BODY(ftw, dirpath, fn, nopenfd)
239
240PASS_THROUGH_DECL(opendir, DIR*, const char* path)
241PASS_THROUGH_BODY(opendir, path)
242
243PASS_THROUGH_DECL(dlopen, void*, const char* path, int flag)
244PASS_THROUGH_BODY(dlopen, path, flag)
245
246/*
247 * Opposite of path translation -- remove prefix.
248 *
249 * It looks like BSD allows you to pass a NULL value for "buf" to inspire
250 * getcwd to allocate storage with malloc() (as an extension to the POSIX
251 * definition, which doesn't specify this). getcwd() is a system call
252 * under Linux, so this doesn't work, but that doesn't stop gdb from
253 * trying to use it anyway.
254 */
255char* getcwd(char* buf, size_t size)
256{
257 CALLTRACEV("%s %p %d\n", __FUNCTION__, buf, size);
258
259 char* result = _ws_getcwd(buf, size);
260 if (buf != NULL && result != NULL) {
261 if (memcmp(buf, gWrapSim.remapBaseDir,
262 gWrapSim.remapBaseDirLen) == 0)
263 {
264 memmove(buf, buf + gWrapSim.remapBaseDirLen,
265 strlen(buf + gWrapSim.remapBaseDirLen)+1);
266 CALLTRACE("rewrite getcwd() -> %s\n", result);
267 } else {
268 CALLTRACE("not rewriting getcwd(%s)\n", result);
269 }
270 }
271 return result;
272}
273
274/*
275 * Need to tweak both pathnames.
276 */
277int link(const char* oldPath, const char* newPath)
278{
279 CALLTRACEV("%s\n", __FUNCTION__);
280
281 char pathBuf1[PATH_MAX];
282 char pathBuf2[PATH_MAX];
283 return _ws_link(rewritePath("link-1", pathBuf1, oldPath),
284 rewritePath("link-2", pathBuf2, newPath));
285}
286
287/*
288 * Need to tweak both pathnames.
289 */
290int rename(const char* oldPath, const char* newPath)
291{
292 CALLTRACEV("%s\n", __FUNCTION__);
293
294 char pathBuf1[PATH_MAX];
295 char pathBuf2[PATH_MAX];
296 return _ws_rename(rewritePath("rename-1", pathBuf1, oldPath),
297 rewritePath("rename-2", pathBuf2, newPath));
298}
299
300/*
301 * Need to tweak both pathnames.
302 */
303int symlink(const char* oldPath, const char* newPath)
304{
305 CALLTRACEV("%s\n", __FUNCTION__);
306
307 char pathBuf1[PATH_MAX];
308 char pathBuf2[PATH_MAX];
309 return _ws_symlink(rewritePath("symlink-1", pathBuf1, oldPath),
310 rewritePath("symlink-2", pathBuf2, newPath));
311}
312
313/*
314 * glibc stat turns into this (32-bit).
315 */
316int __xstat(int version, const char* path, struct stat* sbuf)
317{
318 CALLTRACEV("%s\n", __FUNCTION__);
319 char pathBuf[PATH_MAX];
320 return _ws___xstat(version, rewritePath("__xstat", pathBuf, path),
321 sbuf);
322}
323
324/*
325 * glibc stat turns into this (64-bit).
326 */
327int __xstat64(int version, const char* path, struct stat* sbuf)
328{
329 CALLTRACEV("%s\n", __FUNCTION__);
330 char pathBuf[PATH_MAX];
331 return _ws___xstat64(version, rewritePath("__xstat64", pathBuf, path),
332 sbuf);
333}
334
335/*
336 * glibc lstat turns into this (32-bit).
337 */
338int __lxstat(int version, const char* path, struct stat* sbuf)
339{
340 CALLTRACEV("%s\n", __FUNCTION__);
341 char pathBuf[PATH_MAX];
342 return _ws___lxstat(version, rewritePath("__lxstat", pathBuf, path),
343 sbuf);
344}
345
346/*
347 * glibc lstat turns into this (64-bit).
348 */
349int __lxstat64(int version, const char* path, struct stat* sbuf)
350{
351 CALLTRACEV("%s\n", __FUNCTION__);
352 char pathBuf[PATH_MAX];
353 return _ws___lxstat64(version, rewritePath("__lxstat64", pathBuf, path),
354 sbuf);
355}
356
357/*
358 * Copy the argument list out of varargs for execl/execlp/execle. This
359 * leaves the argc value in _argc, and a NULL-terminated array of character
360 * pointers in _argv. We stop at the first NULL argument, so we shouldn't
361 * end up copying "envp" out.
362 *
363 * We could use gcc __builtin_apply_args to just pass stuff through,
364 * but that may not get along with the path rewriting. It's unclear
365 * whether we want to rewrite the first argument (i.e. the string that
366 * becomes argv[0]); it only makes sense if the exec'ed program is also
367 * getting remapped.
368 */
369#define COPY_EXEC_ARGLIST(_first, _argc, _argv) \
370 int _argc = 0; \
371 { \
372 va_list vargs; \
373 va_start(vargs, _first); \
374 while (1) { \
375 _argc++; \
376 const char* val = va_arg(vargs, const char*); \
377 if (val == NULL) \
378 break; \
379 } \
380 va_end(vargs); \
381 } \
382 const char* _argv[_argc+1]; \
383 _argv[0] = _first; \
384 { \
385 va_list vargs; \
386 int i; \
387 va_start(vargs, _first); \
388 for (i = 1; i < _argc; i++) { \
389 _argv[i] = va_arg(vargs, const char*); \
390 } \
391 va_end(vargs); \
392 } \
393 _argv[_argc] = NULL;
394
395/*
396 * Debug dump.
397 */
398static void dumpExecArgs(const char* callName, const char* path,
399 int argc, const char* argv[], char* const envp[])
400{
401 int i;
402
403 CALLTRACE("Calling %s '%s' (envp=%p)\n", callName, path, envp);
404 for (i = 0; i <= argc; i++)
405 CALLTRACE(" %d: %s\n", i, argv[i]);
406}
407
408/*
409 * Extract varargs, convert paths, hand off to execv.
410 */
411int execl(const char* path, const char* arg, ...)
412{
413 CALLTRACEV("%s\n", __FUNCTION__);
414
415 char pathBuf[PATH_MAX];
416
417 COPY_EXEC_ARGLIST(arg, argc, argv);
418 dumpExecArgs("execl", path, argc, argv, NULL);
419 path = rewritePath("execl", pathBuf, path);
420 return _ws_execv(path, (char* const*) argv);
421}
422
423/*
424 * Extract varargs, convert paths, hand off to execve.
425 *
426 * The execle prototype in the man page isn't valid C -- it shows the
427 * "envp" argument after the "...". We have to pull it out with the rest
428 * of the varargs.
429 */
430int execle(const char* path, const char* arg, ...)
431{
432 CALLTRACEV("%s\n", __FUNCTION__);
433
434 char pathBuf[PATH_MAX];
435
436 COPY_EXEC_ARGLIST(arg, argc, argv);
437
438 /* run through again and find envp */
439 char* const* envp;
440
441 va_list vargs;
442 va_start(vargs, arg);
443 while (1) {
444 const char* val = va_arg(vargs, const char*);
445 if (val == NULL) {
446 envp = va_arg(vargs, char* const*);
447 break;
448 }
449 }
450 va_end(vargs);
451
452 dumpExecArgs("execle", path, argc, argv, envp);
453 path = rewritePath("execl", pathBuf, path);
454
455 return _ws_execve(path, (char* const*) argv, envp);
456}
457
458/*
459 * Extract varargs, convert paths, hand off to execvp.
460 */
461int execlp(const char* file, const char* arg, ...)
462{
463 CALLTRACEV("%s\n", __FUNCTION__);
464
465 char pathBuf[PATH_MAX];
466
467 COPY_EXEC_ARGLIST(arg, argc, argv);
468 dumpExecArgs("execlp", file, argc, argv, NULL);
469 file = rewritePath("execlp", pathBuf, file);
470 return _ws_execvp(file, (char* const*) argv);
471}
472
473/*
474 * Update path, forward to execv.
475 */
476int execv(const char* path, char* const argv[])
477{
478 CALLTRACEV("%s\n", __FUNCTION__);
479
480 char pathBuf[PATH_MAX];
481
482 path = rewritePath("execv", pathBuf, path);
483 return _ws_execv(path, argv);
484}
485
486/*
487 * Shouldn't need to do anything unless they specified a full path to execvp.
488 */
489int execvp(const char* file, char* const argv[])
490{
491 CALLTRACEV("%s\n", __FUNCTION__);
492
493 char pathBuf[PATH_MAX];
494
495 file = rewritePath("execvp", pathBuf, file);
496 return _ws_execvp(file, argv);
497}
498
499
500/*
501 * ===========================================================================
502 * Device fakery
503 * ===========================================================================
504 */
505
506/*
507 * Need to do filesystem translation and show fake devices.
508 */
509int access(const char* pathName, int mode)
510{
511 CALLTRACEV("%s\n", __FUNCTION__);
512
513 int status = wsInterceptDeviceAccess(pathName, mode);
514 if (status == 0)
515 return 0;
516 else if (status == -2)
517 return -1; // errno already set
518 else {
519 char pathBuf[PATH_MAX];
520 return _ws_access(rewritePath("access", pathBuf, pathName), mode);
521 }
522}
523
524/*
525 * Common handler for open().
526 */
527int openCommon(const char* pathName, int flags, mode_t mode)
528{
529 char pathBuf[PATH_MAX];
530 int fd;
531
532 assert(gWrapSim.initialized);
533
534 fd = wsInterceptDeviceOpen(pathName, flags);
535 if (fd >= 0) {
536 return fd;
537 } else if (fd == -2) {
538 /* errno should be set */
539 return -1;
540 }
541
542 if ((flags & O_CREAT) != 0) {
543 fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, mode);
544 CALLTRACE("open(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd);
545 } else {
546 fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, 0);
547 CALLTRACE("open(%s, 0x%x) = %d\n", pathName, flags, fd);
548 }
549 return fd;
550}
551
552/*
553 * Replacement open() and variants.
554 *
555 * We have to use the vararg decl for the standard call so it matches
556 * the definition in fcntl.h.
557 */
558int open(const char* pathName, int flags, ...)
559{
560 CALLTRACEV("%s\n", __FUNCTION__);
561
562 mode_t mode = 0;
563 if ((flags & O_CREAT) != 0) {
564 va_list args;
565
566 va_start(args, flags);
567 mode = va_arg(args, mode_t);
568 va_end(args);
569 }
570
571 return openCommon(pathName, flags, mode);
572}
573int __open(const char* pathName, int flags, mode_t mode)
574{
575 CALLTRACEV("%s\n", __FUNCTION__);
576
577 return openCommon(pathName, flags, mode);
578}
579
580/*
581 * Common handler for open64().
582 */
583int open64Common(const char* pathName, int flags, mode_t mode)
584{
585 char pathBuf[PATH_MAX];
586 int fd;
587
588 assert(gWrapSim.initialized);
589
590 fd = wsInterceptDeviceOpen(pathName, flags);
591 if (fd >= 0) {
592 return fd;
593 }
594
595 if ((flags & O_CREAT) != 0) {
596 fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, mode);
597 CALLTRACE("open64(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd);
598 } else {
599 fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, 0);
600 CALLTRACE("open64(%s, 0x%x) = %d\n", pathName, flags, fd);
601 }
602 return fd;
603}
604
605/*
606 * Replacement open64() and variants.
607 *
608 * We have to use the vararg decl for the standard call so it matches
609 * the definition in fcntl.h.
610 */
611int open64(const char* pathName, int flags, ...)
612{
613 CALLTRACEV("%s\n", __FUNCTION__);
614
615 mode_t mode = 0;
616 if ((flags & O_CREAT) != 0) {
617 va_list args;
618
619 va_start(args, flags);
620 mode = va_arg(args, mode_t);
621 va_end(args);
622 }
623 return open64Common(pathName, flags, mode);
624}
625int __open64(const char* pathName, int flags, mode_t mode)
626{
627 CALLTRACEV("%s\n", __FUNCTION__);
628
629 return open64Common(pathName, flags, mode);
630}
631
632
633/*
634 * Close a file descriptor.
635 */
636int close(int fd)
637{
638 CALLTRACEV("%s(%d)\n", __FUNCTION__, fd);
639
640 FakeDev* dev = wsFakeDevFromFd(fd);
641 if (dev != NULL) {
642 int result = dev->close(dev, fd);
643 wsFreeFakeDev(dev);
644 return result;
645 } else {
646 CALLTRACE("close(%d)\n", fd);
647 return _ws_close(fd);
648 }
649}
650
651/*
652 * Map a region.
653 */
654void* mmap(void* start, size_t length, int prot, int flags, int fd,
655 __off_t offset)
656{
657 CALLTRACEV("%s\n", __FUNCTION__);
658
659 FakeDev* dev = wsFakeDevFromFd(fd);
660 if (dev != NULL) {
661 return dev->mmap(dev, start, length, prot, flags, fd, offset);
662 } else {
663 CALLTRACE("mmap(%p, %d, %d, %d, %d, %d)\n",
664 start, (int) length, prot, flags, fd, (int) offset);
665 return _ws_mmap(start, length, prot, flags, fd, offset);
666 }
667}
668
669/*
670 * Map a region.
671 */
672void* mmap64(void* start, size_t length, int prot, int flags, int fd,
673 __off64_t offset)
674{
675 CALLTRACEV("%s\n", __FUNCTION__);
676
677 FakeDev* dev = wsFakeDevFromFd(fd);
678 if (dev != NULL) {
679 return dev->mmap(dev, start, length, prot, flags, fd, (__off_t) offset);
680 } else {
681 CALLTRACE("mmap64(%p, %d, %d, %d, %d, %d)\n",
682 start, (int) length, prot, flags, fd, (int) offset);
683 return _ws_mmap(start, length, prot, flags, fd, offset);
684 }
685}
686
687/*
688 * The Linux headers show this with a vararg header, but as far as I can
689 * tell the kernel always expects 3 args.
690 */
691int ioctl(int fd, int request, ...)
692{
693 CALLTRACEV("%s(%d, %d, ...)\n", __FUNCTION__, fd, request);
694
695 FakeDev* dev = wsFakeDevFromFd(fd);
696 va_list args;
697 void* argp;
698
699 /* extract argp from varargs */
700 va_start(args, request);
701 argp = va_arg(args, void*);
702 va_end(args);
703
704 if (dev != NULL) {
705 return dev->ioctl(dev, fd, request, argp);
706 } else {
707 CALLTRACE("ioctl(%d, 0x%x, %p)\n", fd, request, argp);
708 return _ws_ioctl(fd, request, argp);
709 }
710}
711
712/*
713 * Read data.
714 */
715ssize_t read(int fd, void* buf, size_t count)
716{
717 CALLTRACEV("%s\n", __FUNCTION__);
718
719 FakeDev* dev = wsFakeDevFromFd(fd);
720 if (dev != NULL) {
721 return dev->read(dev, fd, buf, count);
722 } else {
723 CALLTRACE("read(%d, %p, %u)\n", fd, buf, count);
724 return _ws_read(fd, buf, count);
725 }
726}
727
728/*
729 * Write data.
730 */
731ssize_t write(int fd, const void* buf, size_t count)
732{
733 CALLTRACEV("%s\n", __FUNCTION__);
734
735 FakeDev* dev = wsFakeDevFromFd(fd);
736 if (dev != NULL) {
737 return dev->write(dev, fd, buf, count);
738 } else {
739 CALLTRACE("write(%d, %p, %u)\n", fd, buf, count);
740 return _ws_write(fd, buf, count);
741 }
742}
743
744/*
745 * Read a data vector.
746 */
747ssize_t readv(int fd, const struct iovec* vector, int count)
748{
749 CALLTRACEV("%s\n", __FUNCTION__);
750
751 FakeDev* dev = wsFakeDevFromFd(fd);
752 if (dev != NULL) {
753 return dev->readv(dev, fd, vector, count);
754 } else {
755 CALLTRACE("readv(%d, %p, %u)\n", fd, vector, count);
756 return _ws_readv(fd, vector, count);
757 }
758}
759
760/*
761 * Write a data vector.
762 */
763ssize_t writev(int fd, const struct iovec* vector, int count)
764{
765 CALLTRACEV("%s\n", __FUNCTION__);
766
767 FakeDev* dev = wsFakeDevFromFd(fd);
768 if (dev != NULL) {
769 return dev->writev(dev, fd, vector, count);
770 } else {
771 CALLTRACE("writev(%d, %p, %u)\n", fd, vector, count);
772 return _ws_writev(fd, vector, count);
773 }
774}
775
776/*
777 * Set the scheduling priority. The sim doesn't run as root, so we have
778 * to fake this out.
779 *
780 * For now, do some basic verification of the which and who parameters,
781 * but otherwise return success. In the future we may want to track
782 * these so getpriority works.
783 */
784int setpriority(__priority_which_t which, id_t who, int what)
785{
786 CALLTRACEV("%s\n", __FUNCTION__);
787
788 if (which != PRIO_PROCESS &&
789 which != PRIO_PGRP &&
790 which != PRIO_USER) {
791 return EINVAL;
792 }
793
794 if ((int)who < 0) {
795 return ESRCH;
796 }
797
798 return 0;
799}
800
801#if 0
802/*
803 * Create a pipe. (Only needed for debugging an fd leak.)
804 */
805int pipe(int filedes[2])
806{
807 CALLTRACEV("%s\n", __FUNCTION__);
808
809 int result = _ws_pipe(filedes);
810 if (result == 0)
811 CALLTRACE("pipe(%p) -> %d,%d\n", filedes, filedes[0], filedes[1]);
812 if (filedes[0] == 83)
813 abort();
814 return result;
815}
816#endif