[PATCH] NFS: Add support for NFSv3 ACLs
Andreas Gruenbacher [Wed, 22 Jun 2005 17:16:27 +0000 (17:16 +0000)]
 This adds acl support fo nfs clients via the NFSACL protocol extension, by
 implementing the getxattr, listxattr, setxattr, and removexattr iops for the
 system.posix_acl_access and system.posix_acl_default attributes.  This patch
 implements a dumb version that uses no caching (and thus adds some overhead).
 (Another patch in this patchset adds caching as well.)

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>

13 files changed:
fs/Kconfig
fs/nfs/Makefile
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/nfs3acl.c [new file with mode: 0644]
fs/nfs/nfs3proc.c
fs/nfs/nfs3xdr.c
fs/nfs/nfsroot.c
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_mount.h
include/linux/nfs_xdr.h

index d44b04d..a7c0cc3 100644 (file)
@@ -1268,6 +1268,7 @@ config NFS_FS
        depends on INET
        select LOCKD
        select SUNRPC
+       select NFS_ACL_SUPPORT if NFS_V3_ACL
        help
          If you are connected to some other (usually local) Unix computer
          (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
@@ -1310,6 +1311,16 @@ config NFS_V3
 
          If unsure, say Y.
 
+config NFS_V3_ACL
+       bool "Provide client support for the NFSv3 ACL protocol extension"
+       depends on NFS_V3
+       help
+         Implement the NFSv3 ACL protocol extension for manipulating POSIX
+         Access Control Lists.  The server should also be compiled with
+         the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option.
+
+         If unsure, say N.
+
 config NFS_V4
        bool "Provide NFSv4 client support (EXPERIMENTAL)"
        depends on NFS_FS && EXPERIMENTAL
index b4baa03..8b3bb71 100644 (file)
@@ -8,6 +8,7 @@ nfs-y                   := dir.o file.o inode.o nfs2xdr.o pagelist.o \
                           proc.o read.o symlink.o unlink.o write.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o      
 nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
+nfs-$(CONFIG_NFS_V3_ACL)       += nfs3acl.o
 nfs-$(CONFIG_NFS_V4)   += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
                           delegation.o idmap.o \
                           callback.o callback_xdr.o callback_proc.o
index 5720537..2c6a959 100644 (file)
@@ -75,6 +75,27 @@ struct inode_operations nfs_dir_inode_operations = {
        .setattr        = nfs_setattr,
 };
 
+#ifdef CONFIG_NFS_V3
+struct inode_operations nfs3_dir_inode_operations = {
+       .create         = nfs_create,
+       .lookup         = nfs_lookup,
+       .link           = nfs_link,
+       .unlink         = nfs_unlink,
+       .symlink        = nfs_symlink,
+       .mkdir          = nfs_mkdir,
+       .rmdir          = nfs_rmdir,
+       .mknod          = nfs_mknod,
+       .rename         = nfs_rename,
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+       .listxattr      = nfs3_listxattr,
+       .getxattr       = nfs3_getxattr,
+       .setxattr       = nfs3_setxattr,
+       .removexattr    = nfs3_removexattr,
+};
+#endif  /* CONFIG_NFS_V3 */
+
 #ifdef CONFIG_NFS_V4
 
 static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);
index 55c9075..a606708 100644 (file)
@@ -71,6 +71,18 @@ struct inode_operations nfs_file_inode_operations = {
        .setattr        = nfs_setattr,
 };
 
+#ifdef CONFIG_NFS_V3
+struct inode_operations nfs3_file_inode_operations = {
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
+       .listxattr      = nfs3_listxattr,
+       .getxattr       = nfs3_getxattr,
+       .setxattr       = nfs3_setxattr,
+       .removexattr    = nfs3_removexattr,
+};
+#endif  /* CONFIG_NFS_v3 */
+
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)    (0)
index 97b3fe7..440b9cb 100644 (file)
@@ -108,6 +108,21 @@ static struct rpc_program  nfs_program = {
        .pipe_dir_name          = "/nfs",
 };
 
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_stat         nfsacl_rpcstat = { &nfsacl_program };
+static struct rpc_version *    nfsacl_version[] = {
+       [3]                     = &nfsacl_version3,
+};
+
+struct rpc_program             nfsacl_program = {
+       .name =                 "nfsacl",
+       .number =               NFS_ACL_PROGRAM,
+       .nrvers =               sizeof(nfsacl_version) / sizeof(nfsacl_version[0]),
+       .version =              nfsacl_version,
+       .stats =                &nfsacl_rpcstat,
+};
+#endif  /* CONFIG_NFS_V3_ACL */
+
 static inline unsigned long
 nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
 {
@@ -165,6 +180,9 @@ nfs_umount_begin(struct super_block *sb)
        /* -EIO all pending I/O */
        if (!IS_ERR(rpc))
                rpc_killall_tasks(rpc);
+       rpc = NFS_SB(sb)->client_acl;
+       if (!IS_ERR(rpc))
+               rpc_killall_tasks(rpc);
 }
 
 
@@ -461,8 +479,17 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
                atomic_inc(&server->client->cl_count);
                server->client_sys = server->client;
        }
-
        if (server->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3_ACL
+               if (!(server->flags & NFS_MOUNT_NOACL)) {
+                       server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
+                       /* No errors! Assume that Sun nfsacls are supported */
+                       if (!IS_ERR(server->client_acl))
+                               server->caps |= NFS_CAP_ACLS;
+               }
+#else
+               server->flags &= ~NFS_MOUNT_NOACL;
+#endif /* CONFIG_NFS_V3_ACL */
                if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
                        server->namelen = NFS3_MAXNAMLEN;
                sb->s_time_gran = 1;
@@ -546,6 +573,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
                { NFS_MOUNT_NOCTO, ",nocto", "" },
                { NFS_MOUNT_NOAC, ",noac", "" },
                { NFS_MOUNT_NONLM, ",nolock", ",lock" },
+               { NFS_MOUNT_NOACL, ",noacl", "" },
                { 0, NULL, NULL }
        };
        struct proc_nfs_info *nfs_infop;
@@ -1452,7 +1480,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
        memset(server, 0, sizeof(struct nfs_server));
        /* Zero out the NFS state stuff */
        init_nfsv4_state(server);
-       server->client = server->client_sys = ERR_PTR(-EINVAL);
+       server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
 
        root = &server->fh;
        if (data->flags & NFS_MOUNT_VER3)
@@ -1513,6 +1541,8 @@ static void nfs_kill_super(struct super_block *s)
                rpc_shutdown_client(server->client);
        if (!IS_ERR(server->client_sys))
                rpc_shutdown_client(server->client_sys);
+       if (!IS_ERR(server->client_acl))
+               rpc_shutdown_client(server->client_acl);
 
        if (!(server->flags & NFS_MOUNT_NONLM))
                lockd_down();   /* release rpc.lockd */
@@ -1794,7 +1824,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
        memset(server, 0, sizeof(struct nfs_server));
        /* Zero out the NFS state stuff */
        init_nfsv4_state(server);
-       server->client = server->client_sys = ERR_PTR(-EINVAL);
+       server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
 
        p = nfs_copy_user_string(NULL, &data->hostname, 256);
        if (IS_ERR(p))
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
new file mode 100644 (file)
index 0000000..393ba79
--- /dev/null
@@ -0,0 +1,303 @@
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+#include <linux/xattr_acl.h>
+#include <linux/nfsacl.h>
+
+#define NFSDBG_FACILITY        NFSDBG_PROC
+
+ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+       struct inode *inode = dentry->d_inode;
+       struct posix_acl *acl;
+       int pos=0, len=0;
+
+#      define output(s) do {                                           \
+                       if (pos + sizeof(s) <= size) {                  \
+                               memcpy(buffer + pos, s, sizeof(s));     \
+                               pos += sizeof(s);                       \
+                       }                                               \
+                       len += sizeof(s);                               \
+               } while(0)
+
+       acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       if (acl) {
+               output("system.posix_acl_access");
+               posix_acl_release(acl);
+       }
+
+       if (S_ISDIR(inode->i_mode)) {
+               acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT);
+               if (IS_ERR(acl))
+                       return PTR_ERR(acl);
+               if (acl) {
+                       output("system.posix_acl_default");
+                       posix_acl_release(acl);
+               }
+       }
+
+#      undef output
+
+       if (!buffer || len <= size)
+               return len;
+       return -ERANGE;
+}
+
+ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
+               void *buffer, size_t size)
+{
+       struct inode *inode = dentry->d_inode;
+       struct posix_acl *acl;
+       int type, error = 0;
+
+       if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+               type = ACL_TYPE_ACCESS;
+       else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+               type = ACL_TYPE_DEFAULT;
+       else
+               return -EOPNOTSUPP;
+
+       acl = nfs3_proc_getacl(inode, type);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       else if (acl) {
+               if (type == ACL_TYPE_ACCESS && acl->a_count == 0)
+                       error = -ENODATA;
+               else
+                       error = posix_acl_to_xattr(acl, buffer, size);
+               posix_acl_release(acl);
+       } else
+               error = -ENODATA;
+
+       return error;
+}
+
+int nfs3_setxattr(struct dentry *dentry, const char *name,
+            const void *value, size_t size, int flags)
+{
+       struct inode *inode = dentry->d_inode;
+       struct posix_acl *acl;
+       int type, error;
+
+       if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+               type = ACL_TYPE_ACCESS;
+       else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+               type = ACL_TYPE_DEFAULT;
+       else
+               return -EOPNOTSUPP;
+
+       acl = posix_acl_from_xattr(value, size);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       error = nfs3_proc_setacl(inode, type, acl);
+       posix_acl_release(acl);
+
+       return error;
+}
+
+int nfs3_removexattr(struct dentry *dentry, const char *name)
+{
+       struct inode *inode = dentry->d_inode;
+       int type;
+
+       if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+               type = ACL_TYPE_ACCESS;
+       else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+               type = ACL_TYPE_DEFAULT;
+       else
+               return -EOPNOTSUPP;
+
+       return nfs3_proc_setacl(inode, type, NULL);
+}
+
+struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_fattr fattr;
+       struct page *pages[NFSACL_MAXPAGES] = { };
+       struct nfs3_getaclargs args = {
+               .fh = NFS_FH(inode),
+               /* The xdr layer may allocate pages here. */
+               .pages = pages,
+       };
+       struct nfs3_getaclres res = {
+               .fattr =        &fattr,
+       };
+       struct posix_acl *acl = NULL;
+       int status, count;
+
+       if (!nfs_server_capable(inode, NFS_CAP_ACLS))
+               return ERR_PTR(-EOPNOTSUPP);
+
+       switch (type) {
+               case ACL_TYPE_ACCESS:
+                       args.mask = NFS_ACLCNT|NFS_ACL;
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       if (!S_ISDIR(inode->i_mode))
+                               return NULL;
+                       args.mask = NFS_DFACLCNT|NFS_DFACL;
+                       break;
+
+               default:
+                       return ERR_PTR(-EINVAL);
+       }
+
+       dprintk("NFS call getacl\n");
+       status = rpc_call(server->client_acl, ACLPROC3_GETACL,
+                         &args, &res, 0);
+       dprintk("NFS reply getacl: %d\n", status);
+
+       /* pages may have been allocated at the xdr layer. */
+       for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
+               __free_page(args.pages[count]);
+
+       switch (status) {
+               case 0:
+                       status = nfs_refresh_inode(inode, &fattr);
+                       break;
+               case -EPFNOSUPPORT:
+               case -EPROTONOSUPPORT:
+                       dprintk("NFS_V3_ACL extension not supported; disabling\n");
+                       server->caps &= ~NFS_CAP_ACLS;
+               case -ENOTSUPP:
+                       status = -EOPNOTSUPP;
+               default:
+                       goto getout;
+       }
+       if ((args.mask & res.mask) != args.mask) {
+               status = -EIO;
+               goto getout;
+       }
+
+       if (res.acl_access != NULL) {
+               if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
+                       posix_acl_release(res.acl_access);
+                       res.acl_access = NULL;
+               }
+       }
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       acl = res.acl_access;
+                       res.acl_access = NULL;
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       acl = res.acl_default;
+                       res.acl_default = NULL;
+       }
+
+getout:
+       posix_acl_release(res.acl_access);
+       posix_acl_release(res.acl_default);
+
+       if (status != 0) {
+               posix_acl_release(acl);
+               acl = ERR_PTR(status);
+       }
+       return acl;
+}
+
+static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
+                 struct posix_acl *dfacl)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_fattr fattr;
+       struct page *pages[NFSACL_MAXPAGES] = { };
+       struct nfs3_setaclargs args = {
+               .inode = inode,
+               .mask = NFS_ACL,
+               .acl_access = acl,
+               .pages = pages,
+       };
+       int status, count;
+
+       status = -EOPNOTSUPP;
+       if (!nfs_server_capable(inode, NFS_CAP_ACLS))
+               goto out;
+
+       /* We are doing this here, because XDR marshalling can only
+          return -ENOMEM. */
+       status = -ENOSPC;
+       if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES)
+               goto out;
+       if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES)
+               goto out;
+       if (S_ISDIR(inode->i_mode)) {
+               args.mask |= NFS_DFACL;
+               args.acl_default = dfacl;
+       }
+
+       dprintk("NFS call setacl\n");
+       nfs_begin_data_update(inode);
+       status = rpc_call(server->client_acl, ACLPROC3_SETACL,
+                         &args, &fattr, 0);
+       NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
+       nfs_end_data_update(inode);
+       dprintk("NFS reply setacl: %d\n", status);
+
+       /* pages may have been allocated at the xdr layer. */
+       for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
+               __free_page(args.pages[count]);
+
+       switch (status) {
+               case 0:
+                       status = nfs_refresh_inode(inode, &fattr);
+                       break;
+               case -EPFNOSUPPORT:
+               case -EPROTONOSUPPORT:
+                       dprintk("NFS_V3_ACL SETACL RPC not supported"
+                                       "(will not retry)\n");
+                       server->caps &= ~NFS_CAP_ACLS;
+               case -ENOTSUPP:
+                       status = -EOPNOTSUPP;
+       }
+out:
+       return status;
+}
+
+int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl)
+{
+       struct posix_acl *alloc = NULL, *dfacl = NULL;
+       int status;
+
+       if (S_ISDIR(inode->i_mode)) {
+               switch(type) {
+                       case ACL_TYPE_ACCESS:
+                               alloc = dfacl = nfs3_proc_getacl(inode,
+                                               ACL_TYPE_DEFAULT);
+                               if (IS_ERR(alloc))
+                                       goto fail;
+                               break;
+
+                       case ACL_TYPE_DEFAULT:
+                               dfacl = acl;
+                               alloc = acl = nfs3_proc_getacl(inode,
+                                               ACL_TYPE_ACCESS);
+                               if (IS_ERR(alloc))
+                                       goto fail;
+                               break;
+
+                       default:
+                               return -EINVAL;
+               }
+       } else if (type != ACL_TYPE_ACCESS)
+                       return -EINVAL;
+
+       if (acl == NULL) {
+               alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+               if (IS_ERR(alloc))
+                       goto fail;
+       }
+       status = nfs3_proc_setacls(inode, acl, dfacl);
+       posix_acl_release(alloc);
+       return status;
+
+fail:
+       return PTR_ERR(alloc);
+}
index 53953a7..d03bac0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/nfs_page.h>
 #include <linux/lockd/bind.h>
 #include <linux/smp_lock.h>
+#include <linux/nfs_mount.h>
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
@@ -45,7 +46,7 @@ static inline int
 nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
 {
        struct rpc_message msg = {
-               .rpc_proc       = &nfs3_procedures[proc],
+               .rpc_proc       = &clnt->cl_procinfo[proc],
                .rpc_argp       = argp,
                .rpc_resp       = resp,
        };
@@ -825,8 +826,8 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
 struct nfs_rpc_ops     nfs_v3_clientops = {
        .version        = 3,                    /* protocol version */
        .dentry_ops     = &nfs_dentry_operations,
-       .dir_inode_ops  = &nfs_dir_inode_operations,
-       .file_inode_ops = &nfs_file_inode_operations,
+       .dir_inode_ops  = &nfs3_dir_inode_operations,
+       .file_inode_ops = &nfs3_file_inode_operations,
        .getroot        = nfs3_proc_get_root,
        .getattr        = nfs3_proc_getattr,
        .setattr        = nfs3_proc_setattr,
index a3593d4..a4437fb 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfsacl.h>
 
 #define NFSDBG_FACILITY                NFSDBG_XDR
 
@@ -79,6 +80,11 @@ extern int                   nfs_stat_to_errno(int);
 #define NFS3_pathconfres_sz    (1+NFS3_post_op_attr_sz+6)
 #define NFS3_commitres_sz      (1+NFS3_wcc_data_sz+2)
 
+#define ACL3_getaclargs_sz     (NFS3_fh_sz+1)
+#define ACL3_setaclargs_sz     (NFS3_fh_sz+1+2*(2+5*3))
+#define ACL3_getaclres_sz      (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
+#define ACL3_setaclres_sz      (1+NFS3_post_op_attr_sz)
+
 /*
  * Map file type to S_IFMT bits
  */
@@ -627,6 +633,74 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Encode GETACL arguments
+ */
+static int
+nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
+                   struct nfs3_getaclargs *args)
+{
+       struct rpc_auth *auth = req->rq_task->tk_auth;
+       unsigned int replen;
+
+       p = xdr_encode_fhandle(p, args->fh);
+       *p++ = htonl(args->mask);
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+       if (args->mask & (NFS_ACL | NFS_DFACL)) {
+               /* Inline the page array */
+               replen = (RPC_REPHDRSIZE + auth->au_rslack +
+                         ACL3_getaclres_sz) << 2;
+               xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
+                                NFSACL_MAXPAGES << PAGE_SHIFT);
+       }
+       return 0;
+}
+
+/*
+ * Encode SETACL arguments
+ */
+static int
+nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
+                   struct nfs3_setaclargs *args)
+{
+       struct xdr_buf *buf = &req->rq_snd_buf;
+       unsigned int base, len_in_head, len = nfsacl_size(
+               (args->mask & NFS_ACL)   ? args->acl_access  : NULL,
+               (args->mask & NFS_DFACL) ? args->acl_default : NULL);
+       int count, err;
+
+       p = xdr_encode_fhandle(p, NFS_FH(args->inode));
+       *p++ = htonl(args->mask);
+       base = (char *)p - (char *)buf->head->iov_base;
+       /* put as much of the acls into head as possible. */
+       len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
+       len -= len_in_head;
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + len_in_head);
+
+       for (count = 0; (count << PAGE_SHIFT) < len; count++) {
+               args->pages[count] = alloc_page(GFP_KERNEL);
+               if (!args->pages[count]) {
+                       while (count)
+                               __free_page(args->pages[--count]);
+                       return -ENOMEM;
+               }
+       }
+       xdr_encode_pages(buf, args->pages, 0, len);
+
+       err = nfsacl_encode(buf, base, args->inode,
+                           (args->mask & NFS_ACL) ?
+                           args->acl_access : NULL, 1, 0);
+       if (err > 0)
+               err = nfsacl_encode(buf, base + err, args->inode,
+                                   (args->mask & NFS_DFACL) ?
+                                   args->acl_default : NULL, 1,
+                                   NFS_ACL_DEFAULT);
+       return (err > 0) ? 0 : err;
+}
+#endif  /* CONFIG_NFS_V3_ACL */
+
 /*
  * NFS XDR decode functions
  */
@@ -978,6 +1052,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Decode GETACL reply
+ */
+static int
+nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
+                  struct nfs3_getaclres *res)
+{
+       struct xdr_buf *buf = &req->rq_rcv_buf;
+       int status = ntohl(*p++);
+       struct posix_acl **acl;
+       unsigned int *aclcnt;
+       int err, base;
+
+       if (status != 0)
+               return -nfs_stat_to_errno(status);
+       p = xdr_decode_post_op_attr(p, res->fattr);
+       res->mask = ntohl(*p++);
+       if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+               return -EINVAL;
+       base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
+
+       acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
+       aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
+       err = nfsacl_decode(buf, base, aclcnt, acl);
+
+       acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
+       aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
+       if (err > 0)
+               err = nfsacl_decode(buf, base + err, aclcnt, acl);
+       return (err > 0) ? 0 : err;
+}
+
+/*
+ * Decode setacl reply.
+ */
+static int
+nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+       int status = ntohl(*p++);
+
+       if (status)
+               return -nfs_stat_to_errno(status);
+       xdr_decode_post_op_attr(p, fattr);
+       return 0;
+}
+#endif  /* CONFIG_NFS_V3_ACL */
+
 #ifndef MAX
 # define MAX(a, b)     (((a) > (b))? (a) : (b))
 #endif
@@ -1021,3 +1143,28 @@ struct rpc_version               nfs_version3 = {
        .procs                  = nfs3_procedures
 };
 
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_procinfo     nfs3_acl_procedures[] = {
+       [ACLPROC3_GETACL] = {
+               .p_proc = ACLPROC3_GETACL,
+               .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
+               .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
+               .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
+               .p_timer = 1,
+       },
+       [ACLPROC3_SETACL] = {
+               .p_proc = ACLPROC3_SETACL,
+               .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
+               .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
+               .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
+               .p_timer = 0,
+       },
+};
+
+struct rpc_version             nfsacl_version3 = {
+       .number                 = 3,
+       .nrprocs                = sizeof(nfs3_acl_procedures)/
+                                 sizeof(nfs3_acl_procedures[0]),
+       .procs                  = nfs3_acl_procedures,
+};
+#endif  /* CONFIG_NFS_V3_ACL */
index fd5bc59..1b272a1 100644 (file)
@@ -124,6 +124,7 @@ enum {
        Opt_soft, Opt_hard, Opt_intr,
        Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, 
        Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp,
+       Opt_acl, Opt_noacl,
        /* Error token */
        Opt_err
 };
@@ -158,6 +159,8 @@ static match_table_t __initdata tokens = {
        {Opt_udp, "udp"},
        {Opt_tcp, "proto=tcp"},
        {Opt_tcp, "tcp"},
+       {Opt_acl, "acl"},
+       {Opt_noacl, "noacl"},
        {Opt_err, NULL}
        
 };
@@ -266,6 +269,12 @@ static int __init root_nfs_parse(char *name, char *buf)
                        case Opt_tcp:
                                nfs_data.flags |= NFS_MOUNT_TCP;
                                break;
+                       case Opt_acl:
+                               nfs_data.flags &= ~NFS_MOUNT_NOACL;
+                               break;
+                       case Opt_noacl:
+                               nfs_data.flags |= NFS_MOUNT_NOACL;
+                               break;
                        default : 
                                return 0;
                }
index d2b5d7e..3a5e442 100644 (file)
@@ -301,6 +301,9 @@ extern u32 root_nfs_parse_addr(char *name); /*__init*/
  * linux/fs/nfs/file.c
  */
 extern struct inode_operations nfs_file_inode_operations;
+#ifdef CONFIG_NFS_V3
+extern struct inode_operations nfs3_file_inode_operations;
+#endif /* CONFIG_NFS_V3 */
 extern struct file_operations nfs_file_operations;
 extern struct address_space_operations nfs_file_aops;
 
@@ -316,6 +319,22 @@ static inline struct rpc_cred *nfs_file_cred(struct file *file)
 }
 
 /*
+ * linux/fs/nfs/xattr.c
+ */
+#ifdef CONFIG_NFS_V3_ACL
+extern ssize_t nfs3_listxattr(struct dentry *, char *, size_t);
+extern ssize_t nfs3_getxattr(struct dentry *, const char *, void *, size_t);
+extern int nfs3_setxattr(struct dentry *, const char *,
+                       const void *, size_t, int);
+extern int nfs3_removexattr (struct dentry *, const char *name);
+#else
+# define nfs3_listxattr NULL
+# define nfs3_getxattr NULL
+# define nfs3_setxattr NULL
+# define nfs3_removexattr NULL
+#endif
+
+/*
  * linux/fs/nfs/direct.c
  */
 extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t,
@@ -329,6 +348,9 @@ extern ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf,
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
+#ifdef CONFIG_NFS_V3
+extern struct inode_operations nfs3_dir_inode_operations;
+#endif /* CONFIG_NFS_V3 */
 extern struct file_operations nfs_dir_operations;
 extern struct dentry_operations nfs_dentry_operations;
 
@@ -450,6 +472,15 @@ static inline void nfs_readdata_free(struct nfs_read_data *p)
 extern void  nfs_readdata_release(struct rpc_task *task);
 
 /*
+ * linux/fs/nfs3proc.c
+ */
+#ifdef CONFIG_NFS_V3_ACL
+extern struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type);
+extern int nfs3_proc_setacl(struct inode *inode, int type,
+                           struct posix_acl *acl);
+#endif /* CONFIG_NFS_V3_ACL */
+
+/*
  * linux/fs/mount_clnt.c
  * (Used only by nfsroot module)
  */
index fc51645..3d3a305 100644 (file)
@@ -10,6 +10,7 @@
 struct nfs_server {
        struct rpc_clnt *       client;         /* RPC client handle */
        struct rpc_clnt *       client_sys;     /* 2nd handle for FSINFO */
+       struct rpc_clnt *       client_acl;     /* ACL RPC client handle */
        struct nfs_rpc_ops *    rpc_ops;        /* NFS protocol vector */
        struct backing_dev_info backing_dev_info;
        int                     flags;          /* various flags */
index 0071428..659c754 100644 (file)
@@ -58,6 +58,7 @@ struct nfs_mount_data {
 #define NFS_MOUNT_KERBEROS     0x0100  /* 3 */
 #define NFS_MOUNT_NONLM                0x0200  /* 3 */
 #define NFS_MOUNT_BROKEN_SUID  0x0400  /* 4 */
+#define NFS_MOUNT_NOACL                0x0800  /* 4 */
 #define NFS_MOUNT_STRICTLOCK   0x1000  /* reserved for NFSv4 */
 #define NFS_MOUNT_SECFLAVOUR   0x2000  /* 5 */
 #define NFS_MOUNT_FLAGMASK     0xFFFF
index 46b206b..a2bf691 100644 (file)
@@ -2,6 +2,7 @@
 #define _LINUX_NFS_XDR_H
 
 #include <linux/sunrpc/xprt.h>
+#include <linux/nfsacl.h>
 
 struct nfs4_fsid {
        __u64 major;
@@ -368,6 +369,20 @@ struct nfs_readdirargs {
        struct page **          pages;
 };
 
+struct nfs3_getaclargs {
+       struct nfs_fh *         fh;
+       int                     mask;
+       struct page **          pages;
+};
+
+struct nfs3_setaclargs {
+       struct inode *          inode;
+       int                     mask;
+       struct posix_acl *      acl_access;
+       struct posix_acl *      acl_default;
+       struct page **          pages;
+};
+
 struct nfs_diropok {
        struct nfs_fh *         fh;
        struct nfs_fattr *      fattr;
@@ -491,6 +506,15 @@ struct nfs3_readdirres {
        int                     plus;
 };
 
+struct nfs3_getaclres {
+       struct nfs_fattr *      fattr;
+       int                     mask;
+       unsigned int            acl_access_count;
+       unsigned int            acl_default_count;
+       struct posix_acl *      acl_access;
+       struct posix_acl *      acl_default;
+};
+
 #ifdef CONFIG_NFS_V4
 
 typedef u64 clientid4;
@@ -748,4 +772,7 @@ extern struct rpc_version   nfs_version2;
 extern struct rpc_version      nfs_version3;
 extern struct rpc_version      nfs_version4;
 
+extern struct rpc_version      nfsacl_version3;
+extern struct rpc_program      nfsacl_program;
+
 #endif