Files
linux-stable-mirror/fs/lockd/svc4proc.c
T
NeilBrownandChuck Lever 0b47424032 lockd: fix TEST handling when not all permissions are available.
The F_GETLK fcntl can work with either read access or write access or
both.  It can query F_RDLCK and F_WRLCK locks in either case.

However lockd currently treats F_GETLK similar to F_SETLK in that read
access is required to query an F_RDLCK lock and write access is required
to query a F_WRLCK lock.

This is wrong and can cause problems - e.g.  when qemu accesses a
read-only (e.g. iso) filesystem image over NFS (though why it queries
if it can get a write lock - I don't know.  But it does, and this works
with local filesystems).

So we need TEST requests to be handled differently.  To do this:

- change nlm_do_fopen() to accept O_RDWR as a mode and in that case
  succeed if either a O_RDONLY or O_WRONLY file can be opened.
- change nlm_lookup_file() to accept a mode argument from caller,
  instead of deducing base on lock time, and pass that on to nlm_do_fopen()
- change nlm4svc_retrieve_args() and nlmsvc_retrieve_args() to detect
  TEST requests and pass O_RDWR as a mode to nlm_lookup_file, passing
  the same mode as before for other requests.  Also set
   lock->fl.c.flc_file to whichever file is available for TEST requests.
- change nlmsvc_testlock() to also not calculate the mode, but to use
  whatever was stored in lock->fl.c.flc_file.

This behaviour of lockd - requesting O_WRONLY access to TEST for
exclusive locks - has been present at least since git history began.
However it was hidden until recently because knfsd ignored the access
requested by lockd and required only READ access for all locking
requests (unless the underlying filesystem provided an f_op->open
function which checked access permissions).

The commit mentioned in Fixes: below changed nfsd_permission() to NOT
override the access request for LOCK requests and this exposed the bug
that we are now fixing.

Note that there is another issue that this patch does not address.
The flock(.., LOCK_EX) call is permitted on a read-only file descriptor.
Linux NFS maps this to NLM locking as whole-file byte-range locks.
nfsd will see this as though it were fcntl( F_SETLK (F_WRLCK)) and will
now require write access, which it might not be able to get.
It is not clear if this is a problem in practice, or what the best
solution might be.  So no attempt is made to address it.

Reported-by: Tj <tj.iam.tj@proton.me>
Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128861
Fixes: 4cc9b9f2bf ("nfsd: refine and rename NFSD_MAY_LOCK")
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2026-05-21 17:08:47 -04:00

1430 lines
40 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* 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/sunrpc/svc_xprt.h>
#include "lockd.h"
/*
* xdr.h defines SM_MAXSTRLEN and SM_PRIV_SIZE as macros.
* nlm4xdr_gen.h defines them as enum constants. Undefine the
* macros to allow the xdrgen enum definitions to be used.
*/
#undef SM_MAXSTRLEN
#undef SM_PRIV_SIZE
#include "share.h"
#include "nlm4xdr_gen.h"
/*
* Wrapper structures combine xdrgen types with legacy nlm_lock.
* The xdrgen field must be first so the structure can be cast
* to its XDR type for the RPC dispatch layer.
*/
struct nlm4_testargs_wrapper {
struct nlm4_testargs xdrgen;
struct nlm_lock lock;
};
static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0);
struct nlm4_lockargs_wrapper {
struct nlm4_lockargs xdrgen;
struct nlm_cookie cookie;
struct nlm_lock lock;
};
static_assert(offsetof(struct nlm4_lockargs_wrapper, xdrgen) == 0);
struct nlm4_cancargs_wrapper {
struct nlm4_cancargs xdrgen;
struct nlm_lock lock;
};
static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0);
struct nlm4_unlockargs_wrapper {
struct nlm4_unlockargs xdrgen;
struct nlm_lock lock;
};
static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0);
struct nlm4_notifyargs_wrapper {
struct nlm4_notifyargs xdrgen;
struct nlm_reboot reboot;
};
static_assert(offsetof(struct nlm4_notifyargs_wrapper, xdrgen) == 0);
struct nlm4_notify_wrapper {
struct nlm4_notify xdrgen;
};
static_assert(offsetof(struct nlm4_notify_wrapper, xdrgen) == 0);
struct nlm4_testres_wrapper {
struct nlm4_testres xdrgen;
struct nlm_lock lock;
};
struct nlm4_shareargs_wrapper {
struct nlm4_shareargs xdrgen;
struct nlm_lock lock;
};
static_assert(offsetof(struct nlm4_shareargs_wrapper, xdrgen) == 0);
static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0);
struct nlm4_res_wrapper {
struct nlm4_res xdrgen;
struct nlm_cookie cookie;
};
static_assert(offsetof(struct nlm4_res_wrapper, xdrgen) == 0);
struct nlm4_shareres_wrapper {
struct nlm4_shareres xdrgen;
};
static_assert(offsetof(struct nlm4_shareres_wrapper, xdrgen) == 0);
static __be32
nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object)
{
if (object->len > NLM_MAXCOOKIELEN)
return nlm_lck_denied_nolocks;
cookie->len = object->len;
memcpy(cookie->data, object->data, object->len);
return nlm_granted;
}
static __be32
nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock)
{
if (alock->fh.len > NFS_MAXFHSIZE)
return nlm_lck_denied;
lock->fh.size = alock->fh.len;
memcpy(lock->fh.data, alock->fh.data, alock->fh.len);
lock->oh.len = alock->oh.len;
lock->oh.data = alock->oh.data;
lock->svid = alock->svid;
locks_init_lock(&lock->fl);
lockd_set_file_lock_range4(&lock->fl, alock->l_offset, alock->l_len);
return nlm_granted;
}
static struct nlm_host *
nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
if (!nlmsvc_ops)
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
return NULL;
if (monitored && nsm_monitor(host) < 0) {
nlmsvc_release_host(host);
return NULL;
}
return host;
}
static __be32
nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
struct nlm_lock *lock, struct nlm_file **filp,
struct nlm4_lock *xdr_lock, unsigned char type)
{
bool is_test = (rqstp->rq_proc == NLMPROC4_TEST ||
rqstp->rq_proc == NLMPROC4_TEST_MSG);
struct file_lock *fl = &lock->fl;
struct nlm_file *file = NULL;
int mode;
__be32 error;
if (xdr_lock->fh.len > NFS_MAXFHSIZE)
return nlm_lck_denied_nolocks;
lock->fh.size = xdr_lock->fh.len;
memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len);
lock->oh.len = xdr_lock->oh.len;
lock->oh.data = xdr_lock->oh.data;
lock->svid = xdr_lock->svid;
lock->lock_start = xdr_lock->l_offset;
lock->lock_len = xdr_lock->l_len;
if (lock->lock_start > OFFSET_MAX ||
(lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
return nlm4_fbig;
locks_init_lock(fl);
fl->c.flc_type = type;
lockd_set_file_lock_range4(fl, lock->lock_start, lock->lock_len);
mode = is_test ? O_RDWR : lock_to_openmode(fl);
error = nlm_lookup_file(rqstp, &file, lock, mode);
switch (error) {
case nlm_granted:
break;
case nlm__int__stale_fh:
return nlm4_stale_fh;
case nlm__int__failed:
return nlm4_failed;
default:
return error;
}
*filp = file;
fl->c.flc_flags = FL_POSIX;
fl->c.flc_file = is_test ? nlmsvc_file_file(file)
: file->f_file[mode];
fl->c.flc_pid = current->tgid;
fl->fl_lmops = &nlmsvc_lock_operations;
nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid);
if (!fl->c.flc_owner)
return nlm_lck_denied_nolocks;
return nlm_granted;
}
/**
* nlm4svc_proc_null - NULL: Test for presence of service
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully
*
* RPC synopsis:
* void NLMPROC4_NULL(void) = 0;
*/
static __be32
nlm4svc_proc_null(struct svc_rqst *rqstp)
{
return rpc_success;
}
/**
* nlm4svc_proc_test - TEST: Check for conflicting lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The server would be able to grant the
* requested lock.
* %NLM4_DENIED: The requested lock conflicted with existing
* lock reservations for the file.
* %NLM4_DENIED_NOLOCKS: The server could not allocate the resources
* needed to process the request.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32 nlm4svc_proc_test(struct svc_rqst *rqstp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct nlm4_testres_wrapper *resp = rqstp->rq_resp;
struct nlm_file *file = NULL;
struct nlm_host *host;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock,
type);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlmsvc_testlock(rqstp, file, host,
&argp->lock, &resp->lock);
nlmsvc_release_lockowner(&argp->lock);
if (resp->xdrgen.stat.stat == nlm_lck_denied) {
struct nlm_lock *conf = &resp->lock;
struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder;
holder->exclusive = (conf->fl.c.flc_type != F_RDLCK);
holder->svid = conf->svid;
holder->oh.len = conf->oh.len;
holder->oh.data = conf->oh.data;
holder->l_offset = conf->fl.fl_start;
if (conf->fl.fl_end == OFFSET_MAX)
holder->l_len = 0;
else
holder->l_len = conf->fl.fl_end - conf->fl.fl_start + 1;
}
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
static __be32
nlm4svc_do_lock(struct svc_rqst *rqstp, bool monitored)
{
struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct nlm4_res_wrapper *resp = rqstp->rq_resp;
struct nlm_file *file = NULL;
struct nlm_host *host = NULL;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat.stat = nlm4_netobj_to_cookie(&argp->cookie,
&argp->xdrgen.cookie);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name,
monitored);
if (!host)
goto out;
resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock,
type);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlmsvc_lock(rqstp, file, host, &argp->lock,
argp->xdrgen.block, &argp->cookie,
argp->xdrgen.reclaim);
if (resp->xdrgen.stat.stat == nlm__int__deadlock)
resp->xdrgen.stat.stat = nlm4_deadlock;
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_lock - LOCK: Establish a monitored lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_res NLMPROC4_LOCK(nlm4_lockargs) = 2;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The requested lock was granted.
* %NLM4_DENIED: The requested lock conflicted with existing
* lock reservations for the file.
* %NLM4_DENIED_NOLOCKS: The server could not allocate the resources
* needed to process the request.
* %NLM4_BLOCKED: The blocking request cannot be granted
* immediately. The server will send an
* NLMPROC4_GRANTED callback to the client when
* the lock can be granted.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DEADLCK: The request could not be granted and
* blocking would cause a deadlock.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32
nlm4svc_proc_lock(struct svc_rqst *rqstp)
{
return nlm4svc_do_lock(rqstp, true);
}
/**
* nlm4svc_proc_cancel - CANCEL: Cancel an outstanding blocked lock request
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully
* %rpc_drop_reply: Do not send an RPC reply
*
* RPC synopsis:
* nlm4_res NLMPROC4_CANCEL(nlm4_cancargs) = 3;
*
* Permissible procedure status codes:
* %NLM4_LCK_GRANTED: The requested lock was canceled.
* %NLM4_LCK_DENIED: There was no lock to cancel.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32
nlm4svc_proc_cancel(struct svc_rqst *rqstp)
{
struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct nlm4_res_wrapper *resp = rqstp->rq_resp;
struct net *net = SVC_NET(rqstp);
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
if (locks_in_grace(net))
goto out;
resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock,
type);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlmsvc_cancel_blocked(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_unlock - UNLOCK: Remove a lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The requested lock was released.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32
nlm4svc_proc_unlock(struct svc_rqst *rqstp)
{
struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_res_wrapper *resp = rqstp->rq_resp;
struct net *net = SVC_NET(rqstp);
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
if (locks_in_grace(net))
goto out;
resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock,
F_UNLCK);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_granted - GRANTED: Server grants a previously blocked lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
*
* RPC synopsis:
* nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The requested lock was granted.
* %NLM4_DENIED: The server could not allocate the resources
* needed to process the request.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*/
static __be32
nlm4svc_proc_granted(struct svc_rqst *rqstp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_res_wrapper *resp = rqstp->rq_resp;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat.stat = nlm4_lock_to_nlm_lock(&argp->lock,
&argp->xdrgen.alock);
if (resp->xdrgen.stat.stat)
goto out;
resp->xdrgen.stat.stat = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
out:
return rpc_success;
}
/*
* This is the generic lockd callback for async RPC calls
*/
static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
{
}
static void nlm4svc_callback_release(void *data)
{
nlmsvc_release_call(data);
}
static const struct rpc_call_ops nlm4svc_callback_ops = {
.rpc_call_done = nlm4svc_callback_exit,
.rpc_release = nlm4svc_callback_release,
};
/*
* Dispatch an async callback RPC to a client with a pre-resolved host.
* Caller provides a reference to @host; this function takes ownership
* and releases it via nlmsvc_release_host() before returning.
*/
static __be32
nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc,
__be32 (*func)(struct svc_rqst *, struct nlm_res *))
{
struct nlm_rqst *call;
__be32 stat;
call = nlm_alloc_call(host);
nlmsvc_release_host(host);
if (call == NULL)
return rpc_system_err;
stat = func(rqstp, &call->a_res);
if (stat != 0) {
nlmsvc_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_res *resp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct nlm_lockowner *owner;
struct nlm_file *file = NULL;
struct nlm_host *host = NULL;
resp->status = nlm_lck_denied_nolocks;
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock, type);
if (resp->status)
goto out;
owner = argp->lock.fl.c.flc_owner;
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
&resp->lock);
nlmsvc_put_lockowner(owner);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_test_msg - TEST_MSG: Check for conflicting lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* RPC synopsis:
* void NLMPROC4_TEST_MSG(nlm4_testargs) = 6;
*
* The response to this request is delivered via the TEST_RES procedure.
*/
static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
return rpc_system_err;
return nlm4svc_callback(rqstp, host, NLMPROC4_TEST_RES,
__nlm4svc_proc_test_msg);
}
static __be32
__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct nlm_file *file = NULL;
struct nlm_host *host = NULL;
resp->status = nlm_lck_denied_nolocks;
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
if (!host)
goto out;
resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock, type);
if (resp->status)
goto out;
resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
argp->xdrgen.block, &resp->cookie,
argp->xdrgen.reclaim);
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->status == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_lock_msg - LOCK_MSG: Establish a monitored lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* RPC synopsis:
* void NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7;
*
* The response to this request is delivered via the LOCK_RES procedure.
*/
static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp)
{
struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
if (!host)
return rpc_system_err;
return nlm4svc_callback(rqstp, host, NLMPROC4_LOCK_RES,
__nlm4svc_proc_lock_msg);
}
static __be32
__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
struct net *net = SVC_NET(rqstp);
struct nlm_file *file = NULL;
struct nlm_host *host = NULL;
resp->status = nlm_lck_denied_nolocks;
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
resp->status = nlm_lck_denied_grace_period;
if (locks_in_grace(net))
goto out;
resp->status = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock, type);
if (resp->status)
goto out;
resp->status = nlmsvc_cancel_blocked(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->status == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* RPC synopsis:
* void NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8;
*
* The response to this request is delivered via the CANCEL_RES procedure.
*/
static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp)
{
struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
return rpc_system_err;
return nlm4svc_callback(rqstp, host, NLMPROC4_CANCEL_RES,
__nlm4svc_proc_cancel_msg);
}
static __be32
__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
struct net *net = SVC_NET(rqstp);
struct nlm_file *file = NULL;
struct nlm_host *host = NULL;
resp->status = nlm_lck_denied_nolocks;
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
resp->status = nlm_lck_denied_grace_period;
if (locks_in_grace(net))
goto out;
resp->status = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
goto out;
resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
&file, &argp->xdrgen.alock, F_UNLCK);
if (resp->status)
goto out;
resp->status = nlmsvc_unlock(net, file, &argp->lock);
nlmsvc_release_lockowner(&argp->lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->status == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* RPC synopsis:
* void NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9;
*
* The response to this request is delivered via the UNLOCK_RES procedure.
*/
static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp)
{
struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
return rpc_system_err;
return nlm4svc_callback(rqstp, host, NLMPROC4_UNLOCK_RES,
__nlm4svc_proc_unlock_msg);
}
static __be32
__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
resp->status = nlm_lck_denied;
if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
goto out;
if (nlm4_lock_to_nlm_lock(&argp->lock, &argp->xdrgen.alock))
goto out;
resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
out:
return rpc_success;
}
/**
* nlm4svc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* RPC synopsis:
* void NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10;
*
* The response to this request is delivered via the GRANTED_RES procedure.
*/
static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)
{
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
if (!host)
return rpc_system_err;
return nlm4svc_callback(rqstp, host, NLMPROC4_GRANTED_RES,
__nlm4svc_proc_granted_msg);
}
/**
* nlm4svc_proc_granted_res - GRANTED_RES: Lock Granted result
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
*
* RPC synopsis:
* void NLMPROC4_GRANTED_RES(nlm4_res) = 15;
*/
static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm4_res_wrapper *argp = rqstp->rq_argp;
if (!nlmsvc_ops)
return rpc_success;
if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
return rpc_success;
nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat);
return rpc_success;
}
/**
* nlm4svc_proc_sm_notify - SM_NOTIFY: Peer has rebooted
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_system_err: RPC execution failed.
*
* The SM_NOTIFY procedure is a private callback from Linux statd and is
* not part of the official NLM protocol.
*
* RPC synopsis:
* void NLMPROC4_SM_NOTIFY(nlm4_notifyargs) = 16;
*/
static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp)
{
struct nlm4_notifyargs_wrapper *argp = rqstp->rq_argp;
struct nlm_reboot *reboot = &argp->reboot;
if (!nlm_privileged_requester(rqstp)) {
char buf[RPC_MAX_ADDRBUFLEN];
pr_warn("lockd: rejected NSM callback from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
return rpc_system_err;
}
reboot->len = argp->xdrgen.notify.name.len;
reboot->mon = (char *)argp->xdrgen.notify.name.data;
reboot->state = argp->xdrgen.notify.state;
memcpy(&reboot->priv.data, argp->xdrgen.private,
sizeof(reboot->priv.data));
nlm_host_rebooted(SVC_NET(rqstp), reboot);
return rpc_success;
}
/**
* nlm4svc_proc_unused - stub for unused procedures
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_proc_unavail: Program can't support procedure.
*/
static __be32 nlm4svc_proc_unused(struct svc_rqst *rqstp)
{
return rpc_proc_unavail;
}
/**
* nlm4svc_proc_share - SHARE: Open a file using DOS file-sharing modes
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_shareres NLMPROC4_SHARE(nlm4_shareargs) = 20;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The requested share lock was granted.
* %NLM4_DENIED: The requested lock conflicted with existing
* lock reservations for the file.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32 nlm4svc_proc_share(struct svc_rqst *rqstp)
{
struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
struct nlm_lock *lock = &argp->lock;
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
struct nlm4_lock xdr_lock = {
.fh = argp->xdrgen.share.fh,
.oh = argp->xdrgen.share.oh,
.svid = LOCKD_SHARE_SVID,
};
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat = nlm_lck_denied_grace_period;
if (locks_in_grace(SVC_NET(rqstp)) && !argp->xdrgen.reclaim)
goto out;
resp->xdrgen.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true);
if (!host)
goto out;
resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file,
&xdr_lock, F_RDLCK);
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_share_file(host, file, &lock->oh,
argp->xdrgen.share.access,
argp->xdrgen.share.mode);
nlmsvc_release_lockowner(lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_unshare - UNSHARE: Release a share reservation
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_shareres NLMPROC4_UNSHARE(nlm4_shareargs) = 21;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The share reservation was released.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp)
{
struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
struct nlm_lock *lock = &argp->lock;
struct nlm4_lock xdr_lock = {
.fh = argp->xdrgen.share.fh,
.oh = argp->xdrgen.share.oh,
.svid = LOCKD_SHARE_SVID,
};
struct nlm_host *host = NULL;
struct nlm_file *file = NULL;
resp->xdrgen.cookie = argp->xdrgen.cookie;
resp->xdrgen.stat = nlm_lck_denied_grace_period;
if (locks_in_grace(SVC_NET(rqstp)))
goto out;
resp->xdrgen.stat = nlm_lck_denied_nolocks;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true);
if (!host)
goto out;
resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file,
&xdr_lock, F_RDLCK);
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
nlmsvc_release_lockowner(lock);
out:
if (file)
nlm_release_file(file);
nlmsvc_release_host(host);
return resp->xdrgen.stat == nlm__int__drop_reply ?
rpc_drop_reply : rpc_success;
}
/**
* nlm4svc_proc_nm_lock - NM_LOCK: Establish a non-monitored lock
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
* %rpc_drop_reply: Do not send an RPC reply.
*
* RPC synopsis:
* nlm4_res NLMPROC4_NM_LOCK(nlm4_lockargs) = 22;
*
* Permissible procedure status codes:
* %NLM4_GRANTED: The requested lock was granted.
* %NLM4_DENIED: The requested lock conflicted with existing
* lock reservations for the file.
* %NLM4_DENIED_NOLOCKS: The server could not allocate the resources
* needed to process the request.
* %NLM4_BLOCKED: The blocking request cannot be granted
* immediately. The server will send an
* NLMPROC4_GRANTED callback to the client when
* the lock can be granted.
* %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is
* re-establishing existing locks, and is not
* yet ready to accept normal service requests.
*
* The Linux NLM server implementation also returns:
* %NLM4_DEADLCK: The request could not be granted and
* blocking would cause a deadlock.
* %NLM4_STALE_FH: The request specified an invalid file handle.
* %NLM4_FBIG: The request specified a length or offset
* that exceeds the range supported by the
* server.
* %NLM4_FAILED: The request failed for an unspecified reason.
*/
static __be32 nlm4svc_proc_nm_lock(struct svc_rqst *rqstp)
{
return nlm4svc_do_lock(rqstp, false);
}
/**
* nlm4svc_proc_free_all - FREE_ALL: Discard client's lock and share state
* @rqstp: RPC transaction context
*
* Returns:
* %rpc_success: RPC executed successfully.
*
* RPC synopsis:
* void NLMPROC4_FREE_ALL(nlm4_notify) = 23;
*/
static __be32 nlm4svc_proc_free_all(struct svc_rqst *rqstp)
{
struct nlm4_notify_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.name, false);
if (!host)
goto out;
nlmsvc_free_host_resources(host);
nlmsvc_release_host(host);
out:
return rpc_success;
}
/*
* NLMv4 Server procedures.
*/
static const struct svc_procedure nlm4svc_procedures[24] = {
[NLMPROC4_NULL] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = XDR_void,
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "NULL",
},
[NLMPROC4_TEST] = {
.pc_func = nlm4svc_proc_test,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_nlm4_testres,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_testres_wrapper),
.pc_xdrressize = NLM4_nlm4_testres_sz,
.pc_name = "TEST",
},
[NLMPROC4_LOCK] = {
.pc_func = nlm4svc_proc_lock,
.pc_decode = nlm4_svc_decode_nlm4_lockargs,
.pc_encode = nlm4_svc_encode_nlm4_res,
.pc_argsize = sizeof(struct nlm4_lockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_res_wrapper),
.pc_xdrressize = NLM4_nlm4_res_sz,
.pc_name = "LOCK",
},
[NLMPROC4_CANCEL] = {
.pc_func = nlm4svc_proc_cancel,
.pc_decode = nlm4_svc_decode_nlm4_cancargs,
.pc_encode = nlm4_svc_encode_nlm4_res,
.pc_argsize = sizeof(struct nlm4_cancargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_res_wrapper),
.pc_xdrressize = NLM4_nlm4_res_sz,
.pc_name = "CANCEL",
},
[NLMPROC4_UNLOCK] = {
.pc_func = nlm4svc_proc_unlock,
.pc_decode = nlm4_svc_decode_nlm4_unlockargs,
.pc_encode = nlm4_svc_encode_nlm4_res,
.pc_argsize = sizeof(struct nlm4_unlockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_res_wrapper),
.pc_xdrressize = NLM4_nlm4_res_sz,
.pc_name = "UNLOCK",
},
[NLMPROC4_GRANTED] = {
.pc_func = nlm4svc_proc_granted,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_nlm4_res,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_res_wrapper),
.pc_xdrressize = NLM4_nlm4_res_sz,
.pc_name = "GRANTED",
},
[NLMPROC4_TEST_MSG] = {
.pc_func = nlm4svc_proc_test_msg,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "TEST_MSG",
},
[NLMPROC4_LOCK_MSG] = {
.pc_func = nlm4svc_proc_lock_msg,
.pc_decode = nlm4_svc_decode_nlm4_lockargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_lockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "LOCK_MSG",
},
[NLMPROC4_CANCEL_MSG] = {
.pc_func = nlm4svc_proc_cancel_msg,
.pc_decode = nlm4_svc_decode_nlm4_cancargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_cancargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "CANCEL_MSG",
},
[NLMPROC4_UNLOCK_MSG] = {
.pc_func = nlm4svc_proc_unlock_msg,
.pc_decode = nlm4_svc_decode_nlm4_unlockargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_unlockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "UNLOCK_MSG",
},
[NLMPROC4_GRANTED_MSG] = {
.pc_func = nlm4svc_proc_granted_msg,
.pc_decode = nlm4_svc_decode_nlm4_testargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "GRANTED_MSG",
},
[NLMPROC4_TEST_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_testres,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_testres),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "TEST_RES",
},
[NLMPROC4_LOCK_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "LOCK_RES",
},
[NLMPROC4_CANCEL_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "CANCEL_RES",
},
[NLMPROC4_UNLOCK_RES] = {
.pc_func = nlm4svc_proc_null,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "UNLOCK_RES",
},
[NLMPROC4_GRANTED_RES] = {
.pc_func = nlm4svc_proc_granted_res,
.pc_decode = nlm4_svc_decode_nlm4_res,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_res_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "GRANTED_RES",
},
[NLMPROC4_SM_NOTIFY] = {
.pc_func = nlm4svc_proc_sm_notify,
.pc_decode = nlm4_svc_decode_nlm4_notifyargs,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_notifyargs_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "SM_NOTIFY",
},
[17] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "UNUSED",
},
[18] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "UNUSED",
},
[19] = {
.pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4_svc_decode_void,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = 0,
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "UNUSED",
},
[NLMPROC4_SHARE] = {
.pc_func = nlm4svc_proc_share,
.pc_decode = nlm4_svc_decode_nlm4_shareargs,
.pc_encode = nlm4_svc_encode_nlm4_shareres,
.pc_argsize = sizeof(struct nlm4_shareargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_shareres_wrapper),
.pc_xdrressize = NLM4_nlm4_shareres_sz,
.pc_name = "SHARE",
},
[NLMPROC4_UNSHARE] = {
.pc_func = nlm4svc_proc_unshare,
.pc_decode = nlm4_svc_decode_nlm4_shareargs,
.pc_encode = nlm4_svc_encode_nlm4_shareres,
.pc_argsize = sizeof(struct nlm4_shareargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_shareres_wrapper),
.pc_xdrressize = NLM4_nlm4_shareres_sz,
.pc_name = "UNSHARE",
},
[NLMPROC4_NM_LOCK] = {
.pc_func = nlm4svc_proc_nm_lock,
.pc_decode = nlm4_svc_decode_nlm4_lockargs,
.pc_encode = nlm4_svc_encode_nlm4_res,
.pc_argsize = sizeof(struct nlm4_lockargs_wrapper),
.pc_argzero = 0,
.pc_ressize = sizeof(struct nlm4_res_wrapper),
.pc_xdrressize = NLM4_nlm4_res_sz,
.pc_name = "NM_LOCK",
},
[NLMPROC4_FREE_ALL] = {
.pc_func = nlm4svc_proc_free_all,
.pc_decode = nlm4_svc_decode_nlm4_notify,
.pc_encode = nlm4_svc_encode_void,
.pc_argsize = sizeof(struct nlm4_notify_wrapper),
.pc_argzero = 0,
.pc_ressize = 0,
.pc_xdrressize = XDR_void,
.pc_name = "FREE_ALL",
},
};
/*
* Storage requirements for XDR arguments and results
*/
union nlm4svc_xdrstore {
struct nlm4_testargs_wrapper testargs;
struct nlm4_lockargs_wrapper lockargs;
struct nlm4_cancargs_wrapper cancargs;
struct nlm4_unlockargs_wrapper unlockargs;
struct nlm4_notifyargs_wrapper notifyargs;
struct nlm4_shareargs_wrapper shareargs;
struct nlm4_notify_wrapper notify;
struct nlm4_testres_wrapper testres;
struct nlm4_res_wrapper res;
struct nlm4_shareres_wrapper shareres;
};
static DEFINE_PER_CPU_ALIGNED(unsigned long,
nlm4svc_call_counters[ARRAY_SIZE(nlm4svc_procedures)]);
const struct svc_version nlmsvc_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nlm4svc_procedures),
.vs_proc = nlm4svc_procedures,
.vs_count = nlm4svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlm4svc_xdrstore),
};