Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 224712) +++ sys/kern/kern_jail.c (working copy) @@ -531,6 +531,7 @@ int gotchildmax, gotenforce, gothid, gotslevel; int fi, jid, jsys, len, level; int childmax, slevel, vfslocked; + int disablefullpath = 0; #if defined(INET) || defined(INET6) int ii, ij; #endif @@ -897,30 +898,40 @@ error = EINVAL; goto done_free; } - if (len < 2 || (len == 2 && path[0] == '/')) - path = NULL; - else { + NDINIT(&nd, LOOKUP, + FOLLOW | LOCKLEAF | MPSAFE, UIO_SYSSPACE, + path, td); + error = namei(&nd); + if (error) + goto done_free; + vfslocked = NDHASGIANT(&nd); + root = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + error = vn_path_to_global_path(td, root, path, + MAXPATHLEN); + if (error == ENODEV) { + disablefullpath = 1; + if (len < 2 || (len == 2 && path[0] == '/')) + path = NULL; + } else if (error != 0) { + VFS_UNLOCK_GIANT(vfslocked); + goto done_free; + } + VOP_UNLOCK(root, 0); + if (root->v_type != VDIR) { + error = ENOTDIR; + vrele(root); + VFS_UNLOCK_GIANT(vfslocked); + goto done_free; + } + VFS_UNLOCK_GIANT(vfslocked); + if (disablefullpath) { /* Leave room for a real-root full pathname. */ if (len + (path[0] == '/' && strcmp(mypr->pr_path, "/") ? strlen(mypr->pr_path) : 0) > MAXPATHLEN) { error = ENAMETOOLONG; goto done_free; } - NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW, UIO_SYSSPACE, - path, td); - error = namei(&nd); - if (error) - goto done_free; - vfslocked = NDHASGIANT(&nd); - root = nd.ni_vp; - NDFREE(&nd, NDF_ONLY_PNBUF); - if (root->v_type != VDIR) { - error = ENOTDIR; - vrele(root); - VFS_UNLOCK_GIANT(vfslocked); - goto done_free; - } - VFS_UNLOCK_GIANT(vfslocked); } } @@ -1583,7 +1594,8 @@ } if (path != NULL) { /* Try to keep a real-rooted full pathname. */ - if (path[0] == '/' && strcmp(mypr->pr_path, "/")) + if (disablefullpath && + path[0] == '/' && strcmp(mypr->pr_path, "/")) snprintf(pr->pr_path, sizeof(pr->pr_path), "%s%s", mypr->pr_path, path); else Index: sys/kern/vfs_mount.c =================================================================== --- sys/kern/vfs_mount.c (revision 224712) +++ sys/kern/vfs_mount.c (working copy) @@ -1070,16 +1070,14 @@ NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if ((fsflags & MNT_UPDATE) == 0) { - error = vfs_domount_first(td, vfsp, fspath, vp, fsflags, - optlist); - } else { + error = vn_path_to_global_path(td, vp, fspath, MNAMELEN); + if (error == 0 || error == ENODEV) + error = vfs_domount_first(td, vfsp, fspath, vp, + fsflags, optlist); + } else error = vfs_domount_update(td, vp, fsflags, optlist); - } mtx_unlock(&Giant); - ASSERT_VI_UNLOCKED(vp, __func__); - ASSERT_VOP_UNLOCKED(vp, __func__); - return (error); } @@ -1105,6 +1103,7 @@ } */ *uap; { struct mount *mp; + struct nameidata nd; char *pathbuf; int error, id0, id1; @@ -1140,6 +1139,25 @@ mtx_unlock(&mountlist_mtx); } else { AUDIT_ARG_UPATH1(td, pathbuf); + /* + * If we are jailed and this is not a root jail try to find + * global path for path argument. + */ + if (jailed(td->td_ucred) && + td->td_ucred->cr_prison->pr_root != rootvnode) { + NDINIT(&nd, LOOKUP, + FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, pathbuf, td); + if (namei(&nd) == 0) { + if (NDHASGIANT(&nd)) + mtx_unlock(&Giant); + NDFREE(&nd, NDF_ONLY_PNBUF); + error = vn_path_to_global_path(td, nd.ni_vp, + pathbuf, MNAMELEN); + if (error == 0 || error == ENODEV) + vput(nd.ni_vp); + } + } mtx_lock(&mountlist_mtx); TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) { if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0) Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c (revision 224712) +++ sys/kern/vfs_cache.c (working copy) @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -908,7 +909,6 @@ return (error); } - #ifndef _SYS_SYSPROTO_H_ struct __getcwd_args { u_char *buf; @@ -1246,3 +1246,64 @@ buf[l] = '\0'; return (0); } + +/* + * This function updates path to vnode's full global path + * and checks maximum allowed path size against pathlen. + * Requires a locked vnode, leaves vnode locked on success or + * on ENODEV (debug.disablefullpath == 1) and unlocked on failure. + */ +int +vn_path_to_global_path(struct thread *td, struct vnode *vn, char *path, + u_int pathlen) +{ + struct nameidata nd; + struct vnode *vn1; + char *rpath, *fbuf; + int error; + + ASSERT_VOP_ELOCKED(vp, __func__); + + if (disablefullpath) + return (ENODEV); + + /* Construct global filesystem path from vp. */ + VOP_UNLOCK(vn, 0); + error = vn_fullpath_global(td, vn, &rpath, &fbuf); + if (error != 0) { + vrele(vn); + return (error); + } + if (strlen(rpath) >= pathlen) { + vrele(vn); + error = ENAMETOOLONG; + goto out; + } + + /* + * Re-lookup the vnode by path. As a side effect, the vnode is + * relocked. If vnode was renamed, return ENOENT. + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, path, td); + error = namei(&nd); + if (error != 0) { + vrele(vn); + goto out; + } + if (NDHASGIANT(&nd)) + mtx_unlock(&Giant); + NDFREE(&nd, NDF_ONLY_PNBUF); + vn1 = nd.ni_vp; + vrele(vn); + if (vn1 != vn) { + vput(vn1); + error = ENOENT; + goto out; + } + + strlcpy(path,rpath,pathlen); +out: + free(fbuf, M_TEMP); + return (error); +} Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h (revision 224699) +++ sys/sys/vnode.h (working copy) @@ -604,6 +604,8 @@ int vn_fullpath_global(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf); int vn_commname(struct vnode *vn, char *buf, u_int buflen); +int vn_path_to_global_path(struct thread *td, struct vnode *vn, + char *path, u_int pathlen); int vaccess(enum vtype type, mode_t file_mode, uid_t file_uid, gid_t file_gid, accmode_t accmode, struct ucred *cred, int *privused);