Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
10
fs/lockd/Makefile
Normal file
10
fs/lockd/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Makefile for the linux lock manager stuff
|
||||
#
|
||||
|
||||
obj-$(CONFIG_LOCKD) += lockd.o
|
||||
|
||||
lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
|
||||
svcproc.o svcsubs.o mon.o xdr.o
|
||||
lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
|
||||
lockd-objs := $(lockd-objs-y)
|
||||
224
fs/lockd/clntlock.c
Normal file
224
fs/lockd/clntlock.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* linux/fs/lockd/clntlock.c
|
||||
*
|
||||
* Lock handling for the client side NLM implementation
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/smp_lock.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_CLIENT
|
||||
|
||||
/*
|
||||
* Local function prototypes
|
||||
*/
|
||||
static int reclaimer(void *ptr);
|
||||
|
||||
/*
|
||||
* The following functions handle blocking and granting from the
|
||||
* client perspective.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the representation of a blocked client lock.
|
||||
*/
|
||||
struct nlm_wait {
|
||||
struct list_head b_list; /* linked list */
|
||||
wait_queue_head_t b_wait; /* where to wait on */
|
||||
struct nlm_host * b_host;
|
||||
struct file_lock * b_lock; /* local file lock */
|
||||
unsigned short b_reclaim; /* got to reclaim lock */
|
||||
__be32 b_status; /* grant callback status */
|
||||
};
|
||||
|
||||
static LIST_HEAD(nlm_blocked);
|
||||
|
||||
/*
|
||||
* Queue up a lock for blocking so that the GRANTED request can see it
|
||||
*/
|
||||
struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_wait *block;
|
||||
|
||||
block = kmalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (block != NULL) {
|
||||
block->b_host = host;
|
||||
block->b_lock = fl;
|
||||
init_waitqueue_head(&block->b_wait);
|
||||
block->b_status = nlm_lck_blocked;
|
||||
list_add(&block->b_list, &nlm_blocked);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
void nlmclnt_finish_block(struct nlm_wait *block)
|
||||
{
|
||||
if (block == NULL)
|
||||
return;
|
||||
list_del(&block->b_list);
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
/*
|
||||
* Block on a lock
|
||||
*/
|
||||
int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
|
||||
{
|
||||
long ret;
|
||||
|
||||
/* A borken server might ask us to block even if we didn't
|
||||
* request it. Just say no!
|
||||
*/
|
||||
if (block == NULL)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Go to sleep waiting for GRANT callback. Some servers seem
|
||||
* to lose callbacks, however, so we're going to poll from
|
||||
* time to time just to make sure.
|
||||
*
|
||||
* For now, the retry frequency is pretty high; normally
|
||||
* a 1 minute timeout would do. See the comment before
|
||||
* nlmclnt_lock for an explanation.
|
||||
*/
|
||||
ret = wait_event_interruptible_timeout(block->b_wait,
|
||||
block->b_status != nlm_lck_blocked,
|
||||
timeout);
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
req->a_res.status = block->b_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The server lockd has called us back to tell us the lock was granted
|
||||
*/
|
||||
__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
|
||||
{
|
||||
const struct file_lock *fl = &lock->fl;
|
||||
const struct nfs_fh *fh = &lock->fh;
|
||||
struct nlm_wait *block;
|
||||
__be32 res = nlm_lck_denied;
|
||||
|
||||
/*
|
||||
* Look up blocked request based on arguments.
|
||||
* Warning: must not use cookie to match it!
|
||||
*/
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
struct file_lock *fl_blocked = block->b_lock;
|
||||
|
||||
if (fl_blocked->fl_start != fl->fl_start)
|
||||
continue;
|
||||
if (fl_blocked->fl_end != fl->fl_end)
|
||||
continue;
|
||||
/*
|
||||
* Careful! The NLM server will return the 32-bit "pid" that
|
||||
* we put on the wire: in this case the lockowner "pid".
|
||||
*/
|
||||
if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
|
||||
continue;
|
||||
if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
|
||||
continue;
|
||||
if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
|
||||
continue;
|
||||
/* Alright, we found a lock. Set the return status
|
||||
* and wake up the caller
|
||||
*/
|
||||
block->b_status = nlm_granted;
|
||||
wake_up(&block->b_wait);
|
||||
res = nlm_granted;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following procedures deal with the recovery of locks after a
|
||||
* server crash.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Reclaim all locks on server host. We do this by spawning a separate
|
||||
* reclaimer thread.
|
||||
*/
|
||||
void
|
||||
nlmclnt_recovery(struct nlm_host *host)
|
||||
{
|
||||
if (!host->h_reclaiming++) {
|
||||
nlm_get_host(host);
|
||||
__module_get(THIS_MODULE);
|
||||
if (kernel_thread(reclaimer, host, CLONE_KERNEL) < 0)
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
reclaimer(void *ptr)
|
||||
{
|
||||
struct nlm_host *host = (struct nlm_host *) ptr;
|
||||
struct nlm_wait *block;
|
||||
struct file_lock *fl, *next;
|
||||
u32 nsmstate;
|
||||
|
||||
daemonize("%s-reclaim", host->h_name);
|
||||
allow_signal(SIGKILL);
|
||||
|
||||
down_write(&host->h_rwsem);
|
||||
|
||||
/* This one ensures that our parent doesn't terminate while the
|
||||
* reclaim is in progress */
|
||||
lock_kernel();
|
||||
lockd_up(0); /* note: this cannot fail as lockd is already running */
|
||||
|
||||
dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
|
||||
|
||||
restart:
|
||||
nsmstate = host->h_nsmstate;
|
||||
|
||||
/* Force a portmap getport - the peer's lockd will
|
||||
* most likely end up on a different port.
|
||||
*/
|
||||
host->h_nextrebind = jiffies;
|
||||
nlm_rebind_host(host);
|
||||
|
||||
/* First, reclaim all locks that have been granted. */
|
||||
list_splice_init(&host->h_granted, &host->h_reclaim);
|
||||
list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
|
||||
list_del_init(&fl->fl_u.nfs_fl.list);
|
||||
|
||||
/* Why are we leaking memory here? --okir */
|
||||
if (signalled())
|
||||
continue;
|
||||
if (nlmclnt_reclaim(host, fl) != 0)
|
||||
continue;
|
||||
list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted);
|
||||
if (host->h_nsmstate != nsmstate) {
|
||||
/* Argh! The server rebooted again! */
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
host->h_reclaiming = 0;
|
||||
up_write(&host->h_rwsem);
|
||||
dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
|
||||
|
||||
/* Now, wake up all processes that sleep on a blocked lock */
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
if (block->b_host == host) {
|
||||
block->b_status = nlm_lck_denied_grace_period;
|
||||
wake_up(&block->b_wait);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release host handle after use */
|
||||
nlm_release_host(host);
|
||||
lockd_down();
|
||||
unlock_kernel();
|
||||
module_put_and_exit(0);
|
||||
}
|
||||
797
fs/lockd/clntproc.c
Normal file
797
fs/lockd/clntproc.c
Normal file
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* linux/fs/lockd/clntproc.c
|
||||
*
|
||||
* RPC procedures for the client side NLM implementation
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_CLIENT
|
||||
#define NLMCLNT_GRACE_WAIT (5*HZ)
|
||||
#define NLMCLNT_POLL_TIMEOUT (30*HZ)
|
||||
#define NLMCLNT_MAX_RETRIES 3
|
||||
|
||||
static int nlmclnt_test(struct nlm_rqst *, struct file_lock *);
|
||||
static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
|
||||
static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
|
||||
static int nlm_stat_to_errno(__be32 stat);
|
||||
static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host);
|
||||
static int nlmclnt_cancel(struct nlm_host *, int , struct file_lock *);
|
||||
|
||||
static const struct rpc_call_ops nlmclnt_unlock_ops;
|
||||
static const struct rpc_call_ops nlmclnt_cancel_ops;
|
||||
|
||||
/*
|
||||
* Cookie counter for NLM requests
|
||||
*/
|
||||
static atomic_t nlm_cookie = ATOMIC_INIT(0x1234);
|
||||
|
||||
void nlmclnt_next_cookie(struct nlm_cookie *c)
|
||||
{
|
||||
u32 cookie = atomic_inc_return(&nlm_cookie);
|
||||
|
||||
memcpy(c->data, &cookie, 4);
|
||||
c->len=4;
|
||||
}
|
||||
|
||||
static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner)
|
||||
{
|
||||
atomic_inc(&lockowner->count);
|
||||
return lockowner;
|
||||
}
|
||||
|
||||
static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
|
||||
{
|
||||
if (!atomic_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
|
||||
return;
|
||||
list_del(&lockowner->list);
|
||||
spin_unlock(&lockowner->host->h_lock);
|
||||
nlm_release_host(lockowner->host);
|
||||
kfree(lockowner);
|
||||
}
|
||||
|
||||
static inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid)
|
||||
{
|
||||
struct nlm_lockowner *lockowner;
|
||||
list_for_each_entry(lockowner, &host->h_lockowners, list) {
|
||||
if (lockowner->pid == pid)
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t __nlm_alloc_pid(struct nlm_host *host)
|
||||
{
|
||||
uint32_t res;
|
||||
do {
|
||||
res = host->h_pidcount++;
|
||||
} while (nlm_pidbusy(host, res) < 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct nlm_lockowner *__nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
|
||||
{
|
||||
struct nlm_lockowner *lockowner;
|
||||
list_for_each_entry(lockowner, &host->h_lockowners, list) {
|
||||
if (lockowner->owner != owner)
|
||||
continue;
|
||||
return nlm_get_lockowner(lockowner);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nlm_lockowner *nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
|
||||
{
|
||||
struct nlm_lockowner *res, *new = NULL;
|
||||
|
||||
spin_lock(&host->h_lock);
|
||||
res = __nlm_find_lockowner(host, owner);
|
||||
if (res == NULL) {
|
||||
spin_unlock(&host->h_lock);
|
||||
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
||||
spin_lock(&host->h_lock);
|
||||
res = __nlm_find_lockowner(host, owner);
|
||||
if (res == NULL && new != NULL) {
|
||||
res = new;
|
||||
atomic_set(&new->count, 1);
|
||||
new->owner = owner;
|
||||
new->pid = __nlm_alloc_pid(host);
|
||||
new->host = nlm_get_host(host);
|
||||
list_add(&new->list, &host->h_lockowners);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
spin_unlock(&host->h_lock);
|
||||
kfree(new);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
|
||||
*/
|
||||
static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_args *argp = &req->a_args;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
nlmclnt_next_cookie(&argp->cookie);
|
||||
argp->state = nsm_local_state;
|
||||
memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh));
|
||||
lock->caller = utsname()->nodename;
|
||||
lock->oh.data = req->a_owner;
|
||||
lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s",
|
||||
(unsigned int)fl->fl_u.nfs_fl.owner->pid,
|
||||
utsname()->nodename);
|
||||
lock->svid = fl->fl_u.nfs_fl.owner->pid;
|
||||
lock->fl.fl_start = fl->fl_start;
|
||||
lock->fl.fl_end = fl->fl_end;
|
||||
lock->fl.fl_type = fl->fl_type;
|
||||
}
|
||||
|
||||
static void nlmclnt_release_lockargs(struct nlm_rqst *req)
|
||||
{
|
||||
BUG_ON(req->a_args.lock.fl.fl_ops != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the main entry point for the NLM client.
|
||||
*/
|
||||
int
|
||||
nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct rpc_clnt *client = NFS_CLIENT(inode);
|
||||
struct sockaddr_in addr;
|
||||
struct nfs_server *nfssrv = NFS_SERVER(inode);
|
||||
struct nlm_host *host;
|
||||
struct nlm_rqst *call;
|
||||
sigset_t oldset;
|
||||
unsigned long flags;
|
||||
int status, vers;
|
||||
|
||||
vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
|
||||
if (NFS_PROTO(inode)->version > 3) {
|
||||
printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
rpc_peeraddr(client, (struct sockaddr *) &addr, sizeof(addr));
|
||||
host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers,
|
||||
nfssrv->nfs_client->cl_hostname,
|
||||
strlen(nfssrv->nfs_client->cl_hostname));
|
||||
if (host == NULL)
|
||||
return -ENOLCK;
|
||||
|
||||
call = nlm_alloc_call(host);
|
||||
if (call == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
nlmclnt_locks_init_private(fl, host);
|
||||
/* Set up the argument struct */
|
||||
nlmclnt_setlockargs(call, fl);
|
||||
|
||||
/* Keep the old signal mask */
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
oldset = current->blocked;
|
||||
|
||||
/* If we're cleaning up locks because the process is exiting,
|
||||
* perform the RPC call asynchronously. */
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd))
|
||||
&& fl->fl_type == F_UNLCK
|
||||
&& (current->flags & PF_EXITING)) {
|
||||
sigfillset(¤t->blocked); /* Mask all signals */
|
||||
recalc_sigpending();
|
||||
|
||||
call->a_flags = RPC_TASK_ASYNC;
|
||||
}
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
|
||||
if (fl->fl_type != F_UNLCK) {
|
||||
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
|
||||
status = nlmclnt_lock(call, fl);
|
||||
} else
|
||||
status = nlmclnt_unlock(call, fl);
|
||||
} else if (IS_GETLK(cmd))
|
||||
status = nlmclnt_test(call, fl);
|
||||
else
|
||||
status = -EINVAL;
|
||||
|
||||
fl->fl_ops->fl_release_private(fl);
|
||||
fl->fl_ops = NULL;
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
current->blocked = oldset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
dprintk("lockd: clnt proc returns %d\n", status);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(nlmclnt_proc);
|
||||
|
||||
/*
|
||||
* Allocate an NLM RPC call struct
|
||||
*
|
||||
* Note: the caller must hold a reference to host. In case of failure,
|
||||
* this reference will be released.
|
||||
*/
|
||||
struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
|
||||
{
|
||||
struct nlm_rqst *call;
|
||||
|
||||
for(;;) {
|
||||
call = kzalloc(sizeof(*call), GFP_KERNEL);
|
||||
if (call != NULL) {
|
||||
locks_init_lock(&call->a_args.lock.fl);
|
||||
locks_init_lock(&call->a_res.lock.fl);
|
||||
call->a_host = host;
|
||||
return call;
|
||||
}
|
||||
if (signalled())
|
||||
break;
|
||||
printk("nlm_alloc_call: failed, waiting for memory\n");
|
||||
schedule_timeout_interruptible(5*HZ);
|
||||
}
|
||||
nlm_release_host(host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nlm_release_call(struct nlm_rqst *call)
|
||||
{
|
||||
nlm_release_host(call->a_host);
|
||||
nlmclnt_release_lockargs(call);
|
||||
kfree(call);
|
||||
}
|
||||
|
||||
static void nlmclnt_rpc_release(void *data)
|
||||
{
|
||||
return nlm_release_call(data);
|
||||
}
|
||||
|
||||
static int nlm_wait_on_grace(wait_queue_head_t *queue)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
int status = -EINTR;
|
||||
|
||||
prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE);
|
||||
if (!signalled ()) {
|
||||
schedule_timeout(NLMCLNT_GRACE_WAIT);
|
||||
try_to_freeze();
|
||||
if (!signalled ())
|
||||
status = 0;
|
||||
}
|
||||
finish_wait(queue, &wait);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic NLM call
|
||||
*/
|
||||
static int
|
||||
nlmclnt_call(struct nlm_rqst *req, u32 proc)
|
||||
{
|
||||
struct nlm_host *host = req->a_host;
|
||||
struct rpc_clnt *clnt;
|
||||
struct nlm_args *argp = &req->a_args;
|
||||
struct nlm_res *resp = &req->a_res;
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = argp,
|
||||
.rpc_resp = resp,
|
||||
};
|
||||
int status;
|
||||
|
||||
dprintk("lockd: call procedure %d on %s\n",
|
||||
(int)proc, host->h_name);
|
||||
|
||||
do {
|
||||
if (host->h_reclaiming && !argp->reclaim)
|
||||
goto in_grace_period;
|
||||
|
||||
/* If we have no RPC client yet, create one. */
|
||||
if ((clnt = nlm_bind_host(host)) == NULL)
|
||||
return -ENOLCK;
|
||||
msg.rpc_proc = &clnt->cl_procinfo[proc];
|
||||
|
||||
/* Perform the RPC call. If an error occurs, try again */
|
||||
if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
|
||||
dprintk("lockd: rpc_call returned error %d\n", -status);
|
||||
switch (status) {
|
||||
case -EPROTONOSUPPORT:
|
||||
status = -EINVAL;
|
||||
break;
|
||||
case -ECONNREFUSED:
|
||||
case -ETIMEDOUT:
|
||||
case -ENOTCONN:
|
||||
nlm_rebind_host(host);
|
||||
status = -EAGAIN;
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
return signalled () ? -EINTR : status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (resp->status == nlm_lck_denied_grace_period) {
|
||||
dprintk("lockd: server in grace period\n");
|
||||
if (argp->reclaim) {
|
||||
printk(KERN_WARNING
|
||||
"lockd: spurious grace period reject?!\n");
|
||||
return -ENOLCK;
|
||||
}
|
||||
} else {
|
||||
if (!argp->reclaim) {
|
||||
/* We appear to be out of the grace period */
|
||||
wake_up_all(&host->h_gracewait);
|
||||
}
|
||||
dprintk("lockd: server returns status %d\n", resp->status);
|
||||
return 0; /* Okay, call complete */
|
||||
}
|
||||
|
||||
in_grace_period:
|
||||
/*
|
||||
* The server has rebooted and appears to be in the grace
|
||||
* period during which locks are only allowed to be
|
||||
* reclaimed.
|
||||
* We can only back off and try again later.
|
||||
*/
|
||||
status = nlm_wait_on_grace(&host->h_gracewait);
|
||||
} while (status == 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic NLM call, async version.
|
||||
*/
|
||||
static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
|
||||
{
|
||||
struct nlm_host *host = req->a_host;
|
||||
struct rpc_clnt *clnt;
|
||||
|
||||
dprintk("lockd: call procedure %d on %s (async)\n",
|
||||
(int)proc, host->h_name);
|
||||
|
||||
/* If we have no RPC client yet, create one. */
|
||||
clnt = nlm_bind_host(host);
|
||||
if (clnt == NULL)
|
||||
goto out_err;
|
||||
msg->rpc_proc = &clnt->cl_procinfo[proc];
|
||||
|
||||
/* bootstrap and kick off the async RPC call */
|
||||
return rpc_call_async(clnt, msg, RPC_TASK_ASYNC, tk_ops, req);
|
||||
out_err:
|
||||
tk_ops->rpc_release(req);
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = &req->a_args,
|
||||
.rpc_resp = &req->a_res,
|
||||
};
|
||||
return __nlm_async_call(req, proc, &msg, tk_ops);
|
||||
}
|
||||
|
||||
int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = &req->a_res,
|
||||
};
|
||||
return __nlm_async_call(req, proc, &msg, tk_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST for the presence of a conflicting lock
|
||||
*/
|
||||
static int
|
||||
nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = nlmclnt_call(req, NLMPROC_TEST);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
|
||||
switch (req->a_res.status) {
|
||||
case nlm_granted:
|
||||
fl->fl_type = F_UNLCK;
|
||||
break;
|
||||
case nlm_lck_denied:
|
||||
/*
|
||||
* Report the conflicting lock back to the application.
|
||||
*/
|
||||
fl->fl_start = req->a_res.lock.fl.fl_start;
|
||||
fl->fl_end = req->a_res.lock.fl.fl_start;
|
||||
fl->fl_type = req->a_res.lock.fl.fl_type;
|
||||
fl->fl_pid = 0;
|
||||
break;
|
||||
default:
|
||||
status = nlm_stat_to_errno(req->a_res.status);
|
||||
}
|
||||
out:
|
||||
nlm_release_call(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
|
||||
{
|
||||
new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
|
||||
new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner);
|
||||
list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);
|
||||
}
|
||||
|
||||
static void nlmclnt_locks_release_private(struct file_lock *fl)
|
||||
{
|
||||
list_del(&fl->fl_u.nfs_fl.list);
|
||||
nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
|
||||
}
|
||||
|
||||
static struct file_lock_operations nlmclnt_lock_ops = {
|
||||
.fl_copy_lock = nlmclnt_locks_copy_lock,
|
||||
.fl_release_private = nlmclnt_locks_release_private,
|
||||
};
|
||||
|
||||
static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host)
|
||||
{
|
||||
BUG_ON(fl->fl_ops != NULL);
|
||||
fl->fl_u.nfs_fl.state = 0;
|
||||
fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner);
|
||||
INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);
|
||||
fl->fl_ops = &nlmclnt_lock_ops;
|
||||
}
|
||||
|
||||
static int do_vfs_lock(struct file_lock *fl)
|
||||
{
|
||||
int res = 0;
|
||||
switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
|
||||
case FL_POSIX:
|
||||
res = posix_lock_file_wait(fl->fl_file, fl);
|
||||
break;
|
||||
case FL_FLOCK:
|
||||
res = flock_lock_file_wait(fl->fl_file, fl);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* LOCK: Try to create a lock
|
||||
*
|
||||
* Programmer Harassment Alert
|
||||
*
|
||||
* When given a blocking lock request in a sync RPC call, the HPUX lockd
|
||||
* will faithfully return LCK_BLOCKED but never cares to notify us when
|
||||
* the lock could be granted. This way, our local process could hang
|
||||
* around forever waiting for the callback.
|
||||
*
|
||||
* Solution A: Implement busy-waiting
|
||||
* Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
|
||||
*
|
||||
* For now I am implementing solution A, because I hate the idea of
|
||||
* re-implementing lockd for a third time in two months. The async
|
||||
* calls shouldn't be too hard to do, however.
|
||||
*
|
||||
* This is one of the lovely things about standards in the NFS area:
|
||||
* they're so soft and squishy you can't really blame HP for doing this.
|
||||
*/
|
||||
static int
|
||||
nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_host *host = req->a_host;
|
||||
struct nlm_res *resp = &req->a_res;
|
||||
struct nlm_wait *block = NULL;
|
||||
unsigned char fl_flags = fl->fl_flags;
|
||||
int status = -ENOLCK;
|
||||
|
||||
if (nsm_monitor(host) < 0) {
|
||||
printk(KERN_NOTICE "lockd: failed to monitor %s\n",
|
||||
host->h_name);
|
||||
goto out;
|
||||
}
|
||||
fl->fl_flags |= FL_ACCESS;
|
||||
status = do_vfs_lock(fl);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
|
||||
block = nlmclnt_prepare_block(host, fl);
|
||||
again:
|
||||
for(;;) {
|
||||
/* Reboot protection */
|
||||
fl->fl_u.nfs_fl.state = host->h_state;
|
||||
status = nlmclnt_call(req, NLMPROC_LOCK);
|
||||
if (status < 0)
|
||||
goto out_unblock;
|
||||
if (!req->a_args.block)
|
||||
break;
|
||||
/* Did a reclaimer thread notify us of a server reboot? */
|
||||
if (resp->status == nlm_lck_denied_grace_period)
|
||||
continue;
|
||||
if (resp->status != nlm_lck_blocked)
|
||||
break;
|
||||
/* Wait on an NLM blocking lock */
|
||||
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
|
||||
/* if we were interrupted. Send a CANCEL request to the server
|
||||
* and exit
|
||||
*/
|
||||
if (status < 0)
|
||||
goto out_unblock;
|
||||
if (resp->status != nlm_lck_blocked)
|
||||
break;
|
||||
}
|
||||
|
||||
if (resp->status == nlm_granted) {
|
||||
down_read(&host->h_rwsem);
|
||||
/* Check whether or not the server has rebooted */
|
||||
if (fl->fl_u.nfs_fl.state != host->h_state) {
|
||||
up_read(&host->h_rwsem);
|
||||
goto again;
|
||||
}
|
||||
/* Ensure the resulting lock will get added to granted list */
|
||||
fl->fl_flags = fl_flags | FL_SLEEP;
|
||||
if (do_vfs_lock(fl) < 0)
|
||||
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
|
||||
up_read(&host->h_rwsem);
|
||||
}
|
||||
status = nlm_stat_to_errno(resp->status);
|
||||
out_unblock:
|
||||
nlmclnt_finish_block(block);
|
||||
/* Cancel the blocked request if it is still pending */
|
||||
if (resp->status == nlm_lck_blocked)
|
||||
nlmclnt_cancel(host, req->a_args.block, fl);
|
||||
out:
|
||||
nlm_release_call(req);
|
||||
fl->fl_flags = fl_flags;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* RECLAIM: Try to reclaim a lock
|
||||
*/
|
||||
int
|
||||
nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_rqst reqst, *req;
|
||||
int status;
|
||||
|
||||
req = &reqst;
|
||||
memset(req, 0, sizeof(*req));
|
||||
locks_init_lock(&req->a_args.lock.fl);
|
||||
locks_init_lock(&req->a_res.lock.fl);
|
||||
req->a_host = host;
|
||||
req->a_flags = 0;
|
||||
|
||||
/* Set up the argument struct */
|
||||
nlmclnt_setlockargs(req, fl);
|
||||
req->a_args.reclaim = 1;
|
||||
|
||||
if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
|
||||
&& req->a_res.status == nlm_granted)
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
|
||||
"(errno %d, status %d)\n", fl->fl_pid,
|
||||
status, ntohl(req->a_res.status));
|
||||
|
||||
/*
|
||||
* FIXME: This is a serious failure. We can
|
||||
*
|
||||
* a. Ignore the problem
|
||||
* b. Send the owning process some signal (Linux doesn't have
|
||||
* SIGLOST, though...)
|
||||
* c. Retry the operation
|
||||
*
|
||||
* Until someone comes up with a simple implementation
|
||||
* for b or c, I'll choose option a.
|
||||
*/
|
||||
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNLOCK: remove an existing lock
|
||||
*/
|
||||
static int
|
||||
nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_host *host = req->a_host;
|
||||
struct nlm_res *resp = &req->a_res;
|
||||
int status = 0;
|
||||
|
||||
/*
|
||||
* Note: the server is supposed to either grant us the unlock
|
||||
* request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
|
||||
* case, we want to unlock.
|
||||
*/
|
||||
fl->fl_flags |= FL_EXISTS;
|
||||
down_read(&host->h_rwsem);
|
||||
if (do_vfs_lock(fl) == -ENOENT) {
|
||||
up_read(&host->h_rwsem);
|
||||
goto out;
|
||||
}
|
||||
up_read(&host->h_rwsem);
|
||||
|
||||
if (req->a_flags & RPC_TASK_ASYNC)
|
||||
return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
|
||||
|
||||
status = nlmclnt_call(req, NLMPROC_UNLOCK);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
|
||||
if (resp->status == nlm_granted)
|
||||
goto out;
|
||||
|
||||
if (resp->status != nlm_lck_denied_nolocks)
|
||||
printk("lockd: unexpected unlock status: %d\n", resp->status);
|
||||
/* What to do now? I'm out of my depth... */
|
||||
status = -ENOLCK;
|
||||
out:
|
||||
nlm_release_call(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
|
||||
{
|
||||
struct nlm_rqst *req = data;
|
||||
u32 status = ntohl(req->a_res.status);
|
||||
|
||||
if (RPC_ASSASSINATED(task))
|
||||
goto die;
|
||||
|
||||
if (task->tk_status < 0) {
|
||||
dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
|
||||
goto retry_rebind;
|
||||
}
|
||||
if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
|
||||
rpc_delay(task, NLMCLNT_GRACE_WAIT);
|
||||
goto retry_unlock;
|
||||
}
|
||||
if (status != NLM_LCK_GRANTED)
|
||||
printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
|
||||
die:
|
||||
return;
|
||||
retry_rebind:
|
||||
nlm_rebind_host(req->a_host);
|
||||
retry_unlock:
|
||||
rpc_restart_call(task);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmclnt_unlock_ops = {
|
||||
.rpc_call_done = nlmclnt_unlock_callback,
|
||||
.rpc_release = nlmclnt_rpc_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Cancel a blocked lock request.
|
||||
* We always use an async RPC call for this in order not to hang a
|
||||
* process that has been Ctrl-C'ed.
|
||||
*/
|
||||
static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
|
||||
{
|
||||
struct nlm_rqst *req;
|
||||
unsigned long flags;
|
||||
sigset_t oldset;
|
||||
int status;
|
||||
|
||||
/* Block all signals while setting up call */
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
oldset = current->blocked;
|
||||
sigfillset(¤t->blocked);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
req = nlm_alloc_call(nlm_get_host(host));
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
req->a_flags = RPC_TASK_ASYNC;
|
||||
|
||||
nlmclnt_setlockargs(req, fl);
|
||||
req->a_args.block = block;
|
||||
|
||||
status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
current->blocked = oldset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
|
||||
{
|
||||
struct nlm_rqst *req = data;
|
||||
u32 status = ntohl(req->a_res.status);
|
||||
|
||||
if (RPC_ASSASSINATED(task))
|
||||
goto die;
|
||||
|
||||
if (task->tk_status < 0) {
|
||||
dprintk("lockd: CANCEL call error %d, retrying.\n",
|
||||
task->tk_status);
|
||||
goto retry_cancel;
|
||||
}
|
||||
|
||||
dprintk("lockd: cancel status %u (task %u)\n",
|
||||
status, task->tk_pid);
|
||||
|
||||
switch (status) {
|
||||
case NLM_LCK_GRANTED:
|
||||
case NLM_LCK_DENIED_GRACE_PERIOD:
|
||||
case NLM_LCK_DENIED:
|
||||
/* Everything's good */
|
||||
break;
|
||||
case NLM_LCK_DENIED_NOLOCKS:
|
||||
dprintk("lockd: CANCEL failed (server has no locks)\n");
|
||||
goto retry_cancel;
|
||||
default:
|
||||
printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
|
||||
status);
|
||||
}
|
||||
|
||||
die:
|
||||
return;
|
||||
|
||||
retry_cancel:
|
||||
/* Don't ever retry more than 3 times */
|
||||
if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
|
||||
goto die;
|
||||
nlm_rebind_host(req->a_host);
|
||||
rpc_restart_call(task);
|
||||
rpc_delay(task, 30 * HZ);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmclnt_cancel_ops = {
|
||||
.rpc_call_done = nlmclnt_cancel_callback,
|
||||
.rpc_release = nlmclnt_rpc_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an NLM status code to a generic kernel errno
|
||||
*/
|
||||
static int
|
||||
nlm_stat_to_errno(__be32 status)
|
||||
{
|
||||
switch(ntohl(status)) {
|
||||
case NLM_LCK_GRANTED:
|
||||
return 0;
|
||||
case NLM_LCK_DENIED:
|
||||
return -EAGAIN;
|
||||
case NLM_LCK_DENIED_NOLOCKS:
|
||||
case NLM_LCK_DENIED_GRACE_PERIOD:
|
||||
return -ENOLCK;
|
||||
case NLM_LCK_BLOCKED:
|
||||
printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
|
||||
return -ENOLCK;
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
case NLM_DEADLCK:
|
||||
return -EDEADLK;
|
||||
case NLM_ROFS:
|
||||
return -EROFS;
|
||||
case NLM_STALE_FH:
|
||||
return -ESTALE;
|
||||
case NLM_FBIG:
|
||||
return -EOVERFLOW;
|
||||
case NLM_FAILED:
|
||||
return -ENOLCK;
|
||||
#endif
|
||||
}
|
||||
printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
|
||||
return -ENOLCK;
|
||||
}
|
||||
522
fs/lockd/host.c
Normal file
522
fs/lockd/host.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* linux/fs/lockd/host.c
|
||||
*
|
||||
* Management for NLM peer hosts. The nlm_host struct is shared
|
||||
* between client and server implementation. The only reason to
|
||||
* do so is to reduce code bloat.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_HOSTCACHE
|
||||
#define NLM_HOST_MAX 64
|
||||
#define NLM_HOST_NRHASH 32
|
||||
#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1))
|
||||
#define NLM_HOST_REBIND (60 * HZ)
|
||||
#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
|
||||
#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ)
|
||||
|
||||
static struct hlist_head nlm_hosts[NLM_HOST_NRHASH];
|
||||
static unsigned long next_gc;
|
||||
static int nrhosts;
|
||||
static DEFINE_MUTEX(nlm_host_mutex);
|
||||
|
||||
|
||||
static void nlm_gc_hosts(void);
|
||||
static struct nsm_handle * __nsm_find(const struct sockaddr_in *,
|
||||
const char *, int, int);
|
||||
static struct nsm_handle * nsm_find(const struct sockaddr_in *sin,
|
||||
const char *hostname,
|
||||
int hostname_len);
|
||||
|
||||
/*
|
||||
* Common host lookup routine for server & client
|
||||
*/
|
||||
static struct nlm_host *
|
||||
nlm_lookup_host(int server, const struct sockaddr_in *sin,
|
||||
int proto, int version,
|
||||
const char *hostname,
|
||||
int hostname_len)
|
||||
{
|
||||
struct hlist_head *chain;
|
||||
struct hlist_node *pos;
|
||||
struct nlm_host *host;
|
||||
struct nsm_handle *nsm = NULL;
|
||||
int hash;
|
||||
|
||||
dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n",
|
||||
NIPQUAD(sin->sin_addr.s_addr), proto, version,
|
||||
server? "server" : "client",
|
||||
hostname_len,
|
||||
hostname? hostname : "<none>");
|
||||
|
||||
|
||||
hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
|
||||
|
||||
/* Lock hash table */
|
||||
mutex_lock(&nlm_host_mutex);
|
||||
|
||||
if (time_after_eq(jiffies, next_gc))
|
||||
nlm_gc_hosts();
|
||||
|
||||
/* We may keep several nlm_host objects for a peer, because each
|
||||
* nlm_host is identified by
|
||||
* (address, protocol, version, server/client)
|
||||
* We could probably simplify this a little by putting all those
|
||||
* different NLM rpc_clients into one single nlm_host object.
|
||||
* This would allow us to have one nlm_host per address.
|
||||
*/
|
||||
chain = &nlm_hosts[hash];
|
||||
hlist_for_each_entry(host, pos, chain, h_hash) {
|
||||
if (!nlm_cmp_addr(&host->h_addr, sin))
|
||||
continue;
|
||||
|
||||
/* See if we have an NSM handle for this client */
|
||||
if (!nsm)
|
||||
nsm = host->h_nsmhandle;
|
||||
|
||||
if (host->h_proto != proto)
|
||||
continue;
|
||||
if (host->h_version != version)
|
||||
continue;
|
||||
if (host->h_server != server)
|
||||
continue;
|
||||
|
||||
/* Move to head of hash chain. */
|
||||
hlist_del(&host->h_hash);
|
||||
hlist_add_head(&host->h_hash, chain);
|
||||
|
||||
nlm_get_host(host);
|
||||
goto out;
|
||||
}
|
||||
if (nsm)
|
||||
atomic_inc(&nsm->sm_count);
|
||||
|
||||
host = NULL;
|
||||
|
||||
/* Sadly, the host isn't in our hash table yet. See if
|
||||
* we have an NSM handle for it. If not, create one.
|
||||
*/
|
||||
if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len)))
|
||||
goto out;
|
||||
|
||||
host = kzalloc(sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
nsm_release(nsm);
|
||||
goto out;
|
||||
}
|
||||
host->h_name = nsm->sm_name;
|
||||
host->h_addr = *sin;
|
||||
host->h_addr.sin_port = 0; /* ouch! */
|
||||
host->h_version = version;
|
||||
host->h_proto = proto;
|
||||
host->h_rpcclnt = NULL;
|
||||
mutex_init(&host->h_mutex);
|
||||
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
|
||||
host->h_expires = jiffies + NLM_HOST_EXPIRE;
|
||||
atomic_set(&host->h_count, 1);
|
||||
init_waitqueue_head(&host->h_gracewait);
|
||||
init_rwsem(&host->h_rwsem);
|
||||
host->h_state = 0; /* pseudo NSM state */
|
||||
host->h_nsmstate = 0; /* real NSM state */
|
||||
host->h_nsmhandle = nsm;
|
||||
host->h_server = server;
|
||||
hlist_add_head(&host->h_hash, chain);
|
||||
INIT_LIST_HEAD(&host->h_lockowners);
|
||||
spin_lock_init(&host->h_lock);
|
||||
INIT_LIST_HEAD(&host->h_granted);
|
||||
INIT_LIST_HEAD(&host->h_reclaim);
|
||||
|
||||
if (++nrhosts > NLM_HOST_MAX)
|
||||
next_gc = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&nlm_host_mutex);
|
||||
return host;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a host
|
||||
*/
|
||||
static void
|
||||
nlm_destroy_host(struct nlm_host *host)
|
||||
{
|
||||
struct rpc_clnt *clnt;
|
||||
|
||||
BUG_ON(!list_empty(&host->h_lockowners));
|
||||
BUG_ON(atomic_read(&host->h_count));
|
||||
|
||||
/*
|
||||
* Release NSM handle and unmonitor host.
|
||||
*/
|
||||
nsm_unmonitor(host);
|
||||
|
||||
if ((clnt = host->h_rpcclnt) != NULL) {
|
||||
if (atomic_read(&clnt->cl_users)) {
|
||||
printk(KERN_WARNING
|
||||
"lockd: active RPC handle\n");
|
||||
clnt->cl_dead = 1;
|
||||
} else {
|
||||
rpc_destroy_client(host->h_rpcclnt);
|
||||
}
|
||||
}
|
||||
kfree(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an NLM server handle in the cache. If there is none, create it.
|
||||
*/
|
||||
struct nlm_host *
|
||||
nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version,
|
||||
const char *hostname, int hostname_len)
|
||||
{
|
||||
return nlm_lookup_host(0, sin, proto, version,
|
||||
hostname, hostname_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an NLM client handle in the cache. If there is none, create it.
|
||||
*/
|
||||
struct nlm_host *
|
||||
nlmsvc_lookup_host(struct svc_rqst *rqstp,
|
||||
const char *hostname, int hostname_len)
|
||||
{
|
||||
return nlm_lookup_host(1, svc_addr_in(rqstp),
|
||||
rqstp->rq_prot, rqstp->rq_vers,
|
||||
hostname, hostname_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the NLM RPC client for an NLM peer
|
||||
*/
|
||||
struct rpc_clnt *
|
||||
nlm_bind_host(struct nlm_host *host)
|
||||
{
|
||||
struct rpc_clnt *clnt;
|
||||
|
||||
dprintk("lockd: nlm_bind_host(%08x)\n",
|
||||
(unsigned)ntohl(host->h_addr.sin_addr.s_addr));
|
||||
|
||||
/* Lock host handle */
|
||||
mutex_lock(&host->h_mutex);
|
||||
|
||||
/* If we've already created an RPC client, check whether
|
||||
* RPC rebind is required
|
||||
*/
|
||||
if ((clnt = host->h_rpcclnt) != NULL) {
|
||||
if (time_after_eq(jiffies, host->h_nextrebind)) {
|
||||
rpc_force_rebind(clnt);
|
||||
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
|
||||
dprintk("lockd: next rebind in %ld jiffies\n",
|
||||
host->h_nextrebind - jiffies);
|
||||
}
|
||||
} else {
|
||||
unsigned long increment = nlmsvc_timeout * HZ;
|
||||
struct rpc_timeout timeparms = {
|
||||
.to_initval = increment,
|
||||
.to_increment = increment,
|
||||
.to_maxval = increment * 6UL,
|
||||
.to_retries = 5U,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = host->h_proto,
|
||||
.address = (struct sockaddr *)&host->h_addr,
|
||||
.addrsize = sizeof(host->h_addr),
|
||||
.timeout = &timeparms,
|
||||
.servername = host->h_name,
|
||||
.program = &nlm_program,
|
||||
.version = host->h_version,
|
||||
.authflavor = RPC_AUTH_UNIX,
|
||||
.flags = (RPC_CLNT_CREATE_HARDRTRY |
|
||||
RPC_CLNT_CREATE_AUTOBIND),
|
||||
};
|
||||
|
||||
clnt = rpc_create(&args);
|
||||
if (!IS_ERR(clnt))
|
||||
host->h_rpcclnt = clnt;
|
||||
else {
|
||||
printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
|
||||
clnt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host->h_mutex);
|
||||
return clnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force a portmap lookup of the remote lockd port
|
||||
*/
|
||||
void
|
||||
nlm_rebind_host(struct nlm_host *host)
|
||||
{
|
||||
dprintk("lockd: rebind host %s\n", host->h_name);
|
||||
if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
|
||||
rpc_force_rebind(host->h_rpcclnt);
|
||||
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment NLM host count
|
||||
*/
|
||||
struct nlm_host * nlm_get_host(struct nlm_host *host)
|
||||
{
|
||||
if (host) {
|
||||
dprintk("lockd: get host %s\n", host->h_name);
|
||||
atomic_inc(&host->h_count);
|
||||
host->h_expires = jiffies + NLM_HOST_EXPIRE;
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release NLM host after use
|
||||
*/
|
||||
void nlm_release_host(struct nlm_host *host)
|
||||
{
|
||||
if (host != NULL) {
|
||||
dprintk("lockd: release host %s\n", host->h_name);
|
||||
BUG_ON(atomic_read(&host->h_count) < 0);
|
||||
if (atomic_dec_and_test(&host->h_count)) {
|
||||
BUG_ON(!list_empty(&host->h_lockowners));
|
||||
BUG_ON(!list_empty(&host->h_granted));
|
||||
BUG_ON(!list_empty(&host->h_reclaim));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We were notified that the host indicated by address &sin
|
||||
* has rebooted.
|
||||
* Release all resources held by that peer.
|
||||
*/
|
||||
void nlm_host_rebooted(const struct sockaddr_in *sin,
|
||||
const char *hostname, int hostname_len,
|
||||
u32 new_state)
|
||||
{
|
||||
struct hlist_head *chain;
|
||||
struct hlist_node *pos;
|
||||
struct nsm_handle *nsm;
|
||||
struct nlm_host *host;
|
||||
|
||||
dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n",
|
||||
hostname, NIPQUAD(sin->sin_addr));
|
||||
|
||||
/* Find the NSM handle for this peer */
|
||||
if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0)))
|
||||
return;
|
||||
|
||||
/* When reclaiming locks on this peer, make sure that
|
||||
* we set up a new notification */
|
||||
nsm->sm_monitored = 0;
|
||||
|
||||
/* Mark all hosts tied to this NSM state as having rebooted.
|
||||
* We run the loop repeatedly, because we drop the host table
|
||||
* lock for this.
|
||||
* To avoid processing a host several times, we match the nsmstate.
|
||||
*/
|
||||
again: mutex_lock(&nlm_host_mutex);
|
||||
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
|
||||
hlist_for_each_entry(host, pos, chain, h_hash) {
|
||||
if (host->h_nsmhandle == nsm
|
||||
&& host->h_nsmstate != new_state) {
|
||||
host->h_nsmstate = new_state;
|
||||
host->h_state++;
|
||||
|
||||
nlm_get_host(host);
|
||||
mutex_unlock(&nlm_host_mutex);
|
||||
|
||||
if (host->h_server) {
|
||||
/* We're server for this guy, just ditch
|
||||
* all the locks he held. */
|
||||
nlmsvc_free_host_resources(host);
|
||||
} else {
|
||||
/* He's the server, initiate lock recovery. */
|
||||
nlmclnt_recovery(host);
|
||||
}
|
||||
|
||||
nlm_release_host(host);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&nlm_host_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down the hosts module.
|
||||
* Note that this routine is called only at server shutdown time.
|
||||
*/
|
||||
void
|
||||
nlm_shutdown_hosts(void)
|
||||
{
|
||||
struct hlist_head *chain;
|
||||
struct hlist_node *pos;
|
||||
struct nlm_host *host;
|
||||
|
||||
dprintk("lockd: shutting down host module\n");
|
||||
mutex_lock(&nlm_host_mutex);
|
||||
|
||||
/* First, make all hosts eligible for gc */
|
||||
dprintk("lockd: nuking all hosts...\n");
|
||||
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
|
||||
hlist_for_each_entry(host, pos, chain, h_hash)
|
||||
host->h_expires = jiffies - 1;
|
||||
}
|
||||
|
||||
/* Then, perform a garbage collection pass */
|
||||
nlm_gc_hosts();
|
||||
mutex_unlock(&nlm_host_mutex);
|
||||
|
||||
/* complain if any hosts are left */
|
||||
if (nrhosts) {
|
||||
printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
|
||||
dprintk("lockd: %d hosts left:\n", nrhosts);
|
||||
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
|
||||
hlist_for_each_entry(host, pos, chain, h_hash) {
|
||||
dprintk(" %s (cnt %d use %d exp %ld)\n",
|
||||
host->h_name, atomic_read(&host->h_count),
|
||||
host->h_inuse, host->h_expires);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect any unused NLM hosts.
|
||||
* This GC combines reference counting for async operations with
|
||||
* mark & sweep for resources held by remote clients.
|
||||
*/
|
||||
static void
|
||||
nlm_gc_hosts(void)
|
||||
{
|
||||
struct hlist_head *chain;
|
||||
struct hlist_node *pos, *next;
|
||||
struct nlm_host *host;
|
||||
|
||||
dprintk("lockd: host garbage collection\n");
|
||||
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
|
||||
hlist_for_each_entry(host, pos, chain, h_hash)
|
||||
host->h_inuse = 0;
|
||||
}
|
||||
|
||||
/* Mark all hosts that hold locks, blocks or shares */
|
||||
nlmsvc_mark_resources();
|
||||
|
||||
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
|
||||
hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
|
||||
if (atomic_read(&host->h_count) || host->h_inuse
|
||||
|| time_before(jiffies, host->h_expires)) {
|
||||
dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
|
||||
host->h_name, atomic_read(&host->h_count),
|
||||
host->h_inuse, host->h_expires);
|
||||
continue;
|
||||
}
|
||||
dprintk("lockd: delete host %s\n", host->h_name);
|
||||
hlist_del_init(&host->h_hash);
|
||||
|
||||
nlm_destroy_host(host);
|
||||
nrhosts--;
|
||||
}
|
||||
}
|
||||
|
||||
next_gc = jiffies + NLM_HOST_COLLECT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Manage NSM handles
|
||||
*/
|
||||
static LIST_HEAD(nsm_handles);
|
||||
static DEFINE_MUTEX(nsm_mutex);
|
||||
|
||||
static struct nsm_handle *
|
||||
__nsm_find(const struct sockaddr_in *sin,
|
||||
const char *hostname, int hostname_len,
|
||||
int create)
|
||||
{
|
||||
struct nsm_handle *nsm = NULL;
|
||||
struct list_head *pos;
|
||||
|
||||
if (!sin)
|
||||
return NULL;
|
||||
|
||||
if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
|
||||
if (printk_ratelimit()) {
|
||||
printk(KERN_WARNING "Invalid hostname \"%.*s\" "
|
||||
"in NFS lock request\n",
|
||||
hostname_len, hostname);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&nsm_mutex);
|
||||
list_for_each(pos, &nsm_handles) {
|
||||
nsm = list_entry(pos, struct nsm_handle, sm_link);
|
||||
|
||||
if (hostname && nsm_use_hostnames) {
|
||||
if (strlen(nsm->sm_name) != hostname_len
|
||||
|| memcmp(nsm->sm_name, hostname, hostname_len))
|
||||
continue;
|
||||
} else if (!nlm_cmp_addr(&nsm->sm_addr, sin))
|
||||
continue;
|
||||
atomic_inc(&nsm->sm_count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!create) {
|
||||
nsm = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
|
||||
if (nsm != NULL) {
|
||||
nsm->sm_addr = *sin;
|
||||
nsm->sm_name = (char *) (nsm + 1);
|
||||
memcpy(nsm->sm_name, hostname, hostname_len);
|
||||
nsm->sm_name[hostname_len] = '\0';
|
||||
atomic_set(&nsm->sm_count, 1);
|
||||
|
||||
list_add(&nsm->sm_link, &nsm_handles);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&nsm_mutex);
|
||||
return nsm;
|
||||
}
|
||||
|
||||
static struct nsm_handle *
|
||||
nsm_find(const struct sockaddr_in *sin, const char *hostname, int hostname_len)
|
||||
{
|
||||
return __nsm_find(sin, hostname, hostname_len, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release an NSM handle
|
||||
*/
|
||||
void
|
||||
nsm_release(struct nsm_handle *nsm)
|
||||
{
|
||||
if (!nsm)
|
||||
return;
|
||||
if (atomic_dec_and_test(&nsm->sm_count)) {
|
||||
mutex_lock(&nsm_mutex);
|
||||
if (atomic_read(&nsm->sm_count) == 0) {
|
||||
list_del(&nsm->sm_link);
|
||||
kfree(nsm);
|
||||
}
|
||||
mutex_unlock(&nsm_mutex);
|
||||
}
|
||||
}
|
||||
269
fs/lockd/mon.c
Normal file
269
fs/lockd/mon.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* linux/fs/lockd/mon.c
|
||||
*
|
||||
* The kernel statd client.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_MONITOR
|
||||
|
||||
static struct rpc_clnt * nsm_create(void);
|
||||
|
||||
static struct rpc_program nsm_program;
|
||||
|
||||
/*
|
||||
* Local NSM state
|
||||
*/
|
||||
int nsm_local_state;
|
||||
|
||||
/*
|
||||
* Common procedure for SM_MON/SM_UNMON calls
|
||||
*/
|
||||
static int
|
||||
nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
|
||||
{
|
||||
struct rpc_clnt *clnt;
|
||||
int status;
|
||||
struct nsm_args args;
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = &args,
|
||||
.rpc_resp = res,
|
||||
};
|
||||
|
||||
clnt = nsm_create();
|
||||
if (IS_ERR(clnt)) {
|
||||
status = PTR_ERR(clnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.mon_name = nsm->sm_name;
|
||||
args.addr = nsm->sm_addr.sin_addr.s_addr;
|
||||
args.prog = NLM_PROGRAM;
|
||||
args.vers = 3;
|
||||
args.proc = NLMPROC_NSM_NOTIFY;
|
||||
memset(res, 0, sizeof(*res));
|
||||
|
||||
msg.rpc_proc = &clnt->cl_procinfo[proc];
|
||||
status = rpc_call_sync(clnt, &msg, 0);
|
||||
if (status < 0)
|
||||
printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
|
||||
status);
|
||||
else
|
||||
status = 0;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up monitoring of a remote host
|
||||
*/
|
||||
int
|
||||
nsm_monitor(struct nlm_host *host)
|
||||
{
|
||||
struct nsm_handle *nsm = host->h_nsmhandle;
|
||||
struct nsm_res res;
|
||||
int status;
|
||||
|
||||
dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
|
||||
BUG_ON(nsm == NULL);
|
||||
|
||||
if (nsm->sm_monitored)
|
||||
return 0;
|
||||
|
||||
status = nsm_mon_unmon(nsm, SM_MON, &res);
|
||||
|
||||
if (status < 0 || res.status != 0)
|
||||
printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
|
||||
else
|
||||
nsm->sm_monitored = 1;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cease to monitor remote host
|
||||
*/
|
||||
int
|
||||
nsm_unmonitor(struct nlm_host *host)
|
||||
{
|
||||
struct nsm_handle *nsm = host->h_nsmhandle;
|
||||
struct nsm_res res;
|
||||
int status = 0;
|
||||
|
||||
if (nsm == NULL)
|
||||
return 0;
|
||||
host->h_nsmhandle = NULL;
|
||||
|
||||
if (atomic_read(&nsm->sm_count) == 1
|
||||
&& nsm->sm_monitored && !nsm->sm_sticky) {
|
||||
dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
|
||||
|
||||
status = nsm_mon_unmon(nsm, SM_UNMON, &res);
|
||||
if (status < 0)
|
||||
printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
|
||||
host->h_name);
|
||||
else
|
||||
nsm->sm_monitored = 0;
|
||||
}
|
||||
nsm_release(nsm);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create NSM client for the local host
|
||||
*/
|
||||
static struct rpc_clnt *
|
||||
nsm_create(void)
|
||||
{
|
||||
struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
|
||||
.sin_port = 0,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = IPPROTO_UDP,
|
||||
.address = (struct sockaddr *)&sin,
|
||||
.addrsize = sizeof(sin),
|
||||
.servername = "localhost",
|
||||
.program = &nsm_program,
|
||||
.version = SM_VERSION,
|
||||
.authflavor = RPC_AUTH_NULL,
|
||||
.flags = (RPC_CLNT_CREATE_ONESHOT),
|
||||
};
|
||||
|
||||
return rpc_create(&args);
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR functions for NSM.
|
||||
*/
|
||||
|
||||
static __be32 *
|
||||
xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
|
||||
{
|
||||
char buffer[20], *name;
|
||||
|
||||
/*
|
||||
* Use the dotted-quad IP address of the remote host as
|
||||
* identifier. Linux statd always looks up the canonical
|
||||
* hostname first for whatever remote hostname it receives,
|
||||
* so this works alright.
|
||||
*/
|
||||
if (nsm_use_hostnames) {
|
||||
name = argp->mon_name;
|
||||
} else {
|
||||
sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr));
|
||||
name = buffer;
|
||||
}
|
||||
if (!(p = xdr_encode_string(p, name))
|
||||
|| !(p = xdr_encode_string(p, utsname()->nodename)))
|
||||
return ERR_PTR(-EIO);
|
||||
*p++ = htonl(argp->prog);
|
||||
*p++ = htonl(argp->vers);
|
||||
*p++ = htonl(argp->proc);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
|
||||
{
|
||||
p = xdr_encode_common(rqstp, p, argp);
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
|
||||
/* Surprise - there may even be room for an IPv6 address now */
|
||||
*p++ = argp->addr;
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
|
||||
{
|
||||
p = xdr_encode_common(rqstp, p, argp);
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
|
||||
{
|
||||
resp->status = ntohl(*p++);
|
||||
resp->state = ntohl(*p++);
|
||||
dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
|
||||
resp->status, resp->state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
|
||||
{
|
||||
resp->state = ntohl(*p++);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
|
||||
#define SM_my_id_sz (3+1+SM_my_name_sz)
|
||||
#define SM_mon_id_sz (1+XDR_QUADLEN(20)+SM_my_id_sz)
|
||||
#define SM_mon_sz (SM_mon_id_sz+4)
|
||||
#define SM_monres_sz 2
|
||||
#define SM_unmonres_sz 1
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(a, b) (((a) > (b))? (a) : (b))
|
||||
#endif
|
||||
|
||||
static struct rpc_procinfo nsm_procedures[] = {
|
||||
[SM_MON] = {
|
||||
.p_proc = SM_MON,
|
||||
.p_encode = (kxdrproc_t) xdr_encode_mon,
|
||||
.p_decode = (kxdrproc_t) xdr_decode_stat_res,
|
||||
.p_bufsiz = MAX(SM_mon_sz, SM_monres_sz) << 2,
|
||||
.p_statidx = SM_MON,
|
||||
.p_name = "MONITOR",
|
||||
},
|
||||
[SM_UNMON] = {
|
||||
.p_proc = SM_UNMON,
|
||||
.p_encode = (kxdrproc_t) xdr_encode_unmon,
|
||||
.p_decode = (kxdrproc_t) xdr_decode_stat,
|
||||
.p_bufsiz = MAX(SM_mon_id_sz, SM_unmonres_sz) << 2,
|
||||
.p_statidx = SM_UNMON,
|
||||
.p_name = "UNMONITOR",
|
||||
},
|
||||
};
|
||||
|
||||
static struct rpc_version nsm_version1 = {
|
||||
.number = 1,
|
||||
.nrprocs = ARRAY_SIZE(nsm_procedures),
|
||||
.procs = nsm_procedures
|
||||
};
|
||||
|
||||
static struct rpc_version * nsm_version[] = {
|
||||
[1] = &nsm_version1,
|
||||
};
|
||||
|
||||
static struct rpc_stat nsm_stats;
|
||||
|
||||
static struct rpc_program nsm_program = {
|
||||
.name = "statd",
|
||||
.number = SM_PROGRAM,
|
||||
.nrvers = ARRAY_SIZE(nsm_version),
|
||||
.version = nsm_version,
|
||||
.stats = &nsm_stats
|
||||
};
|
||||
571
fs/lockd/svc.c
Normal file
571
fs/lockd/svc.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* linux/fs/lockd/svc.c
|
||||
*
|
||||
* This is the central lockd service.
|
||||
*
|
||||
* FIXME: Separate the lockd NFS server functionality from the lockd NFS
|
||||
* client functionality. Oh why didn't Sun create two separate
|
||||
* services in the first place?
|
||||
*
|
||||
* Authors: Olaf Kirch (okir@monad.swb.de)
|
||||
*
|
||||
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/sunrpc/types.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
#include <linux/nfs.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_SVC
|
||||
#define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE)
|
||||
#define ALLOWED_SIGS (sigmask(SIGKILL))
|
||||
|
||||
static struct svc_program nlmsvc_program;
|
||||
|
||||
struct nlmsvc_binding * nlmsvc_ops;
|
||||
EXPORT_SYMBOL(nlmsvc_ops);
|
||||
|
||||
static DEFINE_MUTEX(nlmsvc_mutex);
|
||||
static unsigned int nlmsvc_users;
|
||||
static pid_t nlmsvc_pid;
|
||||
static struct svc_serv *nlmsvc_serv;
|
||||
int nlmsvc_grace_period;
|
||||
unsigned long nlmsvc_timeout;
|
||||
|
||||
static DECLARE_COMPLETION(lockd_start_done);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
|
||||
|
||||
/*
|
||||
* These can be set at insmod time (useful for NFS as root filesystem),
|
||||
* and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003
|
||||
*/
|
||||
static unsigned long nlm_grace_period;
|
||||
static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
static int nlm_udpport, nlm_tcpport;
|
||||
int nsm_use_hostnames = 0;
|
||||
|
||||
/*
|
||||
* Constants needed for the sysctl interface.
|
||||
*/
|
||||
static const unsigned long nlm_grace_period_min = 0;
|
||||
static const unsigned long nlm_grace_period_max = 240;
|
||||
static const unsigned long nlm_timeout_min = 3;
|
||||
static const unsigned long nlm_timeout_max = 20;
|
||||
static const int nlm_port_min = 0, nlm_port_max = 65535;
|
||||
|
||||
static struct ctl_table_header * nlm_sysctl_table;
|
||||
|
||||
static unsigned long set_grace_period(void)
|
||||
{
|
||||
unsigned long grace_period;
|
||||
|
||||
/* Note: nlm_timeout should always be nonzero */
|
||||
if (nlm_grace_period)
|
||||
grace_period = ((nlm_grace_period + nlm_timeout - 1)
|
||||
/ nlm_timeout) * nlm_timeout * HZ;
|
||||
else
|
||||
grace_period = nlm_timeout * 5 * HZ;
|
||||
nlmsvc_grace_period = 1;
|
||||
return grace_period + jiffies;
|
||||
}
|
||||
|
||||
static inline void clear_grace_period(void)
|
||||
{
|
||||
nlmsvc_grace_period = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the lockd kernel thread
|
||||
*/
|
||||
static void
|
||||
lockd(struct svc_rqst *rqstp)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long grace_period_expire;
|
||||
|
||||
/* Lock module and set up kernel thread */
|
||||
/* lockd_up is waiting for us to startup, so will
|
||||
* be holding a reference to this module, so it
|
||||
* is safe to just claim another reference
|
||||
*/
|
||||
__module_get(THIS_MODULE);
|
||||
lock_kernel();
|
||||
|
||||
/*
|
||||
* Let our maker know we're running.
|
||||
*/
|
||||
nlmsvc_pid = current->pid;
|
||||
nlmsvc_serv = rqstp->rq_server;
|
||||
complete(&lockd_start_done);
|
||||
|
||||
daemonize("lockd");
|
||||
|
||||
/* Process request with signals blocked, but allow SIGKILL. */
|
||||
allow_signal(SIGKILL);
|
||||
|
||||
/* kick rpciod */
|
||||
rpciod_up();
|
||||
|
||||
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
|
||||
|
||||
if (!nlm_timeout)
|
||||
nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
nlmsvc_timeout = nlm_timeout * HZ;
|
||||
|
||||
grace_period_expire = set_grace_period();
|
||||
|
||||
/*
|
||||
* The main request loop. We don't terminate until the last
|
||||
* NFS mount or NFS daemon has gone away, and we've been sent a
|
||||
* signal, or else another process has taken over our job.
|
||||
*/
|
||||
while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) {
|
||||
long timeout = MAX_SCHEDULE_TIMEOUT;
|
||||
char buf[RPC_MAX_ADDRBUFLEN];
|
||||
|
||||
if (signalled()) {
|
||||
flush_signals(current);
|
||||
if (nlmsvc_ops) {
|
||||
nlmsvc_invalidate_all();
|
||||
grace_period_expire = set_grace_period();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retry any blocked locks that have been notified by
|
||||
* the VFS. Don't do this during grace period.
|
||||
* (Theoretically, there shouldn't even be blocked locks
|
||||
* during grace period).
|
||||
*/
|
||||
if (!nlmsvc_grace_period) {
|
||||
timeout = nlmsvc_retry_blocked();
|
||||
} else if (time_before(grace_period_expire, jiffies))
|
||||
clear_grace_period();
|
||||
|
||||
/*
|
||||
* Find a socket with data available and call its
|
||||
* recvfrom routine.
|
||||
*/
|
||||
err = svc_recv(rqstp, timeout);
|
||||
if (err == -EAGAIN || err == -EINTR)
|
||||
continue;
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING
|
||||
"lockd: terminating on error %d\n",
|
||||
-err);
|
||||
break;
|
||||
}
|
||||
|
||||
dprintk("lockd: request from %s\n",
|
||||
svc_print_addr(rqstp, buf, sizeof(buf)));
|
||||
|
||||
svc_process(rqstp);
|
||||
}
|
||||
|
||||
flush_signals(current);
|
||||
|
||||
/*
|
||||
* Check whether there's a new lockd process before
|
||||
* shutting down the hosts and clearing the slot.
|
||||
*/
|
||||
if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
|
||||
if (nlmsvc_ops)
|
||||
nlmsvc_invalidate_all();
|
||||
nlm_shutdown_hosts();
|
||||
nlmsvc_pid = 0;
|
||||
nlmsvc_serv = NULL;
|
||||
} else
|
||||
printk(KERN_DEBUG
|
||||
"lockd: new process, skipping host shutdown\n");
|
||||
wake_up(&lockd_exit);
|
||||
|
||||
/* Exit the RPC thread */
|
||||
svc_exit_thread(rqstp);
|
||||
|
||||
/* release rpciod */
|
||||
rpciod_down();
|
||||
|
||||
/* Release module */
|
||||
unlock_kernel();
|
||||
module_put_and_exit(0);
|
||||
}
|
||||
|
||||
|
||||
static int find_socket(struct svc_serv *serv, int proto)
|
||||
{
|
||||
struct svc_sock *svsk;
|
||||
int found = 0;
|
||||
list_for_each_entry(svsk, &serv->sv_permsocks, sk_list)
|
||||
if (svsk->sk_sk->sk_protocol == proto) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make any sockets that are needed but not present.
|
||||
* If nlm_udpport or nlm_tcpport were set as module
|
||||
* options, make those sockets unconditionally
|
||||
*/
|
||||
static int make_socks(struct svc_serv *serv, int proto)
|
||||
{
|
||||
static int warned;
|
||||
int err = 0;
|
||||
|
||||
if (proto == IPPROTO_UDP || nlm_udpport)
|
||||
if (!find_socket(serv, IPPROTO_UDP))
|
||||
err = svc_makesock(serv, IPPROTO_UDP, nlm_udpport,
|
||||
SVC_SOCK_DEFAULTS);
|
||||
if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport))
|
||||
if (!find_socket(serv, IPPROTO_TCP))
|
||||
err = svc_makesock(serv, IPPROTO_TCP, nlm_tcpport,
|
||||
SVC_SOCK_DEFAULTS);
|
||||
|
||||
if (err >= 0) {
|
||||
warned = 0;
|
||||
err = 0;
|
||||
} else if (warned++ == 0)
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: makesock failed, error=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up the lockd process if it's not already up.
|
||||
*/
|
||||
int
|
||||
lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
|
||||
{
|
||||
struct svc_serv * serv;
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
/*
|
||||
* Check whether we're already up and running.
|
||||
*/
|
||||
if (nlmsvc_pid) {
|
||||
if (proto)
|
||||
error = make_socks(nlmsvc_serv, proto);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check: if there's no pid,
|
||||
* we should be the first user ...
|
||||
*/
|
||||
if (nlmsvc_users)
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: no pid, %d users??\n", nlmsvc_users);
|
||||
|
||||
error = -ENOMEM;
|
||||
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
|
||||
if (!serv) {
|
||||
printk(KERN_WARNING "lockd_up: create service failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = make_socks(serv, proto)) < 0)
|
||||
goto destroy_and_out;
|
||||
|
||||
/*
|
||||
* Create the kernel thread and wait for it to start.
|
||||
*/
|
||||
error = svc_create_thread(lockd, serv);
|
||||
if (error) {
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: create thread failed, error=%d\n", error);
|
||||
goto destroy_and_out;
|
||||
}
|
||||
wait_for_completion(&lockd_start_done);
|
||||
|
||||
/*
|
||||
* Note: svc_serv structures have an initial use count of 1,
|
||||
* so we exit through here on both success and failure.
|
||||
*/
|
||||
destroy_and_out:
|
||||
svc_destroy(serv);
|
||||
out:
|
||||
if (!error)
|
||||
nlmsvc_users++;
|
||||
mutex_unlock(&nlmsvc_mutex);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(lockd_up);
|
||||
|
||||
/*
|
||||
* Decrement the user count and bring down lockd if we're the last.
|
||||
*/
|
||||
void
|
||||
lockd_down(void)
|
||||
{
|
||||
static int warned;
|
||||
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
if (nlmsvc_users) {
|
||||
if (--nlmsvc_users)
|
||||
goto out;
|
||||
} else
|
||||
printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid);
|
||||
|
||||
if (!nlmsvc_pid) {
|
||||
if (warned++ == 0)
|
||||
printk(KERN_WARNING "lockd_down: no lockd running.\n");
|
||||
goto out;
|
||||
}
|
||||
warned = 0;
|
||||
|
||||
kill_proc(nlmsvc_pid, SIGKILL, 1);
|
||||
/*
|
||||
* Wait for the lockd process to exit, but since we're holding
|
||||
* the lockd semaphore, we can't wait around forever ...
|
||||
*/
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
interruptible_sleep_on_timeout(&lockd_exit, HZ);
|
||||
if (nlmsvc_pid) {
|
||||
printk(KERN_WARNING
|
||||
"lockd_down: lockd failed to exit, clearing pid\n");
|
||||
nlmsvc_pid = 0;
|
||||
}
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
out:
|
||||
mutex_unlock(&nlmsvc_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(lockd_down);
|
||||
|
||||
/*
|
||||
* Sysctl parameters (same as module parameters, different interface).
|
||||
*/
|
||||
|
||||
static ctl_table nlm_sysctls[] = {
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nlm_grace_period",
|
||||
.data = &nlm_grace_period,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_doulongvec_minmax,
|
||||
.extra1 = (unsigned long *) &nlm_grace_period_min,
|
||||
.extra2 = (unsigned long *) &nlm_grace_period_max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nlm_timeout",
|
||||
.data = &nlm_timeout,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_doulongvec_minmax,
|
||||
.extra1 = (unsigned long *) &nlm_timeout_min,
|
||||
.extra2 = (unsigned long *) &nlm_timeout_max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nlm_udpport",
|
||||
.data = &nlm_udpport,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (int *) &nlm_port_min,
|
||||
.extra2 = (int *) &nlm_port_max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nlm_tcpport",
|
||||
.data = &nlm_tcpport,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (int *) &nlm_port_min,
|
||||
.extra2 = (int *) &nlm_port_max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nsm_use_hostnames",
|
||||
.data = &nsm_use_hostnames,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nsm_local_state",
|
||||
.data = &nsm_local_state,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nlm_sysctl_dir[] = {
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "nfs",
|
||||
.mode = 0555,
|
||||
.child = nlm_sysctls,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nlm_sysctl_root[] = {
|
||||
{
|
||||
.ctl_name = CTL_FS,
|
||||
.procname = "fs",
|
||||
.mode = 0555,
|
||||
.child = nlm_sysctl_dir,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Module (and sysfs) parameters.
|
||||
*/
|
||||
|
||||
#define param_set_min_max(name, type, which_strtol, min, max) \
|
||||
static int param_set_##name(const char *val, struct kernel_param *kp) \
|
||||
{ \
|
||||
char *endp; \
|
||||
__typeof__(type) num = which_strtol(val, &endp, 0); \
|
||||
if (endp == val || *endp || num < (min) || num > (max)) \
|
||||
return -EINVAL; \
|
||||
*((int *) kp->arg) = num; \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
static inline int is_callback(u32 proc)
|
||||
{
|
||||
return proc == NLMPROC_GRANTED
|
||||
|| proc == NLMPROC_GRANTED_MSG
|
||||
|| proc == NLMPROC_TEST_RES
|
||||
|| proc == NLMPROC_LOCK_RES
|
||||
|| proc == NLMPROC_CANCEL_RES
|
||||
|| proc == NLMPROC_UNLOCK_RES
|
||||
|| proc == NLMPROC_NSM_NOTIFY;
|
||||
}
|
||||
|
||||
|
||||
static int lockd_authenticate(struct svc_rqst *rqstp)
|
||||
{
|
||||
rqstp->rq_client = NULL;
|
||||
switch (rqstp->rq_authop->flavour) {
|
||||
case RPC_AUTH_NULL:
|
||||
case RPC_AUTH_UNIX:
|
||||
if (rqstp->rq_proc == 0)
|
||||
return SVC_OK;
|
||||
if (is_callback(rqstp->rq_proc)) {
|
||||
/* Leave it to individual procedures to
|
||||
* call nlmsvc_lookup_host(rqstp)
|
||||
*/
|
||||
return SVC_OK;
|
||||
}
|
||||
return svc_set_client(rqstp);
|
||||
}
|
||||
return SVC_DENIED;
|
||||
}
|
||||
|
||||
|
||||
param_set_min_max(port, int, simple_strtol, 0, 65535)
|
||||
param_set_min_max(grace_period, unsigned long, simple_strtoul,
|
||||
nlm_grace_period_min, nlm_grace_period_max)
|
||||
param_set_min_max(timeout, unsigned long, simple_strtoul,
|
||||
nlm_timeout_min, nlm_timeout_max)
|
||||
|
||||
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
|
||||
MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong,
|
||||
&nlm_grace_period, 0644);
|
||||
module_param_call(nlm_timeout, param_set_timeout, param_get_ulong,
|
||||
&nlm_timeout, 0644);
|
||||
module_param_call(nlm_udpport, param_set_port, param_get_int,
|
||||
&nlm_udpport, 0644);
|
||||
module_param_call(nlm_tcpport, param_set_port, param_get_int,
|
||||
&nlm_tcpport, 0644);
|
||||
module_param(nsm_use_hostnames, bool, 0644);
|
||||
|
||||
/*
|
||||
* Initialising and terminating the module.
|
||||
*/
|
||||
|
||||
static int __init init_nlm(void)
|
||||
{
|
||||
nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
|
||||
return nlm_sysctl_table ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static void __exit exit_nlm(void)
|
||||
{
|
||||
/* FIXME: delete all NLM clients */
|
||||
nlm_shutdown_hosts();
|
||||
unregister_sysctl_table(nlm_sysctl_table);
|
||||
}
|
||||
|
||||
module_init(init_nlm);
|
||||
module_exit(exit_nlm);
|
||||
|
||||
/*
|
||||
* Define NLM program and procedures
|
||||
*/
|
||||
static struct svc_version nlmsvc_version1 = {
|
||||
.vs_vers = 1,
|
||||
.vs_nproc = 17,
|
||||
.vs_proc = nlmsvc_procedures,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
static struct svc_version nlmsvc_version3 = {
|
||||
.vs_vers = 3,
|
||||
.vs_nproc = 24,
|
||||
.vs_proc = nlmsvc_procedures,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
static struct svc_version nlmsvc_version4 = {
|
||||
.vs_vers = 4,
|
||||
.vs_nproc = 24,
|
||||
.vs_proc = nlmsvc_procedures4,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
#endif
|
||||
static struct svc_version * nlmsvc_version[] = {
|
||||
[1] = &nlmsvc_version1,
|
||||
[3] = &nlmsvc_version3,
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
[4] = &nlmsvc_version4,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct svc_stat nlmsvc_stats;
|
||||
|
||||
#define NLM_NRVERS ARRAY_SIZE(nlmsvc_version)
|
||||
static struct svc_program nlmsvc_program = {
|
||||
.pg_prog = NLM_PROGRAM, /* program number */
|
||||
.pg_nvers = NLM_NRVERS, /* number of entries in nlmsvc_version */
|
||||
.pg_vers = nlmsvc_version, /* version table */
|
||||
.pg_name = "lockd", /* service name */
|
||||
.pg_class = "nfsd", /* share authentication with nfsd */
|
||||
.pg_stats = &nlmsvc_stats, /* stats table */
|
||||
.pg_authenticate = &lockd_authenticate /* export authentication */
|
||||
};
|
||||
524
fs/lockd/svc4proc.c
Normal file
524
fs/lockd/svc4proc.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* linux/fs/lockd/svc4proc.c
|
||||
*
|
||||
* Lockd server procedures. We don't implement the NLM_*_RES
|
||||
* procedures because we don't use the async procedures.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/nfsd/nfsd.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/share.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_CLIENT
|
||||
|
||||
/*
|
||||
* Obtain client and file from arguments
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_host **hostp, struct nlm_file **filp)
|
||||
{
|
||||
struct nlm_host *host = NULL;
|
||||
struct nlm_file *file = NULL;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
__be32 error = 0;
|
||||
|
||||
/* nfsd callbacks must have been installed for this procedure */
|
||||
if (!nlmsvc_ops)
|
||||
return nlm_lck_denied_nolocks;
|
||||
|
||||
/* Obtain host handle */
|
||||
if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
|
||||
|| (argp->monitor && nsm_monitor(host) < 0))
|
||||
goto no_locks;
|
||||
*hostp = host;
|
||||
|
||||
/* Obtain file pointer. Not used by FREE_ALL call. */
|
||||
if (filp != NULL) {
|
||||
if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
|
||||
goto no_locks;
|
||||
*filp = file;
|
||||
|
||||
/* Set up the missing parts of the file_lock structure */
|
||||
lock->fl.fl_file = file->f_file;
|
||||
lock->fl.fl_owner = (fl_owner_t) host;
|
||||
lock->fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
no_locks:
|
||||
if (host)
|
||||
nlm_release_host(host);
|
||||
if (error)
|
||||
return error;
|
||||
return nlm_lck_denied_nolocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* NULL: Test for presence of service
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
|
||||
{
|
||||
dprintk("lockd: NULL called\n");
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST: Check for conflicting lock
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: TEST4 called\n");
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept test requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
|
||||
|
||||
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: LOCK called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period && !argp->reclaim) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
#if 0
|
||||
/* If supplied state doesn't match current state, we assume it's
|
||||
* an old request that time-warped somehow. Any error return would
|
||||
* do in this case because it's irrelevant anyway.
|
||||
*
|
||||
* NB: We don't retrieve the remote host's state yet.
|
||||
*/
|
||||
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
|
||||
resp->status = nlm_lck_denied_nolocks;
|
||||
} else
|
||||
#endif
|
||||
|
||||
/* Now try to lock the file */
|
||||
resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
|
||||
argp->block, &argp->cookie);
|
||||
|
||||
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: CANCEL called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Try to cancel request. */
|
||||
resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
|
||||
|
||||
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNLOCK: release a lock
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: UNLOCK called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to remove the lock */
|
||||
resp->status = nlmsvc_unlock(file, &argp->lock);
|
||||
|
||||
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* GRANTED: A server calls us to tell that a process' lock request
|
||||
* was granted
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
dprintk("lockd: GRANTED called\n");
|
||||
resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
|
||||
dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the generic lockd callback for async RPC calls
|
||||
*/
|
||||
static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
|
||||
{
|
||||
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
|
||||
-task->tk_status);
|
||||
}
|
||||
|
||||
static void nlm4svc_callback_release(void *data)
|
||||
{
|
||||
nlm_release_call(data);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlm4svc_callback_ops = {
|
||||
.rpc_call_done = nlm4svc_callback_exit,
|
||||
.rpc_release = nlm4svc_callback_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* `Async' versions of the above service routines. They aren't really,
|
||||
* because we send the callback before the reply proper. I hope this
|
||||
* doesn't break any clients.
|
||||
*/
|
||||
static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *argp,
|
||||
__be32 (*func)(struct svc_rqst *, struct nlm_args *, struct nlm_res *))
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_rqst *call;
|
||||
__be32 stat;
|
||||
|
||||
host = nlmsvc_lookup_host(rqstp,
|
||||
argp->lock.caller,
|
||||
argp->lock.len);
|
||||
if (host == NULL)
|
||||
return rpc_system_err;
|
||||
|
||||
call = nlm_alloc_call(host);
|
||||
if (call == NULL)
|
||||
return rpc_system_err;
|
||||
|
||||
stat = func(rqstp, argp, &call->a_res);
|
||||
if (stat != 0) {
|
||||
nlm_release_call(call);
|
||||
return stat;
|
||||
}
|
||||
|
||||
call->a_flags = RPC_TASK_ASYNC;
|
||||
if (nlm_async_reply(call, proc, &nlm4svc_callback_ops) < 0)
|
||||
return rpc_system_err;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: TEST_MSG called\n");
|
||||
return nlm4svc_callback(rqstp, NLMPROC_TEST_RES, argp, nlm4svc_proc_test);
|
||||
}
|
||||
|
||||
static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: LOCK_MSG called\n");
|
||||
return nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, argp, nlm4svc_proc_lock);
|
||||
}
|
||||
|
||||
static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: CANCEL_MSG called\n");
|
||||
return nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, argp, nlm4svc_proc_cancel);
|
||||
}
|
||||
|
||||
static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: UNLOCK_MSG called\n");
|
||||
return nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, argp, nlm4svc_proc_unlock);
|
||||
}
|
||||
|
||||
static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: GRANTED_MSG called\n");
|
||||
return nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, argp, nlm4svc_proc_granted);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHARE: create a DOS share or alter existing share.
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: SHARE called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period && !argp->reclaim) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to create the share */
|
||||
resp->status = nlmsvc_share_file(host, file, argp);
|
||||
|
||||
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNSHARE: Release a DOS share.
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: UNSHARE called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to lock the file */
|
||||
resp->status = nlmsvc_unshare_file(host, file, argp);
|
||||
|
||||
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* NM_LOCK: Create an unmonitored lock
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
dprintk("lockd: NM_LOCK called\n");
|
||||
|
||||
argp->monitor = 0; /* just clean the monitor flag */
|
||||
return nlm4svc_proc_lock(rqstp, argp, resp);
|
||||
}
|
||||
|
||||
/*
|
||||
* FREE_ALL: Release all locks and shares held by client
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
|
||||
/* Obtain client */
|
||||
if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL))
|
||||
return rpc_success;
|
||||
|
||||
nlmsvc_free_host_resources(host);
|
||||
nlm_release_host(host);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* SM_NOTIFY: private callback from statd (not part of official NLM proto)
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
|
||||
void *resp)
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
|
||||
memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
|
||||
|
||||
dprintk("lockd: SM_NOTIFY called\n");
|
||||
if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
|
||||
|| ntohs(saddr.sin_port) >= 1024) {
|
||||
char buf[RPC_MAX_ADDRBUFLEN];
|
||||
printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
|
||||
svc_print_addr(rqstp, buf, sizeof(buf)));
|
||||
return rpc_system_err;
|
||||
}
|
||||
|
||||
/* Obtain the host pointer for this NFS server and try to
|
||||
* reclaim all locks we hold on this server.
|
||||
*/
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sin_addr.s_addr = argp->addr;
|
||||
nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
|
||||
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* client sent a GRANTED_RES, let's remove the associated block
|
||||
*/
|
||||
static __be32
|
||||
nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
|
||||
void *resp)
|
||||
{
|
||||
if (!nlmsvc_ops)
|
||||
return rpc_success;
|
||||
|
||||
dprintk("lockd: GRANTED_RES called\n");
|
||||
|
||||
nlmsvc_grant_reply(&argp->cookie, argp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NLM Server procedures.
|
||||
*/
|
||||
|
||||
#define nlm4svc_encode_norep nlm4svc_encode_void
|
||||
#define nlm4svc_decode_norep nlm4svc_decode_void
|
||||
#define nlm4svc_decode_testres nlm4svc_decode_void
|
||||
#define nlm4svc_decode_lockres nlm4svc_decode_void
|
||||
#define nlm4svc_decode_unlockres nlm4svc_decode_void
|
||||
#define nlm4svc_decode_cancelres nlm4svc_decode_void
|
||||
#define nlm4svc_decode_grantedres nlm4svc_decode_void
|
||||
|
||||
#define nlm4svc_proc_none nlm4svc_proc_null
|
||||
#define nlm4svc_proc_test_res nlm4svc_proc_null
|
||||
#define nlm4svc_proc_lock_res nlm4svc_proc_null
|
||||
#define nlm4svc_proc_cancel_res nlm4svc_proc_null
|
||||
#define nlm4svc_proc_unlock_res nlm4svc_proc_null
|
||||
|
||||
struct nlm_void { int dummy; };
|
||||
|
||||
#define PROC(name, xargt, xrest, argt, rest, respsize) \
|
||||
{ .pc_func = (svc_procfunc) nlm4svc_proc_##name, \
|
||||
.pc_decode = (kxdrproc_t) nlm4svc_decode_##xargt, \
|
||||
.pc_encode = (kxdrproc_t) nlm4svc_encode_##xrest, \
|
||||
.pc_release = NULL, \
|
||||
.pc_argsize = sizeof(struct nlm_##argt), \
|
||||
.pc_ressize = sizeof(struct nlm_##rest), \
|
||||
.pc_xdrressize = respsize, \
|
||||
}
|
||||
#define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */
|
||||
#define No (1+1024/4) /* netobj */
|
||||
#define St 1 /* status */
|
||||
#define Rg 4 /* range (offset + length) */
|
||||
struct svc_procedure nlmsvc_procedures4[] = {
|
||||
PROC(null, void, void, void, void, 1),
|
||||
PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg),
|
||||
PROC(lock, lockargs, res, args, res, Ck+St),
|
||||
PROC(cancel, cancargs, res, args, res, Ck+St),
|
||||
PROC(unlock, unlockargs, res, args, res, Ck+St),
|
||||
PROC(granted, testargs, res, args, res, Ck+St),
|
||||
PROC(test_msg, testargs, norep, args, void, 1),
|
||||
PROC(lock_msg, lockargs, norep, args, void, 1),
|
||||
PROC(cancel_msg, cancargs, norep, args, void, 1),
|
||||
PROC(unlock_msg, unlockargs, norep, args, void, 1),
|
||||
PROC(granted_msg, testargs, norep, args, void, 1),
|
||||
PROC(test_res, testres, norep, res, void, 1),
|
||||
PROC(lock_res, lockres, norep, res, void, 1),
|
||||
PROC(cancel_res, cancelres, norep, res, void, 1),
|
||||
PROC(unlock_res, unlockres, norep, res, void, 1),
|
||||
PROC(granted_res, res, norep, res, void, 1),
|
||||
/* statd callback */
|
||||
PROC(sm_notify, reboot, void, reboot, void, 1),
|
||||
PROC(none, void, void, void, void, 0),
|
||||
PROC(none, void, void, void, void, 0),
|
||||
PROC(none, void, void, void, void, 0),
|
||||
PROC(share, shareargs, shareres, args, res, Ck+St+1),
|
||||
PROC(unshare, shareargs, shareres, args, res, Ck+St+1),
|
||||
PROC(nm_lock, lockargs, res, args, res, Ck+St),
|
||||
PROC(free_all, notify, void, args, void, 1),
|
||||
|
||||
};
|
||||
697
fs/lockd/svclock.c
Normal file
697
fs/lockd/svclock.c
Normal file
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* linux/fs/lockd/svclock.c
|
||||
*
|
||||
* Handling of server-side locks, mostly of the blocked variety.
|
||||
* This is the ugliest part of lockd because we tread on very thin ice.
|
||||
* GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
|
||||
* IMNSHO introducing the grant callback into the NLM protocol was one
|
||||
* of the worst ideas Sun ever had. Except maybe for the idea of doing
|
||||
* NFS file locking at all.
|
||||
*
|
||||
* I'm trying hard to avoid race conditions by protecting most accesses
|
||||
* to a file's list of blocked locks through a semaphore. The global
|
||||
* list of blocked locks is not protected in this fashion however.
|
||||
* Therefore, some functions (such as the RPC callback for the async grant
|
||||
* call) move blocked locks towards the head of the list *while some other
|
||||
* process might be traversing it*. This should not be a problem in
|
||||
* practice, because this will only cause functions traversing the list
|
||||
* to visit some blocks twice.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/nlm.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_SVCLOCK
|
||||
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
#define nlm_deadlock nlm4_deadlock
|
||||
#else
|
||||
#define nlm_deadlock nlm_lck_denied
|
||||
#endif
|
||||
|
||||
static void nlmsvc_release_block(struct nlm_block *block);
|
||||
static void nlmsvc_insert_block(struct nlm_block *block, unsigned long);
|
||||
static void nlmsvc_remove_block(struct nlm_block *block);
|
||||
|
||||
static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
|
||||
static void nlmsvc_freegrantargs(struct nlm_rqst *call);
|
||||
static const struct rpc_call_ops nlmsvc_grant_ops;
|
||||
|
||||
/*
|
||||
* The list of blocked locks to retry
|
||||
*/
|
||||
static LIST_HEAD(nlm_blocked);
|
||||
|
||||
/*
|
||||
* Insert a blocked lock into the global list
|
||||
*/
|
||||
static void
|
||||
nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
|
||||
{
|
||||
struct nlm_block *b;
|
||||
struct list_head *pos;
|
||||
|
||||
dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
|
||||
if (list_empty(&block->b_list)) {
|
||||
kref_get(&block->b_count);
|
||||
} else {
|
||||
list_del_init(&block->b_list);
|
||||
}
|
||||
|
||||
pos = &nlm_blocked;
|
||||
if (when != NLM_NEVER) {
|
||||
if ((when += jiffies) == NLM_NEVER)
|
||||
when ++;
|
||||
list_for_each(pos, &nlm_blocked) {
|
||||
b = list_entry(pos, struct nlm_block, b_list);
|
||||
if (time_after(b->b_when,when) || b->b_when == NLM_NEVER)
|
||||
break;
|
||||
}
|
||||
/* On normal exit from the loop, pos == &nlm_blocked,
|
||||
* so we will be adding to the end of the list - good
|
||||
*/
|
||||
}
|
||||
|
||||
list_add_tail(&block->b_list, pos);
|
||||
block->b_when = when;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a block from the global list
|
||||
*/
|
||||
static inline void
|
||||
nlmsvc_remove_block(struct nlm_block *block)
|
||||
{
|
||||
if (!list_empty(&block->b_list)) {
|
||||
list_del_init(&block->b_list);
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a block for a given lock
|
||||
*/
|
||||
static struct nlm_block *
|
||||
nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
struct file_lock *fl;
|
||||
|
||||
dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n",
|
||||
file, lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end, lock->fl.fl_type);
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
fl = &block->b_call->a_args.lock.fl;
|
||||
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
|
||||
block->b_file, fl->fl_pid,
|
||||
(long long)fl->fl_start,
|
||||
(long long)fl->fl_end, fl->fl_type,
|
||||
nlmdbg_cookie2a(&block->b_call->a_args.cookie));
|
||||
if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
|
||||
kref_get(&block->b_count);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
|
||||
{
|
||||
if(a->len != b->len)
|
||||
return 0;
|
||||
if(memcmp(a->data,b->data,a->len))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a block with a given NLM cookie.
|
||||
*/
|
||||
static inline struct nlm_block *
|
||||
nlmsvc_find_block(struct nlm_cookie *cookie)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie))
|
||||
goto found;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block);
|
||||
kref_get(&block->b_count);
|
||||
return block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a block and initialize it.
|
||||
*
|
||||
* Note: we explicitly set the cookie of the grant reply to that of
|
||||
* the blocked lock request. The spec explicitly mentions that the client
|
||||
* should _not_ rely on the callback containing the same cookie as the
|
||||
* request, but (as I found out later) that's because some implementations
|
||||
* do just this. Never mind the standards comittees, they support our
|
||||
* logging industries.
|
||||
*
|
||||
* 10 years later: I hope we can safely ignore these old and broken
|
||||
* clients by now. Let's fix this so we can uniquely identify an incoming
|
||||
* GRANTED_RES message by cookie, without having to rely on the client's IP
|
||||
* address. --okir
|
||||
*/
|
||||
static inline struct nlm_block *
|
||||
nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
struct nlm_lock *lock, struct nlm_cookie *cookie)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
struct nlm_host *host;
|
||||
struct nlm_rqst *call = NULL;
|
||||
|
||||
/* Create host handle for callback */
|
||||
host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len);
|
||||
if (host == NULL)
|
||||
return NULL;
|
||||
|
||||
call = nlm_alloc_call(host);
|
||||
if (call == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Allocate memory for block, and initialize arguments */
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (block == NULL)
|
||||
goto failed;
|
||||
kref_init(&block->b_count);
|
||||
INIT_LIST_HEAD(&block->b_list);
|
||||
INIT_LIST_HEAD(&block->b_flist);
|
||||
|
||||
if (!nlmsvc_setgrantargs(call, lock))
|
||||
goto failed_free;
|
||||
|
||||
/* Set notifier function for VFS, and init args */
|
||||
call->a_args.lock.fl.fl_flags |= FL_SLEEP;
|
||||
call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
nlmclnt_next_cookie(&call->a_args.cookie);
|
||||
|
||||
dprintk("lockd: created block %p...\n", block);
|
||||
|
||||
/* Create and initialize the block */
|
||||
block->b_daemon = rqstp->rq_server;
|
||||
block->b_host = host;
|
||||
block->b_file = file;
|
||||
file->f_count++;
|
||||
|
||||
/* Add to file's list of blocks */
|
||||
list_add(&block->b_flist, &file->f_blocks);
|
||||
|
||||
/* Set up RPC arguments for callback */
|
||||
block->b_call = call;
|
||||
call->a_flags = RPC_TASK_ASYNC;
|
||||
call->a_block = block;
|
||||
|
||||
return block;
|
||||
|
||||
failed_free:
|
||||
kfree(block);
|
||||
failed:
|
||||
nlm_release_call(call);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a block. If the lock was cancelled or the grant callback
|
||||
* failed, unlock is set to 1.
|
||||
* It is the caller's responsibility to check whether the file
|
||||
* can be closed hereafter.
|
||||
*/
|
||||
static int nlmsvc_unlink_block(struct nlm_block *block)
|
||||
{
|
||||
int status;
|
||||
dprintk("lockd: unlinking block %p...\n", block);
|
||||
|
||||
/* Remove block from list */
|
||||
status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
|
||||
nlmsvc_remove_block(block);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void nlmsvc_free_block(struct kref *kref)
|
||||
{
|
||||
struct nlm_block *block = container_of(kref, struct nlm_block, b_count);
|
||||
struct nlm_file *file = block->b_file;
|
||||
|
||||
dprintk("lockd: freeing block %p...\n", block);
|
||||
|
||||
/* Remove block from file's list of blocks */
|
||||
mutex_lock(&file->f_mutex);
|
||||
list_del_init(&block->b_flist);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
|
||||
nlmsvc_freegrantargs(block->b_call);
|
||||
nlm_release_call(block->b_call);
|
||||
nlm_release_file(block->b_file);
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
static void nlmsvc_release_block(struct nlm_block *block)
|
||||
{
|
||||
if (block != NULL)
|
||||
kref_put(&block->b_count, nlmsvc_free_block);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all blocks and delete blocks held by
|
||||
* a matching host.
|
||||
*/
|
||||
void nlmsvc_traverse_blocks(struct nlm_host *host,
|
||||
struct nlm_file *file,
|
||||
nlm_host_match_fn_t match)
|
||||
{
|
||||
struct nlm_block *block, *next;
|
||||
|
||||
restart:
|
||||
mutex_lock(&file->f_mutex);
|
||||
list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
|
||||
if (!match(block->b_host, host))
|
||||
continue;
|
||||
/* Do not destroy blocks that are not on
|
||||
* the global retry list - why? */
|
||||
if (list_empty(&block->b_list))
|
||||
continue;
|
||||
kref_get(&block->b_count);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
nlmsvc_unlink_block(block);
|
||||
nlmsvc_release_block(block);
|
||||
goto restart;
|
||||
}
|
||||
mutex_unlock(&file->f_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize arguments for GRANTED call. The nlm_rqst structure
|
||||
* has been cleared already.
|
||||
*/
|
||||
static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
|
||||
{
|
||||
locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
|
||||
memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
|
||||
call->a_args.lock.caller = utsname()->nodename;
|
||||
call->a_args.lock.oh.len = lock->oh.len;
|
||||
|
||||
/* set default data area */
|
||||
call->a_args.lock.oh.data = call->a_owner;
|
||||
call->a_args.lock.svid = lock->fl.fl_pid;
|
||||
|
||||
if (lock->oh.len > NLMCLNT_OHSIZE) {
|
||||
void *data = kmalloc(lock->oh.len, GFP_KERNEL);
|
||||
if (!data)
|
||||
return 0;
|
||||
call->a_args.lock.oh.data = (u8 *) data;
|
||||
}
|
||||
|
||||
memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void nlmsvc_freegrantargs(struct nlm_rqst *call)
|
||||
{
|
||||
if (call->a_args.lock.oh.data != call->a_owner)
|
||||
kfree(call->a_args.lock.oh.data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to establish a lock, and if it can't be granted, block it
|
||||
* if required.
|
||||
*/
|
||||
__be32
|
||||
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
|
||||
{
|
||||
struct nlm_block *block, *newblock = NULL;
|
||||
int error;
|
||||
__be32 ret;
|
||||
|
||||
dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
|
||||
file->f_file->f_path.dentry->d_inode->i_sb->s_id,
|
||||
file->f_file->f_path.dentry->d_inode->i_ino,
|
||||
lock->fl.fl_type, lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end,
|
||||
wait);
|
||||
|
||||
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
again:
|
||||
/* Lock file against concurrent access */
|
||||
mutex_lock(&file->f_mutex);
|
||||
/* Get existing block (in case client is busy-waiting) */
|
||||
block = nlmsvc_lookup_block(file, lock);
|
||||
if (block == NULL) {
|
||||
if (newblock != NULL)
|
||||
lock = &newblock->b_call->a_args.lock;
|
||||
} else
|
||||
lock = &block->b_call->a_args.lock;
|
||||
|
||||
error = posix_lock_file(file->f_file, &lock->fl);
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
|
||||
dprintk("lockd: posix_lock_file returned %d\n", error);
|
||||
|
||||
switch(error) {
|
||||
case 0:
|
||||
ret = nlm_granted;
|
||||
goto out;
|
||||
case -EAGAIN:
|
||||
break;
|
||||
case -EDEADLK:
|
||||
ret = nlm_deadlock;
|
||||
goto out;
|
||||
default: /* includes ENOLCK */
|
||||
ret = nlm_lck_denied_nolocks;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nlm_lck_denied;
|
||||
if (!wait)
|
||||
goto out;
|
||||
|
||||
ret = nlm_lck_blocked;
|
||||
if (block != NULL)
|
||||
goto out;
|
||||
|
||||
/* If we don't have a block, create and initialize it. Then
|
||||
* retry because we may have slept in kmalloc. */
|
||||
/* We have to release f_mutex as nlmsvc_create_block may try to
|
||||
* to claim it while doing host garbage collection */
|
||||
if (newblock == NULL) {
|
||||
mutex_unlock(&file->f_mutex);
|
||||
dprintk("lockd: blocking on this lock (allocating).\n");
|
||||
if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie)))
|
||||
return nlm_lck_denied_nolocks;
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* Append to list of blocked */
|
||||
nlmsvc_insert_block(newblock, NLM_NEVER);
|
||||
out:
|
||||
mutex_unlock(&file->f_mutex);
|
||||
nlmsvc_release_block(newblock);
|
||||
nlmsvc_release_block(block);
|
||||
dprintk("lockd: nlmsvc_lock returned %u\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for presence of a conflicting lock.
|
||||
*/
|
||||
__be32
|
||||
nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
|
||||
struct nlm_lock *conflock)
|
||||
{
|
||||
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
|
||||
file->f_file->f_path.dentry->d_inode->i_sb->s_id,
|
||||
file->f_file->f_path.dentry->d_inode->i_ino,
|
||||
lock->fl.fl_type,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
|
||||
if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) {
|
||||
dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
|
||||
conflock->fl.fl_type,
|
||||
(long long)conflock->fl.fl_start,
|
||||
(long long)conflock->fl.fl_end);
|
||||
conflock->caller = "somehost"; /* FIXME */
|
||||
conflock->len = strlen(conflock->caller);
|
||||
conflock->oh.len = 0; /* don't return OH info */
|
||||
conflock->svid = conflock->fl.fl_pid;
|
||||
return nlm_lck_denied;
|
||||
}
|
||||
|
||||
return nlm_granted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a lock.
|
||||
* This implies a CANCEL call: We send a GRANT_MSG, the client replies
|
||||
* with a GRANT_RES call which gets lost, and calls UNLOCK immediately
|
||||
* afterwards. In this case the block will still be there, and hence
|
||||
* must be removed.
|
||||
*/
|
||||
__be32
|
||||
nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
|
||||
{
|
||||
int error;
|
||||
|
||||
dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
|
||||
file->f_file->f_path.dentry->d_inode->i_sb->s_id,
|
||||
file->f_file->f_path.dentry->d_inode->i_ino,
|
||||
lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
|
||||
/* First, cancel any lock that might be there */
|
||||
nlmsvc_cancel_blocked(file, lock);
|
||||
|
||||
lock->fl.fl_type = F_UNLCK;
|
||||
error = posix_lock_file(file->f_file, &lock->fl);
|
||||
|
||||
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel a previously blocked request.
|
||||
*
|
||||
* A cancel request always overrides any grant that may currently
|
||||
* be in progress.
|
||||
* The calling procedure must check whether the file can be closed.
|
||||
*/
|
||||
__be32
|
||||
nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
int status = 0;
|
||||
|
||||
dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
|
||||
file->f_file->f_path.dentry->d_inode->i_sb->s_id,
|
||||
file->f_file->f_path.dentry->d_inode->i_ino,
|
||||
lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
|
||||
mutex_lock(&file->f_mutex);
|
||||
block = nlmsvc_lookup_block(file, lock);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
if (block != NULL) {
|
||||
status = nlmsvc_unlink_block(block);
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
return status ? nlm_lck_denied : nlm_granted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unblock a blocked lock request. This is a callback invoked from the
|
||||
* VFS layer when a lock on which we blocked is removed.
|
||||
*
|
||||
* This function doesn't grant the blocked lock instantly, but rather moves
|
||||
* the block to the head of nlm_blocked where it can be picked up by lockd.
|
||||
*/
|
||||
static void
|
||||
nlmsvc_notify_blocked(struct file_lock *fl)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
|
||||
dprintk("lockd: VFS unblock notification for block %p\n", fl);
|
||||
list_for_each_entry(block, &nlm_blocked, b_list) {
|
||||
if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
|
||||
nlmsvc_insert_block(block, 0);
|
||||
svc_wake_up(block->b_daemon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "lockd: notification for unknown block!\n");
|
||||
}
|
||||
|
||||
static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
|
||||
{
|
||||
return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
|
||||
}
|
||||
|
||||
struct lock_manager_operations nlmsvc_lock_operations = {
|
||||
.fl_compare_owner = nlmsvc_same_owner,
|
||||
.fl_notify = nlmsvc_notify_blocked,
|
||||
};
|
||||
|
||||
/*
|
||||
* Try to claim a lock that was previously blocked.
|
||||
*
|
||||
* Note that we use both the RPC_GRANTED_MSG call _and_ an async
|
||||
* RPC thread when notifying the client. This seems like overkill...
|
||||
* Here's why:
|
||||
* - we don't want to use a synchronous RPC thread, otherwise
|
||||
* we might find ourselves hanging on a dead portmapper.
|
||||
* - Some lockd implementations (e.g. HP) don't react to
|
||||
* RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
|
||||
*/
|
||||
static void
|
||||
nlmsvc_grant_blocked(struct nlm_block *block)
|
||||
{
|
||||
struct nlm_file *file = block->b_file;
|
||||
struct nlm_lock *lock = &block->b_call->a_args.lock;
|
||||
int error;
|
||||
|
||||
dprintk("lockd: grant blocked lock %p\n", block);
|
||||
|
||||
/* Unlink block request from list */
|
||||
nlmsvc_unlink_block(block);
|
||||
|
||||
/* If b_granted is true this means we've been here before.
|
||||
* Just retry the grant callback, possibly refreshing the RPC
|
||||
* binding */
|
||||
if (block->b_granted) {
|
||||
nlm_rebind_host(block->b_host);
|
||||
goto callback;
|
||||
}
|
||||
|
||||
/* Try the lock operation again */
|
||||
lock->fl.fl_flags |= FL_SLEEP;
|
||||
error = posix_lock_file(file->f_file, &lock->fl);
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
|
||||
switch (error) {
|
||||
case 0:
|
||||
break;
|
||||
case -EAGAIN:
|
||||
dprintk("lockd: lock still blocked\n");
|
||||
nlmsvc_insert_block(block, NLM_NEVER);
|
||||
return;
|
||||
default:
|
||||
printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
|
||||
-error, __FUNCTION__);
|
||||
nlmsvc_insert_block(block, 10 * HZ);
|
||||
return;
|
||||
}
|
||||
|
||||
callback:
|
||||
/* Lock was granted by VFS. */
|
||||
dprintk("lockd: GRANTing blocked lock.\n");
|
||||
block->b_granted = 1;
|
||||
|
||||
/* Schedule next grant callback in 30 seconds */
|
||||
nlmsvc_insert_block(block, 30 * HZ);
|
||||
|
||||
/* Call the client */
|
||||
kref_get(&block->b_count);
|
||||
nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the callback from the RPC layer when the NLM_GRANTED_MSG
|
||||
* RPC call has succeeded or timed out.
|
||||
* Like all RPC callbacks, it is invoked by the rpciod process, so it
|
||||
* better not sleep. Therefore, we put the blocked lock on the nlm_blocked
|
||||
* chain once more in order to have it removed by lockd itself (which can
|
||||
* then sleep on the file semaphore without disrupting e.g. the nfs client).
|
||||
*/
|
||||
static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
|
||||
{
|
||||
struct nlm_rqst *call = data;
|
||||
struct nlm_block *block = call->a_block;
|
||||
unsigned long timeout;
|
||||
|
||||
dprintk("lockd: GRANT_MSG RPC callback\n");
|
||||
|
||||
/* Technically, we should down the file semaphore here. Since we
|
||||
* move the block towards the head of the queue only, no harm
|
||||
* can be done, though. */
|
||||
if (task->tk_status < 0) {
|
||||
/* RPC error: Re-insert for retransmission */
|
||||
timeout = 10 * HZ;
|
||||
} else {
|
||||
/* Call was successful, now wait for client callback */
|
||||
timeout = 60 * HZ;
|
||||
}
|
||||
nlmsvc_insert_block(block, timeout);
|
||||
svc_wake_up(block->b_daemon);
|
||||
}
|
||||
|
||||
static void nlmsvc_grant_release(void *data)
|
||||
{
|
||||
struct nlm_rqst *call = data;
|
||||
|
||||
nlmsvc_release_block(call->a_block);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmsvc_grant_ops = {
|
||||
.rpc_call_done = nlmsvc_grant_callback,
|
||||
.rpc_release = nlmsvc_grant_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* We received a GRANT_RES callback. Try to find the corresponding
|
||||
* block.
|
||||
*/
|
||||
void
|
||||
nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
|
||||
{
|
||||
struct nlm_block *block;
|
||||
|
||||
dprintk("grant_reply: looking for cookie %x, s=%d \n",
|
||||
*(unsigned int *)(cookie->data), status);
|
||||
if (!(block = nlmsvc_find_block(cookie)))
|
||||
return;
|
||||
|
||||
if (block) {
|
||||
if (status == nlm_lck_denied_grace_period) {
|
||||
/* Try again in a couple of seconds */
|
||||
nlmsvc_insert_block(block, 10 * HZ);
|
||||
} else {
|
||||
/* Lock is now held by client, or has been rejected.
|
||||
* In both cases, the block should be removed. */
|
||||
nlmsvc_unlink_block(block);
|
||||
}
|
||||
}
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retry all blocked locks that have been notified. This is where lockd
|
||||
* picks up locks that can be granted, or grant notifications that must
|
||||
* be retransmitted.
|
||||
*/
|
||||
unsigned long
|
||||
nlmsvc_retry_blocked(void)
|
||||
{
|
||||
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
|
||||
struct nlm_block *block;
|
||||
|
||||
while (!list_empty(&nlm_blocked)) {
|
||||
block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
|
||||
|
||||
if (block->b_when == NLM_NEVER)
|
||||
break;
|
||||
if (time_after(block->b_when,jiffies)) {
|
||||
timeout = block->b_when - jiffies;
|
||||
break;
|
||||
}
|
||||
|
||||
dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
|
||||
block, block->b_when);
|
||||
kref_get(&block->b_count);
|
||||
nlmsvc_grant_blocked(block);
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
556
fs/lockd/svcproc.c
Normal file
556
fs/lockd/svcproc.c
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* linux/fs/lockd/svcproc.c
|
||||
*
|
||||
* Lockd server procedures. We don't implement the NLM_*_RES
|
||||
* procedures because we don't use the async procedures.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/nfsd/nfsd.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/share.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_CLIENT
|
||||
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
static __be32
|
||||
cast_to_nlm(__be32 status, u32 vers)
|
||||
{
|
||||
/* Note: status is assumed to be in network byte order !!! */
|
||||
if (vers != 4){
|
||||
switch (status) {
|
||||
case nlm_granted:
|
||||
case nlm_lck_denied:
|
||||
case nlm_lck_denied_nolocks:
|
||||
case nlm_lck_blocked:
|
||||
case nlm_lck_denied_grace_period:
|
||||
break;
|
||||
case nlm4_deadlock:
|
||||
status = nlm_lck_denied;
|
||||
break;
|
||||
default:
|
||||
status = nlm_lck_denied_nolocks;
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
#define cast_status(status) (cast_to_nlm(status, rqstp->rq_vers))
|
||||
#else
|
||||
#define cast_status(status) (status)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Obtain client and file from arguments
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_host **hostp, struct nlm_file **filp)
|
||||
{
|
||||
struct nlm_host *host = NULL;
|
||||
struct nlm_file *file = NULL;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
__be32 error = 0;
|
||||
|
||||
/* nfsd callbacks must have been installed for this procedure */
|
||||
if (!nlmsvc_ops)
|
||||
return nlm_lck_denied_nolocks;
|
||||
|
||||
/* Obtain host handle */
|
||||
if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
|
||||
|| (argp->monitor && nsm_monitor(host) < 0))
|
||||
goto no_locks;
|
||||
*hostp = host;
|
||||
|
||||
/* Obtain file pointer. Not used by FREE_ALL call. */
|
||||
if (filp != NULL) {
|
||||
if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
|
||||
goto no_locks;
|
||||
*filp = file;
|
||||
|
||||
/* Set up the missing parts of the file_lock structure */
|
||||
lock->fl.fl_file = file->f_file;
|
||||
lock->fl.fl_owner = (fl_owner_t) host;
|
||||
lock->fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
no_locks:
|
||||
if (host)
|
||||
nlm_release_host(host);
|
||||
if (error)
|
||||
return error;
|
||||
return nlm_lck_denied_nolocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* NULL: Test for presence of service
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
|
||||
{
|
||||
dprintk("lockd: NULL called\n");
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST: Check for conflicting lock
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: TEST called\n");
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept test requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = cast_status(nlmsvc_testlock(file, &argp->lock, &resp->lock));
|
||||
|
||||
dprintk("lockd: TEST status %d vers %d\n",
|
||||
ntohl(resp->status), rqstp->rq_vers);
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: LOCK called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period && !argp->reclaim) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
#if 0
|
||||
/* If supplied state doesn't match current state, we assume it's
|
||||
* an old request that time-warped somehow. Any error return would
|
||||
* do in this case because it's irrelevant anyway.
|
||||
*
|
||||
* NB: We don't retrieve the remote host's state yet.
|
||||
*/
|
||||
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
|
||||
resp->status = nlm_lck_denied_nolocks;
|
||||
} else
|
||||
#endif
|
||||
|
||||
/* Now try to lock the file */
|
||||
resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock,
|
||||
argp->block, &argp->cookie));
|
||||
|
||||
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: CANCEL called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Try to cancel request. */
|
||||
resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
|
||||
|
||||
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNLOCK: release a lock
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: UNLOCK called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to remove the lock */
|
||||
resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
|
||||
|
||||
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* GRANTED: A server calls us to tell that a process' lock request
|
||||
* was granted
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
dprintk("lockd: GRANTED called\n");
|
||||
resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
|
||||
dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the generic lockd callback for async RPC calls
|
||||
*/
|
||||
static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
|
||||
{
|
||||
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
|
||||
-task->tk_status);
|
||||
}
|
||||
|
||||
static void nlmsvc_callback_release(void *data)
|
||||
{
|
||||
nlm_release_call(data);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmsvc_callback_ops = {
|
||||
.rpc_call_done = nlmsvc_callback_exit,
|
||||
.rpc_release = nlmsvc_callback_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* `Async' versions of the above service routines. They aren't really,
|
||||
* because we send the callback before the reply proper. I hope this
|
||||
* doesn't break any clients.
|
||||
*/
|
||||
static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *argp,
|
||||
__be32 (*func)(struct svc_rqst *, struct nlm_args *, struct nlm_res *))
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_rqst *call;
|
||||
__be32 stat;
|
||||
|
||||
host = nlmsvc_lookup_host(rqstp,
|
||||
argp->lock.caller,
|
||||
argp->lock.len);
|
||||
if (host == NULL)
|
||||
return rpc_system_err;
|
||||
|
||||
call = nlm_alloc_call(host);
|
||||
if (call == NULL)
|
||||
return rpc_system_err;
|
||||
|
||||
stat = func(rqstp, argp, &call->a_res);
|
||||
if (stat != 0) {
|
||||
nlm_release_call(call);
|
||||
return stat;
|
||||
}
|
||||
|
||||
call->a_flags = RPC_TASK_ASYNC;
|
||||
if (nlm_async_reply(call, proc, &nlmsvc_callback_ops) < 0)
|
||||
return rpc_system_err;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: TEST_MSG called\n");
|
||||
return nlmsvc_callback(rqstp, NLMPROC_TEST_RES, argp, nlmsvc_proc_test);
|
||||
}
|
||||
|
||||
static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: LOCK_MSG called\n");
|
||||
return nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, argp, nlmsvc_proc_lock);
|
||||
}
|
||||
|
||||
static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: CANCEL_MSG called\n");
|
||||
return nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, argp, nlmsvc_proc_cancel);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: UNLOCK_MSG called\n");
|
||||
return nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, argp, nlmsvc_proc_unlock);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
dprintk("lockd: GRANTED_MSG called\n");
|
||||
return nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, argp, nlmsvc_proc_granted);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHARE: create a DOS share or alter existing share.
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: SHARE called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept new lock requests during grace period */
|
||||
if (nlmsvc_grace_period && !argp->reclaim) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to create the share */
|
||||
resp->status = cast_status(nlmsvc_share_file(host, file, argp));
|
||||
|
||||
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNSHARE: Release a DOS share.
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
|
||||
dprintk("lockd: UNSHARE called\n");
|
||||
|
||||
resp->cookie = argp->cookie;
|
||||
|
||||
/* Don't accept requests during grace period */
|
||||
if (nlmsvc_grace_period) {
|
||||
resp->status = nlm_lck_denied_grace_period;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/* Obtain client and file */
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
/* Now try to unshare the file */
|
||||
resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
|
||||
|
||||
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
|
||||
nlm_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* NM_LOCK: Create an unmonitored lock
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_res *resp)
|
||||
{
|
||||
dprintk("lockd: NM_LOCK called\n");
|
||||
|
||||
argp->monitor = 0; /* just clean the monitor flag */
|
||||
return nlmsvc_proc_lock(rqstp, argp, resp);
|
||||
}
|
||||
|
||||
/*
|
||||
* FREE_ALL: Release all locks and shares held by client
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
void *resp)
|
||||
{
|
||||
struct nlm_host *host;
|
||||
|
||||
/* Obtain client */
|
||||
if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL))
|
||||
return rpc_success;
|
||||
|
||||
nlmsvc_free_host_resources(host);
|
||||
nlm_release_host(host);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* SM_NOTIFY: private callback from statd (not part of official NLM proto)
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
|
||||
void *resp)
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
|
||||
memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
|
||||
|
||||
dprintk("lockd: SM_NOTIFY called\n");
|
||||
if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
|
||||
|| ntohs(saddr.sin_port) >= 1024) {
|
||||
char buf[RPC_MAX_ADDRBUFLEN];
|
||||
printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
|
||||
svc_print_addr(rqstp, buf, sizeof(buf)));
|
||||
return rpc_system_err;
|
||||
}
|
||||
|
||||
/* Obtain the host pointer for this NFS server and try to
|
||||
* reclaim all locks we hold on this server.
|
||||
*/
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sin_addr.s_addr = argp->addr;
|
||||
nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
|
||||
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* client sent a GRANTED_RES, let's remove the associated block
|
||||
*/
|
||||
static __be32
|
||||
nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
|
||||
void *resp)
|
||||
{
|
||||
if (!nlmsvc_ops)
|
||||
return rpc_success;
|
||||
|
||||
dprintk("lockd: GRANTED_RES called\n");
|
||||
|
||||
nlmsvc_grant_reply(&argp->cookie, argp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* NLM Server procedures.
|
||||
*/
|
||||
|
||||
#define nlmsvc_encode_norep nlmsvc_encode_void
|
||||
#define nlmsvc_decode_norep nlmsvc_decode_void
|
||||
#define nlmsvc_decode_testres nlmsvc_decode_void
|
||||
#define nlmsvc_decode_lockres nlmsvc_decode_void
|
||||
#define nlmsvc_decode_unlockres nlmsvc_decode_void
|
||||
#define nlmsvc_decode_cancelres nlmsvc_decode_void
|
||||
#define nlmsvc_decode_grantedres nlmsvc_decode_void
|
||||
|
||||
#define nlmsvc_proc_none nlmsvc_proc_null
|
||||
#define nlmsvc_proc_test_res nlmsvc_proc_null
|
||||
#define nlmsvc_proc_lock_res nlmsvc_proc_null
|
||||
#define nlmsvc_proc_cancel_res nlmsvc_proc_null
|
||||
#define nlmsvc_proc_unlock_res nlmsvc_proc_null
|
||||
|
||||
struct nlm_void { int dummy; };
|
||||
|
||||
#define PROC(name, xargt, xrest, argt, rest, respsize) \
|
||||
{ .pc_func = (svc_procfunc) nlmsvc_proc_##name, \
|
||||
.pc_decode = (kxdrproc_t) nlmsvc_decode_##xargt, \
|
||||
.pc_encode = (kxdrproc_t) nlmsvc_encode_##xrest, \
|
||||
.pc_release = NULL, \
|
||||
.pc_argsize = sizeof(struct nlm_##argt), \
|
||||
.pc_ressize = sizeof(struct nlm_##rest), \
|
||||
.pc_xdrressize = respsize, \
|
||||
}
|
||||
|
||||
#define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */
|
||||
#define St 1 /* status */
|
||||
#define No (1+1024/4) /* Net Obj */
|
||||
#define Rg 2 /* range - offset + size */
|
||||
|
||||
struct svc_procedure nlmsvc_procedures[] = {
|
||||
PROC(null, void, void, void, void, 1),
|
||||
PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg),
|
||||
PROC(lock, lockargs, res, args, res, Ck+St),
|
||||
PROC(cancel, cancargs, res, args, res, Ck+St),
|
||||
PROC(unlock, unlockargs, res, args, res, Ck+St),
|
||||
PROC(granted, testargs, res, args, res, Ck+St),
|
||||
PROC(test_msg, testargs, norep, args, void, 1),
|
||||
PROC(lock_msg, lockargs, norep, args, void, 1),
|
||||
PROC(cancel_msg, cancargs, norep, args, void, 1),
|
||||
PROC(unlock_msg, unlockargs, norep, args, void, 1),
|
||||
PROC(granted_msg, testargs, norep, args, void, 1),
|
||||
PROC(test_res, testres, norep, res, void, 1),
|
||||
PROC(lock_res, lockres, norep, res, void, 1),
|
||||
PROC(cancel_res, cancelres, norep, res, void, 1),
|
||||
PROC(unlock_res, unlockres, norep, res, void, 1),
|
||||
PROC(granted_res, res, norep, res, void, 1),
|
||||
/* statd callback */
|
||||
PROC(sm_notify, reboot, void, reboot, void, 1),
|
||||
PROC(none, void, void, void, void, 1),
|
||||
PROC(none, void, void, void, void, 1),
|
||||
PROC(none, void, void, void, void, 1),
|
||||
PROC(share, shareargs, shareres, args, res, Ck+St+1),
|
||||
PROC(unshare, shareargs, shareres, args, res, Ck+St+1),
|
||||
PROC(nm_lock, lockargs, res, args, res, Ck+St),
|
||||
PROC(free_all, notify, void, args, void, 0),
|
||||
|
||||
};
|
||||
105
fs/lockd/svcshare.c
Normal file
105
fs/lockd/svcshare.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* linux/fs/lockd/svcshare.c
|
||||
*
|
||||
* Management of DOS shares.
|
||||
*
|
||||
* Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/share.h>
|
||||
|
||||
static inline int
|
||||
nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh)
|
||||
{
|
||||
return share->s_owner.len == oh->len
|
||||
&& !memcmp(share->s_owner.data, oh->data, oh->len);
|
||||
}
|
||||
|
||||
__be32
|
||||
nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
|
||||
struct nlm_args *argp)
|
||||
{
|
||||
struct nlm_share *share;
|
||||
struct xdr_netobj *oh = &argp->lock.oh;
|
||||
u8 *ohdata;
|
||||
|
||||
for (share = file->f_shares; share; share = share->s_next) {
|
||||
if (share->s_host == host && nlm_cmp_owner(share, oh))
|
||||
goto update;
|
||||
if ((argp->fsm_access & share->s_mode)
|
||||
|| (argp->fsm_mode & share->s_access ))
|
||||
return nlm_lck_denied;
|
||||
}
|
||||
|
||||
share = kmalloc(sizeof(*share) + oh->len,
|
||||
GFP_KERNEL);
|
||||
if (share == NULL)
|
||||
return nlm_lck_denied_nolocks;
|
||||
|
||||
/* Copy owner handle */
|
||||
ohdata = (u8 *) (share + 1);
|
||||
memcpy(ohdata, oh->data, oh->len);
|
||||
|
||||
share->s_file = file;
|
||||
share->s_host = host;
|
||||
share->s_owner.data = ohdata;
|
||||
share->s_owner.len = oh->len;
|
||||
share->s_next = file->f_shares;
|
||||
file->f_shares = share;
|
||||
|
||||
update:
|
||||
share->s_access = argp->fsm_access;
|
||||
share->s_mode = argp->fsm_mode;
|
||||
return nlm_granted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a share.
|
||||
*/
|
||||
__be32
|
||||
nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
|
||||
struct nlm_args *argp)
|
||||
{
|
||||
struct nlm_share *share, **shpp;
|
||||
struct xdr_netobj *oh = &argp->lock.oh;
|
||||
|
||||
for (shpp = &file->f_shares; (share = *shpp) != 0; shpp = &share->s_next) {
|
||||
if (share->s_host == host && nlm_cmp_owner(share, oh)) {
|
||||
*shpp = share->s_next;
|
||||
kfree(share);
|
||||
return nlm_granted;
|
||||
}
|
||||
}
|
||||
|
||||
/* X/Open spec says return success even if there was no
|
||||
* corresponding share. */
|
||||
return nlm_granted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traverse all shares for a given file, and delete
|
||||
* those owned by the given (type of) host
|
||||
*/
|
||||
void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file,
|
||||
nlm_host_match_fn_t match)
|
||||
{
|
||||
struct nlm_share *share, **shpp;
|
||||
|
||||
shpp = &file->f_shares;
|
||||
while ((share = *shpp) != NULL) {
|
||||
if (match(share->s_host, host)) {
|
||||
*shpp = share->s_next;
|
||||
kfree(share);
|
||||
continue;
|
||||
}
|
||||
shpp = &share->s_next;
|
||||
}
|
||||
}
|
||||
372
fs/lockd/svcsubs.c
Normal file
372
fs/lockd/svcsubs.c
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* linux/fs/lockd/svcsubs.c
|
||||
*
|
||||
* Various support routines for the NLM server.
|
||||
*
|
||||
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/nfsd/nfsfh.h>
|
||||
#include <linux/nfsd/export.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/share.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_SVCSUBS
|
||||
|
||||
|
||||
/*
|
||||
* Global file hash table
|
||||
*/
|
||||
#define FILE_HASH_BITS 7
|
||||
#define FILE_NRHASH (1<<FILE_HASH_BITS)
|
||||
static struct hlist_head nlm_files[FILE_NRHASH];
|
||||
static DEFINE_MUTEX(nlm_file_mutex);
|
||||
|
||||
#ifdef NFSD_DEBUG
|
||||
static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
|
||||
{
|
||||
u32 *fhp = (u32*)f->data;
|
||||
|
||||
/* print the first 32 bytes of the fh */
|
||||
dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
|
||||
msg, fhp[0], fhp[1], fhp[2], fhp[3],
|
||||
fhp[4], fhp[5], fhp[6], fhp[7]);
|
||||
}
|
||||
|
||||
static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
|
||||
{
|
||||
struct inode *inode = file->f_file->f_path.dentry->d_inode;
|
||||
|
||||
dprintk("lockd: %s %s/%ld\n",
|
||||
msg, inode->i_sb->s_id, inode->i_ino);
|
||||
}
|
||||
#else
|
||||
static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline unsigned int file_hash(struct nfs_fh *f)
|
||||
{
|
||||
unsigned int tmp=0;
|
||||
int i;
|
||||
for (i=0; i<NFS2_FHSIZE;i++)
|
||||
tmp += f->data[i];
|
||||
return tmp & (FILE_NRHASH - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup file info. If it doesn't exist, create a file info struct
|
||||
* and open a (VFS) file for the given inode.
|
||||
*
|
||||
* FIXME:
|
||||
* Note that we open the file O_RDONLY even when creating write locks.
|
||||
* This is not quite right, but for now, we assume the client performs
|
||||
* the proper R/W checking.
|
||||
*/
|
||||
__be32
|
||||
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
|
||||
struct nfs_fh *f)
|
||||
{
|
||||
struct hlist_node *pos;
|
||||
struct nlm_file *file;
|
||||
unsigned int hash;
|
||||
__be32 nfserr;
|
||||
|
||||
nlm_debug_print_fh("nlm_file_lookup", f);
|
||||
|
||||
hash = file_hash(f);
|
||||
|
||||
/* Lock file table */
|
||||
mutex_lock(&nlm_file_mutex);
|
||||
|
||||
hlist_for_each_entry(file, pos, &nlm_files[hash], f_list)
|
||||
if (!nfs_compare_fh(&file->f_handle, f))
|
||||
goto found;
|
||||
|
||||
nlm_debug_print_fh("creating file for", f);
|
||||
|
||||
nfserr = nlm_lck_denied_nolocks;
|
||||
file = kzalloc(sizeof(*file), GFP_KERNEL);
|
||||
if (!file)
|
||||
goto out_unlock;
|
||||
|
||||
memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
|
||||
mutex_init(&file->f_mutex);
|
||||
INIT_HLIST_NODE(&file->f_list);
|
||||
INIT_LIST_HEAD(&file->f_blocks);
|
||||
|
||||
/* Open the file. Note that this must not sleep for too long, else
|
||||
* we would lock up lockd:-) So no NFS re-exports, folks.
|
||||
*
|
||||
* We have to make sure we have the right credential to open
|
||||
* the file.
|
||||
*/
|
||||
if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
|
||||
dprintk("lockd: open failed (error %d)\n", nfserr);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
hlist_add_head(&file->f_list, &nlm_files[hash]);
|
||||
|
||||
found:
|
||||
dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
|
||||
*result = file;
|
||||
file->f_count++;
|
||||
nfserr = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&nlm_file_mutex);
|
||||
return nfserr;
|
||||
|
||||
out_free:
|
||||
kfree(file);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a file after having released all locks, blocks and shares
|
||||
*/
|
||||
static inline void
|
||||
nlm_delete_file(struct nlm_file *file)
|
||||
{
|
||||
nlm_debug_print_file("closing file", file);
|
||||
if (!hlist_unhashed(&file->f_list)) {
|
||||
hlist_del(&file->f_list);
|
||||
nlmsvc_ops->fclose(file->f_file);
|
||||
kfree(file);
|
||||
} else {
|
||||
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all locks on the given file and perform the specified
|
||||
* action.
|
||||
*/
|
||||
static int
|
||||
nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
nlm_host_match_fn_t match)
|
||||
{
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
struct file_lock *fl;
|
||||
struct nlm_host *lockhost;
|
||||
|
||||
again:
|
||||
file->f_locks = 0;
|
||||
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
|
||||
if (fl->fl_lmops != &nlmsvc_lock_operations)
|
||||
continue;
|
||||
|
||||
/* update current lock count */
|
||||
file->f_locks++;
|
||||
|
||||
lockhost = (struct nlm_host *) fl->fl_owner;
|
||||
if (match(lockhost, host)) {
|
||||
struct file_lock lock = *fl;
|
||||
|
||||
lock.fl_type = F_UNLCK;
|
||||
lock.fl_start = 0;
|
||||
lock.fl_end = OFFSET_MAX;
|
||||
if (posix_lock_file(file->f_file, &lock) < 0) {
|
||||
printk("lockd: unlock failure in %s:%d\n",
|
||||
__FILE__, __LINE__);
|
||||
return 1;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inspect a single file
|
||||
*/
|
||||
static inline int
|
||||
nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
|
||||
{
|
||||
nlmsvc_traverse_blocks(host, file, match);
|
||||
nlmsvc_traverse_shares(host, file, match);
|
||||
return nlm_traverse_locks(host, file, match);
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick check whether there are still any locks, blocks or
|
||||
* shares on a given file.
|
||||
*/
|
||||
static inline int
|
||||
nlm_file_inuse(struct nlm_file *file)
|
||||
{
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
struct file_lock *fl;
|
||||
|
||||
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
|
||||
return 1;
|
||||
|
||||
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
|
||||
if (fl->fl_lmops == &nlmsvc_lock_operations)
|
||||
return 1;
|
||||
}
|
||||
file->f_locks = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all files in the file table.
|
||||
*/
|
||||
static int
|
||||
nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match)
|
||||
{
|
||||
struct hlist_node *pos, *next;
|
||||
struct nlm_file *file;
|
||||
int i, ret = 0;
|
||||
|
||||
mutex_lock(&nlm_file_mutex);
|
||||
for (i = 0; i < FILE_NRHASH; i++) {
|
||||
hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) {
|
||||
file->f_count++;
|
||||
mutex_unlock(&nlm_file_mutex);
|
||||
|
||||
/* Traverse locks, blocks and shares of this file
|
||||
* and update file->f_locks count */
|
||||
if (nlm_inspect_file(host, file, match))
|
||||
ret = 1;
|
||||
|
||||
mutex_lock(&nlm_file_mutex);
|
||||
file->f_count--;
|
||||
/* No more references to this file. Let go of it. */
|
||||
if (list_empty(&file->f_blocks) && !file->f_locks
|
||||
&& !file->f_shares && !file->f_count) {
|
||||
hlist_del(&file->f_list);
|
||||
nlmsvc_ops->fclose(file->f_file);
|
||||
kfree(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&nlm_file_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release file. If there are no more remote locks on this file,
|
||||
* close it and free the handle.
|
||||
*
|
||||
* Note that we can't do proper reference counting without major
|
||||
* contortions because the code in fs/locks.c creates, deletes and
|
||||
* splits locks without notification. Our only way is to walk the
|
||||
* entire lock list each time we remove a lock.
|
||||
*/
|
||||
void
|
||||
nlm_release_file(struct nlm_file *file)
|
||||
{
|
||||
dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
|
||||
file, file->f_count);
|
||||
|
||||
/* Lock file table */
|
||||
mutex_lock(&nlm_file_mutex);
|
||||
|
||||
/* If there are no more locks etc, delete the file */
|
||||
if (--file->f_count == 0 && !nlm_file_inuse(file))
|
||||
nlm_delete_file(file);
|
||||
|
||||
mutex_unlock(&nlm_file_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers function for resource traversal
|
||||
*
|
||||
* nlmsvc_mark_host:
|
||||
* used by the garbage collector; simply sets h_inuse.
|
||||
* Always returns 0.
|
||||
*
|
||||
* nlmsvc_same_host:
|
||||
* returns 1 iff the two hosts match. Used to release
|
||||
* all resources bound to a specific host.
|
||||
*
|
||||
* nlmsvc_is_client:
|
||||
* returns 1 iff the host is a client.
|
||||
* Used by nlmsvc_invalidate_all
|
||||
*/
|
||||
static int
|
||||
nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy)
|
||||
{
|
||||
host->h_inuse = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other)
|
||||
{
|
||||
return host == other;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy)
|
||||
{
|
||||
if (host->h_server) {
|
||||
/* we are destroying locks even though the client
|
||||
* hasn't asked us too, so don't unmonitor the
|
||||
* client
|
||||
*/
|
||||
if (host->h_nsmhandle)
|
||||
host->h_nsmhandle->sm_sticky = 1;
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark all hosts that still hold resources
|
||||
*/
|
||||
void
|
||||
nlmsvc_mark_resources(void)
|
||||
{
|
||||
dprintk("lockd: nlmsvc_mark_resources\n");
|
||||
nlm_traverse_files(NULL, nlmsvc_mark_host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release all resources held by the given client
|
||||
*/
|
||||
void
|
||||
nlmsvc_free_host_resources(struct nlm_host *host)
|
||||
{
|
||||
dprintk("lockd: nlmsvc_free_host_resources\n");
|
||||
|
||||
if (nlm_traverse_files(host, nlmsvc_same_host)) {
|
||||
printk(KERN_WARNING
|
||||
"lockd: couldn't remove all locks held by %s\n",
|
||||
host->h_name);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all locks held for clients
|
||||
*/
|
||||
void
|
||||
nlmsvc_invalidate_all(void)
|
||||
{
|
||||
/* Release all locks held by NFS clients.
|
||||
* Previously, the code would call
|
||||
* nlmsvc_free_host_resources for each client in
|
||||
* turn, which is about as inefficient as it gets.
|
||||
* Now we just do it once in nlm_traverse_files.
|
||||
*/
|
||||
nlm_traverse_files(NULL, nlmsvc_is_client);
|
||||
}
|
||||
639
fs/lockd/xdr.c
Normal file
639
fs/lockd/xdr.c
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* linux/fs/lockd/xdr.c
|
||||
*
|
||||
* XDR support for lockd and the lock client.
|
||||
*
|
||||
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/nfs.h>
|
||||
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_XDR
|
||||
|
||||
|
||||
static inline loff_t
|
||||
s32_to_loff_t(__s32 offset)
|
||||
{
|
||||
return (loff_t)offset;
|
||||
}
|
||||
|
||||
static inline __s32
|
||||
loff_t_to_s32(loff_t offset)
|
||||
{
|
||||
__s32 res;
|
||||
if (offset >= NLM_OFFSET_MAX)
|
||||
res = NLM_OFFSET_MAX;
|
||||
else if (offset <= -NLM_OFFSET_MAX)
|
||||
res = -NLM_OFFSET_MAX;
|
||||
else
|
||||
res = offset;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR functions for basic NLM types
|
||||
*/
|
||||
static __be32 *nlm_decode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = ntohl(*p++);
|
||||
|
||||
if(len==0)
|
||||
{
|
||||
c->len=4;
|
||||
memset(c->data, 0, 4); /* hockeypux brain damage */
|
||||
}
|
||||
else if(len<=NLM_MAXCOOKIELEN)
|
||||
{
|
||||
c->len=len;
|
||||
memcpy(c->data, p, len);
|
||||
p+=XDR_QUADLEN(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_NOTICE
|
||||
"lockd: bad cookie size %d (only cookies under %d bytes are supported.)\n", len, NLM_MAXCOOKIELEN);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline __be32 *
|
||||
nlm_encode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
*p++ = htonl(c->len);
|
||||
memcpy(p, c->data, c->len);
|
||||
p+=XDR_QUADLEN(c->len);
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm_decode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
|
||||
printk(KERN_NOTICE
|
||||
"lockd: bad fhandle size %d (should be %d)\n",
|
||||
len, NFS2_FHSIZE);
|
||||
return NULL;
|
||||
}
|
||||
f->size = NFS2_FHSIZE;
|
||||
memset(f->data, 0, sizeof(f->data));
|
||||
memcpy(f->data, p, NFS2_FHSIZE);
|
||||
return p + XDR_QUADLEN(NFS2_FHSIZE);
|
||||
}
|
||||
|
||||
static inline __be32 *
|
||||
nlm_encode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
*p++ = htonl(NFS2_FHSIZE);
|
||||
memcpy(p, f->data, NFS2_FHSIZE);
|
||||
return p + XDR_QUADLEN(NFS2_FHSIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode and decode owner handle
|
||||
*/
|
||||
static inline __be32 *
|
||||
nlm_decode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_decode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static inline __be32 *
|
||||
nlm_encode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_encode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
s32 start, len, end;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len,
|
||||
NLM_MAXSTRLEN))
|
||||
|| !(p = nlm_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
lock->svid = ntohl(*p++);
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->fl_owner = current->files;
|
||||
fl->fl_pid = (pid_t)lock->svid;
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = F_RDLCK; /* as good as anything else */
|
||||
start = ntohl(*p++);
|
||||
len = ntohl(*p++);
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s32_to_loff_t(start);
|
||||
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s32_to_loff_t(end);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a lock as part of an NLM call
|
||||
*/
|
||||
static __be32 *
|
||||
nlm_encode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
__s32 start, len;
|
||||
|
||||
if (!(p = xdr_encode_string(p, lock->caller))
|
||||
|| !(p = nlm_encode_fh(p, &lock->fh))
|
||||
|| !(p = nlm_encode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
|
||||
if (fl->fl_start > NLM_OFFSET_MAX
|
||||
|| (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
|
||||
return NULL;
|
||||
|
||||
start = loff_t_to_s32(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
|
||||
|
||||
*p++ = htonl(lock->svid);
|
||||
*p++ = htonl(start);
|
||||
*p++ = htonl(len);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode result of a TEST/TEST_MSG call
|
||||
*/
|
||||
static __be32 *
|
||||
nlm_encode_testres(__be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
s32 start, len;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return NULL;
|
||||
*p++ = resp->status;
|
||||
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
|
||||
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
|
||||
*p++ = htonl(resp->lock.svid);
|
||||
|
||||
/* Encode owner handle. */
|
||||
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
|
||||
return NULL;
|
||||
|
||||
start = loff_t_to_s32(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
|
||||
|
||||
*p++ = htonl(start);
|
||||
*p++ = htonl(len);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* First, the server side XDR functions
|
||||
*/
|
||||
int
|
||||
nlmsvc_decode_testargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_testres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_encode_testres(p, resp)))
|
||||
return 0;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
argp->reclaim = ntohl(*p++);
|
||||
argp->state = ntohl(*p++);
|
||||
argp->monitor = 1; /* monitor client by default */
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
argp->lock.fl.fl_type = F_UNLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
memset(lock, 0, sizeof(*lock));
|
||||
locks_init_lock(&lock->fl);
|
||||
lock->svid = ~(u32) 0;
|
||||
lock->fl.fl_pid = (pid_t)lock->svid;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
||||
return 0;
|
||||
argp->fsm_mode = ntohl(*p++);
|
||||
argp->fsm_access = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_shareres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
*p++ = xdr_zero; /* sequence argument */
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_notify(struct svc_rqst *rqstp, __be32 *p, struct nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
|
||||
{
|
||||
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
/* Preserve the address in network byte order */
|
||||
argp->addr = *p++;
|
||||
argp->vers = *p++;
|
||||
argp->proto = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
resp->status = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
||||
{
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, the client side XDR functions
|
||||
*/
|
||||
#ifdef NLMCLNT_SUPPORT_SHARES
|
||||
static int
|
||||
nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
nlmclt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
resp->status = *p++;
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
u32 excl;
|
||||
s32 start, len, end;
|
||||
|
||||
memset(&resp->lock, 0, sizeof(resp->lock));
|
||||
locks_init_lock(fl);
|
||||
excl = ntohl(*p++);
|
||||
resp->lock.svid = ntohl(*p++);
|
||||
fl->fl_pid = (pid_t)resp->lock.svid;
|
||||
if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
|
||||
return -EIO;
|
||||
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = excl? F_WRLCK : F_RDLCK;
|
||||
start = ntohl(*p++);
|
||||
len = ntohl(*p++);
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s32_to_loff_t(start);
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s32_to_loff_t(end);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nlmclt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = argp->block? xdr_one : xdr_zero;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
*p++ = argp->reclaim? xdr_one : xdr_zero;
|
||||
*p++ = htonl(argp->state);
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = argp->block? xdr_one : xdr_zero;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
if (!(p = nlm_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = resp->status;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_encode_testres(p, resp)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
resp->status = *p++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer requirements for NLM
|
||||
*/
|
||||
#define NLM_void_sz 0
|
||||
#define NLM_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
|
||||
#define NLM_caller_sz 1+XDR_QUADLEN(sizeof(utsname()->nodename))
|
||||
#define NLM_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ)
|
||||
/* #define NLM_owner_sz 1+XDR_QUADLEN(NLM_MAXOWNER) */
|
||||
#define NLM_fhandle_sz 1+XDR_QUADLEN(NFS2_FHSIZE)
|
||||
#define NLM_lock_sz 3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
|
||||
#define NLM_holder_sz 4+NLM_netobj_sz
|
||||
|
||||
#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz
|
||||
#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz
|
||||
#define NLM_cancargs_sz NLM_cookie_sz+2+NLM_lock_sz
|
||||
#define NLM_unlockargs_sz NLM_cookie_sz+NLM_lock_sz
|
||||
|
||||
#define NLM_testres_sz NLM_cookie_sz+1+NLM_holder_sz
|
||||
#define NLM_res_sz NLM_cookie_sz+1
|
||||
#define NLM_norep_sz 0
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(a, b) (((a) > (b))? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For NLM, a void procedure really returns nothing
|
||||
*/
|
||||
#define nlmclt_decode_norep NULL
|
||||
|
||||
#define PROC(proc, argtype, restype) \
|
||||
[NLMPROC_##proc] = { \
|
||||
.p_proc = NLMPROC_##proc, \
|
||||
.p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \
|
||||
.p_decode = (kxdrproc_t) nlmclt_decode_##restype, \
|
||||
.p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2, \
|
||||
.p_statidx = NLMPROC_##proc, \
|
||||
.p_name = #proc, \
|
||||
}
|
||||
|
||||
static struct rpc_procinfo nlm_procedures[] = {
|
||||
PROC(TEST, testargs, testres),
|
||||
PROC(LOCK, lockargs, res),
|
||||
PROC(CANCEL, cancargs, res),
|
||||
PROC(UNLOCK, unlockargs, res),
|
||||
PROC(GRANTED, testargs, res),
|
||||
PROC(TEST_MSG, testargs, norep),
|
||||
PROC(LOCK_MSG, lockargs, norep),
|
||||
PROC(CANCEL_MSG, cancargs, norep),
|
||||
PROC(UNLOCK_MSG, unlockargs, norep),
|
||||
PROC(GRANTED_MSG, testargs, norep),
|
||||
PROC(TEST_RES, testres, norep),
|
||||
PROC(LOCK_RES, res, norep),
|
||||
PROC(CANCEL_RES, res, norep),
|
||||
PROC(UNLOCK_RES, res, norep),
|
||||
PROC(GRANTED_RES, res, norep),
|
||||
#ifdef NLMCLNT_SUPPORT_SHARES
|
||||
PROC(SHARE, shareargs, shareres),
|
||||
PROC(UNSHARE, shareargs, shareres),
|
||||
PROC(NM_LOCK, lockargs, res),
|
||||
PROC(FREE_ALL, notify, void),
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rpc_version nlm_version1 = {
|
||||
.number = 1,
|
||||
.nrprocs = 16,
|
||||
.procs = nlm_procedures,
|
||||
};
|
||||
|
||||
static struct rpc_version nlm_version3 = {
|
||||
.number = 3,
|
||||
.nrprocs = 24,
|
||||
.procs = nlm_procedures,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
extern struct rpc_version nlm_version4;
|
||||
#endif
|
||||
|
||||
static struct rpc_version * nlm_versions[] = {
|
||||
[1] = &nlm_version1,
|
||||
[3] = &nlm_version3,
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
[4] = &nlm_version4,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rpc_stat nlm_stats;
|
||||
|
||||
struct rpc_program nlm_program = {
|
||||
.name = "lockd",
|
||||
.number = NLM_PROGRAM,
|
||||
.nrvers = ARRAY_SIZE(nlm_versions),
|
||||
.version = nlm_versions,
|
||||
.stats = &nlm_stats,
|
||||
};
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
|
||||
{
|
||||
/*
|
||||
* We can get away with a static buffer because we're only
|
||||
* called with BKL held.
|
||||
*/
|
||||
static char buf[2*NLM_MAXCOOKIELEN+1];
|
||||
int i;
|
||||
int len = sizeof(buf);
|
||||
char *p = buf;
|
||||
|
||||
len--; /* allow for trailing \0 */
|
||||
if (len < 3)
|
||||
return "???";
|
||||
for (i = 0 ; i < cookie->len ; i++) {
|
||||
if (len < 2) {
|
||||
strcpy(p-3, "...");
|
||||
break;
|
||||
}
|
||||
sprintf(p, "%02x", cookie->data[i]);
|
||||
p += 2;
|
||||
len -= 2;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
587
fs/lockd/xdr4.c
Normal file
587
fs/lockd/xdr4.c
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* linux/fs/lockd/xdr4.c
|
||||
*
|
||||
* XDR support for lockd and the lock client.
|
||||
*
|
||||
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
|
||||
* Copyright (C) 1999, Trond Myklebust <trond.myklebust@fys.uio.no>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/nfs.h>
|
||||
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/lockd/sm_inter.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_XDR
|
||||
|
||||
static inline loff_t
|
||||
s64_to_loff_t(__s64 offset)
|
||||
{
|
||||
return (loff_t)offset;
|
||||
}
|
||||
|
||||
|
||||
static inline s64
|
||||
loff_t_to_s64(loff_t offset)
|
||||
{
|
||||
s64 res;
|
||||
if (offset > NLM4_OFFSET_MAX)
|
||||
res = NLM4_OFFSET_MAX;
|
||||
else if (offset < -NLM4_OFFSET_MAX)
|
||||
res = -NLM4_OFFSET_MAX;
|
||||
else
|
||||
res = offset;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR functions for basic NLM types
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_decode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = ntohl(*p++);
|
||||
|
||||
if(len==0)
|
||||
{
|
||||
c->len=4;
|
||||
memset(c->data, 0, 4); /* hockeypux brain damage */
|
||||
}
|
||||
else if(len<=NLM_MAXCOOKIELEN)
|
||||
{
|
||||
c->len=len;
|
||||
memcpy(c->data, p, len);
|
||||
p+=XDR_QUADLEN(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_NOTICE
|
||||
"lockd: bad cookie size %d (only cookies under %d bytes are supported.)\n", len, NLM_MAXCOOKIELEN);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_encode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
*p++ = htonl(c->len);
|
||||
memcpy(p, c->data, c->len);
|
||||
p+=XDR_QUADLEN(c->len);
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
memset(f->data, 0, sizeof(f->data));
|
||||
f->size = ntohl(*p++);
|
||||
if (f->size > NFS_MAXFHSIZE) {
|
||||
printk(KERN_NOTICE
|
||||
"lockd: bad fhandle size %d (should be <=%d)\n",
|
||||
f->size, NFS_MAXFHSIZE);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(f->data, p, f->size);
|
||||
return p + XDR_QUADLEN(f->size);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_encode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
*p++ = htonl(f->size);
|
||||
if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */
|
||||
memcpy(p, f->data, f->size);
|
||||
return p + XDR_QUADLEN(f->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode and decode owner handle
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_decode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_encode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_encode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
__s64 len, start, end;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm4_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm4_decode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
lock->svid = ntohl(*p++);
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->fl_owner = current->files;
|
||||
fl->fl_pid = (pid_t)lock->svid;
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = F_RDLCK; /* as good as anything else */
|
||||
p = xdr_decode_hyper(p, &start);
|
||||
p = xdr_decode_hyper(p, &len);
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s64_to_loff_t(start);
|
||||
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s64_to_loff_t(end);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a lock as part of an NLM call
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_encode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
__s64 start, len;
|
||||
|
||||
if (!(p = xdr_encode_string(p, lock->caller))
|
||||
|| !(p = nlm4_encode_fh(p, &lock->fh))
|
||||
|| !(p = nlm4_encode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
|
||||
if (fl->fl_start > NLM4_OFFSET_MAX
|
||||
|| (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
|
||||
return NULL;
|
||||
|
||||
*p++ = htonl(lock->svid);
|
||||
|
||||
start = loff_t_to_s64(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
|
||||
|
||||
p = xdr_encode_hyper(p, start);
|
||||
p = xdr_encode_hyper(p, len);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode result of a TEST/TEST_MSG call
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_encode_testres(__be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
s64 start, len;
|
||||
|
||||
dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return NULL;
|
||||
*p++ = resp->status;
|
||||
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
|
||||
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
|
||||
*p++ = htonl(resp->lock.svid);
|
||||
|
||||
/* Encode owner handle. */
|
||||
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
|
||||
return NULL;
|
||||
|
||||
start = loff_t_to_s64(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
|
||||
|
||||
p = xdr_encode_hyper(p, start);
|
||||
p = xdr_encode_hyper(p, len);
|
||||
dprintk("xdr: encode_testres (status %u pid %d type %d start %Ld end %Ld)\n",
|
||||
resp->status, (int)resp->lock.svid, fl->fl_type,
|
||||
(long long)fl->fl_start, (long long)fl->fl_end);
|
||||
}
|
||||
|
||||
dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* First, the server side XDR functions
|
||||
*/
|
||||
int
|
||||
nlm4svc_decode_testargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_testres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_encode_testres(p, resp)))
|
||||
return 0;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
argp->reclaim = ntohl(*p++);
|
||||
argp->state = ntohl(*p++);
|
||||
argp->monitor = 1; /* monitor client by default */
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
argp->lock.fl.fl_type = F_UNLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
memset(lock, 0, sizeof(*lock));
|
||||
locks_init_lock(&lock->fl);
|
||||
lock->svid = ~(u32) 0;
|
||||
lock->fl.fl_pid = (pid_t)lock->svid;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm4_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm4_decode_oh(p, &lock->oh)))
|
||||
return 0;
|
||||
argp->fsm_mode = ntohl(*p++);
|
||||
argp->fsm_access = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_shareres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
*p++ = xdr_zero; /* sequence argument */
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_notify(struct svc_rqst *rqstp, __be32 *p, struct nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
|
||||
{
|
||||
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
/* Preserve the address in network byte order */
|
||||
argp->addr = *p++;
|
||||
argp->vers = *p++;
|
||||
argp->proto = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
resp->status = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
||||
{
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, the client side XDR functions
|
||||
*/
|
||||
#ifdef NLMCLNT_SUPPORT_SHARES
|
||||
static int
|
||||
nlm4clt_decode_void(struct rpc_rqst *req, __be32 *p, void *ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm4_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
resp->status = *p++;
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
u32 excl;
|
||||
s64 start, end, len;
|
||||
|
||||
memset(&resp->lock, 0, sizeof(resp->lock));
|
||||
locks_init_lock(fl);
|
||||
excl = ntohl(*p++);
|
||||
resp->lock.svid = ntohl(*p++);
|
||||
fl->fl_pid = (pid_t)resp->lock.svid;
|
||||
if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
|
||||
return -EIO;
|
||||
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = excl? F_WRLCK : F_RDLCK;
|
||||
p = xdr_decode_hyper(p, &start);
|
||||
p = xdr_decode_hyper(p, &len);
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s64_to_loff_t(start);
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s64_to_loff_t(end);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nlm4clt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = argp->block? xdr_one : xdr_zero;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm4_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
*p++ = argp->reclaim? xdr_one : xdr_zero;
|
||||
*p++ = htonl(argp->state);
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = argp->block? xdr_one : xdr_zero;
|
||||
*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
|
||||
if (!(p = nlm4_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
|
||||
{
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
|
||||
return -EIO;
|
||||
if (!(p = nlm4_encode_lock(p, lock)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
*p++ = resp->status;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_encode_testres(p, resp)))
|
||||
return -EIO;
|
||||
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
|
||||
{
|
||||
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
|
||||
return -EIO;
|
||||
resp->status = *p++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer requirements for NLM
|
||||
*/
|
||||
#define NLM4_void_sz 0
|
||||
#define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
|
||||
#define NLM4_caller_sz 1+XDR_QUADLEN(NLM_MAXSTRLEN)
|
||||
#define NLM4_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ)
|
||||
/* #define NLM4_owner_sz 1+XDR_QUADLEN(NLM4_MAXOWNER) */
|
||||
#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE)
|
||||
#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz
|
||||
#define NLM4_holder_sz 6+NLM4_netobj_sz
|
||||
|
||||
#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz
|
||||
#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz
|
||||
#define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz
|
||||
#define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz
|
||||
|
||||
#define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz
|
||||
#define NLM4_res_sz NLM4_cookie_sz+1
|
||||
#define NLM4_norep_sz 0
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(a,b) (((a) > (b))? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For NLM, a void procedure really returns nothing
|
||||
*/
|
||||
#define nlm4clt_decode_norep NULL
|
||||
|
||||
#define PROC(proc, argtype, restype) \
|
||||
[NLMPROC_##proc] = { \
|
||||
.p_proc = NLMPROC_##proc, \
|
||||
.p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \
|
||||
.p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \
|
||||
.p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2, \
|
||||
.p_statidx = NLMPROC_##proc, \
|
||||
.p_name = #proc, \
|
||||
}
|
||||
|
||||
static struct rpc_procinfo nlm4_procedures[] = {
|
||||
PROC(TEST, testargs, testres),
|
||||
PROC(LOCK, lockargs, res),
|
||||
PROC(CANCEL, cancargs, res),
|
||||
PROC(UNLOCK, unlockargs, res),
|
||||
PROC(GRANTED, testargs, res),
|
||||
PROC(TEST_MSG, testargs, norep),
|
||||
PROC(LOCK_MSG, lockargs, norep),
|
||||
PROC(CANCEL_MSG, cancargs, norep),
|
||||
PROC(UNLOCK_MSG, unlockargs, norep),
|
||||
PROC(GRANTED_MSG, testargs, norep),
|
||||
PROC(TEST_RES, testres, norep),
|
||||
PROC(LOCK_RES, res, norep),
|
||||
PROC(CANCEL_RES, res, norep),
|
||||
PROC(UNLOCK_RES, res, norep),
|
||||
PROC(GRANTED_RES, res, norep),
|
||||
#ifdef NLMCLNT_SUPPORT_SHARES
|
||||
PROC(SHARE, shareargs, shareres),
|
||||
PROC(UNSHARE, shareargs, shareres),
|
||||
PROC(NM_LOCK, lockargs, res),
|
||||
PROC(FREE_ALL, notify, void),
|
||||
#endif
|
||||
};
|
||||
|
||||
struct rpc_version nlm_version4 = {
|
||||
.number = 4,
|
||||
.nrprocs = 24,
|
||||
.procs = nlm4_procedures,
|
||||
};
|
||||
Reference in New Issue
Block a user