diff options
-rw-r--r-- | fs/nfs/nfs4proc.c | 31 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 14 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 2 |
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); | ||
3827 | out_ok: | ||
3828 | ret = res.acl_len; | ||
3836 | out_free: | 3829 | out_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 */ |
656 | struct nfs_getaclres { | 656 | struct nfs_getaclres { |
657 | size_t acl_len; | 657 | size_t acl_len; |
658 | size_t acl_data_offset; | 658 | size_t acl_data_offset; |