]>
Commit | Line | Data |
---|---|---|
b3b94faa DT |
1 | /* |
2 | * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. | |
3 | * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. | |
4 | * | |
5 | * This copyrighted material is made available to anyone wishing to use, | |
6 | * modify, copy, or redistribute it subject to the terms and conditions | |
7 | * of the GNU General Public License v.2. | |
8 | */ | |
9 | ||
10 | #include <linux/sched.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/spinlock.h> | |
13 | #include <linux/completion.h> | |
14 | #include <linux/buffer_head.h> | |
15 | #include <linux/posix_acl.h> | |
16 | #include <linux/posix_acl_xattr.h> | |
17 | #include <asm/semaphore.h> | |
18 | ||
19 | #include "gfs2.h" | |
20 | #include "acl.h" | |
21 | #include "eaops.h" | |
22 | #include "eattr.h" | |
23 | #include "glock.h" | |
24 | #include "inode.h" | |
25 | #include "meta_io.h" | |
26 | #include "trans.h" | |
27 | ||
28 | #define ACL_ACCESS 1 | |
29 | #define ACL_DEFAULT 0 | |
30 | ||
31 | int gfs2_acl_validate_set(struct gfs2_inode *ip, int access, | |
32 | struct gfs2_ea_request *er, | |
33 | int *remove, mode_t *mode) | |
34 | { | |
35 | struct posix_acl *acl; | |
36 | int error; | |
37 | ||
38 | error = gfs2_acl_validate_remove(ip, access); | |
39 | if (error) | |
40 | return error; | |
41 | ||
42 | if (!er->er_data) | |
43 | return -EINVAL; | |
44 | ||
45 | acl = posix_acl_from_xattr(er->er_data, er->er_data_len); | |
46 | if (IS_ERR(acl)) | |
47 | return PTR_ERR(acl); | |
48 | if (!acl) { | |
49 | *remove = 1; | |
50 | return 0; | |
51 | } | |
52 | ||
53 | error = posix_acl_valid(acl); | |
54 | if (error) | |
55 | goto out; | |
56 | ||
57 | if (access) { | |
58 | error = posix_acl_equiv_mode(acl, mode); | |
59 | if (!error) | |
60 | *remove = 1; | |
61 | else if (error > 0) | |
62 | error = 0; | |
63 | } | |
64 | ||
65 | out: | |
66 | posix_acl_release(acl); | |
67 | ||
68 | return error; | |
69 | } | |
70 | ||
71 | int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access) | |
72 | { | |
73 | if (!ip->i_sbd->sd_args.ar_posix_acl) | |
74 | return -EOPNOTSUPP; | |
75 | if (current->fsuid != ip->i_di.di_uid && !capable(CAP_FOWNER)) | |
76 | return -EPERM; | |
77 | if (S_ISLNK(ip->i_di.di_mode)) | |
78 | return -EOPNOTSUPP; | |
79 | if (!access && !S_ISDIR(ip->i_di.di_mode)) | |
80 | return -EACCES; | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl, | |
86 | struct gfs2_ea_location *el, char **data, unsigned int *len) | |
87 | { | |
88 | struct gfs2_ea_request er; | |
89 | struct gfs2_ea_location el_this; | |
90 | int error; | |
91 | ||
92 | if (!ip->i_di.di_eattr) | |
93 | return 0; | |
94 | ||
95 | memset(&er, 0, sizeof(struct gfs2_ea_request)); | |
96 | if (access) { | |
97 | er.er_name = GFS2_POSIX_ACL_ACCESS; | |
98 | er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN; | |
99 | } else { | |
100 | er.er_name = GFS2_POSIX_ACL_DEFAULT; | |
101 | er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN; | |
102 | } | |
103 | er.er_type = GFS2_EATYPE_SYS; | |
104 | ||
105 | if (!el) | |
106 | el = &el_this; | |
107 | ||
108 | error = gfs2_ea_find(ip, &er, el); | |
109 | if (error) | |
110 | return error; | |
111 | if (!el->el_ea) | |
112 | return 0; | |
113 | if (!GFS2_EA_DATA_LEN(el->el_ea)) | |
114 | goto out; | |
115 | ||
116 | er.er_data_len = GFS2_EA_DATA_LEN(el->el_ea); | |
117 | er.er_data = kmalloc(er.er_data_len, GFP_KERNEL); | |
118 | error = -ENOMEM; | |
119 | if (!er.er_data) | |
120 | goto out; | |
121 | ||
122 | error = gfs2_ea_get_copy(ip, el, er.er_data); | |
123 | if (error) | |
124 | goto out_kfree; | |
125 | ||
126 | if (acl) { | |
127 | *acl = posix_acl_from_xattr(er.er_data, er.er_data_len); | |
128 | if (IS_ERR(*acl)) | |
129 | error = PTR_ERR(*acl); | |
130 | } | |
131 | ||
132 | out_kfree: | |
133 | if (error || !data) | |
134 | kfree(er.er_data); | |
135 | else { | |
136 | *data = er.er_data; | |
137 | *len = er.er_data_len; | |
138 | } | |
139 | ||
140 | out: | |
141 | if (error || el == &el_this) | |
142 | brelse(el->el_bh); | |
143 | ||
144 | return error; | |
145 | } | |
146 | ||
147 | /** | |
148 | * gfs2_check_acl_locked - Check an ACL to see if we're allowed to do something | |
149 | * @inode: the file we want to do something to | |
150 | * @mask: what we want to do | |
151 | * | |
152 | * Returns: errno | |
153 | */ | |
154 | ||
155 | int gfs2_check_acl_locked(struct inode *inode, int mask) | |
156 | { | |
157 | struct posix_acl *acl = NULL; | |
158 | int error; | |
159 | ||
160 | error = acl_get(get_v2ip(inode), ACL_ACCESS, &acl, NULL, NULL, NULL); | |
161 | if (error) | |
162 | return error; | |
163 | ||
164 | if (acl) { | |
165 | error = posix_acl_permission(inode, acl, mask); | |
166 | posix_acl_release(acl); | |
167 | return error; | |
168 | } | |
169 | ||
170 | return -EAGAIN; | |
171 | } | |
172 | ||
173 | int gfs2_check_acl(struct inode *inode, int mask) | |
174 | { | |
175 | struct gfs2_inode *ip = get_v2ip(inode); | |
176 | struct gfs2_holder i_gh; | |
177 | int error; | |
178 | ||
179 | error = gfs2_glock_nq_init(ip->i_gl, | |
180 | LM_ST_SHARED, LM_FLAG_ANY, | |
181 | &i_gh); | |
182 | if (!error) { | |
183 | error = gfs2_check_acl_locked(inode, mask); | |
184 | gfs2_glock_dq_uninit(&i_gh); | |
185 | } | |
186 | ||
187 | return error; | |
188 | } | |
189 | ||
190 | static int munge_mode(struct gfs2_inode *ip, mode_t mode) | |
191 | { | |
192 | struct gfs2_sbd *sdp = ip->i_sbd; | |
193 | struct buffer_head *dibh; | |
194 | int error; | |
195 | ||
196 | error = gfs2_trans_begin(sdp, RES_DINODE, 0); | |
197 | if (error) | |
198 | return error; | |
199 | ||
200 | error = gfs2_meta_inode_buffer(ip, &dibh); | |
201 | if (!error) { | |
202 | gfs2_assert_withdraw(sdp, | |
203 | (ip->i_di.di_mode & S_IFMT) == (mode & S_IFMT)); | |
204 | ip->i_di.di_mode = mode; | |
d4e9c4c3 | 205 | gfs2_trans_add_bh(ip->i_gl, dibh, 1); |
b3b94faa DT |
206 | gfs2_dinode_out(&ip->i_di, dibh->b_data); |
207 | brelse(dibh); | |
208 | } | |
209 | ||
210 | gfs2_trans_end(sdp); | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip) | |
216 | { | |
217 | struct gfs2_sbd *sdp = dip->i_sbd; | |
218 | struct posix_acl *acl = NULL, *clone; | |
219 | struct gfs2_ea_request er; | |
220 | mode_t mode = ip->i_di.di_mode; | |
221 | int error; | |
222 | ||
223 | if (!sdp->sd_args.ar_posix_acl) | |
224 | return 0; | |
225 | if (S_ISLNK(ip->i_di.di_mode)) | |
226 | return 0; | |
227 | ||
228 | memset(&er, 0, sizeof(struct gfs2_ea_request)); | |
229 | er.er_type = GFS2_EATYPE_SYS; | |
230 | ||
231 | error = acl_get(dip, ACL_DEFAULT, &acl, NULL, | |
232 | &er.er_data, &er.er_data_len); | |
233 | if (error) | |
234 | return error; | |
235 | if (!acl) { | |
236 | mode &= ~current->fs->umask; | |
237 | if (mode != ip->i_di.di_mode) | |
238 | error = munge_mode(ip, mode); | |
239 | return error; | |
240 | } | |
241 | ||
242 | clone = posix_acl_clone(acl, GFP_KERNEL); | |
243 | error = -ENOMEM; | |
244 | if (!clone) | |
245 | goto out; | |
246 | posix_acl_release(acl); | |
247 | acl = clone; | |
248 | ||
249 | if (S_ISDIR(ip->i_di.di_mode)) { | |
250 | er.er_name = GFS2_POSIX_ACL_DEFAULT; | |
251 | er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN; | |
252 | error = gfs2_system_eaops.eo_set(ip, &er); | |
253 | if (error) | |
254 | goto out; | |
255 | } | |
256 | ||
257 | error = posix_acl_create_masq(acl, &mode); | |
258 | if (error < 0) | |
259 | goto out; | |
260 | if (error > 0) { | |
261 | er.er_name = GFS2_POSIX_ACL_ACCESS; | |
262 | er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN; | |
263 | posix_acl_to_xattr(acl, er.er_data, er.er_data_len); | |
264 | er.er_mode = mode; | |
265 | er.er_flags = GFS2_ERF_MODE; | |
266 | error = gfs2_system_eaops.eo_set(ip, &er); | |
267 | if (error) | |
268 | goto out; | |
269 | } else | |
270 | munge_mode(ip, mode); | |
271 | ||
272 | out: | |
273 | posix_acl_release(acl); | |
274 | kfree(er.er_data); | |
275 | return error; | |
276 | } | |
277 | ||
278 | int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr) | |
279 | { | |
280 | struct posix_acl *acl = NULL, *clone; | |
281 | struct gfs2_ea_location el; | |
282 | char *data; | |
283 | unsigned int len; | |
284 | int error; | |
285 | ||
286 | error = acl_get(ip, ACL_ACCESS, &acl, &el, &data, &len); | |
287 | if (error) | |
288 | return error; | |
289 | if (!acl) | |
290 | return gfs2_setattr_simple(ip, attr); | |
291 | ||
292 | clone = posix_acl_clone(acl, GFP_KERNEL); | |
293 | error = -ENOMEM; | |
294 | if (!clone) | |
295 | goto out; | |
296 | posix_acl_release(acl); | |
297 | acl = clone; | |
298 | ||
299 | error = posix_acl_chmod_masq(acl, attr->ia_mode); | |
300 | if (!error) { | |
301 | posix_acl_to_xattr(acl, data, len); | |
302 | error = gfs2_ea_acl_chmod(ip, &el, attr, data); | |
303 | } | |
304 | ||
305 | out: | |
306 | posix_acl_release(acl); | |
307 | brelse(el.el_bh); | |
308 | kfree(data); | |
309 | ||
310 | return error; | |
311 | } | |
312 |