Index: sys/compat/linux/linux_emul.c =================================================================== --- sys/compat/linux/linux_emul.c +++ sys/compat/linux/linux_emul.c @@ -316,6 +316,7 @@ /* * We are switching the process to Linux emulator. */ + linux_xchroot(td); linux_proc_init(td, td, 0); /* Index: sys/compat/linux/linux_mib.h =================================================================== --- sys/compat/linux/linux_mib.h +++ sys/compat/linux/linux_mib.h @@ -67,5 +67,6 @@ extern int linux_ignore_ip_recverr; extern int linux_preserve_vstatus; extern bool linux_map_sched_prio; +extern bool linux_autochroot; #endif /* _LINUX_MIB_H_ */ Index: sys/compat/linux/linux_mib.c =================================================================== --- sys/compat/linux/linux_mib.c +++ sys/compat/linux/linux_mib.c @@ -85,6 +85,10 @@ &linux_map_sched_prio, 0, "Map scheduler priorities to Linux priorities " "(not POSIX compliant)"); +bool linux_autochroot = false; +SYSCTL_BOOL(_compat_linux, OID_AUTO, autochroot, CTLFLAG_RWTUN, + &linux_autochroot, 0, "XXX"); + static int linux_set_osname(struct thread *td, char *osname); static int linux_set_osrelease(struct thread *td, char *osrelease); static int linux_set_oss_version(struct thread *td, int oss_version); Index: sys/compat/linux/linux_util.h =================================================================== --- sys/compat/linux/linux_util.h +++ sys/compat/linux/linux_util.h @@ -122,6 +122,7 @@ int linux_driver_get_major_minor(const char *node, int *major, int *minor); char *linux_get_char_devices(void); void linux_free_get_char_devices(char *string); +int linux_xchroot(struct thread *td); #if defined(KTR) Index: sys/compat/linux/linux_util.c =================================================================== --- sys/compat/linux/linux_util.c +++ sys/compat/linux/linux_util.c @@ -66,6 +66,49 @@ linux_emul_path, sizeof(linux_emul_path), "Linux runtime environment path"); +int +linux_xchroot(struct thread *td) +{ + struct nameidata nd; + int error; + + if (!linux_autochroot) + return (0); + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, + UIO_SYSSPACE, "/compat/ubuntu", td); + error = namei(&nd); + if (error != 0) { + printf("%s: namei returned %d\n", __func__, error); + goto error; + } + error = change_dir(nd.ni_vp, td); + if (error != 0) { + printf("%s: change_dir returned %d\n", __func__, error); + goto e_vunlock; + } +#if 0 +#ifdef MAC + error = mac_vnode_check_chroot(td->td_ucred, nd.ni_vp); + if (error != 0) { + printf("%s: mac_vnode_check_chroot returned %d\n", __func__, error); + goto e_vunlock; + } +#endif +#endif + VOP_UNLOCK(nd.ni_vp); + error = pwd_xchroot(td, nd.ni_vp); + vrele(nd.ni_vp); + NDFREE(&nd, NDF_ONLY_PNBUF); + return (0); +e_vunlock: + vput(nd.ni_vp); +error: + NDFREE(&nd, NDF_ONLY_PNBUF); + return (0); +} + + /* * Search an alternate path before passing pathname arguments on to * system calls. Useful for keeping a separate 'emulation tree'. @@ -77,7 +120,27 @@ linux_emul_convpath(struct thread *td, const char *path, enum uio_seg pathseg, char **pbuf, int cflag, int dfd) { - int retval; + char *buf; + int error, retval; + + if (linux_autochroot) { + buf = (char *) malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + + /* Append the filename to the prefix. */ + if (pathseg == UIO_SYSSPACE) + error = copystr(path, buf, MAXPATHLEN, NULL); + else + error = copyinstr(path, buf, MAXPATHLEN, NULL); + + if (error != 0) { + free(buf, M_TEMP); + *pbuf = NULL; + } else { + *pbuf = buf; + } + + return (error); + } retval = kern_alternate_path(td, linux_emul_path, path, pathseg, pbuf, cflag, dfd); Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -3424,6 +3424,37 @@ return (0); } +int +pwd_xchroot(struct thread *td, struct vnode *vp) +{ + struct filedesc *fdp; + struct pwd *newpwd, *oldpwd; + + fdp = td->td_proc->p_fd; + newpwd = pwd_alloc(); + FILEDESC_XLOCK(fdp); + oldpwd = FILEDESC_XLOCKED_LOAD_PWD(fdp); + if (oldpwd->pwd_jdir != NULL || + oldpwd->pwd_rdir != rootvnode) { + printf("%s: jailed or chrooted\n", __func__); + FILEDESC_XUNLOCK(fdp); + pwd_drop(newpwd); + return (EBUSY); + } + + vrefact(vp); + newpwd->pwd_rdir = vp; + if (oldpwd->pwd_jdir == NULL) { + vrefact(vp); + newpwd->pwd_jdir = vp; + } + pwd_fill(oldpwd, newpwd); + pwd_set(fdp, newpwd); + FILEDESC_XUNLOCK(fdp); + pwd_drop(oldpwd); + return (0); +} + void pwd_chdir(struct thread *td, struct vnode *vp) { Index: sys/sys/filedesc.h =================================================================== --- sys/sys/filedesc.h +++ sys/sys/filedesc.h @@ -297,6 +297,7 @@ /* cdir/rdir/jdir manipulation functions. */ void pwd_chdir(struct thread *td, struct vnode *vp); int pwd_chroot(struct thread *td, struct vnode *vp); +int pwd_xchroot(struct thread *td, struct vnode *vp); void pwd_ensure_dirs(void); void pwd_set_rootvnode(void);