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);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to