linux/fs/nfsd/lockd.c
Chuck Lever 7db001e03d lockd: Have nlm_fopen() return errno values
The nlm_fopen() function is part of the API between nfsd and lockd.

Currently its return value is an on-the-wire NLM status code. But
that forces NFSD to include NLM wire protocol definitions despite
having no other dependency on the NLM wire protocol.

In addition, a CONFIG_LOCKD_V4 Kconfig symbol appears in the middle
of NFSD source code.

Refactor: Let's not use on-the-wire values as part of a high-level
API between two Linux kernel modules. That's what we have errno for,
right?

And, instead of simply moving the CONFIG_LOCKD_V4 check, we can get
rid of it entirely and let the decision of what actual NLM status
code goes on the wire to be left up to NLM version-specific code.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2026-03-29 21:25:09 -04:00

111 lines
3.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* This file contains all the stubs needed when communicating with lockd.
* This level of indirection is necessary so we can run nfsd+lockd without
* requiring the nfs client to be compiled in/loaded, and vice versa.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/file.h>
#include <linux/lockd/bind.h>
#include "nfsd.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_LOCKD
/**
* nlm_fopen - Open an NFSD file
* @rqstp: NLM RPC procedure execution context
* @f: NFS file handle to be opened
* @filp: OUT: an opened struct file
* @flags: the POSIX open flags to use
*
* nlm_fopen() holds the dentry reference until nlm_fclose() releases it.
*
* Returns zero on success or a negative errno value if the file
* cannot be opened.
*/
static int nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f,
struct file **filp, int flags)
{
__be32 nfserr;
int access;
struct svc_fh fh;
/* must initialize before using! but maxsize doesn't matter */
fh_init(&fh,0);
fh.fh_handle.fh_size = f->size;
memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
fh.fh_export = NULL;
/*
* Allow BYPASS_GSS as some client implementations use AUTH_SYS
* for NLM even when GSS is used for NFS.
* Allow OWNER_OVERRIDE as permission might have been changed
* after the file was opened.
* Pass MAY_NLM so that authentication can be completely bypassed
* if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL
* for NLM requests.
*/
access = (flags == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
fh_put(&fh);
switch (nfserr) {
case nfs_ok:
break;
case nfserr_jukebox:
/*
* This error can indicate a presence of a conflicting
* delegation to an NLM lock request. Options are:
* (1) For now, drop this request and make the client
* retry. When delegation is returned, client's lock retry
* will complete.
* (2) NLM4_DENIED as per "spec" signals to the client
* that the lock is unavailable now but client can retry.
* Linux client implementation does not. It treats
* NLM4_DENIED same as NLM4_FAILED and fails the request.
* (3) For the future, treat this as blocked lock and try
* to callback when the delegation is returned but might
* not have a proper lock request to block on.
*/
return -EWOULDBLOCK;
case nfserr_stale:
return -ESTALE;
default:
return -ENOLCK;
}
return 0;
}
/**
* nlm_fclose - Close an NFSD file
* @filp: a struct file that was opened by nlm_fopen()
*/
static void
nlm_fclose(struct file *filp)
{
fput(filp);
}
static const struct nlmsvc_binding nfsd_nlm_ops = {
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
void
nfsd_lockd_init(void)
{
dprintk("nfsd: initializing lockd\n");
nlmsvc_ops = &nfsd_nlm_ops;
}
void
nfsd_lockd_shutdown(void)
{
nlmsvc_ops = NULL;
}