#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
+#include <linux/namei.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
/* Globals */
static time_t lease_time = 90; /* default lease time */
static time_t user_lease_time = 90;
-time_t boot_time;
-static time_t grace_end = 0;
+static time_t boot_time;
+static int in_grace = 1;
static u32 current_clientid = 1;
static u32 current_ownerid = 1;
static u32 current_fileid = 1;
static u32 current_delegid = 1;
static u32 nfs4_init;
-stateid_t zerostateid; /* bits all 0 */
-stateid_t onestateid; /* bits all 1 */
+static stateid_t zerostateid; /* bits all 0 */
+static stateid_t onestateid; /* bits all 1 */
+
+#define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t)))
+#define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t)))
/* forward declarations */
-struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
+static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
static void release_stateid_lockowners(struct nfs4_stateid *open_stp);
+static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
+static void nfs4_set_recdir(char *recdir);
/* Locking:
*
*/
static DECLARE_MUTEX(client_sema);
-kmem_cache_t *stateowner_slab = NULL;
-kmem_cache_t *file_slab = NULL;
-kmem_cache_t *stateid_slab = NULL;
-kmem_cache_t *deleg_slab = NULL;
+static kmem_cache_t *stateowner_slab = NULL;
+static kmem_cache_t *file_slab = NULL;
+static kmem_cache_t *stateid_slab = NULL;
+static kmem_cache_t *deleg_slab = NULL;
void
nfs4_lock_state(void)
*/
/* recall_lock protects the del_recall_lru */
-spinlock_t recall_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t recall_lock = SPIN_LOCK_UNLOCKED;
static struct list_head del_recall_lru;
static void
dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL);
if (dp == NULL)
return dp;
- INIT_LIST_HEAD(&dp->dl_del_perfile);
- INIT_LIST_HEAD(&dp->dl_del_perclnt);
+ INIT_LIST_HEAD(&dp->dl_perfile);
+ INIT_LIST_HEAD(&dp->dl_perclnt);
INIT_LIST_HEAD(&dp->dl_recall_lru);
dp->dl_client = clp;
get_nfs4_file(fp);
current_fh->fh_handle.fh_size);
dp->dl_time = 0;
atomic_set(&dp->dl_count, 1);
- list_add(&dp->dl_del_perfile, &fp->fi_delegations);
- list_add(&dp->dl_del_perclnt, &clp->cl_del_perclnt);
+ list_add(&dp->dl_perfile, &fp->fi_delegations);
+ list_add(&dp->dl_perclnt, &clp->cl_delegations);
return dp;
}
static void
unhash_delegation(struct nfs4_delegation *dp)
{
- list_del_init(&dp->dl_del_perfile);
- list_del_init(&dp->dl_del_perclnt);
+ list_del_init(&dp->dl_perfile);
+ list_del_init(&dp->dl_perclnt);
spin_lock(&recall_lock);
list_del_init(&dp->dl_recall_lru);
spin_unlock(&recall_lock);
#define clientid_hashval(id) \
((id) & CLIENT_HASH_MASK)
-#define clientstr_hashval(name, namelen) \
- (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)
+#define clientstr_hashval(name) \
+ (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
INIT_LIST_HEAD(&reaplist);
spin_lock(&recall_lock);
- while (!list_empty(&clp->cl_del_perclnt)) {
- dp = list_entry(clp->cl_del_perclnt.next, struct nfs4_delegation, dl_del_perclnt);
+ while (!list_empty(&clp->cl_delegations)) {
+ dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
dprintk("NFSD: expire client. dp %p, fp %p\n", dp,
dp->dl_flock);
- list_del_init(&dp->dl_del_perclnt);
+ list_del_init(&dp->dl_perclnt);
list_move(&dp->dl_recall_lru, &reaplist);
}
spin_unlock(&recall_lock);
list_del(&clp->cl_idhash);
list_del(&clp->cl_strhash);
list_del(&clp->cl_lru);
- while (!list_empty(&clp->cl_perclient)) {
- sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient);
+ while (!list_empty(&clp->cl_openowners)) {
+ sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient);
release_stateowner(sop);
}
put_nfs4_client(clp);
}
static struct nfs4_client *
-create_client(struct xdr_netobj name) {
+create_client(struct xdr_netobj name, char *recdir) {
struct nfs4_client *clp;
if (!(clp = alloc_client(name)))
goto out;
+ memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
atomic_set(&clp->cl_count, 1);
atomic_set(&clp->cl_callback.cb_set, 0);
- clp->cl_callback.cb_parsed = 0;
INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_strhash);
- INIT_LIST_HEAD(&clp->cl_perclient);
- INIT_LIST_HEAD(&clp->cl_del_perclnt);
+ INIT_LIST_HEAD(&clp->cl_openowners);
+ INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_lru);
out:
return clp;
get_group_info(target->cr_group_info);
}
-static int
-cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) {
- if (!n1 || !n2)
- return 0;
- return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len));
+static inline int
+same_name(const char *n1, const char *n2) {
+ return 0 == memcmp(n1, n2, HEXDIR_LEN);
}
static int
return 1;
}
-void
+static void
add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval)
{
unsigned int idhashval;
clp->cl_time = get_seconds();
}
-void
+static void
move_to_confirmed(struct nfs4_client *clp)
{
unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id);
list_del_init(&clp->cl_strhash);
list_del_init(&clp->cl_idhash);
list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]);
- strhashval = clientstr_hashval(clp->cl_name.data,
- clp->cl_name.len);
+ strhashval = clientstr_hashval(clp->cl_recdir);
list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]);
renew_client(clp);
}
return NULL;
}
+static struct nfs4_client *
+find_confirmed_client_by_str(const char *dname, unsigned int hashval)
+{
+ struct nfs4_client *clp;
+
+ list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) {
+ if (same_name(clp->cl_recdir, dname))
+ return clp;
+ }
+ return NULL;
+}
+
+static struct nfs4_client *
+find_unconfirmed_client_by_str(const char *dname, unsigned int hashval)
+{
+ struct nfs4_client *clp;
+
+ list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) {
+ if (same_name(clp->cl_recdir, dname))
+ return clp;
+ }
+ return NULL;
+}
+
/* a helper function for parse_callback */
static int
parse_octet(unsigned int *lenp, char **addrp)
}
/* parse and set the setclientid ipv4 callback address */
-int
+static int
parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp)
{
int temp = 0;
return 1;
}
-void
+static void
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
{
struct nfs4_callback *cb = &clp->cl_callback;
goto out_err;
cb->cb_prog = se->se_callback_prog;
cb->cb_ident = se->se_callback_ident;
- cb->cb_parsed = 1;
return;
out_err:
- printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
+ dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
"will not receive delegations\n",
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
- cb->cb_parsed = 0;
return;
}
};
nfs4_verifier clverifier = setclid->se_verf;
unsigned int strhashval;
- struct nfs4_client * conf, * unconf, * new, * clp;
+ struct nfs4_client *conf, *unconf, *new;
int status;
+ char dname[HEXDIR_LEN];
- status = nfserr_inval;
if (!check_name(clname))
- goto out;
+ return nfserr_inval;
+
+ status = nfs4_make_rec_clidname(dname, &clname);
+ if (status)
+ return status;
/*
* XXX The Duplicate Request Cache (DRC) has been checked (??)
* We get here on a DRC miss.
*/
- strhashval = clientstr_hashval(clname.data, clname.len);
+ strhashval = clientstr_hashval(dname);
- conf = NULL;
nfs4_lock_state();
- list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) {
- if (!cmp_name(&clp->cl_name, &clname))
- continue;
+ conf = find_confirmed_client_by_str(dname, strhashval);
+ if (conf) {
/*
* CASE 0:
* clname match, confirmed, different principal
* or different ip_address
*/
status = nfserr_clid_inuse;
- if (!cmp_creds(&clp->cl_cred,&rqstp->rq_cred)) {
+ if (!cmp_creds(&conf->cl_cred, &rqstp->rq_cred)
+ || conf->cl_addr != ip_addr) {
printk("NFSD: setclientid: string in use by client"
"(clientid %08x/%08x)\n",
- clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+ conf->cl_clientid.cl_boot, conf->cl_clientid.cl_id);
goto out;
}
- if (clp->cl_addr != ip_addr) {
- printk("NFSD: setclientid: string in use by client"
- "(clientid %08x/%08x)\n",
- clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
- goto out;
- }
-
- /*
- * cl_name match from a previous SETCLIENTID operation
- * XXX check for additional matches?
- */
- conf = clp;
- break;
- }
- unconf = NULL;
- list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) {
- if (!cmp_name(&clp->cl_name, &clname))
- continue;
- /* cl_name match from a previous SETCLIENTID operation */
- unconf = clp;
- break;
}
+ unconf = find_unconfirmed_client_by_str(dname, strhashval);
status = nfserr_resource;
if (!conf) {
/*
*/
if (unconf)
expire_client(unconf);
- if (!(new = create_client(clname)))
+ new = create_client(clname, dname);
+ if (new == NULL)
goto out;
copy_verf(new, &clverifier);
new->cl_addr = ip_addr;
* nfs4_client, but with the new callback info and a
* new cl_confirm
*/
- if ((unconf) &&
- cmp_verf(&unconf->cl_verifier, &conf->cl_verifier) &&
- cmp_clid(&unconf->cl_clientid, &conf->cl_clientid)) {
- expire_client(unconf);
+ if (unconf) {
+ /* Note this is removing unconfirmed {*x***},
+ * which is stronger than RFC recommended {vxc**}.
+ * This has the advantage that there is at most
+ * one {*x***} in either list at any time.
+ */
+ expire_client(unconf);
}
- if (!(new = create_client(clname)))
+ new = create_client(clname, dname);
+ if (new == NULL)
goto out;
copy_verf(new,&conf->cl_verifier);
new->cl_addr = ip_addr;
* using input clverifier, clname, and callback info
* and generate a new cl_clientid and cl_confirm.
*/
- if (!(new = create_client(clname)))
+ new = create_client(clname, dname);
+ if (new == NULL)
goto out;
copy_verf(new,&clverifier);
new->cl_addr = ip_addr;
* new cl_verifier and a new cl_confirm
*/
expire_client(unconf);
- if (!(new = create_client(clname)))
+ new = create_client(clname, dname);
+ if (new == NULL)
goto out;
copy_verf(new,&clverifier);
new->cl_addr = ip_addr;
nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confirm *setclientid_confirm)
{
u32 ip_addr = rqstp->rq_addr.sin_addr.s_addr;
- struct nfs4_client *clp, *conf = NULL, *unconf = NULL;
+ struct nfs4_client *conf, *unconf;
nfs4_verifier confirm = setclientid_confirm->sc_confirm;
clientid_t * clid = &setclientid_confirm->sc_clientid;
int status;
*/
nfs4_lock_state();
- clp = find_confirmed_client(clid);
- if (clp) {
- status = nfserr_inval;
- /*
- * Found a record for this clientid. If the IP addresses
- * don't match, return ERR_INVAL just as if the record had
- * not been found.
- */
- if (clp->cl_addr != ip_addr) {
- printk("NFSD: setclientid: string in use by client"
- "(clientid %08x/%08x)\n",
- clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
- goto out;
- }
- conf = clp;
- }
- clp = find_unconfirmed_client(clid);
- if (clp) {
- status = nfserr_inval;
- if (clp->cl_addr != ip_addr) {
- printk("NFSD: setclientid: string in use by client"
- "(clientid %08x/%08x)\n",
- clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
- goto out;
- }
- unconf = clp;
- }
- /* CASE 1:
- * unconf record that matches input clientid and input confirm.
- * conf record that matches input clientid.
- * conf and unconf records match names, verifiers
- */
+
+ conf = find_confirmed_client(clid);
+ unconf = find_unconfirmed_client(clid);
+
+ status = nfserr_clid_inuse;
+ if (conf && conf->cl_addr != ip_addr)
+ goto out;
+ if (unconf && unconf->cl_addr != ip_addr)
+ goto out;
+
if ((conf && unconf) &&
(cmp_verf(&unconf->cl_confirm, &confirm)) &&
(cmp_verf(&conf->cl_verifier, &unconf->cl_verifier)) &&
- (cmp_name(&conf->cl_name,&unconf->cl_name)) &&
+ (same_name(conf->cl_recdir,unconf->cl_recdir)) &&
(!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm))) {
+ /* CASE 1:
+ * unconf record that matches input clientid and input confirm.
+ * conf record that matches input clientid.
+ * conf and unconf records match names, verifiers
+ */
if (!cmp_creds(&conf->cl_cred, &unconf->cl_cred))
status = nfserr_clid_inuse;
else {
- expire_client(conf);
- clp = unconf;
- move_to_confirmed(unconf);
+ /* XXX: We just turn off callbacks until we can handle
+ * change request correctly. */
+ atomic_set(&conf->cl_callback.cb_set, 0);
+ gen_confirm(conf);
+ nfsd4_remove_clid_dir(unconf);
+ expire_client(unconf);
status = nfs_ok;
+
}
- goto out;
- }
- /* CASE 2:
- * conf record that matches input clientid.
- * if unconf record that matches input clientid, then unconf->cl_name
- * or unconf->cl_verifier don't match the conf record.
- */
- if ((conf && !unconf) ||
+ } else if ((conf && !unconf) ||
((conf && unconf) &&
(!cmp_verf(&conf->cl_verifier, &unconf->cl_verifier) ||
- !cmp_name(&conf->cl_name, &unconf->cl_name)))) {
- if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) {
+ !same_name(conf->cl_recdir, unconf->cl_recdir)))) {
+ /* CASE 2:
+ * conf record that matches input clientid.
+ * if unconf record matches input clientid, then
+ * unconf->cl_name or unconf->cl_verifier don't match the
+ * conf record.
+ */
+ if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred))
status = nfserr_clid_inuse;
- } else {
- clp = conf;
+ else
status = nfs_ok;
- }
- goto out;
- }
- /* CASE 3:
- * conf record not found.
- * unconf record found.
- * unconf->cl_confirm matches input confirm
- */
- if (!conf && unconf && cmp_verf(&unconf->cl_confirm, &confirm)) {
+ } else if (!conf && unconf
+ && cmp_verf(&unconf->cl_confirm, &confirm)) {
+ /* CASE 3:
+ * conf record not found.
+ * unconf record found.
+ * unconf->cl_confirm matches input confirm
+ */
if (!cmp_creds(&unconf->cl_cred, &rqstp->rq_cred)) {
status = nfserr_clid_inuse;
} else {
- status = nfs_ok;
- clp = unconf;
+ unsigned int hash =
+ clientstr_hashval(unconf->cl_recdir);
+ conf = find_confirmed_client_by_str(unconf->cl_recdir,
+ hash);
+ if (conf) {
+ nfsd4_remove_clid_dir(conf);
+ expire_client(conf);
+ }
move_to_confirmed(unconf);
+ conf = unconf;
+ status = nfs_ok;
}
- goto out;
- }
- /* CASE 4:
- * conf record not found, or if conf, then conf->cl_confirm does not
- * match input confirm.
- * unconf record not found, or if unconf, then unconf->cl_confirm
- * does not match input confirm.
- */
- if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm))) &&
- (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm, &confirm)))) {
+ } else if ((!conf || (conf && !cmp_verf(&conf->cl_confirm, &confirm)))
+ && (!unconf || (unconf && !cmp_verf(&unconf->cl_confirm,
+ &confirm)))) {
+ /* CASE 4:
+ * conf record not found, or if conf, conf->cl_confirm does not
+ * match input confirm.
+ * unconf record not found, or if unconf, unconf->cl_confirm
+ * does not match input confirm.
+ */
status = nfserr_stale_clientid;
- goto out;
+ } else {
+ /* check that we have hit one of the cases...*/
+ status = nfserr_clid_inuse;
}
- /* check that we have hit one of the cases...*/
- status = nfserr_inval;
- goto out;
out:
if (!status)
- nfsd4_probe_callback(clp);
+ nfsd4_probe_callback(conf);
nfs4_unlock_state();
return status;
}
INIT_LIST_HEAD(&sop->so_idhash);
INIT_LIST_HEAD(&sop->so_strhash);
INIT_LIST_HEAD(&sop->so_perclient);
- INIT_LIST_HEAD(&sop->so_perfilestate);
- INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */
+ 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_perclient);
+ list_add(&sop->so_perclient, &clp->cl_openowners);
sop->so_is_open_owner = 1;
sop->so_id = current_ownerid++;
sop->so_client = clp;
{
struct nfs4_stateowner *lock_sop;
- while (!list_empty(&open_stp->st_perlockowner)) {
- lock_sop = list_entry(open_stp->st_perlockowner.next,
- struct nfs4_stateowner, so_perlockowner);
- /* list_del(&open_stp->st_perlockowner); */
+ while (!list_empty(&open_stp->st_lockowners)) {
+ lock_sop = list_entry(open_stp->st_lockowners.next,
+ struct nfs4_stateowner, so_perstateid);
+ /* list_del(&open_stp->st_lockowners); */
BUG_ON(lock_sop->so_is_open_owner);
release_stateowner(lock_sop);
}
list_del(&sop->so_strhash);
if (sop->so_is_open_owner)
list_del(&sop->so_perclient);
- list_del(&sop->so_perlockowner);
- while (!list_empty(&sop->so_perfilestate)) {
- stp = list_entry(sop->so_perfilestate.next,
- struct nfs4_stateid, st_perfilestate);
+ list_del(&sop->so_perstateid);
+ while (!list_empty(&sop->so_stateids)) {
+ stp = list_entry(sop->so_stateids.next,
+ struct nfs4_stateid, st_perstateowner);
if (sop->so_is_open_owner)
release_stateid(stp, OPEN_STATE);
else
unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id);
INIT_LIST_HEAD(&stp->st_hash);
- INIT_LIST_HEAD(&stp->st_perfilestate);
- INIT_LIST_HEAD(&stp->st_perlockowner);
+ INIT_LIST_HEAD(&stp->st_perstateowner);
+ INIT_LIST_HEAD(&stp->st_lockowners);
INIT_LIST_HEAD(&stp->st_perfile);
list_add(&stp->st_hash, &stateid_hashtbl[hashval]);
- list_add(&stp->st_perfilestate, &sop->so_perfilestate);
+ list_add(&stp->st_perstateowner, &sop->so_stateids);
list_add(&stp->st_perfile, &fp->fi_stateids);
stp->st_stateowner = sop;
get_nfs4_file(fp);
stp->st_deny_bmap = 0;
__set_bit(open->op_share_access, &stp->st_access_bmap);
__set_bit(open->op_share_deny, &stp->st_deny_bmap);
+ stp->st_openstp = NULL;
}
static void
list_del(&stp->st_hash);
list_del(&stp->st_perfile);
- list_del(&stp->st_perfilestate);
+ list_del(&stp->st_perstateowner);
if (flags & OPEN_STATE) {
release_stateid_lockowners(stp);
stp->st_vfs_file = NULL;
stp = NULL;
}
-void
+static void
move_to_close_lru(struct nfs4_stateowner *sop)
{
dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop);
sop->so_time = get_seconds();
}
-void
+static void
release_state_owner(struct nfs4_stateid *stp, int flag)
{
struct nfs4_stateowner *sop = stp->st_stateowner;
* released by the laundromat service after the lease period
* to enable us to handle CLOSE replay
*/
- if (sop->so_confirmed && list_empty(&sop->so_perfilestate))
+ if (sop->so_confirmed && list_empty(&sop->so_stateids))
move_to_close_lru(sop);
}
#define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0)
#define TEST_DENY(x) ((x >= 0 || x < 5)?1:0)
-void
+static void
set_access(unsigned int *access, unsigned long bmap) {
int i;
}
}
-void
+static void
set_deny(unsigned int *deny, unsigned long bmap) {
int i;
* Called to check deny when READ with all zero stateid or
* WRITE with all zero or all one stateid
*/
-int
+static int
nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
{
struct inode *ino = current_fh->fh_dentry->d_inode;
fp = find_file(ino);
if (!fp)
return nfs_ok;
- ret = nfserr_share_denied;
+ ret = nfserr_locked;
/* Search for conflicting share reservations */
list_for_each_entry(stp, &fp->fi_stateids, st_perfile) {
if (test_bit(deny_type, &stp->st_deny_bmap) ||
return -EAGAIN;
}
-struct lock_manager_operations nfsd_lease_mng_ops = {
+static struct lock_manager_operations nfsd_lease_mng_ops = {
.fl_break = nfsd_break_deleg_cb,
.fl_release_private = nfsd_release_deleg_cb,
.fl_copy_lock = nfsd_copy_lock_deleg_cb,
if (sop) {
open->op_stateowner = sop;
/* check for replay */
- if (open->op_seqid == sop->so_seqid){
+ if (open->op_seqid == sop->so_seqid - 1){
if (sop->so_replay.rp_buflen)
return NFSERR_REPLAY_ME;
else {
goto renew;
}
} else if (sop->so_confirmed) {
- if (open->op_seqid == sop->so_seqid + 1)
+ if (open->op_seqid == sop->so_seqid)
goto renew;
status = nfserr_bad_seqid;
goto out;
status = nfs_ok;
renew_client(sop->so_client);
out:
- if (status && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
- status = nfserr_reclaim_bad;
return status;
}
{
struct nfs4_delegation *dp;
- list_for_each_entry(dp, &fp->fi_delegations, dl_del_perfile) {
+ list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) {
if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid)
return dp;
}
}
-/* decrement seqid on successful reclaim, it will be bumped in encode_open */
static void
-nfs4_set_claim_prev(struct nfsd4_open *open, int *status)
+nfs4_set_claim_prev(struct nfsd4_open *open)
{
- if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
- if (*status)
- *status = nfserr_reclaim_bad;
- else {
- open->op_stateowner->so_confirmed = 1;
- open->op_stateowner->so_seqid--;
- }
- }
+ open->op_stateowner->so_confirmed = 1;
+ open->op_stateowner->so_client->cl_firststate = 1;
}
/*
struct nfs4_delegation *dp = NULL;
int status;
+ if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+ return nfserr_grace;
+
+ if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+ return nfserr_no_grace;
+
status = nfserr_inval;
if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny))
goto out;
status = nfs4_upgrade_open(rqstp, current_fh, stp, open);
if (status)
goto out;
+ update_stateid(&stp->st_stateid);
} else {
/* Stateid was not found, this is a new OPEN */
int flags = 0;
out:
if (fp)
put_nfs4_file(fp);
- /* CLAIM_PREVIOUS has different error returns */
- nfs4_set_claim_prev(open, &status);
+ if (status == 0 && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+ nfs4_set_claim_prev(open);
/*
* To finish the open response, we just need to set the rflags.
*/
}
renew_client(clp);
status = nfserr_cb_path_down;
- if (!list_empty(&clp->cl_del_perclnt)
+ if (!list_empty(&clp->cl_delegations)
&& !atomic_read(&clp->cl_callback.cb_set))
goto out;
status = nfs_ok;
return status;
}
-time_t
+static void
+end_grace(void)
+{
+ dprintk("NFSD: end of grace period\n");
+ nfsd4_recdir_purge_old();
+ in_grace = 0;
+}
+
+static time_t
nfs4_laundromat(void)
{
struct nfs4_client *clp;
nfs4_lock_state();
dprintk("NFSD: laundromat service - starting\n");
+ if (in_grace)
+ end_grace();
list_for_each_safe(pos, next, &client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
}
dprintk("NFSD: purging unused client (clientid %08x)\n",
clp->cl_clientid.cl_id);
+ nfsd4_remove_clid_dir(clp);
expire_client(clp);
}
INIT_LIST_HEAD(&reaplist);
queue_delayed_work(laundry_wq, &laundromat_work, t*HZ);
}
-/* search ownerid_hashtbl[] and close_lru for stateid owner
- * (stateid->si_stateownerid)
- */
-struct nfs4_stateowner *
-find_openstateowner_id(u32 st_id, int flags) {
+static struct nfs4_stateowner *
+search_close_lru(u32 st_id, int flags)
+{
struct nfs4_stateowner *local = NULL;
- dprintk("NFSD: find_openstateowner_id %d\n", st_id);
if (flags & CLOSE_STATE) {
list_for_each_entry(local, &close_lru, so_close_lru) {
if (local->so_id == st_id)
{
if (stateid->si_boot == boot_time)
return 0;
- printk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
+ dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
stateid->si_generation);
return 1;
return status;
}
+static inline int
+setlkflg (int type)
+{
+ return (type == NFS4_READW_LT || type == NFS4_READ_LT) ?
+ RD_STATE : WR_STATE;
+}
/*
* Checks for sequence id mutating operations.
*/
-int
-nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid)
+static int
+nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
{
- int status;
struct nfs4_stateid *stp;
struct nfs4_stateowner *sop;
"stateid = (%08x/%08x/%08x/%08x)\n", seqid,
stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
stateid->si_generation);
-
+
*stpp = NULL;
*sopp = NULL;
- status = nfserr_bad_stateid;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
printk("NFSD: preprocess_seqid_op: magic stateid!\n");
- goto out;
+ return nfserr_bad_stateid;
}
- status = nfserr_stale_stateid;
if (STALE_STATEID(stateid))
- goto out;
+ return nfserr_stale_stateid;
/*
* We return BAD_STATEID if filehandle doesn't match stateid,
* the confirmed flag is incorrecly set, or the generation
* number is incorrect.
- * If there is no entry in the openfile table for this id,
- * we can't always return BAD_STATEID;
- * this might be a retransmitted CLOSE which has arrived after
- * the openfile has been released.
*/
- if (!(stp = find_stateid(stateid, flags)))
- goto no_nfs4_stateid;
-
- status = nfserr_bad_stateid;
+ stp = find_stateid(stateid, flags);
+ if (stp == NULL) {
+ /*
+ * Also, we should make sure this isn't just the result of
+ * a replayed close:
+ */
+ sop = search_close_lru(stateid->si_stateownerid, flags);
+ if (sop == NULL)
+ return nfserr_bad_stateid;
+ *sopp = sop;
+ goto check_replay;
+ }
- /* for new lock stateowners:
- * check that the lock->v.new.open_stateid
- * refers to an open stateowner
- *
- * check that the lockclid (nfs4_lock->v.new.clientid) is the same
- * as the open_stateid->st_stateowner->so_client->clientid
- */
- if (lockclid) {
+ if (lock) {
struct nfs4_stateowner *sop = stp->st_stateowner;
+ clientid_t *lockclid = &lock->v.new.clientid;
struct nfs4_client *clp = sop->so_client;
+ int lkflg = 0;
+ int status;
+
+ lkflg = setlkflg(lock->lk_type);
+
+ if (lock->lk_is_new) {
+ if (!sop->so_is_open_owner)
+ return nfserr_bad_stateid;
+ if (!cmp_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 (!sop->so_is_open_owner)
- goto out;
- if (!cmp_clid(&clp->cl_clientid, lockclid))
- goto out;
}
if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) {
printk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n");
- goto out;
+ return nfserr_bad_stateid;
}
*stpp = stp;
* For the moment, we ignore the possibility of
* generation number wraparound.
*/
- if (seqid != sop->so_seqid + 1)
+ if (seqid != sop->so_seqid)
goto check_replay;
- if (sop->so_confirmed) {
- if (flags & CONFIRM) {
- printk("NFSD: preprocess_seqid_op: expected unconfirmed stateowner!\n");
- goto out;
- }
+ if (sop->so_confirmed && flags & CONFIRM) {
+ printk("NFSD: preprocess_seqid_op: expected"
+ " unconfirmed stateowner!\n");
+ return nfserr_bad_stateid;
}
- else {
- if (!(flags & CONFIRM)) {
- printk("NFSD: preprocess_seqid_op: stateowner not confirmed yet!\n");
- goto out;
- }
+ if (!sop->so_confirmed && !(flags & CONFIRM)) {
+ printk("NFSD: preprocess_seqid_op: stateowner not"
+ " confirmed yet!\n");
+ return nfserr_bad_stateid;
}
if (stateid->si_generation > stp->st_stateid.si_generation) {
printk("NFSD: preprocess_seqid_op: future stateid?!\n");
- goto out;
+ return nfserr_bad_stateid;
}
- status = nfserr_old_stateid;
if (stateid->si_generation < stp->st_stateid.si_generation) {
printk("NFSD: preprocess_seqid_op: old stateid!\n");
- goto out;
+ return nfserr_old_stateid;
}
- /* XXX renew the client lease here */
- status = nfs_ok;
-
-out:
- return status;
-
-no_nfs4_stateid:
-
- /*
- * We determine whether this is a bad stateid or a replay,
- * starting by trying to look up the stateowner.
- * If stateowner is not found - stateid is bad.
- */
- if (!(sop = find_openstateowner_id(stateid->si_stateownerid, flags))) {
- printk("NFSD: preprocess_seqid_op: no stateowner or nfs4_stateid!\n");
- status = nfserr_bad_stateid;
- goto out;
- }
- *sopp = sop;
+ renew_client(sop->so_client);
+ return nfs_ok;
check_replay:
- if (seqid == sop->so_seqid) {
- printk("NFSD: preprocess_seqid_op: retransmission?\n");
+ if (seqid == sop->so_seqid - 1) {
+ dprintk("NFSD: preprocess_seqid_op: retransmission?\n");
/* indicate replay to calling function */
- status = NFSERR_REPLAY_ME;
- } else {
- printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d\n", sop->so_seqid +1, seqid);
-
- *sopp = NULL;
- status = nfserr_bad_seqid;
+ return NFSERR_REPLAY_ME;
}
- goto out;
+ printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d)\n",
+ sop->so_seqid, seqid);
+ *sopp = NULL;
+ return nfserr_bad_seqid;
}
int
-nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_confirm *oc)
+nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_confirm *oc, struct nfs4_stateowner **replay_owner)
{
int status;
struct nfs4_stateowner *sop;
stp->st_stateid.si_stateownerid,
stp->st_stateid.si_fileid,
stp->st_stateid.si_generation);
+
+ nfsd4_create_clid_dir(sop->so_client);
out:
- if (oc->oc_stateowner)
+ if (oc->oc_stateowner) {
nfs4_get_stateowner(oc->oc_stateowner);
+ *replay_owner = oc->oc_stateowner;
+ }
nfs4_unlock_state();
return status;
}
}
int
-nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od)
+nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od, struct nfs4_stateowner **replay_owner)
{
int status;
struct nfs4_stateid *stp;
memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t));
status = nfs_ok;
out:
- if (od->od_stateowner)
+ if (od->od_stateowner) {
nfs4_get_stateowner(od->od_stateowner);
+ *replay_owner = od->od_stateowner;
+ }
nfs4_unlock_state();
return status;
}
* nfs4_unlock_state() called after encode
*/
int
-nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_close *close)
+nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_close *close, struct nfs4_stateowner **replay_owner)
{
int status;
struct nfs4_stateid *stp;
/* release_state_owner() calls nfsd_close() if needed */
release_state_owner(stp, OPEN_STATE);
out:
- if (close->cl_stateowner)
+ if (close->cl_stateowner) {
nfs4_get_stateowner(close->cl_stateowner);
+ *replay_owner = close->cl_stateowner;
+ }
nfs4_unlock_state();
return status;
}
static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE];
static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
-struct nfs4_stateid *
+static struct nfs4_stateid *
find_stateid(stateid_t *stid, int flags)
{
struct nfs4_stateid *local = NULL;
(local->st_stateid.si_fileid == f_id))
return local;
}
- } else
- printk("NFSD: find_stateid: ERROR: no state flag\n");
+ }
return NULL;
}
lock->fl_end = OFFSET_MAX;
}
-int
+static int
nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval)
{
struct nfs4_stateowner *local = NULL;
deny->ld_type = NFS4_WRITE_LT;
}
-static struct nfs4_stateowner *
-find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid)
-{
- struct nfs4_stateowner *local = NULL;
- int i;
-
- for (i = 0; i < LOCK_HASH_SIZE; i++) {
- list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) {
- if (!cmp_owner_str(local, owner, clid))
- continue;
- return local;
- }
- }
- return NULL;
-}
-
static struct nfs4_stateowner *
find_lockstateowner_str(struct inode *inode, clientid_t *clid,
struct xdr_netobj *owner)
* occured.
*
* strhashval = lock_ownerstr_hashval
- * so_seqid = lock->lk_new_lock_seqid - 1: it gets bumped in encode
*/
static struct nfs4_stateowner *
INIT_LIST_HEAD(&sop->so_idhash);
INIT_LIST_HEAD(&sop->so_strhash);
INIT_LIST_HEAD(&sop->so_perclient);
- INIT_LIST_HEAD(&sop->so_perfilestate);
- INIT_LIST_HEAD(&sop->so_perlockowner);
+ 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_perlockowner, &open_stp->st_perlockowner);
+ list_add(&sop->so_perstateid, &open_stp->st_lockowners);
sop->so_is_open_owner = 0;
sop->so_id = current_ownerid++;
sop->so_client = clp;
- sop->so_seqid = lock->lk_new_lock_seqid - 1;
+ /* 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;
return sop;
}
-struct nfs4_stateid *
+static struct nfs4_stateid *
alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struct nfs4_stateid *open_stp)
{
struct nfs4_stateid *stp;
goto out;
INIT_LIST_HEAD(&stp->st_hash);
INIT_LIST_HEAD(&stp->st_perfile);
- INIT_LIST_HEAD(&stp->st_perfilestate);
- INIT_LIST_HEAD(&stp->st_perlockowner); /* not used */
+ INIT_LIST_HEAD(&stp->st_perstateowner);
+ INIT_LIST_HEAD(&stp->st_lockowners); /* not used */
list_add(&stp->st_hash, &lockstateid_hashtbl[hashval]);
list_add(&stp->st_perfile, &fp->fi_stateids);
- list_add(&stp->st_perfilestate, &sop->so_perfilestate);
+ list_add(&stp->st_perstateowner, &sop->so_stateids);
stp->st_stateowner = sop;
get_nfs4_file(fp);
stp->st_file = fp;
stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */
stp->st_access_bmap = open_stp->st_access_bmap;
stp->st_deny_bmap = open_stp->st_deny_bmap;
+ stp->st_openstp = open_stp;
out:
return stp;
}
-int
+static int
check_lock_length(u64 offset, u64 length)
{
return ((length == 0) || ((length != ~(u64)0) &&
* LOCK operation
*/
int
-nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock)
+nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock *lock, struct nfs4_stateowner **replay_owner)
{
- struct nfs4_stateowner *lock_sop = NULL, *open_sop = NULL;
+ struct nfs4_stateowner *open_sop = NULL;
+ struct nfs4_stateowner *lock_sop = NULL;
struct nfs4_stateid *lock_stp;
struct file *filp;
struct file_lock file_lock;
(long long) lock->lk_offset,
(long long) lock->lk_length);
- if (nfs4_in_grace() && !lock->lk_reclaim)
- return nfserr_grace;
- if (!nfs4_in_grace() && lock->lk_reclaim)
- return nfserr_no_grace;
-
if (check_lock_length(lock->lk_offset, lock->lk_length))
return nfserr_inval;
nfs4_lock_state();
if (lock->lk_is_new) {
- /*
- * Client indicates that this is a new lockowner.
- * Use open owner and open stateid to create lock owner and lock
- * stateid.
- */
+ /*
+ * Client indicates that this is a new lockowner.
+ * Use open owner and open stateid to create lock owner and
+ * lock stateid.
+ */
struct nfs4_stateid *open_stp = NULL;
struct nfs4_file *fp;
status = nfserr_stale_clientid;
- if (STALE_CLIENTID(&lock->lk_new_clientid)) {
- printk("NFSD: nfsd4_lock: clientid is stale!\n");
- goto out;
- }
-
- /* is the new lock seqid presented by the client zero? */
- status = nfserr_bad_seqid;
- if (lock->v.new.lock_seqid != 0)
+ if (STALE_CLIENTID(&lock->lk_new_clientid))
goto out;
/* validate and update open stateid and open seqid */
lock->lk_new_open_seqid,
&lock->lk_new_open_stateid,
CHECK_FH | OPEN_STATE,
- &open_sop, &open_stp,
- &lock->v.new.clientid);
- if (status) {
- if (lock->lk_reclaim)
- status = nfserr_reclaim_bad;
+ &lock->lk_stateowner, &open_stp,
+ lock);
+ if (status)
goto out;
- }
+ open_sop = lock->lk_stateowner;
/* create lockowner and lock stateid */
fp = open_stp->st_file;
strhashval = lock_ownerstr_hashval(fp->fi_inode,
open_sop->so_client->cl_clientid.cl_id,
&lock->v.new.owner);
- /*
- * If we already have this lock owner, the client is in
- * error (or our bookeeping is wrong!)
- * for asking for a 'new lock'.
- */
- status = nfserr_bad_stateid;
- lock_sop = find_lockstateowner(&lock->v.new.owner,
- &lock->v.new.clientid);
- if (lock_sop)
- goto out;
+ /* 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;
- if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock)))
+ lock_sop = alloc_init_lock_stateowner(strhashval,
+ open_sop->so_client, open_stp, lock);
+ if (lock_sop == NULL)
goto out;
- if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner,
- fp, open_stp)) == NULL) {
- release_stateowner(lock->lk_stateowner);
- lock->lk_stateowner = NULL;
+ lock_stp = alloc_init_lock_stateid(lock_sop, fp, open_stp);
+ if (lock_stp == NULL) {
+ release_stateowner(lock_sop);
goto out;
}
- /* bump the open seqid used to create the lock */
- open_sop->so_seqid++;
} else {
/* lock (lock owner + lock stateid) already exists */
status = nfs4_preprocess_seqid_op(current_fh,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
CHECK_FH | LOCK_STATE,
- &lock->lk_stateowner, &lock_stp, NULL);
+ &lock->lk_stateowner, &lock_stp, lock);
if (status)
goto out;
+ lock_sop = lock->lk_stateowner;
}
/* lock->lk_stateowner and lock_stp have been created or found */
filp = lock_stp->st_vfs_file;
if ((status = fh_verify(rqstp, current_fh, S_IFREG, MAY_LOCK))) {
- printk("NFSD: nfsd4_lock: permission denied!\n");
+ dprintk("NFSD: nfsd4_lock: permission denied!\n");
goto out;
}
+ status = nfserr_grace;
+ if (nfs4_in_grace() && !lock->lk_reclaim)
+ goto out;
+ status = nfserr_no_grace;
+ if (!nfs4_in_grace() && lock->lk_reclaim)
+ goto out;
+
locks_init_lock(&file_lock);
switch (lock->lk_type) {
case NFS4_READ_LT:
status = nfserr_inval;
goto out;
}
- file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner;
+ file_lock.fl_owner = (fl_owner_t)lock_sop;
file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
out_destroy_new_stateid:
if (lock->lk_is_new) {
dprintk("NFSD: nfsd4_lock: destroy new stateid!\n");
- /*
- * An error encountered after instantiation of the new
- * stateid has forced us to destroy it.
- */
- if (!seqid_mutating_err(status))
- open_sop->so_seqid--;
-
+ /*
+ * An error encountered after instantiation of the new
+ * stateid has forced us to destroy it.
+ */
release_state_owner(lock_stp, LOCK_STATE);
}
out:
- if (lock->lk_stateowner)
+ if (lock->lk_stateowner) {
nfs4_get_stateowner(lock->lk_stateowner);
+ *replay_owner = lock->lk_stateowner;
+ }
nfs4_unlock_state();
return status;
}
nfs4_lock_state();
status = nfserr_stale_clientid;
- if (STALE_CLIENTID(&lockt->lt_clientid)) {
- printk("NFSD: nfsd4_lockt: clientid is stale!\n");
+ if (STALE_CLIENTID(&lockt->lt_clientid))
goto out;
- }
if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) {
- printk("NFSD: nfsd4_lockt: fh_verify() failed!\n");
+ dprintk("NFSD: nfsd4_lockt: fh_verify() failed!\n");
if (status == nfserr_symlink)
status = nfserr_inval;
goto out;
}
int
-nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_locku *locku)
+nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_locku *locku, struct nfs4_stateowner **replay_owner)
{
struct nfs4_stateid *stp;
struct file *filp = NULL;
if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
file_lock.fl_ops->fl_release_private(&file_lock);
if (status) {
- printk("NFSD: nfs4_locku: posix_lock_file failed!\n");
+ dprintk("NFSD: nfs4_locku: posix_lock_file failed!\n");
goto out_nfserr;
}
/*
memcpy(&locku->lu_stateid, &stp->st_stateid, sizeof(stateid_t));
out:
- if (locku->lu_stateowner)
+ if (locku->lu_stateowner) {
nfs4_get_stateowner(locku->lu_stateowner);
+ *replay_owner = locku->lu_stateowner;
+ }
nfs4_unlock_state();
return status;
nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner)
{
clientid_t *clid = &rlockowner->rl_clientid;
- struct nfs4_stateowner *local = NULL;
+ struct nfs4_stateowner *sop;
+ struct nfs4_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
+ struct list_head matches;
+ int i;
int status;
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
/* XXX check for lease expiration */
status = nfserr_stale_clientid;
- if (STALE_CLIENTID(clid)) {
- printk("NFSD: nfsd4_release_lockowner: clientid is stale!\n");
+ if (STALE_CLIENTID(clid))
return status;
- }
nfs4_lock_state();
- status = nfs_ok;
- local = find_lockstateowner(owner, clid);
- if (local) {
- struct nfs4_stateid *stp;
-
- /* check for any locks held by any stateid
- * associated with the (lock) stateowner */
- status = nfserr_locks_held;
- list_for_each_entry(stp, &local->so_perfilestate,
- st_perfilestate) {
- if (check_for_locks(stp->st_vfs_file, local))
- goto out;
+ status = nfserr_locks_held;
+ /* XXX: we're doing a linear search through all the lockowners.
+ * Yipes! For now we'll just hope clients aren't really using
+ * release_lockowner much, but eventually we have to fix these
+ * data structures. */
+ INIT_LIST_HEAD(&matches);
+ for (i = 0; i < LOCK_HASH_SIZE; i++) {
+ list_for_each_entry(sop, &lock_ownerid_hashtbl[i], so_idhash) {
+ if (!cmp_owner_str(sop, owner, clid))
+ continue;
+ list_for_each_entry(stp, &sop->so_stateids,
+ st_perstateowner) {
+ if (check_for_locks(stp->st_vfs_file, sop))
+ goto out;
+ /* Note: so_perclient unused for lockowners,
+ * so it's OK to fool with here. */
+ list_add(&sop->so_perclient, &matches);
+ }
}
- /* no locks held by (lock) stateowner */
- status = nfs_ok;
- release_stateowner(local);
+ }
+ /* Clients probably won't expect us to return with some (but not all)
+ * of the lockowner state released; so don't release any until all
+ * have been checked. */
+ status = nfs_ok;
+ while (!list_empty(&matches)) {
+ sop = list_entry(matches.next, struct nfs4_stateowner,
+ so_perclient);
+ /* unhash_stateowner deletes so_perclient only
+ * for openowners. */
+ list_del(&sop->so_perclient);
+ release_stateowner(sop);
}
out:
nfs4_unlock_state();
}
static inline struct nfs4_client_reclaim *
-alloc_reclaim(int namelen)
+alloc_reclaim(void)
{
- struct nfs4_client_reclaim *crp = NULL;
+ return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
+}
- crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
- if (!crp)
- return NULL;
- crp->cr_name.data = kmalloc(namelen, GFP_KERNEL);
- if (!crp->cr_name.data) {
- kfree(crp);
- return NULL;
- }
- return crp;
+int
+nfs4_has_reclaimed_state(const char *name)
+{
+ unsigned int strhashval = clientstr_hashval(name);
+ struct nfs4_client *clp;
+
+ clp = find_confirmed_client_by_str(name, strhashval);
+ return clp ? 1 : 0;
}
/*
* failure => all reset bets are off, nfserr_no_grace...
*/
-static int
-nfs4_client_to_reclaim(char *name, int namlen)
+int
+nfs4_client_to_reclaim(const char *name)
{
unsigned int strhashval;
struct nfs4_client_reclaim *crp = NULL;
- dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", namlen, name);
- crp = alloc_reclaim(namlen);
+ dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name);
+ crp = alloc_reclaim();
if (!crp)
return 0;
- strhashval = clientstr_hashval(name, namlen);
+ strhashval = clientstr_hashval(name);
INIT_LIST_HEAD(&crp->cr_strhash);
list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
- memcpy(crp->cr_name.data, name, namlen);
- crp->cr_name.len = namlen;
+ memcpy(crp->cr_recdir, name, HEXDIR_LEN);
reclaim_str_hashtbl_size++;
return 1;
}
crp = list_entry(reclaim_str_hashtbl[i].next,
struct nfs4_client_reclaim, cr_strhash);
list_del(&crp->cr_strhash);
- kfree(crp->cr_name.data);
kfree(crp);
reclaim_str_hashtbl_size--;
}
/*
* called from OPEN, CLAIM_PREVIOUS with a new clientid. */
-struct nfs4_client_reclaim *
+static struct nfs4_client_reclaim *
nfs4_find_reclaim_client(clientid_t *clid)
{
unsigned int strhashval;
if (clp == NULL)
return NULL;
- dprintk("NFSD: nfs4_find_reclaim_client for %.*s\n",
- clp->cl_name.len, clp->cl_name.data);
+ dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n",
+ clp->cl_name.len, clp->cl_name.data,
+ clp->cl_recdir);
/* find clp->cl_name in reclaim_str_hashtbl */
- strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len);
+ strhashval = clientstr_hashval(clp->cl_recdir);
list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
- if (cmp_name(&crp->cr_name, &clp->cl_name)) {
+ if (same_name(crp->cr_recdir, clp->cl_recdir)) {
return crp;
}
}
reclaim_str_hashtbl_size = 0;
}
+static void
+nfsd4_load_reboot_recovery_data(void)
+{
+ int status;
+
+ nfs4_lock_state();
+ nfsd4_init_recdir(user_recovery_dirname);
+ status = nfsd4_recdir_load();
+ nfs4_unlock_state();
+ if (status)
+ printk("NFSD: Failure reading reboot recovery data\n");
+}
+
/* initialization to perform when the nfsd service is started: */
static void
boot_time = get_seconds();
grace_time = max(user_lease_time, lease_time);
lease_time = user_lease_time;
+ in_grace = 1;
printk("NFSD: starting %ld-second grace period\n", grace_time);
- grace_end = boot_time + grace_time;
laundry_wq = create_singlethread_workqueue("nfsd4");
- queue_delayed_work(laundry_wq, &laundromat_work, NFSD_LEASE_TIME*HZ);
+ queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ);
}
int
status = nfsd4_init_slabs();
if (status)
return status;
+ nfsd4_load_reboot_recovery_data();
__nfs4_state_start();
nfs4_init = 1;
return 0;
int
nfs4_in_grace(void)
{
- return get_seconds() < grace_end;
-}
-
-void
-set_no_grace(void)
-{
- printk("NFSD: ERROR in reboot recovery. State reclaims will fail.\n");
- grace_end = get_seconds();
+ return in_grace;
}
time_t
cancel_delayed_work(&laundromat_work);
flush_workqueue(laundry_wq);
destroy_workqueue(laundry_wq);
+ nfsd4_shutdown_recdir();
nfs4_init = 0;
}
nfs4_unlock_state();
}
+static void
+nfs4_set_recdir(char *recdir)
+{
+ nfs4_lock_state();
+ strcpy(user_recovery_dirname, recdir);
+ nfs4_unlock_state();
+}
+
+/*
+ * Change the NFSv4 recovery directory to recdir.
+ */
+int
+nfs4_reset_recoverydir(char *recdir)
+{
+ int status;
+ struct nameidata nd;
+
+ status = path_lookup(recdir, LOOKUP_FOLLOW, &nd);
+ if (status)
+ return status;
+ status = -ENOTDIR;
+ if (S_ISDIR(nd.dentry->d_inode->i_mode)) {
+ nfs4_set_recdir(recdir);
+ status = 0;
+ }
+ path_release(&nd);
+ return status;
+}
+
/*
* Called when leasetime is changed.
*