summaryrefslogtreecommitdiff
path: root/fs/nfsd
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/auth.c19
-rw-r--r--fs/nfsd/export.c294
-rw-r--r--fs/nfsd/lockd.c1
-rw-r--r--fs/nfsd/nfs4acl.c12
-rw-r--r--fs/nfsd/nfs4callback.c20
-rw-r--r--fs/nfsd/nfs4idmap.c19
-rw-r--r--fs/nfsd/nfs4proc.c45
-rw-r--r--fs/nfsd/nfs4state.c65
-rw-r--r--fs/nfsd/nfs4xdr.c101
-rw-r--r--fs/nfsd/nfsctl.c3
-rw-r--r--fs/nfsd/nfsfh.c71
-rw-r--r--fs/nfsd/nfsproc.c3
-rw-r--r--fs/nfsd/nfssvc.c12
-rw-r--r--fs/nfsd/vfs.c173
14 files changed, 640 insertions, 198 deletions
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 6e92b0fe5323..21928056e35e 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -9,20 +9,35 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/export.h>
#define CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE))
+int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
+{
+ struct exp_flavor_info *f;
+ struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
+
+ for (f = exp->ex_flavors; f < end; f++) {
+ if (f->pseudoflavor == rqstp->rq_flavor)
+ return f->flags;
+ }
+ return exp->ex_flags;
+
+}
+
int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
{
struct svc_cred cred = rqstp->rq_cred;
int i;
+ int flags = nfsexp_flags(rqstp, exp);
int ret;
- if (exp->ex_flags & NFSEXP_ALLSQUASH) {
+ if (flags & NFSEXP_ALLSQUASH) {
cred.cr_uid = exp->ex_anon_uid;
cred.cr_gid = exp->ex_anon_gid;
cred.cr_group_info = groups_alloc(0);
- } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) {
+ } else if (flags & NFSEXP_ROOTSQUASH) {
struct group_info *gi;
if (!cred.cr_uid)
cred.cr_uid = exp->ex_anon_uid;
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 79bd03b8bbf8..cba899a3494e 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -26,12 +26,15 @@
#include <linux/mount.h>
#include <linux/hash.h>
#include <linux/module.h>
+#include <linux/exportfs.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/nfsfh.h>
#include <linux/nfsd/syscall.h>
#include <linux/lockd/bind.h>
+#include <linux/sunrpc/msg_prot.h>
+#include <linux/sunrpc/gss_api.h>
#define NFSDDBG_FACILITY NFSDDBG_EXPORT
@@ -451,8 +454,48 @@ out_free_all:
return err;
}
+static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
+{
+ int listsize, err;
+ struct exp_flavor_info *f;
+
+ err = get_int(mesg, &listsize);
+ if (err)
+ return err;
+ if (listsize < 0 || listsize > MAX_SECINFO_LIST)
+ return -EINVAL;
+
+ for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) {
+ err = get_int(mesg, &f->pseudoflavor);
+ if (err)
+ return err;
+ /*
+ * Just a quick sanity check; we could also try to check
+ * whether this pseudoflavor is supported, but at worst
+ * an unsupported pseudoflavor on the export would just
+ * be a pseudoflavor that won't match the flavor of any
+ * authenticated request. The administrator will
+ * probably discover the problem when someone fails to
+ * authenticate.
+ */
+ if (f->pseudoflavor < 0)
+ return -EINVAL;
+ err = get_int(mesg, &f->flags);
+ if (err)
+ return err;
+ /* Only some flags are allowed to differ between flavors: */
+ if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp->ex_flags))
+ return -EINVAL;
+ }
+ exp->ex_nflavors = listsize;
+ return 0;
+}
+
#else /* CONFIG_NFSD_V4 */
-static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; }
+static inline int
+fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;}
+static inline int
+secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }
#endif
static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
@@ -476,6 +519,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
exp.ex_uuid = NULL;
+ /* secinfo */
+ exp.ex_nflavors = 0;
+
if (mesg[mlen-1] != '\n')
return -EINVAL;
mesg[mlen-1] = 0;
@@ -518,9 +564,10 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
/* flags */
err = get_int(&mesg, &an_int);
- if (err == -ENOENT)
+ if (err == -ENOENT) {
+ err = 0;
set_bit(CACHE_NEGATIVE, &exp.h.flags);
- else {
+ } else {
if (err || an_int < 0) goto out;
exp.ex_flags= an_int;
@@ -553,7 +600,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
if (exp.ex_uuid == NULL)
err = -ENOMEM;
}
- } else
+ } else if (strcmp(buf, "secinfo") == 0)
+ err = secinfo_parse(&mesg, buf, &exp);
+ else
/* quietly ignore unknown words and anything
* following. Newer user-space can try to set
* new values, then see what the result was.
@@ -593,6 +642,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
static void exp_flags(struct seq_file *m, int flag, int fsid,
uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs);
+static void show_secinfo(struct seq_file *m, struct svc_export *exp);
static int svc_export_show(struct seq_file *m,
struct cache_detail *cd,
@@ -622,6 +672,7 @@ static int svc_export_show(struct seq_file *m,
seq_printf(m, "%02x", exp->ex_uuid[i]);
}
}
+ show_secinfo(m, exp);
}
seq_puts(m, ")\n");
return 0;
@@ -654,6 +705,7 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
{
struct svc_export *new = container_of(cnew, struct svc_export, h);
struct svc_export *item = container_of(citem, struct svc_export, h);
+ int i;
new->ex_flags = item->ex_flags;
new->ex_anon_uid = item->ex_anon_uid;
@@ -669,6 +721,10 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
item->ex_fslocs.locations_count = 0;
new->ex_fslocs.migrated = item->ex_fslocs.migrated;
item->ex_fslocs.migrated = 0;
+ new->ex_nflavors = item->ex_nflavors;
+ for (i = 0; i < MAX_SECINFO_LIST; i++) {
+ new->ex_flavors[i] = item->ex_flavors[i];
+ }
}
static struct cache_head *svc_export_alloc(void)
@@ -738,16 +794,18 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
int err;
if (!clp)
- return NULL;
+ return ERR_PTR(-ENOENT);
key.ek_client = clp;
key.ek_fsidtype = fsid_type;
memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
ek = svc_expkey_lookup(&key);
- if (ek != NULL)
- if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp)))
- ek = ERR_PTR(err);
+ if (ek == NULL)
+ return ERR_PTR(-ENOMEM);
+ err = cache_check(&svc_expkey_cache, &ek->h, reqp);
+ if (err)
+ return ERR_PTR(err);
return ek;
}
@@ -808,30 +866,21 @@ exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry,
struct cache_req *reqp)
{
struct svc_export *exp, key;
+ int err;
if (!clp)
- return NULL;
+ return ERR_PTR(-ENOENT);
key.ex_client = clp;
key.ex_mnt = mnt;
key.ex_dentry = dentry;
exp = svc_export_lookup(&key);
- if (exp != NULL) {
- int err;
-
- err = cache_check(&svc_export_cache, &exp->h, reqp);
- switch (err) {
- case 0: break;
- case -EAGAIN:
- case -ETIMEDOUT:
- exp = ERR_PTR(err);
- break;
- default:
- exp = NULL;
- }
- }
-
+ if (exp == NULL)
+ return ERR_PTR(-ENOMEM);
+ err = cache_check(&svc_export_cache, &exp->h, reqp);
+ if (err)
+ return ERR_PTR(err);
return exp;
}
@@ -847,7 +896,7 @@ exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry,
dget(dentry);
exp = exp_get_by_name(clp, mnt, dentry, reqp);
- while (exp == NULL && !IS_ROOT(dentry)) {
+ while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) {
struct dentry *parent;
parent = dget_parent(dentry);
@@ -900,7 +949,7 @@ static void exp_fsid_unhash(struct svc_export *exp)
return;
ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid);
- if (ek && !IS_ERR(ek)) {
+ if (!IS_ERR(ek)) {
ek->h.expiry_time = get_seconds()-1;
cache_put(&ek->h, &svc_expkey_cache);
}
@@ -938,7 +987,7 @@ static void exp_unhash(struct svc_export *exp)
struct inode *inode = exp->ex_dentry->d_inode;
ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino);
- if (ek && !IS_ERR(ek)) {
+ if (!IS_ERR(ek)) {
ek->h.expiry_time = get_seconds()-1;
cache_put(&ek->h, &svc_expkey_cache);
}
@@ -989,13 +1038,12 @@ exp_export(struct nfsctl_export *nxp)
/* must make sure there won't be an ex_fsid clash */
if ((nxp->ex_flags & NFSEXP_FSID) &&
- (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) &&
- !IS_ERR(fsid_key) &&
+ (!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) &&
fsid_key->ek_mnt &&
(fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) )
goto finish;
- if (exp) {
+ if (!IS_ERR(exp)) {
/* just a flags/id/fsid update */
exp_fsid_unhash(exp);
@@ -1104,7 +1152,7 @@ exp_unexport(struct nfsctl_export *nxp)
err = -EINVAL;
exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL);
path_release(&nd);
- if (!exp)
+ if (IS_ERR(exp))
goto out_domain;
exp_do_unexport(exp);
@@ -1149,10 +1197,6 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize)
err = PTR_ERR(exp);
goto out;
}
- if (!exp) {
- dprintk("nfsd: exp_rootfh export not found.\n");
- goto out;
- }
/*
* fh must be initialized before calling fh_compose
@@ -1176,17 +1220,130 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv,
{
struct svc_export *exp;
struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp);
- if (!ek || IS_ERR(ek))
+ if (IS_ERR(ek))
return ERR_PTR(PTR_ERR(ek));
exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp);
cache_put(&ek->h, &svc_expkey_cache);
- if (!exp || IS_ERR(exp))
+ if (IS_ERR(exp))
return ERR_PTR(PTR_ERR(exp));
return exp;
}
+__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
+{
+ struct exp_flavor_info *f;
+ struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
+
+ /* legacy gss-only clients are always OK: */
+ if (exp->ex_client == rqstp->rq_gssclient)
+ return 0;
+ /* ip-address based client; check sec= export option: */
+ for (f = exp->ex_flavors; f < end; f++) {
+ if (f->pseudoflavor == rqstp->rq_flavor)
+ return 0;
+ }
+ /* defaults in absence of sec= options: */
+ if (exp->ex_nflavors == 0) {
+ if (rqstp->rq_flavor == RPC_AUTH_NULL ||
+ rqstp->rq_flavor == RPC_AUTH_UNIX)
+ return 0;
+ }
+ return nfserr_wrongsec;
+}
+
+/*
+ * Uses rq_client and rq_gssclient to find an export; uses rq_client (an
+ * auth_unix client) if it's available and has secinfo information;
+ * otherwise, will try to use rq_gssclient.
+ *
+ * Called from functions that handle requests; functions that do work on
+ * behalf of mountd are passed a single client name to use, and should
+ * use exp_get_by_name() or exp_find().
+ */
+struct svc_export *
+rqst_exp_get_by_name(struct svc_rqst *rqstp, struct vfsmount *mnt,
+ struct dentry *dentry)
+{
+ struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
+
+ if (rqstp->rq_client == NULL)
+ goto gss;
+
+ /* First try the auth_unix client: */
+ exp = exp_get_by_name(rqstp->rq_client, mnt, dentry,
+ &rqstp->rq_chandle);
+ if (PTR_ERR(exp) == -ENOENT)
+ goto gss;
+ if (IS_ERR(exp))
+ return exp;
+ /* If it has secinfo, assume there are no gss/... clients */
+ if (exp->ex_nflavors > 0)
+ return exp;
+gss:
+ /* Otherwise, try falling back on gss client */
+ if (rqstp->rq_gssclient == NULL)
+ return exp;
+ gssexp = exp_get_by_name(rqstp->rq_gssclient, mnt, dentry,
+ &rqstp->rq_chandle);
+ if (PTR_ERR(gssexp) == -ENOENT)
+ return exp;
+ if (!IS_ERR(exp))
+ exp_put(exp);
+ return gssexp;
+}
+
+struct svc_export *
+rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
+{
+ struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
+
+ if (rqstp->rq_client == NULL)
+ goto gss;
+
+ /* First try the auth_unix client: */
+ exp = exp_find(rqstp->rq_client, fsid_type, fsidv, &rqstp->rq_chandle);
+ if (PTR_ERR(exp) == -ENOENT)
+ goto gss;
+ if (IS_ERR(exp))
+ return exp;
+ /* If it has secinfo, assume there are no gss/... clients */
+ if (exp->ex_nflavors > 0)
+ return exp;
+gss:
+ /* Otherwise, try falling back on gss client */
+ if (rqstp->rq_gssclient == NULL)
+ return exp;
+ gssexp = exp_find(rqstp->rq_gssclient, fsid_type, fsidv,
+ &rqstp->rq_chandle);
+ if (PTR_ERR(gssexp) == -ENOENT)
+ return exp;
+ if (!IS_ERR(exp))
+ exp_put(exp);
+ return gssexp;
+}
+
+struct svc_export *
+rqst_exp_parent(struct svc_rqst *rqstp, struct vfsmount *mnt,
+ struct dentry *dentry)
+{
+ struct svc_export *exp;
+
+ dget(dentry);
+ exp = rqst_exp_get_by_name(rqstp, mnt, dentry);
+
+ while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) {
+ struct dentry *parent;
+
+ parent = dget_parent(dentry);
+ dput(dentry);
+ dentry = parent;
+ exp = rqst_exp_get_by_name(rqstp, mnt, dentry);
+ }
+ dput(dentry);
+ return exp;
+}
/*
* Called when we need the filehandle for the root of the pseudofs,
@@ -1194,8 +1351,7 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv,
* export point with fsid==0
*/
__be32
-exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp,
- struct cache_req *creq)
+exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
{
struct svc_export *exp;
__be32 rv;
@@ -1203,12 +1359,16 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp,
mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL);
- exp = exp_find(clp, FSID_NUM, fsidv, creq);
+ exp = rqst_exp_find(rqstp, FSID_NUM, fsidv);
+ if (PTR_ERR(exp) == -ENOENT)
+ return nfserr_perm;
if (IS_ERR(exp))
return nfserrno(PTR_ERR(exp));
- if (exp == NULL)
- return nfserr_perm;
rv = fh_compose(fhp, exp, exp->ex_dentry, NULL);
+ if (rv)
+ goto out;
+ rv = check_nfsd_access(exp, rqstp);
+out:
exp_put(exp);
return rv;
}
@@ -1296,28 +1456,62 @@ static struct flags {
{ 0, {"", ""}}
};
-static void exp_flags(struct seq_file *m, int flag, int fsid,
- uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc)
+static void show_expflags(struct seq_file *m, int flags, int mask)
{
- int first = 0;
struct flags *flg;
+ int state, first = 0;
for (flg = expflags; flg->flag; flg++) {
- int state = (flg->flag & flag)?0:1;
+ if (flg->flag & ~mask)
+ continue;
+ state = (flg->flag & flags) ? 0 : 1;
if (*flg->name[state])
seq_printf(m, "%s%s", first++?",":"", flg->name[state]);
}
+}
+
+static void show_secinfo_flags(struct seq_file *m, int flags)
+{
+ seq_printf(m, ",");
+ show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);
+}
+
+static void show_secinfo(struct seq_file *m, struct svc_export *exp)
+{
+ struct exp_flavor_info *f;
+ struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
+ int lastflags = 0, first = 0;
+
+ if (exp->ex_nflavors == 0)
+ return;
+ for (f = exp->ex_flavors; f < end; f++) {
+ if (first || f->flags != lastflags) {
+ if (!first)
+ show_secinfo_flags(m, lastflags);
+ seq_printf(m, ",sec=%d", f->pseudoflavor);
+ lastflags = f->flags;
+ } else {
+ seq_printf(m, ":%d", f->pseudoflavor);
+ }
+ }
+ show_secinfo_flags(m, lastflags);
+}
+
+static void exp_flags(struct seq_file *m, int flag, int fsid,
+ uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc)
+{
+ show_expflags(m, flag, NFSEXP_ALLFLAGS);
if (flag & NFSEXP_FSID)
- seq_printf(m, "%sfsid=%d", first++?",":"", fsid);
+ seq_printf(m, ",fsid=%d", fsid);
if (anonu != (uid_t)-2 && anonu != (0x10000-2))
- seq_printf(m, "%sanonuid=%d", first++?",":"", anonu);
+ seq_printf(m, ",anonuid=%u", anonu);
if (anong != (gid_t)-2 && anong != (0x10000-2))
- seq_printf(m, "%sanongid=%d", first++?",":"", anong);
+ seq_printf(m, ",anongid=%u", anong);
if (fsloc && fsloc->locations_count > 0) {
char *loctype = (fsloc->migrated) ? "refer" : "replicas";
int i;
- seq_printf(m, "%s%s=", first++?",":"", loctype);
+ seq_printf(m, ",%s=", loctype);
seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\");
seq_putc(m, '@');
seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\");
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 221acd1f11f6..9e4a568a5013 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -65,6 +65,7 @@ nlm_fclose(struct file *filp)
static struct nlmsvc_binding nfsd_nlm_ops = {
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
+ .get_grace_period = get_nfs4_grace_period,
};
void
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index cc3b7badd486..b6ed38380ab8 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -183,8 +183,13 @@ static void
summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
{
struct posix_acl_entry *pa, *pe;
- pas->users = 0;
- pas->groups = 0;
+
+ /*
+ * Only pas.users and pas.groups need initialization; previous
+ * posix_acl_valid() calls ensure that the other fields will be
+ * initialized in the following loop. But, just to placate gcc:
+ */
+ memset(pas, 0, sizeof(*pas));
pas->mask = 07;
pe = acl->a_entries + acl->a_count;
@@ -732,13 +737,16 @@ int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl,
*pacl = posix_state_to_acl(&effective_acl_state, flags);
if (IS_ERR(*pacl)) {
ret = PTR_ERR(*pacl);
+ *pacl = NULL;
goto out_dstate;
}
*dpacl = posix_state_to_acl(&default_acl_state,
flags | NFS4_ACL_TYPE_DEFAULT);
if (IS_ERR(*dpacl)) {
ret = PTR_ERR(*dpacl);
+ *dpacl = NULL;
posix_acl_release(*pacl);
+ *pacl = NULL;
goto out_dstate;
}
sort_pacl(*pacl);
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 864090edc28b..31d6633c7fe4 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -75,7 +75,7 @@ enum nfs_cb_opnum4 {
#define op_enc_sz 1
#define op_dec_sz 2
#define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
-#define enc_stateid_sz 16
+#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2)
#define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
1 + enc_stateid_sz + \
enc_nfs4_fh_sz)
@@ -394,7 +394,6 @@ nfsd4_probe_callback(struct nfs4_client *clp)
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp,
};
- char clientname[16];
int status;
if (atomic_read(&cb->cb_set))
@@ -417,11 +416,6 @@ nfsd4_probe_callback(struct nfs4_client *clp)
memset(program->stats, 0, sizeof(cb->cb_stat));
program->stats->program = program;
- /* Just here to make some printk's more useful: */
- snprintf(clientname, sizeof(clientname),
- "%u.%u.%u.%u", NIPQUAD(addr.sin_addr));
- args.servername = clientname;
-
/* Create RPC client */
cb->cb_client = rpc_create(&args);
if (IS_ERR(cb->cb_client)) {
@@ -429,29 +423,23 @@ nfsd4_probe_callback(struct nfs4_client *clp)
goto out_err;
}
- /* Kick rpciod, put the call on the wire. */
- if (rpciod_up() != 0)
- goto out_clnt;
-
/* the task holds a reference to the nfs4_client struct */
atomic_inc(&clp->cl_count);
msg.rpc_cred = nfsd4_lookupcred(clp,0);
if (IS_ERR(msg.rpc_cred))
- goto out_rpciod;
+ goto out_release_clp;
status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL);
put_rpccred(msg.rpc_cred);
if (status != 0) {
dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
- goto out_rpciod;
+ goto out_release_clp;
}
return;
-out_rpciod:
+out_release_clp:
atomic_dec(&clp->cl_count);
- rpciod_down();
-out_clnt:
rpc_shutdown_client(cb->cb_client);
out_err:
cb->cb_client = NULL;
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c
index 45aa21ce6784..2ccffde81b84 100644
--- a/fs/nfsd/nfs4idmap.c
+++ b/fs/nfsd/nfs4idmap.c
@@ -138,7 +138,7 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
char idstr[11];
qword_add(bpp, blen, ent->authname);
- snprintf(idstr, sizeof(idstr), "%d", ent->id);
+ snprintf(idstr, sizeof(idstr), "%u", ent->id);
qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user");
qword_add(bpp, blen, idstr);
@@ -165,7 +165,7 @@ idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
return 0;
}
ent = container_of(h, struct ent, h);
- seq_printf(m, "%s %s %d", ent->authname,
+ seq_printf(m, "%s %s %u", ent->authname,
ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
ent->id);
if (test_bit(CACHE_VALID, &h->flags))
@@ -349,7 +349,7 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
ent->name);
if (test_bit(CACHE_VALID, &h->flags))
- seq_printf(m, " %d", ent->id);
+ seq_printf(m, " %u", ent->id);
seq_printf(m, "\n");
return 0;
}
@@ -587,6 +587,15 @@ idmap_lookup(struct svc_rqst *rqstp,
return ret;
}
+static char *
+rqst_authname(struct svc_rqst *rqstp)
+{
+ struct auth_domain *clp;
+
+ clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client;
+ return clp->name;
+}
+
static int
idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen,
uid_t *id)
@@ -600,7 +609,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
return -EINVAL;
memcpy(key.name, name, namelen);
key.name[namelen] = '\0';
- strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname));
+ strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item);
if (ret == -ENOENT)
ret = -ESRCH; /* nfserr_badname */
@@ -620,7 +629,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
};
int ret;
- strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname));
+ strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item);
if (ret == -ENOENT)
return sprintf(name, "%u", id);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 8522729830db..29b7e63cb32c 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -47,6 +47,7 @@
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
#include <linux/nfs4_acl.h>
+#include <linux/sunrpc/gss_api.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -99,7 +100,15 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
status = nfsd_create_v3(rqstp, current_fh, open->op_fname.data,
open->op_fname.len, &open->op_iattr,
&resfh, open->op_createmode,
- (u32 *)open->op_verf.data, &open->op_truncate, &created);
+ (u32 *)open->op_verf.data,
+ &open->op_truncate, &created);
+
+ /* If we ever decide to use different attrs to store the
+ * verifier in nfsd_create_v3, then we'll need to change this
+ */
+ if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0)
+ open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS |
+ FATTR4_WORD1_TIME_MODIFY);
} else {
status = nfsd_lookup(rqstp, current_fh,
open->op_fname.data, open->op_fname.len, &resfh);
@@ -286,8 +295,7 @@ nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status;
fh_put(&cstate->current_fh);
- status = exp_pseudoroot(rqstp->rq_client, &cstate->current_fh,
- &rqstp->rq_chandle);
+ status = exp_pseudoroot(rqstp, &cstate->current_fh);
return status;
}
@@ -474,8 +482,8 @@ nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 ret;
fh_init(&tmp_fh, NFS4_FHSIZE);
- if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh,
- &rqstp->rq_chandle)) != 0)
+ ret = exp_pseudoroot(rqstp, &tmp_fh);
+ if (ret)
return ret;
if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) {
fh_put(&tmp_fh);
@@ -611,6 +619,30 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
static __be32
+nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_secinfo *secinfo)
+{
+ struct svc_fh resfh;
+ struct svc_export *exp;
+ struct dentry *dentry;
+ __be32 err;
+
+ fh_init(&resfh, NFS4_FHSIZE);
+ err = nfsd_lookup_dentry(rqstp, &cstate->current_fh,
+ secinfo->si_name, secinfo->si_namelen,
+ &exp, &dentry);
+ if (err)
+ return err;
+ if (dentry->d_inode == NULL) {
+ exp_put(exp);
+ err = nfserr_noent;
+ } else
+ secinfo->si_exp = exp;
+ dput(dentry);
+ return err;
+}
+
+static __be32
nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_setattr *setattr)
{
@@ -1009,6 +1041,9 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
[OP_SAVEFH] = {
.op_func = (nfsd4op_func)nfsd4_savefh,
},
+ [OP_SECINFO] = {
+ .op_func = (nfsd4op_func)nfsd4_secinfo,
+ },
[OP_SETATTR] = {
.op_func = (nfsd4op_func)nfsd4_setattr,
},
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3cc8ce422ab1..3f559700788f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -49,8 +49,10 @@
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
#include <linux/namei.h>
+#include <linux/swap.h>
#include <linux/mutex.h>
#include <linux/lockd/bind.h>
+#include <linux/module.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -149,6 +151,7 @@ get_nfs4_file(struct nfs4_file *fi)
}
static int num_delegations;
+unsigned int max_delegations;
/*
* Open owner state (share locks)
@@ -192,7 +195,9 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback;
dprintk("NFSD alloc_init_deleg\n");
- if (num_delegations > STATEID_HASH_SIZE * 4)
+ if (fp->fi_had_conflict)
+ return NULL;
+ if (num_delegations > max_delegations)
return NULL;
dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL);
if (dp == NULL)
@@ -251,7 +256,7 @@ nfs4_close_delegation(struct nfs4_delegation *dp)
/* The following nfsd_close may not actually close the file,
* but we want to remove the lease in any case. */
if (dp->dl_flock)
- setlease(filp, F_UNLCK, &dp->dl_flock);
+ vfs_setlease(filp, F_UNLCK, &dp->dl_flock);
nfsd_close(filp);
}
@@ -378,7 +383,6 @@ shutdown_callback_client(struct nfs4_client *clp)
if (clnt) {
clp->cl_callback.cb_client = NULL;
rpc_shutdown_client(clnt);
- rpciod_down();
}
}
@@ -1000,6 +1004,7 @@ alloc_init_file(struct inode *ino)
list_add(&fp->fi_hash, &file_hashtbl[hashval]);
fp->fi_inode = igrab(ino);
fp->fi_id = current_fileid++;
+ fp->fi_had_conflict = false;
return fp;
}
return NULL;
@@ -1027,19 +1032,19 @@ static int
nfsd4_init_slabs(void)
{
stateowner_slab = kmem_cache_create("nfsd4_stateowners",
- sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL);
+ sizeof(struct nfs4_stateowner), 0, 0, NULL);
if (stateowner_slab == NULL)
goto out_nomem;
file_slab = kmem_cache_create("nfsd4_files",
- sizeof(struct nfs4_file), 0, 0, NULL, NULL);
+ sizeof(struct nfs4_file), 0, 0, NULL);
if (file_slab == NULL)
goto out_nomem;
stateid_slab = kmem_cache_create("nfsd4_stateids",
- sizeof(struct nfs4_stateid), 0, 0, NULL, NULL);
+ sizeof(struct nfs4_stateid), 0, 0, NULL);
if (stateid_slab == NULL)
goto out_nomem;
deleg_slab = kmem_cache_create("nfsd4_delegations",
- sizeof(struct nfs4_delegation), 0, 0, NULL, NULL);
+ sizeof(struct nfs4_delegation), 0, 0, NULL);
if (deleg_slab == NULL)
goto out_nomem;
return 0;
@@ -1326,6 +1331,7 @@ do_recall(void *__dp)
{
struct nfs4_delegation *dp = __dp;
+ dp->dl_file->fi_had_conflict = true;
nfsd4_cb_recall(dp);
return 0;
}
@@ -1396,7 +1402,7 @@ void nfsd_release_deleg_cb(struct file_lock *fl)
/*
* Set the delegation file_lock back pointer.
*
- * Called from __setlease() with lock_kernel() held.
+ * Called from setlease() with lock_kernel() held.
*/
static
void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl)
@@ -1410,7 +1416,7 @@ void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl)
}
/*
- * Called from __setlease() with lock_kernel() held
+ * Called from setlease() with lock_kernel() held
*/
static
int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try)
@@ -1710,10 +1716,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
fl.fl_file = stp->st_vfs_file;
fl.fl_pid = current->tgid;
- /* setlease checks to see if delegation should be handed out.
+ /* vfs_setlease checks to see if delegation should be handed out.
* the lock_manager callbacks fl_mylease and fl_change are used
*/
- if ((status = setlease(stp->st_vfs_file,
+ if ((status = vfs_setlease(stp->st_vfs_file,
flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK, &flp))) {
dprintk("NFSD: setlease failed [%d], no delegation\n", status);
unhash_delegation(dp);
@@ -3191,20 +3197,49 @@ nfsd4_load_reboot_recovery_data(void)
printk("NFSD: Failure reading reboot recovery data\n");
}
+unsigned long
+get_nfs4_grace_period(void)
+{
+ return max(user_lease_time, lease_time) * HZ;
+}
+
+/*
+ * Since the lifetime of a delegation isn't limited to that of an open, a
+ * client may quite reasonably hang on to a delegation as long as it has
+ * the inode cached. This becomes an obvious problem the first time a
+ * client's inode cache approaches the size of the server's total memory.
+ *
+ * For now we avoid this problem by imposing a hard limit on the number
+ * of delegations, which varies according to the server's memory size.
+ */
+static void
+set_max_delegations(void)
+{
+ /*
+ * Allow at most 4 delegations per megabyte of RAM. Quick
+ * estimates suggest that in the worst case (where every delegation
+ * is for a different inode), a delegation could take about 1.5K,
+ * giving a worst case usage of about 6% of memory.
+ */
+ max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT);
+}
+
/* initialization to perform when the nfsd service is started: */
static void
__nfs4_state_start(void)
{
- time_t grace_time;
+ unsigned long grace_time;
boot_time = get_seconds();
- grace_time = max(user_lease_time, lease_time);
+ grace_time = get_nfs_grace_period();
lease_time = user_lease_time;
in_grace = 1;
- printk("NFSD: starting %ld-second grace period\n", grace_time);
+ printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
+ grace_time/HZ);
laundry_wq = create_singlethread_workqueue("nfsd4");
- queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ);
+ queue_delayed_work(laundry_wq, &laundromat_work, grace_time);
+ set_max_delegations();
}
int
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 15809dfd88a5..8ef0964179bc 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -56,6 +56,8 @@
#include <linux/nfsd_idmap.h>
#include <linux/nfs4.h>
#include <linux/nfs4_acl.h>
+#include <linux/sunrpc/gss_api.h>
+#include <linux/sunrpc/svcauth_gss.h>
#define NFSDDBG_FACILITY NFSDDBG_XDR
@@ -819,6 +821,23 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
}
static __be32
+nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
+ struct nfsd4_secinfo *secinfo)
+{
+ DECODE_HEAD;
+
+ READ_BUF(4);
+ READ32(secinfo->si_namelen);
+ READ_BUF(secinfo->si_namelen);
+ SAVEMEM(secinfo->si_name, secinfo->si_namelen);
+ status = check_filename(secinfo->si_name, secinfo->si_namelen,
+ nfserr_noent);
+ if (status)
+ return status;
+ DECODE_TAIL;
+}
+
+static __be32
nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
{
DECODE_HEAD;
@@ -1131,6 +1150,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
case OP_SAVEFH:
op->status = nfs_ok;
break;
+ case OP_SECINFO:
+ op->status = nfsd4_decode_secinfo(argp, &op->u.secinfo);
+ break;
case OP_SETATTR:
op->status = nfsd4_decode_setattr(argp, &op->u.setattr);
break;
@@ -1296,7 +1318,7 @@ static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *
char *path, *rootpath;
fh_init(&tmp_fh, NFS4_FHSIZE);
- *stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle);
+ *stat = exp_pseudoroot(rqstp, &tmp_fh);
if (*stat)
return NULL;
rootpath = tmp_fh.fh_export->ex_path;
@@ -1847,11 +1869,19 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
if (d_mountpoint(dentry)) {
int err;
+ /*
+ * Why the heck aren't we just using nfsd_lookup??
+ * Different "."/".." handling? Something else?
+ * At least, add a comment here to explain....
+ */
err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
if (err) {
nfserr = nfserrno(err);
goto out_put;
}
+ nfserr = check_nfsd_access(exp, cd->rd_rqstp);
+ if (nfserr)
+ goto out_put;
}
nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
@@ -2419,6 +2449,72 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
}
}
+static void
+nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
+ struct nfsd4_secinfo *secinfo)
+{
+ int i = 0;
+ struct svc_export *exp = secinfo->si_exp;
+ u32 nflavs;
+ struct exp_flavor_info *flavs;
+ struct exp_flavor_info def_flavs[2];
+ ENCODE_HEAD;
+
+ if (nfserr)
+ goto out;
+ if (exp->ex_nflavors) {
+ flavs = exp->ex_flavors;
+ nflavs = exp->ex_nflavors;
+ } else { /* Handling of some defaults in absence of real secinfo: */
+ flavs = def_flavs;
+ if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) {
+ nflavs = 2;
+ flavs[0].pseudoflavor = RPC_AUTH_UNIX;
+ flavs[1].pseudoflavor = RPC_AUTH_NULL;
+ } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) {
+ nflavs = 1;
+ flavs[0].pseudoflavor
+ = svcauth_gss_flavor(exp->ex_client);
+ } else {
+ nflavs = 1;
+ flavs[0].pseudoflavor
+ = exp->ex_client->flavour->flavour;
+ }
+ }
+
+ RESERVE_SPACE(4);
+ WRITE32(nflavs);
+ ADJUST_ARGS();
+ for (i = 0; i < nflavs; i++) {
+ u32 flav = flavs[i].pseudoflavor;
+ struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
+
+ if (gm) {
+ RESERVE_SPACE(4);
+ WRITE32(RPC_AUTH_GSS);
+ ADJUST_ARGS();
+ RESERVE_SPACE(4 + gm->gm_oid.len);
+ WRITE32(gm->gm_oid.len);
+ WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
+ ADJUST_ARGS();
+ RESERVE_SPACE(4);
+ WRITE32(0); /* qop */
+ ADJUST_ARGS();
+ RESERVE_SPACE(4);
+ WRITE32(gss_pseudoflavor_to_service(gm, flav));
+ ADJUST_ARGS();
+ gss_mech_put(gm);
+ } else {
+ RESERVE_SPACE(4);
+ WRITE32(flav);
+ ADJUST_ARGS();
+ }
+ }
+out:
+ if (exp)
+ exp_put(exp);
+}
+
/*
* The SETATTR encode routine is special -- it always encodes a bitmap,
* regardless of the error status.
@@ -2559,6 +2655,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
break;
case OP_SAVEFH:
break;
+ case OP_SECINFO:
+ nfsd4_encode_secinfo(resp, op->status, &op->u.secinfo);
+ break;
case OP_SETATTR:
nfsd4_encode_setattr(resp, op->status, &op->u.setattr);
break;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 71c686dc7257..baac89d917ca 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -35,7 +35,6 @@
#include <linux/nfsd/cache.h>
#include <linux/nfsd/xdr.h>
#include <linux/nfsd/syscall.h>
-#include <linux/nfsd/interface.h>
#include <asm/uaccess.h>
@@ -245,7 +244,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
}
exp_readunlock();
if (err == 0)
- err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
+ err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
out:
return err;
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 6ca2d24fc216..7011d62acfc8 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -15,10 +15,12 @@
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/dcache.h>
+#include <linux/exportfs.h>
#include <linux/mount.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcauth_gss.h>
#include <linux/nfsd/nfsd.h>
#define NFSDDBG_FACILITY NFSDDBG_FH
@@ -27,10 +29,6 @@
static int nfsd_nr_verified;
static int nfsd_nr_put;
-extern struct export_operations export_op_default;
-
-#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
-
/*
* our acceptability function.
* if NOSUBTREECHECK, accept anything
@@ -123,8 +121,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
int data_left = fh->fh_size/4;
error = nfserr_stale;
- if (rqstp->rq_client == NULL)
- goto out;
if (rqstp->rq_vers > 2)
error = nfserr_badhandle;
if (rqstp->rq_vers == 4 && fh->fh_size == 0)
@@ -148,7 +144,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
fh->fh_fsid[1] = fh->fh_fsid[2];
}
if ((data_left -= len)<0) goto out;
- exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle);
+ exp = rqst_exp_find(rqstp, fh->fh_fsid_type, datap);
datap += len;
} else {
dev_t xdev;
@@ -159,19 +155,17 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
xdev = old_decode_dev(fh->ofh_xdev);
xino = u32_to_ino_t(fh->ofh_xino);
mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
- exp = exp_find(rqstp->rq_client, FSID_DEV, tfh,
- &rqstp->rq_chandle);
+ exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
}
- if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN
- || PTR_ERR(exp) == -ETIMEDOUT)) {
- error = nfserrno(PTR_ERR(exp));
+ error = nfserr_stale;
+ if (PTR_ERR(exp) == -ENOENT)
goto out;
- }
- error = nfserr_stale;
- if (!exp || IS_ERR(exp))
+ if (IS_ERR(exp)) {
+ error = nfserrno(PTR_ERR(exp));
goto out;
+ }
/* Check if the request originated from a secure port. */
error = nfserr_perm;
@@ -211,11 +205,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
if (fileid_type == 0)
dentry = dget(exp->ex_dentry);
else {
- struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
- dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
- datap, data_left,
- fileid_type,
- nfsd_acceptable, exp);
+ dentry = exportfs_decode_fh(exp->ex_mnt, datap,
+ data_left, fileid_type,
+ nfsd_acceptable, exp);
}
if (dentry == NULL)
goto out;
@@ -257,8 +249,19 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
if (error)
goto out;
+ if (!(access & MAY_LOCK)) {
+ /*
+ * pseudoflavor restrictions are not enforced on NLM,
+ * which clients virtually always use auth_sys for,
+ * even while using RPCSEC_GSS for NFS.
+ */
+ error = check_nfsd_access(exp, rqstp);
+ if (error)
+ goto out;
+ }
+
/* Finally, check access permissions. */
- error = nfsd_permission(exp, dentry, access);
+ error = nfsd_permission(rqstp, exp, dentry, access);
if (error) {
dprintk("fh_verify: %s/%s permission failure, "
@@ -286,15 +289,13 @@ out:
static inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
__u32 *datap, int *maxsize)
{
- struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
-
if (dentry == exp->ex_dentry) {
*maxsize = 0;
return 0;
}
- return CALL(nop,encode_fh)(dentry, datap, maxsize,
- !(exp->ex_flags&NFSEXP_NOSUBTREECHECK));
+ return exportfs_encode_fh(dentry, datap, maxsize,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
}
/*
@@ -565,13 +566,23 @@ enum fsid_source fsid_source(struct svc_fh *fhp)
case FSID_DEV:
case FSID_ENCODE_DEV:
case FSID_MAJOR_MINOR:
- return FSIDSOURCE_DEV;
+ if (fhp->fh_export->ex_dentry->d_inode->i_sb->s_type->fs_flags
+ & FS_REQUIRES_DEV)
+ return FSIDSOURCE_DEV;
+ break;
case FSID_NUM:
- return FSIDSOURCE_FSID;
- default:
if (fhp->fh_export->ex_flags & NFSEXP_FSID)
return FSIDSOURCE_FSID;
- else
- return FSIDSOURCE_UUID;
+ break;
+ default:
+ break;
}
+ /* either a UUID type filehandle, or the filehandle doesn't
+ * match the export.
+ */
+ if (fhp->fh_export->ex_flags & NFSEXP_FSID)
+ return FSIDSOURCE_FSID;
+ if (fhp->fh_export->ex_uuid)
+ return FSIDSOURCE_UUID;
+ return FSIDSOURCE_DEV;
}
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index b2c7147aa921..977a71f64e19 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -278,7 +278,8 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
* echo thing > device-special-file-or-pipe
* by doing a CREATE with type==0
*/
- nfserr = nfsd_permission(newfhp->fh_export,
+ nfserr = nfsd_permission(rqstp,
+ newfhp->fh_export,
newfhp->fh_dentry,
MAY_WRITE|MAY_LOCAL_ACCESS);
if (nfserr && nfserr != nfserr_rofs)
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index ff55950efb43..a8c89ae4c743 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <linux/freezer.h>
#include <linux/fs_struct.h>
#include <linux/sunrpc/types.h>
@@ -432,6 +433,7 @@ nfsd(struct svc_rqst *rqstp)
* dirty pages.
*/
current->flags |= PF_LESS_THROTTLE;
+ set_freezable();
/*
* The main request loop
@@ -492,6 +494,15 @@ out:
module_put_and_exit(0);
}
+static __be32 map_new_errors(u32 vers, __be32 nfserr)
+{
+ if (nfserr == nfserr_jukebox && vers == 2)
+ return nfserr_dropit;
+ if (nfserr == nfserr_wrongsec && vers < 4)
+ return nfserr_acces;
+ return nfserr;
+}
+
int
nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
@@ -534,6 +545,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
/* Now call the procedure handler, and encode NFS status. */
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
+ nfserr = map_new_errors(rqstp->rq_vers, nfserr);
if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2)
nfserr = nfserr_dropit;
if (nfserr == nfserr_dropit) {
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 9a68061dde31..7867151ebb83 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -23,7 +23,7 @@
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/major.h>
-#include <linux/ext2_fs.h>
+#include <linux/splice.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
@@ -113,21 +113,22 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts));
- exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle);
+ exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts);
if (IS_ERR(exp2)) {
- err = PTR_ERR(exp2);
+ if (PTR_ERR(exp2) != -ENOENT)
+ err = PTR_ERR(exp2);
dput(mounts);
mntput(mnt);
goto out;
}
- if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) {
+ if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
/* successfully crossed mount point */
exp_put(exp);
*expp = exp2;
dput(dentry);
*dpp = mounts;
} else {
- if (exp2) exp_put(exp2);
+ exp_put(exp2);
dput(mounts);
}
mntput(mnt);
@@ -135,21 +136,10 @@ out:
return err;
}
-/*
- * Look up one component of a pathname.
- * N.B. After this call _both_ fhp and resfh need an fh_put
- *
- * If the lookup would cross a mountpoint, and the mounted filesystem
- * is exported to the client with NFSEXP_NOHIDE, then the lookup is
- * accepted as it stands and the mounted directory is
- * returned. Otherwise the covered directory is returned.
- * NOTE: this mountpoint crossing is not supported properly by all
- * clients and is explicitly disallowed for NFSv3
- * NeilBrown <neilb@cse.unsw.edu.au>
- */
__be32
-nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
- int len, struct svc_fh *resfh)
+nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ const char *name, int len,
+ struct svc_export **exp_ret, struct dentry **dentry_ret)
{
struct svc_export *exp;
struct dentry *dparent;
@@ -168,8 +158,6 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
exp = fhp->fh_export;
exp_get(exp);
- err = nfserr_acces;
-
/* Lookup the name, but don't follow links */
if (isdotent(name, len)) {
if (len==1)
@@ -190,17 +178,15 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
dput(dentry);
dentry = dp;
- exp2 = exp_parent(exp->ex_client, mnt, dentry,
- &rqstp->rq_chandle);
- if (IS_ERR(exp2)) {
+ exp2 = rqst_exp_parent(rqstp, mnt, dentry);
+ if (PTR_ERR(exp2) == -ENOENT) {
+ dput(dentry);
+ dentry = dget(dparent);
+ } else if (IS_ERR(exp2)) {
host_err = PTR_ERR(exp2);
dput(dentry);
mntput(mnt);
goto out_nfserr;
- }
- if (!exp2) {
- dput(dentry);
- dentry = dget(dparent);
} else {
exp_put(exp);
exp = exp2;
@@ -223,6 +209,41 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
}
}
}
+ *dentry_ret = dentry;
+ *exp_ret = exp;
+ return 0;
+
+out_nfserr:
+ exp_put(exp);
+ return nfserrno(host_err);
+}
+
+/*
+ * Look up one component of a pathname.
+ * N.B. After this call _both_ fhp and resfh need an fh_put
+ *
+ * If the lookup would cross a mountpoint, and the mounted filesystem
+ * is exported to the client with NFSEXP_NOHIDE, then the lookup is
+ * accepted as it stands and the mounted directory is
+ * returned. Otherwise the covered directory is returned.
+ * NOTE: this mountpoint crossing is not supported properly by all
+ * clients and is explicitly disallowed for NFSv3
+ * NeilBrown <neilb@cse.unsw.edu.au>
+ */
+__be32
+nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
+ int len, struct svc_fh *resfh)
+{
+ struct svc_export *exp;
+ struct dentry *dentry;
+ __be32 err;
+
+ err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
+ if (err)
+ return err;
+ err = check_nfsd_access(exp, rqstp);
+ if (err)
+ goto out;
/*
* Note: we compose the file handle now, but as the
* dentry may be negative, it may need to be updated.
@@ -230,16 +251,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = fh_compose(resfh, exp, dentry, fhp);
if (!err && !dentry->d_inode)
err = nfserr_noent;
- dput(dentry);
out:
+ dput(dentry);
exp_put(exp);
return err;
-
-out_nfserr:
- err = nfserrno(host_err);
- goto out;
}
+
/*
* Set various file attributes.
* N.B. After this call fhp needs an fh_put
@@ -311,7 +329,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
/* The size case is special. It changes the file as well as the attributes. */
if (iap->ia_valid & ATTR_SIZE) {
if (iap->ia_size < inode->i_size) {
- err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE);
+ err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE);
if (err)
goto out;
}
@@ -435,7 +453,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
/* Get inode */
error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);
if (error)
- goto out;
+ return error;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
@@ -444,33 +462,25 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
if (host_error == -EINVAL) {
- error = nfserr_attrnotsupp;
- goto out;
+ return nfserr_attrnotsupp;
} else if (host_error < 0)
goto out_nfserr;
host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
if (host_error < 0)
- goto out_nfserr;
+ goto out_release;
- if (S_ISDIR(inode->i_mode)) {
+ if (S_ISDIR(inode->i_mode))
host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
- if (host_error < 0)
- goto out_nfserr;
- }
-
- error = nfs_ok;
-out:
+out_release:
posix_acl_release(pacl);
posix_acl_release(dpacl);
- return (error);
out_nfserr:
if (host_error == -EOPNOTSUPP)
- error = nfserr_attrnotsupp;
+ return nfserr_attrnotsupp;
else
- error = nfserrno(host_error);
- goto out;
+ return nfserrno(host_error);
}
static struct posix_acl *
@@ -607,7 +617,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
sresult |= map->access;
- err2 = nfsd_permission(export, dentry, map->how);
+ err2 = nfsd_permission(rqstp, export, dentry, map->how);
switch (err2) {
case nfs_ok:
result |= map->access;
@@ -801,26 +811,32 @@ found:
}
/*
- * Grab and keep cached pages assosiated with a file in the svc_rqst
- * so that they can be passed to the netowork sendmsg/sendpage routines
- * directrly. They will be released after the sending has completed.
+ * Grab and keep cached pages associated with a file in the svc_rqst
+ * so that they can be passed to the network sendmsg/sendpage routines
+ * directly. They will be released after the sending has completed.
*/
static int
-nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size)
+nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
+ struct splice_desc *sd)
{
- unsigned long count = desc->count;
- struct svc_rqst *rqstp = desc->arg.data;
+ struct svc_rqst *rqstp = sd->u.data;
struct page **pp = rqstp->rq_respages + rqstp->rq_resused;
+ struct page *page = buf->page;
+ size_t size;
+ int ret;
+
+ ret = buf->ops->confirm(pipe, buf);
+ if (unlikely(ret))
+ return ret;
- if (size > count)
- size = count;
+ size = sd->len;
if (rqstp->rq_res.page_len == 0) {
get_page(page);
put_page(*pp);
*pp = page;
rqstp->rq_resused++;
- rqstp->rq_res.page_base = offset;
+ rqstp->rq_res.page_base = buf->offset;
rqstp->rq_res.page_len = size;
} else if (page != pp[-1]) {
get_page(page);
@@ -832,11 +848,15 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset
} else
rqstp->rq_res.page_len += size;
- desc->count = count - size;
- desc->written += size;
return size;
}
+static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
+ struct splice_desc *sd)
+{
+ return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
+}
+
static __be32
nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
@@ -861,10 +881,16 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
if (ra && ra->p_set)
file->f_ra = ra->p_ra;
- if (file->f_op->sendfile && rqstp->rq_sendfile_ok) {
+ if (file->f_op->splice_read && rqstp->rq_splice_ok) {
+ struct splice_desc sd = {
+ .len = 0,
+ .total_len = *count,
+ .pos = offset,
+ .u.data = rqstp,
+ };
+
rqstp->rq_resused = 1;
- host_err = file->f_op->sendfile(file, &offset, *count,
- nfsd_read_actor, rqstp);
+ host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
} else {
oldfs = get_fs();
set_fs(KERNEL_DS);
@@ -1018,7 +1044,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
__be32 err;
if (file) {
- err = nfsd_permission(fhp->fh_export, fhp->fh_dentry,
+ err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
MAY_READ|MAY_OWNER_OVERRIDE);
if (err)
goto out;
@@ -1047,7 +1073,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
__be32 err = 0;
if (file) {
- err = nfsd_permission(fhp->fh_export, fhp->fh_dentry,
+ err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
MAY_WRITE|MAY_OWNER_OVERRIDE);
if (err)
goto out;
@@ -1284,7 +1310,10 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (createmode == NFS3_CREATE_EXCLUSIVE) {
/* solaris7 gets confused (bugid 4218508) if these have
- * the high bit set, so just clear the high bits.
+ * the high bit set, so just clear the high bits. If this is
+ * ever changed to use different attrs for storing the
+ * verifier, then do_open_lookup() will also need to be fixed
+ * accordingly.
*/
v_mtime = verifier[0]&0x7fffffff;
v_atime = verifier[1]&0x7fffffff;
@@ -1772,11 +1801,17 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat)
return err;
}
+static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp)
+{
+ return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY;
+}
+
/*
* Check for a user's access permissions to this inode.
*/
__be32
-nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
+nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
+ struct dentry *dentry, int acc)
{
struct inode *inode = dentry->d_inode;
int err;
@@ -1807,7 +1842,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
*/
if (!(acc & MAY_LOCAL_ACCESS))
if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
- if (EX_RDONLY(exp) || IS_RDONLY(inode))
+ if (exp_rdonly(rqstp, exp) || IS_RDONLY(inode))
return nfserr_rofs;
if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
return nfserr_perm;