diff options
Diffstat (limited to 'linux-user/syscall.c')
-rw-r--r-- | linux-user/syscall.c | 52 |
1 files changed, 49 insertions, 3 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 2bf9e7ec44..e100025b0d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4600,6 +4600,52 @@ int get_osversion(void) return osversion; } +static int do_open(void *cpu_env, const char *pathname, int flags, mode_t mode) +{ + struct fake_open { + const char *filename; + int (*fill)(void *cpu_env, int fd); + }; + const struct fake_open *fake_open; + static const struct fake_open fakes[] = { + { NULL, NULL } + }; + + for (fake_open = fakes; fake_open->filename; fake_open++) { + if (!strncmp(pathname, fake_open->filename, + strlen(fake_open->filename))) { + break; + } + } + + if (fake_open->filename) { + const char *tmpdir; + char filename[PATH_MAX]; + int fd, r; + + /* create temporary file to map stat to */ + tmpdir = getenv("TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + snprintf(filename, sizeof(filename), "%s/qemu-open.XXXXXX", tmpdir); + fd = mkstemp(filename); + if (fd < 0) { + return fd; + } + unlink(filename); + + if ((r = fake_open->fill(cpu_env, fd))) { + close(fd); + return r; + } + lseek(fd, 0, SEEK_SET); + + return fd; + } + + return get_errno(open(path(pathname), flags, mode)); +} + /* do_syscall() should always have a single exit point at the end so that actions, such as logging of syscall results, can be performed. All errnos that do_syscall() returns must be -TARGET_<errcode>. */ @@ -4685,9 +4731,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_open: if (!(p = lock_user_string(arg1))) goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); + ret = get_errno(do_open(cpu_env, p, + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); unlock_user(p, arg1, 0); break; #if defined(TARGET_NR_openat) && defined(__NR_openat) |