]>
Commit | Line | Data |
---|---|---|
ef14f0c1 CH |
1 | /* |
2 | * Copyright (c) 2008, Christoph Hellwig | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it would be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | */ | |
18 | #include "xfs.h" | |
a4fbe6ab | 19 | #include "xfs_format.h" |
69432832 | 20 | #include "xfs_log_format.h" |
7fd36c44 | 21 | #include "xfs_trans_resv.h" |
0a8aa193 | 22 | #include "xfs_mount.h" |
a4fbe6ab DC |
23 | #include "xfs_inode.h" |
24 | #include "xfs_acl.h" | |
25 | #include "xfs_attr.h" | |
0b1b213f | 26 | #include "xfs_trace.h" |
5a0e3ad6 | 27 | #include <linux/slab.h> |
ef14f0c1 CH |
28 | #include <linux/xattr.h> |
29 | #include <linux/posix_acl_xattr.h> | |
30 | ||
31 | ||
ef14f0c1 CH |
32 | /* |
33 | * Locking scheme: | |
34 | * - all ACL updates are protected by inode->i_mutex, which is taken before | |
35 | * calling into this file. | |
ef14f0c1 CH |
36 | */ |
37 | ||
38 | STATIC struct posix_acl * | |
0a8aa193 | 39 | xfs_acl_from_disk( |
86a21c79 AG |
40 | const struct xfs_acl *aclp, |
41 | int len, | |
42 | int max_entries) | |
ef14f0c1 CH |
43 | { |
44 | struct posix_acl_entry *acl_e; | |
45 | struct posix_acl *acl; | |
86a21c79 | 46 | const struct xfs_acl_entry *ace; |
093019cf | 47 | unsigned int count, i; |
ef14f0c1 | 48 | |
86a21c79 AG |
49 | if (len < sizeof(*aclp)) |
50 | return ERR_PTR(-EFSCORRUPTED); | |
ef14f0c1 | 51 | count = be32_to_cpu(aclp->acl_cnt); |
86a21c79 | 52 | if (count > max_entries || XFS_ACL_SIZE(count) != len) |
fa8b18ed | 53 | return ERR_PTR(-EFSCORRUPTED); |
ef14f0c1 CH |
54 | |
55 | acl = posix_acl_alloc(count, GFP_KERNEL); | |
56 | if (!acl) | |
57 | return ERR_PTR(-ENOMEM); | |
58 | ||
59 | for (i = 0; i < count; i++) { | |
60 | acl_e = &acl->a_entries[i]; | |
61 | ace = &aclp->acl_entry[i]; | |
62 | ||
63 | /* | |
64 | * The tag is 32 bits on disk and 16 bits in core. | |
65 | * | |
66 | * Because every access to it goes through the core | |
67 | * format first this is not a problem. | |
68 | */ | |
69 | acl_e->e_tag = be32_to_cpu(ace->ae_tag); | |
70 | acl_e->e_perm = be16_to_cpu(ace->ae_perm); | |
71 | ||
72 | switch (acl_e->e_tag) { | |
73 | case ACL_USER: | |
288bbe0e DE |
74 | acl_e->e_uid = xfs_uid_to_kuid(be32_to_cpu(ace->ae_id)); |
75 | break; | |
ef14f0c1 | 76 | case ACL_GROUP: |
288bbe0e | 77 | acl_e->e_gid = xfs_gid_to_kgid(be32_to_cpu(ace->ae_id)); |
ef14f0c1 CH |
78 | break; |
79 | case ACL_USER_OBJ: | |
80 | case ACL_GROUP_OBJ: | |
81 | case ACL_MASK: | |
82 | case ACL_OTHER: | |
ef14f0c1 CH |
83 | break; |
84 | default: | |
85 | goto fail; | |
86 | } | |
87 | } | |
88 | return acl; | |
89 | ||
90 | fail: | |
91 | posix_acl_release(acl); | |
92 | return ERR_PTR(-EINVAL); | |
93 | } | |
94 | ||
95 | STATIC void | |
96 | xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl) | |
97 | { | |
98 | const struct posix_acl_entry *acl_e; | |
99 | struct xfs_acl_entry *ace; | |
100 | int i; | |
101 | ||
102 | aclp->acl_cnt = cpu_to_be32(acl->a_count); | |
103 | for (i = 0; i < acl->a_count; i++) { | |
104 | ace = &aclp->acl_entry[i]; | |
105 | acl_e = &acl->a_entries[i]; | |
106 | ||
107 | ace->ae_tag = cpu_to_be32(acl_e->e_tag); | |
288bbe0e DE |
108 | switch (acl_e->e_tag) { |
109 | case ACL_USER: | |
110 | ace->ae_id = cpu_to_be32(xfs_kuid_to_uid(acl_e->e_uid)); | |
111 | break; | |
112 | case ACL_GROUP: | |
113 | ace->ae_id = cpu_to_be32(xfs_kgid_to_gid(acl_e->e_gid)); | |
114 | break; | |
115 | default: | |
116 | ace->ae_id = cpu_to_be32(ACL_UNDEFINED_ID); | |
117 | break; | |
118 | } | |
119 | ||
ef14f0c1 CH |
120 | ace->ae_perm = cpu_to_be16(acl_e->e_perm); |
121 | } | |
122 | } | |
123 | ||
ef14f0c1 CH |
124 | struct posix_acl * |
125 | xfs_get_acl(struct inode *inode, int type) | |
126 | { | |
127 | struct xfs_inode *ip = XFS_I(inode); | |
2401dc29 | 128 | struct posix_acl *acl = NULL; |
ef14f0c1 | 129 | struct xfs_acl *xfs_acl; |
a9273ca5 | 130 | unsigned char *ea_name; |
ef14f0c1 | 131 | int error; |
0a8aa193 | 132 | int len; |
ef14f0c1 | 133 | |
4e34e719 CH |
134 | trace_xfs_get_acl(ip); |
135 | ||
ef14f0c1 CH |
136 | switch (type) { |
137 | case ACL_TYPE_ACCESS: | |
138 | ea_name = SGI_ACL_FILE; | |
ef14f0c1 CH |
139 | break; |
140 | case ACL_TYPE_DEFAULT: | |
141 | ea_name = SGI_ACL_DEFAULT; | |
ef14f0c1 CH |
142 | break; |
143 | default: | |
1cbd20d8 | 144 | BUG(); |
ef14f0c1 CH |
145 | } |
146 | ||
ef14f0c1 CH |
147 | /* |
148 | * If we have a cached ACLs value just return it, not need to | |
149 | * go out to the disk. | |
150 | */ | |
0a8aa193 | 151 | len = XFS_ACL_MAX_SIZE(ip->i_mount); |
fdd3ccee DC |
152 | xfs_acl = kmem_zalloc_large(len, KM_SLEEP); |
153 | if (!xfs_acl) | |
154 | return ERR_PTR(-ENOMEM); | |
ef14f0c1 | 155 | |
2451337d | 156 | error = xfs_attr_get(ip, ea_name, (unsigned char *)xfs_acl, |
a9273ca5 | 157 | &len, ATTR_ROOT); |
ef14f0c1 CH |
158 | if (error) { |
159 | /* | |
160 | * If the attribute doesn't exist make sure we have a negative | |
b8a7a3a6 | 161 | * cache entry, for any other error assume it is transient. |
ef14f0c1 | 162 | */ |
b8a7a3a6 AG |
163 | if (error != -ENOATTR) |
164 | acl = ERR_PTR(error); | |
165 | } else { | |
166 | acl = xfs_acl_from_disk(xfs_acl, len, | |
167 | XFS_ACL_MAX_ENTRIES(ip->i_mount)); | |
ef14f0c1 | 168 | } |
fdd3ccee | 169 | kmem_free(xfs_acl); |
ef14f0c1 CH |
170 | return acl; |
171 | } | |
172 | ||
173 | STATIC int | |
2401dc29 | 174 | __xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) |
ef14f0c1 CH |
175 | { |
176 | struct xfs_inode *ip = XFS_I(inode); | |
a9273ca5 | 177 | unsigned char *ea_name; |
ef14f0c1 CH |
178 | int error; |
179 | ||
ef14f0c1 CH |
180 | switch (type) { |
181 | case ACL_TYPE_ACCESS: | |
182 | ea_name = SGI_ACL_FILE; | |
ef14f0c1 CH |
183 | break; |
184 | case ACL_TYPE_DEFAULT: | |
185 | if (!S_ISDIR(inode->i_mode)) | |
186 | return acl ? -EACCES : 0; | |
187 | ea_name = SGI_ACL_DEFAULT; | |
ef14f0c1 CH |
188 | break; |
189 | default: | |
190 | return -EINVAL; | |
191 | } | |
192 | ||
193 | if (acl) { | |
194 | struct xfs_acl *xfs_acl; | |
0a8aa193 | 195 | int len = XFS_ACL_MAX_SIZE(ip->i_mount); |
ef14f0c1 | 196 | |
fdd3ccee DC |
197 | xfs_acl = kmem_zalloc_large(len, KM_SLEEP); |
198 | if (!xfs_acl) | |
199 | return -ENOMEM; | |
ef14f0c1 CH |
200 | |
201 | xfs_acl_to_disk(xfs_acl, acl); | |
0a8aa193 DC |
202 | |
203 | /* subtract away the unused acl entries */ | |
204 | len -= sizeof(struct xfs_acl_entry) * | |
205 | (XFS_ACL_MAX_ENTRIES(ip->i_mount) - acl->a_count); | |
ef14f0c1 | 206 | |
2451337d | 207 | error = xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl, |
ef14f0c1 CH |
208 | len, ATTR_ROOT); |
209 | ||
fdd3ccee | 210 | kmem_free(xfs_acl); |
ef14f0c1 CH |
211 | } else { |
212 | /* | |
213 | * A NULL ACL argument means we want to remove the ACL. | |
214 | */ | |
2451337d | 215 | error = xfs_attr_remove(ip, ea_name, ATTR_ROOT); |
ef14f0c1 CH |
216 | |
217 | /* | |
218 | * If the attribute didn't exist to start with that's fine. | |
219 | */ | |
220 | if (error == -ENOATTR) | |
221 | error = 0; | |
222 | } | |
223 | ||
224 | if (!error) | |
1cbd20d8 | 225 | set_cached_acl(inode, type, acl); |
ef14f0c1 CH |
226 | return error; |
227 | } | |
228 | ||
ef14f0c1 | 229 | static int |
d3fb6120 | 230 | xfs_set_mode(struct inode *inode, umode_t mode) |
ef14f0c1 CH |
231 | { |
232 | int error = 0; | |
233 | ||
234 | if (mode != inode->i_mode) { | |
235 | struct iattr iattr; | |
236 | ||
d6d59bad | 237 | iattr.ia_valid = ATTR_MODE | ATTR_CTIME; |
ef14f0c1 | 238 | iattr.ia_mode = mode; |
c2050a45 | 239 | iattr.ia_ctime = current_time(inode); |
ef14f0c1 | 240 | |
2451337d | 241 | error = xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL); |
ef14f0c1 CH |
242 | } |
243 | ||
244 | return error; | |
245 | } | |
246 | ||
ef14f0c1 | 247 | int |
2401dc29 | 248 | xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) |
ef14f0c1 | 249 | { |
431547b3 | 250 | int error = 0; |
ef14f0c1 | 251 | |
2401dc29 | 252 | if (!acl) |
ef14f0c1 CH |
253 | goto set_acl; |
254 | ||
4ae69fea | 255 | error = -E2BIG; |
0a8aa193 | 256 | if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb))) |
2401dc29 | 257 | return error; |
ef14f0c1 CH |
258 | |
259 | if (type == ACL_TYPE_ACCESS) { | |
07393101 | 260 | umode_t mode; |
ef14f0c1 | 261 | |
07393101 JK |
262 | error = posix_acl_update_mode(inode, &mode, &acl); |
263 | if (error) | |
264 | return error; | |
ef14f0c1 CH |
265 | error = xfs_set_mode(inode, mode); |
266 | if (error) | |
2401dc29 | 267 | return error; |
ef14f0c1 CH |
268 | } |
269 | ||
270 | set_acl: | |
2401dc29 | 271 | return __xfs_set_acl(inode, type, acl); |
ef14f0c1 | 272 | } |