1153323Srodrigc/*
2159451Srodrigc * Copyright (c) 2001-2002,2005 Silicon Graphics, Inc.
3159451Srodrigc * All Rights Reserved.
4153323Srodrigc *
5159451Srodrigc * This program is free software; you can redistribute it and/or
6159451Srodrigc * modify it under the terms of the GNU General Public License as
7153323Srodrigc * published by the Free Software Foundation.
8153323Srodrigc *
9159451Srodrigc * This program is distributed in the hope that it would be useful,
10159451Srodrigc * but WITHOUT ANY WARRANTY; without even the implied warranty of
11159451Srodrigc * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12159451Srodrigc * GNU General Public License for more details.
13153323Srodrigc *
14159451Srodrigc * You should have received a copy of the GNU General Public License
15159451Srodrigc * along with this program; if not, write the Free Software Foundation,
16159451Srodrigc * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17153323Srodrigc */
18153323Srodrigc#include "xfs.h"
19159451Srodrigc#include "xfs_fs.h"
20159451Srodrigc#include "xfs_types.h"
21159451Srodrigc#include "xfs_bit.h"
22153323Srodrigc#include "xfs_inum.h"
23159451Srodrigc#include "xfs_ag.h"
24153323Srodrigc#include "xfs_dir.h"
25153323Srodrigc#include "xfs_dir2.h"
26159451Srodrigc#include "xfs_bmap_btree.h"
27153323Srodrigc#include "xfs_alloc_btree.h"
28153323Srodrigc#include "xfs_ialloc_btree.h"
29153323Srodrigc#include "xfs_dir_sf.h"
30153323Srodrigc#include "xfs_dir2_sf.h"
31159451Srodrigc#include "xfs_attr_sf.h"
32153323Srodrigc#include "xfs_dinode.h"
33153323Srodrigc#include "xfs_inode.h"
34159451Srodrigc#include "xfs_btree.h"
35153323Srodrigc#include "xfs_acl.h"
36153323Srodrigc#include "xfs_mac.h"
37153323Srodrigc#include "xfs_attr.h"
38153323Srodrigc
39159451Srodrigc#include <linux/capability.h>
40153323Srodrigc#include <linux/posix_acl_xattr.h>
41153323Srodrigc
42153323SrodrigcSTATIC int	xfs_acl_setmode(xfs_vnode_t *, xfs_acl_t *, int *);
43153323SrodrigcSTATIC void     xfs_acl_filter_mode(mode_t, xfs_acl_t *);
44153323SrodrigcSTATIC void	xfs_acl_get_endian(xfs_acl_t *);
45153323SrodrigcSTATIC int	xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *);
46153323SrodrigcSTATIC int	xfs_acl_invalid(xfs_acl_t *);
47153323SrodrigcSTATIC void	xfs_acl_sync_mode(mode_t, xfs_acl_t *);
48153323SrodrigcSTATIC void	xfs_acl_get_attr(xfs_vnode_t *, xfs_acl_t *, int, int, int *);
49153323SrodrigcSTATIC void	xfs_acl_set_attr(xfs_vnode_t *, xfs_acl_t *, int, int *);
50153323SrodrigcSTATIC int	xfs_acl_allow_set(xfs_vnode_t *, int);
51153323Srodrigc
52153323Srodrigckmem_zone_t *xfs_acl_zone;
53153323Srodrigc
54153323Srodrigc
55153323Srodrigc/*
56153323Srodrigc * Test for existence of access ACL attribute as efficiently as possible.
57153323Srodrigc */
58153323Srodrigcint
59153323Srodrigcxfs_acl_vhasacl_access(
60153323Srodrigc	xfs_vnode_t		*vp)
61153323Srodrigc{
62153323Srodrigc	int		error;
63153323Srodrigc
64153323Srodrigc	xfs_acl_get_attr(vp, NULL, _ACL_TYPE_ACCESS, ATTR_KERNOVAL, &error);
65153323Srodrigc	return (error == 0);
66153323Srodrigc}
67153323Srodrigc
68153323Srodrigc/*
69153323Srodrigc * Test for existence of default ACL attribute as efficiently as possible.
70153323Srodrigc */
71153323Srodrigcint
72153323Srodrigcxfs_acl_vhasacl_default(
73153323Srodrigc	xfs_vnode_t		*vp)
74153323Srodrigc{
75153323Srodrigc	int		error;
76153323Srodrigc
77159451Srodrigc	if (!VN_ISDIR(vp))
78153323Srodrigc		return 0;
79153323Srodrigc	xfs_acl_get_attr(vp, NULL, _ACL_TYPE_DEFAULT, ATTR_KERNOVAL, &error);
80153323Srodrigc	return (error == 0);
81153323Srodrigc}
82153323Srodrigc
83153323Srodrigc/*
84153323Srodrigc * Convert from extended attribute representation to in-memory for XFS.
85153323Srodrigc */
86153323SrodrigcSTATIC int
87153323Srodrigcposix_acl_xattr_to_xfs(
88153323Srodrigc	posix_acl_xattr_header	*src,
89153323Srodrigc	size_t			size,
90153323Srodrigc	xfs_acl_t		*dest)
91153323Srodrigc{
92153323Srodrigc	posix_acl_xattr_entry	*src_entry;
93153323Srodrigc	xfs_acl_entry_t		*dest_entry;
94153323Srodrigc	int			n;
95153323Srodrigc
96153323Srodrigc	if (!src || !dest)
97153323Srodrigc		return EINVAL;
98153323Srodrigc
99153323Srodrigc	if (size < sizeof(posix_acl_xattr_header))
100153323Srodrigc		return EINVAL;
101153323Srodrigc
102153323Srodrigc	if (src->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
103159451Srodrigc		return EOPNOTSUPP;
104153323Srodrigc
105153323Srodrigc	memset(dest, 0, sizeof(xfs_acl_t));
106153323Srodrigc	dest->acl_cnt = posix_acl_xattr_count(size);
107153323Srodrigc	if (dest->acl_cnt < 0 || dest->acl_cnt > XFS_ACL_MAX_ENTRIES)
108153323Srodrigc		return EINVAL;
109153323Srodrigc
110153323Srodrigc	/*
111153323Srodrigc	 * acl_set_file(3) may request that we set default ACLs with
112153323Srodrigc	 * zero length -- defend (gracefully) against that here.
113153323Srodrigc	 */
114153323Srodrigc	if (!dest->acl_cnt)
115153323Srodrigc		return 0;
116153323Srodrigc
117153323Srodrigc	src_entry = (posix_acl_xattr_entry *)((char *)src + sizeof(*src));
118153323Srodrigc	dest_entry = &dest->acl_entry[0];
119153323Srodrigc
120153323Srodrigc	for (n = 0; n < dest->acl_cnt; n++, src_entry++, dest_entry++) {
121153323Srodrigc		dest_entry->ae_perm = le16_to_cpu(src_entry->e_perm);
122153323Srodrigc		if (_ACL_PERM_INVALID(dest_entry->ae_perm))
123153323Srodrigc			return EINVAL;
124153323Srodrigc		dest_entry->ae_tag  = le16_to_cpu(src_entry->e_tag);
125153323Srodrigc		switch(dest_entry->ae_tag) {
126153323Srodrigc		case ACL_USER:
127153323Srodrigc		case ACL_GROUP:
128153323Srodrigc			dest_entry->ae_id = le32_to_cpu(src_entry->e_id);
129153323Srodrigc			break;
130153323Srodrigc		case ACL_USER_OBJ:
131153323Srodrigc		case ACL_GROUP_OBJ:
132153323Srodrigc		case ACL_MASK:
133153323Srodrigc		case ACL_OTHER:
134153323Srodrigc			dest_entry->ae_id = ACL_UNDEFINED_ID;
135153323Srodrigc			break;
136153323Srodrigc		default:
137153323Srodrigc			return EINVAL;
138153323Srodrigc		}
139153323Srodrigc	}
140153323Srodrigc	if (xfs_acl_invalid(dest))
141153323Srodrigc		return EINVAL;
142153323Srodrigc
143153323Srodrigc	return 0;
144153323Srodrigc}
145153323Srodrigc
146153323Srodrigc/*
147159451Srodrigc * Comparison function called from xfs_sort().
148153323Srodrigc * Primary key is ae_tag, secondary key is ae_id.
149153323Srodrigc */
150153323SrodrigcSTATIC int
151153323Srodrigcxfs_acl_entry_compare(
152153323Srodrigc	const void	*va,
153153323Srodrigc	const void	*vb)
154153323Srodrigc{
155153323Srodrigc	xfs_acl_entry_t	*a = (xfs_acl_entry_t *)va,
156153323Srodrigc			*b = (xfs_acl_entry_t *)vb;
157153323Srodrigc
158153323Srodrigc	if (a->ae_tag == b->ae_tag)
159153323Srodrigc		return (a->ae_id - b->ae_id);
160153323Srodrigc	return (a->ae_tag - b->ae_tag);
161153323Srodrigc}
162153323Srodrigc
163153323Srodrigc/*
164153323Srodrigc * Convert from in-memory XFS to extended attribute representation.
165153323Srodrigc */
166153323SrodrigcSTATIC int
167153323Srodrigcposix_acl_xfs_to_xattr(
168153323Srodrigc	xfs_acl_t		*src,
169153323Srodrigc	posix_acl_xattr_header	*dest,
170153323Srodrigc	size_t			size)
171153323Srodrigc{
172153323Srodrigc	int			n;
173153323Srodrigc	size_t			new_size = posix_acl_xattr_size(src->acl_cnt);
174153323Srodrigc	posix_acl_xattr_entry	*dest_entry;
175153323Srodrigc	xfs_acl_entry_t		*src_entry;
176153323Srodrigc
177153323Srodrigc	if (size < new_size)
178153323Srodrigc		return -ERANGE;
179153323Srodrigc
180153323Srodrigc	/* Need to sort src XFS ACL by <ae_tag,ae_id> */
181159451Srodrigc	xfs_sort(src->acl_entry, src->acl_cnt, sizeof(src->acl_entry[0]),
182159451Srodrigc		 xfs_acl_entry_compare);
183153323Srodrigc
184153323Srodrigc	dest->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
185153323Srodrigc	dest_entry = &dest->a_entries[0];
186153323Srodrigc	src_entry = &src->acl_entry[0];
187153323Srodrigc	for (n = 0; n < src->acl_cnt; n++, dest_entry++, src_entry++) {
188153323Srodrigc		dest_entry->e_perm = cpu_to_le16(src_entry->ae_perm);
189153323Srodrigc		if (_ACL_PERM_INVALID(src_entry->ae_perm))
190153323Srodrigc			return -EINVAL;
191153323Srodrigc		dest_entry->e_tag  = cpu_to_le16(src_entry->ae_tag);
192153323Srodrigc		switch (src_entry->ae_tag) {
193153323Srodrigc		case ACL_USER:
194153323Srodrigc		case ACL_GROUP:
195153323Srodrigc			dest_entry->e_id = cpu_to_le32(src_entry->ae_id);
196153323Srodrigc				break;
197153323Srodrigc		case ACL_USER_OBJ:
198153323Srodrigc		case ACL_GROUP_OBJ:
199153323Srodrigc		case ACL_MASK:
200153323Srodrigc		case ACL_OTHER:
201153323Srodrigc			dest_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
202153323Srodrigc			break;
203153323Srodrigc		default:
204153323Srodrigc			return -EINVAL;
205153323Srodrigc		}
206153323Srodrigc	}
207153323Srodrigc	return new_size;
208153323Srodrigc}
209153323Srodrigc
210153323Srodrigcint
211153323Srodrigcxfs_acl_vget(
212153323Srodrigc	xfs_vnode_t		*vp,
213153323Srodrigc	void		*acl,
214153323Srodrigc	size_t		size,
215153323Srodrigc	int		kind)
216153323Srodrigc{
217153323Srodrigc	int			error;
218153323Srodrigc	xfs_acl_t		*xfs_acl = NULL;
219153323Srodrigc	posix_acl_xattr_header	*ext_acl = acl;
220153323Srodrigc	int			flags = 0;
221153323Srodrigc
222153323Srodrigc	VN_HOLD(vp);
223153323Srodrigc	if(size) {
224153323Srodrigc		if (!(_ACL_ALLOC(xfs_acl))) {
225153323Srodrigc			error = ENOMEM;
226153323Srodrigc			goto out;
227153323Srodrigc		}
228153323Srodrigc		memset(xfs_acl, 0, sizeof(xfs_acl_t));
229153323Srodrigc	} else
230153323Srodrigc		flags = ATTR_KERNOVAL;
231153323Srodrigc
232153323Srodrigc	xfs_acl_get_attr(vp, xfs_acl, kind, flags, &error);
233153323Srodrigc	if (error)
234153323Srodrigc		goto out;
235153323Srodrigc
236153323Srodrigc	if (!size) {
237153323Srodrigc		error = -posix_acl_xattr_size(XFS_ACL_MAX_ENTRIES);
238153323Srodrigc	} else {
239153323Srodrigc		if (xfs_acl_invalid(xfs_acl)) {
240153323Srodrigc			error = EINVAL;
241153323Srodrigc			goto out;
242153323Srodrigc		}
243153323Srodrigc		if (kind == _ACL_TYPE_ACCESS) {
244153323Srodrigc			xfs_vattr_t	va;
245153323Srodrigc
246153323Srodrigc			va.va_mask = XFS_AT_MODE;
247153323Srodrigc			XVOP_GETATTR(vp, &va, 0, sys_cred, error);
248153323Srodrigc			if (error)
249153323Srodrigc				goto out;
250153323Srodrigc			xfs_acl_sync_mode(va.va_mode, xfs_acl);
251153323Srodrigc		}
252153323Srodrigc		error = -posix_acl_xfs_to_xattr(xfs_acl, ext_acl, size);
253153323Srodrigc	}
254153323Srodrigcout:
255153323Srodrigc	VN_RELE(vp);
256153323Srodrigc	if(xfs_acl)
257153323Srodrigc		_ACL_FREE(xfs_acl);
258153323Srodrigc	return -error;
259153323Srodrigc}
260153323Srodrigc
261153323Srodrigcint
262153323Srodrigcxfs_acl_vremove(
263153323Srodrigc	xfs_vnode_t		*vp,
264153323Srodrigc	int		kind)
265153323Srodrigc{
266153323Srodrigc	int		error;
267153323Srodrigc
268153323Srodrigc	VN_HOLD(vp);
269153323Srodrigc	error = xfs_acl_allow_set(vp, kind);
270153323Srodrigc	if (!error) {
271153323Srodrigc		XVOP_ATTR_REMOVE(vp, kind == _ACL_TYPE_DEFAULT?
272153323Srodrigc				SGI_ACL_DEFAULT: SGI_ACL_FILE,
273153323Srodrigc				ATTR_ROOT, sys_cred, error);
274153323Srodrigc		if (error == ENOATTR)
275153323Srodrigc			error = 0;	/* 'scool */
276153323Srodrigc	}
277153323Srodrigc	VN_RELE(vp);
278153323Srodrigc	return -error;
279153323Srodrigc}
280153323Srodrigc
281153323Srodrigcint
282153323Srodrigcxfs_acl_vset(
283153323Srodrigc	xfs_vnode_t			*vp,
284153323Srodrigc	void			*acl,
285153323Srodrigc	size_t			size,
286153323Srodrigc	int			kind)
287153323Srodrigc{
288153323Srodrigc	posix_acl_xattr_header	*ext_acl = acl;
289153323Srodrigc	xfs_acl_t		*xfs_acl;
290153323Srodrigc	int			error;
291153323Srodrigc	int			basicperms = 0; /* more than std unix perms? */
292153323Srodrigc
293153323Srodrigc	if (!acl)
294153323Srodrigc		return -EINVAL;
295153323Srodrigc
296153323Srodrigc	if (!(_ACL_ALLOC(xfs_acl)))
297153323Srodrigc		return -ENOMEM;
298153323Srodrigc
299153323Srodrigc	error = posix_acl_xattr_to_xfs(ext_acl, size, xfs_acl);
300153323Srodrigc	if (error) {
301153323Srodrigc		_ACL_FREE(xfs_acl);
302153323Srodrigc		return -error;
303153323Srodrigc	}
304153323Srodrigc	if (!xfs_acl->acl_cnt) {
305153323Srodrigc		_ACL_FREE(xfs_acl);
306153323Srodrigc		return 0;
307153323Srodrigc	}
308153323Srodrigc
309153323Srodrigc	VN_HOLD(vp);
310153323Srodrigc	error = xfs_acl_allow_set(vp, kind);
311153323Srodrigc	if (error)
312153323Srodrigc		goto out;
313153323Srodrigc
314153323Srodrigc	/* Incoming ACL exists, set file mode based on its value */
315153323Srodrigc	if (kind == _ACL_TYPE_ACCESS)
316153323Srodrigc		xfs_acl_setmode(vp, xfs_acl, &basicperms);
317153323Srodrigc
318153323Srodrigc	/*
319153323Srodrigc	 * If we have more than std unix permissions, set up the actual attr.
320153323Srodrigc	 * Otherwise, delete any existing attr.  This prevents us from
321153323Srodrigc	 * having actual attrs for permissions that can be stored in the
322153323Srodrigc	 * standard permission bits.
323153323Srodrigc	 */
324153323Srodrigc	if (!basicperms) {
325153323Srodrigc		xfs_acl_set_attr(vp, xfs_acl, kind, &error);
326153323Srodrigc	} else {
327153323Srodrigc		xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);
328153323Srodrigc	}
329153323Srodrigc
330153323Srodrigcout:
331153323Srodrigc	VN_RELE(vp);
332153323Srodrigc	_ACL_FREE(xfs_acl);
333153323Srodrigc	return -error;
334153323Srodrigc}
335153323Srodrigc
336153323Srodrigcint
337153323Srodrigcxfs_acl_iaccess(
338153323Srodrigc	xfs_inode_t	*ip,
339153323Srodrigc	mode_t		mode,
340153323Srodrigc	cred_t		*cr)
341153323Srodrigc{
342153323Srodrigc	xfs_acl_t	*acl;
343159451Srodrigc	int		rval;
344153323Srodrigc
345153323Srodrigc	if (!(_ACL_ALLOC(acl)))
346153323Srodrigc		return -1;
347153323Srodrigc
348153323Srodrigc	/* If the file has no ACL return -1. */
349159451Srodrigc	rval = sizeof(xfs_acl_t);
350159451Srodrigc	if (xfs_attr_fetch(ip, SGI_ACL_FILE, SGI_ACL_FILE_SIZE,
351159451Srodrigc			(char *)acl, &rval, ATTR_ROOT | ATTR_KERNACCESS, cr)) {
352153323Srodrigc		_ACL_FREE(acl);
353153323Srodrigc		return -1;
354153323Srodrigc	}
355153323Srodrigc	xfs_acl_get_endian(acl);
356153323Srodrigc
357153323Srodrigc	/* If the file has an empty ACL return -1. */
358153323Srodrigc	if (acl->acl_cnt == XFS_ACL_NOT_PRESENT) {
359153323Srodrigc		_ACL_FREE(acl);
360153323Srodrigc		return -1;
361153323Srodrigc	}
362153323Srodrigc
363153323Srodrigc	/* Synchronize ACL with mode bits */
364153323Srodrigc	xfs_acl_sync_mode(ip->i_d.di_mode, acl);
365153323Srodrigc
366159451Srodrigc	rval = xfs_acl_access(ip->i_d.di_uid, ip->i_d.di_gid, acl, mode, cr);
367153323Srodrigc	_ACL_FREE(acl);
368159451Srodrigc	return rval;
369153323Srodrigc}
370153323Srodrigc
371153323SrodrigcSTATIC int
372153323Srodrigcxfs_acl_allow_set(
373153323Srodrigc	xfs_vnode_t		*vp,
374153323Srodrigc	int		kind)
375153323Srodrigc{
376153323Srodrigc	xfs_vattr_t	va;
377153323Srodrigc	int		error;
378153323Srodrigc
379153323Srodrigc	if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
380153323Srodrigc		return EPERM;
381159451Srodrigc	if (kind == _ACL_TYPE_DEFAULT && !VN_ISDIR(vp))
382153323Srodrigc		return ENOTDIR;
383153323Srodrigc	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
384153323Srodrigc		return EROFS;
385153323Srodrigc	va.va_mask = XFS_AT_UID;
386153323Srodrigc	XVOP_GETATTR(vp, &va, 0, NULL, error);
387153323Srodrigc	if (error)
388153323Srodrigc		return error;
389153323Srodrigc	if (va.va_uid != current->fsuid && !capable(CAP_FOWNER))
390153323Srodrigc		return EPERM;
391153323Srodrigc	return error;
392153323Srodrigc}
393153323Srodrigc
394153323Srodrigc/*
395153323Srodrigc * The access control process to determine the access permission:
396153323Srodrigc *	if uid == file owner id, use the file owner bits.
397153323Srodrigc *	if gid == file owner group id, use the file group bits.
398159451Srodrigc *	scan ACL for a matching user or group, and use matched entry
399153323Srodrigc *	permission. Use total permissions of all matching group entries,
400153323Srodrigc *	until all acl entries are exhausted. The final permission produced
401153323Srodrigc *	by matching acl entry or entries needs to be & with group permission.
402153323Srodrigc *	if not owner, owning group, or matching entry in ACL, use file
403159451Srodrigc *	other bits.
404153323Srodrigc */
405153323SrodrigcSTATIC int
406153323Srodrigcxfs_acl_capability_check(
407153323Srodrigc	mode_t		mode,
408159451Srodrigc	cred_t		*cr)
409153323Srodrigc{
410153323Srodrigc	if ((mode & ACL_READ) && !capable_cred(cr, CAP_DAC_READ_SEARCH))
411153323Srodrigc		return EACCES;
412153323Srodrigc	if ((mode & ACL_WRITE) && !capable_cred(cr, CAP_DAC_OVERRIDE))
413153323Srodrigc		return EACCES;
414159451Srodrigc	if ((mode & ACL_EXECUTE) && !capable_cred(cr, CAP_DAC_OVERRIDE))
415153323Srodrigc		return EACCES;
416153323Srodrigc
417153323Srodrigc	return 0;
418153323Srodrigc}
419153323Srodrigc
420153323Srodrigc/*
421153323Srodrigc * Note: cr is only used here for the capability check if the ACL test fails.
422153323Srodrigc *       It is not used to find out the credentials uid or groups etc, as was
423153323Srodrigc *       done in IRIX. It is assumed that the uid and groups for the current
424153323Srodrigc *       thread are taken from "current" instead of the cr parameter.
425153323Srodrigc */
426153323SrodrigcSTATIC int
427153323Srodrigcxfs_acl_access(
428153323Srodrigc	uid_t		fuid,
429153323Srodrigc	gid_t		fgid,
430153323Srodrigc	xfs_acl_t	*fap,
431153323Srodrigc	mode_t		md,
432153323Srodrigc	cred_t		*cr)
433153323Srodrigc{
434153323Srodrigc	xfs_acl_entry_t	matched;
435153323Srodrigc	int		i, allows;
436153323Srodrigc	int		maskallows = -1;	/* true, but not 1, either */
437153323Srodrigc	int		seen_userobj = 0;
438153323Srodrigc
439153323Srodrigc	matched.ae_tag = 0;	/* Invalid type */
440159451Srodrigc	matched.ae_perm = 0;
441153323Srodrigc	md >>= 6;	/* Normalize the bits for comparison */
442153323Srodrigc
443153323Srodrigc	for (i = 0; i < fap->acl_cnt; i++) {
444153323Srodrigc		/*
445153323Srodrigc		 * Break out if we've got a user_obj entry or
446153323Srodrigc		 * a user entry and the mask (and have processed USER_OBJ)
447153323Srodrigc		 */
448153323Srodrigc		if (matched.ae_tag == ACL_USER_OBJ)
449153323Srodrigc			break;
450153323Srodrigc		if (matched.ae_tag == ACL_USER) {
451153323Srodrigc			if (maskallows != -1 && seen_userobj)
452153323Srodrigc				break;
453153323Srodrigc			if (fap->acl_entry[i].ae_tag != ACL_MASK &&
454153323Srodrigc			    fap->acl_entry[i].ae_tag != ACL_USER_OBJ)
455153323Srodrigc				continue;
456153323Srodrigc		}
457153323Srodrigc		/* True if this entry allows the requested access */
458153323Srodrigc		allows = ((fap->acl_entry[i].ae_perm & md) == md);
459153323Srodrigc
460153323Srodrigc		switch (fap->acl_entry[i].ae_tag) {
461153323Srodrigc		case ACL_USER_OBJ:
462153323Srodrigc			seen_userobj = 1;
463153323Srodrigc			if (fuid != current->fsuid)
464153323Srodrigc				continue;
465153323Srodrigc			matched.ae_tag = ACL_USER_OBJ;
466153323Srodrigc			matched.ae_perm = allows;
467153323Srodrigc			break;
468153323Srodrigc		case ACL_USER:
469153323Srodrigc			if (fap->acl_entry[i].ae_id != current->fsuid)
470153323Srodrigc				continue;
471153323Srodrigc			matched.ae_tag = ACL_USER;
472153323Srodrigc			matched.ae_perm = allows;
473153323Srodrigc			break;
474153323Srodrigc		case ACL_GROUP_OBJ:
475153323Srodrigc			if ((matched.ae_tag == ACL_GROUP_OBJ ||
476153323Srodrigc			    matched.ae_tag == ACL_GROUP) && !allows)
477153323Srodrigc				continue;
478153323Srodrigc			if (!in_group_p(fgid))
479153323Srodrigc				continue;
480153323Srodrigc			matched.ae_tag = ACL_GROUP_OBJ;
481153323Srodrigc			matched.ae_perm = allows;
482153323Srodrigc			break;
483153323Srodrigc		case ACL_GROUP:
484153323Srodrigc			if ((matched.ae_tag == ACL_GROUP_OBJ ||
485153323Srodrigc			    matched.ae_tag == ACL_GROUP) && !allows)
486153323Srodrigc				continue;
487153323Srodrigc			if (!in_group_p(fap->acl_entry[i].ae_id))
488153323Srodrigc				continue;
489153323Srodrigc			matched.ae_tag = ACL_GROUP;
490153323Srodrigc			matched.ae_perm = allows;
491153323Srodrigc			break;
492153323Srodrigc		case ACL_MASK:
493153323Srodrigc			maskallows = allows;
494153323Srodrigc			break;
495153323Srodrigc		case ACL_OTHER:
496153323Srodrigc			if (matched.ae_tag != 0)
497153323Srodrigc				continue;
498153323Srodrigc			matched.ae_tag = ACL_OTHER;
499153323Srodrigc			matched.ae_perm = allows;
500153323Srodrigc			break;
501153323Srodrigc		}
502153323Srodrigc	}
503153323Srodrigc	/*
504153323Srodrigc	 * First possibility is that no matched entry allows access.
505153323Srodrigc	 * The capability to override DAC may exist, so check for it.
506153323Srodrigc	 */
507153323Srodrigc	switch (matched.ae_tag) {
508153323Srodrigc	case ACL_OTHER:
509153323Srodrigc	case ACL_USER_OBJ:
510153323Srodrigc		if (matched.ae_perm)
511153323Srodrigc			return 0;
512153323Srodrigc		break;
513153323Srodrigc	case ACL_USER:
514153323Srodrigc	case ACL_GROUP_OBJ:
515153323Srodrigc	case ACL_GROUP:
516153323Srodrigc		if (maskallows && matched.ae_perm)
517153323Srodrigc			return 0;
518153323Srodrigc		break;
519153323Srodrigc	case 0:
520153323Srodrigc		break;
521153323Srodrigc	}
522153323Srodrigc
523159451Srodrigc	return xfs_acl_capability_check(md, cr);
524153323Srodrigc}
525153323Srodrigc
526153323Srodrigc/*
527153323Srodrigc * ACL validity checker.
528153323Srodrigc *   This acl validation routine checks each ACL entry read in makes sense.
529153323Srodrigc */
530153323SrodrigcSTATIC int
531153323Srodrigcxfs_acl_invalid(
532153323Srodrigc	xfs_acl_t	*aclp)
533153323Srodrigc{
534153323Srodrigc	xfs_acl_entry_t	*entry, *e;
535153323Srodrigc	int		user = 0, group = 0, other = 0, mask = 0;
536153323Srodrigc	int		mask_required = 0;
537153323Srodrigc	int		i, j;
538153323Srodrigc
539153323Srodrigc	if (!aclp)
540153323Srodrigc		goto acl_invalid;
541153323Srodrigc
542153323Srodrigc	if (aclp->acl_cnt > XFS_ACL_MAX_ENTRIES)
543153323Srodrigc		goto acl_invalid;
544153323Srodrigc
545153323Srodrigc	for (i = 0; i < aclp->acl_cnt; i++) {
546153323Srodrigc		entry = &aclp->acl_entry[i];
547153323Srodrigc		switch (entry->ae_tag) {
548153323Srodrigc		case ACL_USER_OBJ:
549153323Srodrigc			if (user++)
550153323Srodrigc				goto acl_invalid;
551153323Srodrigc			break;
552153323Srodrigc		case ACL_GROUP_OBJ:
553153323Srodrigc			if (group++)
554153323Srodrigc				goto acl_invalid;
555153323Srodrigc			break;
556153323Srodrigc		case ACL_OTHER:
557153323Srodrigc			if (other++)
558153323Srodrigc				goto acl_invalid;
559153323Srodrigc			break;
560153323Srodrigc		case ACL_USER:
561153323Srodrigc		case ACL_GROUP:
562153323Srodrigc			for (j = i + 1; j < aclp->acl_cnt; j++) {
563153323Srodrigc				e = &aclp->acl_entry[j];
564153323Srodrigc				if (e->ae_id == entry->ae_id &&
565153323Srodrigc				    e->ae_tag == entry->ae_tag)
566153323Srodrigc					goto acl_invalid;
567153323Srodrigc			}
568153323Srodrigc			mask_required++;
569153323Srodrigc			break;
570153323Srodrigc		case ACL_MASK:
571153323Srodrigc			if (mask++)
572153323Srodrigc				goto acl_invalid;
573153323Srodrigc			break;
574153323Srodrigc		default:
575153323Srodrigc			goto acl_invalid;
576153323Srodrigc		}
577153323Srodrigc	}
578153323Srodrigc	if (!user || !group || !other || (mask_required && !mask))
579153323Srodrigc		goto acl_invalid;
580153323Srodrigc	else
581153323Srodrigc		return 0;
582153323Srodrigcacl_invalid:
583153323Srodrigc	return EINVAL;
584153323Srodrigc}
585153323Srodrigc
586153323Srodrigc/*
587153323Srodrigc * Do ACL endian conversion.
588153323Srodrigc */
589153323SrodrigcSTATIC void
590153323Srodrigcxfs_acl_get_endian(
591153323Srodrigc	xfs_acl_t	*aclp)
592153323Srodrigc{
593153323Srodrigc	xfs_acl_entry_t	*ace, *end;
594153323Srodrigc
595153323Srodrigc	INT_SET(aclp->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
596153323Srodrigc	end = &aclp->acl_entry[0]+aclp->acl_cnt;
597153323Srodrigc	for (ace = &aclp->acl_entry[0]; ace < end; ace++) {
598153323Srodrigc		INT_SET(ace->ae_tag, ARCH_CONVERT, ace->ae_tag);
599153323Srodrigc		INT_SET(ace->ae_id, ARCH_CONVERT, ace->ae_id);
600153323Srodrigc		INT_SET(ace->ae_perm, ARCH_CONVERT, ace->ae_perm);
601153323Srodrigc	}
602153323Srodrigc}
603153323Srodrigc
604153323Srodrigc/*
605153323Srodrigc * Get the ACL from the EA and do endian conversion.
606153323Srodrigc */
607153323SrodrigcSTATIC void
608153323Srodrigcxfs_acl_get_attr(
609153323Srodrigc	xfs_vnode_t		*vp,
610153323Srodrigc	xfs_acl_t	*aclp,
611153323Srodrigc	int		kind,
612153323Srodrigc	int		flags,
613153323Srodrigc	int		*error)
614153323Srodrigc{
615153323Srodrigc	int		len = sizeof(xfs_acl_t);
616153323Srodrigc
617153323Srodrigc	ASSERT((flags & ATTR_KERNOVAL) ? (aclp == NULL) : 1);
618153323Srodrigc	flags |= ATTR_ROOT;
619153323Srodrigc	XVOP_ATTR_GET(vp,
620153323Srodrigc		kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE : SGI_ACL_DEFAULT,
621153323Srodrigc		(char *)aclp, &len, flags, sys_cred, *error);
622153323Srodrigc	if (*error || (flags & ATTR_KERNOVAL))
623153323Srodrigc		return;
624153323Srodrigc	xfs_acl_get_endian(aclp);
625153323Srodrigc}
626153323Srodrigc
627153323Srodrigc/*
628153323Srodrigc * Set the EA with the ACL and do endian conversion.
629153323Srodrigc */
630153323SrodrigcSTATIC void
631153323Srodrigcxfs_acl_set_attr(
632153323Srodrigc	xfs_vnode_t		*vp,
633153323Srodrigc	xfs_acl_t	*aclp,
634153323Srodrigc	int		kind,
635153323Srodrigc	int		*error)
636153323Srodrigc{
637153323Srodrigc	xfs_acl_entry_t	*ace, *newace, *end;
638153323Srodrigc	xfs_acl_t	*newacl;
639153323Srodrigc	int		len;
640153323Srodrigc
641153323Srodrigc	if (!(_ACL_ALLOC(newacl))) {
642153323Srodrigc		*error = ENOMEM;
643153323Srodrigc		return;
644153323Srodrigc	}
645153323Srodrigc
646153323Srodrigc	len = sizeof(xfs_acl_t) -
647153323Srodrigc	      (sizeof(xfs_acl_entry_t) * (XFS_ACL_MAX_ENTRIES - aclp->acl_cnt));
648153323Srodrigc	end = &aclp->acl_entry[0]+aclp->acl_cnt;
649153323Srodrigc	for (ace = &aclp->acl_entry[0], newace = &newacl->acl_entry[0];
650153323Srodrigc	     ace < end;
651153323Srodrigc	     ace++, newace++) {
652153323Srodrigc		INT_SET(newace->ae_tag, ARCH_CONVERT, ace->ae_tag);
653153323Srodrigc		INT_SET(newace->ae_id, ARCH_CONVERT, ace->ae_id);
654153323Srodrigc		INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm);
655153323Srodrigc	}
656153323Srodrigc	INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
657153323Srodrigc	XVOP_ATTR_SET(vp,
658153323Srodrigc		kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE: SGI_ACL_DEFAULT,
659153323Srodrigc		(char *)newacl, len, ATTR_ROOT, sys_cred, *error);
660153323Srodrigc	_ACL_FREE(newacl);
661153323Srodrigc}
662153323Srodrigc
663153323Srodrigcint
664153323Srodrigcxfs_acl_vtoacl(
665153323Srodrigc	xfs_vnode_t		*vp,
666153323Srodrigc	xfs_acl_t	*access_acl,
667153323Srodrigc	xfs_acl_t	*default_acl)
668153323Srodrigc{
669153323Srodrigc	xfs_vattr_t	va;
670153323Srodrigc	int		error = 0;
671153323Srodrigc
672153323Srodrigc	if (access_acl) {
673153323Srodrigc		/*
674153323Srodrigc		 * Get the Access ACL and the mode.  If either cannot
675153323Srodrigc		 * be obtained for some reason, invalidate the access ACL.
676153323Srodrigc		 */
677153323Srodrigc		xfs_acl_get_attr(vp, access_acl, _ACL_TYPE_ACCESS, 0, &error);
678153323Srodrigc		if (!error) {
679153323Srodrigc			/* Got the ACL, need the mode... */
680153323Srodrigc			va.va_mask = XFS_AT_MODE;
681153323Srodrigc			XVOP_GETATTR(vp, &va, 0, sys_cred, error);
682153323Srodrigc		}
683153323Srodrigc
684153323Srodrigc		if (error)
685153323Srodrigc			access_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
686153323Srodrigc		else /* We have a good ACL and the file mode, synchronize. */
687153323Srodrigc			xfs_acl_sync_mode(va.va_mode, access_acl);
688153323Srodrigc	}
689153323Srodrigc
690153323Srodrigc	if (default_acl) {
691153323Srodrigc		xfs_acl_get_attr(vp, default_acl, _ACL_TYPE_DEFAULT, 0, &error);
692153323Srodrigc		if (error)
693153323Srodrigc			default_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
694153323Srodrigc	}
695153323Srodrigc	return error;
696153323Srodrigc}
697153323Srodrigc
698153323Srodrigc/*
699153323Srodrigc * This function retrieves the parent directory's acl, processes it
700153323Srodrigc * and lets the child inherit the acl(s) that it should.
701153323Srodrigc */
702153323Srodrigcint
703153323Srodrigcxfs_acl_inherit(
704153323Srodrigc	xfs_vnode_t	*vp,
705153323Srodrigc	xfs_vattr_t	*vap,
706153323Srodrigc	xfs_acl_t	*pdaclp)
707153323Srodrigc{
708153323Srodrigc	xfs_acl_t	*cacl;
709153323Srodrigc	int		error = 0;
710153323Srodrigc	int		basicperms = 0;
711153323Srodrigc
712153323Srodrigc	/*
713153323Srodrigc	 * If the parent does not have a default ACL, or it's an
714153323Srodrigc	 * invalid ACL, we're done.
715153323Srodrigc	 */
716153323Srodrigc	if (!vp)
717153323Srodrigc		return 0;
718153323Srodrigc	if (!pdaclp || xfs_acl_invalid(pdaclp))
719153323Srodrigc		return 0;
720153323Srodrigc
721153323Srodrigc	/*
722153323Srodrigc	 * Copy the default ACL of the containing directory to
723153323Srodrigc	 * the access ACL of the new file and use the mode that
724153323Srodrigc	 * was passed in to set up the correct initial values for
725153323Srodrigc	 * the u::,g::[m::], and o:: entries.  This is what makes
726153323Srodrigc	 * umask() "work" with ACL's.
727153323Srodrigc	 */
728153323Srodrigc
729153323Srodrigc	if (!(_ACL_ALLOC(cacl)))
730153323Srodrigc		return ENOMEM;
731153323Srodrigc
732153323Srodrigc	memcpy(cacl, pdaclp, sizeof(xfs_acl_t));
733153323Srodrigc	xfs_acl_filter_mode(vap->va_mode, cacl);
734153323Srodrigc	xfs_acl_setmode(vp, cacl, &basicperms);
735153323Srodrigc
736153323Srodrigc	/*
737153323Srodrigc	 * Set the Default and Access ACL on the file.  The mode is already
738153323Srodrigc	 * set on the file, so we don't need to worry about that.
739153323Srodrigc	 *
740153323Srodrigc	 * If the new file is a directory, its default ACL is a copy of
741153323Srodrigc	 * the containing directory's default ACL.
742153323Srodrigc	 */
743159451Srodrigc	if (VN_ISDIR(vp))
744153323Srodrigc		xfs_acl_set_attr(vp, pdaclp, _ACL_TYPE_DEFAULT, &error);
745153323Srodrigc	if (!error && !basicperms)
746153323Srodrigc		xfs_acl_set_attr(vp, cacl, _ACL_TYPE_ACCESS, &error);
747153323Srodrigc	_ACL_FREE(cacl);
748153323Srodrigc	return error;
749153323Srodrigc}
750153323Srodrigc
751153323Srodrigc/*
752153323Srodrigc * Set up the correct mode on the file based on the supplied ACL.  This
753153323Srodrigc * makes sure that the mode on the file reflects the state of the
754153323Srodrigc * u::,g::[m::], and o:: entries in the ACL.  Since the mode is where
755153323Srodrigc * the ACL is going to get the permissions for these entries, we must
756153323Srodrigc * synchronize the mode whenever we set the ACL on a file.
757153323Srodrigc */
758153323SrodrigcSTATIC int
759153323Srodrigcxfs_acl_setmode(
760153323Srodrigc	xfs_vnode_t		*vp,
761153323Srodrigc	xfs_acl_t	*acl,
762153323Srodrigc	int		*basicperms)
763153323Srodrigc{
764153323Srodrigc	xfs_vattr_t	va;
765153323Srodrigc	xfs_acl_entry_t	*ap;
766153323Srodrigc	xfs_acl_entry_t	*gap = NULL;
767153323Srodrigc	int		i, error, nomask = 1;
768153323Srodrigc
769153323Srodrigc	*basicperms = 1;
770153323Srodrigc
771153323Srodrigc	if (acl->acl_cnt == XFS_ACL_NOT_PRESENT)
772153323Srodrigc		return 0;
773153323Srodrigc
774153323Srodrigc	/*
775153323Srodrigc	 * Copy the u::, g::, o::, and m:: bits from the ACL into the
776153323Srodrigc	 * mode.  The m:: bits take precedence over the g:: bits.
777153323Srodrigc	 */
778153323Srodrigc	va.va_mask = XFS_AT_MODE;
779153323Srodrigc	XVOP_GETATTR(vp, &va, 0, sys_cred, error);
780153323Srodrigc	if (error)
781153323Srodrigc		return error;
782153323Srodrigc
783153323Srodrigc	va.va_mask = XFS_AT_MODE;
784153323Srodrigc	va.va_mode &= ~(S_IRWXU|S_IRWXG|S_IRWXO);
785153323Srodrigc	ap = acl->acl_entry;
786153323Srodrigc	for (i = 0; i < acl->acl_cnt; ++i) {
787153323Srodrigc		switch (ap->ae_tag) {
788153323Srodrigc		case ACL_USER_OBJ:
789153323Srodrigc			va.va_mode |= ap->ae_perm << 6;
790153323Srodrigc			break;
791153323Srodrigc		case ACL_GROUP_OBJ:
792153323Srodrigc			gap = ap;
793153323Srodrigc			break;
794153323Srodrigc		case ACL_MASK:	/* more than just standard modes */
795153323Srodrigc			nomask = 0;
796153323Srodrigc			va.va_mode |= ap->ae_perm << 3;
797153323Srodrigc			*basicperms = 0;
798153323Srodrigc			break;
799153323Srodrigc		case ACL_OTHER:
800153323Srodrigc			va.va_mode |= ap->ae_perm;
801153323Srodrigc			break;
802153323Srodrigc		default:	/* more than just standard modes */
803153323Srodrigc			*basicperms = 0;
804153323Srodrigc			break;
805153323Srodrigc		}
806153323Srodrigc		ap++;
807153323Srodrigc	}
808153323Srodrigc
809153323Srodrigc	/* Set the group bits from ACL_GROUP_OBJ if there's no ACL_MASK */
810153323Srodrigc	if (gap && nomask)
811153323Srodrigc		va.va_mode |= gap->ae_perm << 3;
812153323Srodrigc
813153323Srodrigc	XVOP_SETATTR(vp, &va, 0, sys_cred, error);
814153323Srodrigc	return error;
815153323Srodrigc}
816153323Srodrigc
817153323Srodrigc/*
818153323Srodrigc * The permissions for the special ACL entries (u::, g::[m::], o::) are
819153323Srodrigc * actually stored in the file mode (if there is both a group and a mask,
820153323Srodrigc * the group is stored in the ACL entry and the mask is stored on the file).
821153323Srodrigc * This allows the mode to remain automatically in sync with the ACL without
822153323Srodrigc * the need for a call-back to the ACL system at every point where the mode
823153323Srodrigc * could change.  This function takes the permissions from the specified mode
824153323Srodrigc * and places it in the supplied ACL.
825153323Srodrigc *
826153323Srodrigc * This implementation draws its validity from the fact that, when the ACL
827153323Srodrigc * was assigned, the mode was copied from the ACL.
828153323Srodrigc * If the mode did not change, therefore, the mode remains exactly what was
829153323Srodrigc * taken from the special ACL entries at assignment.
830153323Srodrigc * If a subsequent chmod() was done, the POSIX spec says that the change in
831153323Srodrigc * mode must cause an update to the ACL seen at user level and used for
832153323Srodrigc * access checks.  Before and after a mode change, therefore, the file mode
833153323Srodrigc * most accurately reflects what the special ACL entries should permit/deny.
834153323Srodrigc *
835153323Srodrigc * CAVEAT: If someone sets the SGI_ACL_FILE attribute directly,
836153323Srodrigc *         the existing mode bits will override whatever is in the
837153323Srodrigc *         ACL. Similarly, if there is a pre-existing ACL that was
838153323Srodrigc *         never in sync with its mode (owing to a bug in 6.5 and
839153323Srodrigc *         before), it will now magically (or mystically) be
840153323Srodrigc *         synchronized.  This could cause slight astonishment, but
841153323Srodrigc *         it is better than inconsistent permissions.
842153323Srodrigc *
843153323Srodrigc * The supplied ACL is a template that may contain any combination
844153323Srodrigc * of special entries.  These are treated as place holders when we fill
845153323Srodrigc * out the ACL.  This routine does not add or remove special entries, it
846153323Srodrigc * simply unites each special entry with its associated set of permissions.
847153323Srodrigc */
848153323SrodrigcSTATIC void
849153323Srodrigcxfs_acl_sync_mode(
850153323Srodrigc	mode_t		mode,
851153323Srodrigc	xfs_acl_t	*acl)
852153323Srodrigc{
853153323Srodrigc	int		i, nomask = 1;
854153323Srodrigc	xfs_acl_entry_t	*ap;
855153323Srodrigc	xfs_acl_entry_t	*gap = NULL;
856153323Srodrigc
857153323Srodrigc	/*
858153323Srodrigc	 * Set ACL entries. POSIX1003.1eD16 requires that the MASK
859153323Srodrigc	 * be set instead of the GROUP entry, if there is a MASK.
860153323Srodrigc	 */
861153323Srodrigc	for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
862153323Srodrigc		switch (ap->ae_tag) {
863153323Srodrigc		case ACL_USER_OBJ:
864153323Srodrigc			ap->ae_perm = (mode >> 6) & 0x7;
865153323Srodrigc			break;
866153323Srodrigc		case ACL_GROUP_OBJ:
867153323Srodrigc			gap = ap;
868153323Srodrigc			break;
869153323Srodrigc		case ACL_MASK:
870153323Srodrigc			nomask = 0;
871153323Srodrigc			ap->ae_perm = (mode >> 3) & 0x7;
872153323Srodrigc			break;
873153323Srodrigc		case ACL_OTHER:
874153323Srodrigc			ap->ae_perm = mode & 0x7;
875153323Srodrigc			break;
876153323Srodrigc		default:
877153323Srodrigc			break;
878153323Srodrigc		}
879153323Srodrigc	}
880153323Srodrigc	/* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
881153323Srodrigc	if (gap && nomask)
882153323Srodrigc		gap->ae_perm = (mode >> 3) & 0x7;
883153323Srodrigc}
884153323Srodrigc
885153323Srodrigc/*
886153323Srodrigc * When inheriting an Access ACL from a directory Default ACL,
887153323Srodrigc * the ACL bits are set to the intersection of the ACL default
888153323Srodrigc * permission bits and the file permission bits in mode. If there
889153323Srodrigc * are no permission bits on the file then we must not give them
890153323Srodrigc * the ACL. This is what what makes umask() work with ACLs.
891153323Srodrigc */
892153323SrodrigcSTATIC void
893153323Srodrigcxfs_acl_filter_mode(
894153323Srodrigc	mode_t		mode,
895153323Srodrigc	xfs_acl_t	*acl)
896153323Srodrigc{
897153323Srodrigc	int		i, nomask = 1;
898153323Srodrigc	xfs_acl_entry_t	*ap;
899153323Srodrigc	xfs_acl_entry_t	*gap = NULL;
900153323Srodrigc
901153323Srodrigc	/*
902153323Srodrigc	 * Set ACL entries. POSIX1003.1eD16 requires that the MASK
903153323Srodrigc	 * be merged with GROUP entry, if there is a MASK.
904153323Srodrigc	 */
905153323Srodrigc	for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
906153323Srodrigc		switch (ap->ae_tag) {
907153323Srodrigc		case ACL_USER_OBJ:
908153323Srodrigc			ap->ae_perm &= (mode >> 6) & 0x7;
909153323Srodrigc			break;
910153323Srodrigc		case ACL_GROUP_OBJ:
911153323Srodrigc			gap = ap;
912153323Srodrigc			break;
913153323Srodrigc		case ACL_MASK:
914153323Srodrigc			nomask = 0;
915153323Srodrigc			ap->ae_perm &= (mode >> 3) & 0x7;
916153323Srodrigc			break;
917153323Srodrigc		case ACL_OTHER:
918153323Srodrigc			ap->ae_perm &= mode & 0x7;
919153323Srodrigc			break;
920153323Srodrigc		default:
921153323Srodrigc			break;
922153323Srodrigc		}
923153323Srodrigc	}
924153323Srodrigc	/* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
925153323Srodrigc	if (gap && nomask)
926153323Srodrigc		gap->ae_perm &= (mode >> 3) & 0x7;
927153323Srodrigc}
928153323Srodrigc
929