svn commit: r340343 - in head/sys: kern sys

Konstantin Belousov kib at FreeBSD.org
Sun Nov 11 00:04:37 UTC 2018


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.


More information about the svn-src-head mailing list