1/*
2 * linux/fs/nfsd/nfs2acl.c
3 *
4 * Process version 2 NFSACL requests.
5 *
6 * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
7 */
8
9#include <linux/sunrpc/svc.h>
10#include <linux/nfs.h>
11#include <linux/nfsd/nfsd.h>
12#include <linux/nfsd/cache.h>
13#include <linux/nfsd/xdr.h>
14#include <linux/nfsd/xdr3.h>
15#include <linux/posix_acl.h>
16#include <linux/nfsacl.h>
17
18#define NFSDDBG_FACILITY		NFSDDBG_PROC
19#define RETURN_STATUS(st)	{ resp->status = (st); return (st); }
20
21/*
22 * NULL call.
23 */
24static __be32
25nfsacld_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
26{
27	return nfs_ok;
28}
29
30/*
31 * Get the Access and/or Default ACL of a file.
32 */
33static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp,
34		struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
35{
36	svc_fh *fh;
37	struct posix_acl *acl;
38	__be32 nfserr = 0;
39
40	dprintk("nfsd: GETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
41
42	fh = fh_copy(&resp->fh, &argp->fh);
43	if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP)))
44		RETURN_STATUS(nfserr_inval);
45
46	if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
47		RETURN_STATUS(nfserr_inval);
48	resp->mask = argp->mask;
49
50	if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
51		acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS);
52		if (IS_ERR(acl)) {
53			int err = PTR_ERR(acl);
54
55			if (err == -ENODATA || err == -EOPNOTSUPP)
56				acl = NULL;
57			else {
58				nfserr = nfserrno(err);
59				goto fail;
60			}
61		}
62		if (acl == NULL) {
63			/* Solaris returns the inode's minimum ACL. */
64
65			struct inode *inode = fh->fh_dentry->d_inode;
66			acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
67		}
68		resp->acl_access = acl;
69	}
70	if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
71		/* Check how Solaris handles requests for the Default ACL
72		   of a non-directory! */
73
74		acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT);
75		if (IS_ERR(acl)) {
76			int err = PTR_ERR(acl);
77
78			if (err == -ENODATA || err == -EOPNOTSUPP)
79				acl = NULL;
80			else {
81				nfserr = nfserrno(err);
82				goto fail;
83			}
84		}
85		resp->acl_default = acl;
86	}
87
88	/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
89	RETURN_STATUS(0);
90
91fail:
92	posix_acl_release(resp->acl_access);
93	posix_acl_release(resp->acl_default);
94	RETURN_STATUS(nfserr);
95}
96
97/*
98 * Set the Access and/or Default ACL of a file.
99 */
100static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp,
101		struct nfsd3_setaclargs *argp,
102		struct nfsd_attrstat *resp)
103{
104	svc_fh *fh;
105	__be32 nfserr = 0;
106
107	dprintk("nfsd: SETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
108
109	fh = fh_copy(&resp->fh, &argp->fh);
110	nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR);
111
112	if (!nfserr) {
113		nfserr = nfserrno( nfsd_set_posix_acl(
114			fh, ACL_TYPE_ACCESS, argp->acl_access) );
115	}
116	if (!nfserr) {
117		nfserr = nfserrno( nfsd_set_posix_acl(
118			fh, ACL_TYPE_DEFAULT, argp->acl_default) );
119	}
120
121	/* argp->acl_{access,default} may have been allocated in
122	   nfssvc_decode_setaclargs. */
123	posix_acl_release(argp->acl_access);
124	posix_acl_release(argp->acl_default);
125	return nfserr;
126}
127
128/*
129 * Check file attributes
130 */
131static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp,
132		struct nfsd_fhandle *argp, struct nfsd_attrstat *resp)
133{
134	dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
135
136	fh_copy(&resp->fh, &argp->fh);
137	return fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
138}
139
140/*
141 * Check file access
142 */
143static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
144		struct nfsd3_accessres *resp)
145{
146	__be32 nfserr;
147
148	dprintk("nfsd: ACCESS(2acl)   %s 0x%x\n",
149			SVCFH_fmt(&argp->fh),
150			argp->access);
151
152	fh_copy(&resp->fh, &argp->fh);
153	resp->access = argp->access;
154	nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
155	return nfserr;
156}
157
158/*
159 * XDR decode functions
160 */
161static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p,
162		struct nfsd3_getaclargs *argp)
163{
164	if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
165		return 0;
166	argp->mask = ntohl(*p); p++;
167
168	return xdr_argsize_check(rqstp, p);
169}
170
171
172static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
173		struct nfsd3_setaclargs *argp)
174{
175	struct kvec *head = rqstp->rq_arg.head;
176	unsigned int base;
177	int n;
178
179	if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
180		return 0;
181	argp->mask = ntohl(*p++);
182	if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
183	    !xdr_argsize_check(rqstp, p))
184		return 0;
185
186	base = (char *)p - (char *)head->iov_base;
187	n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
188			  (argp->mask & NFS_ACL) ?
189			  &argp->acl_access : NULL);
190	if (n > 0)
191		n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
192				  (argp->mask & NFS_DFACL) ?
193				  &argp->acl_default : NULL);
194	return (n > 0);
195}
196
197static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p,
198		struct nfsd_fhandle *argp)
199{
200	if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
201		return 0;
202	return xdr_argsize_check(rqstp, p);
203}
204
205static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
206		struct nfsd3_accessargs *argp)
207{
208	if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
209		return 0;
210	argp->access = ntohl(*p++);
211
212	return xdr_argsize_check(rqstp, p);
213}
214
215/*
216 * XDR encode functions
217 */
218
219/* GETACL */
220static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p,
221		struct nfsd3_getaclres *resp)
222{
223	struct dentry *dentry = resp->fh.fh_dentry;
224	struct inode *inode = dentry->d_inode;
225	struct kvec *head = rqstp->rq_res.head;
226	unsigned int base;
227	int n;
228	int w;
229
230	if (dentry == NULL || dentry->d_inode == NULL)
231		return 0;
232	inode = dentry->d_inode;
233
234	p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
235	*p++ = htonl(resp->mask);
236	if (!xdr_ressize_check(rqstp, p))
237		return 0;
238	base = (char *)p - (char *)head->iov_base;
239
240	rqstp->rq_res.page_len = w = nfsacl_size(
241		(resp->mask & NFS_ACL)   ? resp->acl_access  : NULL,
242		(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
243	while (w > 0) {
244		if (!rqstp->rq_respages[rqstp->rq_resused++])
245			return 0;
246		w -= PAGE_SIZE;
247	}
248
249	n = nfsacl_encode(&rqstp->rq_res, base, inode,
250			  resp->acl_access,
251			  resp->mask & NFS_ACL, 0);
252	if (n > 0)
253		n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
254				  resp->acl_default,
255				  resp->mask & NFS_DFACL,
256				  NFS_ACL_DEFAULT);
257	if (n <= 0)
258		return 0;
259	return 1;
260}
261
262static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p,
263		struct nfsd_attrstat *resp)
264{
265	p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
266	return xdr_ressize_check(rqstp, p);
267}
268
269/* ACCESS */
270static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p,
271		struct nfsd3_accessres *resp)
272{
273	p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
274	*p++ = htonl(resp->access);
275	return xdr_ressize_check(rqstp, p);
276}
277
278/*
279 * XDR release functions
280 */
281static int nfsaclsvc_release_getacl(struct svc_rqst *rqstp, __be32 *p,
282		struct nfsd3_getaclres *resp)
283{
284	fh_put(&resp->fh);
285	posix_acl_release(resp->acl_access);
286	posix_acl_release(resp->acl_default);
287	return 1;
288}
289
290static int nfsaclsvc_release_attrstat(struct svc_rqst *rqstp, __be32 *p,
291		struct nfsd_attrstat *resp)
292{
293	fh_put(&resp->fh);
294	return 1;
295}
296
297static int nfsaclsvc_release_access(struct svc_rqst *rqstp, __be32 *p,
298               struct nfsd3_accessres *resp)
299{
300       fh_put(&resp->fh);
301       return 1;
302}
303
304#define nfsaclsvc_decode_voidargs	NULL
305#define nfsaclsvc_encode_voidres	NULL
306#define nfsaclsvc_release_void		NULL
307#define nfsd3_fhandleargs	nfsd_fhandle
308#define nfsd3_attrstatres	nfsd_attrstat
309#define nfsd3_voidres		nfsd3_voidargs
310struct nfsd3_voidargs { int dummy; };
311
312#define PROC(name, argt, rest, relt, cache, respsize)	\
313 { (svc_procfunc) nfsacld_proc_##name,		\
314   (kxdrproc_t) nfsaclsvc_decode_##argt##args,	\
315   (kxdrproc_t) nfsaclsvc_encode_##rest##res,	\
316   (kxdrproc_t) nfsaclsvc_release_##relt,		\
317   sizeof(struct nfsd3_##argt##args),		\
318   sizeof(struct nfsd3_##rest##res),		\
319   0,						\
320   cache,					\
321   respsize,					\
322 }
323
324#define ST 1		/* status*/
325#define AT 21		/* attributes */
326#define pAT (1+AT)	/* post attributes - conditional */
327#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
328
329static struct svc_procedure		nfsd_acl_procedures2[] = {
330  PROC(null,	void,		void,		void,	  RC_NOCACHE, ST),
331  PROC(getacl,	getacl,		getacl,		getacl,	  RC_NOCACHE, ST+1+2*(1+ACL)),
332  PROC(setacl,	setacl,		attrstat,	attrstat, RC_NOCACHE, ST+AT),
333  PROC(getattr, fhandle,	attrstat,	attrstat, RC_NOCACHE, ST+AT),
334  PROC(access,	access,		access,		access,   RC_NOCACHE, ST+AT+1),
335};
336
337struct svc_version	nfsd_acl_version2 = {
338		.vs_vers	= 2,
339		.vs_nproc	= 5,
340		.vs_proc	= nfsd_acl_procedures2,
341		.vs_dispatch	= nfsd_dispatch,
342		.vs_xdrsize	= NFS3_SVC_XDRSIZE,
343		.vs_hidden	= 1,
344};
345