Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/ext2/acl.c
29267 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* linux/fs/ext2/acl.c
4
*
5
* Copyright (C) 2001-2003 Andreas Gruenbacher, <[email protected]>
6
*/
7
8
#include <linux/init.h>
9
#include <linux/sched.h>
10
#include <linux/slab.h>
11
#include <linux/fs.h>
12
#include "ext2.h"
13
#include "xattr.h"
14
#include "acl.h"
15
16
/*
17
* Convert from filesystem to in-memory representation.
18
*/
19
static struct posix_acl *
20
ext2_acl_from_disk(const void *value, size_t size)
21
{
22
const char *end = (char *)value + size;
23
int n, count;
24
struct posix_acl *acl;
25
26
if (!value)
27
return NULL;
28
if (size < sizeof(ext2_acl_header))
29
return ERR_PTR(-EINVAL);
30
if (((ext2_acl_header *)value)->a_version !=
31
cpu_to_le32(EXT2_ACL_VERSION))
32
return ERR_PTR(-EINVAL);
33
value = (char *)value + sizeof(ext2_acl_header);
34
count = ext2_acl_count(size);
35
if (count < 0)
36
return ERR_PTR(-EINVAL);
37
if (count == 0)
38
return NULL;
39
acl = posix_acl_alloc(count, GFP_KERNEL);
40
if (!acl)
41
return ERR_PTR(-ENOMEM);
42
for (n=0; n < count; n++) {
43
ext2_acl_entry *entry =
44
(ext2_acl_entry *)value;
45
if ((char *)value + sizeof(ext2_acl_entry_short) > end)
46
goto fail;
47
acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
48
acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
49
switch(acl->a_entries[n].e_tag) {
50
case ACL_USER_OBJ:
51
case ACL_GROUP_OBJ:
52
case ACL_MASK:
53
case ACL_OTHER:
54
value = (char *)value +
55
sizeof(ext2_acl_entry_short);
56
break;
57
58
case ACL_USER:
59
value = (char *)value + sizeof(ext2_acl_entry);
60
if ((char *)value > end)
61
goto fail;
62
acl->a_entries[n].e_uid =
63
make_kuid(&init_user_ns,
64
le32_to_cpu(entry->e_id));
65
break;
66
case ACL_GROUP:
67
value = (char *)value + sizeof(ext2_acl_entry);
68
if ((char *)value > end)
69
goto fail;
70
acl->a_entries[n].e_gid =
71
make_kgid(&init_user_ns,
72
le32_to_cpu(entry->e_id));
73
break;
74
75
default:
76
goto fail;
77
}
78
}
79
if (value != end)
80
goto fail;
81
return acl;
82
83
fail:
84
posix_acl_release(acl);
85
return ERR_PTR(-EINVAL);
86
}
87
88
/*
89
* Convert from in-memory to filesystem representation.
90
*/
91
static void *
92
ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
93
{
94
ext2_acl_header *ext_acl;
95
char *e;
96
size_t n;
97
98
*size = ext2_acl_size(acl->a_count);
99
ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
100
sizeof(ext2_acl_entry), GFP_KERNEL);
101
if (!ext_acl)
102
return ERR_PTR(-ENOMEM);
103
ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
104
e = (char *)ext_acl + sizeof(ext2_acl_header);
105
for (n=0; n < acl->a_count; n++) {
106
const struct posix_acl_entry *acl_e = &acl->a_entries[n];
107
ext2_acl_entry *entry = (ext2_acl_entry *)e;
108
entry->e_tag = cpu_to_le16(acl_e->e_tag);
109
entry->e_perm = cpu_to_le16(acl_e->e_perm);
110
switch(acl_e->e_tag) {
111
case ACL_USER:
112
entry->e_id = cpu_to_le32(
113
from_kuid(&init_user_ns, acl_e->e_uid));
114
e += sizeof(ext2_acl_entry);
115
break;
116
case ACL_GROUP:
117
entry->e_id = cpu_to_le32(
118
from_kgid(&init_user_ns, acl_e->e_gid));
119
e += sizeof(ext2_acl_entry);
120
break;
121
122
case ACL_USER_OBJ:
123
case ACL_GROUP_OBJ:
124
case ACL_MASK:
125
case ACL_OTHER:
126
e += sizeof(ext2_acl_entry_short);
127
break;
128
129
default:
130
goto fail;
131
}
132
}
133
return (char *)ext_acl;
134
135
fail:
136
kfree(ext_acl);
137
return ERR_PTR(-EINVAL);
138
}
139
140
/*
141
* inode->i_mutex: don't care
142
*/
143
struct posix_acl *
144
ext2_get_acl(struct inode *inode, int type, bool rcu)
145
{
146
int name_index;
147
char *value = NULL;
148
struct posix_acl *acl;
149
int retval;
150
151
if (rcu)
152
return ERR_PTR(-ECHILD);
153
154
switch (type) {
155
case ACL_TYPE_ACCESS:
156
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
157
break;
158
case ACL_TYPE_DEFAULT:
159
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
160
break;
161
default:
162
BUG();
163
}
164
retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
165
if (retval > 0) {
166
value = kmalloc(retval, GFP_KERNEL);
167
if (!value)
168
return ERR_PTR(-ENOMEM);
169
retval = ext2_xattr_get(inode, name_index, "", value, retval);
170
}
171
if (retval > 0)
172
acl = ext2_acl_from_disk(value, retval);
173
else if (retval == -ENODATA || retval == -ENOSYS)
174
acl = NULL;
175
else
176
acl = ERR_PTR(retval);
177
kfree(value);
178
179
return acl;
180
}
181
182
static int
183
__ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
184
{
185
int name_index;
186
void *value = NULL;
187
size_t size = 0;
188
int error;
189
190
switch(type) {
191
case ACL_TYPE_ACCESS:
192
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
193
break;
194
195
case ACL_TYPE_DEFAULT:
196
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
197
if (!S_ISDIR(inode->i_mode))
198
return acl ? -EACCES : 0;
199
break;
200
201
default:
202
return -EINVAL;
203
}
204
if (acl) {
205
value = ext2_acl_to_disk(acl, &size);
206
if (IS_ERR(value))
207
return (int)PTR_ERR(value);
208
}
209
210
error = ext2_xattr_set(inode, name_index, "", value, size, 0);
211
212
kfree(value);
213
if (!error)
214
set_cached_acl(inode, type, acl);
215
return error;
216
}
217
218
/*
219
* inode->i_mutex: down
220
*/
221
int
222
ext2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
223
struct posix_acl *acl, int type)
224
{
225
int error;
226
int update_mode = 0;
227
struct inode *inode = d_inode(dentry);
228
umode_t mode = inode->i_mode;
229
230
if (type == ACL_TYPE_ACCESS && acl) {
231
error = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode,
232
&acl);
233
if (error)
234
return error;
235
update_mode = 1;
236
}
237
error = __ext2_set_acl(inode, acl, type);
238
if (!error && update_mode) {
239
inode->i_mode = mode;
240
inode_set_ctime_current(inode);
241
mark_inode_dirty(inode);
242
}
243
return error;
244
}
245
246
/*
247
* Initialize the ACLs of a new inode. Called from ext2_new_inode.
248
*
249
* dir->i_mutex: down
250
* inode->i_mutex: up (access to inode is still exclusive)
251
*/
252
int
253
ext2_init_acl(struct inode *inode, struct inode *dir)
254
{
255
struct posix_acl *default_acl, *acl;
256
int error;
257
258
error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
259
if (error)
260
return error;
261
262
if (default_acl) {
263
error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
264
posix_acl_release(default_acl);
265
} else {
266
inode->i_default_acl = NULL;
267
}
268
if (acl) {
269
if (!error)
270
error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
271
posix_acl_release(acl);
272
} else {
273
inode->i_acl = NULL;
274
}
275
return error;
276
}
277
278