The dacl attribute is only supported in NFS version 4.1 and later.  On systems
where NFS version 4.0 is still the default, an additional mount option is
needed:

    mount -t nfs4 -o minorversion=1 [...]

Signed-off-by: Andreas Gruenbacher <agrue...@redhat.com>
---
 fs/nfs/nfs4proc.c       |   2 +-
 fs/nfs/nfs4xdr.c        | 165 ++++++++++++++++++++++++++++++++++++------------
 include/linux/nfs_xdr.h |   2 +-
 3 files changed, 128 insertions(+), 41 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index c2ba4f0..acf39e8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4465,7 +4465,7 @@ static struct richacl *__nfs4_get_acl_uncached(struct 
inode *inode)
        struct nfs_server *server = NFS_SERVER(inode);
        struct page *pages[DIV_ROUND_UP(NFS4ACL_SIZE_MAX, PAGE_SIZE)] = {};
        struct nfs_getaclargs args = {
-               .fh = NFS_FH(inode),
+               .inode = inode,
                .acl_pages = pages,
                .acl_len = ARRAY_SIZE(pages) * PAGE_SIZE,
        };
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 8ccc2a0..52863fc 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1675,6 +1675,12 @@ nfs4_encode_group(struct xdr_stream *xdr, const struct 
nfs_server *server, kgid_
        return 0;
 }
 
+static unsigned int
+nfs4_ace_mask(int minorversion)
+{
+       return minorversion == 0 ? NFS40_ACE_MASK_ALL : NFS4_ACE_MASK_ALL;
+}
+
 static int
 nfs4_encode_ace_who(struct xdr_stream *xdr, const struct nfs_server *server,
                    struct richace *ace)
@@ -1705,6 +1711,7 @@ nfs4_encode_ace_who(struct xdr_stream *xdr, const struct 
nfs_server *server,
 static int
 encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct 
compound_hdr *hdr)
 {
+       unsigned int ace_mask = nfs4_ace_mask(hdr->minorversion);
        int attrlen_offset;
        __be32 attrlen, *p;
        struct richace *ace;
@@ -1713,9 +1720,30 @@ encode_setacl(struct xdr_stream *xdr, struct 
nfs_setaclargs *arg, struct compoun
        encode_nfs4_stateid(xdr, &zero_stateid);
 
        /* Encode attribute bitmap. */
-       p = reserve_space(xdr, 2*4);
-       *p++ = cpu_to_be32(1);
-       *p = cpu_to_be32(FATTR4_WORD0_ACL);
+       if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+               p = reserve_space(xdr, 3*4);
+               *p++ = cpu_to_be32(2);
+               *p++ = 0;
+               *p = cpu_to_be32(FATTR4_WORD1_DACL);
+       } else {
+               p = reserve_space(xdr, 2*4);
+               *p++ = cpu_to_be32(1);
+               *p = cpu_to_be32(FATTR4_WORD0_ACL);
+       }
+
+       /* Reject acls not understood by the server */
+       if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+               BUILD_BUG_ON(NFS4_ACE_MASK_ALL != RICHACE_VALID_MASK);
+       } else {
+               richacl_for_each_entry(ace, arg->acl) {
+                       if (ace->e_flags & RICHACE_INHERITED_ACE)
+                               return -EINVAL;
+               }
+       }
+       richacl_for_each_entry(ace, arg->acl) {
+               if (ace->e_mask & ~ace_mask)
+                       return -EINVAL;
+       }
 
        attrlen_offset = xdr->buf->len;
        p = xdr_reserve_space(xdr, 4);
@@ -1723,6 +1751,14 @@ encode_setacl(struct xdr_stream *xdr, struct 
nfs_setaclargs *arg, struct compoun
                goto fail;
        p++;  /* to be backfilled later */
 
+       if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
+                       goto fail;
+               *p = cpu_to_be32(arg->acl->a_flags);
+       } else if (arg->acl->a_flags)
+               return -EINVAL;
+
        p = xdr_reserve_space(xdr, 4);
        if (!p)
                goto fail;
@@ -1735,15 +1771,7 @@ encode_setacl(struct xdr_stream *xdr, struct 
nfs_setaclargs *arg, struct compoun
        /* Add space for the acl entries. */
        xdr_inline_pages(xdr->buf, xdr->buf->len, arg->acl_pages, 0, 
arg->acl_len);
 
-       if (arg->acl->a_flags)
-               return -EINVAL;
-
        richacl_for_each_entry(ace, arg->acl) {
-               if (ace->e_flags & RICHACE_INHERITED_ACE)
-                       return -EINVAL;
-               if (ace->e_mask & ~NFS4_ACE_MASK_ALL)
-                       return -EINVAL;
-
                p = xdr_reserve_space(xdr, 4*3);
                if (!p)
                        goto fail;
@@ -2627,9 +2655,12 @@ static int 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);
+       encode_putfh(xdr, NFS_FH(args->inode), &hdr);
        replen = hdr.replen + op_decode_hdr_maxsz + 1;
-       encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, &hdr);
+       if (NFS_SERVER(args->inode)->attr_bitmask[1] & FATTR4_WORD1_DACL)
+               encode_getattr_two(xdr, 0, FATTR4_WORD1_MODE | 
FATTR4_WORD1_DACL, &hdr);
+       else
+               encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, 
&hdr);
 
        xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
                args->acl_pages, 0, args->acl_len);
@@ -5395,16 +5426,61 @@ nfs4_decode_ace_who(struct richace *ace, const struct 
nfs_server *server,
        return error;
 }
 
+static struct richacl *
+decode_acl_entries(struct xdr_stream *xdr, const struct nfs_server *server)
+{
+       struct richacl *acl = NULL;
+       struct richace *ace;
+       uint32_t count;
+       int status;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       status = -EIO;
+       if (unlikely(!p))
+               goto out;
+       count = be32_to_cpup(p);
+       status = -ENOMEM;
+       if (count > RICHACL_XATTR_MAX_COUNT)
+               goto out;
+       acl = richacl_alloc(count, GFP_KERNEL);
+       if (!acl)
+               goto out;
+       richacl_for_each_entry(ace, acl) {
+               p = xdr_inline_decode(xdr, 4*3);
+               status = -ENOMEM;
+               if (unlikely(!p))
+                       goto out;  /* acl truncated */
+               ace->e_type = be32_to_cpup(p++);
+               ace->e_flags = be32_to_cpup(p++);
+               status = -EIO;
+               if (ace->e_flags & RICHACE_SPECIAL_WHO)
+                       goto out;
+               ace->e_mask = be32_to_cpup(p++);
+               status = nfs4_decode_ace_who(ace, server, xdr);
+               if (status)
+                       return ERR_PTR(status);
+       }
+       status = 0;
+out:
+       if (status != 0) {
+               richacl_put(acl);
+               acl = ERR_PTR(status);
+       }
+       return acl;
+}
+
 static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
                         struct nfs_getaclres *res)
 {
        static const uint32_t attrs_allowed[3] = {
                [0] = FATTR4_WORD0_ACL,
-               [1] = FATTR4_WORD1_MODE,
+               [1] = FATTR4_WORD1_MODE | FATTR4_WORD1_DACL,
        };
        unsigned int savep;
        uint32_t attrlen,
                 bitmap[3] = {0};
+       struct richacl *acl = NULL;
        int status;
 
        if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
@@ -5415,41 +5491,52 @@ static int decode_getacl(struct xdr_stream *xdr, struct 
rpc_rqst *req,
                goto out;
        if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
                goto out;
-
-       if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
+       if (bitmap[0] & FATTR4_WORD0_ACL) {
                struct richace *ace;
-               uint32_t count;
-               __be32 *p;
 
-               p = xdr_inline_decode(xdr, 4);
-               if (unlikely(!p))
-                       return -ENOMEM;  /* acl truncated */
-               count = be32_to_cpup(p);
-               if (count > RICHACL_XATTR_MAX_COUNT)
-                       return -EIO;
-               res->acl = richacl_alloc(count, GFP_KERNEL);
-               if (!res->acl)
-                       return -ENOMEM;
-               richacl_for_each_entry(ace, res->acl) {
-                       p = xdr_inline_decode(xdr, 4*3);
-                       if (unlikely(!p))
-                               return -ENOMEM;  /* acl truncated */
-                       ace->e_type = be32_to_cpup(p++);
-                       ace->e_flags = be32_to_cpup(p++);
-                       if (ace->e_flags & RICHACE_SPECIAL_WHO)
-                               return -EIO;
-                       ace->e_mask = be32_to_cpup(p++);
-                       status = nfs4_decode_ace_who(ace, res->server, xdr);
-                       if (status)
+               status = -EIO;
+               if (bitmap[1] & FATTR4_WORD1_DACL)
+                       goto out;
+
+               acl = decode_acl_entries(xdr, res->server);
+               status = PTR_ERR(acl);
+               if (IS_ERR(acl))
+                       goto out;
+               status = -EIO;
+
+               richacl_for_each_entry(ace, acl) {
+                       if (ace->e_flags & RICHACE_INHERITED_ACE)
                                goto out;
                }
-       } else
+       } else if (!(bitmap[1] & FATTR4_WORD1_DACL)) {
                status = -EOPNOTSUPP;
+               goto out;
+       }
        if ((status = decode_attr_mode(xdr, bitmap, &res->mode)) < 0)
                goto out;
+       if (bitmap[1] & FATTR4_WORD1_DACL) {
+               unsigned int flags;
+               __be32 *p;
+
+               p = xdr_inline_decode(xdr, 4);
+                status = -EIO;
+                if (unlikely(!p))
+                        goto out;
+                flags = be32_to_cpup(p);
+
+               acl = decode_acl_entries(xdr, res->server);
+               status = PTR_ERR(acl);
+               if (IS_ERR(acl))
+                       goto out;
+               acl->a_flags = flags;
+       }
        status = 0;
 
 out:
+       if (status == 0)
+               res->acl = acl;
+       else
+               richacl_put(acl);
        return status;
 }
 
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 77097ec..3767624 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -651,7 +651,7 @@ struct nfs_setaclres {
 
 struct nfs_getaclargs {
        struct nfs4_sequence_args       seq_args;
-       struct nfs_fh *                 fh;
+       struct inode *                  inode;
        size_t                          acl_len;
        struct page **                  acl_pages;
 };
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to