subr_acl_nfs4.c revision 193850
1171069Sdelphij/*-
2170808Sdelphij * Copyright (c) 2008 Edward Tomasz Napiera��a <trasz@FreeBSD.org>
3170808Sdelphij * All rights reserved.
4170808Sdelphij *
5170808Sdelphij * Redistribution and use in source and binary forms, with or without
6170808Sdelphij * modification, are permitted provided that the following conditions
7170808Sdelphij * are met:
8170808Sdelphij * 1. Redistributions of source code must retain the above copyright
9170808Sdelphij *    notice, this list of conditions and the following disclaimer.
10170808Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
11170808Sdelphij *    notice, this list of conditions and the following disclaimer in the
12170808Sdelphij *    documentation and/or other materials provided with the distribution.
13170808Sdelphij *
14170808Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15170808Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16170808Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17170808Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18170808Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19170808Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20170808Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21170808Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22170808Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23170808Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24170808Sdelphij * SUCH DAMAGE.
25170808Sdelphij */
26170808Sdelphij
27170808Sdelphij/*
28170808Sdelphij * ACL support routines specific to NFSv4 access control lists.  These are
29170808Sdelphij * utility routines for code common across file systems implementing NFSv4
30170808Sdelphij * ACLs.
31170808Sdelphij */
32170808Sdelphij
33170808Sdelphij#ifdef _KERNEL
34170808Sdelphij#include <sys/cdefs.h>
35170808Sdelphij__FBSDID("$FreeBSD: head/sys/kern/subr_acl_nfs4.c 193850 2009-06-09 19:51:22Z trasz $");
36170808Sdelphij
37170808Sdelphij#include <sys/param.h>
38170808Sdelphij#include <sys/systm.h>
39170808Sdelphij#include <sys/mount.h>
40170808Sdelphij#include <sys/priv.h>
41170808Sdelphij#include <sys/vnode.h>
42170808Sdelphij#include <sys/errno.h>
43170808Sdelphij#include <sys/stat.h>
44170808Sdelphij#include <sys/acl.h>
45170808Sdelphij#else
46170808Sdelphij#include <errno.h>
47170808Sdelphij#include <assert.h>
48170808Sdelphij#include <sys/acl.h>
49170808Sdelphij#include <sys/stat.h>
50170808Sdelphij#define KASSERT(a, b) assert(a)
51170808Sdelphij#define CTASSERT(a)
52170808Sdelphij#endif
53170808Sdelphij
54170808Sdelphijstatic int
55170808Sdelphij_acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm,
56170808Sdelphij    acl_entry_type_t entry_type)
57170808Sdelphij{
58170808Sdelphij	if (entry->ae_tag != tag)
59170808Sdelphij		return (0);
60170808Sdelphij
61170808Sdelphij	if (entry->ae_id != ACL_UNDEFINED_ID)
62170808Sdelphij		return (0);
63170808Sdelphij
64170808Sdelphij	if (entry->ae_perm != perm)
65170808Sdelphij		return (0);
66170808Sdelphij
67170808Sdelphij	if (entry->ae_entry_type != entry_type)
68170808Sdelphij		return (0);
69170808Sdelphij
70170808Sdelphij	if (entry->ae_flags != 0)
71170808Sdelphij		return (0);
72171069Sdelphij
73170808Sdelphij	return (1);
74170808Sdelphij}
75170808Sdelphij
76170808Sdelphijstatic struct acl_entry *
77170808Sdelphij_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm,
78170808Sdelphij    acl_entry_type_t entry_type)
79170808Sdelphij{
80170808Sdelphij	struct acl_entry *entry;
81170808Sdelphij
82170808Sdelphij	KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES,
83170808Sdelphij	    ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES"));
84170808Sdelphij
85170808Sdelphij	entry = &(aclp->acl_entry[aclp->acl_cnt]);
86170808Sdelphij	aclp->acl_cnt++;
87170808Sdelphij
88170808Sdelphij	entry->ae_tag = tag;
89170808Sdelphij	entry->ae_id = ACL_UNDEFINED_ID;
90170808Sdelphij	entry->ae_perm = perm;
91170808Sdelphij	entry->ae_entry_type = entry_type;
92170808Sdelphij	entry->ae_flags = 0;
93170808Sdelphij
94170808Sdelphij	return (entry);
95170808Sdelphij}
96170808Sdelphij
97170808Sdelphijstatic struct acl_entry *
98171799Sdelphij_acl_duplicate_entry(struct acl *aclp, int entry_index)
99171799Sdelphij{
100171799Sdelphij	int i;
101171799Sdelphij
102170808Sdelphij	KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES,
103170808Sdelphij	    ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES"));
104171799Sdelphij
105171799Sdelphij	for (i = aclp->acl_cnt; i > entry_index; i--)
106170808Sdelphij		aclp->acl_entry[i] = aclp->acl_entry[i - 1];
107171799Sdelphij
108171799Sdelphij	aclp->acl_cnt++;
109171799Sdelphij
110171799Sdelphij	return (&(aclp->acl_entry[entry_index + 1]));
111171799Sdelphij}
112170808Sdelphij
113170808Sdelphijvoid
114170808Sdelphijacl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id)
115170808Sdelphij{
116170808Sdelphij	int i, meets, must_append;
117170808Sdelphij	struct acl_entry *entry, *copy, *previous,
118170808Sdelphij	    *a1, *a2, *a3, *a4, *a5, *a6;
119170808Sdelphij	mode_t amode;
120170808Sdelphij	const int READ = 04;
121170808Sdelphij	const int WRITE = 02;
122170808Sdelphij	const int EXEC = 01;
123170808Sdelphij
124170808Sdelphij	KASSERT(aclp->acl_cnt >= 0, ("aclp->acl_cnt >= 0"));
125170808Sdelphij	KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES,
126170808Sdelphij	    ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
127170808Sdelphij
128170808Sdelphij	/*
129170808Sdelphij	 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
130170808Sdelphij	 *
131170808Sdelphij	 * 3.16.6.3. Applying a Mode to an Existing ACL
132170808Sdelphij	 */
133170808Sdelphij
134170808Sdelphij	/*
135170808Sdelphij	 * 1. For each ACE:
136170808Sdelphij	 */
137170808Sdelphij	for (i = 0; i < aclp->acl_cnt; i++) {
138170808Sdelphij		entry = &(aclp->acl_entry[i]);
139170808Sdelphij
140170808Sdelphij		/*
141170808Sdelphij		 * 1.1. If the type is neither ALLOW or DENY - skip.
142170808Sdelphij		 */
143170808Sdelphij		if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW &&
144170808Sdelphij		    entry->ae_entry_type != ACL_ENTRY_TYPE_DENY)
145170808Sdelphij			continue;
146170808Sdelphij
147170808Sdelphij		/*
148170808Sdelphij		 * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip.
149170808Sdelphij		 */
150170808Sdelphij		if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY)
151170808Sdelphij			continue;
152170808Sdelphij
153170808Sdelphij		/*
154170808Sdelphij		 * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT
155170808Sdelphij		 *      are set:
156170808Sdelphij		 */
157170808Sdelphij		if (entry->ae_flags &
158170808Sdelphij		    (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) {
159170808Sdelphij			/*
160170808Sdelphij			 * 1.3.1. A copy of the current ACE is made, and placed
161170808Sdelphij			 *        in the ACL immediately following the current
162170808Sdelphij			 *        ACE.
163170808Sdelphij			 */
164170808Sdelphij			copy = _acl_duplicate_entry(aclp, i);
165170808Sdelphij
166170808Sdelphij			/*
167170808Sdelphij			 * 1.3.2. In the first ACE, the flag
168170808Sdelphij			 *        ACL_ENTRY_INHERIT_ONLY is set.
169171070Sdelphij			 */
170170808Sdelphij			entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
171171799Sdelphij
172171799Sdelphij			/*
173170808Sdelphij			 * 1.3.3. In the second ACE, the following flags
174170808Sdelphij			 *        are cleared:
175170808Sdelphij			 *        ACL_ENTRY_FILE_INHERIT,
176170808Sdelphij			 *        ACL_ENTRY_DIRECTORY_INHERIT,
177170808Sdelphij			 *        ACL_ENTRY_NO_PROPAGATE_INHERIT.
178170808Sdelphij			 */
179170808Sdelphij			copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT |
180170808Sdelphij			    ACL_ENTRY_DIRECTORY_INHERIT |
181170808Sdelphij			    ACL_ENTRY_NO_PROPAGATE_INHERIT);
182170808Sdelphij
183171070Sdelphij			/*
184170808Sdelphij			 * The algorithm continues on with the second ACE.
185170808Sdelphij			 */
186171799Sdelphij			i++;
187171799Sdelphij			entry = copy;
188171799Sdelphij		}
189170808Sdelphij
190170808Sdelphij		/*
191170808Sdelphij		 * 1.4. If it's owner@, group@ or everyone@ entry, clear
192170808Sdelphij		 *      ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA
193170808Sdelphij		 *      and ACL_EXECUTE.  Continue to the next entry.
194170808Sdelphij		 */
195170808Sdelphij		if (entry->ae_tag == ACL_USER_OBJ ||
196170808Sdelphij		    entry->ae_tag == ACL_GROUP_OBJ ||
197170808Sdelphij		    entry->ae_tag == ACL_EVERYONE) {
198170808Sdelphij			entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA |
199170808Sdelphij			    ACL_APPEND_DATA | ACL_EXECUTE);
200170808Sdelphij			continue;
201170808Sdelphij		}
202170808Sdelphij
203170808Sdelphij		/*
204170808Sdelphij		 * 1.5. Otherwise, if the "who" field did not match one
205170808Sdelphij		 *      of OWNER@, GROUP@, EVERYONE@:
206170808Sdelphij		 *
207170808Sdelphij		 * 1.5.1. If the type is ALLOW, check the preceding ACE.
208170808Sdelphij		 *        If it does not meet all of the following criteria:
209171069Sdelphij		 */
210170808Sdelphij		if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW)
211170808Sdelphij			continue;
212170808Sdelphij
213170808Sdelphij		meets = 0;
214170808Sdelphij		if (i > 0) {
215170808Sdelphij			meets = 1;
216170808Sdelphij			previous = &(aclp->acl_entry[i - 1]);
217170808Sdelphij
218170808Sdelphij			/*
219170808Sdelphij			 * 1.5.1.1. The type field is DENY,
220170808Sdelphij			 */
221170808Sdelphij			if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY)
222170808Sdelphij				meets = 0;
223171069Sdelphij
224170808Sdelphij			/*
225170808Sdelphij			 * 1.5.1.2. The "who" field is the same as the current
226170808Sdelphij			 *          ACE,
227170808Sdelphij			 *
228170808Sdelphij			 * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP
229170808Sdelphij			 *          is the same as it is in the current ACE,
230170808Sdelphij			 *          and no other flag bits are set,
231170808Sdelphij			 */
232170808Sdelphij			if (previous->ae_id != entry->ae_id ||
233170808Sdelphij			    previous->ae_tag != entry->ae_tag)
234170808Sdelphij				meets = 0;
235170808Sdelphij
236170808Sdelphij			if (previous->ae_flags)
237170808Sdelphij				meets = 0;
238170808Sdelphij
239170808Sdelphij			/*
240171069Sdelphij			 * 1.5.1.4. The mask bits are a subset of the mask bits
241170808Sdelphij			 *          of the current ACE, and are also subset of
242170808Sdelphij			 *          the following: ACL_READ_DATA,
243170808Sdelphij			 *          ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE
244170808Sdelphij			 */
245170808Sdelphij			if (previous->ae_perm & ~(entry->ae_perm))
246170808Sdelphij				meets = 0;
247170808Sdelphij
248170808Sdelphij			if (previous->ae_perm & ~(ACL_READ_DATA |
249170808Sdelphij			    ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE))
250170808Sdelphij				meets = 0;
251170808Sdelphij		}
252171070Sdelphij
253170808Sdelphij		if (!meets) {
254170808Sdelphij			/*
255170808Sdelphij		 	 * Then the ACE of type DENY, with a who equal
256170808Sdelphij			 * to the current ACE, flag bits equal to
257170808Sdelphij			 * (<current ACE flags> & <ACE_IDENTIFIER_GROUP>)
258170808Sdelphij			 * and no mask bits, is prepended.
259170808Sdelphij			 */
260170808Sdelphij			previous = entry;
261170808Sdelphij			entry = _acl_duplicate_entry(aclp, i);
262170808Sdelphij
263170808Sdelphij			/* Adjust counter, as we've just added an entry. */
264171070Sdelphij			i++;
265170808Sdelphij
266170808Sdelphij			previous->ae_tag = entry->ae_tag;
267170808Sdelphij			previous->ae_id = entry->ae_id;
268170808Sdelphij			previous->ae_flags = entry->ae_flags;
269170808Sdelphij			previous->ae_perm = 0;
270170808Sdelphij			previous->ae_entry_type = ACL_ENTRY_TYPE_DENY;
271170808Sdelphij		}
272170808Sdelphij
273171069Sdelphij		/*
274170808Sdelphij		 * 1.5.2. The following modifications are made to the prepended
275170808Sdelphij		 *        ACE.  The intent is to mask the following ACE
276170808Sdelphij		 *        to disallow ACL_READ_DATA, ACL_WRITE_DATA,
277170808Sdelphij		 *        ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group
278170808Sdelphij		 *        permissions of the new mode.  As a special case,
279170808Sdelphij		 *        if the ACE matches the current owner of the file,
280170808Sdelphij		 *        the owner bits are used, rather than the group bits.
281170808Sdelphij		 *        This is reflected in the algorithm below.
282170808Sdelphij		 */
283170808Sdelphij		amode = mode >> 3;
284170808Sdelphij
285170808Sdelphij		/*
286170808Sdelphij		 * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field
287170808Sdelphij		 * in ACE matches the owner of the file, we shift amode three
288170808Sdelphij		 * more bits, in order to have the owner permission bits
289170808Sdelphij		 * placed in the three low order bits of amode.
290170808Sdelphij		 */
291170808Sdelphij		if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id)
292170808Sdelphij			amode = amode >> 3;
293170808Sdelphij
294170808Sdelphij		if (entry->ae_perm & ACL_READ_DATA) {
295170808Sdelphij			if (amode & READ)
296170808Sdelphij				previous->ae_perm &= ~ACL_READ_DATA;
297170808Sdelphij			else
298170808Sdelphij				previous->ae_perm |= ACL_READ_DATA;
299170808Sdelphij		}
300170808Sdelphij
301170808Sdelphij		if (entry->ae_perm & ACL_WRITE_DATA) {
302170808Sdelphij			if (amode & WRITE)
303170808Sdelphij				previous->ae_perm &= ~ACL_WRITE_DATA;
304170808Sdelphij			else
305170808Sdelphij				previous->ae_perm |= ACL_WRITE_DATA;
306170808Sdelphij		}
307170808Sdelphij
308170808Sdelphij		if (entry->ae_perm & ACL_APPEND_DATA) {
309170808Sdelphij			if (amode & WRITE)
310170808Sdelphij				previous->ae_perm &= ~ACL_APPEND_DATA;
311170808Sdelphij			else
312170808Sdelphij				previous->ae_perm |= ACL_APPEND_DATA;
313170808Sdelphij		}
314170808Sdelphij
315170808Sdelphij		if (entry->ae_perm & ACL_EXECUTE) {
316170808Sdelphij			if (amode & EXEC)
317170808Sdelphij				previous->ae_perm &= ~ACL_EXECUTE;
318170808Sdelphij			else
319170808Sdelphij				previous->ae_perm |= ACL_EXECUTE;
320170808Sdelphij		}
321170808Sdelphij
322170808Sdelphij		/*
323170808Sdelphij		 * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags
324170808Sdelphij		 *        of the ALLOW ace:
325170808Sdelphij		 *
326170808Sdelphij		 * XXX: This point is not there in the Falkner's draft.
327170808Sdelphij		 */
328170808Sdelphij		if (entry->ae_tag == ACL_GROUP &&
329170808Sdelphij		    entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) {
330170808Sdelphij			mode_t extramode, ownermode;
331170808Sdelphij			extramode = (mode >> 3) & 07;
332170808Sdelphij			ownermode = mode >> 6;
333170808Sdelphij			extramode &= ~ownermode;
334170808Sdelphij
335170808Sdelphij			if (extramode) {
336170808Sdelphij				if (extramode & READ) {
337170808Sdelphij					entry->ae_perm &= ~ACL_READ_DATA;
338170808Sdelphij					previous->ae_perm &= ~ACL_READ_DATA;
339170808Sdelphij				}
340170808Sdelphij
341170808Sdelphij				if (extramode & WRITE) {
342170808Sdelphij					entry->ae_perm &=
343170808Sdelphij					    ~(ACL_WRITE_DATA | ACL_APPEND_DATA);
344170808Sdelphij					previous->ae_perm &=
345170808Sdelphij					    ~(ACL_WRITE_DATA | ACL_APPEND_DATA);
346170808Sdelphij				}
347170808Sdelphij
348170808Sdelphij				if (extramode & EXEC) {
349170808Sdelphij					entry->ae_perm &= ~ACL_EXECUTE;
350170808Sdelphij					previous->ae_perm &= ~ACL_EXECUTE;
351170808Sdelphij				}
352170808Sdelphij			}
353170808Sdelphij		}
354170808Sdelphij	}
355170808Sdelphij
356170808Sdelphij	/*
357170808Sdelphij	 * 2. If there at least six ACEs, the final six ACEs are examined.
358170808Sdelphij	 *    If they are not equal to what we want, append six ACEs.
359170808Sdelphij	 */
360170808Sdelphij	must_append = 0;
361170808Sdelphij	if (aclp->acl_cnt < 6) {
362170808Sdelphij		must_append = 1;
363170808Sdelphij	} else {
364170808Sdelphij		a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]);
365170808Sdelphij		a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]);
366170808Sdelphij		a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]);
367170808Sdelphij		a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]);
368170808Sdelphij		a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]);
369170808Sdelphij		a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]);
370170808Sdelphij
371170808Sdelphij		if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0,
372170808Sdelphij		    ACL_ENTRY_TYPE_DENY))
373170808Sdelphij			must_append = 1;
374170808Sdelphij		if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL |
375170808Sdelphij		    ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
376170808Sdelphij		    ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW))
377170808Sdelphij			must_append = 1;
378170808Sdelphij		if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0,
379170808Sdelphij		    ACL_ENTRY_TYPE_DENY))
380170808Sdelphij			must_append = 1;
381170808Sdelphij		if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0,
382170808Sdelphij		    ACL_ENTRY_TYPE_ALLOW))
383170808Sdelphij			must_append = 1;
384170808Sdelphij		if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL |
385170808Sdelphij		    ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
386170808Sdelphij		    ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY))
387170808Sdelphij			must_append = 1;
388170808Sdelphij		if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL |
389170808Sdelphij		    ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS |
390170808Sdelphij		    ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW))
391170808Sdelphij			must_append = 1;
392170808Sdelphij	}
393170808Sdelphij
394170808Sdelphij	if (must_append) {
395170808Sdelphij		KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES,
396170808Sdelphij		    ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
397170808Sdelphij
398170808Sdelphij		a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY);
399170808Sdelphij		a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL |
400170808Sdelphij		    ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
401170808Sdelphij		    ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW);
402170808Sdelphij		a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY);
403170808Sdelphij		a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW);
404170808Sdelphij		a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL |
405170808Sdelphij		    ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES |
406170808Sdelphij		    ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY);
407170808Sdelphij		a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL |
408170808Sdelphij		    ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS |
409170808Sdelphij		    ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW);
410170808Sdelphij
411170808Sdelphij		KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL &&
412170808Sdelphij		    a5 != NULL && a6 != NULL, ("couldn't append to ACL."));
413170808Sdelphij	}
414170808Sdelphij
415170808Sdelphij	/*
416170808Sdelphij	 * 3. The final six ACEs are adjusted according to the incoming mode.
417170808Sdelphij	 */
418170808Sdelphij	if (mode & S_IRUSR)
419170808Sdelphij		a2->ae_perm |= ACL_READ_DATA;
420170808Sdelphij	else
421170808Sdelphij		a1->ae_perm |= ACL_READ_DATA;
422170808Sdelphij	if (mode & S_IWUSR)
423170808Sdelphij		a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
424170808Sdelphij	else
425170808Sdelphij		a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
426170808Sdelphij	if (mode & S_IXUSR)
427170808Sdelphij		a2->ae_perm |= ACL_EXECUTE;
428170808Sdelphij	else
429170808Sdelphij		a1->ae_perm |= ACL_EXECUTE;
430170808Sdelphij
431170808Sdelphij	if (mode & S_IRGRP)
432170808Sdelphij		a4->ae_perm |= ACL_READ_DATA;
433170808Sdelphij	else
434170808Sdelphij		a3->ae_perm |= ACL_READ_DATA;
435170808Sdelphij	if (mode & S_IWGRP)
436170808Sdelphij		a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
437170808Sdelphij	else
438171070Sdelphij		a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
439170808Sdelphij	if (mode & S_IXGRP)
440170808Sdelphij		a4->ae_perm |= ACL_EXECUTE;
441170808Sdelphij	else
442170808Sdelphij		a3->ae_perm |= ACL_EXECUTE;
443170808Sdelphij
444170808Sdelphij	if (mode & S_IROTH)
445170808Sdelphij		a6->ae_perm |= ACL_READ_DATA;
446170808Sdelphij	else
447170808Sdelphij		a5->ae_perm |= ACL_READ_DATA;
448170808Sdelphij	if (mode & S_IWOTH)
449170808Sdelphij		a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
450170808Sdelphij	else
451170808Sdelphij		a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA);
452171489Sdelphij	if (mode & S_IXOTH)
453170808Sdelphij		a6->ae_perm |= ACL_EXECUTE;
454171489Sdelphij	else
455170808Sdelphij		a5->ae_perm |= ACL_EXECUTE;
456171489Sdelphij}
457171489Sdelphij
458171489Sdelphijvoid
459171489Sdelphijacl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp)
460171489Sdelphij{
461171489Sdelphij	int i;
462171489Sdelphij	mode_t old_mode = *_mode, mode = 0, seen = 0;
463170808Sdelphij	const struct acl_entry *entry;
464171489Sdelphij
465171489Sdelphij	KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0"));
466171489Sdelphij	KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES,
467171489Sdelphij	    ("aclp->acl_cnt <= ACL_MAX_ENTRIES"));
468170808Sdelphij
469171489Sdelphij	/*
470171489Sdelphij	 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
471170808Sdelphij	 *
472171489Sdelphij	 * 3.16.6.1. Recomputing mode upon SETATTR of ACL
473171489Sdelphij	 */
474171489Sdelphij
475171489Sdelphij	for (i = 0; i < aclp->acl_cnt; i++) {
476171489Sdelphij		entry = &(aclp->acl_entry[i]);
477171489Sdelphij
478171489Sdelphij		if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW &&
479171489Sdelphij		    entry->ae_entry_type != ACL_ENTRY_TYPE_DENY)
480170808Sdelphij			continue;
481170808Sdelphij
482171489Sdelphij		if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY)
483171489Sdelphij			continue;
484170808Sdelphij
485170808Sdelphij		if (entry->ae_tag == ACL_USER_OBJ) {
486171489Sdelphij			if ((entry->ae_perm & ACL_READ_DATA) &&
487171489Sdelphij			    ((seen & S_IRUSR) == 0)) {
488171489Sdelphij				seen |= S_IRUSR;
489171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
490171799Sdelphij					mode |= S_IRUSR;
491171489Sdelphij			}
492171489Sdelphij			if ((entry->ae_perm & ACL_WRITE_DATA) &&
493171489Sdelphij			     ((seen & S_IWUSR) == 0)) {
494171489Sdelphij				seen |= S_IWUSR;
495171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
496171489Sdelphij					mode |= S_IWUSR;
497171489Sdelphij			}
498171489Sdelphij			if ((entry->ae_perm & ACL_EXECUTE) &&
499171489Sdelphij			    ((seen & S_IXUSR) == 0)) {
500171489Sdelphij				seen |= S_IXUSR;
501171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
502171489Sdelphij					mode |= S_IXUSR;
503171489Sdelphij			}
504171489Sdelphij		} else if (entry->ae_tag == ACL_GROUP_OBJ) {
505171489Sdelphij			if ((entry->ae_perm & ACL_READ_DATA) &&
506170808Sdelphij			    ((seen & S_IRGRP) == 0)) {
507170808Sdelphij				seen |= S_IRGRP;
508171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
509171489Sdelphij					mode |= S_IRGRP;
510171489Sdelphij			}
511171489Sdelphij			if ((entry->ae_perm & ACL_WRITE_DATA) &&
512171489Sdelphij			    ((seen & S_IWGRP) == 0)) {
513171489Sdelphij				seen |= S_IWGRP;
514171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
515171489Sdelphij					mode |= S_IWGRP;
516171308Sdelphij			}
517171489Sdelphij			if ((entry->ae_perm & ACL_EXECUTE) &&
518171489Sdelphij			    ((seen & S_IXGRP) == 0)) {
519171489Sdelphij				seen |= S_IXGRP;
520171489Sdelphij				if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
521171489Sdelphij					mode |= S_IXGRP;
522171489Sdelphij			}
523171489Sdelphij		} else if (entry->ae_tag == ACL_EVERYONE) {
524171489Sdelphij			if (entry->ae_perm & ACL_READ_DATA) {
525171489Sdelphij				if ((seen & S_IRUSR) == 0) {
526170808Sdelphij					seen |= S_IRUSR;
527170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
528171069Sdelphij						mode |= S_IRUSR;
529170808Sdelphij				}
530170808Sdelphij				if ((seen & S_IRGRP) == 0) {
531170808Sdelphij					seen |= S_IRGRP;
532170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
533170808Sdelphij						mode |= S_IRGRP;
534170808Sdelphij				}
535170808Sdelphij				if ((seen & S_IROTH) == 0) {
536171489Sdelphij					seen |= S_IROTH;
537171489Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
538170808Sdelphij						mode |= S_IROTH;
539170808Sdelphij				}
540170808Sdelphij			}
541170808Sdelphij			if (entry->ae_perm & ACL_WRITE_DATA) {
542170808Sdelphij				if ((seen & S_IWUSR) == 0) {
543170808Sdelphij					seen |= S_IWUSR;
544170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
545170808Sdelphij						mode |= S_IWUSR;
546170808Sdelphij				}
547170808Sdelphij				if ((seen & S_IWGRP) == 0) {
548170808Sdelphij					seen |= S_IWGRP;
549170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
550170808Sdelphij						mode |= S_IWGRP;
551170808Sdelphij				}
552170808Sdelphij				if ((seen & S_IWOTH) == 0) {
553170808Sdelphij					seen |= S_IWOTH;
554170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
555170808Sdelphij						mode |= S_IWOTH;
556171489Sdelphij				}
557171489Sdelphij			}
558171489Sdelphij			if (entry->ae_perm & ACL_EXECUTE) {
559171489Sdelphij				if ((seen & S_IXUSR) == 0) {
560171489Sdelphij					seen |= S_IXUSR;
561171489Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
562171489Sdelphij						mode |= S_IXUSR;
563171489Sdelphij				}
564171489Sdelphij				if ((seen & S_IXGRP) == 0) {
565171489Sdelphij					seen |= S_IXGRP;
566171489Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
567170808Sdelphij						mode |= S_IXGRP;
568170808Sdelphij				}
569170808Sdelphij				if ((seen & S_IXOTH) == 0) {
570170808Sdelphij					seen |= S_IXOTH;
571170808Sdelphij					if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW)
572170808Sdelphij						mode |= S_IXOTH;
573170808Sdelphij				}
574170808Sdelphij			}
575171069Sdelphij		}
576171489Sdelphij	}
577171489Sdelphij
578171489Sdelphij	*_mode = mode | (old_mode & ACL_PRESERVE_MASK);
579171489Sdelphij}
580171489Sdelphij