nfsd4: remove HAS_SESSION
[firefly-linux-kernel-4.4.55.git] / fs / nfsd / nfs4state.c
index e98f3c2e9492a9d2dd3e86e6e443e924c9f04187..14c8dd64e136930eda8c439ac38ed536c769a063 100644 (file)
@@ -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,10 @@ 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: */
 
@@ -132,18 +134,27 @@ unsigned int max_delegations;
  * Open owner state (share locks)
  */
 
-/* hash tables for nfs4_stateowner */
-#define OWNER_HASH_BITS              8
-#define OWNER_HASH_SIZE             (1 << OWNER_HASH_BITS)
-#define OWNER_HASH_MASK             (OWNER_HASH_SIZE - 1)
+/* hash tables for open owners */
+#define OPEN_OWNER_HASH_BITS              8
+#define OPEN_OWNER_HASH_SIZE             (1 << OPEN_OWNER_HASH_BITS)
+#define OPEN_OWNER_HASH_MASK             (OPEN_OWNER_HASH_SIZE - 1)
 
-#define ownerid_hashval(id) \
-        ((id) & OWNER_HASH_MASK)
-#define ownerstr_hashval(clientid, ownername) \
-        (((clientid) + opaque_hashval((ownername.data), (ownername.len))) & OWNER_HASH_MASK)
+static unsigned int open_ownerid_hashval(const u32 id)
+{
+       return id & OPEN_OWNER_HASH_MASK;
+}
+
+static unsigned int open_ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername)
+{
+       unsigned int ret;
 
-static struct list_head        ownerid_hashtbl[OWNER_HASH_SIZE];
-static struct list_head        ownerstr_hashtbl[OWNER_HASH_SIZE];
+       ret = opaque_hashval(ownername->data, ownername->len);
+       ret += clientid;
+       return ret & OPEN_OWNER_HASH_MASK;
+}
+
+static struct list_head        open_ownerid_hashtbl[OPEN_OWNER_HASH_SIZE];
+static struct list_head        open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE];
 
 /* hash table for nfs4_file */
 #define FILE_HASH_BITS                   8
@@ -154,10 +165,16 @@ static struct list_head   ownerstr_hashtbl[OWNER_HASH_SIZE];
 #define STATEID_HASH_SIZE              (1 << STATEID_HASH_BITS)
 #define STATEID_HASH_MASK              (STATEID_HASH_SIZE - 1)
 
-#define file_hashval(x) \
-        hash_ptr(x, FILE_HASH_BITS)
-#define stateid_hashval(owner_id, file_id)  \
-        (((owner_id) + (file_id)) & STATEID_HASH_MASK)
+static unsigned int file_hashval(struct inode *ino)
+{
+       /* XXX: why are we hashing on inode pointer, anyway? */
+       return hash_ptr(ino, FILE_HASH_BITS);
+}
+
+static unsigned int stateid_hashval(u32 owner_id, u32 file_id)
+{
+       return (owner_id + file_id) & STATEID_HASH_MASK;
+}
 
 static struct list_head file_hashtbl[FILE_HASH_SIZE];
 static struct list_head stateid_hashtbl[STATEID_HASH_SIZE];
@@ -288,10 +305,16 @@ static DEFINE_SPINLOCK(client_lock);
 #define CLIENT_HASH_SIZE                (1 << CLIENT_HASH_BITS)
 #define CLIENT_HASH_MASK                (CLIENT_HASH_SIZE - 1)
 
-#define clientid_hashval(id) \
-       ((id) & CLIENT_HASH_MASK)
-#define clientstr_hashval(name) \
-       (opaque_hashval((name), 8) & CLIENT_HASH_MASK)
+static unsigned int clientid_hashval(u32 id)
+{
+       return id & CLIENT_HASH_MASK;
+}
+
+static unsigned int clientstr_hashval(const char *name)
+{
+       return opaque_hashval(name, 8) & CLIENT_HASH_MASK;
+}
+
 /*
  * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
  * used in reboot/reset lease grace period processing
@@ -381,14 +404,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 +413,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 +1525,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 +1616,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 +1781,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 +1821,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;
@@ -1908,7 +1965,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
         * of 5 bullet points, labeled as CASE0 - CASE4 below.
         */
        unconf = find_unconfirmed_client_by_str(dname, strhashval);
-       status = nfserr_resource;
+       status = nfserr_jukebox;
        if (!conf) {
                /*
                 * RFC 3530 14.2.33 CASE 4:
@@ -2158,51 +2215,61 @@ nfs4_free_stateowner(struct kref *kref)
        kmem_cache_free(stateowner_slab, sop);
 }
 
-static inline struct nfs4_stateowner *
-alloc_stateowner(struct xdr_netobj *owner)
+static void init_nfs4_replay(struct nfs4_replay *rp)
+{
+       rp->rp_status = nfserr_serverfault;
+       rp->rp_buflen = 0;
+       rp->rp_buf = rp->rp_ibuf;
+}
+
+static inline struct nfs4_stateowner *alloc_stateowner(struct xdr_netobj *owner, struct nfs4_client *clp)
 {
        struct nfs4_stateowner *sop;
 
-       if ((sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL))) {
-               if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) {
-                       memcpy(sop->so_owner.data, owner->data, owner->len);
-                       sop->so_owner.len = owner->len;
-                       kref_init(&sop->so_ref);
-                       return sop;
-               } 
+       sop = kmem_cache_alloc(stateowner_slab, GFP_KERNEL);
+       if (!sop)
+               return NULL;
+
+       sop->so_owner.data = kmemdup(owner->data, owner->len, GFP_KERNEL);
+       if (!sop->so_owner.data) {
                kmem_cache_free(stateowner_slab, sop);
+               return NULL;
        }
-       return NULL;
+       sop->so_owner.len = owner->len;
+
+       kref_init(&sop->so_ref);
+       INIT_LIST_HEAD(&sop->so_perclient);
+       INIT_LIST_HEAD(&sop->so_stateids);
+       INIT_LIST_HEAD(&sop->so_perstateid);
+       INIT_LIST_HEAD(&sop->so_close_lru);
+       sop->so_id = current_ownerid++;
+       sop->so_time = 0;
+       sop->so_client = clp;
+       init_nfs4_replay(&sop->so_replay);
+       return sop;
+}
+
+static void hash_openowner(struct nfs4_stateowner *sop, struct nfs4_client *clp, unsigned int strhashval)
+{
+       unsigned int idhashval;
+
+       idhashval = open_ownerid_hashval(sop->so_id);
+       list_add(&sop->so_idhash, &open_ownerid_hashtbl[idhashval]);
+       list_add(&sop->so_strhash, &open_ownerstr_hashtbl[strhashval]);
+       list_add(&sop->so_perclient, &clp->cl_openowners);
 }
 
 static struct nfs4_stateowner *
 alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) {
        struct nfs4_stateowner *sop;
-       struct nfs4_replay *rp;
-       unsigned int idhashval;
 
-       if (!(sop = alloc_stateowner(&open->op_owner)))
+       sop = alloc_stateowner(&open->op_owner, clp);
+       if (!sop)
                return NULL;
-       idhashval = ownerid_hashval(current_ownerid);
-       INIT_LIST_HEAD(&sop->so_idhash);
-       INIT_LIST_HEAD(&sop->so_strhash);
-       INIT_LIST_HEAD(&sop->so_perclient);
-       INIT_LIST_HEAD(&sop->so_stateids);
-       INIT_LIST_HEAD(&sop->so_perstateid);  /* not used */
-       INIT_LIST_HEAD(&sop->so_close_lru);
-       sop->so_time = 0;
-       list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]);
-       list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]);
-       list_add(&sop->so_perclient, &clp->cl_openowners);
        sop->so_is_open_owner = 1;
-       sop->so_id = current_ownerid++;
-       sop->so_client = clp;
        sop->so_seqid = open->op_seqid;
        sop->so_confirmed = 0;
-       rp = &sop->so_replay;
-       rp->rp_status = nfserr_serverfault;
-       rp->rp_buflen = 0;
-       rp->rp_buf = rp->rp_ibuf;
+       hash_openowner(sop, clp, strhashval);
        return sop;
 }
 
@@ -2256,7 +2323,7 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open)
 {
        struct nfs4_stateowner *so = NULL;
 
-       list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) {
+       list_for_each_entry(so, &open_ownerstr_hashtbl[hashval], so_strhash) {
                if (same_owner_str(so, &open->op_owner, &open->op_clientid))
                        return so;
        }
@@ -2337,15 +2404,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 +2454,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,
 };
 
 
@@ -2416,7 +2474,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
        if (STALE_CLIENTID(&open->op_clientid))
                return nfserr_stale_clientid;
 
-       strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner);
+       strhashval = open_ownerstr_hashval(clientid->cl_id, &open->op_owner);
        sop = find_openstateowner_str(strhashval, open);
        open->op_stateowner = sop;
        if (!sop) {
@@ -2454,7 +2512,7 @@ renew:
        if (open->op_stateowner == NULL) {
                sop = alloc_init_open_stateowner(strhashval, clp, open);
                if (sop == NULL)
-                       return nfserr_resource;
+                       return nfserr_jukebox;
                open->op_stateowner = sop;
        }
        list_del_init(&sop->so_close_lru);
@@ -2556,12 +2614,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,
@@ -2584,9 +2648,9 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp,
 
        stp = nfs4_alloc_stateid();
        if (stp == NULL)
-               return nfserr_resource;
+               return nfserr_jukebox;
 
-       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 +2683,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;
@@ -2815,7 +2879,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
                status = nfserr_bad_stateid;
                if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR)
                        goto out;
-               status = nfserr_resource;
+               status = nfserr_jukebox;
                fp = alloc_init_file(ino);
                if (fp == NULL)
                        goto out;
@@ -3104,13 +3168,13 @@ grace_disallows_io(struct inode *inode)
        return locks_in_grace() && mandatory_lock(inode);
 }
 
-static int check_stateid_generation(stateid_t *in, stateid_t *ref, int flags)
+static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
 {
        /*
         * When sessions are used the stateid generation number is ignored
         * when it is zero.
         */
-       if ((flags & HAS_SESSION) && in->si_generation == 0)
+       if (has_session && in->si_generation == 0)
                goto out;
 
        /* If the client sends us a stateid from the future, it's buggy: */
@@ -3137,6 +3201,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, bool has_session)
+{
+       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, has_session);
+       if (status)
+               goto out;
+
+       status = nfs_ok;
+out:
+       return status;
+}
+
 /*
 * Checks for stateid operations
 */
@@ -3156,9 +3251,6 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
        if (grace_disallows_io(ino))
                return nfserr_grace;
 
-       if (nfsd4_has_session(cstate))
-               flags |= HAS_SESSION;
-
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return check_special_stateids(current_fh, stateid, flags);
 
@@ -3175,8 +3267,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
                dp = find_delegation_stateid(ino, stateid);
                if (!dp)
                        goto out;
-               status = check_stateid_generation(stateid, &dp->dl_stateid,
-                                                 flags);
+               status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate));
                if (status)
                        goto out;
                status = nfs4_check_delegmode(dp, flags);
@@ -3197,7 +3288,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
                if (!stp->st_stateowner->so_confirmed)
                        goto out;
                status = check_stateid_generation(stateid, &stp->st_stateid,
-                                                 flags);
+                                                 nfsd4_has_session(cstate));
                if (status)
                        goto out;
                status = nfs4_check_openmode(stp, flags);
@@ -3216,6 +3307,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)
 {
@@ -3251,9 +3417,6 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
        if (STALE_STATEID(stateid))
                return nfserr_stale_stateid;
 
-       if (nfsd4_has_session(cstate))
-               flags |= HAS_SESSION;
-
        /*
        * We return BAD_STATEID if filehandle doesn't match stateid, 
        * the confirmed flag is incorrecly set, or the generation 
@@ -3287,7 +3450,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
                if (lock->lk_is_new) {
                        if (!sop->so_is_open_owner)
                                return nfserr_bad_stateid;
-                       if (!(flags & HAS_SESSION) &&
+                       if (!nfsd4_has_session(cstate) &&
                            !same_clid(&clp->cl_clientid, lockclid))
                                return nfserr_bad_stateid;
                        /* stp is the open stateid */
@@ -3312,7 +3475,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
        *  For the moment, we ignore the possibility of 
        *  generation number wraparound.
        */
-       if (!(flags & HAS_SESSION) && seqid != sop->so_seqid)
+       if (!nfsd4_has_session(cstate) && seqid != sop->so_seqid)
                goto check_replay;
 
        if (sop->so_confirmed && flags & CONFIRM) {
@@ -3325,7 +3488,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
                                " confirmed yet!\n");
                return nfserr_bad_stateid;
        }
-       status = check_stateid_generation(stateid, &stp->st_stateid, flags);
+       status = check_stateid_generation(stateid, &stp->st_stateid, nfsd4_has_session(cstate));
        if (status)
                return status;
        renew_client(sop->so_client);
@@ -3384,18 +3547,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 +3576,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 +3604,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);
@@ -3515,14 +3672,11 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        stateid_t *stateid = &dr->dr_stateid;
        struct inode *inode;
        __be32 status;
-       int flags = 0;
 
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
                return status;
        inode = cstate->current_fh.fh_dentry->d_inode;
 
-       if (nfsd4_has_session(cstate))
-               flags |= HAS_SESSION;
        nfs4_lock_state();
        status = nfserr_bad_stateid;
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
@@ -3537,7 +3691,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        dp = find_delegation_stateid(inode, stateid);
        if (!dp)
                goto out;
-       status = check_stateid_generation(stateid, &dp->dl_stateid, flags);
+       status = check_stateid_generation(stateid, &dp->dl_stateid, nfsd4_has_session(cstate));
        if (status)
                goto out;
        renew_client(dp->dl_client);
@@ -3578,8 +3732,10 @@ last_byte_offset(u64 start, u64 len)
        return end > start ? end - 1: NFS4_MAX_UINT64;
 }
 
-#define lockownerid_hashval(id) \
-        ((id) & LOCK_HASH_MASK)
+static unsigned int lockownerid_hashval(u32 id)
+{
+       return id & LOCK_HASH_MASK;
+}
 
 static inline unsigned int
 lock_ownerstr_hashval(struct inode *inode, u32 cl_id,
@@ -3594,6 +3750,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 +3787,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)
 {
@@ -3700,6 +3902,16 @@ find_lockstateowner_str(struct inode *inode, clientid_t *clid,
        return NULL;
 }
 
+static void hash_lockowner(struct nfs4_stateowner *sop, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp)
+{
+       unsigned int idhashval;
+
+       idhashval = lockownerid_hashval(sop->so_id);
+       list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]);
+       list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]);
+       list_add(&sop->so_perstateid, &open_stp->st_lockowners);
+}
+
 /*
  * Alloc a lock owner structure.
  * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has 
@@ -3711,33 +3923,17 @@ find_lockstateowner_str(struct inode *inode, clientid_t *clid,
 static struct nfs4_stateowner *
 alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_stateid *open_stp, struct nfsd4_lock *lock) {
        struct nfs4_stateowner *sop;
-       struct nfs4_replay *rp;
-       unsigned int idhashval;
 
-       if (!(sop = alloc_stateowner(&lock->lk_new_owner)))
+       sop = alloc_stateowner(&lock->lk_new_owner, clp);
+       if (!sop)
                return NULL;
-       idhashval = lockownerid_hashval(current_ownerid);
-       INIT_LIST_HEAD(&sop->so_idhash);
-       INIT_LIST_HEAD(&sop->so_strhash);
-       INIT_LIST_HEAD(&sop->so_perclient);
        INIT_LIST_HEAD(&sop->so_stateids);
-       INIT_LIST_HEAD(&sop->so_perstateid);
-       INIT_LIST_HEAD(&sop->so_close_lru); /* not used */
-       sop->so_time = 0;
-       list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]);
-       list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]);
-       list_add(&sop->so_perstateid, &open_stp->st_lockowners);
        sop->so_is_open_owner = 0;
-       sop->so_id = current_ownerid++;
-       sop->so_client = clp;
        /* It is the openowner seqid that will be incremented in encode in the
         * case of new lockowners; so increment the lock seqid manually: */
        sop->so_seqid = lock->lk_new_lock_seqid + 1;
        sop->so_confirmed = 1;
-       rp = &sop->so_replay;
-       rp->rp_status = nfserr_serverfault;
-       rp->rp_buflen = 0;
-       rp->rp_buf = rp->rp_ibuf;
+       hash_lockowner(sop, strhashval, clp, open_stp);
        return sop;
 }
 
@@ -3854,7 +4050,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                /* XXX: Do we need to check for duplicate stateowners on
                 * the same file, or should they just be allowed (and
                 * create new stateids)? */
-               status = nfserr_resource;
+               status = nfserr_jukebox;
                lock_sop = alloc_init_lock_stateowner(strhashval,
                                open_sop->so_client, open_stp, lock);
                if (lock_sop == NULL)
@@ -3938,9 +4134,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        case (EDEADLK):
                status = nfserr_deadlock;
                break;
-       default:        
+       default:
                dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",err);
-               status = nfserr_resource;
+               status = nfserrno(err);
                break;
        }
 out:
@@ -3998,12 +4194,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid))
                goto out;
 
-       if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) {
-               dprintk("NFSD: nfsd4_lockt: fh_verify() failed!\n");
-               if (status == nfserr_symlink)
-                       status = nfserr_inval;
+       if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
                goto out;
-       }
 
        inode = cstate->current_fh.fh_dentry->d_inode;
        locks_init_lock(&file_lock);
@@ -4320,9 +4512,9 @@ nfs4_state_init(void)
        for (i = 0; i < FILE_HASH_SIZE; i++) {
                INIT_LIST_HEAD(&file_hashtbl[i]);
        }
-       for (i = 0; i < OWNER_HASH_SIZE; i++) {
-               INIT_LIST_HEAD(&ownerstr_hashtbl[i]);
-               INIT_LIST_HEAD(&ownerid_hashtbl[i]);
+       for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) {
+               INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]);
+               INIT_LIST_HEAD(&open_ownerid_hashtbl[i]);
        }
        for (i = 0; i < STATEID_HASH_SIZE; i++) {
                INIT_LIST_HEAD(&stateid_hashtbl[i]);
@@ -4346,7 +4538,7 @@ nfsd4_load_reboot_recovery_data(void)
        int status;
 
        nfs4_lock_state();
-       nfsd4_init_recdir(user_recovery_dirname);
+       nfsd4_init_recdir();
        status = nfsd4_recdir_load();
        nfs4_unlock_state();
        if (status)
@@ -4455,40 +4647,3 @@ nfs4_state_shutdown(void)
        nfs4_unlock_state();
        nfsd4_destroy_callback_queue();
 }
-
-/*
- * user_recovery_dirname is protected by the nfsd_mutex since it's only
- * accessed when nfsd is starting.
- */
-static void
-nfs4_set_recdir(char *recdir)
-{
-       strcpy(user_recovery_dirname, recdir);
-}
-
-/*
- * Change the NFSv4 recovery directory to recdir.
- */
-int
-nfs4_reset_recoverydir(char *recdir)
-{
-       int status;
-       struct path path;
-
-       status = kern_path(recdir, LOOKUP_FOLLOW, &path);
-       if (status)
-               return status;
-       status = -ENOTDIR;
-       if (S_ISDIR(path.dentry->d_inode->i_mode)) {
-               nfs4_set_recdir(recdir);
-               status = 0;
-       }
-       path_put(&path);
-       return status;
-}
-
-char *
-nfs4_recoverydir(void)
-{
-       return user_recovery_dirname;
-}