summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormm <mm@FreeBSD.org>2012-01-15 12:08:20 +0000
committermm <mm@FreeBSD.org>2012-01-15 12:08:20 +0000
commit9f44ed5ca827811dd8729bb0ab4397c71161fa9c (patch)
tree7cb3e09e5396eca584100f0f0377b0dd5a33d1ba
parent4642718fb42bef8b06538195461ee680c22cd1f9 (diff)
downloadFreeBSD-src-9f44ed5ca827811dd8729bb0ab4397c71161fa9c.zip
FreeBSD-src-9f44ed5ca827811dd8729bb0ab4397c71161fa9c.tar.gz
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
-rw-r--r--sys/kern/kern_jail.c50
-rw-r--r--sys/kern/vfs_cache.c74
-rw-r--r--sys/kern/vfs_mount.c29
-rw-r--r--sys/sys/vnode.h2
4 files changed, 131 insertions, 24 deletions
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 84f5575..de92dc4 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -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
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index 3c8338b..9783e80 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -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);
+}
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 873f425..9100a9c 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -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)
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 7f364ad..c8e2105 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -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);
OpenPOWER on IntegriCloud