aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/nfs4proc.c31
-rw-r--r--fs/nfs/nfs4xdr.c14
-rw-r--r--include/linux/nfs_xdr.h2
3 files changed, 18 insertions, 29 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 6b94f2d52532..1e50326d00dd 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size
3739 struct nfs4_cached_acl *acl; 3739 struct nfs4_cached_acl *acl;
3740 size_t buflen = sizeof(*acl) + acl_len; 3740 size_t buflen = sizeof(*acl) + acl_len;
3741 3741
3742 if (pages && buflen <= PAGE_SIZE) { 3742 if (buflen <= PAGE_SIZE) {
3743 acl = kmalloc(buflen, GFP_KERNEL); 3743 acl = kmalloc(buflen, GFP_KERNEL);
3744 if (acl == NULL) 3744 if (acl == NULL)
3745 goto out; 3745 goto out;
@@ -3784,7 +3784,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
3784 }; 3784 };
3785 unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); 3785 unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
3786 int ret = -ENOMEM, i; 3786 int ret = -ENOMEM, i;
3787 size_t acl_len = 0;
3788 3787
3789 /* As long as we're doing a round trip to the server anyway, 3788 /* As long as we're doing a round trip to the server anyway,
3790 * let's be prepared for a page of acl data. */ 3789 * let's be prepared for a page of acl data. */
@@ -3807,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
3807 args.acl_len = npages * PAGE_SIZE; 3806 args.acl_len = npages * PAGE_SIZE;
3808 args.acl_pgbase = 0; 3807 args.acl_pgbase = 0;
3809 3808
3810 /* Let decode_getfacl know not to fail if the ACL data is larger than
3811 * the page we send as a guess */
3812 if (buf == NULL)
3813 res.acl_flags |= NFS4_ACL_LEN_REQUEST;
3814
3815 dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", 3809 dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n",
3816 __func__, buf, buflen, npages, args.acl_len); 3810 __func__, buf, buflen, npages, args.acl_len);
3817 ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), 3811 ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
@@ -3819,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
3819 if (ret) 3813 if (ret)
3820 goto out_free; 3814 goto out_free;
3821 3815
3822 acl_len = res.acl_len; 3816 /* Handle the case where the passed-in buffer is too short */
3823 if (acl_len > args.acl_len) 3817 if (res.acl_flags & NFS4_ACL_TRUNC) {
3824 nfs4_write_cached_acl(inode, NULL, 0, acl_len); 3818 /* Did the user only issue a request for the acl length? */
3825 else 3819 if (buf == NULL)
3826 nfs4_write_cached_acl(inode, pages, res.acl_data_offset, 3820 goto out_ok;
3827 acl_len);
3828 if (buf) {
3829 ret = -ERANGE; 3821 ret = -ERANGE;
3830 if (acl_len > buflen) 3822 goto out_free;
3831 goto out_free;
3832 _copy_from_pages(buf, pages, res.acl_data_offset,
3833 acl_len);
3834 } 3823 }
3835 ret = acl_len; 3824 nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
3825 if (buf)
3826 _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
3827out_ok:
3828 ret = res.acl_len;
3836out_free: 3829out_free:
3837 for (i = 0; i < npages; i++) 3830 for (i = 0; i < npages; i++)
3838 if (pages[i]) 3831 if (pages[i])
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1bfbd67c556d..541e796e6db5 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5072,18 +5072,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
5072 * are stored with the acl data to handle the problem of 5072 * are stored with the acl data to handle the problem of
5073 * variable length bitmaps.*/ 5073 * variable length bitmaps.*/
5074 res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset; 5074 res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
5075
5076 /* We ignore &savep and don't do consistency checks on
5077 * the attr length. Let userspace figure it out.... */
5078 res->acl_len = attrlen; 5075 res->acl_len = attrlen;
5079 if (attrlen > (xdr->nwords << 2)) { 5076
5080 if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { 5077 /* Check for receive buffer overflow */
5081 /* getxattr interface called with a NULL buf */ 5078 if (res->acl_len > (xdr->nwords << 2) ||
5082 goto out; 5079 res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
5083 } 5080 res->acl_flags |= NFS4_ACL_TRUNC;
5084 dprintk("NFS: acl reply: attrlen %u > page_len %u\n", 5081 dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
5085 attrlen, xdr->nwords << 2); 5082 attrlen, xdr->nwords << 2);
5086 return -EINVAL;
5087 } 5083 }
5088 } else 5084 } else
5089 status = -EOPNOTSUPP; 5085 status = -EOPNOTSUPP;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index ac7c8ae254f2..be9cf3c7e79e 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -652,7 +652,7 @@ struct nfs_getaclargs {
652}; 652};
653 653
654/* getxattr ACL interface flags */ 654/* getxattr ACL interface flags */
655#define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */ 655#define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */
656struct nfs_getaclres { 656struct nfs_getaclres {
657 size_t acl_len; 657 size_t acl_len;
658 size_t acl_data_offset; 658 size_t acl_data_offset;