Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/ext2fs/ext2_acl.c
39483 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2017, Fedor Uporov
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/types.h>
32
#include <sys/stat.h>
33
#include <sys/kernel.h>
34
#include <sys/malloc.h>
35
#include <sys/vnode.h>
36
#include <sys/bio.h>
37
#include <sys/buf.h>
38
#include <sys/endian.h>
39
#include <sys/conf.h>
40
#include <sys/mount.h>
41
#include <sys/extattr.h>
42
43
#include <fs/ext2fs/fs.h>
44
#include <fs/ext2fs/ext2fs.h>
45
#include <fs/ext2fs/inode.h>
46
#include <fs/ext2fs/ext2_acl.h>
47
#include <fs/ext2fs/ext2_extattr.h>
48
#include <fs/ext2fs/ext2_extern.h>
49
#include <fs/ext2fs/ext2_dinode.h>
50
#include <fs/ext2fs/ext2_mount.h>
51
52
#ifdef UFS_ACL
53
54
void
55
ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl)
56
{
57
struct acl_entry *acl_mask, *acl_group_obj;
58
int i;
59
60
/*
61
* Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK
62
* and ACL_GROUP_OBJ for use after we know whether ACL_MASK is
63
* present.
64
*/
65
acl_mask = NULL;
66
acl_group_obj = NULL;
67
for (i = 0; i < acl->acl_cnt; i++) {
68
switch (acl->acl_entry[i].ae_tag) {
69
case ACL_USER_OBJ:
70
acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm(
71
ACL_USER_OBJ, ip->i_mode);
72
acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
73
break;
74
75
case ACL_GROUP_OBJ:
76
acl_group_obj = &acl->acl_entry[i];
77
acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
78
break;
79
80
case ACL_OTHER:
81
acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm(
82
ACL_OTHER, ip->i_mode);
83
acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
84
break;
85
86
case ACL_MASK:
87
acl_mask = &acl->acl_entry[i];
88
acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID;
89
break;
90
91
case ACL_USER:
92
case ACL_GROUP:
93
break;
94
95
default:
96
panic("ext2_sync_acl_from_inode(): bad ae_tag");
97
}
98
}
99
100
if (acl_group_obj == NULL)
101
panic("ext2_sync_acl_from_inode(): no ACL_GROUP_OBJ");
102
103
if (acl_mask == NULL) {
104
/*
105
* There is no ACL_MASK, so update ACL_GROUP_OBJ.
106
*/
107
acl_group_obj->ae_perm = acl_posix1e_mode_to_perm(
108
ACL_GROUP_OBJ, ip->i_mode);
109
} else {
110
/*
111
* Update the ACL_MASK entry instead of ACL_GROUP_OBJ.
112
*/
113
acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ,
114
ip->i_mode);
115
}
116
}
117
118
static void
119
ext2_sync_inode_from_acl(struct acl *acl, struct inode *ip)
120
{
121
122
ip->i_mode &= ACL_PRESERVE_MASK;
123
ip->i_mode |= acl_posix1e_acl_to_mode(acl);
124
}
125
126
/*
127
* Convert from filesystem to in-memory representation.
128
*/
129
static int
130
ext4_acl_from_disk(char *value, size_t size, struct acl *acl)
131
{
132
const char *end;
133
int n, count, s;
134
135
if (value == NULL)
136
return (EINVAL);
137
138
end = value + size;
139
140
if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION)
141
return (EINVAL);
142
143
if (size < sizeof(struct ext2_acl_header))
144
return (EINVAL);
145
146
s = size - sizeof(struct ext2_acl_header);
147
s -= 4 * sizeof(struct ext2_acl_entry_short);
148
if (s < 0)
149
if ((size - sizeof(struct ext2_acl_header)) %
150
sizeof(struct ext2_acl_entry_short))
151
count = -1;
152
else
153
count = (size - sizeof(struct ext2_acl_header)) /
154
sizeof(struct ext2_acl_entry_short);
155
else
156
if (s % sizeof(struct ext2_acl_entry))
157
count = -1;
158
else
159
count = s / sizeof(struct ext2_acl_entry) + 4;
160
161
if (count <= 0 || count > acl->acl_maxcnt)
162
return (EINVAL);
163
164
value = value + sizeof(struct ext2_acl_header);
165
166
for (n = 0; n < count; n++) {
167
struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value;
168
if ((char *)value + sizeof(struct ext2_acl_entry_short) > end)
169
return (EINVAL);
170
171
acl->acl_entry[n].ae_tag = entry->ae_tag;
172
acl->acl_entry[n].ae_perm = entry->ae_perm;
173
174
switch (acl->acl_entry[n].ae_tag) {
175
case ACL_USER_OBJ:
176
case ACL_GROUP_OBJ:
177
case ACL_MASK:
178
case ACL_OTHER:
179
value = (char *)value + sizeof(struct ext2_acl_entry_short);
180
break;
181
182
case ACL_USER:
183
value = (char *)value + sizeof(struct ext2_acl_entry);
184
if ((char *)value > end)
185
return (EINVAL);
186
187
acl->acl_entry[n].ae_id = entry->ae_id;
188
break;
189
190
case ACL_GROUP:
191
value = (char *)value + sizeof(struct ext2_acl_entry);
192
if ((char *)value > end)
193
return (EINVAL);
194
195
acl->acl_entry[n].ae_id = entry->ae_id;
196
break;
197
198
default:
199
return (EINVAL);
200
}
201
}
202
203
if (value != end)
204
return (EINVAL);
205
206
acl->acl_cnt = count;
207
208
return (0);
209
}
210
211
static int
212
ext2_getacl_posix1e(struct vop_getacl_args *ap)
213
{
214
int attrnamespace;
215
const char *attrname;
216
char *value;
217
int len;
218
int error;
219
220
switch (ap->a_type) {
221
case ACL_TYPE_DEFAULT:
222
attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE;
223
attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME;
224
break;
225
case ACL_TYPE_ACCESS:
226
attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE;
227
attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME;
228
break;
229
default:
230
return (EINVAL);
231
}
232
233
len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
234
value = malloc(len, M_ACL, M_WAITOK);
235
236
error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname,
237
&len, value, ap->a_td);
238
if (error == ENOATTR) {
239
switch (ap->a_type) {
240
case ACL_TYPE_ACCESS:
241
ap->a_aclp->acl_cnt = 3;
242
ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ;
243
ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID;
244
ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE;
245
ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ;
246
ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID;
247
ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE;
248
ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER;
249
ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID;
250
ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE;
251
break;
252
253
case ACL_TYPE_DEFAULT:
254
ap->a_aclp->acl_cnt = 0;
255
break;
256
}
257
} else if (error != 0)
258
goto out;
259
260
if (!error) {
261
error = ext4_acl_from_disk(value, len, ap->a_aclp);
262
if (error)
263
goto out;
264
}
265
266
if (error == ENOATTR)
267
error = 0;
268
269
if (ap->a_type == ACL_TYPE_ACCESS)
270
ext2_sync_acl_from_inode(VTOI(ap->a_vp), ap->a_aclp);
271
272
out:
273
free(value, M_TEMP);
274
return (error);
275
}
276
277
int
278
ext2_getacl(struct vop_getacl_args *ap)
279
{
280
281
if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
282
((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
283
return (EOPNOTSUPP);
284
285
if (ap->a_type == ACL_TYPE_NFS4)
286
return (ENOTSUP);
287
288
return (ext2_getacl_posix1e(ap));
289
}
290
291
/*
292
* Convert from in-memory to filesystem representation.
293
*/
294
static int
295
ext4_acl_to_disk(const struct acl *acl, size_t *size, char *value)
296
{
297
struct ext2_acl_header *ext_acl;
298
int disk_size;
299
char *e;
300
size_t n;
301
302
if (acl->acl_cnt <= 4)
303
disk_size = sizeof(struct ext2_acl_header) +
304
acl->acl_cnt * sizeof(struct ext2_acl_entry_short);
305
else
306
disk_size = sizeof(struct ext2_acl_header) +
307
4 * sizeof(struct ext2_acl_entry_short) +
308
(acl->acl_cnt - 4) * sizeof(struct ext2_acl_entry);
309
310
if (disk_size > *size)
311
return (EINVAL);
312
313
*size = disk_size;
314
ext_acl = (struct ext2_acl_header *)value;
315
316
ext_acl->a_version = EXT4_ACL_VERSION;
317
e = (char *)ext_acl + sizeof(struct ext2_acl_header);
318
for (n = 0; n < acl->acl_cnt; n++) {
319
const struct acl_entry *acl_e = &acl->acl_entry[n];
320
struct ext2_acl_entry *entry = (struct ext2_acl_entry *)e;
321
entry->ae_tag = acl_e->ae_tag;
322
entry->ae_perm = acl_e->ae_perm;
323
switch (acl_e->ae_tag) {
324
case ACL_USER:
325
entry->ae_id = acl_e->ae_id;
326
e += sizeof(struct ext2_acl_entry);
327
break;
328
329
case ACL_GROUP:
330
entry->ae_id = acl_e->ae_id;
331
e += sizeof(struct ext2_acl_entry);
332
break;
333
334
case ACL_USER_OBJ:
335
case ACL_GROUP_OBJ:
336
case ACL_MASK:
337
case ACL_OTHER:
338
e += sizeof(struct ext2_acl_entry_short);
339
break;
340
341
default:
342
return (EINVAL);
343
}
344
}
345
346
return (0);
347
}
348
349
static int
350
ext2_setacl_posix1e(struct vop_setacl_args *ap)
351
{
352
struct inode *ip = VTOI(ap->a_vp);
353
char *value;
354
size_t len;
355
int error;
356
357
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
358
return (EINVAL);
359
360
/*
361
* If this is a set operation rather than a delete operation,
362
* invoke VOP_ACLCHECK() on the passed ACL to determine if it is
363
* valid for the target. This will include a check on ap->a_type.
364
*/
365
if (ap->a_aclp != NULL) {
366
/*
367
* Set operation.
368
*/
369
error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp,
370
ap->a_cred, ap->a_td);
371
if (error)
372
return (error);
373
} else {
374
/*
375
* Delete operation.
376
* POSIX.1e allows only deletion of the default ACL on a
377
* directory (ACL_TYPE_DEFAULT).
378
*/
379
if (ap->a_type != ACL_TYPE_DEFAULT)
380
return (EINVAL);
381
if (ap->a_vp->v_type != VDIR)
382
return (ENOTDIR);
383
}
384
385
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
386
return (EROFS);
387
388
/*
389
* Authorize the ACL operation.
390
*/
391
if (ip->i_flags & (IMMUTABLE | APPEND))
392
return (EPERM);
393
394
/*
395
* Must hold VADMIN (be file owner) or have appropriate privilege.
396
*/
397
if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td)))
398
return (error);
399
400
switch (ap->a_type) {
401
case ACL_TYPE_ACCESS:
402
len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
403
value = malloc(len, M_ACL, M_WAITOK | M_ZERO);
404
error = ext4_acl_to_disk(ap->a_aclp, &len, value);
405
if (error == 0)
406
error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
407
POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
408
POSIX1E_ACL_ACCESS_EXTATTR_NAME, len,
409
value, ap->a_td);
410
411
free(value, M_ACL);
412
break;
413
414
case ACL_TYPE_DEFAULT:
415
if (ap->a_aclp == NULL) {
416
error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED,
417
POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
418
POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td);
419
420
/*
421
* Attempting to delete a non-present default ACL
422
* will return success for portability purposes.
423
* (TRIX)
424
*
425
* XXX: Note that since we can't distinguish
426
* "that EA is not supported" from "that EA is not
427
* defined", the success case here overlaps the
428
* the ENOATTR->EOPNOTSUPP case below.
429
*/
430
if (error == ENOATTR)
431
error = 0;
432
} else {
433
len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header);
434
value = malloc(len, M_ACL, M_WAITOK | M_ZERO);
435
error = ext4_acl_to_disk(ap->a_aclp, &len, value);
436
if (error == 0)
437
error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
438
POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
439
POSIX1E_ACL_DEFAULT_EXTATTR_NAME, len,
440
value, ap->a_td);
441
442
free(value, M_ACL);
443
}
444
break;
445
446
default:
447
error = EINVAL;
448
}
449
450
/*
451
* Map lack of attribute definition in UFS_EXTATTR into lack of
452
* support for ACLs on the filesystem.
453
*/
454
if (error == ENOATTR)
455
return (EOPNOTSUPP);
456
457
if (error != 0)
458
return (error);
459
460
if (ap->a_type == ACL_TYPE_ACCESS) {
461
/*
462
* Now that the EA is successfully updated, update the
463
* inode and mark it as changed.
464
*/
465
ext2_sync_inode_from_acl(ap->a_aclp, ip);
466
ip->i_flag |= IN_CHANGE;
467
error = ext2_update(ip->i_vnode, 1);
468
}
469
470
VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB);
471
472
return (error);
473
}
474
475
int
476
ext2_setacl(struct vop_setacl_args *ap)
477
{
478
if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
479
((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
480
return (EOPNOTSUPP);
481
482
if (ap->a_type == ACL_TYPE_NFS4)
483
return (ENOTSUP);
484
485
return (ext2_setacl_posix1e(ap));
486
}
487
488
/*
489
* Check the validity of an ACL for a file.
490
*/
491
int
492
ext2_aclcheck(struct vop_aclcheck_args *ap)
493
{
494
495
if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) ||
496
((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0))
497
return (EOPNOTSUPP);
498
499
if (ap->a_type == ACL_TYPE_NFS4)
500
return (ENOTSUP);
501
502
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
503
return (EINVAL);
504
505
/*
506
* Verify we understand this type of ACL, and that it applies
507
* to this kind of object.
508
* Rely on the acl_posix1e_check() routine to verify the contents.
509
*/
510
switch (ap->a_type) {
511
case ACL_TYPE_ACCESS:
512
break;
513
514
case ACL_TYPE_DEFAULT:
515
if (ap->a_vp->v_type != VDIR)
516
return (EINVAL);
517
break;
518
519
default:
520
return (EINVAL);
521
}
522
523
return (acl_posix1e_check(ap->a_aclp));
524
}
525
526
#endif /* UFS_ACL */
527
528