Merge tag 'nfsd-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd updates from Chuck Lever:
 "Jeff Layton extended the netlink listener-set operation so that
  a management daemon can register NFSD's programs with rpcbind
  itself. This prevents an unresponsive rpcbind from stalling NFSD
  administration for a minute at a time.

  Bulk delegation recall now adjusts the number of delegations it
  wants returned, based on memory pressure. The previous mechanism
  was spec-conformant, but it requested all delegations to be
  returned at once. Clients are free to return few or none in such
  cases, but requesting smaller batches of delegations is more
  neighborly.

  In preparation for handling TLS KeyUpdate, the server's TCP
  receive path has been reworked to handle control records. In the
  process, the use of sock_recvmsg was replaced with ->read_sock,
  which contends less with other holders of the socket lock.

  NFSv2 XDR code is now generated by xdrgen, which mechanically
  generates the encoders and decoders from the RPC language
  specification for the protocol. Replacing hand-built XDR code
  makes prototyping new protocol elements faster, reduces errors,
  and improves memory safety. Replacement of XDR code for newer NFS
  versions will continue in subsequent releases of NFSD.

  The remaining bulk of changes in v7.4 is bug fixes, clean-ups, and
  small optimizations. Sincere thanks to all contributors,
  reviewers, testers, and bug reporters who participated in the v7.4
  NFSD development cycle"

* tag 'nfsd-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (184 commits)
  SUNRPC: use assign_bit() where applicable
  nfsd: Fix id-to-name cache entry leak in idtoname_parse()
  NFSD: Use xdrgen XDR functions for NFSv2 READDIR results
  NFSD: Refactor NFSv2 directory cookie encoding
  NFSD: Add a streaming directory reader
  NFSD: Use xdrgen XDR functions for NFSv2 READDIR arguments
  NFSD: Use xdrgen XDR functions for the NFSv2 STATFS procedure
  NFSD: Use xdrgen XDR functions for NFSv2 RMDIR procedure
  NFSD: Use xdrgen XDR functions for NFSv2 MKDIR procedure
  NFSD: Use xdrgen XDR functions for NFSv2 SYMLINK procedure
  NFSD: Use xdrgen XDR functions for the NFSv2 LINK procedure
  NFSD: Use xdrgen XDR functions for the NFSv2 RENAME procedure
  NFSD: Use xdrgen XDR functions for the NFSv2 REMOVE procedure
  NFSD: Use xdrgen XDR functions for NFSv2 CREATE procedure
  NFSD: Refactor nfsd_proc_create()
  NFSD: Use xdrgen XDR functions for NFSv2 WRITE procedure
  NFSD: Use xdrgen XDR functions for the NFSv2 WRITECACHE procedure
  NFSD: Use xdrgen XDR functions for NFSv2 READ procedure
  NFSD: Use xdrgen XDR functions for NFSv2 READLINK procedure
  NFSD: Use xdrgen XDR functions for the NFSv2 LOOKUP procedure
  ...
This commit is contained in:
Linus Torvalds committed 2026-10-25 15:12:41 -10:00
commit 47439bb163
112 files changed
+9312 -2813

No files matched your search

@@ -16,7 +16,7 @@ addition to the MDS. As of now the file system needs to sit directly on the
exported LUN, striping or concatenation of LUNs on the MDS and clients
is not supported yet.
On a server built with CONFIG_NFSD_SCSI, the pNFS SCSI volume support is
On a server built with CONFIG_NFSD_SCSILAYOUT, the pNFS SCSI volume support is
automatically enabled if the file system is exported using the "pnfs"
option and the underlying SCSI device support persistent reservations.
On the client make sure the kernel has the CONFIG_PNFS_BLOCK option
+54 -1
View File
@@ -42,6 +42,15 @@ definitions:
- none
- tls
- mtls
-
type: flags
name: rpcbind-flags
doc: >-
Constraints that apply to an rpcbind registration. no-udp means the
kernel would not have registered this program and version over UDP,
so the caller must skip the udp and udp6 netids for it.
entries:
- no-udp
attribute-sets:
-
@@ -159,6 +168,23 @@ attribute-sets:
-
name: transport-name
type: string
-
name: rpcbind
attributes:
-
name: program
type: u32
doc: RPC program number to register.
-
name: version
type: u32
doc: RPC version number to register.
-
name: flags
type: u32
enum: rpcbind-flags
enum-as-flags: true
doc: Constraints on the listeners this entry applies to.
-
name: server-sock
attributes:
@@ -167,6 +193,24 @@ attribute-sets:
type: nest
nested-attributes: sock
multi-attr: true
-
name: userspace-rpcbind
type: flag
doc: >-
The caller registers the listeners with rpcbind itself, so the
kernel must not do it. The kernel echoes this attribute in the
reply when it accepts the request. Ownership cannot change while
a server exists.
-
name: rpcbind
type: nest
nested-attributes: rpcbind
multi-attr: true
doc: >-
A program and version that the caller should register for every
listener reported in the same reply, except the netids that flags
rules out. Reply only. NLM is absent because lockd still
registers itself.
-
name: pool-mode
attributes:
@@ -483,13 +527,22 @@ operations:
- version
-
name: listener-set
doc: set nfs running sockets
doc: >-
set nfs running sockets. A request that carries userspace-rpcbind
is answered with a reply rather than a bare ack, and the addr list
in that reply names only the listeners that have an rpcbind netid.
attribute-set: server-sock
flags: [admin-perm]
do:
request:
attributes:
- addr
- userspace-rpcbind
reply:
attributes:
- addr
- userspace-rpcbind
- rpcbind
-
name: listener-get
doc: get nfs running listeners
+254
View File
@@ -0,0 +1,254 @@
/*
* This document was extracted by hand from RFC 1094. Several errors
* and omissions were corrected. Additional sources for this protocol
* specification include:
*
* https://pubs.opengroup.org/onlinepubs/9629799/chap7.htm
*
* Note that RFC 1094's official date of publication (March 1989) is
* before the IETF required its RFCs to carry an explicit copyright
* or other IP ownership notice.
*/
pragma header nfs2;
const NFS_MAXDATA = 8192;
const NFS_MAXPATHLEN = 1024;
const NFS_MAXNAMLEN = 255;
const NFS_COOKIESIZE = 4;
const NFS_FHSIZE = 32;
/*
* RFC 1094 calls the following enum "stat". That name conflicts with
* the Linux kernel's "struct stat" because in C, struct, union, and
* enum tags all share a single tag namespace. Details are specified
* in the C11 standard, Section 6.2.3.
*/
enum nfsstat {
NFS_OK = 0,
NFSERR_PERM = 1,
NFSERR_NOENT = 2,
NFSERR_IO = 5,
NFSERR_NXIO = 6,
NFSERR_ACCES = 13,
NFSERR_EXIST = 17,
NFSERR_NODEV = 19,
NFSERR_NOTDIR = 20,
NFSERR_ISDIR = 21,
NFSERR_FBIG = 27,
NFSERR_NOSPC = 28,
NFSERR_ROFS = 30,
NFSERR_NAMETOOLONG = 63,
NFSERR_NOTEMPTY = 66,
NFSERR_DQUOT = 69,
NFSERR_STALE = 70,
NFSERR_WFLUSH = 99
};
pragma big_endian nfsstat;
enum ftype {
NFNON = 0,
NFREG = 1,
NFDIR = 2,
NFBLK = 3,
NFCHR = 4,
NFLNK = 5
};
typedef opaque fhandle[NFS_FHSIZE];
typedef opaque nfscookie[NFS_COOKIESIZE];
struct timeval {
unsigned int seconds;
unsigned int useconds;
};
struct fattr {
ftype type;
unsigned int mode;
unsigned int nlink;
unsigned int uid;
unsigned int gid;
unsigned int size;
unsigned int blocksize;
unsigned int rdev;
unsigned int blocks;
unsigned int fsid;
unsigned int fileid;
timeval atime;
timeval mtime;
timeval ctime;
};
struct sattr {
unsigned int mode;
unsigned int uid;
unsigned int gid;
unsigned int size;
timeval atime;
timeval mtime;
};
typedef string filename<NFS_MAXNAMLEN>;
typedef string path<NFS_MAXPATHLEN>;
union attrstat switch (nfsstat status) {
case NFS_OK:
fattr attributes;
default:
void;
};
struct diropargs {
fhandle dir;
filename name;
};
struct diropok {
fhandle file;
fattr attributes;
};
union diropres switch (nfsstat status) {
case NFS_OK:
diropok diropok;
default:
void;
};
struct sattrargs {
fhandle file;
sattr attributes;
};
union readlinkres switch (nfsstat status) {
case NFS_OK:
path data;
default:
void;
};
pragma pages readlinkres data;
struct readargs {
fhandle file;
unsigned int offset;
unsigned int count;
unsigned int totalcount;
};
struct readresok {
fattr attributes;
opaque data<NFS_MAXDATA>;
};
pragma pages readresok data;
union readres switch (nfsstat status) {
case NFS_OK:
readresok readresok;
default:
void;
};
struct writeargs {
fhandle file;
unsigned int beginoffset;
unsigned int offset;
unsigned int totalcount;
opaque data<NFS_MAXDATA>;
};
pragma pages writeargs data;
struct createargs {
diropargs where;
sattr attributes;
};
struct renameargs {
diropargs from;
diropargs to;
};
struct linkargs {
fhandle from;
diropargs to;
};
struct symlinkargs {
diropargs from;
path to;
sattr attributes;
};
pragma pages symlinkargs to;
struct readdirargs {
fhandle dir;
nfscookie cookie;
unsigned int count;
};
struct entry {
unsigned int fileid;
filename name;
nfscookie cookie;
entry *nextentry;
};
struct readdirok {
entry *entries;
bool eof;
};
/*
* A spike encoding the entry list through the aggregate codec. The
* list encodes as the value-follows form -- each entry prefixed by
* TRUE, the sequence closed by FALSE -- and the encode hooks stream it
* straight from the directory during reply encoding rather than from a
* materialized array. The struct definitions above are the verbatim
* RFC 1094 types; only this pragma selects the hook-driven codec.
*/
pragma aggregate readdirok entries;
union readdirres switch (nfsstat status) {
case NFS_OK:
readdirok readdirok;
default:
void;
};
struct info {
unsigned int tsize;
unsigned int bsize;
unsigned int blocks;
unsigned int bfree;
unsigned int bavail;
};
union statfsres switch (nfsstat status) {
case NFS_OK:
info info;
default:
void;
};
program NFS_PROGRAM {
version NFS_VERSION {
void NFSPROC_NULL(void) = 0;
attrstat NFSPROC_GETATTR(fhandle) = 1;
attrstat NFSPROC_SETATTR(sattrargs) = 2;
void NFSPROC_ROOT(void) = 3;
diropres NFSPROC_LOOKUP(diropargs) = 4;
readlinkres NFSPROC_READLINK(fhandle) = 5;
readres NFSPROC_READ(readargs) = 6;
void NFSPROC_WRITECACHE(void) = 7;
attrstat NFSPROC_WRITE(writeargs) = 8;
diropres NFSPROC_CREATE(createargs) = 9;
nfsstat NFSPROC_REMOVE(diropargs) = 10;
nfsstat NFSPROC_RENAME(renameargs) = 11;
nfsstat NFSPROC_LINK(linkargs) = 12;
nfsstat NFSPROC_SYMLINK(symlinkargs) = 13;
diropres NFSPROC_MKDIR(createargs) = 14;
nfsstat NFSPROC_RMDIR(diropargs) = 15;
readdirres NFSPROC_READDIR(readdirargs) = 16;
statfsres NFSPROC_STATFS(fhandle) = 17;
} = 2;
} = 100003;
+3 -1
View File
@@ -14433,7 +14433,8 @@ L: linux-nfs@vger.kernel.org
S: Supported
P: Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst
B: https://bugzilla.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git nfsd-testing
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git nfsd-next
F: Documentation/filesystems/nfs/
F: fs/lockd/
F: fs/nfs_common/
@@ -14450,6 +14451,7 @@ F: include/uapi/linux/nfsd/
F: include/uapi/linux/sunrpc/
F: net/sunrpc/
F: tools/net/sunrpc/
F: tools/testing/selftests/nfsd/
KERNEL NFSD BLOCK and SCSI LAYOUT DRIVER
R: Christoph Hellwig <hch@lst.de>
-1
View File
@@ -36,7 +36,6 @@
#include <net/ip.h>
#include <net/addrconf.h>
#include <net/ipv6.h>
#include <linux/nfs.h>
#include "lockd.h"
#include "netns.h"
+30 -3
View File
@@ -295,14 +295,17 @@ restart:
list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
if (!match(block->b_host, host))
continue;
/* Do not destroy blocks that are not on
* the global retry list - why? */
/*
* nlmsvc_retry_blocked() holds f_mutex while the block
* is off nlm_blocked, so a block off the list here has
* been retired.
*/
if (list_empty(&block->b_list))
continue;
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
mutex_unlock(&file->f_mutex);
nlmsvc_unlink_block(block);
mutex_unlock(&file->f_mutex);
nlmsvc_release_block(block);
goto restart;
}
@@ -1012,6 +1015,8 @@ nlmsvc_retry_blocked(struct svc_rqst *rqstp)
{
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
struct nlm_block *block;
struct nlm_file *file;
bool due;
spin_lock(&nlm_blocked_lock);
while (!list_empty(&nlm_blocked) && !svc_thread_should_stop(rqstp)) {
@@ -1023,8 +1028,28 @@ nlmsvc_retry_blocked(struct svc_rqst *rqstp)
timeout = block->b_when - jiffies;
break;
}
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
/*
* Hold f_mutex so nlmsvc_traverse_blocks() cannot scan
* the file while the retry has the block off nlm_blocked.
*/
file = block->b_file;
mutex_lock(&file->f_mutex);
spin_lock(&nlm_blocked_lock);
due = !list_empty(&block->b_list) &&
block->b_when != NLM_NEVER &&
!time_after(block->b_when, jiffies);
spin_unlock(&nlm_blocked_lock);
if (!due) {
mutex_unlock(&file->f_mutex);
nlmsvc_release_block(block);
spin_lock(&nlm_blocked_lock);
continue;
}
dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
block, block->b_when);
if (block->b_flags & B_QUEUED) {
@@ -1033,6 +1058,8 @@ nlmsvc_retry_blocked(struct svc_rqst *rqstp)
retry_deferred_block(block);
} else
nlmsvc_grant_blocked(block);
mutex_unlock(&file->f_mutex);
nlmsvc_release_block(block);
spin_lock(&nlm_blocked_lock);
}
spin_unlock(&nlm_blocked_lock);
-1
View File
@@ -7,7 +7,6 @@
#include <linux/tracepoint.h>
#include <linux/crc32.h>
#include <linux/nfs.h>
#include "lockd.h"
+1 -1
View File
@@ -10,7 +10,7 @@
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/nfs.h>
#include <linux/nfs_fh.h>
#include <linux/sunrpc/xdr.h>
#define SM_MAXSTRLEN 1024
+2
View File
@@ -5221,6 +5221,8 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL);
if (!error)
error = vfs_open(path, file);
if (!error)
file->f_mode |= FMODE_CREATED;
}
if (unlikely(error))
return ERR_PTR(error);
-5
View File
@@ -161,11 +161,6 @@ BLK_LSEG2EXT(struct pnfs_layout_segment *lseg)
return BLK_LO2EXT(lseg->pls_layout);
}
struct bl_pipe_msg {
struct rpc_pipe_msg msg;
wait_queue_head_t *bl_wq;
};
struct bl_msg_hdr {
u8 type;
u16 totallen; /* length of entire message, including hdr itself */
+58 -21
View File
@@ -55,17 +55,15 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
struct net *net = server->nfs_client->cl_net;
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct bl_dev_msg *reply = &nn->bl_mount_reply;
struct bl_pipe_msg bl_pipe_msg;
struct rpc_pipe_msg *msg = &bl_pipe_msg.msg;
struct rpc_pipe_msg *msg = &nn->bl_pipe_msg;
struct rpc_pipe *pipe = nn->bl_device_pipe;
struct bl_msg_hdr *bl_msg;
DECLARE_WAITQUEUE(wq, current);
dev_t dev = 0;
int rc;
dprintk("%s CREATING PIPEFS MESSAGE\n", __func__);
mutex_lock(&nn->bl_mutex);
bl_pipe_msg.bl_wq = &nn->bl_wq;
b->simple.len += 4; /* single volume */
if (b->simple.len > PAGE_SIZE)
@@ -83,17 +81,15 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
nfs4_encode_simple(msg->data + sizeof(*bl_msg), b);
dprintk("%s CALLING USERSPACE DAEMON\n", __func__);
add_wait_queue(&nn->bl_wq, &wq);
rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
if (rc < 0) {
remove_wait_queue(&nn->bl_wq, &wq);
reinit_completion(&nn->bl_done);
rc = rpc_queue_upcall(pipe, msg);
if (rc < 0)
goto out_free_data;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
remove_wait_queue(&nn->bl_wq, &wq);
wait_for_completion(&nn->bl_done);
if (msg->errno < 0)
goto out_free_data;
if (reply->status != BL_DEVICE_REQUEST_PROC) {
printk(KERN_WARNING "%s failed to decode device: %d\n",
__func__, reply->status);
@@ -113,32 +109,73 @@ static ssize_t bl_pipe_downcall(struct file *filp, const char __user *src,
{
struct nfs_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
nfs_net_id);
struct rpc_pipe *pipe = nn->bl_device_pipe;
struct bl_dev_msg reply;
bool accepted;
if (mlen != sizeof (struct bl_dev_msg))
if (mlen != sizeof(reply))
return -EINVAL;
if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0)
if (copy_from_user(&reply, src, mlen) != 0)
return -EFAULT;
wake_up(&nn->bl_wq);
/*
* Accept a reply only after blkmapd has read the whole upcall.
* Completion retires the message, here and in
* bl_pipe_destroy_msg(), so a later reply finds nothing in
* flight.
*/
spin_lock(&pipe->lock);
accepted = rpc_msg_is_inflight(&nn->bl_pipe_msg);
if (accepted) {
nn->bl_pipe_msg.copied = 0;
nn->bl_mount_reply = reply;
complete(&nn->bl_done);
}
spin_unlock(&pipe->lock);
if (!accepted)
return -EINVAL;
return mlen;
}
static void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
struct bl_pipe_msg *bl_pipe_msg =
container_of(msg, struct bl_pipe_msg, msg);
struct nfs_net *nn = container_of(msg, struct nfs_net, bl_pipe_msg);
struct rpc_pipe *pipe = nn->bl_device_pipe;
if (msg->errno >= 0)
return;
wake_up(bl_pipe_msg->bl_wq);
spin_lock(&pipe->lock);
msg->copied = 0;
spin_unlock(&pipe->lock);
complete(&nn->bl_done);
}
/*
* An upcall blkmapd has consumed is off every pipe list, so the
* purges in rpc_pipe_release() and rpc_close_pipes() cannot reach it.
* No downcall can arrive once the pipe is closed; release its waiter
* here.
*/
static void bl_release_pipe(struct inode *inode)
{
struct nfs_net *nn = net_generic(inode->i_sb->s_fs_info, nfs_net_id);
struct rpc_pipe *pipe = nn->bl_device_pipe;
spin_lock(&pipe->lock);
if (rpc_msg_is_inflight(&nn->bl_pipe_msg)) {
nn->bl_pipe_msg.copied = 0;
nn->bl_pipe_msg.errno = -EPIPE;
complete(&nn->bl_done);
}
spin_unlock(&pipe->lock);
}
static const struct rpc_pipe_ops bl_upcall_ops = {
.upcall = rpc_pipe_generic_upcall,
.downcall = bl_pipe_downcall,
.destroy_msg = bl_pipe_destroy_msg,
.release_pipe = bl_release_pipe,
};
static int nfs4blocklayout_register_sb(struct super_block *sb,
@@ -221,7 +258,7 @@ static int nfs4blocklayout_net_init(struct net *net)
int err;
mutex_init(&nn->bl_mutex);
init_waitqueue_head(&nn->bl_wq);
init_completion(&nn->bl_done);
nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0);
if (IS_ERR(nn->bl_device_pipe))
return PTR_ERR(nn->bl_device_pipe);
+4 -1
View File
@@ -10,6 +10,8 @@
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/completion.h>
struct bl_dev_msg {
int32_t status;
@@ -21,8 +23,9 @@ struct nfs_netns_client;
struct nfs_net {
struct cache_detail *nfs_dns_resolve;
struct rpc_pipe *bl_device_pipe;
struct rpc_pipe_msg bl_pipe_msg;
struct bl_dev_msg bl_mount_reply;
wait_queue_head_t bl_wq;
struct completion bl_done;
struct mutex bl_mutex;
struct list_head nfs_client_list;
struct list_head nfs_volume_list;
+1
View File
@@ -402,6 +402,7 @@ static void __nfs42_ssc_close(struct file *filep)
}
static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = {
.owner = THIS_MODULE,
.sco_open = __nfs42_ssc_open,
.sco_close = __nfs42_ssc_close,
};
-25
View File
@@ -58,7 +58,6 @@
#include <linux/rcupdate.h>
#include <linux/uaccess.h>
#include <linux/nfs_ssc.h>
#include <uapi/linux/tls.h>
@@ -92,12 +91,6 @@ const struct super_operations nfs_sops = {
};
EXPORT_SYMBOL_GPL(nfs_sops);
#ifdef CONFIG_NFS_V4_2
static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = {
.sco_sb_deactive = nfs_sb_deactive,
};
#endif
#if IS_ENABLED(CONFIG_NFS_V4)
static int __init register_nfs4_fs(void)
{
@@ -119,18 +112,6 @@ static void unregister_nfs4_fs(void)
}
#endif
#ifdef CONFIG_NFS_V4_2
static void nfs_ssc_register_ops(void)
{
nfs_ssc_register(&nfs_ssc_clnt_ops_tbl);
}
static void nfs_ssc_unregister_ops(void)
{
nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl);
}
#endif /* CONFIG_NFS_V4_2 */
static struct shrinker *acl_shrinker;
/*
@@ -163,9 +144,6 @@ int __init register_nfs_fs(void)
shrinker_register(acl_shrinker);
#ifdef CONFIG_NFS_V4_2
nfs_ssc_register_ops();
#endif
return 0;
error_3:
nfs_unregister_sysctl();
@@ -185,9 +163,6 @@ void __exit unregister_nfs_fs(void)
shrinker_free(acl_shrinker);
nfs_unregister_sysctl();
unregister_nfs4_fs();
#ifdef CONFIG_NFS_V4_2
nfs_ssc_unregister_ops();
#endif
unregister_filesystem(&nfs_fs_type);
}
+3 -2
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <uapi/linux/nfs.h>
#include <linux/nfs_common.h>
#include <linux/nfs4.h>
@@ -53,12 +54,12 @@ static const struct {
* Returns a local errno value, or -EIO if the NFS status code is
* not recognized. This function is used jointly by NFSv2 and NFSv3.
*/
int nfs_stat_to_errno(enum nfs_stat status)
int nfs_stat_to_errno(int status)
{
int i;
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].stat == (int)status)
if (nfs_errtbl[i].stat == status)
return nfs_errtbl[i].errno;
}
return -EIO;
+78 -48
View File
@@ -10,11 +10,81 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/nfs_ssc.h>
#include "../nfs/nfs4_fs.h"
#include <linux/nfsd_ssc.h>
struct nfs_ssc_client_ops_tbl {
const struct nfs4_ssc_client_ops __rcu *ssc_nfs4_ops;
};
struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl;
EXPORT_SYMBOL_GPL(nfs_ssc_client_tbl);
static struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl __read_mostly;
/**
* nfsd42_ssc_open - Open a file to be used for server-to-server copy
* @ss_mnt: active mount point on which the source file resides
* @src_fh: file handle of the source file to be copied
* @stateid: stateid to use for COPY operation
*
* Caller must close the returned file using nfsd42_ssc_close().
*
* Return: an open file, or an ERR_PTR on error
*/
struct file *nfsd42_ssc_open(struct vfsmount *ss_mnt, struct nfs_fh *src_fh,
nfs4_stateid *stateid)
{
/*
* Built under CONFIG_NFS_V4_2_SSC_HELPER, which the NFS client
* enables on its own. The dispatch below is live only when the
* server also sets CONFIG_NFSD_V4_2_INTER_SSC; without it the
* source file cannot be opened, so callers get -EIO.
*/
#if IS_ENABLED(CONFIG_NFSD_V4_2_INTER_SSC)
const struct nfs4_ssc_client_ops *ops;
struct file *res;
/*
* sco_open() sleeps and must not run inside an RCU read-side
* section. Pin the provider module so the open runs with the
* module held; try_module_get() fails once unregister begins,
* and the copy then gets -EIO.
*/
rcu_read_lock();
ops = rcu_dereference(nfs_ssc_client_tbl.ssc_nfs4_ops);
if (ops && try_module_get(ops->owner)) {
rcu_read_unlock();
res = ops->sco_open(ss_mnt, src_fh, stateid);
module_put(ops->owner);
return res;
}
rcu_read_unlock();
#endif
return ERR_PTR(-EIO);
}
EXPORT_SYMBOL_GPL(nfsd42_ssc_open);
/**
* nfsd42_ssc_close - Close a file opened with nfsd42_ssc_open()
* @filp: struct file to be closed
*
* The real cleanup happens unconditionally in nfsd4_cleanup_inter_ssc().
* The client ops table is read under RCU; nfs42_ssc_unregister() calls
* synchronize_rcu() so unregistration cannot complete while a close is
* in flight.
*/
void nfsd42_ssc_close(struct file *filp)
{
/* Live only under CONFIG_NFSD_V4_2_INTER_SSC; see nfsd42_ssc_open(). */
#if IS_ENABLED(CONFIG_NFSD_V4_2_INTER_SSC)
const struct nfs4_ssc_client_ops *ops;
rcu_read_lock();
ops = rcu_dereference(nfs_ssc_client_tbl.ssc_nfs4_ops);
if (ops)
ops->sco_close(filp);
rcu_read_unlock();
#endif
}
EXPORT_SYMBOL_GPL(nfsd42_ssc_close);
#ifdef CONFIG_NFS_V4_2
/**
@@ -26,7 +96,7 @@ EXPORT_SYMBOL_GPL(nfs_ssc_client_tbl);
*/
void nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops)
{
nfs_ssc_client_tbl.ssc_nfs4_ops = ops;
rcu_assign_pointer(nfs_ssc_client_tbl.ssc_nfs4_ops, ops);
}
EXPORT_SYMBOL_GPL(nfs42_ssc_register);
@@ -40,52 +110,12 @@ EXPORT_SYMBOL_GPL(nfs42_ssc_register);
*/
void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops)
{
if (nfs_ssc_client_tbl.ssc_nfs4_ops != ops)
if (rcu_dereference_protected(nfs_ssc_client_tbl.ssc_nfs4_ops,
true) != ops)
return;
nfs_ssc_client_tbl.ssc_nfs4_ops = NULL;
rcu_assign_pointer(nfs_ssc_client_tbl.ssc_nfs4_ops, NULL);
synchronize_rcu();
}
EXPORT_SYMBOL_GPL(nfs42_ssc_unregister);
#endif /* CONFIG_NFS_V4_2 */
#ifdef CONFIG_NFS_V4_2
/**
* nfs_ssc_register - install the NFS_FS client ops in the nfs_ssc_client_tbl
* @ops: NFS_FS ops to be installed
*
* Return values:
* None
*/
void nfs_ssc_register(const struct nfs_ssc_client_ops *ops)
{
nfs_ssc_client_tbl.ssc_nfs_ops = ops;
}
EXPORT_SYMBOL_GPL(nfs_ssc_register);
/**
* nfs_ssc_unregister - uninstall the NFS_FS client ops from
* the nfs_ssc_client_tbl
* @ops: ops to be uninstalled
*
* Return values:
* None
*/
void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops)
{
if (nfs_ssc_client_tbl.ssc_nfs_ops != ops)
return;
nfs_ssc_client_tbl.ssc_nfs_ops = NULL;
}
EXPORT_SYMBOL_GPL(nfs_ssc_unregister);
#else
void nfs_ssc_register(const struct nfs_ssc_client_ops *ops)
{
}
EXPORT_SYMBOL_GPL(nfs_ssc_register);
void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops)
{
}
EXPORT_SYMBOL_GPL(nfs_ssc_unregister);
#endif /* CONFIG_NFS_V4_2 */
+13 -2
View File
@@ -14,7 +14,7 @@ nfsd-y += nfssvc.o nfsctl.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o \
stats.o filecache.o nfs3proc.o nfs3xdr.o \
netlink.o
nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o
nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o nfs2xdr_gen.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
@@ -37,13 +37,24 @@ nfsd-$(CONFIG_DEBUG_FS) += debugfs.o
#
.PHONY: xdrgen
xdrgen: ../../include/linux/sunrpc/xdrgen/nfs4_1.h nfs4xdr_gen.h nfs4xdr_gen.c
XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nfs2.h \
../../include/linux/sunrpc/xdrgen/nfs4_1.h
XDRGEN_DECLARATIONS = nfs2xdr_gen.h nfs4xdr_gen.h
XDRGEN_SOURCE = nfs2xdr_gen.c nfs4xdr_gen.c
xdrgen: $(XDRGEN_DEFINITIONS) $(XDRGEN_DECLARATIONS) $(XDRGEN_SOURCE)
../../include/linux/sunrpc/xdrgen/nfs2.h: ../../Documentation/sunrpc/xdr/nfs2.x
../../tools/net/sunrpc/xdrgen/xdrgen definitions $< > $@
../../include/linux/sunrpc/xdrgen/nfs4_1.h: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen definitions $< > $@
nfs2xdr_gen.h: ../../Documentation/sunrpc/xdr/nfs2.x
../../tools/net/sunrpc/xdrgen/xdrgen declarations $< > $@
nfs4xdr_gen.h: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen declarations $< > $@
nfs2xdr_gen.c: ../../Documentation/sunrpc/xdr/nfs2.x
../../tools/net/sunrpc/xdrgen/xdrgen source $< > $@
nfs4xdr_gen.c: ../../Documentation/sunrpc/xdr/nfs4_1.x
../../tools/net/sunrpc/xdrgen/xdrgen source $< > $@
+1
View File
@@ -9,6 +9,7 @@
#include <linux/nfsd/debug.h>
#include "nfserr.h"
#include "blocklayoutxdr.h"
#include "pnfs.h"
#include "filecache.h"
+11
View File
@@ -8,11 +8,22 @@
#include <linux/nfs4.h>
#include "nfsd.h"
#include "nfserr.h"
#include "blocklayoutxdr.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS
static __be32
nfsd4_decode_deviceid4(struct xdr_stream *xdr, struct nfsd4_deviceid *devid)
{
__be32 *p = xdr_inline_decode(xdr, NFS4_DEVICEID4_SIZE);
if (unlikely(!p))
return nfserr_bad_xdr;
svcxdr_decode_deviceid4(p, devid);
return nfs_ok;
}
/**
* nfsd4_block_encode_layoutget - encode block/scsi layout extent array
+4 -4
View File
@@ -21,6 +21,8 @@
#include <uapi/linux/nfsd_netlink.h>
#include "nfsd.h"
#include "nfserr.h"
#include "nfs4ctl.h"
#include "nfsfh.h"
#include "netns.h"
#include "pnfs.h"
@@ -1890,21 +1892,19 @@ __be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp,
* check_nfsd_access - check if access to export is allowed.
* @exp: svc_export that is being accessed.
* @rqstp: svc_rqst attempting to access @exp.
* @may_bypass_gss: reduce strictness of authorization check
*
* Return values:
* %nfs_ok if access is granted, or
* %nfserr_wrongsec if access is denied
*/
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
bool may_bypass_gss)
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
{
__be32 status;
status = check_xprtsec_policy(exp, rqstp);
if (status != nfs_ok)
return status;
return check_security_flavor(exp, rqstp, may_bypass_gss);
return check_security_flavor(exp, rqstp, false);
}
/*
+1 -2
View File
@@ -104,8 +104,7 @@ int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp);
__be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp);
__be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp,
bool may_bypass_gss);
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
bool may_bypass_gss);
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
/*
* Function declarations
+3 -2
View File
@@ -43,6 +43,7 @@
#include "vfs.h"
#include "nfsd.h"
#include "nfserr.h"
#include "nfsfh.h"
#include "netns.h"
#include "filecache.h"
@@ -1285,9 +1286,9 @@ open_file:
}
status = nfserrno(ret);
trace_nfsd_file_open(nf, status);
if (status == nfs_ok)
status = nfsd_file_get_dio_attrs(fhp, nf);
}
if (status == nfs_ok)
status = nfsd_file_get_dio_attrs(fhp, nf);
} else
status = nfserr_jukebox;
/*
+14 -10
View File
@@ -13,7 +13,9 @@
#include <linux/sunrpc/addr.h>
#include "nfserr.h"
#include "flexfilelayoutxdr.h"
#include "auth.h"
#include "pnfs.h"
#include "vfs.h"
@@ -23,10 +25,10 @@ static __be32
nfsd4_ff_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
const struct svc_fh *fhp, struct nfsd4_layoutget *args)
{
struct user_namespace *userns = nfsd_user_namespace(rqstp);
struct nfsd4_layout_seg *seg = &args->lg_seg;
u32 device_generation = 0;
int error;
uid_t u;
struct pnfs_ff_layout *fl;
@@ -49,20 +51,22 @@ nfsd4_ff_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
fl->flags = FF_FLAGS_NO_LAYOUTCOMMIT | FF_FLAGS_NO_IO_THRU_MDS |
FF_FLAGS_NO_READ_IO;
/* Do not allow a IOMODE_READ segment to have write pemissions */
if (seg->iomode == IOMODE_READ) {
u = from_kuid(&init_user_ns, inode->i_uid) + 1;
fl->uid = make_kuid(&init_user_ns, u);
} else
fl->uid = inode->i_uid;
fl->gid = inode->i_gid;
fl->uid = from_kuid_munged(userns, inode->i_uid);
fl->gid = from_kgid_munged(userns, inode->i_gid);
/*
* Do not allow an IOMODE_READ segment to have write permissions.
* The group is left intact so group-readable files stay readable;
* nfsd_setuser() squashes an unmapped uid to the export's anon ID.
*/
if (seg->iomode == IOMODE_READ)
fl->uid++;
error = nfsd4_set_deviceid(&fl->deviceid, fhp, device_generation);
if (error)
goto out_error;
fl->fh.size = fhp->fh_handle.fh_size;
memcpy(fl->fh.data, &fhp->fh_handle.fh_raw, fl->fh.size);
fh_copy_shallow(&fl->fh, &fhp->fh_handle);
/* Give whole file layout segments */
seg->offset = 0;
+5 -4
View File
@@ -6,6 +6,7 @@
#include <linux/nfs4.h>
#include "nfsd.h"
#include "nfserr.h"
#include "flexfilelayoutxdr.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS
@@ -30,10 +31,10 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
struct ff_idmap uid;
struct ff_idmap gid;
fh_len = 4 + xdr_align_size(fl->fh.size);
fh_len = 4 + xdr_align_size(fl->fh.fh_size);
uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid));
gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid));
uid.len = sprintf(uid.buf, "%u", fl->uid);
gid.len = sprintf(gid.buf, "%u", fl->gid);
/* data server entry: deviceid + efficiency + stateid + fh list +
* user + group + flags + stats_collect_hint
@@ -68,7 +69,7 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
sizeof(stateid_opaque_t));
*p++ = cpu_to_be32(1); /* single file handle */
p = xdr_encode_opaque(p, fl->fh.data, fl->fh.size);
p = xdr_encode_opaque(p, fl->fh.fh_raw, fl->fh.fh_size);
p = xdr_encode_opaque(p, uid.buf, uid.len);
p = xdr_encode_opaque(p, gid.buf, gid.len);
+5 -3
View File
@@ -6,6 +6,7 @@
#define _NFSD_FLEXFILELAYOUTXDR_H 1
#include <linux/inet.h>
#include "nfsfh.h"
#include "xdr4.h"
#define FF_FLAGS_NO_LAYOUTCOMMIT 1
@@ -35,11 +36,12 @@ struct pnfs_ff_device_addr {
struct pnfs_ff_layout {
u32 flags;
u32 stats_collect_hint;
kuid_t uid;
kgid_t gid;
/* Values to encode; nfsd4_ff_proc_layoutget() has mapped these */
u32 uid;
u32 gid;
struct nfsd4_deviceid deviceid;
stateid_t stateid;
struct nfs_fh fh;
struct knfsd_fh fh;
};
__be32 nfsd4_ff_encode_getdeviceinfo(struct xdr_stream *xdr,
+4 -6
View File
@@ -11,11 +11,9 @@
#include <linux/exportfs.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs_common.h>
#include <linux/nfs_fh.h>
#include <linux/nfslocalio.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>
#include <linux/string.h>
#include "nfsd.h"
@@ -55,7 +53,7 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
struct nfsd_file *localio;
__be32 beres;
if (nfs_fh->size > NFS4_FHSIZE)
if (nfs_fh->size > NFS_MAXFHSIZE)
return ERR_PTR(-EINVAL);
if (!nfsd_net_try_get(net))
@@ -68,7 +66,7 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
return localio;
/* nfs_fh -> svc_fh */
fh_init(&fh, NFS4_FHSIZE);
fh_init(&fh, NFSD_FHSIZE_UNSPEC);
fh.fh_handle.fh_size = nfs_fh->size;
memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size);
@@ -179,7 +177,7 @@ static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
struct localio_uuidarg *argp = rqstp->rq_argp;
u8 uuid[UUID_SIZE];
if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
if (xdr_stream_decode_opaque_fixed(xdr, uuid, UUID_SIZE) < 0)
return false;
import_uuid(&argp->uuid, uuid);
+3 -2
View File
@@ -9,7 +9,9 @@
#include <linux/file.h>
#include <linux/lockd/bind.h>
#include <linux/nfs_fh.h>
#include "nfsd.h"
#include "nfserr.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_LOCKD
@@ -33,8 +35,7 @@ static int nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f,
int access;
struct svc_fh fh;
/* must initialize before using! but maxsize doesn't matter */
fh_init(&fh,0);
fh_init(&fh, NFSD_FHSIZE_UNSPEC);
fh.fh_handle.fh_size = f->size;
memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
fh.fh_export = NULL;
+3 -2
View File
@@ -79,8 +79,9 @@ static const struct nla_policy nfsd_version_set_nl_policy[NFSD_A_SERVER_PROTO_VE
};
/* NFSD_CMD_LISTENER_SET - do */
static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_SOCK_ADDR + 1] = {
static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND + 1] = {
[NFSD_A_SERVER_SOCK_ADDR] = NLA_POLICY_NESTED(nfsd_sock_nl_policy),
[NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND] = { .type = NLA_FLAG, },
};
/* NFSD_CMD_POOL_MODE_SET - do */
@@ -153,7 +154,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.cmd = NFSD_CMD_LISTENER_SET,
.doit = nfsd_nl_listener_set_doit,
.policy = nfsd_listener_set_nl_policy,
.maxattr = NFSD_A_SERVER_SOCK_ADDR,
.maxattr = NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
+15 -2
View File
@@ -238,8 +238,21 @@ struct nfsd_net {
int nfs4_max_clients;
atomic_t nfsd_courtesy_clients;
struct shrinker *nfsd_client_shrinker;
struct work_struct nfsd_shrinker_work;
/* per-namespace; num_delegations in nfs4state.c is host-wide */
atomic_long_t nfsd_delegations;
struct shrinker *nfsd_courtesy_shrinker;
struct shrinker *nfsd_deleg_shrinker;
struct work_struct nfsd_courtesy_work;
struct work_struct nfsd_deleg_work;
/* courtesy scan requests the reaper has not retired yet */
atomic_long_t nfsd_shrink_backlog;
/* delegation scan requests the reaper has not retired yet */
atomic_long_t nfsd_deleg_backlog;
/* when deleg_reaper() last swept the client list */
time64_t nfsd_last_recall_any;
/* last time an admin-revoke happened for NFSv4.0 */
time64_t nfs40_last_revoke;
+65 -1
View File
@@ -6,15 +6,73 @@
*/
#include "nfsd.h"
#include "nfserr.h"
/* FIXME: nfsacl.h is a broken header */
#include <linux/nfsacl.h>
#include <linux/gfp.h>
#include "cache.h"
#include "xdr.h"
#include "xdr3.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
/*
* These maps are identical to the NFSv3 maps (nfs3proc.c). This enables
* the behavior of the two versions to diverge if needed.
*/
static const struct nfsd_access_map nfsd2_regaccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE },
{ 0, 0 }
};
static const struct nfsd_access_map nfsd2_diraccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_LOOKUP, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE },
{ NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE },
{ 0, 0 }
};
/*
* Some clients - Solaris 2.6 at least, make an access call to the NFS
* server to check for access for things like /dev/null (which really,
* NFSD doesn't care about). So NFSD provides simple access checking
* for those objects, looking mainly at mode bits, ignoring read-only
* filesystem checks.
*/
static const struct nfsd_access_map nfsd2_otheraccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ 0, 0 }
};
static const struct nfsd_access_maps nfsd2_access_maps = {
.regular = nfsd2_regaccess,
.directory = nfsd2_diraccess,
.other = nfsd2_otheraccess,
};
static __be32 nfsacld_map_status(__be32 status)
{
switch (status) {
case nfserr_nofilehandle:
case nfserr_badhandle:
status = nfserr_stale;
break;
case nfserr_wrongsec:
status = nfserr_acces;
break;
}
return status;
}
/*
* NULL call.
*/
@@ -79,6 +137,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
out:
resp->status = nfsacld_map_status(resp->status);
return rpc_success;
fail:
@@ -137,6 +196,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
out:
/* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */
resp->status = nfsacld_map_status(resp->status);
return rpc_success;
out_drop_lock:
@@ -163,6 +223,7 @@ static __be32 nfsacld_proc_getattr(struct svc_rqst *rqstp)
goto out;
resp->status = fh_getattr(&resp->fh, &resp->stat);
out:
resp->status = nfsacld_map_status(resp->status);
return rpc_success;
}
@@ -180,11 +241,14 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
fh_copy(&resp->fh, &argp->fh);
resp->access = argp->access;
resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
resp->status = nfsd_access(rqstp, &resp->fh, &nfsd2_access_maps,
&resp->access, NULL);
if (resp->status != nfs_ok)
goto out;
resp->status = fh_getattr(&resp->fh, &resp->stat);
out:
resp->status = nfsacld_map_status(resp->status);
return rpc_success;
}
File diff suppressed because it is too large. Load diff
+46
View File
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs2.x */
/* XDR specification modification time: Tue Sep 8 11:26:28 2026 */
#ifndef _LINUX_XDRGEN_NFS2_DECL_H
#define _LINUX_XDRGEN_NFS2_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/nfs2.h>
enum {
NFS2_READDIROK_ENTRIES,
};
bool nfs2_readdirok_encode_begin(struct xdrgen_aggregate_cursor *c);
bool nfs2_readdirok_encode(struct xdrgen_aggregate_cursor *c, struct entry *out);
bool nfs2_readdirok_encode_end(struct xdrgen_aggregate_cursor *c, bool ok);
bool nfs2_readdirok_decode_begin(struct xdrgen_aggregate_cursor *c);
bool nfs2_readdirok_decode(struct xdrgen_aggregate_cursor *c, const struct entry *in);
bool nfs2_readdirok_decode_end(struct xdrgen_aggregate_cursor *c, bool ok);
bool nfs_svc_decode_fhandle(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_attrstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_nfsstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs_svc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
#endif /* _LINUX_XDRGEN_NFS2_DECL_H */
+16
View File
@@ -6,6 +6,7 @@
*/
#include "nfsd.h"
#include "nfserr.h"
/* FIXME: nfsacl.h is a broken header */
#include <linux/nfsacl.h>
#include <linux/gfp.h>
@@ -13,6 +14,19 @@
#include "xdr3.h"
#include "vfs.h"
static __be32 nfsd3_map_status(__be32 status)
{
switch (status) {
case nfserr_nofilehandle:
status = nfserr_badhandle;
break;
case nfserr_wrongsec:
status = nfserr_acces;
break;
}
return status;
}
/*
* NULL call.
*/
@@ -71,6 +85,7 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
/* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
out:
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
fail:
@@ -124,6 +139,7 @@ out_errno:
resp->status = nfserrno(error);
out:
/* argp->acl_{access,default} are released in nfs3svc_release_setacl. */
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
+76 -3
View File
@@ -9,10 +9,12 @@
#include <linux/ext2_fs.h>
#include <linux/magic.h>
#include <linux/namei.h>
#include <linux/nfs3.h>
#include "cache.h"
#include "xdr3.h"
#include "vfs.h"
#include "nfserr.h"
#include "filecache.h"
#include "trace.h"
@@ -48,6 +50,58 @@ static bool nfsd3_time_in_range(const struct iattr *iap)
return true;
}
static const struct nfsd_access_map nfsd3_regaccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE },
{ 0, 0 }
};
static const struct nfsd_access_map nfsd3_diraccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_LOOKUP, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE },
{ NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE },
{ 0, 0 }
};
/*
* Some clients - Solaris 2.6 at least, make an access call to the NFS
* server to check for access for things like /dev/null (which really,
* NFSD doesn't care about). So NFSD provides simple access checking
* for those objects, looking mainly at mode bits, ignoring read-only
* filesystem checks.
*/
static const struct nfsd_access_map nfsd3_otheraccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ 0, 0 }
};
static const struct nfsd_access_maps nfsd3_access_maps = {
.regular = nfsd3_regaccess,
.directory = nfsd3_diraccess,
.other = nfsd3_otheraccess,
};
static int nfsd3_iocb_flags(enum nfs3_stable_how how)
{
switch (how) {
case NFS_FILE_SYNC:
/* persist data and timestamps */
return IOCB_DSYNC | IOCB_SYNC;
case NFS_DATA_SYNC:
/* persist data only */
return IOCB_DSYNC;
default:
return 0;
}
}
static __be32 nfsd3_map_status(__be32 status)
{
switch (status) {
@@ -171,7 +225,8 @@ nfsd3_proc_access(struct svc_rqst *rqstp)
fh_copy(&resp->fh, &argp->fh);
resp->access = argp->access;
resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
resp->status = nfsd_access(rqstp, &resp->fh, &nfsd3_access_maps,
&resp->access, NULL);
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
}
@@ -260,7 +315,8 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
resp->committed = argp->stable;
resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
&argp->payload, &cnt,
resp->committed, resp->verf);
nfsd3_iocb_flags(resp->committed),
resp->verf);
resp->count = cnt;
resp->status = nfsd3_map_status(resp->status);
return rpc_success;
@@ -282,6 +338,7 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_attrs attrs = {
.na_iattr = iap,
};
struct svc_export *exp;
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
@@ -320,7 +377,23 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
exp = exp_get(fhp->fh_export);
if (argp->createmode == NFS3_CREATE_UNCHECKED) {
/*
* If name is already in dcache we need to check for mountpoints
*/
if (d_is_reg(child) &&
unlikely(nfsd_mountpoint(child, exp))) {
status = nfsd_cross_mnt(rqstp, &child, &exp);
if (status != nfs_ok) {
exp_put(exp);
goto out;
}
}
}
status = fh_compose(resfhp, exp, child, fhp);
exp_put(exp);
if (status != nfs_ok)
goto out;
+6
View File
@@ -8,11 +8,15 @@
*/
#include <linux/namei.h>
#include <linux/nfs3.h>
#include <linux/sunrpc/svc_xprt.h>
#include "nfsd.h"
#include "xdr3.h"
#include "auth.h"
#include "netns.h"
#include "vfs.h"
#include "nfserr.h"
/*
* Force construction of an empty post-op attr
@@ -556,6 +560,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return false;
if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
return false;
if (args->stable > NFS_FILE_SYNC)
return false;
/* opaque data */
if (xdr_stream_decode_u32(xdr, &args->len) < 0)
+1
View File
@@ -40,6 +40,7 @@
#include "nfsfh.h"
#include "nfsd.h"
#include "nfserr.h"
#include "acl.h"
#include "vfs.h"
+6 -2
View File
@@ -37,6 +37,7 @@
#include <linux/sunrpc/svc_xprt.h>
#include <linux/slab.h>
#include "nfsd.h"
#include "nfserr.h"
#include "state.h"
#include "netns.h"
#include "stats.h"
@@ -1529,12 +1530,14 @@ out:
/**
* nfsd41_cb_destroy_referring_call_list - release referring call info
* @cb: context of a callback that has completed
* @cb: context of callback to release referring calls from
*
* Callers who allocate referring calls using nfsd41_cb_referring_call() must
* release those resources by calling nfsd41_cb_destroy_referring_call_list.
*
* Caller serializes access to @cb.
* Caller serializes access to @cb. No CB_COMPOUND for @cb may be in
* flight, because encode_cb_sequence4args() walks this list as it
* encodes.
*/
void nfsd41_cb_destroy_referring_call_list(struct nfsd4_callback *cb)
{
@@ -1556,6 +1559,7 @@ void nfsd41_cb_destroy_referring_call_list(struct nfsd4_callback *cb)
list_del(&rcl->__list);
kfree(rcl);
}
cb->cb_nr_referring_call_list = 0;
}
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
+83
View File
@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Entry points by which the knfsd core drives the optional NFSv4
* subsystem: state lifecycle, the laundromat workqueue, the recovery
* directory, junctions, the CLD notifier, and leases-net setup.
*
* Separated from nfsd.h so that the many translation units that
* include nfsd.h but call none of these -- among them the NFSv2 and
* NFSv3 paths -- do not have to parse them. The CONFIG_NFSD_V4=n
* stubs let the version-agnostic callers invoke the routines
* unconditionally.
*/
#ifndef LINUX_NFSD_NFS4CTL_H
#define LINUX_NFSD_NFS4CTL_H
#include <linux/stddef.h>
#include <linux/types.h>
struct net;
struct inode;
struct dentry;
struct svc_rqst;
struct nfsd_net;
#ifdef CONFIG_NFSD_V4
extern unsigned long max_delegations;
int nfsd4_init_slabs(void);
void nfsd4_free_slabs(void);
int nfs4_state_start(void);
int nfs4_state_start_net(struct net *net);
void nfs4_state_shutdown(void);
void nfs4_state_shutdown_net(struct net *net);
int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void);
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
int nfsd4_create_laundry_wq(void);
void nfsd4_destroy_laundry_wq(void);
bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode);
extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
#endif
extern void nfsd4_init_leases_net(struct nfsd_net *nn);
#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { }
static inline int nfs4_state_start(void) { return 0; }
static inline int nfs4_state_start_net(struct net *net) { return 0; }
static inline void nfs4_state_shutdown(void) { }
static inline void nfs4_state_shutdown_net(struct net *net) { }
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
static inline char * nfs4_recoverydir(void) {return NULL; }
static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
{
return false;
}
static inline int nfsd4_create_laundry_wq(void) { return 0; };
static inline void nfsd4_destroy_laundry_wq(void) {};
static inline bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp,
struct inode *inode)
{
return false;
}
static inline int nfsd4_is_junction(struct dentry *dentry)
{
return 0;
}
static inline void nfsd4_init_leases_net(struct nfsd_net *nn) { };
#define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0)
#endif /* CONFIG_NFSD_V4 */
#endif /* LINUX_NFSD_NFS4CTL_H */
+4 -1
View File
@@ -41,6 +41,7 @@
#include "auth.h"
#include "idmap.h"
#include "nfsd.h"
#include "nfserr.h"
#include "netns.h"
#include "vfs.h"
@@ -253,8 +254,10 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
/* Name */
error = -EINVAL;
len = qword_get(&buf, buf1, PAGE_SIZE);
if (len < 0 || len >= IDMAP_NAMESZ)
if (len < 0 || len >= IDMAP_NAMESZ) {
cache_put(&res->h, cd);
goto out;
}
if (len == 0)
set_bit(CACHE_NEGATIVE, &ent.h.flags);
else
+4 -1
View File
@@ -9,6 +9,7 @@
#include <linux/sched.h>
#include <linux/sunrpc/addr.h>
#include "nfserr.h"
#include "pnfs.h"
#include "netns.h"
#include "trace.h"
@@ -127,10 +128,12 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
void nfsd4_setup_layout_type(struct svc_export *exp)
{
struct nfsd_net *nn = net_generic(exp->cd->net, nfsd_net_id);
struct super_block *sb = exp->ex_path.mnt->mnt_sb;
expfs_block_layouts_t block_supported = exportfs_layouts_supported(sb);
if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT) &&
nfsd_vers(nn, 3, NFSD_TEST))
exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
(block_supported & EXPFS_BLOCK_IN_BAND_ID))
+284 -191
View File
@@ -38,19 +38,22 @@
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/addr.h>
#include <linux/nfs_ssc.h>
#include <linux/nfsd_ssc.h>
#include "attr4.h"
#include "idmap.h"
#include "cache.h"
#include "xdr4.h"
#include "nfs4ctl.h"
#include "vfs.h"
#include "current_stateid.h"
#include "netns.h"
#include "acl.h"
#include "pnfs.h"
#include "nfserr.h"
#include "trace.h"
static bool inter_copy_offload_enable;
@@ -69,6 +72,57 @@ MODULE_PARM_DESC(nfsd4_ssc_umount_timeout,
#define NFSDDBG_FACILITY NFSDDBG_PROC
static const struct nfsd_access_map nfsd4_regaccess[] = {
{ NFS4_ACCESS_READ, NFSD_MAY_READ },
{ NFS4_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS4_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS4_ACCESS_EXTEND, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
{ 0, 0 }
};
static const struct nfsd_access_map nfsd4_diraccess[] = {
{ NFS4_ACCESS_READ, NFSD_MAY_READ },
{ NFS4_ACCESS_LOOKUP, NFSD_MAY_EXEC },
{ NFS4_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS4_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE },
{ NFS4_ACCESS_DELETE, NFSD_MAY_REMOVE },
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
{ 0, 0 }
};
static const struct nfsd_access_map nfsd4_otheraccess[] = {
{ NFS4_ACCESS_READ, NFSD_MAY_READ },
{ NFS4_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS4_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ NFS4_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ 0, 0 }
};
static const struct nfsd_access_maps nfsd4_access_maps = {
.regular = nfsd4_regaccess,
.directory = nfsd4_diraccess,
.other = nfsd4_otheraccess,
};
static int nfsd4_iocb_flags(enum stable_how4 how)
{
switch (how) {
case FILE_SYNC4:
/* persist data and timestamps */
return IOCB_DSYNC | IOCB_SYNC;
case DATA_SYNC4:
/* persist data only */
return IOCB_DSYNC;
default:
return 0;
}
}
static u32 nfsd_attrmask[] = {
NFSD_WRITEABLE_ATTRS_WORD0,
NFSD_WRITEABLE_ATTRS_WORD1,
@@ -169,23 +223,17 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
return fh_verify(rqstp, current_fh, S_IFREG, accmode);
}
static __be32 nfsd_check_obj_isreg(struct svc_fh *fh, u32 minor_version)
static int nfsd_check_obj_isreg(struct dentry *child)
{
umode_t mode = d_inode(fh->fh_dentry)->i_mode;
umode_t mode = d_inode(child)->i_mode;
if (S_ISREG(mode))
return nfs_ok;
return 0;
if (S_ISDIR(mode))
return nfserr_isdir;
return -EISDIR;
if (S_ISLNK(mode))
return nfserr_symlink;
/* RFC 7530 - 16.16.6 */
if (minor_version == 0)
return nfserr_symlink;
else
return nfserr_wrong_type;
return -ELOOP;
return -EFTYPE;
}
static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh *resfh)
@@ -202,40 +250,50 @@ static inline bool nfsd4_create_is_exclusive(int createmode)
createmode == NFS4_CREATE_EXCLUSIVE4_1;
}
static __be32
nfsd4_vfs_create(struct svc_fh *fhp, struct dentry **child,
struct nfsd4_open *open)
static struct file *do_lookup_open(struct path *parent,
struct qstr *name,
unsigned int oflags,
umode_t mode)
{
struct file *filp;
struct file *filp = NULL;
struct path path;
int oflags;
struct dentry *child;
int want_write_err = 0;
oflags = O_CREAT | O_LARGEFILE;
if (nfsd4_create_is_exclusive(open->op_createmode))
oflags |= O_EXCL;
want_write_err = mnt_want_write(parent->mnt);
switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) {
case NFS4_SHARE_ACCESS_WRITE:
oflags |= O_WRONLY;
break;
case NFS4_SHARE_ACCESS_BOTH:
oflags |= O_RDWR;
break;
default:
oflags |= O_RDONLY;
child = start_creating(&nop_mnt_idmap, parent->dentry, name);
if (IS_ERR(child)) {
filp = ERR_CAST(child);
goto out;
}
path.mnt = parent->mnt;
path.dentry = child;
path.mnt = fhp->fh_export->ex_path.mnt;
path.dentry = *child;
filp = dentry_create(&path, oflags, open->op_iattr.ia_mode,
current_cred());
*child = path.dentry;
if (d_really_is_positive(child)) {
/*
* open the file so that we consistently have a valid
* op_filp and consequently a valid ->f_path.dentry.
*/
int err = nfsd_check_obj_isreg(child);
if (IS_ERR(filp))
return nfserrno(PTR_ERR(filp));
open->op_filp = filp;
return nfs_ok;
if (err)
filp = ERR_PTR(err);
else
filp = dentry_open(&path, oflags, current_cred());
} else if (!(oflags & O_CREAT)) {
filp = ERR_PTR(-ENOENT);
} else if (want_write_err) {
filp = ERR_PTR(want_write_err);
} else {
filp = dentry_create(&path, oflags, mode, current_cred());
child = path.dentry;
}
end_creating(child);
out:
if (!want_write_err)
mnt_drop_write(parent->mnt);
return filp;
}
/*
@@ -254,11 +312,15 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
.na_iattr = iap,
.na_seclabel = &open->op_label,
};
struct dentry *parent, *child = ERR_PTR(-EINVAL);
int oflags = O_CREAT | O_LARGEFILE;
struct dentry *child = ERR_PTR(-EINVAL);
struct path parent = {
.mnt = fhp->fh_export->ex_path.mnt,
.dentry = fhp->fh_dentry,
};
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
int host_err;
__be32 status, create_status;
int want_write_err;
if (name_is_dot_dotdot(open->op_fname, open->op_fnamelen))
return nfserr_exist;
@@ -268,43 +330,69 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
if (status != nfs_ok)
return status;
parent = fhp->fh_dentry;
inode = d_inode(parent);
host_err = fh_want_write(fhp);
if (host_err)
return nfserrno(host_err);
if (open->op_createmode == NFS4_CREATE_UNCHECKED) {
/*
* If name is already in dcache we need to check for mountpoints
*/
child = try_lookup_noperm(&QSTR_LEN(open->op_fname,
open->op_fnamelen),
parent.dentry);
if (child && !IS_ERR(child) && d_is_reg(child) &&
unlikely(nfsd_mountpoint(child, fhp->fh_export))) {
struct svc_export *exp = exp_get(fhp->fh_export);
if (open->op_acl) {
status = nfsd_cross_mnt(rqstp, &child, &exp);
if (status == nfs_ok)
status = fh_compose(resfhp, exp,
child, fhp);
fh_fill_post_noop(fhp);
open->op_truncate =
(iap->ia_valid & ATTR_SIZE) &&
!iap->ia_size;
dput(child);
exp_put(exp);
return status;
}
if (!IS_ERR(child))
dput(child);
}
if (!IS_POSIXACL(d_inode(parent.dentry)))
iap->ia_mode &= ~current_umask();
/*
* For the EXCLUSIVE modes we do our own uniqueness tests
* so don't want O_EXCL.
*/
if (open->op_createmode == NFS4_CREATE_GUARDED)
oflags |= O_EXCL;
switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) {
case NFS4_SHARE_ACCESS_WRITE:
oflags |= O_WRONLY;
break;
case NFS4_SHARE_ACCESS_BOTH:
oflags |= O_RDWR;
break;
default:
oflags |= O_RDONLY;
}
if (!is_create_with_attrs(open)) {
/* No attrs to check */
} else if (open->op_acl) {
if (open->op_dpacl || open->op_pacl) {
status = nfserr_inval;
goto out;
/* Cannot specify both NFSv4 and Posix ACLs */
return nfserr_inval;
}
if (is_create_with_attrs(open)) {
status = nfsd4_acl_to_attr(NF4REG, open->op_acl,
status = nfsd4_acl_to_attr(NF4REG, open->op_acl,
&attrs);
if (status)
goto out;
}
} else if (is_create_with_attrs(open)) {
/* The dpacl and pacl will get released by nfsd_attrs_free(). */
attrs.na_dpacl = open->op_dpacl;
attrs.na_pacl = open->op_pacl;
open->op_dpacl = NULL;
open->op_pacl = NULL;
}
child = start_creating(&nop_mnt_idmap, parent,
&QSTR_LEN(open->op_fname, open->op_fnamelen));
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
goto out;
}
if (d_really_is_negative(child)) {
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
if (status != nfs_ok)
goto out;
if (status)
return status;
} else {
attrs.na_dpacl = posix_acl_dup(open->op_dpacl);
attrs.na_pacl = posix_acl_dup(open->op_pacl);
}
v_mtime = 0;
@@ -322,24 +410,63 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
*/
v_mtime = verifier[0] & 0x7fffffff;
v_atime = verifier[1] & 0x7fffffff;
iap->ia_valid |= ATTR_MTIME | ATTR_ATIME |
ATTR_MTIME_SET|ATTR_ATIME_SET;
iap->ia_mtime.tv_sec = v_mtime;
iap->ia_atime.tv_sec = v_atime;
iap->ia_mtime.tv_nsec = 0;
iap->ia_atime.tv_nsec = 0;
}
if (d_really_is_positive(child)) {
/* NFSv4 protocol requires change attributes even though
* no change happened.
*/
status = fh_fill_both_attrs(fhp);
if (status != nfs_ok)
goto out;
create_status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
if (create_status)
/* Might still succeed if no create is needed */
oflags &= ~O_CREAT;
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
open->op_filp = do_lookup_open(&parent,
&QSTR_LEN(open->op_fname,
open->op_fnamelen),
oflags,
open->op_iattr.ia_mode);
if (IS_ERR(open->op_filp)) {
int hosterr = PTR_ERR(open->op_filp);
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
if (!d_is_reg(child))
break;
if (open->op_createmode != NFS4_CREATE_UNCHECKED) {
switch (hosterr) {
case -EISDIR:
case -ELOOP:
case -EFTYPE:
hosterr = -EEXIST;
}
}
status = nfserrno(hosterr);
open->op_filp = NULL;
if (status == nfserr_noent && create_status)
status = create_status;
goto out;
}
child = open->op_filp->f_path.dentry;
open->op_created = open->op_filp->f_mode & FMODE_CREATED;
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
if (!open->op_created &&
nfsd4_create_is_exclusive(open->op_createmode) &&
inode_get_mtime_sec(d_inode(child)) == v_mtime &&
inode_get_atime_sec(d_inode(child)) == v_atime &&
d_inode(child)->i_size == 0)
open->op_created = true;
if (!open->op_created) {
if (open->op_createmode == NFS4_CREATE_UNCHECKED) {
/* NFSv4 protocol requires change attributes
* even though no change happened.
*/
fh_fill_post_noop(fhp);
/*
* In NFSv4, we don't want to truncate the file
@@ -347,63 +474,30 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
* some other reason. Furthermore, if the size is
* nonzero, we should ignore it according to spec!
*/
open->op_truncate = (iap->ia_valid & ATTR_SIZE) &&
!iap->ia_size;
break;
case NFS4_CREATE_GUARDED:
open->op_truncate = (d_is_reg(child) &&
(iap->ia_valid & ATTR_SIZE) &&
!iap->ia_size);
} else
status = nfserr_exist;
break;
case NFS4_CREATE_EXCLUSIVE:
if (inode_get_mtime_sec(d_inode(child)) == v_mtime &&
inode_get_atime_sec(d_inode(child)) == v_atime &&
d_inode(child)->i_size == 0) {
open->op_created = true;
break; /* subtle */
}
status = nfserr_exist;
break;
case NFS4_CREATE_EXCLUSIVE4_1:
if (inode_get_mtime_sec(d_inode(child)) == v_mtime &&
inode_get_atime_sec(d_inode(child)) == v_atime &&
d_inode(child)->i_size == 0) {
open->op_created = true;
goto set_attr; /* subtle */
}
status = nfserr_exist;
}
goto out;
}
if (!IS_POSIXACL(inode))
iap->ia_mode &= ~current_umask();
status = fh_fill_pre_attrs(fhp);
if (status != nfs_ok)
goto out;
status = nfsd4_vfs_create(fhp, &child, open);
if (status != nfs_ok)
goto out;
open->op_created = true;
/* file was created */
fh_fill_post_attrs(fhp);
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
/* A newly created file already has a file size of zero. */
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;
if (nfsd4_create_is_exclusive(open->op_createmode)) {
iap->ia_valid = ATTR_MTIME | ATTR_ATIME |
ATTR_MTIME_SET|ATTR_ATIME_SET;
iap->ia_mtime.tv_sec = v_mtime;
iap->ia_atime.tv_sec = v_atime;
iap->ia_mtime.tv_nsec = 0;
iap->ia_atime.tv_nsec = 0;
}
set_attr:
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
/* We will need write access to set the attrs */
want_write_err = fh_want_write(fhp);
if (!want_write_err) {
status = nfsd_create_setattr(rqstp, fhp,
resfhp, &attrs);
fh_drop_write(fhp);
} else if (nfsd_attrs_valid(&attrs)) {
/* Needed write access */
status = nfserrno(want_write_err);
}
if (attrs.na_labelerr)
open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
@@ -414,9 +508,7 @@ set_attr:
if (attrs.na_paclerr)
open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
out:
end_creating(child);
nfsd_attrs_free(&attrs);
fh_drop_write(fhp);
return status;
}
@@ -465,6 +557,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
fh_init(*resfh, NFS4_FHSIZE);
open->op_truncate = false;
status = fh_fill_pre_attrs_unlocked(current_fh);
if (status)
goto out;
if (open->op_create) {
/* FIXME: check session persistence and pnfs flags.
* The nfsv4.1 spec requires the following semantics:
@@ -496,15 +591,15 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
} else {
status = nfsd_lookup(rqstp, current_fh,
open->op_fname, open->op_fnamelen, *resfh);
if (status == nfs_ok)
/* NFSv4 protocol requires change attributes even though
* no change happened.
*/
status = fh_fill_both_attrs(current_fh);
/*
* NFSv4 protocol requires change attributes even though
* no change happened.
*/
fh_fill_post_noop(current_fh);
}
if (status)
goto out;
status = nfsd_check_obj_isreg(*resfh, cstate->minorversion);
status = nfserrno(nfsd_check_obj_isreg((*resfh)->fh_dentry));
if (status)
goto out;
@@ -516,6 +611,10 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
status = do_open_permission(rqstp, *resfh, open, accmode);
set_change_info(&open->op_cinfo, current_fh);
out:
if (status == nfserr_wrong_type && cstate->minorversion == 0)
/* RFC 7530 - 16.16.6 */
return nfserr_symlink;
return status;
}
@@ -703,13 +802,6 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat
return nfsd4_open(rqstp, cstate, &op->u);
}
static void
nfsd4_open_release(union nfsd4_op_u *u)
{
posix_acl_release(u->open.op_dpacl);
posix_acl_release(u->open.op_pacl);
}
/*
* filehandle-manipulating ops.
*/
@@ -788,17 +880,18 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_access *access = &u->access;
u32 access_full;
access_full = NFS3_ACCESS_FULL;
access_full = NFS4_ACCESS_READ | NFS4_ACCESS_LOOKUP |
NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND |
NFS4_ACCESS_DELETE | NFS4_ACCESS_EXECUTE;
if (cstate->minorversion >= 2)
access_full |= NFS4_ACCESS_XALIST | NFS4_ACCESS_XAREAD |
NFS4_ACCESS_XAWRITE;
if (access->ac_req_access & ~access_full)
return nfserr_inval;
access->ac_resp_access = access->ac_req_access;
return nfsd_access(rqstp, &cstate->current_fh, &access->ac_resp_access,
&access->ac_supported);
return nfsd_access(rqstp, &cstate->current_fh, &nfsd4_access_maps,
&access->ac_resp_access, &access->ac_supported);
}
static __be32
@@ -829,16 +922,13 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_attrs attrs = {
.na_iattr = &create->cr_iattr,
.na_seclabel = &create->cr_label,
.na_dpacl = create->cr_dpacl,
.na_pacl = create->cr_pacl,
.na_dpacl = posix_acl_dup(create->cr_dpacl),
.na_pacl = posix_acl_dup(create->cr_pacl),
};
struct svc_fh resfh;
__be32 status;
dev_t rdev;
create->cr_dpacl = NULL;
create->cr_pacl = NULL;
fh_init(&resfh, NFS4_FHSIZE);
status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP);
@@ -1248,8 +1338,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_attrs attrs = {
.na_iattr = &setattr->sa_iattr,
.na_seclabel = &setattr->sa_label,
.na_pacl = setattr->sa_pacl,
.na_dpacl = setattr->sa_dpacl,
.na_pacl = posix_acl_dup(setattr->sa_pacl),
.na_dpacl = posix_acl_dup(setattr->sa_dpacl),
};
bool save_no_wcc, deleg_attrs;
struct nfs4_stid *st = NULL;
@@ -1257,10 +1347,6 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status = nfs_ok;
int err;
/* Transfer ownership to attrs for cleanup via nfsd_attrs_free() */
setattr->sa_pacl = NULL;
setattr->sa_dpacl = NULL;
deleg_attrs = setattr->sa_bmval[2] & (FATTR4_WORD2_TIME_DELEG_ACCESS |
FATTR4_WORD2_TIME_DELEG_MODIFY);
@@ -1377,7 +1463,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
write->wr_how_written = write->wr_stable_how;
status = nfsd_vfs_write(rqstp, &cstate->current_fh, nf,
write->wr_offset, &write->wr_payload,
&cnt, write->wr_how_written,
&cnt, nfsd4_iocb_flags(write->wr_how_written),
(__be32 *)write->wr_verifier.data);
nfsd_file_put(nf);
@@ -1431,16 +1517,37 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
struct nfsd4_clone *clone = &u->clone;
struct nfsd_file *src, *dst;
bool sync_failed = false;
errseq_t since;
__be32 status;
int host_err;
status = nfsd4_verify_copy(rqstp, cstate, &clone->cl_src_stateid, &src,
&clone->cl_dst_stateid, &dst);
if (status)
goto out;
status = nfsd4_clone_file_range(rqstp, src, clone->cl_src_pos,
dst, clone->cl_dst_pos, clone->cl_count,
EX_ISSYNC(cstate->current_fh.fh_export));
host_err = nfsd_clone_file_range(src->nf_file, clone->cl_src_pos,
dst->nf_file, clone->cl_dst_pos,
clone->cl_count, &since);
if (!host_err && EX_ISSYNC(cstate->current_fh.fh_export)) {
host_err = nfsd_clone_sync_range(src->nf_file, dst->nf_file,
clone->cl_dst_pos,
clone->cl_count, since);
sync_failed = host_err < 0;
}
if (host_err < 0) {
trace_nfsd_clone_file_range_err(rqstp, &cstate->save_fh,
clone->cl_src_pos, &cstate->current_fh,
clone->cl_dst_pos, clone->cl_count, host_err);
if (sync_failed) {
struct nfsd_net *nn = net_generic(dst->nf_net,
nfsd_net_id);
nfsd_maybe_reset_write_verifier(nn, rqstp, host_err);
}
}
status = nfserrno(host_err);
if (!status && (READ_ONCE(dst->nf_file->f_mode) & FMODE_NOCMTIME) != 0)
nfsd_update_cmtime_attr(dst->nf_file, 0);
@@ -1653,13 +1760,6 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
struct nfs_fh *src_fh,
nfs4_stateid *stateid);
extern void nfs42_ssc_close(struct file *filep);
extern void nfs_sb_deactive(struct super_block *sb);
#define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys"
/*
@@ -1882,7 +1982,7 @@ nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id);
long timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
nfs42_ssc_close(filp);
nfsd42_ssc_close(filp);
fput(filp);
spin_lock(&nn->nfsd_ssc_lock);
@@ -1914,12 +2014,6 @@ nfsd4_cleanup_inter_ssc(struct nfsd4_ssc_umount_item *nsui, struct file *filp,
{
}
static struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
struct nfs_fh *src_fh,
nfs4_stateid *stateid)
{
return NULL;
}
#endif /* CONFIG_NFSD_V4_2_INTER_SSC */
static __be32
@@ -1974,7 +2068,7 @@ static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync)
{
copy->cp_res.wr_stable_how =
test_bit(NFSD4_COPY_F_COMMITTED, &copy->cp_flags) ?
NFS_FILE_SYNC : NFS_UNSTABLE;
FILE_SYNC4 : UNSTABLE4;
nfsd4_copy_set_sync(copy, sync);
}
@@ -2142,8 +2236,8 @@ static int nfsd4_do_async_copy(void *data)
if (nfsd4_ssc_is_inter(copy)) {
struct file *filp;
filp = nfs42_ssc_open(copy->ss_nsui->nsui_vfsmount,
&copy->c_fh, &copy->stateid);
filp = nfsd42_ssc_open(copy->ss_nsui->nsui_vfsmount,
&copy->c_fh, &copy->stateid);
if (IS_ERR(filp)) {
switch (PTR_ERR(filp)) {
case -EBADF:
@@ -3328,7 +3422,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
if (current_fh->fh_export &&
need_wrongsec_check(rqstp))
op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
op->status = check_nfsd_access(current_fh->fh_export, rqstp);
}
encode_op:
if (op->status == nfserr_replay_me) {
@@ -3842,7 +3936,6 @@ static const struct nfsd4_operation nfsd4_ops[] = {
},
[OP_OPEN] = {
.op_func = nfsd4_open,
.op_release = nfsd4_open_release,
.op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
.op_name = "OP_OPEN",
.op_rsize_bop = nfsd4_open_rsize,
+74 -18
View File
@@ -48,6 +48,7 @@
#include <linux/nfsd/cld.h>
#include "nfsd.h"
#include "nfs4ctl.h"
#include "state.h"
#include "vfs.h"
#include "netns.h"
@@ -648,6 +649,9 @@ struct cld_upcall {
struct list_head cu_list;
struct cld_net *cu_net;
struct completion cu_done;
/* daemon has read the whole upcall; protected by cn_lock */
bool cu_inflight;
struct rpc_pipe_msg cu_pipe_msg;
union {
struct cld_msg_hdr cu_hdr;
struct cld_msg cu_msg;
@@ -659,22 +663,22 @@ static int
__cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
{
int ret;
struct rpc_pipe_msg msg;
struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
struct rpc_pipe_msg *msg = &cup->cu_pipe_msg;
memset(&msg, 0, sizeof(msg));
msg.data = cmsg;
msg.len = nn->client_tracking_ops->msglen;
memset(msg, 0, sizeof(*msg));
msg->data = cmsg;
msg->len = nn->client_tracking_ops->msglen;
ret = rpc_queue_upcall(pipe, &msg);
ret = rpc_queue_upcall(pipe, msg);
if (ret < 0) {
goto out;
}
wait_for_completion(&cup->cu_done);
if (msg.errno < 0)
ret = msg.errno;
if (msg->errno < 0)
ret = msg->errno;
out:
return ret;
}
@@ -727,6 +731,11 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
name.len = namelen;
if (get_user(princhashlen, &ci->cc_princhash.cp_len))
return -EFAULT;
if (princhashlen > SHA256_DIGEST_SIZE) {
dprintk("%s: invalid princhashlen (%u)",
__func__, princhashlen);
return -EINVAL;
}
if (princhashlen > 0) {
princhashcopy = memdup_user(
&ci->cc_princhash.cp_data,
@@ -808,6 +817,13 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
spin_lock(&cn->cn_lock);
list_for_each_entry(tmp, &cn->cn_list, cu_list) {
if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
/*
* Completing now would return the waiter
* while its message is still queued on the
* pipe.
*/
if (!tmp->cu_inflight)
break;
cup = tmp;
if (status != -EINPROGRESS)
list_del_init(&cup->cu_list);
@@ -816,17 +832,20 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
}
spin_unlock(&cn->cn_lock);
/* couldn't find upcall? */
/* no upcall to complete? */
if (!cup) {
dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid);
dprintk("%s: no completable upcall -- xid=%u\n", __func__, xid);
return -EINVAL;
}
if (status == -EINPROGRESS)
return __cld_pipe_inprogress_downcall(cmsg, nn);
if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0) {
cup->cu_pipe_msg.errno = -EFAULT;
complete(&cup->cu_done);
return -EFAULT;
}
complete(&cup->cu_done);
return mlen;
@@ -835,21 +854,52 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
static void
cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
struct cld_msg *cmsg = msg->data;
struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
cu_u.cu_msg);
struct cld_upcall *cup = container_of(msg, struct cld_upcall,
cu_pipe_msg);
struct cld_net *cn = cup->cu_net;
/*
* errno >= 0 means the daemon read the whole message and a
* downcall will complete the upcall.
*/
spin_lock(&cn->cn_lock);
cup->cu_inflight = msg->errno >= 0;
spin_unlock(&cn->cn_lock);
/* errno >= 0 means we got a downcall */
if (msg->errno >= 0)
return;
complete(&cup->cu_done);
}
/*
* An upcall the daemon has consumed is off every pipe list, so the
* purge in rpc_pipe_release() and rpc_close_pipes() cannot reach it.
* Release its waiter here instead; no downcall can arrive now.
*/
static void
cld_release_pipe(struct inode *inode)
{
struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
struct cld_net *cn = nn->cld_net;
struct cld_upcall *cup;
spin_lock(&cn->cn_lock);
list_for_each_entry(cup, &cn->cn_list, cu_list) {
if (!cup->cu_inflight)
continue;
cup->cu_inflight = false;
cup->cu_pipe_msg.errno = -EPIPE;
complete(&cup->cu_done);
}
spin_unlock(&cn->cn_lock);
}
static const struct rpc_pipe_ops cld_upcall_ops = {
.upcall = rpc_pipe_generic_upcall,
.downcall = cld_pipe_downcall,
.destroy_msg = cld_pipe_destroy_msg,
.release_pipe = cld_release_pipe,
};
static int
@@ -916,18 +966,24 @@ __nfsd4_init_cld_pipe(struct net *net)
}
spin_lock_init(&cn->cn_lock);
INIT_LIST_HEAD(&cn->cn_list);
#ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING
cn->cn_has_legacy = false;
#endif
/*
* The pipe's methods reach @cn through nn->cld_net, so set
* it before the pipe can be opened.
*/
nn->cld_net = cn;
ret = nfsd4_cld_register_net(net, cn->cn_pipe);
if (unlikely(ret))
goto err_destroy_data;
#ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING
cn->cn_has_legacy = false;
#endif
nn->cld_net = cn;
return 0;
err_destroy_data:
nn->cld_net = NULL;
rpc_destroy_pipe_data(cn->cn_pipe);
err:
kfree(cn);
+500 -87
View File
@@ -45,13 +45,15 @@
#include <linux/string_helpers.h>
#include <linux/fsnotify.h>
#include <linux/rhashtable.h>
#include <linux/nfs_ssc.h>
#include <linux/nfsd_ssc.h>
#include "xdr4.h"
#include "nfs4ctl.h"
#include "xdr4cb.h"
#include "vfs.h"
#include "current_stateid.h"
#include "stats.h"
#include "nfserr.h"
#include "netns.h"
#include "pnfs.h"
@@ -91,7 +93,9 @@ static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
static void nfsd4_end_grace(struct nfsd_net *nn);
static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
static void nfsd4_file_hash_remove(struct nfs4_file *fi);
static void deleg_reaper(struct nfsd_net *nn);
static void deleg_reaper(struct nfsd_net *nn, unsigned long backlog);
static void nfsd4_drop_revoked_stid(struct nfs4_stid *s)
__releases(&s->sc_client->cl_lock);
static const struct lease_manager_operations nfsd_lease_mng_ops;
@@ -1157,14 +1161,18 @@ static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp)
*/
static void nfs4_free_deleg(struct nfs4_stid *stid)
{
struct nfsd_net *nn = net_generic(stid->sc_client->net, nfsd_net_id);
struct nfs4_delegation *dp = delegstateid(stid);
WARN_ON_ONCE(!list_empty(&stid->sc_cp_list));
WARN_ON_ONCE(!list_empty(&dp->dl_perfile));
WARN_ON_ONCE(!list_empty(&dp->dl_perclnt));
WARN_ON_ONCE(!list_empty(&dp->dl_recall_lru));
/* The list outlives one recall, so ->release() cannot free it. */
nfsd41_cb_destroy_referring_call_list(&dp->dl_recall);
kmem_cache_free(deleg_slab, stid);
atomic_long_dec(&num_delegations);
atomic_long_dec(&nn->nfsd_delegations);
}
/*
@@ -1249,6 +1257,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
struct nfs4_clnt_odstate *odstate, u32 dl_type,
void (*sc_free)(struct nfs4_stid *))
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfs4_delegation *dp;
struct nfs4_stid *stid;
long n;
@@ -1257,6 +1266,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
return NULL;
n = atomic_long_inc_return(&num_delegations);
atomic_long_inc(&nn->nfsd_delegations);
if (n < 0 || n > max_delegations)
goto out_dec;
@@ -1279,6 +1289,9 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
dp->dl_type = dl_type;
dp->dl_retries = 1;
dp->dl_recalled = false;
dp->dl_recall_rejected = false;
dp->dl_recall_grant.valid = false;
dp->dl_recall_grant.retired_at_send = false;
get_nfs4_file(fp);
dp->dl_stid.sc_file = fp;
nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
@@ -1286,6 +1299,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
return dp;
out_dec:
atomic_long_dec(&num_delegations);
atomic_long_dec(&nn->nfsd_delegations);
return NULL;
}
@@ -1314,6 +1328,7 @@ static void nfs4_free_dir_deleg(struct nfs4_stid *stid)
for (i = 0; i < ncn->ncn_evt_cnt; ++i)
nfsd_notify_event_put(ncn->ncn_evt[i]);
kfree(ncn->ncn_nf);
kfree(ncn->ncn_masks);
for (i = 0; i < NOTIFY4_PAGE_ARRAY_SIZE; i++) {
if (!ncn->ncn_pages[i])
break;
@@ -1346,6 +1361,11 @@ alloc_init_dir_deleg(struct nfs4_client *clp, struct nfs4_file *fp)
nfs4_put_stid(&dp->dl_stid);
return NULL;
}
ncn->ncn_masks = kcalloc(NOTIFY4_EVENT_QUEUE_SIZE, sizeof(*ncn->ncn_masks), GFP_KERNEL);
if (!ncn->ncn_masks) {
nfs4_put_stid(&dp->dl_stid);
return NULL;
}
spin_lock_init(&ncn->ncn_lock);
nfsd4_init_cb(&ncn->ncn_cb, dp->dl_stid.sc_client,
&nfsd4_cb_notify_ops, NFSPROC4_CLNT_CB_NOTIFY);
@@ -1512,6 +1532,7 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
dp->dl_stid.sc_type = SC_TYPE_DELEG;
list_add(&dp->dl_perfile, &fp->fi_delegations);
list_add(&dp->dl_perclnt, &clp->cl_delegations);
clp->cl_deleg_count++;
return 0;
}
@@ -1543,6 +1564,7 @@ unhash_delegation_locked(struct nfs4_delegation *dp, unsigned short statusmask)
++dp->dl_time;
spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
dp->dl_stid.sc_client->cl_deleg_count--;
list_del_init(&dp->dl_recall_lru);
list_del_init(&dp->dl_perfile);
spin_unlock(&fp->fi_lock);
@@ -1563,27 +1585,22 @@ static void destroy_delegation(struct nfs4_delegation *dp)
}
/**
* revoke_delegation - perform nfs4 delegation structure cleanup
* @dp: pointer to the delegation
* revoke_delegation - dispose of a delegation the server has revoked
* @dp: delegation to dispose of
*
* This function assumes that it's called either from the administrative
* interface (nfsd4_revoke_states()) that's revoking a specific delegation
* stateid or it's called from a laundromat thread (nfsd4_landromat()) that
* determined that this specific state has expired and needs to be revoked
* (both mark state with the appropriate stid sc_status mode). It is also
* assumed that a reference was taken on the @dp state. This function
* consumes that reference.
* The caller holds a reference on @dp, which this function consumes.
* On NFSv4.1 and newer, @dp's sc_status must already carry
* SC_STATUS_REVOKED or SC_STATUS_ADMIN_REVOKED.
*
* If this function finds that the @dp state is SC_STATUS_FREED it means
* that a FREE_STATEID operation for this stateid has been processed and
* we can proceed to removing it from recalled list. However, if @dp state
* isn't marked SC_STATUS_FREED, it means we need place it on the cl_revoked
* list and wait for the FREE_STATEID to arrive from the client. At the same
* time, we need to mark it as SC_STATUS_FREEABLE to indicate to the
* nfsd4_free_stateid() function that this stateid has already been added
* to the cl_revoked list and that nfsd4_free_stateid() is now responsible
* for removing it from the list. Inspection of where the delegation state
* in the revocation process is protected by the clp->cl_lock.
* @dp is parked on the client's cl_revoked list to await a FREE_STATEID.
* Where none can arrive, @dp is destroyed here instead: FREE_STATEID has
* already freed it, or the client rejected the recall with
* NFS4ERR_BADHANDLE or NFS4ERR_BAD_STATEID and holds no record of the
* delegation. NFS4ERR_ADMIN_REVOKED still prompts one, so an
* administrative revoke waits on cl_revoked.
*
* Context: Takes and releases the client's cl_lock; may sleep after
* dropping it.
*/
static void revoke_delegation(struct nfs4_delegation *dp)
{
@@ -1601,6 +1618,19 @@ static void revoke_delegation(struct nfs4_delegation *dp)
list_del_init(&dp->dl_recall_lru);
goto out;
}
if (dp->dl_recall_rejected &&
!(dp->dl_stid.sc_status & SC_STATUS_ADMIN_REVOKED)) {
/*
* SC_STATUS_CLOSED, set under cl_lock, makes a racing
* FREE_STATEID bail out rather than drop this reference
* too. The put releases what cl_revoked would have held.
*/
dp->dl_stid.sc_status |= SC_STATUS_CLOSED;
spin_unlock(&clp->cl_lock);
nfs4_put_stid(&dp->dl_stid);
destroy_unhashed_deleg(dp);
return;
}
list_add(&dp->dl_recall_lru, &clp->cl_revoked);
dp->dl_stid.sc_status |= SC_STATUS_FREEABLE;
out:
@@ -2789,10 +2819,16 @@ void nfsd4_put_client(struct nfs4_client *clp)
static void
free_client(struct nfs4_client *clp)
{
while (!list_empty(&clp->cl_sessions)) {
LIST_HEAD(reaplist);
/* client_info_show() walks cl_sessions under cl_lock */
spin_lock(&clp->cl_lock);
list_splice_init(&clp->cl_sessions, &reaplist);
spin_unlock(&clp->cl_lock);
while (!list_empty(&reaplist)) {
struct nfsd4_session *ses;
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
se_perclnt);
ses = list_entry(reaplist.next, struct nfsd4_session,
se_perclnt);
list_del(&ses->se_perclnt);
WARN_ON_ONCE(atomic_read(&ses->se_ref));
free_session(ses);
@@ -2889,11 +2925,18 @@ __destroy_client(struct nfs4_client *clp)
list_del_init(&dp->dl_recall_lru);
destroy_unhashed_deleg(dp);
}
/*
* A CB_RECALL reply can release revoked delegations concurrently:
* nfsd4_shutdown_callback() has not run yet.
*/
spin_lock(&clp->cl_lock);
while (!list_empty(&clp->cl_revoked)) {
dp = list_entry(clp->cl_revoked.next, struct nfs4_delegation, dl_recall_lru);
list_del_init(&dp->dl_recall_lru);
nfs4_put_stid(&dp->dl_stid);
/* this function drops ->cl_lock */
nfsd4_drop_revoked_stid(&dp->dl_stid);
spin_lock(&clp->cl_lock);
}
spin_unlock(&clp->cl_lock);
while (!list_empty(&clp->cl_openowners)) {
oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
nfs4_get_stateowner(&oo->oo_owner);
@@ -3737,14 +3780,9 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
struct nfsd_notify_event *nne = events[i];
if (!error) {
u32 *maskp = (u32 *)xdr_reserve_space(&stream, sizeof(*maskp));
u32 *maskp = &ncn->ncn_masks[i];
u8 *p;
if (!maskp) {
error = true;
goto put_event;
}
p = nfsd4_encode_notify_event(&stream, nne, dp, nf, maskp);
if (!p) {
pr_notice("Could not generate CB_NOTIFY from fsnotify mask 0x%x\n",
@@ -3762,13 +3800,10 @@ put_event:
nfsd_notify_event_put(nne);
}
if (!error && (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS))) {
u32 *maskp = (u32 *)xdr_reserve_space(&stream, sizeof(*maskp));
u32 *maskp = &ncn->ncn_masks[count];
u8 *p;
if (maskp)
p = nfsd4_encode_dir_attr_change(&stream, dp, nf);
else
p = ERR_PTR(-ENOBUFS);
p = nfsd4_encode_dir_attr_change(&stream, dp, nf);
if (IS_ERR(p)) {
/*
@@ -4216,12 +4251,18 @@ nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid)
static bool client_has_openowners(struct nfs4_client *clp)
{
struct nfs4_openowner *oo;
bool found = false;
spin_lock(&clp->cl_lock);
list_for_each_entry(oo, &clp->cl_openowners, oo_perclient) {
if (!list_empty(&oo->oo_owner.so_stateids))
return true;
if (!list_empty(&oo->oo_owner.so_stateids)) {
found = true;
break;
}
}
return false;
spin_unlock(&clp->cl_lock);
return found;
}
static bool client_has_state(struct nfs4_client *clp)
@@ -5016,6 +5057,7 @@ __be32
nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
struct nfsd4_compoundargs *args = rqstp->rq_argp;
struct nfsd4_sequence *seq = &u->sequence;
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct xdr_stream *xdr = resp->xdr;
@@ -5025,6 +5067,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_conn *conn;
__be32 status;
int buflen;
u32 maxlen, respsize;
struct net *net = SVC_NET(rqstp);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@@ -5102,7 +5145,22 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
session->se_fchannel.maxresp_sz;
status = (seq->cachethis) ? nfserr_rep_too_big_to_cache :
nfserr_rep_too_big;
if (xdr_restrict_buflen(xdr, buflen - rqstp->rq_auth_slack))
if (buflen < rqstp->rq_auth_slack)
goto out_put_session;
maxlen = buflen - rqstp->rq_auth_slack;
/*
* A SEQUENCE result too large for maxlen never reaches
* nfsd4_encode_sequence(), so cstate.data_offset stays zero and
* the reply cache overruns the slot.
*/
respsize = nfsd4_max_reply(rqstp, &args->ops[0]);
if (!nfsd4_last_compound_op(rqstp))
respsize += COMPOUND_ERR_SLACK_SPACE;
if (xdr->buf->len + respsize > maxlen)
goto out_put_session;
if (xdr_restrict_buflen(xdr, maxlen))
goto out_put_session;
svc_reserve_auth(rqstp, buflen);
@@ -5118,6 +5176,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
slot->sl_flags &= ~NFSD4_SLOT_CACHETHIS;
cstate->slot = slot;
cstate->slot_owned = true;
cstate->session = session;
cstate->clp = clp;
@@ -5192,7 +5251,11 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
struct nfsd4_compound_state *cs = &resp->cstate;
if (nfsd4_has_session(cs)) {
if (cs->status != nfserr_replay_cache) {
/*
* Only the request that claimed the slot may update its
* cached reply and clear NFSD4_SLOT_INUSE.
*/
if (cs->slot_owned) {
nfsd4_store_cache_entry(resp);
cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
}
@@ -5526,26 +5589,110 @@ out:
return -ENOMEM;
}
#define NFSD_RECALL_ANY_COOLDOWN_SECS 5
static unsigned long
nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
nfsd4_courtesy_shrinker_count(struct shrinker *shrink,
struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
long count;
long backlog, count;
count = atomic_read(&nn->nfsd_courtesy_clients);
if (!count)
count = atomic_long_read(&num_delegations);
if (count)
queue_work(laundry_wq, &nn->nfsd_shrinker_work);
return (unsigned long)count;
return 0;
queue_work(laundry_wq, &nn->nfsd_courtesy_work);
/* Work already queued is not available to reclaim again. */
backlog = atomic_long_read(&nn->nfsd_shrink_backlog);
return count > backlog ? count - backlog : 0;
}
static unsigned long
nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
nfsd4_deleg_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
time64_t elapsed;
long backlog, count;
count = atomic_long_read(&nn->nfsd_delegations);
if (!count)
return 0;
/*
* Delegations the last sweep reached stay unreclaimable until
* deleg_reaper()'s cooldown expires. CB_RECALL_ANY leaves the
* choice of delegations to the client, so there is no return
* to wait on instead.
*/
elapsed = ktime_get_boottime_seconds() -
READ_ONCE(nn->nfsd_last_recall_any);
if (elapsed < NFSD_RECALL_ANY_COOLDOWN_SECS)
return 0;
/*
* Unlike the courtesy shrinker, this one queues no work.
* Nothing is recalled until a scan request arrives. Subtract
* the requests already recorded, or concurrent reclaimers
* each see the whole namespace and stack a scan on top of it.
*/
backlog = atomic_long_read(&nn->nfsd_deleg_backlog);
return count > backlog ? count - backlog : 0;
}
static unsigned long
nfsd4_courtesy_shrinker_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
atomic_long_add(sc->nr_to_scan, &nn->nfsd_shrink_backlog);
queue_work(laundry_wq, &nn->nfsd_courtesy_work);
/*
* The reaper runs from laundry_wq. Report no progress rather
* than claim memory that is not free yet.
*/
return SHRINK_STOP;
}
static unsigned long
nfsd4_deleg_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
atomic_long_add(sc->nr_to_scan, &nn->nfsd_deleg_backlog);
queue_work(laundry_wq, &nn->nfsd_deleg_work);
/*
* The reaper sends CB_RECALL_ANY, so nothing is free when
* this returns.
*/
return SHRINK_STOP;
}
static struct shrinker *
nfsd4_alloc_state_shrinker(struct nfsd_net *nn, const char *name,
unsigned long (*count)(struct shrinker *,
struct shrink_control *),
unsigned long (*scan)(struct shrinker *,
struct shrink_control *))
{
struct shrinker *shrink;
shrink = shrinker_alloc(0, "%s:%s", name, nn->nfsd_name);
if (!shrink)
return NULL;
shrink->count_objects = count;
shrink->scan_objects = scan;
shrink->private_data = nn;
shrinker_register(shrink);
return shrink;
}
void
nfsd4_init_leases_net(struct nfsd_net *nn)
{
@@ -6063,6 +6210,80 @@ bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode)
return timeo > 0;
}
/*
* gen_sessionid() composes a sessionid from the client's clientid and a
* sequence counter, so the sequence alone identifies the granting session.
*/
static void nfsd4_recall_grant_sessionid(const struct nfs4_delegation *dp,
struct nfsd4_sessionid *sid)
{
sid->clientid = dp->dl_stid.sc_client->cl_clientid;
sid->sequence = dp->dl_recall_grant.sessionid_seq;
sid->reserved = 0;
}
static bool nfsd4_recall_grant_slot_retired(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_stid.sc_client;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfsd4_session *ses;
struct nfsd4_sessionid sid;
bool retired = false;
void *entry;
if (!dp->dl_recall_grant.valid)
return false;
nfsd4_recall_grant_sessionid(dp, &sid);
/*
* A missing session does not prove the client saw the grant: a
* DESTROY_SESSION unhashes its own session before the reply to
* that compound is encoded.
*/
spin_lock(&nn->client_lock);
ses = __find_in_sessionid_hashtbl((struct nfs4_sessionid *)&sid,
clp->net);
entry = ses ? xa_load(&ses->se_slots, dp->dl_recall_grant.slotid) : NULL;
if (xa_is_value(entry)) {
/*
* A slot is freed only once the client has acknowledged
* the smaller slot table, which it cannot do while a
* request on that slot is outstanding.
*/
retired = true;
} else if (entry) {
struct nfsd4_slot *slot = entry;
/*
* A reactivated slot was freed and rebuilt, so the same
* acknowledgment applies. The seqid test errs toward
* revoking: a rebuilt slot restarting at seqid 1 matches
* an old grant.
*/
retired = (slot->sl_flags & NFSD4_SLOT_REUSED) ||
((slot->sl_flags & NFSD4_SLOT_INITIALIZED) &&
slot->sl_seqid != dp->dl_recall_grant.seqid);
}
spin_unlock(&nn->client_lock);
return retired;
}
/*
* ->prepare does not run on every send: nfsd4_run_cb_work() skips it
* on a requeue, and a retry via rpc_restart_call_prepare() re-enters
* the RPC layer beneath it. The granting request does not change, so
* a send inherits a correct list. Retirement is the one transition
* the list has to follow.
*/
static void nfsd4_refresh_recall_grant(struct nfs4_delegation *dp)
{
dp->dl_recall_grant.retired_at_send =
nfsd4_recall_grant_slot_retired(dp);
if (dp->dl_recall_grant.retired_at_send)
nfsd41_cb_destroy_referring_call_list(&dp->dl_recall);
}
static bool nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
{
struct nfs4_delegation *dp = cb_to_delegation(cb);
@@ -6084,9 +6305,46 @@ static bool nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
}
spin_unlock(&nn->deleg_lock);
nfsd4_refresh_recall_grant(dp);
if (dp->dl_recall_grant.valid && !dp->dl_recall_grant.retired_at_send) {
struct nfsd4_sessionid sid;
nfsd4_recall_grant_sessionid(dp, &sid);
nfsd41_cb_referring_call(&dp->dl_recall,
(struct nfs4_sessionid *)&sid,
dp->dl_recall_grant.slotid,
dp->dl_recall_grant.seqid);
}
return true;
}
/*
* cl_lock orders this against a laundromat reaping @dp: either
* revoke_delegation() observes dl_recall_rejected and destroys @dp, or
* it reached cl_revoked first and @dp is released here instead.
*/
static void nfsd4_deleg_recall_rejected(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_stid.sc_client;
spin_lock(&clp->cl_lock);
if (dp->dl_stid.sc_status & (SC_STATUS_CLOSED | SC_STATUS_FREED |
SC_STATUS_ADMIN_REVOKED)) {
spin_unlock(&clp->cl_lock);
return;
}
if (dp->dl_stid.sc_status & SC_STATUS_FREEABLE) {
dp->dl_stid.sc_status |= SC_STATUS_CLOSED;
/* this function drops ->cl_lock */
nfsd4_drop_revoked_stid(&dp->dl_stid);
return;
}
dp->dl_recall_rejected = true;
spin_unlock(&clp->cl_lock);
}
static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
struct rpc_task *task)
{
@@ -6094,27 +6352,32 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
trace_nfsd_cb_recall_done(&dp->dl_stid.sc_stateid, task);
if (dp->dl_stid.sc_status)
/* CLOSED or REVOKED */
return 1;
switch (task->tk_status) {
case 0:
return 1;
case -NFS4ERR_DELAY:
if (dp->dl_stid.sc_status)
/* CLOSED or REVOKED */
return 1;
rpc_delay(task, 2 * HZ);
return 0;
case -EBADHANDLE:
case -NFS4ERR_BAD_STATEID:
/*
* Race: client probably got cb_recall before open reply
* granting delegation.
* Retirement of the granting slot proves the client saw
* the grant. Trust the rejection only if the slot had
* retired when this recall was sent.
*/
if (dp->dl_retries--) {
if (dp->dl_recall_grant.retired_at_send) {
nfsd4_deleg_recall_rejected(dp);
return 1;
}
if (!dp->dl_stid.sc_status && dp->dl_retries--) {
nfsd4_refresh_recall_grant(dp);
rpc_delay(task, 2 * HZ);
return 0;
}
fallthrough;
return 1;
default:
return 1;
}
@@ -6708,9 +6971,25 @@ static bool nfsd4_want_deleg_timestamps(const struct nfsd4_open *open)
return open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
}
static void
nfs4_delegation_record_grant_slot(struct nfs4_delegation *dp,
const struct nfsd4_compound_state *cstate)
{
const struct nfsd4_sessionid *sid;
if (!cstate->session)
return;
sid = (struct nfsd4_sessionid *)cstate->session->se_sessionid.data;
dp->dl_recall_grant.sessionid_seq = sid->sequence;
dp->dl_recall_grant.slotid = cstate->slot->sl_index;
dp->dl_recall_grant.seqid = cstate->slot->sl_seqid;
dp->dl_recall_grant.valid = true;
}
static struct nfs4_delegation *
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *parent)
nfs4_set_delegation(struct nfsd4_open *open,
const struct nfsd4_compound_state *cstate,
struct nfs4_ol_stateid *stp, struct svc_fh *parent)
{
bool deleg_ts = nfsd4_want_deleg_timestamps(open);
struct nfs4_client *clp = stp->st_stid.sc_client;
@@ -6800,6 +7079,14 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
dp = alloc_init_deleg(clp, fp, odstate, dl_type);
if (!dp)
goto out_delegees;
/*
* Record the granting slot before kernel_setlease() makes @dp
* visible to lease breakers. A conflicting open can drive
* CB_RECALL to completion from that point on.
*/
nfs4_delegation_record_grant_slot(dp, cstate);
if (stp->st_stid.sc_export)
dp->dl_stid.sc_export = exp_get(stp->st_stid.sc_export);
@@ -6964,6 +7251,7 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
struct nfs4_ol_stateid *stp, struct svc_fh *currentfh,
struct svc_fh *fh)
{
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfs4_openowner *oo = openowner(stp->st_stateowner);
bool deleg_ts = nfsd4_want_deleg_timestamps(open);
struct nfs4_client *clp = stp->st_stid.sc_client;
@@ -7000,7 +7288,7 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
default:
goto out_no_deleg;
}
dp = nfs4_set_delegation(open, stp, parent);
dp = nfs4_set_delegation(open, &resp->cstate, stp, parent);
if (IS_ERR(dp))
goto out_no_deleg;
@@ -7624,6 +7912,7 @@ nfs4_laundromat(struct nfsd_net *nn)
struct nfs4_cpntf_state *cps;
struct nfs4_client *clp;
copy_stateid_t *cps_t;
long held, host, n;
int i;
if (clients_still_reclaiming(nn)) {
@@ -7737,8 +8026,22 @@ nfs4_laundromat(struct nfsd_net *nn)
/* service the server-to-server copy delayed unmount list */
nfsd4_ssc_expire_umount(nn);
#endif
if (atomic_long_read(&num_delegations) >= max_delegations)
deleg_reaper(nn);
/*
* set_max_delegations() computes a zero max_delegations on a
* server with very little memory. @host is a divisor below.
*/
host = atomic_long_read(&num_delegations);
if (host && host >= max_delegations) {
/*
* max_delegations bounds the host, but the laundromat
* runs once per network namespace. Requesting the whole
* overage in each would multiply the request, so take
* only this namespace's share.
*/
held = atomic_long_read(&nn->nfsd_delegations);
n = host - max_delegations + 1;
deleg_reaper(nn, DIV64_U64_ROUND_UP((u64)n * held, host));
}
out:
return max_t(time64_t, lt.new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
}
@@ -7766,49 +8069,146 @@ courtesy_client_reaper(struct nfsd_net *nn)
nfs4_process_client_reaplist(&reaplist);
}
/* The two passes in deleg_reaper() must agree on which clients are asked. */
static bool
deleg_reaper_eligible(const struct nfs4_client *clp, time64_t now)
{
if (clp->cl_minorversion == 0)
return false;
if (clp->cl_state != NFSD4_ACTIVE)
return false;
if (atomic_read(&clp->cl_delegs_in_recall))
return false;
if (test_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags))
return false;
if (now - clp->cl_ra_time < NFSD_RECALL_ANY_COOLDOWN_SECS)
return false;
if (clp->cl_cb_state != NFSD4_CB_UP)
return false;
return true;
}
static void
deleg_reaper(struct nfsd_net *nn)
deleg_reaper(struct nfsd_net *nn, unsigned long backlog)
{
struct list_head *pos, *next;
struct nfs4_client *clp;
unsigned long remaining, share, total;
unsigned int count;
time64_t now;
/*
* Recalling a delegation before it is needed costs the client
* an OPEN when it next touches the file. Leave
* nfsd_last_recall_any unstamped so the next sweep is not
* delayed.
*/
if (!backlog)
return;
now = ktime_get_boottime_seconds();
spin_lock(&nn->client_lock);
/*
* Only the clients this sweep asks contribute to the
* apportionment. Dividing the request among holders that are
* skipped under-serves it, and the shortfall goes nowhere:
* nfsd4_deleg_shrinker_worker() has already cleared
* nfsd_deleg_backlog.
*/
total = 0;
list_for_each(pos, &nn->client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
if (!deleg_reaper_eligible(clp, now))
continue;
/*
* This read races with hash_delegation_locked() and
* unhash_delegation_locked() on other CPUs. A stale
* count only skews the keep value; the next
* laundromat pass sees a more current one.
*/
total += data_race(READ_ONCE(clp->cl_deleg_count));
}
if (!total)
goto out;
/*
* Reclaim asks in batches and is not bound by what the count
* callback reported, so the backlog can exceed what these
* clients hold. Cap it to keep each share within the client's
* own count.
*/
backlog = min(backlog, total);
remaining = backlog;
list_for_each_safe(pos, next, &nn->client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
if (clp->cl_state != NFSD4_ACTIVE)
if (!deleg_reaper_eligible(clp, now))
continue;
if (list_empty(&clp->cl_delegations))
continue;
if (atomic_read(&clp->cl_delegs_in_recall))
continue;
if (ktime_get_boottime_seconds() - clp->cl_ra_time < 5)
continue;
if (clp->cl_cb_state != NFSD4_CB_UP)
count = data_race(READ_ONCE(clp->cl_deleg_count));
if (!count)
continue;
if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags))
continue;
/* release in nfsd4_cb_recall_any_release */
kref_get(&clp->cl_nfsdfs.cl_ref);
clp->cl_ra_time = ktime_get_boottime_seconds();
clp->cl_ra->ra_keep = 0;
clp->cl_ra_time = now;
/*
* Rounding up guarantees every holder gives up at least
* one. The round-up can overshoot @backlog, so stop
* once the request is met. client_lru is ordered by
* last renewal, so the least active clients are asked
* first.
*/
share = DIV64_U64_ROUND_UP((u64)backlog * count, total);
share = min(share, remaining);
remaining -= share;
clp->cl_ra->ra_keep = count - share;
clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG) |
BIT(RCA4_TYPE_MASK_WDATA_DLG);
BIT(RCA4_TYPE_MASK_WDATA_DLG) |
BIT(RCA4_TYPE_MASK_DIR_DLG);
trace_nfsd_cb_recall_any(clp->cl_ra);
nfsd4_run_cb(&clp->cl_ra->ra_cb);
if (!remaining)
break;
}
out:
spin_unlock(&nn->client_lock);
/*
* Stamp the sweep even when no recall went out. A sweep that
* found nothing eligible finds nothing on an immediate retry.
*/
WRITE_ONCE(nn->nfsd_last_recall_any, now);
}
static void
nfsd4_state_shrinker_worker(struct work_struct *work)
nfsd4_courtesy_shrinker_worker(struct work_struct *work)
{
struct nfsd_net *nn = container_of(work, struct nfsd_net,
nfsd_shrinker_work);
nfsd_courtesy_work);
long backlog;
/*
* Retire only the requests sampled here, so that requests
* arriving while the reaper runs are still discounted by
* nfsd4_courtesy_shrinker_count().
*/
backlog = atomic_long_read(&nn->nfsd_shrink_backlog);
courtesy_client_reaper(nn);
deleg_reaper(nn);
atomic_long_sub(backlog, &nn->nfsd_shrink_backlog);
}
static void
nfsd4_deleg_shrinker_worker(struct work_struct *work)
{
struct nfsd_net *nn = container_of(work, struct nfsd_net,
nfsd_deleg_work);
deleg_reaper(nn, atomic_long_xchg(&nn->nfsd_deleg_backlog, 0));
}
static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)
@@ -9770,21 +10170,31 @@ static int nfs4_state_create_net(struct net *net)
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
/* Make sure this cannot run until client tracking is initialised */
disable_delayed_work(&nn->laundromat_work);
INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
INIT_WORK(&nn->nfsd_courtesy_work, nfsd4_courtesy_shrinker_worker);
INIT_WORK(&nn->nfsd_deleg_work, nfsd4_deleg_shrinker_worker);
atomic_long_set(&nn->nfsd_shrink_backlog, 0);
atomic_long_set(&nn->nfsd_deleg_backlog, 0);
nn->nfsd_last_recall_any = 0;
get_net(net);
nn->nfsd_client_shrinker = shrinker_alloc(0, "nfsd-client");
if (!nn->nfsd_client_shrinker)
nn->nfsd_courtesy_shrinker =
nfsd4_alloc_state_shrinker(nn, "nfsd-courtesy",
nfsd4_courtesy_shrinker_count,
nfsd4_courtesy_shrinker_scan);
if (!nn->nfsd_courtesy_shrinker)
goto err_shrinker;
nn->nfsd_client_shrinker->scan_objects = nfsd4_state_shrinker_scan;
nn->nfsd_client_shrinker->count_objects = nfsd4_state_shrinker_count;
nn->nfsd_client_shrinker->private_data = nn;
shrinker_register(nn->nfsd_client_shrinker);
nn->nfsd_deleg_shrinker =
nfsd4_alloc_state_shrinker(nn, "nfsd-delegation",
nfsd4_deleg_shrinker_count,
nfsd4_deleg_shrinker_scan);
if (!nn->nfsd_deleg_shrinker)
goto err_deleg_shrinker;
return 0;
err_deleg_shrinker:
shrinker_free(nn->nfsd_courtesy_shrinker);
err_shrinker:
put_net(net);
kfree(nn->sessionid_hashtbl);
@@ -9885,8 +10295,10 @@ nfs4_state_shutdown_net(struct net *net)
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
shrinker_free(nn->nfsd_client_shrinker);
cancel_work_sync(&nn->nfsd_shrinker_work);
shrinker_free(nn->nfsd_courtesy_shrinker);
shrinker_free(nn->nfsd_deleg_shrinker);
cancel_work_sync(&nn->nfsd_courtesy_work);
cancel_work_sync(&nn->nfsd_deleg_work);
disable_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
@@ -10294,6 +10706,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
dp = alloc_init_dir_deleg(clp, fp);
if (!dp)
goto out_delegees;
nfs4_delegation_record_grant_slot(dp, cstate);
if (cstate->current_fh.fh_export)
dp->dl_stid.sc_export =
exp_get(cstate->current_fh.fh_export);
+157 -70
View File
@@ -54,6 +54,7 @@
#include "xdr4.h"
#include "vfs.h"
#include "state.h"
#include "nfserr.h"
#include "cache.h"
#include "netns.h"
#include "pnfs.h"
@@ -113,27 +114,27 @@ static int zero_clientid(clientid_t *clid)
return (clid->cl_boot == 0) && (clid->cl_id == 0);
}
/**
* svcxdr_tmpalloc - allocate memory to be freed after compound processing
* @argp: NFSv4 compound argument structure
* @len: length of buffer to allocate
*
* Allocates a buffer of size @len to be freed when processing the compound
* operation described in @argp finishes.
*/
static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
svcxdr_tmpalloc_release(struct nfsd4_compoundargs *argp, size_t len,
void (*release)(void *))
{
struct svcxdr_tmpbuf *tb;
tb = kmalloc_flex(*tb, buf, len);
if (!tb)
return NULL;
tb->release = release;
tb->next = argp->to_free;
argp->to_free = tb;
return tb->buf;
}
static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
{
return svcxdr_tmpalloc_release(argp, len, NULL);
}
/*
* For xdr strings that need to be passed to other kernel api's
* as null-terminated strings.
@@ -441,10 +442,16 @@ nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp,
return status;
}
static void svcxdr_release_pacl(void *p)
{
posix_acl_release(*(struct posix_acl **)p);
}
static noinline __be32
nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
{
struct posix_acl_entry *ace;
struct posix_acl **slot;
__be32 status;
u32 count;
@@ -484,6 +491,15 @@ nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
if (count >= 3)
sort_pacl_range(*acl, 0, count - 1);
slot = svcxdr_tmpalloc_release(argp, sizeof(*slot),
svcxdr_release_pacl);
if (!slot) {
posix_acl_release(*acl);
*acl = NULL;
return nfserr_jukebox;
}
*slot = *acl;
return nfs_ok;
}
@@ -676,7 +692,6 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
status = nfsd4_decode_posixacl(argp, &pacl);
if (status) {
posix_acl_release(*dpaclp);
*dpaclp = NULL;
return status;
}
@@ -686,12 +701,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
/* request sanity: did attrlist4 contain the expected number of words? */
if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) {
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
posix_acl_release(*dpaclp);
posix_acl_release(*paclp);
*dpaclp = NULL;
*paclp = NULL;
#endif
return nfserr_bad_xdr;
}
@@ -1606,7 +1617,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
return nfserr_bad_xdr;
if (xdr_stream_decode_u32(argp->xdr, &write->wr_stable_how) < 0)
return nfserr_bad_xdr;
if (write->wr_stable_how > NFS_FILE_SYNC)
if (write->wr_stable_how > FILE_SYNC4)
return nfserr_bad_xdr;
if (xdr_stream_decode_u32(argp->xdr, &write->wr_buflen) < 0)
return nfserr_bad_xdr;
@@ -1919,6 +1930,17 @@ nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs *argp,
}
#ifdef CONFIG_NFSD_PNFS
static __be32
nfsd4_decode_deviceid4(struct xdr_stream *xdr, struct nfsd4_deviceid *devid)
{
__be32 *p = xdr_inline_decode(xdr, NFS4_DEVICEID4_SIZE);
if (unlikely(!p))
return nfserr_bad_xdr;
svcxdr_decode_deviceid4(p, devid);
return nfs_ok;
}
static __be32
nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
union nfsd4_op_u *u)
@@ -2320,10 +2342,8 @@ static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
char **bufp, size_t buflen)
{
struct page **pages = xdr->pages;
struct kvec *head = xdr->head;
char *tmp, *dp;
u32 len;
char *tmp;
if (buflen <= head->iov_len) {
/*
@@ -2338,19 +2358,8 @@ nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
if (tmp == NULL)
return nfserr_jukebox;
dp = tmp;
memcpy(dp, head->iov_base, head->iov_len);
buflen -= head->iov_len;
dp += head->iov_len;
while (buflen > 0) {
len = min_t(u32, buflen, PAGE_SIZE);
memcpy(dp, page_address(*pages), len);
buflen -= len;
dp += len;
pages++;
}
if (read_bytes_from_xdr_buf(xdr, 0, tmp, buflen))
return nfserr_bad_xdr;
*bufp = tmp;
return 0;
@@ -2467,7 +2476,7 @@ nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp,
&setxattr->setxa_buf, size);
}
return nfs_ok;
return status;
}
static __be32
@@ -2733,6 +2742,87 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
return true;
}
static __always_inline __be32
nfsd4_encode_bool(struct xdr_stream *xdr, bool val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(p == NULL))
return nfserr_resource;
*p = val ? xdr_one : xdr_zero;
return nfs_ok;
}
static __always_inline __be32
nfsd4_encode_uint32_t(struct xdr_stream *xdr, u32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(p == NULL))
return nfserr_resource;
*p = cpu_to_be32(val);
return nfs_ok;
}
#define nfsd4_encode_aceflag4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_acemask4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_acetype4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_count4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_mode4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_nfs_lease4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_qop4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_sequenceid4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_slotid4(x, v) nfsd4_encode_uint32_t(x, v)
static __always_inline __be32
nfsd4_encode_uint64_t(struct xdr_stream *xdr, u64 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
if (unlikely(p == NULL))
return nfserr_resource;
put_unaligned_be64(val, p);
return nfs_ok;
}
#define nfsd4_encode_changeid4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_nfs_cookie4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_length4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_offset4(x, v) nfsd4_encode_uint64_t(x, v)
static __always_inline __be32
nfsd4_encode_opaque_fixed(struct xdr_stream *xdr, const void *data,
size_t size)
{
__be32 *p = xdr_reserve_space(xdr, xdr_align_size(size));
size_t pad = xdr_pad_size(size);
if (unlikely(p == NULL))
return nfserr_resource;
memcpy(p, data, size);
if (pad)
memset((char *)p + size, 0, pad);
return nfs_ok;
}
static __always_inline __be32
nfsd4_encode_opaque(struct xdr_stream *xdr, const void *data, size_t size)
{
size_t pad = xdr_pad_size(size);
__be32 *p;
p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(size));
if (unlikely(p == NULL))
return nfserr_resource;
*p++ = cpu_to_be32(size);
memcpy(p, data, size);
if (pad)
memset((char *)p + size, 0, pad);
return nfs_ok;
}
#define nfsd4_encode_component4(x, d, s) nfsd4_encode_opaque(x, d, s)
static __be32 nfsd4_encode_nfs_fh4(struct xdr_stream *xdr,
const struct knfsd_fh *fh_handle)
{
@@ -3110,7 +3200,7 @@ static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32
*bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
*bmval0 & FATTR4_WORD0_FS_LOCATIONS)
*rdattr_err = NFSERR_MOVED;
*rdattr_err = NFS4ERR_MOVED;
else
return nfserr_moved;
}
@@ -4285,21 +4375,16 @@ out:
static bool
nfsd4_setup_notify_entry4(struct notify_entry4 *ne, struct xdr_stream *xdr,
struct dentry *dentry, struct nfs4_delegation *dp,
struct nfsd_file *nf, char *name, u32 namelen)
struct nfsd_file *nf, char *name, u32 namelen,
u32 *attrmask)
{
struct path path = nf->nf_file->f_path;
struct nfsd4_fattr_args args = { };
const u32 *reqmask;
uint32_t *attrmask;
__be32 status;
bool parent;
int ret;
/* Reserve space for attrmask */
attrmask = xdr_reserve_space(xdr, 3 * sizeof(uint32_t));
if (!attrmask)
return false;
ne->ne_file.data = name;
ne->ne_file.len = namelen;
ne->ne_attrs.attrmask.element = attrmask;
@@ -4383,6 +4468,7 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *
struct nfs4_delegation *dp, struct nfsd_file *nf,
u32 *notify_mask)
{
u32 attrmask[3][3] = { };
u8 *p = NULL;
*notify_mask = 0;
@@ -4391,7 +4477,8 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *
struct notify_remove4 nr = { };
if (!nfsd4_setup_notify_entry4(&nr.nrm_old_entry, xdr, nne->ne_dentry, dp,
nf, nne->ne_name, nne->ne_namelen))
nf, nne->ne_name, nne->ne_namelen,
attrmask[0]))
goto out_err;
p = (u8 *)xdr->p;
if (!xdrgen_encode_notify_remove4(xdr, &nr))
@@ -4402,14 +4489,16 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *
struct notify_remove4 old = { };
if (!nfsd4_setup_notify_entry4(&na.nad_new_entry, xdr, nne->ne_dentry, dp,
nf, nne->ne_name, nne->ne_namelen))
nf, nne->ne_name, nne->ne_namelen,
attrmask[0]))
goto out_err;
/* If a file was overwritten, report it in nad_old_entry */
if (nne->ne_target) {
if (!nfsd4_setup_notify_entry4(&old.nrm_old_entry, xdr,
NULL, dp, nf,
nne->ne_name, nne->ne_namelen))
nne->ne_name, nne->ne_namelen,
attrmask[1]))
goto out_err;
na.nad_old_entry.count = 1;
na.nad_old_entry.element = &old;
@@ -4428,19 +4517,19 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event *
/* Don't send any attributes in the old_entry since they're the same in new */
if (!nfsd4_setup_notify_entry4(&nr.nrn_old_entry.nrm_old_entry, xdr,
NULL, dp, nf, nne->ne_name,
nne->ne_namelen))
nne->ne_namelen, attrmask[0]))
goto out_err;
if (!nfsd4_setup_notify_entry4(&nr.nrn_new_entry.nad_new_entry, xdr,
nne->ne_dentry, dp, nf, newname,
nne->ne_newnamelen))
nne->ne_newnamelen, attrmask[1]))
goto out_err;
/* If a file was overwritten, report it in nad_old_entry */
if (nne->ne_target) {
if (!nfsd4_setup_notify_entry4(&old.nrm_old_entry, xdr,
NULL, dp, nf, newname,
nne->ne_newnamelen))
nne->ne_newnamelen, attrmask[2]))
goto out_err;
nr.nrn_new_entry.nad_old_entry.count = 1;
nr.nrn_new_entry.nad_old_entry.element = &old;
@@ -4476,11 +4565,12 @@ u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct nfs4_delegation
{
struct dentry *dentry = nf->nf_file->f_path.dentry;
struct notify_attr4 na = { };
u32 attrmask[3] = { };
u8 *p;
/* RFC 8881 s10.4.3: ne_file must be a zero-length string for dir attrs */
if (!nfsd4_setup_notify_entry4(&na.na_changed_entry, xdr,
dentry, dp, nf, "", 0))
dentry, dp, nf, "", 0, attrmask))
return ERR_PTR(-ENOBUFS);
/* No requested attributes to report; omit the event */
@@ -4506,6 +4596,7 @@ static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
xdr->p = p;
xdr->end = (void *)p + bytes;
buf->buflen = bytes;
xdr->xdrgen_ctx = NULL;
}
__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
@@ -4574,8 +4665,6 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
* directly from the mountpoint dentry.
*/
if (nfsd_mountpoint(dentry, exp)) {
int err;
if (!(exp->ex_flags & NFSEXP_V4ROOT)
&& !attributes_need_mount(cd->rd_bmval)) {
ignore_crossmnt = 1;
@@ -4586,12 +4675,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
* Different "."/".." handling? Something else?
* At least, add a comment here to explain....
*/
err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
if (err) {
nfserr = nfserrno(err);
goto out_put;
}
nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
nfserr = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
if (nfserr)
goto out_put;
crossed = true;
@@ -6274,7 +6358,7 @@ nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
if (!p)
return nfserr_resource;
memcpy(p, buf, cplen);
xdr_encode_opaque_fixed(p, buf, cplen);
buf += cplen;
buflen -= cplen;
@@ -6284,17 +6368,9 @@ nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
if (!p)
return nfserr_resource;
memcpy(p, buf, cplen);
if (cplen < PAGE_SIZE) {
/*
* We're done, with a length that wasn't page
* aligned, so possibly not word aligned. Pad
* any trailing bytes with 0.
*/
xdr_encode_opaque_fixed(p, NULL, cplen);
xdr_encode_opaque_fixed(p, buf, cplen);
if (cplen < PAGE_SIZE)
break;
}
buflen -= PAGE_SIZE;
buf += PAGE_SIZE;
@@ -6637,11 +6713,20 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
unsigned int op_status_offset;
nfsd4_enc encoder;
if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
/*
* nfsd4_proc_compound() stops the COMPOUND early only
* when op->status is set, so a header that cannot be
* encoded has to report the failure here.
*/
if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT) {
op->status = nfsd4_check_resp_size(resp, XDR_UNIT * 2);
goto release;
}
op_status_offset = xdr->buf->len;
if (!xdr_reserve_space(xdr, XDR_UNIT))
if (!xdr_reserve_space(xdr, XDR_UNIT)) {
op->status = nfsd4_check_resp_size(resp, XDR_UNIT);
goto release;
}
if (op->opnum == OP_ILLEGAL)
goto status;
@@ -6751,7 +6836,10 @@ void nfsd4_release_compoundargs(struct svc_rqst *rqstp)
}
while (args->to_free) {
struct svcxdr_tmpbuf *tb = args->to_free;
args->to_free = tb->next;
if (tb->release)
tb->release(tb->buf);
kfree(tb);
}
}
@@ -6801,8 +6889,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
*p++ = resp->cstate.status;
*p++ = htonl(resp->taglen);
memcpy(p, resp->tag, resp->taglen);
p += XDR_QUADLEN(resp->taglen);
p = xdr_encode_opaque_fixed(p, resp->tag, resp->taglen);
*p++ = htonl(resp->opcnt);
nfsd4_sequence_done(resp);
+1
View File
@@ -19,6 +19,7 @@
#include <net/checksum.h>
#include "nfsd.h"
#include "nfserr.h"
#include "netns.h"
#include "stats.h"
#include "cache.h"
+299 -24
View File
@@ -20,9 +20,12 @@
#include <linux/module.h>
#include <linux/fsnotify.h>
#include <linux/nfslocalio.h>
#include <linux/nfs3.h>
#include "idmap.h"
#include "nfsd.h"
#include "nfserr.h"
#include "nfs4ctl.h"
#include "netns.h"
#include "stats.h"
#include "cache.h"
@@ -477,7 +480,7 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
char *mesg = buf;
int i;
int rv;
int len;
size_t len;
int npools;
int *nthreads;
struct net *net = netns(file);
@@ -531,9 +534,13 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
mesg = buf;
size = SIMPLE_TRANSACTION_LIMIT;
for (i = 0; i < npools && size > 0; i++) {
snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
len = strlen(mesg);
for (i = 0; i < npools; i++) {
len = snprintf(mesg, size, "%d%c", nthreads[i],
(i == npools - 1 ? '\n' : ' '));
if (len >= size) {
rv = -ENAMETOOLONG;
goto out_free;
}
size -= len;
mesg += len;
}
@@ -741,7 +748,14 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
return -EINVAL;
trace_nfsd_ctl_ports_addfd(net, fd);
err = nfsd_create_serv(net);
/*
* svc_register() is a no-op once userland owns rpcbind, and this
* interface has no way to hand the new listener to that owner.
*/
if (nn->nfsd_serv && nn->nfsd_serv->sv_no_rpcbind)
return -EBUSY;
err = nfsd_create_serv(net, false);
if (err != 0)
return err;
@@ -773,7 +787,7 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
return -EINVAL;
trace_nfsd_ctl_ports_addxprt(net, transport, port);
err = nfsd_create_serv(net);
err = nfsd_create_serv(net, false);
if (err != 0)
return err;
@@ -1581,14 +1595,29 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
rqstp->rq_proc == NFSPROC4_COMPOUND) {
/* NFSv4 compound */
struct nfsd4_compoundargs *args;
struct nfsd4_op *ops;
u32 opcnt;
int j;
args = rqstp->rq_argp;
genl_rqstp.rq_opcnt = min_t(u32, args->opcnt,
opcnt = READ_ONCE(args->opcnt);
ops = READ_ONCE(args->ops);
/*
* Finish the seqcount retry before
* dereferencing ops. An unchanged counter means
* opcnt and ops came from the same COMPOUND,
* where opcnt cannot exceed what ops holds.
*/
smp_rmb();
if (READ_ONCE(rqstp->rq_status_counter) !=
status_counter)
continue;
genl_rqstp.rq_opcnt = min_t(u32, opcnt,
ARRAY_SIZE(genl_rqstp.rq_opnum));
for (j = 0; j < genl_rqstp.rq_opcnt; j++)
genl_rqstp.rq_opnum[j] =
args->ops[j].opnum;
genl_rqstp.rq_opnum[j] = ops[j].opnum;
}
#endif /* CONFIG_NFSD_V4 */
@@ -1973,21 +2002,40 @@ err_free_msg:
return err;
}
/*
* Transport classes NFSD knows how to instantiate. Vetting the name here
* keeps a bogus string from reaching svc_xprt_create_from_sa(), where an
* unknown name triggers a request_module("svc%s", name) upcall under
* nfsd_mutex.
*/
static bool nfsd_nl_transport_supported(const char *name)
{
static const char * const supported[] = { "tcp", "udp", "rdma" };
int i;
for (i = 0; i < ARRAY_SIZE(supported); i++)
if (!strcmp(name, supported[i]))
return true;
return false;
}
/* Upper bound on the number of listeners a single request may carry. */
#define NFSD_NL_LISTENER_MAX 1024
/**
* nfsd_nl_validate_listeners - sanity-check the listener list from userland
* @info: netlink metadata and command arguments
*
* Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry
* is well-formed: it parses against the policy, carries both an address and
* a transport name, and the address is long enough for its family. Doing
* this up front lets the callers below assume every entry is valid and
* guarantees we make no changes when the request is malformed.
* Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that the list is
* not oversized and that each entry is well-formed.
*
* Return: 0 if every entry is valid, or a negative errno otherwise.
* Return: the number of entries if every entry is valid, or a negative
* errno otherwise.
*/
static int nfsd_nl_validate_listeners(struct genl_info *info)
{
const struct nlattr *attr;
unsigned int count = 0;
int rem;
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
@@ -1996,6 +2044,11 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
struct sockaddr *sa;
int err;
if (++count > NFSD_NL_LISTENER_MAX) {
NL_SET_ERR_MSG(info->extack, "too many listeners");
return -E2BIG;
}
err = nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack);
if (err < 0)
@@ -2004,6 +2057,13 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
return -EINVAL;
if (!nfsd_nl_transport_supported(nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]))) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NFSD_A_SOCK_TRANSPORT_NAME],
"unsupported transport name");
return -EPROTONOSUPPORT;
}
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family))
return -EINVAL;
@@ -2024,7 +2084,129 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
}
}
return 0;
return count;
}
static size_t nfsd_nl_listener_set_msgsize(struct svc_serv *serv)
{
size_t size = GENL_HDRLEN + /* genlmsg_iput() */
nla_total_size(0); /* userspace-rpcbind */
struct svc_xprt *xprt;
unsigned int p;
lockdep_assert_held(&nfsd_mutex);
for (p = 0; p < serv->sv_nprogs; p++)
size += serv->sv_programs[p].pg_nvers *
(nla_total_size(0) + /* rpcbind nest */
nla_total_size(sizeof(u32)) + /* program */
nla_total_size(sizeof(u32)) + /* version */
nla_total_size(sizeof(u32))); /* flags */
spin_lock_bh(&serv->sv_lock);
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
if (!test_bit(XPT_RPCB_UNREG, &xprt->xpt_flags))
continue;
size += nla_total_size(0) + /* addr nest */
nla_total_size(strlen(xprt->xpt_class->xcl_name) + 1) +
nla_total_size(sizeof(struct sockaddr_storage));
}
spin_unlock_bh(&serv->sv_lock);
return size;
}
static struct sk_buff *
nfsd_nl_listener_set_msg(struct genl_info *info, struct net *net,
struct svc_serv *serv)
{
struct svc_xprt *xprt;
struct sk_buff *skb;
unsigned int p, i;
void *hdr;
int err;
lockdep_assert_held(&nfsd_mutex);
skb = genlmsg_new(nfsd_nl_listener_set_msgsize(serv), GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
hdr = genlmsg_iput(skb, info);
if (!hdr) {
err = -EMSGSIZE;
goto err_free_msg;
}
if (nla_put_flag(skb, NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND)) {
err = -EMSGSIZE;
goto err_free_msg;
}
for (p = 0; p < serv->sv_nprogs; p++) {
const struct svc_program *progp = &serv->sv_programs[p];
for (i = 0; i < progp->pg_nvers; i++) {
struct nlattr *attr;
u32 flags = 0;
if (!nfsd_version_registerable(net, progp, i))
continue;
if (progp->pg_vers[i]->vs_need_cong_ctrl)
flags |= NFSD_RPCBIND_FLAGS_NO_UDP;
attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_RPCBIND);
if (!attr) {
err = -EMSGSIZE;
goto err_free_msg;
}
if (nla_put_u32(skb, NFSD_A_RPCBIND_PROGRAM,
progp->pg_prog) ||
nla_put_u32(skb, NFSD_A_RPCBIND_VERSION, i) ||
(flags && nla_put_u32(skb, NFSD_A_RPCBIND_FLAGS,
flags))) {
err = -EMSGSIZE;
goto err_free_msg;
}
nla_nest_end(skb, attr);
}
}
spin_lock_bh(&serv->sv_lock);
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
struct nlattr *attr;
if (!test_bit(XPT_RPCB_UNREG, &xprt->xpt_flags))
continue;
attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR);
if (!attr) {
err = -EMSGSIZE;
goto err_serv_unlock;
}
if (nla_put_string(skb, NFSD_A_SOCK_TRANSPORT_NAME,
xprt->xpt_class->xcl_name) ||
nla_put(skb, NFSD_A_SOCK_ADDR,
sizeof(struct sockaddr_storage),
&xprt->xpt_local)) {
err = -EMSGSIZE;
goto err_serv_unlock;
}
nla_nest_end(skb, attr);
}
spin_unlock_bh(&serv->sv_lock);
genlmsg_end(skb, hdr);
return skb;
err_serv_unlock:
spin_unlock_bh(&serv->sv_lock);
err_free_msg:
nlmsg_free(skb);
return ERR_PTR(err);
}
/**
@@ -2037,12 +2219,20 @@ static int nfsd_nl_validate_listeners(struct genl_info *info)
int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
const struct nlattr *bad_attr = NULL;
struct svc_xprt *xprt, *tmp;
const char *bad_xprt = NULL;
struct sk_buff *rskb = NULL;
unsigned int rpcb_failures;
const struct nlattr *attr;
bool skipped_rpcb = false;
bool userspace_rpcbind;
bool bad_rpcb = false;
struct svc_serv *serv;
LIST_HEAD(permsocks);
struct nfsd_net *nn;
bool delete = false;
int nlisteners;
int err, rem;
/*
@@ -2050,19 +2240,36 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
* malformed request fails cleanly without creating a serv or touching
* the existing listeners.
*/
err = nfsd_nl_validate_listeners(info);
if (err)
return err;
nlisteners = nfsd_nl_validate_listeners(info);
if (nlisteners < 0)
return nlisteners;
userspace_rpcbind = nla_get_flag(info->attrs[NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND]);
mutex_lock(&nfsd_mutex);
err = nfsd_create_serv(net);
nn = net_generic(net, nfsd_net_id);
/*
* An empty list destroys the serv, and nfsd_destroy_serv() drops
* whatever svc_bind() took either way, so teardown is not an
* ownership change. Only a request that leaves a listener standing
* has to agree with the serv it found.
*/
if (nlisteners && nn->nfsd_serv &&
nn->nfsd_serv->sv_no_rpcbind != userspace_rpcbind) {
NL_SET_ERR_MSG(info->extack,
"cannot change rpcbind ownership while a server exists");
mutex_unlock(&nfsd_mutex);
return -EBUSY;
}
err = nfsd_create_serv(net, userspace_rpcbind);
if (err) {
mutex_unlock(&nfsd_mutex);
return err;
}
nn = net_generic(net, nfsd_net_id);
serv = nn->nfsd_serv;
spin_lock_bh(&serv->sv_lock);
@@ -2128,13 +2335,16 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
if (delete)
svc_xprt_destroy_all(serv, net, false);
rpcb_failures = svc_rpcb_failure_count(serv);
/* walk list of addrs again, open any that still don't exist */
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
GENL_HDRLEN, rem) {
struct nlattr *tb[NFSD_A_SOCK_MAX + 1];
const char *xcl_name;
struct sockaddr *sa;
int ret;
bool hit_rpcb;
int flags, ret;
/* validated up front in nfsd_nl_validate_listeners() */
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
@@ -2153,11 +2363,72 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
continue;
}
ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0,
flags = (userspace_rpcbind || skipped_rpcb) ? SVC_SOCK_ANONYMOUS : 0;
ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, flags,
current_cred());
hit_rpcb = false;
if (!userspace_rpcbind && !skipped_rpcb &&
svc_rpcb_failure_count(serv) != rpcb_failures) {
skipped_rpcb = true;
hit_rpcb = true;
if (ret < 0)
ret = svc_xprt_create_from_sa(serv, xcl_name,
net, sa,
SVC_SOCK_ANONYMOUS,
current_cred());
}
/* always save the latest error */
if (ret < 0)
if (ret < 0) {
bad_attr = attr;
bad_xprt = xcl_name;
bad_rpcb = hit_rpcb;
err = ret;
}
}
/*
* The ack carries the errno of the last entry that failed. Point at
* that entry as well, since several entries can share a transport
* name and the errno alone cannot tell them apart.
*/
if (err) {
NL_SET_BAD_ATTR(info->extack, bad_attr);
if (bad_rpcb)
NL_SET_ERR_MSG_FMT(info->extack,
"cannot create %s listener; rpcbind did not answer",
bad_xprt);
else if (skipped_rpcb)
NL_SET_ERR_MSG_FMT(info->extack,
"cannot create %s listener; rpcbind did not answer earlier",
bad_xprt);
else
NL_SET_ERR_MSG_FMT(info->extack,
"cannot create %s listener",
bad_xprt);
} else if (skipped_rpcb) {
NL_SET_ERR_MSG(info->extack,
"rpcbind did not answer, some listeners are not registered");
}
/*
* Build the reply before the serv can go away, and only on success.
* A caller that got an errno has nothing to register.
*/
if (!err && userspace_rpcbind) {
rskb = nfsd_nl_listener_set_msg(info, net, serv);
if (IS_ERR(rskb)) {
err = PTR_ERR(rskb);
rskb = NULL;
/*
* The listeners are up and the errno alone reads as
* if nothing happened. Retrying is safe: a request
* that matches the running set recreates nothing.
*/
NL_SET_ERR_MSG(info->extack,
"listeners are up but the reply could not be built; retry to fetch it");
}
}
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
@@ -2166,6 +2437,10 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
out_unlock_mtx:
mutex_unlock(&nfsd_mutex);
/* rskb is only built once err is known to be zero. */
if (rskb)
return genlmsg_reply(rskb, info);
return err;
}
+4 -239
View File
@@ -12,10 +12,6 @@
#include <linux/types.h>
#include <linux/mount.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
@@ -119,9 +115,11 @@ extern const struct svc_version localio_version1;
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
bool nfsd_version_registerable(struct net *net,
const struct svc_program *progp, u32 version);
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
void nfsd_reset_versions(struct nfsd_net *nn);
int nfsd_create_serv(struct net *net);
int nfsd_create_serv(struct net *net, bool no_rpcbind);
void nfsd_destroy_serv(struct net *net);
#ifdef CONFIG_DEBUG_FS
@@ -147,49 +145,7 @@ extern u64 nfsd_io_cache_write __read_mostly;
extern int nfsd_max_blksize;
static inline int nfsd_v4client(struct svc_rqst *rq)
{
return rq && rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
}
/*
* NFSv4 State
*/
#ifdef CONFIG_NFSD_V4
extern unsigned long max_delegations;
int nfsd4_init_slabs(void);
void nfsd4_free_slabs(void);
int nfs4_state_start(void);
int nfs4_state_start_net(struct net *net);
void nfs4_state_shutdown(void);
void nfs4_state_shutdown_net(struct net *net);
int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void);
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
int nfsd4_create_laundry_wq(void);
void nfsd4_destroy_laundry_wq(void);
bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode);
#else
static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { }
static inline int nfs4_state_start(void) { return 0; }
static inline int nfs4_state_start_net(struct net *net) { return 0; }
static inline void nfs4_state_shutdown(void) { }
static inline void nfs4_state_shutdown_net(struct net *net) { }
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
static inline char * nfs4_recoverydir(void) {return NULL; }
static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
{
return false;
}
static inline int nfsd4_create_laundry_wq(void) { return 0; };
static inline void nfsd4_destroy_laundry_wq(void) {};
static inline bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp,
struct inode *inode)
{
return false;
}
#endif
bool nfsd_v4client(struct svc_rqst *rqstp);
/*
* lockd binding
@@ -198,195 +154,4 @@ void nfsd_lockd_init(void);
void nfsd_lockd_shutdown(void);
/*
* These macros provide pre-xdr'ed values for faster operation.
*/
#define nfs_ok cpu_to_be32(NFS_OK)
#define nfserr_perm cpu_to_be32(NFSERR_PERM)
#define nfserr_noent cpu_to_be32(NFSERR_NOENT)
#define nfserr_io cpu_to_be32(NFSERR_IO)
#define nfserr_nxio cpu_to_be32(NFSERR_NXIO)
#define nfserr_acces cpu_to_be32(NFSERR_ACCES)
#define nfserr_exist cpu_to_be32(NFSERR_EXIST)
#define nfserr_xdev cpu_to_be32(NFSERR_XDEV)
#define nfserr_nodev cpu_to_be32(NFSERR_NODEV)
#define nfserr_notdir cpu_to_be32(NFSERR_NOTDIR)
#define nfserr_isdir cpu_to_be32(NFSERR_ISDIR)
#define nfserr_inval cpu_to_be32(NFSERR_INVAL)
#define nfserr_fbig cpu_to_be32(NFSERR_FBIG)
#define nfserr_nospc cpu_to_be32(NFSERR_NOSPC)
#define nfserr_rofs cpu_to_be32(NFSERR_ROFS)
#define nfserr_mlink cpu_to_be32(NFSERR_MLINK)
#define nfserr_nametoolong cpu_to_be32(NFSERR_NAMETOOLONG)
#define nfserr_notempty cpu_to_be32(NFSERR_NOTEMPTY)
#define nfserr_dquot cpu_to_be32(NFSERR_DQUOT)
#define nfserr_stale cpu_to_be32(NFSERR_STALE)
#define nfserr_remote cpu_to_be32(NFSERR_REMOTE)
#define nfserr_wflush cpu_to_be32(NFSERR_WFLUSH)
#define nfserr_badhandle cpu_to_be32(NFSERR_BADHANDLE)
#define nfserr_notsync cpu_to_be32(NFSERR_NOT_SYNC)
#define nfserr_badcookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_notsupp cpu_to_be32(NFSERR_NOTSUPP)
#define nfserr_toosmall cpu_to_be32(NFSERR_TOOSMALL)
#define nfserr_serverfault cpu_to_be32(NFSERR_SERVERFAULT)
#define nfserr_badtype cpu_to_be32(NFSERR_BADTYPE)
#define nfserr_jukebox cpu_to_be32(NFSERR_JUKEBOX)
#define nfserr_denied cpu_to_be32(NFSERR_DENIED)
#define nfserr_deadlock cpu_to_be32(NFSERR_DEADLOCK)
#define nfserr_expired cpu_to_be32(NFSERR_EXPIRED)
#define nfserr_bad_cookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_same cpu_to_be32(NFSERR_SAME)
#define nfserr_clid_inuse cpu_to_be32(NFSERR_CLID_INUSE)
#define nfserr_stale_clientid cpu_to_be32(NFSERR_STALE_CLIENTID)
#define nfserr_resource cpu_to_be32(NFSERR_RESOURCE)
#define nfserr_moved cpu_to_be32(NFSERR_MOVED)
#define nfserr_nofilehandle cpu_to_be32(NFSERR_NOFILEHANDLE)
#define nfserr_minor_vers_mismatch cpu_to_be32(NFSERR_MINOR_VERS_MISMATCH)
#define nfserr_share_denied cpu_to_be32(NFSERR_SHARE_DENIED)
#define nfserr_stale_stateid cpu_to_be32(NFSERR_STALE_STATEID)
#define nfserr_old_stateid cpu_to_be32(NFSERR_OLD_STATEID)
#define nfserr_bad_stateid cpu_to_be32(NFSERR_BAD_STATEID)
#define nfserr_bad_seqid cpu_to_be32(NFSERR_BAD_SEQID)
#define nfserr_symlink cpu_to_be32(NFSERR_SYMLINK)
#define nfserr_not_same cpu_to_be32(NFSERR_NOT_SAME)
#define nfserr_lock_range cpu_to_be32(NFSERR_LOCK_RANGE)
#define nfserr_restorefh cpu_to_be32(NFSERR_RESTOREFH)
#define nfserr_attrnotsupp cpu_to_be32(NFSERR_ATTRNOTSUPP)
#define nfserr_bad_xdr cpu_to_be32(NFSERR_BAD_XDR)
#define nfserr_openmode cpu_to_be32(NFSERR_OPENMODE)
#define nfserr_badowner cpu_to_be32(NFSERR_BADOWNER)
#define nfserr_locks_held cpu_to_be32(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal cpu_to_be32(NFSERR_OP_ILLEGAL)
#define nfserr_grace cpu_to_be32(NFSERR_GRACE)
#define nfserr_no_grace cpu_to_be32(NFSERR_NO_GRACE)
#define nfserr_reclaim_bad cpu_to_be32(NFSERR_RECLAIM_BAD)
#define nfserr_badname cpu_to_be32(NFSERR_BADNAME)
#define nfserr_admin_revoked cpu_to_be32(NFS4ERR_ADMIN_REVOKED)
#define nfserr_cb_path_down cpu_to_be32(NFSERR_CB_PATH_DOWN)
#define nfserr_locked cpu_to_be32(NFSERR_LOCKED)
#define nfserr_wrongsec cpu_to_be32(NFSERR_WRONGSEC)
#define nfserr_delay cpu_to_be32(NFS4ERR_DELAY)
#define nfserr_badiomode cpu_to_be32(NFS4ERR_BADIOMODE)
#define nfserr_badlayout cpu_to_be32(NFS4ERR_BADLAYOUT)
#define nfserr_bad_session_digest cpu_to_be32(NFS4ERR_BAD_SESSION_DIGEST)
#define nfserr_badsession cpu_to_be32(NFS4ERR_BADSESSION)
#define nfserr_badslot cpu_to_be32(NFS4ERR_BADSLOT)
#define nfserr_complete_already cpu_to_be32(NFS4ERR_COMPLETE_ALREADY)
#define nfserr_conn_not_bound_to_session cpu_to_be32(NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
#define nfserr_deleg_already_wanted cpu_to_be32(NFS4ERR_DELEG_ALREADY_WANTED)
#define nfserr_back_chan_busy cpu_to_be32(NFS4ERR_BACK_CHAN_BUSY)
#define nfserr_layouttrylater cpu_to_be32(NFS4ERR_LAYOUTTRYLATER)
#define nfserr_layoutunavailable cpu_to_be32(NFS4ERR_LAYOUTUNAVAILABLE)
#define nfserr_nomatching_layout cpu_to_be32(NFS4ERR_NOMATCHING_LAYOUT)
#define nfserr_recallconflict cpu_to_be32(NFS4ERR_RECALLCONFLICT)
#define nfserr_unknown_layouttype cpu_to_be32(NFS4ERR_UNKNOWN_LAYOUTTYPE)
#define nfserr_seq_misordered cpu_to_be32(NFS4ERR_SEQ_MISORDERED)
#define nfserr_sequence_pos cpu_to_be32(NFS4ERR_SEQUENCE_POS)
#define nfserr_req_too_big cpu_to_be32(NFS4ERR_REQ_TOO_BIG)
#define nfserr_rep_too_big cpu_to_be32(NFS4ERR_REP_TOO_BIG)
#define nfserr_rep_too_big_to_cache cpu_to_be32(NFS4ERR_REP_TOO_BIG_TO_CACHE)
#define nfserr_retry_uncached_rep cpu_to_be32(NFS4ERR_RETRY_UNCACHED_REP)
#define nfserr_unsafe_compound cpu_to_be32(NFS4ERR_UNSAFE_COMPOUND)
#define nfserr_too_many_ops cpu_to_be32(NFS4ERR_TOO_MANY_OPS)
#define nfserr_op_not_in_session cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION)
#define nfserr_hash_alg_unsupp cpu_to_be32(NFS4ERR_HASH_ALG_UNSUPP)
#define nfserr_clientid_busy cpu_to_be32(NFS4ERR_CLIENTID_BUSY)
#define nfserr_pnfs_io_hole cpu_to_be32(NFS4ERR_PNFS_IO_HOLE)
#define nfserr_seq_false_retry cpu_to_be32(NFS4ERR_SEQ_FALSE_RETRY)
#define nfserr_bad_high_slot cpu_to_be32(NFS4ERR_BAD_HIGH_SLOT)
#define nfserr_deadsession cpu_to_be32(NFS4ERR_DEADSESSION)
#define nfserr_encr_alg_unsupp cpu_to_be32(NFS4ERR_ENCR_ALG_UNSUPP)
#define nfserr_pnfs_no_layout cpu_to_be32(NFS4ERR_PNFS_NO_LAYOUT)
#define nfserr_not_only_op cpu_to_be32(NFS4ERR_NOT_ONLY_OP)
#define nfserr_wrong_cred cpu_to_be32(NFS4ERR_WRONG_CRED)
#define nfserr_wrong_type cpu_to_be32(NFS4ERR_WRONG_TYPE)
#define nfserr_dirdeleg_unavail cpu_to_be32(NFS4ERR_DIRDELEG_UNAVAIL)
#define nfserr_reject_deleg cpu_to_be32(NFS4ERR_REJECT_DELEG)
#define nfserr_returnconflict cpu_to_be32(NFS4ERR_RETURNCONFLICT)
#define nfserr_deleg_revoked cpu_to_be32(NFS4ERR_DELEG_REVOKED)
#define nfserr_partner_notsupp cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP)
#define nfserr_partner_no_auth cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH)
#define nfserr_union_notsupp cpu_to_be32(NFS4ERR_UNION_NOTSUPP)
#define nfserr_offload_denied cpu_to_be32(NFS4ERR_OFFLOAD_DENIED)
#define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS)
#define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL)
#define nfserr_file_open cpu_to_be32(NFS4ERR_FILE_OPEN)
#define nfserr_xattr2big cpu_to_be32(NFS4ERR_XATTR2BIG)
#define nfserr_noxattr cpu_to_be32(NFS4ERR_NOXATTR)
/*
* Error codes for internal use. These are based at an impossible
* nfsstat4 value so that, once converted to be32, they cannot conflict
* with any value defined by the protocol (compare the nlm__int__* codes
* in fs/lockd/lockd.h).
*/
enum {
/* end-of-file indicator in readdir */
NFSERR_EOF = 30000,
#define nfserr_eof cpu_to_be32(NFSERR_EOF)
/* replay detected */
NFSERR_REPLAY_ME,
#define nfserr_replay_me cpu_to_be32(NFSERR_REPLAY_ME)
/* nfs41 replay detected */
NFSERR_REPLAY_CACHE,
#define nfserr_replay_cache cpu_to_be32(NFSERR_REPLAY_CACHE)
/* symlink found where dir expected - handled differently to
* other symlink found errors by NFSv3.
*/
NFSERR_SYMLINK_NOT_DIR,
#define nfserr_symlink_not_dir cpu_to_be32(NFSERR_SYMLINK_NOT_DIR)
};
#ifdef CONFIG_NFSD_V4
/* before processing a COMPOUND operation, we have to check that there
* is enough space in the buffer for XDR encode to succeed. otherwise,
* we might process an operation with side effects, and be unable to
* tell the client that the operation succeeded.
*
* COMPOUND_SLACK_SPACE - this is the minimum bytes of buffer space
* needed to encode an "ordinary" _successful_ operation. (GETATTR,
* READ, READDIR, and READLINK have their own buffer checks.) if we
* fall below this level, we fail the next operation with NFS4ERR_RESOURCE.
*
* COMPOUND_ERR_SLACK_SPACE - this is the minimum bytes of buffer space
* needed to encode an operation which has failed with NFS4ERR_RESOURCE.
* care is taken to ensure that we never fall below this level for any
* reason.
*/
#define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
#define NFSD_COURTESY_CLIENT_TIMEOUT (24 * 60 * 60) /* seconds */
#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128
#define NFS4_CLIENTS_PER_GB 1024
#define NFSD_DELEGRETURN_TIMEOUT (HZ / 34) /* 30ms */
#define NFSD_CB_GETATTR_TIMEOUT NFSD_DELEGRETURN_TIMEOUT
extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
#endif
extern void nfsd4_init_leases_net(struct nfsd_net *nn);
#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry)
{
return 0;
}
static inline void nfsd4_init_leases_net(struct nfsd_net *nn) { };
#define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0)
#endif /* CONFIG_NFSD_V4 */
#endif /* LINUX_NFSD_NFSD_H */
+159
View File
@@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Pre-xdr'ed nfsd error values and nfsd-internal error codes.
*
* Separated from nfsd.h so that nfsd.h itself does not have to pull
* in <linux/nfs4.h>: the NFS4ERR_* values used below are the only
* reason that include was needed.
*/
#ifndef LINUX_NFSD_NFSERR_H
#define LINUX_NFSD_NFSERR_H
#include <linux/sunrpc/xdrgen/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
/*
* These macros provide pre-xdr'ed values for faster operation.
*/
#define nfs_ok cpu_to_be32(NFS_OK)
#define nfserr_perm cpu_to_be32(NFSERR_PERM)
#define nfserr_noent cpu_to_be32(NFSERR_NOENT)
#define nfserr_io cpu_to_be32(NFSERR_IO)
#define nfserr_nxio cpu_to_be32(NFSERR_NXIO)
#define nfserr_acces cpu_to_be32(NFSERR_ACCES)
#define nfserr_exist cpu_to_be32(NFSERR_EXIST)
#define nfserr_xdev cpu_to_be32(NFS3ERR_XDEV)
#define nfserr_nodev cpu_to_be32(NFSERR_NODEV)
#define nfserr_notdir cpu_to_be32(NFSERR_NOTDIR)
#define nfserr_isdir cpu_to_be32(NFSERR_ISDIR)
#define nfserr_inval cpu_to_be32(NFS3ERR_INVAL)
#define nfserr_fbig cpu_to_be32(NFSERR_FBIG)
#define nfserr_nospc cpu_to_be32(NFSERR_NOSPC)
#define nfserr_rofs cpu_to_be32(NFSERR_ROFS)
#define nfserr_mlink cpu_to_be32(NFS3ERR_MLINK)
#define nfserr_nametoolong cpu_to_be32(NFSERR_NAMETOOLONG)
#define nfserr_notempty cpu_to_be32(NFSERR_NOTEMPTY)
#define nfserr_dquot cpu_to_be32(NFSERR_DQUOT)
#define nfserr_stale cpu_to_be32(NFSERR_STALE)
#define nfserr_remote cpu_to_be32(NFS3ERR_REMOTE)
#define nfserr_wflush cpu_to_be32(NFSERR_WFLUSH)
#define nfserr_badhandle cpu_to_be32(NFS3ERR_BADHANDLE)
#define nfserr_notsync cpu_to_be32(NFS3ERR_NOT_SYNC)
#define nfserr_badcookie cpu_to_be32(NFS3ERR_BAD_COOKIE)
#define nfserr_notsupp cpu_to_be32(NFS3ERR_NOTSUPP)
#define nfserr_toosmall cpu_to_be32(NFS3ERR_TOOSMALL)
#define nfserr_serverfault cpu_to_be32(NFS3ERR_SERVERFAULT)
#define nfserr_badtype cpu_to_be32(NFS3ERR_BADTYPE)
#define nfserr_jukebox cpu_to_be32(NFS3ERR_JUKEBOX)
#define nfserr_delay cpu_to_be32(NFS4ERR_DELAY)
#define nfserr_same cpu_to_be32(NFS4ERR_SAME)
#define nfserr_denied cpu_to_be32(NFS4ERR_DENIED)
#define nfserr_deadlock cpu_to_be32(NFS4ERR_DEADLOCK)
#define nfserr_expired cpu_to_be32(NFS4ERR_EXPIRED)
#define nfserr_bad_cookie cpu_to_be32(NFS4ERR_BAD_COOKIE)
#define nfserr_clid_inuse cpu_to_be32(NFS4ERR_CLID_INUSE)
#define nfserr_stale_clientid cpu_to_be32(NFS4ERR_STALE_CLIENTID)
#define nfserr_resource cpu_to_be32(NFS4ERR_RESOURCE)
#define nfserr_moved cpu_to_be32(NFS4ERR_MOVED)
#define nfserr_nofilehandle cpu_to_be32(NFS4ERR_NOFILEHANDLE)
#define nfserr_minor_vers_mismatch cpu_to_be32(NFS4ERR_MINOR_VERS_MISMATCH)
#define nfserr_share_denied cpu_to_be32(NFS4ERR_SHARE_DENIED)
#define nfserr_stale_stateid cpu_to_be32(NFS4ERR_STALE_STATEID)
#define nfserr_old_stateid cpu_to_be32(NFS4ERR_OLD_STATEID)
#define nfserr_bad_stateid cpu_to_be32(NFS4ERR_BAD_STATEID)
#define nfserr_bad_seqid cpu_to_be32(NFS4ERR_BAD_SEQID)
#define nfserr_symlink cpu_to_be32(NFS4ERR_SYMLINK)
#define nfserr_not_same cpu_to_be32(NFS4ERR_NOT_SAME)
#define nfserr_lock_range cpu_to_be32(NFS4ERR_LOCK_RANGE)
#define nfserr_restorefh cpu_to_be32(NFS4ERR_RESTOREFH)
#define nfserr_attrnotsupp cpu_to_be32(NFS4ERR_ATTRNOTSUPP)
#define nfserr_bad_xdr cpu_to_be32(NFS4ERR_BADXDR)
#define nfserr_openmode cpu_to_be32(NFS4ERR_OPENMODE)
#define nfserr_badowner cpu_to_be32(NFS4ERR_BADOWNER)
#define nfserr_locks_held cpu_to_be32(NFS4ERR_LOCKS_HELD)
#define nfserr_op_illegal cpu_to_be32(NFS4ERR_OP_ILLEGAL)
#define nfserr_grace cpu_to_be32(NFS4ERR_GRACE)
#define nfserr_no_grace cpu_to_be32(NFS4ERR_NO_GRACE)
#define nfserr_reclaim_bad cpu_to_be32(NFS4ERR_RECLAIM_BAD)
#define nfserr_badname cpu_to_be32(NFS4ERR_BADNAME)
#define nfserr_admin_revoked cpu_to_be32(NFS4ERR_ADMIN_REVOKED)
#define nfserr_cb_path_down cpu_to_be32(NFS4ERR_CB_PATH_DOWN)
#define nfserr_locked cpu_to_be32(NFS4ERR_LOCKED)
#define nfserr_wrongsec cpu_to_be32(NFS4ERR_WRONGSEC)
#define nfserr_badiomode cpu_to_be32(NFS4ERR_BADIOMODE)
#define nfserr_badlayout cpu_to_be32(NFS4ERR_BADLAYOUT)
#define nfserr_bad_session_digest cpu_to_be32(NFS4ERR_BAD_SESSION_DIGEST)
#define nfserr_badsession cpu_to_be32(NFS4ERR_BADSESSION)
#define nfserr_badslot cpu_to_be32(NFS4ERR_BADSLOT)
#define nfserr_complete_already cpu_to_be32(NFS4ERR_COMPLETE_ALREADY)
#define nfserr_conn_not_bound_to_session cpu_to_be32(NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
#define nfserr_deleg_already_wanted cpu_to_be32(NFS4ERR_DELEG_ALREADY_WANTED)
#define nfserr_back_chan_busy cpu_to_be32(NFS4ERR_BACK_CHAN_BUSY)
#define nfserr_layouttrylater cpu_to_be32(NFS4ERR_LAYOUTTRYLATER)
#define nfserr_layoutunavailable cpu_to_be32(NFS4ERR_LAYOUTUNAVAILABLE)
#define nfserr_nomatching_layout cpu_to_be32(NFS4ERR_NOMATCHING_LAYOUT)
#define nfserr_recallconflict cpu_to_be32(NFS4ERR_RECALLCONFLICT)
#define nfserr_unknown_layouttype cpu_to_be32(NFS4ERR_UNKNOWN_LAYOUTTYPE)
#define nfserr_seq_misordered cpu_to_be32(NFS4ERR_SEQ_MISORDERED)
#define nfserr_sequence_pos cpu_to_be32(NFS4ERR_SEQUENCE_POS)
#define nfserr_req_too_big cpu_to_be32(NFS4ERR_REQ_TOO_BIG)
#define nfserr_rep_too_big cpu_to_be32(NFS4ERR_REP_TOO_BIG)
#define nfserr_rep_too_big_to_cache cpu_to_be32(NFS4ERR_REP_TOO_BIG_TO_CACHE)
#define nfserr_retry_uncached_rep cpu_to_be32(NFS4ERR_RETRY_UNCACHED_REP)
#define nfserr_unsafe_compound cpu_to_be32(NFS4ERR_UNSAFE_COMPOUND)
#define nfserr_too_many_ops cpu_to_be32(NFS4ERR_TOO_MANY_OPS)
#define nfserr_op_not_in_session cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION)
#define nfserr_hash_alg_unsupp cpu_to_be32(NFS4ERR_HASH_ALG_UNSUPP)
#define nfserr_clientid_busy cpu_to_be32(NFS4ERR_CLIENTID_BUSY)
#define nfserr_pnfs_io_hole cpu_to_be32(NFS4ERR_PNFS_IO_HOLE)
#define nfserr_seq_false_retry cpu_to_be32(NFS4ERR_SEQ_FALSE_RETRY)
#define nfserr_bad_high_slot cpu_to_be32(NFS4ERR_BAD_HIGH_SLOT)
#define nfserr_deadsession cpu_to_be32(NFS4ERR_DEADSESSION)
#define nfserr_encr_alg_unsupp cpu_to_be32(NFS4ERR_ENCR_ALG_UNSUPP)
#define nfserr_pnfs_no_layout cpu_to_be32(NFS4ERR_PNFS_NO_LAYOUT)
#define nfserr_not_only_op cpu_to_be32(NFS4ERR_NOT_ONLY_OP)
#define nfserr_wrong_cred cpu_to_be32(NFS4ERR_WRONG_CRED)
#define nfserr_wrong_type cpu_to_be32(NFS4ERR_WRONG_TYPE)
#define nfserr_dirdeleg_unavail cpu_to_be32(NFS4ERR_DIRDELEG_UNAVAIL)
#define nfserr_reject_deleg cpu_to_be32(NFS4ERR_REJECT_DELEG)
#define nfserr_returnconflict cpu_to_be32(NFS4ERR_RETURNCONFLICT)
#define nfserr_deleg_revoked cpu_to_be32(NFS4ERR_DELEG_REVOKED)
#define nfserr_partner_notsupp cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP)
#define nfserr_partner_no_auth cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH)
#define nfserr_union_notsupp cpu_to_be32(NFS4ERR_UNION_NOTSUPP)
#define nfserr_offload_denied cpu_to_be32(NFS4ERR_OFFLOAD_DENIED)
#define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS)
#define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL)
#define nfserr_file_open cpu_to_be32(NFS4ERR_FILE_OPEN)
#define nfserr_xattr2big cpu_to_be32(NFS4ERR_XATTR2BIG)
#define nfserr_noxattr cpu_to_be32(NFS4ERR_NOXATTR)
/*
* Error codes for internal use. These are based at an impossible
* nfsstat4 value so that, once converted to be32, they cannot conflict
* with any value defined by the protocol (compare the nlm__int__* codes
* in fs/lockd/lockd.h).
*/
enum {
/* end-of-file indicator in readdir */
NFSERR_EOF = 30000,
#define nfserr_eof cpu_to_be32(NFSERR_EOF)
/* replay detected */
NFSERR_REPLAY_ME,
#define nfserr_replay_me cpu_to_be32(NFSERR_REPLAY_ME)
/* nfs41 replay detected */
NFSERR_REPLAY_CACHE,
#define nfserr_replay_cache cpu_to_be32(NFSERR_REPLAY_CACHE)
/* symlink found where dir expected - handled differently to
* other symlink found errors by NFSv3.
*/
NFSERR_SYMLINK_NOT_DIR,
#define nfserr_symlink_not_dir cpu_to_be32(NFSERR_SYMLINK_NOT_DIR)
};
#endif /* LINUX_NFSD_NFSERR_H */
+38 -35
View File
@@ -9,10 +9,12 @@
*/
#include <linux/exportfs.h>
#include <linux/nfs3.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <crypto/utils.h>
#include "nfsd.h"
#include "nfserr.h"
#include "netns.h"
#include "stats.h"
#include "vfs.h"
@@ -334,6 +336,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net,
}
switch (fhp->fh_maxsize) {
case NFSD_FHSIZE_UNSPEC:
break;
case NFS4_FHSIZE:
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
fhp->fh_no_atomic_attr = true;
@@ -782,34 +786,53 @@ __be32 fh_getattr(const struct svc_fh *fhp, struct kstat *stat)
AT_STATX_SYNC_AS_STAT));
}
/**
* fh_fill_pre_attrs - Fill in pre-op attributes
* @fhp: file handle to be updated
*
*/
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
static __be32 __must_check __fh_fill_pre_attrs(struct svc_fh *fhp)
{
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
struct kstat stat;
__be32 err;
if (fhp->fh_no_wcc || fhp->fh_pre_saved)
return nfs_ok;
err = fh_getattr(fhp, &stat);
err = fh_getattr(fhp, &fhp->fh_post_attr);
if (err)
return err;
if (v4)
fhp->fh_pre_change = nfsd4_change_attribute(&stat);
fhp->fh_pre_change = fhp->fh_post_change =
nfsd4_change_attribute(&fhp->fh_post_attr);
fhp->fh_pre_mtime = stat.mtime;
fhp->fh_pre_ctime = stat.ctime;
fhp->fh_pre_size = stat.size;
fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
fhp->fh_pre_size = fhp->fh_post_attr.size;
fhp->fh_pre_saved = true;
return nfs_ok;
}
/**
* fh_fill_pre_attrs - Fill in pre-op attributes
* @fhp: file handle to be updated
*
* Post-op attrs are filled and pre-op attrs are copied
* from there. The post-op attrs can later be replaced by
* fh_fill_post_attrs() or activated by fh_fill_post_noop().
*
* The inode must be locked.
*
* Returns: error from vfs_getattr() which must be checked.
*/
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
{
lockdep_assert_held_write(&fhp->fh_dentry->d_inode->i_rwsem);
return __fh_fill_pre_attrs(fhp);
}
__be32 __must_check fh_fill_pre_attrs_unlocked(struct svc_fh *fhp)
{
fhp->fh_no_atomic_attr = true;
return __fh_fill_pre_attrs(fhp);
}
/**
* fh_fill_post_attrs - Fill in post-op attributes
* @fhp: file handle to be updated
@@ -826,6 +849,9 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp)
if (fhp->fh_post_saved)
printk("nfsd: inode locked twice during operation.\n");
if (!fhp->fh_no_atomic_attr)
lockdep_assert_held_write(&fhp->fh_dentry->d_inode->i_rwsem);
err = fh_getattr(fhp, &fhp->fh_post_attr);
if (err)
return err;
@@ -837,29 +863,6 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp)
return nfs_ok;
}
/**
* fh_fill_both_attrs - Fill pre-op and post-op attributes
* @fhp: file handle to be updated
*
* This is used when the directory wasn't changed, but wcc attributes
* are needed anyway.
*/
__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp)
{
__be32 err;
err = fh_fill_post_attrs(fhp);
if (err)
return err;
fhp->fh_pre_change = fhp->fh_post_change;
fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
fhp->fh_pre_size = fhp->fh_post_attr.size;
fhp->fh_pre_saved = true;
return nfs_ok;
}
/*
* Release a file handle.
*/
+32 -1
View File
@@ -111,6 +111,11 @@ typedef struct svc_fh {
struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */
} svc_fh;
struct nfsd_fhandle {
struct svc_fh fh;
};
#define NFSD4_FH_FOREIGN (1<<0)
#define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f))
#define HAS_FH_FLAG(c, f) ((c)->fh_flags & (f))
@@ -246,6 +251,20 @@ fh_copy_shallow(struct knfsd_fh *dst, const struct knfsd_fh *src)
memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size);
}
#define NFSD_FHSIZE_UNSPEC 0
/**
* fh_init - Prepare a file handle for fh_compose() or fh_verify()
* @fhp: File handle to initialize
* @maxsize: Largest file handle, in bytes, to build in @fhp
*
* @maxsize bounds the handle fh_compose() may build: NFS_FHSIZE,
* NFS3_FHSIZE, and NFS4_FHSIZE additionally select version-specific
* handling in fh_verify(). Callers that only verify an incoming
* handle pass NFSD_FHSIZE_UNSPEC, which cannot be composed.
*
* Return: @fhp
*/
static __inline__ struct svc_fh *
fh_init(struct svc_fh *fhp, int maxsize)
{
@@ -337,6 +356,18 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
u64 nfsd4_change_attribute(const struct kstat *stat);
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp);
__be32 __must_check fh_fill_pre_attrs_unlocked(struct svc_fh *fhp);
__be32 fh_fill_post_attrs(struct svc_fh *fhp);
__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp);
/**
* fh_fill_post_noop - Copy pre attrs to post attrs
* @fhp: file handle to be updated
*
* This is used when the directory wasn't changed, but wcc attributes
* are needed anyway.
*/
static inline void fh_fill_post_noop(struct svc_fh *fhp)
{
fhp->fh_post_saved = true;
}
#endif /* _LINUX_NFSD_NFSFH_H */
+1086 -511
View File
File diff suppressed because it is too large. Load diff
+53 -21
View File
@@ -25,7 +25,10 @@
#include <net/addrconf.h>
#include <net/ipv6.h>
#include <net/net_namespace.h>
#include "nfsd.h"
#include "nfserr.h"
#include "nfs4ctl.h"
#include "cache.h"
#include "vfs.h"
#include "netns.h"
@@ -38,11 +41,6 @@
atomic_t nfsd_th_cnt = ATOMIC_INIT(0);
static int nfsd(void *vrqstp);
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
static int nfsd_acl_rpcbind_set(struct net *,
const struct svc_program *,
u32, int,
unsigned short,
unsigned short);
static __be32 nfsd_acl_init_request(struct svc_rqst *,
const struct svc_program *,
struct svc_process_info *);
@@ -124,7 +122,7 @@ struct svc_program nfsd_programs[] = {
.pg_class = "nfsd",
.pg_authenticate = svc_set_client,
.pg_init_request = nfsd_acl_init_request,
.pg_rpcbind_set = nfsd_acl_rpcbind_set,
.pg_rpcbind_set = nfsd_rpcbind_set,
},
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
@@ -204,6 +202,11 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
return 0;
}
bool nfsd_v4client(struct svc_rqst *rqstp)
{
return rqstp && rqstp->rq_prog == NFS_PROGRAM && rqstp->rq_vers == 4;
}
bool nfsd_net_try_get(struct net *net) __must_hold(rcu)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@@ -599,7 +602,14 @@ struct svc_rqst *nfsd_current_rqst(void)
return NULL;
}
int nfsd_create_serv(struct net *net)
/**
* nfsd_create_serv - create the svc_serv for a namespace if it has none
* @net: network namespace to operate within
* @no_rpcbind: true if the caller registers the listeners with rpcbind
*
* Return: 0 on success or a negative errno.
*/
int nfsd_create_serv(struct net *net, bool no_rpcbind)
{
int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@@ -627,6 +637,9 @@ int nfsd_create_serv(struct net *net)
return -ENOMEM;
}
/* svc_bind() reads this, so set it first. */
serv->sv_no_rpcbind = no_rpcbind;
error = svc_bind(serv, net);
if (error < 0) {
svc_destroy(&serv);
@@ -767,7 +780,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const c
strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename,
sizeof(nn->nfsd_name));
error = nfsd_create_serv(net);
error = nfsd_create_serv(net, false);
if (error)
goto out;
serv = nn->nfsd_serv;
@@ -795,18 +808,6 @@ nfsd_support_acl_version(int vers)
return false;
}
static int
nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp,
u32 version, int family, unsigned short proto,
unsigned short port)
{
if (!nfsd_support_acl_version(version) ||
!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
return 0;
return svc_generic_rpcbind_set(net, progp, version, family,
proto, port);
}
static __be32
nfsd_acl_init_request(struct svc_rqst *rqstp,
const struct svc_program *progp,
@@ -841,12 +842,43 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
}
#endif
/**
* nfsd_version_registerable - would nfsd register [@progp, @version]?
* @net: network namespace to query
* @progp: RPC program to query
* @version: RPC version to query
*
* Answers the question for a listener of any protocol. A caller that asks
* about one listener must apply vs_need_cong_ctrl itself.
*
* Return: true when the version is a candidate for rpcbind registration.
*/
bool nfsd_version_registerable(struct net *net,
const struct svc_program *progp, u32 version)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
/*
* nfsd_acl_version[] is built by CONFIG_NFSD_V2_ACL and
* CONFIG_NFSD_V3_ACL, so a NULL entry already answers for nfsacl and
* nfsd_support_acl_version() would add nothing here.
*/
if (version >= progp->pg_nvers || !progp->pg_vers[version])
return false;
/* nfslocalio is hidden and never reaches rpcbind. */
if (progp->pg_vers[version]->vs_hidden)
return false;
return nfsd_vers(nn, version, NFSD_TEST);
}
static int
nfsd_rpcbind_set(struct net *net, const struct svc_program *progp,
u32 version, int family, unsigned short proto,
unsigned short port)
{
if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
if (!nfsd_version_registerable(net, progp, version))
return 0;
return svc_generic_rpcbind_set(net, progp, version, family,
proto, port);
+158 -448
View File
@@ -8,18 +8,18 @@
#include <linux/filelock.h>
#include "vfs.h"
#include "nfserr.h"
#include "xdr.h"
#include "nfs2xdr_gen.h"
#include "auth.h"
#include "trace.h"
/*
* Sun convention: a sattr time-useconds field of one full second (an
* otherwise out-of-range value) means "set this time to the current
* server time." It's needed to make permissions checks for the "touch"
* program across NFSv2 mounts work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by Brent Callaghan,
* Addison-Wesley, ISBN 0-201-32750-5
* Linux-internal ftype values for socket and unknown inodes, not
* in RFC 1094's wire enum; values match enum nfs_ftype.
*/
#define NFS2_SATTR_SET_TO_SERVER_TIME (1000000)
#define NFBAD (7)
#define NFSOCK (6)
/*
* Mapping of S_IF* types to NFS file types
@@ -82,19 +82,6 @@ svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
return true;
}
static bool
svcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp)
{
__be32 *p;
p = xdr_reserve_space(xdr, NFS_FHSIZE);
if (!p)
return false;
memcpy(p, &fhp->fh_handle.fh_raw, NFS_FHSIZE);
return true;
}
static __be32 *
encode_timeval(__be32 *p, const struct timespec64 *time)
{
@@ -106,113 +93,6 @@ encode_timeval(__be32 *p, const struct timespec64 *time)
return p;
}
static bool
svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
{
u32 size, i;
__be32 *p;
char *c;
if (xdr_stream_decode_u32(xdr, &size) < 0)
return false;
if (size == 0 || size > NFS_MAXNAMLEN)
return false;
p = xdr_inline_decode(xdr, size);
if (!p)
return false;
*len = size;
*name = (char *)p;
for (i = 0, c = *name; i < size; i++, c++)
if (*c == '\0' || *c == '/')
return false;
return true;
}
static bool
svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
char **name, unsigned int *len)
{
return svcxdr_decode_fhandle(xdr, fhp) &&
svcxdr_decode_filename(xdr, name, len);
}
static bool
svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
struct iattr *iap)
{
u32 tmp1, tmp2;
__be32 *p;
p = xdr_inline_decode(xdr, XDR_UNIT * 8);
if (!p)
return false;
iap->ia_valid = 0;
/*
* Some Sun clients put 0xffff in the mode field when they
* mean 0xffffffff.
*/
tmp1 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
iap->ia_valid |= ATTR_MODE;
iap->ia_mode = tmp1;
}
tmp1 = be32_to_cpup(p++);
if (tmp1 != (u32)-1) {
iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
if (uid_valid(iap->ia_uid))
iap->ia_valid |= ATTR_UID;
}
tmp1 = be32_to_cpup(p++);
if (tmp1 != (u32)-1) {
iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
if (gid_valid(iap->ia_gid))
iap->ia_valid |= ATTR_GID;
}
tmp1 = be32_to_cpup(p++);
if (tmp1 != (u32)-1) {
iap->ia_valid |= ATTR_SIZE;
iap->ia_size = tmp1;
}
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
/*
* Range test here to prevent the multiplication from
* wrapping to a valid (but incorrect) value on 32-bit
* platforms.
*/
if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
return false;
iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
iap->ia_atime.tv_sec = tmp1;
iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
iap->ia_valid &= ~ATTR_ATIME_SET;
}
tmp1 = be32_to_cpup(p++);
tmp2 = be32_to_cpup(p++);
if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME)
return false;
iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
iap->ia_mtime.tv_sec = tmp1;
iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME)
iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
}
return true;
}
/**
* svcxdr_encode_fattr - Encode NFSv2 file attributes
* @rqstp: Context of a completed RPC transaction
@@ -294,146 +174,10 @@ nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_fhandle(xdr, &args->fh);
}
bool
nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_sattrargs *args = rqstp->rq_argp;
return svcxdr_decode_fhandle(xdr, &args->fh) &&
svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
}
bool
nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_diropargs *args = rqstp->rq_argp;
return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
}
bool
nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_readargs *args = rqstp->rq_argp;
u32 totalcount;
if (!svcxdr_decode_fhandle(xdr, &args->fh))
return false;
if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
return false;
if (xdr_stream_decode_u32(xdr, &args->count) < 0)
return false;
/* totalcount is ignored */
if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
return false;
return true;
}
bool
nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_writeargs *args = rqstp->rq_argp;
u32 beginoffset, totalcount;
if (!svcxdr_decode_fhandle(xdr, &args->fh))
return false;
/* beginoffset is ignored */
if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
return false;
if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
return false;
/* totalcount is ignored */
if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
return false;
/* opaque data */
if (xdr_stream_decode_u32(xdr, &args->len) < 0)
return false;
if (args->len > NFS_MAXDATA)
return false;
return xdr_stream_subsegment(xdr, &args->payload, args->len);
}
bool
nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_createargs *args = rqstp->rq_argp;
return svcxdr_decode_diropargs(xdr, &args->fh,
&args->name, &args->len) &&
svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
}
bool
nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_renameargs *args = rqstp->rq_argp;
return svcxdr_decode_diropargs(xdr, &args->ffh,
&args->fname, &args->flen) &&
svcxdr_decode_diropargs(xdr, &args->tfh,
&args->tname, &args->tlen);
}
bool
nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_linkargs *args = rqstp->rq_argp;
return svcxdr_decode_fhandle(xdr, &args->ffh) &&
svcxdr_decode_diropargs(xdr, &args->tfh,
&args->tname, &args->tlen);
}
bool
nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_symlinkargs *args = rqstp->rq_argp;
struct kvec *head = rqstp->rq_arg.head;
if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
return false;
if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
return false;
if (args->tlen == 0)
return false;
args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
if (!args->first.iov_base)
return false;
return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
}
bool
nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_readdirargs *args = rqstp->rq_argp;
if (!svcxdr_decode_fhandle(xdr, &args->fh))
return false;
if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
return false;
if (xdr_stream_decode_u32(xdr, &args->count) < 0)
return false;
return true;
}
/*
* XDR encode functions
*/
bool
nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_stat *resp = rqstp->rq_resp;
return svcxdr_encode_stat(xdr, resp->status);
}
bool
nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
@@ -451,205 +195,177 @@ nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return true;
}
bool
nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_diropres *resp = rqstp->rq_resp;
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
switch (resp->status) {
case nfs_ok:
if (!svcxdr_encode_fhandle(xdr, &resp->fh))
return false;
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
return false;
break;
}
return true;
}
bool
nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_readlinkres *resp = rqstp->rq_resp;
struct kvec *head = rqstp->rq_res.head;
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
switch (resp->status) {
case nfs_ok:
if (xdr_stream_encode_u32(xdr, resp->len) < 0)
return false;
svcxdr_encode_opaque_pages(rqstp, xdr, &resp->page, 0,
resp->len);
if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
return false;
break;
}
return true;
}
bool
nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_readres *resp = rqstp->rq_resp;
struct kvec *head = rqstp->rq_res.head;
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
switch (resp->status) {
case nfs_ok:
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
return false;
if (xdr_stream_encode_u32(xdr, resp->count) < 0)
return false;
svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
rqstp->rq_res.page_base,
resp->count);
if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
return false;
break;
}
return true;
}
bool
nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_readdirres *resp = rqstp->rq_resp;
struct xdr_buf *dirlist = &resp->dirlist;
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
switch (resp->status) {
case nfs_ok:
svcxdr_encode_opaque_pages(rqstp, xdr, dirlist->pages, 0,
dirlist->len);
/* no more entries */
if (xdr_stream_encode_item_absent(xdr) < 0)
return false;
if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
return false;
break;
}
return true;
}
bool
nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nfsd_statfsres *resp = rqstp->rq_resp;
struct kstatfs *stat = &resp->stats;
__be32 *p;
if (!svcxdr_encode_stat(xdr, resp->status))
return false;
switch (resp->status) {
case nfs_ok:
p = xdr_reserve_space(xdr, XDR_UNIT * 5);
if (!p)
return false;
*p++ = cpu_to_be32(NFS_MAXDATA);
*p++ = cpu_to_be32(stat->f_bsize);
*p++ = cpu_to_be32(stat->f_blocks);
*p++ = cpu_to_be32(stat->f_bfree);
*p = cpu_to_be32(stat->f_bavail);
break;
}
return true;
}
/*
* READDIR reply entry list (RFC 1094). The entry list is the
* value-follows form of "entry *entries": each entry prefixed by TRUE,
* the sequence closed by FALSE. xdrgen's aggregate codec owns that
* framing; the hooks below stream one entry at a time straight from the
* directory into the live reply, mirroring nfsd4_encode_dirlist4. The
* directory was opened in nfsd_proc_readdir(), so reading and encoding
* happen together here during reply encoding.
*/
/**
* nfssvc_encode_nfscookie - Encode a directory offset cookie
* @resp: readdir result context
* @offset: offset cookie to encode
* nfssvc_encode_nfscookie - Back-patch a directory entry cookie
* @xdr: stream holding the reserved cookie slot
* @pos: byte position of the cookie slot, or 0 when there is none
* @cookie: cookie value to write
*
* The buffer space for the offset cookie has already been reserved
* by svcxdr_encode_entry_common().
* An NFSv2 entry's cookie is the offset at which the following entry is
* read. That offset is not known until the following entry is pulled,
* so each entry is encoded with a placeholder cookie that a later call
* overwrites once the resume offset is known.
*/
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset)
void nfssvc_encode_nfscookie(struct xdr_stream *xdr, unsigned int pos,
u32 cookie)
{
__be32 cookie = cpu_to_be32(offset);
__be32 wire_cookie = cpu_to_be32(cookie);
if (!resp->cookie_offset)
if (!pos)
return;
write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
sizeof(cookie));
resp->cookie_offset = 0;
write_bytes_to_xdr_buf(xdr->buf, pos, &wire_cookie, XDR_UNIT);
}
static bool
svcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name,
int namlen, loff_t offset, u64 ino)
/**
* nfs2_readdirok_encode_begin - size the streaming entry budget
* @c: aggregate cursor for the entry list
*
* Derive the entry byte budget from the space left in the live reply
* and the client's count hint, reserving room for the list terminator
* and the eof flag that follow the entries.
*
* Return: true.
*/
bool nfs2_readdirok_encode_begin(struct xdrgen_aggregate_cursor *c)
{
struct xdr_buf *dirlist = &resp->dirlist;
struct xdr_stream *xdr = &resp->xdr;
if (xdr_stream_encode_item_present(xdr) < 0)
return false;
/* fileid */
if (xdr_stream_encode_u32(xdr, (u32)ino) < 0)
return false;
/* name */
if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS2_MAXNAMLEN)) < 0)
return false;
/* cookie */
resp->cookie_offset = dirlist->len;
if (xdr_stream_encode_u32(xdr, ~0U) < 0)
return false;
struct svc_rqst *rqstp = c->ctx;
struct readdirres_wrapper *resp = rqstp->rq_resp;
struct xdr_stream *xdr = c->xdr;
int bytes_left;
/* Reserve the terminator FALSE and the eof bool (two words). */
bytes_left = xdr->buf->buflen - xdr->buf->len - XDR_UNIT * 2;
if (bytes_left < 0)
bytes_left = 0;
resp->space_left = min_t(u32, bytes_left,
clamp(resp->count, (u32)(XDR_UNIT * 2),
(u32)PAGE_SIZE) - XDR_UNIT * 2);
resp->cookie_offset = 0;
return true;
}
/**
* nfssvc_encode_entry - encode one NFSv2 READDIR entry
* @data: directory context
* @name: name of the object to be encoded
* @namlen: length of that name, in bytes
* @offset: the offset of the previous entry
* @ino: the fileid of this entry
* @d_type: unused
* nfs2_readdirok_encode - stream the next READDIR entry
* @c: aggregate cursor for the entry list
* @out: OUT: entry the framing encodes when one is produced
*
* Return values:
* %0: Entry was successfully encoded.
* %-EINVAL: An encoding problem occurred, secondary status code in resp->common.err
* Back-patch the previous entry's placeholder cookie with this entry's
* resume offset, then, if the reply budget allows, project the next
* directory entry into @out for the generated per-entry encoder.
*
* On exit, the following fields are updated:
* - resp->xdr
* - resp->common.err
* - resp->cookie_offset
* Return: true when @out holds an entry to encode; false to end the
* list -- because the directory is exhausted, a host error struck, or
* the reply budget filled. On budget exhaustion the previous entry's
* cookie already points at the rejected entry, the client's resume
* point.
*/
int nfssvc_encode_entry(void *data, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
bool nfs2_readdirok_encode(struct xdrgen_aggregate_cursor *c,
struct entry *out)
{
struct readdir_cd *ccd = data;
struct nfsd_readdirres *resp = container_of(ccd,
struct nfsd_readdirres,
common);
unsigned int starting_length = resp->dirlist.len;
struct svc_rqst *rqstp = c->ctx;
struct readdirres_wrapper *resp = rqstp->rq_resp;
struct xdr_stream *xdr = c->xdr;
struct buffered_dirent *de;
int namlen;
u32 need;
/* The offset cookie for the previous entry */
nfssvc_encode_nfscookie(resp, offset);
/*
* The previous entry was just encoded; commit the stream and note
* its cookie slot -- the entry's last XDR word -- so it can be
* back-patched once this entry's resume offset is known.
*/
if (c->index) {
xdr_commit_encode(xdr);
resp->cookie_offset = xdr->buf->len - XDR_UNIT;
}
if (!svcxdr_encode_entry_common(resp, name, namlen, offset, ino))
goto out_toosmall;
de = nfsd_readdir_next(&resp->iter);
if (!de)
return false;
xdr_commit_encode(&resp->xdr);
resp->common.err = nfs_ok;
return 0;
/* The previous entry's cookie is this entry's resume offset. */
nfssvc_encode_nfscookie(xdr, resp->cookie_offset, (u32)de->offset);
out_toosmall:
resp->cookie_offset = 0;
resp->common.err = nfserr_toosmall;
resp->dirlist.len = starting_length;
return -EINVAL;
namlen = min_t(int, de->namlen, NFS_MAXNAMLEN);
/* value-follows + fileid + name (length + data) + cookie */
need = XDR_UNIT * (4 + XDR_QUADLEN(namlen));
if (need > resp->space_left)
return false;
resp->space_left -= need;
out->fileid = (u32)de->ino;
out->name.len = namlen;
out->name.data = (unsigned char *)de->name;
memset(out->cookie, 0, sizeof(out->cookie)); /* back-patched later */
trace_nfsd_dirent(resp->iter.fhp, de->ino, de->name, namlen);
return true;
}
/**
* nfs2_readdirok_encode_end - finish the streamed entry list
* @c: aggregate cursor for the entry list
* @ok: false if the framing hit a wire error while encoding the list
*
* Back-patch the final entry's cookie with the directory's resume
* offset and report eof. A list cut short by the reply budget or a
* host error reports eof false, so the client reads the rest with a
* follow-up request.
*
* Return: true.
*/
bool nfs2_readdirok_encode_end(struct xdrgen_aggregate_cursor *c, bool ok)
{
struct svc_rqst *rqstp = c->ctx;
struct readdirres_wrapper *resp = rqstp->rq_resp;
struct xdr_stream *xdr = c->xdr;
if (ok)
nfssvc_encode_nfscookie(xdr, resp->cookie_offset,
(u32)resp->iter.offset);
resp->xdrgen.u.readdirok.eof = resp->iter.eof;
/*
* The xdr_stream primitives don't manage rq_next_page, and
* svcrdma retains only the pages below it for Send completion.
* The eof word that follows starts a new page when this one is
* full.
*/
rqstp->rq_next_page = xdr->page_ptr + 1;
if (xdr->p == xdr->end)
rqstp->rq_next_page++;
return true;
}
/*
* A server never decodes a READDIR result; these satisfy the linkage
* of the generated (unused) decoder.
*/
bool nfs2_readdirok_decode_begin(struct xdrgen_aggregate_cursor *c)
{
return false;
}
bool nfs2_readdirok_decode(struct xdrgen_aggregate_cursor *c,
const struct entry *in)
{
return false;
}
bool nfs2_readdirok_decode_end(struct xdrgen_aggregate_cursor *c, bool ok)
{
return false;
}
/*
@@ -662,16 +378,10 @@ void nfssvc_release_attrstat(struct svc_rqst *rqstp)
fh_put(&resp->fh);
}
void nfssvc_release_diropres(struct svc_rqst *rqstp)
void nfssvc_release_readdirres(struct svc_rqst *rqstp)
{
struct nfsd_diropres *resp = rqstp->rq_resp;
fh_put(&resp->fh);
}
void nfssvc_release_readres(struct svc_rqst *rqstp)
{
struct nfsd_readres *resp = rqstp->rq_resp;
struct readdirres_wrapper *resp = rqstp->rq_resp;
nfsd_readdir_close(&resp->iter);
fh_put(&resp->fh);
}
+41 -5
View File
@@ -45,6 +45,25 @@
#include "nfsfh.h"
#include "nfsd.h"
/*
* Before processing a COMPOUND operation, we have to check that there
* is enough space in the buffer for XDR encode to succeed. otherwise,
* we might process an operation with side effects, and be unable to
* tell the client that the operation succeeded.
*
* COMPOUND_ERR_SLACK_SPACE - this is the minimum bytes of buffer space
* needed to encode an operation which has failed with NFS4ERR_RESOURCE.
* care is taken to ensure that we never fall below this level for any
* reason.
*/
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128
#define NFS4_CLIENTS_PER_GB 1024
#define NFSD_DELEGRETURN_TIMEOUT (HZ / 34) /* 30ms */
#define NFSD_CB_GETATTR_TIMEOUT NFSD_DELEGRETURN_TIMEOUT
typedef struct {
u32 cl_boot;
u32 cl_id;
@@ -126,10 +145,13 @@ struct nfs4_stid {
#define SC_TYPE_COPY BIT(4)
unsigned short sc_type;
/* nn->deleg_lock protects sc_status for delegation stateids.
* ->cl_lock protects sc_status for open and lock stateids.
* ->st_mutex also protect sc_status for open stateids.
* ->ls_lock protects sc_status for layout stateids.
/*
* nn->deleg_lock protects sc_status for hashed delegation stateids.
* ->cl_lock protects the bits set as one is disposed of
* (SC_STATUS_CLOSED, SC_STATUS_FREEABLE, SC_STATUS_FREED) and
* sc_status for open and lock stateids. ->st_mutex also protects
* sc_status for open stateids. ->ls_lock protects sc_status for
* layout stateids.
*/
/*
* For an open stateid kept around *only* to process close replays.
@@ -249,6 +271,7 @@ struct nfsd4_cb_notify {
struct nfsd_notify_event *ncn_evt[NOTIFY4_EVENT_QUEUE_SIZE]; // list of events
struct page *ncn_pages[NOTIFY4_PAGE_ARRAY_SIZE]; // for encoding
struct notify4 *ncn_nf; // array of notify4's to be sent
u32 *ncn_masks; // host-order notify_mask backing for ncn_nf[]
bool ncn_encode_err; // did encoding fail?
struct nfsd4_callback ncn_cb; // notify4 callback
};
@@ -270,7 +293,8 @@ struct nfsd4_cb_notify {
* If the server attempts to recall a delegation and the client doesn't do so
* before a timeout, the server may also revoke the delegation. In that case,
* the object will either be destroyed (v4.0) or moved to a per-client list of
* revoked delegations (v4.1+).
* revoked delegations (v4.1+). A v4.1+ client that rejects the recall holds
* no record of the delegation, so the object is destroyed rather than listed.
*
* This object is a superset of the nfs4_stid.
*/
@@ -286,9 +310,19 @@ struct nfs4_delegation {
int dl_retries;
struct nfsd4_callback dl_recall;
bool dl_recalled;
bool dl_recall_rejected;
bool dl_written;
bool dl_setattr;
/* Forward-channel slot that carried the granting request */
struct {
u32 sessionid_seq;
u32 slotid;
u32 seqid;
bool valid;
bool retired_at_send;
} dl_recall_grant;
union {
/* for CB_GETATTR */
struct nfs4_cb_fattr dl_cb_fattr;
@@ -599,6 +633,8 @@ struct nfs4_client {
unsigned int cl_state;
atomic_t cl_delegs_in_recall;
/* Length of cl_delegations, updated under nn->deleg_lock */
unsigned int cl_deleg_count;
struct nfsd4_cb_recall_any *cl_ra;
time64_t cl_ra_time;
+44 -1
View File
@@ -13,7 +13,6 @@
#include <linux/sunrpc/xprt.h>
#include <trace/misc/fs.h>
#include <trace/misc/fsnotify.h>
#include <trace/misc/nfs.h>
#include <trace/misc/sunrpc.h>
#include "export.h"
@@ -790,6 +789,20 @@ TRACE_EVENT(nfsd_stateowner_replay,
__entry->opnum, __entry->status)
);
#define show_nfs4_seq4_status(x) \
__print_flags(x, "|", \
{ SEQ4_STATUS_CB_PATH_DOWN, "CB_PATH_DOWN" }, \
{ SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING, "CB_GSS_CONTEXTS_EXPIRING" }, \
{ SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED, "CB_GSS_CONTEXTS_EXPIRED" }, \
{ SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED, "EXPIRED_ALL_STATE_REVOKED" }, \
{ SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED, "EXPIRED_SOME_STATE_REVOKED" }, \
{ SEQ4_STATUS_ADMIN_STATE_REVOKED, "ADMIN_STATE_REVOKED" }, \
{ SEQ4_STATUS_RECALLABLE_STATE_REVOKED, "RECALLABLE_STATE_REVOKED" }, \
{ SEQ4_STATUS_LEASE_MOVED, "LEASE_MOVED" }, \
{ SEQ4_STATUS_RESTART_RECLAIM_NEEDED, "RESTART_RECLAIM_NEEDED" }, \
{ SEQ4_STATUS_CB_PATH_DOWN_SESSION, "CB_PATH_DOWN_SESSION" }, \
{ SEQ4_STATUS_BACKCHANNEL_FAULT, "BACKCHANNEL_FAULT" })
TRACE_EVENT_CONDITION(nfsd_seq4_status,
TP_PROTO(
const struct svc_rqst *rqstp,
@@ -1694,6 +1707,14 @@ TRACE_EVENT(nfsd_cb_setup_err,
/* Not a real opcode, but there is no 0 operation. */
#define _CB_NULL 0
TRACE_DEFINE_ENUM(OP_CB_GETATTR);
TRACE_DEFINE_ENUM(OP_CB_RECALL);
TRACE_DEFINE_ENUM(OP_CB_LAYOUTRECALL);
TRACE_DEFINE_ENUM(OP_CB_RECALL_ANY);
TRACE_DEFINE_ENUM(OP_CB_NOTIFY);
TRACE_DEFINE_ENUM(OP_CB_NOTIFY_LOCK);
TRACE_DEFINE_ENUM(OP_CB_OFFLOAD);
#define show_nfsd_cb_opcode(val) \
__print_symbolic(val, \
{ _CB_NULL, "CB_NULL" }, \
@@ -1918,6 +1939,28 @@ TRACE_EVENT(nfsd_cb_offload,
__entry->fh_hash, __entry->count, __entry->status)
);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_RDATA_DLG);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_WDATA_DLG);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_DIR_DLG);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_FILE_LAYOUT);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_BLK_LAYOUT);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_OBJ_LAYOUT_MIN);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_OBJ_LAYOUT_MAX);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_OTHER_LAYOUT_MIN);
TRACE_DEFINE_ENUM(RCA4_TYPE_MASK_OTHER_LAYOUT_MAX);
#define show_rca_mask(x) \
__print_flags(x, "|", \
{ BIT(RCA4_TYPE_MASK_RDATA_DLG), "RDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_WDATA_DLG), "WDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_DIR_DLG), "DIR_DLG" }, \
{ BIT(RCA4_TYPE_MASK_FILE_LAYOUT), "FILE_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_BLK_LAYOUT), "BLK_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MIN), "OBJ_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MAX), "OBJ_LAYOUT_MAX" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MIN), "OTHER_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MAX), "OTHER_LAYOUT_MAX" })
TRACE_EVENT(nfsd_cb_recall_any,
TP_PROTO(
const struct nfsd4_cb_recall_any *ra
+272 -265
View File
@@ -34,15 +34,14 @@
#include <linux/sunrpc/xdr.h>
#include <linux/fileattr.h>
#include "xdr3.h"
#ifdef CONFIG_NFSD_V4
#include "acl.h"
#include "idmap.h"
#include "xdr4.h"
#endif /* CONFIG_NFSD_V4 */
#include "nfsd.h"
#include "nfserr.h"
#include "nfs4ctl.h"
#include "netns.h"
#include "stats.h"
#include "vfs.h"
@@ -63,7 +62,7 @@ u64 nfsd_io_cache_write __read_mostly = NFSD_IO_BUFFERED;
* it's an error we don't expect, log it once and return nfserr_io.
*/
__be32
nfserrno (int errno)
nfserrno(int errno)
{
static struct {
__be32 nfserr;
@@ -107,6 +106,8 @@ nfserrno (int errno)
{ nfserr_perm, -ENOKEY },
{ nfserr_no_grace, -ENOGRACE},
{ nfserr_io, -EBADMSG },
{ nfserr_symlink, -ELOOP },
{ nfserr_wrong_type, -EFTYPE },
};
int i;
@@ -118,15 +119,15 @@ nfserrno (int errno)
return nfserr_io;
}
/*
* Called from nfsd_lookup and encode_dirent. Check if we have crossed
/*
* Called from nfsd_lookup and encode_dirent. Check if we have crossed
* a mount point.
* Returns -EAGAIN or -ETIMEDOUT leaving *dpp and *expp unchanged,
* Returns an nfs error leaving *dpp and *expp unchanged,
* or nfs_ok having possibly changed *dpp and *expp
*/
int
nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp)
__be32
nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp)
{
struct svc_export *exp = *expp, *exp2 = NULL;
struct dentry *dentry = *dpp;
@@ -134,6 +135,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
.dentry = dget(dentry)};
unsigned int follow_flags = 0;
int err = 0;
__be32 nfserr = nfs_ok;
if (exp->ex_flags & NFSEXP_CROSSMOUNT)
follow_flags = LOOKUP_AUTOMOUNT;
@@ -163,23 +165,28 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
err = 0;
} else if (nfsd_v4client(rqstp) ||
(exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
/* successfully crossed mount point */
/*
* This is subtle: path.dentry is *not* on path.mnt
* at this point. The only reason we are safe is that
* original mnt is pinned down by exp, so we should
* put path *before* putting exp
*/
*dpp = path.dentry;
path.dentry = dentry;
*expp = exp2;
exp2 = exp;
nfserr = check_nfsd_access(exp, rqstp);
if (nfserr == nfs_ok) {
/* successfully crossed mount point */
/*
* This is subtle: path.dentry is *not* on path.mnt
* at this point. The only reason we are safe is that
* original mnt is pinned down by exp, so we should
* put path *before* putting exp
*/
*dpp = path.dentry;
path.dentry = dentry;
*expp = exp2;
exp2 = exp;
}
}
out:
path_put(&path);
if (exp2)
exp_put(exp2);
return err;
if (nfserr)
return nfserr;
return nfserrno(err);
}
static void follow_to_parent(struct path *path)
@@ -277,10 +284,12 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (IS_ERR(dentry))
goto out_nfserr;
if (nfsd_mountpoint(dentry, exp)) {
host_err = nfsd_cross_mnt(rqstp, &dentry, &exp);
if (host_err) {
__be32 nfserr = nfsd_cross_mnt(rqstp, &dentry, &exp);
if (nfserr) {
dput(dentry);
goto out_nfserr;
exp_put(exp);
return nfserr;
}
}
}
@@ -327,9 +336,6 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
if (err)
return err;
err = check_nfsd_access(exp, rqstp, false);
if (err)
goto out;
/*
* Note: we compose the file handle now, but as the
* dentry may be negative, it may need to be updated.
@@ -337,15 +343,26 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = fh_compose(resfh, exp, dentry, fhp);
if (!err && d_really_is_negative(dentry))
err = nfserr_noent;
out:
dput(dentry);
exp_put(exp);
return err;
}
static void
commit_reset_write_verifier(struct nfsd_net *nn, struct svc_rqst *rqstp,
int err)
/**
* nfsd_maybe_reset_write_verifier - Reset the write verifier after an I/O error
* @nn: nfsd namespace holding the write verifier
* @rqstp: RPC transaction context
* @err: errno reported by the failed operation
*
* A write verifier reset tells clients that unstable data the server has
* already acknowledged might have been lost. Client response is to resend
* in-flight dirty data.
*
* Context: Process context.
*/
void nfsd_maybe_reset_write_verifier(struct nfsd_net *nn,
struct svc_rqst *rqstp, int err)
{
switch (err) {
case -EAGAIN:
@@ -682,56 +699,67 @@ int nfsd4_is_junction(struct dentry *dentry)
return 1;
}
static struct nfsd4_compound_state *nfsd4_get_cstate(struct svc_rqst *rqstp)
/**
* nfsd_clone_file_range - Clone a range of one file into another
* @src: file the range is cloned from
* @src_pos: offset in @src where the source range begins
* @dst: file the range is cloned into
* @dst_pos: offset in @dst where the destination range begins
* @count: length of the range, or zero to clone through end-of-file
* @since: receives @dst's writeback error state, sampled before the clone
*
* A caller that has to place the cloned data on durable storage passes
* @since to nfsd_clone_sync_range() once this call succeeds. Sampling
* happens here because a writeback error raised by the clone's own
* dirty pages has to fall inside the sampled interval.
*
* Context: Process context.
* Return: zero on success, or a negative errno
*/
int nfsd_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
u64 dst_pos, u64 count, errseq_t *since)
{
return &((struct nfsd4_compoundres *)rqstp->rq_resp)->cstate;
loff_t cloned;
*since = READ_ONCE(dst->f_wb_err);
cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);
if (cloned < 0)
return cloned;
if (count && cloned != count)
return -EINVAL;
return 0;
}
__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp,
struct nfsd_file *nf_src, u64 src_pos,
struct nfsd_file *nf_dst, u64 dst_pos,
u64 count, bool sync)
/**
* nfsd_clone_sync_range - Commit a cloned range to durable storage
* @src: file the range was cloned from, whose metadata is committed too
* @dst: file the range was cloned into
* @dst_pos: offset in @dst where the cloned range begins
* @count: length of the range, or zero if the clone ran to end-of-file
* @since: @dst's writeback error state as sampled by
* nfsd_clone_file_range()
*
* Context: Process context.
* Return: zero on success, or a negative errno
*/
int nfsd_clone_sync_range(struct file *src, struct file *dst, u64 dst_pos,
u64 count, errseq_t since)
{
struct file *src = nf_src->nf_file;
struct file *dst = nf_dst->nf_file;
errseq_t since;
loff_t cloned;
__be32 ret = 0;
loff_t dst_end = count ? dst_pos + count - 1 : LLONG_MAX;
int status;
since = READ_ONCE(dst->f_wb_err);
cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);
if (cloned < 0) {
ret = nfserrno(cloned);
goto out_err;
status = vfs_fsync_range(dst, dst_pos, dst_end, 0);
if (!status)
status = filemap_check_wb_err(dst->f_mapping, since);
if (!status) {
/*
* A reflink marks extents shared in the source inode too,
* so the source's metadata has to reach durable storage
* even though its data is untouched.
*/
status = commit_inode_metadata(file_inode(src));
}
if (count && cloned != count) {
ret = nfserrno(-EINVAL);
goto out_err;
}
if (sync) {
loff_t dst_end = count ? dst_pos + count - 1 : LLONG_MAX;
int status = vfs_fsync_range(dst, dst_pos, dst_end, 0);
if (!status)
status = filemap_check_wb_err(dst->f_mapping, since);
if (!status)
status = commit_inode_metadata(file_inode(src));
if (status < 0) {
struct nfsd_net *nn = net_generic(nf_dst->nf_net,
nfsd_net_id);
trace_nfsd_clone_file_range_err(rqstp,
&nfsd4_get_cstate(rqstp)->save_fh,
src_pos,
&nfsd4_get_cstate(rqstp)->current_fh,
dst_pos,
count, status);
commit_reset_write_verifier(nn, rqstp, status);
ret = nfserrno(status);
}
}
out_err:
return ret;
return status;
}
ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
@@ -773,64 +801,21 @@ __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
#endif /* defined(CONFIG_NFSD_V4) */
/*
* Check server access rights to a file system object
/**
* nfsd_access - Check caller's access rights to a file system object
* @rqstp: RPC transaction context
* @fhp: target NFS filehandle
* @maps: tables mapping on-the-wire access bits to NFSD_MAY flags
* @access: requested access bits on entry, permitted bits on return
* @supported: optional output of the access bits the server supports
*
* Return: nfs_ok on success, otherwise an nfserr status code
*/
struct accessmap {
u32 access;
int how;
};
static struct accessmap nfs3_regaccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE },
#ifdef CONFIG_NFSD_V4
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
#endif
{ 0, 0 }
};
static struct accessmap nfs3_diraccess[] = {
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_LOOKUP, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC},
{ NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE },
{ NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE },
#ifdef CONFIG_NFSD_V4
{ NFS4_ACCESS_XAREAD, NFSD_MAY_READ },
{ NFS4_ACCESS_XAWRITE, NFSD_MAY_WRITE },
{ NFS4_ACCESS_XALIST, NFSD_MAY_READ },
#endif
{ 0, 0 }
};
static struct accessmap nfs3_anyaccess[] = {
/* Some clients - Solaris 2.6 at least, make an access call
* to the server to check for access for things like /dev/null
* (which really, the server doesn't care about). So
* We provide simple access checking for them, looking
* mainly at mode bits, and we make sure to ignore read-only
* filesystem checks
*/
{ NFS3_ACCESS_READ, NFSD_MAY_READ },
{ NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC },
{ NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS },
{ 0, 0 }
};
__be32
nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *supported)
__be32 nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
const struct nfsd_access_maps *maps,
u32 *access, u32 *supported)
{
struct accessmap *map;
const struct nfsd_access_map *map;
struct svc_export *export;
struct dentry *dentry;
u32 query, result = 0, sresult = 0;
@@ -844,12 +829,11 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
dentry = fhp->fh_dentry;
if (d_is_reg(dentry))
map = nfs3_regaccess;
map = maps->regular;
else if (d_is_dir(dentry))
map = nfs3_diraccess;
map = maps->directory;
else
map = nfs3_anyaccess;
map = maps->other;
query = *access;
for (; map->access; map++) {
@@ -859,7 +843,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
sresult |= map->access;
err2 = nfsd_permission(&rqstp->rq_cred, export,
dentry, map->how);
dentry, map->may);
switch (err2) {
case nfs_ok:
result |= map->access;
@@ -1415,7 +1399,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
* @offset: Byte offset of start
* @payload: xdr_buf containing the write payload
* @cnt: IN: number of bytes to write, OUT: number of bytes actually written
* @stable: An NFS stable_how value
* @iocb_flags: VFS IOCB_* flags expressing the requested write stability
* @verf: NFS WRITE verifier
*
* Upon return, caller must invoke fh_put on @fhp.
@@ -1427,7 +1411,7 @@ __be32
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset,
const struct xdr_buf *payload, unsigned long *cnt,
int stable, __be32 *verf)
int iocb_flags, __be32 *verf)
{
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct file *file = nf->nf_file;
@@ -1464,21 +1448,11 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
exp = fhp->fh_export;
if (!EX_ISSYNC(exp))
stable = NFS_UNSTABLE;
iocb_flags = 0;
init_sync_kiocb(&kiocb, file);
kiocb.ki_pos = offset;
if (likely(!fhp->fh_use_wgather)) {
switch (stable) {
case NFS_FILE_SYNC:
/* persist data and timestamps */
kiocb.ki_flags |= IOCB_DSYNC | IOCB_SYNC;
break;
case NFS_DATA_SYNC:
/* persist data only */
kiocb.ki_flags |= IOCB_DSYNC;
break;
}
}
if (likely(!fhp->fh_use_wgather))
kiocb.ki_flags |= iocb_flags;
nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload);
if (nvecs < 0) {
@@ -1508,21 +1482,21 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
break;
}
if (host_err < 0) {
commit_reset_write_verifier(nn, rqstp, host_err);
nfsd_maybe_reset_write_verifier(nn, rqstp, host_err);
goto out_nfserr;
}
nfsd_stats_io_write_add(nn, exp, *cnt);
fsnotify_modify(file);
host_err = filemap_check_wb_err(file->f_mapping, since);
if (host_err < 0) {
commit_reset_write_verifier(nn, rqstp, host_err);
nfsd_maybe_reset_write_verifier(nn, rqstp, host_err);
goto out_nfserr;
}
if (stable && fhp->fh_use_wgather) {
if (iocb_flags && fhp->fh_use_wgather) {
host_err = wait_for_concurrent_writes(file);
if (host_err < 0)
commit_reset_write_verifier(nn, rqstp, host_err);
nfsd_maybe_reset_write_verifier(nn, rqstp, host_err);
}
out_nfserr:
@@ -1610,7 +1584,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
* @offset: Byte offset of start
* @payload: xdr_buf containing the write payload
* @cnt: IN: number of bytes to write, OUT: number of bytes actually written
* @stable: An NFS stable_how value
* @iocb_flags: VFS IOCB_* flags expressing the requested write stability
* @verf: NFS WRITE verifier
*
* Upon return, caller must invoke fh_put on @fhp.
@@ -1620,8 +1594,8 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
*/
__be32
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
const struct xdr_buf *payload, unsigned long *cnt, int stable,
__be32 *verf)
const struct xdr_buf *payload, unsigned long *cnt,
int iocb_flags, __be32 *verf)
{
struct nfsd_file *nf;
__be32 err;
@@ -1633,7 +1607,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
goto out;
err = nfsd_vfs_write(rqstp, fhp, nf, offset, payload, cnt,
stable, verf);
iocb_flags, verf);
nfsd_file_put(nf);
out:
trace_nfsd_write_done(rqstp, fhp, offset, *cnt);
@@ -1698,14 +1672,14 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
err2 = filemap_check_wb_err(nf->nf_file->f_mapping,
since);
if (err2 < 0)
commit_reset_write_verifier(nn, rqstp, err2);
nfsd_maybe_reset_write_verifier(nn, rqstp, err2);
err = nfserrno(err2);
break;
case -EINVAL:
err = nfserr_notsupp;
break;
default:
commit_reset_write_verifier(nn, rqstp, err2);
nfsd_maybe_reset_write_verifier(nn, rqstp, err2);
err = nfserrno(err2);
}
} else
@@ -2368,33 +2342,18 @@ out:
*
* This is based heavily on the implementation of same in XFS.
*/
struct buffered_dirent {
u64 ino;
loff_t offset;
int namlen;
unsigned int d_type;
char name[];
};
struct readdir_data {
struct dir_context ctx;
char *dirent;
size_t used;
int full;
};
static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name,
int namlen, loff_t offset, u64 ino,
unsigned int d_type)
{
struct readdir_data *buf =
container_of(ctx, struct readdir_data, ctx);
struct buffered_dirent *de = (void *)(buf->dirent + buf->used);
struct nfsd_readdir_iter *iter =
container_of(ctx, struct nfsd_readdir_iter, ctx);
struct buffered_dirent *de = (void *)(iter->page + iter->used);
unsigned int reclen;
reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64));
if (buf->used + reclen > PAGE_SIZE) {
buf->full = 1;
if (iter->used + reclen > PAGE_SIZE) {
iter->full = 1;
return false;
}
@@ -2403,79 +2362,128 @@ static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name,
de->ino = ino;
de->d_type = d_type;
memcpy(de->name, name, namlen);
buf->used += reclen;
iter->used += reclen;
return true;
}
static __be32 nfsd_buffered_readdir(struct file *file, struct svc_fh *fhp,
nfsd_filldir_t func, struct readdir_cd *cdp,
loff_t *offsetp)
/**
* nfsd_readdir_open - open a directory for streaming readdir
* @rqstp: RPC transaction context
* @fhp: NFS file handle of directory to be read
* @offsetp: seek offset at which to resume reading
* @iter: OUT: directory reader to initialize
*
* On success the directory is open and positioned at @offsetp, and
* @iter is ready for nfsd_readdir_next(). The caller must release
* @iter with nfsd_readdir_close() once done, including on error paths
* that follow a successful open.
*
* Return: nfs_ok on success, otherwise an nfsstat code. On error @iter
* is left safe to pass to nfsd_readdir_close().
*/
__be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t *offsetp, struct nfsd_readdir_iter *iter)
{
struct buffered_dirent *de;
int host_err;
int size;
loff_t offset;
struct readdir_data buf = {
.ctx.actor = nfsd_buffered_filldir,
.dirent = kmalloc(PAGE_SIZE, GFP_KERNEL)
};
struct file *file;
loff_t offset = *offsetp;
__be32 err;
if (!buf.dirent)
return nfserrno(-ENOMEM);
memset(iter, 0, sizeof(*iter));
iter->ctx.actor = nfsd_buffered_filldir;
offset = *offsetp;
err = nfsd_open(rqstp, fhp, S_IFDIR, NFSD_MAY_READ, &file);
if (err)
return err;
while (1) {
unsigned int reclen;
if (fhp->fh_64bit_cookies)
file->f_mode |= FMODE_64BITHASH;
else
file->f_mode |= FMODE_32BITHASH;
cdp->err = nfserr_eof; /* will be cleared on successful read */
buf.used = 0;
buf.full = 0;
host_err = iterate_dir(file, &buf.ctx);
if (buf.full)
host_err = 0;
if (host_err < 0)
break;
size = buf.used;
if (!size)
break;
de = (struct buffered_dirent *)buf.dirent;
while (size > 0) {
offset = de->offset;
if (func(cdp, de->name, de->namlen, de->offset,
de->ino, de->d_type))
break;
if (cdp->err != nfs_ok)
break;
trace_nfsd_dirent(fhp, de->ino, de->name, de->namlen);
reclen = ALIGN(sizeof(*de) + de->namlen,
sizeof(u64));
size -= reclen;
de = (struct buffered_dirent *)((char *)de + reclen);
}
if (size > 0) /* We bailed out early */
break;
offset = vfs_llseek(file, 0, SEEK_CUR);
offset = vfs_llseek(file, offset, SEEK_SET);
if (offset < 0) {
err = nfserrno((int)offset);
goto out_close;
}
kfree((buf.dirent));
iter->page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!iter->page) {
err = nfserrno(-ENOMEM);
goto out_close;
}
if (host_err)
return nfserrno(host_err);
iter->file = file;
iter->fhp = fhp;
iter->offset = offset;
return nfs_ok;
*offsetp = offset;
return cdp->err;
out_close:
nfsd_filp_close(file);
return err;
}
/**
* nfsd_readdir_next - yield the next buffered directory entry
* @iter: directory reader initialized by nfsd_readdir_open()
*
* Refill from the filesystem as needed and return the next entry. The
* returned pointer is valid until the following nfsd_readdir_next() or
* nfsd_readdir_close() call.
*
* Return: the next entry, or NULL at end of directory or on a host
* error. On NULL, @iter->eof marks a clean end and @iter->host_err a
* filesystem error; @iter->offset holds the resume cookie either way.
*/
struct buffered_dirent *nfsd_readdir_next(struct nfsd_readdir_iter *iter)
{
struct buffered_dirent *de;
unsigned int reclen;
while (iter->remaining <= 0) {
/* Between batches the resume cookie advances past the last. */
if (iter->batched)
iter->offset = vfs_llseek(iter->file, 0, SEEK_CUR);
iter->used = 0;
iter->full = 0;
iter->host_err = iterate_dir(iter->file, &iter->ctx);
if (iter->full)
iter->host_err = 0;
if (iter->host_err < 0)
return NULL;
if (!iter->used) {
iter->eof = true;
return NULL;
}
iter->batched = true;
iter->pos = iter->page;
iter->remaining = iter->used;
}
de = (struct buffered_dirent *)iter->pos;
iter->offset = de->offset;
reclen = ALIGN(sizeof(*de) + de->namlen, sizeof(u64));
iter->pos += reclen;
iter->remaining -= reclen;
return de;
}
/**
* nfsd_readdir_close - release a directory reader
* @iter: directory reader to release
*
* Safe to call on an @iter that nfsd_readdir_open() left initialized,
* whether the open succeeded or failed, and safe to call more than once.
*/
void nfsd_readdir_close(struct nfsd_readdir_iter *iter)
{
if (iter->file) {
nfsd_filp_close(iter->file);
iter->file = NULL;
}
kfree(iter->page);
iter->page = NULL;
}
/**
@@ -2497,37 +2505,36 @@ static __be32 nfsd_buffered_readdir(struct file *file, struct svc_fh *fhp,
* returned.
*/
__be32
nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
struct readdir_cd *cdp, nfsd_filldir_t func)
{
__be32 err;
struct file *file;
loff_t offset = *offsetp;
int may_flags = NFSD_MAY_READ;
struct nfsd_readdir_iter iter;
struct buffered_dirent *de;
__be32 err;
err = nfsd_open(rqstp, fhp, S_IFDIR, may_flags, &file);
err = nfsd_readdir_open(rqstp, fhp, offsetp, &iter);
if (err)
goto out;
return err;
if (fhp->fh_64bit_cookies)
file->f_mode |= FMODE_64BITHASH;
else
file->f_mode |= FMODE_32BITHASH;
offset = vfs_llseek(file, offset, SEEK_SET);
if (offset < 0) {
err = nfserrno((int)offset);
goto out_close;
while ((de = nfsd_readdir_next(&iter)) != NULL) {
if (func(cdp, de->name, de->namlen, de->offset, de->ino,
de->d_type))
break;
if (cdp->err != nfs_ok)
break;
trace_nfsd_dirent(fhp, de->ino, de->name, de->namlen);
}
if (!de)
cdp->err = nfserr_eof; /* clean end, or reported in host_err */
*offsetp = iter.offset;
err = nfsd_buffered_readdir(file, fhp, func, cdp, offsetp);
nfsd_readdir_close(&iter);
if (err == nfserr_eof || err == nfserr_toosmall)
err = nfs_ok; /* can still be found in ->err */
out_close:
nfsd_filp_close(file);
out:
return err;
if (iter.host_err)
return nfserrno(iter.host_err);
if (cdp->err == nfserr_eof || cdp->err == nfserr_toosmall)
return nfs_ok; /* can still be found in ->err */
return cdp->err;
}
/**
+70 -9
View File
@@ -37,6 +37,17 @@
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
struct nfsd_access_map {
u32 access;
int may;
};
struct nfsd_access_maps {
const struct nfsd_access_map *regular;
const struct nfsd_access_map *directory;
const struct nfsd_access_map *other;
};
struct nfsd_file;
/*
@@ -48,6 +59,41 @@ struct readdir_cd {
__be32 err; /* nfs_ok, nfserr, or nfserr_eof */
};
/*
* One directory entry buffered out of the underlying filesystem.
* Buffering decouples the ->iterate_shared() call from the consumer,
* which may call ->lookup() and so must not run inside iterate_dir().
*/
struct buffered_dirent {
u64 ino;
loff_t offset;
int namlen;
unsigned int d_type;
char name[];
};
/*
* Streaming directory reader. nfsd_readdir_open() opens the directory
* and seeks to the start cookie, nfsd_readdir_next() yields one entry
* at a time (refilling from the filesystem as needed), and
* nfsd_readdir_close() releases it. The reader holds the open file, so
* a consumer can pull entries while a reply is being encoded.
*/
struct nfsd_readdir_iter {
struct dir_context ctx; /* drives iterate_dir() */
struct file *file; /* open directory */
struct svc_fh *fhp; /* directory file handle */
char *page; /* one batch of raw entries */
size_t used; /* bytes filled this batch */
int full; /* batch buffer filled up */
char *pos; /* next entry to yield */
int remaining; /* bytes left from @pos */
loff_t offset; /* resume cookie */
int host_err; /* set once iterate_dir() fails */
bool batched; /* a batch has been read */
bool eof; /* directory exhausted */
};
/* nfsd/vfs.c */
struct nfsd_attrs {
struct iattr *na_iattr; /* input */
@@ -75,9 +121,14 @@ static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
attrs->na_pacl || attrs->na_dpacl);
}
struct nfsd_net;
__be32 nfserrno (int errno);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp);
void nfsd_maybe_reset_write_verifier(struct nfsd_net *nn,
struct svc_rqst *rqstp,
int err);
__be32 nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp);
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int, struct svc_fh *);
__be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
@@ -89,10 +140,11 @@ int nfsd_mountpoint(struct dentry *, struct svc_export *);
#ifdef CONFIG_NFSD_V4
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
struct file *, loff_t, loff_t, int);
__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp,
struct nfsd_file *nf_src, u64 src_pos,
struct nfsd_file *nf_dst, u64 dst_pos,
u64 count, bool sync);
int nfsd_clone_file_range(struct file *src, u64 src_pos,
struct file *dst, u64 dst_pos,
u64 count, errseq_t *since);
int nfsd_clone_sync_range(struct file *src, struct file *dst,
u64 dst_pos, u64 count, errseq_t since);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
struct nfsd_attrs *attrs, int type, dev_t rdev,
@@ -100,7 +152,9 @@ __be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct nfsd_attrs *attrs,
int type, dev_t rdev, struct svc_fh *res);
__be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
__be32 nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
const struct nfsd_access_maps *maps,
u32 *access, u32 *supported);
__be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct svc_fh *resfhp, struct nfsd_attrs *iap);
__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp,
@@ -135,11 +189,13 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
u32 *eof);
__be32 nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, const struct xdr_buf *payload,
unsigned long *cnt, int stable, __be32 *verf);
unsigned long *cnt, int iocb_flags,
__be32 *verf);
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset,
const struct xdr_buf *payload,
unsigned long *cnt, int stable, __be32 *verf);
unsigned long *cnt, int iocb_flags,
__be32 *verf);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
@@ -157,6 +213,11 @@ __be32 nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
char *name, int len);
__be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *,
loff_t *, struct readdir_cd *, nfsd_filldir_t);
__be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t *offsetp,
struct nfsd_readdir_iter *iter);
struct buffered_dirent *nfsd_readdir_next(struct nfsd_readdir_iter *iter);
void nfsd_readdir_close(struct nfsd_readdir_iter *iter);
__be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *,
struct kstatfs *, int access);
int nfsd_get_case_info(struct dentry *dentry,
+26 -147
View File
@@ -1,173 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* XDR types for nfsd. This is mainly a typing exercise. */
#ifndef LINUX_NFSD_H
#define LINUX_NFSD_H
#ifndef _LINUX_NFSD_XDR_H
#define _LINUX_NFSD_XDR_H
#include <linux/vfs.h>
#include "nfsd.h"
#include "nfsfh.h"
#include "vfs.h"
struct nfsd_fhandle {
struct svc_fh fh;
};
struct nfsd_sattrargs {
struct svc_fh fh;
struct iattr attrs;
};
struct nfsd_diropargs {
struct svc_fh fh;
char * name;
unsigned int len;
};
struct nfsd_readargs {
struct svc_fh fh;
__u32 offset;
__u32 count;
};
struct nfsd_writeargs {
svc_fh fh;
__u32 offset;
__u32 len;
struct xdr_buf payload;
};
struct nfsd_createargs {
struct svc_fh fh;
char * name;
unsigned int len;
struct iattr attrs;
};
struct nfsd_renameargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd_linkargs {
struct svc_fh ffh;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd_symlinkargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
char * tname;
unsigned int tlen;
struct iattr attrs;
struct kvec first;
};
struct nfsd_readdirargs {
struct svc_fh fh;
__u32 cookie;
__u32 count;
};
struct nfsd_stat {
__be32 status;
};
struct nfsd_attrstat {
__be32 status;
struct svc_fh fh;
struct kstat stat;
};
struct nfsd_diropres {
__be32 status;
struct svc_fh fh;
struct kstat stat;
};
struct nfsd_readlinkres {
__be32 status;
int len;
struct page *page;
};
struct nfsd_readres {
__be32 status;
struct svc_fh fh;
unsigned long count;
struct kstat stat;
struct page **pages;
};
struct nfsd_readdirres {
/* Components of the reply */
__be32 status;
int count;
/* Used to encode the reply's entry list */
struct xdr_stream xdr;
struct xdr_buf dirlist;
struct readdir_cd common;
unsigned int cookie_offset;
};
struct nfsd_statfsres {
__be32 status;
struct kstatfs stats;
};
/*
* Storage requirements for XDR arguments and results.
* Declared here rather than in nfsproc.c with the other proc wrappers
* because READDIR's encode hooks in nfsxdr.c reference it. The xdrgen
* field must be first so the struct can be cast to its XDR type for the
* RPC dispatch layer.
*
* The file handle lives here rather than in an argument wrapper: the
* RPC layer zeroes the result before decode, so ->pc_release can run on
* a request that failed to decode.
*/
union nfsd_xdrstore {
struct nfsd_sattrargs sattr;
struct nfsd_diropargs dirop;
struct nfsd_readargs read;
struct nfsd_writeargs write;
struct nfsd_createargs create;
struct nfsd_renameargs rename;
struct nfsd_linkargs link;
struct nfsd_symlinkargs symlink;
struct nfsd_readdirargs readdir;
struct readdirres_wrapper {
struct readdirres xdrgen;
struct svc_fh fh;
/* Streaming state for encoding the reply's entry list */
struct nfsd_readdir_iter iter; /* directory reader */
u32 count; /* client's reply size hint */
u32 space_left; /* remaining entry budget */
unsigned int cookie_offset; /* prev entry's cookie slot */
};
#define NFS2_SVC_XDRSIZE sizeof(union nfsd_xdrstore)
static_assert(offsetof(struct readdirres_wrapper, xdrgen) == 0);
bool nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset);
int nfssvc_encode_entry(void *data, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type);
void nfssvc_encode_nfscookie(struct xdr_stream *xdr, unsigned int pos,
u32 cookie);
void nfssvc_release_readdirres(struct svc_rqst *rqstp);
void nfssvc_release_attrstat(struct svc_rqst *rqstp);
void nfssvc_release_diropres(struct svc_rqst *rqstp);
void nfssvc_release_readres(struct svc_rqst *rqstp);
/* Helper functions for NFSv2 ACL code */
bool svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp);
@@ -175,4 +54,4 @@ bool svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status);
bool svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
const struct svc_fh *fhp, const struct kstat *stat);
#endif /* LINUX_NFSD_H */
#endif /* _LINUX_NFSD_XDR_H */
+3 -2
View File
@@ -8,7 +8,8 @@
#ifndef _LINUX_NFSD_XDR3_H
#define _LINUX_NFSD_XDR3_H
#include "xdr.h"
#include <linux/vfs.h>
#include "vfs.h"
struct nfsd3_sattrargs {
@@ -39,7 +40,7 @@ struct nfsd3_writeargs {
svc_fh fh;
__u64 offset;
__u32 count;
int stable;
__u32 stable;
__u32 len;
struct xdr_buf payload;
};
+17 -139
View File
@@ -37,6 +37,8 @@
#ifndef _LINUX_NFSD_XDR4_H
#define _LINUX_NFSD_XDR4_H
#include <linux/nfs_fh.h>
#include "state.h"
#include "vfs.h"
@@ -50,134 +52,6 @@
#define HAS_CSTATE_FLAG(c, f) ((c)->sid_flags & (f))
#define CLEAR_CSTATE_FLAG(c, f) ((c)->sid_flags &= ~(f))
/**
* nfsd4_encode_bool - Encode an XDR bool type result
* @xdr: target XDR stream
* @val: boolean value to encode
*
* Return values:
* %nfs_ok: @val encoded; @xdr advanced to next position
* %nfserr_resource: stream buffer space exhausted
*/
static __always_inline __be32
nfsd4_encode_bool(struct xdr_stream *xdr, bool val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(p == NULL))
return nfserr_resource;
*p = val ? xdr_one : xdr_zero;
return nfs_ok;
}
/**
* nfsd4_encode_uint32_t - Encode an XDR uint32_t type result
* @xdr: target XDR stream
* @val: integer value to encode
*
* Return values:
* %nfs_ok: @val encoded; @xdr advanced to next position
* %nfserr_resource: stream buffer space exhausted
*/
static __always_inline __be32
nfsd4_encode_uint32_t(struct xdr_stream *xdr, u32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(p == NULL))
return nfserr_resource;
*p = cpu_to_be32(val);
return nfs_ok;
}
#define nfsd4_encode_aceflag4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_acemask4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_acetype4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_count4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_mode4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_nfs_lease4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_qop4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_sequenceid4(x, v) nfsd4_encode_uint32_t(x, v)
#define nfsd4_encode_slotid4(x, v) nfsd4_encode_uint32_t(x, v)
/**
* nfsd4_encode_uint64_t - Encode an XDR uint64_t type result
* @xdr: target XDR stream
* @val: integer value to encode
*
* Return values:
* %nfs_ok: @val encoded; @xdr advanced to next position
* %nfserr_resource: stream buffer space exhausted
*/
static __always_inline __be32
nfsd4_encode_uint64_t(struct xdr_stream *xdr, u64 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
if (unlikely(p == NULL))
return nfserr_resource;
put_unaligned_be64(val, p);
return nfs_ok;
}
#define nfsd4_encode_changeid4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_nfs_cookie4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_length4(x, v) nfsd4_encode_uint64_t(x, v)
#define nfsd4_encode_offset4(x, v) nfsd4_encode_uint64_t(x, v)
/**
* nfsd4_encode_opaque_fixed - Encode a fixed-length XDR opaque type result
* @xdr: target XDR stream
* @data: pointer to data
* @size: length of data in bytes
*
* Return values:
* %nfs_ok: @data encoded; @xdr advanced to next position
* %nfserr_resource: stream buffer space exhausted
*/
static __always_inline __be32
nfsd4_encode_opaque_fixed(struct xdr_stream *xdr, const void *data,
size_t size)
{
__be32 *p = xdr_reserve_space(xdr, xdr_align_size(size));
size_t pad = xdr_pad_size(size);
if (unlikely(p == NULL))
return nfserr_resource;
memcpy(p, data, size);
if (pad)
memset((char *)p + size, 0, pad);
return nfs_ok;
}
/**
* nfsd4_encode_opaque - Encode a variable-length XDR opaque type result
* @xdr: target XDR stream
* @data: pointer to data
* @size: length of data in bytes
*
* Return values:
* %nfs_ok: @data encoded; @xdr advanced to next position
* %nfserr_resource: stream buffer space exhausted
*/
static __always_inline __be32
nfsd4_encode_opaque(struct xdr_stream *xdr, const void *data, size_t size)
{
size_t pad = xdr_pad_size(size);
__be32 *p;
p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(size));
if (unlikely(p == NULL))
return nfserr_resource;
*p++ = cpu_to_be32(size);
memcpy(p, data, size);
if (pad)
memset((char *)p + size, 0, pad);
return nfs_ok;
}
#define nfsd4_encode_component4(x, d, s) nfsd4_encode_opaque(x, d, s)
struct nfsd4_compound_state {
struct svc_fh current_fh;
struct svc_fh save_fh;
@@ -188,6 +62,7 @@ struct nfsd4_compound_state {
struct nfsd4_slot *slot;
int data_offset;
bool spo_must_allowed;
bool slot_owned;
size_t iovlen;
u32 minorversion;
__be32 status;
@@ -642,17 +517,6 @@ svcxdr_decode_deviceid4(__be32 *p, struct nfsd4_deviceid *devid)
return p;
}
static inline __be32
nfsd4_decode_deviceid4(struct xdr_stream *xdr, struct nfsd4_deviceid *devid)
{
__be32 *p = xdr_inline_decode(xdr, NFS4_DEVICEID4_SIZE);
if (unlikely(!p))
return nfserr_bad_xdr;
svcxdr_decode_deviceid4(p, devid);
return nfs_ok;
}
struct nfsd4_layout_seg {
u32 iomode;
u64 offset;
@@ -736,6 +600,19 @@ struct nfsd4_cb_offload {
u32 co_referring_seqno;
};
struct nfsd4_ssc_umount_item {
struct list_head nsui_list;
bool nsui_busy;
/*
* nsui_refcnt inited to 2, 1 on list and 1 for consumer. Entry
* is removed when refcnt drops to 1 and nsui_expire expires.
*/
refcount_t nsui_refcnt;
unsigned long nsui_expire;
struct vfsmount *nsui_vfsmount;
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
};
struct nfsd4_copy {
/* request */
stateid_t cp_src_stateid;
@@ -924,6 +801,7 @@ bool nfsd4_cache_this_op(struct nfsd4_op *);
*/
struct svcxdr_tmpbuf {
struct svcxdr_tmpbuf *next;
void (*release)(void *buf);
char buf[];
};
+11 -9
View File
@@ -6,29 +6,30 @@
#define cb_compound_enc_hdr_sz 4
#define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2))
#define sessionid_sz (NFS4_MAX_SESSIONID_LEN >> 2)
#define op_enc_sz 1
#define enc_referring_call4_sz (1 + 1)
#define enc_referring_call_list4_sz (sessionid_sz + 1 + \
enc_referring_call4_sz)
#define cb_sequence_enc_sz (sessionid_sz + 4 + \
enc_referring_call_list4_sz)
#define cb_sequence_enc_sz (op_enc_sz + sessionid_sz + 4 + \
1 + enc_referring_call_list4_sz)
#define cb_sequence_dec_sz (op_dec_sz + sessionid_sz + 4)
#define op_enc_sz 1
#define op_dec_sz 2
#define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2)
#define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + enc_stateid_sz + \
enc_nfs4_fh_sz)
op_enc_sz + enc_stateid_sz + \
1 + enc_nfs4_fh_sz)
#define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)
#define NFS4_enc_cb_layout_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + 3 + \
enc_nfs4_fh_sz + 4)
op_enc_sz + 3 + 1 + \
enc_nfs4_fh_sz + 4 + \
enc_stateid_sz)
#define NFS4_dec_cb_layout_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)
@@ -47,7 +48,7 @@
#define NFS4_enc_cb_notify_lock_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
2 + 1 + \
op_enc_sz + 2 + 1 + \
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
enc_nfs4_fh_sz)
#define NFS4_dec_cb_notify_lock_sz (cb_compound_dec_hdr_sz + \
@@ -57,6 +58,7 @@
XDR_QUADLEN(NFS4_VERIFIER_SIZE))
#define NFS4_enc_cb_offload_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
op_enc_sz + \
enc_nfs4_fh_sz + \
enc_stateid_sz + \
enc_cb_offload_info_sz)
@@ -65,7 +67,7 @@
op_dec_sz)
#define NFS4_enc_cb_recall_any_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + 1 + 1)
op_enc_sz + 1 + 1 + 1)
#define NFS4_dec_cb_recall_any_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)
+2 -53
View File
@@ -11,59 +11,8 @@
#include <linux/cred.h>
#include <linux/sunrpc/auth.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/string.h>
#include <linux/crc32.h>
#include <linux/nfs_fh.h>
#include <uapi/linux/nfs.h>
/* The LOCALIO program is entirely private to Linux and is
* NOT part of the uapi.
*/
#define NFS_LOCALIO_PROGRAM 400122
#define LOCALIOPROC_NULL 0
#define LOCALIOPROC_UUID_IS_LOCAL 1
/*
* This is the kernel NFS client file handle representation
*/
#define NFS_MAXFHSIZE 128
struct nfs_fh {
unsigned short size;
unsigned char data[NFS_MAXFHSIZE];
};
/*
* Returns a zero iff the size and data fields match.
* Checks only "size" bytes in the data field.
*/
static inline int nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
{
return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
}
static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
{
target->size = source->size;
memcpy(target->data, source->data, source->size);
}
enum nfs3_stable_how {
NFS_UNSTABLE = 0,
NFS_DATA_SYNC = 1,
NFS_FILE_SYNC = 2,
/* used by direct.c to mark verf as invalid */
NFS_INVALID_STABLE_HOW = -1
};
/**
* nfs_fhandle_hash - calculate the crc32 hash for the filehandle
* @fh - pointer to filehandle
*
* returns a crc32 hash for the filehandle that is compatible with
* the one displayed by "wireshark".
*/
static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return ~crc32_le(0xFFFFFFFF, &fh->data[0], fh->size);
}
#endif /* _LINUX_NFS_H */
+43
View File
@@ -7,6 +7,49 @@
#include <uapi/linux/nfs3.h>
/*
* NFSv3 error status values.
* See RFC 1813 Section 2.5
*/
enum {
NFS3ERR_PERM = 1,
NFS3ERR_NOENT = 2,
NFS3ERR_IO = 5,
NFS3ERR_NXIO = 6,
NFS3ERR_ACCES = 13,
NFS3ERR_EXIST = 17,
NFS3ERR_XDEV = 18,
NFS3ERR_NODEV = 19,
NFS3ERR_NOTDIR = 20,
NFS3ERR_ISDIR = 21,
NFS3ERR_INVAL = 22,
NFS3ERR_FBIG = 27,
NFS3ERR_NOSPC = 28,
NFS3ERR_ROFS = 30,
NFS3ERR_MLINK = 31,
NFS3ERR_NAMETOOLONG = 63,
NFS3ERR_NOTEMPTY = 66,
NFS3ERR_DQUOT = 69,
NFS3ERR_STALE = 70,
NFS3ERR_REMOTE = 71,
NFS3ERR_BADHANDLE = 10001,
NFS3ERR_NOT_SYNC = 10002,
NFS3ERR_BAD_COOKIE = 10003,
NFS3ERR_NOTSUPP = 10004,
NFS3ERR_TOOSMALL = 10005,
NFS3ERR_SERVERFAULT = 10006,
NFS3ERR_BADTYPE = 10007,
NFS3ERR_JUKEBOX = 10008,
};
enum nfs3_stable_how {
NFS_UNSTABLE = 0,
NFS_DATA_SYNC = 1,
NFS_FILE_SYNC = 2,
/* used to mark verf as invalid */
NFS_INVALID_STABLE_HOW = -1
};
/* Number of 32bit words in post_op_attr */
#define NFS3_POST_OP_ATTR_WORDS 22
+6
View File
@@ -263,6 +263,12 @@ enum why_no_delegation4 { /* new to v4.1 */
WND4_IS_DIR = 8,
};
enum stable_how4 {
UNSTABLE4 = 0,
DATA_SYNC4 = 1,
FILE_SYNC4 = 2,
};
enum lock_type4 {
NFS4_UNLOCK_LT = 0,
NFS4_READ_LT = 1,
+2 -2
View File
@@ -6,11 +6,11 @@
#define _LINUX_NFS_COMMON_H
#include <linux/errno.h>
#include <uapi/linux/nfs.h>
#include <linux/types.h>
/* Mapping from NFS error code to "errno" error code. */
int nfs_stat_to_errno(enum nfs_stat status);
int nfs_stat_to_errno(int status);
int nfs4_stat_to_errno(int stat);
__u32 nfs_localio_errno_to_nfs4_stat(int errno);
+63
View File
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* struct nfs_fh is an NFS version-agnostic data structure that
* stores an NFS file handle. It is also commonly used in NFS
* related APIs.
*/
#ifndef _LINUX_NFS_FH_H
#define _LINUX_NFS_FH_H
#include <linux/types.h>
#include <linux/string.h>
#include <linux/crc32.h>
/*
* The largest file handle size today is an NFSv4 file handle,
* which can be up to 128 octets long.
*/
#define NFS_MAXFHSIZE 128
struct nfs_fh {
unsigned short size;
unsigned char data[NFS_MAXFHSIZE];
};
/**
* nfs_compare_fh - Compare two NFS file handles
* @a: An NFS file handle to be compared
* @b: An NFS file handle to be compared
*
* Checks only "size" bytes in each data field.
*
* Return: %false if the two file handles are equal, otherwise %true
*/
static inline bool nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
{
return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
}
/**
* nfs_copy_fh - Copy an NFS file handle
* @target: Destination file handle
* @source: Source file handle
*
* Copies source->size bytes of file handle data into target.
*/
static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
{
target->size = source->size;
memcpy(target->data, source->data, source->size);
}
/**
* nfs_fhandle_hash - Calculate the crc32 hash for the filehandle
* @fh: An NFS file handle to hash
*
* Return: a crc32 hash for the filehandle that is compatible with
* the one displayed by "wireshark"
*/
static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return ~crc32_le(0xFFFFFFFF, &fh->data[0], fh->size);
}
#endif /* _LINUX_NFS_FH_H */
+11 -58
View File
@@ -2,80 +2,33 @@
/*
* include/linux/nfs_ssc.h
*
* NFSv4.2 server-to-server copy, NFS client side APIs
*
* Author: Dai Ngo <dai.ngo@oracle.com>
*
* Copyright (c) 2020, Oracle and/or its affiliates.
*/
#include <linux/nfs_fs.h>
#include <linux/sunrpc/svc.h>
#ifndef _LINUX_NFS_SSC_H
#define _LINUX_NFS_SSC_H
extern struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl;
#include <linux/nfs_fh.h>
#include <linux/nfs4.h>
struct file;
struct vfsmount;
/*
* NFS_V4
*/
struct nfs4_ssc_client_ops {
struct module *owner;
struct file *(*sco_open)(struct vfsmount *ss_mnt,
struct nfs_fh *src_fh, nfs4_stateid *stateid);
void (*sco_close)(struct file *filep);
};
/*
* NFS_FS
*/
struct nfs_ssc_client_ops {
void (*sco_sb_deactive)(struct super_block *sb);
};
struct nfs_ssc_client_ops_tbl {
const struct nfs4_ssc_client_ops *ssc_nfs4_ops;
const struct nfs_ssc_client_ops *ssc_nfs_ops;
};
extern void nfs42_ssc_register_ops(void);
extern void nfs42_ssc_unregister_ops(void);
extern void nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops);
extern void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
static inline struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
struct nfs_fh *src_fh, nfs4_stateid *stateid)
{
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
return (*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_open)(ss_mnt, src_fh, stateid);
return ERR_PTR(-EIO);
}
static inline void nfs42_ssc_close(struct file *filep)
{
if (nfs_ssc_client_tbl.ssc_nfs4_ops)
(*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep);
}
#endif
struct nfsd4_ssc_umount_item {
struct list_head nsui_list;
bool nsui_busy;
/*
* nsui_refcnt inited to 2, 1 on list and 1 for consumer. Entry
* is removed when refcnt drops to 1 and nsui_expire expires.
*/
refcount_t nsui_refcnt;
unsigned long nsui_expire;
struct vfsmount *nsui_vfsmount;
char nsui_ipaddr[RPC_MAX_ADDRBUFLEN + 1];
};
/*
* NFS_FS
*/
extern void nfs_ssc_register(const struct nfs_ssc_client_ops *ops);
extern void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops);
static inline void nfs_do_sb_deactive(struct super_block *sb)
{
if (nfs_ssc_client_tbl.ssc_nfs_ops)
(*nfs_ssc_client_tbl.ssc_nfs_ops->sco_sb_deactive)(sb);
}
#endif /* _LINUX_NFS_SSC_H */
+38
View File
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/nfsd_ssc.h
*
* NFSv4.2 server-to-server copy, NFS server side APIs
*
* Author: Dai Ngo <dai.ngo@oracle.com>
*
* Copyright (c) 2020, Oracle and/or its affiliates.
*/
#ifndef _LINUX_NFSD_SSC_H
#define _LINUX_NFSD_SSC_H
#include <linux/nfs_fh.h>
#include <linux/nfs4.h>
struct file;
struct vfsmount;
#if IS_ENABLED(CONFIG_NFS_V4_2_SSC_HELPER)
struct file *nfsd42_ssc_open(struct vfsmount *ss_mnt, struct nfs_fh *src_fh,
nfs4_stateid *stateid);
void nfsd42_ssc_close(struct file *filp);
#else
static inline struct file *nfsd42_ssc_open(struct vfsmount *ss_mnt,
struct nfs_fh *src_fh,
nfs4_stateid *stateid)
{
return ERR_PTR(-EIO);
}
static inline void nfsd42_ssc_close(struct file *filp)
{
}
#endif
#endif /* _LINUX_NFSD_SSC_H */
+10 -1
View File
@@ -13,9 +13,18 @@
#include <linux/uuid.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/nfs.h>
#include <linux/nfs_fh.h>
#include <net/net_namespace.h>
/*
* The LOCALIO program is entirely private to Linux and is NOT part of
* the uapi.
*/
#define NFS_LOCALIO_PROGRAM 400122
#define LOCALIOPROC_NULL 0
#define LOCALIOPROC_UUID_IS_LOCAL 1
struct nfs_client;
struct nfs_file_localio;
+2 -1
View File
@@ -199,7 +199,8 @@ struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
int rpcb_create_local(struct net *);
void rpcb_put_local(struct net *);
int rpcb_register(struct net *, u32, u32, int, unsigned short);
int rpcb_register(struct net *net, u32 prog, u32 vers, int prot,
unsigned short port);
int rpcb_v4_register(struct net *net, const u32 program,
const u32 version,
const struct sockaddr *address,
+93 -2
View File
@@ -78,6 +78,7 @@ struct svc_serv {
unsigned int sv_max_payload; /* datagram payload size */
unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */
unsigned int sv_xdrsize; /* XDR buffer size */
atomic_t sv_rpcb_failures; /* unanswered rpcbind calls */
struct list_head sv_permsocks; /* all permanent sockets */
struct list_head sv_tempsocks; /* all temporary sockets */
int sv_tmpcnt; /* count of temporary "valid" sockets */
@@ -86,6 +87,8 @@ struct svc_serv {
char * sv_name; /* service name */
bool sv_is_pooled; /* is this a pooled service? */
/* Caller registers with rpcbind itself. Set before svc_bind(). */
bool sv_no_rpcbind;
struct svc_pool * sv_pools; /* array of thread pools */
int (*sv_threadfn)(void *data);
@@ -451,6 +454,7 @@ int sunrpc_set_pool_mode(const char *val);
int sunrpc_get_pool_mode(char *val, size_t size);
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
int svc_bind(struct svc_serv *serv, struct net *net);
unsigned int svc_rpcb_failure_count(struct svc_serv *serv);
struct svc_serv *svc_create(struct svc_program *, unsigned int,
int (*threadfn)(void *data));
bool svc_rqst_replace_page(struct svc_rqst *rqstp,
@@ -471,8 +475,9 @@ unsigned int svc_serv_maxthreads(const struct svc_serv *serv);
int svc_pool_stats_open(struct svc_info *si, struct file *file);
void svc_process(struct svc_rqst *rqstp);
void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp);
int svc_register(const struct svc_serv *, struct net *, const int,
const unsigned short, const unsigned short);
int svc_register(struct svc_serv *serv, struct net *net,
const int family, const unsigned short proto,
const unsigned short port);
void svc_wake_up(struct svc_serv *);
void svc_reserve(struct svc_rqst *rqstp, int space);
@@ -529,6 +534,8 @@ static inline void svc_reserve_auth(struct svc_rqst *rqstp, int space)
* svcxdr_init_decode - Prepare an xdr_stream for Call decoding
* @rqstp: controlling server RPC transaction context
*
* The stream records @rqstp, so a codec running on it reaches the
* transaction with svcxdr_rqst().
*/
static inline void svcxdr_init_decode(struct svc_rqst *rqstp)
{
@@ -541,12 +548,15 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp)
xdr_init_decode(xdr, buf, argv->iov_base, NULL);
xdr_set_scratch_folio(xdr, rqstp->rq_scratch_folio);
xdr->xdrgen_ctx = rqstp;
}
/**
* svcxdr_init_encode - Prepare an xdr_stream for svc Reply encoding
* @rqstp: controlling server RPC transaction context
*
* The stream records @rqstp, so a codec running on it reaches the
* transaction with svcxdr_rqst().
*/
static inline void svcxdr_init_encode(struct svc_rqst *rqstp)
{
@@ -564,6 +574,19 @@ static inline void svcxdr_init_encode(struct svc_rqst *rqstp)
xdr->page_ptr = buf->pages - 1;
buf->buflen = PAGE_SIZE * (rqstp->rq_page_end - buf->pages);
xdr->rqst = NULL;
xdr->xdrgen_ctx = rqstp;
}
/**
* svcxdr_rqst - Retrieve the transaction bound to an xdr_stream
* @xdr: stream to query
*
* Return: the controlling svc_rqst when @xdr was initialized by
* svcxdr_init_decode() or svcxdr_init_encode(), otherwise NULL.
*/
static inline struct svc_rqst *svcxdr_rqst(struct xdr_stream *xdr)
{
return xdr->xdrgen_ctx;
}
/**
@@ -587,6 +610,74 @@ static inline void svcxdr_encode_opaque_pages(struct svc_rqst *rqstp,
xdr->page_ptr = rqstp->rq_next_page - 1;
}
/**
* svcxdr_encode_opaque_payload - Encode a page-resident opaque data item
* @xdr: xdr_stream to be updated
* @len: number of octets of content in the data item
*
* Context: Process context. @xdr must have been initialized by
* svcxdr_init_encode() and still be positioned in the reply
* head.
*
* Return:
* %true: Success
* %false: The reply head cannot hold the length prefix and XDR
* padding, or the transport could not accommodate the result payload
*/
static inline bool svcxdr_encode_opaque_payload(struct xdr_stream *xdr, u32 len)
{
struct svc_rqst *rqstp = svcxdr_rqst(xdr);
struct xdr_buf *buf = xdr->buf;
/*
* The length prefix and any pad word must land in the reply
* head. A prefix that spills into the first Reply page lands
* on the payload itself, and xdr_write_pages() BUGs when the
* pad word does not fit.
*/
if ((xdr->end - xdr->p) * XDR_UNIT < XDR_UNIT + xdr_pad_size(len))
return false;
if (xdr_stream_encode_u32(xdr, len) < 0)
return false;
svcxdr_encode_opaque_pages(rqstp, xdr, buf->pages, buf->page_base, len);
if (svc_encode_result_payload(rqstp, buf->head->iov_len, len) < 0)
return false;
return true;
}
/**
* svcxdr_decode_opaque_payload - Decode a page-resident opaque data item
* @xdr: xdr_stream to be decoded
* @payload: on success, describes the octets of the data item's content
* @maxlen: largest data item length the caller will accept, or zero for
* no limit
*
* A bulk payload such as the content of an NFS WRITE request resides in
* the pages of the Receive buffer. Rather than copy it, set @payload to
* describe the item's content in place.
*
* Context: Process context. @xdr must have been initialized by
* svcxdr_init_decode().
*
* Return:
* %true: @payload describes the item in place and @xdr has advanced
* past it
* %false: a bounds error occurred, or the length prefix exceeds
* @maxlen; @payload is undefined
*/
static inline bool svcxdr_decode_opaque_payload(struct xdr_stream *xdr,
struct xdr_buf *payload,
u32 maxlen)
{
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (maxlen && len > maxlen)
return false;
return xdr_stream_subsegment(xdr, payload, len);
}
/**
* svcxdr_set_auth_slack -
* @rqstp: RPC transaction
+1 -1
View File
@@ -142,7 +142,7 @@ enum {
#define RPCSVC_MAXPAYLOAD_RDMA RPCSVC_MAXPAYLOAD
/**
* svc_rdma_send_cid_init - Initialize a Receive Queue completion ID
* svc_rdma_recv_cid_init - Initialize a Receive Queue completion ID
* @rdma: controlling transport
* @cid: completion ID to initialize
*/
+4 -1
View File
@@ -37,6 +37,9 @@ struct svc_xprt_class {
struct list_head xcl_list;
u32 xcl_max_payload;
int xcl_ident;
u32 xcl_flags;
/* Set only on classes whose xpo_has_wspace() reads xpt_reserved */
#define SVC_XPRT_FLAG_WSPACE_RESERVE BIT(0)
};
/*
@@ -59,7 +62,7 @@ struct svc_xprt {
unsigned long xpt_flags;
struct svc_serv *xpt_server; /* service for transport */
atomic_t xpt_reserved; /* space on outq that is rsvd */
atomic_t xpt_reserved; /* outq space rsvd, UDP only */
atomic_t xpt_nr_rqsts; /* Number of requests */
struct mutex xpt_mutex; /* to serialize sending data */
spinlock_t xpt_lock; /* protects sk_deferred
+1
View File
@@ -244,6 +244,7 @@ struct xdr_stream {
unsigned int nwords; /* Remaining decode buffer length */
struct rpc_rqst *rqst; /* For debugging */
void *xdrgen_ctx; /* Context for generated codecs */
};
/*
+34
View File
@@ -5,11 +5,17 @@
* This header defines XDR data type primitives specified in
* Section 4 of RFC 4506, used by RPC programs implemented
* in the Linux kernel.
*
* A generated definition can also embed a struct xdr_buf, which
* carries a page-resident payload captured by reference, so this
* header pulls in the kernel's XDR types as well.
*/
#ifndef _SUNRPC_XDRGEN__DEFS_H_
#define _SUNRPC_XDRGEN__DEFS_H_
#include <linux/sunrpc/xdr.h>
#define TRUE (true)
#define FALSE (false)
@@ -23,6 +29,34 @@ typedef struct {
u8 *data;
} opaque;
/*
* Cursor a hook-driven aggregate codec hands to its begin/item/end
* hooks, one element at a time, in place of a materialized C array.
* The generated framing owns it. @xdr is the RPC layer's stream and
* @ctx its xdrgen_ctx, the svc_rqst on the server. @index is the
* current element; @member_id selects among a type's marked members.
* For a counted array @count is the wire length: a decoder fills it
* before the begin hook runs, an encoder's begin hook sets it. The
* hook contract is under "Pragma aggregate" in
* tools/net/sunrpc/xdrgen/README.
*
* An optional-data list ("type *name") carries no count. Its encoder
* pulls elements until the item hook returns false. False ends the
* list rather than failing the encode: the framing writes the
* value-follows terminator, so a producer that stops early leaves a
* valid truncated list on the wire. A producer that stops for a
* mid-list error records it in the application state @ctx reaches,
* and either returns false from the end hook, which fails the
* encode, or reports it in the procedure status.
*/
struct xdrgen_aggregate_cursor {
struct xdr_stream *xdr;
u32 index;
u32 count;
unsigned int member_id;
void *ctx;
};
#define XDR_void (0)
#define XDR_bool (1)
#define XDR_short (1)
+298
View File
@@ -0,0 +1,298 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs2.x */
/* XDR specification modification time: Tue Sep 8 11:26:28 2026 */
#ifndef _LINUX_XDRGEN_NFS2_DEF_H
#define _LINUX_XDRGEN_NFS2_DEF_H
#include <linux/types.h>
#include <linux/sunrpc/xdrgen/_defs.h>
enum { NFS_MAXDATA = 8192 };
enum { NFS_MAXPATHLEN = 1024 };
enum { NFS_MAXNAMLEN = 255 };
enum { NFS_COOKIESIZE = 4 };
enum { NFS_FHSIZE = 32 };
enum nfsstat {
NFS_OK = 0,
NFSERR_PERM = 1,
NFSERR_NOENT = 2,
NFSERR_IO = 5,
NFSERR_NXIO = 6,
NFSERR_ACCES = 13,
NFSERR_EXIST = 17,
NFSERR_NODEV = 19,
NFSERR_NOTDIR = 20,
NFSERR_ISDIR = 21,
NFSERR_FBIG = 27,
NFSERR_NOSPC = 28,
NFSERR_ROFS = 30,
NFSERR_NAMETOOLONG = 63,
NFSERR_NOTEMPTY = 66,
NFSERR_DQUOT = 69,
NFSERR_STALE = 70,
NFSERR_WFLUSH = 99,
};
typedef __be32 nfsstat;
enum ftype {
NFNON = 0,
NFREG = 1,
NFDIR = 2,
NFBLK = 3,
NFCHR = 4,
NFLNK = 5,
};
typedef enum ftype ftype;
typedef u8 fhandle[NFS_FHSIZE];
typedef u8 nfscookie[NFS_COOKIESIZE];
struct timeval {
u32 seconds;
u32 useconds;
};
struct fattr {
ftype type;
u32 mode;
u32 nlink;
u32 uid;
u32 gid;
u32 size;
u32 blocksize;
u32 rdev;
u32 blocks;
u32 fsid;
u32 fileid;
struct timeval atime;
struct timeval mtime;
struct timeval ctime;
};
struct sattr {
u32 mode;
u32 uid;
u32 gid;
u32 size;
struct timeval atime;
struct timeval mtime;
};
typedef string filename;
typedef string path;
struct attrstat {
nfsstat status;
union {
struct fattr attributes;
} u;
};
struct diropargs {
fhandle dir;
filename name;
};
struct diropok {
fhandle file;
struct fattr attributes;
};
struct diropres {
nfsstat status;
union {
struct diropok diropok;
} u;
};
struct sattrargs {
fhandle file;
struct sattr attributes;
};
struct readlinkres {
nfsstat status;
union {
path data;
} u;
};
struct readargs {
fhandle file;
u32 offset;
u32 count;
u32 totalcount;
};
struct readresok {
struct fattr attributes;
opaque data;
};
struct readres {
nfsstat status;
union {
struct readresok readresok;
} u;
};
struct writeargs {
fhandle file;
u32 beginoffset;
u32 offset;
u32 totalcount;
struct xdr_buf data;
};
struct createargs {
struct diropargs where;
struct sattr attributes;
};
struct renameargs {
struct diropargs from;
struct diropargs to;
};
struct linkargs {
fhandle from;
struct diropargs to;
};
struct symlinkargs {
struct diropargs from;
struct xdr_buf to;
struct sattr attributes;
};
struct readdirargs {
fhandle dir;
nfscookie cookie;
u32 count;
};
struct entry {
u32 fileid;
filename name;
nfscookie cookie;
};
struct readdirok {
struct entry *entries;
bool eof;
};
struct readdirres {
nfsstat status;
union {
struct readdirok readdirok;
} u;
};
struct info {
u32 tsize;
u32 bsize;
u32 blocks;
u32 bfree;
u32 bavail;
};
struct statfsres {
nfsstat status;
union {
struct info info;
} u;
};
enum {
NFSPROC_NULL = 0,
NFSPROC_GETATTR = 1,
NFSPROC_SETATTR = 2,
NFSPROC_ROOT = 3,
NFSPROC_LOOKUP = 4,
NFSPROC_READLINK = 5,
NFSPROC_READ = 6,
NFSPROC_WRITECACHE = 7,
NFSPROC_WRITE = 8,
NFSPROC_CREATE = 9,
NFSPROC_REMOVE = 10,
NFSPROC_RENAME = 11,
NFSPROC_LINK = 12,
NFSPROC_SYMLINK = 13,
NFSPROC_MKDIR = 14,
NFSPROC_RMDIR = 15,
NFSPROC_READDIR = 16,
NFSPROC_STATFS = 17,
};
#ifndef NFS_PROGRAM
#define NFS_PROGRAM (100003)
#endif
#define NFS2_nfsstat_sz (XDR_int)
#define NFS2_ftype_sz (XDR_int)
#define NFS2_fhandle_sz (XDR_QUADLEN(NFS_FHSIZE))
#define NFS2_nfscookie_sz (XDR_QUADLEN(NFS_COOKIESIZE))
#define NFS2_timeval_sz \
(XDR_unsigned_int + XDR_unsigned_int)
#define NFS2_fattr_sz \
(NFS2_ftype_sz + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + NFS2_timeval_sz + NFS2_timeval_sz + NFS2_timeval_sz)
#define NFS2_sattr_sz \
(XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + NFS2_timeval_sz + NFS2_timeval_sz)
#define NFS2_filename_sz (XDR_unsigned_int + XDR_QUADLEN(NFS_MAXNAMLEN))
#define NFS2_path_sz (XDR_unsigned_int + XDR_QUADLEN(NFS_MAXPATHLEN))
#define NFS2_attrstat_sz \
(NFS2_nfsstat_sz + NFS2_fattr_sz)
#define NFS2_diropargs_sz \
(NFS2_fhandle_sz + NFS2_filename_sz)
#define NFS2_diropok_sz \
(NFS2_fhandle_sz + NFS2_fattr_sz)
#define NFS2_diropres_sz \
(NFS2_nfsstat_sz + NFS2_diropok_sz)
#define NFS2_sattrargs_sz \
(NFS2_fhandle_sz + NFS2_sattr_sz)
#define NFS2_readlinkres_sz \
(NFS2_nfsstat_sz + NFS2_path_sz)
#define NFS2_readargs_sz \
(NFS2_fhandle_sz + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int)
#define NFS2_readresok_sz \
(NFS2_fattr_sz + XDR_unsigned_int + XDR_QUADLEN(NFS_MAXDATA))
#define NFS2_readres_sz \
(NFS2_nfsstat_sz + NFS2_readresok_sz)
#define NFS2_writeargs_sz \
(NFS2_fhandle_sz + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_QUADLEN(NFS_MAXDATA))
#define NFS2_createargs_sz \
(NFS2_diropargs_sz + NFS2_sattr_sz)
#define NFS2_renameargs_sz \
(NFS2_diropargs_sz + NFS2_diropargs_sz)
#define NFS2_linkargs_sz \
(NFS2_fhandle_sz + NFS2_diropargs_sz)
#define NFS2_symlinkargs_sz \
(NFS2_diropargs_sz + NFS2_path_sz + NFS2_sattr_sz)
#define NFS2_readdirargs_sz \
(NFS2_fhandle_sz + NFS2_nfscookie_sz + XDR_unsigned_int)
#define NFS2_entry_sz \
(XDR_bool + XDR_unsigned_int + NFS2_filename_sz + NFS2_nfscookie_sz)
#define NFS2_readdirok_sz \
(XDR_bool + XDR_bool)
#define NFS2_readdirres_sz \
(NFS2_nfsstat_sz + NFS2_readdirok_sz)
#define NFS2_info_sz \
(XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int + XDR_unsigned_int)
#define NFS2_statfsres_sz \
(NFS2_nfsstat_sz + NFS2_info_sz)
#define NFS2_MAX_ARGS_SZ \
(NFS2_writeargs_sz)
#endif /* _LINUX_XDRGEN_NFS2_DEF_H */
+1 -12
View File
@@ -8,6 +8,7 @@
*/
#include <linux/nfs.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
#include <uapi/linux/nfs.h>
@@ -358,18 +359,6 @@ TRACE_DEFINE_ENUM(IOMODE_ANY);
{ IOMODE_RW, "RW" }, \
{ IOMODE_ANY, "ANY" })
#define show_rca_mask(x) \
__print_flags(x, "|", \
{ BIT(RCA4_TYPE_MASK_RDATA_DLG), "RDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_WDATA_DLG), "WDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_DIR_DLG), "DIR_DLG" }, \
{ BIT(RCA4_TYPE_MASK_FILE_LAYOUT), "FILE_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_BLK_LAYOUT), "BLK_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MIN), "OBJ_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MAX), "OBJ_LAYOUT_MAX" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MIN), "OTHER_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MAX), "OTHER_LAYOUT_MAX" })
#define show_nfs4_seq4_status(x) \
__print_flags(x, "|", \
{ SEQ4_STATUS_CB_PATH_DOWN, "CB_PATH_DOWN" }, \
+20
View File
@@ -50,6 +50,15 @@ enum nfsd_xprtsec_mode {
NFSD_XPRTSEC_MODE_MTLS = 4,
};
/*
* Constraints that apply to an rpcbind registration. no-udp means the kernel
* would not have registered this program and version over UDP, so the caller
* must skip the udp and udp6 netids for it.
*/
enum nfsd_rpcbind_flags {
NFSD_RPCBIND_FLAGS_NO_UDP = 1,
};
enum {
NFSD_A_CACHE_NOTIFY_CACHE_TYPE = 1,
@@ -113,8 +122,19 @@ enum {
NFSD_A_SOCK_MAX = (__NFSD_A_SOCK_MAX - 1)
};
enum {
NFSD_A_RPCBIND_PROGRAM = 1,
NFSD_A_RPCBIND_VERSION,
NFSD_A_RPCBIND_FLAGS,
__NFSD_A_RPCBIND_MAX,
NFSD_A_RPCBIND_MAX = (__NFSD_A_RPCBIND_MAX - 1)
};
enum {
NFSD_A_SERVER_SOCK_ADDR = 1,
NFSD_A_SERVER_SOCK_USERSPACE_RPCBIND,
NFSD_A_SERVER_SOCK_RPCBIND,
__NFSD_A_SERVER_SOCK_MAX,
NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1)
+23 -6
View File
@@ -824,12 +824,18 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
new->h.flavour = &svcauthops_gss;
new->pseudoflavor = pseudoflavor;
if (!try_module_get(svcauthops_gss.owner)) {
stat = -ENODEV;
goto out_free_name;
}
test = auth_domain_lookup(name, &new->h);
if (test != &new->h) {
pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n",
name);
stat = -EADDRINUSE;
auth_domain_put(test);
module_put(svcauthops_gss.owner);
goto out_free_name;
}
return test;
@@ -1085,10 +1091,11 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
}
length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p);
if (length)
memcpy(page_address(in_token->pages[0]), xdr->p, length);
for (to_offs = 0; to_offs < length; to_offs += PAGE_SIZE)
memcpy(page_address(in_token->pages[to_offs >> PAGE_SHIFT]),
(char *)xdr->p + to_offs,
min_t(unsigned int, length - to_offs, PAGE_SIZE));
inlen -= length;
to_offs = length;
from_offs = rqstp->rq_arg.page_base;
while (inlen) {
@@ -1420,10 +1427,18 @@ static ssize_t write_gssp(struct file *file, const char __user *buf,
return res;
if (i != 1)
return -EINVAL;
/*
* Neither the proc entry nor an open descriptor pins @net, so it
* might already be dying when rpc_create() takes its reference.
*/
if (!maybe_get_net(net))
return -ENXIO;
res = set_gssp_clnt(net);
if (res)
return res;
res = set_gss_proxy(net, 1);
if (!res)
res = set_gss_proxy(net, 1);
put_net(net);
if (res)
return res;
return count;
@@ -1995,9 +2010,11 @@ svcauth_gss_domain_release_rcu(struct rcu_head *head)
{
struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head);
struct gss_domain *gd = container_of(dom, struct gss_domain, h);
struct module *owner = dom->flavour->owner;
kfree(dom->name);
kfree(gd);
module_put(owner);
}
static void
+31 -3
View File
@@ -221,6 +221,16 @@ static void rpcb_set_local(struct net *net, struct rpc_clnt *clnt,
# define SUN_LEN(ptr) (offsetof(struct sockaddr_un, sun_path) \
+ 1 + strlen((ptr)->sun_path + 1))
/*
* The kernel's rpcbind client talks only to the local rpcbind, over loopback
* or a local AF_LOCAL socket, where a healthy rpcbind answers in microseconds.
*/
static const struct rpc_timeout rpcb_local_timeout = {
.to_initval = 1 * HZ,
.to_maxval = 1 * HZ,
.to_retries = 0,
};
/*
* Returns zero on success, otherwise a negative errno value
* is returned.
@@ -238,6 +248,7 @@ static int rpcb_create_af_local(struct net *net,
.version = RPCBVERS_2,
.authflavor = RPC_AUTH_NULL,
.cred = current_cred(),
.timeout = &rpcb_local_timeout,
/*
* We turn off the idle timeout to prevent the kernel
* from automatically disconnecting the socket.
@@ -312,6 +323,7 @@ static int rpcb_create_local_net(struct net *net)
.version = RPCBVERS_2,
.authflavor = RPC_AUTH_UNIX,
.cred = current_cred(),
.timeout = &rpcb_local_timeout,
.flags = RPC_CLNT_CREATE_NOPING,
};
struct rpc_clnt *clnt, *clnt4;
@@ -400,7 +412,8 @@ static struct rpc_clnt *rpcb_create(struct net *net, const char *nodename,
return rpc_create(&args);
}
static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, struct rpc_message *msg, bool is_set)
static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt,
struct rpc_message *msg, bool is_set)
{
int flags = RPC_TASK_NOCONNECT;
int error, result = 0;
@@ -410,8 +423,23 @@ static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, stru
msg->rpc_resp = &result;
error = rpc_call_sync(clnt, msg, flags);
if (error < 0)
return error;
if (error < 0) {
switch (error) {
/* rpcbind answered; the reply itself carries the error */
case -EPROTONOSUPPORT:
case -EPFNOSUPPORT:
case -EOPNOTSUPP:
case -EACCES:
/* the call never made it onto the wire */
case -ENOMEM:
case -EMSGSIZE:
/* the caller is going away; this says nothing about rpcbind */
case -ERESTARTSYS:
return error;
}
/* anything else, we assume that rpcbind isn't functional */
return -EIO;
}
if (!result)
return -EACCES;
+65 -7
View File
@@ -337,6 +337,8 @@ static int svc_uses_rpcbind(struct svc_serv *serv)
int svc_bind(struct svc_serv *serv, struct net *net)
{
if (serv->sv_no_rpcbind)
return 0;
if (!svc_uses_rpcbind(serv))
return 0;
return svc_rpcb_setup(serv, net);
@@ -1179,10 +1181,40 @@ int svc_generic_rpcbind_set(struct net *net,
error = svc_rpcbind_set_version(net, progp, version,
family, proto, port);
/* -EIO means no answer, not a refusal, so vs_rpcb_optnl must keep it. */
if (error == -EIO)
return error;
return (vers->vs_rpcb_optnl) ? 0 : error;
}
EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
/**
* svc_rpcb_failure_count - local rpcbind calls for @serv that got no answer
* @serv: RPC service to query
*
* svc_register() adds one for each of its calls that got no answer. A reply
* that refuses one entry does not count, because rpcbind answered and the
* next entry may still succeed.
*
* The count is kept per serv rather than per net. The local rpcbind client
* is per-net and lockd shares it, but a count that another service can move
* says nothing about this serv's own calls.
*
* This is for callers that cannot see the svc_register() return, because a
* transport class sits in between. Such a caller reads the count before it
* starts and compares as it goes, so there is no state to reset between
* operations. The count never resets, and callers must not attach meaning
* to the value itself.
*
* Return: the number of unanswered calls since this serv was created.
*/
unsigned int svc_rpcb_failure_count(struct svc_serv *serv)
{
return atomic_read(&serv->sv_rpcb_failures);
}
EXPORT_SYMBOL_GPL(svc_rpcb_failure_count);
/**
* svc_register - register an RPC service with the local portmapper
* @serv: svc_serv struct for the service to register
@@ -1193,10 +1225,11 @@ EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
*
* Service is registered for any address in the passed-in protocol family
*/
int svc_register(const struct svc_serv *serv, struct net *net,
int svc_register(struct svc_serv *serv, struct net *net,
const int family, const unsigned short proto,
const unsigned short port)
{
bool noanswer = false;
unsigned int p, i;
int error = 0;
@@ -1204,22 +1237,43 @@ int svc_register(const struct svc_serv *serv, struct net *net,
if (proto == 0 && port == 0)
return -EINVAL;
if (serv->sv_no_rpcbind)
return 0;
for (p = 0; p < serv->sv_nprogs; p++) {
struct svc_program *progp = &serv->sv_programs[p];
for (i = 0; i < progp->pg_nvers; i++) {
const struct svc_version *vers = progp->pg_vers[i];
int ret;
error = progp->pg_rpcbind_set(net, progp, i,
ret = progp->pg_rpcbind_set(net, progp, i,
family, proto, port);
if (error < 0) {
if (ret == -EIO) {
noanswer = true;
if (vers && vers->vs_rpcb_optnl)
ret = 0;
}
if (ret < 0) {
printk(KERN_WARNING "svc: failed to register "
"%sv%u RPC service (errno %d).\n",
progp->pg_name, i, -error);
progp->pg_name, i, -ret);
if (!error)
error = ret;
break;
}
if (noanswer)
break;
}
/* Give up on trying to register anything if it didn't respond */
if (noanswer)
break;
}
if (noanswer)
atomic_inc(&serv->sv_rpcb_failures);
return error;
}
@@ -1230,8 +1284,8 @@ int svc_register(const struct svc_serv *serv, struct net *net,
* any "inet6" entries anyway. So a PMAP_UNSET should be sufficient
* in this case to clear all existing entries for [program, version].
*/
static void __svc_unregister(struct net *net, const u32 program, const u32 version,
const char *progname)
static int __svc_unregister(struct net *net, const u32 program, const u32 version,
const char *progname)
{
int error;
@@ -1245,6 +1299,7 @@ static void __svc_unregister(struct net *net, const u32 program, const u32 versi
error = rpcb_register(net, program, version, 0, 0);
trace_svc_unregister(progname, version, error);
return error;
}
/*
@@ -1271,10 +1326,13 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net)
continue;
if (progp->pg_vers[i]->vs_hidden)
continue;
__svc_unregister(net, progp->pg_prog, i, progp->pg_name);
if (__svc_unregister(net, progp->pg_prog, i,
progp->pg_name) == -EIO)
goto out;
}
}
out:
rcu_read_lock();
sighand = rcu_dereference(current->sighand);
spin_lock_irqsave(&sighand->siglock, flags);
+53 -30
View File
@@ -64,8 +64,10 @@ static LIST_HEAD(svc_xprt_class_list);
* - Can be set or cleared at any time.
* - After a set, svc_xprt_enqueue must be called to enqueue
* the transport for processing.
* - After a clear, the transport must be read/accepted.
* If this succeeds, it must be set again.
* - After clearing XPT_CONN, the transport must be
* accepted. If this succeeds, the bit must be set again.
* - xpo_recvfrom decides when XPT_DATA is cleared; see
* svc_xprt_received.
* XPT_CLOSE:
* - Can set at any time. It is never cleared.
* XPT_DEAD:
@@ -218,8 +220,9 @@ EXPORT_SYMBOL_GPL(svc_xprt_init);
* The caller must hold the XPT_BUSY bit and must
* not thereafter touch transport data.
*
* Note: XPT_DATA only gets cleared when a read-attempt finds no (or
* insufficient) data.
* Note: xpo_recvfrom decides when to clear XPT_DATA. A transport may
* leave the bit set until a read attempt finds no (or insufficient)
* data, or clear it as soon as it consumes the last queued receive.
*/
void svc_xprt_received(struct svc_xprt *xprt)
{
@@ -475,11 +478,11 @@ static bool svc_xprt_ready(struct svc_xprt *xprt)
/*
* If another cpu has recently updated xpt_flags,
* sk_sock->flags, xpt_reserved, or xpt_nr_rqsts, we need to
* know about it; otherwise it's possible that both that cpu and
* this one could call svc_xprt_enqueue() without either
* svc_xprt_enqueue() recognizing that the conditions below
* are satisfied, and we could stall indefinitely:
* sk_sock->flags, xpt_reserved (UDP only), or xpt_nr_rqsts,
* we need to know about it; otherwise it's possible that both
* that cpu and this one could call svc_xprt_enqueue() without
* either svc_xprt_enqueue() recognizing that the conditions
* below are satisfied, and we could stall indefinitely:
*/
smp_rmb();
xpt_flags = READ_ONCE(xprt->xpt_flags);
@@ -551,6 +554,10 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)
* to make sure the reply fits. This function reduces that reserved
* space to be the amount of space used already, plus @space.
*
* The transport's reservation is tracked only on classes that set
* SVC_XPRT_FLAG_WSPACE_RESERVE. On the others, only @rqstp's
* reservation is updated.
*
*/
void svc_reserve(struct svc_rqst *rqstp, int space)
{
@@ -559,10 +566,12 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
space += rqstp->rq_res.head[0].iov_len;
if (xprt && space < rqstp->rq_reserved) {
atomic_sub((rqstp->rq_reserved - space),
&xprt->xpt_reserved);
if (xprt->xpt_class->xcl_flags & SVC_XPRT_FLAG_WSPACE_RESERVE) {
atomic_sub((rqstp->rq_reserved - space),
&xprt->xpt_reserved);
svc_xprt_resource_released(xprt);
}
rqstp->rq_reserved = space;
svc_xprt_resource_released(xprt);
}
}
EXPORT_SYMBOL_GPL(svc_reserve);
@@ -852,8 +861,8 @@ static void svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) {
newxpt->xpt_cred = get_cred(xprt->xpt_cred);
svc_add_new_temp_xprt(serv, newxpt);
trace_svc_xprt_accept(newxpt, serv->sv_name);
svc_add_new_temp_xprt(serv, newxpt);
} else {
module_put(xprt->xpt_class->xcl_owner);
}
@@ -869,7 +878,8 @@ static void svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
else
len = xprt->xpt_ops->xpo_recvfrom(rqstp);
rqstp->rq_reserved = serv->sv_max_mesg;
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
if (xprt->xpt_class->xcl_flags & SVC_XPRT_FLAG_WSPACE_RESERVE)
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
if (len <= 0)
goto out;
@@ -1101,6 +1111,23 @@ static void call_xpt_users(struct svc_xprt *xprt)
spin_unlock(&xprt->xpt_lock);
}
/*
* If rpcbind stops answering, every listener still to be destroyed would
* only wait out the same timeout again. Drop the flag on the rest of this
* teardown, which is every listener already marked for close.
*/
static void svc_xprt_clear_rpcb_unreg(struct svc_serv *serv, struct net *net)
{
struct svc_xprt *xprt;
spin_lock_bh(&serv->sv_lock);
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list)
if (xprt->xpt_net == net &&
test_bit(XPT_CLOSE, &xprt->xpt_flags))
clear_bit(XPT_RPCB_UNREG, &xprt->xpt_flags);
spin_unlock_bh(&serv->sv_lock);
}
/*
* Remove a dead transport
*/
@@ -1115,11 +1142,15 @@ static void svc_delete_xprt(struct svc_xprt *xprt)
struct svc_sock *svsk = container_of(xprt, struct svc_sock,
sk_xprt);
struct socket *sock = svsk->sk_sock;
unsigned int failures = svc_rpcb_failure_count(serv);
if (svc_register(serv, xprt->xpt_net, sock->sk->sk_family,
sock->sk->sk_protocol, 0) < 0)
pr_warn("failed to unregister %s with rpcbind\n",
xprt->xpt_class->xcl_name);
if (svc_rpcb_failure_count(serv) != failures)
svc_xprt_clear_rpcb_unreg(serv, xprt->xpt_net);
}
if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
@@ -1235,7 +1266,7 @@ void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net,
msleep(delay++);
}
if (unregister)
if (unregister && !serv->sv_no_rpcbind)
svc_rpcb_cleanup(serv, net);
}
EXPORT_SYMBOL_GPL(svc_xprt_destroy_all);
@@ -1268,13 +1299,8 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many)
}
/*
* Save the request off for later processing. The request buffer looks
* like this:
*
* <xprt-header><rpc-header><rpc-pagelist><rpc-tail>
*
* This code can only handle requests that consist of an xprt-header
* and rpc-header.
* Save the request off for later processing. Only a Call that fits
* entirely in rq_arg.head[0] can be deferred.
*/
static struct cache_deferred_req *svc_defer(struct cache_req *req)
{
@@ -1287,8 +1313,11 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
dr = rqstp->rq_deferred;
rqstp->rq_deferred = NULL;
} else {
size_t skip;
size_t size;
if (rqstp->rq_arg.len > rqstp->rq_arg.head[0].iov_len)
return NULL;
/* FIXME maybe discard if size too large */
size = sizeof(struct svc_deferred_req) + rqstp->rq_arg.len;
dr = kmalloc(size, GFP_KERNEL);
@@ -1302,9 +1331,7 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
dr->daddr = rqstp->rq_daddr;
dr->argslen = rqstp->rq_arg.len >> 2;
/* back up head to the start of the buffer and copy */
skip = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len;
memcpy(dr->args, rqstp->rq_arg.head[0].iov_base - skip,
memcpy(dr->args, rqstp->rq_arg.head[0].iov_base,
dr->argslen << 2);
}
dr->xprt_ctxt = rqstp->rq_xprt_ctxt;
@@ -1327,17 +1354,13 @@ static noinline int svc_deferred_recv(struct svc_rqst *rqstp)
trace_svc_defer_recv(dr);
/* setup iov_base past transport header */
rqstp->rq_arg.head[0].iov_base = dr->args;
/* The iov_len does not include the transport header bytes */
rqstp->rq_arg.head[0].iov_len = dr->argslen << 2;
rqstp->rq_arg.page_len = 0;
/* The rq_arg.len includes the transport header bytes */
rqstp->rq_arg.len = dr->argslen << 2;
rqstp->rq_prot = dr->prot;
memcpy(&rqstp->rq_addr, &dr->addr, dr->addrlen);
rqstp->rq_addrlen = dr->addrlen;
/* Save off transport header len in case we get deferred again */
rqstp->rq_daddr = dr->daddr;
rqstp->rq_xprt_ctxt = dr->xprt_ctxt;
+9
View File
@@ -540,6 +540,13 @@ static int unix_gid_parse(struct cache_detail *cd,
if (ugp) {
struct cache_head *ch;
ug.h.flags = 0;
/*
* mountd sends at least the user's primary group on
* success, so an empty list can only mean the lookup
* failed. Keep the credential's own groups instead.
*/
if (gids == 0)
set_bit(CACHE_NEGATIVE, &ug.h.flags);
ug.h.expiry_time = expiry;
ch = sunrpc_cache_update(cd,
&ug.h, &ugp->h,
@@ -730,6 +737,8 @@ static int sunrpc_nl_parse_one_unix_gid(struct cache_detail *cd,
boot.tv_sec;
if (tb[SUNRPC_A_UNIX_GID_NEGATIVE]) {
/* failed lookup: keep the credential's own groups */
set_bit(CACHE_NEGATIVE, &ug.h.flags);
ug.gi = groups_alloc(0);
if (!ug.gi)
return -ENOMEM;
+307 -201
View File
@@ -8,15 +8,6 @@
* evenly when servicing a single client. May need to modify the
* svc_xprt_enqueue procedure...
*
* TCP support is largely untested and may be a little slow. The problem
* is that we currently do two separate recvfrom's, one for the 4-byte
* record length, and the second for the actual record. This could possibly
* be improved by always reading a minimum size of around 100 bytes and
* tucking any superfluous bytes away in a temporary store. Still, that
* leaves write requests out in the rain. An alternative may be to peek at
* the first skb in the queue, and if it matches the next TCP sequence
* number, to extract the record marker. Yuck.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
@@ -238,136 +229,108 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
return len;
}
static int
svc_tcp_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
struct cmsghdr *cmsg, int ret)
{
u8 content_type = tls_get_record_type(sock->sk, cmsg);
u8 level, description;
switch (content_type) {
case 0:
break;
case TLS_RECORD_TYPE_DATA:
/* TLS sets EOR at the end of each application data
* record, even though there might be more frames
* waiting to be decrypted.
*/
msg->msg_flags &= ~MSG_EOR;
break;
case TLS_RECORD_TYPE_ALERT:
tls_alert_recv(sock->sk, msg, &level, &description);
ret = (level == TLS_ALERT_LEVEL_FATAL) ?
-ENOTCONN : -EAGAIN;
break;
default:
/* discard this record type */
ret = -EAGAIN;
}
return ret;
}
static int
svc_tcp_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags)
/*
* The ->read_sock data path invokes neither security_socket_recvmsg()
* nor the sock:sock_recv_length tracepoint. Dispatch ->recvmsg
* directly so the whole receive path behaves one way.
*/
static int svc_tcp_recv_cmsg(struct socket *sock, int flags,
struct kvec *payload, u8 *type,
unsigned int *msg_flags)
{
const struct proto_ops *ops = READ_ONCE(sock->ops);
union {
struct cmsghdr cmsg;
u8 buf[CMSG_SPACE(sizeof(u8))];
} u;
u8 alert[2];
struct kvec alert_kvec = {
.iov_base = alert,
.iov_len = sizeof(alert),
};
} u = {};
struct msghdr msg = {
.msg_flags = *msg_flags,
.msg_control = &u,
.msg_controllen = sizeof(u),
.msg_control = &u,
.msg_controllen = sizeof(u),
};
int ret;
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &alert_kvec, 1,
alert_kvec.iov_len);
ret = sock_recvmsg(sock, &msg, MSG_DONTWAIT);
if (ret > 0 &&
tls_get_record_type(sock->sk, &u.cmsg) == TLS_RECORD_TYPE_ALERT) {
iov_iter_revert(&msg.msg_iter, ret);
ret = svc_tcp_sock_process_cmsg(sock, &msg, &u.cmsg, -EAGAIN);
}
iov_iter_kvec(&msg.msg_iter, ITER_DEST, payload, 1, payload->iov_len);
ret = ops->recvmsg(sock, &msg, msg_data_left(&msg), flags);
if (ret < 0)
return ret;
*msg_flags = msg.msg_flags;
*type = tls_get_record_type(sock->sk, &u.cmsg);
if (!*type && ret)
return -EBADMSG;
return ret;
}
static int
svc_tcp_sock_recvmsg(struct svc_sock *svsk, struct msghdr *msg)
static int svc_tcp_recv_ctrl_record(struct svc_sock *svsk)
{
int ret;
u8 alert[2], type, level, description;
struct socket *sock = svsk->sk_sock;
ret = sock_recvmsg(sock, msg, MSG_DONTWAIT);
if (msg->msg_flags & MSG_CTRUNC) {
msg->msg_flags &= ~(MSG_CTRUNC | MSG_EOR);
if (ret == 0 || ret == -EIO)
ret = svc_tcp_sock_recv_cmsg(sock, &msg->msg_flags);
}
return ret;
}
#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
static void svc_flush_bvec(const struct bio_vec *bvec, size_t size, size_t seek)
{
struct bvec_iter bi = {
.bi_size = size + seek,
struct kvec recv_kvec = {
.iov_base = alert,
.iov_len = sizeof(alert),
};
struct bio_vec bv;
unsigned int msg_flags;
struct msghdr msg = {};
int ret;
bvec_iter_advance(bvec, &bi, seek & PAGE_MASK);
for_each_bvec(bv, bvec, bi, bi)
flush_dcache_page(bv.bv_page);
}
#else
static inline void svc_flush_bvec(const struct bio_vec *bvec, size_t size,
size_t seek)
{
}
#endif
if (!test_bit(XPT_TLS_SESSION, &svsk->sk_xprt.xpt_flags))
return 0;
/*
* Read from @rqstp's transport socket. The incoming message fills whole
* pages in @rqstp's rq_pages array until the last page of the message
* has been received into a partial page.
*/
static ssize_t svc_tcp_read_msg(struct svc_rqst *rqstp, size_t buflen,
size_t seek)
{
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
struct bio_vec *bvec = rqstp->rq_bvec;
struct msghdr msg = { NULL };
unsigned int i;
ssize_t len;
size_t t;
clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE)
bvec_set_page(&bvec[i], rqstp->rq_pages[i], PAGE_SIZE, 0);
iov_iter_bvec(&msg.msg_iter, ITER_DEST, bvec, i, buflen);
if (seek) {
iov_iter_advance(&msg.msg_iter, seek);
buflen -= seek;
}
len = svc_tcp_sock_recvmsg(svsk, &msg);
if (len > 0)
svc_flush_bvec(bvec, len, seek);
/* If we read a full record, then assume there may be more
* data to read (stream based sockets only!)
/* A data record can become ready between ->read_sock returning
* and this probe. A plain receive would take two octets of it
* as RPC payload, so peek.
*/
if (len == buflen)
ret = svc_tcp_recv_cmsg(sock, MSG_DONTWAIT | MSG_PEEK,
&recv_kvec, &type, &msg_flags);
if (ret == -EAGAIN || (!ret && !type))
return 0;
if (ret < 0)
return ret;
if (type == TLS_RECORD_TYPE_DATA) {
/* The peek parks the decrypted record on ctx->rx_list,
* where it draws no further data_ready. Re-arm or the
* RPC hangs until the client times out.
*/
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
return 0;
}
if (type != TLS_RECORD_TYPE_ALERT)
return -EPROTO;
return len;
ret = svc_tcp_recv_cmsg(sock, MSG_DONTWAIT, &recv_kvec, &type,
&msg_flags);
/* The peek found a record at the head, so an -EAGAIN here is
* spurious. Propagating it strands the record with no later
* announcement, so return -EBADMSG, which closes the transport.
*/
if (ret == -EAGAIN)
return -EBADMSG;
if (ret < 0)
return ret;
/* An Alert record carries exactly one two-octet message (RFC
* 8446 Section 5.1). recv_kvec caps the receive at two, so a
* longer record produces the same count. MSG_EOR appears only
* once kTLS has drained the whole record.
*/
if (ret != sizeof(alert) || !(msg_flags & MSG_EOR))
return -EBADMSG;
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &recv_kvec, 1,
recv_kvec.iov_len);
tls_alert_recv(sock->sk, &msg, &level, &description);
/* RFC 8446 Section 6: every alert but a closure alert is
* an error alert. kTLS raises no data_ready for records it
* already holds, so re-arm for what sits behind the alert.
*/
switch (description) {
case TLS_ALERT_DESC_CLOSE_NOTIFY:
case TLS_ALERT_DESC_USER_CANCELED:
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
return -EAGAIN;
default:
return -ENOTCONN;
}
}
/*
@@ -389,10 +352,8 @@ static void svc_sock_setbufsize(struct svc_sock *svsk, unsigned int nreqs)
static void svc_sock_secure_port(struct svc_rqst *rqstp)
{
if (svc_port_is_privileged(svc_addr(rqstp)))
set_bit(RQ_SECURE, &rqstp->rq_flags);
else
clear_bit(RQ_SECURE, &rqstp->rq_flags);
assign_bit(RQ_SECURE, &rqstp->rq_flags,
svc_port_is_privileged(svc_addr(rqstp)));
}
/*
@@ -837,6 +798,7 @@ static struct svc_xprt_class svc_udp_class = {
.xcl_ops = &svc_udp_ops,
.xcl_max_payload = RPCSVC_MAXPAYLOAD_UDP,
.xcl_ident = XPRT_TRANSPORT_UDP,
.xcl_flags = SVC_XPRT_FLAG_WSPACE_RESERVE,
};
static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)
@@ -978,10 +940,8 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt)
slen = offsetof(struct sockaddr, sa_data);
svc_xprt_set_local(&newsvsk->sk_xprt, sin, slen);
if (sock_is_loopback(newsock->sk))
set_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags);
else
clear_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags);
assign_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags,
sock_is_loopback(newsock->sk));
if (serv->sv_stats)
serv->sv_stats->nettcpconn++;
@@ -992,14 +952,14 @@ failed:
return NULL;
}
static size_t svc_tcp_restore_pages(struct svc_sock *svsk,
struct svc_rqst *rqstp)
static void svc_tcp_restore_pages(struct svc_sock *svsk,
struct svc_rqst *rqstp)
{
size_t len = svsk->sk_datalen;
unsigned int i, npages;
if (!len)
return 0;
return;
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
if (rqstp->rq_pages[i] != NULL)
@@ -1009,7 +969,6 @@ static size_t svc_tcp_restore_pages(struct svc_sock *svsk,
svsk->sk_pages[i] = NULL;
}
rqstp->rq_arg.head[0].iov_base = page_address(rqstp->rq_pages[0]);
return len;
}
static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp)
@@ -1048,50 +1007,6 @@ out:
svsk->sk_datalen = 0;
}
/*
* Receive fragment record header into sk_marker.
*/
static ssize_t svc_tcp_read_marker(struct svc_sock *svsk,
struct svc_rqst *rqstp)
{
ssize_t want, len;
/* If we haven't gotten the record length yet,
* get the next four bytes.
*/
if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
struct msghdr msg = { NULL };
struct kvec iov;
want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
iov.iov_base = ((char *)&svsk->sk_marker) + svsk->sk_tcplen;
iov.iov_len = want;
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, want);
len = svc_tcp_sock_recvmsg(svsk, &msg);
if (len < 0)
return len;
svsk->sk_tcplen += len;
if (len < want) {
/* call again to read the remaining bytes */
goto err_short;
}
trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
if (svc_sock_reclen(svsk) + svsk->sk_datalen >
svsk->sk_xprt.xpt_server->sv_max_mesg)
goto err_too_large;
}
return svc_sock_reclen(svsk);
err_too_large:
net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
svsk->sk_xprt.xpt_server->sv_name,
svc_sock_reclen(svsk),
(struct sockaddr *)&svsk->sk_xprt.xpt_remote);
svc_xprt_deferred_close(&svsk->sk_xprt);
err_short:
return -EAGAIN;
}
static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
{
struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt;
@@ -1129,20 +1044,162 @@ unlock_eagain:
static void svc_tcp_fragment_received(struct svc_sock *svsk)
{
/* If we have more data, signal svc_xprt_enqueue() to try again */
svsk->sk_tcplen = 0;
svsk->sk_marker = xdr_zero;
}
/*
* A non-final fragment carries four octets of marker and may carry
* no payload at all. sk_datalen advances only by the payload, so a
* run of tiny fragments exhausts ->read_sock's byte budget before
* the sv_max_mesg check trips, and a run of empty ones never trips
* it. Cap the fragments per socket-lock hold. The cap leaves the
* record incomplete, and svc_tcp_recvfrom() resumes it on the next
* call.
*/
#define SVC_TCP_MAX_FRAGS 256
struct svc_tcp_recv_ctx {
struct svc_rqst *rqstp;
unsigned int frags;
bool complete;
};
/*
* Nothing reads the message body before the message is complete, and
* partial receives refill the same pages. Flush once here, after the
* socket lock is released, rather than once per copy in the actor.
*/
static void svc_tcp_flush_pages(struct svc_sock *svsk,
struct svc_rqst *rqstp)
{
unsigned int pg, pages = DIV_ROUND_UP(svsk->sk_datalen, PAGE_SIZE);
for (pg = 0; pg < pages; pg++)
flush_dcache_page(rqstp->rq_pages[pg]);
}
/*
* Mapping the message's unfilled remainder would re-map untouched
* pages on every call, at a cost that grows with the message rather
* than with the octets copied.
*/
static void svc_tcp_recv_iter_init(struct svc_rqst *rqstp,
struct iov_iter *iter, size_t body_off,
size_t len)
{
unsigned int first = body_off >> PAGE_SHIFT;
size_t seek = offset_in_page(body_off);
unsigned int i, pages = DIV_ROUND_UP(seek + len, PAGE_SIZE);
for (i = 0; i < pages; i++)
bvec_set_page(&rqstp->rq_bvec[i], rqstp->rq_pages[first + i],
PAGE_SIZE, 0);
iov_iter_bvec(iter, ITER_DEST, rqstp->rq_bvec, pages, seek + len);
iov_iter_advance(iter, seek);
}
/*
* ->read_sock actor, called under the socket lock. sk_datalen is both
* the count of body octets received so far and their write offset into
* rq_pages.
*/
static int svc_tcp_recv_actor(read_descriptor_t *desc, struct sk_buff *skb,
unsigned int offset, size_t len)
{
struct svc_tcp_recv_ctx *ctx = desc->arg.data;
struct svc_rqst *rqstp = ctx->rqstp;
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
size_t reclen, received, want, take, n;
size_t consumed = 0;
if (!desc->count)
return 0;
len = min(len, desc->count);
if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
n = min(want, len);
if (skb_copy_bits(skb, offset,
(char *)&svsk->sk_marker + svsk->sk_tcplen,
n))
goto fault;
svsk->sk_tcplen += n;
offset += n;
len -= n;
consumed += n;
desc->count -= n;
if (svsk->sk_tcplen < sizeof(rpc_fraghdr))
return consumed;
trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
if (svc_sock_reclen(svsk) + svsk->sk_datalen >
svsk->sk_xprt.xpt_server->sv_max_mesg) {
net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
svsk->sk_xprt.xpt_server->sv_name,
svc_sock_reclen(svsk),
(struct sockaddr *)&svsk->sk_xprt.xpt_remote);
desc->error = -EMSGSIZE;
desc->count = 0;
return consumed;
}
}
reclen = svc_sock_reclen(svsk);
received = svsk->sk_tcplen - sizeof(rpc_fraghdr);
want = reclen - received;
take = min(want, len);
if (take) {
struct iov_iter iter;
svc_tcp_recv_iter_init(rqstp, &iter, svsk->sk_datalen, take);
if (skb_copy_datagram_iter(skb, offset, &iter, take))
goto fault;
svsk->sk_datalen += take;
svsk->sk_tcplen += take;
consumed += take;
desc->count -= take;
}
if (take == want) {
if (svc_sock_final_rec(svsk)) {
ctx->complete = true;
desc->count = 0;
} else {
svc_tcp_fragment_received(svsk);
if (++ctx->frags >= SVC_TCP_MAX_FRAGS)
desc->count = 0;
}
}
return consumed;
fault:
desc->error = -EFAULT;
desc->count = 0;
return consumed;
}
static bool svc_tcp_at_urg_mark(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
return tp->urg_data && tp->urg_seq == tp->copied_seq;
}
/**
* svc_tcp_recvfrom - Receive data from a TCP socket
* @rqstp: request structure into which to receive an RPC Call
*
* Called in a loop when XPT_DATA has been set.
*
* Read the 4-byte stream record marker, then use the record length
* in that marker to set up exactly the resources needed to receive
* the next RPC message into @rqstp.
* Context: Process context. Takes and releases the socket lock.
*
* Returns:
* On success, the number of bytes in a received RPC Call, or
@@ -1157,29 +1214,69 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
struct svc_serv *serv = svsk->sk_xprt.xpt_server;
size_t want, base;
struct svc_tcp_recv_ctx ctx = {
.rqstp = rqstp,
};
read_descriptor_t desc = {
.arg.data = &ctx,
.count = serv->sv_max_mesg + sizeof(rpc_fraghdr),
};
struct socket *sock = svsk->sk_sock;
ssize_t len;
__be32 *p;
__be32 calldir;
clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
len = svc_tcp_read_marker(svsk, rqstp);
if (len < 0)
goto error;
svc_tcp_restore_pages(svsk, rqstp);
base = svc_tcp_restore_pages(svsk, rqstp);
want = len - (svsk->sk_tcplen - sizeof(rpc_fraghdr));
len = svc_tcp_read_msg(rqstp, base + want, base);
if (len >= 0) {
trace_svcsock_tcp_recv(&svsk->sk_xprt, len);
svsk->sk_tcplen += len;
svsk->sk_datalen += len;
lock_sock(sock->sk);
len = sock->ops->read_sock(sock->sk, &desc, svc_tcp_recv_actor);
/* ->read_sock stops at urgent data and consumes none of it.
* Only recvmsg() clears the condition, and this path calls
* none, so every later read stops at the same octet. An RPC
* stream carries no urgent data, so close the connection.
*
* The read that first reaches the mark consumes the octets
* ahead of it, so the stop does not show up as a zero len. A
* record completed ahead of the mark is returned first. The
* XPT_DATA set below brings the next call back here with
* nothing left to consume.
*/
if (!ctx.complete && svc_tcp_at_urg_mark(sock->sk))
desc.error = -EPROTO;
release_sock(sock->sk);
/* ->read_sock returns the octets consumed before an actor
* failure, so a positive len can accompany desc.error.
*/
if (desc.error < 0) {
len = desc.error;
goto err_nuts;
}
if (len != want || !svc_sock_final_rec(svsk))
if (len >= 0)
trace_svcsock_tcp_recv(&svsk->sk_xprt, len);
if (!ctx.complete) {
if (!desc.count) {
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
goto err_incomplete;
}
/* A zero return leaves no record at the head to classify.
* -EINVAL means a control record sits there. Screen the
* other errors out first, because a probe calls
* sock_error(), whose xchg clears sk->sk_err as it reads.
*/
if (len <= 0 && len != -EINVAL)
goto err_incomplete;
len = svc_tcp_recv_ctrl_record(svsk);
goto err_incomplete;
}
if (svsk->sk_datalen < 8)
goto err_nuts;
svc_tcp_flush_pages(svsk, rqstp);
rqstp->rq_arg.len = svsk->sk_datalen;
rqstp->rq_arg.page_base = 0;
if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
@@ -1190,10 +1287,14 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
rqstp->rq_xprt_ctxt = NULL;
rqstp->rq_prot = IPPROTO_TCP;
if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
set_bit(RQ_LOCAL, &rqstp->rq_flags);
else
clear_bit(RQ_LOCAL, &rqstp->rq_flags);
assign_bit(RQ_LOCAL, &rqstp->rq_flags,
test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags));
/* Completing one message stops ->read_sock with whatever
* follows still queued, and no path from here re-arms XPT_DATA.
* The queued message would wait for unrelated traffic.
*/
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
calldir = p[1];
@@ -1219,19 +1320,21 @@ err_incomplete:
svc_tcp_save_pages(svsk, rqstp);
if (len < 0 && len != -EAGAIN)
goto err_delete;
if (len == want)
svc_tcp_fragment_received(svsk);
else
if (svsk->sk_tcplen >= sizeof(rpc_fraghdr))
trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
svc_sock_reclen(svsk),
svsk->sk_tcplen - sizeof(rpc_fraghdr));
else
trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
goto err_noclose;
error:
if (len != -EAGAIN)
goto err_delete;
trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
goto err_noclose;
err_nuts:
/* svc_tcp_save_pages() has not run, so svsk->sk_pages[] is
* empty. A non-zero sk_datalen makes the teardown-time
* svc_tcp_clear_pages() walk empty slots and WARN.
*/
svsk->sk_datalen = 0;
err_delete:
trace_svcsock_tcp_recv_err(&svsk->sk_xprt, len);
@@ -1541,6 +1644,9 @@ int svc_addsock(struct svc_serv *serv, struct net *net, const int fd,
err = -EISCONN;
if (so->state > SS_UNCONNECTED)
goto out;
err = -EBUSY;
if (so->sk->sk_user_data)
goto out;
err = -ENOENT;
if (!try_module_get(THIS_MODULE))
goto out;
+3
View File
@@ -1161,6 +1161,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
iov->iov_len += len;
}
xdr->rqst = rqst;
xdr->xdrgen_ctx = NULL;
}
EXPORT_SYMBOL_GPL(xdr_init_encode);
@@ -1180,6 +1181,7 @@ void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf)
xdr->p = page_address(*xdr->page_ptr);
xdr->end = (void *)xdr->p + min_t(u32, buf->buflen, PAGE_SIZE);
xdr->rqst = NULL;
xdr->xdrgen_ctx = NULL;
}
EXPORT_SYMBOL_GPL(xdr_init_encode_pages);
@@ -1618,6 +1620,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
xdr->p = p;
}
xdr->rqst = rqst;
xdr->xdrgen_ctx = NULL;
}
EXPORT_SYMBOL_GPL(xdr_init_decode);
+14 -4
View File
@@ -925,8 +925,9 @@ static noinline void svc_rdma_read_complete(struct svc_rqst *rqstp,
* %-ENOTCONN if posting failed (connection is lost),
* %-EIO if rdma_rw initialization failed (DMA mapping, etc).
*
* Called in a loop when XPT_DATA is set. XPT_DATA is cleared only
* when there are no remaining ctxt's to process.
* Called in a loop when XPT_DATA is set. XPT_DATA is cleared as
* soon as both receive queues are empty, so a consumed ctxt does
* not leave a stale bit behind.
*
* The next ctxt is removed from the "receive" lists.
*
@@ -960,6 +961,15 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q);
if (ctxt) {
list_del(&ctxt->rc_list);
/* Producers add to these queues and set XPT_DATA under
* this lock, so the clear cannot race one. The clear can
* drop the XPT_DATA that svc_rdma_send_ctxt_put() sets
* for sc_send_release_list. svc_xprt_release() drains
* that list before this thread looks for more work.
*/
if (list_empty(&rdma_xprt->sc_read_complete_q) &&
list_empty(&rdma_xprt->sc_rq_dto_q))
clear_bit(XPT_DATA, &xprt->xpt_flags);
spin_unlock(&rdma_xprt->sc_rq_dto_lock);
svc_xprt_received(xprt);
svc_rdma_read_complete(rqstp, ctxt);
@@ -968,8 +978,8 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_rq_dto_q);
if (ctxt)
list_del(&ctxt->rc_list);
else
/* No new incoming requests, terminate the loop */
/* sc_read_complete_q was empty above, under this same lock. */
if (list_empty(&rdma_xprt->sc_rq_dto_q))
clear_bit(XPT_DATA, &xprt->xpt_flags);
spin_unlock(&rdma_xprt->sc_rq_dto_lock);
+2 -1
View File
@@ -471,7 +471,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
newxprt->sc_max_requests = svcrdma_max_requests;
newxprt->sc_max_bc_requests = svcrdma_max_bc_requests;
newxprt->sc_recv_batch = RPCRDMA_MAX_RECV_BATCH;
newxprt->sc_fc_credits = cpu_to_be32(newxprt->sc_max_requests);
/* Qualify the transport's resource defaults with the
* capabilities of this particular device.
@@ -492,6 +491,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
newxprt->sc_max_bc_requests = 2;
}
newxprt->sc_fc_credits = cpu_to_be32(newxprt->sc_max_requests);
/* Estimate the needed number of rdma_rw contexts. The maximum
* Read and Write chunks have one segment each. Each request
* can involve one Read chunk and either a Write chunk or Reply
+150 -4
View File
@@ -147,8 +147,94 @@ Pragmas
-------
Pragma directives specify exceptions to the normal generation of
encoding and decoding functions. Currently one directive is
implemented: "public".
encoding and decoding functions. The directives are described
below.
Pragma aggregate
------ ---------
pragma aggregate <struct> <member> ;
A variable-length array member is normally encoded and decoded by
staging it in a C array and walking that array. When the application
object is not that array -- for instance when the member is a wire
projection of a different in-kernel structure -- building it is
wasted work. This directive marks such a member so that xdrgen emits a
codec that owns only the wire framing (the length prefix, the bound
check, and the per-element codec) and drives the application
through begin/item/end hooks, processing one element at a time with
no staged array. The marked member must be a struct member
declared either as a variable-length array or as an optional-data
list ("type *name"); xdrgen rejects a directive naming anything
else, and generates the codec for the server side only.
The two forms differ only in framing. A variable-length array
carries a u32 element count, so the framing writes that count and
bound-checks it. Its generated decoder hands each element to the
ordinary element decoder from a local variable, so the element
type may not contain a variable-length array or an optional-data
member at any depth; xdrgen rejects a directive whose element type
does. The array must declare a maximum size: the decoder hands the
wire count to the begin hook before it decodes any element, and
only the declared bound limits that count. xdrgen rejects a
directive naming an unbounded array. An optional-data list carries
no count: each element is prefixed by a value-follows TRUE and the
sequence is closed by a FALSE, which the framing writes after the
last element. The element type must therefore be a pointer type,
whose codec owns that framing; xdrgen rejects any other element
type. That form has no count to loop on, so its encoder pulls
elements until the item hook returns false. False ends the list
rather than failing the encode: a producer stops when its source
is exhausted or when a reply budget fills. A producer that stops
for an error records it in its own state and either fails the
encode from the end hook or reports it in the procedure status.
For the optional-data form xdrgen generates a working
encoder only; the decoder it emits fails without consuming the
list, so xdrgen also rejects the directive when the struct is
reachable from an RPC argument.
For example:
pragma aggregate secattr aclent;
pragma aggregate secattr dfaclent;
The hooks are supplied by hand. Their names derive from the pragma
header and the containing struct, so that two programs sharing a
type name resolve to distinct symbols. All marked members of one
struct share a hook set, so they share one element type as well;
xdrgen rejects a marked member whose element type differs from
that of a member already marked in the same struct. The cursor's
member_id field selects the member:
nfs_acl2_secattr_encode_begin()
nfs_acl2_secattr_encode()
nfs_acl2_secattr_encode_end()
nfs_acl2_secattr_decode_begin()
nfs_acl2_secattr_decode()
nfs_acl2_secattr_decode_end()
For a variable-length array, a decoder fills the cursor's count from
the wire before calling the begin hook, and an encoder's begin hook
sets that count for the framing to bound-check and emit. An
optional-data list's encoder does not read the count and its
decoder never fills it. Once a begin hook
has succeeded its end hook runs, so that it can release what begin
took; the end hook receives the running success flag.
For each marked member xdrgen emits an enumeration constant -- the
pragma header, struct, and member joined by underscores and
upper-cased -- in the generated header, and the generated framing
initializes the cursor's member_id with it. The hooks compare
member_id against these constants rather than bare integers:
NFS_ACL2_SECATTR_ACLENT
NFS_ACL2_SECATTR_DFACLENT
The hooks reach their application state through the xdr_stream's
xdrgen_ctx pointer: svcxdr_init_decode() and svcxdr_init_encode()
bind it to the svc_rqst, and the generated framing copies it into
the cursor's ctx field, from which a hook reaches rq_argp or
rq_resp. xdrgen emits the hook prototypes in the generated header.
Pragma big_endian
------ ----------
@@ -230,6 +316,65 @@ Adds these prototypes in the generated header:
And, in the generated source code, both of these functions appear
without the "static __maybe_unused" modifiers.
Pragma pages
------ -----
pragma pages <type> <member> ;
A variable-length opaque or string member is normally encoded by
copying its content into the Reply buffer. When the member carries
a bulk payload that a server-side read operation has already
deposited in the pages of the Reply buffer, that copy is wasted
work, and it prevents a transport capable of direct data placement
from conveying the payload separately from the rest of the Reply.
This directive marks such a member so that the emitted encoder
inserts the payload pages into the encoding stream by reference and
marks the byte range as a result payload.
The marked member may be a member of a struct or an arm of a union,
and its type may be named directly or reached through a typedef. A
union arm declared directly as a string is the one exception: xdrgen
generates such an arm as a char *, which carries no length field, so
the directive is rejected there and the arm has to be declared
through a typedef. For example:
pragma pages READ3resok data;
pragma pages readlinkres data;
The first marks the opaque data member of struct READ3resok; the
second marks the path arm of union readlinkres, where path is
"typedef string path<NFS_MAXPATHLEN>".
Whether the directive drives the encoder or the decoder follows
from how the containing type is used. A server decodes arguments
and encodes results, so the directive on a member of a result type
(such as READ3resok) marks a payload to encode by reference, while
the directive on a member of an argument type (such as the data
member of an NFS WRITE argument) marks a payload to decode in place.
An argument type here is the struct named as an RPC procedure
argument, any aggregate reachable from it, and any typedef alias of
either; a result type is defined the same way. A decoded member
and an encoded one take different C representations, so a marked
type that is reachable from both an argument and a result is
rejected. The directive on a union arm of an argument type is
rejected as well:
the union generator emits only the ordinary copying decoder, so a
page-resident union arm needs generator support that does not yet
exist.
The emitted encoder reads only the marked member's length field to
determine how many octets of page content to insert; the member's
data pointer is not read. The emitted decoder reads the length
prefix and captures the payload octets by reference, leaving them in
the pages of the Receive buffer. A decoded page-resident member is
represented as a struct xdr_buf so that a later stage can hand the
payload to the server's file system without a copy.
The server and client manage payload pages differently, so this
directive applies only to server-side code generation. Client-side
handling of a page-resident member remains future work.
Future Work
-----------
@@ -242,11 +387,12 @@ Expand the README into a user guide similar to rpcgen(1)
Add more pragma directives:
* @pages -- use xdr_read/write_pages() for the specified opaque
field
* @skip -- do not decode, but rather skip, the specified argument
field
Extend pragma pages to client-side code generation, and to union
arms of RPC procedure arguments
Enable something like a #include to dynamically insert the content
of other specification files
+56 -12
View File
@@ -13,6 +13,7 @@ from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration
from xdr_ast import public_apis, get_header_name
from xdr_ast import pages_members, pages_member_maxsize, pages_member_is_decoded
def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None:
@@ -23,9 +24,13 @@ def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> Non
def emit_pointer_member_definition(
environment: Environment, field: _XdrDeclaration
environment: Environment, field: _XdrDeclaration, struct_name: str, peer: str
) -> None:
"""Emit a definition for one field in an XDR struct"""
if peer == "server" and pages_member_is_decoded(struct_name, field.name):
template = get_jinja2_template(environment, "definition", "pages_opaque")
print(template.render(name=field.name))
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "definition", field.template)
print(
@@ -78,22 +83,36 @@ def emit_pointer_member_definition(
)
def emit_pointer_definition(environment: Environment, node: _XdrPointer) -> None:
def emit_pointer_definition(
environment: Environment, node: _XdrPointer, peer: str
) -> None:
"""Emit a definition for an XDR pointer type"""
template = get_jinja2_template(environment, "definition", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_definition(environment, field)
emit_pointer_member_definition(environment, field, node.name, peer)
template = get_jinja2_template(environment, "definition", "close")
print(template.render(name=node.name))
def emit_pointer_member_decoder(
environment: Environment, field: _XdrDeclaration
environment: Environment,
field: _XdrDeclaration,
struct_name: str,
peer: str,
) -> None:
"""Emit a decoder for one field in an XDR pointer"""
if peer == "server" and pages_member_is_decoded(struct_name, field.name):
template = get_jinja2_template(environment, "decoder", "pages_opaque")
print(
template.render(
name=field.name,
maxsize=pages_member_maxsize(field),
)
)
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "decoder", field.template)
print(
@@ -158,22 +177,45 @@ def emit_pointer_member_decoder(
)
def emit_pointer_decoder(environment: Environment, node: _XdrPointer) -> None:
def emit_pointer_decoder(
environment: Environment, node: _XdrPointer, peer: str
) -> None:
"""Emit one decoder function for an XDR pointer type"""
template = get_jinja2_template(environment, "decoder", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_decoder(environment, field)
emit_pointer_member_decoder(environment, field, node.name, peer)
template = get_jinja2_template(environment, "decoder", "close")
print(template.render())
def emit_pointer_member_encoder(
environment: Environment, field: _XdrDeclaration
environment: Environment,
field: _XdrDeclaration,
struct_name: str,
peer: str,
) -> None:
"""Emit an encoder for one field in a XDR pointer"""
if (struct_name, field.name) in pages_members:
if peer != "server":
raise NotImplementedError(
"pragma pages is server-side only; "
+ peer
+ " generation is not yet supported"
)
# Both representations carry the length field, which is all the
# pages encoder reads, so pages_member_is_decoded() does not
# gate this site.
template = get_jinja2_template(environment, "encoder", "pages_opaque")
print(
template.render(
name=field.name,
maxsize=pages_member_maxsize(field),
)
)
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "encoder", field.template)
print(
@@ -235,13 +277,15 @@ def emit_pointer_member_encoder(
)
def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None:
def emit_pointer_encoder(
environment: Environment, node: _XdrPointer, peer: str
) -> None:
"""Emit one encoder function for an XDR pointer type"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_encoder(environment, field)
emit_pointer_member_encoder(environment, field, node.name, peer)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
@@ -273,15 +317,15 @@ class XdrPointerGenerator(SourceGenerator):
def emit_definition(self, node: _XdrPointer) -> None:
"""Emit one declaration for an XDR pointer type"""
emit_pointer_definition(self.environment, node)
emit_pointer_definition(self.environment, node, self.peer)
def emit_decoder(self, node: _XdrPointer) -> None:
"""Emit one decoder function for an XDR pointer type"""
emit_pointer_decoder(self.environment, node)
emit_pointer_decoder(self.environment, node, self.peer)
def emit_encoder(self, node: _XdrPointer) -> None:
"""Emit one encoder function for an XDR pointer type"""
emit_pointer_encoder(self.environment, node)
emit_pointer_encoder(self.environment, node, self.peer)
def emit_maxsize(self, node: _XdrPointer) -> None:
"""Emit one maxsize macro for an XDR pointer type"""
+190 -13
View File
@@ -12,7 +12,31 @@ from xdr_ast import _XdrBasic, _XdrString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration
from xdr_ast import public_apis, get_header_name
from xdr_ast import public_apis, get_header_name, aggregate_members
from xdr_ast import pages_members, pages_member_maxsize, pages_member_is_decoded
def aggregate_hook_base(struct_name: str) -> str:
"""Return the application hook base name for a struct's aggregate members.
The pragma header name prefixes it so two programs that share a
type name (nfs_acl2 vs nfs_acl3) derive distinct external hook
symbols. All marked members of one struct share a hook set; the
cursor's member_id tells them apart.
"""
return "_".join((get_header_name(), struct_name))
def aggregate_member_symbol(struct_name: str, member_name: str) -> str:
"""Return the symbolic member id for one marked aggregate member.
The generated framing sets the cursor's member_id to this constant
and the hooks compare against it. The enum in aggregate_hooks.j2
assigns the values implicitly in field order, so a hook that
compared against the bare integer would silently bind to the
wrong member once the specification's members were reordered.
"""
return "_".join((aggregate_hook_base(struct_name), member_name)).upper()
def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
@@ -20,12 +44,36 @@ def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
if node.name in public_apis:
template = get_jinja2_template(environment, "declaration", "close")
print(template.render(name=node.name))
marked = [
field
for field in node.fields
if (node.name, field.name) in aggregate_members
]
if marked:
template = get_jinja2_template(
environment, "declaration", "aggregate_hooks"
)
print(
template.render(
hook=aggregate_hook_base(node.name),
c_type=kernel_c_type(marked[0].spec),
classifier=marked[0].spec.c_classifier,
members=[
aggregate_member_symbol(node.name, field.name)
for field in marked
],
)
)
def emit_struct_member_definition(
environment: Environment, field: _XdrDeclaration
environment: Environment, field: _XdrDeclaration, struct_name: str, peer: str
) -> None:
"""Emit a definition for one field in an XDR struct"""
if peer == "server" and pages_member_is_decoded(struct_name, field.name):
template = get_jinja2_template(environment, "definition", "pages_opaque")
print(template.render(name=field.name))
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "definition", field.template)
print(
@@ -78,22 +126,79 @@ def emit_struct_member_definition(
)
def emit_struct_definition(environment: Environment, node: _XdrStruct) -> None:
def emit_struct_definition(
environment: Environment, node: _XdrStruct, peer: str
) -> None:
"""Emit one definition for an XDR struct type"""
template = get_jinja2_template(environment, "definition", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_definition(environment, field)
emit_struct_member_definition(environment, field, node.name, peer)
template = get_jinja2_template(environment, "definition", "close")
print(template.render(name=node.name))
def emit_struct_member_decoder(
environment: Environment, field: _XdrDeclaration
environment: Environment,
field: _XdrDeclaration,
struct_name: str,
peer: str,
) -> None:
"""Emit a decoder for one field in an XDR struct"""
if peer == "server" and pages_member_is_decoded(struct_name, field.name):
template = get_jinja2_template(environment, "decoder", "pages_opaque")
print(
template.render(
name=field.name,
maxsize=pages_member_maxsize(field),
)
)
return
if isinstance(field, _XdrVariableLengthArray) and (
(struct_name, field.name) in aggregate_members
):
if peer != "server":
raise NotImplementedError(
"pragma aggregate is server-side only; "
+ peer
+ " generation is not yet supported"
)
template = get_jinja2_template(environment, "decoder", "aggregate_array")
print(
template.render(
name=field.name,
type=field.spec.type_name,
c_type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
maxsize=field.maxsize,
hook=aggregate_hook_base(struct_name),
member_sym=aggregate_member_symbol(struct_name, field.name),
)
)
return
if isinstance(field, _XdrOptionalData) and (
(struct_name, field.name) in aggregate_members
):
if peer != "server":
raise NotImplementedError(
"pragma aggregate is server-side only; "
+ peer
+ " generation is not yet supported"
)
template = get_jinja2_template(environment, "decoder", "aggregate_optional")
print(
template.render(
name=field.name,
type=field.spec.type_name,
c_type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
hook=aggregate_hook_base(struct_name),
member_sym=aggregate_member_symbol(struct_name, field.name),
)
)
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "decoder", field.template)
print(
@@ -158,22 +263,94 @@ def emit_struct_member_decoder(
)
def emit_struct_decoder(environment: Environment, node: _XdrStruct) -> None:
def emit_struct_decoder(
environment: Environment, node: _XdrStruct, peer: str
) -> None:
"""Emit one decoder function for an XDR struct type"""
template = get_jinja2_template(environment, "decoder", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_decoder(environment, field)
emit_struct_member_decoder(environment, field, node.name, peer)
# The optional-data aggregate decoder fails and closes the
# function; the members after it are never decoded.
if isinstance(field, _XdrOptionalData) and (
(node.name, field.name) in aggregate_members
):
return
template = get_jinja2_template(environment, "decoder", "close")
print(template.render())
def emit_struct_member_encoder(
environment: Environment, field: _XdrDeclaration
environment: Environment,
field: _XdrDeclaration,
struct_name: str,
peer: str,
) -> None:
"""Emit an encoder for one field in an XDR struct"""
if isinstance(field, _XdrVariableLengthArray) and (
(struct_name, field.name) in aggregate_members
):
if peer != "server":
raise NotImplementedError(
"pragma aggregate is server-side only; "
+ peer
+ " generation is not yet supported"
)
template = get_jinja2_template(environment, "encoder", "aggregate_array")
print(
template.render(
name=field.name,
type=field.spec.type_name,
c_type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
maxsize=field.maxsize,
hook=aggregate_hook_base(struct_name),
member_sym=aggregate_member_symbol(struct_name, field.name),
)
)
return
if isinstance(field, _XdrOptionalData) and (
(struct_name, field.name) in aggregate_members
):
if peer != "server":
raise NotImplementedError(
"pragma aggregate is server-side only; "
+ peer
+ " generation is not yet supported"
)
template = get_jinja2_template(environment, "encoder", "aggregate_optional")
print(
template.render(
name=field.name,
type=field.spec.type_name,
c_type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
hook=aggregate_hook_base(struct_name),
member_sym=aggregate_member_symbol(struct_name, field.name),
)
)
return
if (struct_name, field.name) in pages_members:
if peer != "server":
raise NotImplementedError(
"pragma pages is server-side only; "
+ peer
+ " generation is not yet supported"
)
# Both representations carry the length field, which is all the
# pages encoder reads, so pages_member_is_decoded() does not
# gate this site.
template = get_jinja2_template(environment, "encoder", "pages_opaque")
print(
template.render(
name=field.name,
maxsize=pages_member_maxsize(field),
)
)
return
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "encoder", field.template)
print(
@@ -235,13 +412,13 @@ def emit_struct_member_encoder(
)
def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None:
def emit_struct_encoder(environment: Environment, node: _XdrStruct, peer: str) -> None:
"""Emit one encoder function for an XDR struct type"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_encoder(environment, field)
emit_struct_member_encoder(environment, field, node.name, peer)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
@@ -273,15 +450,15 @@ class XdrStructGenerator(SourceGenerator):
def emit_definition(self, node: _XdrStruct) -> None:
"""Emit one definition for an XDR struct type"""
emit_struct_definition(self.environment, node)
emit_struct_definition(self.environment, node, self.peer)
def emit_decoder(self, node: _XdrStruct) -> None:
"""Emit one decoder function for an XDR struct type"""
emit_struct_decoder(self.environment, node)
emit_struct_decoder(self.environment, node, self.peer)
def emit_encoder(self, node: _XdrStruct) -> None:
"""Emit one encoder function for an XDR struct type"""
emit_struct_encoder(self.environment, node)
emit_struct_encoder(self.environment, node, self.peer)
def emit_maxsize(self, node: _XdrStruct) -> None:
"""Emit one maxsize macro for an XDR struct type"""
+55 -14
View File
@@ -9,7 +9,8 @@ from generators import SourceGenerator
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, _XdrString, get_header_name
from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis, big_endian
from xdr_ast import _XdrDeclaration, _XdrCaseSpec
from xdr_ast import public_apis, big_endian, pages_members, pages_member_maxsize
def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None:
@@ -214,13 +215,41 @@ def emit_union_switch_spec_encoder(
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_pages_arm_encoder(
environment: Environment, union_name: str, arm: _XdrDeclaration, peer: str
) -> bool:
"""Emit a page-resident encoder for a "pragma pages" union arm
Return True when the arm is pages-marked and was emitted here, so
the caller skips the ordinary arm encoder.
"""
if (union_name, arm.name) not in pages_members:
return False
if peer != "server":
raise NotImplementedError(
"pragma pages is server-side only; "
+ peer
+ " generation is not yet supported"
)
template = get_jinja2_template(environment, "encoder", "pages_opaque")
print(
template.render(
name=arm.name,
maxsize=pages_member_maxsize(arm),
)
)
return True
def emit_union_arm_encoder(
environment: Environment, node: _XdrCaseSpec
environment: Environment, node: _XdrCaseSpec, union_name: str, peer: str
) -> None:
"""Emit encoder for an XDR union's arm (data only, no case/break)"""
if isinstance(node.arm, _XdrVoid):
return
if emit_union_pages_arm_encoder(environment, union_name, node.arm, peer):
return
if isinstance(node.arm, _XdrString):
type_name = "char *"
else:
@@ -237,7 +266,11 @@ def emit_union_arm_encoder(
def emit_union_case_spec_encoder(
environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool
environment: Environment,
node: _XdrCaseSpec,
big_endian_discriminant: bool,
union_name: str,
peer: str,
) -> None:
"""Emit encoder functions for an XDR union's case arm"""
@@ -254,19 +287,22 @@ def emit_union_case_spec_encoder(
for case in node.values:
print(template.render(case=case))
template = get_jinja2_template(environment, "encoder", node.arm.template)
print(
template.render(
name=node.arm.name,
type=type_name,
if not emit_union_pages_arm_encoder(environment, union_name, node.arm, peer):
template = get_jinja2_template(environment, "encoder", node.arm.template)
print(
template.render(
name=node.arm.name,
type=type_name,
)
)
)
template = get_jinja2_template(environment, "encoder", "break")
print(template.render())
def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None:
def emit_union_default_spec_encoder(
environment: Environment, node: _XdrUnion, peer: str
) -> None:
"""Emit an encoder function for an XDR union's default arm"""
default_case = node.default
@@ -282,6 +318,9 @@ def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -
print(template.render())
return
if emit_union_pages_arm_encoder(environment, node.name, default_case.arm, peer):
return
template = get_jinja2_template(environment, "encoder", default_case.arm.template)
print(
template.render(
@@ -291,7 +330,7 @@ def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -
)
def emit_union_encoder(environment, node: _XdrUnion) -> None:
def emit_union_encoder(environment, node: _XdrUnion, peer: str) -> None:
"""Emit one XDR union encoder"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
@@ -304,7 +343,7 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None:
# Find and emit the TRUE case
for case in node.cases:
if case.values and case.values[0] == "TRUE":
emit_union_arm_encoder(environment, case)
emit_union_arm_encoder(environment, case, node.name, peer)
break
template = get_jinja2_template(environment, "encoder", "close")
@@ -317,9 +356,11 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None:
environment,
case,
node.discriminant.spec.type_name in big_endian,
node.name,
peer,
)
emit_union_default_spec_encoder(environment, node)
emit_union_default_spec_encoder(environment, node, peer)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
@@ -359,7 +400,7 @@ class XdrUnionGenerator(SourceGenerator):
def emit_encoder(self, node: _XdrUnion) -> None:
"""Emit one encoder function for an XDR union"""
emit_union_encoder(self.environment, node)
emit_union_encoder(self.environment, node, self.peer)
def emit_maxsize(self, node: _XdrUnion) -> None:
"""Emit one maxsize macro for an XDR union"""
@@ -100,6 +100,7 @@ directive : big_endian_directive
| pages_directive
| public_directive
| skip_directive
| aggregate_directive
big_endian_directive : "big_endian"
exclude_directive : "exclude"
@@ -107,6 +108,7 @@ header_directive : "header"
pages_directive : "pages"
public_directive : "public"
skip_directive : "skip"
aggregate_directive : "aggregate"
//
// XDR language primitives
+12 -7
View File
@@ -5,6 +5,7 @@
can be compiled for the Linux kernel."""
import logging
import sys
from argparse import Namespace
from lark import logger
@@ -126,12 +127,16 @@ def subcmd(args: Namespace) -> int:
except XdrSemanticError as e:
handle_semantic_error(e, source, args.filename)
return 1
match args.peer:
case "server":
generate_server_source(args.filename, ast, args.language)
case "client":
generate_client_source(args.filename, ast, args.language)
case _:
print("Code generation for", args.peer, "is not yet supported")
try:
match args.peer:
case "server":
generate_server_source(args.filename, ast, args.language)
case "client":
generate_client_source(args.filename, ast, args.language)
case _:
print("Code generation for", args.peer, "is not yet supported")
except NotImplementedError as e:
sys.stderr.write(f"{args.filename}: {e}\n")
return 1
return 0
@@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (page-resident variable-length opaque) */
{% endif %}
if (!svcxdr_decode_opaque_payload(xdr, &ptr->{{ name }}, {{ maxsize }}))
return false;
@@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (page-resident variable-length opaque) */
{% endif %}
struct xdr_buf {{ name }};
@@ -0,0 +1,10 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (page-resident variable-length opaque) */
{% endif %}
{% if maxsize != "0" %}
if (value->{{ name }}.len > {{ maxsize }})
return false;
{% endif %}
if (!svcxdr_encode_opaque_payload(xdr, value->{{ name }}.len))
return false;
@@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
enum {
{% for member in members %}
{{ member }},
{% endfor %}
};
bool {{ hook }}_encode_begin(struct xdrgen_aggregate_cursor *c);
bool {{ hook }}_encode(struct xdrgen_aggregate_cursor *c, {{ classifier }}{{ c_type }} *out);
bool {{ hook }}_encode_end(struct xdrgen_aggregate_cursor *c, bool ok);
bool {{ hook }}_decode_begin(struct xdrgen_aggregate_cursor *c);
bool {{ hook }}_decode(struct xdrgen_aggregate_cursor *c, const {{ classifier }}{{ c_type }} *in);
bool {{ hook }}_decode_end(struct xdrgen_aggregate_cursor *c, bool ok);
@@ -0,0 +1,33 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (aggregate array) */
{% endif %}
{
struct xdrgen_aggregate_cursor cursor = {
.xdr = xdr,
.member_id = {{ member_sym }},
.ctx = xdr->xdrgen_ctx,
};
bool ok = true;
if (xdr_stream_decode_u32(xdr, &cursor.count) < 0)
return false;
if (cursor.count > {{ maxsize }})
return false;
if (!{{ hook }}_decode_begin(&cursor))
return false;
for (cursor.index = 0; cursor.index < cursor.count; cursor.index++) {
{{ classifier }}{{ c_type }} element = {};
if (!xdrgen_decode_{{ type }}(xdr, &element)) {
ok = false;
break;
}
if (!{{ hook }}_decode(&cursor, &element)) {
ok = false;
break;
}
}
if (!{{ hook }}_decode_end(&cursor, ok) || !ok)
return false;
}
@@ -0,0 +1,10 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{# The element decoder does not report presence, so the value-follows
framing cannot be decoded. Failing here keeps a caller from decoding
the members that follow from the list's own data. This closes the
function, so the struct decoder emits nothing after it. #}
{% if annotate %}
/* member {{ name }} (aggregate list): not decodable */
{% endif %}
return false;
}
@@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (page-resident variable-length opaque) */
{% endif %}
if (!svcxdr_decode_opaque_payload(xdr, &ptr->{{ name }}, {{ maxsize }}))
return false;
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff. Show more