Re: [PATCH v2 3/3] NFSD: Clean up symlink argument XDR decoders

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> On Jan 25, 2018, at 8:33 AM, Bruce Fields <bfields@xxxxxxxxxxxx> wrote:
> 
>> On Tue, Jan 23, 2018 at 02:30:57PM -0800, Chuck Lever wrote:
>> 
>> 
>>> On Jan 23, 2018, at 12:52 PM, Bruce Fields <bfields@xxxxxxxxxxxx> wrote:
>>> 
>>> On Mon, Jan 22, 2018 at 05:09:35PM -0800, Chuck Lever wrote:
>>>> 
>>>> 
>>>>> On Jan 22, 2018, at 2:00 PM, J. Bruce Fields <bfields@xxxxxxxxxxxx> wrote:
>>>>> 
>>>>> On Wed, Jan 03, 2018 at 03:42:35PM -0500, Chuck Lever wrote:
>>>>>> Move common code in NFSD's symlink arg decoders into a helper. The
>>>>>> immediate benefits include:
>>>>>> 
>>>>>> - one fewer data copies on transports that support DDP
>>>>>> - no memory allocation in the NFSv4 XDR decoder
>>>>>> - consistent error checking across all versions
>>>>>> - reduction of code duplication
>>>>>> - support for both legal forms of SYMLINK requests on RDMA
>>>>>> transports for all versions of NFS (in particular, NFSv2, for
>>>>>> completeness)
>>>>>> 
>>>>>> In the long term, this helper is an appropriate spot to perform a
>>>>>> per-transport call-out to fill the pathname argument using, say,
>>>>>> RDMA Reads.
>>>>>> 
>>>>>> Filling the pathname in the proc function also means that eventually
>>>>>> the incoming filehandle can be interpreted so that filesystem-
>>>>>> specific memory can be allocated as a sink for the pathname
>>>>>> argument, rather than using anonymous pages.
>>>>>> 
>>>>>> Wondering why the current code punts a zero-length SYMLINK. Is it
>>>>>> illegal to create a zero-length SYMLINK on Linux?
>>>>> 
>>>>> SYMLINK(2) says
>>>>> 
>>>>>    ENOENT A directory component in linkpath does not exist or is a
>>>>>    dangling symbolic link, or target or linkpath is an empty
>>>>>    string.
>>>>> 
>>>>> That doesn't explain the INVAL, or why this is the right place to be
>>>>> checking it.
>>>> 
>>>> RFC 1813:
>>>> 
>>>> NFS3ERR_IO
>>>>     NFS3ERR_ACCES
>>>>     NFS3ERR_EXIST
>>>>     NFS3ERR_NOTDIR
>>>>     NFS3ERR_NOSPC
>>>>     NFS3ERR_ROFS
>>>>     NFS3ERR_NAMETOOLONG
>>>>     NFS3ERR_DQUOT
>>>>     NFS3ERR_STALE
>>>>     NFS3ERR_BADHANDLE
>>>>     NFS3ERR_NOTSUPP
>>>>     NFS3ERR_SERVERFAULT
>>>> 
>>>> Interestingly, neither INVAL nor NOENT are valid
>>>> status codes for NFSv3 SYMLINK. NFS3ERR_NOTSUPP
>>>> might be closest, I suppose.
>>>> 
>>>> RFC 5661 says explicitly:
>>>> 
>>>> If the objname has a length of zero, or if objname does not obey the
>>>> UTF-8 definition, the error NFS4ERR_INVAL will be returned.
>>>> 
>>>> And lists these as valid status codes for CREATE(NF4LNK):
>>>> 
>>>> | NFS4ERR_ACCESS, NFS4ERR_ATTRNOTSUPP,       |
>>>> | NFS4ERR_BADCHAR, NFS4ERR_BADNAME,          |
>>>> | NFS4ERR_BADOWNER, NFS4ERR_BADTYPE,         |
>>>> | NFS4ERR_BADXDR, NFS4ERR_DEADSESSION,       |
>>>> | NFS4ERR_DELAY, NFS4ERR_DQUOT,              |
>>>> | NFS4ERR_EXIST, NFS4ERR_FHEXPIRED,          |
>>>> | NFS4ERR_INVAL, NFS4ERR_IO, NFS4ERR_MLINK,  |
>>>> | NFS4ERR_MOVED, NFS4ERR_NAMETOOLONG,        |
>>>> | NFS4ERR_NOFILEHANDLE, NFS4ERR_NOSPC,       |
>>>> | NFS4ERR_NOTDIR, NFS4ERR_OP_NOT_IN_SESSION, |
>>>> | NFS4ERR_PERM, NFS4ERR_REP_TOO_BIG,         |
>>>> | NFS4ERR_REP_TOO_BIG_TO_CACHE,              |
>>>> | NFS4ERR_REQ_TOO_BIG,                       |
>>>> | NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_ROFS,  |
>>>> | NFS4ERR_SERVERFAULT, NFS4ERR_STALE,        |
>>>> | NFS4ERR_TOO_MANY_OPS,                      |
>>>> | NFS4ERR_UNSAFE_COMPOUND                    |
>>>> 
>>>> 
>>>>> I'm a little nervous about the NULL termination in
>>>>> svc_fill_symlink_pathname; how do we know it's safe to write a zero
>>>>> there?  I haven't checked it carefully yet.
>>>> 
>>>> svc_fill_symlink_pathname grabs a whole fresh page
>>>> from @rqstp. It is safe to write bytes anywhere in
>>>> that page.
>>> 
>>> How do we know it's safe to grab another page?
>> 
>> It's safe in the v3 case because that's what the v3
>> decoder currently does. I think you are concerned
>> about the NFSv4 COMPOUND case on TCP, for arbitrarily
>> large compounds? Given that we have 257-ish pages
>> in the rqstp->rq_pages array (ie, that number is
>> bounded) I'm not sure how we can ever be sure there
>> are enough pages for all combinations of NFS
>> operations.
> 
> Right, I think it just depends on some maximum lengths being checked
> earlier.  So my worry is that this my violate some assumptions about how
> many pages we need to handle a request of a certain length.
> 
> I'll go remind myself how this works.  We need some documentation here
> at least.

And/or some helper functions that manage the use of rq_pages,
while updating rq_respages as needed.


> So I may end up postponing these after all....
> 
>>> (Could we run out of
>>> pages?  I'd probably know the answer if I'd rewritten this code
>>> recently, but I haven't and the details are swapped out....)
>>> 
>>> Also I don't think that's true in the first->iov_base == 0 case.
>> 
>> This case occurs when an RDMA transport has filled
>> in the first page of arg->pages with the symlink
>> contents. It's also a whole fresh page that was
>> obtained from rqstp->rq_pages.
> 
> OK, I wasn't following that case.
> 
> --b.
> 
>> 
>> Here, are you concerned about the client sending a
>> COMPOUND over RPC/RDMA that has more than one SYMLINK
>> operation? Our server currently supports only one Read
>> chunk per RPC.
>> 
>> 
>>> --b.
>>> 
>>>> 
>>>> 
>>>>> --g.
>>>>> 
>>>>>> 
>>>>>> Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx>
>>>>>> ---
>>>>>> fs/nfsd/nfs3proc.c         |   10 +++++++
>>>>>> fs/nfsd/nfs3xdr.c          |   51 ++++++++-------------------------
>>>>>> fs/nfsd/nfs4proc.c         |    7 +++++
>>>>>> fs/nfsd/nfs4xdr.c          |   10 +++++--
>>>>>> fs/nfsd/nfsproc.c          |   14 +++++----
>>>>>> fs/nfsd/nfsxdr.c           |   49 +++++++++++++++++++-------------
>>>>>> fs/nfsd/xdr.h              |    1 +
>>>>>> fs/nfsd/xdr3.h             |    1 +
>>>>>> fs/nfsd/xdr4.h             |    2 +
>>>>>> include/linux/sunrpc/svc.h |    2 +
>>>>>> net/sunrpc/svc.c           |   67 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>> 11 files changed, 146 insertions(+), 68 deletions(-)
>>>>>> 
>>>>>> diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
>>>>>> index 2dd95eb..6259a4b 100644
>>>>>> --- a/fs/nfsd/nfs3proc.c
>>>>>> +++ b/fs/nfsd/nfs3proc.c
>>>>>> @@ -283,6 +283,16 @@
>>>>>>    struct nfsd3_diropres *resp = rqstp->rq_resp;
>>>>>>    __be32    nfserr;
>>>>>> 
>>>>>> +    if (argp->tlen == 0)
>>>>>> +        RETURN_STATUS(nfserr_inval);
>>>>>> +    if (argp->tlen > NFS3_MAXPATHLEN)
>>>>>> +        RETURN_STATUS(nfserr_nametoolong);
>>>>>> +
>>>>>> +    argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
>>>>>> +                        argp->tlen);
>>>>>> +    if (IS_ERR(argp->tname))
>>>>>> +        RETURN_STATUS(nfserrno(PTR_ERR(argp->tname)));
>>>>>> +
>>>>>>    dprintk("nfsd: SYMLINK(3)  %s %.*s -> %.*s\n",
>>>>>>                SVCFH_fmt(&argp->ffh),
>>>>>>                argp->flen, argp->fname,
>>>>>> diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
>>>>>> index 240cdb0e..78b555b 100644
>>>>>> --- a/fs/nfsd/nfs3xdr.c
>>>>>> +++ b/fs/nfsd/nfs3xdr.c
>>>>>> @@ -452,51 +452,24 @@ void fill_post_wcc(struct svc_fh *fhp)
>>>>>> nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
>>>>>> {
>>>>>>    struct nfsd3_symlinkargs *args = rqstp->rq_argp;
>>>>>> -    unsigned int len, avail;
>>>>>> -    char *old, *new;
>>>>>> -    struct kvec *vec;
>>>>>> +    char *base = (char *)p;
>>>>>> +    size_t dlen;
>>>>>> 
>>>>>>    if (!(p = decode_fh(p, &args->ffh)) ||
>>>>>> -        !(p = decode_filename(p, &args->fname, &args->flen))
>>>>>> -        )
>>>>>> +        !(p = decode_filename(p, &args->fname, &args->flen)))
>>>>>>        return 0;
>>>>>>    p = decode_sattr3(p, &args->attrs);
>>>>>> 
>>>>>> -    /* now decode the pathname, which might be larger than the first page.
>>>>>> -     * As we have to check for nul's anyway, we copy it into a new page
>>>>>> -     * This page appears in the rq_res.pages list, but as pages_len is always
>>>>>> -     * 0, it won't get in the way
>>>>>> -     */
>>>>>> -    len = ntohl(*p++);
>>>>>> -    if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
>>>>>> -        return 0;
>>>>>> -    args->tname = new = page_address(*(rqstp->rq_next_page++));
>>>>>> -    args->tlen = len;
>>>>>> -    /* first copy and check from the first page */
>>>>>> -    old = (char*)p;
>>>>>> -    vec = &rqstp->rq_arg.head[0];
>>>>>> -    if ((void *)old > vec->iov_base + vec->iov_len)
>>>>>> -        return 0;
>>>>>> -    avail = vec->iov_len - (old - (char*)vec->iov_base);
>>>>>> -    while (len && avail && *old) {
>>>>>> -        *new++ = *old++;
>>>>>> -        len--;
>>>>>> -        avail--;
>>>>>> -    }
>>>>>> -    /* now copy next page if there is one */
>>>>>> -    if (len && !avail && rqstp->rq_arg.page_len) {
>>>>>> -        avail = min_t(unsigned int, rqstp->rq_arg.page_len, PAGE_SIZE);
>>>>>> -        old = page_address(rqstp->rq_arg.pages[0]);
>>>>>> -    }
>>>>>> -    while (len && avail && *old) {
>>>>>> -        *new++ = *old++;
>>>>>> -        len--;
>>>>>> -        avail--;
>>>>>> -    }
>>>>>> -    *new = '\0';
>>>>>> -    if (len)
>>>>>> -        return 0;
>>>>>> +    args->tlen = ntohl(*p++);
>>>>>> 
>>>>>> +    args->first.iov_base = p;
>>>>>> +    args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
>>>>>> +    args->first.iov_len -= (char *)p - base;
>>>>>> +
>>>>>> +    dlen = args->first.iov_len + rqstp->rq_arg.page_len +
>>>>>> +           rqstp->rq_arg.tail[0].iov_len;
>>>>>> +    if (dlen < XDR_QUADLEN(args->tlen) << 2)
>>>>>> +        return 0;
>>>>>>    return 1;
>>>>>> }
>>>>>> 
>>>>>> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
>>>>>> index 5029b96..36bd1f7 100644
>>>>>> --- a/fs/nfsd/nfs4proc.c
>>>>>> +++ b/fs/nfsd/nfs4proc.c
>>>>>> @@ -605,6 +605,13 @@ static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net)
>>>>>> 
>>>>>>    switch (create->cr_type) {
>>>>>>    case NF4LNK:
>>>>>> +        if (create->cr_datalen > NFS4_MAXPATHLEN)
>>>>>> +            return nfserr_nametoolong;
>>>>>> +        create->cr_data =
>>>>>> +            svc_fill_symlink_pathname(rqstp, &create->cr_first,
>>>>>> +                          create->cr_datalen);
>>>>>> +        if (IS_ERR(create->cr_data))
>>>>>> +            return nfserrno(PTR_ERR(create->cr_data));
>>>>>>        status = nfsd_symlink(rqstp, &cstate->current_fh,
>>>>>>                      create->cr_name, create->cr_namelen,
>>>>>>                      create->cr_data, &resfh);
>>>>>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>>>>>> index bd25230..d05384e 100644
>>>>>> --- a/fs/nfsd/nfs4xdr.c
>>>>>> +++ b/fs/nfsd/nfs4xdr.c
>>>>>> @@ -648,6 +648,7 @@ static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
>>>>>> static __be32
>>>>>> nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
>>>>>> {
>>>>>> +    struct kvec *head;
>>>>>>    DECODE_HEAD;
>>>>>> 
>>>>>>    READ_BUF(4);
>>>>>> @@ -656,10 +657,13 @@ static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
>>>>>>    case NF4LNK:
>>>>>>        READ_BUF(4);
>>>>>>        create->cr_datalen = be32_to_cpup(p++);
>>>>>> +        if (create->cr_datalen == 0)
>>>>>> +            return nfserr_inval;
>>>>>> +        head = argp->rqstp->rq_arg.head;
>>>>>> +        create->cr_first.iov_base = p;
>>>>>> +        create->cr_first.iov_len = head->iov_len;
>>>>>> +        create->cr_first.iov_len -= (char *)p - (char *)head->iov_base;
>>>>>>        READ_BUF(create->cr_datalen);
>>>>>> -        create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen);
>>>>>> -        if (!create->cr_data)
>>>>>> -            return nfserr_jukebox;
>>>>>>        break;
>>>>>>    case NF4BLK:
>>>>>>    case NF4CHR:
>>>>>> diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
>>>>>> index 1995ea6..f107f9f 100644
>>>>>> --- a/fs/nfsd/nfsproc.c
>>>>>> +++ b/fs/nfsd/nfsproc.c
>>>>>> @@ -449,17 +449,19 @@
>>>>>>    struct svc_fh    newfh;
>>>>>>    __be32        nfserr;
>>>>>> 
>>>>>> +    if (argp->tlen > NFS_MAXPATHLEN)
>>>>>> +        return nfserr_nametoolong;
>>>>>> +
>>>>>> +    argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
>>>>>> +                        argp->tlen);
>>>>>> +    if (IS_ERR(argp->tname))
>>>>>> +        return nfserrno(PTR_ERR(argp->tname));
>>>>>> +
>>>>>>    dprintk("nfsd: SYMLINK  %s %.*s -> %.*s\n",
>>>>>>        SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
>>>>>>        argp->tlen, argp->tname);
>>>>>> 
>>>>>>    fh_init(&newfh, NFS_FHSIZE);
>>>>>> -    /*
>>>>>> -     * Crazy hack: the request fits in a page, and already-decoded
>>>>>> -     * attributes follow argp->tname, so it's safe to just write a
>>>>>> -     * null to ensure it's null-terminated:
>>>>>> -     */
>>>>>> -    argp->tname[argp->tlen] = '\0';
>>>>>>    nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
>>>>>>                         argp->tname, &newfh);
>>>>>> 
>>>>>> diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
>>>>>> index 165e25e..8fcd047 100644
>>>>>> --- a/fs/nfsd/nfsxdr.c
>>>>>> +++ b/fs/nfsd/nfsxdr.c
>>>>>> @@ -71,22 +71,6 @@ __be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp)
>>>>>> }
>>>>>> 
>>>>>> static __be32 *
>>>>>> -decode_pathname(__be32 *p, char **namp, unsigned int *lenp)
>>>>>> -{
>>>>>> -    char        *name;
>>>>>> -    unsigned int    i;
>>>>>> -
>>>>>> -    if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
>>>>>> -        for (i = 0, name = *namp; i < *lenp; i++, name++) {
>>>>>> -            if (*name == '\0')
>>>>>> -                return NULL;
>>>>>> -        }
>>>>>> -    }
>>>>>> -
>>>>>> -    return p;
>>>>>> -}
>>>>>> -
>>>>>> -static __be32 *
>>>>>> decode_sattr(__be32 *p, struct iattr *iap)
>>>>>> {
>>>>>>    u32    tmp, tmp1;
>>>>>> @@ -383,14 +367,39 @@ __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *f
>>>>>> nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
>>>>>> {
>>>>>>    struct nfsd_symlinkargs *args = rqstp->rq_argp;
>>>>>> +    char *base = (char *)p;
>>>>>> +    size_t xdrlen;
>>>>>> 
>>>>>>    if (   !(p = decode_fh(p, &args->ffh))
>>>>>> -        || !(p = decode_filename(p, &args->fname, &args->flen))
>>>>>> -        || !(p = decode_pathname(p, &args->tname, &args->tlen)))
>>>>>> +        || !(p = decode_filename(p, &args->fname, &args->flen)))
>>>>>>        return 0;
>>>>>> -    p = decode_sattr(p, &args->attrs);
>>>>>> 
>>>>>> -    return xdr_argsize_check(rqstp, p);
>>>>>> +    args->tlen = ntohl(*p++);
>>>>>> +    if (args->tlen == 0)
>>>>>> +        return 0;
>>>>>> +
>>>>>> +    args->first.iov_base = p;
>>>>>> +    args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
>>>>>> +    args->first.iov_len -= (char *)p - base;
>>>>>> +
>>>>>> +    /* This request is never larger than a page. Therefore,
>>>>>> +     * transport will deliver either:
>>>>>> +     * 1. pathname in the pagelist -> sattr is in the tail.
>>>>>> +     * 2. everything in the head buffer -> sattr is in the head.
>>>>>> +     */
>>>>>> +    if (rqstp->rq_arg.page_len) {
>>>>>> +        if (args->tlen != rqstp->rq_arg.page_len)
>>>>>> +            return 0;
>>>>>> +        p = rqstp->rq_arg.tail[0].iov_base;
>>>>>> +    } else {
>>>>>> +        xdrlen = XDR_QUADLEN(args->tlen);
>>>>>> +        if (xdrlen > args->first.iov_len - (8 * sizeof(__be32)))
>>>>>> +            return 0;
>>>>>> +        p += xdrlen;
>>>>>> +    }
>>>>>> +    decode_sattr(p, &args->attrs);
>>>>>> +
>>>>>> +    return 1;
>>>>>> }
>>>>>> 
>>>>>> int
>>>>>> diff --git a/fs/nfsd/xdr.h b/fs/nfsd/xdr.h
>>>>>> index a765c41..ea7cca3 100644
>>>>>> --- a/fs/nfsd/xdr.h
>>>>>> +++ b/fs/nfsd/xdr.h
>>>>>> @@ -72,6 +72,7 @@ struct nfsd_symlinkargs {
>>>>>>    char *            tname;
>>>>>>    unsigned int        tlen;
>>>>>>    struct iattr        attrs;
>>>>>> +    struct kvec        first;
>>>>>> };
>>>>>> 
>>>>>> struct nfsd_readdirargs {
>>>>>> diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
>>>>>> index deccf7f..2cb29e9 100644
>>>>>> --- a/fs/nfsd/xdr3.h
>>>>>> +++ b/fs/nfsd/xdr3.h
>>>>>> @@ -90,6 +90,7 @@ struct nfsd3_symlinkargs {
>>>>>>    char *            tname;
>>>>>>    unsigned int        tlen;
>>>>>>    struct iattr        attrs;
>>>>>> +    struct kvec        first;
>>>>>> };
>>>>>> 
>>>>>> struct nfsd3_readdirargs {
>>>>>> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
>>>>>> index d56219d..b485cd1 100644
>>>>>> --- a/fs/nfsd/xdr4.h
>>>>>> +++ b/fs/nfsd/xdr4.h
>>>>>> @@ -110,6 +110,7 @@ struct nfsd4_create {
>>>>>>        struct {
>>>>>>            u32 datalen;
>>>>>>            char *data;
>>>>>> +            struct kvec first;
>>>>>>        } link;   /* NF4LNK */
>>>>>>        struct {
>>>>>>            u32 specdata1;
>>>>>> @@ -124,6 +125,7 @@ struct nfsd4_create {
>>>>>> };
>>>>>> #define cr_datalen    u.link.datalen
>>>>>> #define cr_data        u.link.data
>>>>>> +#define cr_first    u.link.first
>>>>>> #define cr_specdata1    u.dev.specdata1
>>>>>> #define cr_specdata2    u.dev.specdata2
>>>>>> 
>>>>>> diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
>>>>>> index 238b9ae..fd5846e 100644
>>>>>> --- a/include/linux/sunrpc/svc.h
>>>>>> +++ b/include/linux/sunrpc/svc.h
>>>>>> @@ -495,6 +495,8 @@ int           svc_register(const struct svc_serv *, struct net *, const int,
>>>>>> char *           svc_print_addr(struct svc_rqst *, char *, size_t);
>>>>>> unsigned int       svc_fill_write_vector(struct svc_rqst *rqstp,
>>>>>>                     struct kvec *first, size_t total);
>>>>>> +char          *svc_fill_symlink_pathname(struct svc_rqst *rqstp,
>>>>>> +                         struct kvec *first, size_t total);
>>>>>> 
>>>>>> #define    RPC_MAX_ADDRBUFLEN    (63U)
>>>>>> 
>>>>>> diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
>>>>>> index 759b668..fc93406 100644
>>>>>> --- a/net/sunrpc/svc.c
>>>>>> +++ b/net/sunrpc/svc.c
>>>>>> @@ -1578,3 +1578,70 @@ unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, struct kvec *first,
>>>>>>    return i;
>>>>>> }
>>>>>> EXPORT_SYMBOL_GPL(svc_fill_write_vector);
>>>>>> +
>>>>>> +/**
>>>>>> + * svc_fill_symlink_pathname - Construct pathname argument for VFS symlink call
>>>>>> + * @rqstp: svc_rqst to operate on
>>>>>> + * @first: buffer containing first section of pathname
>>>>>> + * @total: total length of the pathname argument
>>>>>> + *
>>>>>> + * Returns pointer to a NUL-terminated string, or an ERR_PTR. The buffer is
>>>>>> + * released automatically when @rqstp is recycled.
>>>>>> + */
>>>>>> +char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first,
>>>>>> +                size_t total)
>>>>>> +{
>>>>>> +    struct xdr_buf *arg = &rqstp->rq_arg;
>>>>>> +    struct page **pages;
>>>>>> +    char *result;
>>>>>> +
>>>>>> +    /* VFS API demands a NUL-terminated pathname. This function
>>>>>> +     * uses a page from @rqstp as the pathname buffer, to enable
>>>>>> +     * direct placement. Thus the total buffer size is PAGE_SIZE.
>>>>>> +     * Space in this buffer for NUL-termination requires that we
>>>>>> +     * cap the size of the returned symlink pathname just a
>>>>>> +     * little early.
>>>>>> +     */
>>>>>> +    if (total > PAGE_SIZE - 1)
>>>>>> +        return ERR_PTR(-ENAMETOOLONG);
>>>>>> +
>>>>>> +    /* Some types of transport can present the pathname entirely
>>>>>> +     * in rq_arg.pages. If not, then copy the pathname into one
>>>>>> +     * page.
>>>>>> +     */
>>>>>> +    pages = arg->pages;
>>>>>> +    WARN_ON_ONCE(arg->page_base != 0);
>>>>>> +    if (first->iov_base == 0) {
>>>>>> +        result = page_address(*pages);
>>>>>> +        result[total] = '\0';
>>>>>> +    } else {
>>>>>> +        size_t len, remaining;
>>>>>> +        char *dst;
>>>>>> +
>>>>>> +        result = page_address(*(rqstp->rq_next_page++));
>>>>>> +        dst = result;
>>>>>> +        remaining = total;
>>>>>> +
>>>>>> +        len = min_t(size_t, total, first->iov_len);
>>>>>> +        memcpy(dst, first->iov_base, len);
>>>>>> +        dst += len;
>>>>>> +        remaining -= len;
>>>>>> +
>>>>>> +        /* No more than one page left */
>>>>>> +        if (remaining) {
>>>>>> +            len = min_t(size_t, remaining, PAGE_SIZE);
>>>>>> +            memcpy(dst, page_address(*pages), len);
>>>>>> +            dst += len;
>>>>>> +        }
>>>>>> +
>>>>>> +        *dst = '\0';
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Sanity check: we don't allow the pathname argument to
>>>>>> +     * contain a NUL byte.
>>>>>> +     */
>>>>>> +    if (strlen(result) != total)
>>>>>> +        return ERR_PTR(-EINVAL);
>>>>>> +    return result;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname);
>>>> 
>>>> --
>>>> Chuck Lever
>> 
>> --
>> Chuck Lever
>> 
>> 

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux