xref: /freebsd/sys/kern/subr_acl_nfs4.c (revision 830940567b49bb0c08dfaed40418999e76616909)
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$");
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 }
580