Author: mm
Date: Tue Feb 14 17:09:20 2012
New Revision: 231692
URL: http://svn.freebsd.org/changeset/base/231692

Log:
  MFC r230129,r230143,r230407,r231012:
  
  MFC r320129 [1]:
  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.
  
  MFC r230143 [2]:
  Fix missing in r230129:
  
  kern_jail.c: initialize fullpath_disabled to zero
  vfs_cache.c: add missing dot in comment
  
  MFC r230407 [3]:
  Use separate buffer for global path to avoid overflow of path buffer.
  
  MFC r231012:
  Analogous to r230407 a separate path buffer in vfs_mount.c is required
  for r230129. Fixes a out of bounds write to fspath.
  
  Reviewed by:  kib [1] [2], jamie [3]

Modified:
  stable/9/sys/kern/kern_jail.c
  stable/9/sys/kern/vfs_cache.c
  stable/9/sys/kern/vfs_mount.c
  stable/9/sys/sys/vnode.h
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/kern/kern_jail.c
==============================================================================
--- stable/9/sys/kern/kern_jail.c       Tue Feb 14 16:47:52 2012        
(r231691)
+++ stable/9/sys/kern/kern_jail.c       Tue Feb 14 17:09:20 2012        
(r231692)
@@ -521,6 +521,7 @@ kern_jail_set(struct thread *td, struct 
        struct prison *pr, *deadpr, *mypr, *ppr, *tpr;
        struct vnode *root;
        char *domain, *errmsg, *host, *name, *namelc, *p, *path, *uuid;
+       char *g_path;
 #if defined(INET) || defined(INET6)
        struct prison *tppr;
        void *op;
@@ -531,6 +532,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
@@ -574,6 +576,7 @@ kern_jail_set(struct thread *td, struct 
 #ifdef INET6
        ip6 = NULL;
 #endif
+       g_path = NULL;
 
        error = vfs_copyopt(opts, "jid", &jid, sizeof(jid));
        if (error == ENOENT)
@@ -880,6 +883,7 @@ kern_jail_set(struct thread *td, struct 
        }
 #endif
 
+       fullpath_disabled = 0;
        root = NULL;
        error = vfs_getopt(opts, "path", (void **)&path, &len);
        if (error == ENOENT)
@@ -897,30 +901,44 @@ 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);
+               g_path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+               strlcpy(g_path, path, MAXPATHLEN);
+               error = vn_path_to_global_path(td, root, g_path, MAXPATHLEN);
+               if (error == 0)
+                       path = g_path;
+               else if (error == ENODEV) {
+                       /* proceed if sysctl debug.disablefullpath == 1 */
+                       fullpath_disabled = 1;
+                       if (len < 2 || (len == 2 && path[0] == '/'))
+                               path = NULL;
+               } else {
+                       /* 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 +1601,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
@@ -1806,6 +1825,8 @@ kern_jail_set(struct thread *td, struct 
 #ifdef INET6
        free(ip6, M_PRISON);
 #endif
+       if (g_path != NULL)
+               free(g_path, M_TEMP);
        vfs_freeopts(opts);
        return (error);
 }

Modified: stable/9/sys/kern/vfs_cache.c
==============================================================================
--- stable/9/sys/kern/vfs_cache.c       Tue Feb 14 16:47:52 2012        
(r231691)
+++ stable/9/sys/kern/vfs_cache.c       Tue Feb 14 17:09:20 2012        
(r231692)
@@ -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>
@@ -1277,3 +1278,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: stable/9/sys/kern/vfs_mount.c
==============================================================================
--- stable/9/sys/kern/vfs_mount.c       Tue Feb 14 16:47:52 2012        
(r231691)
+++ stable/9/sys/kern/vfs_mount.c       Tue Feb 14 17:09:20 2012        
(r231692)
@@ -1024,6 +1024,7 @@ vfs_domount(
        struct vfsconf *vfsp;
        struct nameidata nd;
        struct vnode *vp;
+       char *pathbuf;
        int error;
 
        /*
@@ -1087,11 +1088,17 @@ 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 {
+               pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
+               strcpy(pathbuf, fspath);
+               error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
+               /* debug.disablefullpath == 1 results in ENODEV */
+               if (error == 0 || error == ENODEV) {
+                       error = vfs_domount_first(td, vfsp, pathbuf, vp,
+                           fsflags, optlist);
+               }
+               free(pathbuf, M_TEMP);
+       } else
                error = vfs_domount_update(td, vp, fsflags, optlist);
-       }
        mtx_unlock(&Giant);
 
        ASSERT_VI_UNLOCKED(vp, __func__);
@@ -1121,9 +1128,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) {
@@ -1157,6 +1165,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: stable/9/sys/sys/vnode.h
==============================================================================
--- stable/9/sys/sys/vnode.h    Tue Feb 14 16:47:52 2012        (r231691)
+++ stable/9/sys/sys/vnode.h    Tue Feb 14 17:09:20 2012        (r231692)
@@ -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-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to