summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4xdr.c
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2011-12-07 11:55:27 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-01-05 10:42:42 -0500
commitbf118a342f10dafe44b14451a1392c3254629a1f (patch)
tree50c2800c7c92f8e47e4a0a440e2a97847f678e19 /fs/nfs/nfs4xdr.c
parent3476f114addb7b96912840a234702f660a1f460b (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.c31
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
4959static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, 4961static 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
6033out: 6048out:
6034 return status; 6049 return status;