acl.c 5.54 KB
Newer Older
David Teigland's avatar
David Teigland committed
1
2
/*
 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3
 * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
David Teigland's avatar
David Teigland committed
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.
David Teigland's avatar
David Teigland committed
8
9
10
11
12
13
14
15
16
 */

#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
17
#include <linux/gfs2_ondisk.h>
David Teigland's avatar
David Teigland committed
18
19

#include "gfs2.h"
20
#include "incore.h"
David Teigland's avatar
David Teigland committed
21
#include "acl.h"
22
#include "xattr.h"
David Teigland's avatar
David Teigland committed
23
24
25
26
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "trans.h"
27
#include "util.h"
David Teigland's avatar
David Teigland committed
28
29
30
31
32

#define ACL_ACCESS 1
#define ACL_DEFAULT 0

int gfs2_acl_validate_set(struct gfs2_inode *ip, int access,
33
			  struct gfs2_ea_request *er, int *remove, mode_t *mode)
David Teigland's avatar
David Teigland committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{
	struct posix_acl *acl;
	int error;

	error = gfs2_acl_validate_remove(ip, access);
	if (error)
		return error;

	if (!er->er_data)
		return -EINVAL;

	acl = posix_acl_from_xattr(er->er_data, er->er_data_len);
	if (IS_ERR(acl))
		return PTR_ERR(acl);
	if (!acl) {
		*remove = 1;
		return 0;
	}

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

	if (access) {
		error = posix_acl_equiv_mode(acl, mode);
		if (!error)
			*remove = 1;
		else if (error > 0)
			error = 0;
	}

65
out:
David Teigland's avatar
David Teigland committed
66
67
68
69
70
71
	posix_acl_release(acl);
	return error;
}

int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access)
{
72
	if (!GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl)
David Teigland's avatar
David Teigland committed
73
		return -EOPNOTSUPP;
74
	if (!is_owner_or_cap(&ip->i_inode))
David Teigland's avatar
David Teigland committed
75
		return -EPERM;
76
	if (S_ISLNK(ip->i_inode.i_mode))
David Teigland's avatar
David Teigland committed
77
		return -EOPNOTSUPP;
78
	if (!access && !S_ISDIR(ip->i_inode.i_mode))
David Teigland's avatar
David Teigland committed
79
80
81
82
83
		return -EACCES;

	return 0;
}

84
85
86
static int acl_get(struct gfs2_inode *ip, const char *name,
		   struct posix_acl **acl, struct gfs2_ea_location *el,
		   char **datap, unsigned int *lenp)
David Teigland's avatar
David Teigland committed
87
{
88
89
	char *data;
	unsigned int len;
David Teigland's avatar
David Teigland committed
90
91
	int error;

92
93
	el->el_bh = NULL;

94
	if (!ip->i_eattr)
David Teigland's avatar
David Teigland committed
95
96
		return 0;

97
	error = gfs2_ea_find(ip, GFS2_EATYPE_SYS, name, el);
David Teigland's avatar
David Teigland committed
98
99
100
101
102
103
104
	if (error)
		return error;
	if (!el->el_ea)
		return 0;
	if (!GFS2_EA_DATA_LEN(el->el_ea))
		goto out;

105
106
	len = GFS2_EA_DATA_LEN(el->el_ea);
	data = kmalloc(len, GFP_NOFS);
David Teigland's avatar
David Teigland committed
107
	error = -ENOMEM;
108
	if (!data)
David Teigland's avatar
David Teigland committed
109
110
		goto out;

111
112
	error = gfs2_ea_get_copy(ip, el, data, len);
	if (error < 0)
David Teigland's avatar
David Teigland committed
113
		goto out_kfree;
114
	error = 0;
David Teigland's avatar
David Teigland committed
115
116

	if (acl) {
117
		*acl = posix_acl_from_xattr(data, len);
David Teigland's avatar
David Teigland committed
118
119
120
121
		if (IS_ERR(*acl))
			error = PTR_ERR(*acl);
	}

122
out_kfree:
123
124
125
126
127
	if (error || !datap) {
		kfree(data);
	} else {
		*datap = data;
		*lenp = len;
David Teigland's avatar
David Teigland committed
128
	}
129
out:
David Teigland's avatar
David Teigland committed
130
131
132
133
	return error;
}

/**
134
 * gfs2_check_acl - Check an ACL to see if we're allowed to do something
David Teigland's avatar
David Teigland committed
135
136
137
138
139
140
 * @inode: the file we want to do something to
 * @mask: what we want to do
 *
 * Returns: errno
 */

141
int gfs2_check_acl(struct inode *inode, int mask)
David Teigland's avatar
David Teigland committed
142
{
143
	struct gfs2_ea_location el;
David Teigland's avatar
David Teigland committed
144
145
146
	struct posix_acl *acl = NULL;
	int error;

147
148
	error = acl_get(GFS2_I(inode), GFS2_POSIX_ACL_ACCESS, &acl, &el, NULL, NULL);
	brelse(el.el_bh);
David Teigland's avatar
David Teigland committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
	if (error)
		return error;

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

	return -EAGAIN;
}

static int munge_mode(struct gfs2_inode *ip, mode_t mode)
{
163
	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
David Teigland's avatar
David Teigland committed
164
165
166
167
168
169
170
171
172
173
	struct buffer_head *dibh;
	int error;

	error = gfs2_trans_begin(sdp, RES_DINODE, 0);
	if (error)
		return error;

	error = gfs2_meta_inode_buffer(ip, &dibh);
	if (!error) {
		gfs2_assert_withdraw(sdp,
174
175
				(ip->i_inode.i_mode & S_IFMT) == (mode & S_IFMT));
		ip->i_inode.i_mode = mode;
176
		gfs2_trans_add_bh(ip->i_gl, dibh, 1);
177
		gfs2_dinode_out(ip, dibh->b_data);
David Teigland's avatar
David Teigland committed
178
179
180
181
182
183
184
185
186
187
		brelse(dibh);
	}

	gfs2_trans_end(sdp);

	return 0;
}

int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
{
188
	struct gfs2_ea_location el;
189
	struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
David Teigland's avatar
David Teigland committed
190
	struct posix_acl *acl = NULL, *clone;
191
	mode_t mode = ip->i_inode.i_mode;
192
193
	char *data = NULL;
	unsigned int len;
David Teigland's avatar
David Teigland committed
194
195
196
197
	int error;

	if (!sdp->sd_args.ar_posix_acl)
		return 0;
198
	if (S_ISLNK(ip->i_inode.i_mode))
David Teigland's avatar
David Teigland committed
199
200
		return 0;

201
202
	error = acl_get(dip, GFS2_POSIX_ACL_DEFAULT, &acl, &el, &data, &len);
	brelse(el.el_bh);
David Teigland's avatar
David Teigland committed
203
204
205
	if (error)
		return error;
	if (!acl) {
Al Viro's avatar
Al Viro committed
206
		mode &= ~current_umask();
207
		if (mode != ip->i_inode.i_mode)
David Teigland's avatar
David Teigland committed
208
209
210
211
			error = munge_mode(ip, mode);
		return error;
	}

Josef Bacik's avatar
Josef Bacik committed
212
	clone = posix_acl_clone(acl, GFP_NOFS);
David Teigland's avatar
David Teigland committed
213
214
215
216
217
218
	error = -ENOMEM;
	if (!clone)
		goto out;
	posix_acl_release(acl);
	acl = clone;

219
	if (S_ISDIR(ip->i_inode.i_mode)) {
220
221
		error = gfs2_xattr_set(&ip->i_inode, GFS2_EATYPE_SYS,
				       GFS2_POSIX_ACL_DEFAULT, data, len, 0);
David Teigland's avatar
David Teigland committed
222
223
224
225
226
227
228
		if (error)
			goto out;
	}

	error = posix_acl_create_masq(acl, &mode);
	if (error < 0)
		goto out;
229
230
	if (error == 0)
		goto munge;
David Teigland's avatar
David Teigland committed
231

232
233
234
235
236
237
238
	posix_acl_to_xattr(acl, data, len);
	error = gfs2_xattr_set(&ip->i_inode, GFS2_EATYPE_SYS,
			       GFS2_POSIX_ACL_ACCESS, data, len, 0);
	if (error)
		goto out;
munge:
	error = munge_mode(ip, mode);
239
out:
David Teigland's avatar
David Teigland committed
240
	posix_acl_release(acl);
241
	kfree(data);
David Teigland's avatar
David Teigland committed
242
243
244
245
246
247
248
249
250
251
252
	return error;
}

int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
{
	struct posix_acl *acl = NULL, *clone;
	struct gfs2_ea_location el;
	char *data;
	unsigned int len;
	int error;

253
	error = acl_get(ip, GFS2_POSIX_ACL_ACCESS, &acl, &el, &data, &len);
David Teigland's avatar
David Teigland committed
254
	if (error)
255
		goto out_brelse;
David Teigland's avatar
David Teigland committed
256
257
258
	if (!acl)
		return gfs2_setattr_simple(ip, attr);

Josef Bacik's avatar
Josef Bacik committed
259
	clone = posix_acl_clone(acl, GFP_NOFS);
David Teigland's avatar
David Teigland committed
260
261
262
263
264
265
266
267
268
269
270
271
	error = -ENOMEM;
	if (!clone)
		goto out;
	posix_acl_release(acl);
	acl = clone;

	error = posix_acl_chmod_masq(acl, attr->ia_mode);
	if (!error) {
		posix_acl_to_xattr(acl, data, len);
		error = gfs2_ea_acl_chmod(ip, &el, attr, data);
	}

272
out:
David Teigland's avatar
David Teigland committed
273
274
	posix_acl_release(acl);
	kfree(data);
275
276
out_brelse:
	brelse(el.el_bh);
David Teigland's avatar
David Teigland committed
277
278
279
	return error;
}