]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
[PATCH] NFS: Add support for NFSv3 ACLs
authorAndreas Gruenbacher <agruen@suse.de>
Wed, 22 Jun 2005 17:16:27 +0000 (17:16 +0000)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 22 Jun 2005 20:07:24 +0000 (16:07 -0400)
 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 d44b04d9b0a9415d53b92aeb712e669af8b697ab..a7c0cc3203cba461c6a243e11af710d9fbcf7354 100644 (file)
@@ -1268,6 +1268,7 @@ config NFS_FS
        depends on INET
        select LOCKD
        select SUNRPC
        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
        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.
 
 
          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
 config NFS_V4
        bool "Provide NFSv4 client support (EXPERIMENTAL)"
        depends on NFS_FS && EXPERIMENTAL
index b4baa031edf4ddcbf1dbfad15bd3b24b82cbfd58..8b3bb715d177397d1f9df4e6f308c1108b911231 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
                           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
 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 5720537bffdd8012627cddc703b51a4a37b8faaf..2c6a959456849f16141928f44fdb728893d9d60a 100644 (file)
@@ -75,6 +75,27 @@ struct inode_operations nfs_dir_inode_operations = {
        .setattr        = nfs_setattr,
 };
 
        .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 *);
 #ifdef CONFIG_NFS_V4
 
 static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);
index 55c907592490a7fa6357c2bc17018b378c25433f..a606708264edb9802bef65b19240b1d049cbc643 100644 (file)
@@ -71,6 +71,18 @@ struct inode_operations nfs_file_inode_operations = {
        .setattr        = nfs_setattr,
 };
 
        .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)
 /* Hack for future NFS swap support */
 #ifndef IS_SWAPFILE
 # define IS_SWAPFILE(inode)    (0)
index 97b3fe7ece630509191409a8823e4ca717c51a4b..440b9cbb6f8116e006b0657b1791e9765fd5f5fc 100644 (file)
@@ -108,6 +108,21 @@ static struct rpc_program  nfs_program = {
        .pipe_dir_name          = "/nfs",
 };
 
        .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)
 {
 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);
        /* -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;
        }
                atomic_inc(&server->client->cl_count);
                server->client_sys = server->client;
        }
-
        if (server->flags & NFS_MOUNT_VER3) {
        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;
                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_NOCTO, ",nocto", "" },
                { NFS_MOUNT_NOAC, ",noac", "" },
                { NFS_MOUNT_NONLM, ",nolock", ",lock" },
+               { NFS_MOUNT_NOACL, ",noacl", "" },
                { 0, NULL, NULL }
        };
        struct proc_nfs_info *nfs_infop;
                { 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);
        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)
 
        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);
                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 */
 
        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);
        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))
 
        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 53953a775714b6e30f6d913ee654653b2bb01a0e..d03bac0cc42f23b92f6465776e9a3438074bf725 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/nfs_page.h>
 #include <linux/lockd/bind.h>
 #include <linux/smp_lock.h>
 #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
 
 
 #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 = {
 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,
        };
                .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,
 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,
        .getroot        = nfs3_proc_get_root,
        .getattr        = nfs3_proc_getattr,
        .setattr        = nfs3_proc_setattr,
index a3593d47e5ab7ec3b70d9eeeaf139c91d433564c..a4437fb177f08b4b5b6c20b0f7bf8868b9a94bf6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs.h>
 #include <linux/nfs3.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfsacl.h>
 
 #define NFSDBG_FACILITY                NFSDBG_XDR
 
 
 #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 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
  */
 /*
  * 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;
 }
 
        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
  */
 /*
  * NFS XDR decode functions
  */
@@ -978,6 +1052,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
        return 0;
 }
 
        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
 #ifndef MAX
 # define MAX(a, b)     (((a) > (b))? (a) : (b))
 #endif
@@ -1021,3 +1143,28 @@ struct rpc_version               nfs_version3 = {
        .procs                  = nfs3_procedures
 };
 
        .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 fd5bc596fe8a79b34e72ffd262a7681e973e7e03..1b272a135a31e793ff4d852ba46f222a9b7701aa 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_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
 };
        /* 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_udp, "udp"},
        {Opt_tcp, "proto=tcp"},
        {Opt_tcp, "tcp"},
+       {Opt_acl, "acl"},
+       {Opt_noacl, "noacl"},
        {Opt_err, NULL}
        
 };
        {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_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;
                }
                        default : 
                                return 0;
                }
index d2b5d7e0e85a99477e2807b6cd5fc8a3a2465194..3a5e442ac7762ccb9ff7936413c607466ddc75ae 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;
  * 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;
 
 extern struct file_operations nfs_file_operations;
 extern struct address_space_operations nfs_file_aops;
 
@@ -315,6 +318,22 @@ static inline struct rpc_cred *nfs_file_cred(struct file *file)
        return NULL;
 }
 
        return NULL;
 }
 
+/*
+ * 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
  */
 /*
  * linux/fs/nfs/direct.c
  */
@@ -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;
  * 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;
 
 extern struct file_operations nfs_dir_operations;
 extern struct dentry_operations nfs_dentry_operations;
 
@@ -449,6 +471,15 @@ static inline void nfs_readdata_free(struct nfs_read_data *p)
 
 extern void  nfs_readdata_release(struct rpc_task *task);
 
 
 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)
 /*
  * linux/fs/mount_clnt.c
  * (Used only by nfsroot module)
index fc51645d61eec32e11937926ac4478888bedd27c..3d3a305488cfa91375e280f18d62c5f60fde2a56 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 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 */
        struct nfs_rpc_ops *    rpc_ops;        /* NFS protocol vector */
        struct backing_dev_info backing_dev_info;
        int                     flags;          /* various flags */
index 0071428231f98f99ab1ae85069e36b9d178cbdef..659c7543845482f236b63386b1212caeff147870 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_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
 #define NFS_MOUNT_STRICTLOCK   0x1000  /* reserved for NFSv4 */
 #define NFS_MOUNT_SECFLAVOUR   0x2000  /* 5 */
 #define NFS_MOUNT_FLAGMASK     0xFFFF
index 46b206b460c02dc1b1756210ef1c268a5ca6865a..a2bf6914ff1b3aa1c444ef1478ac078a44070db6 100644 (file)
@@ -2,6 +2,7 @@
 #define _LINUX_NFS_XDR_H
 
 #include <linux/sunrpc/xprt.h>
 #define _LINUX_NFS_XDR_H
 
 #include <linux/sunrpc/xprt.h>
+#include <linux/nfsacl.h>
 
 struct nfs4_fsid {
        __u64 major;
 
 struct nfs4_fsid {
        __u64 major;
@@ -368,6 +369,20 @@ struct nfs_readdirargs {
        struct page **          pages;
 };
 
        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;
 struct nfs_diropok {
        struct nfs_fh *         fh;
        struct nfs_fattr *      fattr;
@@ -491,6 +506,15 @@ struct nfs3_readdirres {
        int                     plus;
 };
 
        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;
 #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      nfs_version3;
 extern struct rpc_version      nfs_version4;
 
+extern struct rpc_version      nfsacl_version3;
+extern struct rpc_program      nfsacl_program;
+
 #endif
 #endif