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

Pull nfsd updates from Chuck Lever:
 "Neil Brown and Jeff Layton contributed a dynamic thread pool sizing
  mechanism for NFSD. The sunrpc layer now tracks minimum and maximum
  thread counts per pool, and NFSD adjusts running thread counts based
  on workload: idle threads exit after a timeout when the pool exceeds
  its minimum, and new threads spawn automatically when all threads are
  busy. Administrators control this behavior via the nfsdctl netlink
  interface.

  Rick Macklem, FreeBSD NFS maintainer, generously contributed server-
  side support for the POSIX ACL extension to NFSv4, as specified in
  draft-ietf-nfsv4-posix-acls. This extension allows NFSv4 clients to
  get and set POSIX access and default ACLs using native NFSv4
  operations, eliminating the need for sideband protocols. The feature
  is gated by a Kconfig option since the IETF draft has not yet been
  ratified.

  Chuck Lever delivered numerous improvements to the xdrgen tool. Error
  reporting now covers parsing, AST transformation, and invalid
  declarations. Generated enum decoders validate incoming values against
  valid enumerator lists. New features include pass-through line support
  for embedding C directives in XDR specifications, 16-bit integer
  types, and program number definitions. Several code generation issues
  were also addressed.

  When an administrator revokes NFSv4 state for a filesystem via the
  unlock_fs interface, ongoing async COPY operations referencing that
  filesystem are now cancelled, with CB_OFFLOAD callbacks notifying
  affected clients.

  The remaining patches in this pull request are clean-ups and minor
  optimizations. Sincere thanks to all contributors, reviewers, testers,
  and bug reporters who participated in the v7.0 NFSD development cycle"

* tag 'nfsd-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (45 commits)
  NFSD: Add POSIX ACL file attributes to SUPPATTR bitmasks
  NFSD: Add POSIX draft ACL support to the NFSv4 SETATTR operation
  NFSD: Add support for POSIX draft ACLs for file creation
  NFSD: Add support for XDR decoding POSIX draft ACLs
  NFSD: Refactor nfsd_setattr()'s ACL error reporting
  NFSD: Do not allow NFSv4 (N)VERIFY to check POSIX ACL attributes
  NFSD: Add nfsd4_encode_fattr4_posix_access_acl
  NFSD: Add nfsd4_encode_fattr4_posix_default_acl
  NFSD: Add nfsd4_encode_fattr4_acl_trueform_scope
  NFSD: Add nfsd4_encode_fattr4_acl_trueform
  Add RPC language definition of NFSv4 POSIX ACL extension
  NFSD: Add a Kconfig setting to enable support for NFSv4 POSIX ACLs
  xdrgen: Implement pass-through lines in specifications
  nfsd: cancel async COPY operations when admin revokes filesystem state
  nfsd: add controls to set the minimum number of threads per pool
  nfsd: adjust number of running nfsd threads based on activity
  sunrpc: allow svc_recv() to return -ETIMEDOUT and -EBUSY
  sunrpc: split new thread creation into a separate function
  sunrpc: introduce the concept of a minimum number of threads per pool
  sunrpc: track the max number of requested threads in a pool
  ...
This commit is contained in:
Linus Torvalds
2026-02-12 08:23:53 -08:00
68 changed files with 2267 additions and 372 deletions
+5
View File
@@ -78,6 +78,9 @@ attribute-sets:
-
name: scope
type: string
-
name: min-threads
type: u32
-
name: version
attributes:
@@ -159,6 +162,7 @@ operations:
- gracetime
- leasetime
- scope
- min-threads
-
name: threads-get
doc: get the number of running threads
@@ -170,6 +174,7 @@ operations:
- gracetime
- leasetime
- scope
- min-threads
-
name: version-set
doc: set nfs enabled versions
+61
View File
@@ -53,6 +53,11 @@ typedef unsigned int uint32_t;
*/
typedef uint32_t bitmap4<>;
typedef opaque utf8string<>;
typedef utf8string utf8str_cis;
typedef utf8string utf8str_cs;
typedef utf8string utf8str_mixed;
/*
* Timeval
*/
@@ -184,3 +189,59 @@ enum open_delegation_type4 {
OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5
};
/*
* The following content was extracted from draft-ietf-nfsv4-posix-acls
*/
enum aclmodel4 {
ACL_MODEL_NFS4 = 1,
ACL_MODEL_POSIX_DRAFT = 2,
ACL_MODEL_NONE = 3
};
pragma public aclmodel4;
enum aclscope4 {
ACL_SCOPE_FILE_OBJECT = 1,
ACL_SCOPE_FILE_SYSTEM = 2,
ACL_SCOPE_SERVER = 3
};
pragma public aclscope4;
enum posixacetag4 {
POSIXACE4_TAG_USER_OBJ = 1,
POSIXACE4_TAG_USER = 2,
POSIXACE4_TAG_GROUP_OBJ = 3,
POSIXACE4_TAG_GROUP = 4,
POSIXACE4_TAG_MASK = 5,
POSIXACE4_TAG_OTHER = 6
};
pragma public posixacetag4;
typedef uint32_t posixaceperm4;
pragma public posixaceperm4;
/* Bit definitions for posixaceperm4. */
const POSIXACE4_PERM_EXECUTE = 0x00000001;
const POSIXACE4_PERM_WRITE = 0x00000002;
const POSIXACE4_PERM_READ = 0x00000004;
struct posixace4 {
posixacetag4 tag;
posixaceperm4 perm;
utf8str_mixed who;
};
typedef aclmodel4 fattr4_acl_trueform;
typedef aclscope4 fattr4_acl_trueform_scope;
typedef posixace4 fattr4_posix_default_acl<>;
typedef posixace4 fattr4_posix_access_acl<>;
%/*
% * New for POSIX ACL extension
% */
const FATTR4_ACL_TRUEFORM = 89;
const FATTR4_ACL_TRUEFORM_SCOPE = 90;
const FATTR4_POSIX_DEFAULT_ACL = 91;
const FATTR4_POSIX_ACCESS_ACL = 92;
+3 -3
View File
@@ -141,7 +141,7 @@ lockd(void *vrqstp)
*/
while (!svc_thread_should_stop(rqstp)) {
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp);
svc_recv(rqstp, 0);
}
if (nlmsvc_ops)
nlmsvc_invalidate_all();
@@ -340,7 +340,7 @@ static int lockd_get(void)
return -ENOMEM;
}
error = svc_set_num_threads(serv, NULL, 1);
error = svc_set_num_threads(serv, 0, 1);
if (error < 0) {
svc_destroy(&serv);
return error;
@@ -368,7 +368,7 @@ static void lockd_put(void)
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif
svc_set_num_threads(nlmsvc_serv, NULL, 0);
svc_set_num_threads(nlmsvc_serv, 0, 0);
timer_delete_sync(&nlmsvc_retry);
svc_destroy(&nlmsvc_serv);
dprintk("lockd_down: service destroyed\n");
-4
View File
@@ -641,10 +641,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
error = vfs_test_lock(file->f_file[mode], &conflock->fl);
if (error) {
/* We can't currently deal with deferred test requests */
if (error == FILE_LOCK_DEFERRED)
WARN_ON_ONCE(1);
ret = nlm_lck_denied_nolocks;
goto out;
}
+14 -3
View File
@@ -2262,12 +2262,23 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
*/
int vfs_test_lock(struct file *filp, struct file_lock *fl)
{
int error = 0;
WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops);
WARN_ON_ONCE(filp != fl->c.flc_file);
if (filp->f_op->lock)
return filp->f_op->lock(filp, F_GETLK, fl);
posix_test_lock(filp, fl);
return 0;
error = filp->f_op->lock(filp, F_GETLK, fl);
else
posix_test_lock(filp, fl);
/*
* We don't expect FILE_LOCK_DEFERRED and callers cannot
* handle it.
*/
if (WARN_ON_ONCE(error == FILE_LOCK_DEFERRED))
error = -EIO;
return error;
}
EXPORT_SYMBOL_GPL(vfs_test_lock);
+5 -5
View File
@@ -81,7 +81,7 @@ nfs4_callback_svc(void *vrqstp)
set_freezable();
while (!svc_thread_should_stop(rqstp))
svc_recv(rqstp);
svc_recv(rqstp, 0);
svc_exit_thread(rqstp);
return 0;
@@ -119,9 +119,9 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
if (serv->sv_nrthreads == nrservs)
return 0;
ret = svc_set_num_threads(serv, NULL, nrservs);
ret = svc_set_num_threads(serv, 0, nrservs);
if (ret) {
svc_set_num_threads(serv, NULL, 0);
svc_set_num_threads(serv, 0, 0);
return ret;
}
dprintk("nfs_callback_up: service started\n");
@@ -242,7 +242,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
cb_info->users++;
err_net:
if (!cb_info->users) {
svc_set_num_threads(cb_info->serv, NULL, 0);
svc_set_num_threads(cb_info->serv, 0, 0);
svc_destroy(&cb_info->serv);
}
err_create:
@@ -268,7 +268,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
nfs_callback_down_net(minorversion, serv, net);
cb_info->users--;
if (cb_info->users == 0) {
svc_set_num_threads(serv, NULL, 0);
svc_set_num_threads(serv, 0, 0);
dprintk("nfs_callback_down: service destroyed\n");
xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv);
}
+19
View File
@@ -186,3 +186,22 @@ config NFSD_V4_DELEG_TIMESTAMPS
draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This
is currently an experimental feature and is therefore left disabled
by default.
config NFSD_V4_POSIX_ACLS
bool "Support NFSv4 POSIX draft ACLs"
depends on NFSD_V4
default n
help
Include experimental support for POSIX Access Control Lists
(ACLs) in NFSv4 as specified in the IETF draft
draft-ietf-nfsv4-posix-acls. This protocol extension enables
NFSv4 clients to retrieve and modify POSIX ACLs on exported
filesystems that support them.
This feature is based on an unratified IETF draft
specification that may change in ways that impact
interoperability with existing clients. Enable only for
testing environments or when interoperability with specific
clients that implement this draft is required.
If unsure, say N.
+9 -1
View File
@@ -26,7 +26,15 @@ nfsd-$(CONFIG_NFSD_FLEXFILELAYOUT) += flexfilelayout.o flexfilelayoutxdr.o
nfsd-$(CONFIG_NFS_LOCALIO) += localio.o
nfsd-$(CONFIG_DEBUG_FS) += debugfs.o
#
# XDR code generation (requires Python and additional packages)
#
# The generated *xdr_gen.{h,c} files are checked into git. Normal kernel
# builds do not require the xdrgen tool or its Python dependencies.
#
# Developers modifying .x files in Documentation/sunrpc/xdr/ should run
# "make xdrgen" to regenerate the affected files.
#
.PHONY: xdrgen
xdrgen: ../../include/linux/sunrpc/xdrgen/nfs4_1.h nfs4xdr_gen.h nfs4xdr_gen.c
+1
View File
@@ -49,5 +49,6 @@ int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_acl **acl);
__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
struct nfsd_attrs *attr);
void sort_pacl_range(struct posix_acl *pacl, int start, int end);
#endif /* LINUX_NFS4_ACL_H */
+3 -2
View File
@@ -24,11 +24,12 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = {
};
/* NFSD_CMD_THREADS_SET - do */
static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = {
static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = {
[NFSD_A_SERVER_THREADS] = { .type = NLA_U32, },
[NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, },
[NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, },
[NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, },
[NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, },
};
/* NFSD_CMD_VERSION_SET - do */
@@ -57,7 +58,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.cmd = NFSD_CMD_THREADS_SET,
.doit = nfsd_nl_threads_set_doit,
.policy = nfsd_threads_set_nl_policy,
.maxattr = NFSD_A_SERVER_SCOPE,
.maxattr = NFSD_A_SERVER_MIN_THREADS,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
+6 -1
View File
@@ -67,7 +67,6 @@ struct nfsd_net {
struct lock_manager nfsd4_manager;
bool grace_ended;
bool grace_end_forced;
bool client_tracking_active;
time64_t boot_time;
struct dentry *nfsd_client_dir;
@@ -130,6 +129,12 @@ struct nfsd_net {
seqlock_t writeverf_lock;
unsigned char writeverf[8];
/*
* Minimum number of threads to run per pool. If 0 then the
* min == max requested number of threads.
*/
unsigned int min_threads;
u32 clientid_base;
u32 clientid_counter;
u32 clverifier_counter;
+1 -1
View File
@@ -45,7 +45,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
inode = d_inode(fh->fh_dentry);
if (argp->mask & ~NFS_ACL_MASK) {
resp->status = nfserr_inval;
resp->status = nfserr_io;
goto out;
}
resp->mask = argp->mask;
+13 -4
View File
@@ -369,12 +369,21 @@ pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2)
return false;
}
static void
sort_pacl_range(struct posix_acl *pacl, int start, int end) {
/**
* sort_pacl_range - sort a range of POSIX ACL entries by tag and id
* @pacl: POSIX ACL containing entries to sort
* @start: starting index of range to sort
* @end: ending index of range to sort (inclusive)
*
* Sorts ACL entries in place so that USER entries are ordered by UID
* and GROUP entries are ordered by GID. Required before calling
* posix_acl_valid().
*/
void sort_pacl_range(struct posix_acl *pacl, int start, int end)
{
int sorted = 0, i;
/* We just do a bubble sort; easy to do in place, and we're not
* expecting acl's to be long enough to justify anything more. */
/* Bubble sort: acceptable here because ACLs are typically short. */
while (!sorted) {
sorted = 1;
for (i = start; i < end; i++) {
+46 -6
View File
@@ -643,34 +643,74 @@ static __be32 encode_name_from_id(struct xdr_stream *xdr,
return idmap_id_to_name(xdr, rqstp, type, id);
}
__be32
nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,
kuid_t *uid)
/**
* nfsd_map_name_to_uid - Map user@domain to local UID
* @rqstp: RPC execution context
* @name: user@domain name to be mapped
* @namelen: length of name, in bytes
* @uid: OUT: mapped local UID value
*
* Returns nfs_ok on success or an NFSv4 status code on failure.
*/
__be32 nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name,
size_t namelen, kuid_t *uid)
{
__be32 status;
u32 id = -1;
/*
* The idmap lookup below triggers an upcall that invokes
* cache_check(). RQ_USEDEFERRAL must be clear to prevent
* cache_check() from setting RQ_DROPME via svc_defer().
* NFSv4 servers are not permitted to drop requests. Also
* RQ_DROPME will force NFSv4.1 session slot processing to
* be skipped.
*/
WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags));
if (name == NULL || namelen == 0)
return nfserr_inval;
status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id);
if (status)
return status;
*uid = make_kuid(nfsd_user_namespace(rqstp), id);
if (!uid_valid(*uid))
status = nfserr_badowner;
return status;
}
__be32
nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
kgid_t *gid)
/**
* nfsd_map_name_to_gid - Map user@domain to local GID
* @rqstp: RPC execution context
* @name: user@domain name to be mapped
* @namelen: length of name, in bytes
* @gid: OUT: mapped local GID value
*
* Returns nfs_ok on success or an NFSv4 status code on failure.
*/
__be32 nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name,
size_t namelen, kgid_t *gid)
{
__be32 status;
u32 id = -1;
/*
* The idmap lookup below triggers an upcall that invokes
* cache_check(). RQ_USEDEFERRAL must be clear to prevent
* cache_check() from setting RQ_DROPME via svc_defer().
* NFSv4 servers are not permitted to drop requests. Also
* RQ_DROPME will force NFSv4.1 session slot processing to
* be skipped.
*/
WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags));
if (name == NULL || namelen == 0)
return nfserr_inval;
status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id);
if (status)
return status;
*gid = make_kgid(nfsd_user_namespace(rqstp), id);
if (!gid_valid(*gid))
status = nfserr_badowner;
+216 -49
View File
@@ -81,8 +81,8 @@ static u32 nfsd41_ex_attrmask[] = {
};
static __be32
check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
u32 *bmval, u32 *writable)
check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval,
u32 *writable)
{
struct dentry *dentry = cstate->current_fh.fh_dentry;
struct svc_export *exp = cstate->current_fh.fh_export;
@@ -91,6 +91,10 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_attrnotsupp;
if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry)))
return nfserr_attrnotsupp;
if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
FATTR4_WORD2_POSIX_ACCESS_ACL)) &&
!IS_POSIXACL(d_inode(dentry)))
return nfserr_attrnotsupp;
if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) &&
!(exp->ex_flags & NFSEXP_SECURITY_LABEL))
return nfserr_attrnotsupp;
@@ -103,21 +107,25 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
static __be32
nfsd4_check_open_attributes(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
nfsd4_check_open_attributes(struct nfsd4_compound_state *cstate,
struct nfsd4_open *open)
{
__be32 status = nfs_ok;
if (open->op_create == NFS4_OPEN_CREATE) {
if (open->op_createmode == NFS4_CREATE_UNCHECKED
|| open->op_createmode == NFS4_CREATE_GUARDED)
status = check_attr_support(rqstp, cstate,
open->op_bmval, nfsd_attrmask);
else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1)
status = check_attr_support(rqstp, cstate,
open->op_bmval, nfsd41_ex_attrmask);
}
if (open->op_create != NFS4_OPEN_CREATE)
return status;
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
case NFS4_CREATE_GUARDED:
status = check_attr_support(cstate, open->op_bmval,
nfsd_attrmask);
break;
case NFS4_CREATE_EXCLUSIVE4_1:
status = check_attr_support(cstate, open->op_bmval,
nfsd41_ex_attrmask);
break;
}
return status;
}
@@ -266,8 +274,20 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (host_err)
return nfserrno(host_err);
if (is_create_with_attrs(open))
nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
if (open->op_acl) {
if (open->op_dpacl || open->op_pacl) {
status = nfserr_inval;
goto out_write;
}
if (is_create_with_attrs(open))
nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
} 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));
@@ -378,8 +398,12 @@ set_attr:
if (attrs.na_labelerr)
open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
if (attrs.na_aclerr)
if (attrs.na_paclerr || attrs.na_dpaclerr)
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
if (attrs.na_dpaclerr)
open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
if (attrs.na_paclerr)
open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
out:
end_creating(child);
nfsd_attrs_free(&attrs);
@@ -547,8 +571,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
open->op_rqstp = rqstp;
/* This check required by spec. */
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval;
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) {
status = nfserr_inval;
goto out_err;
}
open->op_created = false;
/*
@@ -557,8 +583,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
*/
if (nfsd4_has_session(cstate) &&
!test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) &&
open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
return nfserr_grace;
open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) {
status = nfserr_grace;
goto out_err;
}
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);
@@ -584,7 +612,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}
status = nfsd4_check_open_attributes(rqstp, cstate, open);
status = nfsd4_check_open_attributes(cstate, open);
if (status)
goto out;
@@ -645,6 +673,9 @@ out:
}
nfsd4_cleanup_open_state(cstate, open);
nfsd4_bump_seqid(cstate, status);
out_err:
posix_acl_release(open->op_dpacl);
posix_acl_release(open->op_pacl);
return status;
}
@@ -785,23 +816,34 @@ 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,
};
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);
if (status)
return status;
goto out_aftermask;
status = check_attr_support(rqstp, cstate, create->cr_bmval,
nfsd_attrmask);
status = check_attr_support(cstate, create->cr_bmval, nfsd_attrmask);
if (status)
return status;
goto out_aftermask;
status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs);
if (create->cr_acl) {
if (create->cr_dpacl || create->cr_pacl) {
status = nfserr_inval;
goto out_aftermask;
}
status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl,
&attrs);
}
current->fs->umask = create->cr_umask;
switch (create->cr_type) {
case NF4LNK:
@@ -860,14 +902,19 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (attrs.na_labelerr)
create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
if (attrs.na_aclerr)
if (attrs.na_paclerr || attrs.na_dpaclerr)
create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;
if (attrs.na_dpaclerr)
create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
if (attrs.na_paclerr)
create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
set_change_info(&create->cr_cinfo, &cstate->current_fh);
fh_dup2(&cstate->current_fh, &resfh);
out:
fh_put(&resfh);
out_umask:
current->fs->umask = 0;
out_aftermask:
nfsd_attrs_free(&attrs);
return status;
}
@@ -1172,6 +1219,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,
};
bool save_no_wcc, deleg_attrs;
struct nfs4_stid *st = NULL;
@@ -1179,6 +1228,10 @@ 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);
@@ -1192,7 +1245,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&cstate->current_fh, &setattr->sa_stateid,
flags, NULL, &st);
if (status)
return status;
goto out_err;
}
if (deleg_attrs) {
@@ -1210,18 +1263,24 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (st)
nfs4_put_stid(st);
if (status)
return status;
goto out_err;
err = fh_want_write(&cstate->current_fh);
if (err)
return nfserrno(err);
if (err) {
status = nfserrno(err);
goto out_err;
}
status = nfs_ok;
status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
nfsd_attrmask);
status = check_attr_support(cstate, setattr->sa_bmval, nfsd_attrmask);
if (status)
goto out;
if (setattr->sa_acl && (attrs.na_dpacl || attrs.na_pacl)) {
status = nfserr_inval;
goto out;
}
inode = cstate->current_fh.fh_dentry->d_inode;
status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG,
setattr->sa_acl, &attrs);
@@ -1235,10 +1294,13 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!status)
status = nfserrno(attrs.na_labelerr);
if (!status)
status = nfserrno(attrs.na_aclerr);
status = nfserrno(attrs.na_dpaclerr);
if (!status)
status = nfserrno(attrs.na_paclerr);
out:
nfsd_attrs_free(&attrs);
fh_drop_write(&cstate->current_fh);
out_err:
nfsd_attrs_free(&attrs);
return status;
}
@@ -1430,14 +1492,26 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
kfree(copy);
}
static void release_copy_files(struct nfsd4_copy *copy);
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
{
trace_nfsd_copy_async_cancel(copy);
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
kthread_stop(copy->copy_task);
copy->nfserr = nfs_ok;
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
copy->nfserr = nfs_ok;
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
}
/*
* The copy was removed from async_copies before this function
* was called, so the reaper cannot clean it up. Release files
* here regardless of who won the STOPPED race. If the thread
* set STOPPED, it has finished using the files. If STOPPED
* was set here, kthread_stop() waited for the thread to exit.
*/
release_copy_files(copy);
nfs4_put_copy(copy);
}
@@ -1465,6 +1539,72 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
nfsd4_stop_copy(copy);
}
static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy,
const struct super_block *sb)
{
if (copy->nf_src &&
file_inode(copy->nf_src->nf_file)->i_sb == sb)
return true;
if (copy->nf_dst &&
file_inode(copy->nf_dst->nf_file)->i_sb == sb)
return true;
return false;
}
/**
* nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb
* @net: net namespace containing the copy operations
* @sb: targeted superblock
*/
void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct nfsd4_copy *copy, *tmp;
struct nfs4_client *clp;
unsigned int idhashval;
LIST_HEAD(to_cancel);
spin_lock(&nn->client_lock);
for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
struct list_head *head = &nn->conf_id_hashtbl[idhashval];
list_for_each_entry(clp, head, cl_idhash) {
spin_lock(&clp->async_lock);
list_for_each_entry_safe(copy, tmp,
&clp->async_copies, copies) {
if (nfsd4_copy_on_sb(copy, sb)) {
refcount_inc(&copy->refcount);
/*
* Hold a reference on the client while
* nfsd4_stop_copy() runs. Unlike
* nfsd4_unhash_copy(), cp_clp is not
* NULLed here because nfsd4_send_cb_offload()
* needs a valid client to send CB_OFFLOAD.
* That function takes its own reference to
* survive callback flight.
*/
kref_get(&clp->cl_nfsdfs.cl_ref);
copy->nfserr = nfserr_admin_revoked;
set_bit(NFSD4_COPY_F_CB_ERROR,
&copy->cp_flags);
list_move(&copy->copies, &to_cancel);
}
}
spin_unlock(&clp->async_lock);
}
}
spin_unlock(&nn->client_lock);
list_for_each_entry_safe(copy, tmp, &to_cancel, copies) {
struct nfs4_client *clp = copy->cp_clp;
list_del_init(&copy->copies);
nfsd4_stop_copy(copy);
nfsd4_put_client(clp);
}
}
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt,
@@ -1754,6 +1894,7 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
nfsd4_put_client(cb->cb_clp);
}
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
@@ -1873,10 +2014,14 @@ static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst)
static void release_copy_files(struct nfsd4_copy *copy)
{
if (copy->nf_src)
if (copy->nf_src) {
nfsd_file_put(copy->nf_src);
if (copy->nf_dst)
copy->nf_src = NULL;
}
if (copy->nf_dst) {
nfsd_file_put(copy->nf_dst);
copy->nf_dst = NULL;
}
}
static void cleanup_async_copy(struct nfsd4_copy *copy)
@@ -1895,18 +2040,34 @@ static void cleanup_async_copy(struct nfsd4_copy *copy)
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
{
struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload;
struct nfs4_client *clp = copy->cp_clp;
/*
* cp_clp is NULL when called via nfsd4_shutdown_copy() during
* client destruction. Skip the callback; the client is gone.
*/
if (!clp) {
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
return;
}
memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res));
memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh));
cbo->co_nfserr = copy->nfserr;
cbo->co_retries = 5;
nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops,
/*
* Hold a reference on the client while the callback is in flight.
* Released in nfsd4_cb_offload_release().
*/
kref_get(&clp->cl_nfsdfs.cl_ref);
nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops,
NFSPROC4_CLNT_CB_OFFLOAD);
nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid,
cbo->co_referring_slotid,
cbo->co_referring_seqno);
trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid,
trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid,
&cbo->co_fh, copy->cp_count, copy->nfserr);
nfsd4_try_run_cb(&cbo->co_cb);
}
@@ -1921,6 +2082,7 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
static int nfsd4_do_async_copy(void *data)
{
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
__be32 nfserr = nfs_ok;
trace_nfsd_copy_async(copy);
if (nfsd4_ssc_is_inter(copy)) {
@@ -1931,23 +2093,25 @@ static int nfsd4_do_async_copy(void *data)
if (IS_ERR(filp)) {
switch (PTR_ERR(filp)) {
case -EBADF:
copy->nfserr = nfserr_wrong_type;
nfserr = nfserr_wrong_type;
break;
default:
copy->nfserr = nfserr_offload_denied;
nfserr = nfserr_offload_denied;
}
/* ss_mnt will be unmounted by the laundromat */
goto do_callback;
}
copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
false);
nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file,
false);
nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst);
} else {
copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
copy->nf_dst->nf_file, false);
nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file,
copy->nf_dst->nf_file, false);
}
do_callback:
if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags))
copy->nfserr = nfserr;
/* The kthread exits forthwith. Ensure that a subsequent
* OFFLOAD_CANCEL won't try to kill it again. */
set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
@@ -2271,7 +2435,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
return status;
status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
status = check_attr_support(cstate, verify->ve_bmval, NULL);
if (status)
return status;
@@ -2281,6 +2445,11 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (verify->ve_attrlen & 3)
return nfserr_inval;
/* The POSIX draft ACLs cannot be tested via (N)VERIFY. */
if (verify->ve_bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
FATTR4_WORD2_POSIX_ACCESS_ACL))
return nfserr_inval;
/* count in words:
* bitmap_len(1) + bitmap(2) + attr_len(1) = 4
*/
@@ -3016,8 +3185,6 @@ encode_op:
BUG_ON(cstate->replay_owner);
out:
cstate->status = status;
/* Reset deferral mechanism for RPC deferrals */
set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
return rpc_success;
}
+29 -23
View File
@@ -1253,7 +1253,7 @@ static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct f
if (ret) {
struct inode *inode = file_inode(f);
pr_notice_ratelimited("Unable to update timestamps on inode %02x:%02x:%lu: %d\n",
pr_notice_ratelimited("nfsd: Unable to update timestamps on inode %02x:%02x:%lu: %d\n",
MAJOR(inode->i_sb->s_dev),
MINOR(inode->i_sb->s_dev),
inode->i_ino, ret);
@@ -2413,7 +2413,13 @@ static void __free_client(struct kref *k)
kmem_cache_free(client_slab, clp);
}
static void drop_client(struct nfs4_client *clp)
/**
* nfsd4_put_client - release a reference on an nfs4_client
* @clp: the client to be released
*
* When the last reference is released, the client is freed.
*/
void nfsd4_put_client(struct nfs4_client *clp)
{
kref_put(&clp->cl_nfsdfs.cl_ref, __free_client);
}
@@ -2435,7 +2441,7 @@ free_client(struct nfs4_client *clp)
clp->cl_nfsd_dentry = NULL;
wake_up_all(&expiry_wq);
}
drop_client(clp);
nfsd4_put_client(clp);
}
/* must be called under the client_lock */
@@ -2833,7 +2839,7 @@ static int client_info_show(struct seq_file *m, void *v)
spin_unlock(&clp->cl_lock);
seq_puts(m, "\n");
drop_client(clp);
nfsd4_put_client(clp);
return 0;
}
@@ -3099,7 +3105,7 @@ static int client_states_open(struct inode *inode, struct file *file)
ret = seq_open(file, &states_seq_ops);
if (ret) {
drop_client(clp);
nfsd4_put_client(clp);
return ret;
}
s = file->private_data;
@@ -3113,7 +3119,7 @@ static int client_opens_release(struct inode *inode, struct file *file)
struct nfs4_client *clp = m->private;
/* XXX: alternatively, we could get/drop in seq start/stop */
drop_client(clp);
nfsd4_put_client(clp);
return seq_release(inode, file);
}
@@ -3169,7 +3175,7 @@ static ssize_t client_ctl_write(struct file *file, const char __user *buf,
if (!clp)
return -ENXIO;
force_expire_client(clp);
drop_client(clp);
nfsd4_put_client(clp);
return 7;
}
@@ -3204,7 +3210,7 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
drop_client(clp);
nfsd4_put_client(clp);
}
static int
@@ -6353,7 +6359,8 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
dp->dl_ctime = stat.ctime;
dp->dl_mtime = stat.mtime;
spin_lock(&f->f_lock);
f->f_mode |= FMODE_NOCMTIME;
if (deleg_ts)
f->f_mode |= FMODE_NOCMTIME;
spin_unlock(&f->f_lock);
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
} else {
@@ -6637,14 +6644,14 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn)
{
if (!nn->client_tracking_ops)
return false;
spin_lock(&nn->client_lock);
if (nn->grace_ended || !nn->client_tracking_active) {
spin_unlock(&nn->client_lock);
if (READ_ONCE(nn->grace_ended))
return false;
}
/* laundromat_work must be initialised now, though it might be disabled */
WRITE_ONCE(nn->grace_end_forced, true);
/* mod_delayed_work() doesn't queue work after
* nfs4_state_shutdown_net() has called disable_delayed_work_sync()
*/
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
spin_unlock(&nn->client_lock);
return true;
}
@@ -8980,7 +8987,6 @@ static int nfs4_state_create_net(struct net *net)
nn->boot_time = ktime_get_real_seconds();
nn->grace_ended = false;
nn->grace_end_forced = false;
nn->client_tracking_active = false;
nn->nfsd4_manager.block_opens = true;
INIT_LIST_HEAD(&nn->nfsd4_manager.list);
INIT_LIST_HEAD(&nn->client_lru);
@@ -8995,6 +9001,8 @@ static int nfs4_state_create_net(struct net *net)
INIT_LIST_HEAD(&nn->blocked_locks_lru);
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);
get_net(net);
@@ -9062,9 +9070,7 @@ nfs4_state_start_net(struct net *net)
locks_start_grace(net, &nn->nfsd4_manager);
nfsd4_client_tracking_init(net);
/* safe for laundromat to run now */
spin_lock(&nn->client_lock);
nn->client_tracking_active = true;
spin_unlock(&nn->client_lock);
enable_delayed_work(&nn->laundromat_work);
if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
goto skip_grace;
printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n",
@@ -9113,10 +9119,7 @@ nfs4_state_shutdown_net(struct net *net)
shrinker_free(nn->nfsd_client_shrinker);
cancel_work_sync(&nn->nfsd_shrinker_work);
spin_lock(&nn->client_lock);
nn->client_tracking_active = false;
spin_unlock(&nn->client_lock);
cancel_delayed_work_sync(&nn->laundromat_work);
disable_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
INIT_LIST_HEAD(&reaplist);
@@ -9520,8 +9523,10 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
spin_unlock(&clp->cl_lock);
spin_unlock(&state_lock);
if (!status)
if (!status) {
put_nfs4_file(fp);
return dp;
}
/* Something failed. Drop the lease and clean up the stid */
kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp);
@@ -9529,5 +9534,6 @@ out_put_stid:
nfs4_put_stid(&dp->dl_stid);
out_delegees:
put_deleg_file(fp);
put_nfs4_file(fp);
return ERR_PTR(status);
}
+357 -6
View File
@@ -43,6 +43,7 @@
#include <linux/sunrpc/addr.h>
#include <linux/xattr.h>
#include <linux/vmalloc.h>
#include <linux/nfsacl.h>
#include <uapi/linux/xattr.h>
@@ -377,10 +378,111 @@ nfsd4_decode_security_label(struct nfsd4_compoundargs *argp,
return nfs_ok;
}
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
static short nfsd4_posixacetag4_to_tag(posixacetag4 tag)
{
switch (tag) {
case POSIXACE4_TAG_USER_OBJ: return ACL_USER_OBJ;
case POSIXACE4_TAG_GROUP_OBJ: return ACL_GROUP_OBJ;
case POSIXACE4_TAG_USER: return ACL_USER;
case POSIXACE4_TAG_GROUP: return ACL_GROUP;
case POSIXACE4_TAG_MASK: return ACL_MASK;
case POSIXACE4_TAG_OTHER: return ACL_OTHER;
}
return ACL_OTHER;
}
static __be32
nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp,
struct posix_acl_entry *ace)
{
posixaceperm4 perm;
__be32 *p, status;
posixacetag4 tag;
u32 len;
if (!xdrgen_decode_posixacetag4(argp->xdr, &tag))
return nfserr_bad_xdr;
ace->e_tag = nfsd4_posixacetag4_to_tag(tag);
if (!xdrgen_decode_posixaceperm4(argp->xdr, &perm))
return nfserr_bad_xdr;
if (perm & ~S_IRWXO)
return nfserr_bad_xdr;
ace->e_perm = perm;
if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
return nfserr_bad_xdr;
p = xdr_inline_decode(argp->xdr, len);
if (!p)
return nfserr_bad_xdr;
switch (tag) {
case POSIXACE4_TAG_USER:
if (len > 0)
status = nfsd_map_name_to_uid(argp->rqstp,
(char *)p, len, &ace->e_uid);
else
status = nfserr_bad_xdr;
break;
case POSIXACE4_TAG_GROUP:
if (len > 0)
status = nfsd_map_name_to_gid(argp->rqstp,
(char *)p, len, &ace->e_gid);
else
status = nfserr_bad_xdr;
break;
default:
status = nfs_ok;
}
return status;
}
static noinline __be32
nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
{
struct posix_acl_entry *ace;
__be32 status;
u32 count;
if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
return nfserr_bad_xdr;
*acl = posix_acl_alloc(count, GFP_KERNEL);
if (*acl == NULL)
return nfserr_resource;
(*acl)->a_count = count;
for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) {
status = nfsd4_decode_posixace4(argp, ace);
if (status) {
posix_acl_release(*acl);
*acl = NULL;
return status;
}
}
/*
* posix_acl_valid() requires the ACEs to be sorted.
* If they are already sorted, sort_pacl_range() will return
* after one pass through the ACEs, since it implements bubble sort.
* Note that a count == 0 is used to delete a POSIX ACL and a count
* of 1 or 2 will always be found invalid by posix_acl_valid().
*/
if (count >= 3)
sort_pacl_range(*acl, 0, count - 1);
return nfs_ok;
}
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
static __be32
nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
struct iattr *iattr, struct nfs4_acl **acl,
struct xdr_netobj *label, int *umask)
struct xdr_netobj *label, int *umask,
struct posix_acl **dpaclp, struct posix_acl **paclp)
{
unsigned int starting_pos;
u32 attrlist4_count;
@@ -543,9 +645,40 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG;
}
*dpaclp = NULL;
*paclp = NULL;
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
if (bmval[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) {
struct posix_acl *dpacl;
status = nfsd4_decode_posixacl(argp, &dpacl);
if (status)
return status;
*dpaclp = dpacl;
}
if (bmval[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) {
struct posix_acl *pacl;
status = nfsd4_decode_posixacl(argp, &pacl);
if (status) {
posix_acl_release(*dpaclp);
*dpaclp = NULL;
return status;
}
*paclp = pacl;
}
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
/* request sanity: did attrlist4 contain the expected number of words? */
if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
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;
}
return nfs_ok;
}
@@ -849,7 +982,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
status = nfsd4_decode_fattr4(argp, create->cr_bmval,
ARRAY_SIZE(create->cr_bmval),
&create->cr_iattr, &create->cr_acl,
&create->cr_label, &create->cr_umask);
&create->cr_label, &create->cr_umask,
&create->cr_dpacl, &create->cr_pacl);
if (status)
return status;
@@ -1000,7 +1134,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
status = nfsd4_decode_fattr4(argp, open->op_bmval,
ARRAY_SIZE(open->op_bmval),
&open->op_iattr, &open->op_acl,
&open->op_label, &open->op_umask);
&open->op_label, &open->op_umask,
&open->op_dpacl, &open->op_pacl);
if (status)
return status;
break;
@@ -1018,7 +1153,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
status = nfsd4_decode_fattr4(argp, open->op_bmval,
ARRAY_SIZE(open->op_bmval),
&open->op_iattr, &open->op_acl,
&open->op_label, &open->op_umask);
&open->op_label, &open->op_umask,
&open->op_dpacl, &open->op_pacl);
if (status)
return status;
break;
@@ -1345,7 +1481,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
ARRAY_SIZE(setattr->sa_bmval),
&setattr->sa_iattr, &setattr->sa_acl,
&setattr->sa_label, NULL);
&setattr->sa_label, NULL, &setattr->sa_dpacl,
&setattr->sa_pacl);
}
static __be32
@@ -2849,6 +2986,89 @@ nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
{ return 0; }
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
static int nfsd4_posix_tagtotype(short tag)
{
switch (tag) {
case ACL_USER_OBJ: return POSIXACE4_TAG_USER_OBJ;
case ACL_GROUP_OBJ: return POSIXACE4_TAG_GROUP_OBJ;
case ACL_USER: return POSIXACE4_TAG_USER;
case ACL_GROUP: return POSIXACE4_TAG_GROUP;
case ACL_MASK: return POSIXACE4_TAG_MASK;
case ACL_OTHER: return POSIXACE4_TAG_OTHER;
default: return -EINVAL;
}
}
static __be32
nfsd4_encode_posixace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
struct posix_acl_entry *acep)
{
__be32 status;
int type;
type = nfsd4_posix_tagtotype(acep->e_tag);
if (type < 0)
return nfserr_resource;
if (!xdrgen_encode_posixacetag4(xdr, type))
return nfserr_resource;
if (!xdrgen_encode_posixaceperm4(xdr, acep->e_perm))
return nfserr_resource;
/* who */
switch (acep->e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
return nfserr_resource;
break;
case ACL_USER:
status = nfsd4_encode_user(xdr, rqstp, acep->e_uid);
if (status != nfs_ok)
return status;
break;
case ACL_GROUP:
status = nfsd4_encode_group(xdr, rqstp, acep->e_gid);
if (status != nfs_ok)
return status;
break;
default:
return nfserr_resource;
}
return nfs_ok;
}
static __be32
nfsd4_encode_posixacl(struct xdr_stream *xdr, struct svc_rqst *rqstp,
struct posix_acl *acl)
{
__be32 status;
int i;
if (!acl) {
if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
return nfserr_resource;
return nfs_ok;
}
if (acl->a_count > NFS_ACL_MAX_ENTRIES)
return nfserr_resource;
if (xdr_stream_encode_u32(xdr, acl->a_count) != XDR_UNIT)
return nfserr_resource;
for (i = 0; i < acl->a_count; i++) {
status = nfsd4_encode_posixace4(xdr, rqstp, &acl->a_entries[i]);
if (status != nfs_ok)
return status;
}
return nfs_ok;
}
#endif /* CONFIG_NFSD_V4_POSIX_ACL */
static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
{
/* As per referral draft: */
@@ -2929,6 +3149,10 @@ struct nfsd4_fattr_args {
u64 change_attr;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
struct lsm_context context;
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
struct posix_acl *dpacl;
struct posix_acl *pacl;
#endif
u32 rdattr_err;
bool contextsupport;
@@ -3470,6 +3694,42 @@ static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr,
return nfs_ok;
}
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
aclmodel4 trueform = ACL_MODEL_NONE;
if (IS_POSIXACL(d_inode(args->dentry)))
trueform = ACL_MODEL_POSIX_DRAFT;
if (!xdrgen_encode_aclmodel4(xdr, trueform))
return nfserr_resource;
return nfs_ok;
}
static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
if (!xdrgen_encode_aclscope4(xdr, ACL_SCOPE_FILE_SYSTEM))
return nfserr_resource;
return nfs_ok;
}
static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
return nfsd4_encode_posixacl(xdr, args->rqstp, args->dpacl);
}
static __be32 nfsd4_encode_fattr4_posix_access_acl(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
return nfsd4_encode_posixacl(xdr, args->rqstp, args->pacl);
}
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs,
[FATTR4_TYPE] = nfsd4_encode_fattr4_type,
@@ -3573,6 +3833,22 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_TIME_DELEG_ACCESS] = nfsd4_encode_fattr4__inval,
[FATTR4_TIME_DELEG_MODIFY] = nfsd4_encode_fattr4__inval,
[FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments,
/* Reserved */
[87] = nfsd4_encode_fattr4__inval,
[88] = nfsd4_encode_fattr4__inval,
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope,
[FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4_posix_default_acl,
[FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4_posix_access_acl,
#else
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop,
[FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4__noop,
[FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4__noop,
#endif
};
/*
@@ -3613,6 +3889,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
args.context.context = NULL;
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
args.dpacl = NULL;
args.pacl = NULL;
#endif
/*
* Make a local copy of the attribute bitmap that can be modified.
@@ -3719,6 +3999,55 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
}
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
if (attrmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) {
struct inode *inode = d_inode(dentry);
struct posix_acl *dpacl;
if (S_ISDIR(inode->i_mode)) {
dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(dpacl)) {
switch (PTR_ERR(dpacl)) {
case -EOPNOTSUPP:
attrmask[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
break;
case -EINVAL:
status = nfserr_attrnotsupp;
goto out;
default:
err = PTR_ERR(dpacl);
goto out_nfserr;
}
} else {
args.dpacl = dpacl;
}
}
}
if (attrmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) {
struct inode *inode = d_inode(dentry);
struct posix_acl *pacl;
pacl = get_inode_acl(inode, ACL_TYPE_ACCESS);
if (!pacl)
pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
if (IS_ERR(pacl)) {
switch (PTR_ERR(pacl)) {
case -EOPNOTSUPP:
attrmask[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
break;
case -EINVAL:
status = nfserr_attrnotsupp;
goto out;
default:
err = PTR_ERR(pacl);
goto out_nfserr;
}
} else {
args.pacl = pacl;
}
}
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
/* attrmask */
status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
attrmask[2]);
@@ -3742,6 +4071,12 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
status = nfs_ok;
out:
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
if (args.dpacl)
posix_acl_release(args.dpacl);
if (args.pacl)
posix_acl_release(args.pacl);
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (args.context.context)
security_release_secctx(&args.context);
@@ -6013,6 +6348,22 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
args->ops = args->iops;
args->rqstp = rqstp;
/*
* NFSv4 operation decoders can invoke svc cache lookups
* that trigger svc_defer() when RQ_USEDEFERRAL is set,
* setting RQ_DROPME. This creates two problems:
*
* 1. Non-idempotency: Compounds make it too hard to avoid
* problems if a request is deferred and replayed.
*
* 2. Session slot leakage (NFSv4.1+): If RQ_DROPME is set
* during decode but SEQUENCE executes successfully, the
* session slot will be marked INUSE. The request is then
* dropped before encoding, so the slot is never released,
* rendering it permanently unusable by the client.
*/
clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
return nfsd4_decode_compound(args);
}
+332 -19
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
// XDR specification modification time: Mon Oct 14 09:10:13 2024
// XDR specification modification time: Thu Jan 8 23:12:07 2026
#include <linux/sunrpc/svc.h>
@@ -11,13 +11,13 @@ static bool __maybe_unused
xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
{
return xdrgen_decode_hyper(xdr, ptr);
};
}
static bool __maybe_unused
xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
{
return xdrgen_decode_unsigned_int(xdr, ptr);
};
}
static bool __maybe_unused
xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
@@ -28,7 +28,31 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr)
{
return xdrgen_decode_opaque(xdr, ptr, 0);
}
static bool __maybe_unused
xdrgen_decode_utf8str_cis(struct xdr_stream *xdr, utf8str_cis *ptr)
{
return xdrgen_decode_utf8string(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_utf8str_cs(struct xdr_stream *xdr, utf8str_cs *ptr)
{
return xdrgen_decode_utf8string(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr)
{
return xdrgen_decode_utf8string(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
@@ -38,13 +62,13 @@ xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
{
return xdrgen_decode_bool(xdr, ptr);
};
}
static bool __maybe_unused
xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
@@ -60,7 +84,7 @@ xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *pt
if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr)
@@ -69,6 +93,15 @@ xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_ac
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_ARGS_SHARE_ACCESS_READ:
case OPEN_ARGS_SHARE_ACCESS_WRITE:
case OPEN_ARGS_SHARE_ACCESS_BOTH:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -80,6 +113,16 @@ xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, open_args_share_deny
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_ARGS_SHARE_DENY_NONE:
case OPEN_ARGS_SHARE_DENY_READ:
case OPEN_ARGS_SHARE_DENY_WRITE:
case OPEN_ARGS_SHARE_DENY_BOTH:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -91,6 +134,19 @@ xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, open_args_sha
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG:
case OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG:
case OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL:
case OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
case OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED:
case OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS:
case OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -102,6 +158,19 @@ xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, open_args_open_claim
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_ARGS_OPEN_CLAIM_NULL:
case OPEN_ARGS_OPEN_CLAIM_PREVIOUS:
case OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR:
case OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV:
case OPEN_ARGS_OPEN_CLAIM_FH:
case OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH:
case OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -113,6 +182,16 @@ xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, open_args_createmode
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_ARGS_CREATEMODE_UNCHECKED4:
case OPEN_ARGS_CREATE_MODE_GUARDED:
case OPEN_ARGS_CREATEMODE_EXCLUSIVE4:
case OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -121,19 +200,28 @@ bool
xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
{
return xdrgen_decode_open_arguments4(xdr, ptr);
};
}
/*
* Determine what OPEN supports.
*/
bool
xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
{
return xdrgen_decode_nfstime4(xdr, ptr);
};
}
bool
xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
{
return xdrgen_decode_nfstime4(xdr, ptr);
};
}
/*
* New RECOMMENDED Attribute for
* delegation caching of times
*/
static bool __maybe_unused
xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr)
@@ -142,21 +230,152 @@ xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case OPEN_DELEGATE_NONE:
case OPEN_DELEGATE_READ:
case OPEN_DELEGATE_WRITE:
case OPEN_DELEGATE_NONE_EXT:
case OPEN_DELEGATE_READ_ATTRS_DELEG:
case OPEN_DELEGATE_WRITE_ATTRS_DELEG:
break;
default:
return false;
}
*ptr = val;
return true;
}
bool
xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case ACL_MODEL_NFS4:
case ACL_MODEL_POSIX_DRAFT:
case ACL_MODEL_NONE:
break;
default:
return false;
}
*ptr = val;
return true;
}
bool
xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case ACL_SCOPE_FILE_OBJECT:
case ACL_SCOPE_FILE_SYSTEM:
case ACL_SCOPE_SERVER:
break;
default:
return false;
}
*ptr = val;
return true;
}
bool
xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case POSIXACE4_TAG_USER_OBJ:
case POSIXACE4_TAG_USER:
case POSIXACE4_TAG_GROUP_OBJ:
case POSIXACE4_TAG_GROUP:
case POSIXACE4_TAG_MASK:
case POSIXACE4_TAG_OTHER:
break;
default:
return false;
}
*ptr = val;
return true;
}
bool
xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr)
{
return xdrgen_decode_uint32_t(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_posixace4(struct xdr_stream *xdr, struct posixace4 *ptr)
{
if (!xdrgen_decode_posixacetag4(xdr, &ptr->tag))
return false;
if (!xdrgen_decode_posixaceperm4(xdr, &ptr->perm))
return false;
if (!xdrgen_decode_utf8str_mixed(xdr, &ptr->who))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_fattr4_acl_trueform(struct xdr_stream *xdr, fattr4_acl_trueform *ptr)
{
return xdrgen_decode_aclmodel4(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, fattr4_acl_trueform_scope *ptr)
{
return xdrgen_decode_aclscope4(xdr, ptr);
}
static bool __maybe_unused
xdrgen_decode_fattr4_posix_default_acl(struct xdr_stream *xdr, fattr4_posix_default_acl *ptr)
{
if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
return false;
for (u32 i = 0; i < ptr->count; i++)
if (!xdrgen_decode_posixace4(xdr, &ptr->element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_decode_fattr4_posix_access_acl(struct xdr_stream *xdr, fattr4_posix_access_acl *ptr)
{
if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
return false;
for (u32 i = 0; i < ptr->count; i++)
if (!xdrgen_decode_posixace4(xdr, &ptr->element[i]))
return false;
return true;
}
/*
* New for POSIX ACL extension
*/
static bool __maybe_unused
xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
{
return xdrgen_encode_hyper(xdr, value);
};
}
static bool __maybe_unused
xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
{
return xdrgen_encode_unsigned_int(xdr, value);
};
}
static bool __maybe_unused
xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
@@ -167,7 +386,31 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value)
{
return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
}
static bool __maybe_unused
xdrgen_encode_utf8str_cis(struct xdr_stream *xdr, const utf8str_cis value)
{
return xdrgen_encode_utf8string(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_utf8str_cs(struct xdr_stream *xdr, const utf8str_cs value)
{
return xdrgen_encode_utf8string(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value)
{
return xdrgen_encode_utf8string(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
@@ -177,13 +420,13 @@ xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
{
return xdrgen_encode_bool(xdr, value);
};
}
static bool __maybe_unused
xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
@@ -199,7 +442,7 @@ xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_argument
if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
return false;
return true;
};
}
static bool __maybe_unused
xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value)
@@ -235,22 +478,92 @@ bool
xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value)
{
return xdrgen_encode_open_arguments4(xdr, value);
};
}
bool
xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value)
{
return xdrgen_encode_nfstime4(xdr, value);
};
}
bool
xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value)
{
return xdrgen_encode_nfstime4(xdr, value);
};
}
static bool __maybe_unused
xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
bool
xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
bool
xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
bool
xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
bool
xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value)
{
return xdrgen_encode_uint32_t(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_posixace4(struct xdr_stream *xdr, const struct posixace4 *value)
{
if (!xdrgen_encode_posixacetag4(xdr, value->tag))
return false;
if (!xdrgen_encode_posixaceperm4(xdr, value->perm))
return false;
if (!xdrgen_encode_utf8str_mixed(xdr, value->who))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_fattr4_acl_trueform(struct xdr_stream *xdr, const fattr4_acl_trueform value)
{
return xdrgen_encode_aclmodel4(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, const fattr4_acl_trueform_scope value)
{
return xdrgen_encode_aclscope4(xdr, value);
}
static bool __maybe_unused
xdrgen_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, const fattr4_posix_default_acl value)
{
if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value.count; i++)
if (!xdrgen_encode_posixace4(xdr, &value.element[i]))
return false;
return true;
}
static bool __maybe_unused
xdrgen_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, const fattr4_posix_access_acl value)
{
if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value.count; i++)
if (!xdrgen_encode_posixace4(xdr, &value.element[i]))
return false;
return true;
}
+11 -1
View File
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
/* XDR specification modification time: Thu Jan 8 23:12:07 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
@@ -21,5 +21,15 @@ bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4
bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr);
bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value);
bool xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr);
bool xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value);
bool xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr);
bool xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value);
bool xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr);
bool xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value);
bool xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr);
bool xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value);
#endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */
+7
View File
@@ -285,6 +285,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
* 2. Is that directory a mount point, or
* 3. Is that directory the root of an exported file system?
*/
nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb);
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
nn = net_generic(netns(file), nfsd_net_id);
@@ -1642,6 +1643,10 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
scope = nla_data(attr);
}
attr = info->attrs[NFSD_A_SERVER_MIN_THREADS];
if (attr)
nn->min_threads = nla_get_u32(attr);
ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope);
if (ret > 0)
ret = 0;
@@ -1681,6 +1686,8 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
nn->nfsd4_grace) ||
nla_put_u32(skb, NFSD_A_SERVER_LEASETIME,
nn->nfsd4_lease) ||
nla_put_u32(skb, NFSD_A_SERVER_MIN_THREADS,
nn->min_threads) ||
nla_put_string(skb, NFSD_A_SERVER_SCOPE,
nn->nfsd_name);
if (err)

Some files were not shown because too many files have changed in this diff Show More