Introduce vn_path_to_global_path()

This function updates path string to vnode's full global path and checks
the size of the new path string against the pathlen argument.

In vfs_domount(), sys_unmount() and kern_jail_set() this new function
is used to update the supplied path argument to the respective global path.

Unbreaks jailed zfs(8) with enforce_statfs set to 1.

Reviewed by:	kib
MFC after:	1 month
This commit is contained in:
Martin Matuska 2012-01-15 12:08:20 +00:00
parent 4347075067
commit f6e633a9e1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=230129
4 changed files with 131 additions and 24 deletions

View File

@ -531,6 +531,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
int gotchildmax, gotenforce, gothid, gotslevel;
int fi, jid, jsys, len, level;
int childmax, slevel, vfslocked;
int fullpath_disabled;
#if defined(INET) || defined(INET6)
int ii, ij;
#endif
@ -897,30 +898,40 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
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) {
/* proceed if sysctl debug.disablefullpath == 1 */
fullpath_disabled = 1;
if (len < 2 || (len == 2 && path[0] == '/'))
path = NULL;
} else if (error != 0) {
/* exit on other errors */
VFS_UNLOCK_GIANT(vfslocked);
goto done_free;
}
if (root->v_type != VDIR) {
error = ENOTDIR;
vput(root);
VFS_UNLOCK_GIANT(vfslocked);
goto done_free;
}
VOP_UNLOCK(root, 0);
VFS_UNLOCK_GIANT(vfslocked);
if (fullpath_disabled) {
/* 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 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
}
if (path != NULL) {
/* Try to keep a real-rooted full pathname. */
if (path[0] == '/' && strcmp(mypr->pr_path, "/"))
if (fullpath_disabled && path[0] == '/' &&
strcmp(mypr->pr_path, "/"))
snprintf(pr->pr_path, sizeof(pr->pr_path), "%s%s",
mypr->pr_path, path);
else

View File

@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/proc.h>
@ -1278,3 +1279,76 @@ vn_commname(struct vnode *vp, char *buf, u_int buflen)
buf[l] = '\0';
return (0);
}
/*
* This function updates path string to vnode's full global path
* and checks the size of the new path string against the pathlen argument.
*
* Requires a locked, referenced vnode and GIANT lock held.
* Vnode is re-locked on success or ENODEV, otherwise unlocked.
*
* If sysctl debug.disablefullpath is set, ENODEV is returned,
* vnode is left locked and path remain untouched.
*
* If vp is a directory, the call to vn_fullpath_global() always succeeds
* because it falls back to the ".." lookup if the namecache lookup fails
*/
int
vn_path_to_global_path(struct thread *td, struct vnode *vp, char *path,
u_int pathlen)
{
struct nameidata nd;
struct vnode *vp1;
char *rpath, *fbuf;
int error, vfslocked;
VFS_ASSERT_GIANT(vp->v_mount);
ASSERT_VOP_ELOCKED(vp, __func__);
/* Return ENODEV if sysctl debug.disablefullpath==1 */
if (disablefullpath)
return (ENODEV);
/* Construct global filesystem path from vp. */
VOP_UNLOCK(vp, 0);
error = vn_fullpath_global(td, vp, &rpath, &fbuf);
if (error != 0) {
vrele(vp);
return (error);
}
if (strlen(rpath) >= pathlen) {
vrele(vp);
error = ENAMETOOLONG;
goto out;
}
/*
* Re-lookup the vnode by path to detect a possible rename.
* 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(vp);
goto out;
}
vfslocked = NDHASGIANT(&nd);
NDFREE(&nd, NDF_ONLY_PNBUF);
vp1 = nd.ni_vp;
vrele(vp);
if (vp1 == vp)
strcpy(path, rpath);
else {
vput(vp1);
error = ENOENT;
}
VFS_UNLOCK_GIANT(vfslocked);
out:
free(fbuf, M_TEMP);
return (error);
}

View File

@ -1085,11 +1085,14 @@ vfs_domount(
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);
/* debug.disablefullpath == 1 results in ENODEV */
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__);
@ -1119,9 +1122,10 @@ sys_unmount(td, uap)
int flags;
} */ *uap;
{
struct nameidata nd;
struct mount *mp;
char *pathbuf;
int error, id0, id1;
int error, id0, id1, vfslocked;
AUDIT_ARG_VALUE(uap->flags);
if (jailed(td->td_ucred) || usermount == 0) {
@ -1155,6 +1159,21 @@ sys_unmount(td, uap)
mtx_unlock(&mountlist_mtx);
} else {
AUDIT_ARG_UPATH1(td, pathbuf);
/*
* Try to find global path for path argument.
*/
NDINIT(&nd, LOOKUP,
FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
UIO_SYSSPACE, pathbuf, td);
if (namei(&nd) == 0) {
vfslocked = NDHASGIANT(&nd);
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);
VFS_UNLOCK_GIANT(vfslocked);
}
mtx_lock(&mountlist_mtx);
TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0)

View File

@ -605,6 +605,8 @@ int vn_fullpath(struct thread *td, struct vnode *vn,
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 *vp,
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);