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: */
* 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
#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];
#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
* 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:
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;
}
list_add(&stp->st_hash, &stateid_hashtbl[hashval]);
list_add(&stp->st_perstateowner, &sop->so_stateids);
list_add(&stp->st_perfile, &fp->fi_stateids);
+ stp->st_type = NFS4_OPEN_STID;
stp->st_stateowner = sop;
get_nfs4_file(fp);
stp->st_file = fp;
{
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;
}
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) {
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);
stp = nfs4_alloc_stateid();
if (stp == NULL)
- return nfserr_resource;
+ return nfserr_jukebox;
status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
if (status) {
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;
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: */
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)
+__be32 nfs4_validate_stateid(stateid_t *stateid, bool has_session)
{
struct nfs4_stateid *stp = NULL;
__be32 status = nfserr_stale_stateid;
if (!stp->st_stateowner->so_confirmed)
goto out;
- status = check_stateid_generation(stateid, &stp->st_stateid, flags);
+ status = check_stateid_generation(stateid, &stp->st_stateid, has_session);
if (status)
goto out;
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);
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);
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);
}
}
- if (is_open_stateid(stp)) {
+ if (stp->st_type == NFS4_OPEN_STID) {
ret = nfserr_locks_held;
goto out;
} else {
nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
stateid_t *stateid, int flags,
struct nfs4_stateowner **sopp,
- struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
+ struct nfs4_stateid **stpp)
{
struct nfs4_stateid *stp;
struct nfs4_stateowner *sop;
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
*stpp = stp;
*sopp = sop = stp->st_stateowner;
- if (lock) {
- clientid_t *lockclid = &lock->v.new.clientid;
- struct nfs4_client *clp = sop->so_client;
- int lkflg = 0;
- __be32 status;
-
- lkflg = setlkflg(lock->lk_type);
-
- if (lock->lk_is_new) {
- if (!sop->so_is_open_owner)
- return nfserr_bad_stateid;
- if (!(flags & HAS_SESSION) &&
- !same_clid(&clp->cl_clientid, lockclid))
- return nfserr_bad_stateid;
- /* stp is the open stateid */
- status = nfs4_check_openmode(stp, lkflg);
- if (status)
- return status;
- } else {
- /* stp is the lock stateid */
- status = nfs4_check_openmode(stp->st_openstp, lkflg);
- if (status)
- return status;
- }
- }
-
if (nfs4_check_fh(current_fh, stp)) {
dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n");
return nfserr_bad_stateid;
* 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) {
" 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);
if ((status = nfs4_preprocess_seqid_op(cstate,
oc->oc_seqid, &oc->oc_req_stateid,
CONFIRM | OPEN_STATE,
- &oc->oc_stateowner, &stp, NULL)))
+ &oc->oc_stateowner, &stp)))
goto out;
sop = oc->oc_stateowner;
od->od_seqid,
&od->od_stateid,
OPEN_STATE,
- &od->od_stateowner, &stp, NULL)))
+ &od->od_stateowner, &stp)))
goto out;
status = nfserr_inval;
close->cl_seqid,
&close->cl_stateid,
OPEN_STATE | CLOSE_STATE,
- &close->cl_stateowner, &stp, NULL)))
+ &close->cl_stateowner, &stp)))
goto out;
status = nfs_ok;
update_stateid(&stp->st_stateid);
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))
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);
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,
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
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;
}
list_add(&stp->st_perfile, &fp->fi_stateids);
list_add(&stp->st_perstateowner, &sop->so_stateids);
stp->st_stateowner = sop;
+ stp->st_type = NFS4_LOCK_STID;
get_nfs4_file(fp);
stp->st_file = fp;
stp->st_stateid.si_boot = boot_time;
struct file_lock conflock;
__be32 status = 0;
unsigned int strhashval;
+ int lkflg;
int err;
dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n",
lock->lk_new_open_seqid,
&lock->lk_new_open_stateid,
OPEN_STATE,
- &lock->lk_replay_owner, &open_stp,
- lock);
+ &lock->lk_replay_owner, &open_stp);
if (status)
goto out;
+ status = nfserr_bad_stateid;
open_sop = lock->lk_replay_owner;
+ if (!nfsd4_has_session(cstate) &&
+ !same_clid(&open_sop->so_client->cl_clientid,
+ &lock->v.new.clientid))
+ goto out;
/* create lockowner and lock stateid */
fp = open_stp->st_file;
strhashval = lock_ownerstr_hashval(fp->fi_inode,
/* 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)
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
LOCK_STATE,
- &lock->lk_replay_owner, &lock_stp, lock);
+ &lock->lk_replay_owner, &lock_stp);
if (status)
goto out;
lock_sop = lock->lk_replay_owner;
}
/* lock->lk_replay_owner and lock_stp have been created or found */
+ lkflg = setlkflg(lock->lk_type);
+ status = nfs4_check_openmode(lock_stp, lkflg);
+ if (status)
+ goto out;
+
status = nfserr_grace;
if (locks_in_grace() && !lock->lk_reclaim)
goto out;
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:
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);
locku->lu_seqid,
&locku->lu_stateid,
LOCK_STATE,
- &locku->lu_stateowner, &stp, NULL)))
+ &locku->lu_stateowner, &stp)))
goto out;
filp = find_any_file(stp->st_file);
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]);
int status;
nfs4_lock_state();
- nfsd4_init_recdir(user_recovery_dirname);
+ nfsd4_init_recdir();
status = nfsd4_recdir_load();
nfs4_unlock_state();
if (status)
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;
-}