> On Mar 20, 2020, at 12:47 PM, Frank van der Linden <fllinden@xxxxxxxxxx> wrote: > > Hi Chuck, > > On Thu, Mar 12, 2020 at 03:16:37PM -0400, Chuck Lever wrote: >>> +static __be32 >>> +nfsd4_decode_setxattr(struct nfsd4_compoundargs *argp, >>> + struct nfsd4_setxattr *setxattr) >>> +{ >>> + DECODE_HEAD; >>> + u32 flags, maxcount, size; >>> + struct kvec head; >>> + struct page **pagelist; >>> + >>> + READ_BUF(4); >>> + flags = be32_to_cpup(p++); >>> + >>> + if (flags > SETXATTR4_REPLACE) >>> + return nfserr_inval; >>> + setxattr->setxa_flags = flags; >>> + >>> + status = nfsd4_decode_xattr_name(argp, &setxattr->setxa_name); >>> + if (status) >>> + return status; >>> + >>> + maxcount = svc_max_payload(argp->rqstp); >>> + maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount); >>> + >>> + READ_BUF(4); >>> + size = be32_to_cpup(p++); >>> + if (size > maxcount) >>> + return nfserr_xattr2big; >>> + >>> + setxattr->setxa_len = size; >>> + if (size > 0) { >>> + status = svcxdr_construct_vector(argp, &head, &pagelist, size); >>> + if (status) >>> + return status; >>> + >>> + status = nfsd4_vbuf_from_stream(argp, &head, pagelist, >>> + &setxattr->setxa_buf, size); >>> + } >> >> Now I'm wondering if read_bytes_from_xdr_buf() might be adequate >> for this purpose, so you can avoid open-coding all of this logic. > > This took a little longer, I had to check my notes, Thanks for checking! > but basically the reasons for doing it this way are: > > * The nfsd decode path uses nfsd4_compoundargs, which doesn't have an > xdr_stream (it has a page array). So read_bytes_from_xdr_buf isn't > a natural fit. > * READ_BUF/read_buf don't deal with > PAGE_SIZE chunks, but xattrs may > be larger than that. > > The other code that deals with > PAGE_SIZE chunks is the write code. So, > I factored out some code from the write code, used that, and added a function > to process the resulting kvec / pagelist (nfs4_vbuf_from_stream). Yes, I only recently discovered that the xdr_stream helpers do not work for decoding data items that are page size or larger. For the record, my IMA prototype takes this approach for decode_fattr: + if (bmval[2] & FATTR4_WORD2_IMA) { + READ_BUF(4); + len += 4; + dummy32 = be32_to_cpup(p++); + READ_BUF(dummy32); + if (dummy32 > NFS4_MAXIMALEN) + return nfserr_badlabel; + *ima = svcxdr_tmpalloc(argp, sizeof(**ima)); + if (*ima == NULL) + return nfserr_jukebox; + + len += (XDR_QUADLEN(dummy32) << 2); + READMEM(buf, dummy32); + (*ima)->len = dummy32; + (*ima)->data = svcxdr_dupstr(argp, buf, dummy32); + if ((*ima)->data == NULL) + return nfserr_jukebox; + } else + ima = NULL; Although, an IMA blob is never larger than a page, IIRC. > There definitely seem to be several copy functions in both the client > and server code that basically do the same, but in slightly different ways, > depending on whether they use an XDR buf or not, whether the pages are > mapped or not, etc. Seems like a good candidate for a cleanup, but I > considered it to be out of scope for these patches. "out of scope" - probably so. Depending on Bruce's thinking, we can add this to the list of janitorial tasks. For my peace of mind, "from_stream" implies there _is_ an xdr_stream in use, even though the function does not have a struct xdr_stream parameter. Perhaps a different naming scheme would be wise. -- Chuck Lever