diff options
author | Andreas Gruenbacher <agruen@suse.de> | 2005-06-22 13:16:27 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:24 -0400 |
commit | b7fa0554cf1ba6d6895cd0a5b02989a26e0bc704 (patch) | |
tree | 83eb405f3ff78c17695999df38c99484e3aee01f /fs | |
parent | a257cdd0e2179630d3201c32ba14d7fcb3c3a055 (diff) |
[PATCH] NFS: Add support for NFSv3 ACLs
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 11 | ||||
-rw-r--r-- | fs/nfs/Makefile | 1 | ||||
-rw-r--r-- | fs/nfs/dir.c | 21 | ||||
-rw-r--r-- | fs/nfs/file.c | 12 | ||||
-rw-r--r-- | fs/nfs/inode.c | 36 | ||||
-rw-r--r-- | fs/nfs/nfs3acl.c | 303 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 7 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 147 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 9 |
9 files changed, 541 insertions, 6 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index d44b04d9b0a9..a7c0cc3203cb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
@@ -1268,6 +1268,7 @@ config NFS_FS | |||
1268 | depends on INET | 1268 | depends on INET |
1269 | select LOCKD | 1269 | select LOCKD |
1270 | select SUNRPC | 1270 | select SUNRPC |
1271 | select NFS_ACL_SUPPORT if NFS_V3_ACL | ||
1271 | help | 1272 | help |
1272 | If you are connected to some other (usually local) Unix computer | 1273 | If you are connected to some other (usually local) Unix computer |
1273 | (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing | 1274 | (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing |
@@ -1310,6 +1311,16 @@ config NFS_V3 | |||
1310 | 1311 | ||
1311 | If unsure, say Y. | 1312 | If unsure, say Y. |
1312 | 1313 | ||
1314 | config NFS_V3_ACL | ||
1315 | bool "Provide client support for the NFSv3 ACL protocol extension" | ||
1316 | depends on NFS_V3 | ||
1317 | help | ||
1318 | Implement the NFSv3 ACL protocol extension for manipulating POSIX | ||
1319 | Access Control Lists. The server should also be compiled with | ||
1320 | the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. | ||
1321 | |||
1322 | If unsure, say N. | ||
1323 | |||
1313 | config NFS_V4 | 1324 | config NFS_V4 |
1314 | bool "Provide NFSv4 client support (EXPERIMENTAL)" | 1325 | bool "Provide NFSv4 client support (EXPERIMENTAL)" |
1315 | depends on NFS_FS && EXPERIMENTAL | 1326 | depends on NFS_FS && EXPERIMENTAL |
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index b4baa031edf4..8b3bb715d177 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile | |||
@@ -8,6 +8,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \ | |||
8 | proc.o read.o symlink.o unlink.o write.o | 8 | proc.o read.o symlink.o unlink.o write.o |
9 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o | 9 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o |
10 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o | 10 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o |
11 | nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o | ||
11 | nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ | 12 | nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ |
12 | delegation.o idmap.o \ | 13 | delegation.o idmap.o \ |
13 | callback.o callback_xdr.o callback_proc.o | 14 | callback.o callback_xdr.o callback_proc.o |
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5720537bffdd..2c6a95945684 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -75,6 +75,27 @@ struct inode_operations nfs_dir_inode_operations = { | |||
75 | .setattr = nfs_setattr, | 75 | .setattr = nfs_setattr, |
76 | }; | 76 | }; |
77 | 77 | ||
78 | #ifdef CONFIG_NFS_V3 | ||
79 | struct inode_operations nfs3_dir_inode_operations = { | ||
80 | .create = nfs_create, | ||
81 | .lookup = nfs_lookup, | ||
82 | .link = nfs_link, | ||
83 | .unlink = nfs_unlink, | ||
84 | .symlink = nfs_symlink, | ||
85 | .mkdir = nfs_mkdir, | ||
86 | .rmdir = nfs_rmdir, | ||
87 | .mknod = nfs_mknod, | ||
88 | .rename = nfs_rename, | ||
89 | .permission = nfs_permission, | ||
90 | .getattr = nfs_getattr, | ||
91 | .setattr = nfs_setattr, | ||
92 | .listxattr = nfs3_listxattr, | ||
93 | .getxattr = nfs3_getxattr, | ||
94 | .setxattr = nfs3_setxattr, | ||
95 | .removexattr = nfs3_removexattr, | ||
96 | }; | ||
97 | #endif /* CONFIG_NFS_V3 */ | ||
98 | |||
78 | #ifdef CONFIG_NFS_V4 | 99 | #ifdef CONFIG_NFS_V4 |
79 | 100 | ||
80 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); | 101 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); |
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 55c907592490..a606708264ed 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -71,6 +71,18 @@ struct inode_operations nfs_file_inode_operations = { | |||
71 | .setattr = nfs_setattr, | 71 | .setattr = nfs_setattr, |
72 | }; | 72 | }; |
73 | 73 | ||
74 | #ifdef CONFIG_NFS_V3 | ||
75 | struct inode_operations nfs3_file_inode_operations = { | ||
76 | .permission = nfs_permission, | ||
77 | .getattr = nfs_getattr, | ||
78 | .setattr = nfs_setattr, | ||
79 | .listxattr = nfs3_listxattr, | ||
80 | .getxattr = nfs3_getxattr, | ||
81 | .setxattr = nfs3_setxattr, | ||
82 | .removexattr = nfs3_removexattr, | ||
83 | }; | ||
84 | #endif /* CONFIG_NFS_v3 */ | ||
85 | |||
74 | /* Hack for future NFS swap support */ | 86 | /* Hack for future NFS swap support */ |
75 | #ifndef IS_SWAPFILE | 87 | #ifndef IS_SWAPFILE |
76 | # define IS_SWAPFILE(inode) (0) | 88 | # define IS_SWAPFILE(inode) (0) |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 97b3fe7ece63..440b9cbb6f81 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -108,6 +108,21 @@ static struct rpc_program nfs_program = { | |||
108 | .pipe_dir_name = "/nfs", | 108 | .pipe_dir_name = "/nfs", |
109 | }; | 109 | }; |
110 | 110 | ||
111 | #ifdef CONFIG_NFS_V3_ACL | ||
112 | static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; | ||
113 | static struct rpc_version * nfsacl_version[] = { | ||
114 | [3] = &nfsacl_version3, | ||
115 | }; | ||
116 | |||
117 | struct rpc_program nfsacl_program = { | ||
118 | .name = "nfsacl", | ||
119 | .number = NFS_ACL_PROGRAM, | ||
120 | .nrvers = sizeof(nfsacl_version) / sizeof(nfsacl_version[0]), | ||
121 | .version = nfsacl_version, | ||
122 | .stats = &nfsacl_rpcstat, | ||
123 | }; | ||
124 | #endif /* CONFIG_NFS_V3_ACL */ | ||
125 | |||
111 | static inline unsigned long | 126 | static inline unsigned long |
112 | nfs_fattr_to_ino_t(struct nfs_fattr *fattr) | 127 | nfs_fattr_to_ino_t(struct nfs_fattr *fattr) |
113 | { | 128 | { |
@@ -165,6 +180,9 @@ nfs_umount_begin(struct super_block *sb) | |||
165 | /* -EIO all pending I/O */ | 180 | /* -EIO all pending I/O */ |
166 | if (!IS_ERR(rpc)) | 181 | if (!IS_ERR(rpc)) |
167 | rpc_killall_tasks(rpc); | 182 | rpc_killall_tasks(rpc); |
183 | rpc = NFS_SB(sb)->client_acl; | ||
184 | if (!IS_ERR(rpc)) | ||
185 | rpc_killall_tasks(rpc); | ||
168 | } | 186 | } |
169 | 187 | ||
170 | 188 | ||
@@ -461,8 +479,17 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) | |||
461 | atomic_inc(&server->client->cl_count); | 479 | atomic_inc(&server->client->cl_count); |
462 | server->client_sys = server->client; | 480 | server->client_sys = server->client; |
463 | } | 481 | } |
464 | |||
465 | if (server->flags & NFS_MOUNT_VER3) { | 482 | if (server->flags & NFS_MOUNT_VER3) { |
483 | #ifdef CONFIG_NFS_V3_ACL | ||
484 | if (!(server->flags & NFS_MOUNT_NOACL)) { | ||
485 | server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); | ||
486 | /* No errors! Assume that Sun nfsacls are supported */ | ||
487 | if (!IS_ERR(server->client_acl)) | ||
488 | server->caps |= NFS_CAP_ACLS; | ||
489 | } | ||
490 | #else | ||
491 | server->flags &= ~NFS_MOUNT_NOACL; | ||
492 | #endif /* CONFIG_NFS_V3_ACL */ | ||
466 | if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) | 493 | if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) |
467 | server->namelen = NFS3_MAXNAMLEN; | 494 | server->namelen = NFS3_MAXNAMLEN; |
468 | sb->s_time_gran = 1; | 495 | sb->s_time_gran = 1; |
@@ -546,6 +573,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
546 | { NFS_MOUNT_NOCTO, ",nocto", "" }, | 573 | { NFS_MOUNT_NOCTO, ",nocto", "" }, |
547 | { NFS_MOUNT_NOAC, ",noac", "" }, | 574 | { NFS_MOUNT_NOAC, ",noac", "" }, |
548 | { NFS_MOUNT_NONLM, ",nolock", ",lock" }, | 575 | { NFS_MOUNT_NONLM, ",nolock", ",lock" }, |
576 | { NFS_MOUNT_NOACL, ",noacl", "" }, | ||
549 | { 0, NULL, NULL } | 577 | { 0, NULL, NULL } |
550 | }; | 578 | }; |
551 | struct proc_nfs_info *nfs_infop; | 579 | struct proc_nfs_info *nfs_infop; |
@@ -1452,7 +1480,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, | |||
1452 | memset(server, 0, sizeof(struct nfs_server)); | 1480 | memset(server, 0, sizeof(struct nfs_server)); |
1453 | /* Zero out the NFS state stuff */ | 1481 | /* Zero out the NFS state stuff */ |
1454 | init_nfsv4_state(server); | 1482 | init_nfsv4_state(server); |
1455 | server->client = server->client_sys = ERR_PTR(-EINVAL); | 1483 | server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); |
1456 | 1484 | ||
1457 | root = &server->fh; | 1485 | root = &server->fh; |
1458 | if (data->flags & NFS_MOUNT_VER3) | 1486 | if (data->flags & NFS_MOUNT_VER3) |
@@ -1513,6 +1541,8 @@ static void nfs_kill_super(struct super_block *s) | |||
1513 | rpc_shutdown_client(server->client); | 1541 | rpc_shutdown_client(server->client); |
1514 | if (!IS_ERR(server->client_sys)) | 1542 | if (!IS_ERR(server->client_sys)) |
1515 | rpc_shutdown_client(server->client_sys); | 1543 | rpc_shutdown_client(server->client_sys); |
1544 | if (!IS_ERR(server->client_acl)) | ||
1545 | rpc_shutdown_client(server->client_acl); | ||
1516 | 1546 | ||
1517 | if (!(server->flags & NFS_MOUNT_NONLM)) | 1547 | if (!(server->flags & NFS_MOUNT_NONLM)) |
1518 | lockd_down(); /* release rpc.lockd */ | 1548 | lockd_down(); /* release rpc.lockd */ |
@@ -1794,7 +1824,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, | |||
1794 | memset(server, 0, sizeof(struct nfs_server)); | 1824 | memset(server, 0, sizeof(struct nfs_server)); |
1795 | /* Zero out the NFS state stuff */ | 1825 | /* Zero out the NFS state stuff */ |
1796 | init_nfsv4_state(server); | 1826 | init_nfsv4_state(server); |
1797 | server->client = server->client_sys = ERR_PTR(-EINVAL); | 1827 | server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); |
1798 | 1828 | ||
1799 | p = nfs_copy_user_string(NULL, &data->hostname, 256); | 1829 | p = nfs_copy_user_string(NULL, &data->hostname, 256); |
1800 | if (IS_ERR(p)) | 1830 | if (IS_ERR(p)) |
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c new file mode 100644 index 000000000000..393ba79fc14f --- /dev/null +++ b/fs/nfs/nfs3acl.c | |||
@@ -0,0 +1,303 @@ | |||
1 | #include <linux/fs.h> | ||
2 | #include <linux/nfs.h> | ||
3 | #include <linux/nfs3.h> | ||
4 | #include <linux/nfs_fs.h> | ||
5 | #include <linux/xattr_acl.h> | ||
6 | #include <linux/nfsacl.h> | ||
7 | |||
8 | #define NFSDBG_FACILITY NFSDBG_PROC | ||
9 | |||
10 | ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size) | ||
11 | { | ||
12 | struct inode *inode = dentry->d_inode; | ||
13 | struct posix_acl *acl; | ||
14 | int pos=0, len=0; | ||
15 | |||
16 | # define output(s) do { \ | ||
17 | if (pos + sizeof(s) <= size) { \ | ||
18 | memcpy(buffer + pos, s, sizeof(s)); \ | ||
19 | pos += sizeof(s); \ | ||
20 | } \ | ||
21 | len += sizeof(s); \ | ||
22 | } while(0) | ||
23 | |||
24 | acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS); | ||
25 | if (IS_ERR(acl)) | ||
26 | return PTR_ERR(acl); | ||
27 | if (acl) { | ||
28 | output("system.posix_acl_access"); | ||
29 | posix_acl_release(acl); | ||
30 | } | ||
31 | |||
32 | if (S_ISDIR(inode->i_mode)) { | ||
33 | acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT); | ||
34 | if (IS_ERR(acl)) | ||
35 | return PTR_ERR(acl); | ||
36 | if (acl) { | ||
37 | output("system.posix_acl_default"); | ||
38 | posix_acl_release(acl); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | # undef output | ||
43 | |||
44 | if (!buffer || len <= size) | ||
45 | return len; | ||
46 | return -ERANGE; | ||
47 | } | ||
48 | |||
49 | ssize_t nfs3_getxattr(struct dentry *dentry, const char *name, | ||
50 | void *buffer, size_t size) | ||
51 | { | ||
52 | struct inode *inode = dentry->d_inode; | ||
53 | struct posix_acl *acl; | ||
54 | int type, error = 0; | ||
55 | |||
56 | if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) | ||
57 | type = ACL_TYPE_ACCESS; | ||
58 | else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) | ||
59 | type = ACL_TYPE_DEFAULT; | ||
60 | else | ||
61 | return -EOPNOTSUPP; | ||
62 | |||
63 | acl = nfs3_proc_getacl(inode, type); | ||
64 | if (IS_ERR(acl)) | ||
65 | return PTR_ERR(acl); | ||
66 | else if (acl) { | ||
67 | if (type == ACL_TYPE_ACCESS && acl->a_count == 0) | ||
68 | error = -ENODATA; | ||
69 | else | ||
70 | error = posix_acl_to_xattr(acl, buffer, size); | ||
71 | posix_acl_release(acl); | ||
72 | } else | ||
73 | error = -ENODATA; | ||
74 | |||
75 | return error; | ||
76 | } | ||
77 | |||
78 | int nfs3_setxattr(struct dentry *dentry, const char *name, | ||
79 | const void *value, size_t size, int flags) | ||
80 | { | ||
81 | struct inode *inode = dentry->d_inode; | ||
82 | struct posix_acl *acl; | ||
83 | int type, error; | ||
84 | |||
85 | if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) | ||
86 | type = ACL_TYPE_ACCESS; | ||
87 | else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) | ||
88 | type = ACL_TYPE_DEFAULT; | ||
89 | else | ||
90 | return -EOPNOTSUPP; | ||
91 | |||
92 | acl = posix_acl_from_xattr(value, size); | ||
93 | if (IS_ERR(acl)) | ||
94 | return PTR_ERR(acl); | ||
95 | error = nfs3_proc_setacl(inode, type, acl); | ||
96 | posix_acl_release(acl); | ||
97 | |||
98 | return error; | ||
99 | } | ||
100 | |||
101 | int nfs3_removexattr(struct dentry *dentry, const char *name) | ||
102 | { | ||
103 | struct inode *inode = dentry->d_inode; | ||
104 | int type; | ||
105 | |||
106 | if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) | ||
107 | type = ACL_TYPE_ACCESS; | ||
108 | else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) | ||
109 | type = ACL_TYPE_DEFAULT; | ||
110 | else | ||
111 | return -EOPNOTSUPP; | ||
112 | |||
113 | return nfs3_proc_setacl(inode, type, NULL); | ||
114 | } | ||
115 | |||
116 | struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) | ||
117 | { | ||
118 | struct nfs_server *server = NFS_SERVER(inode); | ||
119 | struct nfs_fattr fattr; | ||
120 | struct page *pages[NFSACL_MAXPAGES] = { }; | ||
121 | struct nfs3_getaclargs args = { | ||
122 | .fh = NFS_FH(inode), | ||
123 | /* The xdr layer may allocate pages here. */ | ||
124 | .pages = pages, | ||
125 | }; | ||
126 | struct nfs3_getaclres res = { | ||
127 | .fattr = &fattr, | ||
128 | }; | ||
129 | struct posix_acl *acl = NULL; | ||
130 | int status, count; | ||
131 | |||
132 | if (!nfs_server_capable(inode, NFS_CAP_ACLS)) | ||
133 | return ERR_PTR(-EOPNOTSUPP); | ||
134 | |||
135 | switch (type) { | ||
136 | case ACL_TYPE_ACCESS: | ||
137 | args.mask = NFS_ACLCNT|NFS_ACL; | ||
138 | break; | ||
139 | |||
140 | case ACL_TYPE_DEFAULT: | ||
141 | if (!S_ISDIR(inode->i_mode)) | ||
142 | return NULL; | ||
143 | args.mask = NFS_DFACLCNT|NFS_DFACL; | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | return ERR_PTR(-EINVAL); | ||
148 | } | ||
149 | |||
150 | dprintk("NFS call getacl\n"); | ||
151 | status = rpc_call(server->client_acl, ACLPROC3_GETACL, | ||
152 | &args, &res, 0); | ||
153 | dprintk("NFS reply getacl: %d\n", status); | ||
154 | |||
155 | /* pages may have been allocated at the xdr layer. */ | ||
156 | for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) | ||
157 | __free_page(args.pages[count]); | ||
158 | |||
159 | switch (status) { | ||
160 | case 0: | ||
161 | status = nfs_refresh_inode(inode, &fattr); | ||
162 | break; | ||
163 | case -EPFNOSUPPORT: | ||
164 | case -EPROTONOSUPPORT: | ||
165 | dprintk("NFS_V3_ACL extension not supported; disabling\n"); | ||
166 | server->caps &= ~NFS_CAP_ACLS; | ||
167 | case -ENOTSUPP: | ||
168 | status = -EOPNOTSUPP; | ||
169 | default: | ||
170 | goto getout; | ||
171 | } | ||
172 | if ((args.mask & res.mask) != args.mask) { | ||
173 | status = -EIO; | ||
174 | goto getout; | ||
175 | } | ||
176 | |||
177 | if (res.acl_access != NULL) { | ||
178 | if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) { | ||
179 | posix_acl_release(res.acl_access); | ||
180 | res.acl_access = NULL; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | switch(type) { | ||
185 | case ACL_TYPE_ACCESS: | ||
186 | acl = res.acl_access; | ||
187 | res.acl_access = NULL; | ||
188 | break; | ||
189 | |||
190 | case ACL_TYPE_DEFAULT: | ||
191 | acl = res.acl_default; | ||
192 | res.acl_default = NULL; | ||
193 | } | ||
194 | |||
195 | getout: | ||
196 | posix_acl_release(res.acl_access); | ||
197 | posix_acl_release(res.acl_default); | ||
198 | |||
199 | if (status != 0) { | ||
200 | posix_acl_release(acl); | ||
201 | acl = ERR_PTR(status); | ||
202 | } | ||
203 | return acl; | ||
204 | } | ||
205 | |||
206 | static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, | ||
207 | struct posix_acl *dfacl) | ||
208 | { | ||
209 | struct nfs_server *server = NFS_SERVER(inode); | ||
210 | struct nfs_fattr fattr; | ||
211 | struct page *pages[NFSACL_MAXPAGES] = { }; | ||
212 | struct nfs3_setaclargs args = { | ||
213 | .inode = inode, | ||
214 | .mask = NFS_ACL, | ||
215 | .acl_access = acl, | ||
216 | .pages = pages, | ||
217 | }; | ||
218 | int status, count; | ||
219 | |||
220 | status = -EOPNOTSUPP; | ||
221 | if (!nfs_server_capable(inode, NFS_CAP_ACLS)) | ||
222 | goto out; | ||
223 | |||
224 | /* We are doing this here, because XDR marshalling can only | ||
225 | return -ENOMEM. */ | ||
226 | status = -ENOSPC; | ||
227 | if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES) | ||
228 | goto out; | ||
229 | if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES) | ||
230 | goto out; | ||
231 | if (S_ISDIR(inode->i_mode)) { | ||
232 | args.mask |= NFS_DFACL; | ||
233 | args.acl_default = dfacl; | ||
234 | } | ||
235 | |||
236 | dprintk("NFS call setacl\n"); | ||
237 | nfs_begin_data_update(inode); | ||
238 | status = rpc_call(server->client_acl, ACLPROC3_SETACL, | ||
239 | &args, &fattr, 0); | ||
240 | NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS; | ||
241 | nfs_end_data_update(inode); | ||
242 | dprintk("NFS reply setacl: %d\n", status); | ||
243 | |||
244 | /* pages may have been allocated at the xdr layer. */ | ||
245 | for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) | ||
246 | __free_page(args.pages[count]); | ||
247 | |||
248 | switch (status) { | ||
249 | case 0: | ||
250 | status = nfs_refresh_inode(inode, &fattr); | ||
251 | break; | ||
252 | case -EPFNOSUPPORT: | ||
253 | case -EPROTONOSUPPORT: | ||
254 | dprintk("NFS_V3_ACL SETACL RPC not supported" | ||
255 | "(will not retry)\n"); | ||
256 | server->caps &= ~NFS_CAP_ACLS; | ||
257 | case -ENOTSUPP: | ||
258 | status = -EOPNOTSUPP; | ||
259 | } | ||
260 | out: | ||
261 | return status; | ||
262 | } | ||
263 | |||
264 | int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl) | ||
265 | { | ||
266 | struct posix_acl *alloc = NULL, *dfacl = NULL; | ||
267 | int status; | ||
268 | |||
269 | if (S_ISDIR(inode->i_mode)) { | ||
270 | switch(type) { | ||
271 | case ACL_TYPE_ACCESS: | ||
272 | alloc = dfacl = nfs3_proc_getacl(inode, | ||
273 | ACL_TYPE_DEFAULT); | ||
274 | if (IS_ERR(alloc)) | ||
275 | goto fail; | ||
276 | break; | ||
277 | |||
278 | case ACL_TYPE_DEFAULT: | ||
279 | dfacl = acl; | ||
280 | alloc = acl = nfs3_proc_getacl(inode, | ||
281 | ACL_TYPE_ACCESS); | ||
282 | if (IS_ERR(alloc)) | ||
283 | goto fail; | ||
284 | break; | ||
285 | |||
286 | default: | ||
287 | return -EINVAL; | ||
288 | } | ||
289 | } else if (type != ACL_TYPE_ACCESS) | ||
290 | return -EINVAL; | ||
291 | |||
292 | if (acl == NULL) { | ||
293 | alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); | ||
294 | if (IS_ERR(alloc)) | ||
295 | goto fail; | ||
296 | } | ||
297 | status = nfs3_proc_setacls(inode, acl, dfacl); | ||
298 | posix_acl_release(alloc); | ||
299 | return status; | ||
300 | |||
301 | fail: | ||
302 | return PTR_ERR(alloc); | ||
303 | } | ||
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 53953a775714..d03bac0cc42f 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/nfs_page.h> | 17 | #include <linux/nfs_page.h> |
18 | #include <linux/lockd/bind.h> | 18 | #include <linux/lockd/bind.h> |
19 | #include <linux/smp_lock.h> | 19 | #include <linux/smp_lock.h> |
20 | #include <linux/nfs_mount.h> | ||
20 | 21 | ||
21 | #define NFSDBG_FACILITY NFSDBG_PROC | 22 | #define NFSDBG_FACILITY NFSDBG_PROC |
22 | 23 | ||
@@ -45,7 +46,7 @@ static inline int | |||
45 | nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) | 46 | nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) |
46 | { | 47 | { |
47 | struct rpc_message msg = { | 48 | struct rpc_message msg = { |
48 | .rpc_proc = &nfs3_procedures[proc], | 49 | .rpc_proc = &clnt->cl_procinfo[proc], |
49 | .rpc_argp = argp, | 50 | .rpc_argp = argp, |
50 | .rpc_resp = resp, | 51 | .rpc_resp = resp, |
51 | }; | 52 | }; |
@@ -825,8 +826,8 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) | |||
825 | struct nfs_rpc_ops nfs_v3_clientops = { | 826 | struct nfs_rpc_ops nfs_v3_clientops = { |
826 | .version = 3, /* protocol version */ | 827 | .version = 3, /* protocol version */ |
827 | .dentry_ops = &nfs_dentry_operations, | 828 | .dentry_ops = &nfs_dentry_operations, |
828 | .dir_inode_ops = &nfs_dir_inode_operations, | 829 | .dir_inode_ops = &nfs3_dir_inode_operations, |
829 | .file_inode_ops = &nfs_file_inode_operations, | 830 | .file_inode_ops = &nfs3_file_inode_operations, |
830 | .getroot = nfs3_proc_get_root, | 831 | .getroot = nfs3_proc_get_root, |
831 | .getattr = nfs3_proc_getattr, | 832 | .getattr = nfs3_proc_getattr, |
832 | .setattr = nfs3_proc_setattr, | 833 | .setattr = nfs3_proc_setattr, |
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index a3593d47e5ab..a4437fb177f0 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/nfs.h> | 21 | #include <linux/nfs.h> |
22 | #include <linux/nfs3.h> | 22 | #include <linux/nfs3.h> |
23 | #include <linux/nfs_fs.h> | 23 | #include <linux/nfs_fs.h> |
24 | #include <linux/nfsacl.h> | ||
24 | 25 | ||
25 | #define NFSDBG_FACILITY NFSDBG_XDR | 26 | #define NFSDBG_FACILITY NFSDBG_XDR |
26 | 27 | ||
@@ -79,6 +80,11 @@ extern int nfs_stat_to_errno(int); | |||
79 | #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) | 80 | #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) |
80 | #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) | 81 | #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) |
81 | 82 | ||
83 | #define ACL3_getaclargs_sz (NFS3_fh_sz+1) | ||
84 | #define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3)) | ||
85 | #define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3)) | ||
86 | #define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz) | ||
87 | |||
82 | /* | 88 | /* |
83 | * Map file type to S_IFMT bits | 89 | * Map file type to S_IFMT bits |
84 | */ | 90 | */ |
@@ -627,6 +633,74 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | |||
627 | return 0; | 633 | return 0; |
628 | } | 634 | } |
629 | 635 | ||
636 | #ifdef CONFIG_NFS_V3_ACL | ||
637 | /* | ||
638 | * Encode GETACL arguments | ||
639 | */ | ||
640 | static int | ||
641 | nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p, | ||
642 | struct nfs3_getaclargs *args) | ||
643 | { | ||
644 | struct rpc_auth *auth = req->rq_task->tk_auth; | ||
645 | unsigned int replen; | ||
646 | |||
647 | p = xdr_encode_fhandle(p, args->fh); | ||
648 | *p++ = htonl(args->mask); | ||
649 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
650 | |||
651 | if (args->mask & (NFS_ACL | NFS_DFACL)) { | ||
652 | /* Inline the page array */ | ||
653 | replen = (RPC_REPHDRSIZE + auth->au_rslack + | ||
654 | ACL3_getaclres_sz) << 2; | ||
655 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, | ||
656 | NFSACL_MAXPAGES << PAGE_SHIFT); | ||
657 | } | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * Encode SETACL arguments | ||
663 | */ | ||
664 | static int | ||
665 | nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p, | ||
666 | struct nfs3_setaclargs *args) | ||
667 | { | ||
668 | struct xdr_buf *buf = &req->rq_snd_buf; | ||
669 | unsigned int base, len_in_head, len = nfsacl_size( | ||
670 | (args->mask & NFS_ACL) ? args->acl_access : NULL, | ||
671 | (args->mask & NFS_DFACL) ? args->acl_default : NULL); | ||
672 | int count, err; | ||
673 | |||
674 | p = xdr_encode_fhandle(p, NFS_FH(args->inode)); | ||
675 | *p++ = htonl(args->mask); | ||
676 | base = (char *)p - (char *)buf->head->iov_base; | ||
677 | /* put as much of the acls into head as possible. */ | ||
678 | len_in_head = min_t(unsigned int, buf->head->iov_len - base, len); | ||
679 | len -= len_in_head; | ||
680 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + len_in_head); | ||
681 | |||
682 | for (count = 0; (count << PAGE_SHIFT) < len; count++) { | ||
683 | args->pages[count] = alloc_page(GFP_KERNEL); | ||
684 | if (!args->pages[count]) { | ||
685 | while (count) | ||
686 | __free_page(args->pages[--count]); | ||
687 | return -ENOMEM; | ||
688 | } | ||
689 | } | ||
690 | xdr_encode_pages(buf, args->pages, 0, len); | ||
691 | |||
692 | err = nfsacl_encode(buf, base, args->inode, | ||
693 | (args->mask & NFS_ACL) ? | ||
694 | args->acl_access : NULL, 1, 0); | ||
695 | if (err > 0) | ||
696 | err = nfsacl_encode(buf, base + err, args->inode, | ||
697 | (args->mask & NFS_DFACL) ? | ||
698 | args->acl_default : NULL, 1, | ||
699 | NFS_ACL_DEFAULT); | ||
700 | return (err > 0) ? 0 : err; | ||
701 | } | ||
702 | #endif /* CONFIG_NFS_V3_ACL */ | ||
703 | |||
630 | /* | 704 | /* |
631 | * NFS XDR decode functions | 705 | * NFS XDR decode functions |
632 | */ | 706 | */ |
@@ -978,6 +1052,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | |||
978 | return 0; | 1052 | return 0; |
979 | } | 1053 | } |
980 | 1054 | ||
1055 | #ifdef CONFIG_NFS_V3_ACL | ||
1056 | /* | ||
1057 | * Decode GETACL reply | ||
1058 | */ | ||
1059 | static int | ||
1060 | nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p, | ||
1061 | struct nfs3_getaclres *res) | ||
1062 | { | ||
1063 | struct xdr_buf *buf = &req->rq_rcv_buf; | ||
1064 | int status = ntohl(*p++); | ||
1065 | struct posix_acl **acl; | ||
1066 | unsigned int *aclcnt; | ||
1067 | int err, base; | ||
1068 | |||
1069 | if (status != 0) | ||
1070 | return -nfs_stat_to_errno(status); | ||
1071 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
1072 | res->mask = ntohl(*p++); | ||
1073 | if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) | ||
1074 | return -EINVAL; | ||
1075 | base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base; | ||
1076 | |||
1077 | acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL; | ||
1078 | aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL; | ||
1079 | err = nfsacl_decode(buf, base, aclcnt, acl); | ||
1080 | |||
1081 | acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL; | ||
1082 | aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL; | ||
1083 | if (err > 0) | ||
1084 | err = nfsacl_decode(buf, base + err, aclcnt, acl); | ||
1085 | return (err > 0) ? 0 : err; | ||
1086 | } | ||
1087 | |||
1088 | /* | ||
1089 | * Decode setacl reply. | ||
1090 | */ | ||
1091 | static int | ||
1092 | nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | ||
1093 | { | ||
1094 | int status = ntohl(*p++); | ||
1095 | |||
1096 | if (status) | ||
1097 | return -nfs_stat_to_errno(status); | ||
1098 | xdr_decode_post_op_attr(p, fattr); | ||
1099 | return 0; | ||
1100 | } | ||
1101 | #endif /* CONFIG_NFS_V3_ACL */ | ||
1102 | |||
981 | #ifndef MAX | 1103 | #ifndef MAX |
982 | # define MAX(a, b) (((a) > (b))? (a) : (b)) | 1104 | # define MAX(a, b) (((a) > (b))? (a) : (b)) |
983 | #endif | 1105 | #endif |
@@ -1021,3 +1143,28 @@ struct rpc_version nfs_version3 = { | |||
1021 | .procs = nfs3_procedures | 1143 | .procs = nfs3_procedures |
1022 | }; | 1144 | }; |
1023 | 1145 | ||
1146 | #ifdef CONFIG_NFS_V3_ACL | ||
1147 | static struct rpc_procinfo nfs3_acl_procedures[] = { | ||
1148 | [ACLPROC3_GETACL] = { | ||
1149 | .p_proc = ACLPROC3_GETACL, | ||
1150 | .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs, | ||
1151 | .p_decode = (kxdrproc_t) nfs3_xdr_getaclres, | ||
1152 | .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2, | ||
1153 | .p_timer = 1, | ||
1154 | }, | ||
1155 | [ACLPROC3_SETACL] = { | ||
1156 | .p_proc = ACLPROC3_SETACL, | ||
1157 | .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs, | ||
1158 | .p_decode = (kxdrproc_t) nfs3_xdr_setaclres, | ||
1159 | .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2, | ||
1160 | .p_timer = 0, | ||
1161 | }, | ||
1162 | }; | ||
1163 | |||
1164 | struct rpc_version nfsacl_version3 = { | ||
1165 | .number = 3, | ||
1166 | .nrprocs = sizeof(nfs3_acl_procedures)/ | ||
1167 | sizeof(nfs3_acl_procedures[0]), | ||
1168 | .procs = nfs3_acl_procedures, | ||
1169 | }; | ||
1170 | #endif /* CONFIG_NFS_V3_ACL */ | ||
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index fd5bc596fe8a..1b272a135a31 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c | |||
@@ -124,6 +124,7 @@ enum { | |||
124 | Opt_soft, Opt_hard, Opt_intr, | 124 | Opt_soft, Opt_hard, Opt_intr, |
125 | Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, | 125 | Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, |
126 | Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, | 126 | Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, |
127 | Opt_acl, Opt_noacl, | ||
127 | /* Error token */ | 128 | /* Error token */ |
128 | Opt_err | 129 | Opt_err |
129 | }; | 130 | }; |
@@ -158,6 +159,8 @@ static match_table_t __initdata tokens = { | |||
158 | {Opt_udp, "udp"}, | 159 | {Opt_udp, "udp"}, |
159 | {Opt_tcp, "proto=tcp"}, | 160 | {Opt_tcp, "proto=tcp"}, |
160 | {Opt_tcp, "tcp"}, | 161 | {Opt_tcp, "tcp"}, |
162 | {Opt_acl, "acl"}, | ||
163 | {Opt_noacl, "noacl"}, | ||
161 | {Opt_err, NULL} | 164 | {Opt_err, NULL} |
162 | 165 | ||
163 | }; | 166 | }; |
@@ -266,6 +269,12 @@ static int __init root_nfs_parse(char *name, char *buf) | |||
266 | case Opt_tcp: | 269 | case Opt_tcp: |
267 | nfs_data.flags |= NFS_MOUNT_TCP; | 270 | nfs_data.flags |= NFS_MOUNT_TCP; |
268 | break; | 271 | break; |
272 | case Opt_acl: | ||
273 | nfs_data.flags &= ~NFS_MOUNT_NOACL; | ||
274 | break; | ||
275 | case Opt_noacl: | ||
276 | nfs_data.flags |= NFS_MOUNT_NOACL; | ||
277 | break; | ||
269 | default : | 278 | default : |
270 | return 0; | 279 | return 0; |
271 | } | 280 | } |