diff mbox series

[6/6] linux-user: Use *at functions to implement interp_prefix

Message ID 20180531224911.23725-7-richard.henderson@linaro.org
State New
Headers show
Series linux-user: Reorg interp_prefix handling | expand

Commit Message

Richard Henderson May 31, 2018, 10:49 p.m. UTC
If the interp_prefix is a complete chroot, it may have a *lot* of files.
Setting up the cache for this is quite expensive.

For the most part, we can use the *at versions of various syscalls to
attempt the operation in the prefix.  For the few cases that remain,
attempt the operation in the prefix via concatenation and then retry
if that fails.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 linux-user/qemu.h    |  37 ++++++++++
 linux-user/elfload.c |   5 +-
 linux-user/main.c    |  26 ++++++-
 linux-user/syscall.c | 163 +++++++++++++++++++++++++++++--------------
 4 files changed, 174 insertions(+), 57 deletions(-)

-- 
2.17.0

Comments

Laurent Vivier June 4, 2018, 1:04 a.m. UTC | #1
On 01/06/2018 00:49, Richard Henderson wrote:
> If the interp_prefix is a complete chroot, it may have a *lot* of files.

> Setting up the cache for this is quite expensive.

> 

> For the most part, we can use the *at versions of various syscalls to

> attempt the operation in the prefix.  For the few cases that remain,

> attempt the operation in the prefix via concatenation and then retry

> if that fails.

> 


I like the idea, but it breaks real chroot.

You can test it with:

wget
https://github.com/vivier/linux-user-test-scrips/raw/master/create_chroot.sh

then

sudo sh ./create_chroot.sh /path/to/static/qemu-s390x stretch

It fails with:

Reading package lists... Done
E: Method /usr/lib/apt/methods/http did not start correctly
E: Failed to fetch http://ftp.us.debian.org/debian/dists/stretch/InRelease
E: Some index files failed to download. They have been ignored, or old
ones used instead.
stretch s390x FAILED


Thanks,
Laurent
Richard Henderson June 5, 2018, 5:27 a.m. UTC | #2
On 06/03/2018 06:04 PM, Laurent Vivier wrote:
> On 01/06/2018 00:49, Richard Henderson wrote:

>> If the interp_prefix is a complete chroot, it may have a *lot* of files.

>> Setting up the cache for this is quite expensive.

>>

>> For the most part, we can use the *at versions of various syscalls to

>> attempt the operation in the prefix.  For the few cases that remain,

>> attempt the operation in the prefix via concatenation and then retry

>> if that fails.

>>

> 

> I like the idea, but it breaks real chroot.

> 

> You can test it with:

> 

> wget

> https://github.com/vivier/linux-user-test-scrips/raw/master/create_chroot.sh

> 

> then

> 

> sudo sh ./create_chroot.sh /path/to/static/qemu-s390x stretch


*shrug* Works for me, at least as far as I can test.

Your script doesn't work outside debian, lacking debootstrap.
At the moment, I can't build on debian *at all*.  Some bit of the build
infrastructure is off and QEMU_FULL_VERSION gets incorrectly defined.  I have
no idea how or why it is different than Fedora.

On Fedora 28, one can no longer build a static qemu.  We depend on libraries
for which Fedora no longer ships static versions.

Do you have some specific binary that fails?


r~
Laurent Vivier June 5, 2018, 6:33 a.m. UTC | #3
On 05/06/2018 07:27, Richard Henderson wrote:
> On 06/03/2018 06:04 PM, Laurent Vivier wrote:

>> On 01/06/2018 00:49, Richard Henderson wrote:

>>> If the interp_prefix is a complete chroot, it may have a *lot* of files.

>>> Setting up the cache for this is quite expensive.

>>>

>>> For the most part, we can use the *at versions of various syscalls to

>>> attempt the operation in the prefix.  For the few cases that remain,

>>> attempt the operation in the prefix via concatenation and then retry

>>> if that fails.

>>>

>>

>> I like the idea, but it breaks real chroot.

>>

>> You can test it with:

>>

>> wget

>> https://github.com/vivier/linux-user-test-scrips/raw/master/create_chroot.sh

>>

>> then

>>

>> sudo sh ./create_chroot.sh /path/to/static/qemu-s390x stretch

> 

> *shrug* Works for me, at least as far as I can test.

> 

> Your script doesn't work outside debian, lacking debootstrap

Yes, but Fedora has debootstrap.

> At the moment, I can't build on debian *at all*.  Some bit of the build

> infrastructure is off and QEMU_FULL_VERSION gets incorrectly defined.  I have

> no idea how or why it is different than Fedora.

> 

> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

> for which Fedora no longer ships static versions.


I build my test binaries on Fedora 28.

My configure is:

./configure' '--enable-linux-user' '--disable-system' '--static'
'--disable-tools' '--disable-docs'

> Do you have some specific binary that fails?


for qemu, qemu-ppc64le fails, and I think it"s "apt-get update" that is
executed.

Thanks,
Laurent
Peter Maydell June 5, 2018, 10:52 a.m. UTC | #4
On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:
> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

> for which Fedora no longer ships static versions.


Do you mean you can't build the system binaries statically (that's
never really been very supported), or that you can't do a build
with configure --disable-system --disable-tools --static ?

The latter would be more concerning.

thanks
-- PMM
Laurent Vivier June 5, 2018, 12:05 p.m. UTC | #5
Le 05/06/2018 à 12:52, Peter Maydell a écrit :
> On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:

>> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

>> for which Fedora no longer ships static versions.

> 

> Do you mean you can't build the system binaries statically (that's

> never really been very supported), or that you can't do a build

> with configure --disable-system --disable-tools --static ?

> 

> The latter would be more concerning.


On Fedora I have also to disable gcrypt, nettle and libiscsi because we
don't have the statically linked libraries, but we don't need them with
linux-user.

Thanks,
Laurent
Peter Maydell June 5, 2018, 12:14 p.m. UTC | #6
On 5 June 2018 at 13:05, Laurent Vivier <laurent@vivier.eu> wrote:
> Le 05/06/2018 à 12:52, Peter Maydell a écrit :

>> On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:

>>> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

>>> for which Fedora no longer ships static versions.

>>

>> Do you mean you can't build the system binaries statically (that's

>> never really been very supported), or that you can't do a build

>> with configure --disable-system --disable-tools --static ?

>>

>> The latter would be more concerning.

>

> On Fedora I have also to disable gcrypt, nettle and libiscsi because we

> don't have the statically linked libraries, but we don't need them with

> linux-user.


Do we actually try to link with those for linux-user? If not,
maybe we could make configure a bit more helpful about not
requiring you to manually --disable-foo them.

thanks
-- PMM
Laurent Vivier June 5, 2018, 12:23 p.m. UTC | #7
Le 05/06/2018 à 14:14, Peter Maydell a écrit :
> On 5 June 2018 at 13:05, Laurent Vivier <laurent@vivier.eu> wrote:

>> Le 05/06/2018 à 12:52, Peter Maydell a écrit :

>>> On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:

>>>> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

>>>> for which Fedora no longer ships static versions.

>>>

>>> Do you mean you can't build the system binaries statically (that's

>>> never really been very supported), or that you can't do a build

>>> with configure --disable-system --disable-tools --static ?

>>>

>>> The latter would be more concerning.

>>

>> On Fedora I have also to disable gcrypt, nettle and libiscsi because we

>> don't have the statically linked libraries, but we don't need them with

>> linux-user.

> 

> Do we actually try to link with those for linux-user? If not,

> maybe we could make configure a bit more helpful about not

> requiring you to manually --disable-foo them.


They are used by tests/qemu-iotests/socket_scm_helper and
qemu-bridge-helper.

Perhaps we can disable qemu-iotests with linux-user only and to not
build qemu-bridge-helper when tools are disabled?

Thanks,
Laurent
Daniel P. Berrangé June 5, 2018, 12:27 p.m. UTC | #8
On Tue, Jun 05, 2018 at 02:23:56PM +0200, Laurent Vivier wrote:
> Le 05/06/2018 à 14:14, Peter Maydell a écrit :

> > On 5 June 2018 at 13:05, Laurent Vivier <laurent@vivier.eu> wrote:

> >> Le 05/06/2018 à 12:52, Peter Maydell a écrit :

> >>> On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:

> >>>> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

> >>>> for which Fedora no longer ships static versions.

> >>>

> >>> Do you mean you can't build the system binaries statically (that's

> >>> never really been very supported), or that you can't do a build

> >>> with configure --disable-system --disable-tools --static ?

> >>>

> >>> The latter would be more concerning.

> >>

> >> On Fedora I have also to disable gcrypt, nettle and libiscsi because we

> >> don't have the statically linked libraries, but we don't need them with

> >> linux-user.

> > 

> > Do we actually try to link with those for linux-user? If not,

> > maybe we could make configure a bit more helpful about not

> > requiring you to manually --disable-foo them.

> 

> They are used by tests/qemu-iotests/socket_scm_helper and

> qemu-bridge-helper.

> 

> Perhaps we can disable qemu-iotests with linux-user only and to not

> build qemu-bridge-helper when tools are disabled?


Actually we should not build qemu-bridge-helper if all system emulator
builds are disabled, since its not intended as a standalone tool.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
Peter Maydell June 5, 2018, 12:33 p.m. UTC | #9
On 5 June 2018 at 13:23, Laurent Vivier <laurent@vivier.eu> wrote:
> They are used by tests/qemu-iotests/socket_scm_helper and

> qemu-bridge-helper.

>

> Perhaps we can disable qemu-iotests with linux-user only and to not

> build qemu-bridge-helper when tools are disabled?


Yeah, we shouldn't be building those if you're using --disable-system
--disable-tools...

thanks
-- PMM
Laurent Vivier June 5, 2018, 12:37 p.m. UTC | #10
Le 05/06/2018 à 14:33, Peter Maydell a écrit :
> On 5 June 2018 at 13:23, Laurent Vivier <laurent@vivier.eu> wrote:

>> They are used by tests/qemu-iotests/socket_scm_helper and

>> qemu-bridge-helper.

>>

>> Perhaps we can disable qemu-iotests with linux-user only and to not

>> build qemu-bridge-helper when tools are disabled?

> 

> Yeah, we shouldn't be building those if you're using --disable-system

> --disable-tools...


So something like that should do the work:

diff --git a/Makefile b/Makefile
....
+ifeq ($(CONFIG_SOFTMMU),y)
 HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
+endif
...
diff --git a/tests/Makefile.include b/tests/Makefile.include
...
+ifeq ($(CONFIG_SOFTMMU),y)
 QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) =
tests/qemu-iotests/socket_scm_helper$(EXESUF)
+endif
...

I prepare a patch...

Thanks,

Laurent
Richard Henderson June 5, 2018, 1:45 p.m. UTC | #11
On 06/05/2018 03:52 AM, Peter Maydell wrote:
> On 5 June 2018 at 06:27, Richard Henderson <richard.henderson@linaro.org> wrote:

>> On Fedora 28, one can no longer build a static qemu.  We depend on libraries

>> for which Fedora no longer ships static versions.

> 

> Do you mean you can't build the system binaries statically (that's

> never really been very supported), or that you can't do a build

> with configure --disable-system --disable-tools --static ?


You're right that I can build with --disable-tools.
Although even there I have to also manually fiddle capstone.

I'm annoyed that "pkg-config --exists --static foo" does not really do what it
implies (to me at least), since Fedora does not ship the static library.


r~
Peter Maydell June 5, 2018, 2:14 p.m. UTC | #12
On 5 June 2018 at 14:45, Richard Henderson <richard.henderson@linaro.org> wrote:
> I'm annoyed that "pkg-config --exists --static foo" does not really do what it

> implies (to me at least), since Fedora does not ship the static library.


Personally I would like to consider that a bug in the distro's
pkg-config files, though I think last time I suggested that
there was a contrary view.

thanks
-- PMM
Richard Henderson June 5, 2018, 2:18 p.m. UTC | #13
On 06/04/2018 11:33 PM, Laurent Vivier wrote:
> Yes, but Fedora has debootstrap.


Fair enough.  That's new to me.

I don't see how your script is supposed to work wrt binfmt-misc.  If you're
copying in the static qemu, you need to adjust, or install, the interpreter.

Therefore I am unsurprised to find that your script fails at

    cp "$QEMU_PATH" $CHROOT/ && \
    chroot $CHROOT ./debootstrap/debootstrap --second-stage || exit


r~
Laurent Vivier June 5, 2018, 2:27 p.m. UTC | #14
Le 05/06/2018 à 16:18, Richard Henderson a écrit :
> On 06/04/2018 11:33 PM, Laurent Vivier wrote:

>> Yes, but Fedora has debootstrap.

> 

> Fair enough.  That's new to me.

> 

> I don't see how your script is supposed to work wrt binfmt-misc.  If you're

> copying in the static qemu, you need to adjust, or install, the interpreter.


Yes, I use scripts/qemu-binfmt-conf.sh to do that:

./scripts/qemu-binfmt-conf.sh --qemu-path / --systemd ppc64le
--credential yes
systemctl restart systemd-binfmt.service

And it is restored on any reboot.

Thanks,
Laurent
Laurent Vivier June 7, 2018, 8:01 a.m. UTC | #15
Le 05/06/2018 à 07:27, Richard Henderson a écrit :
> On 06/03/2018 06:04 PM, Laurent Vivier wrote:

>> On 01/06/2018 00:49, Richard Henderson wrote:

>>> If the interp_prefix is a complete chroot, it may have a *lot* of files.

>>> Setting up the cache for this is quite expensive.

>>>

>>> For the most part, we can use the *at versions of various syscalls to

>>> attempt the operation in the prefix.  For the few cases that remain,

>>> attempt the operation in the prefix via concatenation and then retry

>>> if that fails.

>>>

>>

>> I like the idea, but it breaks real chroot.

>>

>> You can test it with:

>>

>> wget

>> https://github.com/vivier/linux-user-test-scrips/raw/master/create_chroot.sh

>>

>> then

>>

>> sudo sh ./create_chroot.sh /path/to/static/qemu-s390x stretch

> 

> *shrug* Works for me, at least as far as I can test.

> 

> Your script doesn't work outside debian, lacking debootstrap.

> At the moment, I can't build on debian *at all*.  Some bit of the build

> infrastructure is off and QEMU_FULL_VERSION gets incorrectly defined.  I have

> no idea how or why it is different than Fedora.


I had this problem on Fedora building in a separate directory, "make
distclean" at the top source directory fixed the problem for me.

Thanks,
Laurent
Richard Henderson June 7, 2018, 4:43 p.m. UTC | #16
On 06/07/2018 01:01 AM, Laurent Vivier wrote:
>> Your script doesn't work outside debian, lacking debootstrap.

>> At the moment, I can't build on debian *at all*.  Some bit of the build

>> infrastructure is off and QEMU_FULL_VERSION gets incorrectly defined.  I have

>> no idea how or why it is different than Fedora.

> 

> I had this problem on Fedora building in a separate directory, "make

> distclean" at the top source directory fixed the problem for me.


Thanks.  I had never seen this failure mode before.


r~
Evan Green June 15, 2018, 10:51 p.m. UTC | #17
Apologies for getting lost in the dust storm, but where does this
leave this patch? Sounds like there's a real failure caused by it?
-Evan
On Thu, Jun 7, 2018 at 9:43 AM Richard Henderson
<richard.henderson@linaro.org> wrote:
>

> On 06/07/2018 01:01 AM, Laurent Vivier wrote:

> >> Your script doesn't work outside debian, lacking debootstrap.

> >> At the moment, I can't build on debian *at all*.  Some bit of the build

> >> infrastructure is off and QEMU_FULL_VERSION gets incorrectly defined.  I have

> >> no idea how or why it is different than Fedora.

> >

> > I had this problem on Fedora building in a separate directory, "make

> > distclean" at the top source directory fixed the problem for me.

>

> Thanks.  I had never seen this failure mode before.

>

>

> r~
diff mbox series

Patch

diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 33dafbe0e4..05a82a3628 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -471,7 +471,44 @@  void mmap_fork_start(void);
 void mmap_fork_end(int child);
 
 /* main.c */
+extern int interp_dirfd;
 extern unsigned long guest_stack_size;
+char *interp_prefix_path(const char *path);
+
+/* If PATH is absolute, attempt an operation first within interp_dirfd,
+ * using OPENAT_EXPR.  If that fails with ENOENT, or if PATH is not
+ * absolute, only use NORMAL_EXPR.
+ */
+#define TRY_INTERP_FD(RET, PATH, OPENAT_EXPR, NORMAL_EXPR)  \
+    do {                                                    \
+        if (interp_dirfd >= 0 && (PATH)[0] == '/') {        \
+            RET = OPENAT_EXPR;                              \
+            if (RET != -1 || errno != ENOENT) {             \
+                break;                                      \
+            }                                               \
+        }                                                   \
+        RET = NORMAL_EXPR;                                  \
+    } while (0)
+
+/* If PATH is absolute, attempt an operation first with interp_prefix
+ * prefixed.  If that fails with ENOENT, or if PATH is not absolute,
+ * only attempt with PATH.
+ */
+#define TRY_INTERP_PATH(RET, PATH, EXPR)                    \
+    do {                                                    \
+        char *new_##PATH = interp_prefix_path(PATH);        \
+        if (new_##PATH) {                                   \
+            __typeof(PATH) save_##PATH = PATH;              \
+            PATH = new_##PATH;                              \
+            RET = EXPR;                                     \
+            free(new_##PATH);                               \
+            PATH = save_##PATH;                             \
+            if (RET != -1 || errno != ENOENT) {             \
+                break;                                      \
+            }                                               \
+        }                                                   \
+        RET = EXPR;                                         \
+    } while (0)
 
 /* user access */
 
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 13bc78d0c8..abdf5bbf01 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -6,7 +6,6 @@ 
 
 #include "qemu.h"
 #include "disas/disas.h"
-#include "qemu/path.h"
 
 #ifdef _ARCH_PPC64
 #undef ARCH_DLINFO
@@ -2375,7 +2374,9 @@  static void load_elf_interp(const char *filename, struct image_info *info,
 {
     int fd, retval;
 
-    fd = open(path(filename), O_RDONLY);
+    TRY_INTERP_FD(fd, filename,
+                  openat(interp_dirfd, filename + 1, O_RDONLY),
+                  open(filename, O_RDONLY));
     if (fd < 0) {
         goto exit_perror;
     }
diff --git a/linux-user/main.c b/linux-user/main.c
index ee3f323c08..4e956fc00c 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -23,7 +23,6 @@ 
 
 #include "qapi/error.h"
 #include "qemu.h"
-#include "qemu/path.h"
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/help_option.h"
@@ -89,9 +88,27 @@  unsigned long reserved_va;
 
 static void usage(int exitcode);
 
+int interp_dirfd;
 static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
 const char *qemu_uname_release;
 
+char *interp_prefix_path(const char *path)
+{
+    size_t i_len, p_len;
+    char *ret;
+
+    if (interp_prefix == NULL || path[0] != '/') {
+        return NULL;
+    }
+    i_len = strlen(interp_prefix);
+    p_len = strlen(path) + 1;
+    ret = g_malloc(i_len + p_len);
+
+    memcpy(ret, interp_prefix, i_len);
+    memcpy(ret + i_len, path, p_len);
+    return ret;
+}
+
 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
    we allocate a bigger stack. Need a better solution, for example
    by remapping the process stack directly at the right place */
@@ -671,7 +688,12 @@  int main(int argc, char **argv, char **envp)
     memset(&bprm, 0, sizeof (bprm));
 
     /* Scan interp_prefix dir for replacement files. */
-    init_paths(interp_prefix);
+    interp_dirfd = open(interp_prefix, O_CLOEXEC | O_DIRECTORY | O_PATH);
+    if (interp_dirfd >= 0) {
+        add_hostfd(interp_dirfd);
+    } else {
+        interp_prefix = NULL;
+    }
 
     init_qemu_uname_release();
 
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d7513d5dac..b75dd9a5bc 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -19,7 +19,6 @@ 
 #define _ATFILE_SOURCE
 #include "qemu/osdep.h"
 #include "qemu/cutils.h"
-#include "qemu/path.h"
 #include <elf.h>
 #include <endian.h>
 #include <grp.h>
@@ -7401,7 +7400,10 @@  static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname,
     fh = g_malloc0(total_size);
     fh->handle_bytes = size;
 
-    ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags));
+    TRY_INTERP_FD(ret, name,
+                  name_to_handle_at(interp_dirfd, name + 1, fh, &mid, flags),
+                  name_to_handle_at(dirfd, name, fh, &mid, flags));
+    ret = get_errno(ret);
     unlock_user(name, pathname, 0);
 
     /* man name_to_handle_at(2):
@@ -7777,6 +7779,7 @@  static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags,
 #endif
         { NULL, NULL, NULL }
     };
+    int ret;
 
     if (is_proc_myself(pathname, "exe")) {
         int execfd = qemu_getauxval(AT_EXECFD);
@@ -7816,7 +7819,10 @@  static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags,
         return fd;
     }
 
-    return safe_openat(dirfd, path(pathname), flags, mode);
+    TRY_INTERP_FD(ret, pathname,
+                  safe_openat(interp_dirfd, pathname + 1, flags, mode),
+                  safe_openat(dirfd, pathname, flags, mode));
+    return ret;
 }
 
 #define TIMER_MAGIC 0x0caf0000
@@ -7969,6 +7975,7 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     struct stat st;
     struct statfs stfs;
     void *p;
+    char *fn;
 
 #if defined(DEBUG_ERESTARTSYS)
     /* Debug-only code for exercising the syscall-restart code paths
@@ -8531,10 +8538,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             } else {
                 tvp = NULL;
             }
-            if (!(p = lock_user_string(arg2)))
+            if (!(fn = lock_user_string(arg2))) {
                 goto efault;
-            ret = get_errno(futimesat(arg1, path(p), tvp));
-            unlock_user(p, arg2, 0);
+            }
+            TRY_INTERP_FD(ret, fn,
+                          futimesat(interp_dirfd, fn + 1, tvp),
+                          futimesat(arg1, fn, tvp));
+            ret = get_errno(ret);
+            unlock_user(fn, arg2, 0);
         }
         break;
 #endif
@@ -8548,10 +8559,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #endif
 #ifdef TARGET_NR_access
     case TARGET_NR_access:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(access(path(p), arg2));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      faccessat(interp_dirfd, fn + 1, arg2, 0),
+                      access(fn, arg2));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
         break;
 #endif
 #if defined(TARGET_NR_faccessat) && defined(__NR_faccessat)
@@ -8559,10 +8574,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         if (is_hostfd(arg1)) {
             goto ebadf;
         }
-        if (!(p = lock_user_string(arg2)))
+        if (!(fn = lock_user_string(arg2))) {
             goto efault;
-        ret = get_errno(faccessat(arg1, p, arg3, 0));
-        unlock_user(p, arg2, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      faccessat(interp_dirfd, fn + 1, arg3, 0),
+                      faccessat(arg1, fn, arg3, 0));
+        ret = get_errno(ret);
+        unlock_user(fn, arg2, 0);
         break;
 #endif
 #ifdef TARGET_NR_nice /* not on alpha */
@@ -8713,10 +8732,12 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         if (arg1 == 0) {
             ret = get_errno(acct(NULL));
         } else {
-            if (!(p = lock_user_string(arg1)))
+            if (!(fn = lock_user_string(arg1))) {
                 goto efault;
-            ret = get_errno(acct(path(p)));
-            unlock_user(p, arg1, 0);
+            }
+            TRY_INTERP_PATH(ret, fn, acct(fn));
+            ret = get_errno(ret);
+            unlock_user(fn, arg1, 0);
         }
         break;
 #ifdef TARGET_NR_umount2
@@ -9507,14 +9528,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_readlink:
         {
             void *p2;
-            p = lock_user_string(arg1);
+            fn = lock_user_string(arg1);
             p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
-            if (!p || !p2) {
+            if (!fn || !p2) {
                 ret = -TARGET_EFAULT;
             } else if (!arg3) {
                 /* Short circuit this for the magic exe check. */
                 ret = -TARGET_EINVAL;
-            } else if (is_proc_myself((const char *)p, "exe")) {
+            } else if (is_proc_myself(fn, "exe")) {
                 char real[PATH_MAX], *temp;
                 temp = realpath(exec_path, real);
                 /* Return value is # of bytes that we wrote to the buffer. */
@@ -9528,10 +9549,13 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                     memcpy(p2, real, ret);
                 }
             } else {
-                ret = get_errno(readlink(path(p), p2, arg3));
+                TRY_INTERP_FD(ret, fn,
+                              readlinkat(interp_dirfd, fn + 1, p2, arg3),
+                              readlink(fn, p2, arg3));
+                ret = get_errno(ret);
             }
             unlock_user(p2, arg2, ret);
-            unlock_user(p, arg1, 0);
+            unlock_user(fn, arg1, 0);
         }
         break;
 #endif
@@ -9541,20 +9565,23 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             goto ebadf;
         } else {
             void *p2;
-            p  = lock_user_string(arg2);
+            fn = lock_user_string(arg2);
             p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0);
-            if (!p || !p2) {
+            if (!fn || !p2) {
                 ret = -TARGET_EFAULT;
-            } else if (is_proc_myself((const char *)p, "exe")) {
+            } else if (is_proc_myself(fn, "exe")) {
                 char real[PATH_MAX], *temp;
                 temp = realpath(exec_path, real);
                 ret = temp == NULL ? get_errno(-1) : strlen(real) ;
                 snprintf((char *)p2, arg4, "%s", real);
             } else {
-                ret = get_errno(readlinkat(arg1, path(p), p2, arg4));
+                TRY_INTERP_FD(ret, fn,
+                              readlinkat(interp_dirfd, fn + 1, p2, arg4),
+                              readlinkat(arg1, fn, p2, arg4));
+                ret = get_errno(ret);
             }
             unlock_user(p2, arg3, ret);
-            unlock_user(p, arg2, 0);
+            unlock_user(fn, arg2, 0);
         }
         break;
 #endif
@@ -9739,10 +9766,12 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         goto unimplemented;
 #endif
     case TARGET_NR_statfs:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(statfs(path(p), &stfs));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_PATH(ret, fn, statfs(fn, &stfs));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
     convert_statfs:
         if (!is_error(ret)) {
             struct target_statfs *target_stfs;
@@ -9777,10 +9806,12 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         goto convert_statfs;
 #ifdef TARGET_NR_statfs64
     case TARGET_NR_statfs64:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(statfs(path(p), &stfs));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_PATH(ret, fn, statfs(fn, &stfs));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
     convert_statfs64:
         if (!is_error(ret)) {
             struct target_statfs64 *target_stfs;
@@ -10065,18 +10096,26 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         break;
 #ifdef TARGET_NR_stat
     case TARGET_NR_stat:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(stat(path(p), &st));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      fstatat(interp_dirfd, fn + 1, &st, 0),
+                      stat(fn, &st));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
         goto do_stat;
 #endif
 #ifdef TARGET_NR_lstat
     case TARGET_NR_lstat:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(lstat(path(p), &st));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      fstatat(interp_dirfd, fn + 1, &st, AT_SYMLINK_NOFOLLOW),
+                      lstat(fn, &st));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
         goto do_stat;
 #endif
     case TARGET_NR_fstat:
@@ -11261,20 +11300,28 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #endif
 #ifdef TARGET_NR_stat64
     case TARGET_NR_stat64:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(stat(path(p), &st));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      fstatat(interp_dirfd, fn + 1, &st, 0),
+                      stat(fn, &st));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
         if (!is_error(ret))
             ret = host_to_target_stat64(cpu_env, arg2, &st);
         break;
 #endif
 #ifdef TARGET_NR_lstat64
     case TARGET_NR_lstat64:
-        if (!(p = lock_user_string(arg1)))
+        if (!(fn = lock_user_string(arg1))) {
             goto efault;
-        ret = get_errno(lstat(path(p), &st));
-        unlock_user(p, arg1, 0);
+        }
+        TRY_INTERP_FD(ret, fn,
+                      fstatat(interp_dirfd, fn + 1, &st, AT_SYMLINK_NOFOLLOW),
+                      lstat(fn, &st));
+        ret = get_errno(ret);
+        unlock_user(fn, arg1, 0);
         if (!is_error(ret))
             ret = host_to_target_stat64(cpu_env, arg2, &st);
         break;
@@ -11299,9 +11346,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         if (is_hostfd(arg1)) {
             goto ebadf;
         }
-        if (!(p = lock_user_string(arg2)))
+        if (!(fn = lock_user_string(arg2))) {
             goto efault;
-        ret = get_errno(fstatat(arg1, path(p), &st, arg4));
+        }
+        TRY_INTERP_FD(ret, fn,
+                      fstatat(interp_dirfd, fn + 1, &st, arg4),
+                      fstatat(arg1, fn, &st, arg4));
+        ret = get_errno(ret);
+        unlock_user(fn, arg2, 0);
         if (!is_error(ret))
             ret = host_to_target_stat64(cpu_env, arg3, &st);
         break;
@@ -12339,12 +12391,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             if (!arg2)
                 ret = get_errno(sys_utimensat(arg1, NULL, tsp, arg4));
             else {
-                if (!(p = lock_user_string(arg2))) {
-                    ret = -TARGET_EFAULT;
-                    goto fail;
+                if (!(fn = lock_user_string(arg2))) {
+                    goto efault;
                 }
-                ret = get_errno(sys_utimensat(arg1, path(p), tsp, arg4));
-                unlock_user(p, arg2, 0);
+                TRY_INTERP_FD(ret, fn,
+                              sys_utimensat(interp_dirfd, fn + 1, tsp, arg4),
+                              sys_utimensat(arg1, fn, tsp, arg4));
+                ret = get_errno(ret);
+                unlock_user(fn, arg2, 0);
             }
         }
 	break;
@@ -12376,9 +12430,12 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         if (is_hostfd(arg1)) {
             goto ebadf;
         }
-        p = lock_user_string(arg2);
-        ret = get_errno(sys_inotify_add_watch(arg1, path(p), arg3));
-        unlock_user(p, arg2, 0);
+        if (!(fn = lock_user_string(arg2))) {
+            goto efault;
+        }
+        TRY_INTERP_PATH(ret, fn, sys_inotify_add_watch(arg1, fn, arg3));
+        ret = get_errno(ret);
+        unlock_user(fn, arg2, 0);
         break;
 #endif
 #if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch)