Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux lock manager stuff
#
ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_LOCKD) += lockd.o
lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcshare.o svcproc.o svcsubs.o mon.o trace.o netlink.o \
nlm3xdr_gen.o
lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o svc4proc.o nlm4xdr_gen.o
lockd-$(CONFIG_PROC_FS) += procfs.o
#
# XDR code generation (requires Python and additional packages)
#
# The generated *xdr_gen.{h,c} files are checked into git. Normal kernel
# builds do not require the xdrgen tool or its Python dependencies.
#
# Developers modifying .x files in Documentation/sunrpc/xdr/ should run
# "make xdrgen" to regenerate the affected files.
#
.PHONY: xdrgen
XDRGEN = ../../tools/net/sunrpc/xdrgen/xdrgen
XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h \
../../include/linux/sunrpc/xdrgen/nlm3.h
XDRGEN_DECLARATIONS = nlm4xdr_gen.h nlm3xdr_gen.h
XDRGEN_SOURCE = nlm4xdr_gen.c nlm3xdr_gen.c
xdrgen: $(XDRGEN_DEFINITIONS) $(XDRGEN_DECLARATIONS) $(XDRGEN_SOURCE)
../../include/linux/sunrpc/xdrgen/nlm4.h: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) definitions $< > $@
../../include/linux/sunrpc/xdrgen/nlm3.h: ../../Documentation/sunrpc/xdr/nlm3.x
$(XDRGEN) definitions $< > $@
nlm4xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) declarations $< > $@
nlm3xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm3.x
$(XDRGEN) declarations $< > $@
nlm4xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm4.x
$(XDRGEN) source --peer server $< > $@
nlm3xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm3.x
$(XDRGEN) source --peer server $< > $@
+582
View File
@@ -0,0 +1,582 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/lockd/clnt4xdr.c
*
* XDR functions to encode/decode NLM version 4 RPC arguments and results.
*
* NLM client-side only.
*
* Copyright (C) 2010, Oracle. All rights reserved.
*/
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include "lockd.h"
#include <uapi/linux/nfs3.h>
#define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
# error "NLM host name cannot be larger than NLM's maximum string length!"
#endif
/*
* Declare the space requirements for NLM arguments and replies as
* number of 32bit-words
*/
#define NLM4_void_sz (0)
#define NLM4_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
#define NLM4_caller_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM4_owner_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM4_fhandle_sz (1+(NFS3_FHSIZE>>2))
#define NLM4_lock_sz (5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz)
#define NLM4_holder_sz (6+NLM4_owner_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)
static 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;
}
static void nlm4_compute_offsets(const struct lockd_lock *lock,
u64 *l_offset, u64 *l_len)
{
const struct file_lock *fl = &lock->fl;
*l_offset = loff_t_to_s64(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
*l_len = 0;
else
*l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
}
/*
* Encode/decode NLMv4 basic data types
*
* Basic NLMv4 data types are defined in Appendix II, section 6.1.4
* of RFC 1813: "NFS Version 3 Protocol Specification" and in Chapter
* 10 of X/Open's "Protocols for Interworking: XNFS, Version 3W".
*
* Not all basic data types have their own encoding and decoding
* functions. For run-time efficiency, some data types are encoded
* or decoded inline.
*/
static void encode_bool(struct xdr_stream *xdr, const int value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = value ? xdr_one : xdr_zero;
}
static void encode_int32(struct xdr_stream *xdr, const s32 value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(value);
}
/*
* typedef opaque netobj<MAXNETOBJ_SZ>
*/
static void encode_netobj(struct xdr_stream *xdr,
const u8 *data, const unsigned int length)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, data, length);
}
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
ssize_t ret;
ret = xdr_stream_decode_opaque_inline(xdr, (void *)&obj->data,
XDR_MAX_NETOBJ);
if (unlikely(ret < 0))
return -EIO;
obj->len = ret;
return 0;
}
/*
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
const struct lockd_cookie *cookie)
{
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
struct lockd_cookie *cookie)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
/* apparently HPUX can return empty cookies */
if (length == 0)
goto out_hpux;
if (length > NLM_MAXCOOKIELEN)
goto out_size;
p = xdr_inline_decode(xdr, length);
if (unlikely(p == NULL))
goto out_overflow;
cookie->len = length;
memcpy(cookie->data, p, length);
return 0;
out_hpux:
cookie->len = 4;
memset(cookie->data, 0, 4);
return 0;
out_size:
dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO;
out_overflow:
return -EIO;
}
/*
* netobj fh;
*/
static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
{
encode_netobj(xdr, (u8 *)&fh->data, fh->size);
}
/*
* enum nlm4_stats {
* NLM4_GRANTED = 0,
* NLM4_DENIED = 1,
* NLM4_DENIED_NOLOCKS = 2,
* NLM4_BLOCKED = 3,
* NLM4_DENIED_GRACE_PERIOD = 4,
* NLM4_DEADLCK = 5,
* NLM4_ROFS = 6,
* NLM4_STALE_FH = 7,
* NLM4_FBIG = 8,
* NLM4_FAILED = 9
* };
*
* struct nlm4_stat {
* nlm4_stats stat;
* };
*
* NB: we don't swap bytes for the NLM status values. The upper
* layers deal directly with the status value in network byte
* order.
*/
static void encode_nlm4_stat(struct xdr_stream *xdr,
const __be32 stat)
{
__be32 *p;
BUG_ON(be32_to_cpu(stat) > NLM_FAILED);
p = xdr_reserve_space(xdr, 4);
*p = stat;
}
static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
if (unlikely(ntohl(*p) > ntohl(nlm4_failed)))
goto out_bad_xdr;
*stat = *p;
return 0;
out_bad_xdr:
dprintk("%s: server returned invalid nlm4_stats value: %u\n",
__func__, be32_to_cpup(p));
return -EIO;
out_overflow:
return -EIO;
}
/*
* struct nlm4_holder {
* bool exclusive;
* int32 svid;
* netobj oh;
* uint64 l_offset;
* uint64 l_len;
* };
*/
static void encode_nlm4_holder(struct xdr_stream *xdr,
const struct lockd_res *result)
{
const struct lockd_lock *lock = &result->lock;
u64 l_offset, l_len;
__be32 *p;
encode_bool(xdr, lock->fl.c.flc_type == F_RDLCK);
encode_int32(xdr, lock->svid);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4);
nlm4_compute_offsets(lock, &l_offset, &l_len);
p = xdr_encode_hyper(p, l_offset);
xdr_encode_hyper(p, l_len);
}
static int decode_nlm4_holder(struct xdr_stream *xdr, struct lockd_res *result)
{
struct lockd_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u64 l_offset, l_len;
u32 exclusive;
int error;
__be32 *p;
memset(lock, 0, sizeof(*lock));
locks_init_lock(fl);
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
exclusive = be32_to_cpup(p++);
lock->svid = be32_to_cpup(p);
fl->c.flc_pid = (pid_t)lock->svid;
error = decode_netobj(xdr, &lock->oh);
if (unlikely(error))
goto out;
p = xdr_inline_decode(xdr, 8 + 8);
if (unlikely(p == NULL))
goto out_overflow;
fl->c.flc_flags = FL_POSIX;
fl->c.flc_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
p = xdr_decode_hyper(p, &l_offset);
xdr_decode_hyper(p, &l_len);
lockd_set_file_lock_range4(fl, l_offset, l_len);
error = 0;
out:
return error;
out_overflow:
return -EIO;
}
/*
* string caller_name<LM_MAXSTRLEN>;
*/
static void encode_caller_name(struct xdr_stream *xdr, const char *name)
{
/* NB: client-side does not set lock->len */
u32 length = strlen(name);
__be32 *p;
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, name, length);
}
/*
* struct nlm4_lock {
* string caller_name<LM_MAXSTRLEN>;
* netobj fh;
* netobj oh;
* int32 svid;
* uint64 l_offset;
* uint64 l_len;
* };
*/
static void encode_nlm4_lock(struct xdr_stream *xdr,
const struct lockd_lock *lock)
{
u64 l_offset, l_len;
__be32 *p;
encode_caller_name(xdr, lock->caller);
encode_fh(xdr, &lock->fh);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 8 + 8);
*p++ = cpu_to_be32(lock->svid);
nlm4_compute_offsets(lock, &l_offset, &l_len);
p = xdr_encode_hyper(p, l_offset);
xdr_encode_hyper(p, l_len);
}
/*
* NLMv4 XDR encode functions
*
* NLMv4 argument types are defined in Appendix II of RFC 1813:
* "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* struct nlm4_testargs {
* netobj cookie;
* bool exclusive;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_lockargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm4_lock alock;
* bool reclaim;
* int state;
* };
*/
static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
encode_bool(xdr, args->reclaim);
encode_int32(xdr, args->state);
}
/*
* struct nlm4_cancargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_unlockargs {
* netobj cookie;
* struct nlm4_lock alock;
* };
*/
static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm4_lock(xdr, lock);
}
/*
* struct nlm4_res {
* netobj cookie;
* nlm4_stat stat;
* };
*/
static void nlm4_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
}
/*
* union nlm4_testrply switch (nlm4_stats stat) {
* case NLM4_DENIED:
* struct nlm4_holder holder;
* default:
* void;
* };
*
* struct nlm4_testres {
* netobj cookie;
* nlm4_testrply test_stat;
* };
*/
static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm4_stat(xdr, result->status);
if (result->status == nlm_lck_denied)
encode_nlm4_holder(xdr, result);
}
/*
* NLMv4 XDR decode functions
*
* NLMv4 argument types are defined in Appendix II of RFC 1813:
* "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* union nlm4_testrply switch (nlm4_stats stat) {
* case NLM4_DENIED:
* struct nlm4_holder holder;
* default:
* void;
* };
*
* struct nlm4_testres {
* netobj cookie;
* nlm4_testrply test_stat;
* };
*/
static int decode_nlm4_testrply(struct xdr_stream *xdr,
struct lockd_res *result)
{
int error;
error = decode_nlm4_stat(xdr, &result->status);
if (unlikely(error))
goto out;
if (result->status == nlm_lck_denied)
error = decode_nlm4_holder(xdr, result);
out:
return error;
}
static int nlm4_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm4_testrply(xdr, result);
out:
return error;
}
/*
* struct nlm4_res {
* netobj cookie;
* nlm4_stat stat;
* };
*/
static int nlm4_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm4_stat(xdr, &result->status);
out:
return error;
}
/*
* For NLM, a void procedure really returns nothing
*/
#define nlm4_xdr_dec_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = nlm4_xdr_enc_##argtype, \
.p_decode = nlm4_xdr_dec_##restype, \
.p_arglen = NLM4_##argtype##_sz, \
.p_replen = NLM4_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static const 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),
};
static unsigned int nlm_version4_counts[ARRAY_SIZE(nlm4_procedures)];
const struct rpc_version nlm_version4 = {
.number = 4,
.nrprocs = ARRAY_SIZE(nlm4_procedures),
.procs = nlm4_procedures,
.counts = nlm_version4_counts,
};
+297
View File
@@ -0,0 +1,297 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/slab.h>
#include <linux/time.h>
#include <linux/nfs_fs.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/kthread.h>
#include "lockd.h"
#include "trace.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.
*/
static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
/**
* nlmclnt_init - Set up per-NFS mount point lockd data structures
* @nlm_init: pointer to arguments structure
*
* Returns pointer to an appropriate nlm_host struct,
* or an ERR_PTR value.
*/
struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
{
struct nlm_host *host;
u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
int status;
status = lockd_up(nlm_init->net, nlm_init->cred);
if (status < 0)
return ERR_PTR(status);
host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
nlm_init->protocol, nlm_version,
nlm_init->hostname, nlm_init->noresvport,
nlm_init->net, nlm_init->cred);
if (host == NULL)
goto out_nohost;
if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL)
goto out_nobind;
host->h_nlmclnt_ops = nlm_init->nlmclnt_ops;
return host;
out_nobind:
nlmclnt_release_host(host);
out_nohost:
lockd_down(nlm_init->net);
return ERR_PTR(-ENOLCK);
}
EXPORT_SYMBOL_GPL(nlmclnt_init);
/**
* nlmclnt_done - Release resources allocated by nlmclnt_init()
* @host: nlm_host structure reserved by nlmclnt_init()
*
*/
void nlmclnt_done(struct nlm_host *host)
{
struct net *net = host->net;
nlmclnt_release_host(host);
lockd_down(net);
}
EXPORT_SYMBOL_GPL(nlmclnt_done);
void nlmclnt_prepare_block(struct nlm_wait *block, struct nlm_host *host, struct file_lock *fl)
{
block->b_host = host;
block->b_lock = fl;
init_waitqueue_head(&block->b_wait);
block->b_status = nlm_lck_blocked;
}
struct rpc_clnt *nlmclnt_rpc_clnt(struct nlm_host *host)
{
return host->h_rpcclnt;
}
EXPORT_SYMBOL_GPL(nlmclnt_rpc_clnt);
/*
* Queue up a lock for blocking so that the GRANTED request can see it
*/
void nlmclnt_queue_block(struct nlm_wait *block)
{
spin_lock(&nlm_blocked_lock);
list_add(&block->b_list, &nlm_blocked);
spin_unlock(&nlm_blocked_lock);
}
/*
* Dequeue the block and return its final status
*/
__be32 nlmclnt_dequeue_block(struct nlm_wait *block)
{
__be32 status;
spin_lock(&nlm_blocked_lock);
list_del(&block->b_list);
status = block->b_status;
spin_unlock(&nlm_blocked_lock);
return status;
}
/*
* Block on a lock
*/
int nlmclnt_wait(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;
/* Reset the lock status after a server reboot so we resend */
if (block->b_status == nlm_lck_denied_grace_period)
block->b_status = nlm_lck_blocked;
return 0;
}
/*
* The server lockd has called us back to tell us the lock was granted
*/
__be32 nlmclnt_grant(const struct sockaddr *addr, const struct lockd_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!
*/
spin_lock(&nlm_blocked_lock);
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 (!rpc_cmp_addr(nlm_addr(block->b_host), addr))
continue;
if (nfs_compare_fh(NFS_FH(file_inode(fl_blocked->c.flc_file)), 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;
}
spin_unlock(&nlm_blocked_lock);
trace_nlmclnt_grant(lock, addr, svc_addr_len(addr), res);
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)
{
struct task_struct *task;
if (!host->h_reclaiming++) {
nlm_get_host(host);
task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
if (IS_ERR(task))
printk(KERN_ERR "lockd: unable to spawn reclaimer "
"thread. Locks for %s won't be reclaimed! "
"(%ld)\n", host->h_name, PTR_ERR(task));
}
}
static int
reclaimer(void *ptr)
{
struct nlm_host *host = (struct nlm_host *) ptr;
struct nlm_wait *block;
struct nlm_rqst *req;
struct file_lock *fl, *next;
u32 nsmstate;
struct net *net = host->net;
req = kmalloc_obj(*req);
if (!req)
return 0;
allow_signal(SIGKILL);
down_write(&host->h_rwsem);
lockd_up(net, NULL); /* 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);
/*
* sending this thread a SIGKILL will result in any unreclaimed
* locks being removed from the h_granted list. This means that
* the kernel will not attempt to reclaim them again if a new
* reclaimer thread is spawned for this host.
*/
if (signalled())
continue;
if (nlmclnt_reclaim(host, fl, req) != 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 */
spin_lock(&nlm_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);
}
}
spin_unlock(&nlm_blocked_lock);
/* Release host handle after use */
nlmclnt_release_host(host);
lockd_down(net);
kfree(req);
return 0;
}
+889
View File
@@ -0,0 +1,889 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/nfs_fs.h>
#include <linux/utsname.h>
#include <linux/freezer.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include "lockd.h"
#include "trace.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 lockd_cookie *c)
{
u32 cookie = atomic_inc_return(&nlm_cookie);
memcpy(c->data, &cookie, 4);
c->len=4;
}
static struct nlm_lockowner *
nlmclnt_get_lockowner(struct nlm_lockowner *lockowner)
{
refcount_inc(&lockowner->count);
return lockowner;
}
static void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner)
{
if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
return;
list_del(&lockowner->list);
spin_unlock(&lockowner->host->h_lock);
nlmclnt_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 *__nlmclnt_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 nlmclnt_get_lockowner(lockowner);
}
return NULL;
}
static struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner)
{
struct nlm_lockowner *res, *new = NULL;
spin_lock(&host->h_lock);
res = __nlmclnt_find_lockowner(host, owner);
if (res == NULL) {
spin_unlock(&host->h_lock);
new = kmalloc_obj(*new);
spin_lock(&host->h_lock);
res = __nlmclnt_find_lockowner(host, owner);
if (res == NULL && new != NULL) {
res = new;
refcount_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 lockd_args *argp = &req->a_args;
struct lockd_lock *lock = &argp->lock;
char *nodename = req->a_host->h_rpcclnt->cl_nodename;
nlmclnt_next_cookie(&argp->cookie);
memcpy(&lock->fh, NFS_FH(file_inode(fl->c.flc_file)),
sizeof(struct nfs_fh));
lock->caller = 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,
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.c.flc_type = fl->c.flc_type;
}
static void nlmclnt_release_lockargs(struct nlm_rqst *req)
{
WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL);
}
/**
* nlmclnt_proc - Perform a single client-side lock request
* @host: address of a valid nlm_host context representing the NLM server
* @cmd: fcntl-style file lock operation to perform
* @fl: address of arguments for the lock operation
* @data: address of data to be sent to callback operations
*
*/
int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data)
{
struct nlm_rqst *call;
int status;
const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops;
call = nlm_alloc_call(host);
if (call == NULL)
return -ENOMEM;
if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
nlmclnt_ops->nlmclnt_alloc_call(data);
nlmclnt_locks_init_private(fl, host);
if (!fl->fl_u.nfs_fl.owner) {
/* lockowner allocation has failed */
nlmclnt_release_call(call);
return -ENOMEM;
}
/* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
call->a_callback_data = data;
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->c.flc_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;
dprintk("lockd: clnt proc returns %d\n", status);
return status;
}
EXPORT_SYMBOL_GPL(nlmclnt_proc);
/*
* Allocate an NLM RPC call struct
*/
struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
{
struct nlm_rqst *call;
for(;;) {
call = kzalloc_obj(*call);
if (call != NULL) {
refcount_set(&call->a_count, 1);
locks_init_lock(&call->a_args.lock.fl);
locks_init_lock(&call->a_res.lock.fl);
call->a_host = nlm_get_host(host);
return call;
}
if (signalled())
break;
printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ);
}
return NULL;
}
void nlmclnt_release_call(struct nlm_rqst *call)
{
const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops;
if (!refcount_dec_and_test(&call->a_count))
return;
if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call)
nlmclnt_ops->nlmclnt_release_call(call->a_callback_data);
nlmclnt_release_host(call->a_host);
nlmclnt_release_lockargs(call);
kfree(call);
}
static void nlmclnt_rpc_release(void *data)
{
nlmclnt_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(const struct cred *cred, struct nlm_rqst *req, u32 proc)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
struct lockd_args *argp = &req->a_args;
struct lockd_res *resp = &req->a_res;
struct rpc_message msg = {
.rpc_argp = argp,
.rpc_resp = resp,
.rpc_cred = cred,
};
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",
ntohl(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 struct rpc_task *__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;
struct rpc_task_setup task_setup_data = {
.rpc_message = msg,
.callback_ops = tk_ops,
.callback_data = req,
.flags = RPC_TASK_ASYNC,
};
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];
task_setup_data.rpc_client = clnt;
/* bootstrap and kick off the async RPC call */
return rpc_run_task(&task_setup_data);
out_err:
tk_ops->rpc_release(req);
return ERR_PTR(-ENOLCK);
}
static int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
{
struct rpc_task *task;
task = __nlm_async_call(req, proc, msg, tk_ops);
if (IS_ERR(task))
return PTR_ERR(task);
rpc_put_task(task);
return 0;
}
/*
* NLM asynchronous call.
*/
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_do_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_do_async_call(req, proc, &msg, tk_ops);
}
/*
* NLM client asynchronous call.
*
* Note that although the calls are asynchronous, and are therefore
* guaranteed to complete, we still always attempt to wait for
* completion in order to be able to correctly track the lock
* state.
*/
static int nlmclnt_async_call(const struct cred *cred, 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,
.rpc_cred = cred,
};
struct rpc_task *task;
int err;
task = __nlm_async_call(req, proc, &msg, tk_ops);
if (IS_ERR(task))
return PTR_ERR(task);
err = rpc_wait_for_completion_task(task);
rpc_put_task(task);
return err;
}
/*
* 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(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_TEST);
if (status < 0)
goto out;
switch (req->a_res.status) {
case nlm_granted:
fl->c.flc_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_end;
fl->c.flc_type = req->a_res.lock.fl.c.flc_type;
fl->c.flc_pid = -req->a_res.lock.fl.c.flc_pid;
break;
default:
status = nlm_stat_to_errno(req->a_res.status);
}
out:
trace_nlmclnt_test(&req->a_args.lock,
(const struct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
nlmclnt_release_call(req);
return status;
}
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
new->fl_u.nfs_fl.owner = nlmclnt_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);
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
}
static void nlmclnt_locks_release_private(struct file_lock *fl)
{
spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
list_del(&fl->fl_u.nfs_fl.list);
spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner);
}
static const 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)
{
fl->fl_u.nfs_fl.state = 0;
fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host,
fl->c.flc_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)
{
return locks_lock_file_wait(fl->c.flc_file, fl);
}
/*
* 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)
{
const struct cred *cred = nfs_file_cred(fl->c.flc_file);
struct nlm_host *host = req->a_host;
struct lockd_res *resp = &req->a_res;
struct nlm_wait block;
unsigned char flags = fl->c.flc_flags;
unsigned char type;
__be32 b_status;
int status = -ENOLCK;
if (nsm_monitor(host) < 0)
goto out;
req->a_args.state = nsm_local_state;
fl->c.flc_flags |= FL_ACCESS;
status = do_vfs_lock(fl);
fl->c.flc_flags = flags;
if (status < 0)
goto out;
nlmclnt_prepare_block(&block, host, fl);
again:
/*
* Initialise resp->status to a valid non-zero value,
* since 0 == nlm_lck_granted
*/
resp->status = nlm_lck_blocked;
/*
* A GRANTED callback can come at any time -- even before the reply
* to the LOCK request arrives, so we queue the wait before
* requesting the lock.
*/
nlmclnt_queue_block(&block);
for (;;) {
/* Reboot protection */
fl->fl_u.nfs_fl.state = host->h_state;
status = nlmclnt_call(cred, req, NLMPROC_LOCK);
if (status < 0)
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_wait(&block, req, NLMCLNT_POLL_TIMEOUT);
if (status < 0)
break;
if (block.b_status != nlm_lck_blocked)
break;
}
b_status = nlmclnt_dequeue_block(&block);
if (resp->status == nlm_lck_blocked)
resp->status = b_status;
/* if we were interrupted while blocking, then cancel the lock request
* and exit
*/
if (resp->status == nlm_lck_blocked) {
if (!req->a_args.block)
goto out_unlock;
if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
goto out;
}
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->c.flc_flags |= FL_SLEEP;
if (do_vfs_lock(fl) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
up_read(&host->h_rwsem);
fl->c.flc_flags = flags;
status = 0;
}
if (status < 0)
goto out_unlock;
/*
* EAGAIN doesn't make sense for sleeping locks, and in some
* cases NLM_LCK_DENIED is returned for a permanent error. So
* turn it into an ENOLCK.
*/
if (resp->status == nlm_lck_denied && (flags & FL_SLEEP))
status = -ENOLCK;
else
status = nlm_stat_to_errno(resp->status);
out:
trace_nlmclnt_lock(&req->a_args.lock,
(const struct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
nlmclnt_release_call(req);
return status;
out_unlock:
/* Fatal error: ensure that we remove the lock altogether */
trace_nlmclnt_lock(&req->a_args.lock,
(const struct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
dprintk("lockd: lock attempt ended in fatal error.\n"
" Attempting to unlock.\n");
type = fl->c.flc_type;
fl->c.flc_type = F_UNLCK;
down_read(&host->h_rwsem);
do_vfs_lock(fl);
up_read(&host->h_rwsem);
fl->c.flc_type = type;
fl->c.flc_flags = flags;
nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
return status;
}
/*
* RECLAIM: Try to reclaim a lock
*/
int
nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl,
struct nlm_rqst *req)
{
int status;
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;
/* Set up the argument struct */
nlmclnt_setlockargs(req, fl);
req->a_args.reclaim = 1;
status = nlmclnt_call(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_LOCK);
if (status >= 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->c.flc_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 lockd_res *resp = &req->a_res;
int status;
unsigned char flags = fl->c.flc_flags;
/*
* 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->c.flc_flags |= FL_EXISTS;
down_read(&host->h_rwsem);
status = do_vfs_lock(fl);
up_read(&host->h_rwsem);
fl->c.flc_flags = flags;
if (status == -ENOENT) {
status = 0;
goto out;
}
refcount_inc(&req->a_count);
status = nlmclnt_async_call(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
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",
ntohl(resp->status));
/* What to do now? I'm out of my depth... */
status = -ENOLCK;
out:
trace_nlmclnt_unlock(&req->a_args.lock,
(const struct sockaddr *)&req->a_host->h_addr,
req->a_host->h_addrlen, req->a_res.status);
nlmclnt_release_call(req);
return status;
}
static void nlmclnt_unlock_prepare(struct rpc_task *task, void *data)
{
struct nlm_rqst *req = data;
const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops;
bool defer_call = false;
if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare)
defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data);
if (!defer_call)
rpc_call_start(task);
}
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_SIGNALLED(task))
goto die;
if (task->tk_status < 0) {
dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
switch (task->tk_status) {
case -EACCES:
case -EIO:
goto die;
default:
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_prepare = nlmclnt_unlock_prepare,
.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;
int status;
dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
" Attempting to cancel lock.\n");
req = nlm_alloc_call(host);
if (!req)
return -ENOMEM;
req->a_flags = RPC_TASK_ASYNC;
nlmclnt_setlockargs(req, fl);
req->a_args.block = block;
refcount_inc(&req->a_count);
status = nlmclnt_async_call(nfs_file_cred(fl->c.flc_file), req,
NLMPROC_CANCEL, &nlmclnt_cancel_ops);
if (status == 0 && req->a_res.status == nlm_lck_denied)
status = -ENOLCK;
nlmclnt_release_call(req);
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_SIGNALLED(task))
goto die;
if (task->tk_status < 0) {
dprintk("lockd: CANCEL call error %d, retrying.\n",
task->tk_status);
goto retry_cancel;
}
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",
ntohl(status));
return -ENOLCK;
}
+614
View File
@@ -0,0 +1,614 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/lockd/clntxdr.c
*
* XDR functions to encode/decode NLM version 1 and 3 RPC
* arguments and results. NLM version 2 is not specified
* by a standard, thus it is not implemented.
*
* NLM client-side only.
*
* Copyright (C) 2010, Oracle. All rights reserved.
*/
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include "lockd.h"
#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
/*
* Declare the space requirements for NLM arguments and replies as
* number of 32bit-words
*/
#define NLM_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
#define NLM_caller_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM_owner_sz (1+(NLMCLNT_OHSIZE>>2))
#define NLM_fhandle_sz (1+(NFS2_FHSIZE>>2))
#define NLM_lock_sz (3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz)
#define NLM_holder_sz (4+NLM_owner_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)
static 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;
}
static void nlm_compute_offsets(const struct lockd_lock *lock,
u32 *l_offset, u32 *l_len)
{
const struct file_lock *fl = &lock->fl;
*l_offset = loff_t_to_s32(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
*l_len = 0;
else
*l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
}
/*
* Encode/decode NLMv3 basic data types
*
* Basic NLMv3 data types are not defined in an IETF standards
* document. X/Open has a description of these data types that
* is useful. See Chapter 10 of "Protocols for Interworking:
* XNFS, Version 3W".
*
* Not all basic data types have their own encoding and decoding
* functions. For run-time efficiency, some data types are encoded
* or decoded inline.
*/
static void encode_bool(struct xdr_stream *xdr, const int value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = value ? xdr_one : xdr_zero;
}
static void encode_int32(struct xdr_stream *xdr, const s32 value)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(value);
}
/*
* typedef opaque netobj<MAXNETOBJ_SZ>
*/
static void encode_netobj(struct xdr_stream *xdr,
const u8 *data, const unsigned int length)
{
__be32 *p;
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, data, length);
}
static int decode_netobj(struct xdr_stream *xdr,
struct xdr_netobj *obj)
{
ssize_t ret;
ret = xdr_stream_decode_opaque_inline(xdr, (void *)&obj->data,
XDR_MAX_NETOBJ);
if (unlikely(ret < 0))
return -EIO;
obj->len = ret;
return 0;
}
/*
* netobj cookie;
*/
static void encode_cookie(struct xdr_stream *xdr,
const struct lockd_cookie *cookie)
{
encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
}
static int decode_cookie(struct xdr_stream *xdr,
struct lockd_cookie *cookie)
{
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
length = be32_to_cpup(p++);
/* apparently HPUX can return empty cookies */
if (length == 0)
goto out_hpux;
if (length > NLM_MAXCOOKIELEN)
goto out_size;
p = xdr_inline_decode(xdr, length);
if (unlikely(p == NULL))
goto out_overflow;
cookie->len = length;
memcpy(cookie->data, p, length);
return 0;
out_hpux:
cookie->len = 4;
memset(cookie->data, 0, 4);
return 0;
out_size:
dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO;
out_overflow:
return -EIO;
}
/*
* netobj fh;
*/
static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
{
encode_netobj(xdr, (u8 *)&fh->data, NFS2_FHSIZE);
}
/*
* enum nlm_stats {
* LCK_GRANTED = 0,
* LCK_DENIED = 1,
* LCK_DENIED_NOLOCKS = 2,
* LCK_BLOCKED = 3,
* LCK_DENIED_GRACE_PERIOD = 4
* };
*
*
* struct nlm_stat {
* nlm_stats stat;
* };
*
* NB: we don't swap bytes for the NLM status values. The upper
* layers deal directly with the status value in network byte
* order.
*/
static void encode_nlm_stat(struct xdr_stream *xdr,
const __be32 stat)
{
__be32 *p;
WARN_ON_ONCE(be32_to_cpu(stat) > NLM_LCK_DENIED_GRACE_PERIOD);
p = xdr_reserve_space(xdr, 4);
*p = stat;
}
static int decode_nlm_stat(struct xdr_stream *xdr,
__be32 *stat)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
goto out_overflow;
if (unlikely(ntohl(*p) > ntohl(nlm_lck_denied_grace_period)))
goto out_enum;
*stat = *p;
return 0;
out_enum:
dprintk("%s: server returned invalid nlm_stats value: %u\n",
__func__, be32_to_cpup(p));
return -EIO;
out_overflow:
return -EIO;
}
/*
* struct nlm_holder {
* bool exclusive;
* int uppid;
* netobj oh;
* unsigned l_offset;
* unsigned l_len;
* };
*/
static void encode_nlm_holder(struct xdr_stream *xdr,
const struct lockd_res *result)
{
const struct lockd_lock *lock = &result->lock;
u32 l_offset, l_len;
__be32 *p;
encode_bool(xdr, lock->fl.c.flc_type == F_RDLCK);
encode_int32(xdr, lock->svid);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4);
nlm_compute_offsets(lock, &l_offset, &l_len);
*p++ = cpu_to_be32(l_offset);
*p = cpu_to_be32(l_len);
}
static int decode_nlm_holder(struct xdr_stream *xdr, struct lockd_res *result)
{
struct lockd_lock *lock = &result->lock;
struct file_lock *fl = &lock->fl;
u32 exclusive, l_offset, l_len;
int error;
__be32 *p;
s32 end;
memset(lock, 0, sizeof(*lock));
locks_init_lock(fl);
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
exclusive = be32_to_cpup(p++);
lock->svid = be32_to_cpup(p);
fl->c.flc_pid = (pid_t)lock->svid;
error = decode_netobj(xdr, &lock->oh);
if (unlikely(error))
goto out;
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
goto out_overflow;
fl->c.flc_flags = FL_POSIX;
fl->c.flc_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
l_offset = be32_to_cpup(p++);
l_len = be32_to_cpup(p);
end = l_offset + l_len - 1;
fl->fl_start = (loff_t)l_offset;
if (l_len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = (loff_t)end;
error = 0;
out:
return error;
out_overflow:
return -EIO;
}
/*
* string caller_name<LM_MAXSTRLEN>;
*/
static void encode_caller_name(struct xdr_stream *xdr, const char *name)
{
/* NB: client-side does not set lock->len */
u32 length = strlen(name);
__be32 *p;
p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, name, length);
}
/*
* struct nlm_lock {
* string caller_name<LM_MAXSTRLEN>;
* netobj fh;
* netobj oh;
* int uppid;
* unsigned l_offset;
* unsigned l_len;
* };
*/
static void encode_nlm_lock(struct xdr_stream *xdr,
const struct lockd_lock *lock)
{
u32 l_offset, l_len;
__be32 *p;
encode_caller_name(xdr, lock->caller);
encode_fh(xdr, &lock->fh);
encode_netobj(xdr, lock->oh.data, lock->oh.len);
p = xdr_reserve_space(xdr, 4 + 4 + 4);
*p++ = cpu_to_be32(lock->svid);
nlm_compute_offsets(lock, &l_offset, &l_len);
*p++ = cpu_to_be32(l_offset);
*p = cpu_to_be32(l_len);
}
/*
* NLMv3 XDR encode functions
*
* NLMv3 argument types are defined in Chapter 10 of The Open Group's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* struct nlm_testargs {
* netobj cookie;
* bool exclusive;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_lockargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm_lock alock;
* bool reclaim;
* int state;
* };
*/
static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
encode_bool(xdr, args->reclaim);
encode_int32(xdr, args->state);
}
/*
* struct nlm_cancargs {
* netobj cookie;
* bool block;
* bool exclusive;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.c.flc_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_unlockargs {
* netobj cookie;
* struct nlm_lock alock;
* };
*/
static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_args *args = data;
const struct lockd_lock *lock = &args->lock;
encode_cookie(xdr, &args->cookie);
encode_nlm_lock(xdr, lock);
}
/*
* struct nlm_res {
* netobj cookie;
* nlm_stat stat;
* };
*/
static void nlm_xdr_enc_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
}
/*
* union nlm_testrply switch (nlm_stats stat) {
* case LCK_DENIED:
* struct nlm_holder holder;
* default:
* void;
* };
*
* struct nlm_testres {
* netobj cookie;
* nlm_testrply test_stat;
* };
*/
static void encode_nlm_testrply(struct xdr_stream *xdr,
const struct lockd_res *result)
{
if (result->status == nlm_lck_denied)
encode_nlm_holder(xdr, result);
}
static void nlm_xdr_enc_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct lockd_res *result = data;
encode_cookie(xdr, &result->cookie);
encode_nlm_stat(xdr, result->status);
encode_nlm_testrply(xdr, result);
}
/*
* NLMv3 XDR decode functions
*
* NLMv3 result types are defined in Chapter 10 of The Open Group's
* "Protocols for Interworking: XNFS, Version 3W".
*/
/*
* union nlm_testrply switch (nlm_stats stat) {
* case LCK_DENIED:
* struct nlm_holder holder;
* default:
* void;
* };
*
* struct nlm_testres {
* netobj cookie;
* nlm_testrply test_stat;
* };
*/
static int decode_nlm_testrply(struct xdr_stream *xdr,
struct lockd_res *result)
{
int error;
error = decode_nlm_stat(xdr, &result->status);
if (unlikely(error))
goto out;
if (result->status == nlm_lck_denied)
error = decode_nlm_holder(xdr, result);
out:
return error;
}
static int nlm_xdr_dec_testres(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm_testrply(xdr, result);
out:
return error;
}
/*
* struct nlm_res {
* netobj cookie;
* nlm_stat stat;
* };
*/
static int nlm_xdr_dec_res(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *data)
{
struct lockd_res *result = data;
int error;
error = decode_cookie(xdr, &result->cookie);
if (unlikely(error))
goto out;
error = decode_nlm_stat(xdr, &result->status);
out:
return error;
}
/*
* For NLM, a void procedure really returns nothing
*/
#define nlm_xdr_dec_norep NULL
#define PROC(proc, argtype, restype) \
[NLMPROC_##proc] = { \
.p_proc = NLMPROC_##proc, \
.p_encode = nlm_xdr_enc_##argtype, \
.p_decode = nlm_xdr_dec_##restype, \
.p_arglen = NLM_##argtype##_sz, \
.p_replen = NLM_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \
.p_name = #proc, \
}
static const 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),
};
static unsigned int nlm_version1_counts[ARRAY_SIZE(nlm_procedures)];
static const struct rpc_version nlm_version1 = {
.number = 1,
.nrprocs = ARRAY_SIZE(nlm_procedures),
.procs = nlm_procedures,
.counts = nlm_version1_counts,
};
static unsigned int nlm_version3_counts[ARRAY_SIZE(nlm_procedures)];
static const struct rpc_version nlm_version3 = {
.number = 3,
.nrprocs = ARRAY_SIZE(nlm_procedures),
.procs = nlm_procedures,
.counts = nlm_version3_counts,
};
static const struct rpc_version *nlm_versions[] = {
[1] = &nlm_version1,
[3] = &nlm_version3,
#ifdef CONFIG_LOCKD_V4
[4] = &nlm_version4,
#endif
};
static struct rpc_stat nlm_rpc_stats;
const struct rpc_program nlm_program = {
.name = "lockd",
.number = NLM_PROGRAM,
.nrvers = ARRAY_SIZE(nlm_versions),
.version = nlm_versions,
.stats = &nlm_rpc_stats,
};
+723
View File
@@ -0,0 +1,723 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/in6.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/svc.h>
#include <linux/mutex.h>
#include <linux/sunrpc/svc_xprt.h>
#include <net/ipv6.h>
#include "lockd.h"
#include "netns.h"
#define NLMDBG_FACILITY NLMDBG_HOSTCACHE
#define NLM_HOST_NRHASH 32
#define NLM_HOST_REBIND (60 * HZ)
#define NLM_HOST_EXPIRE (300 * HZ)
#define NLM_HOST_COLLECT (120 * HZ)
static struct hlist_head nlm_server_hosts[NLM_HOST_NRHASH];
static struct hlist_head nlm_client_hosts[NLM_HOST_NRHASH];
#define for_each_host(host, chain, table) \
for ((chain) = (table); \
(chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
hlist_for_each_entry((host), (chain), h_hash)
#define for_each_host_safe(host, next, chain, table) \
for ((chain) = (table); \
(chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
hlist_for_each_entry_safe((host), (next), \
(chain), h_hash)
static unsigned long nrhosts;
static DEFINE_MUTEX(nlm_host_mutex);
static void nlm_gc_hosts(struct net *net);
struct nlm_lookup_host_info {
const int server; /* search for server|client */
const struct sockaddr *sap; /* address to search for */
const size_t salen; /* it's length */
const unsigned short protocol; /* transport to search for*/
const u32 version; /* NLM version to search for */
const char *hostname; /* remote's hostname */
const size_t hostname_len; /* it's length */
const int noresvport; /* use non-priv port */
struct net *net; /* network namespace to bind */
const struct cred *cred;
};
/*
* Hash function must work well on big- and little-endian platforms
*/
static unsigned int __nlm_hash32(const __be32 n)
{
unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
return hash ^ (hash >> 8);
}
static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
{
const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
return __nlm_hash32(sin->sin_addr.s_addr);
}
static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
{
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
const struct in6_addr addr = sin6->sin6_addr;
return __nlm_hash32(addr.s6_addr32[0]) ^
__nlm_hash32(addr.s6_addr32[1]) ^
__nlm_hash32(addr.s6_addr32[2]) ^
__nlm_hash32(addr.s6_addr32[3]);
}
static unsigned int nlm_hash_address(const struct sockaddr *sap)
{
unsigned int hash;
switch (sap->sa_family) {
case AF_INET:
hash = __nlm_hash_addr4(sap);
break;
case AF_INET6:
hash = __nlm_hash_addr6(sap);
break;
default:
hash = 0;
}
return hash & (NLM_HOST_NRHASH - 1);
}
/*
* Allocate and initialize an nlm_host. Common to both client and server.
*/
static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
struct nsm_handle *nsm)
{
struct nlm_host *host = NULL;
unsigned long now = jiffies;
if (nsm != NULL)
refcount_inc(&nsm->sm_count);
else {
nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
ni->hostname, ni->hostname_len);
if (unlikely(nsm == NULL)) {
dprintk("lockd: %s failed; no nsm handle\n",
__func__);
goto out;
}
}
host = kmalloc_obj(*host);
if (unlikely(host == NULL)) {
dprintk("lockd: %s failed; no memory\n", __func__);
nsm_release(nsm);
goto out;
}
memcpy(nlm_addr(host), ni->sap, ni->salen);
host->h_addrlen = ni->salen;
rpc_set_port(nlm_addr(host), 0);
host->h_srcaddrlen = 0;
host->h_rpcclnt = NULL;
host->h_name = nsm->sm_name;
host->h_version = ni->version;
host->h_proto = ni->protocol;
host->h_reclaiming = 0;
host->h_server = ni->server;
host->h_noresvport = ni->noresvport;
host->h_inuse = 0;
init_waitqueue_head(&host->h_gracewait);
init_rwsem(&host->h_rwsem);
host->h_state = 0;
host->h_nsmstate = 0;
host->h_pidcount = 0;
refcount_set(&host->h_count, 1);
mutex_init(&host->h_mutex);
host->h_nextrebind = now + NLM_HOST_REBIND;
host->h_expires = now + NLM_HOST_EXPIRE;
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
INIT_LIST_HEAD(&host->h_granted);
INIT_LIST_HEAD(&host->h_reclaim);
host->h_nsmhandle = nsm;
host->h_addrbuf = nsm->sm_addrbuf;
host->net = ni->net;
host->h_cred = get_cred(ni->cred);
strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
out:
return host;
}
/*
* Destroy an nlm_host and free associated resources
*
* Caller must hold nlm_host_mutex.
*/
static void nlm_destroy_host_locked(struct nlm_host *host)
{
struct rpc_clnt *clnt;
struct lockd_net *ln = net_generic(host->net, lockd_net_id);
dprintk("lockd: destroy host %s\n", host->h_name);
hlist_del_init(&host->h_hash);
nsm_unmonitor(host);
nsm_release(host->h_nsmhandle);
clnt = host->h_rpcclnt;
if (clnt != NULL)
rpc_shutdown_client(clnt);
put_cred(host->h_cred);
kfree(host);
ln->nrhosts--;
nrhosts--;
}
/**
* nlmclnt_lookup_host - Find an NLM host handle matching a remote server
* @sap: network address of server
* @salen: length of server address
* @protocol: transport protocol to use
* @version: NLM protocol version
* @hostname: '\0'-terminated hostname of server
* @noresvport: 1 if non-privileged port should be used
* @net: pointer to net namespace
* @cred: pointer to cred
*
* Returns an nlm_host structure that matches the passed-in
* [server address, transport protocol, NLM version, server hostname].
* If one doesn't already exist in the host cache, a new handle is
* created and returned.
*/
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen,
const unsigned short protocol,
const u32 version,
const char *hostname,
int noresvport,
struct net *net,
const struct cred *cred)
{
struct nlm_lookup_host_info ni = {
.server = 0,
.sap = sap,
.salen = salen,
.protocol = protocol,
.version = version,
.hostname = hostname,
.hostname_len = strlen(hostname),
.noresvport = noresvport,
.net = net,
.cred = cred,
};
struct hlist_head *chain;
struct nlm_host *host;
struct nsm_handle *nsm = NULL;
struct lockd_net *ln = net_generic(net, lockd_net_id);
dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
(hostname ? hostname : "<none>"), version,
(protocol == IPPROTO_UDP ? "udp" : "tcp"));
mutex_lock(&nlm_host_mutex);
chain = &nlm_client_hosts[nlm_hash_address(sap)];
hlist_for_each_entry(host, chain, h_hash) {
if (host->net != net)
continue;
if (!rpc_cmp_addr(nlm_addr(host), sap))
continue;
/* Same address. Share an NSM handle if we already have one */
if (nsm == NULL)
nsm = host->h_nsmhandle;
if (host->h_proto != protocol)
continue;
if (host->h_version != version)
continue;
nlm_get_host(host);
dprintk("lockd: %s found host %s (%s)\n", __func__,
host->h_name, host->h_addrbuf);
goto out;
}
host = nlm_alloc_host(&ni, nsm);
if (unlikely(host == NULL))
goto out;
hlist_add_head(&host->h_hash, chain);
ln->nrhosts++;
nrhosts++;
dprintk("lockd: %s created host %s (%s)\n", __func__,
host->h_name, host->h_addrbuf);
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/**
* nlmclnt_release_host - release client nlm_host
* @host: nlm_host to release
*
*/
void nlmclnt_release_host(struct nlm_host *host)
{
if (host == NULL)
return;
dprintk("lockd: release client host %s\n", host->h_name);
WARN_ON_ONCE(host->h_server);
if (refcount_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
WARN_ON_ONCE(!list_empty(&host->h_lockowners));
WARN_ON_ONCE(!list_empty(&host->h_granted));
WARN_ON_ONCE(!list_empty(&host->h_reclaim));
nlm_destroy_host_locked(host);
mutex_unlock(&nlm_host_mutex);
}
}
/* Callback for rpc_cancel_tasks() - matches all tasks for cancellation */
static bool nlmclnt_match_all(const struct rpc_task *task, const void *data)
{
return true;
}
/**
* nlmclnt_shutdown_rpc_clnt - safely shut down NLM client RPC operations
* @host: nlm_host to shut down
*
* Cancels outstanding RPC tasks and marks the client as shut down.
* Synchronizes with nlmclnt_release_host() via nlm_host_mutex to prevent
* races between shutdown and host destruction. Safe to call if h_rpcclnt
* is NULL or already shut down.
*/
void nlmclnt_shutdown_rpc_clnt(struct nlm_host *host)
{
struct rpc_clnt *clnt;
mutex_lock(&nlm_host_mutex);
clnt = host->h_rpcclnt;
if (clnt) {
clnt->cl_shutdown = 1;
rpc_cancel_tasks(clnt, -EIO, nlmclnt_match_all, NULL);
}
mutex_unlock(&nlm_host_mutex);
}
EXPORT_SYMBOL_GPL(nlmclnt_shutdown_rpc_clnt);
/**
* nlmsvc_lookup_host - Find an NLM host handle matching a remote client
* @rqstp: incoming NLM request
* @hostname: name of client host
* @hostname_len: length of client hostname
*
* Returns an nlm_host structure that matches the [client address,
* transport protocol, NLM version, client hostname] of the passed-in
* NLM request. If one doesn't already exist in the host cache, a
* new handle is created and returned.
*
* Before possibly creating a new nlm_host, construct a sockaddr
* for a specific source address in case the local system has
* multiple network addresses. The family of the address in
* rq_daddr is guaranteed to be the same as the family of the
* address in rq_addr, so it's safe to use the same family for
* the source address.
*/
struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname,
const size_t hostname_len)
{
struct hlist_head *chain;
struct nlm_host *host = NULL;
struct nsm_handle *nsm = NULL;
struct sockaddr *src_sap = svc_daddr(rqstp);
size_t src_len = rqstp->rq_daddrlen;
struct net *net = SVC_NET(rqstp);
struct nlm_lookup_host_info ni = {
.server = 1,
.sap = svc_addr(rqstp),
.salen = rqstp->rq_addrlen,
.protocol = rqstp->rq_prot,
.version = rqstp->rq_vers,
.hostname = hostname,
.hostname_len = hostname_len,
.net = net,
};
struct lockd_net *ln = net_generic(net, lockd_net_id);
dprintk("lockd: %s(host='%.*s', vers=%u, proto=%s)\n", __func__,
(int)hostname_len, hostname, rqstp->rq_vers,
(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
mutex_lock(&nlm_host_mutex);
if (time_after_eq(jiffies, ln->next_gc))
nlm_gc_hosts(net);
chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
hlist_for_each_entry(host, chain, h_hash) {
if (host->net != net)
continue;
if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
continue;
/* Same address. Share an NSM handle if we already have one */
if (nsm == NULL)
nsm = host->h_nsmhandle;
if (host->h_proto != ni.protocol)
continue;
if (host->h_version != ni.version)
continue;
if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
continue;
/* Move to head of hash chain. */
hlist_del(&host->h_hash);
hlist_add_head(&host->h_hash, chain);
nlm_get_host(host);
dprintk("lockd: %s found host %s (%s)\n",
__func__, host->h_name, host->h_addrbuf);
goto out;
}
host = nlm_alloc_host(&ni, nsm);
if (unlikely(host == NULL))
goto out;
memcpy(nlm_srcaddr(host), src_sap, src_len);
host->h_srcaddrlen = src_len;
hlist_add_head(&host->h_hash, chain);
ln->nrhosts++;
nrhosts++;
refcount_inc(&host->h_count);
dprintk("lockd: %s created host %s (%s)\n",
__func__, host->h_name, host->h_addrbuf);
out:
mutex_unlock(&nlm_host_mutex);
return host;
}
/**
* nlmsvc_release_host - release server nlm_host
* @host: nlm_host to release
*
* Host is destroyed later in nlm_gc_host().
*/
void nlmsvc_release_host(struct nlm_host *host)
{
if (host == NULL)
return;
dprintk("lockd: release server host %s\n", host->h_name);
WARN_ON_ONCE(!host->h_server);
refcount_dec(&host->h_count);
}
/*
* 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 %s (%s)\n",
host->h_name, host->h_addrbuf);
/* 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) {
nlm_rebind_host(host);
} else {
unsigned long increment = nlm_timeout * HZ;
struct rpc_timeout timeparms = {
.to_initval = increment,
.to_increment = increment,
.to_maxval = increment * 6UL,
.to_retries = 5U,
};
struct rpc_create_args args = {
.net = host->net,
.protocol = host->h_proto,
.address = nlm_addr(host),
.addrsize = host->h_addrlen,
.timeout = &timeparms,
.servername = host->h_name,
.program = &nlm_program,
.version = host->h_version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_NOPING |
RPC_CLNT_CREATE_AUTOBIND |
RPC_CLNT_CREATE_REUSEPORT),
.cred = host->h_cred,
};
/*
* lockd retries server side blocks automatically so we want
* those to be soft RPC calls. Client side calls need to be
* hard RPC tasks.
*/
if (!host->h_server)
args.flags |= RPC_CLNT_CREATE_HARDRTRY;
if (host->h_noresvport)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
if (host->h_srcaddrlen)
args.saddress = nlm_srcaddr(host);
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;
}
/**
* nlm_rebind_host - If needed, force a portmap lookup of the peer's lockd port
* @host: NLM host handle for peer
*
* This is not needed when using a connection-oriented protocol, such as TCP.
* The existing autobind mechanism is sufficient to force a rebind when
* required, e.g. on connection state transitions.
*/
void
nlm_rebind_host(struct nlm_host *host)
{
if (host->h_proto != IPPROTO_UDP)
return;
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);
refcount_inc(&host->h_count);
host->h_expires = jiffies + NLM_HOST_EXPIRE;
}
return host;
}
static struct nlm_host *next_host_state(struct hlist_head *cache,
struct nsm_handle *nsm,
const struct lockd_reboot *info)
{
struct nlm_host *host;
struct hlist_head *chain;
mutex_lock(&nlm_host_mutex);
for_each_host(host, chain, cache) {
if (host->h_nsmhandle == nsm
&& host->h_nsmstate != info->state) {
host->h_nsmstate = info->state;
host->h_state++;
nlm_get_host(host);
mutex_unlock(&nlm_host_mutex);
return host;
}
}
mutex_unlock(&nlm_host_mutex);
return NULL;
}
/**
* nlm_host_rebooted - Release all resources held by rebooted host
* @net: network namespace
* @info: pointer to decoded results of NLM_SM_NOTIFY call
*
* We were notified that the specified host has rebooted. Release
* all resources held by that peer.
*/
void nlm_host_rebooted(const struct net *net, const struct lockd_reboot *info)
{
struct nsm_handle *nsm;
struct nlm_host *host;
nsm = nsm_reboot_lookup(net, info);
if (unlikely(nsm == NULL))
return;
/* 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.
*/
while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
nlmsvc_free_host_resources(host);
nlmsvc_release_host(host);
}
while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
nlmclnt_recovery(host);
nlmclnt_release_host(host);
}
nsm_release(nsm);
}
static void nlm_complain_hosts(struct net *net)
{
struct hlist_head *chain;
struct nlm_host *host;
if (net) {
struct lockd_net *ln = net_generic(net, lockd_net_id);
if (ln->nrhosts == 0)
return;
pr_warn("lockd: couldn't shutdown host module for net %x!\n",
net->ns.inum);
dprintk("lockd: %lu hosts left in net %x:\n", ln->nrhosts,
net->ns.inum);
} else {
if (nrhosts == 0)
return;
printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
dprintk("lockd: %lu hosts left:\n", nrhosts);
}
for_each_host(host, chain, nlm_server_hosts) {
if (net && host->net != net)
continue;
dprintk(" %s (cnt %d use %d exp %ld net %x)\n",
host->h_name, refcount_read(&host->h_count),
host->h_inuse, host->h_expires, host->net->ns.inum);
}
}
void
nlm_shutdown_hosts_net(struct net *net)
{
struct hlist_head *chain;
struct nlm_host *host;
mutex_lock(&nlm_host_mutex);
/* First, make all hosts eligible for gc */
dprintk("lockd: nuking all hosts in net %x...\n",
net ? net->ns.inum : 0);
for_each_host(host, chain, nlm_server_hosts) {
if (net && host->net != net)
continue;
host->h_expires = jiffies - 1;
if (host->h_rpcclnt) {
rpc_shutdown_client(host->h_rpcclnt);
host->h_rpcclnt = NULL;
}
nlmsvc_free_host_resources(host);
}
/* Then, perform a garbage collection pass */
nlm_gc_hosts(net);
nlm_complain_hosts(net);
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)
{
dprintk("lockd: shutting down host module\n");
nlm_shutdown_hosts_net(NULL);
}
/*
* 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(struct net *net)
{
struct hlist_head *chain;
struct hlist_node *next;
struct nlm_host *host;
dprintk("lockd: host garbage collection for net %x\n",
net ? net->ns.inum : 0);
for_each_host(host, chain, nlm_server_hosts) {
if (net && host->net != net)
continue;
host->h_inuse = 0;
}
/* Mark all hosts that hold locks, blocks or shares */
nlmsvc_mark_resources(net);
for_each_host_safe(host, next, chain, nlm_server_hosts) {
if (net && host->net != net)
continue;
if (host->h_inuse || time_before(jiffies, host->h_expires)) {
dprintk("nlm_gc_hosts skipping %s "
"(cnt %d use %d exp %ld net %x)\n",
host->h_name, refcount_read(&host->h_count),
host->h_inuse, host->h_expires,
host->net->ns.inum);
continue;
}
if (refcount_dec_if_one(&host->h_count))
nlm_destroy_host_locked(host);
}
if (net) {
struct lockd_net *ln = net_generic(net, lockd_net_id);
ln->next_gc = jiffies + NLM_HOST_COLLECT;
}
}
+483
View File
@@ -0,0 +1,483 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LOCKD_LOCKD_H
#define _LOCKD_LOCKD_H
#include <linux/exportfs.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/ipv6.h>
#include <linux/fs.h>
#include <linux/kref.h>
#include <linux/refcount.h>
#include <linux/utsname.h>
#include "nlm.h"
#include <linux/lockd/bind.h>
#include "xdr.h"
#include <linux/sunrpc/debug.h>
#include <linux/sunrpc/svc.h>
/*
* Enable lockd debugging.
* Requires CONFIG_SUNRPC_DEBUG.
*/
#undef ifdebug
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define ifdebug(flag) if (unlikely(nlm_debug & NLMDBG_##flag))
#else
# define ifdebug(flag) if (0)
#endif
#define NLMDBG_SVC 0x0001
#define NLMDBG_CLIENT 0x0002
#define NLMDBG_CLNTLOCK 0x0004
#define NLMDBG_SVCLOCK 0x0008
#define NLMDBG_MONITOR 0x0010
#define NLMDBG_CLNTSUBS 0x0020
#define NLMDBG_SVCSUBS 0x0040
#define NLMDBG_HOSTCACHE 0x0080
#define NLMDBG_XDR 0x0100
#define NLMDBG_ALL 0x7fff
/*
* Version string
*/
#define LOCKD_VERSION "0.5"
/*
* Default timeout for RPC calls (seconds)
*/
#define LOCKD_DFLT_TIMEO 10
/*
* Number of leading bytes of nfs_fh.data that file_hash()
* digests when bucketing nlm_files[]. Sized for historical
* NFSv2 handles; nfs_fh.data must be initialized at least
* this far before lookup, regardless of fh.size.
*/
#define LOCKD_FH_HASH_SIZE 32
/* error codes new to NLMv4 */
#define nlm4_deadlock cpu_to_be32(NLM_DEADLCK)
#define nlm4_rofs cpu_to_be32(NLM_ROFS)
#define nlm4_stale_fh cpu_to_be32(NLM_STALE_FH)
#define nlm4_fbig cpu_to_be32(NLM_FBIG)
#define nlm4_failed cpu_to_be32(NLM_FAILED)
/*
* Internal-use status codes, not to be placed on the wire.
* Version handlers translate these to appropriate wire values.
*/
#define nlm__int__drop_reply cpu_to_be32(30000)
#define nlm__int__deadlock cpu_to_be32(30001)
#define nlm__int__stale_fh cpu_to_be32(30002)
#define nlm__int__failed cpu_to_be32(30003)
/*
* Lockd host handle (used both by the client and server personality).
*/
struct nlm_host {
struct hlist_node h_hash; /* doubly linked list */
struct sockaddr_storage h_addr; /* peer address */
size_t h_addrlen;
struct sockaddr_storage h_srcaddr; /* our address (optional) */
size_t h_srcaddrlen;
struct rpc_clnt *h_rpcclnt; /* RPC client to talk to peer */
char *h_name; /* remote hostname */
u32 h_version; /* interface version */
unsigned short h_proto; /* transport proto */
unsigned short h_reclaiming : 1,
h_server : 1, /* server side, not client side */
h_noresvport : 1,
h_inuse : 1;
wait_queue_head_t h_gracewait; /* wait while reclaiming */
struct rw_semaphore h_rwsem; /* Reboot recovery lock */
u32 h_state; /* pseudo-state counter */
u32 h_nsmstate; /* true remote NSM state */
u32 h_pidcount; /* Pseudopids */
refcount_t h_count; /* reference count */
struct mutex h_mutex; /* mutex for pmap binding */
unsigned long h_nextrebind; /* next portmap call */
unsigned long h_expires; /* eligible for GC */
struct list_head h_lockowners; /* Lockowners for the client */
spinlock_t h_lock;
struct list_head h_granted; /* Locks in GRANTED state */
struct list_head h_reclaim; /* Locks in RECLAIM state */
struct nsm_handle *h_nsmhandle; /* NSM status handle */
char *h_addrbuf; /* address eyecatcher */
struct net *net; /* host net */
const struct cred *h_cred;
char nodename[UNX_MAXNODENAME + 1];
const struct nlmclnt_operations *h_nlmclnt_ops; /* Callback ops for NLM users */
};
/*
* The largest string sm_addrbuf should hold is a full-size IPv6 address
* (no "::" anywhere) with a scope ID. The buffer size is computed to
* hold eight groups of colon-separated four-hex-digit numbers, a
* percent sign, a scope id (at most 32 bits, in decimal), and NUL.
*/
#define NSM_ADDRBUF ((8 * 4 + 7) + (1 + 10) + 1)
struct nsm_handle {
struct list_head sm_link;
refcount_t sm_count;
char *sm_mon_name;
char *sm_name;
struct sockaddr_storage sm_addr;
size_t sm_addrlen;
unsigned int sm_monitored : 1,
sm_sticky : 1; /* don't unmonitor */
struct nsm_private sm_priv;
char sm_addrbuf[NSM_ADDRBUF];
};
/*
* Rigorous type checking on sockaddr type conversions
*/
static inline struct sockaddr *nlm_addr(const struct nlm_host *host)
{
return (struct sockaddr *)&host->h_addr;
}
static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host)
{
return (struct sockaddr *)&host->h_srcaddr;
}
/*
* Map an fl_owner_t into a unique 32-bit "pid"
*/
struct nlm_lockowner {
struct list_head list;
refcount_t count;
struct nlm_host *host;
fl_owner_t owner;
uint32_t pid;
};
/*
* 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 */
__be32 b_status; /* grant callback status */
};
/*
* Memory chunk for NLM client RPC request.
*/
#define NLMCLNT_OHSIZE ((__NEW_UTS_LEN) + 10u)
struct nlm_rqst {
refcount_t a_count;
unsigned int a_flags; /* initial RPC task flags */
struct nlm_host * a_host; /* host handle */
struct lockd_args a_args; /* arguments */
struct lockd_res a_res; /* result */
struct nlm_block * a_block;
unsigned int a_retries; /* Retry count */
u8 a_owner[NLMCLNT_OHSIZE];
void * a_callback_data; /* sent to nlmclnt_operations callbacks */
};
struct lockd_share;
/*
* This struct describes a file held open by lockd on behalf of
* an NFS client.
*/
struct nlm_file {
struct hlist_node f_list; /* linked list */
struct nfs_fh f_handle; /* NFS file handle */
struct file * f_file[2]; /* VFS file pointers,
indexed by O_ flags */
struct lockd_share * f_shares; /* DOS shares */
struct list_head f_blocks; /* blocked locks */
unsigned int f_locks; /* guesstimate # of locks */
unsigned int f_count; /* reference count */
struct mutex f_mutex; /* avoid concurrent access */
};
/*
* This is a server block (i.e. a lock requested by some client which
* couldn't be granted because of a conflicting lock).
*/
#define NLM_NEVER (~(unsigned long) 0)
/* timeout on non-blocking call: */
#define NLM_TIMEOUT (7 * HZ)
struct nlm_block {
struct kref b_count; /* Reference count */
struct list_head b_list; /* linked list of all blocks */
struct list_head b_flist; /* linked list (per file) */
struct nlm_rqst * b_call; /* RPC args & callback info */
struct svc_serv * b_daemon; /* NLM service */
struct nlm_host * b_host; /* host handle for RPC clnt */
unsigned long b_when; /* next re-xmit */
unsigned int b_id; /* block id */
unsigned char b_granted; /* VFS granted lock */
struct nlm_file * b_file; /* file in question */
struct cache_req * b_cache_req; /* deferred request handling */
struct cache_deferred_req * b_deferred_req;
unsigned int b_flags; /* block flags */
#define B_QUEUED 1 /* lock queued */
#define B_GOT_CALLBACK 2 /* got lock or conflicting lock */
#define B_TIMED_OUT 4 /* filesystem too slow to respond */
};
/*
* Global variables
*/
extern const struct rpc_program nlm_program;
extern const struct svc_version nlmsvc_version1;
extern const struct svc_version nlmsvc_version3;
#ifdef CONFIG_LOCKD_V4
extern const struct svc_version nlmsvc_version4;
#endif
extern int nlmsvc_grace_period;
extern unsigned long nlm_timeout;
extern bool nsm_use_hostnames;
extern u32 nsm_local_state;
extern struct timer_list nlmsvc_retry;
/*
* Lockd client functions
*/
struct nlm_rqst * nlm_alloc_call(struct nlm_host *host);
int nlm_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *);
int nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *);
void nlmclnt_release_call(struct nlm_rqst *);
void nlmclnt_prepare_block(struct nlm_wait *block, struct nlm_host *host,
struct file_lock *fl);
void nlmclnt_queue_block(struct nlm_wait *block);
__be32 nlmclnt_dequeue_block(struct nlm_wait *block);
int nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout);
__be32 nlmclnt_grant(const struct sockaddr *addr,
const struct lockd_lock *lock);
void nlmclnt_recovery(struct nlm_host *);
int nlmclnt_reclaim(struct nlm_host *, struct file_lock *,
struct nlm_rqst *);
void nlmclnt_next_cookie(struct lockd_cookie *);
#ifdef CONFIG_LOCKD_V4
extern const struct rpc_version nlm_version4;
#endif
/*
* Host cache
*/
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen,
const unsigned short protocol,
const u32 version,
const char *hostname,
int noresvport,
struct net *net,
const struct cred *cred);
void nlmclnt_release_host(struct nlm_host *);
struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname,
const size_t hostname_len);
void nlmsvc_release_host(struct nlm_host *);
struct rpc_clnt * nlm_bind_host(struct nlm_host *);
void nlm_rebind_host(struct nlm_host *);
struct nlm_host * nlm_get_host(struct nlm_host *);
void nlm_shutdown_hosts(void);
void nlm_shutdown_hosts_net(struct net *net);
void nlm_host_rebooted(const struct net *net,
const struct lockd_reboot *);
/*
* Host monitoring
*/
int nsm_monitor(const struct nlm_host *host);
void nsm_unmonitor(const struct nlm_host *host);
struct nsm_handle *nsm_get_handle(const struct net *net,
const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len);
struct nsm_handle *nsm_reboot_lookup(const struct net *net,
const struct lockd_reboot *info);
void nsm_release(struct nsm_handle *nsm);
/*
* This is used in garbage collection and resource reclaim
* A return value != 0 means destroy the lock/block/share
*/
typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
/*
* Server-side lock handling
*/
int lock_to_openmode(struct file_lock *);
__be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
struct nlm_host *, struct lockd_lock *, int,
struct lockd_cookie *, int);
__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct lockd_lock *);
__be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_host *host, struct lockd_lock *lock,
struct lockd_lock *conflock);
__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct lockd_lock *);
void nlmsvc_retry_blocked(struct svc_rqst *rqstp);
void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
nlm_host_match_fn_t match);
void nlmsvc_grant_reply(struct lockd_cookie *, __be32);
void nlmsvc_release_call(struct nlm_rqst *);
void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t);
int nlmsvc_dispatch(struct svc_rqst *rqstp);
/*
* File handling for the server personality
*/
__be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
struct lockd_lock *, int);
void nlm_release_file(struct nlm_file *);
void nlmsvc_put_lockowner(struct nlm_lockowner *);
void nlmsvc_release_lockowner(struct lockd_lock *);
void nlmsvc_mark_resources(struct net *);
void nlmsvc_free_host_resources(struct nlm_host *);
void nlmsvc_invalidate_all(void);
static inline struct file *nlmsvc_file_file(const struct nlm_file *file)
{
return file->f_file[O_RDONLY] ?
file->f_file[O_RDONLY] : file->f_file[O_WRONLY];
}
static inline struct inode *nlmsvc_file_inode(struct nlm_file *file)
{
return file_inode(nlmsvc_file_file(file));
}
static inline bool
nlmsvc_file_cannot_lock(const struct nlm_file *file)
{
return exportfs_cannot_lock(nlmsvc_file_file(file)->f_path.dentry->d_sb->s_export_op);
}
static inline int __nlm_privileged_request4(const struct sockaddr *sap)
{
const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
if (ntohs(sin->sin_port) > 1023)
return 0;
return ipv4_is_loopback(sin->sin_addr.s_addr);
}
#if IS_ENABLED(CONFIG_IPV6)
static inline int __nlm_privileged_request6(const struct sockaddr *sap)
{
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
if (ntohs(sin6->sin6_port) > 1023)
return 0;
if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_MAPPED)
return ipv4_is_loopback(sin6->sin6_addr.s6_addr32[3]);
return ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK;
}
#else /* IS_ENABLED(CONFIG_IPV6) */
static inline int __nlm_privileged_request6(const struct sockaddr *sap)
{
return 0;
}
#endif /* IS_ENABLED(CONFIG_IPV6) */
/*
* Ensure incoming requests are from local privileged callers.
*
* Return TRUE if sender is local and is connecting via a privileged port;
* otherwise return FALSE.
*/
static inline int nlm_privileged_requester(const struct svc_rqst *rqstp)
{
const struct sockaddr *sap = svc_addr(rqstp);
switch (sap->sa_family) {
case AF_INET:
return __nlm_privileged_request4(sap);
case AF_INET6:
return __nlm_privileged_request6(sap);
default:
return 0;
}
}
/*
* Compare two NLM locks.
* When the second lock is of type F_UNLCK, this acts like a wildcard.
*/
static inline int nlm_compare_locks(const struct file_lock *fl1,
const struct file_lock *fl2)
{
return file_inode(fl1->c.flc_file) == file_inode(fl2->c.flc_file)
&& fl1->c.flc_pid == fl2->c.flc_pid
&& fl1->c.flc_owner == fl2->c.flc_owner
&& fl1->fl_start == fl2->fl_start
&& fl1->fl_end == fl2->fl_end
&&(fl1->c.flc_type == fl2->c.flc_type || fl2->c.flc_type == F_UNLCK);
}
/**
* lockd_set_file_lock_range3 - set the byte range of a file_lock
* @fl: file_lock whose length fields are to be initialized
* @off: starting offset of the lock, in bytes
* @len: length of the byte range, in bytes, or zero
*
* NLMv3 uses a (start, length) representation for lock byte ranges,
* while the kernel's file_lock uses (start, end). Treat a length of
* zero or arithmetic overflow (end wrapping negative when the sum
* exceeds S32_MAX) as "lock to end of file."
*/
static inline void
lockd_set_file_lock_range3(struct file_lock *fl, u32 off, u32 len)
{
s32 end = off + len - 1;
fl->fl_start = off;
if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = end;
}
/**
* lockd_set_file_lock_range4 - set the byte range of a file_lock
* @fl: file_lock whose length fields are to be initialized
* @off: starting offset of the lock, in bytes
* @len: length of the byte range, in bytes, or zero
*
* The NLMv4 protocol represents lock byte ranges as (start, length),
* where length zero means "lock to end of file." The kernel's file_lock
* structure uses (start, end) representation. Convert from NLMv4 format
* to file_lock format, clamping the starting offset and treating
* arithmetic overflow as "lock to EOF."
*/
static inline void
lockd_set_file_lock_range4(struct file_lock *fl, u64 off, u64 len)
{
u64 clamped_off = (off > OFFSET_MAX) ? OFFSET_MAX : off;
s64 end = clamped_off + len - 1;
fl->fl_start = clamped_off;
if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = end;
}
extern const struct lock_manager_operations nlmsvc_lock_operations;
#endif /* _LOCKD_LOCKD_H */
+581
View File
@@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/lockd/mon.c
*
* The kernel statd client.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/xprtsock.h>
#include <linux/sunrpc/svc.h>
#include <linux/unaligned.h>
#include "lockd.h"
#include "netns.h"
#define NLMDBG_FACILITY NLMDBG_MONITOR
#define NSM_PROGRAM 100024
#define NSM_VERSION 1
enum {
NSMPROC_NULL,
NSMPROC_STAT,
NSMPROC_MON,
NSMPROC_UNMON,
NSMPROC_UNMON_ALL,
NSMPROC_SIMU_CRASH,
NSMPROC_NOTIFY,
};
struct nsm_args {
struct nsm_private *priv;
u32 prog; /* RPC callback info */
u32 vers;
u32 proc;
char *mon_name;
const char *nodename;
};
struct nsm_res {
u32 status;
u32 state;
};
static const struct rpc_program nsm_program;
static DEFINE_SPINLOCK(nsm_lock);
/*
* Local NSM state
*/
u32 __read_mostly nsm_local_state;
bool __read_mostly nsm_use_hostnames;
static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
{
return (struct sockaddr *)&nsm->sm_addr;
}
static struct rpc_clnt *nsm_create(struct net *net, const char *nodename)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct rpc_create_args args = {
.net = net,
.protocol = XPRT_TRANSPORT_TCP,
.address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin),
.servername = "rpc.statd",
.nodename = nodename,
.program = &nsm_program,
.version = NSM_VERSION,
.authflavor = RPC_AUTH_NULL,
.flags = RPC_CLNT_CREATE_NOPING,
.cred = current_cred(),
};
return rpc_create(&args);
}
static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
const struct nlm_host *host)
{
int status;
struct rpc_clnt *clnt;
struct nsm_args args = {
.priv = &nsm->sm_priv,
.prog = NLM_PROGRAM,
.vers = 3,
.proc = NLMPROC_NSM_NOTIFY,
.mon_name = nsm->sm_mon_name,
.nodename = host->nodename,
};
struct rpc_message msg = {
.rpc_argp = &args,
.rpc_resp = res,
};
memset(res, 0, sizeof(*res));
clnt = nsm_create(host->net, host->nodename);
if (IS_ERR(clnt)) {
dprintk("lockd: failed to create NSM upcall transport, "
"status=%ld, net=%x\n", PTR_ERR(clnt),
host->net->ns.inum);
return PTR_ERR(clnt);
}
msg.rpc_proc = &clnt->cl_procinfo[proc];
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
if (status == -ECONNREFUSED) {
dprintk("lockd: NSM upcall RPC failed, status=%d, forcing rebind\n",
status);
rpc_force_rebind(clnt);
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
}
if (status < 0)
dprintk("lockd: NSM upcall RPC failed, status=%d\n",
status);
else
status = 0;
rpc_shutdown_client(clnt);
return status;
}
/**
* nsm_monitor - Notify a peer in case we reboot
* @host: pointer to nlm_host of peer to notify
*
* If this peer is not already monitored, this function sends an
* upcall to the local rpc.statd to record the name/address of
* the peer to notify in case we reboot.
*
* Returns zero if the peer is monitored by the local rpc.statd;
* otherwise a negative errno value is returned.
*/
int nsm_monitor(const struct nlm_host *host)
{
struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res;
int status;
dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
if (nsm->sm_monitored)
return 0;
/*
* Choose whether to record the caller_name or IP address of
* this peer in the local rpc.statd's database.
*/
nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, host);
if (unlikely(res.status != 0))
status = -EIO;
if (unlikely(status < 0)) {
pr_notice_ratelimited("lockd: cannot monitor %s\n", nsm->sm_name);
return status;
}
nsm->sm_monitored = 1;
if (unlikely(nsm_local_state != res.state)) {
nsm_local_state = res.state;
dprintk("lockd: NSM state changed to %d\n", nsm_local_state);
}
return 0;
}
/**
* nsm_unmonitor - Unregister peer notification
* @host: pointer to nlm_host of peer to stop monitoring
*
* If this peer is monitored, this function sends an upcall to
* tell the local rpc.statd not to send this peer a notification
* when we reboot.
*/
void nsm_unmonitor(const struct nlm_host *host)
{
struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res;
int status;
if (refcount_read(&nsm->sm_count) == 1
&& nsm->sm_monitored && !nsm->sm_sticky) {
dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, host);
if (res.status != 0)
status = -EIO;
if (status < 0)
printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
nsm->sm_name);
else
nsm->sm_monitored = 0;
}
}
static struct nsm_handle *nsm_lookup_hostname(const struct list_head *nsm_handles,
const char *hostname, const size_t len)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, nsm_handles, sm_link)
if (strlen(nsm->sm_name) == len &&
memcmp(nsm->sm_name, hostname, len) == 0)
return nsm;
return NULL;
}
static struct nsm_handle *nsm_lookup_addr(const struct list_head *nsm_handles,
const struct sockaddr *sap)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, nsm_handles, sm_link)
if (rpc_cmp_addr(nsm_addr(nsm), sap))
return nsm;
return NULL;
}
static struct nsm_handle *nsm_lookup_priv(const struct list_head *nsm_handles,
const struct nsm_private *priv)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, nsm_handles, sm_link)
if (memcmp(nsm->sm_priv.data, priv->data,
sizeof(priv->data)) == 0)
return nsm;
return NULL;
}
/*
* Construct a unique cookie to match this nsm_handle to this monitored
* host. It is passed to the local rpc.statd via NSMPROC_MON, and
* returned via NLMPROC_SM_NOTIFY, in the "priv" field of these
* requests.
*
* The NSM protocol requires that these cookies be unique while the
* system is running. We prefer a stronger requirement of making them
* unique across reboots. If user space bugs cause a stale cookie to
* be sent to the kernel, it could cause the wrong host to lose its
* lock state if cookies were not unique across reboots.
*
* The cookies are exposed only to local user space via loopback. They
* do not appear on the physical network. If we want greater security
* for some reason, nsm_init_private() could perform a one-way hash to
* obscure the contents of the cookie.
*/
static void nsm_init_private(struct nsm_handle *nsm)
{
u64 *p = (u64 *)&nsm->sm_priv.data;
s64 ns;
ns = ktime_get_ns();
put_unaligned(ns, p);
put_unaligned((unsigned long)nsm, p + 1);
}
static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len)
{
struct nsm_handle *new;
if (!hostname)
return NULL;
new = kzalloc(sizeof(*new) + hostname_len + 1, GFP_KERNEL);
if (unlikely(new == NULL))
return NULL;
refcount_set(&new->sm_count, 1);
new->sm_name = (char *)(new + 1);
memcpy(nsm_addr(new), sap, salen);
new->sm_addrlen = salen;
nsm_init_private(new);
if (rpc_ntop(nsm_addr(new), new->sm_addrbuf,
sizeof(new->sm_addrbuf)) == 0)
(void)snprintf(new->sm_addrbuf, sizeof(new->sm_addrbuf),
"unsupported address family");
memcpy(new->sm_name, hostname, hostname_len);
new->sm_name[hostname_len] = '\0';
return new;
}
/**
* nsm_get_handle - Find or create a cached nsm_handle
* @net: network namespace
* @sap: pointer to socket address of handle to find
* @salen: length of socket address
* @hostname: pointer to C string containing hostname to find
* @hostname_len: length of C string
*
* Behavior is modulated by the global nsm_use_hostnames variable.
*
* Returns a cached nsm_handle after bumping its ref count, or
* returns a fresh nsm_handle if a handle that matches @sap and/or
* @hostname cannot be found in the handle cache. Returns NULL if
* an error occurs.
*/
struct nsm_handle *nsm_get_handle(const struct net *net,
const struct sockaddr *sap,
const size_t salen, const char *hostname,
const size_t hostname_len)
{
struct nsm_handle *cached, *new = NULL;
struct lockd_net *ln = net_generic(net, lockd_net_id);
if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
if (printk_ratelimit()) {
printk(KERN_WARNING "Invalid hostname \"%.*s\" "
"in NFS lock request\n",
(int)hostname_len, hostname);
}
return NULL;
}
retry:
spin_lock(&nsm_lock);
if (nsm_use_hostnames && hostname != NULL)
cached = nsm_lookup_hostname(&ln->nsm_handles,
hostname, hostname_len);
else
cached = nsm_lookup_addr(&ln->nsm_handles, sap);
if (cached != NULL) {
refcount_inc(&cached->sm_count);
spin_unlock(&nsm_lock);
kfree(new);
dprintk("lockd: found nsm_handle for %s (%s), "
"cnt %d\n", cached->sm_name,
cached->sm_addrbuf,
refcount_read(&cached->sm_count));
return cached;
}
if (new != NULL) {
list_add(&new->sm_link, &ln->nsm_handles);
spin_unlock(&nsm_lock);
dprintk("lockd: created nsm_handle for %s (%s)\n",
new->sm_name, new->sm_addrbuf);
return new;
}
spin_unlock(&nsm_lock);
new = nsm_create_handle(sap, salen, hostname, hostname_len);
if (unlikely(new == NULL))
return NULL;
goto retry;
}
/**
* nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
* @net: network namespace
* @info: pointer to NLMPROC_SM_NOTIFY arguments
*
* Returns a matching nsm_handle if found in the nsm cache. The returned
* nsm_handle's reference count is bumped. Otherwise returns NULL if some
* error occurred.
*/
struct nsm_handle *nsm_reboot_lookup(const struct net *net,
const struct lockd_reboot *info)
{
struct nsm_handle *cached;
struct lockd_net *ln = net_generic(net, lockd_net_id);
spin_lock(&nsm_lock);
cached = nsm_lookup_priv(&ln->nsm_handles, &info->priv);
if (unlikely(cached == NULL)) {
spin_unlock(&nsm_lock);
dprintk("lockd: never saw rebooted peer '%.*s' before\n",
info->len, info->mon);
return cached;
}
refcount_inc(&cached->sm_count);
spin_unlock(&nsm_lock);
dprintk("lockd: host %s (%s) rebooted, cnt %d\n",
cached->sm_name, cached->sm_addrbuf,
refcount_read(&cached->sm_count));
return cached;
}
/**
* nsm_release - Release an NSM handle
* @nsm: pointer to handle to be released
*
*/
void nsm_release(struct nsm_handle *nsm)
{
if (refcount_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
list_del(&nsm->sm_link);
spin_unlock(&nsm_lock);
dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
nsm->sm_name, nsm->sm_addrbuf);
kfree(nsm);
}
}
/*
* XDR functions for NSM.
*
* See https://www.opengroup.org/ for details on the Network
* Status Monitor wire protocol.
*/
static void encode_nsm_string(struct xdr_stream *xdr, const char *string)
{
const u32 len = strlen(string);
__be32 *p;
p = xdr_reserve_space(xdr, 4 + len);
xdr_encode_opaque(p, string, len);
}
/*
* "mon_name" specifies the host to be monitored.
*/
static void encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
{
encode_nsm_string(xdr, argp->mon_name);
}
/*
* The "my_id" argument specifies the hostname and RPC procedure
* to be called when the status manager receives notification
* (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
* has changed.
*/
static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{
__be32 *p;
encode_nsm_string(xdr, argp->nodename);
p = xdr_reserve_space(xdr, 4 + 4 + 4);
*p++ = cpu_to_be32(argp->prog);
*p++ = cpu_to_be32(argp->vers);
*p = cpu_to_be32(argp->proc);
}
/*
* The "mon_id" argument specifies the non-private arguments
* of an NSMPROC_MON or NSMPROC_UNMON call.
*/
static void encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{
encode_mon_name(xdr, argp);
encode_my_id(xdr, argp);
}
/*
* The "priv" argument may contain private information required
* by the NSMPROC_MON call. This information will be supplied in the
* NLMPROC_SM_NOTIFY call.
*/
static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
{
__be32 *p;
p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
}
static void nsm_xdr_enc_mon(struct rpc_rqst *req, struct xdr_stream *xdr,
const void *argp)
{
encode_mon_id(xdr, argp);
encode_priv(xdr, argp);
}
static void nsm_xdr_enc_unmon(struct rpc_rqst *req, struct xdr_stream *xdr,
const void *argp)
{
encode_mon_id(xdr, argp);
}
static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nsm_res *resp = data;
__be32 *p;
p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL))
return -EIO;
resp->status = be32_to_cpup(p++);
resp->state = be32_to_cpup(p);
dprintk("lockd: %s status %d state %d\n",
__func__, resp->status, resp->state);
return 0;
}
static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nsm_res *resp = data;
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
return -EIO;
resp->state = be32_to_cpup(p);
dprintk("lockd: %s state %d\n", __func__, resp->state);
return 0;
}
#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
#define SM_my_id_sz (SM_my_name_sz+3)
#define SM_mon_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
#define SM_mon_id_sz (SM_mon_name_sz+SM_my_id_sz)
#define SM_priv_sz (XDR_QUADLEN(SM_PRIV_SIZE))
#define SM_mon_sz (SM_mon_id_sz+SM_priv_sz)
#define SM_monres_sz 2
#define SM_unmonres_sz 1
static const struct rpc_procinfo nsm_procedures[] = {
[NSMPROC_MON] = {
.p_proc = NSMPROC_MON,
.p_encode = nsm_xdr_enc_mon,
.p_decode = nsm_xdr_dec_stat_res,
.p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz,
.p_statidx = NSMPROC_MON,
.p_name = "MONITOR",
},
[NSMPROC_UNMON] = {
.p_proc = NSMPROC_UNMON,
.p_encode = nsm_xdr_enc_unmon,
.p_decode = nsm_xdr_dec_stat,
.p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz,
.p_statidx = NSMPROC_UNMON,
.p_name = "UNMONITOR",
},
};
static unsigned int nsm_version1_counts[ARRAY_SIZE(nsm_procedures)];
static const struct rpc_version nsm_version1 = {
.number = 1,
.nrprocs = ARRAY_SIZE(nsm_procedures),
.procs = nsm_procedures,
.counts = nsm_version1_counts,
};
static const struct rpc_version *nsm_version[] = {
[1] = &nsm_version1,
};
static struct rpc_stat nsm_stats;
static const struct rpc_program nsm_program = {
.name = "statd",
.number = NSM_PROGRAM,
.nrvers = ARRAY_SIZE(nsm_version),
.version = nsm_version,
.stats = &nsm_stats
};
+45
View File
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel source */
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
#include "netlink.h"
#include <uapi/linux/lockd_netlink.h>
/* LOCKD_CMD_SERVER_SET - do */
static const struct nla_policy lockd_server_set_nl_policy[LOCKD_A_SERVER_UDP_PORT + 1] = {
[LOCKD_A_SERVER_GRACETIME] = { .type = NLA_U32, },
[LOCKD_A_SERVER_TCP_PORT] = { .type = NLA_U16, },
[LOCKD_A_SERVER_UDP_PORT] = { .type = NLA_U16, },
};
/* Ops table for lockd */
static const struct genl_split_ops lockd_nl_ops[] = {
{
.cmd = LOCKD_CMD_SERVER_SET,
.doit = lockd_nl_server_set_doit,
.policy = lockd_server_set_nl_policy,
.maxattr = LOCKD_A_SERVER_UDP_PORT,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = LOCKD_CMD_SERVER_GET,
.doit = lockd_nl_server_get_doit,
.flags = GENL_CMD_CAP_DO,
},
};
struct genl_family lockd_nl_family __ro_after_init = {
.name = LOCKD_FAMILY_NAME,
.version = LOCKD_FAMILY_VERSION,
.netnsok = true,
.parallel_ops = true,
.module = THIS_MODULE,
.split_ops = lockd_nl_ops,
.n_split_ops = ARRAY_SIZE(lockd_nl_ops),
};
+20
View File
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel header */
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_LOCKD_GEN_H
#define _LINUX_LOCKD_GEN_H
#include <net/netlink.h>
#include <net/genetlink.h>
#include <uapi/linux/lockd_netlink.h>
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info);
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info);
extern struct genl_family lockd_nl_family;
#endif /* _LINUX_LOCKD_GEN_H */
+25
View File
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LOCKD_NETNS_H__
#define __LOCKD_NETNS_H__
#include <linux/fs.h>
#include <linux/filelock.h>
#include <net/netns/generic.h>
struct lockd_net {
unsigned int nlmsvc_users;
unsigned long next_gc;
unsigned long nrhosts;
u32 gracetime;
u16 tcp_port;
u16 udp_port;
struct delayed_work grace_period_end;
struct lock_manager lockd_manager;
struct list_head nsm_handles;
};
extern unsigned int lockd_net_id;
#endif
+56
View File
@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Declarations for the Network Lock Manager protocol.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LOCKD_NLM_H
#define _LOCKD_NLM_H
/* Maximum file offset in file_lock.fl_end */
# define NLM_OFFSET_MAX ((s32) 0x7fffffff)
# define NLM4_OFFSET_MAX ((s64) ((~(u64)0) >> 1))
/* Return states for NLM */
enum {
NLM_LCK_GRANTED = 0,
NLM_LCK_DENIED = 1,
NLM_LCK_DENIED_NOLOCKS = 2,
NLM_LCK_BLOCKED = 3,
NLM_LCK_DENIED_GRACE_PERIOD = 4,
#ifdef CONFIG_LOCKD_V4
NLM_DEADLCK = 5,
NLM_ROFS = 6,
NLM_STALE_FH = 7,
NLM_FBIG = 8,
NLM_FAILED = 9,
#endif
};
#define NLM_PROGRAM 100021
#define NLMPROC_NULL 0
#define NLMPROC_TEST 1
#define NLMPROC_LOCK 2
#define NLMPROC_CANCEL 3
#define NLMPROC_UNLOCK 4
#define NLMPROC_GRANTED 5
#define NLMPROC_TEST_MSG 6
#define NLMPROC_LOCK_MSG 7
#define NLMPROC_CANCEL_MSG 8
#define NLMPROC_UNLOCK_MSG 9
#define NLMPROC_GRANTED_MSG 10
#define NLMPROC_TEST_RES 11
#define NLMPROC_LOCK_RES 12
#define NLMPROC_CANCEL_RES 13
#define NLMPROC_UNLOCK_RES 14
#define NLMPROC_GRANTED_RES 15
#define NLMPROC_NSM_NOTIFY 16 /* statd callback */
#define NLMPROC_SHARE 20
#define NLMPROC_UNSHARE 21
#define NLMPROC_NM_LOCK 22
#define NLMPROC_FREE_ALL 23
#endif /* _LOCKD_NLM_H */
+714
View File
@@ -0,0 +1,714 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x
// XDR specification modification time: Thu Apr 23 10:56:34 2026
#include <linux/sunrpc/svc.h>
#include "nlm3xdr_gen.h"
static bool __maybe_unused
xdrgen_decode_netobj(struct xdr_stream *xdr, netobj *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, MAXNETOBJ_SZ);
}
static bool __maybe_unused
xdrgen_decode_nlm_stats(struct xdr_stream *xdr, nlm_stats *ptr)
{
__be32 raw;
u32 val;
if (xdr_stream_decode_be32(xdr, &raw) < 0)
return false;
val = be32_to_cpu(raw);
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case LCK_GRANTED:
case LCK_DENIED:
case LCK_DENIED_NOLOCKS:
case LCK_BLOCKED:
case LCK_DENIED_GRACE_PERIOD:
break;
default:
return false;
}
*ptr = raw;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_stat(struct xdr_stream *xdr, struct nlm_stat *ptr)
{
if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_res(struct xdr_stream *xdr, struct nlm_res *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm_stat(xdr, &ptr->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_holder(struct xdr_stream *xdr, struct nlm_holder *ptr)
{
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_int(xdr, &ptr->uppid))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset))
return false;
if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_testrply(struct xdr_stream *xdr, struct nlm_testrply *ptr)
{
if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
return false;
switch (ptr->stat) {
case __constant_cpu_to_be32(LCK_DENIED):
if (!xdrgen_decode_nlm_holder(xdr, &ptr->u.holder))
return false;
break;
default:
break;
}
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_testres(struct xdr_stream *xdr, struct nlm_testres *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm_testrply(xdr, &ptr->test_stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_lock(struct xdr_stream *xdr, struct nlm_lock *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->fh))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_int(xdr, &ptr->uppid))
return false;
if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_offset))
return false;
if (!xdrgen_decode_unsigned_int(xdr, &ptr->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_lockargs(struct xdr_stream *xdr, struct nlm_lockargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->block))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
return false;
if (!xdrgen_decode_int(xdr, &ptr->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_cancargs(struct xdr_stream *xdr, struct nlm_cancargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->block))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_testargs(struct xdr_stream *xdr, struct nlm_testargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_unlockargs(struct xdr_stream *xdr, struct nlm_unlockargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_fsh_mode(struct xdr_stream *xdr, fsh_mode *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsm_DN:
case fsm_DR:
case fsm_DW:
case fsm_DRW:
break;
default:
return false;
}
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_fsh_access(struct xdr_stream *xdr, fsh_access *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsa_NONE:
case fsa_R:
case fsa_W:
case fsa_RW:
break;
default:
return false;
}
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_share(struct xdr_stream *xdr, struct nlm_share *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->fh))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_fsh_mode(xdr, &ptr->mode))
return false;
if (!xdrgen_decode_fsh_access(xdr, &ptr->access))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_shareargs(struct xdr_stream *xdr, struct nlm_shareargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm_share(xdr, &ptr->share))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_shareres(struct xdr_stream *xdr, struct nlm_shareres *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm_stats(xdr, &ptr->stat))
return false;
if (!xdrgen_decode_int(xdr, &ptr->sequence))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_notify(struct xdr_stream *xdr, struct nlm_notify *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXNAMELEN))
return false;
if (!xdrgen_decode_long(xdr, &ptr->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm_notifyargs(struct xdr_stream *xdr, struct nlm_notifyargs *ptr)
{
if (!xdrgen_decode_nlm_notify(xdr, &ptr->notify))
return false;
if (xdr_stream_decode_opaque_fixed(xdr, ptr->private, SM_PRIV_SIZE) < 0)
return false;
return true;
}
/**
* nlm_svc_decode_void - Decode a void argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_decode_void(xdr);
}
/**
* nlm_svc_decode_nlm_testargs - Decode a nlm_testargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_testargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_testargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_lockargs - Decode a nlm_lockargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_lockargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_lockargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_cancargs - Decode a nlm_cancargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_cancargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_cancargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_unlockargs - Decode a nlm_unlockargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_unlockargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_unlockargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_testres - Decode a nlm_testres argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_testres *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_testres(xdr, argp);
}
/**
* nlm_svc_decode_nlm_res - Decode a nlm_res argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_res *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_res(xdr, argp);
}
/**
* nlm_svc_decode_nlm_notifyargs - Decode a nlm_notifyargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_notifyargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_notifyargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_shareargs - Decode a nlm_shareargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_shareargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_shareargs(xdr, argp);
}
/**
* nlm_svc_decode_nlm_notify - Decode a nlm_notify argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_notify *argp = rqstp->rq_argp;
return xdrgen_decode_nlm_notify(xdr, argp);
}
static bool __maybe_unused
xdrgen_encode_netobj(struct xdr_stream *xdr, const netobj value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_nlm_stats(struct xdr_stream *xdr, nlm_stats value)
{
return xdr_stream_encode_be32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_nlm_stat(struct xdr_stream *xdr, const struct nlm_stat *value)
{
if (!xdrgen_encode_nlm_stats(xdr, value->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_res(struct xdr_stream *xdr, const struct nlm_res *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm_stat(xdr, &value->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_holder(struct xdr_stream *xdr, const struct nlm_holder *value)
{
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_int(xdr, value->uppid))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_unsigned_int(xdr, value->l_offset))
return false;
if (!xdrgen_encode_unsigned_int(xdr, value->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_testrply(struct xdr_stream *xdr, const struct nlm_testrply *ptr)
{
if (!xdrgen_encode_nlm_stats(xdr, ptr->stat))
return false;
switch (ptr->stat) {
case __constant_cpu_to_be32(LCK_DENIED):
if (!xdrgen_encode_nlm_holder(xdr, &ptr->u.holder))
return false;
break;
default:
break;
}
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_testres(struct xdr_stream *xdr, const struct nlm_testres *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm_testrply(xdr, &value->test_stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_lock(struct xdr_stream *xdr, const struct nlm_lock *value)
{
if (value->caller_name.len > LM_MAXSTRLEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
return false;
if (!xdrgen_encode_netobj(xdr, value->fh))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_int(xdr, value->uppid))
return false;
if (!xdrgen_encode_unsigned_int(xdr, value->l_offset))
return false;
if (!xdrgen_encode_unsigned_int(xdr, value->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_lockargs(struct xdr_stream *xdr, const struct nlm_lockargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->block))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
return false;
if (!xdrgen_encode_bool(xdr, value->reclaim))
return false;
if (!xdrgen_encode_int(xdr, value->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_cancargs(struct xdr_stream *xdr, const struct nlm_cancargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->block))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_testargs(struct xdr_stream *xdr, const struct nlm_testargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_unlockargs(struct xdr_stream *xdr, const struct nlm_unlockargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_fsh_mode(struct xdr_stream *xdr, fsh_mode value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_fsh_access(struct xdr_stream *xdr, fsh_access value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_nlm_share(struct xdr_stream *xdr, const struct nlm_share *value)
{
if (value->caller_name.len > LM_MAXSTRLEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
return false;
if (!xdrgen_encode_netobj(xdr, value->fh))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_fsh_mode(xdr, value->mode))
return false;
if (!xdrgen_encode_fsh_access(xdr, value->access))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_shareargs(struct xdr_stream *xdr, const struct nlm_shareargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm_share(xdr, &value->share))
return false;
if (!xdrgen_encode_bool(xdr, value->reclaim))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_shareres(struct xdr_stream *xdr, const struct nlm_shareres *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm_stats(xdr, value->stat))
return false;
if (!xdrgen_encode_int(xdr, value->sequence))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_notify(struct xdr_stream *xdr, const struct nlm_notify *value)
{
if (value->name.len > LM_MAXNAMELEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->name.data, value->name.len) < 0)
return false;
if (!xdrgen_encode_long(xdr, value->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm_notifyargs(struct xdr_stream *xdr, const struct nlm_notifyargs *value)
{
if (!xdrgen_encode_nlm_notify(xdr, &value->notify))
return false;
if (xdr_stream_encode_opaque_fixed(xdr, value->private, SM_PRIV_SIZE) < 0)
return false;
return true;
}
/**
* nlm_svc_encode_void - Encode a void result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_encode_void(xdr);
}
/**
* nlm_svc_encode_nlm_testres - Encode a nlm_testres result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_testres *resp = rqstp->rq_resp;
return xdrgen_encode_nlm_testres(xdr, resp);
}
/**
* nlm_svc_encode_nlm_res - Encode a nlm_res result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_res *resp = rqstp->rq_resp;
return xdrgen_encode_nlm_res(xdr, resp);
}
/**
* nlm_svc_encode_nlm_shareres - Encode a nlm_shareres result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_shareres *resp = rqstp->rq_resp;
return xdrgen_encode_nlm_shareres(xdr, resp);
}
+32
View File
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */
/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */
#ifndef _LINUX_XDRGEN_NLM3_DECL_H
#define _LINUX_XDRGEN_NLM3_DECL_H
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/xdrgen/_defs.h>
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nlm3.h>
bool nlm_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_decode_nlm_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm_svc_encode_nlm_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
#endif /* _LINUX_XDRGEN_NLM3_DECL_H */
+724
View File
@@ -0,0 +1,724 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x
// XDR specification modification time: Thu Dec 25 13:10:19 2025
#include <linux/sunrpc/svc.h>
#include "nlm4xdr_gen.h"
static bool __maybe_unused
xdrgen_decode_netobj(struct xdr_stream *xdr, netobj *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, MAXNETOBJ_SZ);
}
static bool __maybe_unused
xdrgen_decode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_fsh4_access(struct xdr_stream *xdr, fsh4_access *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}
static bool __maybe_unused
xdrgen_decode_uint64(struct xdr_stream *xdr, uint64 *ptr)
{
return xdrgen_decode_unsigned_hyper(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_int64(struct xdr_stream *xdr, int64 *ptr)
{
return xdrgen_decode_hyper(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_uint32(struct xdr_stream *xdr, uint32 *ptr)
{
return xdrgen_decode_unsigned_long(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_int32(struct xdr_stream *xdr, int32 *ptr)
{
return xdrgen_decode_long(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats *ptr)
{
return xdr_stream_decode_be32(xdr, ptr) == 0;
}
static bool __maybe_unused
xdrgen_decode_nlm4_holder(struct xdr_stream *xdr, struct nlm4_holder *ptr)
{
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_int32(xdr, &ptr->svid))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_uint64(xdr, &ptr->l_offset))
return false;
if (!xdrgen_decode_uint64(xdr, &ptr->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_testrply(struct xdr_stream *xdr, struct nlm4_testrply *ptr)
{
if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat))
return false;
switch (ptr->stat) {
case __constant_cpu_to_be32(NLM4_DENIED):
if (!xdrgen_decode_nlm4_holder(xdr, &ptr->u.holder))
return false;
break;
default:
break;
}
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_stat(struct xdr_stream *xdr, struct nlm4_stat *ptr)
{
if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_res(struct xdr_stream *xdr, struct nlm4_res *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm4_stat(xdr, &ptr->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_testres(struct xdr_stream *xdr, struct nlm4_testres *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm4_testrply(xdr, &ptr->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_lock(struct xdr_stream *xdr, struct nlm4_lock *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->fh))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_int32(xdr, &ptr->svid))
return false;
if (!xdrgen_decode_uint64(xdr, &ptr->l_offset))
return false;
if (!xdrgen_decode_uint64(xdr, &ptr->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_lockargs(struct xdr_stream *xdr, struct nlm4_lockargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->block))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
return false;
if (!xdrgen_decode_int32(xdr, &ptr->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_cancargs(struct xdr_stream *xdr, struct nlm4_cancargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->block))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_testargs(struct xdr_stream *xdr, struct nlm4_testargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->exclusive))
return false;
if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_unlockargs(struct xdr_stream *xdr, struct nlm4_unlockargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_share(struct xdr_stream *xdr, struct nlm4_share *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->fh))
return false;
if (!xdrgen_decode_netobj(xdr, &ptr->oh))
return false;
if (!xdrgen_decode_fsh4_mode(xdr, &ptr->mode))
return false;
if (!xdrgen_decode_fsh4_access(xdr, &ptr->access))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_shareargs(struct xdr_stream *xdr, struct nlm4_shareargs *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm4_share(xdr, &ptr->share))
return false;
if (!xdrgen_decode_bool(xdr, &ptr->reclaim))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_shareres(struct xdr_stream *xdr, struct nlm4_shareres *ptr)
{
if (!xdrgen_decode_netobj(xdr, &ptr->cookie))
return false;
if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat))
return false;
if (!xdrgen_decode_int32(xdr, &ptr->sequence))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_notify(struct xdr_stream *xdr, struct nlm4_notify *ptr)
{
if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXNAMELEN))
return false;
if (!xdrgen_decode_int32(xdr, &ptr->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_nlm4_notifyargs(struct xdr_stream *xdr, struct nlm4_notifyargs *ptr)
{
if (!xdrgen_decode_nlm4_notify(xdr, &ptr->notify))
return false;
if (xdr_stream_decode_opaque_fixed(xdr, ptr->private, SM_PRIV_SIZE) < 0)
return false;
return true;
}
/**
* nlm4_svc_decode_void - Decode a void argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_decode_void(xdr);
}
/**
* nlm4_svc_decode_nlm4_testargs - Decode a nlm4_testargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_testargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_testargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_lockargs - Decode a nlm4_lockargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_lockargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_lockargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_cancargs - Decode a nlm4_cancargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_cancargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_cancargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_unlockargs - Decode a nlm4_unlockargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_unlockargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_unlockargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_testres - Decode a nlm4_testres argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_testres *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_testres(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_res - Decode a nlm4_res argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_res *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_res(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_notifyargs - Decode a nlm4_notifyargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_notifyargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_notifyargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_shareargs - Decode a nlm4_shareargs argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_shareargs *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_shareargs(xdr, argp);
}
/**
* nlm4_svc_decode_nlm4_notify - Decode a nlm4_notify argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool nlm4_svc_decode_nlm4_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_notify *argp = rqstp->rq_argp;
return xdrgen_decode_nlm4_notify(xdr, argp);
}
static bool __maybe_unused
xdrgen_encode_netobj(struct xdr_stream *xdr, const netobj value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_fsh4_access(struct xdr_stream *xdr, fsh4_access value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_uint64(struct xdr_stream *xdr, const uint64 value)
{
return xdrgen_encode_unsigned_hyper(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_int64(struct xdr_stream *xdr, const int64 value)
{
return xdrgen_encode_hyper(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_uint32(struct xdr_stream *xdr, const uint32 value)
{
return xdrgen_encode_unsigned_long(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_int32(struct xdr_stream *xdr, const int32 value)
{
return xdrgen_encode_long(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats value)
{
return xdr_stream_encode_be32(xdr, value) == XDR_UNIT;
}
static bool __maybe_unused
xdrgen_encode_nlm4_holder(struct xdr_stream *xdr, const struct nlm4_holder *value)
{
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_int32(xdr, value->svid))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_uint64(xdr, value->l_offset))
return false;
if (!xdrgen_encode_uint64(xdr, value->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_testrply(struct xdr_stream *xdr, const struct nlm4_testrply *ptr)
{
if (!xdrgen_encode_nlm4_stats(xdr, ptr->stat))
return false;
switch (ptr->stat) {
case __constant_cpu_to_be32(NLM4_DENIED):
if (!xdrgen_encode_nlm4_holder(xdr, &ptr->u.holder))
return false;
break;
default:
break;
}
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_stat(struct xdr_stream *xdr, const struct nlm4_stat *value)
{
if (!xdrgen_encode_nlm4_stats(xdr, value->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_res(struct xdr_stream *xdr, const struct nlm4_res *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm4_stat(xdr, &value->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_testres(struct xdr_stream *xdr, const struct nlm4_testres *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm4_testrply(xdr, &value->stat))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_lock(struct xdr_stream *xdr, const struct nlm4_lock *value)
{
if (value->caller_name.len > LM_MAXSTRLEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
return false;
if (!xdrgen_encode_netobj(xdr, value->fh))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_int32(xdr, value->svid))
return false;
if (!xdrgen_encode_uint64(xdr, value->l_offset))
return false;
if (!xdrgen_encode_uint64(xdr, value->l_len))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_lockargs(struct xdr_stream *xdr, const struct nlm4_lockargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->block))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm4_lock(xdr, &value->alock))
return false;
if (!xdrgen_encode_bool(xdr, value->reclaim))
return false;
if (!xdrgen_encode_int32(xdr, value->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_cancargs(struct xdr_stream *xdr, const struct nlm4_cancargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->block))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm4_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_testargs(struct xdr_stream *xdr, const struct nlm4_testargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_bool(xdr, value->exclusive))
return false;
if (!xdrgen_encode_nlm4_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_unlockargs(struct xdr_stream *xdr, const struct nlm4_unlockargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm4_lock(xdr, &value->alock))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_share(struct xdr_stream *xdr, const struct nlm4_share *value)
{
if (value->caller_name.len > LM_MAXSTRLEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0)
return false;
if (!xdrgen_encode_netobj(xdr, value->fh))
return false;
if (!xdrgen_encode_netobj(xdr, value->oh))
return false;
if (!xdrgen_encode_fsh4_mode(xdr, value->mode))
return false;
if (!xdrgen_encode_fsh4_access(xdr, value->access))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_shareargs(struct xdr_stream *xdr, const struct nlm4_shareargs *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm4_share(xdr, &value->share))
return false;
if (!xdrgen_encode_bool(xdr, value->reclaim))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_shareres(struct xdr_stream *xdr, const struct nlm4_shareres *value)
{
if (!xdrgen_encode_netobj(xdr, value->cookie))
return false;
if (!xdrgen_encode_nlm4_stats(xdr, value->stat))
return false;
if (!xdrgen_encode_int32(xdr, value->sequence))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_notify(struct xdr_stream *xdr, const struct nlm4_notify *value)
{
if (value->name.len > LM_MAXNAMELEN)
return false;
if (xdr_stream_encode_opaque(xdr, value->name.data, value->name.len) < 0)
return false;
if (!xdrgen_encode_int32(xdr, value->state))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_nlm4_notifyargs(struct xdr_stream *xdr, const struct nlm4_notifyargs *value)
{
if (!xdrgen_encode_nlm4_notify(xdr, &value->notify))
return false;
if (xdr_stream_encode_opaque_fixed(xdr, value->private, SM_PRIV_SIZE) < 0)
return false;
return true;
}
/**
* nlm4_svc_encode_void - Encode a void result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return xdrgen_encode_void(xdr);
}
/**
* nlm4_svc_encode_nlm4_testres - Encode a nlm4_testres result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm4_svc_encode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_testres *resp = rqstp->rq_resp;
return xdrgen_encode_nlm4_testres(xdr, resp);
}
/**
* nlm4_svc_encode_nlm4_res - Encode a nlm4_res result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm4_svc_encode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_res *resp = rqstp->rq_resp;
return xdrgen_encode_nlm4_res(xdr, resp);
}
/**
* nlm4_svc_encode_nlm4_shareres - Encode a nlm4_shareres result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool nlm4_svc_encode_nlm4_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm4_shareres *resp = rqstp->rq_resp;
return xdrgen_encode_nlm4_shareres(xdr, resp);
}
+32
View File
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */
/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */
#ifndef _LINUX_XDRGEN_NLM4_DECL_H
#define _LINUX_XDRGEN_NLM4_DECL_H
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/xdrgen/_defs.h>
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nlm4.h>
bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_decode_nlm4_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nlm4_svc_encode_nlm4_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
#endif /* _LINUX_XDRGEN_NLM4_DECL_H */
+92
View File
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Procfs support for lockd
*
* Copyright (c) 2014 Jeff Layton <jlayton@primarydata.com>
*/
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
#include "netns.h"
#include "procfs.h"
/*
* We only allow strings that start with 'Y', 'y', or '1'.
*/
static ssize_t
nlm_end_grace_write(struct file *file, const char __user *buf, size_t size,
loff_t *pos)
{
char *data;
struct lockd_net *ln = net_generic(current->nsproxy->net_ns,
lockd_net_id);
if (size < 1)
return -EINVAL;
data = simple_transaction_get(file, buf, size);
if (IS_ERR(data))
return PTR_ERR(data);
switch(data[0]) {
case 'Y':
case 'y':
case '1':
locks_end_grace(&ln->lockd_manager);
break;
default:
return -EINVAL;
}
return size;
}
static ssize_t
nlm_end_grace_read(struct file *file, char __user *buf, size_t size,
loff_t *pos)
{
struct lockd_net *ln = net_generic(current->nsproxy->net_ns,
lockd_net_id);
char resp[3];
resp[0] = list_empty(&ln->lockd_manager.list) ? 'Y' : 'N';
resp[1] = '\n';
resp[2] = '\0';
return simple_read_from_buffer(buf, size, pos, resp, sizeof(resp));
}
static const struct proc_ops lockd_end_grace_proc_ops = {
.proc_write = nlm_end_grace_write,
.proc_read = nlm_end_grace_read,
.proc_lseek = default_llseek,
.proc_release = simple_transaction_release,
};
int __init
lockd_create_procfs(void)
{
struct proc_dir_entry *entry;
entry = proc_mkdir("fs/lockd", NULL);
if (!entry)
return -ENOMEM;
entry = proc_create("nlm_end_grace", S_IRUGO|S_IWUSR, entry,
&lockd_end_grace_proc_ops);
if (!entry) {
remove_proc_entry("fs/lockd", NULL);
return -ENOMEM;
}
return 0;
}
void __exit
lockd_remove_procfs(void)
{
remove_proc_entry("fs/lockd/nlm_end_grace", NULL);
remove_proc_entry("fs/lockd", NULL);
}
+27
View File
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Procfs support for lockd
*
* Copyright (c) 2014 Jeff Layton <jlayton@primarydata.com>
*/
#ifndef _LOCKD_PROCFS_H
#define _LOCKD_PROCFS_H
#if IS_ENABLED(CONFIG_PROC_FS)
int lockd_create_procfs(void);
void lockd_remove_procfs(void);
#else
static inline int
lockd_create_procfs(void)
{
return 0;
}
static inline void
lockd_remove_procfs(void)
{
return;
}
#endif /* IS_ENABLED(CONFIG_PROC_FS) */
#endif /* _LOCKD_PROCFS_H */
+33
View File
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* DOS share management for lockd.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LOCKD_SHARE_H
#define _LOCKD_SHARE_H
/* Synthetic svid for lockowner lookup during share operations */
#define LOCKD_SHARE_SVID (~(u32)0)
/*
* DOS share for a specific file
*/
struct lockd_share {
struct lockd_share * s_next; /* linked list */
struct nlm_host * s_host; /* client host */
struct nlm_file * s_file; /* shared file */
struct xdr_netobj s_owner; /* owner handle */
u32 s_access; /* access mode */
u32 s_mode; /* deny mode */
};
__be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh, u32 access, u32 mode);
__be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh);
void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *,
nlm_host_match_fn_t);
#endif /* _LOCKD_SHARE_H */
+791
View File
@@ -0,0 +1,791 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/signal.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/uio.h>
#include <linux/smp.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/inetdevice.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 <linux/sunrpc/svc_xprt.h>
#include <net/ip.h>
#include <net/addrconf.h>
#include <net/ipv6.h>
#include <linux/nfs.h>
#include "lockd.h"
#include "netns.h"
#include "procfs.h"
#include "netlink.h"
#define NLMDBG_FACILITY NLMDBG_SVC
static struct svc_program nlmsvc_program;
const struct nlmsvc_binding *nlmsvc_ops;
EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users;
static struct svc_serv *nlmsvc_serv;
static void nlmsvc_request_retry(struct timer_list *tl)
{
svc_wake_up(nlmsvc_serv);
}
DEFINE_TIMER(nlmsvc_retry, nlmsvc_request_retry);
unsigned int lockd_net_id;
/*
* 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;
unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
static int nlm_udpport, nlm_tcpport;
/*
* 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;
#ifdef CONFIG_SYSCTL
static const int nlm_port_min = 0, nlm_port_max = 65535;
static struct ctl_table_header * nlm_sysctl_table;
#endif
static unsigned long get_lockd_grace_period(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
/* Return the net-ns specific grace period, if there is one */
if (ln->gracetime)
return ln->gracetime * HZ;
/* Note: nlm_timeout should always be nonzero */
if (nlm_grace_period)
return roundup(nlm_grace_period, nlm_timeout) * HZ;
else
return nlm_timeout * 5 * HZ;
}
static void grace_ender(struct work_struct *grace)
{
struct delayed_work *dwork = to_delayed_work(grace);
struct lockd_net *ln = container_of(dwork, struct lockd_net,
grace_period_end);
locks_end_grace(&ln->lockd_manager);
}
static void set_grace_period(struct net *net)
{
unsigned long grace_period = get_lockd_grace_period(net);
struct lockd_net *ln = net_generic(net, lockd_net_id);
locks_start_grace(net, &ln->lockd_manager);
cancel_delayed_work_sync(&ln->grace_period_end);
schedule_delayed_work(&ln->grace_period_end, grace_period);
}
/*
* This is the lockd kernel thread
*/
static int
lockd(void *vrqstp)
{
struct svc_rqst *rqstp = vrqstp;
struct net *net = &init_net;
struct lockd_net *ln = net_generic(net, lockd_net_id);
svc_thread_init_status(rqstp, 0);
/* try_to_freeze() is called from svc_recv() */
set_freezable();
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
/*
* The main request loop. We don't terminate until the last
* NFS mount or NFS daemon has gone away.
*/
while (!svc_thread_should_stop(rqstp)) {
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp, 0);
}
if (nlmsvc_ops)
nlmsvc_invalidate_all();
nlm_shutdown_hosts();
cancel_delayed_work_sync(&ln->grace_period_end);
locks_end_grace(&ln->lockd_manager);
dprintk("lockd_down: service stopped\n");
svc_exit_thread(rqstp);
return 0;
}
static int create_lockd_listener(struct svc_serv *serv, const char *name,
struct net *net, const int family,
const unsigned short port,
const struct cred *cred)
{
struct svc_xprt *xprt;
xprt = svc_find_xprt(serv, name, net, family, 0);
if (xprt == NULL)
return svc_xprt_create(serv, name, net, family, port,
SVC_SOCK_DEFAULTS, cred);
svc_xprt_put(xprt);
return 0;
}
static int create_lockd_family(struct svc_serv *serv, struct net *net,
const int family, const struct cred *cred)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
int err;
err = create_lockd_listener(serv, "udp", net, family,
ln->udp_port ? ln->udp_port : nlm_udpport, cred);
if (err < 0)
return err;
return create_lockd_listener(serv, "tcp", net, family,
ln->tcp_port ? ln->tcp_port : nlm_tcpport, cred);
}
/*
* Ensure there are active UDP and TCP listeners for lockd.
*
* Even if we have only TCP NFS mounts and/or TCP NFSDs, some
* local services (such as rpc.statd) still require UDP, and
* some NFS servers do not yet support NLM over TCP.
*
* Returns zero if all listeners are available; otherwise a
* negative errno value is returned.
*/
static int make_socks(struct svc_serv *serv, struct net *net,
const struct cred *cred)
{
static int warned;
int err;
err = create_lockd_family(serv, net, PF_INET, cred);
if (err < 0)
goto out_err;
err = create_lockd_family(serv, net, PF_INET6, cred);
if (err < 0 && err != -EAFNOSUPPORT)
goto out_err;
warned = 0;
return 0;
out_err:
if (warned++ == 0)
printk(KERN_WARNING
"lockd_up: makesock failed, error=%d\n", err);
svc_xprt_destroy_all(serv, net, true);
return err;
}
static int lockd_up_net(struct svc_serv *serv, struct net *net,
const struct cred *cred)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
int error;
if (ln->nlmsvc_users++)
return 0;
error = svc_bind(serv, net);
if (error)
goto err_bind;
error = make_socks(serv, net, cred);
if (error < 0)
goto err_bind;
set_grace_period(net);
dprintk("%s: per-net data created; net=%x\n", __func__, net->ns.inum);
return 0;
err_bind:
ln->nlmsvc_users--;
return error;
}
static void lockd_down_net(struct svc_serv *serv, struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
if (ln->nlmsvc_users) {
if (--ln->nlmsvc_users == 0) {
nlm_shutdown_hosts_net(net);
cancel_delayed_work_sync(&ln->grace_period_end);
locks_end_grace(&ln->lockd_manager);
svc_xprt_destroy_all(serv, net, true);
}
} else {
pr_err("%s: no users! net=%x\n",
__func__, net->ns.inum);
BUG();
}
}
static int lockd_inetaddr_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct sockaddr_in sin;
if (event != NETDEV_DOWN)
goto out;
if (nlmsvc_serv) {
dprintk("lockd_inetaddr_event: removed %pI4\n",
&ifa->ifa_local);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ifa->ifa_local;
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin);
}
out:
return NOTIFY_DONE;
}
static struct notifier_block lockd_inetaddr_notifier = {
.notifier_call = lockd_inetaddr_event,
};
#if IS_ENABLED(CONFIG_IPV6)
static int lockd_inet6addr_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct sockaddr_in6 sin6;
if (event != NETDEV_DOWN)
goto out;
if (nlmsvc_serv) {
dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = ifa->addr;
if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
sin6.sin6_scope_id = ifa->idev->dev->ifindex;
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6);
}
out:
return NOTIFY_DONE;
}
static struct notifier_block lockd_inet6addr_notifier = {
.notifier_call = lockd_inet6addr_event,
};
#endif
static int lockd_get(void)
{
struct svc_serv *serv;
unsigned int bufsize;
int error;
if (nlmsvc_serv) {
nlmsvc_users++;
return 0;
}
/*
* 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);
#ifdef CONFIG_LOCKD_V4
bufsize = 1024 + max3(nlmsvc_version1.vs_xdrsize,
nlmsvc_version3.vs_xdrsize,
nlmsvc_version4.vs_xdrsize);
#else
bufsize = 1024 + max(nlmsvc_version1.vs_xdrsize,
nlmsvc_version3.vs_xdrsize);
#endif
serv = svc_create(&nlmsvc_program, bufsize, lockd);
if (!serv) {
printk(KERN_WARNING "lockd_up: create service failed\n");
return -ENOMEM;
}
error = svc_set_num_threads(serv, 0, 1);
if (error < 0) {
svc_destroy(&serv);
return error;
}
nlmsvc_serv = serv;
register_inetaddr_notifier(&lockd_inetaddr_notifier);
#if IS_ENABLED(CONFIG_IPV6)
register_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif
dprintk("lockd_up: service created\n");
nlmsvc_users++;
return 0;
}
static void lockd_put(void)
{
if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n"))
return;
if (--nlmsvc_users)
return;
unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif
svc_set_num_threads(nlmsvc_serv, 0, 0);
timer_delete_sync(&nlmsvc_retry);
svc_destroy(&nlmsvc_serv);
dprintk("lockd_down: service destroyed\n");
}
/*
* Bring up the lockd process if it's not already up.
*/
int lockd_up(struct net *net, const struct cred *cred)
{
int error;
mutex_lock(&nlmsvc_mutex);
error = lockd_get();
if (error)
goto err;
error = lockd_up_net(nlmsvc_serv, net, cred);
if (error < 0) {
lockd_put();
goto err;
}
err:
mutex_unlock(&nlmsvc_mutex);
return error;
}
EXPORT_SYMBOL_GPL(lockd_up);
/*
* Decrement the user count and bring down lockd if we're the last.
*/
void
lockd_down(struct net *net)
{
mutex_lock(&nlmsvc_mutex);
lockd_down_net(nlmsvc_serv, net);
lockd_put();
mutex_unlock(&nlmsvc_mutex);
}
EXPORT_SYMBOL_GPL(lockd_down);
#ifdef CONFIG_SYSCTL
/*
* Sysctl parameters (same as module parameters, different interface).
*/
static const struct ctl_table nlm_sysctls[] = {
{
.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,
},
{
.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,
},
{
.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,
},
{
.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,
},
{
.procname = "nsm_use_hostnames",
.data = &nsm_use_hostnames,
.maxlen = sizeof(bool),
.mode = 0644,
.proc_handler = proc_dobool,
},
{
.procname = "nsm_local_state",
.data = &nsm_local_state,
.maxlen = sizeof(nsm_local_state),
.mode = 0644,
.proc_handler = proc_douintvec,
.extra1 = SYSCTL_ZERO,
},
};
#endif /* CONFIG_SYSCTL */
/*
* Module (and sysfs) parameters.
*/
#define param_set_min_max(name, type, which_strtol, min, max) \
static int param_set_##name(const char *val, const struct kernel_param *kp) \
{ \
char *endp; \
__typeof__(type) num = which_strtol(val, &endp, 0); \
if (endp == val || *endp || num < (min) || num > (max)) \
return -EINVAL; \
*((type *) 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 enum svc_auth_status lockd_authenticate(struct svc_rqst *rqstp)
{
rqstp->rq_client = NULL;
switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL:
case RPC_AUTH_UNIX:
rqstp->rq_auth_stat = rpc_auth_ok;
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);
}
rqstp->rq_auth_stat = rpc_autherr_badcred;
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);
static int lockd_init_net(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
INIT_LIST_HEAD(&ln->lockd_manager.list);
ln->lockd_manager.block_opens = false;
INIT_LIST_HEAD(&ln->nsm_handles);
return 0;
}
static void lockd_exit_net(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
WARN_ONCE(!list_empty(&ln->lockd_manager.list),
"net %x %s: lockd_manager.list is not empty\n",
net->ns.inum, __func__);
WARN_ONCE(!list_empty(&ln->nsm_handles),
"net %x %s: nsm_handles list is not empty\n",
net->ns.inum, __func__);
WARN_ONCE(delayed_work_pending(&ln->grace_period_end),
"net %x %s: grace_period_end was not cancelled\n",
net->ns.inum, __func__);
}
static struct pernet_operations lockd_net_ops = {
.init = lockd_init_net,
.exit = lockd_exit_net,
.id = &lockd_net_id,
.size = sizeof(struct lockd_net),
};
/*
* Initialising and terminating the module.
*/
static int __init init_nlm(void)
{
int err;
#ifdef CONFIG_SYSCTL
err = -ENOMEM;
nlm_sysctl_table = register_sysctl("fs/nfs", nlm_sysctls);
if (nlm_sysctl_table == NULL)
goto err_sysctl;
#endif
err = register_pernet_subsys(&lockd_net_ops);
if (err)
goto err_pernet;
err = genl_register_family(&lockd_nl_family);
if (err)
goto err_netlink;
err = lockd_create_procfs();
if (err)
goto err_procfs;
return 0;
err_procfs:
genl_unregister_family(&lockd_nl_family);
err_netlink:
unregister_pernet_subsys(&lockd_net_ops);
err_pernet:
#ifdef CONFIG_SYSCTL
unregister_sysctl_table(nlm_sysctl_table);
err_sysctl:
#endif
return err;
}
static void __exit exit_nlm(void)
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
genl_unregister_family(&lockd_nl_family);
lockd_remove_procfs();
unregister_pernet_subsys(&lockd_net_ops);
#ifdef CONFIG_SYSCTL
unregister_sysctl_table(nlm_sysctl_table);
#endif
}
module_init(init_nlm);
module_exit(exit_nlm);
/**
* nlmsvc_dispatch - Process an NLM Request
* @rqstp: incoming request
*
* Return values:
* %0: Processing complete; do not send a Reply
* %1: Processing complete; send Reply in rqstp->rq_res
*/
int nlmsvc_dispatch(struct svc_rqst *rqstp)
{
const struct svc_procedure *procp = rqstp->rq_procinfo;
__be32 *statp = rqstp->rq_accept_statp;
if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
goto out_decode_err;
*statp = procp->pc_func(rqstp);
if (*statp == rpc_drop_reply)
return 0;
if (*statp != rpc_success)
return 1;
if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
goto out_encode_err;
return 1;
out_decode_err:
*statp = rpc_garbage_args;
return 1;
out_encode_err:
*statp = rpc_system_err;
return 1;
}
/*
* Define NLM program and procedures
*/
static const struct svc_version *nlmsvc_version[] = {
[1] = &nlmsvc_version1,
[3] = &nlmsvc_version3,
#ifdef CONFIG_LOCKD_V4
[4] = &nlmsvc_version4,
#endif
};
#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_authenticate = &lockd_authenticate, /* export authentication */
.pg_init_request = svc_generic_init_request,
.pg_rpcbind_set = svc_generic_rpcbind_set,
};
/**
* lockd_nl_server_set_doit - set the lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* This updates the per-net values. When updating the values in the init_net
* namespace, also update the "legacy" global values.
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
const struct nlattr *attr;
if (GENL_REQ_ATTR_CHECK(info, LOCKD_A_SERVER_GRACETIME))
return -EINVAL;
if (info->attrs[LOCKD_A_SERVER_GRACETIME] ||
info->attrs[LOCKD_A_SERVER_TCP_PORT] ||
info->attrs[LOCKD_A_SERVER_UDP_PORT]) {
attr = info->attrs[LOCKD_A_SERVER_GRACETIME];
if (attr) {
u32 gracetime = nla_get_u32(attr);
if (gracetime > nlm_grace_period_max)
return -EINVAL;
ln->gracetime = gracetime;
if (net == &init_net)
nlm_grace_period = gracetime;
}
attr = info->attrs[LOCKD_A_SERVER_TCP_PORT];
if (attr) {
ln->tcp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_tcpport = ln->tcp_port;
}
attr = info->attrs[LOCKD_A_SERVER_UDP_PORT];
if (attr) {
ln->udp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_udpport = ln->udp_port;
}
}
return 0;
}
/**
* lockd_nl_server_get_doit - get lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
void *hdr;
int err;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = genlmsg_iput(skb, info);
if (!hdr) {
err = -EMSGSIZE;
goto err_free_msg;
}
err = nla_put_u32(skb, LOCKD_A_SERVER_GRACETIME, ln->gracetime) ||
nla_put_u16(skb, LOCKD_A_SERVER_TCP_PORT, ln->tcp_port) ||
nla_put_u16(skb, LOCKD_A_SERVER_UDP_PORT, ln->udp_port);
if (err)
goto err_free_msg;
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_free_msg:
nlmsg_free(skb);
return err;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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 "lockd.h"
#include "share.h"
static inline int
nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh)
{
return share->s_owner.len == oh->len
&& !memcmp(share->s_owner.data, oh->data, oh->len);
}
/**
* nlmsvc_share_file - create a share
* @host: Network client peer
* @file: File to be shared
* @oh: Share owner handle
* @access: Requested access mode
* @mode: Requested file sharing mode
*
* Returns an NLM status code.
*/
__be32
nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh, u32 access, u32 mode)
{
struct lockd_share *share;
u8 *ohdata;
if (nlmsvc_file_cannot_lock(file))
return nlm_lck_denied_nolocks;
for (share = file->f_shares; share; share = share->s_next) {
if (share->s_host == host && nlm_cmp_owner(share, oh))
goto update;
if ((access & share->s_mode) || (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 = access;
share->s_mode = mode;
return nlm_granted;
}
/**
* nlmsvc_unshare_file - delete a share
* @host: Network client peer
* @file: File to be unshared
* @oh: Share owner handle
*
* Returns an NLM status code.
*/
__be32
nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh)
{
struct lockd_share *share, **shpp;
if (nlmsvc_file_cannot_lock(file))
return nlm_lck_denied_nolocks;
for (shpp = &file->f_shares; (share = *shpp) != NULL;
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 lockd_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;
}
}
+533
View File
@@ -0,0 +1,533 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/slab.h>
#include <linux/mutex.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/addr.h>
#include <linux/module.h>
#include <linux/mount.h>
#include "lockd.h"
#include "share.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 CONFIG_SUNRPC_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 = nlmsvc_file_inode(file);
dprintk("lockd: %s %s/%llu\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 < LOCKD_FH_HASH_SIZE; i++)
tmp += f->data[i];
return tmp & (FILE_NRHASH - 1);
}
int lock_to_openmode(struct file_lock *lock)
{
return lock_is_write(lock) ? O_WRONLY : O_RDONLY;
}
/*
* Open the file. Note that if we're reexporting, for example,
* this could block the lockd thread for a while.
*
* We have to make sure we have the right credential to open
* the file.
*
* @mode is O_RDONLY, O_WRONLY, or O_RDWR. O_RDWR means success
* is achieved with EITHER O_RDONLY or O_WRONLY; it does not
* require both.
*/
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
__be32 nlmerr = nlm__int__failed;
__be32 deferred = 0;
int error;
int m;
for (m = O_RDONLY; m <= O_WRONLY; m++) {
struct file **fp = &file->f_file[m];
if (mode != O_RDWR && mode != m)
continue;
if (*fp)
return nlm_granted;
error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
if (!error)
return nlm_granted;
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
case -EWOULDBLOCK:
nlmerr = nlm__int__drop_reply;
deferred = nlmerr;
break;
case -ESTALE:
nlmerr = nlm__int__stale_fh;
break;
default:
nlmerr = nlm__int__failed;
break;
}
}
return deferred ? deferred : nlmerr;
}
/*
* Lookup file info. If it doesn't exist, create a file info struct
* and open a (VFS) file for the given inode.
*/
__be32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
struct lockd_lock *lock, int mode)
{
struct nlm_file *file;
unsigned int hash;
__be32 nfserr;
nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
hash = file_hash(&lock->fh);
/* Lock file table */
mutex_lock(&nlm_file_mutex);
hlist_for_each_entry(file, &nlm_files[hash], f_list)
if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
mutex_lock(&file->f_mutex);
nfserr = nlm_do_fopen(rqstp, file, mode);
mutex_unlock(&file->f_mutex);
if (nfserr)
goto out_unlock;
goto found;
}
nlm_debug_print_fh("creating file for", &lock->fh);
nfserr = nlm_lck_denied_nolocks;
file = kzalloc_obj(*file);
if (!file)
goto out_free;
memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
mutex_init(&file->f_mutex);
INIT_HLIST_NODE(&file->f_list);
INIT_LIST_HEAD(&file->f_blocks);
nfserr = nlm_do_fopen(rqstp, file, mode);
if (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++;
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);
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
kfree(file);
} else {
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
}
}
static int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
{
struct file_lock lock;
locks_init_lock(&lock);
lock.c.flc_type = F_UNLCK;
lock.fl_start = 0;
lock.fl_end = OFFSET_MAX;
lock.c.flc_owner = fl->c.flc_owner;
lock.c.flc_pid = fl->c.flc_pid;
lock.c.flc_flags = FL_POSIX;
lock.c.flc_file = file->f_file[O_RDONLY];
if (lock.c.flc_file && vfs_lock_file(lock.c.flc_file, F_SETLK, &lock, NULL))
goto out_err;
lock.c.flc_file = file->f_file[O_WRONLY];
if (lock.c.flc_file && vfs_lock_file(lock.c.flc_file, F_SETLK, &lock, NULL))
goto out_err;
return 0;
out_err:
pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
return 1;
}
/*
* 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 file_lock_context *flctx = locks_inode_context(inode);
struct nlm_host *lockhost;
if (!flctx || list_empty_careful(&flctx->flc_posix))
return 0;
again:
file->f_locks = 0;
spin_lock(&flctx->flc_lock);
for_each_file_lock(fl, &flctx->flc_posix) {
if (fl->fl_lmops != &nlmsvc_lock_operations)
continue;
/* update current lock count */
file->f_locks++;
lockhost = ((struct nlm_lockowner *) fl->c.flc_owner)->host;
if (match(lockhost, host)) {
spin_unlock(&flctx->flc_lock);
if (nlm_unlock_files(file, fl))
return 1;
goto again;
}
}
spin_unlock(&flctx->flc_lock);
return 0;
}
static int
nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
{
return 1;
}
/*
* 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;
struct file_lock_context *flctx = locks_inode_context(inode);
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
return 1;
if (flctx && !list_empty_careful(&flctx->flc_posix)) {
spin_lock(&flctx->flc_lock);
for_each_file_lock(fl, &flctx->flc_posix) {
if (fl->fl_lmops == &nlmsvc_lock_operations) {
spin_unlock(&flctx->flc_lock);
return 1;
}
}
spin_unlock(&flctx->flc_lock);
}
file->f_locks = 0;
return 0;
}
static void nlm_close_files(struct nlm_file *file)
{
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
}
/*
* Loop over all files in the file table.
*/
static int
nlm_traverse_files(void *data, nlm_host_match_fn_t match,
int (*is_failover_file)(void *data, struct nlm_file *file))
{
struct hlist_node *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, next, &nlm_files[i], f_list) {
if (is_failover_file && !is_failover_file(data, file))
continue;
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(data, 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);
nlm_close_files(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 only for those
* hosts, which passed network check.
* 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(void *data, struct nlm_host *hint)
{
struct nlm_host *host = data;
if ((hint->net == NULL) ||
(host->net == hint->net))
host->h_inuse = 1;
return 0;
}
static int
nlmsvc_same_host(void *data, struct nlm_host *other)
{
struct nlm_host *host = data;
return host == other;
}
static int
nlmsvc_is_client(void *data, struct nlm_host *dummy)
{
struct nlm_host *host = data;
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(struct net *net)
{
struct nlm_host hint;
dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
hint.net = net;
nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
}
/*
* 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, NULL)) {
printk(KERN_WARNING
"lockd: couldn't remove all locks held by %s\n",
host->h_name);
BUG();
}
}
/**
* nlmsvc_invalidate_all - remove all locks held for clients
*
* Release all locks held by NFS clients.
*
*/
void
nlmsvc_invalidate_all(void)
{
/*
* 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, NULL);
}
static int
nlmsvc_match_sb(void *datap, struct nlm_file *file)
{
struct super_block *sb = datap;
return sb == nlmsvc_file_inode(file)->i_sb;
}
/**
* nlmsvc_unlock_all_by_sb - release locks held on this file system
* @sb: super block
*
* Release all locks held by clients accessing this file system.
*/
int
nlmsvc_unlock_all_by_sb(struct super_block *sb)
{
int ret;
ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
return ret ? -EIO : 0;
}
EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
static int
nlmsvc_match_ip(void *datap, struct nlm_host *host)
{
return rpc_cmp_addr(nlm_srcaddr(host), datap);
}
/**
* nlmsvc_unlock_all_by_ip - release local locks by IP address
* @server_addr: server's IP address as seen by clients
*
* Release all locks held by clients accessing this host
* via the passed in IP address.
*/
int
nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
{
int ret;
ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
return ret ? -EIO : 0;
}
EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);
+3
View File
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-2.0
#define CREATE_TRACE_POINTS
#include "trace.h"
+107
View File
@@ -0,0 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM lockd
#if !defined(_TRACE_LOCKD_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_LOCKD_H
#include <linux/tracepoint.h>
#include <linux/crc32.h>
#include <linux/nfs.h>
#include "lockd.h"
#ifdef CONFIG_LOCKD_V4
#define NLM_STATUS_LIST \
nlm_status_code(LCK_GRANTED) \
nlm_status_code(LCK_DENIED) \
nlm_status_code(LCK_DENIED_NOLOCKS) \
nlm_status_code(LCK_BLOCKED) \
nlm_status_code(LCK_DENIED_GRACE_PERIOD) \
nlm_status_code(DEADLCK) \
nlm_status_code(ROFS) \
nlm_status_code(STALE_FH) \
nlm_status_code(FBIG) \
nlm_status_code_end(FAILED)
#else
#define NLM_STATUS_LIST \
nlm_status_code(LCK_GRANTED) \
nlm_status_code(LCK_DENIED) \
nlm_status_code(LCK_DENIED_NOLOCKS) \
nlm_status_code(LCK_BLOCKED) \
nlm_status_code_end(LCK_DENIED_GRACE_PERIOD)
#endif
#undef nlm_status_code
#undef nlm_status_code_end
#define nlm_status_code(x) TRACE_DEFINE_ENUM(NLM_##x);
#define nlm_status_code_end(x) TRACE_DEFINE_ENUM(NLM_##x);
NLM_STATUS_LIST
#undef nlm_status_code
#undef nlm_status_code_end
#define nlm_status_code(x) { NLM_##x, #x },
#define nlm_status_code_end(x) { NLM_##x, #x }
#define show_nlm_status(x) __print_symbolic(x, NLM_STATUS_LIST)
DECLARE_EVENT_CLASS(nlmclnt_lock_event,
TP_PROTO(
const struct lockd_lock *lock,
const struct sockaddr *addr,
unsigned int addrlen,
__be32 status
),
TP_ARGS(lock, addr, addrlen, status),
TP_STRUCT__entry(
__field(u32, oh)
__field(u32, svid)
__field(u32, fh)
__field(unsigned long, status)
__field(loff_t, start)
__field(loff_t, end)
__sockaddr(addr, addrlen)
),
TP_fast_assign(
__entry->oh = ~crc32_le(0xffffffff, lock->oh.data, lock->oh.len);
__entry->svid = lock->svid;
__entry->fh = nfs_fhandle_hash(&lock->fh);
__entry->start = lock->fl.fl_start;
__entry->end = lock->fl.fl_end;
__entry->status = be32_to_cpu(status);
__assign_sockaddr(addr, addr, addrlen);
),
TP_printk(
"addr=%pISpc oh=0x%08x svid=0x%08x fh=0x%08x start=%lld end=%lld status=%s",
__get_sockaddr(addr), __entry->oh, __entry->svid,
__entry->fh, __entry->start, __entry->end,
show_nlm_status(__entry->status)
)
);
#define DEFINE_NLMCLNT_EVENT(name) \
DEFINE_EVENT(nlmclnt_lock_event, name, \
TP_PROTO( \
const struct lockd_lock *lock, \
const struct sockaddr *addr, \
unsigned int addrlen, \
__be32 status \
), \
TP_ARGS(lock, addr, addrlen, status))
DEFINE_NLMCLNT_EVENT(nlmclnt_test);
DEFINE_NLMCLNT_EVENT(nlmclnt_lock);
DEFINE_NLMCLNT_EVENT(nlmclnt_unlock);
DEFINE_NLMCLNT_EVENT(nlmclnt_grant);
#endif /* _TRACE_LOCKD_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>
+85
View File
@@ -0,0 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* XDR types for the NLM protocol
*
* Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LOCKD_XDR_H
#define _LOCKD_XDR_H
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/nfs.h>
#include <linux/sunrpc/xdr.h>
#define SM_MAXSTRLEN 1024
#define SM_PRIV_SIZE 16
struct nsm_private {
unsigned char data[SM_PRIV_SIZE];
};
#define NLM_MAXCOOKIELEN 32
#define NLM_MAXSTRLEN 1024
#define nlm_granted cpu_to_be32(NLM_LCK_GRANTED)
#define nlm_lck_denied cpu_to_be32(NLM_LCK_DENIED)
#define nlm_lck_denied_nolocks cpu_to_be32(NLM_LCK_DENIED_NOLOCKS)
#define nlm_lck_blocked cpu_to_be32(NLM_LCK_BLOCKED)
#define nlm_lck_denied_grace_period cpu_to_be32(NLM_LCK_DENIED_GRACE_PERIOD)
/* Lock info passed via NLM */
struct lockd_lock {
char * caller;
unsigned int len; /* length of "caller" */
struct nfs_fh fh;
struct xdr_netobj oh;
u32 svid;
u64 lock_start;
u64 lock_len;
struct file_lock fl;
};
/*
* NLM cookies. Technically they can be 1K, but Linux only uses 8 bytes.
* FreeBSD uses 16, Apple Mac OS X 10.3 uses 20. Therefore we set it to
* 32 bytes.
*/
struct lockd_cookie {
unsigned char data[NLM_MAXCOOKIELEN];
unsigned int len;
};
/*
* Generic lockd arguments for all but sm_notify
*/
struct lockd_args {
struct lockd_cookie cookie;
struct lockd_lock lock;
u32 block;
u32 reclaim;
u32 state;
};
/*
* Generic lockd result
*/
struct lockd_res {
struct lockd_cookie cookie;
__be32 status;
struct lockd_lock lock;
};
/*
* statd callback when client has rebooted
*/
struct lockd_reboot {
char *mon;
unsigned int len;
u32 state;
struct nsm_private priv;
};
#endif /* _LOCKD_XDR_H */