nfsd: filter lookup results in V4ROOT case
[safe/jmp/linux-2.6] / fs / nfsd / vfs.c
index a293f02..eaf2f0d 100644 (file)
  * Zerocpy NFS support (C) 2002 Hirokazu Takahashi <taka@valinux.co.jp>
  */
 
-#include <linux/string.h>
-#include <linux/time.h>
-#include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/file.h>
-#include <linux/mount.h>
-#include <linux/major.h>
 #include <linux/splice.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
 #include <linux/fcntl.h>
-#include <linux/net.h>
-#include <linux/unistd.h>
-#include <linux/slab.h>
-#include <linux/pagemap.h>
-#include <linux/in.h>
-#include <linux/module.h>
 #include <linux/namei.h>
-#include <linux/vfs.h>
 #include <linux/delay.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/nfsd/nfsd.h>
-#ifdef CONFIG_NFSD_V3
-#include <linux/nfs3.h>
-#include <linux/nfsd/xdr3.h>
-#endif /* CONFIG_NFSD_V3 */
-#include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 #include <linux/fsnotify.h>
-#include <linux/posix_acl.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/xattr.h>
+#include <linux/jhash.h>
+#include <linux/ima.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_NFSD_V3
+#include "xdr3.h"
+#endif /* CONFIG_NFSD_V3 */
+
 #ifdef CONFIG_NFSD_V4
-#include <linux/nfs4.h>
 #include <linux/nfs4_acl.h>
 #include <linux/nfsd_idmap.h>
-#include <linux/security.h>
 #endif /* CONFIG_NFSD_V4 */
-#include <linux/jhash.h>
-#include <linux/ima.h>
 
-#include <asm/uaccess.h>
+#include "nfsd.h"
+#include "vfs.h"
 
 #define NFSDDBG_FACILITY               NFSDDBG_FILEOP
 
@@ -116,8 +99,16 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
 
        exp2 = rqst_exp_get_by_name(rqstp, &path);
        if (IS_ERR(exp2)) {
-               if (PTR_ERR(exp2) != -ENOENT)
-                       err = PTR_ERR(exp2);
+               err = PTR_ERR(exp2);
+               /*
+                * We normally allow NFS clients to continue
+                * "underneath" a mountpoint that is not exported.
+                * The exception is V4ROOT, where no traversal is ever
+                * allowed without an explicit export of the new
+                * directory.
+                */
+               if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT))
+                       err = 0;
                path_put(&path);
                goto out;
        }
@@ -141,6 +132,53 @@ out:
        return err;
 }
 
+static void follow_to_parent(struct path *path)
+{
+       struct dentry *dp;
+
+       while (path->dentry == path->mnt->mnt_root && follow_up(path))
+               ;
+       dp = dget_parent(path->dentry);
+       dput(path->dentry);
+       path->dentry = dp;
+}
+
+static int nfsd_lookup_parent(struct svc_rqst *rqstp, struct dentry *dparent, struct svc_export **exp, struct dentry **dentryp)
+{
+       struct svc_export *exp2;
+       struct path path = {.mnt = mntget((*exp)->ex_path.mnt),
+                           .dentry = dget(dparent)};
+
+       follow_to_parent(&path);
+
+       exp2 = rqst_exp_parent(rqstp, &path);
+       if (PTR_ERR(exp2) == -ENOENT) {
+               *dentryp = dget(dparent);
+       } else if (IS_ERR(exp2)) {
+               path_put(&path);
+               return PTR_ERR(exp2);
+       } else {
+               *dentryp = dget(path.dentry);
+               exp_put(*exp);
+               *exp = exp2;
+       }
+       path_put(&path);
+       return 0;
+}
+
+/*
+ * For nfsd purposes, we treat V4ROOT exports as though there was an
+ * export at *every* directory.
+ */
+static int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
+{
+       if (d_mountpoint(dentry))
+               return 1;
+       if (!(exp->ex_flags & NFSEXP_V4ROOT))
+               return 0;
+       return dentry->d_inode != NULL;
+}
+
 __be32
 nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
                   const char *name, unsigned int len,
@@ -169,35 +207,13 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
                        dentry = dget(dparent);
                else if (dparent != exp->ex_path.dentry)
                        dentry = dget_parent(dparent);
-               else if (!EX_NOHIDE(exp))
+               else if (!EX_NOHIDE(exp) && !nfsd_v4client(rqstp))
                        dentry = dget(dparent); /* .. == . just like at / */
                else {
                        /* checking mountpoint crossing is very different when stepping up */
-                       struct svc_export *exp2 = NULL;
-                       struct dentry *dp;
-                       struct path path = {.mnt = mntget(exp->ex_path.mnt),
-                                           .dentry = dget(dparent)};
-
-                       while (path.dentry == path.mnt->mnt_root &&
-                              follow_up(&path))
-                               ;
-                       dp = dget_parent(path.dentry);
-                       dput(path.dentry);
-                       path.dentry = dp;
-
-                       exp2 = rqst_exp_parent(rqstp, &path);
-                       if (PTR_ERR(exp2) == -ENOENT) {
-                               dentry = dget(dparent);
-                       } else if (IS_ERR(exp2)) {
-                               host_err = PTR_ERR(exp2);
-                               path_put(&path);
+                       host_err = nfsd_lookup_parent(rqstp, dparent, &exp, &dentry);
+                       if (host_err)
                                goto out_nfserr;
-                       } else {
-                               dentry = dget(path.dentry);
-                               exp_put(exp);
-                               exp = exp2;
-                       }
-                       path_put(&path);
                }
        } else {
                fh_lock(fhp);
@@ -208,7 +224,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
                /*
                 * check if we have crossed a mount point ...
                 */
-               if (d_mountpoint(dentry)) {
+               if (nfsd_mountpoint(dentry, exp)) {
                        if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
                                dput(dentry);
                                goto out_nfserr;