(op->opnum == OP_SETCLIENTID) ||
(op->opnum == OP_SETCLIENTID_CONFIRM) ||
(op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) ||
+ (op->opnum == OP_RELEASE_LOCKOWNER) ||
(op->opnum == OP_SETATTR))) {
op->status = nfserr_nofilehandle;
goto encode_op;
case OP_WRITE:
op->status = nfsd4_write(rqstp, ¤t_fh, &op->u.write);
break;
+ case OP_RELEASE_LOCKOWNER:
+ op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner);
+ break;
default:
BUG_ON(op->status == nfs_ok);
break;
#include <linux/nfsd/cache.h>
#include <linux/mount.h>
#include <linux/workqueue.h>
+#include <linux/smp_lock.h>
#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
goto out;
}
+/*
+ * returns
+ * 1: locks held by lockowner
+ * 0: no locks held by lockowner
+ */
+static int
+check_for_locks(struct file *filp, struct nfs4_stateowner *lowner)
+{
+ struct file_lock **flpp;
+ struct inode *inode = filp->f_dentry->d_inode;
+ int status = 0;
+
+ lock_kernel();
+ for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) {
+ if ((*flpp)->fl_owner == (fl_owner_t)lowner)
+ status = 1;
+ goto out;
+ }
+out:
+ unlock_kernel();
+ return status;
+}
+
+int
+nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner)
+{
+ clientid_t *clid = &rlockowner->rl_clientid;
+ struct list_head *pos, *next;
+ struct nfs4_stateowner *local = NULL;
+ struct xdr_netobj *owner = &rlockowner->rl_owner;
+ int status, i;
+
+ dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
+ clid->cl_boot, clid->cl_id);
+
+ /* XXX check for lease expiration */
+
+ status = nfserr_stale_clientid;
+ if (STALE_CLIENTID(clid)) {
+ printk("NFSD: nfsd4_release_lockowner: clientid is stale!\n");
+ return status;
+ }
+
+ nfs4_lock_state();
+
+ /* find the lockowner */
+ status = nfs_ok;
+ for (i=0; i < LOCK_HASH_SIZE; i++) {
+ list_for_each_safe(pos, next, &lock_ownerstr_hashtbl[i]) {
+ local = list_entry(pos, struct nfs4_stateowner,
+ so_strhash);
+ if(cmp_owner_str(local, owner, clid))
+ break;
+ }
+ }
+ 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_safe(pos, next, &local->so_perfilestate) {
+ stp = list_entry(pos, struct nfs4_stateid,
+ st_perfilestate);
+ if(stp->st_vfs_set) {
+ if (check_for_locks(&stp->st_vfs_file, local))
+ goto out;
+ }
+ }
+ /* no locks held by (lock) stateowner */
+ status = nfs_ok;
+ release_stateowner(local);
+ }
+out:
+ nfs4_unlock_state();
+ return status;
+}
+
/*
* Start and stop routines
*/
DECODE_TAIL;
}
+static int
+nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
+{
+ DECODE_HEAD;
+
+ READ_BUF(12);
+ COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
+ READ32(rlockowner->rl_owner.len);
+ READ_BUF(rlockowner->rl_owner.len);
+ READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
+
+ DECODE_TAIL;
+}
+
static int
nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
{
case OP_WRITE:
op->status = nfsd4_decode_write(argp, &op->u.write);
break;
+ case OP_RELEASE_LOCKOWNER:
+ op->status = nfsd4_decode_release_lockowner(argp, &op->u.release_lockowner);
+ break;
default:
/*
* According to spec, anything greater than OP_WRITE
case OP_WRITE:
nfsd4_encode_write(resp, op->status, &op->u.write);
break;
+ case OP_RELEASE_LOCKOWNER:
+ break;
default:
break;
}
OP_SETCLIENTID_CONFIRM = 36,
OP_VERIFY = 37,
OP_WRITE = 38,
+ OP_RELEASE_LOCKOWNER = 39,
};
enum nfsstat4 {
#define nfserr_attrnotsupp __constant_htonl(NFSERR_ATTRNOTSUPP)
#define nfserr_bad_xdr __constant_htonl(NFSERR_BAD_XDR)
#define nfserr_openmode __constant_htonl(NFSERR_OPENMODE)
+#define nfserr_locks_held __constant_htonl(NFSERR_LOCKS_HELD)
/* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped.
u32 * offset;
};
+struct nfsd4_release_lockowner {
+ clientid_t rl_clientid;
+ struct xdr_netobj rl_owner;
+};
struct nfsd4_readlink {
struct svc_rqst *rl_rqstp; /* request */
struct svc_fh * rl_fhp; /* request */
struct nfsd4_setclientid_confirm setclientid_confirm;
struct nfsd4_verify verify;
struct nfsd4_write write;
+ struct nfsd4_release_lockowner release_lockowner;
} u;
struct nfs4_replay * replay;
};
struct nfsd4_lockt *lockt);
extern int nfsd4_locku(struct svc_rqst *rqstp, struct svc_fh *current_fh,
struct nfsd4_locku *locku);
+extern int
+nfsd4_release_lockowner(struct svc_rqst *rqstp,
+ struct nfsd4_release_lockowner *rlockowner);
#endif
/*