1 /*-
2 * Copyright (c) 2008 Edward Tomasz NapieraĆa <trasz@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 /*
28 * ACL support routines specific to NFSv4 access control lists. These are
29 * utility routines for code common across file systems implementing NFSv4
30 * ACLs.
31 */
32
33 #ifdef _KERNEL
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: releng/8.0/sys/kern/subr_acl_nfs4.c 193850 2009-06-09 19:51:22Z trasz $");
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/mount.h>
40 #include <sys/priv.h>
41 #include <sys/vnode.h>
42 #include <sys/errno.h>
43 #include <sys/stat.h>
44 #include <sys/acl.h>
45 #else
46 #include <errno.h>
47 #include <assert.h>
48 #include <sys/acl.h>
49 #include <sys/stat.h>
50 #define KASSERT(a, b) assert(a)
51 #define CTASSERT(a)
52 #endif
53
54 static int
55 _acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm,
56 acl_entry_type_t entry_type)
57 {
58 if (entry->ae_tag != tag)
59 return (0);
60
61 if (entry->ae_id != ACL_UNDEFINED_ID)
62 return (0);
63
64 if (entry->ae_perm != perm)
65 return (0);
66
67 if (entry->ae_entry_type != entry_type)
68 return (0);
69
70 if (entry->ae_flags != 0)
71 return (0);
72
73 return (1);
74 }
75
76 static struct acl_entry *
77 _acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm,
78 acl_entry_type_t entry_type)
79 {
80 struct acl_entry *entry;
81
82 KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES,
83 ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES"));
84
85 entry = &(aclp->acl_entry[aclp->acl_cnt]);
86 aclp->acl_cnt++;
87
88 entry->ae_tag = tag;
89 entry->ae_id = ACL_UNDEFINED_ID;
90 entry->ae_perm = perm;
91 entry->ae_entry_type = entry_type;
92 entry->ae_flags = 0;
93
94 return (entry);
95 }
96
97 static struct acl_entry *
98 _acl_duplicate_entry(struct acl *aclp, int entry_index)
99 {
100 int i;
101
102 KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES,
103 ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES"));
104
105 for (i = aclp->acl_cnt; i > entry_index; i--)
106 aclp->acl_entry[i] = aclp->acl_entry[i - 1];
107
108 aclp->acl_cnt++;
109
110 return (&(aclp->acl_entry[entry_index + 1]));
111 }
112
113 void
114 acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id)
115 {
116 int i, meets, must_append;
117 struct acl_entry *entry, *copy, *previous,
118 *a1, *a2, *a3, *a4, *a5, *a6;
119 mode_t amode;
120 const int READ = 04;
121 const int WRITE = 02;
122 const int EXEC = 01;
123
124 KASSERT(aclp->acl_cnt >= 0, ("aclp->acl_cnt >= 0"));
125 KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES,
126 ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
127
128 /*
129 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
130 *
131 * 3.16.6.3. Applying a Mode to an Existing ACL
132 */
133
134 /*
135 * 1. For each ACE:
136 */
137 for (i = 0; i < aclp->acl_cnt; i++) {
138 entry = &(aclp->acl_entry[i]);
139
140 /*
141 * 1.1. If the type is neither ALLOW or DENY - skip.
142 */
143 if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW &&
144 entry->ae_entry_type != ACL_ENTRY_TYPE_DENY)
145 continue;
146
147 /*
148 * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip.
149 */
150 if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY)
151 continue;
152
153 /*
154 * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT
155 * are set:
156 */
157 if (entry->ae_flags &
158 (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) {
159 /*
160 * 1.3.1. A copy of the current ACE is made, and placed
161 * in the ACL immediately following the current
162 * ACE.
163 */
164 copy = _acl_duplicate_entry(aclp, i);
165
166 /*
167 * 1.3.2. In the first ACE, the flag
168 * ACL_ENTRY_INHERIT_ONLY is set.
169 */
170 entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
171
172 /*
173 * 1.3.3. In the second ACE, the following flags
174 * are cleared:
175 * ACL_ENTRY_FILE_INHERIT,
176 * ACL_ENTRY_DIRECTORY_INHERIT,
177 * ACL_ENTRY_NO_PROPAGATE_INHERIT.
178 */
179 copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT |
180 ACL_ENTRY_DIRECTORY_INHERIT |
181 ACL_ENTRY_NO_PROPAGATE_INHERIT);
182
183 /*
184 * The algorithm continues on with the second ACE.
185 */
186 i++;
187 entry = copy;
188 }
189
190 /*
191 * 1.4. If it's owner@, group@ or everyone@ entry, clear
192 * ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA
193 * and ACL_EXECUTE. Continue to the next entry.
194 */
195 if (entry->ae_tag == ACL_USER_OBJ ||
196 entry->ae_tag == ACL_GROUP_OBJ ||
197 entry->ae_tag == ACL_EVERYONE) {
198 entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA |
199 ACL_APPEND_DATA | ACL_EXECUTE);
200 continue;
201 }
202
203 /*
204 * 1.5. Otherwise, if the "who" field did not match one
205 * of OWNER@, GROUP@, EVERYONE@:
206 *
207 * 1.5.1. If the type is ALLOW, check the preceding ACE.
208 * If it does not meet all of the following criteria:
209 */
210 if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW)
211 continue;
212
213 meets = 0;
214 if (i > 0) {
215 meets = 1;
216 previous = &(aclp->acl_entry[i - 1]);
217
218 /*
219 * 1.5.1.1. The type field is DENY,
220 */
221 if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY)
222 meets = 0;
223
224 /*
225 * 1.5.1.2. The "who" field is the same as the current
226 * ACE,
227 *
228 * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP
229 * is the same as it is in the current ACE,
230 * and no other flag bits are set,
231 */
232 if (previous->ae_id != entry->ae_id ||
233 previous->ae_tag != entry->ae_tag)
234 meets = 0;
235
236 if (previous->ae_flags)
237 meets = 0;
238
239 /*
240 * 1.5.1.4. The mask bits are a subset of the mask bits
241 * of the current ACE, and are also subset of
242 * the following: ACL_READ_DATA,
243 * ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE
244 */
245 if (previous->ae_perm & ~(entry->ae_perm))
246 meets = 0;
247
248 if (previous->ae_perm & ~(ACL_READ_DATA |
249 ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE))
250 meets = 0;
251 }
252
253 if (!meets) {
254 /*
255 * Then the ACE of type DENY, with a who equal
256 * to the current ACE, flag bits equal to
257 * (<current ACE flags> & <ACE_IDENTIFIER_GROUP>)
258 * and no mask bits, is prepended.
259 */
260 previous = entry;
261 entry = _acl_duplicate_entry(aclp, i);
262
263 /* Adjust counter, as we've just added an entry. */
264 i++;
265
266 previous->ae_tag = entry->ae_tag;
267 previous->ae_id = entry->ae_id;
268 previous->ae_flags = entry->ae_flags;
269 previous->ae_perm = 0;
270 previous->ae_entry_type = ACL_ENTRY_TYPE_DENY;
271 }
272
273 /*
274 * 1.5.2. The following modifications are made to the prepended
275 * ACE. The intent is to mask the following ACE
276 * to disallow ACL_READ_DATA, ACL_WRITE_DATA,
277 * ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group
278 * permissions of the new mode. As a special case,
279 * if the ACE matches the current owner of the file,
280 * the owner bits are used, rather than the group bits.
281 * This is reflected in the algorithm below.
282 */
283 amode = mode >> 3;
284
285 /*
286 * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field
287 * in ACE matches the owner of the file, we shift amode three
288 * more bits, in order to have the owner permission bits
289 * placed in the three low order bits of amode.
290 */
291 if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id)
292 amode = amode >> 3;
293
294 if (entry->ae_perm & ACL_READ_DATA) {
295 if (amode & READ)
296 previous->ae_perm &= ~ACL_READ_DATA;
297 else
298 previous->ae_perm |= ACL_READ_DATA;
299 }
300
301 if (entry->ae_perm & ACL_WRITE_DATA) {
302 if (amode & WRITE)
303 previous->ae_perm &= ~ACL_WRITE_DATA;
304 else
305 previous->ae_perm |= ACL_WRITE_DATA;
306 }
307
308 if (entry->ae_perm & ACL_APPEND_DATA) {
309 if (amode & WRITE)
310 previous->ae_perm &= ~ACL_APPEND_DATA;
311 else
312 previous->ae_perm |= ACL_APPEND_DATA;
313 }
314
315 if (entry->ae_perm & ACL_EXECUTE) {
316 if (amode & EXEC)
317 previous->ae_perm &= ~ACL_EXECUTE;
318 else
319 previous->ae_perm |= ACL_EXECUTE;
320 }
321
322 /*
323 * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags
324 * of the ALLOW ace:
325 *
326 * XXX: This point is not there in the Falkner's draft.
327 */
328 if (entry->ae_tag == ACL_GROUP &&
329 entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) {
330 mode_t extramode, ownermode;
331 extramode = (mode >> 3) & 07;
332 ownermode = mode >> 6;
333 extramode &= ~ownermode;
334
335 if (extramode) {
336 if (extramode & READ) {
337 entry->ae_perm &= ~ACL_READ_DATA;
338 previous->ae_perm &= ~ACL_READ_DATA;
339 }
340
341 if (extramode & WRITE) {
342 entry->ae_perm &=
343 ~(ACL_WRITE_DATA | ACL_APPEND_DATA);
344 previous->ae_perm &=
345 ~(ACL_WRITE_DATA | ACL_APPEND_DATA);
346 }
347
348 if (extramode & EXEC) {
349 entry->ae_perm &= ~ACL_EXECUTE;
350 previous->ae_perm &= ~ACL_EXECUTE;
351 }
352 }
353 }
354 }
355
356 /*
357 * 2. If there at least six ACEs, the final six ACEs are examined.
358 * If they are not equal to what we want, append six ACEs.
359 */
360 must_append = 0;
361 if (aclp->acl_cnt < 6) {
362 must_append = 1;
363 } else {
364 a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]);
365 a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]);
366 a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]);
367 a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]);
368 a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]);
369 a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]);
370
371 if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0,
372 ACL_ENTRY_TYPE_DENY))
373 must_append = 1;
374 if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL |
375 ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
376 ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW))
377 must_append = 1;
378 if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0,
379 ACL_ENTRY_TYPE_DENY))
380 must_append = 1;
381 if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0,
382 ACL_ENTRY_TYPE_ALLOW))
383 must_append = 1;
384 if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL |
385 ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
386 ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY))
387 must_append = 1;
388 if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL |
389 ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS |
390 ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW))
391 must_append = 1;
392 }
393
394 if (must_append) {
395 KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES,
396 ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
397
398 a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY);
399 a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL |
400 ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
401 ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW);
402 a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY);
403 a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW);
404 a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL |
405 ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
406 ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY);
407 a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL |
408 ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS |
409 ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW);
410
411 KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL &&
412 a5 != NULL && a6 != NULL, ("couldn't append to ACL."));
413 }
414
415 /*
416 * 3. The final six ACEs are adjusted according to the incoming mode.
417 */
418 if (mode & S_IRUSR)
419 a2->ae_perm |= ACL_READ_DATA;
420 else
421 a1->ae_perm |= ACL_READ_DATA;
422 if (mode & S_IWUSR)
423 a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
424 else
425 a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
426 if (mode & S_IXUSR)
427 a2->ae_perm |= ACL_EXECUTE;
428 else
429 a1->ae_perm |= ACL_EXECUTE;
430
431 if (mode & S_IRGRP)
432 a4->ae_perm |= ACL_READ_DATA;
433 else
434 a3->ae_perm |= ACL_READ_DATA;
435 if (mode & S_IWGRP)
436 a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
437 else
438 a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
439 if (mode & S_IXGRP)
440 a4->ae_perm |= ACL_EXECUTE;
441 else
442 a3->ae_perm |= ACL_EXECUTE;
443
444 if (mode & S_IROTH)
445 a6->ae_perm |= ACL_READ_DATA;
446 else
447 a5->ae_perm |= ACL_READ_DATA;
448 if (mode & S_IWOTH)
449 a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
450 else
451 a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
452 if (mode & S_IXOTH)
453 a6->ae_perm |= ACL_EXECUTE;
454 else
455 a5->ae_perm |= ACL_EXECUTE;
456 }
457
458 void
459 acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp)
460 {
461 int i;
462 mode_t old_mode = *_mode, mode = 0, seen = 0;
463 const struct acl_entry *entry;
464
465 KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0"));
466 KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES,
467 ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
468
469 /*
470 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
471 *
472 * 3.16.6.1. Recomputing mode upon SETATTR of ACL
473 */
474
475 for (i = 0; i < aclp->acl_cnt; i++) {
476 entry = &(aclp->acl_entry[i]);
477
478 if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW &&
479 entry->ae_entry_type != ACL_ENTRY_TYPE_DENY)
480 continue;
481
482 if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY)
483 continue;
484
485 if (entry->ae_tag == ACL_USER_OBJ) {
486 if ((entry->ae_perm & ACL_READ_DATA) &&
487 ((seen & S_IRUSR) == 0)) {
488 seen |= S_IRUSR;
489 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
490 mode |= S_IRUSR;
491 }
492 if ((entry->ae_perm & ACL_WRITE_DATA) &&
493 ((seen & S_IWUSR) == 0)) {
494 seen |= S_IWUSR;
495 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
496 mode |= S_IWUSR;
497 }
498 if ((entry->ae_perm & ACL_EXECUTE) &&
499 ((seen & S_IXUSR) == 0)) {
500 seen |= S_IXUSR;
501 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
502 mode |= S_IXUSR;
503 }
504 } else if (entry->ae_tag == ACL_GROUP_OBJ) {
505 if ((entry->ae_perm & ACL_READ_DATA) &&
506 ((seen & S_IRGRP) == 0)) {
507 seen |= S_IRGRP;
508 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
509 mode |= S_IRGRP;
510 }
511 if ((entry->ae_perm & ACL_WRITE_DATA) &&
512 ((seen & S_IWGRP) == 0)) {
513 seen |= S_IWGRP;
514 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
515 mode |= S_IWGRP;
516 }
517 if ((entry->ae_perm & ACL_EXECUTE) &&
518 ((seen & S_IXGRP) == 0)) {
519 seen |= S_IXGRP;
520 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
521 mode |= S_IXGRP;
522 }
523 } else if (entry->ae_tag == ACL_EVERYONE) {
524 if (entry->ae_perm & ACL_READ_DATA) {
525 if ((seen & S_IRUSR) == 0) {
526 seen |= S_IRUSR;
527 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
528 mode |= S_IRUSR;
529 }
530 if ((seen & S_IRGRP) == 0) {
531 seen |= S_IRGRP;
532 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
533 mode |= S_IRGRP;
534 }
535 if ((seen & S_IROTH) == 0) {
536 seen |= S_IROTH;
537 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
538 mode |= S_IROTH;
539 }
540 }
541 if (entry->ae_perm & ACL_WRITE_DATA) {
542 if ((seen & S_IWUSR) == 0) {
543 seen |= S_IWUSR;
544 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
545 mode |= S_IWUSR;
546 }
547 if ((seen & S_IWGRP) == 0) {
548 seen |= S_IWGRP;
549 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
550 mode |= S_IWGRP;
551 }
552 if ((seen & S_IWOTH) == 0) {
553 seen |= S_IWOTH;
554 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
555 mode |= S_IWOTH;
556 }
557 }
558 if (entry->ae_perm & ACL_EXECUTE) {
559 if ((seen & S_IXUSR) == 0) {
560 seen |= S_IXUSR;
561 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
562 mode |= S_IXUSR;
563 }
564 if ((seen & S_IXGRP) == 0) {
565 seen |= S_IXGRP;
566 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
567 mode |= S_IXGRP;
568 }
569 if ((seen & S_IXOTH) == 0) {
570 seen |= S_IXOTH;
571 if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
572 mode |= S_IXOTH;
573 }
574 }
575 }
576 }
577
578 *_mode = mode | (old_mode & ACL_PRESERVE_MASK);
579 }
Cache object: 23a56527ba15101218302f48db2d901b
|