aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r--fs/nfs/nfs4proc.c96
1 files changed, 57 insertions, 39 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index fcc2408d7ab0..3b1080118452 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3426,19 +3426,6 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
3426 */ 3426 */
3427#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) 3427#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
3428 3428
3429static void buf_to_pages(const void *buf, size_t buflen,
3430 struct page **pages, unsigned int *pgbase)
3431{
3432 const void *p = buf;
3433
3434 *pgbase = offset_in_page(buf);
3435 p -= *pgbase;
3436 while (p < buf + buflen) {
3437 *(pages++) = virt_to_page(p);
3438 p += PAGE_CACHE_SIZE;
3439 }
3440}
3441
3442static int buf_to_pages_noslab(const void *buf, size_t buflen, 3429static int buf_to_pages_noslab(const void *buf, size_t buflen,
3443 struct page **pages, unsigned int *pgbase) 3430 struct page **pages, unsigned int *pgbase)
3444{ 3431{
@@ -3535,9 +3522,19 @@ out:
3535 nfs4_set_cached_acl(inode, acl); 3522 nfs4_set_cached_acl(inode, acl);
3536} 3523}
3537 3524
3525/*
3526 * The getxattr API returns the required buffer length when called with a
3527 * NULL buf. The NFSv4 acl tool then calls getxattr again after allocating
3528 * the required buf. On a NULL buf, we send a page of data to the server
3529 * guessing that the ACL request can be serviced by a page. If so, we cache
3530 * up to the page of ACL data, and the 2nd call to getxattr is serviced by
3531 * the cache. If not so, we throw away the page, and cache the required
3532 * length. The next getxattr call will then produce another round trip to
3533 * the server, this time with the input buf of the required size.
3534 */
3538static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen) 3535static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
3539{ 3536{
3540 struct page *pages[NFS4ACL_MAXPAGES]; 3537 struct page *pages[NFS4ACL_MAXPAGES] = {NULL, };
3541 struct nfs_getaclargs args = { 3538 struct nfs_getaclargs args = {
3542 .fh = NFS_FH(inode), 3539 .fh = NFS_FH(inode),
3543 .acl_pages = pages, 3540 .acl_pages = pages,
@@ -3552,41 +3549,60 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
3552 .rpc_argp = &args, 3549 .rpc_argp = &args,
3553 .rpc_resp = &res, 3550 .rpc_resp = &res,
3554 }; 3551 };
3555 struct page *localpage = NULL; 3552 int ret = -ENOMEM, npages, i, acl_len = 0;
3556 int ret;
3557 3553
3558 if (buflen < PAGE_SIZE) { 3554 npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
3559 /* As long as we're doing a round trip to the server anyway, 3555 /* As long as we're doing a round trip to the server anyway,
3560 * let's be prepared for a page of acl data. */ 3556 * let's be prepared for a page of acl data. */
3561 localpage = alloc_page(GFP_KERNEL); 3557 if (npages == 0)
3562 resp_buf = page_address(localpage); 3558 npages = 1;
3563 if (localpage == NULL) 3559
3564 return -ENOMEM; 3560 for (i = 0; i < npages; i++) {
3565 args.acl_pages[0] = localpage; 3561 pages[i] = alloc_page(GFP_KERNEL);
3566 args.acl_pgbase = 0; 3562 if (!pages[i])
3567 args.acl_len = PAGE_SIZE; 3563 goto out_free;
3568 } else { 3564 }
3569 resp_buf = buf; 3565 if (npages > 1) {
3570 buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase); 3566 /* for decoding across pages */
3567 args.acl_scratch = alloc_page(GFP_KERNEL);
3568 if (!args.acl_scratch)
3569 goto out_free;
3571 } 3570 }
3572 ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0); 3571 args.acl_len = npages * PAGE_SIZE;
3572 args.acl_pgbase = 0;
3573 /* Let decode_getfacl know not to fail if the ACL data is larger than
3574 * the page we send as a guess */
3575 if (buf == NULL)
3576 res.acl_flags |= NFS4_ACL_LEN_REQUEST;
3577 resp_buf = page_address(pages[0]);
3578
3579 dprintk("%s buf %p buflen %ld npages %d args.acl_len %ld\n",
3580 __func__, buf, buflen, npages, args.acl_len);
3581 ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
3582 &msg, &args.seq_args, &res.seq_res, 0);
3573 if (ret) 3583 if (ret)
3574 goto out_free; 3584 goto out_free;
3575 if (res.acl_len > args.acl_len) 3585
3576 nfs4_write_cached_acl(inode, NULL, res.acl_len); 3586 acl_len = res.acl_len - res.acl_data_offset;
3587 if (acl_len > args.acl_len)
3588 nfs4_write_cached_acl(inode, NULL, acl_len);
3577 else 3589 else
3578 nfs4_write_cached_acl(inode, resp_buf, res.acl_len); 3590 nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset,
3591 acl_len);
3579 if (buf) { 3592 if (buf) {
3580 ret = -ERANGE; 3593 ret = -ERANGE;
3581 if (res.acl_len > buflen) 3594 if (acl_len > buflen)
3582 goto out_free; 3595 goto out_free;
3583 if (localpage) 3596 _copy_from_pages(buf, pages, res.acl_data_offset,
3584 memcpy(buf, resp_buf, res.acl_len); 3597 res.acl_len);
3585 } 3598 }
3586 ret = res.acl_len; 3599 ret = acl_len;
3587out_free: 3600out_free:
3588 if (localpage) 3601 for (i = 0; i < npages; i++)
3589 __free_page(localpage); 3602 if (pages[i])
3603 __free_page(pages[i]);
3604 if (args.acl_scratch)
3605 __free_page(args.acl_scratch);
3590 return ret; 3606 return ret;
3591} 3607}
3592 3608
@@ -3617,6 +3633,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
3617 nfs_zap_acl_cache(inode); 3633 nfs_zap_acl_cache(inode);
3618 ret = nfs4_read_cached_acl(inode, buf, buflen); 3634 ret = nfs4_read_cached_acl(inode, buf, buflen);
3619 if (ret != -ENOENT) 3635 if (ret != -ENOENT)
3636 /* -ENOENT is returned if there is no ACL or if there is an ACL
3637 * but no cached acl data, just the acl length */
3620 return ret; 3638 return ret;
3621 return nfs4_get_acl_uncached(inode, buf, buflen); 3639 return nfs4_get_acl_uncached(inode, buf, buflen);
3622} 3640}