From ac843f0d053e506cd6127f3a6368595a45140fcd Mon Sep 17 00:00:00 2001
From: Vitaliy Gusev <gusev.vitaliy@nexenta.com>
Date: Wed, 5 Dec 2012 22:26:13 +0400
Subject: [PATCH 04/30] closes #12112 rb3823 - nfs-nohide: lookup("..") for
 submount should be correct

---
 usr/src/uts/common/fs/nfs/nfs3_srv.c | 25 ++++++++++++--
 usr/src/uts/common/fs/nfs/nfs_srv.c  | 51 +++++++++++++++++++++++++---
 usr/src/uts/common/nfs/nfs.h         |  2 +-
 3 files changed, 70 insertions(+), 8 deletions(-)

diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c
index dc9164aafb..e3cf57b3a7 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c
@@ -382,6 +382,13 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
 
 	dvap = NULL;
 
+	/*
+	 * The passed argument exportinfo is released by the
+	 * caller, common_dispatch
+	 */
+	if (exi != NULL)
+		exi_hold(exi);
+
 	/*
 	 * Allow lookups from the root - the default
 	 * location of the public filehandle.
@@ -420,8 +427,19 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
 	fhp = &args->what.dir;
 	if (strcmp(args->what.name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
-		resp->status = NFS3ERR_NOENT;
-		goto out1;
+		if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
+		    (dvp->v_flag & VROOT)) {
+			/*
+			 * special case for ".." and 'nohide'exported root
+			 */
+			if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
+				resp->status = NFS3ERR_ACCES;
+				goto out1;
+			}
+		} else {
+			resp->status = NFS3ERR_NOENT;
+			goto out1;
+		}
 	}
 
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
@@ -562,6 +580,9 @@ out:
 	} else
 		resp->status = puterrno3(error);
 out1:
+	if (exi != NULL)
+		exi_rele(exi);
+
 	DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
 	    cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
 
diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c
index beb925441b..d90a2d9d40 100644
--- a/usr/src/uts/common/fs/nfs/nfs_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs_srv.c
@@ -375,6 +375,34 @@ rfs_cross_mnt(vnode_t **vpp, struct exportinfo **exip)
 	return (0);
 }
 
+/*
+ * Given mounted "dvp" and "exi", go upper mountpoint
+ * with dvp/exi correction
+ * Return 0 in success
+ */
+int
+rfs_climb_crossmnt(vnode_t **dvpp, struct exportinfo **exip, cred_t *cr)
+{
+	struct exportinfo *exi;
+	vnode_t *dvp = *dvpp;
+
+	ASSERT(dvp->v_flag & VROOT);
+
+	VN_HOLD(dvp);
+	dvp = untraverse(dvp);
+	exi = nfs_vptoexi(NULL, dvp, cr, NULL, NULL, FALSE);
+	if (exi == NULL) {
+		VN_RELE(dvp);
+		return (-1);
+	}
+
+	exi_rele(*exip);
+	*exip = exi;
+	VN_RELE(*dvpp);
+	*dvpp = dvp;
+
+	return (0);
+}
 /*
  * Directory lookup.
  * Returns an fhandle and file attributes for file name in a directory.
@@ -427,6 +455,8 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
 		}
 	}
 
+	exi_hold(exi);
+
 	/*
 	 * Not allow lookup beyond root.
 	 * If the filehandle matches a filehandle of the exi,
@@ -434,9 +464,19 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
 	 */
 	if (strcmp(da->da_name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
-		VN_RELE(dvp);
-		dr->dr_status = NFSERR_NOENT;
-		return;
+		if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
+		    (dvp->v_flag & VROOT)) {
+			/*
+			 * special case for ".." and 'nohide'exported root
+			 */
+			if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
+				error = NFSERR_ACCES;
+				goto out;
+			}
+		} else  {
+			error = NFSERR_NOENT;
+			goto out;
+		}
 	}
 
 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
@@ -444,8 +484,8 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
 	    MAXPATHLEN);
 
 	if (name == NULL) {
-		dr->dr_status = NFSERR_ACCES;
-		return;
+		error = NFSERR_ACCES;
+		goto out;
 	}
 
 	/*
@@ -503,6 +543,7 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
 		VN_RELE(vp);
 	}
 
+out:
 	VN_RELE(dvp);
 
 	/*
diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h
index ff1f67cfe6..976909e3f6 100644
--- a/usr/src/uts/common/nfs/nfs.h
+++ b/usr/src/uts/common/nfs/nfs.h
@@ -2278,7 +2278,7 @@ extern int	rfs_publicfh_mclookup(char *, vnode_t *, cred_t *, vnode_t **,
     struct exportinfo **, struct sec_ol *);
 extern int	rfs_pathname(char *, vnode_t **, vnode_t **, vnode_t *,
     cred_t *, int);
-extern int rfs_cross_mnt(vnode_t **, struct exportinfo **);
+extern int	rfs_cross_mnt(vnode_t **, struct exportinfo **);
 extern int	rfs_climb_crossmnt(vnode_t **, struct exportinfo **, cred_t *);
 
 extern vtype_t		nf3_to_vt[];
-- 
2.21.0

