Blame view

fs/nfs/nfs3acl.c 7.95 KB
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
1
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
2
  #include <linux/gfp.h>
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
3
4
5
  #include <linux/nfs.h>
  #include <linux/nfs3.h>
  #include <linux/nfs_fs.h>
334a13ec3   Christoph Hellwig   [PATCH] really re...
6
  #include <linux/posix_acl_xattr.h>
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
7
  #include <linux/nfsacl.h>
f41f74183   Trond Myklebust   NFS: Ensure we za...
8
  #include "internal.h"
3fc3edf14   Trond Myklebust   NFSv3: Fix missin...
9
  #include "nfs3_fs.h"
f41f74183   Trond Myklebust   NFS: Ensure we za...
10

b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
11
  #define NFSDBG_FACILITY	NFSDBG_PROC
b8a7a3a66   Andreas Gruenbacher   posix_acl: Inode ...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  /*
   * nfs3_prepare_get_acl, nfs3_complete_get_acl, nfs3_abort_get_acl: Helpers for
   * caching get_acl results in a race-free way.  See fs/posix_acl.c:get_acl()
   * for explanations.
   */
  static void nfs3_prepare_get_acl(struct posix_acl **p)
  {
  	struct posix_acl *sentinel = uncached_acl_sentinel(current);
  
  	if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED) {
  		/* Not the first reader or sentinel already in place. */
  	}
  }
  
  static void nfs3_complete_get_acl(struct posix_acl **p, struct posix_acl *acl)
  {
  	struct posix_acl *sentinel = uncached_acl_sentinel(current);
  
  	/* Only cache the ACL if our sentinel is still in place. */
  	posix_acl_dup(acl);
  	if (cmpxchg(p, sentinel, acl) != sentinel)
  		posix_acl_release(acl);
  }
  
  static void nfs3_abort_get_acl(struct posix_acl **p)
  {
  	struct posix_acl *sentinel = uncached_acl_sentinel(current);
  
  	/* Remove our sentinel upon failure. */
  	cmpxchg(p, sentinel, ACL_NOT_CACHED);
  }
013cdf108   Christoph Hellwig   nfs: use generic ...
43
  struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
44
45
  {
  	struct nfs_server *server = NFS_SERVER(inode);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
46
47
48
49
50
51
52
  	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 = {
17280175c   Trond Myklebust   NFS: Fix a number...
53
  		NULL,
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
54
  	};
dead28da8   Chuck Lever   SUNRPC: eliminate...
55
56
57
58
  	struct rpc_message msg = {
  		.rpc_argp	= &args,
  		.rpc_resp	= &res,
  	};
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
59
60
61
62
  	int status, count;
  
  	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
  		return ERR_PTR(-EOPNOTSUPP);
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
63
64
65
  	status = nfs_revalidate_inode(server, inode);
  	if (status < 0)
  		return ERR_PTR(status);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
66

5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
67
68
69
70
71
72
73
74
75
76
77
78
  	/*
  	 * Only get the access acl when explicitly requested: We don't
  	 * need it for access decisions, and only some applications use
  	 * it. Applications which request the access acl first are not
  	 * penalized from this optimization.
  	 */
  	if (type == ACL_TYPE_ACCESS)
  		args.mask |= NFS_ACLCNT|NFS_ACL;
  	if (S_ISDIR(inode->i_mode))
  		args.mask |= NFS_DFACLCNT|NFS_DFACL;
  	if (args.mask == 0)
  		return NULL;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
79
80
81
  
  	dprintk("NFS call getacl
  ");
dead28da8   Chuck Lever   SUNRPC: eliminate...
82
  	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL];
6e94d6299   Trond Myklebust   NFS: Reduce stack...
83
84
85
  	res.fattr = nfs_alloc_fattr();
  	if (res.fattr == NULL)
  		return ERR_PTR(-ENOMEM);
b8a7a3a66   Andreas Gruenbacher   posix_acl: Inode ...
86
87
88
89
  	if (args.mask & NFS_ACL)
  		nfs3_prepare_get_acl(&inode->i_acl);
  	if (args.mask & NFS_DFACL)
  		nfs3_prepare_get_acl(&inode->i_default_acl);
dead28da8   Chuck Lever   SUNRPC: eliminate...
90
  	status = rpc_call_sync(server->client_acl, &msg, 0);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
91
92
93
94
95
96
97
98
99
  	dprintk("NFS reply getacl: %d
  ", 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:
6e94d6299   Trond Myklebust   NFS: Reduce stack...
100
  			status = nfs_refresh_inode(inode, res.fattr);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  			break;
  		case -EPFNOSUPPORT:
  		case -EPROTONOSUPPORT:
  			dprintk("NFS_V3_ACL extension not supported; disabling
  ");
  			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) {
718360c59   Noah Massey   nfs: fix setting ...
118
  		if ((posix_acl_equiv_mode(res.acl_access, NULL) == 0) ||
013cdf108   Christoph Hellwig   nfs: use generic ...
119
  		    res.acl_access->a_count == 0) {
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
120
121
122
123
  			posix_acl_release(res.acl_access);
  			res.acl_access = NULL;
  		}
  	}
013cdf108   Christoph Hellwig   nfs: use generic ...
124
  	if (res.mask & NFS_ACL)
b8a7a3a66   Andreas Gruenbacher   posix_acl: Inode ...
125
  		nfs3_complete_get_acl(&inode->i_acl, res.acl_access);
013cdf108   Christoph Hellwig   nfs: use generic ...
126
127
  	else
  		forget_cached_acl(inode, ACL_TYPE_ACCESS);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
128

013cdf108   Christoph Hellwig   nfs: use generic ...
129
  	if (res.mask & NFS_DFACL)
b8a7a3a66   Andreas Gruenbacher   posix_acl: Inode ...
130
  		nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default);
013cdf108   Christoph Hellwig   nfs: use generic ...
131
132
133
134
135
136
137
138
139
140
  	else
  		forget_cached_acl(inode, ACL_TYPE_DEFAULT);
  
  	nfs_free_fattr(res.fattr);
  	if (type == ACL_TYPE_ACCESS) {
  		posix_acl_release(res.acl_default);
  		return res.acl_access;
  	} else {
  		posix_acl_release(res.acl_access);
  		return res.acl_default;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
141
142
143
  	}
  
  getout:
b8a7a3a66   Andreas Gruenbacher   posix_acl: Inode ...
144
145
  	nfs3_abort_get_acl(&inode->i_acl);
  	nfs3_abort_get_acl(&inode->i_default_acl);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
146
147
  	posix_acl_release(res.acl_access);
  	posix_acl_release(res.acl_default);
6e94d6299   Trond Myklebust   NFS: Reduce stack...
148
  	nfs_free_fattr(res.fattr);
013cdf108   Christoph Hellwig   nfs: use generic ...
149
  	return ERR_PTR(status);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
150
  }
8f493b9cf   Trond Myklebust   NFSv3: Fix return...
151
  static int __nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
013cdf108   Christoph Hellwig   nfs: use generic ...
152
  		struct posix_acl *dfacl)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
153
154
  {
  	struct nfs_server *server = NFS_SERVER(inode);
6e94d6299   Trond Myklebust   NFS: Reduce stack...
155
  	struct nfs_fattr *fattr;
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
156
  	struct page *pages[NFSACL_MAXPAGES];
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
157
158
159
160
161
162
  	struct nfs3_setaclargs args = {
  		.inode = inode,
  		.mask = NFS_ACL,
  		.acl_access = acl,
  		.pages = pages,
  	};
dead28da8   Chuck Lever   SUNRPC: eliminate...
163
164
165
166
  	struct rpc_message msg = {
  		.rpc_argp	= &args,
  		.rpc_resp	= &fattr,
  	};
f87d928f6   Trond Myklebust   NFSv3: Fix anothe...
167
168
169
170
  	int status = 0;
  
  	if (acl == NULL && (!S_ISDIR(inode->i_mode) || dfacl == NULL))
  		goto out;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
171
172
173
174
  
  	status = -EOPNOTSUPP;
  	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
  		goto out;
f61f6da0d   Chuck Lever   NFS: Prevent memo...
175
176
  	/* We are doing this here because XDR marshalling does not
  	 * return any results, it BUGs. */
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
177
178
179
180
181
182
183
184
  	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;
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  		args.len = nfsacl_size(acl, dfacl);
  	} else
  		args.len = nfsacl_size(acl, NULL);
  
  	if (args.len > NFS_ACL_INLINE_BUFSIZE) {
  		unsigned int npages = 1 + ((args.len - 1) >> PAGE_SHIFT);
  
  		status = -ENOMEM;
  		do {
  			args.pages[args.npages] = alloc_page(GFP_KERNEL);
  			if (args.pages[args.npages] == NULL)
  				goto out_freepages;
  			args.npages++;
  		} while (args.npages < npages);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
199
200
201
202
  	}
  
  	dprintk("NFS call setacl
  ");
6e94d6299   Trond Myklebust   NFS: Reduce stack...
203
204
205
206
  	status = -ENOMEM;
  	fattr = nfs_alloc_fattr();
  	if (fattr == NULL)
  		goto out_freepages;
dead28da8   Chuck Lever   SUNRPC: eliminate...
207
  	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
6e94d6299   Trond Myklebust   NFS: Reduce stack...
208
  	msg.rpc_resp = fattr;
dead28da8   Chuck Lever   SUNRPC: eliminate...
209
  	status = rpc_call_sync(server->client_acl, &msg, 0);
f41f74183   Trond Myklebust   NFS: Ensure we za...
210
211
  	nfs_access_zap_cache(inode);
  	nfs_zap_acl_cache(inode);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
212
213
  	dprintk("NFS reply setacl: %d
  ", status);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
214
215
  	switch (status) {
  		case 0:
6e94d6299   Trond Myklebust   NFS: Reduce stack...
216
  			status = nfs_refresh_inode(inode, fattr);
013cdf108   Christoph Hellwig   nfs: use generic ...
217
218
  			set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
  			set_cached_acl(inode, ACL_TYPE_DEFAULT, dfacl);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
219
220
221
222
223
224
225
226
227
228
  			break;
  		case -EPFNOSUPPORT:
  		case -EPROTONOSUPPORT:
  			dprintk("NFS_V3_ACL SETACL RPC not supported"
  					"(will not retry)
  ");
  			server->caps &= ~NFS_CAP_ACLS;
  		case -ENOTSUPP:
  			status = -EOPNOTSUPP;
  	}
6e94d6299   Trond Myklebust   NFS: Reduce stack...
229
  	nfs_free_fattr(fattr);
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
230
231
232
233
234
  out_freepages:
  	while (args.npages != 0) {
  		args.npages--;
  		__free_page(args.pages[args.npages]);
  	}
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
235
236
237
  out:
  	return status;
  }
8f493b9cf   Trond Myklebust   NFSv3: Fix return...
238
239
240
241
242
243
244
245
  int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
  		struct posix_acl *dfacl)
  {
  	int ret;
  	ret = __nfs3_proc_setacls(inode, acl, dfacl);
  	return (ret == -EOPNOTSUPP) ? 0 : ret;
  
  }
013cdf108   Christoph Hellwig   nfs: use generic ...
246
  int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
247
248
249
250
251
252
  {
  	struct posix_acl *alloc = NULL, *dfacl = NULL;
  	int status;
  
  	if (S_ISDIR(inode->i_mode)) {
  		switch(type) {
013cdf108   Christoph Hellwig   nfs: use generic ...
253
254
255
256
257
258
259
260
261
262
263
264
  		case ACL_TYPE_ACCESS:
  			alloc = dfacl = get_acl(inode, ACL_TYPE_DEFAULT);
  			if (IS_ERR(alloc))
  				goto fail;
  			break;
  
  		case ACL_TYPE_DEFAULT:
  			dfacl = acl;
  			alloc = acl = get_acl(inode, ACL_TYPE_ACCESS);
  			if (IS_ERR(alloc))
  				goto fail;
  			break;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
265
  		}
013cdf108   Christoph Hellwig   nfs: use generic ...
266
  	}
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
267
268
269
270
271
272
  
  	if (acl == NULL) {
  		alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
  		if (IS_ERR(alloc))
  			goto fail;
  	}
8f493b9cf   Trond Myklebust   NFSv3: Fix return...
273
  	status = __nfs3_proc_setacls(inode, acl, dfacl);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
274
275
276
277
278
279
  	posix_acl_release(alloc);
  	return status;
  
  fail:
  	return PTR_ERR(alloc);
  }
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
280

013cdf108   Christoph Hellwig   nfs: use generic ...
281
282
283
284
285
  const struct xattr_handler *nfs3_xattr_handlers[] = {
  	&posix_acl_access_xattr_handler,
  	&posix_acl_default_xattr_handler,
  	NULL,
  };
74adf83f5   Christoph Hellwig   nfs: only show Po...
286
287
288
289
290
291
292
293
294
  
  static int
  nfs3_list_one_acl(struct inode *inode, int type, const char *name, void *data,
  		size_t size, ssize_t *result)
  {
  	struct posix_acl *acl;
  	char *p = data + *result;
  
  	acl = get_acl(inode, type);
7a9e75a18   Andrey Utkin   nfs3_list_one_acl...
295
  	if (IS_ERR_OR_NULL(acl))
74adf83f5   Christoph Hellwig   nfs: only show Po...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  		return 0;
  
  	posix_acl_release(acl);
  
  	*result += strlen(name);
  	*result += 1;
  	if (!size)
  		return 0;
  	if (*result > size)
  		return -ERANGE;
  
  	strcpy(p, name);
  	return 0;
  }
  
  ssize_t
  nfs3_listxattr(struct dentry *dentry, char *data, size_t size)
  {
2b0143b5c   David Howells   VFS: normal files...
314
  	struct inode *inode = d_inode(dentry);
74adf83f5   Christoph Hellwig   nfs: only show Po...
315
316
317
318
  	ssize_t result = 0;
  	int error;
  
  	error = nfs3_list_one_acl(inode, ACL_TYPE_ACCESS,
97d792992   Andreas Gruenbacher   posix acls: Remov...
319
  			XATTR_NAME_POSIX_ACL_ACCESS, data, size, &result);
74adf83f5   Christoph Hellwig   nfs: only show Po...
320
321
322
323
  	if (error)
  		return error;
  
  	error = nfs3_list_one_acl(inode, ACL_TYPE_DEFAULT,
97d792992   Andreas Gruenbacher   posix acls: Remov...
324
  			XATTR_NAME_POSIX_ACL_DEFAULT, data, size, &result);
74adf83f5   Christoph Hellwig   nfs: only show Po...
325
326
327
328
  	if (error)
  		return error;
  	return result;
  }