nfs3acl.c 6.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7
/*
 * Process version 3 NFSACL requests.
 *
 * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
 */

8 9
#include "nfsd.h"
/* FIXME: nfsacl.h is a broken header */
10
#include <linux/nfsacl.h>
11
#include <linux/gfp.h>
12 13
#include "cache.h"
#include "xdr3.h"
14
#include "vfs.h"
15 16 17 18

/*
 * NULL call.
 */
A
Al Viro 已提交
19
static __be32
20
nfsd3_proc_null(struct svc_rqst *rqstp)
21
{
22
	return rpc_success;
23 24 25 26 27
}

/*
 * Get the Access and/or Default ACL of a file.
 */
28
static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
29
{
30 31
	struct nfsd3_getaclargs *argp = rqstp->rq_argp;
	struct nfsd3_getaclres *resp = rqstp->rq_resp;
32
	struct posix_acl *acl;
33 34
	struct inode *inode;
	svc_fh *fh;
35 36

	fh = fh_copy(&resp->fh, &argp->fh);
37 38 39
	resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
	if (resp->status != nfs_ok)
		goto out;
40

41
	inode = d_inode(fh->fh_dentry);
42

43 44 45 46
	if (argp->mask & ~NFS_ACL_MASK) {
		resp->status = nfserr_inval;
		goto out;
	}
47 48 49
	resp->mask = argp->mask;

	if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
50
		acl = get_acl(inode, ACL_TYPE_ACCESS);
51 52 53 54
		if (acl == NULL) {
			/* Solaris returns the inode's minimum ACL. */
			acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
		}
55
		if (IS_ERR(acl)) {
56
			resp->status = nfserrno(PTR_ERR(acl));
57 58
			goto fail;
		}
59 60 61 62 63
		resp->acl_access = acl;
	}
	if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
		/* Check how Solaris handles requests for the Default ACL
		   of a non-directory! */
64
		acl = get_acl(inode, ACL_TYPE_DEFAULT);
65
		if (IS_ERR(acl)) {
66
			resp->status = nfserrno(PTR_ERR(acl));
67
			goto fail;
68 69 70 71 72
		}
		resp->acl_default = acl;
	}

	/* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
73
out:
74
	return rpc_success;
75 76 77 78

fail:
	posix_acl_release(resp->acl_access);
	posix_acl_release(resp->acl_default);
79
	goto out;
80 81 82 83 84
}

/*
 * Set the Access and/or Default ACL of a file.
 */
85
static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
86
{
87 88
	struct nfsd3_setaclargs *argp = rqstp->rq_argp;
	struct nfsd3_attrstat *resp = rqstp->rq_resp;
89
	struct inode *inode;
90
	svc_fh *fh;
91
	int error;
92 93

	fh = fh_copy(&resp->fh, &argp->fh);
94 95
	resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
	if (resp->status != nfs_ok)
96
		goto out;
97

98
	inode = d_inode(fh->fh_dentry);
99

100 101 102 103
	error = fh_want_write(fh);
	if (error)
		goto out_errno;

104 105 106
	fh_lock(fh);

	error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
107
	if (error)
108 109
		goto out_drop_lock;
	error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
110

111 112
out_drop_lock:
	fh_unlock(fh);
113 114
	fh_drop_write(fh);
out_errno:
115
	resp->status = nfserrno(error);
116
out:
117 118 119 120
	/* argp->acl_{access,default} may have been allocated in
	   nfs3svc_decode_setaclargs. */
	posix_acl_release(argp->acl_access);
	posix_acl_release(argp->acl_default);
121
	return rpc_success;
122 123 124 125 126
}

/*
 * XDR decode functions
 */
127
static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
128
{
129 130
	struct nfsd3_getaclargs *args = rqstp->rq_argp;

131 132
	p = nfs3svc_decode_fh(p, &args->fh);
	if (!p)
133 134 135 136 137 138 139
		return 0;
	args->mask = ntohl(*p); p++;

	return xdr_argsize_check(rqstp, p);
}


140
static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
141
{
142
	struct nfsd3_setaclargs *args = rqstp->rq_argp;
143 144 145 146
	struct kvec *head = rqstp->rq_arg.head;
	unsigned int base;
	int n;

147 148
	p = nfs3svc_decode_fh(p, &args->fh);
	if (!p)
149 150
		return 0;
	args->mask = ntohl(*p++);
151
	if (args->mask & ~NFS_ACL_MASK ||
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
	    !xdr_argsize_check(rqstp, p))
		return 0;

	base = (char *)p - (char *)head->iov_base;
	n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
			  (args->mask & NFS_ACL) ?
			  &args->acl_access : NULL);
	if (n > 0)
		n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
				  (args->mask & NFS_DFACL) ?
				  &args->acl_default : NULL);
	return (n > 0);
}

/*
 * XDR encode functions
 */

/* GETACL */
171
static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
172
{
173
	struct nfsd3_getaclres *resp = rqstp->rq_resp;
174 175
	struct dentry *dentry = resp->fh.fh_dentry;

176
	*p++ = resp->status;
177
	p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
178 179
	if (resp->status == 0 && dentry && d_really_is_positive(dentry)) {
		struct inode *inode = d_inode(dentry);
180 181 182
		struct kvec *head = rqstp->rq_res.head;
		unsigned int base;
		int n;
183
		int w;
184 185 186 187 188 189

		*p++ = htonl(resp->mask);
		if (!xdr_ressize_check(rqstp, p))
			return 0;
		base = (char *)p - (char *)head->iov_base;

190 191 192
		rqstp->rq_res.page_len = w = nfsacl_size(
			(resp->mask & NFS_ACL)   ? resp->acl_access  : NULL,
			(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
193
		while (w > 0) {
194
			if (!*(rqstp->rq_next_page++))
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
				return 0;
			w -= PAGE_SIZE;
		}

		n = nfsacl_encode(&rqstp->rq_res, base, inode,
				  resp->acl_access,
				  resp->mask & NFS_ACL, 0);
		if (n > 0)
			n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
					  resp->acl_default,
					  resp->mask & NFS_DFACL,
					  NFS_ACL_DEFAULT);
		if (n <= 0)
			return 0;
	} else
		if (!xdr_ressize_check(rqstp, p))
			return 0;

	return 1;
}

/* SETACL */
217
static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p)
218
{
219 220
	struct nfsd3_attrstat *resp = rqstp->rq_resp;

221
	*p++ = resp->status;
222 223 224 225 226 227 228
	p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
	return xdr_ressize_check(rqstp, p);
}

/*
 * XDR release functions
 */
229
static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
230
{
231 232
	struct nfsd3_getaclres *resp = rqstp->rq_resp;

233 234 235 236 237 238 239 240 241 242 243 244
	fh_put(&resp->fh);
	posix_acl_release(resp->acl_access);
	posix_acl_release(resp->acl_default);
}

struct nfsd3_voidargs { int dummy; };

#define ST 1		/* status*/
#define AT 21		/* attributes */
#define pAT (1+AT)	/* post attributes - conditional */
#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */

245 246 247
static const struct svc_procedure nfsd_acl_procedures3[3] = {
	[ACLPROC3_NULL] = {
		.pc_func = nfsd3_proc_null,
248
		.pc_decode = nfs3svc_decode_voidarg,
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
		.pc_encode = nfs3svc_encode_voidres,
		.pc_argsize = sizeof(struct nfsd3_voidargs),
		.pc_ressize = sizeof(struct nfsd3_voidargs),
		.pc_cachetype = RC_NOCACHE,
		.pc_xdrressize = ST,
	},
	[ACLPROC3_GETACL] = {
		.pc_func = nfsd3_proc_getacl,
		.pc_decode = nfs3svc_decode_getaclargs,
		.pc_encode = nfs3svc_encode_getaclres,
		.pc_release = nfs3svc_release_getacl,
		.pc_argsize = sizeof(struct nfsd3_getaclargs),
		.pc_ressize = sizeof(struct nfsd3_getaclres),
		.pc_cachetype = RC_NOCACHE,
		.pc_xdrressize = ST+1+2*(1+ACL),
	},
	[ACLPROC3_SETACL] = {
		.pc_func = nfsd3_proc_setacl,
		.pc_decode = nfs3svc_decode_setaclargs,
		.pc_encode = nfs3svc_encode_setaclres,
		.pc_release = nfs3svc_release_fhandle,
		.pc_argsize = sizeof(struct nfsd3_setaclargs),
		.pc_ressize = sizeof(struct nfsd3_attrstat),
		.pc_cachetype = RC_NOCACHE,
		.pc_xdrressize = ST+pAT,
	},
275 276
};

277
static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)];
278 279 280 281 282 283 284
const struct svc_version nfsd_acl_version3 = {
	.vs_vers	= 3,
	.vs_nproc	= 3,
	.vs_proc	= nfsd_acl_procedures3,
	.vs_count	= nfsd_acl_count3,
	.vs_dispatch	= nfsd_dispatch,
	.vs_xdrsize	= NFS3_SVC_XDRSIZE,
285 286
};