Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * linux/fs/ext4/acl.c
4 : *
5 : * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
6 : */
7 :
8 : #include <linux/quotaops.h>
9 : #include "ext4_jbd2.h"
10 : #include "ext4.h"
11 : #include "xattr.h"
12 : #include "acl.h"
13 :
14 : /*
15 : * Convert from filesystem to in-memory representation.
16 : */
17 : static struct posix_acl *
18 0 : ext4_acl_from_disk(const void *value, size_t size)
19 : {
20 0 : const char *end = (char *)value + size;
21 0 : int n, count;
22 0 : struct posix_acl *acl;
23 :
24 0 : if (!value)
25 : return NULL;
26 0 : if (size < sizeof(ext4_acl_header))
27 : return ERR_PTR(-EINVAL);
28 0 : if (((ext4_acl_header *)value)->a_version !=
29 : cpu_to_le32(EXT4_ACL_VERSION))
30 : return ERR_PTR(-EINVAL);
31 0 : value = (char *)value + sizeof(ext4_acl_header);
32 0 : count = ext4_acl_count(size);
33 0 : if (count < 0)
34 : return ERR_PTR(-EINVAL);
35 0 : if (count == 0)
36 : return NULL;
37 0 : acl = posix_acl_alloc(count, GFP_NOFS);
38 0 : if (!acl)
39 : return ERR_PTR(-ENOMEM);
40 0 : for (n = 0; n < count; n++) {
41 0 : ext4_acl_entry *entry =
42 : (ext4_acl_entry *)value;
43 0 : if ((char *)value + sizeof(ext4_acl_entry_short) > end)
44 0 : goto fail;
45 0 : acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
46 0 : acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
47 :
48 0 : switch (acl->a_entries[n].e_tag) {
49 : case ACL_USER_OBJ:
50 : case ACL_GROUP_OBJ:
51 : case ACL_MASK:
52 : case ACL_OTHER:
53 : value = (char *)value +
54 : sizeof(ext4_acl_entry_short);
55 : break;
56 :
57 0 : case ACL_USER:
58 0 : value = (char *)value + sizeof(ext4_acl_entry);
59 0 : if ((char *)value > end)
60 0 : goto fail;
61 0 : acl->a_entries[n].e_uid =
62 0 : make_kuid(&init_user_ns,
63 0 : le32_to_cpu(entry->e_id));
64 0 : break;
65 0 : case ACL_GROUP:
66 0 : value = (char *)value + sizeof(ext4_acl_entry);
67 0 : if ((char *)value > end)
68 0 : goto fail;
69 0 : acl->a_entries[n].e_gid =
70 0 : make_kgid(&init_user_ns,
71 0 : le32_to_cpu(entry->e_id));
72 0 : break;
73 :
74 0 : default:
75 0 : goto fail;
76 : }
77 : }
78 0 : if (value != end)
79 0 : goto fail;
80 : return acl;
81 :
82 0 : fail:
83 0 : posix_acl_release(acl);
84 0 : return ERR_PTR(-EINVAL);
85 : }
86 :
87 : /*
88 : * Convert from in-memory to filesystem representation.
89 : */
90 : static void *
91 0 : ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
92 : {
93 0 : ext4_acl_header *ext_acl;
94 0 : char *e;
95 0 : size_t n;
96 :
97 0 : *size = ext4_acl_size(acl->a_count);
98 0 : ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
99 : sizeof(ext4_acl_entry), GFP_NOFS);
100 0 : if (!ext_acl)
101 : return ERR_PTR(-ENOMEM);
102 0 : ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
103 0 : e = (char *)ext_acl + sizeof(ext4_acl_header);
104 0 : for (n = 0; n < acl->a_count; n++) {
105 0 : const struct posix_acl_entry *acl_e = &acl->a_entries[n];
106 0 : ext4_acl_entry *entry = (ext4_acl_entry *)e;
107 0 : entry->e_tag = cpu_to_le16(acl_e->e_tag);
108 0 : entry->e_perm = cpu_to_le16(acl_e->e_perm);
109 0 : switch (acl_e->e_tag) {
110 0 : case ACL_USER:
111 0 : entry->e_id = cpu_to_le32(
112 : from_kuid(&init_user_ns, acl_e->e_uid));
113 0 : e += sizeof(ext4_acl_entry);
114 0 : break;
115 0 : case ACL_GROUP:
116 0 : entry->e_id = cpu_to_le32(
117 : from_kgid(&init_user_ns, acl_e->e_gid));
118 0 : e += sizeof(ext4_acl_entry);
119 0 : break;
120 :
121 0 : case ACL_USER_OBJ:
122 : case ACL_GROUP_OBJ:
123 : case ACL_MASK:
124 : case ACL_OTHER:
125 0 : e += sizeof(ext4_acl_entry_short);
126 0 : break;
127 :
128 0 : default:
129 0 : goto fail;
130 : }
131 : }
132 : return (char *)ext_acl;
133 :
134 : fail:
135 0 : kfree(ext_acl);
136 0 : return ERR_PTR(-EINVAL);
137 : }
138 :
139 : /*
140 : * Inode operation get_posix_acl().
141 : *
142 : * inode->i_rwsem: don't care
143 : */
144 : struct posix_acl *
145 0 : ext4_get_acl(struct inode *inode, int type, bool rcu)
146 : {
147 0 : int name_index;
148 0 : char *value = NULL;
149 0 : struct posix_acl *acl;
150 0 : int retval;
151 :
152 0 : if (rcu)
153 : return ERR_PTR(-ECHILD);
154 :
155 0 : switch (type) {
156 : case ACL_TYPE_ACCESS:
157 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
158 : break;
159 0 : case ACL_TYPE_DEFAULT:
160 0 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
161 0 : break;
162 0 : default:
163 0 : BUG();
164 : }
165 0 : retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
166 0 : if (retval > 0) {
167 0 : value = kmalloc(retval, GFP_NOFS);
168 0 : if (!value)
169 : return ERR_PTR(-ENOMEM);
170 0 : retval = ext4_xattr_get(inode, name_index, "", value, retval);
171 : }
172 0 : if (retval > 0)
173 0 : acl = ext4_acl_from_disk(value, retval);
174 0 : else if (retval == -ENODATA || retval == -ENOSYS)
175 : acl = NULL;
176 : else
177 0 : acl = ERR_PTR(retval);
178 0 : kfree(value);
179 :
180 0 : return acl;
181 : }
182 :
183 : /*
184 : * Set the access or default ACL of an inode.
185 : *
186 : * inode->i_rwsem: down unless called from ext4_new_inode
187 : */
188 : static int
189 0 : __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
190 : struct posix_acl *acl, int xattr_flags)
191 : {
192 0 : int name_index;
193 0 : void *value = NULL;
194 0 : size_t size = 0;
195 0 : int error;
196 :
197 0 : switch (type) {
198 : case ACL_TYPE_ACCESS:
199 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
200 : break;
201 :
202 0 : case ACL_TYPE_DEFAULT:
203 0 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
204 0 : if (!S_ISDIR(inode->i_mode))
205 0 : return acl ? -EACCES : 0;
206 : break;
207 :
208 : default:
209 : return -EINVAL;
210 : }
211 0 : if (acl) {
212 0 : value = ext4_acl_to_disk(acl, &size);
213 0 : if (IS_ERR(value))
214 0 : return (int)PTR_ERR(value);
215 : }
216 :
217 0 : error = ext4_xattr_set_handle(handle, inode, name_index, "",
218 : value, size, xattr_flags);
219 :
220 0 : kfree(value);
221 0 : if (!error)
222 0 : set_cached_acl(inode, type, acl);
223 :
224 : return error;
225 : }
226 :
227 : int
228 0 : ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
229 : struct posix_acl *acl, int type)
230 : {
231 0 : handle_t *handle;
232 0 : int error, credits, retries = 0;
233 0 : size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
234 0 : struct inode *inode = d_inode(dentry);
235 0 : umode_t mode = inode->i_mode;
236 0 : int update_mode = 0;
237 :
238 0 : error = dquot_initialize(inode);
239 0 : if (error)
240 : return error;
241 0 : retry:
242 0 : error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
243 : &credits);
244 0 : if (error)
245 0 : return error;
246 :
247 0 : handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
248 0 : if (IS_ERR(handle))
249 0 : return PTR_ERR(handle);
250 :
251 0 : if ((type == ACL_TYPE_ACCESS) && acl) {
252 0 : error = posix_acl_update_mode(idmap, inode, &mode, &acl);
253 0 : if (error)
254 0 : goto out_stop;
255 0 : if (mode != inode->i_mode)
256 0 : update_mode = 1;
257 : }
258 :
259 0 : error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
260 0 : if (!error && update_mode) {
261 0 : inode->i_mode = mode;
262 0 : inode->i_ctime = current_time(inode);
263 0 : error = ext4_mark_inode_dirty(handle, inode);
264 : }
265 0 : out_stop:
266 0 : ext4_journal_stop(handle);
267 0 : if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
268 0 : goto retry;
269 : return error;
270 : }
271 :
272 : /*
273 : * Initialize the ACLs of a new inode. Called from ext4_new_inode.
274 : *
275 : * dir->i_rwsem: down
276 : * inode->i_rwsem: up (access to inode is still exclusive)
277 : */
278 : int
279 0 : ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
280 : {
281 0 : struct posix_acl *default_acl, *acl;
282 0 : int error;
283 :
284 0 : error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
285 0 : if (error)
286 : return error;
287 :
288 0 : if (default_acl) {
289 0 : error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
290 : default_acl, XATTR_CREATE);
291 0 : posix_acl_release(default_acl);
292 : } else {
293 0 : inode->i_default_acl = NULL;
294 : }
295 0 : if (acl) {
296 0 : if (!error)
297 0 : error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
298 : acl, XATTR_CREATE);
299 0 : posix_acl_release(acl);
300 : } else {
301 0 : inode->i_acl = NULL;
302 : }
303 : return error;
304 : }
|