diff options
author | Andy Adamson <andros@netapp.com> | 2011-12-07 11:55:27 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-01-05 10:42:42 -0500 |
commit | bf118a342f10dafe44b14451a1392c3254629a1f (patch) | |
tree | 50c2800c7c92f8e47e4a0a440e2a97847f678e19 /fs/nfs/nfs4xdr.c | |
parent | 3476f114addb7b96912840a234702f660a1f460b (diff) |
NFSv4: include bitmap in nfsv4 get acl data
The NFSv4 bitmap size is unbounded: a server can return an arbitrary
sized bitmap in an FATTR4_WORD0_ACL request. Replace using the
nfs4_fattr_bitmap_maxsz as a guess to the maximum bitmask returned by a server
with the inclusion of the bitmap (xdr length plus bitmasks) and the acl data
xdr length to the (cached) acl page data.
This is a general solution to commit e5012d1f "NFSv4.1: update
nfs4_fattr_bitmap_maxsz" and fixes hitting a BUG_ON in xdr_shrink_bufhead
when getting ACLs.
Fix a bug in decode_getacl that returned -EINVAL on ACLs > page when getxattr
was called with a NULL buffer, preventing ACL > PAGE_SIZE from being retrieved.
Cc: stable@kernel.org
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4xdr.c')
-rw-r--r-- | fs/nfs/nfs4xdr.c | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index e6161b213ed1..dcaf69309d8e 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -2517,11 +2517,13 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, | |||
2517 | encode_compound_hdr(xdr, req, &hdr); | 2517 | encode_compound_hdr(xdr, req, &hdr); |
2518 | encode_sequence(xdr, &args->seq_args, &hdr); | 2518 | encode_sequence(xdr, &args->seq_args, &hdr); |
2519 | encode_putfh(xdr, args->fh, &hdr); | 2519 | encode_putfh(xdr, args->fh, &hdr); |
2520 | replen = hdr.replen + op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz + 1; | 2520 | replen = hdr.replen + op_decode_hdr_maxsz + 1; |
2521 | encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr); | 2521 | encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr); |
2522 | 2522 | ||
2523 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, | 2523 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, |
2524 | args->acl_pages, args->acl_pgbase, args->acl_len); | 2524 | args->acl_pages, args->acl_pgbase, args->acl_len); |
2525 | xdr_set_scratch_buffer(xdr, page_address(args->acl_scratch), PAGE_SIZE); | ||
2526 | |||
2525 | encode_nops(&hdr); | 2527 | encode_nops(&hdr); |
2526 | } | 2528 | } |
2527 | 2529 | ||
@@ -4957,17 +4959,18 @@ decode_restorefh(struct xdr_stream *xdr) | |||
4957 | } | 4959 | } |
4958 | 4960 | ||
4959 | static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | 4961 | static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, |
4960 | size_t *acl_len) | 4962 | struct nfs_getaclres *res) |
4961 | { | 4963 | { |
4962 | __be32 *savep; | 4964 | __be32 *savep, *bm_p; |
4963 | uint32_t attrlen, | 4965 | uint32_t attrlen, |
4964 | bitmap[3] = {0}; | 4966 | bitmap[3] = {0}; |
4965 | struct kvec *iov = req->rq_rcv_buf.head; | 4967 | struct kvec *iov = req->rq_rcv_buf.head; |
4966 | int status; | 4968 | int status; |
4967 | 4969 | ||
4968 | *acl_len = 0; | 4970 | res->acl_len = 0; |
4969 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) | 4971 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) |
4970 | goto out; | 4972 | goto out; |
4973 | bm_p = xdr->p; | ||
4971 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) | 4974 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) |
4972 | goto out; | 4975 | goto out; |
4973 | if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) | 4976 | if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) |
@@ -4979,18 +4982,30 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
4979 | size_t hdrlen; | 4982 | size_t hdrlen; |
4980 | u32 recvd; | 4983 | u32 recvd; |
4981 | 4984 | ||
4985 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words | ||
4986 | * are stored with the acl data to handle the problem of | ||
4987 | * variable length bitmaps.*/ | ||
4988 | xdr->p = bm_p; | ||
4989 | res->acl_data_offset = be32_to_cpup(bm_p) + 2; | ||
4990 | res->acl_data_offset <<= 2; | ||
4991 | |||
4982 | /* We ignore &savep and don't do consistency checks on | 4992 | /* We ignore &savep and don't do consistency checks on |
4983 | * the attr length. Let userspace figure it out.... */ | 4993 | * the attr length. Let userspace figure it out.... */ |
4984 | hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; | 4994 | hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; |
4995 | attrlen += res->acl_data_offset; | ||
4985 | recvd = req->rq_rcv_buf.len - hdrlen; | 4996 | recvd = req->rq_rcv_buf.len - hdrlen; |
4986 | if (attrlen > recvd) { | 4997 | if (attrlen > recvd) { |
4987 | dprintk("NFS: server cheating in getattr" | 4998 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { |
4988 | " acl reply: attrlen %u > recvd %u\n", | 4999 | /* getxattr interface called with a NULL buf */ |
5000 | res->acl_len = attrlen; | ||
5001 | goto out; | ||
5002 | } | ||
5003 | dprintk("NFS: acl reply: attrlen %u > recvd %u\n", | ||
4989 | attrlen, recvd); | 5004 | attrlen, recvd); |
4990 | return -EINVAL; | 5005 | return -EINVAL; |
4991 | } | 5006 | } |
4992 | xdr_read_pages(xdr, attrlen); | 5007 | xdr_read_pages(xdr, attrlen); |
4993 | *acl_len = attrlen; | 5008 | res->acl_len = attrlen; |
4994 | } else | 5009 | } else |
4995 | status = -EOPNOTSUPP; | 5010 | status = -EOPNOTSUPP; |
4996 | 5011 | ||
@@ -6028,7 +6043,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, | |||
6028 | status = decode_putfh(xdr); | 6043 | status = decode_putfh(xdr); |
6029 | if (status) | 6044 | if (status) |
6030 | goto out; | 6045 | goto out; |
6031 | status = decode_getacl(xdr, rqstp, &res->acl_len); | 6046 | status = decode_getacl(xdr, rqstp, res); |
6032 | 6047 | ||
6033 | out: | 6048 | out: |
6034 | return status; | 6049 | return status; |