852 lines
24 KiB
C
852 lines
24 KiB
C
/*
|
|
* 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
|