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