Author: mjg
Date: Fri Jan 17 14:42:25 2020
New Revision: 356830
URL: https://svnweb.freebsd.org/changeset/base/356830

Log:
  vfs: provide F_ISUNIONSTACK as a kludge for libc
  
  Prior to introduction of this op libc's readdir would call fstatfs(2), in
  effect unnecessarily copying kilobytes of data just to check fs name and a
  mount flag.
  
  Reviewed by:  kib (previous version)
  Differential Revision:        https://reviews.freebsd.org/D23162

Modified:
  head/lib/libc/gen/opendir.c
  head/lib/libc/sys/fcntl.2
  head/sys/fs/unionfs/union_vfsops.c
  head/sys/kern/kern_descrip.c
  head/sys/sys/fcntl.h
  head/sys/sys/mount.h

Modified: head/lib/libc/gen/opendir.c
==============================================================================
--- head/lib/libc/gen/opendir.c Fri Jan 17 14:40:09 2020        (r356829)
+++ head/lib/libc/gen/opendir.c Fri Jan 17 14:42:25 2020        (r356830)
@@ -273,7 +273,25 @@ _filldir(DIR *dirp, bool use_current_pos)
        return (true);
 }
 
+static bool
+is_unionstack(int fd)
+{
+       struct statfs sfb;
+       int unionstack;
 
+       unionstack = _fcntl(fd, F_ISUNIONSTACK);
+       if (unionstack != -1)
+               return (unionstack);
+
+       /*
+        * Temporary compat for kernels which don't provide F_ISUNIONSTACK.
+        */
+       if (_fstatfs(fd, &sfb) < 0)
+               return (true);
+       return (strcmp(sfb.f_fstypename, "unionfs") == 0 ||
+           (sfb.f_flags & MNT_UNION));
+}
+
 /*
  * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
  */
@@ -312,12 +330,7 @@ __opendir_common(int fd, int flags, bool use_current_p
         */
        unionstack = false;
        if (flags & DTF_NODUP) {
-               struct statfs sfb;
-
-               if (_fstatfs(fd, &sfb) == 0) {
-                       unionstack = strcmp(sfb.f_fstypename, "unionfs") == 0 ||
-                           (sfb.f_flags & MNT_UNION);
-               }
+               unionstack = is_unionstack(fd);
        }
 
        if (unionstack) {

Modified: head/lib/libc/sys/fcntl.2
==============================================================================
--- head/lib/libc/sys/fcntl.2   Fri Jan 17 14:40:09 2020        (r356829)
+++ head/lib/libc/sys/fcntl.2   Fri Jan 17 14:42:25 2020        (r356830)
@@ -28,7 +28,7 @@
 .\"     @(#)fcntl.2    8.2 (Berkeley) 1/12/94
 .\" $FreeBSD$
 .\"
-.Dd September 4, 2019
+.Dd January 17, 2020
 .Dt FCNTL 2
 .Os
 .Sh NAME
@@ -185,6 +185,11 @@ Add seals to the file as described below, if the under
 seals.
 .It Dv F_GET_SEALS
 Get seals associated with the file, if the underlying filesystem supports 
seals.
+.It Dv F_ISUNIONSTACK
+Check if the vnode is part of a union stack (either the "union" flag from
+.Xr mount 2
+or unionfs).
+This is a hack not intended to be used outside of libc.
 .El
 .Pp
 The flags for the

Modified: head/sys/fs/unionfs/union_vfsops.c
==============================================================================
--- head/sys/fs/unionfs/union_vfsops.c  Fri Jan 17 14:40:09 2020        
(r356829)
+++ head/sys/fs/unionfs/union_vfsops.c  Fri Jan 17 14:42:25 2020        
(r356830)
@@ -296,7 +296,7 @@ unionfs_domount(struct mount *mp)
        if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) &&
            (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
                mp->mnt_flag |= MNT_LOCAL;
-       mp->mnt_kern_flag |= MNTK_NOMSYNC;
+       mp->mnt_kern_flag |= MNTK_NOMSYNC | MNTK_UNIONFS;
        MNT_IUNLOCK(mp);
 
        /*

Modified: head/sys/kern/kern_descrip.c
==============================================================================
--- head/sys/kern/kern_descrip.c        Fri Jan 17 14:40:09 2020        
(r356829)
+++ head/sys/kern/kern_descrip.c        Fri Jan 17 14:42:25 2020        
(r356830)
@@ -489,6 +489,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_
        struct filedescent *fde;
        struct proc *p;
        struct vnode *vp;
+       struct mount *mp;
        int error, flg, seals, tmp;
        uint64_t bsize;
        off_t foffset;
@@ -813,6 +814,49 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_
                        atomic_clear_int(&fp->f_flag, FRDAHEAD);
                }
                VOP_UNLOCK(vp);
+               fdrop(fp, td);
+               break;
+
+       case F_ISUNIONSTACK:
+               /*
+                * Check if the vnode is part of a union stack (either the
+                * "union" flag from mount(2) or unionfs).
+                *
+                * Prior to introduction of this op libc's readdir would call
+                * fstatfs(2), in effect unnecessarily copying kilobytes of
+                * data just to check fs name and a mount flag.
+                *
+                * Fixing the code to handle everything in the kernel instead
+                * is a non-trivial endeavor and has low priority, thus this
+                * horrible kludge facilitates the current behavior in a much
+                * cheaper manner until someone(tm) sorts this out.
+                */
+               error = fget_unlocked(fdp, fd, &cap_no_rights, &fp, NULL);
+               if (error != 0)
+                       break;
+               if (fp->f_type != DTYPE_VNODE) {
+                       fdrop(fp, td);
+                       error = EBADF;
+                       break;
+               }
+               vp = fp->f_vnode;
+               /*
+                * Since we don't prevent dooming the vnode even non-null mp
+                * found can become immediately stale. This is tolerable since
+                * mount points are type-stable (providing safe memory access)
+                * and any vfs op on this vnode going forward will return an
+                * error (meaning return value in this case is meaningless).
+                */
+               mp = (struct mount *)atomic_load_ptr(&vp->v_mount);
+               if (__predict_false(mp == NULL)) {
+                       fdrop(fp, td);
+                       error = EBADF;
+                       break;
+               }
+               td->td_retval[0] = 0;
+               if (mp->mnt_kern_flag & MNTK_UNIONFS ||
+                   mp->mnt_flag & MNT_UNION)
+                       td->td_retval[0] = 1;
                fdrop(fp, td);
                break;
 

Modified: head/sys/sys/fcntl.h
==============================================================================
--- head/sys/sys/fcntl.h        Fri Jan 17 14:40:09 2020        (r356829)
+++ head/sys/sys/fcntl.h        Fri Jan 17 14:42:25 2020        (r356830)
@@ -250,6 +250,7 @@ typedef     __pid_t         pid_t;
 #define        F_DUP2FD_CLOEXEC 18             /* Like F_DUP2FD, but 
FD_CLOEXEC is set */
 #define        F_ADD_SEALS     19
 #define        F_GET_SEALS     20
+#define        F_ISUNIONSTACK  21              /* Kludge for libc, don't use 
it. */
 
 /* Seals (F_ADD_SEALS, F_GET_SEALS). */
 #define        F_SEAL_SEAL     0x0001          /* Prevent adding sealings */

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h        Fri Jan 17 14:40:09 2020        (r356829)
+++ head/sys/sys/mount.h        Fri Jan 17 14:42:25 2020        (r356830)
@@ -414,6 +414,7 @@ void          __mnt_vnode_markerfree_lazy(struct vnode
 #define        MNTK_USES_BCACHE        0x00004000 /* FS uses the buffer cache. 
*/
 #define        MNTK_TEXT_REFS          0x00008000 /* Keep use ref for text */
 #define        MNTK_VMSETSIZE_BUG      0x00010000
+#define        MNTK_UNIONFS    0x00020000      /* A hack for F_ISUNIONSTACK */
 #define MNTK_NOASYNC   0x00800000      /* disable async */
 #define MNTK_UNMOUNT   0x01000000      /* unmount in progress */
 #define        MNTK_MWAIT      0x02000000      /* waiting for unmount to 
finish */
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to