[PATCH] NFSD: Add server support for NFSv3 ACLs.
authorAndreas Gruenbacher <agruen@suse.de>
Wed, 22 Jun 2005 17:16:26 +0000 (17:16 +0000)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 22 Jun 2005 20:07:23 +0000 (16:07 -0400)
 This adds functions for encoding and decoding POSIX ACLs for the NFSACL
 protocol extension, and the GETACL and SETACL RPCs.  The implementation is
 compatible with NFSACL in Solaris.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Acked-by: Olaf Kirch <okir@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
16 files changed:
fs/Kconfig
fs/Makefile
fs/nfs_common/Makefile [new file with mode: 0644]
fs/nfs_common/nfsacl.c [new file with mode: 0644]
fs/nfsd/Makefile
fs/nfsd/nfs2acl.c [new file with mode: 0644]
fs/nfsd/nfs3acl.c [new file with mode: 0644]
fs/nfsd/nfs3xdr.c
fs/nfsd/nfssvc.c
fs/nfsd/nfsxdr.c
fs/nfsd/vfs.c
include/linux/nfsacl.h [new file with mode: 0644]
include/linux/nfsd/nfsd.h
include/linux/nfsd/xdr.h
include/linux/nfsd/xdr3.h
include/linux/sunrpc/svc.h

index 178e274..d44b04d 100644 (file)
@@ -1353,6 +1353,7 @@ config NFSD
        select LOCKD
        select SUNRPC
        select EXPORTFS
+       select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL
        help
          If you want your Linux box to act as an NFS *server*, so that other
          computers on your local network which support NFS can access certain
@@ -1376,6 +1377,10 @@ config NFSD
          To compile the NFS server support as a module, choose M here: the
          module will be called nfsd.  If unsure, say N.
 
+config NFSD_V2_ACL
+       bool
+       depends on NFSD
+
 config NFSD_V3
        bool "Provide NFSv3 server support"
        depends on NFSD
@@ -1383,6 +1388,16 @@ config NFSD_V3
          If you would like to include the NFSv3 server as well as the NFSv2
          server, say Y here.  If unsure, say Y.
 
+config NFSD_V3_ACL
+       bool "Provide server support for the NFSv3 ACL protocol extension"
+       depends on NFSD_V3
+       select NFSD_V2_ACL
+       help
+         Implement the NFSv3 ACL protocol extension for manipulating POSIX
+         Access Control Lists on exported file systems. NFS clients should
+         be compiled with the NFSv3 ACL protocol extension; see the
+         CONFIG_NFS_V3_ACL option.  If unsure, say N.
+
 config NFSD_V4
        bool "Provide NFSv4 server support (EXPERIMENTAL)"
        depends on NFSD_V3 && EXPERIMENTAL
@@ -1427,6 +1442,15 @@ config LOCKD_V4
 config EXPORTFS
        tristate
 
+config NFS_ACL_SUPPORT
+       tristate
+       select FS_POSIX_ACL
+
+config NFS_COMMON
+       bool
+       depends on NFSD || NFS_FS
+       default y
+
 config SUNRPC
        tristate
 
index 443f2bc..fc92e59 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_BINFMT_FLAT)     += binfmt_flat.o
 
 obj-$(CONFIG_FS_MBCACHE)       += mbcache.o
 obj-$(CONFIG_FS_POSIX_ACL)     += posix_acl.o xattr_acl.o
+obj-$(CONFIG_NFS_COMMON)       += nfs_common/
 
 obj-$(CONFIG_QUOTA)            += dquot.o
 obj-$(CONFIG_QFMT_V1)          += quota_v1.o
diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
new file mode 100644 (file)
index 0000000..f689ed8
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for Linux filesystem routines that are shared by client and server.
+#
+
+obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
+
+nfs_acl-objs := nfsacl.o
diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c
new file mode 100644 (file)
index 0000000..18c58c3
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * fs/nfs_common/nfsacl.c
+ *
+ *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+/*
+ * The Solaris nfsacl protocol represents some ACLs slightly differently
+ * than POSIX 1003.1e draft 17 does (and we do):
+ *
+ *  - Minimal ACLs always have an ACL_MASK entry, so they have
+ *    four instead of three entries.
+ *  - The ACL_MASK entry in such minimal ACLs always has the same
+ *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
+ *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
+ *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
+ *    entries contain the identifiers of the owner and owning group.
+ *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
+ *  - ACL entries in the kernel are kept sorted in ascending order
+ *    of (e_tag, e_id). Solaris ACLs are unsorted.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/nfsacl.h>
+#include <linux/nfs3.h>
+#include <linux/sort.h>
+
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nfsacl_encode);
+EXPORT_SYMBOL(nfsacl_decode);
+
+struct nfsacl_encode_desc {
+       struct xdr_array2_desc desc;
+       unsigned int count;
+       struct posix_acl *acl;
+       int typeflag;
+       uid_t uid;
+       gid_t gid;
+};
+
+static int
+xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
+{
+       struct nfsacl_encode_desc *nfsacl_desc =
+               (struct nfsacl_encode_desc *) desc;
+       u32 *p = (u32 *) elem;
+
+       if (nfsacl_desc->count < nfsacl_desc->acl->a_count) {
+               struct posix_acl_entry *entry =
+                       &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
+
+               *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
+               switch(entry->e_tag) {
+                       case ACL_USER_OBJ:
+                               *p++ = htonl(nfsacl_desc->uid);
+                               break;
+                       case ACL_GROUP_OBJ:
+                               *p++ = htonl(nfsacl_desc->gid);
+                               break;
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               *p++ = htonl(entry->e_id);
+                               break;
+                       default:  /* Solaris depends on that! */
+                               *p++ = 0;
+                               break;
+               }
+               *p++ = htonl(entry->e_perm & S_IRWXO);
+       } else {
+               const struct posix_acl_entry *pa, *pe;
+               int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE;
+
+               FOREACH_ACL_ENTRY(pa, nfsacl_desc->acl, pe) {
+                       if (pa->e_tag == ACL_GROUP_OBJ) {
+                               group_obj_perm = pa->e_perm & S_IRWXO;
+                               break;
+                       }
+               }
+               /* fake up ACL_MASK entry */
+               *p++ = htonl(ACL_MASK | nfsacl_desc->typeflag);
+               *p++ = htonl(0);
+               *p++ = htonl(group_obj_perm);
+       }
+
+       return 0;
+}
+
+unsigned int
+nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
+             struct posix_acl *acl, int encode_entries, int typeflag)
+{
+       int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
+       struct nfsacl_encode_desc nfsacl_desc = {
+               .desc = {
+                       .elem_size = 12,
+                       .array_len = encode_entries ? entries : 0,
+                       .xcode = xdr_nfsace_encode,
+               },
+               .acl = acl,
+               .typeflag = typeflag,
+               .uid = inode->i_uid,
+               .gid = inode->i_gid,
+       };
+       int err;
+
+       if (entries > NFS_ACL_MAX_ENTRIES ||
+           xdr_encode_word(buf, base, entries))
+               return -EINVAL;
+       err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
+       if (!err)
+               err = 8 + nfsacl_desc.desc.elem_size *
+                         nfsacl_desc.desc.array_len;
+       return err;
+}
+
+struct nfsacl_decode_desc {
+       struct xdr_array2_desc desc;
+       unsigned int count;
+       struct posix_acl *acl;
+};
+
+static int
+xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
+{
+       struct nfsacl_decode_desc *nfsacl_desc =
+               (struct nfsacl_decode_desc *) desc;
+       u32 *p = (u32 *) elem;
+       struct posix_acl_entry *entry;
+
+       if (!nfsacl_desc->acl) {
+               if (desc->array_len > NFS_ACL_MAX_ENTRIES)
+                       return -EINVAL;
+               nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
+               if (!nfsacl_desc->acl)
+                       return -ENOMEM;
+               nfsacl_desc->count = 0;
+       }
+
+       entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
+       entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
+       entry->e_id = ntohl(*p++);
+       entry->e_perm = ntohl(*p++);
+
+       switch(entry->e_tag) {
+               case ACL_USER_OBJ:
+               case ACL_USER:
+               case ACL_GROUP_OBJ:
+               case ACL_GROUP:
+               case ACL_OTHER:
+                       if (entry->e_perm & ~S_IRWXO)
+                               return -EINVAL;
+                       break;
+               case ACL_MASK:
+                       /* Solaris sometimes sets additonal bits in the mask */
+                       entry->e_perm &= S_IRWXO;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+cmp_acl_entry(const void *x, const void *y)
+{
+       const struct posix_acl_entry *a = x, *b = y;
+
+       if (a->e_tag != b->e_tag)
+               return a->e_tag - b->e_tag;
+       else if (a->e_id > b->e_id)
+               return 1;
+       else if (a->e_id < b->e_id)
+               return -1;
+       else
+               return 0;
+}
+
+/*
+ * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
+ */
+static int
+posix_acl_from_nfsacl(struct posix_acl *acl)
+{
+       struct posix_acl_entry *pa, *pe,
+              *group_obj = NULL, *mask = NULL;
+
+       if (!acl)
+               return 0;
+
+       sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
+            cmp_acl_entry, NULL);
+
+       /* Clear undefined identifier fields and find the ACL_GROUP_OBJ
+          and ACL_MASK entries. */
+       FOREACH_ACL_ENTRY(pa, acl, pe) {
+               switch(pa->e_tag) {
+                       case ACL_USER_OBJ:
+                               pa->e_id = ACL_UNDEFINED_ID;
+                               break;
+                       case ACL_GROUP_OBJ:
+                               pa->e_id = ACL_UNDEFINED_ID;
+                               group_obj = pa;
+                               break;
+                       case ACL_MASK:
+                               mask = pa;
+                               /* fall through */
+                       case ACL_OTHER:
+                               pa->e_id = ACL_UNDEFINED_ID;
+                               break;
+               }
+       }
+       if (acl->a_count == 4 && group_obj && mask &&
+           mask->e_perm == group_obj->e_perm) {
+               /* remove bogus ACL_MASK entry */
+               memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
+                                     sizeof(struct posix_acl_entry));
+               acl->a_count = 3;
+       }
+       return 0;
+}
+
+unsigned int
+nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
+             struct posix_acl **pacl)
+{
+       struct nfsacl_decode_desc nfsacl_desc = {
+               .desc = {
+                       .elem_size = 12,
+                       .xcode = pacl ? xdr_nfsace_decode : NULL,
+               },
+       };
+       u32 entries;
+       int err;
+
+       if (xdr_decode_word(buf, base, &entries) ||
+           entries > NFS_ACL_MAX_ENTRIES)
+               return -EINVAL;
+       err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
+       if (err)
+               return err;
+       if (pacl) {
+               if (entries != nfsacl_desc.desc.array_len ||
+                   posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
+                       posix_acl_release(nfsacl_desc.acl);
+                       return -EINVAL;
+               }
+               *pacl = nfsacl_desc.acl;
+       }
+       if (aclcnt)
+               *aclcnt = entries;
+       return 8 + nfsacl_desc.desc.elem_size *
+                  nfsacl_desc.desc.array_len;
+}
index b8680a2..9f043f4 100644 (file)
@@ -6,7 +6,9 @@ obj-$(CONFIG_NFSD)      += nfsd.o
 
 nfsd-y                         := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
                           export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
+nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
 nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
+nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
 nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
                           nfs4acl.o nfs4callback.o
 nfsd-objs              := $(nfsd-y)
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
new file mode 100644 (file)
index 0000000..7cbf068
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * linux/fs/nfsd/nfsacl.c
+ *
+ * Process version 2 NFSACL requests.
+ *
+ * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfs.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/xdr3.h>
+#include <linux/posix_acl.h>
+#include <linux/nfsacl.h>
+
+#define NFSDDBG_FACILITY               NFSDDBG_PROC
+#define RETURN_STATUS(st)      { resp->status = (st); return (st); }
+
+/*
+ * NULL call.
+ */
+static int
+nfsacld_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+       return nfs_ok;
+}
+
+/*
+ * Get the Access and/or Default ACL of a file.
+ */
+static int nfsacld_proc_getacl(struct svc_rqst * rqstp,
+               struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
+{
+       svc_fh *fh;
+       struct posix_acl *acl;
+       int nfserr = 0;
+
+       dprintk("nfsd: GETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
+
+       fh = fh_copy(&resp->fh, &argp->fh);
+       if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP)))
+               RETURN_STATUS(nfserr_inval);
+
+       if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+               RETURN_STATUS(nfserr_inval);
+       resp->mask = argp->mask;
+
+       if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
+               acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS);
+               if (IS_ERR(acl)) {
+                       int err = PTR_ERR(acl);
+
+                       if (err == -ENODATA || err == -EOPNOTSUPP)
+                               acl = NULL;
+                       else {
+                               nfserr = nfserrno(err);
+                               goto fail;
+                       }
+               }
+               if (acl == NULL) {
+                       /* Solaris returns the inode's minimum ACL. */
+
+                       struct inode *inode = fh->fh_dentry->d_inode;
+                       acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+               }
+               resp->acl_access = acl;
+       }
+       if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
+               /* Check how Solaris handles requests for the Default ACL
+                  of a non-directory! */
+
+               acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT);
+               if (IS_ERR(acl)) {
+                       int err = PTR_ERR(acl);
+
+                       if (err == -ENODATA || err == -EOPNOTSUPP)
+                               acl = NULL;
+                       else {
+                               nfserr = nfserrno(err);
+                               goto fail;
+                       }
+               }
+               resp->acl_default = acl;
+       }
+
+       /* resp->acl_{access,default} are released in nfssvc_release_getacl. */
+       RETURN_STATUS(0);
+
+fail:
+       posix_acl_release(resp->acl_access);
+       posix_acl_release(resp->acl_default);
+       RETURN_STATUS(nfserr);
+}
+
+/*
+ * Set the Access and/or Default ACL of a file.
+ */
+static int nfsacld_proc_setacl(struct svc_rqst * rqstp,
+               struct nfsd3_setaclargs *argp,
+               struct nfsd_attrstat *resp)
+{
+       svc_fh *fh;
+       int nfserr = 0;
+
+       dprintk("nfsd: SETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
+
+       fh = fh_copy(&resp->fh, &argp->fh);
+       nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+
+       if (!nfserr) {
+               nfserr = nfserrno( nfsd_set_posix_acl(
+                       fh, ACL_TYPE_ACCESS, argp->acl_access) );
+       }
+       if (!nfserr) {
+               nfserr = nfserrno( nfsd_set_posix_acl(
+                       fh, ACL_TYPE_DEFAULT, argp->acl_default) );
+       }
+
+       /* argp->acl_{access,default} may have been allocated in
+          nfssvc_decode_setaclargs. */
+       posix_acl_release(argp->acl_access);
+       posix_acl_release(argp->acl_default);
+       return nfserr;
+}
+
+/*
+ * Check file attributes
+ */
+static int nfsacld_proc_getattr(struct svc_rqst * rqstp,
+               struct nfsd_fhandle *argp, struct nfsd_attrstat *resp)
+{
+       dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
+
+       fh_copy(&resp->fh, &argp->fh);
+       return fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+}
+
+/*
+ * Check file access
+ */
+static int nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
+               struct nfsd3_accessres *resp)
+{
+       int nfserr;
+
+       dprintk("nfsd: ACCESS(2acl)   %s 0x%x\n",
+                       SVCFH_fmt(&argp->fh),
+                       argp->access);
+
+       fh_copy(&resp->fh, &argp->fh);
+       resp->access = argp->access;
+       nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
+       return nfserr;
+}
+
+/*
+ * XDR decode functions
+ */
+static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclargs *argp)
+{
+       if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+               return 0;
+       argp->mask = ntohl(*p); p++;
+
+       return xdr_argsize_check(rqstp, p);
+}
+
+
+static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_setaclargs *argp)
+{
+       struct kvec *head = rqstp->rq_arg.head;
+       unsigned int base;
+       int n;
+
+       if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+               return 0;
+       argp->mask = ntohl(*p++);
+       if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
+           !xdr_argsize_check(rqstp, p))
+               return 0;
+
+       base = (char *)p - (char *)head->iov_base;
+       n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
+                         (argp->mask & NFS_ACL) ?
+                         &argp->acl_access : NULL);
+       if (n > 0)
+               n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
+                                 (argp->mask & NFS_DFACL) ?
+                                 &argp->acl_default : NULL);
+       return (n > 0);
+}
+
+static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd_fhandle *argp)
+{
+       if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+               return 0;
+       return xdr_argsize_check(rqstp, p);
+}
+
+static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_accessargs *argp)
+{
+       if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+               return 0;
+       argp->access = ntohl(*p++);
+
+       return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+
+/* GETACL */
+static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclres *resp)
+{
+       struct dentry *dentry = resp->fh.fh_dentry;
+       struct inode *inode = dentry->d_inode;
+       int w = nfsacl_size(
+               (resp->mask & NFS_ACL)   ? resp->acl_access  : NULL,
+               (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
+       struct kvec *head = rqstp->rq_res.head;
+       unsigned int base;
+       int n;
+
+       if (dentry == NULL || dentry->d_inode == NULL)
+               return 0;
+       inode = dentry->d_inode;
+
+       p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+       *p++ = htonl(resp->mask);
+       if (!xdr_ressize_check(rqstp, p))
+               return 0;
+       base = (char *)p - (char *)head->iov_base;
+
+       rqstp->rq_res.page_len = w;
+       while (w > 0) {
+               if (!svc_take_res_page(rqstp))
+                       return 0;
+               w -= PAGE_SIZE;
+       }
+
+       n = nfsacl_encode(&rqstp->rq_res, base, inode,
+                         resp->acl_access,
+                         resp->mask & NFS_ACL, 0);
+       if (n > 0)
+               n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
+                                 resp->acl_default,
+                                 resp->mask & NFS_DFACL,
+                                 NFS_ACL_DEFAULT);
+       if (n <= 0)
+               return 0;
+       return 1;
+}
+
+static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd_attrstat *resp)
+{
+       p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+       return xdr_ressize_check(rqstp, p);
+}
+
+/* ACCESS */
+static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_accessres *resp)
+{
+       p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+       *p++ = htonl(resp->access);
+       return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+static int nfsaclsvc_release_getacl(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclres *resp)
+{
+       fh_put(&resp->fh);
+       posix_acl_release(resp->acl_access);
+       posix_acl_release(resp->acl_default);
+       return 1;
+}
+
+static int nfsaclsvc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd_fhandle *resp)
+{
+       fh_put(&resp->fh);
+       return 1;
+}
+
+#define nfsaclsvc_decode_voidargs      NULL
+#define nfsaclsvc_encode_voidres       NULL
+#define nfsaclsvc_release_void         NULL
+#define nfsd3_fhandleargs      nfsd_fhandle
+#define nfsd3_attrstatres      nfsd_attrstat
+#define nfsd3_voidres          nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize)  \
+ { (svc_procfunc) nfsacld_proc_##name,         \
+   (kxdrproc_t) nfsaclsvc_decode_##argt##args, \
+   (kxdrproc_t) nfsaclsvc_encode_##rest##res,  \
+   (kxdrproc_t) nfsaclsvc_release_##relt,              \
+   sizeof(struct nfsd3_##argt##args),          \
+   sizeof(struct nfsd3_##rest##res),           \
+   0,                                          \
+   cache,                                      \
+   respsize,                                   \
+ }
+
+#define ST 1           /* status*/
+#define AT 21          /* attributes */
+#define pAT (1+AT)     /* post attributes - conditional */
+#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
+
+static struct svc_procedure            nfsd_acl_procedures2[] = {
+  PROC(null,   void,           void,           void,     RC_NOCACHE, ST),
+  PROC(getacl, getacl,         getacl,         getacl,   RC_NOCACHE, ST+1+2*(1+ACL)),
+  PROC(setacl, setacl,         attrstat,       fhandle,  RC_NOCACHE, ST+AT),
+  PROC(getattr, fhandle,       attrstat,       fhandle,  RC_NOCACHE, ST+AT),
+  PROC(access, access,         access,         fhandle,  RC_NOCACHE, ST+AT+1),
+};
+
+struct svc_version     nfsd_acl_version2 = {
+               .vs_vers        = 2,
+               .vs_nproc       = 5,
+               .vs_proc        = nfsd_acl_procedures2,
+               .vs_dispatch    = nfsd_dispatch,
+               .vs_xdrsize     = NFS3_SVC_XDRSIZE,
+};
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
new file mode 100644 (file)
index 0000000..64ba405
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * linux/fs/nfsd/nfs3acl.c
+ *
+ * Process version 3 NFSACL requests.
+ *
+ * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfs3.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr3.h>
+#include <linux/posix_acl.h>
+#include <linux/nfsacl.h>
+
+#define RETURN_STATUS(st)      { resp->status = (st); return (st); }
+
+/*
+ * NULL call.
+ */
+static int
+nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+       return nfs_ok;
+}
+
+/*
+ * Get the Access and/or Default ACL of a file.
+ */
+static int nfsd3_proc_getacl(struct svc_rqst * rqstp,
+               struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
+{
+       svc_fh *fh;
+       struct posix_acl *acl;
+       int nfserr = 0;
+
+       fh = fh_copy(&resp->fh, &argp->fh);
+       if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP)))
+               RETURN_STATUS(nfserr_inval);
+
+       if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+               RETURN_STATUS(nfserr_inval);
+       resp->mask = argp->mask;
+
+       if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
+               acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS);
+               if (IS_ERR(acl)) {
+                       int err = PTR_ERR(acl);
+
+                       if (err == -ENODATA || err == -EOPNOTSUPP)
+                               acl = NULL;
+                       else {
+                               nfserr = nfserrno(err);
+                               goto fail;
+                       }
+               }
+               if (acl == NULL) {
+                       /* Solaris returns the inode's minimum ACL. */
+
+                       struct inode *inode = fh->fh_dentry->d_inode;
+                       acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+               }
+               resp->acl_access = acl;
+       }
+       if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
+               /* Check how Solaris handles requests for the Default ACL
+                  of a non-directory! */
+
+               acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT);
+               if (IS_ERR(acl)) {
+                       int err = PTR_ERR(acl);
+
+                       if (err == -ENODATA || err == -EOPNOTSUPP)
+                               acl = NULL;
+                       else {
+                               nfserr = nfserrno(err);
+                               goto fail;
+                       }
+               }
+               resp->acl_default = acl;
+       }
+
+       /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
+       RETURN_STATUS(0);
+
+fail:
+       posix_acl_release(resp->acl_access);
+       posix_acl_release(resp->acl_default);
+       RETURN_STATUS(nfserr);
+}
+
+/*
+ * Set the Access and/or Default ACL of a file.
+ */
+static int nfsd3_proc_setacl(struct svc_rqst * rqstp,
+               struct nfsd3_setaclargs *argp,
+               struct nfsd3_attrstat *resp)
+{
+       svc_fh *fh;
+       int nfserr = 0;
+
+       fh = fh_copy(&resp->fh, &argp->fh);
+       nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+
+       if (!nfserr) {
+               nfserr = nfserrno( nfsd_set_posix_acl(
+                       fh, ACL_TYPE_ACCESS, argp->acl_access) );
+       }
+       if (!nfserr) {
+               nfserr = nfserrno( nfsd_set_posix_acl(
+                       fh, ACL_TYPE_DEFAULT, argp->acl_default) );
+       }
+
+       /* argp->acl_{access,default} may have been allocated in
+          nfs3svc_decode_setaclargs. */
+       posix_acl_release(argp->acl_access);
+       posix_acl_release(argp->acl_default);
+       RETURN_STATUS(nfserr);
+}
+
+/*
+ * XDR decode functions
+ */
+static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclargs *args)
+{
+       if (!(p = nfs3svc_decode_fh(p, &args->fh)))
+               return 0;
+       args->mask = ntohl(*p); p++;
+
+       return xdr_argsize_check(rqstp, p);
+}
+
+
+static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_setaclargs *args)
+{
+       struct kvec *head = rqstp->rq_arg.head;
+       unsigned int base;
+       int n;
+
+       if (!(p = nfs3svc_decode_fh(p, &args->fh)))
+               return 0;
+       args->mask = ntohl(*p++);
+       if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
+           !xdr_argsize_check(rqstp, p))
+               return 0;
+
+       base = (char *)p - (char *)head->iov_base;
+       n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
+                         (args->mask & NFS_ACL) ?
+                         &args->acl_access : NULL);
+       if (n > 0)
+               n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
+                                 (args->mask & NFS_DFACL) ?
+                                 &args->acl_default : NULL);
+       return (n > 0);
+}
+
+/*
+ * XDR encode functions
+ */
+
+/* GETACL */
+static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclres *resp)
+{
+       struct dentry *dentry = resp->fh.fh_dentry;
+
+       p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
+       if (resp->status == 0 && dentry && dentry->d_inode) {
+               struct inode *inode = dentry->d_inode;
+               int w = nfsacl_size(
+                       (resp->mask & NFS_ACL)   ? resp->acl_access  : NULL,
+                       (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
+               struct kvec *head = rqstp->rq_res.head;
+               unsigned int base;
+               int n;
+
+               *p++ = htonl(resp->mask);
+               if (!xdr_ressize_check(rqstp, p))
+                       return 0;
+               base = (char *)p - (char *)head->iov_base;
+
+               rqstp->rq_res.page_len = w;
+               while (w > 0) {
+                       if (!svc_take_res_page(rqstp))
+                               return 0;
+                       w -= PAGE_SIZE;
+               }
+
+               n = nfsacl_encode(&rqstp->rq_res, base, inode,
+                                 resp->acl_access,
+                                 resp->mask & NFS_ACL, 0);
+               if (n > 0)
+                       n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
+                                         resp->acl_default,
+                                         resp->mask & NFS_DFACL,
+                                         NFS_ACL_DEFAULT);
+               if (n <= 0)
+                       return 0;
+       } else
+               if (!xdr_ressize_check(rqstp, p))
+                       return 0;
+
+       return 1;
+}
+
+/* SETACL */
+static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_attrstat *resp)
+{
+       p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
+
+       return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+static int nfs3svc_release_getacl(struct svc_rqst *rqstp, u32 *p,
+               struct nfsd3_getaclres *resp)
+{
+       fh_put(&resp->fh);
+       posix_acl_release(resp->acl_access);
+       posix_acl_release(resp->acl_default);
+       return 1;
+}
+
+#define nfs3svc_decode_voidargs                NULL
+#define nfs3svc_release_void           NULL
+#define nfsd3_setaclres                        nfsd3_attrstat
+#define nfsd3_voidres                  nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize)  \
+ { (svc_procfunc) nfsd3_proc_##name,           \
+   (kxdrproc_t) nfs3svc_decode_##argt##args,   \
+   (kxdrproc_t) nfs3svc_encode_##rest##res,    \
+   (kxdrproc_t) nfs3svc_release_##relt,                \
+   sizeof(struct nfsd3_##argt##args),          \
+   sizeof(struct nfsd3_##rest##res),           \
+   0,                                          \
+   cache,                                      \
+   respsize,                                   \
+ }
+
+#define ST 1           /* status*/
+#define AT 21          /* attributes */
+#define pAT (1+AT)     /* post attributes - conditional */
+#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
+
+static struct svc_procedure            nfsd_acl_procedures3[] = {
+  PROC(null,   void,           void,           void,     RC_NOCACHE, ST),
+  PROC(getacl, getacl,         getacl,         getacl,   RC_NOCACHE, ST+1+2*(1+ACL)),
+  PROC(setacl, setacl,         setacl,         fhandle,  RC_NOCACHE, ST+pAT),
+};
+
+struct svc_version     nfsd_acl_version3 = {
+               .vs_vers        = 3,
+               .vs_nproc       = 3,
+               .vs_proc        = nfsd_acl_procedures3,
+               .vs_dispatch    = nfsd_dispatch,
+               .vs_xdrsize     = NFS3_SVC_XDRSIZE,
+};
+
index 11f8068..e0e134d 100644 (file)
@@ -71,6 +71,12 @@ decode_fh(u32 *p, struct svc_fh *fhp)
        return p + XDR_QUADLEN(size);
 }
 
+/* Helper function for NFSv3 ACL code */
+u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp)
+{
+       return decode_fh(p, fhp);
+}
+
 static inline u32 *
 encode_fh(u32 *p, struct svc_fh *fhp)
 {
@@ -233,6 +239,13 @@ encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
        return p;
 }
 
+/* Helper for NFSv3 ACLs */
+u32 *
+nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+       return encode_post_op_attr(rqstp, p, fhp);
+}
+
 /*
  * Enocde weak cache consistency data
  */
index 02ded7c..79b25b1 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/nfsd/stats.h>
 #include <linux/nfsd/cache.h>
 #include <linux/lockd/bind.h>
+#include <linux/nfsacl.h>
 
 #define NFSDDBG_FACILITY       NFSDDBG_SVC
 
@@ -362,6 +363,31 @@ nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp)
        return 1;
 }
 
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+static struct svc_stat nfsd_acl_svcstats;
+static struct svc_version *    nfsd_acl_version[] = {
+       [2] = &nfsd_acl_version2,
+       [3] = &nfsd_acl_version3,
+};
+
+#define NFSD_ACL_NRVERS                (sizeof(nfsd_acl_version)/sizeof(nfsd_acl_version[0]))
+static struct svc_program      nfsd_acl_program = {
+       .pg_prog                = NFS_ACL_PROGRAM,
+       .pg_nvers               = NFSD_ACL_NRVERS,
+       .pg_vers                = nfsd_acl_version,
+       .pg_name                = "nfsd",
+       .pg_stats               = &nfsd_acl_svcstats,
+};
+
+static struct svc_stat nfsd_acl_svcstats = {
+       .program        = &nfsd_acl_program,
+};
+
+#define nfsd_acl_program_p     &nfsd_acl_program
+#else
+#define nfsd_acl_program_p     NULL
+#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
+
 extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
 
 static struct svc_version *    nfsd_version[] = {
@@ -376,6 +402,7 @@ static struct svc_version * nfsd_version[] = {
 
 #define NFSD_NRVERS            (sizeof(nfsd_version)/sizeof(nfsd_version[0]))
 struct svc_program             nfsd_program = {
+       .pg_next                = nfsd_acl_program_p,
        .pg_prog                = NFS_PROGRAM,          /* program number */
        .pg_nvers               = NFSD_NRVERS,          /* nr of entries in nfsd_version */
        .pg_vers                = nfsd_version,         /* version table */
index 948b082..b45999f 100644 (file)
@@ -49,6 +49,12 @@ decode_fh(u32 *p, struct svc_fh *fhp)
        return p + (NFS_FHSIZE >> 2);
 }
 
+/* Helper function for NFSv2 ACL code */
+u32 *nfs2svc_decode_fh(u32 *p, struct svc_fh *fhp)
+{
+       return decode_fh(p, fhp);
+}
+
 static inline u32 *
 encode_fh(u32 *p, struct svc_fh *fhp)
 {
@@ -190,6 +196,11 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
        return p;
 }
 
+/* Helper function for NFSv2 ACL code */
+u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+       return encode_fattr(rqstp, p, fhp);
+}
 
 /*
  * XDR decode functions
index e3e9d21..ae3940d 100644 (file)
@@ -46,8 +46,9 @@
 #include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 #include <linux/dnotify.h>
-#ifdef CONFIG_NFSD_V4
+#include <linux/xattr_acl.h>
 #include <linux/posix_acl.h>
+#ifdef CONFIG_NFSD_V4
 #include <linux/posix_acl_xattr.h>
 #include <linux/xattr_acl.h>
 #include <linux/xattr.h>
@@ -1857,3 +1858,107 @@ nfsd_racache_init(int cache_size)
        nfsdstats.ra_size = cache_size;
        return 0;
 }
+
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+struct posix_acl *
+nfsd_get_posix_acl(struct svc_fh *fhp, int type)
+{
+       struct inode *inode = fhp->fh_dentry->d_inode;
+       char *name;
+       void *value = NULL;
+       ssize_t size;
+       struct posix_acl *acl;
+
+       if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr)
+               return ERR_PTR(-EOPNOTSUPP);
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       name = XATTR_NAME_ACL_ACCESS;
+                       break;
+               case ACL_TYPE_DEFAULT:
+                       name = XATTR_NAME_ACL_DEFAULT;
+                       break;
+               default:
+                       return ERR_PTR(-EOPNOTSUPP);
+       }
+
+       size = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0);
+
+       if (size < 0) {
+               acl = ERR_PTR(size);
+               goto getout;
+       } else if (size > 0) {
+               value = kmalloc(size, GFP_KERNEL);
+               if (!value) {
+                       acl = ERR_PTR(-ENOMEM);
+                       goto getout;
+               }
+               size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size);
+               if (size < 0) {
+                       acl = ERR_PTR(size);
+                       goto getout;
+               }
+       }
+       acl = posix_acl_from_xattr(value, size);
+
+getout:
+       kfree(value);
+       return acl;
+}
+
+int
+nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
+{
+       struct inode *inode = fhp->fh_dentry->d_inode;
+       char *name;
+       void *value = NULL;
+       size_t size;
+       int error;
+
+       if (!IS_POSIXACL(inode) || !inode->i_op ||
+           !inode->i_op->setxattr || !inode->i_op->removexattr)
+               return -EOPNOTSUPP;
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       name = XATTR_NAME_ACL_ACCESS;
+                       break;
+               case ACL_TYPE_DEFAULT:
+                       name = XATTR_NAME_ACL_DEFAULT;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+       }
+
+       if (acl && acl->a_count) {
+               size = xattr_acl_size(acl->a_count);
+               value = kmalloc(size, GFP_KERNEL);
+               if (!value)
+                       return -ENOMEM;
+               size = posix_acl_to_xattr(acl, value, size);
+               if (size < 0) {
+                       error = size;
+                       goto getout;
+               }
+       } else
+               size = 0;
+
+       if (!fhp->fh_locked)
+               fh_lock(fhp);  /* unlocking is done automatically */
+       if (size)
+               error = inode->i_op->setxattr(fhp->fh_dentry, name,
+                                             value, size, 0);
+       else {
+               if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
+                       error = 0;
+               else {
+                       error = inode->i_op->removexattr(fhp->fh_dentry, name);
+                       if (error == -ENODATA)
+                               error = 0;
+               }
+       }
+
+getout:
+       kfree(value);
+       return error;
+}
+#endif  /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
diff --git a/include/linux/nfsacl.h b/include/linux/nfsacl.h
new file mode 100644 (file)
index 0000000..54487a9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * File: linux/nfsacl.h
+ *
+ * (C) 2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+#ifndef __LINUX_NFSACL_H
+#define __LINUX_NFSACL_H
+
+#define NFS_ACL_PROGRAM        100227
+
+#define ACLPROC2_GETACL                1
+#define ACLPROC2_SETACL                2
+#define ACLPROC2_GETATTR       3
+#define ACLPROC2_ACCESS                4
+
+#define ACLPROC3_GETACL                1
+#define ACLPROC3_SETACL                2
+
+
+/* Flags for the getacl/setacl mode */
+#define NFS_ACL                        0x0001
+#define NFS_ACLCNT             0x0002
+#define NFS_DFACL              0x0004
+#define NFS_DFACLCNT           0x0008
+
+/* Flag for Default ACL entries */
+#define NFS_ACL_DEFAULT                0x1000
+
+#ifdef __KERNEL__
+
+#include <linux/posix_acl.h>
+
+/* Maximum number of ACL entries over NFS */
+#define NFS_ACL_MAX_ENTRIES    1024
+
+#define NFSACL_MAXWORDS                (2*(2+3*NFS_ACL_MAX_ENTRIES))
+#define NFSACL_MAXPAGES                ((2*(8+12*NFS_ACL_MAX_ENTRIES) + PAGE_SIZE-1) \
+                                >> PAGE_SHIFT)
+
+static inline unsigned int
+nfsacl_size(struct posix_acl *acl_access, struct posix_acl *acl_default)
+{
+       unsigned int w = 16;
+       w += max(acl_access ? (int)acl_access->a_count : 3, 4) * 12;
+       if (acl_default)
+               w += max((int)acl_default->a_count, 4) * 12;
+       return w;
+}
+
+extern unsigned int
+nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
+             struct posix_acl *acl, int encode_entries, int typeflag);
+extern unsigned int
+nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
+             struct posix_acl **pacl);
+
+#endif /* __KERNEL__ */
+#endif  /* __LINUX_NFSACL_H */
index 8f85d9a..4bf931d 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/unistd.h>
 #include <linux/dirent.h>
 #include <linux/fs.h>
+#include <linux/posix_acl.h>
 #include <linux/mount.h>
 
 #include <linux/nfsd/debug.h>
@@ -124,6 +125,21 @@ int                nfsd_statfs(struct svc_rqst *, struct svc_fh *,
 int            nfsd_notify_change(struct inode *, struct iattr *);
 int            nfsd_permission(struct svc_export *, struct dentry *, int);
 
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+#ifdef CONFIG_NFSD_V2_ACL
+extern struct svc_version nfsd_acl_version2;
+#else
+#define nfsd_acl_version2 NULL
+#endif
+#ifdef CONFIG_NFSD_V3_ACL
+extern struct svc_version nfsd_acl_version3;
+#else
+#define nfsd_acl_version3 NULL
+#endif
+struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int);
+int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
+#endif
+
 
 /* 
  * NFSv4 State
index ecccef7..130d4f5 100644 (file)
@@ -169,4 +169,8 @@ int nfssvc_encode_entry(struct readdir_cd *, const char *name,
 
 int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
 
+/* Helper functions for NFSv2 ACL code */
+u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp);
+u32 *nfs2svc_decode_fh(u32 *p, struct svc_fh *fhp);
+
 #endif /* LINUX_NFSD_H */
index 0ae9e0e..21e18ce 100644 (file)
@@ -110,6 +110,19 @@ struct nfsd3_commitargs {
        __u32                   count;
 };
 
+struct nfsd3_getaclargs {
+       struct svc_fh           fh;
+       int                     mask;
+};
+
+struct posix_acl;
+struct nfsd3_setaclargs {
+       struct svc_fh           fh;
+       int                     mask;
+       struct posix_acl        *acl_access;
+       struct posix_acl        *acl_default;
+};
+
 struct nfsd3_attrstat {
        __u32                   status;
        struct svc_fh           fh;
@@ -209,6 +222,14 @@ struct nfsd3_commitres {
        struct svc_fh           fh;
 };
 
+struct nfsd3_getaclres {
+       __u32                   status;
+       struct svc_fh           fh;
+       int                     mask;
+       struct posix_acl        *acl_access;
+       struct posix_acl        *acl_default;
+};
+
 /* dummy type for release */
 struct nfsd3_fhandle_pair {
        __u32                   dummy;
@@ -241,6 +262,7 @@ union nfsd3_xdrstore {
        struct nfsd3_fsinfores          fsinfores;
        struct nfsd3_pathconfres        pathconfres;
        struct nfsd3_commitres          commitres;
+       struct nfsd3_getaclres          getaclres;
 };
 
 #define NFS3_SVC_XDRSIZE               sizeof(union nfsd3_xdrstore)
@@ -316,6 +338,10 @@ int nfs3svc_encode_entry(struct readdir_cd *, const char *name,
 int nfs3svc_encode_entry_plus(struct readdir_cd *, const char *name,
                                int namlen, loff_t offset, ino_t ino,
                                unsigned int);
+/* Helper functions for NFSv3 ACL code */
+u32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p,
+                               struct svc_fh *fhp);
+u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp);
 
 
 #endif /* _LINUX_NFSD_XDR3_H */
index facb944..5af8800 100644 (file)
@@ -185,6 +185,17 @@ xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
        return vec->iov_len <= PAGE_SIZE;
 }
 
+static inline struct page *
+svc_take_res_page(struct svc_rqst *rqstp)
+{
+       if (rqstp->rq_arghi <= rqstp->rq_argused)
+               return NULL;
+       rqstp->rq_arghi--;
+       rqstp->rq_respages[rqstp->rq_resused] =
+               rqstp->rq_argpages[rqstp->rq_arghi];
+       return rqstp->rq_respages[rqstp->rq_resused++];
+}
+
 static inline int svc_take_page(struct svc_rqst *rqstp)
 {
        if (rqstp->rq_arghi <= rqstp->rq_argused)