acl.c 7.1 KB
Newer Older
D
David Teigland 已提交
1 2
/*
 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3
 * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
D
David Teigland 已提交
4 5 6
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
7
 * of the GNU General Public License version 2.
D
David Teigland 已提交
8 9 10 11 12 13 14
 */

#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
S
Steven Whitehouse 已提交
15
#include <linux/xattr.h>
D
David Teigland 已提交
16 17
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
18
#include <linux/gfs2_ondisk.h>
D
David Teigland 已提交
19 20

#include "gfs2.h"
21
#include "incore.h"
D
David Teigland 已提交
22
#include "acl.h"
23
#include "xattr.h"
D
David Teigland 已提交
24 25 26 27
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "trans.h"
28
#include "util.h"
D
David Teigland 已提交
29

S
Steven Whitehouse 已提交
30
static const char *gfs2_acl_name(int type)
D
David Teigland 已提交
31
{
S
Steven Whitehouse 已提交
32 33 34 35 36 37 38 39
	switch (type) {
	case ACL_TYPE_ACCESS:
		return GFS2_POSIX_ACL_ACCESS;
	case ACL_TYPE_DEFAULT:
		return GFS2_POSIX_ACL_DEFAULT;
	}
	return NULL;
}
D
David Teigland 已提交
40

S
Steven Whitehouse 已提交
41 42 43 44 45 46
static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
{
	struct posix_acl *acl;
	const char *name;
	char *data;
	int len;
47

48
	if (!ip->i_eattr)
S
Steven Whitehouse 已提交
49
		return NULL;
D
David Teigland 已提交
50

S
Steven Whitehouse 已提交
51 52 53 54
	acl = get_cached_acl(&ip->i_inode, type);
	if (acl != ACL_NOT_CACHED)
		return acl;

S
Steven Whitehouse 已提交
55 56 57
	name = gfs2_acl_name(type);
	if (name == NULL)
		return ERR_PTR(-EINVAL);
D
David Teigland 已提交
58

S
Steven Whitehouse 已提交
59 60 61 62 63
	len = gfs2_xattr_acl_get(ip, name, &data);
	if (len < 0)
		return ERR_PTR(len);
	if (len == 0)
		return NULL;
D
David Teigland 已提交
64

S
Steven Whitehouse 已提交
65 66 67
	acl = posix_acl_from_xattr(data, len);
	kfree(data);
	return acl;
D
David Teigland 已提交
68 69 70
}

/**
S
Steven Whitehouse 已提交
71
 * gfs2_check_acl - Check an ACL to see if we're allowed to do something
D
David Teigland 已提交
72 73 74 75 76 77
 * @inode: the file we want to do something to
 * @mask: what we want to do
 *
 * Returns: errno
 */

78
int gfs2_check_acl(struct inode *inode, int mask)
D
David Teigland 已提交
79
{
S
Steven Whitehouse 已提交
80
	struct posix_acl *acl;
D
David Teigland 已提交
81 82
	int error;

83
	if (mask & MAY_NOT_BLOCK) {
84 85 86 87
		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
			return -ECHILD;
		return -EAGAIN;
	}
88

S
Steven Whitehouse 已提交
89 90 91
	acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
	if (IS_ERR(acl))
		return PTR_ERR(acl);
D
David Teigland 已提交
92 93 94 95 96 97 98 99 100 101

	if (acl) {
		error = posix_acl_permission(inode, acl, mask);
		posix_acl_release(acl);
		return error;
	}

	return -EAGAIN;
}

102
static int gfs2_set_mode(struct inode *inode, mode_t mode)
D
David Teigland 已提交
103
{
104
	int error = 0;
D
David Teigland 已提交
105

106 107
	if (mode != inode->i_mode) {
		struct iattr iattr;
D
David Teigland 已提交
108

109 110
		iattr.ia_valid = ATTR_MODE;
		iattr.ia_mode = mode;
D
David Teigland 已提交
111

112 113
		error = gfs2_setattr_simple(GFS2_I(inode), &iattr);
	}
D
David Teigland 已提交
114

115
	return error;
D
David Teigland 已提交
116 117
}

S
Steven Whitehouse 已提交
118
static int gfs2_acl_set(struct inode *inode, int type, struct posix_acl *acl)
D
David Teigland 已提交
119 120
{
	int error;
S
Steven Whitehouse 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134
	int len;
	char *data;
	const char *name = gfs2_acl_name(type);

	BUG_ON(name == NULL);
	len = posix_acl_to_xattr(acl, NULL, 0);
	if (len == 0)
		return 0;
	data = kmalloc(len, GFP_NOFS);
	if (data == NULL)
		return -ENOMEM;
	error = posix_acl_to_xattr(acl, data, len);
	if (error < 0)
		goto out;
135
	error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS);
S
Steven Whitehouse 已提交
136 137
	if (!error)
		set_cached_acl(inode, type, acl);
S
Steven Whitehouse 已提交
138 139 140 141 142 143 144 145 146 147 148
out:
	kfree(data);
	return error;
}

int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode)
{
	struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
	struct posix_acl *acl, *clone;
	mode_t mode = inode->i_mode;
	int error = 0;
D
David Teigland 已提交
149 150 151

	if (!sdp->sd_args.ar_posix_acl)
		return 0;
S
Steven Whitehouse 已提交
152
	if (S_ISLNK(inode->i_mode))
D
David Teigland 已提交
153 154
		return 0;

S
Steven Whitehouse 已提交
155 156 157
	acl = gfs2_acl_get(dip, ACL_TYPE_DEFAULT);
	if (IS_ERR(acl))
		return PTR_ERR(acl);
D
David Teigland 已提交
158
	if (!acl) {
A
Al Viro 已提交
159
		mode &= ~current_umask();
S
Steven Whitehouse 已提交
160 161
		if (mode != inode->i_mode)
			error = gfs2_set_mode(inode, mode);
D
David Teigland 已提交
162 163 164
		return error;
	}

S
Steven Whitehouse 已提交
165 166 167 168 169 170
	if (S_ISDIR(inode->i_mode)) {
		error = gfs2_acl_set(inode, ACL_TYPE_DEFAULT, acl);
		if (error)
			goto out;
	}

J
Josef Bacik 已提交
171
	clone = posix_acl_clone(acl, GFP_NOFS);
D
David Teigland 已提交
172 173 174 175 176 177 178 179 180
	error = -ENOMEM;
	if (!clone)
		goto out;
	posix_acl_release(acl);
	acl = clone;

	error = posix_acl_create_masq(acl, &mode);
	if (error < 0)
		goto out;
181 182
	if (error == 0)
		goto munge;
D
David Teigland 已提交
183

S
Steven Whitehouse 已提交
184
	error = gfs2_acl_set(inode, ACL_TYPE_ACCESS, acl);
185 186 187
	if (error)
		goto out;
munge:
S
Steven Whitehouse 已提交
188
	error = gfs2_set_mode(inode, mode);
189
out:
D
David Teigland 已提交
190 191 192 193 194 195
	posix_acl_release(acl);
	return error;
}

int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
{
S
Steven Whitehouse 已提交
196
	struct posix_acl *acl, *clone;
D
David Teigland 已提交
197 198 199 200
	char *data;
	unsigned int len;
	int error;

S
Steven Whitehouse 已提交
201 202 203
	acl = gfs2_acl_get(ip, ACL_TYPE_ACCESS);
	if (IS_ERR(acl))
		return PTR_ERR(acl);
D
David Teigland 已提交
204 205 206
	if (!acl)
		return gfs2_setattr_simple(ip, attr);

J
Josef Bacik 已提交
207
	clone = posix_acl_clone(acl, GFP_NOFS);
D
David Teigland 已提交
208 209 210 211 212 213 214 215
	error = -ENOMEM;
	if (!clone)
		goto out;
	posix_acl_release(acl);
	acl = clone;

	error = posix_acl_chmod_masq(acl, attr->ia_mode);
	if (!error) {
S
Steven Whitehouse 已提交
216 217 218 219 220
		len = posix_acl_to_xattr(acl, NULL, 0);
		data = kmalloc(len, GFP_NOFS);
		error = -ENOMEM;
		if (data == NULL)
			goto out;
D
David Teigland 已提交
221
		posix_acl_to_xattr(acl, data, len);
S
Steven Whitehouse 已提交
222 223
		error = gfs2_xattr_acl_chmod(ip, attr, data);
		kfree(data);
S
Steven Whitehouse 已提交
224
		set_cached_acl(&ip->i_inode, ACL_TYPE_ACCESS, acl);
D
David Teigland 已提交
225 226
	}

227
out:
D
David Teigland 已提交
228 229 230 231
	posix_acl_release(acl);
	return error;
}

S
Steven Whitehouse 已提交
232 233 234 235 236 237 238 239 240
static int gfs2_acl_type(const char *name)
{
	if (strcmp(name, GFS2_POSIX_ACL_ACCESS) == 0)
		return ACL_TYPE_ACCESS;
	if (strcmp(name, GFS2_POSIX_ACL_DEFAULT) == 0)
		return ACL_TYPE_DEFAULT;
	return -EINVAL;
}

241 242
static int gfs2_xattr_system_get(struct dentry *dentry, const char *name,
				 void *buffer, size_t size, int xtype)
S
Steven Whitehouse 已提交
243
{
244
	struct inode *inode = dentry->d_inode;
245
	struct gfs2_sbd *sdp = GFS2_SB(inode);
S
Steven Whitehouse 已提交
246
	struct posix_acl *acl;
S
Steven Whitehouse 已提交
247
	int type;
S
Steven Whitehouse 已提交
248
	int error;
S
Steven Whitehouse 已提交
249

250 251 252
	if (!sdp->sd_args.ar_posix_acl)
		return -EOPNOTSUPP;

S
Steven Whitehouse 已提交
253 254 255 256
	type = gfs2_acl_type(name);
	if (type < 0)
		return type;

S
Steven Whitehouse 已提交
257 258 259 260 261
	acl = gfs2_acl_get(GFS2_I(inode), type);
	if (IS_ERR(acl))
		return PTR_ERR(acl);
	if (acl == NULL)
		return -ENODATA;
S
Steven Whitehouse 已提交
262

S
Steven Whitehouse 已提交
263 264 265 266 267
	error = posix_acl_to_xattr(acl, buffer, size);
	posix_acl_release(acl);

	return error;
}
S
Steven Whitehouse 已提交
268

269 270 271
static int gfs2_xattr_system_set(struct dentry *dentry, const char *name,
				 const void *value, size_t size, int flags,
				 int xtype)
S
Steven Whitehouse 已提交
272
{
273
	struct inode *inode = dentry->d_inode;
S
Steven Whitehouse 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	struct posix_acl *acl = NULL;
	int error = 0, type;

	if (!sdp->sd_args.ar_posix_acl)
		return -EOPNOTSUPP;

	type = gfs2_acl_type(name);
	if (type < 0)
		return type;
	if (flags & XATTR_CREATE)
		return -EINVAL;
	if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
		return value ? -EACCES : 0;
	if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER))
		return -EPERM;
	if (S_ISLNK(inode->i_mode))
		return -EOPNOTSUPP;

	if (!value)
		goto set_acl;

	acl = posix_acl_from_xattr(value, size);
	if (!acl) {
		/*
		 * acl_set_file(3) may request that we set default ACLs with
		 * zero length -- defend (gracefully) against that here.
		 */
		goto out;
	}
	if (IS_ERR(acl)) {
		error = PTR_ERR(acl);
		goto out;
	}

	error = posix_acl_valid(acl);
	if (error)
		goto out_release;

	error = -EINVAL;
	if (acl->a_count > GFS2_ACL_MAX_ENTRIES)
		goto out_release;

	if (type == ACL_TYPE_ACCESS) {
		mode_t mode = inode->i_mode;
		error = posix_acl_equiv_mode(acl, &mode);

		if (error <= 0) {
			posix_acl_release(acl);
			acl = NULL;

			if (error < 0)
				return error;
		}

		error = gfs2_set_mode(inode, mode);
		if (error)
			goto out_release;
	}

set_acl:
335
	error = __gfs2_xattr_set(inode, name, value, size, 0, GFS2_EATYPE_SYS);
S
Steven Whitehouse 已提交
336 337 338 339 340 341
	if (!error) {
		if (acl)
			set_cached_acl(inode, type, acl);
		else
			forget_cached_acl(inode, type);
	}
S
Steven Whitehouse 已提交
342 343 344 345 346 347
out_release:
	posix_acl_release(acl);
out:
	return error;
}

S
Stephen Hemminger 已提交
348
const struct xattr_handler gfs2_xattr_system_handler = {
S
Steven Whitehouse 已提交
349
	.prefix = XATTR_SYSTEM_PREFIX,
350
	.flags  = GFS2_EATYPE_SYS,
S
Steven Whitehouse 已提交
351 352 353 354
	.get    = gfs2_xattr_system_get,
	.set    = gfs2_xattr_system_set,
};