Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/fs/nfsd/export.c.save linux-2.6.32.i686/fs/nfsd/export.c
Jesse Keating 2f82dda
--- linux-2.6.32.i686/fs/nfsd/export.c.save	2009-12-04 10:24:17.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/fs/nfsd/export.c	2009-12-04 10:40:52.000000000 -0500
Jesse Keating 2f82dda
@@ -372,10 +372,12 @@ static struct svc_export *svc_export_loo
Jesse Keating 2f82dda
 static int check_export(struct inode *inode, int flags, unsigned char *uuid)
Jesse Keating 2f82dda
 {
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-	/* We currently export only dirs and regular files.
Jesse Keating 2f82dda
-	 * This is what umountd does.
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * We currently export only dirs, regular files, and (for v4
Jesse Keating 2f82dda
+	 * pseudoroot) symlinks.
Jesse Keating 2f82dda
 	 */
Jesse Keating 2f82dda
 	if (!S_ISDIR(inode->i_mode) &&
Jesse Keating 2f82dda
+	    !S_ISLNK(inode->i_mode) &&
Jesse Keating 2f82dda
 	    !S_ISREG(inode->i_mode))
Jesse Keating 2f82dda
 		return -ENOTDIR;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -1425,6 +1427,7 @@ static struct flags {
Jesse Keating 2f82dda
 	{ NFSEXP_CROSSMOUNT, {"crossmnt", ""}},
Jesse Keating 2f82dda
 	{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
Jesse Keating 2f82dda
 	{ NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
Jesse Keating 2f82dda
+	{ NFSEXP_V4ROOT, {"v4root", ""}},
Jesse Keating 2f82dda
 #ifdef MSNFS
Jesse Keating 2f82dda
 	{ NFSEXP_MSNFS, {"msnfs", ""}},
Jesse Keating 2f82dda
 #endif
Jesse Keating 2f82dda
@@ -1505,7 +1508,7 @@ static int e_show(struct seq_file *m, vo
Jesse Keating 2f82dda
 	struct svc_export *exp = container_of(cp, struct svc_export, h);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	if (p == SEQ_START_TOKEN) {
Jesse Keating 2f82dda
-		seq_puts(m, "# Version 1.1\n");
Jesse Keating 2f82dda
+		seq_puts(m, "# Version 1.2\n");
Jesse Keating 2f82dda
 		seq_puts(m, "# Path Client(Flags) # IPs\n");
Jesse Keating 2f82dda
 		return 0;
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/fs/nfsd/nfs4xdr.c.save linux-2.6.32.i686/fs/nfsd/nfs4xdr.c
Jesse Keating 2f82dda
--- linux-2.6.32.i686/fs/nfsd/nfs4xdr.c.save	2009-12-04 10:24:17.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/fs/nfsd/nfs4xdr.c	2009-12-04 10:26:49.000000000 -0500
Jesse Keating 2f82dda
@@ -2204,11 +2204,14 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r
Jesse Keating 2f82dda
 	 * we will not follow the cross mount and will fill the attribtutes
Jesse Keating 2f82dda
 	 * directly from the mountpoint dentry.
Jesse Keating 2f82dda
 	 */
Jesse Keating 2f82dda
-	if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval))
Jesse Keating 2f82dda
-		ignore_crossmnt = 1;
Jesse Keating 2f82dda
-	else if (d_mountpoint(dentry)) {
Jesse Keating 2f82dda
+	if (nfsd_mountpoint(dentry, exp)) {
Jesse Keating 2f82dda
 		int err;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+		if (!(exp->ex_flags & NFSEXP_V4ROOT)
Jesse Keating 2f82dda
+				&& !attributes_need_mount(cd->rd_bmval)) {
Jesse Keating 2f82dda
+			ignore_crossmnt = 1;
Jesse Keating 2f82dda
+			goto out_encode;
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
 		/*
Jesse Keating 2f82dda
 		 * Why the heck aren't we just using nfsd_lookup??
Jesse Keating 2f82dda
 		 * Different "."/".." handling?  Something else?
Jesse Keating 2f82dda
@@ -2224,6 +2227,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r
Jesse Keating 2f82dda
 			goto out_put;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
+out_encode:
Jesse Keating 2f82dda
 	nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
Jesse Keating 2f82dda
 					cd->rd_rqstp, ignore_crossmnt);
Jesse Keating 2f82dda
 out_put:
Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/fs/nfsd/nfsfh.c.save linux-2.6.32.i686/fs/nfsd/nfsfh.c
Jesse Keating 2f82dda
--- linux-2.6.32.i686/fs/nfsd/nfsfh.c.save	2009-12-04 10:24:17.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/fs/nfsd/nfsfh.c	2009-12-04 10:38:26.000000000 -0500
Jesse Keating 2f82dda
@@ -109,6 +109,36 @@ static __be32 nfsd_setuser_and_check_por
Jesse Keating 2f82dda
 	return nfserrno(nfsd_setuser(rqstp, exp));
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
Jesse Keating 2f82dda
+	struct dentry *dentry, struct svc_export *exp)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	if (!(exp->ex_flags & NFSEXP_V4ROOT))
Jesse Keating 2f82dda
+		return nfs_ok;
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * v2/v3 clients have no need for the V4ROOT export--they use
Jesse Keating 2f82dda
+	 * the mount protocl instead; also, further V4ROOT checks may be
Jesse Keating 2f82dda
+	 * in v4-specific code, in which case v2/v3 clients could bypass
Jesse Keating 2f82dda
+	 * them.
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	if (!nfsd_v4client(rqstp))
Jesse Keating 2f82dda
+		return nfserr_stale;
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * We're exposing only the directories and symlinks that have to be
Jesse Keating 2f82dda
+	 * traversed on the way to real exports:
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
Jesse Keating 2f82dda
+		     !S_ISLNK(dentry->d_inode->i_mode)))
Jesse Keating 2f82dda
+		return nfserr_stale;
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * A pseudoroot export gives permission to access only one
Jesse Keating 2f82dda
+	 * single directory; the kernel has to make another upcall
Jesse Keating 2f82dda
+	 * before granting access to anything else under it:
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	if (unlikely(dentry != exp->ex_path.dentry))
Jesse Keating 2f82dda
+		return nfserr_stale;
Jesse Keating 2f82dda
+	return nfs_ok;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 /*
Jesse Keating 2f82dda
  * Use the given filehandle to look up the corresponding export and
Jesse Keating 2f82dda
  * dentry.  On success, the results are used to set fh_export and
Jesse Keating 2f82dda
@@ -317,6 +347,13 @@ fh_verify(struct svc_rqst *rqstp, struct
Jesse Keating 2f82dda
 			goto out;
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * Do some spoof checking if we are on the pseudo root
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	error = check_pseudo_root(rqstp, dentry, exp);
Jesse Keating 2f82dda
+	if (error)
Jesse Keating 2f82dda
+		goto out;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
Jesse Keating 2f82dda
 	if (error)
Jesse Keating 2f82dda
 		goto out;
Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/fs/nfsd/vfs.c.save linux-2.6.32.i686/fs/nfsd/vfs.c
Jesse Keating 2f82dda
--- linux-2.6.32.i686/fs/nfsd/vfs.c.save	2009-12-04 10:24:18.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/fs/nfsd/vfs.c	2009-12-04 10:35:04.000000000 -0500
Jesse Keating 2f82dda
@@ -89,12 +89,6 @@ struct raparm_hbucket {
Jesse Keating 2f82dda
 #define RAPARM_HASH_MASK	(RAPARM_HASH_SIZE-1)
Jesse Keating 2f82dda
 static struct raparm_hbucket	raparm_hash[RAPARM_HASH_SIZE];
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-static inline int
Jesse Keating 2f82dda
-nfsd_v4client(struct svc_rqst *rq)
Jesse Keating 2f82dda
-{
Jesse Keating 2f82dda
-    return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
Jesse Keating 2f82dda
-}
Jesse Keating 2f82dda
-
Jesse Keating 2f82dda
 /* 
Jesse Keating 2f82dda
  * Called from nfsd_lookup and encode_dirent. Check if we have crossed 
Jesse Keating 2f82dda
  * a mount point.
Jesse Keating 2f82dda
@@ -116,8 +110,16 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, s
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	exp2 = rqst_exp_get_by_name(rqstp, &path);
Jesse Keating 2f82dda
 	if (IS_ERR(exp2)) {
Jesse Keating 2f82dda
-		if (PTR_ERR(exp2) != -ENOENT)
Jesse Keating 2f82dda
-			err = PTR_ERR(exp2);
Jesse Keating 2f82dda
+		err = PTR_ERR(exp2);
Jesse Keating 2f82dda
+		/*
Jesse Keating 2f82dda
+		 * We normally allow NFS clients to continue
Jesse Keating 2f82dda
+		 * "underneath" a mountpoint that is not exported.
Jesse Keating 2f82dda
+		 * The exception is V4ROOT, where no traversal is ever
Jesse Keating 2f82dda
+		 * allowed without an explicit export of the new
Jesse Keating 2f82dda
+		 * directory.
Jesse Keating 2f82dda
+		 */
Jesse Keating 2f82dda
+		if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT))
Jesse Keating 2f82dda
+			err = 0;
Jesse Keating 2f82dda
 		path_put(&path);
Jesse Keating 2f82dda
 		goto out;
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
@@ -141,6 +143,19 @@ out:
Jesse Keating 2f82dda
 	return err;
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+/*
Jesse Keating 2f82dda
+ * For nfsd purposes, we treat V4ROOT exports as though there was an
Jesse Keating 2f82dda
+ * export at *every* directory.
Jesse Keating 2f82dda
+ */
Jesse Keating 2f82dda
+int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	if (d_mountpoint(dentry))
Jesse Keating 2f82dda
+		return 1;
Jesse Keating 2f82dda
+	if (!(exp->ex_flags & NFSEXP_V4ROOT))
Jesse Keating 2f82dda
+		return 0;
Jesse Keating 2f82dda
+	return dentry->d_inode != NULL;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 __be32
Jesse Keating 2f82dda
 nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
Jesse Keating 2f82dda
 		   const char *name, unsigned int len,
Jesse Keating 2f82dda
@@ -208,7 +223,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqst
Jesse Keating 2f82dda
 		/*
Jesse Keating 2f82dda
 		 * check if we have crossed a mount point ...
Jesse Keating 2f82dda
 		 */
Jesse Keating 2f82dda
-		if (d_mountpoint(dentry)) {
Jesse Keating 2f82dda
+		if (nfsd_mountpoint(dentry, exp)) {
Jesse Keating 2f82dda
 			if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
Jesse Keating 2f82dda
 				dput(dentry);
Jesse Keating 2f82dda
 				goto out_nfserr;
Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/include/linux/nfsd/export.h.save linux-2.6.32.i686/include/linux/nfsd/export.h
Jesse Keating 2f82dda
--- linux-2.6.32.i686/include/linux/nfsd/export.h.save	2009-12-04 10:24:18.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/include/linux/nfsd/export.h	2009-12-04 10:25:08.000000000 -0500
Jesse Keating 2f82dda
@@ -39,7 +39,17 @@
Jesse Keating 2f82dda
 #define NFSEXP_FSID		0x2000
Jesse Keating 2f82dda
 #define	NFSEXP_CROSSMOUNT	0x4000
Jesse Keating 2f82dda
 #define	NFSEXP_NOACL		0x8000	/* reserved for possible ACL related use */
Jesse Keating 2f82dda
-#define NFSEXP_ALLFLAGS		0xFE3F
Jesse Keating 2f82dda
+/*
Jesse Keating 2f82dda
+ * The NFSEXP_V4ROOT flag causes the kernel to give access only to NFSv4
Jesse Keating 2f82dda
+ * clients, and only to the single directory that is the root of the
Jesse Keating 2f82dda
+ * export; further lookup and readdir operations are treated as if every
Jesse Keating 2f82dda
+ * subdirectory was a mountpoint, and ignored if they are not themselves
Jesse Keating 2f82dda
+ * exported.  This is used by nfsd and mountd to construct the NFSv4
Jesse Keating 2f82dda
+ * pseudofilesystem, which provides access only to paths leading to each
Jesse Keating 2f82dda
+ * exported filesystem.
Jesse Keating 2f82dda
+ */
Jesse Keating 2f82dda
+#define	NFSEXP_V4ROOT		0x10000
Jesse Keating 2f82dda
+#define NFSEXP_ALLFLAGS		0x1FE3F
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /* The flags that may vary depending on security flavor: */
Jesse Keating 2f82dda
 #define NFSEXP_SECINFO_FLAGS	(NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
Jesse Keating 2f82dda
diff -up linux-2.6.32.i686/include/linux/nfsd/nfsd.h.save linux-2.6.32.i686/include/linux/nfsd/nfsd.h
Jesse Keating 2f82dda
--- linux-2.6.32.i686/include/linux/nfsd/nfsd.h.save	2009-12-04 10:24:18.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.i686/include/linux/nfsd/nfsd.h	2009-12-04 10:39:18.000000000 -0500
Jesse Keating 2f82dda
@@ -86,6 +86,7 @@ __be32		 nfsd_lookup_dentry(struct svc_r
Jesse Keating 2f82dda
 				struct svc_export **, struct dentry **);
Jesse Keating 2f82dda
 __be32		nfsd_setattr(struct svc_rqst *, struct svc_fh *,
Jesse Keating 2f82dda
 				struct iattr *, int, time_t);
Jesse Keating 2f82dda
+int nfsd_mountpoint(struct dentry *, struct svc_export *);
Jesse Keating 2f82dda
 #ifdef CONFIG_NFSD_V4
Jesse Keating 2f82dda
 __be32          nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
Jesse Keating 2f82dda
                     struct nfs4_acl *);
Jesse Keating 2f82dda
@@ -394,6 +395,10 @@ static inline u32 nfsd_suppattrs2(u32 mi
Jesse Keating 2f82dda
 	return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
Jesse Keating 2f82dda
 			    : NFSD4_SUPPORTED_ATTRS_WORD2;
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
+static inline int nfsd_v4client(struct svc_rqst *rq)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
Jesse Keating 2f82dda
 #define NFSD_WRITEONLY_ATTRS_WORD1							    \