Blame view

fs/nfs/nfs3acl.c 10.1 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"
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
9
10
11
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
43
44
45
46
47
48
49
50
51
52
53
54
55
  #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;
334a13ec3   Christoph Hellwig   [PATCH] really re...
56
  	if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
57
  		type = ACL_TYPE_ACCESS;
334a13ec3   Christoph Hellwig   [PATCH] really re...
58
  	else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  		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;
334a13ec3   Christoph Hellwig   [PATCH] really re...
84
  	if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
85
  		type = ACL_TYPE_ACCESS;
334a13ec3   Christoph Hellwig   [PATCH] really re...
86
  	else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  		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;
334a13ec3   Christoph Hellwig   [PATCH] really re...
104
  	if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
105
  		type = ACL_TYPE_ACCESS;
334a13ec3   Christoph Hellwig   [PATCH] really re...
106
  	else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
107
108
109
110
111
112
  		type = ACL_TYPE_DEFAULT;
  	else
  		return -EOPNOTSUPP;
  
  	return nfs3_proc_setacl(inode, type, NULL);
  }
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
113
114
  static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi)
  {
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
115
  	if (!IS_ERR(nfsi->acl_access)) {
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
116
117
118
  		posix_acl_release(nfsi->acl_access);
  		nfsi->acl_access = ERR_PTR(-EAGAIN);
  	}
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
119
  	if (!IS_ERR(nfsi->acl_default)) {
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  		posix_acl_release(nfsi->acl_default);
  		nfsi->acl_default = ERR_PTR(-EAGAIN);
  	}
  }
  
  void nfs3_forget_cached_acls(struct inode *inode)
  {
  	dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)
  ", inode->i_sb->s_id,
  		inode->i_ino);
  	spin_lock(&inode->i_lock);
  	__nfs3_forget_cached_acls(NFS_I(inode));
  	spin_unlock(&inode->i_lock);
  }
  
  static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type)
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
138
  	struct posix_acl *acl = ERR_PTR(-EINVAL);
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
139
140
141
142
143
144
145
146
147
148
149
150
  
  	spin_lock(&inode->i_lock);
  	switch(type) {
  		case ACL_TYPE_ACCESS:
  			acl = nfsi->acl_access;
  			break;
  
  		case ACL_TYPE_DEFAULT:
  			acl = nfsi->acl_default;
  			break;
  
  		default:
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
151
  			goto out;
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
152
  	}
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
153
  	if (IS_ERR(acl))
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
154
155
156
  		acl = ERR_PTR(-EAGAIN);
  	else
  		acl = posix_acl_dup(acl);
458818ed7   Trond Myklebust   [PATCH] NFS: Fix ...
157
  out:
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  	spin_unlock(&inode->i_lock);
  	dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p
  ", inode->i_sb->s_id,
  		inode->i_ino, type, acl);
  	return acl;
  }
  
  static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl,
  		    struct posix_acl *dfacl)
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  
  	dprintk("nfs3_cache_acls(%s/%ld, %p, %p)
  ", inode->i_sb->s_id,
  		inode->i_ino, acl, dfacl);
  	spin_lock(&inode->i_lock);
  	__nfs3_forget_cached_acls(NFS_I(inode));
4814f56d1   Andreas Gruenbacher   NFSv3: Client-sid...
175
176
177
178
  	if (!IS_ERR(acl))
  		nfsi->acl_access = posix_acl_dup(acl);
  	if (!IS_ERR(dfacl))
  		nfsi->acl_default = posix_acl_dup(dfacl);
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
179
180
  	spin_unlock(&inode->i_lock);
  }
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
181
182
183
  struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
  {
  	struct nfs_server *server = NFS_SERVER(inode);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
184
185
186
187
188
189
190
  	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 = {
6e94d6299   Trond Myklebust   NFS: Reduce stack...
191
  		0
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
192
  	};
dead28da8   Chuck Lever   SUNRPC: eliminate...
193
194
195
196
  	struct rpc_message msg = {
  		.rpc_argp	= &args,
  		.rpc_resp	= &res,
  	};
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
197
  	struct posix_acl *acl;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
198
199
200
201
  	int status, count;
  
  	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
  		return ERR_PTR(-EOPNOTSUPP);
5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
202
203
204
205
206
207
208
  	status = nfs_revalidate_inode(server, inode);
  	if (status < 0)
  		return ERR_PTR(status);
  	acl = nfs3_get_cached_acl(inode, type);
  	if (acl != ERR_PTR(-EAGAIN))
  		return acl;
  	acl = NULL;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
209

5c6a9f7d9   Andreas Gruenbacher   [PATCH] NFS: Cach...
210
211
212
213
214
215
216
217
218
219
220
221
  	/*
  	 * 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 ...
222
223
224
  
  	dprintk("NFS call getacl
  ");
dead28da8   Chuck Lever   SUNRPC: eliminate...
225
  	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL];
6e94d6299   Trond Myklebust   NFS: Reduce stack...
226
227
228
  	res.fattr = nfs_alloc_fattr();
  	if (res.fattr == NULL)
  		return ERR_PTR(-ENOMEM);
dead28da8   Chuck Lever   SUNRPC: eliminate...
229
  	status = rpc_call_sync(server->client_acl, &msg, 0);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
230
231
232
233
234
235
236
237
238
  	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...
239
  			status = nfs_refresh_inode(inode, res.fattr);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  			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) {
  		if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
  			posix_acl_release(res.acl_access);
  			res.acl_access = NULL;
  		}
  	}
4814f56d1   Andreas Gruenbacher   NFSv3: Client-sid...
262
263
264
  	nfs3_cache_acls(inode,
  		(res.mask & NFS_ACL)   ? res.acl_access  : ERR_PTR(-EINVAL),
  		(res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL));
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  
  	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);
6e94d6299   Trond Myklebust   NFS: Reduce stack...
280
  	nfs_free_fattr(res.fattr);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
281
282
283
284
285
286
287
288
289
290
291
292
  
  	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);
6e94d6299   Trond Myklebust   NFS: Reduce stack...
293
  	struct nfs_fattr *fattr;
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
294
  	struct page *pages[NFSACL_MAXPAGES];
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
295
296
297
298
299
300
  	struct nfs3_setaclargs args = {
  		.inode = inode,
  		.mask = NFS_ACL,
  		.acl_access = acl,
  		.pages = pages,
  	};
dead28da8   Chuck Lever   SUNRPC: eliminate...
301
302
303
304
  	struct rpc_message msg = {
  		.rpc_argp	= &args,
  		.rpc_resp	= &fattr,
  	};
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
305
  	int status;
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
306
307
308
309
  
  	status = -EOPNOTSUPP;
  	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
  		goto out;
f61f6da0d   Chuck Lever   NFS: Prevent memo...
310
311
  	/* We are doing this here because XDR marshalling does not
  	 * return any results, it BUGs. */
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
312
313
314
315
316
317
318
319
  	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 ...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  		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 ...
334
335
336
337
  	}
  
  	dprintk("NFS call setacl
  ");
6e94d6299   Trond Myklebust   NFS: Reduce stack...
338
339
340
341
  	status = -ENOMEM;
  	fattr = nfs_alloc_fattr();
  	if (fattr == NULL)
  		goto out_freepages;
dead28da8   Chuck Lever   SUNRPC: eliminate...
342
  	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
6e94d6299   Trond Myklebust   NFS: Reduce stack...
343
  	msg.rpc_resp = fattr;
dead28da8   Chuck Lever   SUNRPC: eliminate...
344
  	status = rpc_call_sync(server->client_acl, &msg, 0);
f41f74183   Trond Myklebust   NFS: Ensure we za...
345
346
  	nfs_access_zap_cache(inode);
  	nfs_zap_acl_cache(inode);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
347
348
  	dprintk("NFS reply setacl: %d
  ", status);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
349
350
  	switch (status) {
  		case 0:
6e94d6299   Trond Myklebust   NFS: Reduce stack...
351
  			status = nfs_refresh_inode(inode, fattr);
4814f56d1   Andreas Gruenbacher   NFSv3: Client-sid...
352
  			nfs3_cache_acls(inode, acl, dfacl);
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
353
354
355
356
357
358
359
360
361
362
  			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...
363
  	nfs_free_fattr(fattr);
ae46141ff   Trond Myklebust   NFSv3: Fix posix ...
364
365
366
367
368
  out_freepages:
  	while (args.npages != 0) {
  		args.npages--;
  		__free_page(args.pages[args.npages]);
  	}
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
  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);
  }
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
413
414
  
  int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
d3fb61207   Al Viro   switch posix_acl_...
415
  		umode_t mode)
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
416
417
418
419
420
421
422
423
424
425
426
  {
  	struct posix_acl *dfacl, *acl;
  	int error = 0;
  
  	dfacl = nfs3_proc_getacl(dir, ACL_TYPE_DEFAULT);
  	if (IS_ERR(dfacl)) {
  		error = PTR_ERR(dfacl);
  		return (error == -EOPNOTSUPP) ? 0 : error;
  	}
  	if (!dfacl)
  		return 0;
826cae2f2   Al Viro   kill boilerplates...
427
428
  	acl = posix_acl_dup(dfacl);
  	error = posix_acl_create(&acl, GFP_KERNEL, &mode);
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
429
  	if (error < 0)
826cae2f2   Al Viro   kill boilerplates...
430
  		goto out_release_dfacl;
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
431
432
  	error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ?
  						      dfacl : NULL);
055ffbea0   Andreas Gruenbacher   [PATCH] NFS: Fix ...
433
434
435
436
437
  	posix_acl_release(acl);
  out_release_dfacl:
  	posix_acl_release(dfacl);
  	return error;
  }