From 5559c157b79907a901578f93f83eb6732bfcbc1a Mon Sep 17 00:00:00 2001 From: Scott Mayhew Date: Mon, 9 Sep 2024 16:28:54 -0400 Subject: [PATCH 01/16] nfsd: enforce upper limit for namelen in __cld_pipe_inprogress_downcall() This patch is intended to go on top of "nfsd: return -EINVAL when namelen is 0" from Li Lingfeng. Li's patch checks for 0, but we should be enforcing an upper bound as well. Note that if nfsdcld somehow gets an id > NFS4_OPAQUE_LIMIT in its database, it'll truncate it to NFS4_OPAQUE_LIMIT when it does the downcall anyway. Signed-off-by: Scott Mayhew Signed-off-by: Chuck Lever --- fs/nfsd/nfs4recover.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 530686f32e9e..b7d61eb8afe9 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -809,8 +809,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, ci = &cmsg->cm_u.cm_clntinfo; if (get_user(namelen, &ci->cc_name.cn_len)) return -EFAULT; - if (!namelen) { - dprintk("%s: namelen should not be zero", __func__); + if (namelen == 0 || namelen > NFS4_OPAQUE_LIMIT) { + dprintk("%s: invalid namelen (%u)", __func__, namelen); return -EINVAL; } name.data = memdup_user(&ci->cc_name.cn_id, namelen); @@ -835,8 +835,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, cnm = &cmsg->cm_u.cm_name; if (get_user(namelen, &cnm->cn_len)) return -EFAULT; - if (!namelen) { - dprintk("%s: namelen should not be zero", __func__); + if (namelen == 0 || namelen > NFS4_OPAQUE_LIMIT) { + dprintk("%s: invalid namelen (%u)", __func__, namelen); return -EINVAL; } name.data = memdup_user(&cnm->cn_id, namelen); -- 2.50.1 From a078a7dc0eaa9db288ae45319f7f7503968af546 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 29 Aug 2024 09:26:40 -0400 Subject: [PATCH 02/16] nfsd: untangle code in nfsd4_deleg_getattr_conflict() The code in nfsd4_deleg_getattr_conflict() is convoluted and buggy. With this patch we: - properly handle non-nfsd leases. We must not assume flc_owner is a delegation unless fl_lmops == &nfsd_lease_mng_ops - move the main code out of the for loop - have a single exit which calls nfs4_put_stid() (and other exits which don't need to call that) [ jlayton: refactored on top of Neil's other patch: nfsd: fix nfsd4_deleg_getattr_conflict in presence of third party lease ] Fixes: c5967721e106 ("NFSD: handle GETATTR conflict with write delegation") Signed-off-by: NeilBrown Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 131 +++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a49aa75bc0b7..df69dc6af467 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -8834,6 +8834,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, __be32 status; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct file_lock_context *ctx; + struct nfs4_delegation *dp = NULL; struct file_lease *fl; struct iattr attrs; struct nfs4_cb_fattr *ncf; @@ -8843,84 +8844,76 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, ctx = locks_inode_context(inode); if (!ctx) return 0; + +#define NON_NFSD_LEASE ((void *)1) + spin_lock(&ctx->flc_lock); for_each_file_lock(fl, &ctx->flc_lease) { - unsigned char type = fl->c.flc_type; - if (fl->c.flc_flags == FL_LAYOUT) continue; - if (fl->fl_lmops != &nfsd_lease_mng_ops) { - /* - * non-nfs lease, if it's a lease with F_RDLCK then - * we are done; there isn't any write delegation - * on this inode - */ - if (type == F_RDLCK) - break; - - nfsd_stats_wdeleg_getattr_inc(nn); - spin_unlock(&ctx->flc_lock); - - status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); + if (fl->c.flc_type == F_WRLCK) { + if (fl->fl_lmops == &nfsd_lease_mng_ops) + dp = fl->c.flc_owner; + else + dp = NON_NFSD_LEASE; + } + break; + } + if (dp == NULL || dp == NON_NFSD_LEASE || + dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) { + spin_unlock(&ctx->flc_lock); + if (dp == NON_NFSD_LEASE) { + status = nfserrno(nfsd_open_break_lease(inode, + NFSD_MAY_READ)); if (status != nfserr_jukebox || !nfsd_wait_for_delegreturn(rqstp, inode)) return status; - return 0; } - if (type == F_WRLCK) { - struct nfs4_delegation *dp = fl->c.flc_owner; + return 0; + } - if (dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) { - spin_unlock(&ctx->flc_lock); - return 0; - } - nfsd_stats_wdeleg_getattr_inc(nn); - dp = fl->c.flc_owner; - refcount_inc(&dp->dl_stid.sc_count); - ncf = &dp->dl_cb_fattr; - nfs4_cb_getattr(&dp->dl_cb_fattr); - spin_unlock(&ctx->flc_lock); - wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY, - TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT); - if (ncf->ncf_cb_status) { - /* Recall delegation only if client didn't respond */ - status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); - if (status != nfserr_jukebox || - !nfsd_wait_for_delegreturn(rqstp, inode)) { - nfs4_put_stid(&dp->dl_stid); - return status; - } - } - if (!ncf->ncf_file_modified && - (ncf->ncf_initial_cinfo != ncf->ncf_cb_change || - ncf->ncf_cur_fsize != ncf->ncf_cb_fsize)) - ncf->ncf_file_modified = true; - if (ncf->ncf_file_modified) { - int err; - - /* - * Per section 10.4.3 of RFC 8881, the server would - * not update the file's metadata with the client's - * modified size - */ - attrs.ia_mtime = attrs.ia_ctime = current_time(inode); - attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG; - inode_lock(inode); - err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); - inode_unlock(inode); - if (err) { - nfs4_put_stid(&dp->dl_stid); - return nfserrno(err); - } - ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; - *size = ncf->ncf_cur_fsize; - *modified = true; - } - nfs4_put_stid(&dp->dl_stid); - return 0; + nfsd_stats_wdeleg_getattr_inc(nn); + refcount_inc(&dp->dl_stid.sc_count); + ncf = &dp->dl_cb_fattr; + nfs4_cb_getattr(&dp->dl_cb_fattr); + spin_unlock(&ctx->flc_lock); + + wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY, + TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT); + if (ncf->ncf_cb_status) { + /* Recall delegation only if client didn't respond */ + status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ)); + if (status != nfserr_jukebox || + !nfsd_wait_for_delegreturn(rqstp, inode)) + goto out_status; + } + if (!ncf->ncf_file_modified && + (ncf->ncf_initial_cinfo != ncf->ncf_cb_change || + ncf->ncf_cur_fsize != ncf->ncf_cb_fsize)) + ncf->ncf_file_modified = true; + if (ncf->ncf_file_modified) { + int err; + + /* + * Per section 10.4.3 of RFC 8881, the server would + * not update the file's metadata with the client's + * modified size + */ + attrs.ia_mtime = attrs.ia_ctime = current_time(inode); + attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG; + inode_lock(inode); + err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); + inode_unlock(inode); + if (err) { + status = nfserrno(err); + goto out_status; } - break; + ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; + *size = ncf->ncf_cur_fsize; + *modified = true; } - spin_unlock(&ctx->flc_lock); - return 0; + status = 0; +out_status: + nfs4_put_stid(&dp->dl_stid); + return status; } -- 2.50.1 From bf92e5008b17f935a6de8b708551e02c2294121c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 9 Sep 2024 10:40:53 -0400 Subject: [PATCH 03/16] nfsd: fix initial getattr on write delegation At this point in compound processing, currentfh refers to the parent of the file, not the file itself. Get the correct dentry from the delegation stateid instead. Fixes: c5967721e106 ("NFSD: handle GETATTR conflict with write delegation") Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index df69dc6af467..cb5a9ab451c5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5914,6 +5914,28 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) } } +static bool +nfs4_delegation_stat(struct nfs4_delegation *dp, struct svc_fh *currentfh, + struct kstat *stat) +{ + struct nfsd_file *nf = find_rw_file(dp->dl_stid.sc_file); + struct path path; + int rc; + + if (!nf) + return false; + + path.mnt = currentfh->fh_export->ex_path.mnt; + path.dentry = file_dentry(nf->nf_file); + + rc = vfs_getattr(&path, stat, + (STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), + AT_STATX_SYNC_AS_STAT); + + nfsd_file_put(nf); + return rc == 0; +} + /* * The Linux NFS server does not offer write delegations to NFSv4.0 * clients in order to avoid conflicts between write delegations and @@ -5949,7 +5971,6 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, int cb_up; int status = 0; struct kstat stat; - struct path path; cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); open->op_recall = false; @@ -5985,20 +6006,16 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid)); if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) { - open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE; - trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); - path.mnt = currentfh->fh_export->ex_path.mnt; - path.dentry = currentfh->fh_dentry; - if (vfs_getattr(&path, &stat, - (STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), - AT_STATX_SYNC_AS_STAT)) { + if (!nfs4_delegation_stat(dp, currentfh, &stat)) { nfs4_put_stid(&dp->dl_stid); destroy_delegation(dp); goto out_no_deleg; } + open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE; dp->dl_cb_fattr.ncf_cur_fsize = stat.size; dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry)); + trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); } else { open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid); -- 2.50.1 From 45bb63ed20e02ae146336412889fe5450316a84f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 9 Sep 2024 15:06:36 +1000 Subject: [PATCH 04/16] nfsd: fix delegation_blocked() to block correctly for at least 30 seconds The pair of bloom filtered used by delegation_blocked() was intended to block delegations on given filehandles for between 30 and 60 seconds. A new filehandle would be recorded in the "new" bit set. That would then be switch to the "old" bit set between 0 and 30 seconds later, and it would remain as the "old" bit set for 30 seconds. Unfortunately the code intended to clear the old bit set once it reached 30 seconds old, preparing it to be the next new bit set, instead cleared the *new* bit set before switching it to be the old bit set. This means that the "old" bit set is always empty and delegations are blocked between 0 and 30 seconds. This patch updates bd->new before clearing the set with that index, instead of afterwards. Reported-by: Olga Kornievskaia Cc: stable@vger.kernel.org Fixes: 6282cd565553 ("NFSD: Don't hand out delegations for 30 seconds after recalling them.") Signed-off-by: NeilBrown Reviewed-by: Benjamin Coddington Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cb5a9ab451c5..ac1859c7cc9d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1078,7 +1078,8 @@ static void nfs4_free_deleg(struct nfs4_stid *stid) * When a delegation is recalled, the filehandle is stored in the "new" * filter. * Every 30 seconds we swap the filters and clear the "new" one, - * unless both are empty of course. + * unless both are empty of course. This results in delegations for a + * given filehandle being blocked for between 30 and 60 seconds. * * Each filter is 256 bits. We hash the filehandle to 32bit and use the * low 3 bytes as hash-table indices. @@ -1107,9 +1108,9 @@ static int delegation_blocked(struct knfsd_fh *fh) if (ktime_get_seconds() - bd->swap_time > 30) { bd->entries -= bd->old_entries; bd->old_entries = bd->entries; + bd->new = 1-bd->new; memset(bd->set[bd->new], 0, sizeof(bd->set[0])); - bd->new = 1-bd->new; bd->swap_time = ktime_get_seconds(); } spin_unlock(&blocked_delegations_lock); -- 2.50.1 From 4b132aacb0768ac1e652cf517097ea6f237214b9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 13 Sep 2024 14:08:13 -0400 Subject: [PATCH 05/16] tools: Add xdrgen Add a Python-based tool for translating XDR specifications into XDR encoder and decoder functions written in the Linux kernel's C coding style. The generator attempts to match the usual C coding style of the Linux kernel's SunRPC consumers. This approach is similar to the netlink code generator in tools/net/ynl . The maintainability benefits of machine-generated XDR code include: - Stronger type checking - Reduces the number of bugs introduced by human error - Makes the XDR code easier to audit and analyze - Enables rapid prototyping of new RPC-based protocols - Hardens the layering between protocol logic and marshaling - Makes it easier to add observability on demand - Unit tests might be built for both the tool and (automatically) for the generated code In addition, converting the XDR layer to use memory-safe languages such as Rust will be easier if much of the code can be converted automatically. Tested-by: Jeff Layton Signed-off-by: Chuck Lever --- include/linux/sunrpc/xdrgen/_builtins.h | 243 +++++++++ include/linux/sunrpc/xdrgen/_defs.h | 26 + tools/net/sunrpc/xdrgen/.gitignore | 2 + tools/net/sunrpc/xdrgen/README | 244 +++++++++ tools/net/sunrpc/xdrgen/__init__.py | 2 + .../net/sunrpc/xdrgen/generators/__init__.py | 113 ++++ .../net/sunrpc/xdrgen/generators/constant.py | 20 + tools/net/sunrpc/xdrgen/generators/enum.py | 44 ++ .../sunrpc/xdrgen/generators/header_bottom.py | 33 ++ .../sunrpc/xdrgen/generators/header_top.py | 45 ++ tools/net/sunrpc/xdrgen/generators/pointer.py | 272 ++++++++++ tools/net/sunrpc/xdrgen/generators/program.py | 168 ++++++ .../sunrpc/xdrgen/generators/source_top.py | 32 ++ tools/net/sunrpc/xdrgen/generators/struct.py | 272 ++++++++++ tools/net/sunrpc/xdrgen/generators/typedef.py | 255 +++++++++ tools/net/sunrpc/xdrgen/generators/union.py | 243 +++++++++ tools/net/sunrpc/xdrgen/grammars/xdr.lark | 119 ++++ tools/net/sunrpc/xdrgen/subcmds/__init__.py | 2 + .../net/sunrpc/xdrgen/subcmds/declarations.py | 76 +++ .../net/sunrpc/xdrgen/subcmds/definitions.py | 78 +++ tools/net/sunrpc/xdrgen/subcmds/lint.py | 33 ++ tools/net/sunrpc/xdrgen/subcmds/source.py | 118 ++++ .../templates/C/constants/definition.j2 | 3 + .../templates/C/enum/declaration/close.j2 | 4 + .../xdrgen/templates/C/enum/decoder/enum.j2 | 19 + .../templates/C/enum/definition/close.j2 | 2 + .../templates/C/enum/definition/enumerator.j2 | 2 + .../templates/C/enum/definition/open.j2 | 3 + .../xdrgen/templates/C/enum/encoder/enum.j2 | 14 + .../C/header_bottom/declaration/header.j2 | 3 + .../C/header_bottom/definition/header.j2 | 3 + .../C/header_top/declaration/header.j2 | 14 + .../C/header_top/definition/header.j2 | 10 + .../templates/C/pointer/declaration/close.j2 | 4 + .../templates/C/pointer/decoder/basic.j2 | 6 + .../templates/C/pointer/decoder/close.j2 | 3 + .../C/pointer/decoder/fixed_length_array.j2 | 8 + .../C/pointer/decoder/fixed_length_opaque.j2 | 6 + .../templates/C/pointer/decoder/open.j2 | 22 + .../C/pointer/decoder/optional_data.j2 | 6 + .../pointer/decoder/variable_length_array.j2 | 13 + .../pointer/decoder/variable_length_opaque.j2 | 6 + .../pointer/decoder/variable_length_string.j2 | 6 + .../templates/C/pointer/definition/basic.j2 | 5 + .../templates/C/pointer/definition/close.j2 | 2 + .../pointer/definition/fixed_length_array.j2 | 5 + .../pointer/definition/fixed_length_opaque.j2 | 5 + .../templates/C/pointer/definition/open.j2 | 6 + .../C/pointer/definition/optional_data.j2 | 5 + .../definition/variable_length_array.j2 | 8 + .../definition/variable_length_opaque.j2 | 5 + .../definition/variable_length_string.j2 | 5 + .../templates/C/pointer/encoder/basic.j2 | 10 + .../templates/C/pointer/encoder/close.j2 | 3 + .../C/pointer/encoder/fixed_length_array.j2 | 12 + .../C/pointer/encoder/fixed_length_opaque.j2 | 6 + .../templates/C/pointer/encoder/open.j2 | 20 + .../C/pointer/encoder/optional_data.j2 | 6 + .../pointer/encoder/variable_length_array.j2 | 15 + .../pointer/encoder/variable_length_opaque.j2 | 8 + .../pointer/encoder/variable_length_string.j2 | 8 + .../C/program/declaration/argument.j2 | 2 + .../templates/C/program/declaration/result.j2 | 2 + .../templates/C/program/decoder/argument.j2 | 21 + .../templates/C/program/decoder/result.j2 | 22 + .../templates/C/program/definition/close.j2 | 2 + .../templates/C/program/definition/open.j2 | 6 + .../C/program/definition/procedure.j2 | 2 + .../templates/C/program/encoder/argument.j2 | 16 + .../templates/C/program/encoder/result.j2 | 21 + .../xdrgen/templates/C/source_top/client.j2 | 8 + .../xdrgen/templates/C/source_top/server.j2 | 8 + .../templates/C/struct/declaration/close.j2 | 4 + .../templates/C/struct/decoder/basic.j2 | 6 + .../templates/C/struct/decoder/close.j2 | 3 + .../C/struct/decoder/fixed_length_array.j2 | 8 + .../C/struct/decoder/fixed_length_opaque.j2 | 6 + .../xdrgen/templates/C/struct/decoder/open.j2 | 12 + .../C/struct/decoder/optional_data.j2 | 6 + .../C/struct/decoder/variable_length_array.j2 | 13 + .../struct/decoder/variable_length_opaque.j2 | 6 + .../struct/decoder/variable_length_string.j2 | 6 + .../templates/C/struct/definition/basic.j2 | 5 + .../templates/C/struct/definition/close.j2 | 2 + .../C/struct/definition/fixed_length_array.j2 | 5 + .../struct/definition/fixed_length_opaque.j2 | 5 + .../templates/C/struct/definition/open.j2 | 6 + .../C/struct/definition/optional_data.j2 | 5 + .../definition/variable_length_array.j2 | 8 + .../definition/variable_length_opaque.j2 | 5 + .../definition/variable_length_string.j2 | 5 + .../templates/C/struct/encoder/basic.j2 | 10 + .../templates/C/struct/encoder/close.j2 | 3 + .../C/struct/encoder/fixed_length_array.j2 | 12 + .../C/struct/encoder/fixed_length_opaque.j2 | 6 + .../xdrgen/templates/C/struct/encoder/open.j2 | 12 + .../C/struct/encoder/optional_data.j2 | 6 + .../C/struct/encoder/variable_length_array.j2 | 15 + .../struct/encoder/variable_length_opaque.j2 | 8 + .../struct/encoder/variable_length_string.j2 | 8 + .../templates/C/typedef/declaration/basic.j2 | 8 + .../typedef/declaration/fixed_length_array.j2 | 4 + .../declaration/fixed_length_opaque.j2 | 4 + .../declaration/variable_length_array.j2 | 4 + .../declaration/variable_length_opaque.j2 | 4 + .../declaration/variable_length_string.j2 | 4 + .../templates/C/typedef/decoder/basic.j2 | 17 + .../C/typedef/decoder/fixed_length_array.j2 | 25 + .../C/typedef/decoder/fixed_length_opaque.j2 | 17 + .../typedef/decoder/variable_length_array.j2 | 26 + .../typedef/decoder/variable_length_opaque.j2 | 17 + .../typedef/decoder/variable_length_string.j2 | 17 + .../templates/C/typedef/definition/basic.j2 | 6 + .../typedef/definition/fixed_length_array.j2 | 6 + .../typedef/definition/fixed_length_opaque.j2 | 6 + .../definition/variable_length_array.j2 | 9 + .../definition/variable_length_opaque.j2 | 6 + .../definition/variable_length_string.j2 | 6 + .../templates/C/typedef/encoder/basic.j2 | 21 + .../C/typedef/encoder/fixed_length_array.j2 | 25 + .../C/typedef/encoder/fixed_length_opaque.j2 | 17 + .../typedef/encoder/variable_length_array.j2 | 30 ++ .../typedef/encoder/variable_length_opaque.j2 | 17 + .../typedef/encoder/variable_length_string.j2 | 17 + .../xdrgen/templates/C/union/decoder/basic.j2 | 6 + .../xdrgen/templates/C/union/decoder/break.j2 | 2 + .../templates/C/union/decoder/case_spec.j2 | 2 + .../xdrgen/templates/C/union/decoder/close.j2 | 4 + .../templates/C/union/decoder/default_spec.j2 | 2 + .../xdrgen/templates/C/union/decoder/open.j2 | 12 + .../C/union/decoder/optional_data.j2 | 6 + .../templates/C/union/decoder/switch_spec.j2 | 7 + .../C/union/decoder/variable_length_array.j2 | 13 + .../C/union/decoder/variable_length_opaque.j2 | 6 + .../C/union/decoder/variable_length_string.j2 | 6 + .../xdrgen/templates/C/union/decoder/void.j2 | 3 + .../templates/C/union/definition/case_spec.j2 | 2 + .../templates/C/union/definition/close.j2 | 8 + .../C/union/definition/default_spec.j2 | 2 + .../templates/C/union/definition/open.j2 | 6 + .../C/union/definition/switch_spec.j2 | 3 + .../xdrgen/templates/C/union/encoder/basic.j2 | 10 + .../xdrgen/templates/C/union/encoder/break.j2 | 2 + .../templates/C/union/encoder/case_spec.j2 | 2 + .../xdrgen/templates/C/union/encoder/close.j2 | 4 + .../templates/C/union/encoder/default_spec.j2 | 2 + .../xdrgen/templates/C/union/encoder/open.j2 | 12 + .../templates/C/union/encoder/switch_spec.j2 | 7 + .../xdrgen/templates/C/union/encoder/void.j2 | 3 + tools/net/sunrpc/xdrgen/tests/test.x | 36 ++ tools/net/sunrpc/xdrgen/xdr_ast.py | 510 ++++++++++++++++++ tools/net/sunrpc/xdrgen/xdr_parse.py | 36 ++ tools/net/sunrpc/xdrgen/xdrgen | 132 +++++ 153 files changed, 4196 insertions(+) create mode 100644 include/linux/sunrpc/xdrgen/_builtins.h create mode 100644 include/linux/sunrpc/xdrgen/_defs.h create mode 100644 tools/net/sunrpc/xdrgen/.gitignore create mode 100644 tools/net/sunrpc/xdrgen/README create mode 100644 tools/net/sunrpc/xdrgen/__init__.py create mode 100644 tools/net/sunrpc/xdrgen/generators/__init__.py create mode 100644 tools/net/sunrpc/xdrgen/generators/constant.py create mode 100644 tools/net/sunrpc/xdrgen/generators/enum.py create mode 100644 tools/net/sunrpc/xdrgen/generators/header_bottom.py create mode 100644 tools/net/sunrpc/xdrgen/generators/header_top.py create mode 100644 tools/net/sunrpc/xdrgen/generators/pointer.py create mode 100644 tools/net/sunrpc/xdrgen/generators/program.py create mode 100644 tools/net/sunrpc/xdrgen/generators/source_top.py create mode 100644 tools/net/sunrpc/xdrgen/generators/struct.py create mode 100644 tools/net/sunrpc/xdrgen/generators/typedef.py create mode 100644 tools/net/sunrpc/xdrgen/generators/union.py create mode 100644 tools/net/sunrpc/xdrgen/grammars/xdr.lark create mode 100644 tools/net/sunrpc/xdrgen/subcmds/__init__.py create mode 100644 tools/net/sunrpc/xdrgen/subcmds/declarations.py create mode 100644 tools/net/sunrpc/xdrgen/subcmds/definitions.py create mode 100644 tools/net/sunrpc/xdrgen/subcmds/lint.py create mode 100644 tools/net/sunrpc/xdrgen/subcmds/source.py create mode 100644 tools/net/sunrpc/xdrgen/templates/C/constants/definition.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/definition/enumerator.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/definition/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/header_bottom/declaration/header.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/header_bottom/definition/header.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/header_top/declaration/header.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/header_top/definition/header.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/declaration/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/definition/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/definition/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/definition/procedure.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/argument.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/source_top/server.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/definition/case_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/definition/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/definition/default_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/definition/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/definition/switch_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2 create mode 100644 tools/net/sunrpc/xdrgen/tests/test.x create mode 100644 tools/net/sunrpc/xdrgen/xdr_ast.py create mode 100644 tools/net/sunrpc/xdrgen/xdr_parse.py create mode 100755 tools/net/sunrpc/xdrgen/xdrgen diff --git a/include/linux/sunrpc/xdrgen/_builtins.h b/include/linux/sunrpc/xdrgen/_builtins.h new file mode 100644 index 000000000000..68746c59fc9a --- /dev/null +++ b/include/linux/sunrpc/xdrgen/_builtins.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * This header defines XDR data type primitives specified in + * Section 4 of RFC 4506, used by RPC programs implemented + * in the Linux kernel. + */ + +#ifndef _SUNRPC_XDRGEN__BUILTINS_H_ +#define _SUNRPC_XDRGEN__BUILTINS_H_ + +#include + +static inline bool +xdrgen_decode_void(struct xdr_stream *xdr) +{ + return true; +} + +static inline bool +xdrgen_encode_void(struct xdr_stream *xdr) +{ + return true; +} + +static inline bool +xdrgen_decode_bool(struct xdr_stream *xdr, bool *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *ptr = (*p != xdr_zero); + return true; +} + +static inline bool +xdrgen_encode_bool(struct xdr_stream *xdr, bool val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *p = val ? xdr_one : xdr_zero; + return true; +} + +static inline bool +xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *ptr = be32_to_cpup(p); + return true; +} + +static inline bool +xdrgen_encode_int(struct xdr_stream *xdr, s32 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *p = cpu_to_be32(val); + return true; +} + +static inline bool +xdrgen_decode_unsigned_int(struct xdr_stream *xdr, u32 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *ptr = be32_to_cpup(p); + return true; +} + +static inline bool +xdrgen_encode_unsigned_int(struct xdr_stream *xdr, u32 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *p = cpu_to_be32(val); + return true; +} + +static inline bool +xdrgen_decode_long(struct xdr_stream *xdr, s32 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *ptr = be32_to_cpup(p); + return true; +} + +static inline bool +xdrgen_encode_long(struct xdr_stream *xdr, s32 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *p = cpu_to_be32(val); + return true; +} + +static inline bool +xdrgen_decode_unsigned_long(struct xdr_stream *xdr, u32 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *ptr = be32_to_cpup(p); + return true; +} + +static inline bool +xdrgen_encode_unsigned_long(struct xdr_stream *xdr, u32 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT); + + if (unlikely(!p)) + return false; + *p = cpu_to_be32(val); + return true; +} + +static inline bool +xdrgen_decode_hyper(struct xdr_stream *xdr, s64 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2); + + if (unlikely(!p)) + return false; + *ptr = get_unaligned_be64(p); + return true; +} + +static inline bool +xdrgen_encode_hyper(struct xdr_stream *xdr, s64 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2); + + if (unlikely(!p)) + return false; + put_unaligned_be64(val, p); + return true; +} + +static inline bool +xdrgen_decode_unsigned_hyper(struct xdr_stream *xdr, u64 *ptr) +{ + __be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2); + + if (unlikely(!p)) + return false; + *ptr = get_unaligned_be64(p); + return true; +} + +static inline bool +xdrgen_encode_unsigned_hyper(struct xdr_stream *xdr, u64 val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2); + + if (unlikely(!p)) + return false; + put_unaligned_be64(val, p); + return true; +} + +static inline bool +xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen) +{ + __be32 *p; + u32 len; + + if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT)) + return false; + if (unlikely(maxlen && len > maxlen)) + return false; + if (len != 0) { + p = xdr_inline_decode(xdr, len); + if (unlikely(!p)) + return false; + ptr->data = (unsigned char *)p; + } + ptr->len = len; + return true; +} + +static inline bool +xdrgen_encode_string(struct xdr_stream *xdr, string val, u32 maxlen) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len)); + + if (unlikely(!p)) + return false; + xdr_encode_opaque(p, val.data, val.len); + return true; +} + +static inline bool +xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen) +{ + __be32 *p; + u32 len; + + if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT)) + return false; + if (unlikely(maxlen && len > maxlen)) + return false; + if (len != 0) { + p = xdr_inline_decode(xdr, len); + if (unlikely(!p)) + return false; + ptr->data = (u8 *)p; + } + ptr->len = len; + return true; +} + +static inline bool +xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val) +{ + __be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len)); + + if (unlikely(!p)) + return false; + xdr_encode_opaque(p, val.data, val.len); + return true; +} + +#endif /* _SUNRPC_XDRGEN__BUILTINS_H_ */ diff --git a/include/linux/sunrpc/xdrgen/_defs.h b/include/linux/sunrpc/xdrgen/_defs.h new file mode 100644 index 000000000000..be9e62371758 --- /dev/null +++ b/include/linux/sunrpc/xdrgen/_defs.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * This header defines XDR data type primitives specified in + * Section 4 of RFC 4506, used by RPC programs implemented + * in the Linux kernel. + */ + +#ifndef _SUNRPC_XDRGEN__DEFS_H_ +#define _SUNRPC_XDRGEN__DEFS_H_ + +#define TRUE (true) +#define FALSE (false) + +typedef struct { + u32 len; + unsigned char *data; +} string; + +typedef struct { + u32 len; + u8 *data; +} opaque; + +#endif /* _SUNRPC_XDRGEN__DEFS_H_ */ diff --git a/tools/net/sunrpc/xdrgen/.gitignore b/tools/net/sunrpc/xdrgen/.gitignore new file mode 100644 index 000000000000..d7366c2f9be8 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +generators/__pycache__ diff --git a/tools/net/sunrpc/xdrgen/README b/tools/net/sunrpc/xdrgen/README new file mode 100644 index 000000000000..92f7738ad50c --- /dev/null +++ b/tools/net/sunrpc/xdrgen/README @@ -0,0 +1,244 @@ +xdrgen - Linux Kernel XDR code generator + +Introduction +------------ + +SunRPC programs are typically specified using a language defined by +RFC 4506. In fact, all IETF-published NFS specifications provide a +description of the specified protocol using this language. + +Since the 1990's, user space consumers of SunRPC have had access to +a tool that could read such XDR specifications and then generate C +code that implements the RPC portions of that protocol. This tool is +called rpcgen. + +This RPC-level code is code that handles input directly from the +network, and thus a high degree of memory safety and sanity checking +is needed to help ensure proper levels of security. Bugs in this +code can have significant impact on security and performance. + +However, it is code that is repetitive and tedious to write by hand. + +The C code generated by rpcgen makes extensive use of the facilities +of the user space TI-RPC library and libc. Furthermore, the dialect +of the generated code is very traditional K&R C. + +The Linux kernel's implementation of SunRPC-based protocols hand-roll +their XDR implementation. There are two main reasons for this: + +1. libtirpc (and its predecessors) operate only in user space. The + kernel's RPC implementation and its API are significantly + different than libtirpc. + +2. rpcgen-generated code is believed to be less efficient than code + that is hand-written. + +These days, gcc and its kin are capable of optimizing code better +than human authors. There are only a few instances where writing +XDR code by hand will make a measurable performance different. + +In addition, the current hand-written code in the Linux kernel is +difficult to audit and prove that it implements exactly what is in +the protocol specification. + +In order to accrue the benefits of machine-generated XDR code in the +kernel, a tool is needed that will output C code that works against +the kernel's SunRPC implementation rather than libtirpc. + +Enter xdrgen. + + +Dependencies +------------ + +These dependencies are typically packaged by Linux distributions: + +- python3 +- python3-lark +- python3-jinja2 + +These dependencies are available via PyPi: + +- pip install 'lark[interegular]' + + +XDR Specifications +------------------ + +When adding a new protocol implementation to the kernel, the XDR +specification can be derived by feeding a .txt copy of the RFC to +the script located in tools/net/sunrpc/extract.sh. + + $ extract.sh < rfc0001.txt > new2.x + + +Operation +--------- + +Once a .x file is available, use xdrgen to generate source and +header files containing an implementation of XDR encoding and +decoding functions for the specified protocol. + + $ ./xdrgen definitions new2.x > include/linux/sunrpc/xdrgen/new2.h + $ ./xdrgen declarations new2.x > new2xdr_gen.h + +and + + $ ./xdrgen source new2.x > new2xdr_gen.c + +The files are ready to use for a server-side protocol implementation, +or may be used as a guide for implementing these routines by hand. + +By default, the only comments added to this code are kdoc comments +that appear directly in front of the public per-procedure APIs. For +deeper introspection, specifying the "--annotate" flag will insert +additional comments in the generated code to help readers match the +generated code to specific parts of the XDR specification. + +Because the generated code is targeted for the Linux kernel, it +is tagged with a GPLv2-only license. + +The xdrgen tool can also provide lexical and syntax checking of +an XDR specification: + + $ ./xdrgen lint xdr/new.x + + +How It Works +------------ + +xdrgen does not use machine learning to generate source code. The +translation is entirely deterministic. + +RFC 4506 Section 6 contains a BNF grammar of the XDR specification +language. The grammar has been adapted for use by the Python Lark +module. + +The xdr.ebnf file in this directory contains the grammar used to +parse XDR specifications. xdrgen configures Lark using the grammar +in xdr.ebnf. Lark parses the target XDR specification using this +grammar, creating a parse tree. + +xdrgen then transforms the parse tree into an abstract syntax tree. +This tree is passed to a series of code generators. + +The generators are implemented as Python classes residing in the +generators/ directory. Each generator emits code created from Jinja2 +templates stored in the templates/ directory. + +The source code is generated in the same order in which they appear +in the specification to ensure the generated code compiles. This +conforms with the behavior of rpcgen. + +xdrgen assumes that the generated source code is further compiled by +a compiler that can optimize in a number of ways, including: + + - Unused functions are discarded (ie, not added to the executable) + + - Aggressive function inlining removes unnecessary stack frames + + - Single-arm switch statements are replaced by a single conditional + branch + +And so on. + + +Pragmas +------- + +Pragma directives specify exceptions to the normal generation of +encoding and decoding functions. Currently one directive is +implemented: "public". + +Pragma exclude +------ ------- + + pragma exclude ; + +In some cases, a procedure encoder or decoder function might need +special processing that cannot be automatically generated. The +automatically-generated functions might conflict or interfere with +the hand-rolled function. To avoid editing the generated source code +by hand, a pragma can specify that the procedure's encoder and +decoder functions are not included in the generated header and +source. + +For example: + + pragma exclude NFSPROC3_READDIRPLUS; + +Excludes the decoder function for the READDIRPLUS argument and the +encoder function for the READDIRPLUS result. + +Note that because data item encoder and decoder functions are +defined "static __maybe_unused", subsequent compilation +automatically excludes data item encoder and decoder functions that +are used only by excluded procedure. + +Pragma header +------ ------ + + pragma header ; + +Provide a name to use for the header file. For example: + + pragma header nlm4; + +Adds + + #include "nlm4xdr_gen.h" + +to the generated source file. + +Pragma public +------ ------ + + pragma public ; + +Normally XDR encoder and decoder functions are "static". In case an +implementer wants to call these functions from other source code, +s/he can add a public pragma in the input .x file to indicate a set +of functions that should get a prototype in the generated header, +and the function definitions will not be declared static. + +For example: + + pragma public nfsstat3; + +Adds these prototypes in the generated header: + + bool xdrgen_decode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 *ptr); + bool xdrgen_encode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 value); + +And, in the generated source code, both of these functions appear +without the "static __maybe_unused" modifiers. + + +Future Work +----------- + +Finish implementing XDR pointer and list types. + +Generate client-side procedure functions + +Expand the README into a user guide similar to rpcgen(1) + +Add more pragma directives: + + * @pages -- use xdr_read/write_pages() for the specified opaque + field + * @skip -- do not decode, but rather skip, the specified argument + field + +Enable something like a #include to dynamically insert the content +of other specification files + +Properly support line-by-line pass-through via the "%" decorator + +Build a unit test suite for verifying translation of XDR language +into compilable code + +Add a command-line option to insert trace_printk call sites in the +generated source code, for improved (temporary) observability + +Generate kernel Rust code as well as C code diff --git a/tools/net/sunrpc/xdrgen/__init__.py b/tools/net/sunrpc/xdrgen/__init__.py new file mode 100644 index 000000000000..c940e9275252 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +# Just to make sphinx-apidoc document this directory diff --git a/tools/net/sunrpc/xdrgen/generators/__init__.py b/tools/net/sunrpc/xdrgen/generators/__init__.py new file mode 100644 index 000000000000..fd2457461274 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/__init__.py @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0 + +"""Define a base code generator class""" + +import sys +from jinja2 import Environment, FileSystemLoader, Template + +from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier +from xdr_ast import public_apis, pass_by_reference, get_header_name +from xdr_parse import get_xdr_annotate + + +def create_jinja2_environment(language: str, xdr_type: str) -> Environment: + """Open a set of templates based on output language""" + match language: + case "C": + environment = Environment( + loader=FileSystemLoader(sys.path[0] + "/templates/C/" + xdr_type + "/"), + trim_blocks=True, + lstrip_blocks=True, + ) + environment.globals["annotate"] = get_xdr_annotate() + environment.globals["public_apis"] = public_apis + environment.globals["pass_by_reference"] = pass_by_reference + return environment + case _: + raise NotImplementedError("Language not supported") + + +def get_jinja2_template( + environment: Environment, template_type: str, template_name: str +) -> Template: + """Retrieve a Jinja2 template for emitting source code""" + return environment.get_template(template_type + "/" + template_name + ".j2") + + +def find_xdr_program_name(root: Specification) -> str: + """Retrieve the RPC program name from an abstract syntax tree""" + raw_name = get_header_name() + if raw_name != "none": + return raw_name.lower() + for definition in root.definitions: + if isinstance(definition.value, _RpcProgram): + raw_name = definition.value.name + return raw_name.lower().removesuffix("_program").removesuffix("_prog") + return "noprog" + + +def header_guard_infix(filename: str) -> str: + """Extract the header guard infix from the specification filename""" + basename = filename.split("/")[-1] + program = basename.replace(".x", "") + return program.upper() + + +def kernel_c_type(spec: _XdrTypeSpecifier) -> str: + """Return name of C type""" + builtin_native_c_type = { + "bool": "bool", + "int": "s32", + "unsigned_int": "u32", + "long": "s32", + "unsigned_long": "u32", + "hyper": "s64", + "unsigned_hyper": "u64", + } + if spec.type_name in builtin_native_c_type: + return builtin_native_c_type[spec.type_name] + return spec.type_name + + +class Boilerplate: + """Base class to generate boilerplate for source files""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + raise NotImplementedError("No language support defined") + + def emit_declaration(self, filename: str, root: Specification) -> None: + """Emit declaration header boilerplate""" + raise NotImplementedError("Header boilerplate generation not supported") + + def emit_definition(self, filename: str, root: Specification) -> None: + """Emit definition header boilerplate""" + raise NotImplementedError("Header boilerplate generation not supported") + + def emit_source(self, filename: str, root: Specification) -> None: + """Emit generic source code for this XDR type""" + raise NotImplementedError("Source boilerplate generation not supported") + + +class SourceGenerator: + """Base class to generate header and source code for XDR types""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + raise NotImplementedError("No language support defined") + + def emit_declaration(self, node: _XdrAst) -> None: + """Emit one function declaration for this XDR type""" + raise NotImplementedError("Declaration generation not supported") + + def emit_decoder(self, node: _XdrAst) -> None: + """Emit one decoder function for this XDR type""" + raise NotImplementedError("Decoder generation not supported") + + def emit_definition(self, node: _XdrAst) -> None: + """Emit one definition for this XDR type""" + raise NotImplementedError("Definition generation not supported") + + def emit_encoder(self, node: _XdrAst) -> None: + """Emit one encoder function for this XDR type""" + raise NotImplementedError("Encoder generation not supported") diff --git a/tools/net/sunrpc/xdrgen/generators/constant.py b/tools/net/sunrpc/xdrgen/generators/constant.py new file mode 100644 index 000000000000..f2339caf0953 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/constant.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR constants""" + +from generators import SourceGenerator, create_jinja2_environment +from xdr_ast import _XdrConstant + +class XdrConstantGenerator(SourceGenerator): + """Generate source code for XDR constants""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "constants") + self.peer = peer + + def emit_definition(self, node: _XdrConstant) -> None: + """Emit one definition for a constant""" + template = self.environment.get_template("definition.j2") + print(template.render(name=node.name, value=node.value)) diff --git a/tools/net/sunrpc/xdrgen/generators/enum.py b/tools/net/sunrpc/xdrgen/generators/enum.py new file mode 100644 index 000000000000..855e43f4ae38 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/enum.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR enum types""" + +from generators import SourceGenerator, create_jinja2_environment +from xdr_ast import _XdrEnum, public_apis + + +class XdrEnumGenerator(SourceGenerator): + """Generate source code for XDR enum types""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "enum") + self.peer = peer + + def emit_declaration(self, node: _XdrEnum) -> None: + """Emit one declaration pair for an XDR enum type""" + if node.name in public_apis: + template = self.environment.get_template("declaration/close.j2") + print(template.render(name=node.name)) + + def emit_definition(self, node: _XdrEnum) -> None: + """Emit one definition for an XDR enum type""" + template = self.environment.get_template("definition/open.j2") + print(template.render(name=node.name)) + + template = self.environment.get_template("definition/enumerator.j2") + for enumerator in node.enumerators: + print(template.render(name=enumerator.name, value=enumerator.value)) + + template = self.environment.get_template("definition/close.j2") + print(template.render(name=node.name)) + + def emit_decoder(self, node: _XdrEnum) -> None: + """Emit one decoder function for an XDR enum type""" + template = self.environment.get_template("decoder/enum.j2") + print(template.render(name=node.name)) + + def emit_encoder(self, node: _XdrEnum) -> None: + """Emit one encoder function for an XDR enum type""" + template = self.environment.get_template("encoder/enum.j2") + print(template.render(name=node.name)) diff --git a/tools/net/sunrpc/xdrgen/generators/header_bottom.py b/tools/net/sunrpc/xdrgen/generators/header_bottom.py new file mode 100644 index 000000000000..4b55b282dfc0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/header_bottom.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate header bottom boilerplate""" + +import os.path +import time + +from generators import Boilerplate, header_guard_infix +from generators import create_jinja2_environment, get_jinja2_template +from xdr_ast import Specification + + +class XdrHeaderBottomGenerator(Boilerplate): + """Generate header boilerplate""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "header_bottom") + self.peer = peer + + def emit_declaration(self, filename: str, root: Specification) -> None: + """Emit the bottom header guard""" + template = get_jinja2_template(self.environment, "declaration", "header") + print(template.render(infix=header_guard_infix(filename))) + + def emit_definition(self, filename: str, root: Specification) -> None: + """Emit the bottom header guard""" + template = get_jinja2_template(self.environment, "definition", "header") + print(template.render(infix=header_guard_infix(filename))) + + def emit_source(self, filename: str, root: Specification) -> None: + pass diff --git a/tools/net/sunrpc/xdrgen/generators/header_top.py b/tools/net/sunrpc/xdrgen/generators/header_top.py new file mode 100644 index 000000000000..c6bc21c71f19 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/header_top.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate header top boilerplate""" + +import os.path +import time + +from generators import Boilerplate, header_guard_infix +from generators import create_jinja2_environment, get_jinja2_template +from xdr_ast import Specification + + +class XdrHeaderTopGenerator(Boilerplate): + """Generate header boilerplate""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "header_top") + self.peer = peer + + def emit_declaration(self, filename: str, root: Specification) -> None: + """Emit the top header guard""" + template = get_jinja2_template(self.environment, "declaration", "header") + print( + template.render( + infix=header_guard_infix(filename), + filename=filename, + mtime=time.ctime(os.path.getmtime(filename)), + ) + ) + + def emit_definition(self, filename: str, root: Specification) -> None: + """Emit the top header guard""" + template = get_jinja2_template(self.environment, "definition", "header") + print( + template.render( + infix=header_guard_infix(filename), + filename=filename, + mtime=time.ctime(os.path.getmtime(filename)), + ) + ) + + def emit_source(self, filename: str, root: Specification) -> None: + pass diff --git a/tools/net/sunrpc/xdrgen/generators/pointer.py b/tools/net/sunrpc/xdrgen/generators/pointer.py new file mode 100644 index 000000000000..b0b27f1819c8 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/pointer.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR pointer types""" + +from jinja2 import Environment + +from generators import SourceGenerator, kernel_c_type +from generators import create_jinja2_environment, get_jinja2_template + +from xdr_ast import _XdrBasic, _XdrVariableLengthString +from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque +from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray +from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration +from xdr_ast import public_apis + + +def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None: + """Emit a declaration pair for an XDR pointer type""" + if node.name in public_apis: + template = get_jinja2_template(environment, "declaration", "close") + print(template.render(name=node.name)) + + +def emit_pointer_member_definition( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit a definition for one field in an XDR struct""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "definition", field.template) + print(template.render(name=field.name)) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "definition", field.template) + print(template.render(name=field.name)) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + + +def emit_pointer_definition(environment: Environment, node: _XdrPointer) -> None: + """Emit a definition for an XDR pointer type""" + template = get_jinja2_template(environment, "definition", "open") + print(template.render(name=node.name)) + + for field in node.fields[0:-1]: + emit_pointer_member_definition(environment, field) + + template = get_jinja2_template(environment, "definition", "close") + print(template.render(name=node.name)) + + +def emit_pointer_member_decoder( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit a decoder for one field in an XDR pointer""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + size=field.size, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + maxsize=field.maxsize, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + + +def emit_pointer_decoder(environment: Environment, node: _XdrPointer) -> None: + """Emit one decoder function for an XDR pointer type""" + template = get_jinja2_template(environment, "decoder", "open") + print(template.render(name=node.name)) + + for field in node.fields[0:-1]: + emit_pointer_member_decoder(environment, field) + + template = get_jinja2_template(environment, "decoder", "close") + print(template.render()) + + +def emit_pointer_member_encoder( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit an encoder for one field in a XDR pointer""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + + +def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None: + """Emit one encoder function for an XDR pointer type""" + template = get_jinja2_template(environment, "encoder", "open") + print(template.render(name=node.name)) + + for field in node.fields[0:-1]: + emit_pointer_member_encoder(environment, field) + + template = get_jinja2_template(environment, "encoder", "close") + print(template.render()) + + +class XdrPointerGenerator(SourceGenerator): + """Generate source code for XDR pointer""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "pointer") + self.peer = peer + + def emit_declaration(self, node: _XdrPointer) -> None: + """Emit one declaration pair for an XDR pointer type""" + emit_pointer_declaration(self.environment, node) + + def emit_definition(self, node: _XdrPointer) -> None: + """Emit one declaration for an XDR pointer type""" + emit_pointer_definition(self.environment, node) + + def emit_decoder(self, node: _XdrPointer) -> None: + """Emit one decoder function for an XDR pointer type""" + emit_pointer_decoder(self.environment, node) + + def emit_encoder(self, node: _XdrPointer) -> None: + """Emit one encoder function for an XDR pointer type""" + emit_pointer_encoder(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/generators/program.py b/tools/net/sunrpc/xdrgen/generators/program.py new file mode 100644 index 000000000000..83b0ecbae86f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/program.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code for an RPC program's procedures""" + +from jinja2 import Environment + +from generators import SourceGenerator, create_jinja2_environment +from xdr_ast import _RpcProgram, _RpcVersion, excluded_apis + + +def emit_version_definitions( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit procedure numbers for each RPC version's procedures""" + template = environment.get_template("definition/open.j2") + print(template.render(program=program.upper())) + + template = environment.get_template("definition/procedure.j2") + for procedure in version.procedures: + if procedure.name not in excluded_apis: + print( + template.render( + name=procedure.name, + value=procedure.number, + ) + ) + + template = environment.get_template("definition/close.j2") + print(template.render()) + + +def emit_version_declarations( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit declarations for each RPC version's procedures""" + arguments = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + arguments.add(procedure.argument.type_name) + if len(arguments) > 0: + print("") + template = environment.get_template("declaration/argument.j2") + for argument in arguments: + print(template.render(program=program, argument=argument)) + + results = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + results.add(procedure.result.type_name) + if len(results) > 0: + print("") + template = environment.get_template("declaration/result.j2") + for result in results: + print(template.render(program=program, result=result)) + + +def emit_version_argument_decoders( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit server argument decoders for each RPC version's procedures""" + arguments = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + arguments.add(procedure.argument.type_name) + + template = environment.get_template("decoder/argument.j2") + for argument in arguments: + print(template.render(program=program, argument=argument)) + + +def emit_version_result_decoders( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit client result decoders for each RPC version's procedures""" + results = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + results.add(procedure.result.type_name) + + template = environment.get_template("decoder/result.j2") + for result in results: + print(template.render(program=program, result=result)) + + +def emit_version_argument_encoders( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit client argument encoders for each RPC version's procedures""" + arguments = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + arguments.add(procedure.argument.type_name) + + template = environment.get_template("encoder/argument.j2") + for argument in arguments: + print(template.render(program=program, argument=argument)) + + +def emit_version_result_encoders( + environment: Environment, program: str, version: _RpcVersion +) -> None: + """Emit server result encoders for each RPC version's procedures""" + results = set() + for procedure in version.procedures: + if procedure.name not in excluded_apis: + results.add(procedure.result.type_name) + + template = environment.get_template("encoder/result.j2") + for result in results: + print(template.render(program=program, result=result)) + + +class XdrProgramGenerator(SourceGenerator): + """Generate source code for an RPC program's procedures""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "program") + self.peer = peer + + def emit_definition(self, node: _RpcProgram) -> None: + """Emit procedure numbers for each of an RPC programs's procedures""" + raw_name = node.name + program = raw_name.lower().removesuffix("_program").removesuffix("_prog") + + for version in node.versions: + emit_version_definitions(self.environment, program, version) + + def emit_declaration(self, node: _RpcProgram) -> None: + """Emit a declaration pair for each of an RPC programs's procedures""" + raw_name = node.name + program = raw_name.lower().removesuffix("_program").removesuffix("_prog") + + for version in node.versions: + emit_version_declarations(self.environment, program, version) + + def emit_decoder(self, node: _RpcProgram) -> None: + """Emit all decoder functions for an RPC program's procedures""" + raw_name = node.name + program = raw_name.lower().removesuffix("_program").removesuffix("_prog") + match self.peer: + case "server": + for version in node.versions: + emit_version_argument_decoders( + self.environment, program, version, + ) + case "client": + for version in node.versions: + emit_version_result_decoders( + self.environment, program, version, + ) + + def emit_encoder(self, node: _RpcProgram) -> None: + """Emit all encoder functions for an RPC program's procedures""" + raw_name = node.name + program = raw_name.lower().removesuffix("_program").removesuffix("_prog") + match self.peer: + case "server": + for version in node.versions: + emit_version_result_encoders( + self.environment, program, version, + ) + case "client": + for version in node.versions: + emit_version_argument_encoders( + self.environment, program, version, + ) diff --git a/tools/net/sunrpc/xdrgen/generators/source_top.py b/tools/net/sunrpc/xdrgen/generators/source_top.py new file mode 100644 index 000000000000..bcf47d93d6f1 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/source_top.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate source code boilerplate""" + +import os.path +import time + +from generators import Boilerplate +from generators import find_xdr_program_name, create_jinja2_environment +from xdr_ast import _RpcProgram, Specification, get_header_name + + +class XdrSourceTopGenerator(Boilerplate): + """Generate source code boilerplate""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "source_top") + self.peer = peer + + def emit_source(self, filename: str, root: Specification) -> None: + """Emit the top source boilerplate""" + name = find_xdr_program_name(root) + template = self.environment.get_template(self.peer + ".j2") + print( + template.render( + program=name, + filename=filename, + mtime=time.ctime(os.path.getmtime(filename)), + ) + ) diff --git a/tools/net/sunrpc/xdrgen/generators/struct.py b/tools/net/sunrpc/xdrgen/generators/struct.py new file mode 100644 index 000000000000..b694cd470829 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/struct.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR struct types""" + +from jinja2 import Environment + +from generators import SourceGenerator, kernel_c_type +from generators import create_jinja2_environment, get_jinja2_template + +from xdr_ast import _XdrBasic, _XdrVariableLengthString +from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque +from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray +from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration +from xdr_ast import public_apis + + +def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None: + """Emit one declaration pair for an XDR struct type""" + if node.name in public_apis: + template = get_jinja2_template(environment, "declaration", "close") + print(template.render(name=node.name)) + + +def emit_struct_member_definition( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit a definition for one field in an XDR struct""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "definition", field.template) + print(template.render(name=field.name)) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "definition", field.template) + print(template.render(name=field.name)) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "definition", field.template) + print( + template.render( + name=field.name, + type=kernel_c_type(field.spec), + classifier=field.spec.c_classifier, + ) + ) + + +def emit_struct_definition(environment: Environment, node: _XdrStruct) -> None: + """Emit one definition for an XDR struct type""" + template = get_jinja2_template(environment, "definition", "open") + print(template.render(name=node.name)) + + for field in node.fields: + emit_struct_member_definition(environment, field) + + template = get_jinja2_template(environment, "definition", "close") + print(template.render(name=node.name)) + + +def emit_struct_member_decoder( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit a decoder for one field in an XDR struct""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + size=field.size, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + maxsize=field.maxsize, + classifier=field.spec.c_classifier, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "decoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + + +def emit_struct_decoder(environment: Environment, node: _XdrStruct) -> None: + """Emit one decoder function for an XDR struct type""" + template = get_jinja2_template(environment, "decoder", "open") + print(template.render(name=node.name)) + + for field in node.fields: + emit_struct_member_decoder(environment, field) + + template = get_jinja2_template(environment, "decoder", "close") + print(template.render()) + + +def emit_struct_member_encoder( + environment: Environment, field: _XdrDeclaration +) -> None: + """Emit an encoder for one field in an XDR struct""" + if isinstance(field, _XdrBasic): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + ) + ) + elif isinstance(field, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrVariableLengthString): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + size=field.size, + ) + ) + elif isinstance(field, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + maxsize=field.maxsize, + ) + ) + elif isinstance(field, _XdrOptionalData): + template = get_jinja2_template(environment, "encoder", field.template) + print( + template.render( + name=field.name, + type=field.spec.type_name, + classifier=field.spec.c_classifier, + ) + ) + + +def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None: + """Emit one encoder function for an XDR struct type""" + template = get_jinja2_template(environment, "encoder", "open") + print(template.render(name=node.name)) + + for field in node.fields: + emit_struct_member_encoder(environment, field) + + template = get_jinja2_template(environment, "encoder", "close") + print(template.render()) + + +class XdrStructGenerator(SourceGenerator): + """Generate source code for XDR structs""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "struct") + self.peer = peer + + def emit_declaration(self, node: _XdrStruct) -> None: + """Emit one declaration pair for an XDR struct type""" + emit_struct_declaration(self.environment, node) + + def emit_definition(self, node: _XdrStruct) -> None: + """Emit one definition for an XDR struct type""" + emit_struct_definition(self.environment, node) + + def emit_decoder(self, node: _XdrStruct) -> None: + """Emit one decoder function for an XDR struct type""" + emit_struct_decoder(self.environment, node) + + def emit_encoder(self, node: _XdrStruct) -> None: + """Emit one encoder function for an XDR struct type""" + emit_struct_encoder(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/generators/typedef.py b/tools/net/sunrpc/xdrgen/generators/typedef.py new file mode 100644 index 000000000000..85a1b2303333 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/typedef.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR typedefs""" + +from jinja2 import Environment + +from generators import SourceGenerator, kernel_c_type +from generators import create_jinja2_environment, get_jinja2_template + +from xdr_ast import _XdrBasic, _XdrTypedef, _XdrVariableLengthString +from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque +from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray +from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration +from xdr_ast import public_apis + + +def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None: + """Emit a declaration pair for one XDR typedef""" + if node.name not in public_apis: + return + if isinstance(node, _XdrBasic): + template = get_jinja2_template(environment, "declaration", node.template) + print( + template.render( + name=node.name, + type=kernel_c_type(node.spec), + classifier=node.spec.c_classifier, + ) + ) + elif isinstance(node, _XdrVariableLengthString): + template = get_jinja2_template(environment, "declaration", node.template) + print(template.render(name=node.name)) + elif isinstance(node, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "declaration", node.template) + print(template.render(name=node.name, size=node.size)) + elif isinstance(node, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "declaration", node.template) + print(template.render(name=node.name)) + elif isinstance(node, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "declaration", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + size=node.size, + ) + ) + elif isinstance(node, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "declaration", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + classifier=node.spec.c_classifier, + ) + ) + elif isinstance(node, _XdrOptionalData): + raise NotImplementedError(" typedef not yet implemented") + elif isinstance(node, _XdrVoid): + raise NotImplementedError(" typedef not yet implemented") + else: + raise NotImplementedError("typedef: type not recognized") + + +def emit_type_definition(environment: Environment, node: _XdrDeclaration) -> None: + """Emit a definition for one XDR typedef""" + if isinstance(node, _XdrBasic): + template = get_jinja2_template(environment, "definition", node.template) + print( + template.render( + name=node.name, + type=kernel_c_type(node.spec), + classifier=node.spec.c_classifier, + ) + ) + elif isinstance(node, _XdrVariableLengthString): + template = get_jinja2_template(environment, "definition", node.template) + print(template.render(name=node.name)) + elif isinstance(node, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "definition", node.template) + print(template.render(name=node.name, size=node.size)) + elif isinstance(node, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "definition", node.template) + print(template.render(name=node.name)) + elif isinstance(node, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "definition", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + size=node.size, + ) + ) + elif isinstance(node, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "definition", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + classifier=node.spec.c_classifier, + ) + ) + elif isinstance(node, _XdrOptionalData): + raise NotImplementedError(" typedef not yet implemented") + elif isinstance(node, _XdrVoid): + raise NotImplementedError(" typedef not yet implemented") + else: + raise NotImplementedError("typedef: type not recognized") + + +def emit_typedef_decoder(environment: Environment, node: _XdrDeclaration) -> None: + """Emit a decoder function for one XDR typedef""" + if isinstance(node, _XdrBasic): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + ) + ) + elif isinstance(node, _XdrVariableLengthString): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + size=node.size, + ) + ) + elif isinstance(node, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + size=node.size, + classifier=node.spec.c_classifier, + ) + ) + elif isinstance(node, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "decoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrOptionalData): + raise NotImplementedError(" typedef not yet implemented") + elif isinstance(node, _XdrVoid): + raise NotImplementedError(" typedef not yet implemented") + else: + raise NotImplementedError("typedef: type not recognized") + + +def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> None: + """Emit an encoder function for one XDR typedef""" + if isinstance(node, _XdrBasic): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + ) + ) + elif isinstance(node, _XdrVariableLengthString): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrFixedLengthOpaque): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + size=node.size, + ) + ) + elif isinstance(node, _XdrVariableLengthOpaque): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrFixedLengthArray): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + size=node.size, + ) + ) + elif isinstance(node, _XdrVariableLengthArray): + template = get_jinja2_template(environment, "encoder", node.template) + print( + template.render( + name=node.name, + type=node.spec.type_name, + maxsize=node.maxsize, + ) + ) + elif isinstance(node, _XdrOptionalData): + raise NotImplementedError(" typedef not yet implemented") + elif isinstance(node, _XdrVoid): + raise NotImplementedError(" typedef not yet implemented") + else: + raise NotImplementedError("typedef: type not recognized") + + +class XdrTypedefGenerator(SourceGenerator): + """Generate source code for XDR typedefs""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "typedef") + self.peer = peer + + def emit_declaration(self, node: _XdrTypedef) -> None: + """Emit one declaration pair for an XDR enum type""" + emit_typedef_declaration(self.environment, node.declaration) + + def emit_definition(self, node: _XdrTypedef) -> None: + """Emit one definition for an XDR typedef""" + emit_type_definition(self.environment, node.declaration) + + def emit_decoder(self, node: _XdrTypedef) -> None: + """Emit one decoder function for an XDR typedef""" + emit_typedef_decoder(self.environment, node.declaration) + + def emit_encoder(self, node: _XdrTypedef) -> None: + """Emit one encoder function for an XDR typedef""" + emit_typedef_encoder(self.environment, node.declaration) diff --git a/tools/net/sunrpc/xdrgen/generators/union.py b/tools/net/sunrpc/xdrgen/generators/union.py new file mode 100644 index 000000000000..7974967bbb9f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/generators/union.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Generate code to handle XDR unions""" + +from jinja2 import Environment + +from generators import SourceGenerator +from generators import create_jinja2_environment, get_jinja2_template + +from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid +from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis + + +def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None: + """Emit one declaration pair for an XDR union type""" + if node.name in public_apis: + template = get_jinja2_template(environment, "declaration", "close") + print(template.render(name=node.name)) + + +def emit_union_switch_spec_definition( + environment: Environment, node: _XdrDeclaration +) -> None: + """Emit a definition for an XDR union's discriminant""" + assert isinstance(node, _XdrBasic) + template = get_jinja2_template(environment, "definition", "switch_spec") + print( + template.render( + name=node.name, + type=node.spec.type_name, + classifier=node.spec.c_classifier, + ) + ) + + +def emit_union_case_spec_definition( + environment: Environment, node: _XdrDeclaration +) -> None: + """Emit a definition for an XDR union's case arm""" + if isinstance(node.arm, _XdrVoid): + return + assert isinstance(node.arm, _XdrBasic) + template = get_jinja2_template(environment, "definition", "case_spec") + print( + template.render( + name=node.arm.name, + type=node.arm.spec.type_name, + classifier=node.arm.spec.c_classifier, + ) + ) + + +def emit_union_definition(environment: Environment, node: _XdrUnion) -> None: + """Emit one XDR union definition""" + template = get_jinja2_template(environment, "definition", "open") + print(template.render(name=node.name)) + + emit_union_switch_spec_definition(environment, node.discriminant) + + for case in node.cases: + emit_union_case_spec_definition(environment, case) + + if node.default is not None: + emit_union_case_spec_definition(environment, node.default) + + template = get_jinja2_template(environment, "definition", "close") + print(template.render(name=node.name)) + + +def emit_union_switch_spec_decoder( + environment: Environment, node: _XdrDeclaration +) -> None: + """Emit a decoder for an XDR union's discriminant""" + assert isinstance(node, _XdrBasic) + template = get_jinja2_template(environment, "decoder", "switch_spec") + print(template.render(name=node.name, type=node.spec.type_name)) + + +def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None: + """Emit decoder functions for an XDR union's case arm""" + + if isinstance(node.arm, _XdrVoid): + return + + template = get_jinja2_template(environment, "decoder", "case_spec") + for case in node.values: + print(template.render(case=case)) + + assert isinstance(node.arm, _XdrBasic) + template = get_jinja2_template(environment, "decoder", node.arm.template) + print( + template.render( + name=node.arm.name, + type=node.arm.spec.type_name, + classifier=node.arm.spec.c_classifier, + ) + ) + + template = get_jinja2_template(environment, "decoder", "break") + print(template.render()) + + +def emit_union_default_spec_decoder(environment: Environment, node: _XdrUnion) -> None: + """Emit a decoder function for an XDR union's default arm""" + default_case = node.default + + # Avoid a gcc warning about a default case with boolean discriminant + if default_case is None and node.discriminant.spec.type_name == "bool": + return + + template = get_jinja2_template(environment, "decoder", "default_spec") + print(template.render()) + + if default_case is None or isinstance(default_case.arm, _XdrVoid): + template = get_jinja2_template(environment, "decoder", "break") + print(template.render()) + return + + assert isinstance(default_case.arm, _XdrBasic) + template = get_jinja2_template(environment, "decoder", default_case.arm.template) + print( + template.render( + name=default_case.arm.name, + type=default_case.arm.spec.type_name, + classifier=default_case.arm.spec.c_classifier, + ) + ) + + +def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None: + """Emit one XDR union decoder""" + template = get_jinja2_template(environment, "decoder", "open") + print(template.render(name=node.name)) + + emit_union_switch_spec_decoder(environment, node.discriminant) + + for case in node.cases: + emit_union_case_spec_decoder(environment, case) + + emit_union_default_spec_decoder(environment, node) + + template = get_jinja2_template(environment, "decoder", "close") + print(template.render()) + + +def emit_union_switch_spec_encoder( + environment: Environment, node: _XdrDeclaration +) -> None: + """Emit an encoder for an XDR union's discriminant""" + assert isinstance(node, _XdrBasic) + template = get_jinja2_template(environment, "encoder", "switch_spec") + print(template.render(name=node.name, type=node.spec.type_name)) + + +def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None: + """Emit encoder functions for an XDR union's case arm""" + + if isinstance(node.arm, _XdrVoid): + return + + template = get_jinja2_template(environment, "encoder", "case_spec") + for case in node.values: + print(template.render(case=case)) + + assert isinstance(node.arm, _XdrBasic) + template = get_jinja2_template(environment, "encoder", node.arm.template) + print( + template.render( + name=node.arm.name, + type=node.arm.spec.type_name, + ) + ) + + template = get_jinja2_template(environment, "encoder", "break") + print(template.render()) + + +def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None: + """Emit an encoder function for an XDR union's default arm""" + default_case = node.default + + # Avoid a gcc warning about a default case with boolean discriminant + if default_case is None and node.discriminant.spec.type_name == "bool": + return + + template = get_jinja2_template(environment, "encoder", "default_spec") + print(template.render()) + + if default_case is None or isinstance(default_case.arm, _XdrVoid): + template = get_jinja2_template(environment, "encoder", "break") + print(template.render()) + return + + assert isinstance(default_case.arm, _XdrBasic) + template = get_jinja2_template(environment, "encoder", default_case.arm.template) + print( + template.render( + name=default_case.arm.name, + type=default_case.arm.spec.type_name, + ) + ) + + +def emit_union_encoder(environment, node: _XdrUnion) -> None: + """Emit one XDR union encoder""" + template = get_jinja2_template(environment, "encoder", "open") + print(template.render(name=node.name)) + + emit_union_switch_spec_encoder(environment, node.discriminant) + + for case in node.cases: + emit_union_case_spec_encoder(environment, case) + + emit_union_default_spec_encoder(environment, node) + + template = get_jinja2_template(environment, "encoder", "close") + print(template.render()) + + +class XdrUnionGenerator(SourceGenerator): + """Generate source code for XDR unions""" + + def __init__(self, language: str, peer: str): + """Initialize an instance of this class""" + self.environment = create_jinja2_environment(language, "union") + self.peer = peer + + def emit_declaration(self, node: _XdrUnion) -> None: + """Emit one declaration pair for an XDR union""" + emit_union_declaration(self.environment, node) + + def emit_definition(self, node: _XdrUnion) -> None: + """Emit one definition for an XDR union""" + emit_union_definition(self.environment, node) + + def emit_decoder(self, node: _XdrUnion) -> None: + """Emit one decoder function for an XDR union""" + emit_union_decoder(self.environment, node) + + def emit_encoder(self, node: _XdrUnion) -> None: + """Emit one encoder function for an XDR union""" + emit_union_encoder(self.environment, node) diff --git a/tools/net/sunrpc/xdrgen/grammars/xdr.lark b/tools/net/sunrpc/xdrgen/grammars/xdr.lark new file mode 100644 index 000000000000..f3c4552e548d --- /dev/null +++ b/tools/net/sunrpc/xdrgen/grammars/xdr.lark @@ -0,0 +1,119 @@ +// A Lark grammar for the XDR specification language based on +// https://tools.ietf.org/html/rfc4506 Section 6.3 + +declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque + | "opaque" identifier "<" [ value ] ">" -> variable_length_opaque + | "string" identifier "<" [ value ] ">" -> variable_length_string + | type_specifier identifier "[" value "]" -> fixed_length_array + | type_specifier identifier "<" [ value ] ">" -> variable_length_array + | type_specifier "*" identifier -> optional_data + | type_specifier identifier -> basic + | "void" -> void + +value : decimal_constant + | hexadecimal_constant + | octal_constant + | identifier + +constant : decimal_constant | hexadecimal_constant | octal_constant + +type_specifier : unsigned_hyper + | unsigned_long + | unsigned_int + | hyper + | long + | int + | float + | double + | quadruple + | bool + | enum_type_spec + | struct_type_spec + | union_type_spec + | identifier + +unsigned_hyper : "unsigned" "hyper" +unsigned_long : "unsigned" "long" +unsigned_int : "unsigned" "int" +hyper : "hyper" +long : "long" +int : "int" +float : "float" +double : "double" +quadruple : "quadruple" +bool : "bool" + +enum_type_spec : "enum" enum_body + +enum_body : "{" ( identifier "=" value ) ( "," identifier "=" value )* "}" + +struct_type_spec : "struct" struct_body + +struct_body : "{" ( declaration ";" )+ "}" + +union_type_spec : "union" union_body + +union_body : switch_spec "{" case_spec+ [ default_spec ] "}" + +switch_spec : "switch" "(" declaration ")" + +case_spec : ( "case" value ":" )+ declaration ";" + +default_spec : "default" ":" declaration ";" + +constant_def : "const" identifier "=" value ";" + +type_def : "typedef" declaration ";" -> typedef + | "enum" identifier enum_body ";" -> enum + | "struct" identifier struct_body ";" -> struct + | "union" identifier union_body ";" -> union + +specification : definition* + +definition : constant_def + | type_def + | program_def + | pragma_def + +// +// RPC program definitions not specified in RFC 4506 +// + +program_def : "program" identifier "{" version_def+ "}" "=" constant ";" + +version_def : "version" identifier "{" procedure_def+ "}" "=" constant ";" + +procedure_def : type_specifier identifier "(" type_specifier ")" "=" constant ";" + +pragma_def : "pragma" directive identifier [ identifier ] ";" + +directive : exclude_directive + | header_directive + | pages_directive + | public_directive + | skip_directive + +exclude_directive : "exclude" +header_directive : "header" +pages_directive : "pages" +public_directive : "public" +skip_directive : "skip" + +// +// XDR language primitives +// + +identifier : /([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*/ + +decimal_constant : /[\+-]?(0|[1-9][0-9]*)/ +hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/ +octal_constant : /0[0-7]+/ + +PASSTHRU : "%" | "%" /.+/ +%ignore PASSTHRU + +%import common.C_COMMENT +%ignore C_COMMENT + +%import common.WS +%ignore WS diff --git a/tools/net/sunrpc/xdrgen/subcmds/__init__.py b/tools/net/sunrpc/xdrgen/subcmds/__init__.py new file mode 100644 index 000000000000..c940e9275252 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/subcmds/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +# Just to make sphinx-apidoc document this directory diff --git a/tools/net/sunrpc/xdrgen/subcmds/declarations.py b/tools/net/sunrpc/xdrgen/subcmds/declarations.py new file mode 100644 index 000000000000..c5e8d79986ef --- /dev/null +++ b/tools/net/sunrpc/xdrgen/subcmds/declarations.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Translate an XDR specification into executable code that +can be compiled for the Linux kernel.""" + +import logging + +from argparse import Namespace +from lark import logger +from lark.exceptions import UnexpectedInput + +from generators.constant import XdrConstantGenerator +from generators.enum import XdrEnumGenerator +from generators.header_bottom import XdrHeaderBottomGenerator +from generators.header_top import XdrHeaderTopGenerator +from generators.pointer import XdrPointerGenerator +from generators.program import XdrProgramGenerator +from generators.typedef import XdrTypedefGenerator +from generators.struct import XdrStructGenerator +from generators.union import XdrUnionGenerator + +from xdr_ast import transform_parse_tree, _RpcProgram, Specification +from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer +from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion +from xdr_parse import xdr_parser, set_xdr_annotate + +logger.setLevel(logging.INFO) + + +def emit_header_declarations( + root: Specification, language: str, peer: str +) -> None: + """Emit header declarations""" + for definition in root.definitions: + if isinstance(definition.value, _XdrEnum): + gen = XdrEnumGenerator(language, peer) + elif isinstance(definition.value, _XdrPointer): + gen = XdrPointerGenerator(language, peer) + elif isinstance(definition.value, _XdrTypedef): + gen = XdrTypedefGenerator(language, peer) + elif isinstance(definition.value, _XdrStruct): + gen = XdrStructGenerator(language, peer) + elif isinstance(definition.value, _XdrUnion): + gen = XdrUnionGenerator(language, peer) + elif isinstance(definition.value, _RpcProgram): + gen = XdrProgramGenerator(language, peer) + else: + continue + gen.emit_declaration(definition.value) + + +def handle_parse_error(e: UnexpectedInput) -> bool: + """Simple parse error reporting, no recovery attempted""" + print(e) + return True + + +def subcmd(args: Namespace) -> int: + """Generate definitions and declarations""" + + set_xdr_annotate(args.annotate) + parser = xdr_parser() + with open(args.filename, encoding="utf-8") as f: + parse_tree = parser.parse(f.read(), on_error=handle_parse_error) + ast = transform_parse_tree(parse_tree) + + gen = XdrHeaderTopGenerator(args.language, args.peer) + gen.emit_declaration(args.filename, ast) + + emit_header_declarations(ast, args.language, args.peer) + + gen = XdrHeaderBottomGenerator(args.language, args.peer) + gen.emit_declaration(args.filename, ast) + + return 0 diff --git a/tools/net/sunrpc/xdrgen/subcmds/definitions.py b/tools/net/sunrpc/xdrgen/subcmds/definitions.py new file mode 100644 index 000000000000..5cd13d53221f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/subcmds/definitions.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Translate an XDR specification into executable code that +can be compiled for the Linux kernel.""" + +import logging + +from argparse import Namespace +from lark import logger +from lark.exceptions import UnexpectedInput + +from generators.constant import XdrConstantGenerator +from generators.enum import XdrEnumGenerator +from generators.header_bottom import XdrHeaderBottomGenerator +from generators.header_top import XdrHeaderTopGenerator +from generators.pointer import XdrPointerGenerator +from generators.program import XdrProgramGenerator +from generators.typedef import XdrTypedefGenerator +from generators.struct import XdrStructGenerator +from generators.union import XdrUnionGenerator + +from xdr_ast import transform_parse_tree, Specification +from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer +from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion +from xdr_parse import xdr_parser, set_xdr_annotate + +logger.setLevel(logging.INFO) + + +def emit_header_definitions( + root: Specification, language: str, peer: str +) -> None: + """Emit header definitions""" + for definition in root.definitions: + if isinstance(definition.value, _XdrConstant): + gen = XdrConstantGenerator(language, peer) + elif isinstance(definition.value, _XdrEnum): + gen = XdrEnumGenerator(language, peer) + elif isinstance(definition.value, _XdrPointer): + gen = XdrPointerGenerator(language, peer) + elif isinstance(definition.value, _RpcProgram): + gen = XdrProgramGenerator(language, peer) + elif isinstance(definition.value, _XdrTypedef): + gen = XdrTypedefGenerator(language, peer) + elif isinstance(definition.value, _XdrStruct): + gen = XdrStructGenerator(language, peer) + elif isinstance(definition.value, _XdrUnion): + gen = XdrUnionGenerator(language, peer) + else: + continue + gen.emit_definition(definition.value) + + +def handle_parse_error(e: UnexpectedInput) -> bool: + """Simple parse error reporting, no recovery attempted""" + print(e) + return True + + +def subcmd(args: Namespace) -> int: + """Generate definitions""" + + set_xdr_annotate(args.annotate) + parser = xdr_parser() + with open(args.filename, encoding="utf-8") as f: + parse_tree = parser.parse(f.read(), on_error=handle_parse_error) + ast = transform_parse_tree(parse_tree) + + gen = XdrHeaderTopGenerator(args.language, args.peer) + gen.emit_definition(args.filename, ast) + + emit_header_definitions(ast, args.language, args.peer) + + gen = XdrHeaderBottomGenerator(args.language, args.peer) + gen.emit_definition(args.filename, ast) + + return 0 diff --git a/tools/net/sunrpc/xdrgen/subcmds/lint.py b/tools/net/sunrpc/xdrgen/subcmds/lint.py new file mode 100644 index 000000000000..36cc43717d30 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/subcmds/lint.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Translate an XDR specification into executable code that +can be compiled for the Linux kernel.""" + +import logging + +from argparse import Namespace +from lark import logger +from lark.exceptions import UnexpectedInput + +from xdr_parse import xdr_parser +from xdr_ast import transform_parse_tree + +logger.setLevel(logging.DEBUG) + + +def handle_parse_error(e: UnexpectedInput) -> bool: + """Simple parse error reporting, no recovery attempted""" + print(e) + return True + + +def subcmd(args: Namespace) -> int: + """Lexical and syntax check of an XDR specification""" + + parser = xdr_parser() + with open(args.filename, encoding="utf-8") as f: + parse_tree = parser.parse(f.read(), on_error=handle_parse_error) + transform_parse_tree(parse_tree) + + return 0 diff --git a/tools/net/sunrpc/xdrgen/subcmds/source.py b/tools/net/sunrpc/xdrgen/subcmds/source.py new file mode 100644 index 000000000000..00c04ad15b89 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/subcmds/source.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Translate an XDR specification into executable code that +can be compiled for the Linux kernel.""" + +import logging + +from argparse import Namespace +from lark import logger +from lark.exceptions import UnexpectedInput + +from generators.source_top import XdrSourceTopGenerator +from generators.enum import XdrEnumGenerator +from generators.pointer import XdrPointerGenerator +from generators.program import XdrProgramGenerator +from generators.typedef import XdrTypedefGenerator +from generators.struct import XdrStructGenerator +from generators.union import XdrUnionGenerator + +from xdr_ast import transform_parse_tree, _RpcProgram, Specification +from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer +from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion + +from xdr_parse import xdr_parser, set_xdr_annotate + +logger.setLevel(logging.INFO) + + +def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None: + """Emit one XDR decoder function for a source file""" + if isinstance(node, _XdrEnum): + gen = XdrEnumGenerator(language, peer) + elif isinstance(node, _XdrPointer): + gen = XdrPointerGenerator(language, peer) + elif isinstance(node, _XdrTypedef): + gen = XdrTypedefGenerator(language, peer) + elif isinstance(node, _XdrStruct): + gen = XdrStructGenerator(language, peer) + elif isinstance(node, _XdrUnion): + gen = XdrUnionGenerator(language, peer) + elif isinstance(node, _RpcProgram): + gen = XdrProgramGenerator(language, peer) + else: + return + gen.emit_decoder(node) + + +def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None: + """Emit one XDR encoder function for a source file""" + if isinstance(node, _XdrEnum): + gen = XdrEnumGenerator(language, peer) + elif isinstance(node, _XdrPointer): + gen = XdrPointerGenerator(language, peer) + elif isinstance(node, _XdrTypedef): + gen = XdrTypedefGenerator(language, peer) + elif isinstance(node, _XdrStruct): + gen = XdrStructGenerator(language, peer) + elif isinstance(node, _XdrUnion): + gen = XdrUnionGenerator(language, peer) + elif isinstance(node, _RpcProgram): + gen = XdrProgramGenerator(language, peer) + else: + return + gen.emit_encoder(node) + + +def generate_server_source(filename: str, root: Specification, language: str) -> None: + """Generate server-side source code""" + + gen = XdrSourceTopGenerator(language, "server") + gen.emit_source(filename, root) + + for definition in root.definitions: + emit_source_decoder(definition.value, language, "server") + for definition in root.definitions: + emit_source_encoder(definition.value, language, "server") + + +def generate_client_source(filename: str, root: Specification, language: str) -> None: + """Generate server-side source code""" + + gen = XdrSourceTopGenerator(language, "client") + gen.emit_source(filename, root) + + # cel: todo: client needs XDR size macros + + for definition in root.definitions: + emit_source_encoder(definition.value, language, "client") + for definition in root.definitions: + emit_source_decoder(definition.value, language, "client") + + # cel: todo: client needs PROC macros + + +def handle_parse_error(e: UnexpectedInput) -> bool: + """Simple parse error reporting, no recovery attempted""" + print(e) + return True + + +def subcmd(args: Namespace) -> int: + """Generate encoder and decoder functions""" + + set_xdr_annotate(args.annotate) + parser = xdr_parser() + with open(args.filename, encoding="utf-8") as f: + parse_tree = parser.parse(f.read(), on_error=handle_parse_error) + ast = transform_parse_tree(parse_tree) + match args.peer: + case "server": + generate_server_source(args.filename, ast, args.language) + case "client": + generate_client_source(args.filename, ast, args.language) + case _: + print("Code generation for", args.peer, "is not yet supported") + + return 0 diff --git a/tools/net/sunrpc/xdrgen/templates/C/constants/definition.j2 b/tools/net/sunrpc/xdrgen/templates/C/constants/definition.j2 new file mode 100644 index 000000000000..d648ca4193f8 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/constants/definition.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +enum { {{ name }} = {{ value }} }; diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 new file mode 100644 index 000000000000..ab1e576c9531 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 new file mode 100644 index 000000000000..341d829afeda --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2 @@ -0,0 +1,19 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* enum {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + *ptr = val; + return true; +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 new file mode 100644 index 000000000000..9e62344a976a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/enumerator.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/enumerator.j2 new file mode 100644 index 000000000000..ff0b893b8b14 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/enumerator.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + {{ name }} = {{ value }}, diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/definition/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/open.j2 new file mode 100644 index 000000000000..b25335221d48 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/definition/open.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +enum {{ name }} { diff --git a/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 new file mode 100644 index 000000000000..bd0a770e50f2 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2 @@ -0,0 +1,14 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* enum {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/header_bottom/declaration/header.j2 b/tools/net/sunrpc/xdrgen/templates/C/header_bottom/declaration/header.j2 new file mode 100644 index 000000000000..0bb8c6fc0c20 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/header_bottom/declaration/header.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +#endif /* _LINUX_XDRGEN_{{ infix }}_DECL_H */ diff --git a/tools/net/sunrpc/xdrgen/templates/C/header_bottom/definition/header.j2 b/tools/net/sunrpc/xdrgen/templates/C/header_bottom/definition/header.j2 new file mode 100644 index 000000000000..69069d08dc91 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/header_bottom/definition/header.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +#endif /* _LINUX_XDRGEN_{{ infix }}_DEF_H */ diff --git a/tools/net/sunrpc/xdrgen/templates/C/header_top/declaration/header.j2 b/tools/net/sunrpc/xdrgen/templates/C/header_top/declaration/header.j2 new file mode 100644 index 000000000000..ebb4e1d32f85 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/header_top/declaration/header.j2 @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: {{ filename }} */ +/* XDR specification modification time: {{ mtime }} */ + +#ifndef _LINUX_XDRGEN_{{ infix }}_DECL_H +#define _LINUX_XDRGEN_{{ infix }}_DECL_H + +#include + +#include +#include +#include +#include diff --git a/tools/net/sunrpc/xdrgen/templates/C/header_top/definition/header.j2 b/tools/net/sunrpc/xdrgen/templates/C/header_top/definition/header.j2 new file mode 100644 index 000000000000..92f1fd4ba024 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/header_top/definition/header.j2 @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: {{ filename }} */ +/* XDR specification modification time: {{ mtime }} */ + +#ifndef _LINUX_XDRGEN_{{ infix }}_DEF_H +#define _LINUX_XDRGEN_{{ infix }}_DEF_H + +#include +#include diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/declaration/close.j2 new file mode 100644 index 000000000000..816291184e8c --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/declaration/close.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/basic.j2 new file mode 100644 index 000000000000..cde4ab53f4be --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/basic.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/close.j2 new file mode 100644 index 000000000000..5bf010665f84 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/close.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_array.j2 new file mode 100644 index 000000000000..cfd64217ad82 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_array.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { + if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0) + return false; + } diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..b4695ece1884 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/fixed_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length opaque) */ +{% endif %} + if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2 new file mode 100644 index 000000000000..c093d9e3c9ad --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2 @@ -0,0 +1,22 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* pointer {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr) +{ + bool opted; + +{% if annotate %} + /* opted */ +{% endif %} + if (!xdrgen_decode_bool(xdr, &opted)) + return false; + if (!opted) + return true; + diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/optional_data.j2 new file mode 100644 index 000000000000..b6834299a04b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/optional_data.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (optional data) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 new file mode 100644 index 000000000000..f54ccd136762 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 @@ -0,0 +1,13 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length array) */ +{% endif %} + if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT) + return false; +{% if maxsize != "0" %} + if (ptr->{{ name }}.count > {{ maxsize }}) + return false; +{% endif %} + for (u32 i = 0; i < ptr->{{ name }}.count; i++) + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i])) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..9a814de54ae8 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length opaque) */ +{% endif %} + if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 new file mode 100644 index 000000000000..12d20b143b43 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length string) */ +{% endif %} + if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/basic.j2 new file mode 100644 index 000000000000..b3430895f311 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/basic.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (basic) */ +{% endif %} + {{ classifier }}{{ type }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/close.j2 new file mode 100644 index 000000000000..9e62344a976a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/close.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_array.j2 new file mode 100644 index 000000000000..66be836826a0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_array.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (fixed-length array) */ +{% endif %} + {{ type }} {{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_opaque.j2 new file mode 100644 index 000000000000..0daba19aa0f0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/fixed_length_opaque.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (fixed-length opaque) */ +{% endif %} + u8 {{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/open.j2 new file mode 100644 index 000000000000..bc886b818d85 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/open.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* pointer {{ name }} */ +{% endif %} +struct {{ name }} { diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/optional_data.j2 new file mode 100644 index 000000000000..a33341f45e8f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/optional_data.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (optional data) */ +{% endif %} + {{ classifier }}{{ type }} *{{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_array.j2 new file mode 100644 index 000000000000..5d767f9b3674 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_array.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length array) */ +{% endif %} + struct { + u32 count; + {{ classifier }}{{ type }} *element; + } {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_opaque.j2 new file mode 100644 index 000000000000..4d0cd84be3db --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_opaque.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length opaque) */ +{% endif %} + opaque {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 new file mode 100644 index 000000000000..2de2feec77db --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length string) */ +{% endif %} + string {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2 new file mode 100644 index 000000000000..a7d3695c5a6a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2 @@ -0,0 +1,10 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }})) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }})) +{% endif %} + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/close.j2 new file mode 100644 index 000000000000..5bf010665f84 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/close.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_array.j2 new file mode 100644 index 000000000000..b01833a2c7a1 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_array.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { +{% if type in pass_by_reference %} + if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0) +{% else %} + if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0) +{% endif %} + return false; + } diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..07bc91919898 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/fixed_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length opaque) */ +{% endif %} + if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/open.j2 new file mode 100644 index 000000000000..d67fae200261 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/open.j2 @@ -0,0 +1,20 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* pointer {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value) +{ +{% if annotate %} + /* opted */ +{% endif %} + if (!xdrgen_encode_bool(xdr, value != NULL)) + return false; + if (!value) + return true; + diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/optional_data.j2 new file mode 100644 index 000000000000..16fb3e09bba1 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/optional_data.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (optional data) */ +{% endif %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_array.j2 new file mode 100644 index 000000000000..0ec8660d621a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_array.j2 @@ -0,0 +1,15 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length array) */ +{% endif %} + if (value->{{ name }}.count > {{ maxsize }}) + return false; + if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT) + return false; + for (u32 i = 0; i < value->{{ name }}.count; i++) +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i])) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i])) +{% endif %} + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..1d477c2d197a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_opaque.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length opaque) */ +{% endif %} + if (value->{{ name }}.len > {{ maxsize }}) + return false; + if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 new file mode 100644 index 000000000000..cf65b71eaef3 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length string) */ +{% endif %} + if (value->{{ name }}.len > {{ maxsize }}) + return false; + if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2 new file mode 100644 index 000000000000..4364fed19162 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/argument.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr); diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2 new file mode 100644 index 000000000000..e0ea1e849910 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/declaration/result.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr); diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 new file mode 100644 index 000000000000..0b1709cca0d4 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 @@ -0,0 +1,21 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +/** + * {{ program }}_svc_decode_{{ argument }} - Decode a {{ argument }} argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ +{% if argument == 'void' %} + return xdrgen_decode_void(xdr); +{% else %} + struct {{ argument }} *argp = rqstp->rq_argp; + + return xdrgen_decode_{{ argument }}(xdr, argp); +{% endif %} +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 new file mode 100644 index 000000000000..d304eccb5c40 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2 @@ -0,0 +1,22 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* Decode {{ result }} results */ +{% endif %} +static int {{ program }}_xdr_dec_{{ result }}(struct rpc_rqst *req, + struct xdr_stream *xdr, void *data) +{ +{% if result == 'void' %} + xdrgen_decode_void(xdr); +{% else %} + struct {{ result }} *result = data; + + if (!xdrgen_decode_{{ result }}(xdr, result)) + return -EIO; + if (result->stat != nfs_ok) { + trace_nfs_xdr_status(xdr, (int)result->stat); + return {{ program }}_stat_to_errno(result->stat); + } +{% endif %} + return 0; +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/definition/close.j2 new file mode 100644 index 000000000000..9e62344a976a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/definition/close.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/definition/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/definition/open.j2 new file mode 100644 index 000000000000..f9a6d439f156 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/definition/open.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* procedure numbers for {{ program }} */ +{% endif %} +enum { diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/definition/procedure.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/definition/procedure.j2 new file mode 100644 index 000000000000..ff0b893b8b14 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/definition/procedure.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + {{ name }} = {{ value }}, diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/encoder/argument.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/argument.j2 new file mode 100644 index 000000000000..2fbb5bd13aec --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/argument.j2 @@ -0,0 +1,16 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{%if annotate %} +/* Encode {{ argument }} arguments */ +{% endif %} +static void {{ program }}_xdr_enc_{{ argument }}(struct rpc_rqst *req, + struct xdr_stream *xdr, const void *data) +{ +{% if argument == 'void' %} + xdrgen_encode_void(xdr); +{% else %} + const struct {{ argument }} *args = data; + + xdrgen_encode_{{ argument }}(xdr, args); +{% endif %} +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 new file mode 100644 index 000000000000..6fc61a5d47b7 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 @@ -0,0 +1,21 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +/** + * {{ program }}_svc_encode_{{ result }} - Encode a {{ result }} result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ +{% if result == 'void' %} + return xdrgen_encode_void(xdr); +{% else %} + struct {{ result }} *resp = rqstp->rq_resp; + + return xdrgen_encode_{{ result }}(xdr, resp); +{% endif %} +} diff --git a/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 b/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 new file mode 100644 index 000000000000..e3a802cbc4d7 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +// Generated by xdrgen. Manual edits will be lost. +// XDR specification file: {{ filename }} +// XDR specification modification time: {{ mtime }} + +#include + +#include "{{ program }}xdr_gen.h" diff --git a/tools/net/sunrpc/xdrgen/templates/C/source_top/server.j2 b/tools/net/sunrpc/xdrgen/templates/C/source_top/server.j2 new file mode 100644 index 000000000000..974e1d971e5d --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/source_top/server.j2 @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +// Generated by xdrgen. Manual edits will be lost. +// XDR specification file: {{ filename }} +// XDR specification modification time: {{ mtime }} + +#include + +#include "{{ program }}xdr_gen.h" diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2 new file mode 100644 index 000000000000..816291184e8c --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/declaration/close.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2 new file mode 100644 index 000000000000..cde4ab53f4be --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/basic.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2 new file mode 100644 index 000000000000..5bf010665f84 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/close.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2 new file mode 100644 index 000000000000..cfd64217ad82 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_array.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { + if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0) + return false; + } diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..b4695ece1884 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/fixed_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length opaque) */ +{% endif %} + if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2 new file mode 100644 index 000000000000..289e67259f55 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/open.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* struct {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr) +{ diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2 new file mode 100644 index 000000000000..b6834299a04b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/optional_data.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (optional data) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 new file mode 100644 index 000000000000..f54ccd136762 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 @@ -0,0 +1,13 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length array) */ +{% endif %} + if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT) + return false; +{% if maxsize != "0" %} + if (ptr->{{ name }}.count > {{ maxsize }}) + return false; +{% endif %} + for (u32 i = 0; i < ptr->{{ name }}.count; i++) + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i])) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..9a814de54ae8 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length opaque) */ +{% endif %} + if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 new file mode 100644 index 000000000000..12d20b143b43 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length string) */ +{% endif %} + if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/basic.j2 new file mode 100644 index 000000000000..b3430895f311 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/basic.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (basic) */ +{% endif %} + {{ classifier }}{{ type }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/close.j2 new file mode 100644 index 000000000000..9e62344a976a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/close.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_array.j2 new file mode 100644 index 000000000000..66be836826a0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_array.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (fixed-length array) */ +{% endif %} + {{ type }} {{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_opaque.j2 new file mode 100644 index 000000000000..0daba19aa0f0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/fixed_length_opaque.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (fixed-length opaque) */ +{% endif %} + u8 {{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/open.j2 new file mode 100644 index 000000000000..07cbf5424546 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/open.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* struct {{ name }} */ +{% endif %} +struct {{ name }} { diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/optional_data.j2 new file mode 100644 index 000000000000..a33341f45e8f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/optional_data.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (optional data) */ +{% endif %} + {{ classifier }}{{ type }} *{{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_array.j2 new file mode 100644 index 000000000000..5d767f9b3674 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_array.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length array) */ +{% endif %} + struct { + u32 count; + {{ classifier }}{{ type }} *element; + } {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_opaque.j2 new file mode 100644 index 000000000000..4d0cd84be3db --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_opaque.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length opaque) */ +{% endif %} + opaque {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 new file mode 100644 index 000000000000..2de2feec77db --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 @@ -0,0 +1,5 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* (variable-length string) */ +{% endif %} + string {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2 new file mode 100644 index 000000000000..a7d3695c5a6a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/basic.j2 @@ -0,0 +1,10 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }})) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }})) +{% endif %} + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2 new file mode 100644 index 000000000000..5bf010665f84 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/close.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2 new file mode 100644 index 000000000000..b01833a2c7a1 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_array.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { +{% if type in pass_by_reference %} + if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0) +{% else %} + if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0) +{% endif %} + return false; + } diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..07bc91919898 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/fixed_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (fixed-length opaque) */ +{% endif %} + if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2 new file mode 100644 index 000000000000..2286a3adf82a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/open.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* struct {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value) +{ diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2 new file mode 100644 index 000000000000..16fb3e09bba1 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/optional_data.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (optional data) */ +{% endif %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2 new file mode 100644 index 000000000000..0ec8660d621a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_array.j2 @@ -0,0 +1,15 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length array) */ +{% endif %} + if (value->{{ name }}.count > {{ maxsize }}) + return false; + if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT) + return false; + for (u32 i = 0; i < value->{{ name }}.count; i++) +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i])) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i])) +{% endif %} + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..1d477c2d197a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_opaque.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length opaque) */ +{% endif %} + if (value->{{ name }}.len > {{ maxsize }}) + return false; + if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 new file mode 100644 index 000000000000..cf65b71eaef3 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length string) */ +{% endif %} + if (value->{{ name }}.len > {{ maxsize }}) + return false; + if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2 new file mode 100644 index 000000000000..455b10bd90ec --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/basic.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr); +{% if name in pass_by_reference %} +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ name }} *value); +{%- else -%} +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ name }} value); +{% endif %} diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2 new file mode 100644 index 000000000000..3fe3ddd9f359 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_array.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2 new file mode 100644 index 000000000000..3fe3ddd9f359 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/fixed_length_opaque.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2 new file mode 100644 index 000000000000..3fe3ddd9f359 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_array.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2 new file mode 100644 index 000000000000..3fe3ddd9f359 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_opaque.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 new file mode 100644 index 000000000000..3fe3ddd9f359 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value); diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2 new file mode 100644 index 000000000000..da4709403dc9 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/basic.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) +{ +{% if annotate %} + /* (basic) */ +{% endif %} + return xdrgen_decode_{{ type }}(xdr, ptr); +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2 new file mode 100644 index 000000000000..d7c80e472fe3 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_array.j2 @@ -0,0 +1,25 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr) +{ +{% if annotate %} + /* (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { +{%- if classifier == '' %} + if (xdrgen_decode_{{ type }}(xdr, ptr->items[i]) < 0) +{% else %} + if (xdrgen_decode_{{ type }}(xdr, &ptr->items[i]) < 0) +{% endif %} + return false; + } + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..8b4ff08c49e5 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/fixed_length_opaque.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr) +{ +{% if annotate %} + /* (fixed-length opaque) */ +{% endif %} + return xdr_stream_decode_opaque_fixed(xdr, ptr, {{ size }}) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2 new file mode 100644 index 000000000000..e74ffdd98463 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_array.j2 @@ -0,0 +1,26 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr) +{ +{% if annotate %} + /* (variable-length array) */ +{% endif %} + if (xdr_stream_decode_u32(xdr, &ptr->count) < 0) + return false; +{% if maxsize != "0" %} + if (ptr->count > {{ maxsize }}) + return false; +{% endif %} + for (u32 i = 0; i < ptr->count; i++) + if (!xdrgen_decode_{{ type }}(xdr, &ptr->element[i])) + return false; + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..c1b7ad84f99c --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr) +{ +{% if annotate %} + /* (variable-length opaque) */ +{% endif %} + return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 new file mode 100644 index 000000000000..937286d76688 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr) +{ +{% if annotate %} + /* (variable-length string) */ +{% endif %} + return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/basic.j2 new file mode 100644 index 000000000000..1c5f28135eec --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/basic.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (basic) */ +{% endif %} +typedef {{ classifier }}{{ type }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_array.j2 new file mode 100644 index 000000000000..c3a67c952e77 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_array.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (fixed-length array) */ +{% endif %} +typedef {{ type }}{{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_opaque.j2 new file mode 100644 index 000000000000..8788b02fe4f5 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/fixed_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (fixed-length opaque) */ +{% endif %} +typedef u8 {{ name }}[{{ size }}]; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_array.j2 new file mode 100644 index 000000000000..f03393760545 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_array.j2 @@ -0,0 +1,9 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (variable-length array) */ +{% endif %} +typedef struct { + u32 count; + {{ classifier }}{{ type }} *element; +} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_opaque.j2 new file mode 100644 index 000000000000..162f2610af34 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (variable-length opaque) */ +{% endif %} +typedef opaque {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 new file mode 100644 index 000000000000..c03c2df8e625 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} (variable-length string) */ +{% endif %} +typedef string {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2 new file mode 100644 index 000000000000..35effe67e4ef --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/basic.j2 @@ -0,0 +1,21 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +{% if name in pass_by_reference %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} *value) +{% else %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{% endif %} +{ +{% if annotate %} + /* (basic) */ +{% endif %} + return xdrgen_encode_{{ type }}(xdr, value); +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2 new file mode 100644 index 000000000000..95202ad5ad2d --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_array.j2 @@ -0,0 +1,25 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{ +{% if annotate %} + /* (fixed-length array) */ +{% endif %} + for (u32 i = 0; i < {{ size }}; i++) { +{% if type in pass_by_reference %} + if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0) +{% else %} + if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0) +{% endif %} + return false; + } + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2 new file mode 100644 index 000000000000..9c66a11b9912 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/fixed_length_opaque.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{ +{% if annotate %} + /* (fixed-length opaque) */ +{% endif %} + return xdr_stream_encode_opaque_fixed(xdr, value, {{ size }}) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2 new file mode 100644 index 000000000000..2d2384f64918 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_array.j2 @@ -0,0 +1,30 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{ +{% if annotate %} + /* (variable-length array) */ +{% endif %} +{% if maxsize != "0" %} + if (unlikely(value.count > {{ maxsize }})) + return false; +{% endif %} + if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT) + return false; + for (u32 i = 0; i < value.count; i++) +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &value.element[i])) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, value.element[i])) +{% endif %} + return false; + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..8508f13c95b9 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_opaque.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{ +{% if annotate %} + /* (variable-length opaque) */ +{% endif %} + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 new file mode 100644 index 000000000000..3d490ff180d0 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 @@ -0,0 +1,17 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* typedef {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const {{ classifier }}{{ name }} value) +{ +{% if annotate %} + /* (variable-length string) */ +{% endif %} + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2 new file mode 100644 index 000000000000..4d97cc5395eb --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/basic.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2 new file mode 100644 index 000000000000..b286d1407029 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/break.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + break; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2 new file mode 100644 index 000000000000..5fa2163f0a74 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + case {{ case }}: diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2 new file mode 100644 index 000000000000..fdc2dfd1843b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/close.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + } + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2 new file mode 100644 index 000000000000..044a002d0589 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/default_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + default: diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2 new file mode 100644 index 000000000000..eb9941376e49 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/open.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* union {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr) +{ diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2 new file mode 100644 index 000000000000..e4476f5fd8d3 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/optional_data.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (optional data) */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, &ptr->u.{{ name }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2 new file mode 100644 index 000000000000..99b3067ef617 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/switch_spec.j2 @@ -0,0 +1,7 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* discriminant {{ name }} */ +{% endif %} + if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }})) + return false; + switch (ptr->{{ name }}) { diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 new file mode 100644 index 000000000000..eee2b9a68e27 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 @@ -0,0 +1,13 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length array) */ +{% endif %} + if (xdr_stream_decode_u32(xdr, &count) != XDR_UNIT) + return false; + if (count > {{ maxsize }}) + return false; + for (u32 i = 0; i < count; i++) { + if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0) + return false; + } + ptr->{{ name }}.len = count; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2 new file mode 100644 index 000000000000..c9d88ed29c78 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_opaque.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length opaque) */ +{% endif %} + if (!xdrgen_decode_opaque(xdr, (struct opaque *)ptr->u.{{ name }}, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 new file mode 100644 index 000000000000..83b6e5a14e7f --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (variable-length string) */ +{% endif %} + if (!xdrgen_decode_string(xdr, (struct string *)ptr->u.{{ name }}, {{ maxsize }})) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2 new file mode 100644 index 000000000000..65205ce37b36 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/void.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + if (!xdrgen_decode_void(xdr)) + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/definition/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/definition/case_spec.j2 new file mode 100644 index 000000000000..52f8d131b805 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/definition/case_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + {{ classifier }}{{ type }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/definition/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/definition/close.j2 new file mode 100644 index 000000000000..01d716d0099e --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/definition/close.j2 @@ -0,0 +1,8 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + } u; +}; +{%- if name in public_apis %} + +bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr); +bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr); +{%- endif -%} diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/definition/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/definition/default_spec.j2 new file mode 100644 index 000000000000..52f8d131b805 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/definition/default_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + {{ classifier }}{{ type }} {{ name }}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/definition/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/definition/open.j2 new file mode 100644 index 000000000000..20fcfd1fc4e5 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/definition/open.j2 @@ -0,0 +1,6 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* union {{ name }} */ +{% endif %} +struct {{ name }} { diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/definition/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/definition/switch_spec.j2 new file mode 100644 index 000000000000..3e552732502c --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/definition/switch_spec.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + {{ classifier }}{{ type }} {{ name }}; + union { diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2 new file mode 100644 index 000000000000..6452d75c6f9a --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/basic.j2 @@ -0,0 +1,10 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* member {{ name }} (basic) */ +{% endif %} +{% if type in pass_by_reference %} + if (!xdrgen_encode_{{ type }}(xdr, &ptr->u.{{ name }})) +{% else %} + if (!xdrgen_encode_{{ type }}(xdr, ptr->u.{{ name }})) +{% endif %} + return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2 new file mode 100644 index 000000000000..b286d1407029 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/break.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + break; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2 new file mode 100644 index 000000000000..5fa2163f0a74 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + case {{ case }}: diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2 new file mode 100644 index 000000000000..fdc2dfd1843b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/close.j2 @@ -0,0 +1,4 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + } + return true; +}; diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2 new file mode 100644 index 000000000000..044a002d0589 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/default_spec.j2 @@ -0,0 +1,2 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + default: diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2 new file mode 100644 index 000000000000..e5a206df10c6 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/open.j2 @@ -0,0 +1,12 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + +{% if annotate %} +/* union {{ name }} */ +{% endif %} +{% if name in public_apis %} +bool +{% else %} +static bool __maybe_unused +{% endif %} +xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *ptr) +{ diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2 new file mode 100644 index 000000000000..c8c3ecbe038b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/switch_spec.j2 @@ -0,0 +1,7 @@ +{# SPDX-License-Identifier: GPL-2.0 #} +{% if annotate %} + /* discriminant {{ name }} */ +{% endif %} + if (!xdrgen_encode_{{ type }}(xdr, ptr->{{ name }})) + return false; + switch (ptr->{{ name }}) { diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2 new file mode 100644 index 000000000000..84e7c2127d75 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/templates/C/union/encoder/void.j2 @@ -0,0 +1,3 @@ +{# SPDX-License-Identifier: GPL-2.0 #} + if (!xdrgen_encode_void(xdr)) + return false; diff --git a/tools/net/sunrpc/xdrgen/tests/test.x b/tools/net/sunrpc/xdrgen/tests/test.x new file mode 100644 index 000000000000..90c8587f6fe5 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/tests/test.x @@ -0,0 +1,36 @@ +/* Sample XDR specification from RFC 1832 Section 5.5 */ + +const MAXUSERNAME = 32; /* max length of a user name */ +const MAXFILELEN = 65535; /* max length of a file */ +const MAXNAMELEN = 255; /* max length of a file name */ + +/* + * Types of files: + */ +enum filekind { + TEXT = 0, /* ascii data */ + DATA = 1, /* raw data */ + EXEC = 2 /* executable */ +}; + +/* + * File information, per kind of file: + */ +union filetype switch (filekind kind) { +case TEXT: + void; /* no extra information */ +case DATA: + string creator; /* data creator */ +case EXEC: + string interpretor; /* program interpretor */ +}; + +/* + * A complete file: + */ +struct file { + string filename; /* name of file */ + filetype type; /* info about file */ + string owner; /* owner of file */ + opaque data; /* file data */ +}; diff --git a/tools/net/sunrpc/xdrgen/xdr_ast.py b/tools/net/sunrpc/xdrgen/xdr_ast.py new file mode 100644 index 000000000000..dbd3fcf9c957 --- /dev/null +++ b/tools/net/sunrpc/xdrgen/xdr_ast.py @@ -0,0 +1,510 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Define and implement the Abstract Syntax Tree for the XDR language.""" + +import sys +from typing import List +from dataclasses import dataclass + +from lark import ast_utils, Transformer +from lark.tree import Meta + +this_module = sys.modules[__name__] + +excluded_apis = [] +header_name = "none" +public_apis = [] +enums = set() +structs = set() +pass_by_reference = set() + + +@dataclass +class _XdrAst(ast_utils.Ast): + """Base class for the XDR abstract syntax tree""" + + +@dataclass +class _XdrIdentifier(_XdrAst): + """Corresponds to 'identifier' in the XDR language grammar""" + + symbol: str + + +@dataclass +class _XdrValue(_XdrAst): + """Corresponds to 'value' in the XDR language grammar""" + + value: str + + +@dataclass +class _XdrConstantValue(_XdrAst): + """Corresponds to 'constant' in the XDR language grammar""" + + value: int + + +@dataclass +class _XdrTypeSpecifier(_XdrAst): + """Corresponds to 'type_specifier' in the XDR language grammar""" + + type_name: str + c_classifier: str + + +@dataclass +class _XdrDefinedType(_XdrTypeSpecifier): + """Corresponds to a type defined by the input specification""" + + +@dataclass +class _XdrBuiltInType(_XdrTypeSpecifier): + """Corresponds to a built-in XDR type""" + + +@dataclass +class _XdrDeclaration(_XdrAst): + """Base class of XDR type declarations""" + + +@dataclass +class _XdrFixedLengthOpaque(_XdrDeclaration): + """A fixed-length opaque declaration""" + + name: str + size: str + template: str = "fixed_length_opaque" + + +@dataclass +class _XdrVariableLengthOpaque(_XdrDeclaration): + """A variable-length opaque declaration""" + + name: str + maxsize: str + template: str = "variable_length_opaque" + + +@dataclass +class _XdrVariableLengthString(_XdrDeclaration): + """A (NUL-terminated) variable-length string declaration""" + + name: str + maxsize: str + template: str = "variable_length_string" + + +@dataclass +class _XdrFixedLengthArray(_XdrDeclaration): + """A fixed-length array declaration""" + + name: str + spec: _XdrTypeSpecifier + size: str + template: str = "fixed_length_array" + + +@dataclass +class _XdrVariableLengthArray(_XdrDeclaration): + """A variable-length array declaration""" + + name: str + spec: _XdrTypeSpecifier + maxsize: str + template: str = "variable_length_array" + + +@dataclass +class _XdrOptionalData(_XdrDeclaration): + """An 'optional_data' declaration""" + + name: str + spec: _XdrTypeSpecifier + template: str = "optional_data" + + +@dataclass +class _XdrBasic(_XdrDeclaration): + """A 'basic' declaration""" + + name: str + spec: _XdrTypeSpecifier + template: str = "basic" + + +@dataclass +class _XdrVoid(_XdrDeclaration): + """A void declaration""" + + template: str = "void" + + +@dataclass +class _XdrConstant(_XdrAst): + """Corresponds to 'constant_def' in the grammar""" + + name: str + value: str + + +@dataclass +class _XdrEnumerator(_XdrAst): + """An 'identifier = value' enumerator""" + + name: str + value: str + + +@dataclass +class _XdrEnum(_XdrAst): + """An XDR enum definition""" + + name: str + minimum: int + maximum: int + enumerators: List[_XdrEnumerator] + + +@dataclass +class _XdrStruct(_XdrAst): + """An XDR struct definition""" + + name: str + fields: List[_XdrDeclaration] + + +@dataclass +class _XdrPointer(_XdrAst): + """An XDR pointer definition""" + + name: str + fields: List[_XdrDeclaration] + + +@dataclass +class _XdrTypedef(_XdrAst): + """An XDR typedef""" + + declaration: _XdrDeclaration + + +@dataclass +class _XdrCaseSpec(_XdrAst): + """One case in an XDR union""" + + values: List[str] + arm: _XdrDeclaration + template: str = "case_spec" + + +@dataclass +class _XdrDefaultSpec(_XdrAst): + """Default case in an XDR union""" + + arm: _XdrDeclaration + template: str = "default_spec" + + +@dataclass +class _XdrUnion(_XdrAst): + """An XDR union""" + + name: str + discriminant: _XdrDeclaration + cases: List[_XdrCaseSpec] + default: _XdrDeclaration + + +@dataclass +class _RpcProcedure(_XdrAst): + """RPC procedure definition""" + + name: str + number: str + argument: _XdrTypeSpecifier + result: _XdrTypeSpecifier + + +@dataclass +class _RpcVersion(_XdrAst): + """RPC version definition""" + + name: str + number: str + procedures: List[_RpcProcedure] + + +@dataclass +class _RpcProgram(_XdrAst): + """RPC program definition""" + + name: str + number: str + versions: List[_RpcVersion] + + +@dataclass +class _Pragma(_XdrAst): + """Empty class for pragma directives""" + + +@dataclass +class Definition(_XdrAst, ast_utils.WithMeta): + """Corresponds to 'definition' in the grammar""" + + meta: Meta + value: _XdrAst + + +@dataclass +class Specification(_XdrAst, ast_utils.AsList): + """Corresponds to 'specification' in the grammar""" + + definitions: List[Definition] + + +class ParseToAst(Transformer): + """Functions that transform productions into AST nodes""" + + def identifier(self, children): + """Instantiate one _XdrIdentifier object""" + return _XdrIdentifier(children[0].value) + + def value(self, children): + """Instantiate one _XdrValue object""" + if isinstance(children[0], _XdrIdentifier): + return _XdrValue(children[0].symbol) + return _XdrValue(children[0].children[0].value) + + def constant(self, children): + """Instantiate one _XdrConstantValue object""" + match children[0].data: + case "decimal_constant": + value = int(children[0].children[0].value, base=10) + case "hexadecimal_constant": + value = int(children[0].children[0].value, base=16) + case "octal_constant": + value = int(children[0].children[0].value, base=8) + return _XdrConstantValue(value) + + def type_specifier(self, children): + """Instantiate one type_specifier object""" + c_classifier = "" + if isinstance(children[0], _XdrIdentifier): + name = children[0].symbol + if name in enums: + c_classifier = "enum " + if name in structs: + c_classifier = "struct " + return _XdrDefinedType( + type_name=name, + c_classifier=c_classifier, + ) + + token = children[0].data + return _XdrBuiltInType( + type_name=token.value, + c_classifier=c_classifier, + ) + + def constant_def(self, children): + """Instantiate one _XdrConstant object""" + name = children[0].symbol + value = children[1].value + return _XdrConstant(name, value) + + # cel: Python can compute a min() and max() for the enumerator values + # so that the generated code can perform proper range checking. + def enum(self, children): + """Instantiate one _XdrEnum object""" + enum_name = children[0].symbol + enums.add(enum_name) + + i = 0 + enumerators = [] + body = children[1] + while i < len(body.children): + name = body.children[i].symbol + value = body.children[i + 1].value + enumerators.append(_XdrEnumerator(name, value)) + i = i + 2 + + return _XdrEnum(enum_name, 0, 0, enumerators) + + def fixed_length_opaque(self, children): + """Instantiate one _XdrFixedLengthOpaque declaration object""" + name = children[0].symbol + size = children[1].value + + return _XdrFixedLengthOpaque(name, size) + + def variable_length_opaque(self, children): + """Instantiate one _XdrVariableLengthOpaque declaration object""" + name = children[0].symbol + if children[1] is not None: + maxsize = children[1].value + else: + maxsize = "0" + + return _XdrVariableLengthOpaque(name, maxsize) + + def variable_length_string(self, children): + """Instantiate one _XdrVariableLengthString declaration object""" + name = children[0].symbol + if children[1] is not None: + maxsize = children[1].value + else: + maxsize = "0" + + return _XdrVariableLengthString(name, maxsize) + + def fixed_length_array(self, children): + """Instantiate one _XdrFixedLengthArray declaration object""" + spec = children[0] + name = children[1].symbol + size = children[2].value + + return _XdrFixedLengthArray(name, spec, size) + + def variable_length_array(self, children): + """Instantiate one _XdrVariableLengthArray declaration object""" + spec = children[0] + name = children[1].symbol + if children[2] is not None: + maxsize = children[2].value + else: + maxsize = "0" + + return _XdrVariableLengthArray(name, spec, maxsize) + + def optional_data(self, children): + """Instantiate one _XdrOptionalData declaration object""" + spec = children[0] + name = children[1].symbol + structs.add(name) + pass_by_reference.add(name) + + return _XdrOptionalData(name, spec) + + def basic(self, children): + """Instantiate one _XdrBasic object""" + spec = children[0] + name = children[1].symbol + + return _XdrBasic(name, spec) + + def void(self, children): + """Instantiate one _XdrVoid declaration object""" + + return _XdrVoid() + + def struct(self, children): + """Instantiate one _XdrStruct object""" + name = children[0].symbol + structs.add(name) + pass_by_reference.add(name) + fields = children[1].children + + last_field = fields[-1] + if ( + isinstance(last_field, _XdrOptionalData) + and name == last_field.spec.type_name + ): + return _XdrPointer(name, fields) + + return _XdrStruct(name, fields) + + def typedef(self, children): + """Instantiate one _XdrTypedef object""" + new_type = children[0] + if isinstance(new_type, _XdrBasic) and isinstance( + new_type.spec, _XdrDefinedType + ): + if new_type.spec.type_name in pass_by_reference: + pass_by_reference.add(new_type.name) + + return _XdrTypedef(new_type) + + def case_spec(self, children): + """Instantiate one _XdrCaseSpec object""" + values = [] + for item in children[0:-1]: + values.append(item.value) + arm = children[-1] + + return _XdrCaseSpec(values, arm) + + def default_spec(self, children): + """Instantiate one _XdrDefaultSpec object""" + arm = children[0] + + return _XdrDefaultSpec(arm) + + def union(self, children): + """Instantiate one _XdrUnion object""" + name = children[0].symbol + structs.add(name) + pass_by_reference.add(name) + + body = children[1] + discriminant = body.children[0].children[0] + cases = body.children[1:-1] + default = body.children[-1] + + return _XdrUnion(name, discriminant, cases, default) + + def procedure_def(self, children): + """Instantiate one _RpcProcedure object""" + result = children[0] + name = children[1].symbol + argument = children[2] + number = children[3].value + + return _RpcProcedure(name, number, argument, result) + + def version_def(self, children): + """Instantiate one _RpcVersion object""" + name = children[0].symbol + number = children[-1].value + procedures = children[1:-1] + + return _RpcVersion(name, number, procedures) + + def program_def(self, children): + """Instantiate one _RpcProgram object""" + name = children[0].symbol + number = children[-1].value + versions = children[1:-1] + + return _RpcProgram(name, number, versions) + + def pragma_def(self, children): + """Instantiate one _Pragma object""" + directive = children[0].children[0].data + match directive: + case "exclude_directive": + excluded_apis.append(children[1].symbol) + case "header_directive": + global header_name + header_name = children[1].symbol + case "public_directive": + public_apis.append(children[1].symbol) + case _: + raise NotImplementedError("Directive not supported") + return _Pragma() + + +transformer = ast_utils.create_transformer(this_module, ParseToAst()) + + +def transform_parse_tree(parse_tree): + """Transform productions into an abstract syntax tree""" + + return transformer.transform(parse_tree) + + +def get_header_name() -> str: + """Return header name set by pragma header directive""" + return header_name diff --git a/tools/net/sunrpc/xdrgen/xdr_parse.py b/tools/net/sunrpc/xdrgen/xdr_parse.py new file mode 100644 index 000000000000..964b44e675df --- /dev/null +++ b/tools/net/sunrpc/xdrgen/xdr_parse.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Common parsing code for xdrgen""" + +from lark import Lark + + +# Set to True to emit annotation comments in generated source +annotate = False + + +def set_xdr_annotate(set_it: bool) -> None: + """Set 'annotate' if --annotate was specified on the command line""" + global annotate + annotate = set_it + + +def get_xdr_annotate() -> bool: + """Return True if --annotate was specified on the command line""" + return annotate + + +def xdr_parser() -> Lark: + """Return a Lark parser instance configured with the XDR language grammar""" + + return Lark.open( + "grammars/xdr.lark", + rel_to=__file__, + start="specification", + debug=True, + strict=True, + propagate_positions=True, + parser="lalr", + lexer="contextual", + ) diff --git a/tools/net/sunrpc/xdrgen/xdrgen b/tools/net/sunrpc/xdrgen/xdrgen new file mode 100755 index 000000000000..95f303b2861b --- /dev/null +++ b/tools/net/sunrpc/xdrgen/xdrgen @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# ex: set filetype=python: + +"""Translate an XDR specification into executable code that +can be compiled for the Linux kernel.""" + +__author__ = "Chuck Lever" +__copyright__ = "Copyright (c) 2024 Oracle and/or its affiliates." +__license__ = "GPL-2.0 only" +__version__ = "0.2" + +import sys +import argparse + +from subcmds import definitions +from subcmds import declarations +from subcmds import lint +from subcmds import source + + +sys.path.insert(1, "@pythondir@") + + +def main() -> int: + """Parse command-line options""" + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="Convert an XDR specification to Linux kernel source code", + epilog="""\ +Copyright (c) 2024 Oracle and/or its affiliates. + +License GPLv2: +This is free software. You are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law.""", + ) + parser.add_argument( + "--version", + help="Display the version of this tool", + action="version", + version=__version__, + ) + + subcommands = parser.add_subparsers(title="Subcommands", required=True) + + definitions_parser = subcommands.add_parser( + "definitions", help="Generate XDR definitions" + ) + definitions_parser.add_argument( + "--annotate", + action="store_true", + default=False, + help="Add annotation comments", + ) + definitions_parser.add_argument( + "--language", + action="store_true", + default="C", + help="Output language", + ) + definitions_parser.add_argument( + "--peer", + choices=["server", "client",], + default="server", + help="Generate header code for client or server side", + type=str, + ) + definitions_parser.add_argument("filename", help="File containing an XDR specification") + definitions_parser.set_defaults(func=definitions.subcmd) + + declarations_parser = subcommands.add_parser( + "declarations", help="Generate function declarations" + ) + declarations_parser.add_argument( + "--annotate", + action="store_true", + default=False, + help="Add annotation comments", + ) + declarations_parser.add_argument( + "--language", + action="store_true", + default="C", + help="Output language", + ) + declarations_parser.add_argument( + "--peer", + choices=["server", "client",], + default="server", + help="Generate code for client or server side", + type=str, + ) + declarations_parser.add_argument("filename", help="File containing an XDR specification") + declarations_parser.set_defaults(func=declarations.subcmd) + + linter_parser = subcommands.add_parser("lint", help="Check an XDR specification") + linter_parser.add_argument("filename", help="File containing an XDR specification") + linter_parser.set_defaults(func=lint.subcmd) + + source_parser = subcommands.add_parser( + "source", help="Generate XDR encoder and decoder source code" + ) + source_parser.add_argument( + "--annotate", + action="store_true", + default=False, + help="Add annotation comments", + ) + source_parser.add_argument( + "--language", + action="store_true", + default="C", + help="Output language", + ) + source_parser.add_argument( + "--peer", + choices=["server", "client",], + default="server", + help="Generate code for client or server side", + type=str, + ) + source_parser.add_argument("filename", help="File containing an XDR specification") + source_parser.set_defaults(func=source.subcmd) + + args = parser.parse_args() + return args.func(args) + + +try: + if __name__ == "__main__": + sys.exit(main()) +except (SystemExit, KeyboardInterrupt, BrokenPipeError): + sys.exit(1) -- 2.50.1 From 663ad8b1df8724cd5e01df66ea67ce0424fbcdf6 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 10 Sep 2024 15:31:19 -0400 Subject: [PATCH 06/16] xdrgen: Fix return code checking in built-in XDR decoders xdr_stream_encode_u32() returns XDR_UNIT on success. xdr_stream_decode_u32() returns zero or -EMSGSIZE, but never XDR_UNIT. Signed-off-by: Chuck Lever --- include/linux/sunrpc/xdrgen/_builtins.h | 4 ++-- .../templates/C/pointer/decoder/variable_length_array.j2 | 2 +- .../templates/C/struct/decoder/variable_length_array.j2 | 2 +- .../xdrgen/templates/C/union/decoder/variable_length_array.j2 | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/sunrpc/xdrgen/_builtins.h b/include/linux/sunrpc/xdrgen/_builtins.h index 68746c59fc9a..66ca3ece951a 100644 --- a/include/linux/sunrpc/xdrgen/_builtins.h +++ b/include/linux/sunrpc/xdrgen/_builtins.h @@ -184,7 +184,7 @@ xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen) __be32 *p; u32 len; - if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT)) + if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0)) return false; if (unlikely(maxlen && len > maxlen)) return false; @@ -215,7 +215,7 @@ xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen) __be32 *p; u32 len; - if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT)) + if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0)) return false; if (unlikely(maxlen && len > maxlen)) return false; diff --git a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 index f54ccd136762..2f943909cdf7 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_array.j2 @@ -2,7 +2,7 @@ {% if annotate %} /* member {{ name }} (variable-length array) */ {% endif %} - if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT) + if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) < 0) return false; {% if maxsize != "0" %} if (ptr->{{ name }}.count > {{ maxsize }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 index f54ccd136762..2f943909cdf7 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_array.j2 @@ -2,7 +2,7 @@ {% if annotate %} /* member {{ name }} (variable-length array) */ {% endif %} - if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT) + if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) < 0) return false; {% if maxsize != "0" %} if (ptr->{{ name }}.count > {{ maxsize }}) diff --git a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 index eee2b9a68e27..51ad736d2530 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_array.j2 @@ -2,7 +2,7 @@ {% if annotate %} /* member {{ name }} (variable-length array) */ {% endif %} - if (xdr_stream_decode_u32(xdr, &count) != XDR_UNIT) + if (xdr_stream_decode_u32(xdr, &count) < 0) return false; if (count > {{ maxsize }}) return false; -- 2.50.1 From fed8a17c61ffa2ba53dc749068b6f07ecf40e3bf Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 10 Sep 2024 15:51:46 -0400 Subject: [PATCH 07/16] xdrgen: typedefs should use the built-in string and opaque functions 'typedef opaque yada' should use xdrgen's built-in opaque encoder and decoder, to enable better compiler optimization. Signed-off-by: Chuck Lever --- .../templates/C/typedef/decoder/variable_length_opaque.j2 | 2 +- .../templates/C/typedef/decoder/variable_length_string.j2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 index c1b7ad84f99c..f28f8b228ad5 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_opaque.j2 @@ -13,5 +13,5 @@ xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr {% if annotate %} /* (variable-length opaque) */ {% endif %} - return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0; + return xdrgen_decode_opaque(xdr, ptr, {{ maxsize }}); }; diff --git a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 index 937286d76688..56c5a17d6a70 100644 --- a/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 +++ b/tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 @@ -13,5 +13,5 @@ xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ classifier }}{{ name }} *ptr {% if annotate %} /* (variable-length string) */ {% endif %} - return xdr_stream_decode_opaque(xdr, ptr->data, ptr->len) >= 0; + return xdrgen_decode_string(xdr, ptr, {{ maxsize }}); }; -- 2.50.1 From 509abfc7a0ba66afa648e8216306acdc55ec54ed Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 13 Sep 2024 13:50:56 -0400 Subject: [PATCH 08/16] xdrgen: Prevent reordering of encoder and decoder functions I noticed that "xdrgen source" reorders the procedure encoder and decoder functions every time it is run. I would prefer that the generated code be more deterministic: it enables a reader to better see exactly what has changed between runs of the tool. The problem is that Python sets are not ordered. I use a Python set to ensure that, when multiple procedures use a particular argument or result type, the encoder/decoder for that type is emitted only once. Sets aren't ordered, but I can use Python dictionaries for this purpose to ensure the procedure functions are always emitted in the same order if the .x file does not change. Signed-off-by: Chuck Lever --- tools/net/sunrpc/xdrgen/generators/program.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/net/sunrpc/xdrgen/generators/program.py b/tools/net/sunrpc/xdrgen/generators/program.py index 83b0ecbae86f..ac3cf1694b68 100644 --- a/tools/net/sunrpc/xdrgen/generators/program.py +++ b/tools/net/sunrpc/xdrgen/generators/program.py @@ -34,20 +34,20 @@ def emit_version_declarations( environment: Environment, program: str, version: _RpcVersion ) -> None: """Emit declarations for each RPC version's procedures""" - arguments = set() + arguments = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - arguments.add(procedure.argument.type_name) + arguments[procedure.argument.type_name] = None if len(arguments) > 0: print("") template = environment.get_template("declaration/argument.j2") for argument in arguments: print(template.render(program=program, argument=argument)) - results = set() + results = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - results.add(procedure.result.type_name) + results[procedure.result.type_name] = None if len(results) > 0: print("") template = environment.get_template("declaration/result.j2") @@ -59,10 +59,10 @@ def emit_version_argument_decoders( environment: Environment, program: str, version: _RpcVersion ) -> None: """Emit server argument decoders for each RPC version's procedures""" - arguments = set() + arguments = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - arguments.add(procedure.argument.type_name) + arguments[procedure.argument.type_name] = None template = environment.get_template("decoder/argument.j2") for argument in arguments: @@ -73,10 +73,10 @@ def emit_version_result_decoders( environment: Environment, program: str, version: _RpcVersion ) -> None: """Emit client result decoders for each RPC version's procedures""" - results = set() + results = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - results.add(procedure.result.type_name) + results[procedure.result.type_name] = None template = environment.get_template("decoder/result.j2") for result in results: @@ -87,10 +87,10 @@ def emit_version_argument_encoders( environment: Environment, program: str, version: _RpcVersion ) -> None: """Emit client argument encoders for each RPC version's procedures""" - arguments = set() + arguments = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - arguments.add(procedure.argument.type_name) + arguments[procedure.argument.type_name] = None template = environment.get_template("encoder/argument.j2") for argument in arguments: @@ -101,10 +101,10 @@ def emit_version_result_encoders( environment: Environment, program: str, version: _RpcVersion ) -> None: """Emit server result encoders for each RPC version's procedures""" - results = set() + results = dict.fromkeys([]) for procedure in version.procedures: if procedure.name not in excluded_apis: - results.add(procedure.result.type_name) + results[procedure.result.type_name] = None template = environment.get_template("encoder/result.j2") for result in results: -- 2.50.1 From dc0d0f885aa422f621bc1c2124133eff566b0bc8 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 21 Sep 2024 14:25:37 -0400 Subject: [PATCH 09/16] NFSD: Mark filecache "down" if init fails NeilBrown says: > The handling of NFSD_FILE_CACHE_UP is strange. nfsd_file_cache_init() > sets it, but doesn't clear it on failure. So if nfsd_file_cache_init() > fails for some reason, nfsd_file_cache_shutdown() would still try to > clean up if it was called. Reported-by: NeilBrown Fixes: c7b824c3d06c ("NFSD: Replace the "init once" mechanism") Signed-off-by: Chuck Lever --- fs/nfsd/filecache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 24e8f1fbcebb..2603183305b4 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -723,7 +723,7 @@ nfsd_file_cache_init(void) ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params); if (ret) - return ret; + goto out; ret = -ENOMEM; nfsd_file_slab = KMEM_CACHE(nfsd_file, 0); @@ -775,6 +775,8 @@ nfsd_file_cache_init(void) INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker); out: + if (ret) + clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags); return ret; out_notifier: lease_unregister_notifier(&nfsd_file_lease_notifier); -- 2.50.1 From 53e4e17557049d7688ca9dadeae80864d40cf0b7 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Sep 2024 09:46:05 +1000 Subject: [PATCH 10/16] nfsd: nfsd_destroy_serv() must call svc_destroy() even if nfsd_startup_net() failed If nfsd_startup_net() fails and so ->nfsd_net_up is false, nfsd_destroy_serv() doesn't currently call svc_destroy(). It should. Fixes: 1e3577a4521e ("SUNRPC: discard sv_refcnt, and svc_get/svc_put") Signed-off-by: NeilBrown Signed-off-by: Chuck Lever --- fs/nfsd/nfssvc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index defc430f912f..5f8637b7a7a4 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -385,6 +385,9 @@ static void nfsd_shutdown_net(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); + if (!nn->nfsd_net_up) + return; + nfsd_export_flush(net); nfs4_state_shutdown_net(net); nfsd_reply_cache_shutdown(nn); nfsd_file_cache_shutdown_net(net); @@ -492,11 +495,8 @@ void nfsd_destroy_serv(struct net *net) * other initialization has been done except the rpcb information. */ svc_rpcb_cleanup(serv, net); - if (!nn->nfsd_net_up) - return; nfsd_shutdown_net(net); - nfsd_export_flush(net); svc_destroy(&serv); } -- 2.50.1 From c88c150a467fcb670a1608e2272beeee3e86df6e Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Fri, 4 Oct 2024 18:04:03 -0400 Subject: [PATCH 11/16] nfsd: fix possible badness in FREE_STATEID When multiple FREE_STATEIDs are sent for the same delegation stateid, it can lead to a possible either use-after-free or counter refcount underflow errors. In nfsd4_free_stateid() under the client lock we find a delegation stateid, however the code drops the lock before calling nfs4_put_stid(), that allows another FREE_STATE to find the stateid again. The first one will proceed to then free the stateid which leads to either use-after-free or decrementing already zeroed counter. Fixes: 3f29cc82a84c ("nfsd: split sc_status out of sc_type") Signed-off-by: Olga Kornievskaia Reviewed-by: Benjamin Coddington Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ac1859c7cc9d..56b261608af4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -7154,6 +7154,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, switch (s->sc_type) { case SC_TYPE_DELEG: if (s->sc_status & SC_STATUS_REVOKED) { + s->sc_status |= SC_STATUS_CLOSED; spin_unlock(&s->sc_lock); dp = delegstateid(s); list_del_init(&dp->dl_recall_lru); -- 2.50.1 From 8dd91e8d31febf4d9cca3ae1bb4771d33ae7ee5a Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Fri, 18 Oct 2024 15:24:58 -0400 Subject: [PATCH 12/16] nfsd: fix race between laundromat and free_stateid There is a race between laundromat handling of revoked delegations and a client sending free_stateid operation. Laundromat thread finds that delegation has expired and needs to be revoked so it marks the delegation stid revoked and it puts it on a reaper list but then it unlock the state lock and the actual delegation revocation happens without the lock. Once the stid is marked revoked a racing free_stateid processing thread does the following (1) it calls list_del_init() which removes it from the reaper list and (2) frees the delegation stid structure. The laundromat thread ends up not calling the revoke_delegation() function for this particular delegation but that means it will no release the lock lease that exists on the file. Now, a new open for this file comes in and ends up finding that lease list isn't empty and calls nfsd_breaker_owns_lease() which ends up trying to derefence a freed delegation stateid. Leading to the followint use-after-free KASAN warning: kernel: ================================================================== kernel: BUG: KASAN: slab-use-after-free in nfsd_breaker_owns_lease+0x140/0x160 [nfsd] kernel: Read of size 8 at addr ffff0000e73cd0c8 by task nfsd/6205 kernel: kernel: CPU: 2 UID: 0 PID: 6205 Comm: nfsd Kdump: loaded Not tainted 6.11.0-rc7+ #9 kernel: Hardware name: Apple Inc. Apple Virtualization Generic Platform, BIOS 2069.0.0.0.0 08/03/2024 kernel: Call trace: kernel: dump_backtrace+0x98/0x120 kernel: show_stack+0x1c/0x30 kernel: dump_stack_lvl+0x80/0xe8 kernel: print_address_description.constprop.0+0x84/0x390 kernel: print_report+0xa4/0x268 kernel: kasan_report+0xb4/0xf8 kernel: __asan_report_load8_noabort+0x1c/0x28 kernel: nfsd_breaker_owns_lease+0x140/0x160 [nfsd] kernel: nfsd_file_do_acquire+0xb3c/0x11d0 [nfsd] kernel: nfsd_file_acquire_opened+0x84/0x110 [nfsd] kernel: nfs4_get_vfs_file+0x634/0x958 [nfsd] kernel: nfsd4_process_open2+0xa40/0x1a40 [nfsd] kernel: nfsd4_open+0xa08/0xe80 [nfsd] kernel: nfsd4_proc_compound+0xb8c/0x2130 [nfsd] kernel: nfsd_dispatch+0x22c/0x718 [nfsd] kernel: svc_process_common+0x8e8/0x1960 [sunrpc] kernel: svc_process+0x3d4/0x7e0 [sunrpc] kernel: svc_handle_xprt+0x828/0xe10 [sunrpc] kernel: svc_recv+0x2cc/0x6a8 [sunrpc] kernel: nfsd+0x270/0x400 [nfsd] kernel: kthread+0x288/0x310 kernel: ret_from_fork+0x10/0x20 This patch proposes a fixed that's based on adding 2 new additional stid's sc_status values that help coordinate between the laundromat and other operations (nfsd4_free_stateid() and nfsd4_delegreturn()). First to make sure, that once the stid is marked revoked, it is not removed by the nfsd4_free_stateid(), the laundromat take a reference on the stateid. Then, coordinating whether the stid has been put on the cl_revoked list or we are processing FREE_STATEID and need to make sure to remove it from the list, each check that state and act accordingly. If laundromat has added to the cl_revoke list before the arrival of FREE_STATEID, then nfsd4_free_stateid() knows to remove it from the list. If nfsd4_free_stateid() finds that operations arrived before laundromat has placed it on cl_revoke list, it marks the state freed and then laundromat will no longer add it to the list. Also, for nfsd4_delegreturn() when looking for the specified stid, we need to access stid that are marked removed or freeable, it means the laundromat has started processing it but hasn't finished and this delegreturn needs to return nfserr_deleg_revoked and not nfserr_bad_stateid. The latter will not trigger a FREE_STATEID and the lack of it will leave this stid on the cl_revoked list indefinitely. Fixes: 2d4a532d385f ("nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock") CC: stable@vger.kernel.org Signed-off-by: Olga Kornievskaia Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 48 +++++++++++++++++++++++++++++++++++++-------- fs/nfsd/state.h | 2 ++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 56b261608af4..d1a2c677be7e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1359,21 +1359,47 @@ static void destroy_delegation(struct nfs4_delegation *dp) destroy_unhashed_deleg(dp); } +/** + * revoke_delegation - perform nfs4 delegation structure cleanup + * @dp: pointer to the delegation + * + * This function assumes that it's called either from the administrative + * interface (nfsd4_revoke_states()) that's revoking a specific delegation + * stateid or it's called from a laundromat thread (nfsd4_landromat()) that + * determined that this specific state has expired and needs to be revoked + * (both mark state with the appropriate stid sc_status mode). It is also + * assumed that a reference was taken on the @dp state. + * + * If this function finds that the @dp state is SC_STATUS_FREED it means + * that a FREE_STATEID operation for this stateid has been processed and + * we can proceed to removing it from recalled list. However, if @dp state + * isn't marked SC_STATUS_FREED, it means we need place it on the cl_revoked + * list and wait for the FREE_STATEID to arrive from the client. At the same + * time, we need to mark it as SC_STATUS_FREEABLE to indicate to the + * nfsd4_free_stateid() function that this stateid has already been added + * to the cl_revoked list and that nfsd4_free_stateid() is now responsible + * for removing it from the list. Inspection of where the delegation state + * in the revocation process is protected by the clp->cl_lock. + */ static void revoke_delegation(struct nfs4_delegation *dp) { struct nfs4_client *clp = dp->dl_stid.sc_client; WARN_ON(!list_empty(&dp->dl_recall_lru)); + WARN_ON_ONCE(!(dp->dl_stid.sc_status & + (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED))); trace_nfsd_stid_revoke(&dp->dl_stid); - if (dp->dl_stid.sc_status & - (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED)) { - spin_lock(&clp->cl_lock); - refcount_inc(&dp->dl_stid.sc_count); - list_add(&dp->dl_recall_lru, &clp->cl_revoked); - spin_unlock(&clp->cl_lock); + spin_lock(&clp->cl_lock); + if (dp->dl_stid.sc_status & SC_STATUS_FREED) { + list_del_init(&dp->dl_recall_lru); + goto out; } + list_add(&dp->dl_recall_lru, &clp->cl_revoked); + dp->dl_stid.sc_status |= SC_STATUS_FREEABLE; +out: + spin_unlock(&clp->cl_lock); destroy_unhashed_deleg(dp); } @@ -1780,6 +1806,7 @@ void nfsd4_revoke_states(struct net *net, struct super_block *sb) mutex_unlock(&stp->st_mutex); break; case SC_TYPE_DELEG: + refcount_inc(&stid->sc_count); dp = delegstateid(stid); spin_lock(&state_lock); if (!unhash_delegation_locked( @@ -6545,6 +6572,7 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); if (!state_expired(<, dp->dl_time)) break; + refcount_inc(&dp->dl_stid.sc_count); unhash_delegation_locked(dp, SC_STATUS_REVOKED); list_add(&dp->dl_recall_lru, &reaplist); } @@ -7157,7 +7185,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, s->sc_status |= SC_STATUS_CLOSED; spin_unlock(&s->sc_lock); dp = delegstateid(s); - list_del_init(&dp->dl_recall_lru); + if (s->sc_status & SC_STATUS_FREEABLE) + list_del_init(&dp->dl_recall_lru); + s->sc_status |= SC_STATUS_FREED; spin_unlock(&cl->cl_lock); nfs4_put_stid(s); ret = nfs_ok; @@ -7487,7 +7517,9 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) return status; - status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, 0, &s, nn); + status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, + SC_STATUS_REVOKED | SC_STATUS_FREEABLE, + &s, nn); if (status) goto out; dp = delegstateid(s); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 79c743c01a47..35b3564c065f 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -114,6 +114,8 @@ struct nfs4_stid { /* For a deleg stateid kept around only to process free_stateid's: */ #define SC_STATUS_REVOKED BIT(1) #define SC_STATUS_ADMIN_REVOKED BIT(2) +#define SC_STATUS_FREEABLE BIT(3) +#define SC_STATUS_FREED BIT(4) unsigned short sc_status; struct list_head sc_cp_list; -- 2.50.1 From d5ff2fb2e7167e9483846e34148e60c0c016a1f6 Mon Sep 17 00:00:00 2001 From: Yang Erkun Date: Mon, 21 Oct 2024 16:25:40 +0800 Subject: [PATCH 13/16] nfsd: cancel nfsd_shrinker_work using sync mode in nfs4_state_shutdown_net In the normal case, when we excute `echo 0 > /proc/fs/nfsd/threads`, the function `nfs4_state_destroy_net` in `nfs4_state_shutdown_net` will release all resources related to the hashed `nfs4_client`. If the `nfsd_client_shrinker` is running concurrently, the `expire_client` function will first unhash this client and then destroy it. This can lead to the following warning. Additionally, numerous use-after-free errors may occur as well. nfsd_client_shrinker echo 0 > /proc/fs/nfsd/threads expire_client nfsd_shutdown_net unhash_client ... nfs4_state_shutdown_net /* won't wait shrinker exit */ /* cancel_work(&nn->nfsd_shrinker_work) * nfsd_file for this /* won't destroy unhashed client1 */ * client1 still alive nfs4_state_destroy_net */ nfsd_file_cache_shutdown /* trigger warning */ kmem_cache_destroy(nfsd_file_slab) kmem_cache_destroy(nfsd_file_mark_slab) /* release nfsd_file and mark */ __destroy_client ==================================================================== BUG nfsd_file (Not tainted): Objects remaining in nfsd_file on __kmem_cache_shutdown() -------------------------------------------------------------------- CPU: 4 UID: 0 PID: 764 Comm: sh Not tainted 6.12.0-rc3+ #1 dump_stack_lvl+0x53/0x70 slab_err+0xb0/0xf0 __kmem_cache_shutdown+0x15c/0x310 kmem_cache_destroy+0x66/0x160 nfsd_file_cache_shutdown+0xac/0x210 [nfsd] nfsd_destroy_serv+0x251/0x2a0 [nfsd] nfsd_svc+0x125/0x1e0 [nfsd] write_threads+0x16a/0x2a0 [nfsd] nfsctl_transaction_write+0x74/0xa0 [nfsd] vfs_write+0x1a5/0x6d0 ksys_write+0xc1/0x160 do_syscall_64+0x5f/0x170 entry_SYSCALL_64_after_hwframe+0x76/0x7e ==================================================================== BUG nfsd_file_mark (Tainted: G B W ): Objects remaining nfsd_file_mark on __kmem_cache_shutdown() -------------------------------------------------------------------- dump_stack_lvl+0x53/0x70 slab_err+0xb0/0xf0 __kmem_cache_shutdown+0x15c/0x310 kmem_cache_destroy+0x66/0x160 nfsd_file_cache_shutdown+0xc8/0x210 [nfsd] nfsd_destroy_serv+0x251/0x2a0 [nfsd] nfsd_svc+0x125/0x1e0 [nfsd] write_threads+0x16a/0x2a0 [nfsd] nfsctl_transaction_write+0x74/0xa0 [nfsd] vfs_write+0x1a5/0x6d0 ksys_write+0xc1/0x160 do_syscall_64+0x5f/0x170 entry_SYSCALL_64_after_hwframe+0x76/0x7e To resolve this issue, cancel `nfsd_shrinker_work` using synchronous mode in nfs4_state_shutdown_net. Fixes: 7c24fa225081 ("NFSD: replace delayed_work with work_struct for nfsd_client_shrinker") Signed-off-by: Yang Erkun Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d1a2c677be7e..551d2958ec29 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -8716,7 +8716,7 @@ nfs4_state_shutdown_net(struct net *net) struct nfsd_net *nn = net_generic(net, nfsd_net_id); shrinker_free(nn->nfsd_client_shrinker); - cancel_work(&nn->nfsd_shrinker_work); + cancel_work_sync(&nn->nfsd_shrinker_work); cancel_delayed_work_sync(&nn->laundromat_work); locks_end_grace(&nn->nfsd4_manager); -- 2.50.1 From 63fab04cbd0f96191b6e5beedc3b643b01c15889 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 26 Oct 2024 12:02:38 -0400 Subject: [PATCH 14/16] NFSD: Initialize struct nfsd4_copy earlier Ensure the refcount and async_copies fields are initialized early. cleanup_async_copy() will reference these fields if an error occurs in nfsd4_copy(). If they are not correctly initialized, at the very least, a refcount underflow occurs. Reported-by: Olga Kornievskaia Fixes: aadc3bbea163 ("NFSD: Limit the number of concurrent async COPY operations") Reviewed-by: Jeff Layton Tested-by: Olga Kornievskaia Signed-off-by: Chuck Lever --- fs/nfsd/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index b5a6bf4f459f..5fd1ce3fc8fb 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1841,14 +1841,14 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!async_copy) goto out_err; async_copy->cp_nn = nn; + INIT_LIST_HEAD(&async_copy->copies); + refcount_set(&async_copy->refcount, 1); /* Arbitrary cap on number of pending async copy operations */ if (atomic_inc_return(&nn->pending_async_copies) > (int)rqstp->rq_pool->sp_nrthreads) { atomic_dec(&nn->pending_async_copies); goto out_err; } - INIT_LIST_HEAD(&async_copy->copies); - refcount_set(&async_copy->refcount, 1); async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL); if (!async_copy->cp_src) goto out_err; -- 2.50.1 From 8286f8b622990194207df9ab852e0f87c60d35e9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 29 Oct 2024 15:27:19 -0400 Subject: [PATCH 15/16] NFSD: Never decrement pending_async_copies on error The error flow in nfsd4_copy() calls cleanup_async_copy(), which already decrements nn->pending_async_copies. Reported-by: Olga Kornievskaia Fixes: aadc3bbea163 ("NFSD: Limit the number of concurrent async COPY operations") Signed-off-by: Chuck Lever --- fs/nfsd/nfs4proc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 5fd1ce3fc8fb..d32f2dfd148f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1845,10 +1845,8 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, refcount_set(&async_copy->refcount, 1); /* Arbitrary cap on number of pending async copy operations */ if (atomic_inc_return(&nn->pending_async_copies) > - (int)rqstp->rq_pool->sp_nrthreads) { - atomic_dec(&nn->pending_async_copies); + (int)rqstp->rq_pool->sp_nrthreads) goto out_err; - } async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL); if (!async_copy->cp_src) goto out_err; -- 2.50.1 From 63a81588cd2025e75fbaf30b65930b76825c456f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 30 Oct 2024 16:11:30 -0400 Subject: [PATCH 16/16] rpcrdma: Always release the rpcrdma_device's xa_array Dai pointed out that the xa_init_flags() in rpcrdma_add_one() needs to have a matching xa_destroy() in rpcrdma_remove_one() to release underlying memory that the xarray might have accrued during operation. Reported-by: Dai Ngo Fixes: 7e86845a0346 ("rpcrdma: Implement generic device removal") Signed-off-by: Chuck Lever --- net/sunrpc/xprtrdma/ib_client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c index 8507cd4d8921..28c68b5f6823 100644 --- a/net/sunrpc/xprtrdma/ib_client.c +++ b/net/sunrpc/xprtrdma/ib_client.c @@ -153,6 +153,7 @@ static void rpcrdma_remove_one(struct ib_device *device, } trace_rpcrdma_client_remove_one_done(device); + xa_destroy(&rd->rd_xa); kfree(rd); } -- 2.50.1