| /* |
| * Copyright 2007 The Android Open Source Project |
| * |
| * Syscall and library intercepts. |
| */ |
| |
| /* don't remap open() to open64() */ |
| #undef _FILE_OFFSET_BITS |
| |
| #define CREATE_FUNC_STORAGE |
| #include "Common.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/uio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <utime.h> |
| #include <limits.h> |
| #include <ftw.h> |
| #include <assert.h> |
| |
| |
| #if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64 |
| #warning "big" |
| #endif |
| |
| //#define CALLTRACE(format, ...) wsLog(format, ##__VA_ARGS__) |
| #define CALLTRACE(format, ...) ((void)0) |
| |
| //#define CALLTRACEV(format, ...) wsLog(format, ##__VA_ARGS__) |
| #define CALLTRACEV(format, ...) ((void)0) |
| |
| /* |
| When opening certain files, we need to simulate the contents. For example, |
| we can pretend to open the frame buffer, and respond to ioctl()s by |
| returning fake data or telling the front-end to render new data. |
| |
| We want to intercept specific files in /dev. In some cases we want to |
| intercept and reject, e.g. to indicate that a standard Linux device does |
| not exist. |
| |
| Some things we're not going to intercept: |
| /etc/... (e.g. /etc/timezone) -- std. Linux version should be okay |
| /proc/... (e.g. /proc/stat) -- we're showing real pid, so real proc will work |
| |
| For the device drivers we need to intercept: |
| |
| close(), ioctl(), mmap(), open()/open64(), read(), readv(), write(), |
| writev() |
| |
| May also need stat(). We don't need all fd calls, e.g. fchdir() is |
| not likely to succeed on a device driver. The expected uses of mmap() |
| shouldn't require intercepting related calls like madvise() -- we will |
| provide an actual mapping of the requested size. In some cases we will |
| want to return a "real" fd so the app can poll() or select() on it. |
| |
| |
| We also need to consider: |
| getuid/setuid + variations -- fake out multi-user-id stuff |
| |
| |
| We also want to translate filenames, effectively performing a "chroot" |
| without all the baggage that comes with it. The mapping looks like: |
| |
| /system/... --> $ANDROID_PRODUCT_OUT/system/... |
| /data/... --> $ANDROID_PRODUCT_OUT/data/... |
| |
| Translating pathnames requires interception of additional system calls, |
| substituting a new path. Calls include: |
| |
| access(), chdir(), chmod(), chown(), creat(), execve(), getcwd(), |
| lchown(), link(), lstat()/lstat64(), mkdir(), open()/open64(), |
| readlink(), rename(), rmdir(), stat()/stat64(), statfs/statfs64(), |
| symlink(), unlink(), utimes(), |
| |
| Possibly also mknod(), mount(), umount(). |
| |
| The "at" family, notably openat(), should just work since the root comes |
| from an open directory fd. |
| |
| We also need these libc calls, because LD_LIBRARY_PATH substitutes at |
| the libc link level, not the syscall layer: |
| |
| execl(), execlp(), execle(), execv(), execvp(), fopen(), ftw(), getwd(), |
| opendir(), dlopen() |
| |
| It is possible for the cwd to leak out. Some possible leaks: |
| - /proc/[self]/exe |
| - /proc/[self]/cwd |
| - LD_LIBRARY_PATH (which may be awkward to work around) |
| |
| |
| To provide a replacement for the dirent functions -- only required if we |
| want to show "fake" directory contents -- we would need: |
| |
| closedir(), dirfd() readdir(), rewinddir(), scandir(), seekdir(), |
| telldir() |
| |
| |
| */ |
| |
| |
| /* |
| * =========================================================================== |
| * Filename remapping |
| * =========================================================================== |
| */ |
| |
| /* |
| * If appropriate, rewrite the path to point to a different location. |
| * |
| * Returns either "pathBuf" or "origPath" depending on whether or not we |
| * chose to rewrite the path. "origPath" must be a buffer capable of |
| * holding an extended pathname; for best results use PATH_MAX. |
| */ |
| static const char* rewritePath(const char* func, char* pathBuf, |
| const char* origPath) |
| { |
| /* |
| * Rewrite paths that start with "/system/" or "/data/" |
| */ |
| if (origPath[0] != '/') |
| goto skip_rewrite; |
| while (origPath[1] == '/') origPath++; // some apps like to use paths like '//data/data/....' |
| if (memcmp(origPath+1, "system", 6) == 0 && |
| (origPath[7] == '/' || origPath[7] == '\0')) |
| goto do_rewrite; |
| if (memcmp(origPath+1, "data", 4) == 0 && |
| (origPath[5] == '/' || origPath[5] == '\0')) |
| goto do_rewrite; |
| |
| skip_rewrite: |
| /* check to see if something is side-stepping the rewrite */ |
| if (memcmp(origPath, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen) == 0) |
| { |
| wsLog("NOTE: full path used: %s(%s)\n", func, origPath); |
| } |
| |
| CALLTRACE("rewrite %s('%s') --> (not rewritten)\n", func, origPath); |
| return origPath; |
| |
| do_rewrite: |
| memcpy(pathBuf, gWrapSim.remapBaseDir, gWrapSim.remapBaseDirLen); |
| strcpy(pathBuf + gWrapSim.remapBaseDirLen, origPath); |
| CALLTRACE("rewrite %s('%s') --> '%s'\n", func, origPath, pathBuf); |
| return pathBuf; |
| } |
| |
| /* |
| * This works if the pathname is the first argument to the function, and |
| * the function returns "int". |
| */ |
| #define PASS_THROUGH_DECL(_fname, _rtype, ...) \ |
| _rtype _fname( __VA_ARGS__ ) |
| #define PASS_THROUGH_BODY(_fname, _patharg, ...) \ |
| { \ |
| CALLTRACEV("%s(%s)\n", __FUNCTION__, _patharg); \ |
| char pathBuf[PATH_MAX]; \ |
| return _ws_##_fname(rewritePath(#_fname, pathBuf, _patharg), \ |
| ##__VA_ARGS__); \ |
| } |
| |
| |
| PASS_THROUGH_DECL(chdir, int, const char* path) |
| PASS_THROUGH_BODY(chdir, path) |
| |
| PASS_THROUGH_DECL(chmod, int, const char* path, mode_t mode) |
| PASS_THROUGH_BODY(chmod, path, mode) |
| |
| PASS_THROUGH_DECL(chown, int, const char* path, uid_t owner, gid_t group) |
| PASS_THROUGH_BODY(chown, path, owner, group) |
| |
| PASS_THROUGH_DECL(creat, int, const char* path, mode_t mode) |
| PASS_THROUGH_BODY(creat, path, mode) |
| |
| PASS_THROUGH_DECL(execve, int, const char* path, char* const argv[], |
| char* const envp[]) |
| PASS_THROUGH_BODY(execve, path, argv, envp) |
| |
| PASS_THROUGH_DECL(lchown, int, const char* path, uid_t owner, gid_t group) |
| PASS_THROUGH_BODY(lchown, path, owner, group) |
| |
| PASS_THROUGH_DECL(lstat, int, const char* path, struct stat* buf) |
| PASS_THROUGH_BODY(lstat, path, buf) |
| |
| PASS_THROUGH_DECL(lstat64, int, const char* path, struct stat* buf) |
| PASS_THROUGH_BODY(lstat64, path, buf) |
| |
| PASS_THROUGH_DECL(mkdir, int, const char* path, mode_t mode) |
| PASS_THROUGH_BODY(mkdir, path, mode) |
| |
| PASS_THROUGH_DECL(readlink, ssize_t, const char* path, char* buf, size_t bufsiz) |
| PASS_THROUGH_BODY(readlink, path, buf, bufsiz) |
| |
| PASS_THROUGH_DECL(rmdir, int, const char* path) |
| PASS_THROUGH_BODY(rmdir, path) |
| |
| PASS_THROUGH_DECL(stat, int, const char* path, struct stat* buf) |
| PASS_THROUGH_BODY(stat, path, buf) |
| |
| PASS_THROUGH_DECL(stat64, int, const char* path, struct stat* buf) |
| PASS_THROUGH_BODY(stat64, path, buf) |
| |
| PASS_THROUGH_DECL(statfs, int, const char* path, struct statfs* buf) |
| PASS_THROUGH_BODY(statfs, path, buf) |
| |
| PASS_THROUGH_DECL(statfs64, int, const char* path, struct statfs* buf) |
| PASS_THROUGH_BODY(statfs64, path, buf) |
| |
| PASS_THROUGH_DECL(unlink, int, const char* path) |
| PASS_THROUGH_BODY(unlink, path) |
| |
| PASS_THROUGH_DECL(utime, int, const char* path, const struct utimbuf* buf) |
| PASS_THROUGH_BODY(utime, path, buf) |
| |
| PASS_THROUGH_DECL(utimes, int, const char* path, const struct timeval times[2]) |
| PASS_THROUGH_BODY(utimes, path, times) |
| |
| |
| PASS_THROUGH_DECL(fopen, FILE*, const char* path, const char* mode) |
| PASS_THROUGH_BODY(fopen, path, mode) |
| |
| PASS_THROUGH_DECL(fopen64, FILE*, const char* path, const char* mode) |
| PASS_THROUGH_BODY(fopen64, path, mode) |
| |
| PASS_THROUGH_DECL(freopen, FILE*, const char* path, const char* mode, |
| FILE* stream) |
| PASS_THROUGH_BODY(freopen, path, mode, stream) |
| |
| PASS_THROUGH_DECL(ftw, int, const char* dirpath, |
| int (*fn) (const char* fpath, const struct stat* sb, int typeflag), |
| int nopenfd) |
| PASS_THROUGH_BODY(ftw, dirpath, fn, nopenfd) |
| |
| PASS_THROUGH_DECL(opendir, DIR*, const char* path) |
| PASS_THROUGH_BODY(opendir, path) |
| |
| PASS_THROUGH_DECL(dlopen, void*, const char* path, int flag) |
| PASS_THROUGH_BODY(dlopen, path, flag) |
| |
| /* |
| * Opposite of path translation -- remove prefix. |
| * |
| * It looks like BSD allows you to pass a NULL value for "buf" to inspire |
| * getcwd to allocate storage with malloc() (as an extension to the POSIX |
| * definition, which doesn't specify this). getcwd() is a system call |
| * under Linux, so this doesn't work, but that doesn't stop gdb from |
| * trying to use it anyway. |
| */ |
| char* getcwd(char* buf, size_t size) |
| { |
| CALLTRACEV("%s %p %d\n", __FUNCTION__, buf, size); |
| |
| char* result = _ws_getcwd(buf, size); |
| if (buf != NULL && result != NULL) { |
| if (memcmp(buf, gWrapSim.remapBaseDir, |
| gWrapSim.remapBaseDirLen) == 0) |
| { |
| memmove(buf, buf + gWrapSim.remapBaseDirLen, |
| strlen(buf + gWrapSim.remapBaseDirLen)+1); |
| CALLTRACE("rewrite getcwd() -> %s\n", result); |
| } else { |
| CALLTRACE("not rewriting getcwd(%s)\n", result); |
| } |
| } |
| return result; |
| } |
| |
| /* |
| * Need to tweak both pathnames. |
| */ |
| int link(const char* oldPath, const char* newPath) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf1[PATH_MAX]; |
| char pathBuf2[PATH_MAX]; |
| return _ws_link(rewritePath("link-1", pathBuf1, oldPath), |
| rewritePath("link-2", pathBuf2, newPath)); |
| } |
| |
| /* |
| * Need to tweak both pathnames. |
| */ |
| int rename(const char* oldPath, const char* newPath) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf1[PATH_MAX]; |
| char pathBuf2[PATH_MAX]; |
| return _ws_rename(rewritePath("rename-1", pathBuf1, oldPath), |
| rewritePath("rename-2", pathBuf2, newPath)); |
| } |
| |
| /* |
| * Need to tweak both pathnames. |
| */ |
| int symlink(const char* oldPath, const char* newPath) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf1[PATH_MAX]; |
| char pathBuf2[PATH_MAX]; |
| return _ws_symlink(rewritePath("symlink-1", pathBuf1, oldPath), |
| rewritePath("symlink-2", pathBuf2, newPath)); |
| } |
| |
| /* |
| * glibc stat turns into this (32-bit). |
| */ |
| int __xstat(int version, const char* path, struct stat* sbuf) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| char pathBuf[PATH_MAX]; |
| return _ws___xstat(version, rewritePath("__xstat", pathBuf, path), |
| sbuf); |
| } |
| |
| /* |
| * glibc stat turns into this (64-bit). |
| */ |
| int __xstat64(int version, const char* path, struct stat* sbuf) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| char pathBuf[PATH_MAX]; |
| return _ws___xstat64(version, rewritePath("__xstat64", pathBuf, path), |
| sbuf); |
| } |
| |
| /* |
| * glibc lstat turns into this (32-bit). |
| */ |
| int __lxstat(int version, const char* path, struct stat* sbuf) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| char pathBuf[PATH_MAX]; |
| return _ws___lxstat(version, rewritePath("__lxstat", pathBuf, path), |
| sbuf); |
| } |
| |
| /* |
| * glibc lstat turns into this (64-bit). |
| */ |
| int __lxstat64(int version, const char* path, struct stat* sbuf) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| char pathBuf[PATH_MAX]; |
| return _ws___lxstat64(version, rewritePath("__lxstat64", pathBuf, path), |
| sbuf); |
| } |
| |
| /* |
| * Copy the argument list out of varargs for execl/execlp/execle. This |
| * leaves the argc value in _argc, and a NULL-terminated array of character |
| * pointers in _argv. We stop at the first NULL argument, so we shouldn't |
| * end up copying "envp" out. |
| * |
| * We could use gcc __builtin_apply_args to just pass stuff through, |
| * but that may not get along with the path rewriting. It's unclear |
| * whether we want to rewrite the first argument (i.e. the string that |
| * becomes argv[0]); it only makes sense if the exec'ed program is also |
| * getting remapped. |
| */ |
| #define COPY_EXEC_ARGLIST(_first, _argc, _argv) \ |
| int _argc = 0; \ |
| { \ |
| va_list vargs; \ |
| va_start(vargs, _first); \ |
| while (1) { \ |
| _argc++; \ |
| const char* val = va_arg(vargs, const char*); \ |
| if (val == NULL) \ |
| break; \ |
| } \ |
| va_end(vargs); \ |
| } \ |
| const char* _argv[_argc+1]; \ |
| _argv[0] = _first; \ |
| { \ |
| va_list vargs; \ |
| int i; \ |
| va_start(vargs, _first); \ |
| for (i = 1; i < _argc; i++) { \ |
| _argv[i] = va_arg(vargs, const char*); \ |
| } \ |
| va_end(vargs); \ |
| } \ |
| _argv[_argc] = NULL; |
| |
| /* |
| * Debug dump. |
| */ |
| static void dumpExecArgs(const char* callName, const char* path, |
| int argc, const char* argv[], char* const envp[]) |
| { |
| int i; |
| |
| CALLTRACE("Calling %s '%s' (envp=%p)\n", callName, path, envp); |
| for (i = 0; i <= argc; i++) |
| CALLTRACE(" %d: %s\n", i, argv[i]); |
| } |
| |
| /* |
| * Extract varargs, convert paths, hand off to execv. |
| */ |
| int execl(const char* path, const char* arg, ...) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf[PATH_MAX]; |
| |
| COPY_EXEC_ARGLIST(arg, argc, argv); |
| dumpExecArgs("execl", path, argc, argv, NULL); |
| path = rewritePath("execl", pathBuf, path); |
| return _ws_execv(path, (char* const*) argv); |
| } |
| |
| /* |
| * Extract varargs, convert paths, hand off to execve. |
| * |
| * The execle prototype in the man page isn't valid C -- it shows the |
| * "envp" argument after the "...". We have to pull it out with the rest |
| * of the varargs. |
| */ |
| int execle(const char* path, const char* arg, ...) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf[PATH_MAX]; |
| |
| COPY_EXEC_ARGLIST(arg, argc, argv); |
| |
| /* run through again and find envp */ |
| char* const* envp; |
| |
| va_list vargs; |
| va_start(vargs, arg); |
| while (1) { |
| const char* val = va_arg(vargs, const char*); |
| if (val == NULL) { |
| envp = va_arg(vargs, char* const*); |
| break; |
| } |
| } |
| va_end(vargs); |
| |
| dumpExecArgs("execle", path, argc, argv, envp); |
| path = rewritePath("execl", pathBuf, path); |
| |
| return _ws_execve(path, (char* const*) argv, envp); |
| } |
| |
| /* |
| * Extract varargs, convert paths, hand off to execvp. |
| */ |
| int execlp(const char* file, const char* arg, ...) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf[PATH_MAX]; |
| |
| COPY_EXEC_ARGLIST(arg, argc, argv); |
| dumpExecArgs("execlp", file, argc, argv, NULL); |
| file = rewritePath("execlp", pathBuf, file); |
| return _ws_execvp(file, (char* const*) argv); |
| } |
| |
| /* |
| * Update path, forward to execv. |
| */ |
| int execv(const char* path, char* const argv[]) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf[PATH_MAX]; |
| |
| path = rewritePath("execv", pathBuf, path); |
| return _ws_execv(path, argv); |
| } |
| |
| /* |
| * Shouldn't need to do anything unless they specified a full path to execvp. |
| */ |
| int execvp(const char* file, char* const argv[]) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| char pathBuf[PATH_MAX]; |
| |
| file = rewritePath("execvp", pathBuf, file); |
| return _ws_execvp(file, argv); |
| } |
| |
| |
| /* |
| * =========================================================================== |
| * Device fakery |
| * =========================================================================== |
| */ |
| |
| /* |
| * Need to do filesystem translation and show fake devices. |
| */ |
| int access(const char* pathName, int mode) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| int status = wsInterceptDeviceAccess(pathName, mode); |
| if (status == 0) |
| return 0; |
| else if (status == -2) |
| return -1; // errno already set |
| else { |
| char pathBuf[PATH_MAX]; |
| return _ws_access(rewritePath("access", pathBuf, pathName), mode); |
| } |
| } |
| |
| /* |
| * Common handler for open(). |
| */ |
| int openCommon(const char* pathName, int flags, mode_t mode) |
| { |
| char pathBuf[PATH_MAX]; |
| int fd; |
| |
| assert(gWrapSim.initialized); |
| |
| fd = wsInterceptDeviceOpen(pathName, flags); |
| if (fd >= 0) { |
| return fd; |
| } else if (fd == -2) { |
| /* errno should be set */ |
| return -1; |
| } |
| |
| if ((flags & O_CREAT) != 0) { |
| fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, mode); |
| CALLTRACE("open(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd); |
| } else { |
| fd = _ws_open(rewritePath("open", pathBuf, pathName), flags, 0); |
| CALLTRACE("open(%s, 0x%x) = %d\n", pathName, flags, fd); |
| } |
| return fd; |
| } |
| |
| /* |
| * Replacement open() and variants. |
| * |
| * We have to use the vararg decl for the standard call so it matches |
| * the definition in fcntl.h. |
| */ |
| int open(const char* pathName, int flags, ...) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| mode_t mode = 0; |
| if ((flags & O_CREAT) != 0) { |
| va_list args; |
| |
| va_start(args, flags); |
| mode = va_arg(args, mode_t); |
| va_end(args); |
| } |
| |
| return openCommon(pathName, flags, mode); |
| } |
| int __open(const char* pathName, int flags, mode_t mode) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| return openCommon(pathName, flags, mode); |
| } |
| |
| /* |
| * Common handler for open64(). |
| */ |
| int open64Common(const char* pathName, int flags, mode_t mode) |
| { |
| char pathBuf[PATH_MAX]; |
| int fd; |
| |
| assert(gWrapSim.initialized); |
| |
| fd = wsInterceptDeviceOpen(pathName, flags); |
| if (fd >= 0) { |
| return fd; |
| } |
| |
| if ((flags & O_CREAT) != 0) { |
| fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, mode); |
| CALLTRACE("open64(%s, 0x%x, 0%o) = %d\n", pathName, flags, mode, fd); |
| } else { |
| fd = _ws_open64(rewritePath("open64", pathBuf, pathName), flags, 0); |
| CALLTRACE("open64(%s, 0x%x) = %d\n", pathName, flags, fd); |
| } |
| return fd; |
| } |
| |
| /* |
| * Replacement open64() and variants. |
| * |
| * We have to use the vararg decl for the standard call so it matches |
| * the definition in fcntl.h. |
| */ |
| int open64(const char* pathName, int flags, ...) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| mode_t mode = 0; |
| if ((flags & O_CREAT) != 0) { |
| va_list args; |
| |
| va_start(args, flags); |
| mode = va_arg(args, mode_t); |
| va_end(args); |
| } |
| return open64Common(pathName, flags, mode); |
| } |
| int __open64(const char* pathName, int flags, mode_t mode) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| return open64Common(pathName, flags, mode); |
| } |
| |
| |
| int dup(int fd) |
| { |
| CALLTRACEV("%s(%d)\n", __FUNCTION__, fd); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| FakeDev* newDev = dev->dup(dev, fd); |
| if (newDev != NULL) { |
| /* |
| * Now that the device entry is ready, add it to the list. |
| */ |
| wsLog("## dup'ed fake dev %d: '%s' %p\n", |
| newDev->fd, newDev->debugName, newDev->state); |
| gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] = newDev; |
| return newDev->fd; |
| } |
| return -1; |
| } else { |
| CALLTRACE("dup(%d)\n", fd); |
| return _ws_dup(fd); |
| } |
| } |
| |
| |
| /* |
| * Close a file descriptor. |
| */ |
| int close(int fd) |
| { |
| CALLTRACEV("%s(%d)\n", __FUNCTION__, fd); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| int result = dev->close(dev, fd); |
| wsFreeFakeDev(dev); |
| return result; |
| } else { |
| CALLTRACE("close(%d)\n", fd); |
| return _ws_close(fd); |
| } |
| } |
| |
| /* |
| * Map a region. |
| */ |
| void* mmap(void* start, size_t length, int prot, int flags, int fd, |
| __off_t offset) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->mmap(dev, start, length, prot, flags, fd, offset); |
| } else { |
| CALLTRACE("mmap(%p, %d, %d, %d, %d, %d)\n", |
| start, (int) length, prot, flags, fd, (int) offset); |
| return _ws_mmap(start, length, prot, flags, fd, offset); |
| } |
| } |
| |
| /* |
| * Map a region. |
| */ |
| void* mmap64(void* start, size_t length, int prot, int flags, int fd, |
| __off64_t offset) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->mmap(dev, start, length, prot, flags, fd, (__off_t) offset); |
| } else { |
| CALLTRACE("mmap64(%p, %d, %d, %d, %d, %d)\n", |
| start, (int) length, prot, flags, fd, (int) offset); |
| return _ws_mmap(start, length, prot, flags, fd, offset); |
| } |
| } |
| |
| /* |
| * The Linux headers show this with a vararg header, but as far as I can |
| * tell the kernel always expects 3 args. |
| */ |
| int ioctl(int fd, int request, ...) |
| { |
| CALLTRACEV("%s(%d, %d, ...)\n", __FUNCTION__, fd, request); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| va_list args; |
| void* argp; |
| |
| /* extract argp from varargs */ |
| va_start(args, request); |
| argp = va_arg(args, void*); |
| va_end(args); |
| |
| if (dev != NULL) { |
| return dev->ioctl(dev, fd, request, argp); |
| } else { |
| CALLTRACE("ioctl(%d, 0x%x, %p)\n", fd, request, argp); |
| return _ws_ioctl(fd, request, argp); |
| } |
| } |
| |
| /* |
| * Read data. |
| */ |
| ssize_t read(int fd, void* buf, size_t count) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->read(dev, fd, buf, count); |
| } else { |
| CALLTRACE("read(%d, %p, %u)\n", fd, buf, count); |
| return _ws_read(fd, buf, count); |
| } |
| } |
| |
| /* |
| * Write data. |
| */ |
| ssize_t write(int fd, const void* buf, size_t count) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->write(dev, fd, buf, count); |
| } else { |
| CALLTRACE("write(%d, %p, %u)\n", fd, buf, count); |
| return _ws_write(fd, buf, count); |
| } |
| } |
| |
| /* |
| * Read a data vector. |
| */ |
| ssize_t readv(int fd, const struct iovec* vector, int count) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->readv(dev, fd, vector, count); |
| } else { |
| CALLTRACE("readv(%d, %p, %u)\n", fd, vector, count); |
| return _ws_readv(fd, vector, count); |
| } |
| } |
| |
| /* |
| * Write a data vector. |
| */ |
| ssize_t writev(int fd, const struct iovec* vector, int count) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| FakeDev* dev = wsFakeDevFromFd(fd); |
| if (dev != NULL) { |
| return dev->writev(dev, fd, vector, count); |
| } else { |
| CALLTRACE("writev(%d, %p, %u)\n", fd, vector, count); |
| return _ws_writev(fd, vector, count); |
| } |
| } |
| |
| /* |
| * Set the scheduling priority. The sim doesn't run as root, so we have |
| * to fake this out. |
| * |
| * For now, do some basic verification of the which and who parameters, |
| * but otherwise return success. In the future we may want to track |
| * these so getpriority works. |
| */ |
| int setpriority(__priority_which_t which, id_t who, int what) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| if (which != PRIO_PROCESS && |
| which != PRIO_PGRP && |
| which != PRIO_USER) { |
| return EINVAL; |
| } |
| |
| if ((int)who < 0) { |
| return ESRCH; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Pretend to be running as root, so the Android framework |
| * doesn't complain about permission problems all over the |
| * place. |
| */ |
| uid_t getuid(void) |
| { |
| return 0; |
| } |
| |
| #if 0 |
| /* |
| * Create a pipe. (Only needed for debugging an fd leak.) |
| */ |
| int pipe(int filedes[2]) |
| { |
| CALLTRACEV("%s\n", __FUNCTION__); |
| |
| int result = _ws_pipe(filedes); |
| if (result == 0) |
| CALLTRACE("pipe(%p) -> %d,%d\n", filedes, filedes[0], filedes[1]); |
| if (filedes[0] == 83) |
| abort(); |
| return result; |
| } |
| #endif |