summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-25 22:49:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-25 22:49:19 -0700
commit2dad3206db5c3832cde1f58650027fea3ff7adf3 (patch)
tree96314a554afdab5904a939793156d4ae23ec11c1 /fs/nfsd
parent84635d68be4b846ba984a89f386524153330c597 (diff)
parent0c12eaffdf09466f36a9ffe970dda8f4aeb6efc0 (diff)
downloadlinux-2dad3206db5c3832cde1f58650027fea3ff7adf3.tar.bz2
Merge branch 'for-3.1' of git://linux-nfs.org/~bfields/linux
* 'for-3.1' of git://linux-nfs.org/~bfields/linux: nfsd: don't break lease on CLAIM_DELEGATE_CUR locks: rename lock-manager ops nfsd4: update nfsv4.1 implementation notes nfsd: turn on reply cache for NFSv4 nfsd4: call nfsd4_release_compoundargs from pc_release nfsd41: Deny new lock before RECLAIM_COMPLETE done fs: locks: remove init_once nfsd41: check the size of request nfsd41: error out when client sets maxreq_sz or maxresp_sz too small nfsd4: fix file leak on open_downgrade nfsd4: remember to put RW access on stateid destruction NFSD: Added TEST_STATEID operation NFSD: added FREE_STATEID operation svcrpc: fix list-corrupting race on nfsd shutdown rpc: allow autoloading of gss mechanisms svcauth_unix.c: quiet sparse noise svcsock.c: include sunrpc.h to quiet sparse noise nfsd: Remove deprecated nfsctl system call and related code. NFSD: allow OP_DESTROY_CLIENTID to be only op in COMPOUND Fix up trivial conflicts in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/Kconfig12
-rw-r--r--fs/nfsd/cache.h2
-rw-r--r--fs/nfsd/export.c418
-rw-r--r--fs/nfsd/lockd.c2
-rw-r--r--fs/nfsd/nfs4proc.c58
-rw-r--r--fs/nfsd/nfs4state.c265
-rw-r--r--fs/nfsd/nfs4xdr.c144
-rw-r--r--fs/nfsd/nfscache.c3
-rw-r--r--fs/nfsd/nfsctl.c343
-rw-r--r--fs/nfsd/nfssvc.c33
-rw-r--r--fs/nfsd/state.h1
-rw-r--r--fs/nfsd/xdr4.h30
12 files changed, 449 insertions, 862 deletions
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index fbb2a5ef5817..10e6366608f2 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -28,18 +28,6 @@ config NFSD
If unsure, say N.
-config NFSD_DEPRECATED
- bool "Include support for deprecated syscall interface to NFSD"
- depends on NFSD
- default y
- help
- The syscall interface to nfsd was obsoleted in 2.6.0 by a new
- filesystem based interface. The old interface is due for removal
- in 2.6.40. If you wish to remove the interface before then
- say N.
-
- In unsure, say Y.
-
config NFSD_V2_ACL
bool
depends on NFSD
diff --git a/fs/nfsd/cache.h b/fs/nfsd/cache.h
index d892be61016c..93cc9d34c459 100644
--- a/fs/nfsd/cache.h
+++ b/fs/nfsd/cache.h
@@ -69,7 +69,7 @@ enum {
int nfsd_reply_cache_init(void);
void nfsd_reply_cache_shutdown(void);
-int nfsd_cache_lookup(struct svc_rqst *, int);
+int nfsd_cache_lookup(struct svc_rqst *);
void nfsd_cache_update(struct svc_rqst *, int, __be32 *);
#ifdef CONFIG_NFSD_V4
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index b9566e46219f..f4cc1e2bfc54 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -797,58 +797,6 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
return ek;
}
-#ifdef CONFIG_NFSD_DEPRECATED
-static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
- struct svc_export *exp)
-{
- struct svc_expkey key, *ek;
-
- key.ek_client = clp;
- key.ek_fsidtype = fsid_type;
- memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
- key.ek_path = exp->ex_path;
- key.h.expiry_time = NEVER;
- key.h.flags = 0;
-
- ek = svc_expkey_lookup(&key);
- if (ek)
- ek = svc_expkey_update(&key,ek);
- if (ek) {
- cache_put(&ek->h, &svc_expkey_cache);
- return 0;
- }
- return -ENOMEM;
-}
-
-/*
- * Find the client's export entry matching xdev/xino.
- */
-static inline struct svc_expkey *
-exp_get_key(svc_client *clp, dev_t dev, ino_t ino)
-{
- u32 fsidv[3];
-
- if (old_valid_dev(dev)) {
- mk_fsid(FSID_DEV, fsidv, dev, ino, 0, NULL);
- return exp_find_key(clp, FSID_DEV, fsidv, NULL);
- }
- mk_fsid(FSID_ENCODE_DEV, fsidv, dev, ino, 0, NULL);
- return exp_find_key(clp, FSID_ENCODE_DEV, fsidv, NULL);
-}
-
-/*
- * Find the client's export entry matching fsid
- */
-static inline struct svc_expkey *
-exp_get_fsid_key(svc_client *clp, int fsid)
-{
- u32 fsidv[2];
-
- mk_fsid(FSID_NUM, fsidv, 0, 0, fsid, NULL);
-
- return exp_find_key(clp, FSID_NUM, fsidv, NULL);
-}
-#endif
static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
struct cache_req *reqp)
@@ -890,275 +838,7 @@ static struct svc_export *exp_parent(svc_client *clp, struct path *path)
return exp;
}
-#ifdef CONFIG_NFSD_DEPRECATED
-/*
- * Hashtable locking. Write locks are placed only by user processes
- * wanting to modify export information.
- * Write locking only done in this file. Read locking
- * needed externally.
- */
-
-static DECLARE_RWSEM(hash_sem);
-
-void
-exp_readlock(void)
-{
- down_read(&hash_sem);
-}
-
-static inline void
-exp_writelock(void)
-{
- down_write(&hash_sem);
-}
-
-void
-exp_readunlock(void)
-{
- up_read(&hash_sem);
-}
-
-static inline void
-exp_writeunlock(void)
-{
- up_write(&hash_sem);
-}
-#else
-
-/* hash_sem not needed once deprecated interface is removed */
-void exp_readlock(void) {}
-static inline void exp_writelock(void){}
-void exp_readunlock(void) {}
-static inline void exp_writeunlock(void){}
-
-#endif
-
-#ifdef CONFIG_NFSD_DEPRECATED
-static void exp_do_unexport(svc_export *unexp);
-static int exp_verify_string(char *cp, int max);
-
-static void exp_fsid_unhash(struct svc_export *exp)
-{
- struct svc_expkey *ek;
-
- if ((exp->ex_flags & NFSEXP_FSID) == 0)
- return;
-
- ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid);
- if (!IS_ERR(ek)) {
- sunrpc_invalidate(&ek->h, &svc_expkey_cache);
- cache_put(&ek->h, &svc_expkey_cache);
- }
-}
-
-static int exp_fsid_hash(svc_client *clp, struct svc_export *exp)
-{
- u32 fsid[2];
-
- if ((exp->ex_flags & NFSEXP_FSID) == 0)
- return 0;
-
- mk_fsid(FSID_NUM, fsid, 0, 0, exp->ex_fsid, NULL);
- return exp_set_key(clp, FSID_NUM, fsid, exp);
-}
-
-static int exp_hash(struct auth_domain *clp, struct svc_export *exp)
-{
- u32 fsid[2];
- struct inode *inode = exp->ex_path.dentry->d_inode;
- dev_t dev = inode->i_sb->s_dev;
-
- if (old_valid_dev(dev)) {
- mk_fsid(FSID_DEV, fsid, dev, inode->i_ino, 0, NULL);
- return exp_set_key(clp, FSID_DEV, fsid, exp);
- }
- mk_fsid(FSID_ENCODE_DEV, fsid, dev, inode->i_ino, 0, NULL);
- return exp_set_key(clp, FSID_ENCODE_DEV, fsid, exp);
-}
-static void exp_unhash(struct svc_export *exp)
-{
- struct svc_expkey *ek;
- struct inode *inode = exp->ex_path.dentry->d_inode;
-
- ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino);
- if (!IS_ERR(ek)) {
- sunrpc_invalidate(&ek->h, &svc_expkey_cache);
- cache_put(&ek->h, &svc_expkey_cache);
- }
-}
-
-/*
- * Export a file system.
- */
-int
-exp_export(struct nfsctl_export *nxp)
-{
- svc_client *clp;
- struct svc_export *exp = NULL;
- struct svc_export new;
- struct svc_expkey *fsid_key = NULL;
- struct path path;
- int err;
-
- /* Consistency check */
- err = -EINVAL;
- if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
- !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
- goto out;
-
- dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
- nxp->ex_client, nxp->ex_path,
- (unsigned)nxp->ex_dev, (long)nxp->ex_ino,
- nxp->ex_flags);
-
- /* Try to lock the export table for update */
- exp_writelock();
-
- /* Look up client info */
- if (!(clp = auth_domain_find(nxp->ex_client)))
- goto out_unlock;
-
-
- /* Look up the dentry */
- err = kern_path(nxp->ex_path, 0, &path);
- if (err)
- goto out_put_clp;
- err = -EINVAL;
-
- exp = exp_get_by_name(clp, &path, NULL);
-
- memset(&new, 0, sizeof(new));
-
- /* must make sure there won't be an ex_fsid clash */
- if ((nxp->ex_flags & NFSEXP_FSID) &&
- (!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) &&
- fsid_key->ek_path.mnt &&
- (fsid_key->ek_path.mnt != path.mnt ||
- fsid_key->ek_path.dentry != path.dentry))
- goto finish;
-
- if (!IS_ERR(exp)) {
- /* just a flags/id/fsid update */
-
- exp_fsid_unhash(exp);
- exp->ex_flags = nxp->ex_flags;
- exp->ex_anon_uid = nxp->ex_anon_uid;
- exp->ex_anon_gid = nxp->ex_anon_gid;
- exp->ex_fsid = nxp->ex_dev;
-
- err = exp_fsid_hash(clp, exp);
- goto finish;
- }
-
- err = check_export(path.dentry->d_inode, &nxp->ex_flags, NULL);
- if (err) goto finish;
-
- err = -ENOMEM;
-
- dprintk("nfsd: creating export entry %p for client %p\n", exp, clp);
-
- new.h.expiry_time = NEVER;
- new.h.flags = 0;
- new.ex_pathname = kstrdup(nxp->ex_path, GFP_KERNEL);
- if (!new.ex_pathname)
- goto finish;
- new.ex_client = clp;
- new.ex_path = path;
- new.ex_flags = nxp->ex_flags;
- new.ex_anon_uid = nxp->ex_anon_uid;
- new.ex_anon_gid = nxp->ex_anon_gid;
- new.ex_fsid = nxp->ex_dev;
-
- exp = svc_export_lookup(&new);
- if (exp)
- exp = svc_export_update(&new, exp);
-
- if (!exp)
- goto finish;
-
- if (exp_hash(clp, exp) ||
- exp_fsid_hash(clp, exp)) {
- /* failed to create at least one index */
- exp_do_unexport(exp);
- cache_flush();
- } else
- err = 0;
-finish:
- kfree(new.ex_pathname);
- if (!IS_ERR_OR_NULL(exp))
- exp_put(exp);
- if (!IS_ERR_OR_NULL(fsid_key))
- cache_put(&fsid_key->h, &svc_expkey_cache);
- path_put(&path);
-out_put_clp:
- auth_domain_put(clp);
-out_unlock:
- exp_writeunlock();
-out:
- return err;
-}
-
-/*
- * Unexport a file system. The export entry has already
- * been removed from the client's list of exported fs's.
- */
-static void
-exp_do_unexport(svc_export *unexp)
-{
- sunrpc_invalidate(&unexp->h, &svc_export_cache);
- exp_unhash(unexp);
- exp_fsid_unhash(unexp);
-}
-
-
-/*
- * unexport syscall.
- */
-int
-exp_unexport(struct nfsctl_export *nxp)
-{
- struct auth_domain *dom;
- svc_export *exp;
- struct path path;
- int err;
-
- /* Consistency check */
- if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
- !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
- return -EINVAL;
-
- exp_writelock();
-
- err = -EINVAL;
- dom = auth_domain_find(nxp->ex_client);
- if (!dom) {
- dprintk("nfsd: unexport couldn't find %s\n", nxp->ex_client);
- goto out_unlock;
- }
-
- err = kern_path(nxp->ex_path, 0, &path);
- if (err)
- goto out_domain;
-
- err = -EINVAL;
- exp = exp_get_by_name(dom, &path, NULL);
- path_put(&path);
- if (IS_ERR(exp))
- goto out_domain;
-
- exp_do_unexport(exp);
- exp_put(exp);
- err = 0;
-
-out_domain:
- auth_domain_put(dom);
- cache_flush();
-out_unlock:
- exp_writeunlock();
- return err;
-}
-#endif /* CONFIG_NFSD_DEPRECATED */
/*
* Obtain the root fh on behalf of a client.
@@ -1367,7 +1047,6 @@ static void *e_start(struct seq_file *m, loff_t *pos)
unsigned hash, export;
struct cache_head *ch;
- exp_readlock();
read_lock(&svc_export_cache.hash_lock);
if (!n--)
return SEQ_START_TOKEN;
@@ -1418,7 +1097,6 @@ static void e_stop(struct seq_file *m, void *p)
__releases(svc_export_cache.hash_lock)
{
read_unlock(&svc_export_cache.hash_lock);
- exp_readunlock();
}
static struct flags {
@@ -1550,97 +1228,6 @@ const struct seq_operations nfs_exports_op = {
.show = e_show,
};
-#ifdef CONFIG_NFSD_DEPRECATED
-/*
- * Add or modify a client.
- * Change requests may involve the list of host addresses. The list of
- * exports and possibly existing uid maps are left untouched.
- */
-int
-exp_addclient(struct nfsctl_client *ncp)
-{
- struct auth_domain *dom;
- int i, err;
- struct in6_addr addr6;
-
- /* First, consistency check. */
- err = -EINVAL;
- if (! exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
- goto out;
- if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
- goto out;
-
- /* Lock the hashtable */
- exp_writelock();
-
- dom = unix_domain_find(ncp->cl_ident);
-
- err = -ENOMEM;
- if (!dom)
- goto out_unlock;
-
- /* Insert client into hashtable. */
- for (i = 0; i < ncp->cl_naddr; i++) {
- ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6);
- auth_unix_add_addr(&init_net, &addr6, dom);
- }
- auth_unix_forget_old(dom);
- auth_domain_put(dom);
-
- err = 0;
-
-out_unlock:
- exp_writeunlock();
-out:
- return err;
-}
-
-/*
- * Delete a client given an identifier.
- */
-int
-exp_delclient(struct nfsctl_client *ncp)
-{
- int err;
- struct auth_domain *dom;
-
- err = -EINVAL;
- if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
- goto out;
-
- /* Lock the hashtable */
- exp_writelock();
-
- dom = auth_domain_find(ncp->cl_ident);
- /* just make sure that no addresses work
- * and that it will expire soon
- */
- if (dom) {
- err = auth_unix_forget_old(dom);
- auth_domain_put(dom);
- }
-
- exp_writeunlock();
-out:
- return err;
-}
-
-/*
- * Verify that string is non-empty and does not exceed max length.
- */
-static int
-exp_verify_string(char *cp, int max)
-{
- int i;
-
- for (i = 0; i < max; i++)
- if (!cp[i])
- return i;
- cp[i] = 0;
- printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
- return 0;
-}
-#endif /* CONFIG_NFSD_DEPRECATED */
/*
* Initialize the exports module.
@@ -1667,10 +1254,8 @@ nfsd_export_init(void)
void
nfsd_export_flush(void)
{
- exp_writelock();
cache_purge(&svc_expkey_cache);
cache_purge(&svc_export_cache);
- exp_writeunlock();
}
/*
@@ -1682,12 +1267,9 @@ nfsd_export_shutdown(void)
dprintk("nfsd: shutting down export module.\n");
- exp_writelock();
-
cache_unregister(&svc_expkey_cache);
cache_unregister(&svc_export_cache);
svcauth_unix_purge();
- exp_writeunlock();
dprintk("nfsd: export shutdown complete.\n");
}
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 7c831a2731fa..77e7a5cca888 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -35,10 +35,8 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
fh.fh_export = NULL;
- exp_readlock();
nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
fh_put(&fh);
- exp_readunlock();
/* We return nlm error codes as nlm doesn't know
* about nfsd, but nfsd does know about nlm..
*/
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 3a6dbd70b34b..e80777666618 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -291,6 +291,15 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval;
+ /*
+ * RFC5661 18.51.3
+ * Before RECLAIM_COMPLETE done, server should deny new lock
+ */
+ if (nfsd4_has_session(cstate) &&
+ !cstate->session->se_client->cl_firststate &&
+ open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+ return nfserr_grace;
+
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);
@@ -998,6 +1007,15 @@ struct nfsd4_operation {
nfsd4op_func op_func;
u32 op_flags;
char *op_name;
+ /*
+ * We use the DRC for compounds containing non-idempotent
+ * operations, *except* those that are 4.1-specific (since
+ * sessions provide their own EOS), and except for stateful
+ * operations other than setclientid and setclientid_confirm
+ * (since sequence numbers provide EOS for open, lock, etc in
+ * the v4.0 case).
+ */
+ bool op_cacheresult;
};
static struct nfsd4_operation nfsd4_ops[];
@@ -1042,6 +1060,11 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op)
return &nfsd4_ops[op->opnum];
}
+bool nfsd4_cache_this_op(struct nfsd4_op *op)
+{
+ return OPDESC(op)->op_cacheresult;
+}
+
static bool need_wrongsec_check(struct svc_rqst *rqstp)
{
struct nfsd4_compoundres *resp = rqstp->rq_resp;
@@ -1209,7 +1232,6 @@ encode_op:
fh_put(&resp->cstate.save_fh);
BUG_ON(resp->cstate.replay_owner);
out:
- nfsd4_release_compoundargs(args);
/* Reset deferral mechanism for RPC deferrals */
rqstp->rq_usedeferral = 1;
dprintk("nfsv4 compound returned %d\n", ntohl(status));
@@ -1232,6 +1254,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_CREATE] = {
.op_func = (nfsd4op_func)nfsd4_create,
.op_name = "OP_CREATE",
+ .op_cacheresult = true,
},
[OP_DELEGRETURN] = {
.op_func = (nfsd4op_func)nfsd4_delegreturn,
@@ -1249,6 +1272,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_LINK] = {
.op_func = (nfsd4op_func)nfsd4_link,
.op_name = "OP_LINK",
+ .op_cacheresult = true,
},
[OP_LOCK] = {
.op_func = (nfsd4op_func)nfsd4_lock,
@@ -1322,10 +1346,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_REMOVE] = {
.op_func = (nfsd4op_func)nfsd4_remove,
.op_name = "OP_REMOVE",
+ .op_cacheresult = true,
},
[OP_RENAME] = {
.op_name = "OP_RENAME",
.op_func = (nfsd4op_func)nfsd4_rename,
+ .op_cacheresult = true,
},
[OP_RENEW] = {
.op_func = (nfsd4op_func)nfsd4_renew,
@@ -1351,16 +1377,19 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_SETATTR] = {
.op_func = (nfsd4op_func)nfsd4_setattr,
.op_name = "OP_SETATTR",
+ .op_cacheresult = true,
},
[OP_SETCLIENTID] = {
.op_func = (nfsd4op_func)nfsd4_setclientid,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_SETCLIENTID",
+ .op_cacheresult = true,
},
[OP_SETCLIENTID_CONFIRM] = {
.op_func = (nfsd4op_func)nfsd4_setclientid_confirm,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_SETCLIENTID_CONFIRM",
+ .op_cacheresult = true,
},
[OP_VERIFY] = {
.op_func = (nfsd4op_func)nfsd4_verify,
@@ -1369,6 +1398,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_WRITE] = {
.op_func = (nfsd4op_func)nfsd4_write,
.op_name = "OP_WRITE",
+ .op_cacheresult = true,
},
[OP_RELEASE_LOCKOWNER] = {
.op_func = (nfsd4op_func)nfsd4_release_lockowner,
@@ -1402,6 +1432,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_SEQUENCE",
},
+ [OP_DESTROY_CLIENTID] = {
+ .op_func = NULL,
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
+ .op_name = "OP_DESTROY_CLIENTID",
+ },
[OP_RECLAIM_COMPLETE] = {
.op_func = (nfsd4op_func)nfsd4_reclaim_complete,
.op_flags = ALLOWED_WITHOUT_FH,
@@ -1412,6 +1447,16 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = OP_HANDLES_WRONGSEC,
.op_name = "OP_SECINFO_NO_NAME",
},
+ [OP_TEST_STATEID] = {
+ .op_func = (nfsd4op_func)nfsd4_test_stateid,
+ .op_flags = ALLOWED_WITHOUT_FH,
+ .op_name = "OP_TEST_STATEID",
+ },
+ [OP_FREE_STATEID] = {
+ .op_func = (nfsd4op_func)nfsd4_free_stateid,
+ .op_flags = ALLOWED_WITHOUT_FH,
+ .op_name = "OP_FREE_STATEID",
+ },
};
static const char *nfsd4_op_name(unsigned opnum)
@@ -1424,16 +1469,6 @@ static const char *nfsd4_op_name(unsigned opnum)
#define nfsd4_voidres nfsd4_voidargs
struct nfsd4_voidargs { int dummy; };
-/*
- * TODO: At the present time, the NFSv4 server does not do XID caching
- * of requests. Implementing XID caching would not be a serious problem,
- * although it would require a mild change in interfaces since one
- * doesn't know whether an NFSv4 request is idempotent until after the
- * XDR decode. However, XID caching totally confuses pynfs (Peter
- * Astrand's regression testsuite for NFSv4 servers), which reuses
- * XID's liberally, so I've left it unimplemented until pynfs generates
- * better XID's.
- */
static struct svc_procedure nfsd_procedures4[2] = {
[NFSPROC4_NULL] = {
.pc_func = (svc_procfunc) nfsd4_proc_null,
@@ -1449,6 +1484,7 @@ static struct svc_procedure nfsd_procedures4[2] = {
.pc_encode = (kxdrproc_t) nfs4svc_encode_compoundres,
.pc_argsize = sizeof(struct nfsd4_compoundargs),
.pc_ressize = sizeof(struct nfsd4_compoundres),
+ .pc_release = nfsd4_release_compoundargs,
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = NFSD_BUFSIZE/4,
},
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e98f3c2e9492..3787ec117400 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <linux/namei.h>
#include <linux/swap.h>
+#include <linux/pagemap.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/clnt.h>
#include "xdr4.h"
@@ -60,9 +61,12 @@ static u64 current_sessionid = 1;
/* forward declarations */
static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
+static struct nfs4_stateid * search_for_stateid(stateid_t *stid);
+static struct nfs4_delegation * search_for_delegation(stateid_t *stid);
static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
static void nfs4_set_recdir(char *recdir);
+static int check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner);
/* Locking: */
@@ -381,14 +385,6 @@ static int nfs4_access_to_omode(u32 access)
BUG();
}
-static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp)
-{
- unsigned int access;
-
- set_access(&access, stp->st_access_bmap);
- return nfs4_access_to_omode(access);
-}
-
static void unhash_generic_stateid(struct nfs4_stateid *stp)
{
list_del(&stp->st_hash);
@@ -398,11 +394,14 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp)
static void free_generic_stateid(struct nfs4_stateid *stp)
{
- int oflag;
+ int i;
if (stp->st_access_bmap) {
- oflag = nfs4_access_bmap_to_omode(stp);
- nfs4_file_put_access(stp->st_file, oflag);
+ for (i = 1; i < 4; i++) {
+ if (test_bit(i, &stp->st_access_bmap))
+ nfs4_file_put_access(stp->st_file,
+ nfs4_access_to_omode(i));
+ }
}
put_nfs4_file(stp->st_file);
kmem_cache_free(stateid_slab, stp);
@@ -1507,6 +1506,29 @@ nfsd4_replay_create_session(struct nfsd4_create_session *cr_ses,
return slot->sl_status;
}
+#define NFSD_MIN_REQ_HDR_SEQ_SZ ((\
+ 2 * 2 + /* credential,verifier: AUTH_NULL, length 0 */ \
+ 1 + /* MIN tag is length with zero, only length */ \
+ 3 + /* version, opcount, opcode */ \
+ XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \
+ /* seqid, slotID, slotID, cache */ \
+ 4 ) * sizeof(__be32))
+
+#define NFSD_MIN_RESP_HDR_SEQ_SZ ((\
+ 2 + /* verifier: AUTH_NULL, length 0 */\
+ 1 + /* status */ \
+ 1 + /* MIN tag is length with zero, only length */ \
+ 3 + /* opcount, opcode, opstatus*/ \
+ XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \
+ /* seqid, slotID, slotID, slotID, status */ \
+ 5 ) * sizeof(__be32))
+
+static __be32 check_forechannel_attrs(struct nfsd4_channel_attrs fchannel)
+{
+ return fchannel.maxreq_sz < NFSD_MIN_REQ_HDR_SEQ_SZ
+ || fchannel.maxresp_sz < NFSD_MIN_RESP_HDR_SEQ_SZ;
+}
+
__be32
nfsd4_create_session(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@@ -1575,6 +1597,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cr_ses->flags &= ~SESSION4_PERSIST;
cr_ses->flags &= ~SESSION4_RDMA;
+ status = nfserr_toosmall;
+ if (check_forechannel_attrs(cr_ses->fore_channel))
+ goto out;
+
status = nfserr_jukebox;
new = alloc_init_session(rqstp, conf, cr_ses);
if (!new)
@@ -1736,6 +1762,14 @@ static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_sess
return args->opcnt > session->se_fchannel.maxops;
}
+static bool nfsd4_request_too_big(struct svc_rqst *rqstp,
+ struct nfsd4_session *session)
+{
+ struct xdr_buf *xb = &rqstp->rq_arg;
+
+ return xb->len > session->se_fchannel.maxreq_sz;
+}
+
__be32
nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@@ -1768,6 +1802,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
if (nfsd4_session_too_many_ops(rqstp, session))
goto out;
+ status = nfserr_req_too_big;
+ if (nfsd4_request_too_big(rqstp, session))
+ goto out;
+
status = nfserr_badslot;
if (seq->slotid >= session->se_fchannel.maxreqs)
goto out;
@@ -2337,15 +2375,6 @@ out:
return ret;
}
-static inline void
-nfs4_file_downgrade(struct nfs4_file *fp, unsigned int share_access)
-{
- if (share_access & NFS4_SHARE_ACCESS_WRITE)
- nfs4_file_put_access(fp, O_WRONLY);
- if (share_access & NFS4_SHARE_ACCESS_READ)
- nfs4_file_put_access(fp, O_RDONLY);
-}
-
static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
{
/* We're assuming the state code never drops its reference
@@ -2396,8 +2425,8 @@ int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
}
static const struct lock_manager_operations nfsd_lease_mng_ops = {
- .fl_break = nfsd_break_deleg_cb,
- .fl_change = nfsd_change_deleg_cb,
+ .lm_break = nfsd_break_deleg_cb,
+ .lm_change = nfsd_change_deleg_cb,
};
@@ -2556,12 +2585,18 @@ static inline int nfs4_access_to_access(u32 nfs4_access)
return flags;
}
-static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file
-*fp, struct svc_fh *cur_fh, u32 nfs4_access)
+static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
+ struct svc_fh *cur_fh, struct nfsd4_open *open)
{
__be32 status;
- int oflag = nfs4_access_to_omode(nfs4_access);
- int access = nfs4_access_to_access(nfs4_access);
+ int oflag = nfs4_access_to_omode(open->op_share_access);
+ int access = nfs4_access_to_access(open->op_share_access);
+
+ /* CLAIM_DELEGATE_CUR is used in response to a broken lease;
+ * allowing it to break the lease and return EAGAIN leaves the
+ * client unable to make progress in returning the delegation */
+ if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR)
+ access |= NFSD_MAY_NOT_BREAK_LEASE;
if (!fp->fi_fds[oflag]) {
status = nfsd_open(rqstp, cur_fh, S_IFREG, access,
@@ -2586,7 +2621,7 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp,
if (stp == NULL)
return nfserr_resource;
- status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open->op_share_access);
+ status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
if (status) {
kmem_cache_free(stateid_slab, stp);
return status;
@@ -2619,14 +2654,14 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
new_access = !test_bit(op_share_access, &stp->st_access_bmap);
if (new_access) {
- status = nfs4_get_vfs_file(rqstp, fp, cur_fh, op_share_access);
+ status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
if (status)
return status;
}
status = nfsd4_truncate(rqstp, cur_fh, open);
if (status) {
if (new_access) {
- int oflag = nfs4_access_to_omode(new_access);
+ int oflag = nfs4_access_to_omode(op_share_access);
nfs4_file_put_access(fp, oflag);
}
return status;
@@ -3137,6 +3172,37 @@ static int is_delegation_stateid(stateid_t *stateid)
return stateid->si_fileid == 0;
}
+static int is_open_stateid(struct nfs4_stateid *stateid)
+{
+ return stateid->st_openstp == NULL;
+}
+
+__be32 nfs4_validate_stateid(stateid_t *stateid, int flags)
+{
+ struct nfs4_stateid *stp = NULL;
+ __be32 status = nfserr_stale_stateid;
+
+ if (STALE_STATEID(stateid))
+ goto out;
+
+ status = nfserr_expired;
+ stp = search_for_stateid(stateid);
+ if (!stp)
+ goto out;
+ status = nfserr_bad_stateid;
+
+ if (!stp->st_stateowner->so_confirmed)
+ goto out;
+
+ status = check_stateid_generation(stateid, &stp->st_stateid, flags);
+ if (status)
+ goto out;
+
+ status = nfs_ok;
+out:
+ return status;
+}
+
/*
* Checks for stateid operations
*/
@@ -3216,6 +3282,81 @@ out:
return status;
}
+static __be32
+nfsd4_free_delegation_stateid(stateid_t *stateid)
+{
+ struct nfs4_delegation *dp = search_for_delegation(stateid);
+ if (dp)
+ return nfserr_locks_held;
+ return nfserr_bad_stateid;
+}
+
+static __be32
+nfsd4_free_lock_stateid(struct nfs4_stateid *stp)
+{
+ if (check_for_locks(stp->st_file, stp->st_stateowner))
+ return nfserr_locks_held;
+ release_lock_stateid(stp);
+ return nfs_ok;
+}
+
+/*
+ * Test if the stateid is valid
+ */
+__be32
+nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_test_stateid *test_stateid)
+{
+ test_stateid->ts_has_session = nfsd4_has_session(cstate);
+ return nfs_ok;
+}
+
+/*
+ * Free a state id
+ */
+__be32
+nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ stateid_t *stateid = &free_stateid->fr_stateid;
+ struct nfs4_stateid *stp;
+ __be32 ret;
+
+ nfs4_lock_state();
+ if (is_delegation_stateid(stateid)) {
+ ret = nfsd4_free_delegation_stateid(stateid);
+ goto out;
+ }
+
+ stp = search_for_stateid(stateid);
+ if (!stp) {
+ ret = nfserr_bad_stateid;
+ goto out;
+ }
+ if (stateid->si_generation != 0) {
+ if (stateid->si_generation < stp->st_stateid.si_generation) {
+ ret = nfserr_old_stateid;
+ goto out;
+ }
+ if (stateid->si_generation > stp->st_stateid.si_generation) {
+ ret = nfserr_bad_stateid;
+ goto out;
+ }
+ }
+
+ if (is_open_stateid(stp)) {
+ ret = nfserr_locks_held;
+ goto out;
+ } else {
+ ret = nfsd4_free_lock_stateid(stp);
+ goto out;
+ }
+
+out:
+ nfs4_unlock_state();
+ return ret;
+}
+
static inline int
setlkflg (int type)
{
@@ -3384,18 +3525,15 @@ out:
return status;
}
-
-/*
- * unset all bits in union bitmap (bmap) that
- * do not exist in share (from successful OPEN_DOWNGRADE)
- */
-static void
-reset_union_bmap_access(unsigned long access, unsigned long *bmap)
+static inline void nfs4_file_downgrade(struct nfs4_stateid *stp, unsigned int to_access)
{
int i;
+
for (i = 1; i < 4; i++) {
- if ((i & access) != i)
- __clear_bit(i, bmap);
+ if (test_bit(i, &stp->st_access_bmap) && !(i & to_access)) {
+ nfs4_file_put_access(stp->st_file, i);
+ __clear_bit(i, &stp->st_access_bmap);
+ }
}
}
@@ -3416,7 +3554,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
{
__be32 status;
struct nfs4_stateid *stp;
- unsigned int share_access;
dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n",
(int)cstate->current_fh.fh_dentry->d_name.len,
@@ -3445,10 +3582,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
stp->st_deny_bmap, od->od_share_deny);
goto out;
}
- set_access(&share_access, stp->st_access_bmap);
- nfs4_file_downgrade(stp->st_file, share_access & ~od->od_share_access);
+ nfs4_file_downgrade(stp, od->od_share_access);
- reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap);
reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap);
update_stateid(&stp->st_stateid);
@@ -3594,6 +3729,14 @@ static struct list_head lock_ownerid_hashtbl[LOCK_HASH_SIZE];
static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE];
static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
+static int
+same_stateid(stateid_t *id_one, stateid_t *id_two)
+{
+ if (id_one->si_stateownerid != id_two->si_stateownerid)
+ return 0;
+ return id_one->si_fileid == id_two->si_fileid;
+}
+
static struct nfs4_stateid *
find_stateid(stateid_t *stid, int flags)
{
@@ -3623,6 +3766,44 @@ find_stateid(stateid_t *stid, int flags)
return NULL;
}
+static struct nfs4_stateid *
+search_for_stateid(stateid_t *stid)
+{
+ struct nfs4_stateid *local;
+ unsigned int hashval = stateid_hashval(stid->si_stateownerid, stid->si_fileid);
+
+ list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) {
+ if (same_stateid(&local->st_stateid, stid))
+ return local;
+ }
+
+ list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) {
+ if (same_stateid(&local->st_stateid, stid))
+ return local;
+ }
+ return NULL;
+}
+
+static struct nfs4_delegation *
+search_for_delegation(stateid_t *stid)
+{
+ struct nfs4_file *fp;
+ struct nfs4_delegation *dp;
+ struct list_head *pos;
+ int i;
+
+ for (i = 0; i < FILE_HASH_SIZE; i++) {
+ list_for_each_entry(fp, &file_hashtbl[i], fi_hash) {
+ list_for_each(pos, &fp->fi_delegations) {
+ dp = list_entry(pos, struct nfs4_delegation, dl_perfile);
+ if (same_stateid(&dp->dl_stateid, stid))
+ return dp;
+ }
+ }
+ }
+ return NULL;
+}
+
static struct nfs4_delegation *
find_delegation_stateid(struct inode *ino, stateid_t *stid)
{
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 990181103214..c8bf405d19de 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -44,13 +44,15 @@
#include <linux/namei.h>
#include <linux/statfs.h>
#include <linux/utsname.h>
+#include <linux/pagemap.h>
#include <linux/sunrpc/svcauth_gss.h>
#include "idmap.h"
#include "acl.h"
#include "xdr4.h"
#include "vfs.h"
-
+#include "state.h"
+#include "cache.h"
#define NFSDDBG_FACILITY NFSDDBG_XDR
@@ -131,6 +133,22 @@ xdr_error: \
} \
} while (0)
+static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+ savep->p = argp->p;
+ savep->end = argp->end;
+ savep->pagelen = argp->pagelen;
+ savep->pagelist = argp->pagelist;
+}
+
+static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+ argp->p = savep->p;
+ argp->end = savep->end;
+ argp->pagelen = savep->pagelen;
+ argp->pagelist = savep->pagelist;
+}
+
static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
{
/* We want more bytes than seem to be available.
@@ -1246,6 +1264,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
}
static __be32
+nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ DECODE_HEAD;
+
+ READ_BUF(sizeof(stateid_t));
+ READ32(free_stateid->fr_stateid.si_generation);
+ COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+ DECODE_TAIL;
+}
+
+static __be32
nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
struct nfsd4_sequence *seq)
{
@@ -1261,6 +1292,40 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
DECODE_TAIL;
}
+static __be32
+nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
+{
+ unsigned int nbytes;
+ stateid_t si;
+ int i;
+ __be32 *p;
+ __be32 status;
+
+ READ_BUF(4);
+ test_stateid->ts_num_ids = ntohl(*p++);
+
+ nbytes = test_stateid->ts_num_ids * sizeof(stateid_t);
+ if (nbytes > (u32)((char *)argp->end - (char *)argp->p))
+ goto xdr_error;
+
+ test_stateid->ts_saved_args = argp;
+ save_buf(argp, &test_stateid->ts_savedp);
+
+ for (i = 0; i < test_stateid->ts_num_ids; i++) {
+ status = nfsd4_decode_stateid(argp, &si);
+ if (status)
+ return status;
+ }
+
+ status = 0;
+out:
+ return status;
+xdr_error:
+ dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
+ status = nfserr_bad_xdr;
+ goto out;
+}
+
static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
{
DECODE_HEAD;
@@ -1370,7 +1435,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
[OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
- [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid,
[OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -1380,7 +1445,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
[OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
[OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
- [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid,
[OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete,
@@ -1402,6 +1467,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
DECODE_HEAD;
struct nfsd4_op *op;
struct nfsd4_minorversion_ops *ops;
+ bool cachethis = false;
int i;
/*
@@ -1483,7 +1549,16 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
argp->opcnt = i+1;
break;
}
+ /*
+ * We'll try to cache the result in the DRC if any one
+ * op in the compound wants to be cached:
+ */
+ cachethis |= nfsd4_cache_this_op(op);
}
+ /* Sessions make the DRC unnecessary: */
+ if (argp->minorversion)
+ cachethis = false;
+ argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
DECODE_TAIL;
}
@@ -3116,6 +3191,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
}
static __be32
+nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
+ struct nfsd4_free_stateid *free_stateid)
+{
+ __be32 *p;
+
+ if (nfserr)
+ return nfserr;
+
+ RESERVE_SPACE(4);
+ WRITE32(nfserr);
+ ADJUST_ARGS();
+ return nfserr;
+}
+
+static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
struct nfsd4_sequence *seq)
{
@@ -3138,6 +3228,36 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
return 0;
}
+__be32
+nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
+ struct nfsd4_test_stateid *test_stateid)
+{
+ struct nfsd4_compoundargs *argp;
+ stateid_t si;
+ __be32 *p;
+ int i;
+ int valid;
+
+ restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp);
+ argp = test_stateid->ts_saved_args;
+
+ RESERVE_SPACE(4);
+ *p++ = htonl(test_stateid->ts_num_ids);
+ resp->p = p;
+
+ nfs4_lock_state();
+ for (i = 0; i < test_stateid->ts_num_ids; i++) {
+ nfsd4_decode_stateid(argp, &si);
+ valid = nfs4_validate_stateid(&si, test_stateid->ts_has_session);
+ RESERVE_SPACE(4);
+ *p++ = htonl(valid);
+ resp->p = p;
+ }
+ nfs4_unlock_state();
+
+ return nfserr;
+}
+
static __be32
nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
{
@@ -3196,7 +3316,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
- [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_FREE_STATEID] = (nfsd4_enc)nfsd4_encode_free_stateid,
[OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
[OP_GETDEVICEINFO] = (nfsd4_enc)nfsd4_encode_noop,
[OP_GETDEVICELIST] = (nfsd4_enc)nfsd4_encode_noop,
@@ -3206,7 +3326,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence,
[OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop,
- [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_test_stateid,
[OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
[OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop,
[OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop,
@@ -3319,8 +3439,11 @@ nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
return xdr_ressize_check(rqstp, p);
}
-void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
+int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
{
+ struct svc_rqst *rqstp = rq;
+ struct nfsd4_compoundargs *args = rqstp->rq_argp;
+
if (args->ops != args->iops) {
kfree(args->ops);
args->ops = args->iops;
@@ -3333,13 +3456,12 @@ void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
tb->release(tb->buf);
kfree(tb);
}
+ return 1;
}
int
nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
{
- __be32 status;
-
args->p = p;
args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
args->pagelist = rqstp->rq_arg.pages;
@@ -3349,11 +3471,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_comp
args->ops = args->iops;
args->rqstp = rqstp;
- status = nfsd4_decode_compound(args);
- if (status) {
- nfsd4_release_compoundargs(args);
- }
- return !status;
+ return !nfsd4_decode_compound(args);
}
int
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index 4666a209678a..2cbac34a55da 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -118,7 +118,7 @@ hash_refile(struct svc_cacherep *rp)
* Note that no operation within the loop may sleep.
*/
int
-nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
+nfsd_cache_lookup(struct svc_rqst *rqstp)
{
struct hlist_node *hn;
struct hlist_head *rh;
@@ -128,6 +128,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
vers = rqstp->rq_vers,
proc = rqstp->rq_proc;
unsigned long age;
+ int type = rqstp->rq_cachetype;
int rtn;
rqstp->rq_cacherep = NULL;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 2b1449dd2f49..c7716143cbd1 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -24,15 +24,6 @@
*/
enum {
NFSD_Root = 1,
-#ifdef CONFIG_NFSD_DEPRECATED
- NFSD_Svc,
- NFSD_Add,
- NFSD_Del,
- NFSD_Export,
- NFSD_Unexport,
- NFSD_Getfd,
- NFSD_Getfs,
-#endif
NFSD_List,
NFSD_Export_features,
NFSD_Fh,
@@ -59,15 +50,6 @@ enum {
/*
* write() for these nodes.
*/
-#ifdef CONFIG_NFSD_DEPRECATED
-static ssize_t write_svc(struct file *file, char *buf, size_t size);
-static ssize_t write_add(struct file *file, char *buf, size_t size);
-static ssize_t write_del(struct file *file, char *buf, size_t size);
-static ssize_t write_export(struct file *file, char *buf, size_t size);
-static ssize_t write_unexport(struct file *file, char *buf, size_t size);
-static ssize_t write_getfd(struct file *file, char *buf, size_t size);
-static ssize_t write_getfs(struct file *file, char *buf, size_t size);
-#endif
static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
@@ -83,15 +65,6 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
#endif
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
-#ifdef CONFIG_NFSD_DEPRECATED
- [NFSD_Svc] = write_svc,
- [NFSD_Add] = write_add,
- [NFSD_Del] = write_del,
- [NFSD_Export] = write_export,
- [NFSD_Unexport] = write_unexport,
- [NFSD_Getfd] = write_getfd,
- [NFSD_Getfs] = write_getfs,
-#endif
[NFSD_Fh] = write_filehandle,
[NFSD_FO_UnlockIP] = write_unlock_ip,
[NFSD_FO_UnlockFS] = write_unlock_fs,
@@ -130,16 +103,6 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu
static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
{
-#ifdef CONFIG_NFSD_DEPRECATED
- static int warned;
- if (file->f_dentry->d_name.name[0] == '.' && !warned) {
- printk(KERN_INFO
- "Warning: \"%s\" uses deprecated NFSD interface: %s."
- " This will be removed in 2.6.40\n",
- current->comm, file->f_dentry->d_name.name);
- warned = 1;
- }
-#endif
if (! file->private_data) {
/* An attempt to read a transaction file without writing
* causes a 0-byte write so that the file can return
@@ -226,303 +189,6 @@ static const struct file_operations pool_stats_operations = {
* payload - write methods
*/
-#ifdef CONFIG_NFSD_DEPRECATED
-/**
- * write_svc - Start kernel's NFSD server
- *
- * Deprecated. /proc/fs/nfsd/threads is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_svc
- * svc_port: port number of this
- * server's listener
- * svc_nthreads: number of threads to start
- * size: size in bytes of passed in nfsctl_svc
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_svc(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_svc *data;
- int err;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_svc*) buf;
- err = nfsd_svc(data->svc_port, data->svc_nthreads);
- if (err < 0)
- return err;
- return 0;
-}
-
-/**
- * write_add - Add or modify client entry in auth unix cache
- *
- * Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_client
- * cl_ident: '\0'-terminated C string
- * containing domain name
- * of client
- * cl_naddr: no. of items in cl_addrlist
- * cl_addrlist: array of client addresses
- * cl_fhkeytype: ignored
- * cl_fhkeylen: ignored
- * cl_fhkey: ignored
- * size: size in bytes of passed in nfsctl_client
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since
- * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
- */
-static ssize_t write_add(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_client *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_client *)buf;
- return exp_addclient(data);
-}
-
-/**
- * write_del - Remove client from auth unix cache
- *
- * Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_client
- * cl_ident: '\0'-terminated C string
- * containing domain name
- * of client
- * cl_naddr: ignored
- * cl_addrlist: ignored
- * cl_fhkeytype: ignored
- * cl_fhkeylen: ignored
- * cl_fhkey: ignored
- * size: size in bytes of passed in nfsctl_client
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since
- * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
- */
-static ssize_t write_del(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_client *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_client *)buf;
- return exp_delclient(data);
-}
-
-/**
- * write_export - Export part or all of a local file system
- *
- * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_export
- * ex_client: '\0'-terminated C string
- * containing domain name
- * of client allowed to access
- * this export
- * ex_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * ex_dev: fsid to use for this export
- * ex_ino: ignored
- * ex_flags: export flags for this export
- * ex_anon_uid: UID to use for anonymous
- * requests
- * ex_anon_gid: GID to use for anonymous
- * requests
- * size: size in bytes of passed in nfsctl_export
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_export(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_export *data;
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_export*)buf;
- return exp_export(data);
-}
-
-/**
- * write_unexport - Unexport a previously exported file system
- *
- * Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_export
- * ex_client: '\0'-terminated C string
- * containing domain name
- * of client no longer allowed
- * to access this export
- * ex_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * ex_dev: ignored
- * ex_ino: ignored
- * ex_flags: ignored
- * ex_anon_uid: ignored
- * ex_anon_gid: ignored
- * size: size in bytes of passed in nfsctl_export
- * Output:
- * On success: returns zero
- * On error: return code is negative errno value
- */
-static ssize_t write_unexport(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_export *data;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_export*)buf;
- return exp_unexport(data);
-}
-
-/**
- * write_getfs - Get a variable-length NFS file handle by path
- *
- * Deprecated. /proc/fs/nfsd/filehandle is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_fsparm
- * gd_addr: socket address of client
- * gd_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * gd_maxlen: maximum size of returned file
- * handle
- * size: size in bytes of passed in nfsctl_fsparm
- * Output:
- * On success: passed-in buffer filled with a knfsd_fh structure
- * (a variable-length raw NFS file handle);
- * return code is the size in bytes of the file handle
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since gd_addr
- * is the same size as a struct sockaddr_in.
- */
-static ssize_t write_getfs(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_fsparm *data;
- struct sockaddr_in *sin;
- struct auth_domain *clp;
- int err = 0;
- struct knfsd_fh *res;
- struct in6_addr in6;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_fsparm*)buf;
- err = -EPROTONOSUPPORT;
- if (data->gd_addr.sa_family != AF_INET)
- goto out;
- sin = (struct sockaddr_in *)&data->gd_addr;
- if (data->gd_maxlen > NFS3_FHSIZE)
- data->gd_maxlen = NFS3_FHSIZE;
-
- res = (struct knfsd_fh*)buf;
-
- exp_readlock();
-
- ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
-
- clp = auth_unix_lookup(&init_net, &in6);
- if (!clp)
- err = -EPERM;
- else {
- err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
- auth_domain_put(clp);
- }
- exp_readunlock();
- if (err == 0)
- err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
- out:
- return err;
-}
-
-/**
- * write_getfd - Get a fixed-length NFS file handle by path (used by mountd)
- *
- * Deprecated. /proc/fs/nfsd/filehandle is preferred.
- * Function remains to support old versions of nfs-utils.
- *
- * Input:
- * buf: struct nfsctl_fdparm
- * gd_addr: socket address of client
- * gd_path: '\0'-terminated C string
- * containing pathname of
- * directory in local file system
- * gd_version: fdparm structure version
- * size: size in bytes of passed in nfsctl_fdparm
- * Output:
- * On success: passed-in buffer filled with nfsctl_res
- * (a fixed-length raw NFS file handle);
- * return code is the size in bytes of the file handle
- * On error: return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in, since gd_addr
- * is the same size as a struct sockaddr_in.
- */
-static ssize_t write_getfd(struct file *file, char *buf, size_t size)
-{
- struct nfsctl_fdparm *data;
- struct sockaddr_in *sin;
- struct auth_domain *clp;
- int err = 0;
- struct knfsd_fh fh;
- char *res;
- struct in6_addr in6;
-
- if (size < sizeof(*data))
- return -EINVAL;
- data = (struct nfsctl_fdparm*)buf;
- err = -EPROTONOSUPPORT;
- if (data->gd_addr.sa_family != AF_INET)
- goto out;
- err = -EINVAL;
- if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
- goto out;
-
- res = buf;
- sin = (struct sockaddr_in *)&data->gd_addr;
- exp_readlock();
-
- ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
-
- clp = auth_unix_lookup(&init_net, &in6);
- if (!clp)
- err = -EPERM;
- else {
- err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
- auth_domain_put(clp);
- }
- exp_readunlock();
-
- if (err == 0) {
- memset(res,0, NFS_FHSIZE);
- memcpy(res, &fh.fh_base, fh.fh_size);
- err = NFS_FHSIZE;
- }
- out:
- return err;
-}
-#endif /* CONFIG_NFSD_DEPRECATED */
/**
* write_unlock_ip - Release all locks used by a client
@@ -1397,15 +1063,6 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
{
static struct tree_descr nfsd_files[] = {
-#ifdef CONFIG_NFSD_DEPRECATED
- [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
- [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
- [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
- [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
- [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
- [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
- [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
-#endif
[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
[NFSD_Export_features] = {"export_features",
&export_features_operations, S_IRUGO},
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 18743c4d8bca..dc5a1bf476b1 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -528,16 +528,9 @@ nfsd(void *vrqstp)
continue;
}
-
- /* Lock the export hash tables for reading. */
- exp_readlock();
-
validate_process_creds();
svc_process(rqstp);
validate_process_creds();
-
- /* Unlock export hash tables */
- exp_readunlock();
}
/* Clear signals before calling svc_exit_thread() */
@@ -577,8 +570,22 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
rqstp->rq_vers, rqstp->rq_proc);
proc = rqstp->rq_procinfo;
+ /*
+ * Give the xdr decoder a chance to change this if it wants
+ * (necessary in the NFSv4.0 compound case)
+ */
+ rqstp->rq_cachetype = proc->pc_cachetype;
+ /* Decode arguments */
+ xdr = proc->pc_decode;
+ if (xdr && !xdr(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base,
+ rqstp->rq_argp)) {
+ dprintk("nfsd: failed to decode arguments!\n");
+ *statp = rpc_garbage_args;
+ return 1;
+ }
+
/* Check whether we have this call in the cache. */
- switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) {
+ switch (nfsd_cache_lookup(rqstp)) {
case RC_INTR:
case RC_DROPIT:
return 0;
@@ -588,16 +595,6 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
/* do it */
}
- /* Decode arguments */
- xdr = proc->pc_decode;
- if (xdr && !xdr(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base,
- rqstp->rq_argp)) {
- dprintk("nfsd: failed to decode arguments!\n");
- nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
- *statp = rpc_garbage_args;
- return 1;
- }
-
/* need to grab the location to store the status, as
* nfsv4 does some encoding while processing
*/
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 6bd2f3c21f2b..4eefaf1b42e8 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -482,6 +482,7 @@ extern void nfsd4_recdir_purge_old(void);
extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
extern void release_session_client(struct nfsd4_session *);
+extern __be32 nfs4_validate_stateid(stateid_t *, int);
static inline void
nfs4_put_stateowner(struct nfs4_stateowner *so)
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 366401e1a536..d2a8d04428c7 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -342,6 +342,25 @@ struct nfsd4_setclientid_confirm {
nfs4_verifier sc_confirm;
};
+struct nfsd4_saved_compoundargs {
+ __be32 *p;
+ __be32 *end;
+ int pagelen;
+ struct page **pagelist;
+};
+
+struct nfsd4_test_stateid {
+ __be32 ts_num_ids;
+ __be32 ts_has_session;
+ struct nfsd4_compoundargs *ts_saved_args;
+ struct nfsd4_saved_compoundargs ts_savedp;
+};
+
+struct nfsd4_free_stateid {
+ stateid_t fr_stateid; /* request */
+ __be32 fr_status; /* response */
+};
+
/* also used for NVERIFY */
struct nfsd4_verify {
u32 ve_bmval[3]; /* request */
@@ -432,10 +451,14 @@ struct nfsd4_op {
struct nfsd4_destroy_session destroy_session;
struct nfsd4_sequence sequence;
struct nfsd4_reclaim_complete reclaim_complete;
+ struct nfsd4_test_stateid test_stateid;
+ struct nfsd4_free_stateid free_stateid;
} u;
struct nfs4_replay * replay;
};
+bool nfsd4_cache_this_op(struct nfsd4_op *);
+
struct nfsd4_compoundargs {
/* scratch variables for XDR decode */
__be32 * p;
@@ -458,6 +481,7 @@ struct nfsd4_compoundargs {
u32 opcnt;
struct nfsd4_op *ops;
struct nfsd4_op iops[8];
+ int cachetype;
};
struct nfsd4_compoundres {
@@ -559,11 +583,15 @@ extern __be32
nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_release_lockowner *rlockowner);
-extern void nfsd4_release_compoundargs(struct nfsd4_compoundargs *);
+extern int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp);
extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, clientid_t *clid);
+extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
+extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
+ struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
#endif
/*