pnfsblock: limit bio page count
[cascardo/linux.git] / fs / nfs / nfs4xdr.c
index e6161b2..95e92e4 100644 (file)
@@ -2298,7 +2298,7 @@ static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_getfh(xdr, &hdr);
        encode_getfattr(xdr, args->bitmask, &hdr);
        encode_restorefh(xdr, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_getfattr(xdr, args->dir_bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2517,11 +2517,13 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
-       replen = hdr.replen + op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz + 1;
+       replen = hdr.replen + op_decode_hdr_maxsz + 1;
        encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
 
        xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
                args->acl_pages, args->acl_pgbase, args->acl_len);
+       xdr_set_scratch_buffer(xdr, page_address(args->acl_scratch), PAGE_SIZE);
+
        encode_nops(&hdr);
 }
 
@@ -3790,7 +3792,8 @@ out_overflow:
 }
 
 static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
-               const struct nfs_server *server, uint32_t *uid, int may_sleep)
+               const struct nfs_server *server, uint32_t *uid,
+               struct nfs4_string *owner_name)
 {
        uint32_t len;
        __be32 *p;
@@ -3807,8 +3810,12 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
                p = xdr_inline_decode(xdr, len);
                if (unlikely(!p))
                        goto out_overflow;
-               if (!may_sleep) {
-                       /* do nothing */
+               if (owner_name != NULL) {
+                       owner_name->data = kmemdup(p, len, GFP_NOWAIT);
+                       if (owner_name->data != NULL) {
+                               owner_name->len = len;
+                               ret = NFS_ATTR_FATTR_OWNER_NAME;
+                       }
                } else if (len < XDR_MAX_NETOBJ) {
                        if (nfs_map_name_to_uid(server, (char *)p, len, uid) == 0)
                                ret = NFS_ATTR_FATTR_OWNER;
@@ -3828,7 +3835,8 @@ out_overflow:
 }
 
 static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
-               const struct nfs_server *server, uint32_t *gid, int may_sleep)
+               const struct nfs_server *server, uint32_t *gid,
+               struct nfs4_string *group_name)
 {
        uint32_t len;
        __be32 *p;
@@ -3845,8 +3853,12 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
                p = xdr_inline_decode(xdr, len);
                if (unlikely(!p))
                        goto out_overflow;
-               if (!may_sleep) {
-                       /* do nothing */
+               if (group_name != NULL) {
+                       group_name->data = kmemdup(p, len, GFP_NOWAIT);
+                       if (group_name->data != NULL) {
+                               group_name->len = len;
+                               ret = NFS_ATTR_FATTR_GROUP_NAME;
+                       }
                } else if (len < XDR_MAX_NETOBJ) {
                        if (nfs_map_group_to_gid(server, (char *)p, len, gid) == 0)
                                ret = NFS_ATTR_FATTR_GROUP;
@@ -4283,7 +4295,7 @@ xdr_error:
 
 static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                struct nfs_fattr *fattr, struct nfs_fh *fh,
-               const struct nfs_server *server, int may_sleep)
+               const struct nfs_server *server)
 {
        int status;
        umode_t fmode = 0;
@@ -4350,12 +4362,12 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_owner(xdr, bitmap, server, &fattr->uid, may_sleep);
+       status = decode_attr_owner(xdr, bitmap, server, &fattr->uid, fattr->owner_name);
        if (status < 0)
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_group(xdr, bitmap, server, &fattr->gid, may_sleep);
+       status = decode_attr_group(xdr, bitmap, server, &fattr->gid, fattr->group_name);
        if (status < 0)
                goto xdr_error;
        fattr->valid |= status;
@@ -4396,7 +4408,7 @@ xdr_error:
 }
 
 static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr,
-               struct nfs_fh *fh, const struct nfs_server *server, int may_sleep)
+               struct nfs_fh *fh, const struct nfs_server *server)
 {
        __be32 *savep;
        uint32_t attrlen,
@@ -4415,7 +4427,7 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
        if (status < 0)
                goto xdr_error;
 
-       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, server, may_sleep);
+       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, server);
        if (status < 0)
                goto xdr_error;
 
@@ -4426,9 +4438,9 @@ xdr_error:
 }
 
 static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
-               const struct nfs_server *server, int may_sleep)
+               const struct nfs_server *server)
 {
-       return decode_getfattr_generic(xdr, fattr, NULL, server, may_sleep);
+       return decode_getfattr_generic(xdr, fattr, NULL, server);
 }
 
 /*
@@ -4957,17 +4969,18 @@ decode_restorefh(struct xdr_stream *xdr)
 }
 
 static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
-               size_t *acl_len)
+                        struct nfs_getaclres *res)
 {
-       __be32 *savep;
+       __be32 *savep, *bm_p;
        uint32_t attrlen,
                 bitmap[3] = {0};
        struct kvec *iov = req->rq_rcv_buf.head;
        int status;
 
-       *acl_len = 0;
+       res->acl_len = 0;
        if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
                goto out;
+       bm_p = xdr->p;
        if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
                goto out;
        if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
@@ -4979,18 +4992,30 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
                size_t hdrlen;
                u32 recvd;
 
+               /* The bitmap (xdr len + bitmaps) and the attr xdr len words
+                * are stored with the acl data to handle the problem of
+                * variable length bitmaps.*/
+               xdr->p = bm_p;
+               res->acl_data_offset = be32_to_cpup(bm_p) + 2;
+               res->acl_data_offset <<= 2;
+
                /* We ignore &savep and don't do consistency checks on
                 * the attr length.  Let userspace figure it out.... */
                hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
+               attrlen += res->acl_data_offset;
                recvd = req->rq_rcv_buf.len - hdrlen;
                if (attrlen > recvd) {
-                       dprintk("NFS: server cheating in getattr"
-                                       " acl reply: attrlen %u > recvd %u\n",
+                       if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
+                               /* getxattr interface called with a NULL buf */
+                               res->acl_len = attrlen;
+                               goto out;
+                       }
+                       dprintk("NFS: acl reply: attrlen %u > recvd %u\n",
                                        attrlen, recvd);
                        return -EINVAL;
                }
                xdr_read_pages(xdr, attrlen);
-               *acl_len = attrlen;
+               res->acl_len = attrlen;
        } else
                status = -EOPNOTSUPP;
 
@@ -5696,8 +5721,7 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp,
        status = decode_open_downgrade(xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -5723,8 +5747,7 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_access(xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -5753,8 +5776,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server
-                       ,!RPC_IS_ASYNC(rqstp->rq_task));
+       status = decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -5780,8 +5802,7 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp,
                goto out;
        status = decode_getfh(xdr, res->fh);
        if (status == 0)
-               status = decode_getfattr(xdr, res->fattr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task));
+               status = decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -5807,8 +5828,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_remove(xdr, &res->cinfo);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->dir_attr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->dir_attr, res->server);
 out:
        return status;
 }
@@ -5841,14 +5861,12 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        /* Current FH is target directory */
-       if (decode_getfattr(xdr, res->new_fattr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
+       if (decode_getfattr(xdr, res->new_fattr, res->server))
                goto out;
        status = decode_restorefh(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->old_fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->old_fattr, res->server);
 out:
        return status;
 }
@@ -5884,14 +5902,12 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
         * Note order: OP_LINK leaves the directory as the current
         *             filehandle.
         */
-       if (decode_getfattr(xdr, res->dir_attr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
+       if (decode_getfattr(xdr, res->dir_attr, res->server))
                goto out;
        status = decode_restorefh(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -5923,14 +5939,12 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       if (decode_getfattr(xdr, res->fattr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
+       if (decode_getfattr(xdr, res->fattr, res->server))
                goto out;
        status = decode_restorefh(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->dir_fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->dir_fattr, res->server);
 out:
        return status;
 }
@@ -5962,8 +5976,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       status = decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6028,7 +6041,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getacl(xdr, rqstp, &res->acl_len);
+       status = decode_getacl(xdr, rqstp, res);
 
 out:
        return status;
@@ -6061,8 +6074,7 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
         *      an ESTALE error. Shouldn't be a problem,
         *      though, since fattr->valid will remain unset.
         */
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6093,13 +6105,11 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                goto out;
        if (decode_getfh(xdr, &res->fh) != 0)
                goto out;
-       if (decode_getfattr(xdr, res->f_attr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
+       if (decode_getfattr(xdr, res->f_attr, res->server) != 0)
                goto out;
        if (decode_restorefh(xdr) != 0)
                goto out;
-       decode_getfattr(xdr, res->dir_attr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->dir_attr, res->server);
 out:
        return status;
 }
@@ -6147,8 +6157,7 @@ static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp,
        status = decode_open(xdr, res);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->f_attr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->f_attr, res->server);
 out:
        return status;
 }
@@ -6175,8 +6184,7 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp,
        status = decode_setattr(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6356,8 +6364,7 @@ static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        if (res->fattr)
-               decode_getfattr(xdr, res->fattr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task));
+               decode_getfattr(xdr, res->fattr, res->server);
        if (!status)
                status = res->count;
 out:
@@ -6386,8 +6393,7 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        if (res->fattr)
-               decode_getfattr(xdr, res->fattr, res->server,
-                               !RPC_IS_ASYNC(rqstp->rq_task));
+               decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6546,8 +6552,7 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
        status = decode_delegreturn(xdr);
        if (status != 0)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6576,8 +6581,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
                goto out;
        xdr_enter_page(xdr, PAGE_SIZE);
        status = decode_getfattr(xdr, &res->fs_locations->fattr,
-                                res->fs_locations->server,
-                                !RPC_IS_ASYNC(req->rq_task));
+                                res->fs_locations->server);
 out:
        return status;
 }
@@ -6826,8 +6830,7 @@ static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
        status = decode_layoutcommit(xdr, rqstp, res);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6958,7 +6961,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                goto out_overflow;
 
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
-                                       entry->server, 1) < 0)
+                                       entry->server) < 0)
                goto out_overflow;
        if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
                entry->ino = entry->fattr->mounted_on_fileid;