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