nfs3acl.c 8.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/fs.h>
3
#include <linux/gfp.h>
4 5 6
#include <linux/nfs.h>
#include <linux/nfs3.h>
#include <linux/nfs_fs.h>
7
#include <linux/posix_acl_xattr.h>
8 9
#include <linux/nfsacl.h>

10
#include "internal.h"
11
#include "nfs3_fs.h"
12

13 14
#define NFSDBG_FACILITY	NFSDBG_PROC

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
/*
 * 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);
}

47
struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
48 49 50 51 52 53 54 55 56
{
	struct nfs_server *server = NFS_SERVER(inode);
	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 = {
57
		NULL,
58
	};
C
Chuck Lever 已提交
59 60 61 62
	struct rpc_message msg = {
		.rpc_argp	= &args,
		.rpc_resp	= &res,
	};
63 64 65 66 67
	int status, count;

	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
		return ERR_PTR(-EOPNOTSUPP);

68 69 70
	status = nfs_revalidate_inode(server, inode);
	if (status < 0)
		return ERR_PTR(status);
71

72 73 74 75 76 77 78 79 80 81 82 83
	/*
	 * 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;
84 85

	dprintk("NFS call getacl\n");
C
Chuck Lever 已提交
86
	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL];
87 88 89 90
	res.fattr = nfs_alloc_fattr();
	if (res.fattr == NULL)
		return ERR_PTR(-ENOMEM);

91 92 93 94 95
	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);

C
Chuck Lever 已提交
96
	status = rpc_call_sync(server->client_acl, &msg, 0);
97 98 99 100 101 102 103 104
	dprintk("NFS reply getacl: %d\n", 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:
105
			status = nfs_refresh_inode(inode, res.fattr);
106 107 108 109 110
			break;
		case -EPFNOSUPPORT:
		case -EPROTONOSUPPORT:
			dprintk("NFS_V3_ACL extension not supported; disabling\n");
			server->caps &= ~NFS_CAP_ACLS;
111
			/* fall through */
112 113 114 115 116 117 118 119 120 121 122
		case -ENOTSUPP:
			status = -EOPNOTSUPP;
		default:
			goto getout;
	}
	if ((args.mask & res.mask) != args.mask) {
		status = -EIO;
		goto getout;
	}

	if (res.acl_access != NULL) {
123
		if ((posix_acl_equiv_mode(res.acl_access, NULL) == 0) ||
124
		    res.acl_access->a_count == 0) {
125 126 127 128 129
			posix_acl_release(res.acl_access);
			res.acl_access = NULL;
		}
	}

130
	if (res.mask & NFS_ACL)
131
		nfs3_complete_get_acl(&inode->i_acl, res.acl_access);
132 133
	else
		forget_cached_acl(inode, ACL_TYPE_ACCESS);
134

135
	if (res.mask & NFS_DFACL)
136
		nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default);
137 138 139 140 141 142 143 144 145 146
	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;
147 148 149
	}

getout:
150 151
	nfs3_abort_get_acl(&inode->i_acl);
	nfs3_abort_get_acl(&inode->i_default_acl);
152 153
	posix_acl_release(res.acl_access);
	posix_acl_release(res.acl_default);
154
	nfs_free_fattr(res.fattr);
155
	return ERR_PTR(status);
156 157
}

158
static int __nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
159
		struct posix_acl *dfacl)
160 161
{
	struct nfs_server *server = NFS_SERVER(inode);
162
	struct nfs_fattr *fattr;
T
Trond Myklebust 已提交
163
	struct page *pages[NFSACL_MAXPAGES];
164 165 166 167 168 169
	struct nfs3_setaclargs args = {
		.inode = inode,
		.mask = NFS_ACL,
		.acl_access = acl,
		.pages = pages,
	};
C
Chuck Lever 已提交
170 171 172 173
	struct rpc_message msg = {
		.rpc_argp	= &args,
		.rpc_resp	= &fattr,
	};
174 175 176 177
	int status = 0;

	if (acl == NULL && (!S_ISDIR(inode->i_mode) || dfacl == NULL))
		goto out;
178 179 180 181 182

	status = -EOPNOTSUPP;
	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
		goto out;

183 184
	/* We are doing this here because XDR marshalling does not
	 * return any results, it BUGs. */
185 186 187 188 189 190 191 192
	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;
T
Trond Myklebust 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206
		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);
207 208 209
	}

	dprintk("NFS call setacl\n");
210 211 212 213 214
	status = -ENOMEM;
	fattr = nfs_alloc_fattr();
	if (fattr == NULL)
		goto out_freepages;

C
Chuck Lever 已提交
215
	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
216
	msg.rpc_resp = fattr;
C
Chuck Lever 已提交
217
	status = rpc_call_sync(server->client_acl, &msg, 0);
218 219
	nfs_access_zap_cache(inode);
	nfs_zap_acl_cache(inode);
220 221 222 223
	dprintk("NFS reply setacl: %d\n", status);

	switch (status) {
		case 0:
224
			status = nfs_refresh_inode(inode, fattr);
225 226
			set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
			set_cached_acl(inode, ACL_TYPE_DEFAULT, dfacl);
227 228 229 230 231 232
			break;
		case -EPFNOSUPPORT:
		case -EPROTONOSUPPORT:
			dprintk("NFS_V3_ACL SETACL RPC not supported"
					"(will not retry)\n");
			server->caps &= ~NFS_CAP_ACLS;
233
			/* fall through */
234 235 236
		case -ENOTSUPP:
			status = -EOPNOTSUPP;
	}
237
	nfs_free_fattr(fattr);
T
Trond Myklebust 已提交
238 239 240 241 242
out_freepages:
	while (args.npages != 0) {
		args.npages--;
		__free_page(args.pages[args.npages]);
	}
243 244 245 246
out:
	return status;
}

247 248 249 250 251 252 253 254 255
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;

}

256
int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
257 258 259 260 261 262
{
	struct posix_acl *alloc = NULL, *dfacl = NULL;
	int status;

	if (S_ISDIR(inode->i_mode)) {
		switch(type) {
263 264 265 266 267 268 269 270 271 272 273 274
		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;
275
		}
276
	}
277 278 279 280 281 282

	if (acl == NULL) {
		alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
		if (IS_ERR(alloc))
			goto fail;
	}
283
	status = __nfs3_proc_setacls(inode, acl, dfacl);
284 285 286 287 288 289
	posix_acl_release(alloc);
	return status;

fail:
	return PTR_ERR(alloc);
}
290

291 292 293 294 295
const struct xattr_handler *nfs3_xattr_handlers[] = {
	&posix_acl_access_xattr_handler,
	&posix_acl_default_xattr_handler,
	NULL,
};
296 297 298 299 300 301 302 303 304

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);
305
	if (IS_ERR_OR_NULL(acl))
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
		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)
{
324
	struct inode *inode = d_inode(dentry);
325 326 327 328
	ssize_t result = 0;
	int error;

	error = nfs3_list_one_acl(inode, ACL_TYPE_ACCESS,
329
			XATTR_NAME_POSIX_ACL_ACCESS, data, size, &result);
330 331 332 333
	if (error)
		return error;

	error = nfs3_list_one_acl(inode, ACL_TYPE_DEFAULT,
334
			XATTR_NAME_POSIX_ACL_DEFAULT, data, size, &result);
335 336 337 338
	if (error)
		return error;
	return result;
}