nfsd: implement the xattr functions and en/decode logic
Implement the main entry points for the *XATTR operations. Add functions to calculate the reply size for the user extended attribute operations, and implement the XDR encode / decode logic for these operations. Add the user extended attributes operations to nfsd4_ops. Signed-off-by: Frank van der Linden <fllinden@amazon.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
parent
6178713bd4
commit
23e50fe3a5
|
@ -2097,6 +2097,68 @@ out:
|
|||
}
|
||||
#endif /* CONFIG_NFSD_PNFS */
|
||||
|
||||
static __be32
|
||||
nfsd4_getxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
struct nfsd4_getxattr *getxattr = &u->getxattr;
|
||||
|
||||
return nfsd_getxattr(rqstp, &cstate->current_fh,
|
||||
getxattr->getxa_name, &getxattr->getxa_buf,
|
||||
&getxattr->getxa_len);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_setxattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
struct nfsd4_setxattr *setxattr = &u->setxattr;
|
||||
__be32 ret;
|
||||
|
||||
if (opens_in_grace(SVC_NET(rqstp)))
|
||||
return nfserr_grace;
|
||||
|
||||
ret = nfsd_setxattr(rqstp, &cstate->current_fh, setxattr->setxa_name,
|
||||
setxattr->setxa_buf, setxattr->setxa_len,
|
||||
setxattr->setxa_flags);
|
||||
|
||||
if (!ret)
|
||||
set_change_info(&setxattr->setxa_cinfo, &cstate->current_fh);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_listxattrs(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
/*
|
||||
* Get the entire list, then copy out only the user attributes
|
||||
* in the encode function.
|
||||
*/
|
||||
return nfsd_listxattr(rqstp, &cstate->current_fh,
|
||||
&u->listxattrs.lsxa_buf, &u->listxattrs.lsxa_len);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_removexattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
struct nfsd4_removexattr *removexattr = &u->removexattr;
|
||||
__be32 ret;
|
||||
|
||||
if (opens_in_grace(SVC_NET(rqstp)))
|
||||
return nfserr_grace;
|
||||
|
||||
ret = nfsd_removexattr(rqstp, &cstate->current_fh,
|
||||
removexattr->rmxa_name);
|
||||
|
||||
if (!ret)
|
||||
set_change_info(&removexattr->rmxa_cinfo, &cstate->current_fh);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* NULL call.
|
||||
*/
|
||||
|
@ -2706,6 +2768,42 @@ static inline u32 nfsd4_seek_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
|||
return (op_encode_hdr_size + 3) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_getxattr_rsize(struct svc_rqst *rqstp,
|
||||
struct nfsd4_op *op)
|
||||
{
|
||||
u32 maxcount, rlen;
|
||||
|
||||
maxcount = svc_max_payload(rqstp);
|
||||
rlen = min_t(u32, XATTR_SIZE_MAX, maxcount);
|
||||
|
||||
return (op_encode_hdr_size + 1 + XDR_QUADLEN(rlen)) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_setxattr_rsize(struct svc_rqst *rqstp,
|
||||
struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz)
|
||||
* sizeof(__be32);
|
||||
}
|
||||
static inline u32 nfsd4_listxattrs_rsize(struct svc_rqst *rqstp,
|
||||
struct nfsd4_op *op)
|
||||
{
|
||||
u32 maxcount, rlen;
|
||||
|
||||
maxcount = svc_max_payload(rqstp);
|
||||
rlen = min(op->u.listxattrs.lsxa_maxcount, maxcount);
|
||||
|
||||
return (op_encode_hdr_size + 4 + XDR_QUADLEN(rlen)) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_removexattr_rsize(struct svc_rqst *rqstp,
|
||||
struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz)
|
||||
* sizeof(__be32);
|
||||
}
|
||||
|
||||
|
||||
static const struct nfsd4_operation nfsd4_ops[] = {
|
||||
[OP_ACCESS] = {
|
||||
.op_func = nfsd4_access,
|
||||
|
@ -3087,6 +3185,28 @@ static const struct nfsd4_operation nfsd4_ops[] = {
|
|||
.op_name = "OP_COPY_NOTIFY",
|
||||
.op_rsize_bop = nfsd4_copy_notify_rsize,
|
||||
},
|
||||
[OP_GETXATTR] = {
|
||||
.op_func = nfsd4_getxattr,
|
||||
.op_name = "OP_GETXATTR",
|
||||
.op_rsize_bop = nfsd4_getxattr_rsize,
|
||||
},
|
||||
[OP_SETXATTR] = {
|
||||
.op_func = nfsd4_setxattr,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_SETXATTR",
|
||||
.op_rsize_bop = nfsd4_setxattr_rsize,
|
||||
},
|
||||
[OP_LISTXATTRS] = {
|
||||
.op_func = nfsd4_listxattrs,
|
||||
.op_name = "OP_LISTXATTRS",
|
||||
.op_rsize_bop = nfsd4_listxattrs_rsize,
|
||||
},
|
||||
[OP_REMOVEXATTR] = {
|
||||
.op_func = nfsd4_removexattr,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_REMOVEXATTR",
|
||||
.op_rsize_bop = nfsd4_removexattr_rsize,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <linux/pagemap.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/addr.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <uapi/linux/xattr.h>
|
||||
|
||||
#include "idmap.h"
|
||||
#include "acl.h"
|
||||
|
@ -1877,6 +1879,208 @@ nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
|
|||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR data that is more than PAGE_SIZE in size is normally part of a
|
||||
* read or write. However, the size of extended attributes is limited
|
||||
* by the maximum request size, and then further limited by the underlying
|
||||
* filesystem limits. This can exceed PAGE_SIZE (currently, XATTR_SIZE_MAX
|
||||
* is 64k). Since there is no kvec- or page-based interface to xattrs,
|
||||
* and we're not dealing with contiguous pages, we need to do some copying.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decode data into buffer. Uses head and pages constructed by
|
||||
* svcxdr_construct_vector.
|
||||
*/
|
||||
static __be32
|
||||
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct kvec *head,
|
||||
struct page **pages, char **bufp, u32 buflen)
|
||||
{
|
||||
char *tmp, *dp;
|
||||
u32 len;
|
||||
|
||||
if (buflen <= head->iov_len) {
|
||||
/*
|
||||
* We're in luck, the head has enough space. Just return
|
||||
* the head, no need for copying.
|
||||
*/
|
||||
*bufp = head->iov_base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp = svcxdr_tmpalloc(argp, buflen);
|
||||
if (tmp == NULL)
|
||||
return nfserr_jukebox;
|
||||
|
||||
dp = tmp;
|
||||
memcpy(dp, head->iov_base, head->iov_len);
|
||||
buflen -= head->iov_len;
|
||||
dp += head->iov_len;
|
||||
|
||||
while (buflen > 0) {
|
||||
len = min_t(u32, buflen, PAGE_SIZE);
|
||||
memcpy(dp, page_address(*pages), len);
|
||||
|
||||
buflen -= len;
|
||||
dp += len;
|
||||
pages++;
|
||||
}
|
||||
|
||||
*bufp = tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a user extended attribute name from the XDR buffer.
|
||||
* It will not have the "user." prefix, so prepend it.
|
||||
* Lastly, check for nul characters in the name.
|
||||
*/
|
||||
static __be32
|
||||
nfsd4_decode_xattr_name(struct nfsd4_compoundargs *argp, char **namep)
|
||||
{
|
||||
DECODE_HEAD;
|
||||
char *name, *sp, *dp;
|
||||
u32 namelen, cnt;
|
||||
|
||||
READ_BUF(4);
|
||||
namelen = be32_to_cpup(p++);
|
||||
|
||||
if (namelen > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN))
|
||||
return nfserr_nametoolong;
|
||||
|
||||
if (namelen == 0)
|
||||
goto xdr_error;
|
||||
|
||||
READ_BUF(namelen);
|
||||
|
||||
name = svcxdr_tmpalloc(argp, namelen + XATTR_USER_PREFIX_LEN + 1);
|
||||
if (!name)
|
||||
return nfserr_jukebox;
|
||||
|
||||
memcpy(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
|
||||
|
||||
/*
|
||||
* Copy the extended attribute name over while checking for 0
|
||||
* characters.
|
||||
*/
|
||||
sp = (char *)p;
|
||||
dp = name + XATTR_USER_PREFIX_LEN;
|
||||
cnt = namelen;
|
||||
|
||||
while (cnt-- > 0) {
|
||||
if (*sp == '\0')
|
||||
goto xdr_error;
|
||||
*dp++ = *sp++;
|
||||
}
|
||||
*dp = '\0';
|
||||
|
||||
*namep = name;
|
||||
|
||||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* A GETXATTR op request comes without a length specifier. We just set the
|
||||
* maximum length for the reply based on XATTR_SIZE_MAX and the maximum
|
||||
* channel reply size. nfsd_getxattr will probe the length of the xattr,
|
||||
* check it against getxa_len, and allocate + return the value.
|
||||
*/
|
||||
static __be32
|
||||
nfsd4_decode_getxattr(struct nfsd4_compoundargs *argp,
|
||||
struct nfsd4_getxattr *getxattr)
|
||||
{
|
||||
__be32 status;
|
||||
u32 maxcount;
|
||||
|
||||
status = nfsd4_decode_xattr_name(argp, &getxattr->getxa_name);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
maxcount = svc_max_payload(argp->rqstp);
|
||||
maxcount = min_t(u32, XATTR_SIZE_MAX, maxcount);
|
||||
|
||||
getxattr->getxa_len = maxcount;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
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_vector(argp, &head, pagelist,
|
||||
&setxattr->setxa_buf, size);
|
||||
}
|
||||
|
||||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_listxattrs(struct nfsd4_compoundargs *argp,
|
||||
struct nfsd4_listxattrs *listxattrs)
|
||||
{
|
||||
DECODE_HEAD;
|
||||
u32 maxcount;
|
||||
|
||||
READ_BUF(12);
|
||||
p = xdr_decode_hyper(p, &listxattrs->lsxa_cookie);
|
||||
|
||||
/*
|
||||
* If the cookie is too large to have even one user.x attribute
|
||||
* plus trailing '\0' left in a maximum size buffer, it's invalid.
|
||||
*/
|
||||
if (listxattrs->lsxa_cookie >=
|
||||
(XATTR_LIST_MAX / (XATTR_USER_PREFIX_LEN + 2)))
|
||||
return nfserr_badcookie;
|
||||
|
||||
maxcount = be32_to_cpup(p++);
|
||||
if (maxcount < 8)
|
||||
/* Always need at least 2 words (length and one character) */
|
||||
return nfserr_inval;
|
||||
|
||||
maxcount = min(maxcount, svc_max_payload(argp->rqstp));
|
||||
listxattrs->lsxa_maxcount = maxcount;
|
||||
|
||||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_removexattr(struct nfsd4_compoundargs *argp,
|
||||
struct nfsd4_removexattr *removexattr)
|
||||
{
|
||||
return nfsd4_decode_xattr_name(argp, &removexattr->rmxa_name);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p)
|
||||
{
|
||||
|
@ -1973,6 +2177,11 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
|
|||
[OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek,
|
||||
[OP_WRITE_SAME] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_CLONE] = (nfsd4_dec)nfsd4_decode_clone,
|
||||
/* RFC 8276 extended atributes operations */
|
||||
[OP_GETXATTR] = (nfsd4_dec)nfsd4_decode_getxattr,
|
||||
[OP_SETXATTR] = (nfsd4_dec)nfsd4_decode_setxattr,
|
||||
[OP_LISTXATTRS] = (nfsd4_dec)nfsd4_decode_listxattrs,
|
||||
[OP_REMOVEXATTR] = (nfsd4_dec)nfsd4_decode_removexattr,
|
||||
};
|
||||
|
||||
static inline bool
|
||||
|
@ -4458,6 +4667,241 @@ nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
|
|||
return nfserr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode kmalloc-ed buffer in to XDR stream.
|
||||
*/
|
||||
static int
|
||||
nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen)
|
||||
{
|
||||
u32 cplen;
|
||||
__be32 *p;
|
||||
|
||||
cplen = min_t(unsigned long, buflen,
|
||||
((void *)xdr->end - (void *)xdr->p));
|
||||
p = xdr_reserve_space(xdr, cplen);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
|
||||
memcpy(p, buf, cplen);
|
||||
buf += cplen;
|
||||
buflen -= cplen;
|
||||
|
||||
while (buflen) {
|
||||
cplen = min_t(u32, buflen, PAGE_SIZE);
|
||||
p = xdr_reserve_space(xdr, cplen);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
|
||||
memcpy(p, buf, cplen);
|
||||
|
||||
if (cplen < PAGE_SIZE) {
|
||||
/*
|
||||
* We're done, with a length that wasn't page
|
||||
* aligned, so possibly not word aligned. Pad
|
||||
* any trailing bytes with 0.
|
||||
*/
|
||||
xdr_encode_opaque_fixed(p, NULL, cplen);
|
||||
break;
|
||||
}
|
||||
|
||||
buflen -= PAGE_SIZE;
|
||||
buf += PAGE_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_getxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_getxattr *getxattr)
|
||||
{
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
__be32 *p, err;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
|
||||
*p = cpu_to_be32(getxattr->getxa_len);
|
||||
|
||||
if (getxattr->getxa_len == 0)
|
||||
return 0;
|
||||
|
||||
err = nfsd4_vbuf_to_stream(xdr, getxattr->getxa_buf,
|
||||
getxattr->getxa_len);
|
||||
|
||||
kvfree(getxattr->getxa_buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_setxattr(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_setxattr *setxattr)
|
||||
{
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 20);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
|
||||
encode_cinfo(p, &setxattr->setxa_cinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if there are cookie values that can be rejected outright.
|
||||
*/
|
||||
static __be32
|
||||
nfsd4_listxattr_validate_cookie(struct nfsd4_listxattrs *listxattrs,
|
||||
u32 *offsetp)
|
||||
{
|
||||
u64 cookie = listxattrs->lsxa_cookie;
|
||||
|
||||
/*
|
||||
* If the cookie is larger than the maximum number we can fit
|
||||
* in either the buffer we just got back from vfs_listxattr, or,
|
||||
* XDR-encoded, in the return buffer, it's invalid.
|
||||
*/
|
||||
if (cookie > (listxattrs->lsxa_len) / (XATTR_USER_PREFIX_LEN + 2))
|
||||
return nfserr_badcookie;
|
||||
|
||||
if (cookie > (listxattrs->lsxa_maxcount /
|
||||
(XDR_QUADLEN(XATTR_USER_PREFIX_LEN + 2) + 4)))
|
||||
return nfserr_badcookie;
|
||||
|
||||
*offsetp = (u32)cookie;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_listxattrs *listxattrs)
|
||||
{
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
u32 cookie_offset, count_offset, eof;
|
||||
u32 left, xdrleft, slen, count;
|
||||
u32 xdrlen, offset;
|
||||
u64 cookie;
|
||||
char *sp;
|
||||
__be32 status;
|
||||
__be32 *p;
|
||||
u32 nuser;
|
||||
|
||||
eof = 1;
|
||||
|
||||
status = nfsd4_listxattr_validate_cookie(listxattrs, &offset);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Reserve space for the cookie and the name array count. Record
|
||||
* the offsets to save them later.
|
||||
*/
|
||||
cookie_offset = xdr->buf->len;
|
||||
count_offset = cookie_offset + 8;
|
||||
p = xdr_reserve_space(xdr, 12);
|
||||
if (!p) {
|
||||
status = nfserr_resource;
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
left = listxattrs->lsxa_len;
|
||||
sp = listxattrs->lsxa_buf;
|
||||
nuser = 0;
|
||||
|
||||
xdrleft = listxattrs->lsxa_maxcount;
|
||||
|
||||
while (left > 0 && xdrleft > 0) {
|
||||
slen = strlen(sp);
|
||||
|
||||
/*
|
||||
* Check if this a user. attribute, skip it if not.
|
||||
*/
|
||||
if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
|
||||
goto contloop;
|
||||
|
||||
slen -= XATTR_USER_PREFIX_LEN;
|
||||
xdrlen = 4 + ((slen + 3) & ~3);
|
||||
if (xdrlen > xdrleft) {
|
||||
if (count == 0) {
|
||||
/*
|
||||
* Can't even fit the first attribute name.
|
||||
*/
|
||||
status = nfserr_toosmall;
|
||||
goto out;
|
||||
}
|
||||
eof = 0;
|
||||
goto wreof;
|
||||
}
|
||||
|
||||
left -= XATTR_USER_PREFIX_LEN;
|
||||
sp += XATTR_USER_PREFIX_LEN;
|
||||
if (nuser++ < offset)
|
||||
goto contloop;
|
||||
|
||||
|
||||
p = xdr_reserve_space(xdr, xdrlen);
|
||||
if (!p) {
|
||||
status = nfserr_resource;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p = xdr_encode_opaque(p, sp, slen);
|
||||
|
||||
xdrleft -= xdrlen;
|
||||
count++;
|
||||
contloop:
|
||||
sp += slen + 1;
|
||||
left -= slen + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there were user attributes to copy, but we didn't copy
|
||||
* any, the offset was too large (e.g. the cookie was invalid).
|
||||
*/
|
||||
if (nuser > 0 && count == 0) {
|
||||
status = nfserr_badcookie;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wreof:
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p) {
|
||||
status = nfserr_resource;
|
||||
goto out;
|
||||
}
|
||||
*p = cpu_to_be32(eof);
|
||||
|
||||
cookie = offset + count;
|
||||
|
||||
write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8);
|
||||
count = htonl(count);
|
||||
write_bytes_to_xdr_buf(xdr->buf, count_offset, &count, 4);
|
||||
out:
|
||||
if (listxattrs->lsxa_len)
|
||||
kvfree(listxattrs->lsxa_buf);
|
||||
return status;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_removexattr(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_removexattr *removexattr)
|
||||
{
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 20);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
|
||||
p = encode_cinfo(p, &removexattr->rmxa_cinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef __be32(* nfsd4_enc)(struct nfsd4_compoundres *, __be32, void *);
|
||||
|
||||
/*
|
||||
|
@ -4547,6 +4991,12 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
|
|||
[OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek,
|
||||
[OP_WRITE_SAME] = (nfsd4_enc)nfsd4_encode_noop,
|
||||
[OP_CLONE] = (nfsd4_enc)nfsd4_encode_noop,
|
||||
|
||||
/* RFC 8276 extended atributes operations */
|
||||
[OP_GETXATTR] = (nfsd4_enc)nfsd4_encode_getxattr,
|
||||
[OP_SETXATTR] = (nfsd4_enc)nfsd4_encode_setxattr,
|
||||
[OP_LISTXATTRS] = (nfsd4_enc)nfsd4_encode_listxattrs,
|
||||
[OP_REMOVEXATTR] = (nfsd4_enc)nfsd4_encode_removexattr,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -165,7 +165,7 @@ Needs to be updated if more operations are defined in future.*/
|
|||
#define FIRST_NFS4_OP OP_ACCESS
|
||||
#define LAST_NFS40_OP OP_RELEASE_LOCKOWNER
|
||||
#define LAST_NFS41_OP OP_RECLAIM_COMPLETE
|
||||
#define LAST_NFS42_OP OP_CLONE
|
||||
#define LAST_NFS42_OP OP_REMOVEXATTR
|
||||
#define LAST_NFS4_OP LAST_NFS42_OP
|
||||
|
||||
enum nfsstat4 {
|
||||
|
|
Loading…
Reference in New Issue