diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c --- a/sys/amd64/linux/linux_sysvec.c +++ b/sys/amd64/linux/linux_sysvec.c @@ -629,7 +629,7 @@ .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN | - SV_SIG_WAITNDQ | SV_TIMEKEEP, + SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_WANTABIPATH, .sv_set_syscall_retval = linux_set_syscall_retval, .sv_fetch_syscall_args = linux_fetch_syscall_args, .sv_syscallnames = linux_syscallnames, @@ -643,6 +643,7 @@ .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, .sv_set_fork_retval = linux_set_fork_retval, + .sv_alterlink = linux_alternate_link, }; static int diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -854,7 +854,7 @@ .sv_fixlimit = linux32_fixlimit, .sv_maxssiz = &linux32_maxssiz, .sv_flags = SV_ABI_LINUX | SV_ILP32 | SV_IA32 | SV_SHP | - SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP, + SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_WANTABIPATH, .sv_set_syscall_retval = linux32_set_syscall_retval, .sv_fetch_syscall_args = linux32_fetch_syscall_args, .sv_syscallnames = linux32_syscallnames, @@ -868,6 +868,7 @@ .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, .sv_set_fork_retval = linux32_set_fork_retval, + .sv_alterlink = linux_alternate_link, }; static int diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -431,7 +431,7 @@ .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN | - SV_SIG_WAITNDQ | SV_TIMEKEEP, + SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_WANTABIPATH, .sv_set_syscall_retval = linux_set_syscall_retval, .sv_fetch_syscall_args = linux_fetch_syscall_args, .sv_syscallnames = linux_syscallnames, @@ -446,6 +446,7 @@ .sv_onexit = linux_on_exit, .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, + .sv_alterlink = linux_alternate_link, }; static int diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h --- a/sys/compat/linux/linux_util.h +++ b/sys/compat/linux/linux_util.h @@ -38,6 +38,8 @@ #include +struct nameidata; + MALLOC_DECLARE(M_LINUX); MALLOC_DECLARE(M_EPOLL); @@ -45,6 +47,8 @@ extern int linux_use_emul_path; int linux_emul_convpath(const char *, enum uio_seg, char **, int, int); +int linux_alternate_link(struct nameidata *, char *, int *); +int linux_alternate_interp(struct nameidata *, char *, int *); #define LUSECONVPATH(td) atomic_load_int(&linux_use_emul_path) diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c --- a/sys/compat/linux/linux_util.c +++ b/sys/compat/linux/linux_util.c @@ -96,6 +96,29 @@ return (retval); } +int +linux_alternate_link(struct nameidata *ndp, char *cp, int *linklen) +{ + size_t len; + char *ncp; + + if (!LUSECONVPATH(td)) + return (0); + if (*linklen > 0 && cp[0] == '/') { + len = strlen(linux_emul_path); + if ((*linklen + len + 2) > MAXPATHLEN) + return (ENAMETOOLONG); + cp[*linklen] = '\0'; + len = *linklen + len; + ncp = malloc(len, M_TEMP, M_WAITOK); + sprintf(ncp, "%s%s", linux_emul_path, cp); + bcopy(ncp, cp, len); + free(ncp, M_TEMP); + *linklen = len; + } + return (0); +} + void linux_msg(const struct thread *td, const char *fmt, ...) { diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -655,6 +655,7 @@ .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, .sv_set_fork_retval = linux_set_fork_retval, + .sv_alterlink = NULL, }; INIT_SYSENTVEC(aout_sysvec, &linux_sysvec); @@ -684,7 +685,7 @@ .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_LINUX | SV_IA32 | SV_ILP32 | SV_SHP | - SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP, + SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_WANTABIPATH, .sv_set_syscall_retval = linux_set_syscall_retval, .sv_fetch_syscall_args = linux_fetch_syscall_args, .sv_syscallnames = NULL, @@ -698,6 +699,7 @@ .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, .sv_set_fork_retval = linux_set_fork_retval, + .sv_alterlink = linux_alternate_link, }; static int diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -791,6 +791,7 @@ struct nameidata *nd; struct vattr *attr; struct image_params *imgp; + uint64_t nd_flags; u_long rbase; u_long base_addr = 0; int error; @@ -815,11 +816,21 @@ imgp->proc = p; imgp->attr = attr; - NDINIT(nd, LOOKUP, ISOPEN | FOLLOW | LOCKSHARED | LOCKLEAF, - UIO_SYSSPACE, file); + nd_flags = ISOPEN | FOLLOW | LOCKSHARED | LOCKLEAF; + + NDINIT(nd, LOOKUP, nd_flags, UIO_SYSSPACE, file); if ((error = namei(nd)) != 0) { - nd->ni_vp = NULL; - goto fail; + if (__predict_false(SV_PROC_FLAG(p, SV_WANTABIPATH) != 0)) { + nd_flags |= WANTABIPATH; + NDINIT_ABI(nd, LOOKUP, nd_flags, UIO_SYSSPACE, file, p); + if ((error = namei(nd)) != 0) { + nd->ni_vp = NULL; + goto fail; + } + } else { + nd->ni_vp = NULL; + goto fail; + } } NDFREE_PNBUF(nd); imgp->vp = nd->ni_vp; diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -59,6 +59,7 @@ #include #include #include +#include #ifdef KTRACE #include #endif @@ -516,6 +517,18 @@ error = ENAMETOOLONG; goto out; } + + /* + * Allow ABI to alter the link according to the ABI prefix. + */ + if (__predict_false((cnp->cn_flags & WANTABIPATH) != 0)) { + error = (ndp->ni_sv->sv_alterlink)(ndp, cp, &linklen); + if (error != 0) { + if (ndp->ni_pathlen > 1) + uma_zfree(namei_zone, cp); + goto out; + } + } if (ndp->ni_pathlen > 1) { bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); uma_zfree(namei_zone, cnp->cn_pnbuf); @@ -577,6 +590,15 @@ ("%s: FAILIFEXISTS must be passed with LOCKPARENT and without LOCKLEAF", __func__)); } + if ((cnp->cn_flags & WANTABIPATH) != 0) { + KASSERT(cnp->cn_nameiop == LOOKUP, + ("%s: WANTABIPATH passed for op %d", __func__, cnp->cn_nameiop)); + KASSERT((cnp->cn_flags & FOLLOW) == FOLLOW, + ("%s: WANTABIPATH must be passed with FOLLOW", + __func__)); + KASSERT(ndp->ni_sv != NULL, + ("%s: WANTABIPATH requires ni_sv", __func__)); + } #endif ndp->ni_cnd.cn_cred = td->td_ucred; KASSERT(ndp->ni_resflags == 0, ("%s: garbage in ni_resflags: %x\n", @@ -1579,6 +1601,7 @@ { struct nameidata nd, ndroot; char *ptr, *buf, *cp; + struct proc *p; size_t len, sz; int error; @@ -1629,18 +1652,19 @@ * and we want to create a file (cflag is set). We don't * need to worry about the root comparison in this case. */ + p = curproc; if (create) { for (cp = &ptr[len] - 1; *cp != '/'; cp--); *cp = '\0'; - NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, buf); + NDINIT_ABI(&nd, LOOKUP, FOLLOW | WANTABIPATH, UIO_SYSSPACE, buf, p); error = namei(&nd); *cp = '/'; if (error != 0) goto keeporig; } else { - NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, buf); + NDINIT_ABI(&nd, LOOKUP, FOLLOW | WANTABIPATH, UIO_SYSSPACE, buf, p); error = namei(&nd); if (error != 0) diff --git a/sys/sys/namei.h b/sys/sys/namei.h --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -111,6 +111,10 @@ */ struct componentname ni_cnd; struct nameicap_tracker_head ni_cap_tracker; + /* + * Private ABI info + */ + struct sysentvec *ni_sv; /* * Private helper data for UFS, must be at the end. See * NDINIT_PREFILL(). @@ -154,6 +158,7 @@ #define LOCKSHARED 0x0100 /* Shared lock leaf */ #define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */ #define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */ +#define WANTABIPATH 0x200000000ULL /* Alternate ABI symlinks */ #define MODMASK 0xf000001ffULL /* mask of operational modifiers */ /* @@ -207,13 +212,19 @@ * Initialization of a nameidata structure. */ #define NDINIT(ndp, op, flags, segflg, namep) \ - NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, NULL, &cap_no_rights) + NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, NULL, \ + &cap_no_rights, NULL) #define NDINIT_AT(ndp, op, flags, segflg, namep, dirfd) \ - NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, &cap_no_rights) + NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, \ + &cap_no_rights, NULL) #define NDINIT_ATRIGHTS(ndp, op, flags, segflg, namep, dirfd, rightsp) \ - NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, rightsp) + NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, rightsp, NULL) #define NDINIT_ATVP(ndp, op, flags, segflg, namep, vp) \ - NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, vp, &cap_no_rights) + NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, vp, \ + &cap_no_rights, NULL) +#define NDINIT_ABI(ndp, op, flags, segflg, namep, proc) \ + NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, NULL, \ + &cap_no_rights, p->p_sysent) /* * Note the constant pattern may *hide* bugs. @@ -238,7 +249,7 @@ #define NDREINIT_DBG(arg) do { } while (0) #endif -#define NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, startdir, rightsp) \ +#define NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, startdir, rightsp, sv) \ do { \ struct nameidata *_ndp = (ndp); \ cap_rights_t *_rightsp = (rightsp); \ @@ -252,6 +263,7 @@ _ndp->ni_dirfd = dirfd; \ _ndp->ni_startdir = startdir; \ _ndp->ni_resflags = 0; \ + _ndp->ni_sv = sv; \ filecaps_init(&_ndp->ni_filecaps); \ _ndp->ni_rightsneeded = _rightsp; \ } while (0) diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -99,6 +99,7 @@ struct trapframe; struct vnode; struct note_info_list; +struct nameidata; struct sysentvec { int sv_size; /* number of entries */ @@ -160,6 +161,7 @@ /* Only used on x86 */ struct regset **sv_regset_begin; struct regset **sv_regset_end; + int (*sv_alterlink)(struct nameidata *, char *, int *); }; #define SV_ILP32 0x000100 /* 32-bit executable. */ @@ -167,7 +169,7 @@ #define SV_IA32 0x004000 /* Intel 32-bit executable. */ #define SV_AOUT 0x008000 /* a.out executable. */ #define SV_SHP 0x010000 /* Shared page. */ -#define SV_AVAIL1 0x020000 /* Unused */ +#define SV_WANTABIPATH 0x020000 /* Requires sv_alterlink */ #define SV_TIMEKEEP 0x040000 /* Shared page timehands. */ #define SV_ASLR 0x080000 /* ASLR allowed. */ #define SV_RNG_SEED_VER 0x100000 /* random(4) reseed generation. */