]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - 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
CommitLineData
ac27a0ec 1/*
617ba13b 2 * linux/fs/ext4/acl.c
ac27a0ec
DK
3 *
4 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
5 */
6
3dcf5451
CH
7#include "ext4_jbd2.h"
8#include "ext4.h"
ac27a0ec
DK
9#include "xattr.h"
10#include "acl.h"
11
12/*
13 * Convert from filesystem to in-memory representation.
14 */
15static struct posix_acl *
acc94812 16ext4_acl_from_disk(struct super_block *sb, const void *value, size_t size)
ac27a0ec
DK
17{
18 const char *end = (char *)value + size;
19 int n, count;
20 struct posix_acl *acl;
21
22 if (!value)
23 return NULL;
617ba13b 24 if (size < sizeof(ext4_acl_header))
ac27a0ec 25 return ERR_PTR(-EINVAL);
617ba13b
MC
26 if (((ext4_acl_header *)value)->a_version !=
27 cpu_to_le32(EXT4_ACL_VERSION))
ac27a0ec 28 return ERR_PTR(-EINVAL);
617ba13b
MC
29 value = (char *)value + sizeof(ext4_acl_header);
30 count = ext4_acl_count(size);
ac27a0ec
DK
31 if (count < 0)
32 return ERR_PTR(-EINVAL);
33 if (count == 0)
34 return NULL;
216553c4 35 acl = posix_acl_alloc(count, GFP_NOFS);
ac27a0ec
DK
36 if (!acl)
37 return ERR_PTR(-ENOMEM);
2b2d6d01 38 for (n = 0; n < count; n++) {
617ba13b
MC
39 ext4_acl_entry *entry =
40 (ext4_acl_entry *)value;
41 if ((char *)value + sizeof(ext4_acl_entry_short) > end)
ac27a0ec
DK
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);
2b2d6d01
TT
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);
2b2d6d01
TT
53 break;
54
55 case ACL_USER:
af84df93
EB
56 value = (char *)value + sizeof(ext4_acl_entry);
57 if ((char *)value > end)
58 goto fail;
59 acl->a_entries[n].e_uid =
acc94812 60 make_kuid(sb->s_user_ns,
af84df93 61 le32_to_cpu(entry->e_id));
acc94812
SF
62 if (!uid_valid(acl->a_entries[n].e_uid))
63 goto fail;
af84df93 64 break;
2b2d6d01
TT
65 case ACL_GROUP:
66 value = (char *)value + sizeof(ext4_acl_entry);
67 if ((char *)value > end)
ac27a0ec 68 goto fail;
af84df93 69 acl->a_entries[n].e_gid =
acc94812 70 make_kgid(sb->s_user_ns,
af84df93 71 le32_to_cpu(entry->e_id));
acc94812
SF
72 if (!gid_valid(acl->a_entries[n].e_gid))
73 goto fail;
2b2d6d01
TT
74 break;
75
76 default:
77 goto fail;
ac27a0ec
DK
78 }
79 }
80 if (value != end)
81 goto fail;
82 return acl;
83
84fail:
85 posix_acl_release(acl);
86 return ERR_PTR(-EINVAL);
87}
88
89/*
90 * Convert from in-memory to filesystem representation.
91 */
92static void *
acc94812
SF
93ext4_acl_to_disk(struct super_block *sb, const struct posix_acl *acl,
94 size_t *size)
ac27a0ec 95{
617ba13b 96 ext4_acl_header *ext_acl;
ac27a0ec
DK
97 char *e;
98 size_t n;
acc94812
SF
99 uid_t uid;
100 gid_t gid;
ac27a0ec 101
617ba13b
MC
102 *size = ext4_acl_size(acl->a_count);
103 ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
216553c4 104 sizeof(ext4_acl_entry), GFP_NOFS);
ac27a0ec
DK
105 if (!ext_acl)
106 return ERR_PTR(-ENOMEM);
617ba13b
MC
107 ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
108 e = (char *)ext_acl + sizeof(ext4_acl_header);
2b2d6d01 109 for (n = 0; n < acl->a_count; n++) {
af84df93 110 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
617ba13b 111 ext4_acl_entry *entry = (ext4_acl_entry *)e;
af84df93
EB
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) {
2b2d6d01 115 case ACL_USER:
acc94812
SF
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);
af84df93
EB
120 e += sizeof(ext4_acl_entry);
121 break;
2b2d6d01 122 case ACL_GROUP:
acc94812
SF
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);
2b2d6d01
TT
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;
ac27a0ec
DK
139 }
140 }
141 return (char *)ext_acl;
142
143fail:
144 kfree(ext_acl);
145 return ERR_PTR(-EINVAL);
146}
147
ac27a0ec
DK
148/*
149 * Inode operation get_posix_acl().
150 *
151 * inode->i_mutex: don't care
152 */
4e34e719 153struct posix_acl *
617ba13b 154ext4_get_acl(struct inode *inode, int type)
ac27a0ec 155{
ac27a0ec
DK
156 int name_index;
157 char *value = NULL;
158 struct posix_acl *acl;
159 int retval;
160
2b2d6d01
TT
161 switch (type) {
162 case ACL_TYPE_ACCESS:
2b2d6d01
TT
163 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
164 break;
2b2d6d01 165 case ACL_TYPE_DEFAULT:
2b2d6d01
TT
166 name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
167 break;
2b2d6d01 168 default:
073aaa1b 169 BUG();
ac27a0ec 170 }
617ba13b 171 retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
ac27a0ec 172 if (retval > 0) {
216553c4 173 value = kmalloc(retval, GFP_NOFS);
ac27a0ec
DK
174 if (!value)
175 return ERR_PTR(-ENOMEM);
617ba13b 176 retval = ext4_xattr_get(inode, name_index, "", value, retval);
ac27a0ec
DK
177 }
178 if (retval > 0)
acc94812 179 acl = ext4_acl_from_disk(inode->i_sb, value, retval);
ac27a0ec
DK
180 else if (retval == -ENODATA || retval == -ENOSYS)
181 acl = NULL;
182 else
183 acl = ERR_PTR(retval);
184 kfree(value);
185
ac27a0ec
DK
186 return acl;
187}
188
189/*
190 * Set the access or default ACL of an inode.
191 *
617ba13b 192 * inode->i_mutex: down unless called from ext4_new_inode
ac27a0ec
DK
193 */
194static int
64e178a7 195__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
ac27a0ec
DK
196 struct posix_acl *acl)
197{
ac27a0ec
DK
198 int name_index;
199 void *value = NULL;
200 size_t size = 0;
201 int error;
202
2b2d6d01
TT
203 switch (type) {
204 case ACL_TYPE_ACCESS:
205 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
206 if (acl) {
07393101
JK
207 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
208 if (error)
2b2d6d01 209 return error;
eeca7ea1 210 inode->i_ctime = current_time(inode);
07393101 211 ext4_mark_inode_dirty(handle, inode);
2b2d6d01
TT
212 }
213 break;
ac27a0ec 214
2b2d6d01
TT
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;
ac27a0ec 220
2b2d6d01
TT
221 default:
222 return -EINVAL;
ac27a0ec
DK
223 }
224 if (acl) {
acc94812 225 value = ext4_acl_to_disk(inode->i_sb, acl, &size);
ac27a0ec
DK
226 if (IS_ERR(value))
227 return (int)PTR_ERR(value);
228 }
229
617ba13b 230 error = ext4_xattr_set_handle(handle, inode, name_index, "",
ac27a0ec
DK
231 value, size, 0);
232
233 kfree(value);
073aaa1b
AV
234 if (!error)
235 set_cached_acl(inode, type, acl);
2b2d6d01 236
ac27a0ec
DK
237 return error;
238}
239
ac27a0ec 240int
64e178a7 241ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
ac27a0ec 242{
bc26ab5f 243 handle_t *handle;
64e178a7 244 int error, retries = 0;
bc26ab5f 245
bc26ab5f 246retry:
9924a92a 247 handle = ext4_journal_start(inode, EXT4_HT_XATTR,
95eaefbd 248 ext4_jbd2_credits_xattr(inode));
64e178a7
CH
249 if (IS_ERR(handle))
250 return PTR_ERR(handle);
251
252 error = __ext4_set_acl(handle, inode, type, acl);
bc26ab5f 253 ext4_journal_stop(handle);
64e178a7 254 if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
bc26ab5f 255 goto retry;
ac27a0ec
DK
256 return error;
257}
258
259/*
64e178a7
CH
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)
ac27a0ec 264 */
64e178a7
CH
265int
266ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
ac27a0ec 267{
64e178a7 268 struct posix_acl *default_acl, *acl;
ac27a0ec
DK
269 int error;
270
64e178a7
CH
271 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
272 if (error)
273 return error;
ac27a0ec 274
64e178a7
CH
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);
24ec19b0 285 }
ac27a0ec
DK
286 return error;
287}