1/*
2 * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
3 *
4 * This file is released under the GPL.
5 *
6 * Generic ACL support for in-memory filesystems.
7 */
8
9#include <linux/sched.h>
10#include <linux/gfp.h>
11#include <linux/fs.h>
12#include <linux/generic_acl.h>
13#include <linux/posix_acl.h>
14#include <linux/posix_acl_xattr.h>
15
16
17static size_t
18generic_acl_list(struct dentry *dentry, char *list, size_t list_size,
19		const char *name, size_t name_len, int type)
20{
21	struct posix_acl *acl;
22	const char *xname;
23	size_t size;
24
25	acl = get_cached_acl(dentry->d_inode, type);
26	if (!acl)
27		return 0;
28	posix_acl_release(acl);
29
30	switch (type) {
31	case ACL_TYPE_ACCESS:
32		xname = POSIX_ACL_XATTR_ACCESS;
33		break;
34	case ACL_TYPE_DEFAULT:
35		xname = POSIX_ACL_XATTR_DEFAULT;
36		break;
37	default:
38		return 0;
39	}
40	size = strlen(xname) + 1;
41	if (list && size <= list_size)
42		memcpy(list, xname, size);
43	return size;
44}
45
46static int
47generic_acl_get(struct dentry *dentry, const char *name, void *buffer,
48		     size_t size, int type)
49{
50	struct posix_acl *acl;
51	int error;
52
53	if (strcmp(name, "") != 0)
54		return -EINVAL;
55
56	acl = get_cached_acl(dentry->d_inode, type);
57	if (!acl)
58		return -ENODATA;
59	error = posix_acl_to_xattr(acl, buffer, size);
60	posix_acl_release(acl);
61
62	return error;
63}
64
65static int
66generic_acl_set(struct dentry *dentry, const char *name, const void *value,
67		     size_t size, int flags, int type)
68{
69	struct inode *inode = dentry->d_inode;
70	struct posix_acl *acl = NULL;
71	int error;
72
73	if (strcmp(name, "") != 0)
74		return -EINVAL;
75	if (S_ISLNK(inode->i_mode))
76		return -EOPNOTSUPP;
77	if (!is_owner_or_cap(inode))
78		return -EPERM;
79	if (value) {
80		acl = posix_acl_from_xattr(value, size);
81		if (IS_ERR(acl))
82			return PTR_ERR(acl);
83	}
84	if (acl) {
85		mode_t mode;
86
87		error = posix_acl_valid(acl);
88		if (error)
89			goto failed;
90		switch (type) {
91		case ACL_TYPE_ACCESS:
92			mode = inode->i_mode;
93			error = posix_acl_equiv_mode(acl, &mode);
94			if (error < 0)
95				goto failed;
96			inode->i_mode = mode;
97			inode->i_ctime = CURRENT_TIME;
98			if (error == 0) {
99				posix_acl_release(acl);
100				acl = NULL;
101			}
102			break;
103		case ACL_TYPE_DEFAULT:
104			if (!S_ISDIR(inode->i_mode)) {
105				error = -EINVAL;
106				goto failed;
107			}
108			break;
109		}
110	}
111	set_cached_acl(inode, type, acl);
112	error = 0;
113failed:
114	posix_acl_release(acl);
115	return error;
116}
117
118/**
119 * generic_acl_init  -  Take care of acl inheritance at @inode create time
120 *
121 * Files created inside a directory with a default ACL inherit the
122 * directory's default ACL.
123 */
124int
125generic_acl_init(struct inode *inode, struct inode *dir)
126{
127	struct posix_acl *acl = NULL;
128	mode_t mode = inode->i_mode;
129	int error;
130
131	inode->i_mode = mode & ~current_umask();
132	if (!S_ISLNK(inode->i_mode))
133		acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
134	if (acl) {
135		struct posix_acl *clone;
136
137		if (S_ISDIR(inode->i_mode)) {
138			clone = posix_acl_clone(acl, GFP_KERNEL);
139			error = -ENOMEM;
140			if (!clone)
141				goto cleanup;
142			set_cached_acl(inode, ACL_TYPE_DEFAULT, clone);
143			posix_acl_release(clone);
144		}
145		clone = posix_acl_clone(acl, GFP_KERNEL);
146		error = -ENOMEM;
147		if (!clone)
148			goto cleanup;
149		error = posix_acl_create_masq(clone, &mode);
150		if (error >= 0) {
151			inode->i_mode = mode;
152			if (error > 0)
153				set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
154		}
155		posix_acl_release(clone);
156	}
157	error = 0;
158
159cleanup:
160	posix_acl_release(acl);
161	return error;
162}
163
164/**
165 * generic_acl_chmod  -  change the access acl of @inode upon chmod()
166 *
167 * A chmod also changes the permissions of the owner, group/mask, and
168 * other ACL entries.
169 */
170int
171generic_acl_chmod(struct inode *inode)
172{
173	struct posix_acl *acl, *clone;
174	int error = 0;
175
176	if (S_ISLNK(inode->i_mode))
177		return -EOPNOTSUPP;
178	acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
179	if (acl) {
180		clone = posix_acl_clone(acl, GFP_KERNEL);
181		posix_acl_release(acl);
182		if (!clone)
183			return -ENOMEM;
184		error = posix_acl_chmod_masq(clone, inode->i_mode);
185		if (!error)
186			set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
187		posix_acl_release(clone);
188	}
189	return error;
190}
191
192int
193generic_check_acl(struct inode *inode, int mask)
194{
195	struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
196
197	if (acl) {
198		int error = posix_acl_permission(inode, acl, mask);
199		posix_acl_release(acl);
200		return error;
201	}
202	return -EAGAIN;
203}
204
205const struct xattr_handler generic_acl_access_handler = {
206	.prefix = POSIX_ACL_XATTR_ACCESS,
207	.flags	= ACL_TYPE_ACCESS,
208	.list	= generic_acl_list,
209	.get	= generic_acl_get,
210	.set	= generic_acl_set,
211};
212
213const struct xattr_handler generic_acl_default_handler = {
214	.prefix = POSIX_ACL_XATTR_DEFAULT,
215	.flags	= ACL_TYPE_DEFAULT,
216	.list	= generic_acl_list,
217	.get	= generic_acl_get,
218	.set	= generic_acl_set,
219};
220