subr_acl_nfs4.c revision 193850
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: head/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
54static 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
76static 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
97static 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
113void
114acl_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
458void
459acl_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