~ubuntu-branches/ubuntu/wily/linux-ti-omap/wily

1 by Amit Kucheria, Amit Kucheria
[ Amit Kucheria ]
1
/*
2
 * linux/fs/posix_acl.c
3
 *
4
 *  Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org>
5
 *
6
 *  Fixes from William Schumacher incorporated on 15 March 2001.
7
 *     (Reported by Charles Bertsch, <CBertsch@microtest.com>).
8
 */
9
10
/*
11
 *  This file contains generic functions for manipulating
12
 *  POSIX 1003.1e draft standard 17 ACLs.
13
 */
14
15
#include <linux/kernel.h>
16
#include <linux/slab.h>
17
#include <asm/atomic.h>
18
#include <linux/fs.h>
19
#include <linux/sched.h>
20
#include <linux/posix_acl.h>
21
#include <linux/module.h>
22
23
#include <linux/errno.h>
24
25
EXPORT_SYMBOL(posix_acl_alloc);
26
EXPORT_SYMBOL(posix_acl_clone);
27
EXPORT_SYMBOL(posix_acl_valid);
28
EXPORT_SYMBOL(posix_acl_equiv_mode);
29
EXPORT_SYMBOL(posix_acl_from_mode);
30
EXPORT_SYMBOL(posix_acl_create_masq);
31
EXPORT_SYMBOL(posix_acl_chmod_masq);
32
EXPORT_SYMBOL(posix_acl_permission);
33
34
/*
35
 * Allocate a new ACL with the specified number of entries.
36
 */
37
struct posix_acl *
38
posix_acl_alloc(int count, gfp_t flags)
39
{
40
	const size_t size = sizeof(struct posix_acl) +
41
	                    count * sizeof(struct posix_acl_entry);
42
	struct posix_acl *acl = kmalloc(size, flags);
43
	if (acl) {
44
		atomic_set(&acl->a_refcount, 1);
45
		acl->a_count = count;
46
	}
47
	return acl;
48
}
49
50
/*
51
 * Clone an ACL.
52
 */
53
struct posix_acl *
54
posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
55
{
56
	struct posix_acl *clone = NULL;
57
58
	if (acl) {
59
		int size = sizeof(struct posix_acl) + acl->a_count *
60
		           sizeof(struct posix_acl_entry);
61
		clone = kmemdup(acl, size, flags);
62
		if (clone)
63
			atomic_set(&clone->a_refcount, 1);
64
	}
65
	return clone;
66
}
67
68
/*
69
 * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
70
 */
71
int
72
posix_acl_valid(const struct posix_acl *acl)
73
{
74
	const struct posix_acl_entry *pa, *pe;
75
	int state = ACL_USER_OBJ;
76
	unsigned int id = 0;  /* keep gcc happy */
77
	int needs_mask = 0;
78
79
	FOREACH_ACL_ENTRY(pa, acl, pe) {
80
		if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
81
			return -EINVAL;
82
		switch (pa->e_tag) {
83
			case ACL_USER_OBJ:
84
				if (state == ACL_USER_OBJ) {
85
					id = 0;
86
					state = ACL_USER;
87
					break;
88
				}
89
				return -EINVAL;
90
91
			case ACL_USER:
92
				if (state != ACL_USER)
93
					return -EINVAL;
94
				if (pa->e_id == ACL_UNDEFINED_ID ||
95
				    pa->e_id < id)
96
					return -EINVAL;
97
				id = pa->e_id + 1;
98
				needs_mask = 1;
99
				break;
100
101
			case ACL_GROUP_OBJ:
102
				if (state == ACL_USER) {
103
					id = 0;
104
					state = ACL_GROUP;
105
					break;
106
				}
107
				return -EINVAL;
108
109
			case ACL_GROUP:
110
				if (state != ACL_GROUP)
111
					return -EINVAL;
112
				if (pa->e_id == ACL_UNDEFINED_ID ||
113
				    pa->e_id < id)
114
					return -EINVAL;
115
				id = pa->e_id + 1;
116
				needs_mask = 1;
117
				break;
118
119
			case ACL_MASK:
120
				if (state != ACL_GROUP)
121
					return -EINVAL;
122
				state = ACL_OTHER;
123
				break;
124
125
			case ACL_OTHER:
126
				if (state == ACL_OTHER ||
127
				    (state == ACL_GROUP && !needs_mask)) {
128
					state = 0;
129
					break;
130
				}
131
				return -EINVAL;
132
133
			default:
134
				return -EINVAL;
135
		}
136
	}
137
	if (state == 0)
138
		return 0;
139
	return -EINVAL;
140
}
141
142
/*
143
 * Returns 0 if the acl can be exactly represented in the traditional
144
 * file mode permission bits, or else 1. Returns -E... on error.
145
 */
146
int
147
posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
148
{
149
	const struct posix_acl_entry *pa, *pe;
150
	mode_t mode = 0;
151
	int not_equiv = 0;
152
153
	FOREACH_ACL_ENTRY(pa, acl, pe) {
154
		switch (pa->e_tag) {
155
			case ACL_USER_OBJ:
156
				mode |= (pa->e_perm & S_IRWXO) << 6;
157
				break;
158
			case ACL_GROUP_OBJ:
159
				mode |= (pa->e_perm & S_IRWXO) << 3;
160
				break;
161
			case ACL_OTHER:
162
				mode |= pa->e_perm & S_IRWXO;
163
				break;
164
			case ACL_MASK:
165
				mode = (mode & ~S_IRWXG) |
166
				       ((pa->e_perm & S_IRWXO) << 3);
167
				not_equiv = 1;
168
				break;
169
			case ACL_USER:
170
			case ACL_GROUP:
171
				not_equiv = 1;
172
				break;
173
			default:
174
				return -EINVAL;
175
		}
176
	}
177
        if (mode_p)
178
                *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
179
        return not_equiv;
180
}
181
182
/*
183
 * Create an ACL representing the file mode permission bits of an inode.
184
 */
185
struct posix_acl *
186
posix_acl_from_mode(mode_t mode, gfp_t flags)
187
{
188
	struct posix_acl *acl = posix_acl_alloc(3, flags);
189
	if (!acl)
190
		return ERR_PTR(-ENOMEM);
191
192
	acl->a_entries[0].e_tag  = ACL_USER_OBJ;
193
	acl->a_entries[0].e_id   = ACL_UNDEFINED_ID;
194
	acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
195
196
	acl->a_entries[1].e_tag  = ACL_GROUP_OBJ;
197
	acl->a_entries[1].e_id   = ACL_UNDEFINED_ID;
198
	acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
199
200
	acl->a_entries[2].e_tag  = ACL_OTHER;
201
	acl->a_entries[2].e_id   = ACL_UNDEFINED_ID;
202
	acl->a_entries[2].e_perm = (mode & S_IRWXO);
203
	return acl;
204
}
205
206
/*
207
 * Return 0 if current is granted want access to the inode
208
 * by the acl. Returns -E... otherwise.
209
 */
210
int
211
posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
212
{
213
	const struct posix_acl_entry *pa, *pe, *mask_obj;
214
	int found = 0;
215
216
	FOREACH_ACL_ENTRY(pa, acl, pe) {
217
                switch(pa->e_tag) {
218
                        case ACL_USER_OBJ:
219
				/* (May have been checked already) */
220
				if (inode->i_uid == current_fsuid())
221
                                        goto check_perm;
222
                                break;
223
                        case ACL_USER:
224
				if (pa->e_id == current_fsuid())
225
                                        goto mask;
226
				break;
227
                        case ACL_GROUP_OBJ:
228
                                if (in_group_p(inode->i_gid)) {
229
					found = 1;
230
					if ((pa->e_perm & want) == want)
231
						goto mask;
232
                                }
233
				break;
234
                        case ACL_GROUP:
235
                                if (in_group_p(pa->e_id)) {
236
					found = 1;
237
					if ((pa->e_perm & want) == want)
238
						goto mask;
239
                                }
240
                                break;
241
                        case ACL_MASK:
242
                                break;
243
                        case ACL_OTHER:
244
				if (found)
245
					return -EACCES;
246
				else
247
					goto check_perm;
248
			default:
249
				return -EIO;
250
                }
251
        }
252
	return -EIO;
253
254
mask:
255
	for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
256
		if (mask_obj->e_tag == ACL_MASK) {
257
			if ((pa->e_perm & mask_obj->e_perm & want) == want)
258
				return 0;
259
			return -EACCES;
260
		}
261
	}
262
263
check_perm:
264
	if ((pa->e_perm & want) == want)
265
		return 0;
266
	return -EACCES;
267
}
268
269
/*
270
 * Modify acl when creating a new inode. The caller must ensure the acl is
271
 * only referenced once.
272
 *
273
 * mode_p initially must contain the mode parameter to the open() / creat()
274
 * system calls. All permissions that are not granted by the acl are removed.
275
 * The permissions in the acl are changed to reflect the mode_p parameter.
276
 */
277
int
278
posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
279
{
280
	struct posix_acl_entry *pa, *pe;
281
	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
282
	mode_t mode = *mode_p;
283
	int not_equiv = 0;
284
285
	/* assert(atomic_read(acl->a_refcount) == 1); */
286
287
	FOREACH_ACL_ENTRY(pa, acl, pe) {
288
                switch(pa->e_tag) {
289
                        case ACL_USER_OBJ:
290
				pa->e_perm &= (mode >> 6) | ~S_IRWXO;
291
				mode &= (pa->e_perm << 6) | ~S_IRWXU;
292
				break;
293
294
			case ACL_USER:
295
			case ACL_GROUP:
296
				not_equiv = 1;
297
				break;
298
299
                        case ACL_GROUP_OBJ:
300
				group_obj = pa;
301
                                break;
302
303
                        case ACL_OTHER:
304
				pa->e_perm &= mode | ~S_IRWXO;
305
				mode &= pa->e_perm | ~S_IRWXO;
306
                                break;
307
308
                        case ACL_MASK:
309
				mask_obj = pa;
310
				not_equiv = 1;
311
                                break;
312
313
			default:
314
				return -EIO;
315
                }
316
        }
317
318
	if (mask_obj) {
319
		mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
320
		mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
321
	} else {
322
		if (!group_obj)
323
			return -EIO;
324
		group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
325
		mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
326
	}
327
328
	*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
329
        return not_equiv;
330
}
331
332
/*
333
 * Modify the ACL for the chmod syscall.
334
 */
335
int
336
posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)
337
{
338
	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
339
	struct posix_acl_entry *pa, *pe;
340
341
	/* assert(atomic_read(acl->a_refcount) == 1); */
342
343
	FOREACH_ACL_ENTRY(pa, acl, pe) {
344
		switch(pa->e_tag) {
345
			case ACL_USER_OBJ:
346
				pa->e_perm = (mode & S_IRWXU) >> 6;
347
				break;
348
349
			case ACL_USER:
350
			case ACL_GROUP:
351
				break;
352
353
			case ACL_GROUP_OBJ:
354
				group_obj = pa;
355
				break;
356
357
			case ACL_MASK:
358
				mask_obj = pa;
359
				break;
360
361
			case ACL_OTHER:
362
				pa->e_perm = (mode & S_IRWXO);
363
				break;
364
365
			default:
366
				return -EIO;
367
		}
368
	}
369
370
	if (mask_obj) {
371
		mask_obj->e_perm = (mode & S_IRWXG) >> 3;
372
	} else {
373
		if (!group_obj)
374
			return -EIO;
375
		group_obj->e_perm = (mode & S_IRWXG) >> 3;
376
	}
377
378
	return 0;
379
}