Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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
...

+2267 -372
+5
Documentation/netlink/specs/nfsd.yaml
··· 78 78 - 79 79 name: scope 80 80 type: string 81 + - 82 + name: min-threads 83 + type: u32 81 84 - 82 85 name: version 83 86 attributes: ··· 162 159 - gracetime 163 160 - leasetime 164 161 - scope 162 + - min-threads 165 163 - 166 164 name: threads-get 167 165 doc: get the number of running threads ··· 174 170 - gracetime 175 171 - leasetime 176 172 - scope 173 + - min-threads 177 174 - 178 175 name: version-set 179 176 doc: set nfs enabled versions
+61
Documentation/sunrpc/xdr/nfs4_1.x
··· 53 53 */ 54 54 typedef uint32_t bitmap4<>; 55 55 56 + typedef opaque utf8string<>; 57 + typedef utf8string utf8str_cis; 58 + typedef utf8string utf8str_cs; 59 + typedef utf8string utf8str_mixed; 60 + 56 61 /* 57 62 * Timeval 58 63 */ ··· 189 184 OPEN_DELEGATE_READ_ATTRS_DELEG = 4, 190 185 OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5 191 186 }; 187 + 188 + 189 + /* 190 + * The following content was extracted from draft-ietf-nfsv4-posix-acls 191 + */ 192 + 193 + enum aclmodel4 { 194 + ACL_MODEL_NFS4 = 1, 195 + ACL_MODEL_POSIX_DRAFT = 2, 196 + ACL_MODEL_NONE = 3 197 + }; 198 + pragma public aclmodel4; 199 + 200 + enum aclscope4 { 201 + ACL_SCOPE_FILE_OBJECT = 1, 202 + ACL_SCOPE_FILE_SYSTEM = 2, 203 + ACL_SCOPE_SERVER = 3 204 + }; 205 + pragma public aclscope4; 206 + 207 + enum posixacetag4 { 208 + POSIXACE4_TAG_USER_OBJ = 1, 209 + POSIXACE4_TAG_USER = 2, 210 + POSIXACE4_TAG_GROUP_OBJ = 3, 211 + POSIXACE4_TAG_GROUP = 4, 212 + POSIXACE4_TAG_MASK = 5, 213 + POSIXACE4_TAG_OTHER = 6 214 + }; 215 + pragma public posixacetag4; 216 + 217 + typedef uint32_t posixaceperm4; 218 + pragma public posixaceperm4; 219 + 220 + /* Bit definitions for posixaceperm4. */ 221 + const POSIXACE4_PERM_EXECUTE = 0x00000001; 222 + const POSIXACE4_PERM_WRITE = 0x00000002; 223 + const POSIXACE4_PERM_READ = 0x00000004; 224 + 225 + struct posixace4 { 226 + posixacetag4 tag; 227 + posixaceperm4 perm; 228 + utf8str_mixed who; 229 + }; 230 + 231 + typedef aclmodel4 fattr4_acl_trueform; 232 + typedef aclscope4 fattr4_acl_trueform_scope; 233 + typedef posixace4 fattr4_posix_default_acl<>; 234 + typedef posixace4 fattr4_posix_access_acl<>; 235 + 236 + %/* 237 + % * New for POSIX ACL extension 238 + % */ 239 + const FATTR4_ACL_TRUEFORM = 89; 240 + const FATTR4_ACL_TRUEFORM_SCOPE = 90; 241 + const FATTR4_POSIX_DEFAULT_ACL = 91; 242 + const FATTR4_POSIX_ACCESS_ACL = 92;
+3 -3
fs/lockd/svc.c
··· 141 141 */ 142 142 while (!svc_thread_should_stop(rqstp)) { 143 143 nlmsvc_retry_blocked(rqstp); 144 - svc_recv(rqstp); 144 + svc_recv(rqstp, 0); 145 145 } 146 146 if (nlmsvc_ops) 147 147 nlmsvc_invalidate_all(); ··· 340 340 return -ENOMEM; 341 341 } 342 342 343 - error = svc_set_num_threads(serv, NULL, 1); 343 + error = svc_set_num_threads(serv, 0, 1); 344 344 if (error < 0) { 345 345 svc_destroy(&serv); 346 346 return error; ··· 368 368 unregister_inet6addr_notifier(&lockd_inet6addr_notifier); 369 369 #endif 370 370 371 - svc_set_num_threads(nlmsvc_serv, NULL, 0); 371 + svc_set_num_threads(nlmsvc_serv, 0, 0); 372 372 timer_delete_sync(&nlmsvc_retry); 373 373 svc_destroy(&nlmsvc_serv); 374 374 dprintk("lockd_down: service destroyed\n");
-4
fs/lockd/svclock.c
··· 641 641 conflock->fl.c.flc_owner = lock->fl.c.flc_owner; 642 642 error = vfs_test_lock(file->f_file[mode], &conflock->fl); 643 643 if (error) { 644 - /* We can't currently deal with deferred test requests */ 645 - if (error == FILE_LOCK_DEFERRED) 646 - WARN_ON_ONCE(1); 647 - 648 644 ret = nlm_lck_denied_nolocks; 649 645 goto out; 650 646 }
+14 -3
fs/locks.c
··· 2262 2262 */ 2263 2263 int vfs_test_lock(struct file *filp, struct file_lock *fl) 2264 2264 { 2265 + int error = 0; 2266 + 2265 2267 WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops); 2266 2268 WARN_ON_ONCE(filp != fl->c.flc_file); 2267 2269 if (filp->f_op->lock) 2268 - return filp->f_op->lock(filp, F_GETLK, fl); 2269 - posix_test_lock(filp, fl); 2270 - return 0; 2270 + error = filp->f_op->lock(filp, F_GETLK, fl); 2271 + else 2272 + posix_test_lock(filp, fl); 2273 + 2274 + /* 2275 + * We don't expect FILE_LOCK_DEFERRED and callers cannot 2276 + * handle it. 2277 + */ 2278 + if (WARN_ON_ONCE(error == FILE_LOCK_DEFERRED)) 2279 + error = -EIO; 2280 + 2281 + return error; 2271 2282 } 2272 2283 EXPORT_SYMBOL_GPL(vfs_test_lock); 2273 2284
+5 -5
fs/nfs/callback.c
··· 81 81 set_freezable(); 82 82 83 83 while (!svc_thread_should_stop(rqstp)) 84 - svc_recv(rqstp); 84 + svc_recv(rqstp, 0); 85 85 86 86 svc_exit_thread(rqstp); 87 87 return 0; ··· 119 119 if (serv->sv_nrthreads == nrservs) 120 120 return 0; 121 121 122 - ret = svc_set_num_threads(serv, NULL, nrservs); 122 + ret = svc_set_num_threads(serv, 0, nrservs); 123 123 if (ret) { 124 - svc_set_num_threads(serv, NULL, 0); 124 + svc_set_num_threads(serv, 0, 0); 125 125 return ret; 126 126 } 127 127 dprintk("nfs_callback_up: service started\n"); ··· 242 242 cb_info->users++; 243 243 err_net: 244 244 if (!cb_info->users) { 245 - svc_set_num_threads(cb_info->serv, NULL, 0); 245 + svc_set_num_threads(cb_info->serv, 0, 0); 246 246 svc_destroy(&cb_info->serv); 247 247 } 248 248 err_create: ··· 268 268 nfs_callback_down_net(minorversion, serv, net); 269 269 cb_info->users--; 270 270 if (cb_info->users == 0) { 271 - svc_set_num_threads(serv, NULL, 0); 271 + svc_set_num_threads(serv, 0, 0); 272 272 dprintk("nfs_callback_down: service destroyed\n"); 273 273 xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv); 274 274 }
+19
fs/nfsd/Kconfig
··· 186 186 draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This 187 187 is currently an experimental feature and is therefore left disabled 188 188 by default. 189 + 190 + config NFSD_V4_POSIX_ACLS 191 + bool "Support NFSv4 POSIX draft ACLs" 192 + depends on NFSD_V4 193 + default n 194 + help 195 + Include experimental support for POSIX Access Control Lists 196 + (ACLs) in NFSv4 as specified in the IETF draft 197 + draft-ietf-nfsv4-posix-acls. This protocol extension enables 198 + NFSv4 clients to retrieve and modify POSIX ACLs on exported 199 + filesystems that support them. 200 + 201 + This feature is based on an unratified IETF draft 202 + specification that may change in ways that impact 203 + interoperability with existing clients. Enable only for 204 + testing environments or when interoperability with specific 205 + clients that implement this draft is required. 206 + 207 + If unsure, say N.
+9 -1
fs/nfsd/Makefile
··· 26 26 nfsd-$(CONFIG_NFS_LOCALIO) += localio.o 27 27 nfsd-$(CONFIG_DEBUG_FS) += debugfs.o 28 28 29 - 29 + # 30 + # XDR code generation (requires Python and additional packages) 31 + # 32 + # The generated *xdr_gen.{h,c} files are checked into git. Normal kernel 33 + # builds do not require the xdrgen tool or its Python dependencies. 34 + # 35 + # Developers modifying .x files in Documentation/sunrpc/xdr/ should run 36 + # "make xdrgen" to regenerate the affected files. 37 + # 30 38 .PHONY: xdrgen 31 39 32 40 xdrgen: ../../include/linux/sunrpc/xdrgen/nfs4_1.h nfs4xdr_gen.h nfs4xdr_gen.c
+1
fs/nfsd/acl.h
··· 49 49 struct nfs4_acl **acl); 50 50 __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, 51 51 struct nfsd_attrs *attr); 52 + void sort_pacl_range(struct posix_acl *pacl, int start, int end); 52 53 53 54 #endif /* LINUX_NFS4_ACL_H */
+3 -2
fs/nfsd/netlink.c
··· 24 24 }; 25 25 26 26 /* NFSD_CMD_THREADS_SET - do */ 27 - static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = { 27 + static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = { 28 28 [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, }, 29 29 [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, }, 30 30 [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, }, 31 31 [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, }, 32 + [NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, }, 32 33 }; 33 34 34 35 /* NFSD_CMD_VERSION_SET - do */ ··· 58 57 .cmd = NFSD_CMD_THREADS_SET, 59 58 .doit = nfsd_nl_threads_set_doit, 60 59 .policy = nfsd_threads_set_nl_policy, 61 - .maxattr = NFSD_A_SERVER_SCOPE, 60 + .maxattr = NFSD_A_SERVER_MIN_THREADS, 62 61 .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, 63 62 }, 64 63 {
+6 -1
fs/nfsd/netns.h
··· 67 67 struct lock_manager nfsd4_manager; 68 68 bool grace_ended; 69 69 bool grace_end_forced; 70 - bool client_tracking_active; 71 70 time64_t boot_time; 72 71 73 72 struct dentry *nfsd_client_dir; ··· 128 129 129 130 seqlock_t writeverf_lock; 130 131 unsigned char writeverf[8]; 132 + 133 + /* 134 + * Minimum number of threads to run per pool. If 0 then the 135 + * min == max requested number of threads. 136 + */ 137 + unsigned int min_threads; 131 138 132 139 u32 clientid_base; 133 140 u32 clientid_counter;
+1 -1
fs/nfsd/nfs2acl.c
··· 45 45 inode = d_inode(fh->fh_dentry); 46 46 47 47 if (argp->mask & ~NFS_ACL_MASK) { 48 - resp->status = nfserr_inval; 48 + resp->status = nfserr_io; 49 49 goto out; 50 50 } 51 51 resp->mask = argp->mask;
+13 -4
fs/nfsd/nfs4acl.c
··· 369 369 return false; 370 370 } 371 371 372 - static void 373 - sort_pacl_range(struct posix_acl *pacl, int start, int end) { 372 + /** 373 + * sort_pacl_range - sort a range of POSIX ACL entries by tag and id 374 + * @pacl: POSIX ACL containing entries to sort 375 + * @start: starting index of range to sort 376 + * @end: ending index of range to sort (inclusive) 377 + * 378 + * Sorts ACL entries in place so that USER entries are ordered by UID 379 + * and GROUP entries are ordered by GID. Required before calling 380 + * posix_acl_valid(). 381 + */ 382 + void sort_pacl_range(struct posix_acl *pacl, int start, int end) 383 + { 374 384 int sorted = 0, i; 375 385 376 - /* We just do a bubble sort; easy to do in place, and we're not 377 - * expecting acl's to be long enough to justify anything more. */ 386 + /* Bubble sort: acceptable here because ACLs are typically short. */ 378 387 while (!sorted) { 379 388 sorted = 1; 380 389 for (i = start; i < end; i++) {
+46 -6
fs/nfsd/nfs4idmap.c
··· 643 643 return idmap_id_to_name(xdr, rqstp, type, id); 644 644 } 645 645 646 - __be32 647 - nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, 648 - kuid_t *uid) 646 + /** 647 + * nfsd_map_name_to_uid - Map user@domain to local UID 648 + * @rqstp: RPC execution context 649 + * @name: user@domain name to be mapped 650 + * @namelen: length of name, in bytes 651 + * @uid: OUT: mapped local UID value 652 + * 653 + * Returns nfs_ok on success or an NFSv4 status code on failure. 654 + */ 655 + __be32 nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, 656 + size_t namelen, kuid_t *uid) 649 657 { 650 658 __be32 status; 651 659 u32 id = -1; 660 + 661 + /* 662 + * The idmap lookup below triggers an upcall that invokes 663 + * cache_check(). RQ_USEDEFERRAL must be clear to prevent 664 + * cache_check() from setting RQ_DROPME via svc_defer(). 665 + * NFSv4 servers are not permitted to drop requests. Also 666 + * RQ_DROPME will force NFSv4.1 session slot processing to 667 + * be skipped. 668 + */ 669 + WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags)); 652 670 653 671 if (name == NULL || namelen == 0) 654 672 return nfserr_inval; 655 673 656 674 status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); 675 + if (status) 676 + return status; 657 677 *uid = make_kuid(nfsd_user_namespace(rqstp), id); 658 678 if (!uid_valid(*uid)) 659 679 status = nfserr_badowner; 660 680 return status; 661 681 } 662 682 663 - __be32 664 - nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, 665 - kgid_t *gid) 683 + /** 684 + * nfsd_map_name_to_gid - Map user@domain to local GID 685 + * @rqstp: RPC execution context 686 + * @name: user@domain name to be mapped 687 + * @namelen: length of name, in bytes 688 + * @gid: OUT: mapped local GID value 689 + * 690 + * Returns nfs_ok on success or an NFSv4 status code on failure. 691 + */ 692 + __be32 nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, 693 + size_t namelen, kgid_t *gid) 666 694 { 667 695 __be32 status; 668 696 u32 id = -1; 697 + 698 + /* 699 + * The idmap lookup below triggers an upcall that invokes 700 + * cache_check(). RQ_USEDEFERRAL must be clear to prevent 701 + * cache_check() from setting RQ_DROPME via svc_defer(). 702 + * NFSv4 servers are not permitted to drop requests. Also 703 + * RQ_DROPME will force NFSv4.1 session slot processing to 704 + * be skipped. 705 + */ 706 + WARN_ON_ONCE(test_bit(RQ_USEDEFERRAL, &rqstp->rq_flags)); 669 707 670 708 if (name == NULL || namelen == 0) 671 709 return nfserr_inval; 672 710 673 711 status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); 712 + if (status) 713 + return status; 674 714 *gid = make_kgid(nfsd_user_namespace(rqstp), id); 675 715 if (!gid_valid(*gid)) 676 716 status = nfserr_badowner;
+216 -49
fs/nfsd/nfs4proc.c
··· 81 81 }; 82 82 83 83 static __be32 84 - check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 85 - u32 *bmval, u32 *writable) 84 + check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval, 85 + u32 *writable) 86 86 { 87 87 struct dentry *dentry = cstate->current_fh.fh_dentry; 88 88 struct svc_export *exp = cstate->current_fh.fh_export; ··· 90 90 if (!nfsd_attrs_supported(cstate->minorversion, bmval)) 91 91 return nfserr_attrnotsupp; 92 92 if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry))) 93 + return nfserr_attrnotsupp; 94 + if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL | 95 + FATTR4_WORD2_POSIX_ACCESS_ACL)) && 96 + !IS_POSIXACL(d_inode(dentry))) 93 97 return nfserr_attrnotsupp; 94 98 if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) && 95 99 !(exp->ex_flags & NFSEXP_SECURITY_LABEL)) ··· 107 103 } 108 104 109 105 static __be32 110 - nfsd4_check_open_attributes(struct svc_rqst *rqstp, 111 - struct nfsd4_compound_state *cstate, struct nfsd4_open *open) 106 + nfsd4_check_open_attributes(struct nfsd4_compound_state *cstate, 107 + struct nfsd4_open *open) 112 108 { 113 109 __be32 status = nfs_ok; 114 110 115 - if (open->op_create == NFS4_OPEN_CREATE) { 116 - if (open->op_createmode == NFS4_CREATE_UNCHECKED 117 - || open->op_createmode == NFS4_CREATE_GUARDED) 118 - status = check_attr_support(rqstp, cstate, 119 - open->op_bmval, nfsd_attrmask); 120 - else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1) 121 - status = check_attr_support(rqstp, cstate, 122 - open->op_bmval, nfsd41_ex_attrmask); 123 - } 111 + if (open->op_create != NFS4_OPEN_CREATE) 112 + return status; 124 113 114 + switch (open->op_createmode) { 115 + case NFS4_CREATE_UNCHECKED: 116 + case NFS4_CREATE_GUARDED: 117 + status = check_attr_support(cstate, open->op_bmval, 118 + nfsd_attrmask); 119 + break; 120 + case NFS4_CREATE_EXCLUSIVE4_1: 121 + status = check_attr_support(cstate, open->op_bmval, 122 + nfsd41_ex_attrmask); 123 + break; 124 + } 125 125 return status; 126 126 } 127 127 ··· 274 266 if (host_err) 275 267 return nfserrno(host_err); 276 268 277 - if (is_create_with_attrs(open)) 278 - nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); 269 + if (open->op_acl) { 270 + if (open->op_dpacl || open->op_pacl) { 271 + status = nfserr_inval; 272 + goto out_write; 273 + } 274 + if (is_create_with_attrs(open)) 275 + nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); 276 + } else if (is_create_with_attrs(open)) { 277 + /* The dpacl and pacl will get released by nfsd_attrs_free(). */ 278 + attrs.na_dpacl = open->op_dpacl; 279 + attrs.na_pacl = open->op_pacl; 280 + open->op_dpacl = NULL; 281 + open->op_pacl = NULL; 282 + } 279 283 280 284 child = start_creating(&nop_mnt_idmap, parent, 281 285 &QSTR_LEN(open->op_fname, open->op_fnamelen)); ··· 398 378 399 379 if (attrs.na_labelerr) 400 380 open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 401 - if (attrs.na_aclerr) 381 + if (attrs.na_paclerr || attrs.na_dpaclerr) 402 382 open->op_bmval[0] &= ~FATTR4_WORD0_ACL; 383 + if (attrs.na_dpaclerr) 384 + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; 385 + if (attrs.na_paclerr) 386 + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; 403 387 out: 404 388 end_creating(child); 405 389 nfsd_attrs_free(&attrs); ··· 571 547 open->op_rqstp = rqstp; 572 548 573 549 /* This check required by spec. */ 574 - if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) 575 - return nfserr_inval; 550 + if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) { 551 + status = nfserr_inval; 552 + goto out_err; 553 + } 576 554 577 555 open->op_created = false; 578 556 /* ··· 583 557 */ 584 558 if (nfsd4_has_session(cstate) && 585 559 !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) && 586 - open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) 587 - return nfserr_grace; 560 + open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) { 561 + status = nfserr_grace; 562 + goto out_err; 563 + } 588 564 589 565 if (nfsd4_has_session(cstate)) 590 566 copy_clientid(&open->op_clientid, cstate->session); ··· 612 584 goto out; 613 585 } 614 586 615 - status = nfsd4_check_open_attributes(rqstp, cstate, open); 587 + status = nfsd4_check_open_attributes(cstate, open); 616 588 if (status) 617 589 goto out; 618 590 ··· 673 645 } 674 646 nfsd4_cleanup_open_state(cstate, open); 675 647 nfsd4_bump_seqid(cstate, status); 648 + out_err: 649 + posix_acl_release(open->op_dpacl); 650 + posix_acl_release(open->op_pacl); 676 651 return status; 677 652 } 678 653 ··· 816 785 struct nfsd_attrs attrs = { 817 786 .na_iattr = &create->cr_iattr, 818 787 .na_seclabel = &create->cr_label, 788 + .na_dpacl = create->cr_dpacl, 789 + .na_pacl = create->cr_pacl, 819 790 }; 820 791 struct svc_fh resfh; 821 792 __be32 status; 822 793 dev_t rdev; 823 794 795 + create->cr_dpacl = NULL; 796 + create->cr_pacl = NULL; 797 + 824 798 fh_init(&resfh, NFS4_FHSIZE); 825 799 826 800 status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP); 827 801 if (status) 828 - return status; 802 + goto out_aftermask; 829 803 830 - status = check_attr_support(rqstp, cstate, create->cr_bmval, 831 - nfsd_attrmask); 804 + status = check_attr_support(cstate, create->cr_bmval, nfsd_attrmask); 832 805 if (status) 833 - return status; 806 + goto out_aftermask; 834 807 835 - status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); 808 + if (create->cr_acl) { 809 + if (create->cr_dpacl || create->cr_pacl) { 810 + status = nfserr_inval; 811 + goto out_aftermask; 812 + } 813 + status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, 814 + &attrs); 815 + } 836 816 current->fs->umask = create->cr_umask; 837 817 switch (create->cr_type) { 838 818 case NF4LNK: ··· 902 860 903 861 if (attrs.na_labelerr) 904 862 create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; 905 - if (attrs.na_aclerr) 863 + if (attrs.na_paclerr || attrs.na_dpaclerr) 906 864 create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; 865 + if (attrs.na_dpaclerr) 866 + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; 867 + if (attrs.na_paclerr) 868 + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; 907 869 set_change_info(&create->cr_cinfo, &cstate->current_fh); 908 870 fh_dup2(&cstate->current_fh, &resfh); 909 871 out: 910 872 fh_put(&resfh); 911 873 out_umask: 912 874 current->fs->umask = 0; 875 + out_aftermask: 913 876 nfsd_attrs_free(&attrs); 914 877 return status; 915 878 } ··· 1219 1172 struct nfsd_attrs attrs = { 1220 1173 .na_iattr = &setattr->sa_iattr, 1221 1174 .na_seclabel = &setattr->sa_label, 1175 + .na_pacl = setattr->sa_pacl, 1176 + .na_dpacl = setattr->sa_dpacl, 1222 1177 }; 1223 1178 bool save_no_wcc, deleg_attrs; 1224 1179 struct nfs4_stid *st = NULL; 1225 1180 struct inode *inode; 1226 1181 __be32 status = nfs_ok; 1227 1182 int err; 1183 + 1184 + /* Transfer ownership to attrs for cleanup via nfsd_attrs_free() */ 1185 + setattr->sa_pacl = NULL; 1186 + setattr->sa_dpacl = NULL; 1228 1187 1229 1188 deleg_attrs = setattr->sa_bmval[2] & (FATTR4_WORD2_TIME_DELEG_ACCESS | 1230 1189 FATTR4_WORD2_TIME_DELEG_MODIFY); ··· 1245 1192 &cstate->current_fh, &setattr->sa_stateid, 1246 1193 flags, NULL, &st); 1247 1194 if (status) 1248 - return status; 1195 + goto out_err; 1249 1196 } 1250 1197 1251 1198 if (deleg_attrs) { ··· 1263 1210 if (st) 1264 1211 nfs4_put_stid(st); 1265 1212 if (status) 1266 - return status; 1213 + goto out_err; 1267 1214 1268 1215 err = fh_want_write(&cstate->current_fh); 1269 - if (err) 1270 - return nfserrno(err); 1216 + if (err) { 1217 + status = nfserrno(err); 1218 + goto out_err; 1219 + } 1271 1220 status = nfs_ok; 1272 1221 1273 - status = check_attr_support(rqstp, cstate, setattr->sa_bmval, 1274 - nfsd_attrmask); 1222 + status = check_attr_support(cstate, setattr->sa_bmval, nfsd_attrmask); 1275 1223 if (status) 1276 1224 goto out; 1225 + 1226 + if (setattr->sa_acl && (attrs.na_dpacl || attrs.na_pacl)) { 1227 + status = nfserr_inval; 1228 + goto out; 1229 + } 1277 1230 1278 1231 inode = cstate->current_fh.fh_dentry->d_inode; 1279 1232 status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, ··· 1294 1235 if (!status) 1295 1236 status = nfserrno(attrs.na_labelerr); 1296 1237 if (!status) 1297 - status = nfserrno(attrs.na_aclerr); 1238 + status = nfserrno(attrs.na_dpaclerr); 1239 + if (!status) 1240 + status = nfserrno(attrs.na_paclerr); 1298 1241 out: 1299 - nfsd_attrs_free(&attrs); 1300 1242 fh_drop_write(&cstate->current_fh); 1243 + out_err: 1244 + nfsd_attrs_free(&attrs); 1301 1245 return status; 1302 1246 } 1303 1247 ··· 1492 1430 kfree(copy); 1493 1431 } 1494 1432 1433 + static void release_copy_files(struct nfsd4_copy *copy); 1434 + 1495 1435 static void nfsd4_stop_copy(struct nfsd4_copy *copy) 1496 1436 { 1497 1437 trace_nfsd_copy_async_cancel(copy); 1498 1438 if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) { 1499 1439 kthread_stop(copy->copy_task); 1500 - copy->nfserr = nfs_ok; 1440 + if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags)) 1441 + copy->nfserr = nfs_ok; 1501 1442 set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags); 1502 1443 } 1444 + 1445 + /* 1446 + * The copy was removed from async_copies before this function 1447 + * was called, so the reaper cannot clean it up. Release files 1448 + * here regardless of who won the STOPPED race. If the thread 1449 + * set STOPPED, it has finished using the files. If STOPPED 1450 + * was set here, kthread_stop() waited for the thread to exit. 1451 + */ 1452 + release_copy_files(copy); 1503 1453 nfs4_put_copy(copy); 1504 1454 } 1505 1455 ··· 1539 1465 while ((copy = nfsd4_unhash_copy(clp)) != NULL) 1540 1466 nfsd4_stop_copy(copy); 1541 1467 } 1468 + 1469 + static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy, 1470 + const struct super_block *sb) 1471 + { 1472 + if (copy->nf_src && 1473 + file_inode(copy->nf_src->nf_file)->i_sb == sb) 1474 + return true; 1475 + if (copy->nf_dst && 1476 + file_inode(copy->nf_dst->nf_file)->i_sb == sb) 1477 + return true; 1478 + return false; 1479 + } 1480 + 1481 + /** 1482 + * nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb 1483 + * @net: net namespace containing the copy operations 1484 + * @sb: targeted superblock 1485 + */ 1486 + void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) 1487 + { 1488 + struct nfsd_net *nn = net_generic(net, nfsd_net_id); 1489 + struct nfsd4_copy *copy, *tmp; 1490 + struct nfs4_client *clp; 1491 + unsigned int idhashval; 1492 + LIST_HEAD(to_cancel); 1493 + 1494 + spin_lock(&nn->client_lock); 1495 + for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) { 1496 + struct list_head *head = &nn->conf_id_hashtbl[idhashval]; 1497 + 1498 + list_for_each_entry(clp, head, cl_idhash) { 1499 + spin_lock(&clp->async_lock); 1500 + list_for_each_entry_safe(copy, tmp, 1501 + &clp->async_copies, copies) { 1502 + if (nfsd4_copy_on_sb(copy, sb)) { 1503 + refcount_inc(&copy->refcount); 1504 + /* 1505 + * Hold a reference on the client while 1506 + * nfsd4_stop_copy() runs. Unlike 1507 + * nfsd4_unhash_copy(), cp_clp is not 1508 + * NULLed here because nfsd4_send_cb_offload() 1509 + * needs a valid client to send CB_OFFLOAD. 1510 + * That function takes its own reference to 1511 + * survive callback flight. 1512 + */ 1513 + kref_get(&clp->cl_nfsdfs.cl_ref); 1514 + copy->nfserr = nfserr_admin_revoked; 1515 + set_bit(NFSD4_COPY_F_CB_ERROR, 1516 + &copy->cp_flags); 1517 + list_move(&copy->copies, &to_cancel); 1518 + } 1519 + } 1520 + spin_unlock(&clp->async_lock); 1521 + } 1522 + } 1523 + spin_unlock(&nn->client_lock); 1524 + 1525 + list_for_each_entry_safe(copy, tmp, &to_cancel, copies) { 1526 + struct nfs4_client *clp = copy->cp_clp; 1527 + 1528 + list_del_init(&copy->copies); 1529 + nfsd4_stop_copy(copy); 1530 + nfsd4_put_client(clp); 1531 + } 1532 + } 1533 + 1542 1534 #ifdef CONFIG_NFSD_V4_2_INTER_SSC 1543 1535 1544 1536 extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, ··· 1894 1754 container_of(cbo, struct nfsd4_copy, cp_cb_offload); 1895 1755 1896 1756 set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags); 1757 + nfsd4_put_client(cb->cb_clp); 1897 1758 } 1898 1759 1899 1760 static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, ··· 2014 1873 2015 1874 static void release_copy_files(struct nfsd4_copy *copy) 2016 1875 { 2017 - if (copy->nf_src) 1876 + if (copy->nf_src) { 2018 1877 nfsd_file_put(copy->nf_src); 2019 - if (copy->nf_dst) 1878 + copy->nf_src = NULL; 1879 + } 1880 + if (copy->nf_dst) { 2020 1881 nfsd_file_put(copy->nf_dst); 1882 + copy->nf_dst = NULL; 1883 + } 2021 1884 } 2022 1885 2023 1886 static void cleanup_async_copy(struct nfsd4_copy *copy) ··· 2040 1895 static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) 2041 1896 { 2042 1897 struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload; 1898 + struct nfs4_client *clp = copy->cp_clp; 1899 + 1900 + /* 1901 + * cp_clp is NULL when called via nfsd4_shutdown_copy() during 1902 + * client destruction. Skip the callback; the client is gone. 1903 + */ 1904 + if (!clp) { 1905 + set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags); 1906 + return; 1907 + } 2043 1908 2044 1909 memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res)); 2045 1910 memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh)); 2046 1911 cbo->co_nfserr = copy->nfserr; 2047 1912 cbo->co_retries = 5; 2048 1913 2049 - nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, 1914 + /* 1915 + * Hold a reference on the client while the callback is in flight. 1916 + * Released in nfsd4_cb_offload_release(). 1917 + */ 1918 + kref_get(&clp->cl_nfsdfs.cl_ref); 1919 + 1920 + nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops, 2050 1921 NFSPROC4_CLNT_CB_OFFLOAD); 2051 1922 nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid, 2052 1923 cbo->co_referring_slotid, 2053 1924 cbo->co_referring_seqno); 2054 - trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid, 1925 + trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid, 2055 1926 &cbo->co_fh, copy->cp_count, copy->nfserr); 2056 1927 nfsd4_try_run_cb(&cbo->co_cb); 2057 1928 } ··· 2082 1921 static int nfsd4_do_async_copy(void *data) 2083 1922 { 2084 1923 struct nfsd4_copy *copy = (struct nfsd4_copy *)data; 1924 + __be32 nfserr = nfs_ok; 2085 1925 2086 1926 trace_nfsd_copy_async(copy); 2087 1927 if (nfsd4_ssc_is_inter(copy)) { ··· 2093 1931 if (IS_ERR(filp)) { 2094 1932 switch (PTR_ERR(filp)) { 2095 1933 case -EBADF: 2096 - copy->nfserr = nfserr_wrong_type; 1934 + nfserr = nfserr_wrong_type; 2097 1935 break; 2098 1936 default: 2099 - copy->nfserr = nfserr_offload_denied; 1937 + nfserr = nfserr_offload_denied; 2100 1938 } 2101 1939 /* ss_mnt will be unmounted by the laundromat */ 2102 1940 goto do_callback; 2103 1941 } 2104 - copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, 2105 - false); 1942 + nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, 1943 + false); 2106 1944 nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst); 2107 1945 } else { 2108 - copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, 2109 - copy->nf_dst->nf_file, false); 1946 + nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, 1947 + copy->nf_dst->nf_file, false); 2110 1948 } 2111 1949 2112 1950 do_callback: 1951 + if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags)) 1952 + copy->nfserr = nfserr; 2113 1953 /* The kthread exits forthwith. Ensure that a subsequent 2114 1954 * OFFLOAD_CANCEL won't try to kill it again. */ 2115 1955 set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags); ··· 2435 2271 if (status) 2436 2272 return status; 2437 2273 2438 - status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL); 2274 + status = check_attr_support(cstate, verify->ve_bmval, NULL); 2439 2275 if (status) 2440 2276 return status; 2441 2277 ··· 2443 2279 || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) 2444 2280 return nfserr_inval; 2445 2281 if (verify->ve_attrlen & 3) 2282 + return nfserr_inval; 2283 + 2284 + /* The POSIX draft ACLs cannot be tested via (N)VERIFY. */ 2285 + if (verify->ve_bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL | 2286 + FATTR4_WORD2_POSIX_ACCESS_ACL)) 2446 2287 return nfserr_inval; 2447 2288 2448 2289 /* count in words: ··· 3185 3016 BUG_ON(cstate->replay_owner); 3186 3017 out: 3187 3018 cstate->status = status; 3188 - /* Reset deferral mechanism for RPC deferrals */ 3189 - set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); 3190 3019 return rpc_success; 3191 3020 } 3192 3021
+29 -23
fs/nfsd/nfs4state.c
··· 1253 1253 if (ret) { 1254 1254 struct inode *inode = file_inode(f); 1255 1255 1256 - pr_notice_ratelimited("Unable to update timestamps on inode %02x:%02x:%lu: %d\n", 1256 + pr_notice_ratelimited("nfsd: Unable to update timestamps on inode %02x:%02x:%lu: %d\n", 1257 1257 MAJOR(inode->i_sb->s_dev), 1258 1258 MINOR(inode->i_sb->s_dev), 1259 1259 inode->i_ino, ret); ··· 2413 2413 kmem_cache_free(client_slab, clp); 2414 2414 } 2415 2415 2416 - static void drop_client(struct nfs4_client *clp) 2416 + /** 2417 + * nfsd4_put_client - release a reference on an nfs4_client 2418 + * @clp: the client to be released 2419 + * 2420 + * When the last reference is released, the client is freed. 2421 + */ 2422 + void nfsd4_put_client(struct nfs4_client *clp) 2417 2423 { 2418 2424 kref_put(&clp->cl_nfsdfs.cl_ref, __free_client); 2419 2425 } ··· 2441 2435 clp->cl_nfsd_dentry = NULL; 2442 2436 wake_up_all(&expiry_wq); 2443 2437 } 2444 - drop_client(clp); 2438 + nfsd4_put_client(clp); 2445 2439 } 2446 2440 2447 2441 /* must be called under the client_lock */ ··· 2839 2833 spin_unlock(&clp->cl_lock); 2840 2834 seq_puts(m, "\n"); 2841 2835 2842 - drop_client(clp); 2836 + nfsd4_put_client(clp); 2843 2837 2844 2838 return 0; 2845 2839 } ··· 3105 3099 3106 3100 ret = seq_open(file, &states_seq_ops); 3107 3101 if (ret) { 3108 - drop_client(clp); 3102 + nfsd4_put_client(clp); 3109 3103 return ret; 3110 3104 } 3111 3105 s = file->private_data; ··· 3119 3113 struct nfs4_client *clp = m->private; 3120 3114 3121 3115 /* XXX: alternatively, we could get/drop in seq start/stop */ 3122 - drop_client(clp); 3116 + nfsd4_put_client(clp); 3123 3117 return seq_release(inode, file); 3124 3118 } 3125 3119 ··· 3175 3169 if (!clp) 3176 3170 return -ENXIO; 3177 3171 force_expire_client(clp); 3178 - drop_client(clp); 3172 + nfsd4_put_client(clp); 3179 3173 return 7; 3180 3174 } 3181 3175 ··· 3210 3204 { 3211 3205 struct nfs4_client *clp = cb->cb_clp; 3212 3206 3213 - drop_client(clp); 3207 + nfsd4_put_client(clp); 3214 3208 } 3215 3209 3216 3210 static int ··· 6359 6353 dp->dl_ctime = stat.ctime; 6360 6354 dp->dl_mtime = stat.mtime; 6361 6355 spin_lock(&f->f_lock); 6362 - f->f_mode |= FMODE_NOCMTIME; 6356 + if (deleg_ts) 6357 + f->f_mode |= FMODE_NOCMTIME; 6363 6358 spin_unlock(&f->f_lock); 6364 6359 trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); 6365 6360 } else { ··· 6644 6637 { 6645 6638 if (!nn->client_tracking_ops) 6646 6639 return false; 6647 - spin_lock(&nn->client_lock); 6648 - if (nn->grace_ended || !nn->client_tracking_active) { 6649 - spin_unlock(&nn->client_lock); 6640 + if (READ_ONCE(nn->grace_ended)) 6650 6641 return false; 6651 - } 6642 + /* laundromat_work must be initialised now, though it might be disabled */ 6652 6643 WRITE_ONCE(nn->grace_end_forced, true); 6644 + /* mod_delayed_work() doesn't queue work after 6645 + * nfs4_state_shutdown_net() has called disable_delayed_work_sync() 6646 + */ 6653 6647 mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); 6654 - spin_unlock(&nn->client_lock); 6655 6648 return true; 6656 6649 } 6657 6650 ··· 8987 8980 nn->boot_time = ktime_get_real_seconds(); 8988 8981 nn->grace_ended = false; 8989 8982 nn->grace_end_forced = false; 8990 - nn->client_tracking_active = false; 8991 8983 nn->nfsd4_manager.block_opens = true; 8992 8984 INIT_LIST_HEAD(&nn->nfsd4_manager.list); 8993 8985 INIT_LIST_HEAD(&nn->client_lru); ··· 9001 8995 INIT_LIST_HEAD(&nn->blocked_locks_lru); 9002 8996 9003 8997 INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); 8998 + /* Make sure this cannot run until client tracking is initialised */ 8999 + disable_delayed_work(&nn->laundromat_work); 9004 9000 INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker); 9005 9001 get_net(net); 9006 9002 ··· 9070 9062 locks_start_grace(net, &nn->nfsd4_manager); 9071 9063 nfsd4_client_tracking_init(net); 9072 9064 /* safe for laundromat to run now */ 9073 - spin_lock(&nn->client_lock); 9074 - nn->client_tracking_active = true; 9075 - spin_unlock(&nn->client_lock); 9065 + enable_delayed_work(&nn->laundromat_work); 9076 9066 if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) 9077 9067 goto skip_grace; 9078 9068 printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n", ··· 9119 9113 9120 9114 shrinker_free(nn->nfsd_client_shrinker); 9121 9115 cancel_work_sync(&nn->nfsd_shrinker_work); 9122 - spin_lock(&nn->client_lock); 9123 - nn->client_tracking_active = false; 9124 - spin_unlock(&nn->client_lock); 9125 - cancel_delayed_work_sync(&nn->laundromat_work); 9116 + disable_delayed_work_sync(&nn->laundromat_work); 9126 9117 locks_end_grace(&nn->nfsd4_manager); 9127 9118 9128 9119 INIT_LIST_HEAD(&reaplist); ··· 9523 9520 spin_unlock(&clp->cl_lock); 9524 9521 spin_unlock(&state_lock); 9525 9522 9526 - if (!status) 9523 + if (!status) { 9524 + put_nfs4_file(fp); 9527 9525 return dp; 9526 + } 9528 9527 9529 9528 /* Something failed. Drop the lease and clean up the stid */ 9530 9529 kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp); ··· 9534 9529 nfs4_put_stid(&dp->dl_stid); 9535 9530 out_delegees: 9536 9531 put_deleg_file(fp); 9532 + put_nfs4_file(fp); 9537 9533 return ERR_PTR(status); 9538 9534 }
+357 -6
fs/nfsd/nfs4xdr.c
··· 43 43 #include <linux/sunrpc/addr.h> 44 44 #include <linux/xattr.h> 45 45 #include <linux/vmalloc.h> 46 + #include <linux/nfsacl.h> 46 47 47 48 #include <uapi/linux/xattr.h> 48 49 ··· 378 377 return nfs_ok; 379 378 } 380 379 380 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 381 + 382 + static short nfsd4_posixacetag4_to_tag(posixacetag4 tag) 383 + { 384 + switch (tag) { 385 + case POSIXACE4_TAG_USER_OBJ: return ACL_USER_OBJ; 386 + case POSIXACE4_TAG_GROUP_OBJ: return ACL_GROUP_OBJ; 387 + case POSIXACE4_TAG_USER: return ACL_USER; 388 + case POSIXACE4_TAG_GROUP: return ACL_GROUP; 389 + case POSIXACE4_TAG_MASK: return ACL_MASK; 390 + case POSIXACE4_TAG_OTHER: return ACL_OTHER; 391 + } 392 + return ACL_OTHER; 393 + } 394 + 395 + static __be32 396 + nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp, 397 + struct posix_acl_entry *ace) 398 + { 399 + posixaceperm4 perm; 400 + __be32 *p, status; 401 + posixacetag4 tag; 402 + u32 len; 403 + 404 + if (!xdrgen_decode_posixacetag4(argp->xdr, &tag)) 405 + return nfserr_bad_xdr; 406 + ace->e_tag = nfsd4_posixacetag4_to_tag(tag); 407 + 408 + if (!xdrgen_decode_posixaceperm4(argp->xdr, &perm)) 409 + return nfserr_bad_xdr; 410 + if (perm & ~S_IRWXO) 411 + return nfserr_bad_xdr; 412 + ace->e_perm = perm; 413 + 414 + if (xdr_stream_decode_u32(argp->xdr, &len) < 0) 415 + return nfserr_bad_xdr; 416 + p = xdr_inline_decode(argp->xdr, len); 417 + if (!p) 418 + return nfserr_bad_xdr; 419 + switch (tag) { 420 + case POSIXACE4_TAG_USER: 421 + if (len > 0) 422 + status = nfsd_map_name_to_uid(argp->rqstp, 423 + (char *)p, len, &ace->e_uid); 424 + else 425 + status = nfserr_bad_xdr; 426 + break; 427 + case POSIXACE4_TAG_GROUP: 428 + if (len > 0) 429 + status = nfsd_map_name_to_gid(argp->rqstp, 430 + (char *)p, len, &ace->e_gid); 431 + else 432 + status = nfserr_bad_xdr; 433 + break; 434 + default: 435 + status = nfs_ok; 436 + } 437 + 438 + return status; 439 + } 440 + 441 + static noinline __be32 442 + nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl) 443 + { 444 + struct posix_acl_entry *ace; 445 + __be32 status; 446 + u32 count; 447 + 448 + if (xdr_stream_decode_u32(argp->xdr, &count) < 0) 449 + return nfserr_bad_xdr; 450 + 451 + *acl = posix_acl_alloc(count, GFP_KERNEL); 452 + if (*acl == NULL) 453 + return nfserr_resource; 454 + 455 + (*acl)->a_count = count; 456 + for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) { 457 + status = nfsd4_decode_posixace4(argp, ace); 458 + if (status) { 459 + posix_acl_release(*acl); 460 + *acl = NULL; 461 + return status; 462 + } 463 + } 464 + 465 + /* 466 + * posix_acl_valid() requires the ACEs to be sorted. 467 + * If they are already sorted, sort_pacl_range() will return 468 + * after one pass through the ACEs, since it implements bubble sort. 469 + * Note that a count == 0 is used to delete a POSIX ACL and a count 470 + * of 1 or 2 will always be found invalid by posix_acl_valid(). 471 + */ 472 + if (count >= 3) 473 + sort_pacl_range(*acl, 0, count - 1); 474 + 475 + return nfs_ok; 476 + } 477 + 478 + #endif /* CONFIG_NFSD_V4_POSIX_ACLS */ 479 + 381 480 static __be32 382 481 nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, 383 482 struct iattr *iattr, struct nfs4_acl **acl, 384 - struct xdr_netobj *label, int *umask) 483 + struct xdr_netobj *label, int *umask, 484 + struct posix_acl **dpaclp, struct posix_acl **paclp) 385 485 { 386 486 unsigned int starting_pos; 387 487 u32 attrlist4_count; ··· 645 543 ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG; 646 544 } 647 545 546 + *dpaclp = NULL; 547 + *paclp = NULL; 548 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 549 + if (bmval[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) { 550 + struct posix_acl *dpacl; 551 + 552 + status = nfsd4_decode_posixacl(argp, &dpacl); 553 + if (status) 554 + return status; 555 + *dpaclp = dpacl; 556 + } 557 + if (bmval[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) { 558 + struct posix_acl *pacl; 559 + 560 + status = nfsd4_decode_posixacl(argp, &pacl); 561 + if (status) { 562 + posix_acl_release(*dpaclp); 563 + *dpaclp = NULL; 564 + return status; 565 + } 566 + *paclp = pacl; 567 + } 568 + #endif /* CONFIG_NFSD_V4_POSIX_ACLS */ 569 + 648 570 /* request sanity: did attrlist4 contain the expected number of words? */ 649 - if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) 571 + if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) { 572 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 573 + posix_acl_release(*dpaclp); 574 + posix_acl_release(*paclp); 575 + *dpaclp = NULL; 576 + *paclp = NULL; 577 + #endif 650 578 return nfserr_bad_xdr; 579 + } 651 580 652 581 return nfs_ok; 653 582 } ··· 982 849 status = nfsd4_decode_fattr4(argp, create->cr_bmval, 983 850 ARRAY_SIZE(create->cr_bmval), 984 851 &create->cr_iattr, &create->cr_acl, 985 - &create->cr_label, &create->cr_umask); 852 + &create->cr_label, &create->cr_umask, 853 + &create->cr_dpacl, &create->cr_pacl); 986 854 if (status) 987 855 return status; 988 856 ··· 1134 1000 status = nfsd4_decode_fattr4(argp, open->op_bmval, 1135 1001 ARRAY_SIZE(open->op_bmval), 1136 1002 &open->op_iattr, &open->op_acl, 1137 - &open->op_label, &open->op_umask); 1003 + &open->op_label, &open->op_umask, 1004 + &open->op_dpacl, &open->op_pacl); 1138 1005 if (status) 1139 1006 return status; 1140 1007 break; ··· 1153 1018 status = nfsd4_decode_fattr4(argp, open->op_bmval, 1154 1019 ARRAY_SIZE(open->op_bmval), 1155 1020 &open->op_iattr, &open->op_acl, 1156 - &open->op_label, &open->op_umask); 1021 + &open->op_label, &open->op_umask, 1022 + &open->op_dpacl, &open->op_pacl); 1157 1023 if (status) 1158 1024 return status; 1159 1025 break; ··· 1481 1345 return nfsd4_decode_fattr4(argp, setattr->sa_bmval, 1482 1346 ARRAY_SIZE(setattr->sa_bmval), 1483 1347 &setattr->sa_iattr, &setattr->sa_acl, 1484 - &setattr->sa_label, NULL); 1348 + &setattr->sa_label, NULL, &setattr->sa_dpacl, 1349 + &setattr->sa_pacl); 1485 1350 } 1486 1351 1487 1352 static __be32 ··· 2986 2849 { return 0; } 2987 2850 #endif 2988 2851 2852 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 2853 + 2854 + static int nfsd4_posix_tagtotype(short tag) 2855 + { 2856 + switch (tag) { 2857 + case ACL_USER_OBJ: return POSIXACE4_TAG_USER_OBJ; 2858 + case ACL_GROUP_OBJ: return POSIXACE4_TAG_GROUP_OBJ; 2859 + case ACL_USER: return POSIXACE4_TAG_USER; 2860 + case ACL_GROUP: return POSIXACE4_TAG_GROUP; 2861 + case ACL_MASK: return POSIXACE4_TAG_MASK; 2862 + case ACL_OTHER: return POSIXACE4_TAG_OTHER; 2863 + default: return -EINVAL; 2864 + } 2865 + } 2866 + 2867 + static __be32 2868 + nfsd4_encode_posixace4(struct xdr_stream *xdr, struct svc_rqst *rqstp, 2869 + struct posix_acl_entry *acep) 2870 + { 2871 + __be32 status; 2872 + int type; 2873 + 2874 + type = nfsd4_posix_tagtotype(acep->e_tag); 2875 + if (type < 0) 2876 + return nfserr_resource; 2877 + if (!xdrgen_encode_posixacetag4(xdr, type)) 2878 + return nfserr_resource; 2879 + if (!xdrgen_encode_posixaceperm4(xdr, acep->e_perm)) 2880 + return nfserr_resource; 2881 + 2882 + /* who */ 2883 + switch (acep->e_tag) { 2884 + case ACL_USER_OBJ: 2885 + case ACL_GROUP_OBJ: 2886 + case ACL_MASK: 2887 + case ACL_OTHER: 2888 + if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) 2889 + return nfserr_resource; 2890 + break; 2891 + case ACL_USER: 2892 + status = nfsd4_encode_user(xdr, rqstp, acep->e_uid); 2893 + if (status != nfs_ok) 2894 + return status; 2895 + break; 2896 + case ACL_GROUP: 2897 + status = nfsd4_encode_group(xdr, rqstp, acep->e_gid); 2898 + if (status != nfs_ok) 2899 + return status; 2900 + break; 2901 + default: 2902 + return nfserr_resource; 2903 + } 2904 + return nfs_ok; 2905 + } 2906 + 2907 + static __be32 2908 + nfsd4_encode_posixacl(struct xdr_stream *xdr, struct svc_rqst *rqstp, 2909 + struct posix_acl *acl) 2910 + { 2911 + __be32 status; 2912 + int i; 2913 + 2914 + if (!acl) { 2915 + if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) 2916 + return nfserr_resource; 2917 + return nfs_ok; 2918 + } 2919 + 2920 + if (acl->a_count > NFS_ACL_MAX_ENTRIES) 2921 + return nfserr_resource; 2922 + if (xdr_stream_encode_u32(xdr, acl->a_count) != XDR_UNIT) 2923 + return nfserr_resource; 2924 + for (i = 0; i < acl->a_count; i++) { 2925 + status = nfsd4_encode_posixace4(xdr, rqstp, &acl->a_entries[i]); 2926 + if (status != nfs_ok) 2927 + return status; 2928 + } 2929 + 2930 + return nfs_ok; 2931 + } 2932 + 2933 + #endif /* CONFIG_NFSD_V4_POSIX_ACL */ 2934 + 2989 2935 static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err) 2990 2936 { 2991 2937 /* As per referral draft: */ ··· 3149 2929 u64 change_attr; 3150 2930 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 3151 2931 struct lsm_context context; 2932 + #endif 2933 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 2934 + struct posix_acl *dpacl; 2935 + struct posix_acl *pacl; 3152 2936 #endif 3153 2937 u32 rdattr_err; 3154 2938 bool contextsupport; ··· 3694 3470 return nfs_ok; 3695 3471 } 3696 3472 3473 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 3474 + 3475 + static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr, 3476 + const struct nfsd4_fattr_args *args) 3477 + { 3478 + aclmodel4 trueform = ACL_MODEL_NONE; 3479 + 3480 + if (IS_POSIXACL(d_inode(args->dentry))) 3481 + trueform = ACL_MODEL_POSIX_DRAFT; 3482 + if (!xdrgen_encode_aclmodel4(xdr, trueform)) 3483 + return nfserr_resource; 3484 + return nfs_ok; 3485 + } 3486 + 3487 + static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, 3488 + const struct nfsd4_fattr_args *args) 3489 + { 3490 + if (!xdrgen_encode_aclscope4(xdr, ACL_SCOPE_FILE_SYSTEM)) 3491 + return nfserr_resource; 3492 + return nfs_ok; 3493 + } 3494 + 3495 + static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, 3496 + const struct nfsd4_fattr_args *args) 3497 + { 3498 + return nfsd4_encode_posixacl(xdr, args->rqstp, args->dpacl); 3499 + } 3500 + 3501 + static __be32 nfsd4_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, 3502 + const struct nfsd4_fattr_args *args) 3503 + { 3504 + return nfsd4_encode_posixacl(xdr, args->rqstp, args->pacl); 3505 + } 3506 + 3507 + #endif /* CONFIG_NFSD_V4_POSIX_ACLS */ 3508 + 3697 3509 static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = { 3698 3510 [FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs, 3699 3511 [FATTR4_TYPE] = nfsd4_encode_fattr4_type, ··· 3833 3573 [FATTR4_TIME_DELEG_ACCESS] = nfsd4_encode_fattr4__inval, 3834 3574 [FATTR4_TIME_DELEG_MODIFY] = nfsd4_encode_fattr4__inval, 3835 3575 [FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments, 3576 + 3577 + /* Reserved */ 3578 + [87] = nfsd4_encode_fattr4__inval, 3579 + [88] = nfsd4_encode_fattr4__inval, 3580 + 3581 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 3582 + [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform, 3583 + [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope, 3584 + [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4_posix_default_acl, 3585 + [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4_posix_access_acl, 3586 + #else 3587 + [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop, 3588 + [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop, 3589 + [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4__noop, 3590 + [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4__noop, 3591 + #endif 3836 3592 }; 3837 3593 3838 3594 /* ··· 3888 3612 args.acl = NULL; 3889 3613 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 3890 3614 args.context.context = NULL; 3615 + #endif 3616 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 3617 + args.dpacl = NULL; 3618 + args.pacl = NULL; 3891 3619 #endif 3892 3620 3893 3621 /* ··· 3999 3719 } 4000 3720 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ 4001 3721 3722 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 3723 + if (attrmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) { 3724 + struct inode *inode = d_inode(dentry); 3725 + struct posix_acl *dpacl; 3726 + 3727 + if (S_ISDIR(inode->i_mode)) { 3728 + dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT); 3729 + if (IS_ERR(dpacl)) { 3730 + switch (PTR_ERR(dpacl)) { 3731 + case -EOPNOTSUPP: 3732 + attrmask[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; 3733 + break; 3734 + case -EINVAL: 3735 + status = nfserr_attrnotsupp; 3736 + goto out; 3737 + default: 3738 + err = PTR_ERR(dpacl); 3739 + goto out_nfserr; 3740 + } 3741 + } else { 3742 + args.dpacl = dpacl; 3743 + } 3744 + } 3745 + } 3746 + if (attrmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) { 3747 + struct inode *inode = d_inode(dentry); 3748 + struct posix_acl *pacl; 3749 + 3750 + pacl = get_inode_acl(inode, ACL_TYPE_ACCESS); 3751 + if (!pacl) 3752 + pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); 3753 + if (IS_ERR(pacl)) { 3754 + switch (PTR_ERR(pacl)) { 3755 + case -EOPNOTSUPP: 3756 + attrmask[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; 3757 + break; 3758 + case -EINVAL: 3759 + status = nfserr_attrnotsupp; 3760 + goto out; 3761 + default: 3762 + err = PTR_ERR(pacl); 3763 + goto out_nfserr; 3764 + } 3765 + } else { 3766 + args.pacl = pacl; 3767 + } 3768 + } 3769 + #endif /* CONFIG_NFSD_V4_POSIX_ACLS */ 3770 + 4002 3771 /* attrmask */ 4003 3772 status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1], 4004 3773 attrmask[2]); ··· 4071 3742 status = nfs_ok; 4072 3743 4073 3744 out: 3745 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 3746 + if (args.dpacl) 3747 + posix_acl_release(args.dpacl); 3748 + if (args.pacl) 3749 + posix_acl_release(args.pacl); 3750 + #endif /* CONFIG_NFSD_V4_POSIX_ACLS */ 4074 3751 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 4075 3752 if (args.context.context) 4076 3753 security_release_secctx(&args.context); ··· 6347 6012 args->xdr = xdr; 6348 6013 args->ops = args->iops; 6349 6014 args->rqstp = rqstp; 6015 + 6016 + /* 6017 + * NFSv4 operation decoders can invoke svc cache lookups 6018 + * that trigger svc_defer() when RQ_USEDEFERRAL is set, 6019 + * setting RQ_DROPME. This creates two problems: 6020 + * 6021 + * 1. Non-idempotency: Compounds make it too hard to avoid 6022 + * problems if a request is deferred and replayed. 6023 + * 6024 + * 2. Session slot leakage (NFSv4.1+): If RQ_DROPME is set 6025 + * during decode but SEQUENCE executes successfully, the 6026 + * session slot will be marked INUSE. The request is then 6027 + * dropped before encoding, so the slot is never released, 6028 + * rendering it permanently unusable by the client. 6029 + */ 6030 + clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); 6350 6031 6351 6032 return nfsd4_decode_compound(args); 6352 6033 }
+332 -19
fs/nfsd/nfs4xdr_gen.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 // Generated by xdrgen. Manual edits will be lost. 3 3 // XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x 4 - // XDR specification modification time: Mon Oct 14 09:10:13 2024 4 + // XDR specification modification time: Thu Jan 8 23:12:07 2026 5 5 6 6 #include <linux/sunrpc/svc.h> 7 7 ··· 11 11 xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr) 12 12 { 13 13 return xdrgen_decode_hyper(xdr, ptr); 14 - }; 14 + } 15 15 16 16 static bool __maybe_unused 17 17 xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr) 18 18 { 19 19 return xdrgen_decode_unsigned_int(xdr, ptr); 20 - }; 20 + } 21 21 22 22 static bool __maybe_unused 23 23 xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr) ··· 28 28 if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i])) 29 29 return false; 30 30 return true; 31 - }; 31 + } 32 + 33 + static bool __maybe_unused 34 + xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr) 35 + { 36 + return xdrgen_decode_opaque(xdr, ptr, 0); 37 + } 38 + 39 + static bool __maybe_unused 40 + xdrgen_decode_utf8str_cis(struct xdr_stream *xdr, utf8str_cis *ptr) 41 + { 42 + return xdrgen_decode_utf8string(xdr, ptr); 43 + } 44 + 45 + static bool __maybe_unused 46 + xdrgen_decode_utf8str_cs(struct xdr_stream *xdr, utf8str_cs *ptr) 47 + { 48 + return xdrgen_decode_utf8string(xdr, ptr); 49 + } 50 + 51 + static bool __maybe_unused 52 + xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr) 53 + { 54 + return xdrgen_decode_utf8string(xdr, ptr); 55 + } 32 56 33 57 static bool __maybe_unused 34 58 xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr) ··· 62 38 if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds)) 63 39 return false; 64 40 return true; 65 - }; 41 + } 66 42 67 43 static bool __maybe_unused 68 44 xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr) 69 45 { 70 46 return xdrgen_decode_bool(xdr, ptr); 71 - }; 47 + } 72 48 73 49 static bool __maybe_unused 74 50 xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr) ··· 84 60 if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode)) 85 61 return false; 86 62 return true; 87 - }; 63 + } 88 64 89 65 static bool __maybe_unused 90 66 xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr) ··· 93 69 94 70 if (xdr_stream_decode_u32(xdr, &val) < 0) 95 71 return false; 72 + /* Compiler may optimize to a range check for dense enums */ 73 + switch (val) { 74 + case OPEN_ARGS_SHARE_ACCESS_READ: 75 + case OPEN_ARGS_SHARE_ACCESS_WRITE: 76 + case OPEN_ARGS_SHARE_ACCESS_BOTH: 77 + break; 78 + default: 79 + return false; 80 + } 96 81 *ptr = val; 97 82 return true; 98 83 } ··· 113 80 114 81 if (xdr_stream_decode_u32(xdr, &val) < 0) 115 82 return false; 83 + /* Compiler may optimize to a range check for dense enums */ 84 + switch (val) { 85 + case OPEN_ARGS_SHARE_DENY_NONE: 86 + case OPEN_ARGS_SHARE_DENY_READ: 87 + case OPEN_ARGS_SHARE_DENY_WRITE: 88 + case OPEN_ARGS_SHARE_DENY_BOTH: 89 + break; 90 + default: 91 + return false; 92 + } 116 93 *ptr = val; 117 94 return true; 118 95 } ··· 134 91 135 92 if (xdr_stream_decode_u32(xdr, &val) < 0) 136 93 return false; 94 + /* Compiler may optimize to a range check for dense enums */ 95 + switch (val) { 96 + case OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG: 97 + case OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG: 98 + case OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL: 99 + case OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL: 100 + case OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED: 101 + case OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS: 102 + case OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION: 103 + break; 104 + default: 105 + return false; 106 + } 137 107 *ptr = val; 138 108 return true; 139 109 } ··· 158 102 159 103 if (xdr_stream_decode_u32(xdr, &val) < 0) 160 104 return false; 105 + /* Compiler may optimize to a range check for dense enums */ 106 + switch (val) { 107 + case OPEN_ARGS_OPEN_CLAIM_NULL: 108 + case OPEN_ARGS_OPEN_CLAIM_PREVIOUS: 109 + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR: 110 + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV: 111 + case OPEN_ARGS_OPEN_CLAIM_FH: 112 + case OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH: 113 + case OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH: 114 + break; 115 + default: 116 + return false; 117 + } 161 118 *ptr = val; 162 119 return true; 163 120 } ··· 182 113 183 114 if (xdr_stream_decode_u32(xdr, &val) < 0) 184 115 return false; 116 + /* Compiler may optimize to a range check for dense enums */ 117 + switch (val) { 118 + case OPEN_ARGS_CREATEMODE_UNCHECKED4: 119 + case OPEN_ARGS_CREATE_MODE_GUARDED: 120 + case OPEN_ARGS_CREATEMODE_EXCLUSIVE4: 121 + case OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1: 122 + break; 123 + default: 124 + return false; 125 + } 185 126 *ptr = val; 186 127 return true; 187 128 } ··· 200 121 xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr) 201 122 { 202 123 return xdrgen_decode_open_arguments4(xdr, ptr); 203 - }; 124 + } 125 + 126 + /* 127 + * Determine what OPEN supports. 128 + */ 204 129 205 130 bool 206 131 xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr) 207 132 { 208 133 return xdrgen_decode_nfstime4(xdr, ptr); 209 - }; 134 + } 210 135 211 136 bool 212 137 xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr) 213 138 { 214 139 return xdrgen_decode_nfstime4(xdr, ptr); 215 - }; 140 + } 141 + 142 + /* 143 + * New RECOMMENDED Attribute for 144 + * delegation caching of times 145 + */ 216 146 217 147 static bool __maybe_unused 218 148 xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr) ··· 230 142 231 143 if (xdr_stream_decode_u32(xdr, &val) < 0) 232 144 return false; 145 + /* Compiler may optimize to a range check for dense enums */ 146 + switch (val) { 147 + case OPEN_DELEGATE_NONE: 148 + case OPEN_DELEGATE_READ: 149 + case OPEN_DELEGATE_WRITE: 150 + case OPEN_DELEGATE_NONE_EXT: 151 + case OPEN_DELEGATE_READ_ATTRS_DELEG: 152 + case OPEN_DELEGATE_WRITE_ATTRS_DELEG: 153 + break; 154 + default: 155 + return false; 156 + } 233 157 *ptr = val; 234 158 return true; 235 159 } 160 + 161 + bool 162 + xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr) 163 + { 164 + u32 val; 165 + 166 + if (xdr_stream_decode_u32(xdr, &val) < 0) 167 + return false; 168 + /* Compiler may optimize to a range check for dense enums */ 169 + switch (val) { 170 + case ACL_MODEL_NFS4: 171 + case ACL_MODEL_POSIX_DRAFT: 172 + case ACL_MODEL_NONE: 173 + break; 174 + default: 175 + return false; 176 + } 177 + *ptr = val; 178 + return true; 179 + } 180 + 181 + bool 182 + xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr) 183 + { 184 + u32 val; 185 + 186 + if (xdr_stream_decode_u32(xdr, &val) < 0) 187 + return false; 188 + /* Compiler may optimize to a range check for dense enums */ 189 + switch (val) { 190 + case ACL_SCOPE_FILE_OBJECT: 191 + case ACL_SCOPE_FILE_SYSTEM: 192 + case ACL_SCOPE_SERVER: 193 + break; 194 + default: 195 + return false; 196 + } 197 + *ptr = val; 198 + return true; 199 + } 200 + 201 + bool 202 + xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr) 203 + { 204 + u32 val; 205 + 206 + if (xdr_stream_decode_u32(xdr, &val) < 0) 207 + return false; 208 + /* Compiler may optimize to a range check for dense enums */ 209 + switch (val) { 210 + case POSIXACE4_TAG_USER_OBJ: 211 + case POSIXACE4_TAG_USER: 212 + case POSIXACE4_TAG_GROUP_OBJ: 213 + case POSIXACE4_TAG_GROUP: 214 + case POSIXACE4_TAG_MASK: 215 + case POSIXACE4_TAG_OTHER: 216 + break; 217 + default: 218 + return false; 219 + } 220 + *ptr = val; 221 + return true; 222 + } 223 + 224 + bool 225 + xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr) 226 + { 227 + return xdrgen_decode_uint32_t(xdr, ptr); 228 + } 229 + 230 + static bool __maybe_unused 231 + xdrgen_decode_posixace4(struct xdr_stream *xdr, struct posixace4 *ptr) 232 + { 233 + if (!xdrgen_decode_posixacetag4(xdr, &ptr->tag)) 234 + return false; 235 + if (!xdrgen_decode_posixaceperm4(xdr, &ptr->perm)) 236 + return false; 237 + if (!xdrgen_decode_utf8str_mixed(xdr, &ptr->who)) 238 + return false; 239 + return true; 240 + } 241 + 242 + static bool __maybe_unused 243 + xdrgen_decode_fattr4_acl_trueform(struct xdr_stream *xdr, fattr4_acl_trueform *ptr) 244 + { 245 + return xdrgen_decode_aclmodel4(xdr, ptr); 246 + } 247 + 248 + static bool __maybe_unused 249 + xdrgen_decode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, fattr4_acl_trueform_scope *ptr) 250 + { 251 + return xdrgen_decode_aclscope4(xdr, ptr); 252 + } 253 + 254 + static bool __maybe_unused 255 + xdrgen_decode_fattr4_posix_default_acl(struct xdr_stream *xdr, fattr4_posix_default_acl *ptr) 256 + { 257 + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0) 258 + return false; 259 + for (u32 i = 0; i < ptr->count; i++) 260 + if (!xdrgen_decode_posixace4(xdr, &ptr->element[i])) 261 + return false; 262 + return true; 263 + } 264 + 265 + static bool __maybe_unused 266 + xdrgen_decode_fattr4_posix_access_acl(struct xdr_stream *xdr, fattr4_posix_access_acl *ptr) 267 + { 268 + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0) 269 + return false; 270 + for (u32 i = 0; i < ptr->count; i++) 271 + if (!xdrgen_decode_posixace4(xdr, &ptr->element[i])) 272 + return false; 273 + return true; 274 + } 275 + 276 + /* 277 + * New for POSIX ACL extension 278 + */ 236 279 237 280 static bool __maybe_unused 238 281 xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value) 239 282 { 240 283 return xdrgen_encode_hyper(xdr, value); 241 - }; 284 + } 242 285 243 286 static bool __maybe_unused 244 287 xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value) 245 288 { 246 289 return xdrgen_encode_unsigned_int(xdr, value); 247 - }; 290 + } 248 291 249 292 static bool __maybe_unused 250 293 xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value) ··· 386 167 if (!xdrgen_encode_uint32_t(xdr, value.element[i])) 387 168 return false; 388 169 return true; 389 - }; 170 + } 171 + 172 + static bool __maybe_unused 173 + xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value) 174 + { 175 + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; 176 + } 177 + 178 + static bool __maybe_unused 179 + xdrgen_encode_utf8str_cis(struct xdr_stream *xdr, const utf8str_cis value) 180 + { 181 + return xdrgen_encode_utf8string(xdr, value); 182 + } 183 + 184 + static bool __maybe_unused 185 + xdrgen_encode_utf8str_cs(struct xdr_stream *xdr, const utf8str_cs value) 186 + { 187 + return xdrgen_encode_utf8string(xdr, value); 188 + } 189 + 190 + static bool __maybe_unused 191 + xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value) 192 + { 193 + return xdrgen_encode_utf8string(xdr, value); 194 + } 390 195 391 196 static bool __maybe_unused 392 197 xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value) ··· 420 177 if (!xdrgen_encode_uint32_t(xdr, value->nseconds)) 421 178 return false; 422 179 return true; 423 - }; 180 + } 424 181 425 182 static bool __maybe_unused 426 183 xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value) 427 184 { 428 185 return xdrgen_encode_bool(xdr, value); 429 - }; 186 + } 430 187 431 188 static bool __maybe_unused 432 189 xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value) ··· 442 199 if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode)) 443 200 return false; 444 201 return true; 445 - }; 202 + } 446 203 447 204 static bool __maybe_unused 448 205 xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value) ··· 478 235 xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value) 479 236 { 480 237 return xdrgen_encode_open_arguments4(xdr, value); 481 - }; 238 + } 482 239 483 240 bool 484 241 xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value) 485 242 { 486 243 return xdrgen_encode_nfstime4(xdr, value); 487 - }; 244 + } 488 245 489 246 bool 490 247 xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value) 491 248 { 492 249 return xdrgen_encode_nfstime4(xdr, value); 493 - }; 250 + } 494 251 495 252 static bool __maybe_unused 496 253 xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value) 497 254 { 498 255 return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; 256 + } 257 + 258 + bool 259 + xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value) 260 + { 261 + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; 262 + } 263 + 264 + bool 265 + xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value) 266 + { 267 + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; 268 + } 269 + 270 + bool 271 + xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value) 272 + { 273 + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; 274 + } 275 + 276 + bool 277 + xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value) 278 + { 279 + return xdrgen_encode_uint32_t(xdr, value); 280 + } 281 + 282 + static bool __maybe_unused 283 + xdrgen_encode_posixace4(struct xdr_stream *xdr, const struct posixace4 *value) 284 + { 285 + if (!xdrgen_encode_posixacetag4(xdr, value->tag)) 286 + return false; 287 + if (!xdrgen_encode_posixaceperm4(xdr, value->perm)) 288 + return false; 289 + if (!xdrgen_encode_utf8str_mixed(xdr, value->who)) 290 + return false; 291 + return true; 292 + } 293 + 294 + static bool __maybe_unused 295 + xdrgen_encode_fattr4_acl_trueform(struct xdr_stream *xdr, const fattr4_acl_trueform value) 296 + { 297 + return xdrgen_encode_aclmodel4(xdr, value); 298 + } 299 + 300 + static bool __maybe_unused 301 + xdrgen_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, const fattr4_acl_trueform_scope value) 302 + { 303 + return xdrgen_encode_aclscope4(xdr, value); 304 + } 305 + 306 + static bool __maybe_unused 307 + xdrgen_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, const fattr4_posix_default_acl value) 308 + { 309 + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT) 310 + return false; 311 + for (u32 i = 0; i < value.count; i++) 312 + if (!xdrgen_encode_posixace4(xdr, &value.element[i])) 313 + return false; 314 + return true; 315 + } 316 + 317 + static bool __maybe_unused 318 + xdrgen_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, const fattr4_posix_access_acl value) 319 + { 320 + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT) 321 + return false; 322 + for (u32 i = 0; i < value.count; i++) 323 + if (!xdrgen_encode_posixace4(xdr, &value.element[i])) 324 + return false; 325 + return true; 499 326 }
+11 -1
fs/nfsd/nfs4xdr_gen.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Generated by xdrgen. Manual edits will be lost. */ 3 3 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */ 4 - /* XDR specification modification time: Mon Oct 14 09:10:13 2024 */ 4 + /* XDR specification modification time: Thu Jan 8 23:12:07 2026 */ 5 5 6 6 #ifndef _LINUX_XDRGEN_NFS4_1_DECL_H 7 7 #define _LINUX_XDRGEN_NFS4_1_DECL_H ··· 21 21 22 22 bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr); 23 23 bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value); 24 + bool xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr); 25 + bool xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value); 26 + bool xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr); 27 + bool xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value); 28 + bool xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr); 29 + bool xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value); 30 + 31 + bool xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr); 32 + bool xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value); 33 + 24 34 25 35 #endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */
+7
fs/nfsd/nfsctl.c
··· 285 285 * 2. Is that directory a mount point, or 286 286 * 3. Is that directory the root of an exported file system? 287 287 */ 288 + nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb); 288 289 error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); 289 290 mutex_lock(&nfsd_mutex); 290 291 nn = net_generic(netns(file), nfsd_net_id); ··· 1643 1642 scope = nla_data(attr); 1644 1643 } 1645 1644 1645 + attr = info->attrs[NFSD_A_SERVER_MIN_THREADS]; 1646 + if (attr) 1647 + nn->min_threads = nla_get_u32(attr); 1648 + 1646 1649 ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope); 1647 1650 if (ret > 0) 1648 1651 ret = 0; ··· 1686 1681 nn->nfsd4_grace) || 1687 1682 nla_put_u32(skb, NFSD_A_SERVER_LEASETIME, 1688 1683 nn->nfsd4_lease) || 1684 + nla_put_u32(skb, NFSD_A_SERVER_MIN_THREADS, 1685 + nn->min_threads) || 1689 1686 nla_put_string(skb, NFSD_A_SERVER_SCOPE, 1690 1687 nn->nfsd_name); 1691 1688 if (err)
+23 -1
fs/nfsd/nfsd.h
··· 454 454 #define NFSD4_2_SECURITY_ATTRS 0 455 455 #endif 456 456 457 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 458 + #define NFSD4_2_POSIX_ACL_ATTRS \ 459 + (FATTR4_WORD2_ACL_TRUEFORM | \ 460 + FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \ 461 + FATTR4_WORD2_POSIX_DEFAULT_ACL | \ 462 + FATTR4_WORD2_POSIX_ACCESS_ACL) 463 + #else 464 + #define NFSD4_2_POSIX_ACL_ATTRS 0 465 + #endif 466 + 457 467 #define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ 458 468 (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ 459 469 FATTR4_WORD2_MODE_UMASK | \ ··· 472 462 FATTR4_WORD2_XATTR_SUPPORT | \ 473 463 FATTR4_WORD2_TIME_DELEG_ACCESS | \ 474 464 FATTR4_WORD2_TIME_DELEG_MODIFY | \ 475 - FATTR4_WORD2_OPEN_ARGUMENTS) 465 + FATTR4_WORD2_OPEN_ARGUMENTS | \ 466 + NFSD4_2_POSIX_ACL_ATTRS) 476 467 477 468 extern const u32 nfsd_suppattrs[3][3]; 478 469 ··· 541 530 #else 542 531 #define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0 543 532 #endif 533 + #ifdef CONFIG_NFSD_V4_POSIX_ACLS 534 + #define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \ 535 + FATTR4_WORD2_POSIX_DEFAULT_ACL | FATTR4_WORD2_POSIX_ACCESS_ACL 536 + #else 537 + #define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS 0 538 + #endif 544 539 #define NFSD_WRITEABLE_ATTRS_WORD2 \ 545 540 (FATTR4_WORD2_MODE_UMASK \ 546 541 | MAYBE_FATTR4_WORD2_SECURITY_LABEL \ 547 542 | FATTR4_WORD2_TIME_DELEG_ACCESS \ 548 543 | FATTR4_WORD2_TIME_DELEG_MODIFY \ 544 + | MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \ 549 545 ) 550 546 551 547 #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ ··· 568 550 * The FATTR4_WORD2_TIME_DELEG attributes are not to be allowed for 569 551 * OPEN(create) with EXCLUSIVE4_1. It doesn't make sense to set a 570 552 * delegated timestamp on a new file. 553 + * 554 + * This mask includes NFSv4.2-only attributes (e.g., POSIX ACLs). 555 + * Version filtering occurs via nfsd_suppattrs[] before this mask 556 + * is applied, so pre-4.2 clients never see unsupported attributes. 571 557 */ 572 558 #define NFSD_SUPPATTR_EXCLCREAT_WORD2 \ 573 559 (NFSD_WRITEABLE_ATTRS_WORD2 & \
+1 -1
fs/nfsd/nfsproc.c
··· 33 33 break; 34 34 case nfserr_symlink: 35 35 case nfserr_wrong_type: 36 - status = nfserr_inval; 36 + status = nfserr_io; 37 37 break; 38 38 } 39 39 return status;
+51 -13
fs/nfsd/nfssvc.c
··· 580 580 } 581 581 582 582 /* Kill outstanding nfsd threads */ 583 - svc_set_num_threads(serv, NULL, 0); 583 + svc_set_num_threads(serv, 0, 0); 584 584 nfsd_destroy_serv(net); 585 585 mutex_unlock(&nfsd_mutex); 586 586 } ··· 688 688 if (nn->nfsd_serv == NULL || n <= 0) 689 689 return 0; 690 690 691 - /* 692 - * Special case: When n == 1, pass in NULL for the pool, so that the 693 - * change is distributed equally among them. 694 - */ 691 + /* Special case: When n == 1, distribute threads equally among pools. */ 695 692 if (n == 1) 696 - return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]); 693 + return svc_set_num_threads(nn->nfsd_serv, nn->min_threads, nthreads[0]); 697 694 698 695 if (n > nn->nfsd_serv->sv_nrpools) 699 696 n = nn->nfsd_serv->sv_nrpools; ··· 716 719 717 720 /* apply the new numbers */ 718 721 for (i = 0; i < n; i++) { 719 - err = svc_set_num_threads(nn->nfsd_serv, 720 - &nn->nfsd_serv->sv_pools[i], 721 - nthreads[i]); 722 + err = svc_set_pool_threads(nn->nfsd_serv, 723 + &nn->nfsd_serv->sv_pools[i], 724 + nn->min_threads, nthreads[i]); 722 725 if (err) 723 726 goto out; 724 727 } 725 728 726 729 /* Anything undefined in array is considered to be 0 */ 727 730 for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) { 728 - err = svc_set_num_threads(nn->nfsd_serv, 729 - &nn->nfsd_serv->sv_pools[i], 730 - 0); 731 + err = svc_set_pool_threads(nn->nfsd_serv, 732 + &nn->nfsd_serv->sv_pools[i], 733 + 0, 0); 731 734 if (err) 732 735 goto out; 733 736 } ··· 882 885 nfsd(void *vrqstp) 883 886 { 884 887 struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; 888 + struct svc_pool *pool = rqstp->rq_pool; 885 889 struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); 886 890 struct net *net = perm_sock->xpt_net; 887 891 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 892 + bool have_mutex = false; 888 893 889 894 /* At this point, the thread shares current->fs 890 895 * with the init process. We need to create files with the ··· 904 905 * The main request loop 905 906 */ 906 907 while (!svc_thread_should_stop(rqstp)) { 907 - svc_recv(rqstp); 908 + switch (svc_recv(rqstp, 5 * HZ)) { 909 + case -ETIMEDOUT: 910 + /* No work arrived within the timeout window */ 911 + if (mutex_trylock(&nfsd_mutex)) { 912 + if (pool->sp_nrthreads > pool->sp_nrthrmin) { 913 + trace_nfsd_dynthread_kill(net, pool); 914 + set_bit(RQ_VICTIM, &rqstp->rq_flags); 915 + have_mutex = true; 916 + } else { 917 + mutex_unlock(&nfsd_mutex); 918 + } 919 + } else { 920 + trace_nfsd_dynthread_trylock_fail(net, pool); 921 + } 922 + break; 923 + case -EBUSY: 924 + /* No idle threads; consider spawning another */ 925 + if (pool->sp_nrthreads < pool->sp_nrthrmax) { 926 + if (mutex_trylock(&nfsd_mutex)) { 927 + if (pool->sp_nrthreads < pool->sp_nrthrmax) { 928 + int ret; 929 + 930 + trace_nfsd_dynthread_start(net, pool); 931 + ret = svc_new_thread(rqstp->rq_server, pool); 932 + if (ret) 933 + pr_notice_ratelimited("%s: unable to spawn new thread: %d\n", 934 + __func__, ret); 935 + } 936 + mutex_unlock(&nfsd_mutex); 937 + } else { 938 + trace_nfsd_dynthread_trylock_fail(net, pool); 939 + } 940 + } 941 + clear_bit(SP_TASK_STARTING, &pool->sp_flags); 942 + break; 943 + default: 944 + break; 945 + } 908 946 nfsd_file_net_dispose(nn); 909 947 } 910 948 ··· 949 913 950 914 /* Release the thread */ 951 915 svc_exit_thread(rqstp); 916 + if (have_mutex) 917 + mutex_unlock(&nfsd_mutex); 952 918 return 0; 953 919 } 954 920
+5
fs/nfsd/state.h
··· 822 822 823 823 extern void nfsd4_shutdown_callback(struct nfs4_client *); 824 824 extern void nfsd4_shutdown_copy(struct nfs4_client *clp); 825 + void nfsd4_put_client(struct nfs4_client *clp); 825 826 void nfsd4_async_copy_reaper(struct nfsd_net *nn); 826 827 bool nfsd4_has_active_async_copies(struct nfs4_client *clp); 827 828 extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, ··· 843 842 844 843 #ifdef CONFIG_NFSD_V4 845 844 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); 845 + void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); 846 846 #else 847 847 static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) 848 + { 849 + } 850 + static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) 848 851 { 849 852 } 850 853 #endif
+54
fs/nfsd/trace.h
··· 91 91 DEFINE_NFSD_XDR_ERR_EVENT(garbage_args); 92 92 DEFINE_NFSD_XDR_ERR_EVENT(cant_encode); 93 93 94 + DECLARE_EVENT_CLASS(nfsd_dynthread_class, 95 + TP_PROTO( 96 + const struct net *net, 97 + const struct svc_pool *pool 98 + ), 99 + TP_ARGS(net, pool), 100 + TP_STRUCT__entry( 101 + __field(unsigned int, netns_ino) 102 + __field(unsigned int, pool_id) 103 + __field(unsigned int, nrthreads) 104 + __field(unsigned int, nrthrmin) 105 + __field(unsigned int, nrthrmax) 106 + ), 107 + TP_fast_assign( 108 + __entry->netns_ino = net->ns.inum; 109 + __entry->pool_id = pool->sp_id; 110 + __entry->nrthreads = pool->sp_nrthreads; 111 + __entry->nrthrmin = pool->sp_nrthrmin; 112 + __entry->nrthrmax = pool->sp_nrthrmax; 113 + ), 114 + TP_printk("pool=%u nrthreads=%u nrthrmin=%u nrthrmax=%u", 115 + __entry->pool_id, __entry->nrthreads, 116 + __entry->nrthrmin, __entry->nrthrmax 117 + ) 118 + ); 119 + 120 + #define DEFINE_NFSD_DYNTHREAD_EVENT(name) \ 121 + DEFINE_EVENT(nfsd_dynthread_class, nfsd_dynthread_##name, \ 122 + TP_PROTO(const struct net *net, const struct svc_pool *pool), \ 123 + TP_ARGS(net, pool)) 124 + 125 + DEFINE_NFSD_DYNTHREAD_EVENT(start); 126 + DEFINE_NFSD_DYNTHREAD_EVENT(kill); 127 + DEFINE_NFSD_DYNTHREAD_EVENT(trylock_fail); 128 + 94 129 #define show_nfsd_may_flags(x) \ 95 130 __print_flags(x, "|", \ 96 131 { NFSD_MAY_EXEC, "EXEC" }, \ ··· 2161 2126 ), 2162 2127 TP_printk("bsize=%d", 2163 2128 __entry->bsize 2129 + ) 2130 + ); 2131 + 2132 + TRACE_EVENT(nfsd_ctl_minthreads, 2133 + TP_PROTO( 2134 + const struct net *net, 2135 + int minthreads 2136 + ), 2137 + TP_ARGS(net, minthreads), 2138 + TP_STRUCT__entry( 2139 + __field(unsigned int, netns_ino) 2140 + __field(int, minthreads) 2141 + ), 2142 + TP_fast_assign( 2143 + __entry->netns_ino = net->ns.inum; 2144 + __entry->minthreads = minthreads 2145 + ), 2146 + TP_printk("minthreads=%d", 2147 + __entry->minthreads 2164 2148 ) 2165 2149 ); 2166 2150
+27 -7
fs/nfsd/vfs.c
··· 596 596 if (attr->na_seclabel && attr->na_seclabel->len) 597 597 attr->na_labelerr = security_inode_setsecctx(dentry, 598 598 attr->na_seclabel->data, attr->na_seclabel->len); 599 - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) 600 - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, 601 - dentry, ACL_TYPE_ACCESS, 602 - attr->na_pacl); 603 - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && 604 - !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode)) 605 - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, 599 + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) { 600 + if (!S_ISDIR(inode->i_mode)) 601 + attr->na_dpaclerr = -EINVAL; 602 + else if (attr->na_dpacl->a_count > 0) 603 + /* a_count == 0 means delete the ACL. */ 604 + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, 606 605 dentry, ACL_TYPE_DEFAULT, 607 606 attr->na_dpacl); 607 + else 608 + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, 609 + dentry, ACL_TYPE_DEFAULT, 610 + NULL); 611 + } 612 + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) { 613 + /* 614 + * For any file system that is not ACL_SCOPE_FILE_OBJECT, 615 + * a_count == 0 MUST reply nfserr_inval. 616 + * For a file system that is ACL_SCOPE_FILE_OBJECT, 617 + * a_count == 0 deletes the ACL. 618 + * XXX File systems that are ACL_SCOPE_FILE_OBJECT 619 + * are not yet supported. 620 + */ 621 + if (attr->na_pacl->a_count > 0) 622 + attr->na_paclerr = set_posix_acl(&nop_mnt_idmap, 623 + dentry, ACL_TYPE_ACCESS, 624 + attr->na_pacl); 625 + else 626 + attr->na_paclerr = -EINVAL; 627 + } 608 628 out_fill_attrs: 609 629 /* 610 630 * RFC 1813 Section 3.3.2 does not mandate that an NFS server
+2 -1
fs/nfsd/vfs.h
··· 53 53 struct posix_acl *na_dpacl; /* input */ 54 54 55 55 int na_labelerr; /* output */ 56 - int na_aclerr; /* output */ 56 + int na_dpaclerr; /* output */ 57 + int na_paclerr; /* output */ 57 58 }; 58 59 59 60 static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
+7
fs/nfsd/xdr4.h
··· 245 245 int cr_umask; /* request */ 246 246 struct nfsd4_change_info cr_cinfo; /* response */ 247 247 struct nfs4_acl *cr_acl; 248 + struct posix_acl *cr_dpacl; 249 + struct posix_acl *cr_pacl; 248 250 struct xdr_netobj cr_label; 249 251 }; 250 252 #define cr_datalen u.link.datalen ··· 399 397 struct nfs4_ol_stateid *op_stp; /* used during processing */ 400 398 struct nfs4_clnt_odstate *op_odstate; /* used during processing */ 401 399 struct nfs4_acl *op_acl; 400 + struct posix_acl *op_dpacl; 401 + struct posix_acl *op_pacl; 402 402 struct xdr_netobj op_label; 403 403 struct svc_rqst *op_rqstp; 404 404 }; ··· 487 483 struct iattr sa_iattr; /* request */ 488 484 struct nfs4_acl *sa_acl; 489 485 struct xdr_netobj sa_label; 486 + struct posix_acl *sa_dpacl; 487 + struct posix_acl *sa_pacl; 490 488 }; 491 489 492 490 struct nfsd4_setclientid { ··· 738 732 #define NFSD4_COPY_F_COMMITTED (3) 739 733 #define NFSD4_COPY_F_COMPLETED (4) 740 734 #define NFSD4_COPY_F_OFFLOAD_DONE (5) 735 + #define NFSD4_COPY_F_CB_ERROR (6) 741 736 742 737 /* response */ 743 738 __be32 nfserr;
+4
include/linux/nfs4.h
··· 598 598 #define FATTR4_WORD2_TIME_DELEG_ACCESS BIT(FATTR4_TIME_DELEG_ACCESS - 64) 599 599 #define FATTR4_WORD2_TIME_DELEG_MODIFY BIT(FATTR4_TIME_DELEG_MODIFY - 64) 600 600 #define FATTR4_WORD2_OPEN_ARGUMENTS BIT(FATTR4_OPEN_ARGUMENTS - 64) 601 + #define FATTR4_WORD2_ACL_TRUEFORM BIT(FATTR4_ACL_TRUEFORM - 64) 602 + #define FATTR4_WORD2_ACL_TRUEFORM_SCOPE BIT(FATTR4_ACL_TRUEFORM_SCOPE - 64) 603 + #define FATTR4_WORD2_POSIX_DEFAULT_ACL BIT(FATTR4_POSIX_DEFAULT_ACL - 64) 604 + #define FATTR4_WORD2_POSIX_ACCESS_ACL BIT(FATTR4_POSIX_ACCESS_ACL - 64) 601 605 602 606 /* MDS threshold bitmap bits */ 603 607 #define THRESHOLD_RD (1UL << 0)
+10 -3
include/linux/sunrpc/svc.h
··· 35 35 */ 36 36 struct svc_pool { 37 37 unsigned int sp_id; /* pool id; also node id on NUMA */ 38 + unsigned int sp_nrthreads; /* # of threads currently running in pool */ 39 + unsigned int sp_nrthrmin; /* Min number of threads to run per pool */ 40 + unsigned int sp_nrthrmax; /* Max requested number of threads in pool */ 38 41 struct lwq sp_xprts; /* pending transports */ 39 - unsigned int sp_nrthreads; /* # of threads in pool */ 40 42 struct list_head sp_all_threads; /* all server threads */ 41 43 struct llist_head sp_idle_threads; /* idle server threads */ 42 44 ··· 55 53 SP_TASK_PENDING, /* still work to do even if no xprt is queued */ 56 54 SP_NEED_VICTIM, /* One thread needs to agree to exit */ 57 55 SP_VICTIM_REMAINS, /* One thread needs to actually exit */ 56 + SP_TASK_STARTING, /* Task has started but not added to idle yet */ 58 57 }; 59 58 60 59 ··· 74 71 struct svc_stat * sv_stats; /* RPC statistics */ 75 72 spinlock_t sv_lock; 76 73 unsigned int sv_nprogs; /* Number of sv_programs */ 77 - unsigned int sv_nrthreads; /* # of server threads */ 74 + unsigned int sv_nrthreads; /* # of running server threads */ 78 75 unsigned int sv_max_payload; /* datagram payload size */ 79 76 unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */ 80 77 unsigned int sv_xdrsize; /* XDR buffer size */ ··· 443 440 bool svc_rqst_replace_page(struct svc_rqst *rqstp, 444 441 struct page *page); 445 442 void svc_rqst_release_pages(struct svc_rqst *rqstp); 443 + int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool); 446 444 void svc_exit_thread(struct svc_rqst *); 447 445 struct svc_serv * svc_create_pooled(struct svc_program *prog, 448 446 unsigned int nprog, 449 447 struct svc_stat *stats, 450 448 unsigned int bufsize, 451 449 int (*threadfn)(void *data)); 452 - int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); 450 + int svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool, 451 + unsigned int min_threads, unsigned int max_threads); 452 + int svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads, 453 + unsigned int nrservs); 453 454 int svc_pool_stats_open(struct svc_info *si, struct file *file); 454 455 void svc_process(struct svc_rqst *rqstp); 455 456 void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp);
+1 -1
include/linux/sunrpc/svcsock.h
··· 61 61 /* 62 62 * Function prototypes. 63 63 */ 64 - void svc_recv(struct svc_rqst *rqstp); 64 + int svc_recv(struct svc_rqst *rqstp, long timeo); 65 65 void svc_send(struct svc_rqst *rqstp); 66 66 int svc_addsock(struct svc_serv *serv, struct net *net, 67 67 const int fd, char *name_return, const size_t len,
+68 -12
include/linux/sunrpc/xdrgen/_builtins.h
··· 46 46 return true; 47 47 } 48 48 49 + /* 50 + * De facto (non-standard but commonly implemented) signed short type: 51 + * - Wire sends sign-extended 32-bit value (e.g., 0xFFFFFFFF) 52 + * - be32_to_cpup() returns u32 (0xFFFFFFFF) 53 + * - Explicit (s16) cast truncates to 16 bits (0xFFFF = -1) 54 + */ 55 + static inline bool 56 + xdrgen_decode_short(struct xdr_stream *xdr, s16 *ptr) 57 + { 58 + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); 59 + 60 + if (unlikely(!p)) 61 + return false; 62 + *ptr = (s16)be32_to_cpup(p); 63 + return true; 64 + } 65 + 66 + /* 67 + * De facto (non-standard but commonly implemented) signed short type: 68 + * - C integer promotion sign-extends s16 val to int before passing to 69 + * cpu_to_be32() 70 + * - This is well-defined: -1 as s16 -1 as int 0xFFFFFFFF on wire 71 + */ 72 + static inline bool 73 + xdrgen_encode_short(struct xdr_stream *xdr, s16 val) 74 + { 75 + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); 76 + 77 + if (unlikely(!p)) 78 + return false; 79 + *p = cpu_to_be32(val); 80 + return true; 81 + } 82 + 83 + /* 84 + * De facto (non-standard but commonly implemented) unsigned short type: 85 + * 16-bit integer zero-extended to fill one XDR_UNIT. 86 + */ 87 + static inline bool 88 + xdrgen_decode_unsigned_short(struct xdr_stream *xdr, u16 *ptr) 89 + { 90 + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); 91 + 92 + if (unlikely(!p)) 93 + return false; 94 + *ptr = (u16)be32_to_cpup(p); 95 + return true; 96 + } 97 + 98 + static inline bool 99 + xdrgen_encode_unsigned_short(struct xdr_stream *xdr, u16 val) 100 + { 101 + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); 102 + 103 + if (unlikely(!p)) 104 + return false; 105 + *p = cpu_to_be32(val); 106 + return true; 107 + } 108 + 49 109 static inline bool 50 110 xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr) 51 111 { ··· 248 188 return false; 249 189 if (unlikely(maxlen && len > maxlen)) 250 190 return false; 251 - if (len != 0) { 252 - p = xdr_inline_decode(xdr, len); 253 - if (unlikely(!p)) 254 - return false; 255 - ptr->data = (unsigned char *)p; 256 - } 191 + p = xdr_inline_decode(xdr, len); 192 + if (unlikely(!p)) 193 + return false; 194 + ptr->data = (unsigned char *)p; 257 195 ptr->len = len; 258 196 return true; 259 197 } ··· 277 219 return false; 278 220 if (unlikely(maxlen && len > maxlen)) 279 221 return false; 280 - if (len != 0) { 281 - p = xdr_inline_decode(xdr, len); 282 - if (unlikely(!p)) 283 - return false; 284 - ptr->data = (u8 *)p; 285 - } 222 + p = xdr_inline_decode(xdr, len); 223 + if (unlikely(!p)) 224 + return false; 225 + ptr->data = (u8 *)p; 286 226 ptr->len = len; 287 227 return true; 288 228 }
+111 -1
include/linux/sunrpc/xdrgen/nfs4_1.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Generated by xdrgen. Manual edits will be lost. */ 3 3 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */ 4 - /* XDR specification modification time: Mon Oct 14 09:10:13 2024 */ 4 + /* XDR specification modification time: Thu Jan 8 23:12:07 2026 */ 5 5 6 6 #ifndef _LINUX_XDRGEN_NFS4_1_DEF_H 7 7 #define _LINUX_XDRGEN_NFS4_1_DEF_H ··· 17 17 u32 count; 18 18 uint32_t *element; 19 19 } bitmap4; 20 + 21 + typedef opaque utf8string; 22 + 23 + typedef utf8string utf8str_cis; 24 + 25 + typedef utf8string utf8str_cs; 26 + 27 + typedef utf8string utf8str_mixed; 20 28 21 29 struct nfstime4 { 22 30 int64_t seconds; ··· 48 40 OPEN_ARGS_SHARE_ACCESS_WRITE = 2, 49 41 OPEN_ARGS_SHARE_ACCESS_BOTH = 3, 50 42 }; 43 + 51 44 typedef enum open_args_share_access4 open_args_share_access4; 52 45 53 46 enum open_args_share_deny4 { ··· 57 48 OPEN_ARGS_SHARE_DENY_WRITE = 2, 58 49 OPEN_ARGS_SHARE_DENY_BOTH = 3, 59 50 }; 51 + 60 52 typedef enum open_args_share_deny4 open_args_share_deny4; 61 53 62 54 enum open_args_share_access_want4 { ··· 69 59 OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20, 70 60 OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21, 71 61 }; 62 + 72 63 typedef enum open_args_share_access_want4 open_args_share_access_want4; 73 64 74 65 enum open_args_open_claim4 { ··· 81 70 OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5, 82 71 OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6, 83 72 }; 73 + 84 74 typedef enum open_args_open_claim4 open_args_open_claim4; 85 75 86 76 enum open_args_createmode4 { ··· 90 78 OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2, 91 79 OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3, 92 80 }; 81 + 93 82 typedef enum open_args_createmode4 open_args_createmode4; 94 83 95 84 typedef struct open_arguments4 fattr4_open_arguments; 85 + 86 + /* 87 + * Determine what OPEN supports. 88 + */ 96 89 97 90 enum { FATTR4_OPEN_ARGUMENTS = 86 }; 98 91 ··· 106 89 typedef struct nfstime4 fattr4_time_deleg_access; 107 90 108 91 typedef struct nfstime4 fattr4_time_deleg_modify; 92 + 93 + /* 94 + * New RECOMMENDED Attribute for 95 + * delegation caching of times 96 + */ 109 97 110 98 enum { FATTR4_TIME_DELEG_ACCESS = 84 }; 111 99 ··· 146 124 OPEN_DELEGATE_READ_ATTRS_DELEG = 4, 147 125 OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5, 148 126 }; 127 + 149 128 typedef enum open_delegation_type4 open_delegation_type4; 129 + 130 + enum aclmodel4 { 131 + ACL_MODEL_NFS4 = 1, 132 + ACL_MODEL_POSIX_DRAFT = 2, 133 + ACL_MODEL_NONE = 3, 134 + }; 135 + 136 + typedef enum aclmodel4 aclmodel4; 137 + 138 + enum aclscope4 { 139 + ACL_SCOPE_FILE_OBJECT = 1, 140 + ACL_SCOPE_FILE_SYSTEM = 2, 141 + ACL_SCOPE_SERVER = 3, 142 + }; 143 + 144 + typedef enum aclscope4 aclscope4; 145 + 146 + enum posixacetag4 { 147 + POSIXACE4_TAG_USER_OBJ = 1, 148 + POSIXACE4_TAG_USER = 2, 149 + POSIXACE4_TAG_GROUP_OBJ = 3, 150 + POSIXACE4_TAG_GROUP = 4, 151 + POSIXACE4_TAG_MASK = 5, 152 + POSIXACE4_TAG_OTHER = 6, 153 + }; 154 + 155 + typedef enum posixacetag4 posixacetag4; 156 + 157 + typedef uint32_t posixaceperm4; 158 + 159 + enum { POSIXACE4_PERM_EXECUTE = 0x00000001 }; 160 + 161 + enum { POSIXACE4_PERM_WRITE = 0x00000002 }; 162 + 163 + enum { POSIXACE4_PERM_READ = 0x00000004 }; 164 + 165 + struct posixace4 { 166 + posixacetag4 tag; 167 + posixaceperm4 perm; 168 + utf8str_mixed who; 169 + }; 170 + 171 + typedef aclmodel4 fattr4_acl_trueform; 172 + 173 + typedef aclscope4 fattr4_acl_trueform_scope; 174 + 175 + typedef struct { 176 + u32 count; 177 + struct posixace4 *element; 178 + } fattr4_posix_default_acl; 179 + 180 + typedef struct { 181 + u32 count; 182 + struct posixace4 *element; 183 + } fattr4_posix_access_acl; 184 + 185 + /* 186 + * New for POSIX ACL extension 187 + */ 188 + 189 + enum { FATTR4_ACL_TRUEFORM = 89 }; 190 + 191 + enum { FATTR4_ACL_TRUEFORM_SCOPE = 90 }; 192 + 193 + enum { FATTR4_POSIX_DEFAULT_ACL = 91 }; 194 + 195 + enum { FATTR4_POSIX_ACCESS_ACL = 92 }; 150 196 151 197 #define NFS4_int64_t_sz \ 152 198 (XDR_hyper) 153 199 #define NFS4_uint32_t_sz \ 154 200 (XDR_unsigned_int) 155 201 #define NFS4_bitmap4_sz (XDR_unsigned_int) 202 + #define NFS4_utf8string_sz (XDR_unsigned_int) 203 + #define NFS4_utf8str_cis_sz \ 204 + (NFS4_utf8string_sz) 205 + #define NFS4_utf8str_cs_sz \ 206 + (NFS4_utf8string_sz) 207 + #define NFS4_utf8str_mixed_sz \ 208 + (NFS4_utf8string_sz) 156 209 #define NFS4_nfstime4_sz \ 157 210 (NFS4_int64_t_sz + NFS4_uint32_t_sz) 158 211 #define NFS4_fattr4_offline_sz \ ··· 246 149 #define NFS4_fattr4_time_deleg_modify_sz \ 247 150 (NFS4_nfstime4_sz) 248 151 #define NFS4_open_delegation_type4_sz (XDR_int) 152 + #define NFS4_aclmodel4_sz (XDR_int) 153 + #define NFS4_aclscope4_sz (XDR_int) 154 + #define NFS4_posixacetag4_sz (XDR_int) 155 + #define NFS4_posixaceperm4_sz \ 156 + (NFS4_uint32_t_sz) 157 + #define NFS4_posixace4_sz \ 158 + (NFS4_posixacetag4_sz + NFS4_posixaceperm4_sz + NFS4_utf8str_mixed_sz) 159 + #define NFS4_fattr4_acl_trueform_sz \ 160 + (NFS4_aclmodel4_sz) 161 + #define NFS4_fattr4_acl_trueform_scope_sz \ 162 + (NFS4_aclscope4_sz) 163 + #define NFS4_fattr4_posix_default_acl_sz (XDR_unsigned_int) 164 + #define NFS4_fattr4_posix_access_acl_sz (XDR_unsigned_int) 249 165 250 166 #endif /* _LINUX_XDRGEN_NFS4_1_DEF_H */
+1 -1
include/uapi/linux/nfs.h
··· 55 55 NFSERR_NODEV = 19, /* v2 v3 v4 */ 56 56 NFSERR_NOTDIR = 20, /* v2 v3 v4 */ 57 57 NFSERR_ISDIR = 21, /* v2 v3 v4 */ 58 - NFSERR_INVAL = 22, /* v2 v3 v4 */ 58 + NFSERR_INVAL = 22, /* v3 v4 */ 59 59 NFSERR_FBIG = 27, /* v2 v3 v4 */ 60 60 NFSERR_NOSPC = 28, /* v2 v3 v4 */ 61 61 NFSERR_ROFS = 30, /* v2 v3 v4 */
+1
include/uapi/linux/nfsd_netlink.h
··· 35 35 NFSD_A_SERVER_GRACETIME, 36 36 NFSD_A_SERVER_LEASETIME, 37 37 NFSD_A_SERVER_SCOPE, 38 + NFSD_A_SERVER_MIN_THREADS, 38 39 39 40 __NFSD_A_SERVER_MAX, 40 41 NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1)
+64 -18
net/sunrpc/auth_gss/gss_rpc_xdr.c
··· 320 320 321 321 /* status->minor_status */ 322 322 p = xdr_inline_decode(xdr, 8); 323 - if (unlikely(p == NULL)) 324 - return -ENOSPC; 323 + if (unlikely(p == NULL)) { 324 + err = -ENOSPC; 325 + goto out_free_mech; 326 + } 325 327 p = xdr_decode_hyper(p, &status->minor_status); 326 328 327 329 /* status->major_status_string */ 328 330 err = gssx_dec_buffer(xdr, &status->major_status_string); 329 331 if (err) 330 - return err; 332 + goto out_free_mech; 331 333 332 334 /* status->minor_status_string */ 333 335 err = gssx_dec_buffer(xdr, &status->minor_status_string); 334 336 if (err) 335 - return err; 337 + goto out_free_major_status_string; 336 338 337 339 /* status->server_ctx */ 338 340 err = gssx_dec_buffer(xdr, &status->server_ctx); 339 341 if (err) 340 - return err; 342 + goto out_free_minor_status_string; 341 343 342 344 /* we assume we have no options for now, so simply consume them */ 343 345 /* status->options */ 344 346 err = dummy_dec_opt_array(xdr, &status->options); 347 + if (err) 348 + goto out_free_server_ctx; 345 349 350 + return 0; 351 + 352 + out_free_server_ctx: 353 + kfree(status->server_ctx.data); 354 + status->server_ctx.data = NULL; 355 + out_free_minor_status_string: 356 + kfree(status->minor_status_string.data); 357 + status->minor_status_string.data = NULL; 358 + out_free_major_status_string: 359 + kfree(status->major_status_string.data); 360 + status->major_status_string.data = NULL; 361 + out_free_mech: 362 + kfree(status->mech.data); 363 + status->mech.data = NULL; 346 364 return err; 347 365 } 348 366 ··· 523 505 /* name->name_type */ 524 506 err = gssx_dec_buffer(xdr, &dummy_netobj); 525 507 if (err) 526 - return err; 508 + goto out_free_display_name; 527 509 528 510 /* name->exported_name */ 529 511 err = gssx_dec_buffer(xdr, &dummy_netobj); 530 512 if (err) 531 - return err; 513 + goto out_free_display_name; 532 514 533 515 /* name->exported_composite_name */ 534 516 err = gssx_dec_buffer(xdr, &dummy_netobj); 535 517 if (err) 536 - return err; 518 + goto out_free_display_name; 537 519 538 520 /* we assume we have no attributes for now, so simply consume them */ 539 521 /* name->name_attributes */ 540 522 err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array); 541 523 if (err) 542 - return err; 524 + goto out_free_display_name; 543 525 544 526 /* we assume we have no options for now, so simply consume them */ 545 527 /* name->extensions */ 546 528 err = dummy_dec_opt_array(xdr, &dummy_option_array); 529 + if (err) 530 + goto out_free_display_name; 547 531 532 + return 0; 533 + 534 + out_free_display_name: 535 + kfree(name->display_name.data); 536 + name->display_name.data = NULL; 548 537 return err; 549 538 } 550 539 ··· 674 649 /* ctx->state */ 675 650 err = gssx_dec_buffer(xdr, &ctx->state); 676 651 if (err) 677 - return err; 652 + goto out_free_exported_context_token; 678 653 679 654 /* ctx->need_release */ 680 655 err = gssx_dec_bool(xdr, &ctx->need_release); 681 656 if (err) 682 - return err; 657 + goto out_free_state; 683 658 684 659 /* ctx->mech */ 685 660 err = gssx_dec_buffer(xdr, &ctx->mech); 686 661 if (err) 687 - return err; 662 + goto out_free_state; 688 663 689 664 /* ctx->src_name */ 690 665 err = gssx_dec_name(xdr, &ctx->src_name); 691 666 if (err) 692 - return err; 667 + goto out_free_mech; 693 668 694 669 /* ctx->targ_name */ 695 670 err = gssx_dec_name(xdr, &ctx->targ_name); 696 671 if (err) 697 - return err; 672 + goto out_free_src_name; 698 673 699 674 /* ctx->lifetime */ 700 675 p = xdr_inline_decode(xdr, 8+8); 701 - if (unlikely(p == NULL)) 702 - return -ENOSPC; 676 + if (unlikely(p == NULL)) { 677 + err = -ENOSPC; 678 + goto out_free_targ_name; 679 + } 703 680 p = xdr_decode_hyper(p, &ctx->lifetime); 704 681 705 682 /* ctx->ctx_flags */ ··· 710 683 /* ctx->locally_initiated */ 711 684 err = gssx_dec_bool(xdr, &ctx->locally_initiated); 712 685 if (err) 713 - return err; 686 + goto out_free_targ_name; 714 687 715 688 /* ctx->open */ 716 689 err = gssx_dec_bool(xdr, &ctx->open); 717 690 if (err) 718 - return err; 691 + goto out_free_targ_name; 719 692 720 693 /* we assume we have no options for now, so simply consume them */ 721 694 /* ctx->options */ 722 695 err = dummy_dec_opt_array(xdr, &ctx->options); 696 + if (err) 697 + goto out_free_targ_name; 723 698 699 + return 0; 700 + 701 + out_free_targ_name: 702 + kfree(ctx->targ_name.display_name.data); 703 + ctx->targ_name.display_name.data = NULL; 704 + out_free_src_name: 705 + kfree(ctx->src_name.display_name.data); 706 + ctx->src_name.display_name.data = NULL; 707 + out_free_mech: 708 + kfree(ctx->mech.data); 709 + ctx->mech.data = NULL; 710 + out_free_state: 711 + kfree(ctx->state.data); 712 + ctx->state.data = NULL; 713 + out_free_exported_context_token: 714 + kfree(ctx->exported_context_token.data); 715 + ctx->exported_context_token.data = NULL; 724 716 return err; 725 717 } 726 718
+131 -85
net/sunrpc/svc.c
··· 763 763 } 764 764 EXPORT_SYMBOL_GPL(svc_pool_wake_idle_thread); 765 765 766 - static struct svc_pool * 767 - svc_pool_next(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) 766 + /** 767 + * svc_new_thread - spawn a new thread in the given pool 768 + * @serv: the serv to which the pool belongs 769 + * @pool: pool in which thread should be spawned 770 + * 771 + * Create a new thread inside @pool, which is a part of @serv. 772 + * Caller must hold the service mutex. 773 + * 774 + * Returns 0 on success, or -errno on failure. 775 + */ 776 + int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool) 768 777 { 769 - return pool ? pool : &serv->sv_pools[(*state)++ % serv->sv_nrpools]; 770 - } 778 + struct svc_rqst *rqstp; 779 + struct task_struct *task; 780 + int node; 781 + int err = 0; 771 782 772 - static struct svc_pool * 773 - svc_pool_victim(struct svc_serv *serv, struct svc_pool *target_pool, 774 - unsigned int *state) 775 - { 776 - struct svc_pool *pool; 777 - unsigned int i; 783 + node = svc_pool_map_get_node(pool->sp_id); 778 784 779 - pool = target_pool; 780 - 781 - if (!pool) { 782 - for (i = 0; i < serv->sv_nrpools; i++) { 783 - pool = &serv->sv_pools[--(*state) % serv->sv_nrpools]; 784 - if (pool->sp_nrthreads) 785 - break; 786 - } 785 + rqstp = svc_prepare_thread(serv, pool, node); 786 + if (!rqstp) 787 + return -ENOMEM; 788 + task = kthread_create_on_node(serv->sv_threadfn, rqstp, 789 + node, "%s", serv->sv_name); 790 + if (IS_ERR(task)) { 791 + err = PTR_ERR(task); 792 + goto out; 787 793 } 788 794 789 - if (pool && pool->sp_nrthreads) { 790 - set_bit(SP_VICTIM_REMAINS, &pool->sp_flags); 791 - set_bit(SP_NEED_VICTIM, &pool->sp_flags); 792 - return pool; 793 - } 794 - return NULL; 795 + rqstp->rq_task = task; 796 + if (serv->sv_nrpools > 1) 797 + svc_pool_map_set_cpumask(task, pool->sp_id); 798 + 799 + svc_sock_update_bufs(serv); 800 + wake_up_process(task); 801 + 802 + /* Wait for the thread to signal initialization status */ 803 + wait_var_event(&rqstp->rq_err, rqstp->rq_err != -EAGAIN); 804 + err = rqstp->rq_err; 805 + out: 806 + if (err) 807 + svc_exit_thread(rqstp); 808 + return err; 795 809 } 810 + EXPORT_SYMBOL_GPL(svc_new_thread); 796 811 797 812 static int 798 813 svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) 799 814 { 800 - struct svc_rqst *rqstp; 801 - struct task_struct *task; 802 - struct svc_pool *chosen_pool; 803 - unsigned int state = serv->sv_nrthreads-1; 804 - int node; 805 - int err; 815 + int err = 0; 806 816 807 - do { 808 - nrservs--; 809 - chosen_pool = svc_pool_next(serv, pool, &state); 810 - node = svc_pool_map_get_node(chosen_pool->sp_id); 817 + while (!err && nrservs--) 818 + err = svc_new_thread(serv, pool); 811 819 812 - rqstp = svc_prepare_thread(serv, chosen_pool, node); 813 - if (!rqstp) 814 - return -ENOMEM; 815 - task = kthread_create_on_node(serv->sv_threadfn, rqstp, 816 - node, "%s", serv->sv_name); 817 - if (IS_ERR(task)) { 818 - svc_exit_thread(rqstp); 819 - return PTR_ERR(task); 820 - } 821 - 822 - rqstp->rq_task = task; 823 - if (serv->sv_nrpools > 1) 824 - svc_pool_map_set_cpumask(task, chosen_pool->sp_id); 825 - 826 - svc_sock_update_bufs(serv); 827 - wake_up_process(task); 828 - 829 - wait_var_event(&rqstp->rq_err, rqstp->rq_err != -EAGAIN); 830 - err = rqstp->rq_err; 831 - if (err) { 832 - svc_exit_thread(rqstp); 833 - return err; 834 - } 835 - } while (nrservs > 0); 836 - 837 - return 0; 820 + return err; 838 821 } 839 822 840 823 static int 841 824 svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) 842 825 { 843 - unsigned int state = serv->sv_nrthreads-1; 844 - struct svc_pool *victim; 845 - 846 826 do { 847 - victim = svc_pool_victim(serv, pool, &state); 848 - if (!victim) 849 - break; 850 - svc_pool_wake_idle_thread(victim); 851 - wait_on_bit(&victim->sp_flags, SP_VICTIM_REMAINS, 852 - TASK_IDLE); 827 + set_bit(SP_VICTIM_REMAINS, &pool->sp_flags); 828 + set_bit(SP_NEED_VICTIM, &pool->sp_flags); 829 + svc_pool_wake_idle_thread(pool); 830 + wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE); 853 831 nrservs++; 854 832 } while (nrservs < 0); 855 833 return 0; 856 834 } 857 835 858 836 /** 859 - * svc_set_num_threads - adjust number of threads per RPC service 837 + * svc_set_pool_threads - adjust number of threads per pool 860 838 * @serv: RPC service to adjust 861 - * @pool: Specific pool from which to choose threads, or NULL 862 - * @nrservs: New number of threads for @serv (0 or less means kill all threads) 839 + * @pool: Specific pool from which to choose threads 840 + * @min_threads: min number of threads to run in @pool 841 + * @max_threads: max number of threads in @pool (0 means kill all threads) 863 842 * 864 - * Create or destroy threads to make the number of threads for @serv the 865 - * given number. If @pool is non-NULL, change only threads in that pool; 866 - * otherwise, round-robin between all pools for @serv. @serv's 867 - * sv_nrthreads is adjusted for each thread created or destroyed. 843 + * Create or destroy threads in @pool to bring it into an acceptable range 844 + * between @min_threads and @max_threads. 845 + * 846 + * If @min_threads is 0 or larger than @max_threads, then it is ignored and 847 + * the pool will be set to run a static @max_threads number of threads. 868 848 * 869 849 * Caller must ensure mutual exclusion between this and server startup or 870 850 * shutdown. ··· 853 873 * starting a thread. 854 874 */ 855 875 int 856 - svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) 876 + svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool, 877 + unsigned int min_threads, unsigned int max_threads) 857 878 { 858 - if (!pool) 859 - nrservs -= serv->sv_nrthreads; 860 - else 861 - nrservs -= pool->sp_nrthreads; 879 + int delta; 862 880 863 - if (nrservs > 0) 864 - return svc_start_kthreads(serv, pool, nrservs); 865 - if (nrservs < 0) 866 - return svc_stop_kthreads(serv, pool, nrservs); 881 + if (!pool) 882 + return -EINVAL; 883 + 884 + /* clamp min threads to the max */ 885 + if (min_threads > max_threads) 886 + min_threads = max_threads; 887 + 888 + pool->sp_nrthrmin = min_threads; 889 + pool->sp_nrthrmax = max_threads; 890 + 891 + /* 892 + * When min_threads is set, then only change the number of 893 + * threads to bring it within an acceptable range. 894 + */ 895 + if (min_threads) { 896 + if (pool->sp_nrthreads > max_threads) 897 + delta = max_threads; 898 + else if (pool->sp_nrthreads < min_threads) 899 + delta = min_threads; 900 + else 901 + return 0; 902 + } else { 903 + delta = max_threads; 904 + } 905 + 906 + delta -= pool->sp_nrthreads; 907 + if (delta > 0) 908 + return svc_start_kthreads(serv, pool, delta); 909 + if (delta < 0) 910 + return svc_stop_kthreads(serv, pool, delta); 867 911 return 0; 912 + } 913 + EXPORT_SYMBOL_GPL(svc_set_pool_threads); 914 + 915 + /** 916 + * svc_set_num_threads - adjust number of threads in serv 917 + * @serv: RPC service to adjust 918 + * @min_threads: min number of threads to run per pool 919 + * @nrservs: New number of threads for @serv (0 means kill all threads) 920 + * 921 + * Create or destroy threads in @serv to bring it to @nrservs. If there 922 + * are multiple pools then the new threads or victims will be distributed 923 + * evenly among them. 924 + * 925 + * Caller must ensure mutual exclusion between this and server startup or 926 + * shutdown. 927 + * 928 + * Returns zero on success or a negative errno if an error occurred while 929 + * starting a thread. On failure, some pools may have already been 930 + * adjusted; the caller is responsible for recovery. 931 + */ 932 + int 933 + svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads, 934 + unsigned int nrservs) 935 + { 936 + unsigned int base = nrservs / serv->sv_nrpools; 937 + unsigned int remain = nrservs % serv->sv_nrpools; 938 + int i, err = 0; 939 + 940 + for (i = 0; i < serv->sv_nrpools; ++i) { 941 + struct svc_pool *pool = &serv->sv_pools[i]; 942 + int threads = base; 943 + 944 + if (remain) { 945 + ++threads; 946 + --remain; 947 + } 948 + 949 + err = svc_set_pool_threads(serv, pool, min_threads, threads); 950 + if (err) 951 + break; 952 + } 953 + return err; 868 954 } 869 955 EXPORT_SYMBOL_GPL(svc_set_num_threads); 870 956
+42 -9
net/sunrpc/svc_xprt.c
··· 714 714 return true; 715 715 } 716 716 717 - static void svc_thread_wait_for_work(struct svc_rqst *rqstp) 717 + static bool svc_schedule_timeout(long timeo) 718 + { 719 + return schedule_timeout(timeo ? timeo : MAX_SCHEDULE_TIMEOUT) == 0; 720 + } 721 + 722 + static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo) 718 723 { 719 724 struct svc_pool *pool = rqstp->rq_pool; 725 + bool did_timeout = false; 720 726 721 727 if (svc_thread_should_sleep(rqstp)) { 722 728 set_current_state(TASK_IDLE | TASK_FREEZABLE); 723 729 llist_add(&rqstp->rq_idle, &pool->sp_idle_threads); 724 730 if (likely(svc_thread_should_sleep(rqstp))) 725 - schedule(); 731 + did_timeout = svc_schedule_timeout(timeo); 726 732 727 733 while (!llist_del_first_this(&pool->sp_idle_threads, 728 734 &rqstp->rq_idle)) { ··· 740 734 * for this new work. This thread can safely sleep 741 735 * until woken again. 742 736 */ 743 - schedule(); 737 + did_timeout = svc_schedule_timeout(timeo); 744 738 set_current_state(TASK_IDLE | TASK_FREEZABLE); 745 739 } 746 740 __set_current_state(TASK_RUNNING); ··· 748 742 cond_resched(); 749 743 } 750 744 try_to_freeze(); 745 + return did_timeout; 751 746 } 752 747 753 748 static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt) ··· 842 835 /** 843 836 * svc_recv - Receive and process the next request on any transport 844 837 * @rqstp: an idle RPC service thread 838 + * @timeo: timeout (in jiffies) (0 means infinite timeout) 845 839 * 846 840 * This code is carefully organised not to touch any cachelines in 847 841 * the shared svc_serv structure, only cachelines in the local 848 842 * svc_pool. 843 + * 844 + * If the timeout is 0, then the sleep will never time out. 845 + * 846 + * Returns -ETIMEDOUT if idle for an extended period 847 + * -EBUSY if there is more work to do than available threads 848 + * 0 otherwise. 849 849 */ 850 - void svc_recv(struct svc_rqst *rqstp) 850 + int svc_recv(struct svc_rqst *rqstp, long timeo) 851 851 { 852 852 struct svc_pool *pool = rqstp->rq_pool; 853 + bool did_timeout; 854 + int ret = 0; 853 855 854 856 if (!svc_alloc_arg(rqstp)) 855 - return; 857 + return ret; 856 858 857 - svc_thread_wait_for_work(rqstp); 859 + did_timeout = svc_thread_wait_for_work(rqstp, timeo); 860 + 861 + if (did_timeout && svc_thread_should_sleep(rqstp) && 862 + pool->sp_nrthrmin && pool->sp_nrthreads > pool->sp_nrthrmin) 863 + ret = -ETIMEDOUT; 858 864 859 865 clear_bit(SP_TASK_PENDING, &pool->sp_flags); 860 866 861 867 if (svc_thread_should_stop(rqstp)) { 862 868 svc_thread_wake_next(rqstp); 863 - return; 869 + return ret; 864 870 } 865 871 866 872 rqstp->rq_xprt = svc_xprt_dequeue(pool); ··· 885 865 * cache information to be provided. When there are no 886 866 * idle threads, we reduce the wait time. 887 867 */ 888 - if (pool->sp_idle_threads.first) 868 + if (pool->sp_idle_threads.first) { 889 869 rqstp->rq_chandle.thread_wait = 5 * HZ; 890 - else 870 + } else { 891 871 rqstp->rq_chandle.thread_wait = 1 * HZ; 872 + /* 873 + * No idle threads: signal -EBUSY so the caller 874 + * can consider spawning another thread. Use 875 + * SP_TASK_STARTING to limit this signal to one 876 + * thread at a time; the caller clears this flag 877 + * after starting a new thread. 878 + */ 879 + if (!did_timeout && timeo && 880 + !test_and_set_bit(SP_TASK_STARTING, 881 + &pool->sp_flags)) 882 + ret = -EBUSY; 883 + } 892 884 893 885 trace_svc_xprt_dequeue(rqstp); 894 886 svc_handle_xprt(rqstp, xprt); ··· 919 887 } 920 888 } 921 889 #endif 890 + return ret; 922 891 } 923 892 EXPORT_SYMBOL_GPL(svc_recv); 924 893
-2
tools/net/sunrpc/xdrgen/README
··· 250 250 Enable something like a #include to dynamically insert the content 251 251 of other specification files 252 252 253 - Properly support line-by-line pass-through via the "%" decorator 254 - 255 253 Build a unit test suite for verifying translation of XDR language 256 254 into compilable code 257 255
+4 -1
tools/net/sunrpc/xdrgen/generators/__init__.py
··· 6 6 from jinja2 import Environment, FileSystemLoader, Template 7 7 8 8 from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier 9 - from xdr_ast import public_apis, pass_by_reference, get_header_name 9 + from xdr_ast import public_apis, pass_by_reference, structs, get_header_name 10 10 from xdr_parse import get_xdr_annotate 11 11 12 12 ··· 25 25 environment.globals["annotate"] = get_xdr_annotate() 26 26 environment.globals["public_apis"] = public_apis 27 27 environment.globals["pass_by_reference"] = pass_by_reference 28 + environment.globals["structs"] = structs 28 29 return environment 29 30 case _: 30 31 raise NotImplementedError("Language not supported") ··· 59 58 """Return name of C type""" 60 59 builtin_native_c_type = { 61 60 "bool": "bool", 61 + "short": "s16", 62 + "unsigned_short": "u16", 62 63 "int": "s32", 63 64 "unsigned_int": "u32", 64 65 "long": "s32",
+8 -1
tools/net/sunrpc/xdrgen/generators/enum.py
··· 5 5 6 6 from generators import SourceGenerator, create_jinja2_environment 7 7 from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name 8 + from xdr_parse import get_xdr_enum_validation 8 9 9 10 10 11 class XdrEnumGenerator(SourceGenerator): ··· 43 42 template = self.environment.get_template("decoder/enum_be.j2") 44 43 else: 45 44 template = self.environment.get_template("decoder/enum.j2") 46 - print(template.render(name=node.name)) 45 + print( 46 + template.render( 47 + name=node.name, 48 + enumerators=node.enumerators, 49 + validate=get_xdr_enum_validation(), 50 + ) 51 + ) 47 52 48 53 def emit_encoder(self, node: _XdrEnum) -> None: 49 54 """Emit one encoder function for an XDR enum type"""
+26
tools/net/sunrpc/xdrgen/generators/passthru.py
··· 1 + #!/usr/bin/env python3 2 + # ex: set filetype=python: 3 + 4 + """Generate code for XDR pass-through lines""" 5 + 6 + from generators import SourceGenerator, create_jinja2_environment 7 + from xdr_ast import _XdrPassthru 8 + 9 + 10 + class XdrPassthruGenerator(SourceGenerator): 11 + """Generate source code for XDR pass-through content""" 12 + 13 + def __init__(self, language: str, peer: str): 14 + """Initialize an instance of this class""" 15 + self.environment = create_jinja2_environment(language, "passthru") 16 + self.peer = peer 17 + 18 + def emit_definition(self, node: _XdrPassthru) -> None: 19 + """Emit one pass-through line""" 20 + template = self.environment.get_template("definition.j2") 21 + print(template.render(content=node.content)) 22 + 23 + def emit_decoder(self, node: _XdrPassthru) -> None: 24 + """Emit one pass-through line""" 25 + template = self.environment.get_template("source.j2") 26 + print(template.render(content=node.content))
+37 -1
tools/net/sunrpc/xdrgen/generators/program.py
··· 5 5 6 6 from jinja2 import Environment 7 7 8 - from generators import SourceGenerator, create_jinja2_environment 8 + from generators import SourceGenerator, create_jinja2_environment, get_jinja2_template 9 9 from xdr_ast import _RpcProgram, _RpcVersion, excluded_apis 10 + from xdr_ast import max_widths, get_header_name 10 11 11 12 12 13 def emit_version_definitions( ··· 128 127 for version in node.versions: 129 128 emit_version_definitions(self.environment, program, version) 130 129 130 + template = self.environment.get_template("definition/program.j2") 131 + print(template.render(name=raw_name, value=node.number)) 132 + 131 133 def emit_declaration(self, node: _RpcProgram) -> None: 132 134 """Emit a declaration pair for each of an RPC programs's procedures""" 133 135 raw_name = node.name ··· 170 166 emit_version_argument_encoders( 171 167 self.environment, program, version, 172 168 ) 169 + 170 + def emit_maxsize(self, node: _RpcProgram) -> None: 171 + """Emit maxsize macro for maximum RPC argument size""" 172 + header = get_header_name().upper() 173 + 174 + # Find the largest argument across all versions 175 + max_arg_width = 0 176 + max_arg_name = None 177 + for version in node.versions: 178 + for procedure in version.procedures: 179 + if procedure.name in excluded_apis: 180 + continue 181 + arg_name = procedure.argument.type_name 182 + if arg_name == "void": 183 + continue 184 + if arg_name not in max_widths: 185 + continue 186 + if max_widths[arg_name] > max_arg_width: 187 + max_arg_width = max_widths[arg_name] 188 + max_arg_name = arg_name 189 + 190 + if max_arg_name is None: 191 + return 192 + 193 + macro_name = header + "_MAX_ARGS_SZ" 194 + template = get_jinja2_template(self.environment, "maxsize", "max_args") 195 + print( 196 + template.render( 197 + macro=macro_name, 198 + width=header + "_" + max_arg_name + "_sz", 199 + ) 200 + )
+4 -4
tools/net/sunrpc/xdrgen/generators/typedef.py
··· 58 58 elif isinstance(node, _XdrOptionalData): 59 59 raise NotImplementedError("<optional_data> typedef not yet implemented") 60 60 elif isinstance(node, _XdrVoid): 61 - raise NotImplementedError("<void> typedef not yet implemented") 61 + raise ValueError("invalid void usage in RPC Specification") 62 62 else: 63 63 raise NotImplementedError("typedef: type not recognized") 64 64 ··· 104 104 elif isinstance(node, _XdrOptionalData): 105 105 raise NotImplementedError("<optional_data> typedef not yet implemented") 106 106 elif isinstance(node, _XdrVoid): 107 - raise NotImplementedError("<void> typedef not yet implemented") 107 + raise ValueError("invalid void usage in RPC Specification") 108 108 else: 109 109 raise NotImplementedError("typedef: type not recognized") 110 110 ··· 165 165 elif isinstance(node, _XdrOptionalData): 166 166 raise NotImplementedError("<optional_data> typedef not yet implemented") 167 167 elif isinstance(node, _XdrVoid): 168 - raise NotImplementedError("<void> typedef not yet implemented") 168 + raise ValueError("invalid void usage in RPC Specification") 169 169 else: 170 170 raise NotImplementedError("typedef: type not recognized") 171 171 ··· 225 225 elif isinstance(node, _XdrOptionalData): 226 226 raise NotImplementedError("<optional_data> typedef not yet implemented") 227 227 elif isinstance(node, _XdrVoid): 228 - raise NotImplementedError("<void> typedef not yet implemented") 228 + raise ValueError("invalid void usage in RPC Specification") 229 229 else: 230 230 raise NotImplementedError("typedef: type not recognized") 231 231
+95 -20
tools/net/sunrpc/xdrgen/generators/union.py
··· 84 84 print(template.render(name=node.name, type=node.spec.type_name)) 85 85 86 86 87 + def emit_union_arm_decoder( 88 + environment: Environment, node: _XdrCaseSpec 89 + ) -> None: 90 + """Emit decoder for an XDR union's arm (data only, no case/break)""" 91 + 92 + if isinstance(node.arm, _XdrVoid): 93 + return 94 + if isinstance(node.arm, _XdrString): 95 + type_name = "char *" 96 + classifier = "" 97 + else: 98 + type_name = node.arm.spec.type_name 99 + classifier = node.arm.spec.c_classifier 100 + 101 + assert isinstance(node.arm, (_XdrBasic, _XdrString)) 102 + template = get_jinja2_template(environment, "decoder", node.arm.template) 103 + print( 104 + template.render( 105 + name=node.arm.name, 106 + type=type_name, 107 + classifier=classifier, 108 + ) 109 + ) 110 + 111 + 87 112 def emit_union_case_spec_decoder( 88 113 environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool 89 114 ) -> None: ··· 176 151 template = get_jinja2_template(environment, "decoder", "open") 177 152 print(template.render(name=node.name)) 178 153 179 - emit_union_switch_spec_decoder(environment, node.discriminant) 154 + # For boolean discriminants, use if statement instead of switch 155 + if node.discriminant.spec.type_name == "bool": 156 + template = get_jinja2_template(environment, "decoder", "bool_spec") 157 + print(template.render(name=node.discriminant.name, type=node.discriminant.spec.type_name)) 180 158 181 - for case in node.cases: 182 - emit_union_case_spec_decoder( 183 - environment, 184 - case, 185 - node.discriminant.spec.type_name in big_endian, 186 - ) 159 + # Find and emit the TRUE case 160 + for case in node.cases: 161 + if case.values and case.values[0] == "TRUE": 162 + emit_union_arm_decoder(environment, case) 163 + break 187 164 188 - emit_union_default_spec_decoder(environment, node) 165 + template = get_jinja2_template(environment, "decoder", "close") 166 + print(template.render()) 167 + else: 168 + emit_union_switch_spec_decoder(environment, node.discriminant) 189 169 190 - template = get_jinja2_template(environment, "decoder", "close") 191 - print(template.render()) 170 + for case in node.cases: 171 + emit_union_case_spec_decoder( 172 + environment, 173 + case, 174 + node.discriminant.spec.type_name in big_endian, 175 + ) 176 + 177 + emit_union_default_spec_decoder(environment, node) 178 + 179 + template = get_jinja2_template(environment, "decoder", "close") 180 + print(template.render()) 192 181 193 182 194 183 def emit_union_switch_spec_encoder( ··· 212 173 assert isinstance(node, _XdrBasic) 213 174 template = get_jinja2_template(environment, "encoder", "switch_spec") 214 175 print(template.render(name=node.name, type=node.spec.type_name)) 176 + 177 + 178 + def emit_union_arm_encoder( 179 + environment: Environment, node: _XdrCaseSpec 180 + ) -> None: 181 + """Emit encoder for an XDR union's arm (data only, no case/break)""" 182 + 183 + if isinstance(node.arm, _XdrVoid): 184 + return 185 + if isinstance(node.arm, _XdrString): 186 + type_name = "char *" 187 + else: 188 + type_name = node.arm.spec.type_name 189 + 190 + assert isinstance(node.arm, (_XdrBasic, _XdrString)) 191 + template = get_jinja2_template(environment, "encoder", node.arm.template) 192 + print( 193 + template.render( 194 + name=node.arm.name, 195 + type=type_name, 196 + ) 197 + ) 215 198 216 199 217 200 def emit_union_case_spec_encoder( ··· 296 235 template = get_jinja2_template(environment, "encoder", "open") 297 236 print(template.render(name=node.name)) 298 237 299 - emit_union_switch_spec_encoder(environment, node.discriminant) 238 + # For boolean discriminants, use if statement instead of switch 239 + if node.discriminant.spec.type_name == "bool": 240 + template = get_jinja2_template(environment, "encoder", "bool_spec") 241 + print(template.render(name=node.discriminant.name, type=node.discriminant.spec.type_name)) 300 242 301 - for case in node.cases: 302 - emit_union_case_spec_encoder( 303 - environment, 304 - case, 305 - node.discriminant.spec.type_name in big_endian, 306 - ) 243 + # Find and emit the TRUE case 244 + for case in node.cases: 245 + if case.values and case.values[0] == "TRUE": 246 + emit_union_arm_encoder(environment, case) 247 + break 307 248 308 - emit_union_default_spec_encoder(environment, node) 249 + template = get_jinja2_template(environment, "encoder", "close") 250 + print(template.render()) 251 + else: 252 + emit_union_switch_spec_encoder(environment, node.discriminant) 309 253 310 - template = get_jinja2_template(environment, "encoder", "close") 311 - print(template.render()) 254 + for case in node.cases: 255 + emit_union_case_spec_encoder( 256 + environment, 257 + case, 258 + node.discriminant.spec.type_name in big_endian, 259 + ) 260 + 261 + emit_union_default_spec_encoder(environment, node) 262 + 263 + template = get_jinja2_template(environment, "encoder", "close") 264 + print(template.render()) 312 265 313 266 314 267 def emit_union_maxsize(environment: Environment, node: _XdrUnion) -> None:
+8 -2
tools/net/sunrpc/xdrgen/grammars/xdr.lark
··· 20 20 type_specifier : unsigned_hyper 21 21 | unsigned_long 22 22 | unsigned_int 23 + | unsigned_short 23 24 | hyper 24 25 | long 25 26 | int 27 + | short 26 28 | float 27 29 | double 28 30 | quadruple ··· 37 35 unsigned_hyper : "unsigned" "hyper" 38 36 unsigned_long : "unsigned" "long" 39 37 unsigned_int : "unsigned" "int" 38 + unsigned_short : "unsigned" "short" 40 39 hyper : "hyper" 41 40 long : "long" 42 41 int : "int" 42 + short : "short" 43 43 float : "float" 44 44 double : "double" 45 45 quadruple : "quadruple" ··· 78 74 | type_def 79 75 | program_def 80 76 | pragma_def 77 + | passthru_def 78 + 79 + passthru_def : PASSTHRU 81 80 82 81 // 83 82 // RPC program definitions not specified in RFC 4506 ··· 118 111 hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/ 119 112 octal_constant : /0[0-7]+/ 120 113 121 - PASSTHRU : "%" | "%" /.+/ 122 - %ignore PASSTHRU 114 + PASSTHRU : /%.*/ 123 115 124 116 %import common.C_COMMENT 125 117 %ignore C_COMMENT
+16 -12
tools/net/sunrpc/xdrgen/subcmds/declarations.py
··· 8 8 9 9 from argparse import Namespace 10 10 from lark import logger 11 - from lark.exceptions import UnexpectedInput 11 + from lark.exceptions import VisitError 12 12 13 - from generators.constant import XdrConstantGenerator 14 13 from generators.enum import XdrEnumGenerator 15 14 from generators.header_bottom import XdrHeaderBottomGenerator 16 15 from generators.header_top import XdrHeaderTopGenerator ··· 20 21 from generators.union import XdrUnionGenerator 21 22 22 23 from xdr_ast import transform_parse_tree, _RpcProgram, Specification 23 - from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer 24 - from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion 24 + from xdr_ast import _XdrEnum, _XdrPointer, _XdrTypedef, _XdrStruct, _XdrUnion 25 25 from xdr_parse import xdr_parser, set_xdr_annotate 26 + from xdr_parse import make_error_handler, XdrParseError 27 + from xdr_parse import handle_transform_error 26 28 27 29 logger.setLevel(logging.INFO) 28 30 ··· 50 50 gen.emit_declaration(definition.value) 51 51 52 52 53 - def handle_parse_error(e: UnexpectedInput) -> bool: 54 - """Simple parse error reporting, no recovery attempted""" 55 - print(e) 56 - return True 57 - 58 - 59 53 def subcmd(args: Namespace) -> int: 60 54 """Generate definitions and declarations""" 61 55 62 56 set_xdr_annotate(args.annotate) 63 57 parser = xdr_parser() 64 58 with open(args.filename, encoding="utf-8") as f: 65 - parse_tree = parser.parse(f.read(), on_error=handle_parse_error) 66 - ast = transform_parse_tree(parse_tree) 59 + source = f.read() 60 + try: 61 + parse_tree = parser.parse( 62 + source, on_error=make_error_handler(source, args.filename) 63 + ) 64 + except XdrParseError: 65 + return 1 66 + try: 67 + ast = transform_parse_tree(parse_tree) 68 + except VisitError as e: 69 + handle_transform_error(e, source, args.filename) 70 + return 1 67 71 68 72 gen = XdrHeaderTopGenerator(args.language, args.peer) 69 73 gen.emit_declaration(args.filename, ast)
+21 -10
tools/net/sunrpc/xdrgen/subcmds/definitions.py
··· 8 8 9 9 from argparse import Namespace 10 10 from lark import logger 11 - from lark.exceptions import UnexpectedInput 11 + from lark.exceptions import VisitError 12 12 13 13 from generators.constant import XdrConstantGenerator 14 14 from generators.enum import XdrEnumGenerator 15 15 from generators.header_bottom import XdrHeaderBottomGenerator 16 16 from generators.header_top import XdrHeaderTopGenerator 17 + from generators.passthru import XdrPassthruGenerator 17 18 from generators.pointer import XdrPointerGenerator 18 19 from generators.program import XdrProgramGenerator 19 20 from generators.typedef import XdrTypedefGenerator ··· 22 21 from generators.union import XdrUnionGenerator 23 22 24 23 from xdr_ast import transform_parse_tree, Specification 25 - from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer 24 + from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPassthru, _XdrPointer 26 25 from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion 27 26 from xdr_parse import xdr_parser, set_xdr_annotate 27 + from xdr_parse import make_error_handler, XdrParseError 28 + from xdr_parse import handle_transform_error 28 29 29 30 logger.setLevel(logging.INFO) 30 31 ··· 48 45 gen = XdrStructGenerator(language, peer) 49 46 elif isinstance(definition.value, _XdrUnion): 50 47 gen = XdrUnionGenerator(language, peer) 48 + elif isinstance(definition.value, _XdrPassthru): 49 + gen = XdrPassthruGenerator(language, peer) 51 50 else: 52 51 continue 53 52 gen.emit_definition(definition.value) ··· 69 64 gen = XdrStructGenerator(language, peer) 70 65 elif isinstance(definition.value, _XdrUnion): 71 66 gen = XdrUnionGenerator(language, peer) 67 + elif isinstance(definition.value, _RpcProgram): 68 + gen = XdrProgramGenerator(language, peer) 72 69 else: 73 70 continue 74 71 gen.emit_maxsize(definition.value) 75 - 76 - 77 - def handle_parse_error(e: UnexpectedInput) -> bool: 78 - """Simple parse error reporting, no recovery attempted""" 79 - print(e) 80 - return True 81 72 82 73 83 74 def subcmd(args: Namespace) -> int: ··· 82 81 set_xdr_annotate(args.annotate) 83 82 parser = xdr_parser() 84 83 with open(args.filename, encoding="utf-8") as f: 85 - parse_tree = parser.parse(f.read(), on_error=handle_parse_error) 86 - ast = transform_parse_tree(parse_tree) 84 + source = f.read() 85 + try: 86 + parse_tree = parser.parse( 87 + source, on_error=make_error_handler(source, args.filename) 88 + ) 89 + except XdrParseError: 90 + return 1 91 + try: 92 + ast = transform_parse_tree(parse_tree) 93 + except VisitError as e: 94 + handle_transform_error(e, source, args.filename) 95 + return 1 87 96 88 97 gen = XdrHeaderTopGenerator(args.language, args.peer) 89 98 gen.emit_definition(args.filename, ast)
+15 -10
tools/net/sunrpc/xdrgen/subcmds/lint.py
··· 8 8 9 9 from argparse import Namespace 10 10 from lark import logger 11 - from lark.exceptions import UnexpectedInput 11 + from lark.exceptions import VisitError 12 12 13 - from xdr_parse import xdr_parser 13 + from xdr_parse import xdr_parser, make_error_handler, XdrParseError 14 + from xdr_parse import handle_transform_error 14 15 from xdr_ast import transform_parse_tree 15 16 16 17 logger.setLevel(logging.DEBUG) 17 - 18 - 19 - def handle_parse_error(e: UnexpectedInput) -> bool: 20 - """Simple parse error reporting, no recovery attempted""" 21 - print(e) 22 - return True 23 18 24 19 25 20 def subcmd(args: Namespace) -> int: ··· 22 27 23 28 parser = xdr_parser() 24 29 with open(args.filename, encoding="utf-8") as f: 25 - parse_tree = parser.parse(f.read(), on_error=handle_parse_error) 26 - transform_parse_tree(parse_tree) 30 + source = f.read() 31 + try: 32 + parse_tree = parser.parse( 33 + source, on_error=make_error_handler(source, args.filename) 34 + ) 35 + except XdrParseError: 36 + return 1 37 + try: 38 + transform_parse_tree(parse_tree) 39 + except VisitError as e: 40 + handle_transform_error(e, source, args.filename) 41 + return 1 27 42 28 43 return 0
+34 -17
tools/net/sunrpc/xdrgen/subcmds/source.py
··· 8 8 9 9 from argparse import Namespace 10 10 from lark import logger 11 - from lark.exceptions import UnexpectedInput 11 + from lark.exceptions import VisitError 12 12 13 13 from generators.source_top import XdrSourceTopGenerator 14 14 from generators.enum import XdrEnumGenerator 15 + from generators.passthru import XdrPassthruGenerator 15 16 from generators.pointer import XdrPointerGenerator 16 17 from generators.program import XdrProgramGenerator 17 18 from generators.typedef import XdrTypedefGenerator ··· 20 19 from generators.union import XdrUnionGenerator 21 20 22 21 from xdr_ast import transform_parse_tree, _RpcProgram, Specification 23 - from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer 22 + from xdr_ast import _XdrAst, _XdrEnum, _XdrPassthru, _XdrPointer 24 23 from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion 25 24 26 - from xdr_parse import xdr_parser, set_xdr_annotate 25 + from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation 26 + from xdr_parse import make_error_handler, XdrParseError 27 + from xdr_parse import handle_transform_error 27 28 28 29 logger.setLevel(logging.INFO) 29 30 ··· 75 72 gen.emit_source(filename, root) 76 73 77 74 for definition in root.definitions: 78 - emit_source_decoder(definition.value, language, "server") 75 + if isinstance(definition.value, _XdrPassthru): 76 + passthru_gen = XdrPassthruGenerator(language, "server") 77 + passthru_gen.emit_decoder(definition.value) 78 + else: 79 + emit_source_decoder(definition.value, language, "server") 79 80 for definition in root.definitions: 80 - emit_source_encoder(definition.value, language, "server") 81 + if not isinstance(definition.value, _XdrPassthru): 82 + emit_source_encoder(definition.value, language, "server") 81 83 82 84 83 85 def generate_client_source(filename: str, root: Specification, language: str) -> None: 84 - """Generate server-side source code""" 86 + """Generate client-side source code""" 85 87 86 88 gen = XdrSourceTopGenerator(language, "client") 87 89 gen.emit_source(filename, root) 88 90 89 - print("") 90 91 for definition in root.definitions: 91 - emit_source_encoder(definition.value, language, "client") 92 + if isinstance(definition.value, _XdrPassthru): 93 + passthru_gen = XdrPassthruGenerator(language, "client") 94 + passthru_gen.emit_decoder(definition.value) 95 + else: 96 + emit_source_encoder(definition.value, language, "client") 92 97 for definition in root.definitions: 93 - emit_source_decoder(definition.value, language, "client") 98 + if not isinstance(definition.value, _XdrPassthru): 99 + emit_source_decoder(definition.value, language, "client") 94 100 95 101 # cel: todo: client needs PROC macros 96 - 97 - 98 - def handle_parse_error(e: UnexpectedInput) -> bool: 99 - """Simple parse error reporting, no recovery attempted""" 100 - print(e) 101 - return True 102 102 103 103 104 104 def subcmd(args: Namespace) -> int: 105 105 """Generate encoder and decoder functions""" 106 106 107 107 set_xdr_annotate(args.annotate) 108 + set_xdr_enum_validation(not args.no_enum_validation) 108 109 parser = xdr_parser() 109 110 with open(args.filename, encoding="utf-8") as f: 110 - parse_tree = parser.parse(f.read(), on_error=handle_parse_error) 111 - ast = transform_parse_tree(parse_tree) 111 + source = f.read() 112 + try: 113 + parse_tree = parser.parse( 114 + source, on_error=make_error_handler(source, args.filename) 115 + ) 116 + except XdrParseError: 117 + return 1 118 + try: 119 + ast = transform_parse_tree(parse_tree) 120 + except VisitError as e: 121 + handle_transform_error(e, source, args.filename) 122 + return 1 112 123 match args.peer: 113 124 case "server": 114 125 generate_server_source(args.filename, ast, args.language)
-1
tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enum.j2
··· 1 1 {# SPDX-License-Identifier: GPL-2.0 #} 2 - 3 2 bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr); 4 3 bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value);
+11
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
··· 14 14 15 15 if (xdr_stream_decode_u32(xdr, &val) < 0) 16 16 return false; 17 + {% if validate and enumerators %} 18 + /* Compiler may optimize to a range check for dense enums */ 19 + switch (val) { 20 + {% for e in enumerators %} 21 + case {{ e.name }}: 22 + {% endfor %} 23 + break; 24 + default: 25 + return false; 26 + } 27 + {% endif %} 17 28 *ptr = val; 18 29 return true; 19 30 }
+20
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2
··· 10 10 {% endif %} 11 11 xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) 12 12 { 13 + {% if validate and enumerators %} 14 + __be32 raw; 15 + u32 val; 16 + 17 + if (xdr_stream_decode_be32(xdr, &raw) < 0) 18 + return false; 19 + val = be32_to_cpu(raw); 20 + /* Compiler may optimize to a range check for dense enums */ 21 + switch (val) { 22 + {% for e in enumerators %} 23 + case {{ e.name }}: 24 + {% endfor %} 25 + break; 26 + default: 27 + return false; 28 + } 29 + *ptr = raw; 30 + return true; 31 + {% else %} 13 32 return xdr_stream_decode_be32(xdr, ptr) == 0; 33 + {% endif %} 14 34 }
+1
tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2
··· 1 1 {# SPDX-License-Identifier: GPL-2.0 #} 2 2 }; 3 + 3 4 typedef enum {{ name }} {{ name }};
+1
tools/net/sunrpc/xdrgen/templates/C/enum/definition/close_be.j2
··· 1 1 {# SPDX-License-Identifier: GPL-2.0 #} 2 2 }; 3 + 3 4 typedef __be32 {{ name }};
+3
tools/net/sunrpc/xdrgen/templates/C/passthru/definition.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + {{ content }}
+3
tools/net/sunrpc/xdrgen/templates/C/passthru/source.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + {{ content }}
+4
tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2
··· 14 14 {% if argument == 'void' %} 15 15 return xdrgen_decode_void(xdr); 16 16 {% else %} 17 + {% if argument in structs %} 17 18 struct {{ argument }} *argp = rqstp->rq_argp; 19 + {% else %} 20 + {{ argument }} *argp = rqstp->rq_argp; 21 + {% endif %} 18 22 19 23 return xdrgen_decode_{{ argument }}(xdr, argp); 20 24 {% endif %}
+5
tools/net/sunrpc/xdrgen/templates/C/program/definition/program.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + #ifndef {{ name }} 4 + #define {{ name }} ({{ value }}) 5 + #endif
+6
tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2
··· 14 14 {% if result == 'void' %} 15 15 return xdrgen_encode_void(xdr); 16 16 {% else %} 17 + {% if result in structs %} 17 18 struct {{ result }} *resp = rqstp->rq_resp; 18 19 19 20 return xdrgen_encode_{{ result }}(xdr, resp); 21 + {% else %} 22 + {{ result }} *resp = rqstp->rq_resp; 23 + 24 + return xdrgen_encode_{{ result }}(xdr, *resp); 25 + {% endif %} 20 26 {% endif %} 21 27 }
+3
tools/net/sunrpc/xdrgen/templates/C/program/maxsize/max_args.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} \ 3 + ({{ width }})
-1
tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2
··· 8 8 #include <linux/sunrpc/xdr.h> 9 9 #include <linux/sunrpc/xdrgen/_defs.h> 10 10 #include <linux/sunrpc/xdrgen/_builtins.h> 11 - #include <linux/sunrpc/xdrgen/nlm4.h> 12 11 13 12 #include <linux/sunrpc/clnt.h>
+7
tools/net/sunrpc/xdrgen/templates/C/union/decoder/bool_spec.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + {% if annotate %} 3 + /* discriminant {{ name }} */ 4 + {% endif %} 5 + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }})) 6 + return false; 7 + if (ptr->{{ name }}) {
+1
tools/net/sunrpc/xdrgen/templates/C/union/definition/close.j2
··· 3 3 }; 4 4 {%- if name in public_apis %} 5 5 6 + 6 7 bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr); 7 8 bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr); 8 9 {%- endif -%}
+7
tools/net/sunrpc/xdrgen/templates/C/union/encoder/bool_spec.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + {% if annotate %} 3 + /* discriminant {{ name }} */ 4 + {% endif %} 5 + if (!xdrgen_encode_{{ type }}(xdr, ptr->{{ name }})) 6 + return false; 7 + if (ptr->{{ name }}) {
+42 -7
tools/net/sunrpc/xdrgen/xdr_ast.py
··· 34 34 symbolic_widths = { 35 35 "void": ["XDR_void"], 36 36 "bool": ["XDR_bool"], 37 + "short": ["XDR_short"], 38 + "unsigned_short": ["XDR_unsigned_short"], 37 39 "int": ["XDR_int"], 38 40 "unsigned_int": ["XDR_unsigned_int"], 39 41 "long": ["XDR_long"], ··· 50 48 max_widths = { 51 49 "void": 0, 52 50 "bool": 1, 51 + "short": 1, 52 + "unsigned_short": 1, 53 53 "int": 1, 54 54 "unsigned_int": 1, 55 55 "long": 1, ··· 330 326 """An XDR enum definition""" 331 327 332 328 name: str 333 - minimum: int 334 - maximum: int 335 329 enumerators: List[_XdrEnumerator] 336 330 337 331 def max_width(self) -> int: ··· 517 515 518 516 519 517 @dataclass 518 + class _XdrPassthru(_XdrAst): 519 + """Passthrough line to emit verbatim in output""" 520 + 521 + content: str 522 + 523 + 524 + @dataclass 520 525 class Definition(_XdrAst, ast_utils.WithMeta): 521 526 """Corresponds to 'definition' in the grammar""" 522 527 ··· 577 568 value = children[1].value 578 569 return _XdrConstant(name, value) 579 570 580 - # cel: Python can compute a min() and max() for the enumerator values 581 - # so that the generated code can perform proper range checking. 582 571 def enum(self, children): 583 572 """Instantiate one _XdrEnum object""" 584 573 enum_name = children[0].symbol ··· 590 583 enumerators.append(_XdrEnumerator(name, value)) 591 584 i = i + 2 592 585 593 - return _XdrEnum(enum_name, 0, 0, enumerators) 586 + return _XdrEnum(enum_name, enumerators) 594 587 595 588 def fixed_length_opaque(self, children): 596 589 """Instantiate one _XdrFixedLengthOpaque declaration object""" ··· 745 738 raise NotImplementedError("Directive not supported") 746 739 return _Pragma() 747 740 741 + def passthru_def(self, children): 742 + """Instantiate one _XdrPassthru object""" 743 + token = children[0] 744 + content = token.value[1:] 745 + return _XdrPassthru(content) 746 + 748 747 749 748 transformer = ast_utils.create_transformer(this_module, ParseToAst()) 750 749 751 750 751 + def _merge_consecutive_passthru(definitions: List[Definition]) -> List[Definition]: 752 + """Merge consecutive passthru definitions into single nodes""" 753 + result = [] 754 + i = 0 755 + while i < len(definitions): 756 + if isinstance(definitions[i].value, _XdrPassthru): 757 + lines = [definitions[i].value.content] 758 + meta = definitions[i].meta 759 + j = i + 1 760 + while j < len(definitions) and isinstance(definitions[j].value, _XdrPassthru): 761 + lines.append(definitions[j].value.content) 762 + j += 1 763 + merged = _XdrPassthru("\n".join(lines)) 764 + result.append(Definition(meta, merged)) 765 + i = j 766 + else: 767 + result.append(definitions[i]) 768 + i += 1 769 + return result 770 + 771 + 752 772 def transform_parse_tree(parse_tree): 753 773 """Transform productions into an abstract syntax tree""" 754 - 755 - return transformer.transform(parse_tree) 774 + ast = transformer.transform(parse_tree) 775 + ast.definitions = _merge_consecutive_passthru(ast.definitions) 776 + return ast 756 777 757 778 758 779 def get_header_name() -> str:
+138
tools/net/sunrpc/xdrgen/xdr_parse.py
··· 3 3 4 4 """Common parsing code for xdrgen""" 5 5 6 + import sys 7 + from typing import Callable 8 + 6 9 from lark import Lark 10 + from lark.exceptions import UnexpectedInput, UnexpectedToken, VisitError 7 11 8 12 9 13 # Set to True to emit annotation comments in generated source 10 14 annotate = False 15 + 16 + # Set to True to emit enum value validation in decoders 17 + enum_validation = True 18 + 19 + # Map internal Lark token names to human-readable names 20 + TOKEN_NAMES = { 21 + "__ANON_0": "identifier", 22 + "__ANON_1": "number", 23 + "SEMICOLON": "';'", 24 + "LBRACE": "'{'", 25 + "RBRACE": "'}'", 26 + "LPAR": "'('", 27 + "RPAR": "')'", 28 + "LSQB": "'['", 29 + "RSQB": "']'", 30 + "LESSTHAN": "'<'", 31 + "MORETHAN": "'>'", 32 + "EQUAL": "'='", 33 + "COLON": "':'", 34 + "COMMA": "','", 35 + "STAR": "'*'", 36 + "$END": "end of file", 37 + } 38 + 39 + 40 + class XdrParseError(Exception): 41 + """Raised when XDR parsing fails""" 11 42 12 43 13 44 def set_xdr_annotate(set_it: bool) -> None: ··· 50 19 def get_xdr_annotate() -> bool: 51 20 """Return True if --annotate was specified on the command line""" 52 21 return annotate 22 + 23 + 24 + def set_xdr_enum_validation(set_it: bool) -> None: 25 + """Set 'enum_validation' based on command line options""" 26 + global enum_validation 27 + enum_validation = set_it 28 + 29 + 30 + def get_xdr_enum_validation() -> bool: 31 + """Return True when enum validation is enabled for decoder generation""" 32 + return enum_validation 33 + 34 + 35 + def make_error_handler(source: str, filename: str) -> Callable[[UnexpectedInput], bool]: 36 + """Create an error handler that reports the first parse error and aborts. 37 + 38 + Args: 39 + source: The XDR source text being parsed 40 + filename: The name of the file being parsed 41 + 42 + Returns: 43 + An error handler function for use with Lark's on_error parameter 44 + """ 45 + lines = source.splitlines() 46 + 47 + def handle_parse_error(e: UnexpectedInput) -> bool: 48 + """Report a parse error with context and abort parsing""" 49 + line_num = e.line 50 + column = e.column 51 + line_text = lines[line_num - 1] if 0 < line_num <= len(lines) else "" 52 + 53 + # Build the error message 54 + msg_parts = [f"{filename}:{line_num}:{column}: parse error"] 55 + 56 + # Show what was found vs what was expected 57 + if isinstance(e, UnexpectedToken): 58 + token = e.token 59 + if token.type == "__ANON_0": 60 + found = f"identifier '{token.value}'" 61 + elif token.type == "__ANON_1": 62 + found = f"number '{token.value}'" 63 + else: 64 + found = f"'{token.value}'" 65 + msg_parts.append(f"Unexpected {found}") 66 + 67 + # Provide helpful expected tokens list 68 + expected = e.expected 69 + if expected: 70 + readable = [ 71 + TOKEN_NAMES.get(exp, exp.lower().replace("_", " ")) 72 + for exp in sorted(expected) 73 + ] 74 + if len(readable) == 1: 75 + msg_parts.append(f"Expected {readable[0]}") 76 + elif len(readable) <= 4: 77 + msg_parts.append(f"Expected one of: {', '.join(readable)}") 78 + else: 79 + msg_parts.append(str(e).split("\n")[0]) 80 + 81 + # Show the offending line with a caret pointing to the error 82 + msg_parts.append("") 83 + msg_parts.append(f" {line_text}") 84 + prefix = line_text[: column - 1].expandtabs() 85 + msg_parts.append(f" {' ' * len(prefix)}^") 86 + 87 + sys.stderr.write("\n".join(msg_parts) + "\n") 88 + raise XdrParseError() 89 + 90 + return handle_parse_error 91 + 92 + 93 + def handle_transform_error(e: VisitError, source: str, filename: str) -> None: 94 + """Report a transform error with context. 95 + 96 + Args: 97 + e: The VisitError from Lark's transformer 98 + source: The XDR source text being parsed 99 + filename: The name of the file being parsed 100 + """ 101 + lines = source.splitlines() 102 + 103 + # Extract position from the tree node if available 104 + line_num = 0 105 + column = 0 106 + if hasattr(e.obj, "meta") and e.obj.meta: 107 + line_num = e.obj.meta.line 108 + column = e.obj.meta.column 109 + 110 + line_text = lines[line_num - 1] if 0 < line_num <= len(lines) else "" 111 + 112 + # Build the error message 113 + msg_parts = [f"{filename}:{line_num}:{column}: semantic error"] 114 + 115 + # The original exception is typically a KeyError for undefined types 116 + if isinstance(e.orig_exc, KeyError): 117 + msg_parts.append(f"Undefined type '{e.orig_exc.args[0]}'") 118 + else: 119 + msg_parts.append(str(e.orig_exc)) 120 + 121 + # Show the offending line with a caret pointing to the error 122 + if line_text: 123 + msg_parts.append("") 124 + msg_parts.append(f" {line_text}") 125 + prefix = line_text[: column - 1].expandtabs() 126 + msg_parts.append(f" {' ' * len(prefix)}^") 127 + 128 + sys.stderr.write("\n".join(msg_parts) + "\n") 53 129 54 130 55 131 def xdr_parser() -> Lark:
+6 -2
tools/net/sunrpc/xdrgen/xdrgen
··· 123 123 help="Generate code for client or server side", 124 124 type=str, 125 125 ) 126 + source_parser.add_argument( 127 + "--no-enum-validation", 128 + action="store_true", 129 + default=False, 130 + help="Disable enum value validation in decoders", 131 + ) 126 132 source_parser.add_argument("filename", help="File containing an XDR specification") 127 133 source_parser.set_defaults(func=source.subcmd) 128 134 ··· 139 133 try: 140 134 if __name__ == "__main__": 141 135 sys.exit(main()) 142 - except SystemExit: 143 - sys.exit(0) 144 136 except (KeyboardInterrupt, BrokenPipeError): 145 137 sys.exit(1)