Author: kib
Date: Sun Nov 11 00:04:36 2018
New Revision: 340343
URL: https://svnweb.freebsd.org/changeset/base/340343

Log:
  Allow absolute paths for O_BENEATH.
  
  The path must have a tail which does not escape starting/topping
  directory.  The documentation will come shortly, see the man pages
  commit message for the reason of separate commit.
  
  Reviewed by:  jilles (previous version)
  Discussed with:       emaste
  Tested by:    pho
  Sponsored by: The FreeBSD Foundation
  MFC after:    1 week
  Differential revision:        https://reviews.freebsd.org/D17714

Modified:
  head/sys/kern/vfs_lookup.c
  head/sys/sys/namei.h

Modified: head/sys/kern/vfs_lookup.c
==============================================================================
--- head/sys/kern/vfs_lookup.c  Sat Nov 10 23:49:01 2018        (r340342)
+++ head/sys/kern/vfs_lookup.c  Sun Nov 11 00:04:36 2018        (r340343)
@@ -177,6 +177,13 @@ nameicap_tracker_add(struct nameidata *ndp, struct vno
 
        if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0 || dp->v_type != VDIR)
                return;
+       if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | NI_LCF_BENEATH_LATCHED)) ==
+           NI_LCF_BENEATH_ABS) {
+               MPASS((ndp->ni_lcf & NI_LCF_LATCH) != 0);
+               if (dp != ndp->ni_beneath_latch)
+                       return;
+               ndp->ni_lcf |= NI_LCF_BENEATH_LATCHED;
+       }
        nt = uma_zalloc(nt_zone, M_WAITOK);
        vhold(dp);
        nt->dp = dp;
@@ -184,7 +191,7 @@ nameicap_tracker_add(struct nameidata *ndp, struct vno
 }
 
 static void
-nameicap_cleanup(struct nameidata *ndp)
+nameicap_cleanup(struct nameidata *ndp, bool clean_latch)
 {
        struct nameicap_tracker *nt, *nt1;
 
@@ -195,6 +202,8 @@ nameicap_cleanup(struct nameidata *ndp)
                vdrop(nt->dp);
                uma_zfree(nt_zone, nt);
        }
+       if (clean_latch && (ndp->ni_lcf & NI_LCF_LATCH) != 0)
+               vrele(ndp->ni_beneath_latch);
 }
 
 /*
@@ -222,6 +231,11 @@ nameicap_check_dotdot(struct nameidata *ndp, struct vn
                if (dp == nt->dp)
                        return (0);
        }
+       if ((ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) {
+               ndp->ni_lcf &= ~NI_LCF_BENEATH_LATCHED;
+               nameicap_cleanup(ndp, false);
+               return (0);
+       }
        return (ENOTCAPABLE);
 }
 
@@ -242,14 +256,18 @@ namei_handle_root(struct nameidata *ndp, struct vnode 
        struct componentname *cnp;
 
        cnp = &ndp->ni_cnd;
-       if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 ||
-           (cnp->cn_flags & BENEATH) != 0) {
+       if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0) {
 #ifdef KTRACE
                if (KTRPOINT(curthread, KTR_CAPFAIL))
                        ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL);
 #endif
                return (ENOTCAPABLE);
        }
+       if ((cnp->cn_flags & BENEATH) != 0) {
+               ndp->ni_lcf |= NI_LCF_BENEATH_ABS;
+               ndp->ni_lcf &= ~NI_LCF_BENEATH_LATCHED;
+               nameicap_cleanup(ndp, false);
+       }
        while (*(cnp->cn_nameptr) == '/') {
                cnp->cn_nameptr++;
                ndp->ni_pathlen--;
@@ -290,6 +308,7 @@ namei(struct nameidata *ndp)
        struct thread *td;
        struct proc *p;
        cap_rights_t rights;
+       struct filecaps dirfd_caps;
        struct uio auio;
        int error, linklen, startdir_used;
 
@@ -427,6 +446,23 @@ namei(struct nameidata *ndp)
                if (error == 0 && dp->v_type != VDIR)
                        error = ENOTDIR;
        }
+       if (error == 0 && (ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) {
+               if (ndp->ni_dirfd == AT_FDCWD) {
+                       ndp->ni_beneath_latch = fdp->fd_cdir;
+                       vrefact(ndp->ni_beneath_latch);
+               } else {
+                       rights = ndp->ni_rightsneeded;
+                       cap_rights_set(&rights, CAP_LOOKUP);
+                       error = fgetvp_rights(td, ndp->ni_dirfd, &rights,
+                           &dirfd_caps, &ndp->ni_beneath_latch);
+                       if (error == 0 && dp->v_type != VDIR) {
+                               vrele(ndp->ni_beneath_latch);
+                               error = ENOTDIR;
+                       }
+               }
+               if (error == 0)
+                       ndp->ni_lcf |= NI_LCF_LATCH;
+       }
        FILEDESC_SUNLOCK(fdp);
        if (ndp->ni_startdir != NULL && !startdir_used)
                vrele(ndp->ni_startdir);
@@ -456,9 +492,15 @@ namei(struct nameidata *ndp)
                                namei_cleanup_cnp(cnp);
                        } else
                                cnp->cn_flags |= HASBUF;
-                       nameicap_cleanup(ndp);
-                       SDT_PROBE2(vfs, namei, lookup, return, 0, ndp->ni_vp);
-                       return (0);
+                       if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS |
+                           NI_LCF_BENEATH_LATCHED)) == NI_LCF_BENEATH_ABS) {
+                               NDFREE(ndp, 0);
+                               error = ENOTCAPABLE;
+                       }
+                       nameicap_cleanup(ndp, true);
+                       SDT_PROBE2(vfs, namei, lookup, return, error,
+                           (error == 0 ? ndp->ni_vp : NULL));
+                       return (error);
                }
                if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
                        error = ELOOP;
@@ -529,8 +571,9 @@ namei(struct nameidata *ndp)
        vrele(ndp->ni_dvp);
 out:
        vrele(ndp->ni_rootdir);
+       MPASS(error != 0);
        namei_cleanup_cnp(cnp);
-       nameicap_cleanup(ndp);
+       nameicap_cleanup(ndp, true);
        SDT_PROBE2(vfs, namei, lookup, return, error, NULL);
        return (error);
 }

Modified: head/sys/sys/namei.h
==============================================================================
--- head/sys/sys/namei.h        Sat Nov 10 23:49:01 2018        (r340342)
+++ head/sys/sys/namei.h        Sun Nov 11 00:04:36 2018        (r340343)
@@ -100,6 +100,7 @@ struct nameidata {
         */
        struct componentname ni_cnd;
        struct nameicap_tracker_head ni_cap_tracker;
+       struct vnode *ni_beneath_latch;
 };
 
 #ifdef _KERNEL
@@ -163,6 +164,9 @@ struct nameidata {
  */
 #define        NI_LCF_STRICTRELATIVE   0x0001  /* relative lookup only */
 #define        NI_LCF_CAP_DOTDOT       0x0002  /* ".." in strictrelative case 
*/
+#define        NI_LCF_BENEATH_ABS      0x0004  /* BENEATH with absolute path */
+#define        NI_LCF_BENEATH_LATCHED  0x0008  /* BENEATH_ABS traversed 
starting dir */
+#define        NI_LCF_LATCH            0x0010  /* ni_beneath_latch valid */
 
 /*
  * Initialization of a nameidata structure.
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to