diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 22:49:19 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 22:49:19 -0700 |
commit | 2dad3206db5c3832cde1f58650027fea3ff7adf3 (patch) | |
tree | 96314a554afdab5904a939793156d4ae23ec11c1 /fs/nfsd | |
parent | 84635d68be4b846ba984a89f386524153330c597 (diff) | |
parent | 0c12eaffdf09466f36a9ffe970dda8f4aeb6efc0 (diff) |
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/Kconfig | 12 | ||||
-rw-r--r-- | fs/nfsd/cache.h | 2 | ||||
-rw-r--r-- | fs/nfsd/export.c | 418 | ||||
-rw-r--r-- | fs/nfsd/lockd.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 58 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 265 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 144 | ||||
-rw-r--r-- | fs/nfsd/nfscache.c | 3 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 343 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 33 | ||||
-rw-r--r-- | fs/nfsd/state.h | 1 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 30 |
12 files changed, 449 insertions, 862 deletions
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index fbb2a5ef581..10e6366608f 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 d892be61016..93cc9d34c45 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 b9566e46219..f4cc1e2bfc5 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 7c831a2731f..77e7a5cca88 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 3a6dbd70b34..e8077766661 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 e98f3c2e949..3787ec11740 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 * f |