Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Jan 2012 12:08:20 +0000 (UTC)
From:      Martin Matuska <mm@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r230129 - in head/sys: kern sys
Message-ID:  <201201151208.q0FC8KAh082348@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mm
Date: Sun Jan 15 12:08:20 2012
New Revision: 230129
URL: http://svn.freebsd.org/changeset/base/230129

Log:
  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

Modified:
  head/sys/kern/kern_jail.c
  head/sys/kern/vfs_cache.c
  head/sys/kern/vfs_mount.c
  head/sys/sys/vnode.h

Modified: head/sys/kern/kern_jail.c
==============================================================================
--- head/sys/kern/kern_jail.c	Sun Jan 15 09:27:00 2012	(r230128)
+++ head/sys/kern/kern_jail.c	Sun Jan 15 12:08:20 2012	(r230129)
@@ -531,6 +531,7 @@ kern_jail_set(struct thread *td, struct 
 	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 
 			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 
 	}
 	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

Modified: head/sys/kern/vfs_cache.c
==============================================================================
--- head/sys/kern/vfs_cache.c	Sun Jan 15 09:27:00 2012	(r230128)
+++ head/sys/kern/vfs_cache.c	Sun Jan 15 12:08:20 2012	(r230129)
@@ -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,
 	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);
+}

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c	Sun Jan 15 09:27:00 2012	(r230128)
+++ head/sys/kern/vfs_mount.c	Sun Jan 15 12:08:20 2012	(r230129)
@@ -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)

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h	Sun Jan 15 09:27:00 2012	(r230128)
+++ head/sys/sys/vnode.h	Sun Jan 15 12:08:20 2012	(r230129)
@@ -605,6 +605,8 @@ int	vn_fullpath(struct thread *td, struc
 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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201151208.q0FC8KAh082348>