// SPDX-License-Identifier: GPL-2.0 /* * 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 */ #include #include #include #include "lockd.h" /* * xdr.h defines SM_PRIV_SIZE as a macro. nlm3xdr_gen.h defines * it as an enum constant. Undefine the macro before including * the generated header. */ #undef SM_PRIV_SIZE #include "share.h" #include "nlm3xdr_gen.h" /* * Size of an NFSv2 file handle, in bytes, which is 32. * Defined locally to avoid including uapi/linux/nfs2.h. */ #define NLM3_FHSIZE 32 /* * Wrapper structures combine xdrgen types with legacy lockd_lock. * The xdrgen field must be first so the structure can be cast * to its XDR type for the RPC dispatch layer. */ struct nlm_testargs_wrapper { struct nlm_testargs xdrgen; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_testargs_wrapper, xdrgen) == 0); struct nlm_testres_wrapper { struct nlm_testres xdrgen; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_testres_wrapper, xdrgen) == 0); struct nlm_lockargs_wrapper { struct nlm_lockargs xdrgen; struct lockd_cookie cookie; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_lockargs_wrapper, xdrgen) == 0); struct nlm_res_wrapper { struct nlm_res xdrgen; struct lockd_cookie cookie; }; static_assert(offsetof(struct nlm_res_wrapper, xdrgen) == 0); struct nlm_cancargs_wrapper { struct nlm_cancargs xdrgen; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_cancargs_wrapper, xdrgen) == 0); struct nlm_unlockargs_wrapper { struct nlm_unlockargs xdrgen; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_unlockargs_wrapper, xdrgen) == 0); struct nlm_notifyargs_wrapper { struct nlm_notifyargs xdrgen; struct lockd_reboot reboot; }; static_assert(offsetof(struct nlm_notifyargs_wrapper, xdrgen) == 0); struct nlm_shareargs_wrapper { struct nlm_shareargs xdrgen; struct lockd_lock lock; }; static_assert(offsetof(struct nlm_shareargs_wrapper, xdrgen) == 0); struct nlm_shareres_wrapper { struct nlm_shareres xdrgen; }; static_assert(offsetof(struct nlm_shareres_wrapper, xdrgen) == 0); struct nlm_notify_wrapper { struct nlm_notify xdrgen; }; static_assert(offsetof(struct nlm_notify_wrapper, xdrgen) == 0); static __be32 nlm_netobj_to_cookie(struct lockd_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 nlm_lock_to_lockd_lock(struct lockd_lock *lock, struct nlm_lock *alock) { if (alock->fh.len != NLM3_FHSIZE) 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->uppid; lockd_set_file_lock_range3(&lock->fl, alock->l_offset, alock->l_len); return nlm_granted; } static struct nlm_host * nlm3svc_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 nlm3svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, struct lockd_lock *lock, struct nlm_file **filp, struct nlm_lock *xdr_lock, unsigned char type) { bool is_test = (rqstp->rq_proc == NLMPROC_TEST || rqstp->rq_proc == NLMPROC_TEST_MSG); struct file_lock *fl = &lock->fl; struct nlm_file *file = NULL; __be32 error; int mode; if (xdr_lock->fh.len != NLM3_FHSIZE) 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->uppid; lock->lock_start = xdr_lock->l_offset; lock->lock_len = xdr_lock->l_len; locks_init_lock(fl); fl->c.flc_type = type; lockd_set_file_lock_range3(fl, lock->lock_start, lock->lock_len); mode = lock_to_openmode(fl); if (is_test) mode = O_RDWR; error = nlm_lookup_file(rqstp, &file, lock, mode); switch (error) { case nlm_granted: break; case nlm__int__stale_fh: case nlm__int__failed: return nlm_lck_denied_nolocks; default: return error; } *filp = file; fl->c.flc_flags = FL_POSIX; if (is_test) fl->c.flc_file = nlmsvc_file_file(file); else fl->c.flc_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; } static inline __be32 cast_status(__be32 status) { switch (status) { case nlm_granted: case nlm_lck_denied: case nlm_lck_denied_nolocks: case nlm_lck_blocked: case nlm_lck_denied_grace_period: case nlm__int__drop_reply: break; case nlm__int__deadlock: status = nlm_lck_denied; break; default: pr_warn_once("lockd: unhandled internal status %u\n", be32_to_cpu(status)); status = nlm_lck_denied_nolocks; break; } return status; } /** * nlmsvc_proc_null - NULL: Test for presence of service * @rqstp: RPC transaction context * * Return: * %rpc_success: RPC executed successfully * * RPC synopsis: * void NLM_NULL(void) = 0; */ static __be32 nlmsvc_proc_null(struct svc_rqst *rqstp) { return rpc_success; } /** * nlmsvc_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: * nlm_testres NLM_TEST(nlm_testargs) = 1; * * Permissible procedure status codes: * %LCK_GRANTED: The server would be able to grant the * requested lock. * %LCK_DENIED: The requested lock conflicted with existing * lock reservations for the file. * %LCK_DENIED_NOLOCKS: The server could not allocate the resources * needed to process the request. * %LCK_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 nlmsvc_proc_test(struct svc_rqst *rqstp) { struct nlm_testargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; struct nlm_testres_wrapper *resp = rqstp->rq_resp; struct nlm_file *file = NULL; struct nlm_host *host; resp->xdrgen.cookie = argp->xdrgen.cookie; resp->xdrgen.test_stat.stat = nlm_lck_denied_nolocks; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->xdrgen.test_stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock, &file, &argp->xdrgen.alock, type); if (resp->xdrgen.test_stat.stat) goto out; resp->xdrgen.test_stat.stat = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock)); nlmsvc_release_lockowner(&argp->lock); if (resp->xdrgen.test_stat.stat == nlm_lck_denied) { struct lockd_lock *conf = &resp->lock; struct nlm_holder *holder = &resp->xdrgen.test_stat.u.holder; holder->exclusive = (conf->fl.c.flc_type != F_RDLCK); holder->uppid = conf->svid; holder->oh.len = conf->oh.len; holder->oh.data = conf->oh.data; holder->l_offset = min_t(loff_t, conf->fl.fl_start, NLM_OFFSET_MAX); if (conf->fl.fl_end == OFFSET_MAX) holder->l_len = 0; else holder->l_len = min_t(loff_t, conf->fl.fl_end - conf->fl.fl_start + 1, NLM_OFFSET_MAX); } out: if (file) nlm_release_file(file); nlmsvc_release_host(host); return resp->xdrgen.test_stat.stat == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; } static __be32 nlmsvc_do_lock(struct svc_rqst *rqstp, bool monitored) { struct nlm_lockargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; struct nlm_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 = nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie); if (resp->xdrgen.stat.stat) goto out; resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, monitored); if (!host) goto out; resp->xdrgen.stat.stat = nlm3svc_lookup_file(rqstp, host, &argp->lock, &file, &argp->xdrgen.alock, type); if (resp->xdrgen.stat.stat) goto out; resp->xdrgen.stat.stat = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, argp->xdrgen.block, &argp->cookie, argp->xdrgen.reclaim)); 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; } /** * nlmsvc_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: * nlm_res NLM_LOCK(nlm_lockargs) = 2; * * Permissible procedure status codes: * %LCK_GRANTED: The requested lock was granted. * %LCK_DENIED: The requested lock conflicted with existing * lock reservations for the file. * %LCK_DENIED_NOLOCKS: The server could not allocate the resources * needed to process the request. * %LCK_BLOCKED: The blocking request cannot be granted * immediately. The server will send an * NLM_GRANTED callback to the client when * the lock can be granted. * %LCK_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 nlmsvc_proc_lock(struct svc_rqst *rqstp) { return nlmsvc_do_lock(rqstp, true); } /** * nlmsvc_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: * nlm_res NLM_CANCEL(nlm_cancargs) = 3; * * Permissible procedure status codes: * %LCK_GRANTED: The requested lock was canceled. * %LCK_DENIED: There was no lock to cancel. * %LCK_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: * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. */ static __be32 nlmsvc_proc_cancel(struct svc_rqst *rqstp) { struct nlm_cancargs_wrapper *argp = rqstp->rq_argp; unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; struct nlm_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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->xdrgen.stat.stat = nlm3svc_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; } /** * nlmsvc_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: * nlm_res NLM_UNLOCK(nlm_unlockargs) = 4; * * Permissible procedure status codes: * %LCK_GRANTED: The requested lock was released. * %LCK_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: * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. */ static __be32 nlmsvc_proc_unlock(struct svc_rqst *rqstp) { struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp; struct nlm_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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->xdrgen.stat.stat = nlm3svc_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; } /** * nlmsvc_proc_granted - GRANTED: Blocked lock has been granted * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * * RPC synopsis: * nlm_res NLM_GRANTED(nlm_testargs) = 5; * * Permissible procedure status codes: * %LCK_GRANTED: The granted lock was accepted. * %LCK_DENIED: The procedure failed, possibly due to * internal resource constraints. * %LCK_DENIED_GRACE_PERIOD: The client host recently restarted and * its NLM is re-establishing existing locks, * so it is not yet ready to accept callbacks. */ static __be32 nlmsvc_proc_granted(struct svc_rqst *rqstp) { struct nlm_testargs_wrapper *argp = rqstp->rq_argp; struct nlm_res_wrapper *resp = rqstp->rq_resp; resp->xdrgen.cookie = argp->xdrgen.cookie; resp->xdrgen.stat.stat = nlm_lock_to_lockd_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 nlmsvc_callback_exit(struct rpc_task *task, void *data) { } void nlmsvc_release_call(struct nlm_rqst *call) { if (!refcount_dec_and_test(&call->a_count)) return; nlmsvc_release_host(call->a_host); kfree(call); } static void nlmsvc_callback_release(void *data) { nlmsvc_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, struct nlm_host *host, u32 proc, __be32 (*func)(struct svc_rqst *, struct lockd_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, &nlmsvc_callback_ops) < 0) return rpc_system_err; return rpc_success; } static __be32 __nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm_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 (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) goto out; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->status = nlm3svc_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 = cast_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; } /** * nlmsvc_proc_test_msg - TEST_MSG: Check for conflicting lock * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * %rpc_drop_reply: Do not send an RPC reply. * %rpc_garbage_args: The request arguments are malformed. * %rpc_system_err: RPC execution failed. * * RPC synopsis: * void NLMPROC_TEST_MSG(nlm_testargs) = 6; * * The response to this request is delivered via the TEST_RES procedure. */ static __be32 nlmsvc_proc_test_msg(struct svc_rqst *rqstp) { struct nlm_testargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; return nlmsvc_callback(rqstp, host, NLMPROC_TEST_RES, __nlmsvc_proc_test_msg); } static __be32 __nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm_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 (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) goto out; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); if (!host) goto out; resp->status = nlm3svc_lookup_file(rqstp, host, &argp->lock, &file, &argp->xdrgen.alock, type); if (resp->status) goto out; resp->status = cast_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; } /** * nlmsvc_proc_lock_msg - LOCK_MSG: Establish a monitored lock * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * %rpc_drop_reply: Do not send an RPC reply. * %rpc_garbage_args: The request arguments are malformed. * %rpc_system_err: RPC execution failed. * * RPC synopsis: * void NLMPROC_LOCK_MSG(nlm_lockargs) = 7; * * The response to this request is delivered via the LOCK_RES procedure. */ static __be32 nlmsvc_proc_lock_msg(struct svc_rqst *rqstp) { struct nlm_lockargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; return nlmsvc_callback(rqstp, host, NLMPROC_LOCK_RES, __nlmsvc_proc_lock_msg); } static __be32 __nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm_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 (nlm_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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->status = nlm3svc_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; } /** * nlmsvc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * %rpc_drop_reply: Do not send an RPC reply. * %rpc_garbage_args: The request arguments are malformed. * %rpc_system_err: RPC execution failed. * * RPC synopsis: * void NLMPROC_CANCEL_MSG(nlm_cancargs) = 8; * * The response to this request is delivered via the CANCEL_RES procedure. */ static __be32 nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp) { struct nlm_cancargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; return nlmsvc_callback(rqstp, host, NLMPROC_CANCEL_RES, __nlmsvc_proc_cancel_msg); } static __be32 __nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm_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 (nlm_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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) goto out; resp->status = nlm3svc_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; } /** * nlmsvc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * %rpc_drop_reply: Do not send an RPC reply. * %rpc_garbage_args: The request arguments are malformed. * %rpc_system_err: RPC execution failed. * * RPC synopsis: * void NLMPROC_UNLOCK_MSG(nlm_unlockargs) = 9; * * The response to this request is delivered via the UNLOCK_RES procedure. */ static __be32 nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp) { struct nlm_unlockargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; return nlmsvc_callback(rqstp, host, NLMPROC_UNLOCK_RES, __nlmsvc_proc_unlock_msg); } static __be32 __nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct lockd_res *resp) { struct nlm_testargs_wrapper *argp = rqstp->rq_argp; resp->status = nlm_lck_denied; if (nlm_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) goto out; if (nlm_lock_to_lockd_lock(&argp->lock, &argp->xdrgen.alock)) goto out; resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); out: return rpc_success; } /** * nlmsvc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * %rpc_garbage_args: The request arguments are malformed. * %rpc_system_err: RPC execution failed. * * RPC synopsis: * void NLMPROC_GRANTED_MSG(nlm_testargs) = 10; * * The response to this request is delivered via the GRANTED_RES procedure. */ static __be32 nlmsvc_proc_granted_msg(struct svc_rqst *rqstp) { struct nlm_testargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); if (!host) return rpc_system_err; return nlmsvc_callback(rqstp, host, NLMPROC_GRANTED_RES, __nlmsvc_proc_granted_msg); } /** * nlmsvc_proc_granted_res - GRANTED_RES: Lock Granted result * @rqstp: RPC transaction context * * Returns: * %rpc_success: RPC executed successfully. * * RPC synopsis: * void NLMPROC_GRANTED_RES(nlm_res) = 15; */ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp) { struct nlm_res_wrapper *argp = rqstp->rq_argp; if (!nlmsvc_ops) return rpc_success; if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie)) return rpc_success; nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat); return rpc_success; } /** * nlmsvc_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 NLM_SM_NOTIFY(nlm_notifyargs) = 16; */ static __be32 nlmsvc_proc_sm_notify(struct svc_rqst *rqstp) { struct nlm_notifyargs_wrapper *argp = rqstp->rq_argp; struct lockd_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; } /** * nlmsvc_proc_unused - stub for unused procedures * @rqstp: RPC transaction context * * Returns: * %rpc_proc_unavail: Program can't support procedure. */ static __be32 nlmsvc_proc_unused(struct svc_rqst *rqstp) { return rpc_proc_unavail; } /** * nlmsvc_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: * nlm_shareres NLM_SHARE(nlm_shareargs) = 20; * * Permissible procedure status codes: * %LCK_GRANTED: The requested share lock was granted. * %LCK_DENIED: The requested lock conflicted with existing * lock reservations for the file. * %LCK_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: * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. */ static __be32 nlmsvc_proc_share(struct svc_rqst *rqstp) { struct nlm_shareargs_wrapper *argp = rqstp->rq_argp; struct nlm_shareres_wrapper *resp = rqstp->rq_resp; struct lockd_lock *lock = &argp->lock; struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm_lock xdr_lock = { .fh = argp->xdrgen.share.fh, .oh = argp->xdrgen.share.oh, .uppid = 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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false); if (!host) goto out; resp->xdrgen.stat = nlm3svc_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; } /** * nlmsvc_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: * nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21; * * Permissible procedure status codes: * %LCK_GRANTED: The share reservation was released. * %LCK_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: * %LCK_DENIED_NOLOCKS: A needed resource could not be allocated. */ static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp) { struct nlm_shareargs_wrapper *argp = rqstp->rq_argp; struct nlm_shareres_wrapper *resp = rqstp->rq_resp; struct lockd_lock *lock = &argp->lock; struct nlm_lock xdr_lock = { .fh = argp->xdrgen.share.fh, .oh = argp->xdrgen.share.oh, .uppid = 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 = nlm3svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, false); if (!host) goto out; resp->xdrgen.stat = nlm3svc_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; } /** * nlmsvc_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: * nlm_res NLM_NM_LOCK(nlm_lockargs) = 22; * * Permissible procedure status codes: * %LCK_GRANTED: The requested lock was granted. * %LCK_DENIED: The requested lock conflicted with existing * lock reservations for the file. * %LCK_DENIED_NOLOCKS: The server could not allocate the resources * needed to process the request. * %LCK_BLOCKED: The blocking request cannot be granted * immediately. The server will send an * NLM_GRANTED callback to the client when * the lock can be granted. * %LCK_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 nlmsvc_proc_nm_lock(struct svc_rqst *rqstp) { return nlmsvc_do_lock(rqstp, false); } /** * nlmsvc_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 NLMPROC_FREE_ALL(nlm_notify) = 23; */ static __be32 nlmsvc_proc_free_all(struct svc_rqst *rqstp) { struct nlm_notify_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; host = nlm3svc_lookup_host(rqstp, argp->xdrgen.name, false); if (!host) goto out; nlmsvc_free_host_resources(host); nlmsvc_release_host(host); out: return rpc_success; } /* * NLMv3 Server procedures. */ static const struct svc_procedure nlmsvc_procedures[24] = { [NLM_NULL] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlm_svc_decode_void, .pc_encode = nlm_svc_encode_void, .pc_argsize = XDR_void, .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "NULL", }, [NLM_TEST] = { .pc_func = nlmsvc_proc_test, .pc_decode = nlm_svc_decode_nlm_testargs, .pc_encode = nlm_svc_encode_nlm_testres, .pc_argsize = sizeof(struct nlm_testargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_testres_wrapper), .pc_xdrressize = NLM3_nlm_testres_sz, .pc_name = "TEST", }, [NLM_LOCK] = { .pc_func = nlmsvc_proc_lock, .pc_decode = nlm_svc_decode_nlm_lockargs, .pc_encode = nlm_svc_encode_nlm_res, .pc_argsize = sizeof(struct nlm_lockargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_res_wrapper), .pc_xdrressize = NLM3_nlm_res_sz, .pc_name = "LOCK", }, [NLM_CANCEL] = { .pc_func = nlmsvc_proc_cancel, .pc_decode = nlm_svc_decode_nlm_cancargs, .pc_encode = nlm_svc_encode_nlm_res, .pc_argsize = sizeof(struct nlm_cancargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_res_wrapper), .pc_xdrressize = NLM3_nlm_res_sz, .pc_name = "CANCEL", }, [NLM_UNLOCK] = { .pc_func = nlmsvc_proc_unlock, .pc_decode = nlm_svc_decode_nlm_unlockargs, .pc_encode = nlm_svc_encode_nlm_res, .pc_argsize = sizeof(struct nlm_unlockargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_res_wrapper), .pc_xdrressize = NLM3_nlm_res_sz, .pc_name = "UNLOCK", }, [NLM_GRANTED] = { .pc_func = nlmsvc_proc_granted, .pc_decode = nlm_svc_decode_nlm_testargs, .pc_encode = nlm_svc_encode_nlm_res, .pc_argsize = sizeof(struct nlm_testargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_res_wrapper), .pc_xdrressize = NLM3_nlm_res_sz, .pc_name = "GRANTED", }, [NLM_TEST_MSG] = { .pc_func = nlmsvc_proc_test_msg, .pc_decode = nlm_svc_decode_nlm_testargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_testargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "TEST_MSG", }, [NLM_LOCK_MSG] = { .pc_func = nlmsvc_proc_lock_msg, .pc_decode = nlm_svc_decode_nlm_lockargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_lockargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "LOCK_MSG", }, [NLM_CANCEL_MSG] = { .pc_func = nlmsvc_proc_cancel_msg, .pc_decode = nlm_svc_decode_nlm_cancargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_cancargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "CANCEL_MSG", }, [NLM_UNLOCK_MSG] = { .pc_func = nlmsvc_proc_unlock_msg, .pc_decode = nlm_svc_decode_nlm_unlockargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_unlockargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "UNLOCK_MSG", }, [NLM_GRANTED_MSG] = { .pc_func = nlmsvc_proc_granted_msg, .pc_decode = nlm_svc_decode_nlm_testargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_testargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "GRANTED_MSG", }, [NLM_TEST_RES] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlm_svc_decode_nlm_testres, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_testres), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "TEST_RES", }, [NLM_LOCK_RES] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlm_svc_decode_nlm_res, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_res), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "LOCK_RES", }, [NLM_CANCEL_RES] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlm_svc_decode_nlm_res, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_res), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "CANCEL_RES", }, [NLM_UNLOCK_RES] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlm_svc_decode_nlm_res, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_res), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "UNLOCK_RES", }, [NLM_GRANTED_RES] = { .pc_func = nlmsvc_proc_granted_res, .pc_decode = nlm_svc_decode_nlm_res, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_res_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "GRANTED_RES", }, [NLM_SM_NOTIFY] = { .pc_func = nlmsvc_proc_sm_notify, .pc_decode = nlm_svc_decode_nlm_notifyargs, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_notifyargs_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "SM_NOTIFY", }, [17] = { .pc_func = nlmsvc_proc_unused, .pc_decode = nlm_svc_decode_void, .pc_encode = nlm_svc_encode_void, .pc_argsize = 0, .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "UNUSED", }, [18] = { .pc_func = nlmsvc_proc_unused, .pc_decode = nlm_svc_decode_void, .pc_encode = nlm_svc_encode_void, .pc_argsize = 0, .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "UNUSED", }, [19] = { .pc_func = nlmsvc_proc_unused, .pc_decode = nlm_svc_decode_void, .pc_encode = nlm_svc_encode_void, .pc_argsize = 0, .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "UNUSED", }, [NLM_SHARE] = { .pc_func = nlmsvc_proc_share, .pc_decode = nlm_svc_decode_nlm_shareargs, .pc_encode = nlm_svc_encode_nlm_shareres, .pc_argsize = sizeof(struct nlm_shareargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_shareres_wrapper), .pc_xdrressize = NLM3_nlm_shareres_sz, .pc_name = "SHARE", }, [NLM_UNSHARE] = { .pc_func = nlmsvc_proc_unshare, .pc_decode = nlm_svc_decode_nlm_shareargs, .pc_encode = nlm_svc_encode_nlm_shareres, .pc_argsize = sizeof(struct nlm_shareargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_shareres_wrapper), .pc_xdrressize = NLM3_nlm_shareres_sz, .pc_name = "UNSHARE", }, [NLM_NM_LOCK] = { .pc_func = nlmsvc_proc_nm_lock, .pc_decode = nlm_svc_decode_nlm_lockargs, .pc_encode = nlm_svc_encode_nlm_res, .pc_argsize = sizeof(struct nlm_lockargs_wrapper), .pc_argzero = 0, .pc_ressize = sizeof(struct nlm_res_wrapper), .pc_xdrressize = NLM3_nlm_res_sz, .pc_name = "NM_LOCK", }, [NLM_FREE_ALL] = { .pc_func = nlmsvc_proc_free_all, .pc_decode = nlm_svc_decode_nlm_notify, .pc_encode = nlm_svc_encode_void, .pc_argsize = sizeof(struct nlm_notify_wrapper), .pc_argzero = 0, .pc_ressize = 0, .pc_xdrressize = XDR_void, .pc_name = "FREE_ALL", }, }; /* * Storage requirements for XDR arguments and results */ union nlmsvc_xdrstore { struct nlm_testargs_wrapper testargs; struct nlm_lockargs_wrapper lockargs; struct nlm_cancargs_wrapper cancargs; struct nlm_unlockargs_wrapper unlockargs; struct nlm_notifyargs_wrapper notifyargs; struct nlm_shareargs_wrapper shareargs; struct nlm_notify_wrapper notify; struct nlm_testres_wrapper testres; struct nlm_res_wrapper res; struct nlm_shareres_wrapper shareres; }; /* * NLMv1 defines only procedures 1 - 15. Linux lockd also implements * procedures 0 (NULL) and 16 (SM_NOTIFY). */ static DEFINE_PER_CPU_ALIGNED(unsigned long, nlm1svc_call_counters[17]); const struct svc_version nlmsvc_version1 = { .vs_vers = 1, .vs_nproc = 17, .vs_proc = nlmsvc_procedures, .vs_count = nlm1svc_call_counters, .vs_dispatch = nlmsvc_dispatch, .vs_xdrsize = sizeof(union nlmsvc_xdrstore), }; static DEFINE_PER_CPU_ALIGNED(unsigned long, nlm3svc_call_counters[ARRAY_SIZE(nlmsvc_procedures)]); const struct svc_version nlmsvc_version3 = { .vs_vers = 3, .vs_nproc = ARRAY_SIZE(nlmsvc_procedures), .vs_proc = nlmsvc_procedures, .vs_count = nlm3svc_call_counters, .vs_dispatch = nlmsvc_dispatch, .vs_xdrsize = sizeof(union nlmsvc_xdrstore), };