xref: /netbsd/sys/fs/nfs/common/nfs_commonacl.c (revision dca5edad)
1 /*	$NetBSD: nfs_commonacl.c,v 1.2 2016/12/13 22:31:51 pgoyette Exp $	*/
2 /*-
3  * Copyright (c) 2009 Rick Macklem, University of Guelph
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 /* __FBSDID("FreeBSD: head/sys/fs/nfs/nfs_commonacl.c 297793 2016-04-10 23:07:00Z pfg "); */
31 __RCSID("$NetBSD: nfs_commonacl.c,v 1.2 2016/12/13 22:31:51 pgoyette Exp $");
32 
33 #ifndef APPLEKEXT
34 #include <fs/nfs/common/nfsport.h>
35 
36 extern int nfsrv_useacl;
37 #endif
38 
39 static int nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
40     enum vtype type, acl_perm_t *permp);
41 
42 /*
43  * Handle xdr for an ace.
44  */
45 APPLESTATIC int
nfsrv_dissectace(struct nfsrv_descript * nd,struct acl_entry * acep,int * aceerrp,int * acesizep,NFSPROC_T * p)46 nfsrv_dissectace(struct nfsrv_descript *nd, struct acl_entry *acep,
47     int *aceerrp, int *acesizep, NFSPROC_T *p)
48 {
49 	u_int32_t *tl;
50 	int len, gotid = 0, owner = 0, error = 0, aceerr = 0;
51 	u_char *name, namestr[NFSV4_SMALLSTR + 1];
52 	u_int32_t flag, mask, acetype;
53 	gid_t gid;
54 	uid_t uid;
55 
56 	*aceerrp = 0;
57 	acep->ae_flags = 0;
58 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
59 	acetype = fxdr_unsigned(u_int32_t, *tl++);
60 	flag = fxdr_unsigned(u_int32_t, *tl++);
61 	mask = fxdr_unsigned(u_int32_t, *tl++);
62 	len = fxdr_unsigned(int, *tl);
63 	if (len < 0) {
64 		error = NFSERR_BADXDR;
65 		goto nfsmout;
66 	} else if (len == 0) {
67 		/* Netapp filers return a 0 length who for nil users */
68 		acep->ae_tag = ACL_UNDEFINED_TAG;
69 		acep->ae_id = ACL_UNDEFINED_ID;
70 		acep->ae_perm = (acl_perm_t)0;
71 		acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
72 		if (acesizep)
73 			*acesizep = 4 * NFSX_UNSIGNED;
74 		error = 0;
75 		goto nfsmout;
76 	}
77 	if (len > NFSV4_SMALLSTR)
78 		name = malloc(len + 1, M_NFSSTRING, M_WAITOK);
79 	else
80 		name = namestr;
81 	error = nfsrv_mtostr(nd, name, len);
82 	if (error) {
83 		if (len > NFSV4_SMALLSTR)
84 			free(name, M_NFSSTRING);
85 		goto nfsmout;
86 	}
87 	if (len == 6) {
88 		if (!NFSBCMP(name, "OWNER@", 6)) {
89 			acep->ae_tag = ACL_USER_OBJ;
90 			acep->ae_id = ACL_UNDEFINED_ID;
91 			owner = 1;
92 			gotid = 1;
93 		} else if (!NFSBCMP(name, "GROUP@", 6)) {
94 			acep->ae_tag = ACL_GROUP_OBJ;
95 			acep->ae_id = ACL_UNDEFINED_ID;
96 			gotid = 1;
97 		}
98 	} else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
99 		acep->ae_tag = ACL_EVERYONE;
100 		acep->ae_id = ACL_UNDEFINED_ID;
101 		gotid = 1;
102 	}
103 	if (gotid == 0) {
104 		if (flag & NFSV4ACE_IDENTIFIERGROUP) {
105 			acep->ae_tag = ACL_GROUP;
106 			aceerr = nfsv4_strtogid(nd, name, len, &gid, p);
107 			if (aceerr == 0)
108 				acep->ae_id = (uid_t)gid;
109 		} else {
110 			acep->ae_tag = ACL_USER;
111 			aceerr = nfsv4_strtouid(nd, name, len, &uid, p);
112 			if (aceerr == 0)
113 				acep->ae_id = uid;
114 		}
115 	}
116 	if (len > NFSV4_SMALLSTR)
117 		free(name, M_NFSSTRING);
118 
119 	if (aceerr == 0) {
120 		/*
121 		 * Handle the flags.
122 		 */
123 		flag &= ~NFSV4ACE_IDENTIFIERGROUP;
124 		if (flag & NFSV4ACE_FILEINHERIT) {
125 			flag &= ~NFSV4ACE_FILEINHERIT;
126 			acep->ae_flags |= ACL_ENTRY_FILE_INHERIT;
127 		}
128 		if (flag & NFSV4ACE_DIRECTORYINHERIT) {
129 			flag &= ~NFSV4ACE_DIRECTORYINHERIT;
130 			acep->ae_flags |= ACL_ENTRY_DIRECTORY_INHERIT;
131 		}
132 		if (flag & NFSV4ACE_NOPROPAGATEINHERIT) {
133 			flag &= ~NFSV4ACE_NOPROPAGATEINHERIT;
134 			acep->ae_flags |= ACL_ENTRY_NO_PROPAGATE_INHERIT;
135 		}
136 		if (flag & NFSV4ACE_INHERITONLY) {
137 			flag &= ~NFSV4ACE_INHERITONLY;
138 			acep->ae_flags |= ACL_ENTRY_INHERIT_ONLY;
139 		}
140 		if (flag & NFSV4ACE_SUCCESSFULACCESS) {
141 			flag &= ~NFSV4ACE_SUCCESSFULACCESS;
142 			acep->ae_flags |= ACL_ENTRY_SUCCESSFUL_ACCESS;
143 		}
144 		if (flag & NFSV4ACE_FAILEDACCESS) {
145 			flag &= ~NFSV4ACE_FAILEDACCESS;
146 			acep->ae_flags |= ACL_ENTRY_FAILED_ACCESS;
147 		}
148 		/*
149 		 * Set ae_entry_type.
150 		 */
151 		if (acetype == NFSV4ACE_ALLOWEDTYPE)
152 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALLOW;
153 		else if (acetype == NFSV4ACE_DENIEDTYPE)
154 			acep->ae_entry_type = ACL_ENTRY_TYPE_DENY;
155 		else if (acetype == NFSV4ACE_AUDITTYPE)
156 			acep->ae_entry_type = ACL_ENTRY_TYPE_AUDIT;
157 		else if (acetype == NFSV4ACE_ALARMTYPE)
158 			acep->ae_entry_type = ACL_ENTRY_TYPE_ALARM;
159 		else
160 			aceerr = NFSERR_ATTRNOTSUPP;
161 	}
162 
163 	/*
164 	 * Now, check for unsupported flag bits.
165 	 */
166 	if (aceerr == 0 && flag != 0)
167 		aceerr = NFSERR_ATTRNOTSUPP;
168 
169 	/*
170 	 * And turn the mask into perm bits.
171 	 */
172 	if (aceerr == 0)
173 		aceerr = nfsrv_acemasktoperm(acetype, mask, owner, VREG,
174 		    &acep->ae_perm);
175 	*aceerrp = aceerr;
176 	if (acesizep)
177 		*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
178 	error = 0;
179 nfsmout:
180 	NFSEXITCODE(error);
181 	return (error);
182 }
183 
184 /*
185  * Turn an NFSv4 ace mask into R/W/X flag bits.
186  */
187 static int
nfsrv_acemasktoperm(u_int32_t acetype,u_int32_t mask,int owner,enum vtype type,acl_perm_t * permp)188 nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
189     enum vtype type, acl_perm_t *permp)
190 {
191 	acl_perm_t perm = 0x0;
192 	int error = 0;
193 
194 	if (mask & NFSV4ACE_READDATA) {
195 		mask &= ~NFSV4ACE_READDATA;
196 		perm |= ACL_READ_DATA;
197 	}
198 	if (mask & NFSV4ACE_LISTDIRECTORY) {
199 		mask &= ~NFSV4ACE_LISTDIRECTORY;
200 		perm |= ACL_LIST_DIRECTORY;
201 	}
202 	if (mask & NFSV4ACE_WRITEDATA) {
203 		mask &= ~NFSV4ACE_WRITEDATA;
204 		perm |= ACL_WRITE_DATA;
205 	}
206 	if (mask & NFSV4ACE_ADDFILE) {
207 		mask &= ~NFSV4ACE_ADDFILE;
208 		perm |= ACL_ADD_FILE;
209 	}
210 	if (mask & NFSV4ACE_APPENDDATA) {
211 		mask &= ~NFSV4ACE_APPENDDATA;
212 		perm |= ACL_APPEND_DATA;
213 	}
214 	if (mask & NFSV4ACE_ADDSUBDIRECTORY) {
215 		mask &= ~NFSV4ACE_ADDSUBDIRECTORY;
216 		perm |= ACL_ADD_SUBDIRECTORY;
217 	}
218 	if (mask & NFSV4ACE_READNAMEDATTR) {
219 		mask &= ~NFSV4ACE_READNAMEDATTR;
220 		perm |= ACL_READ_NAMED_ATTRS;
221 	}
222 	if (mask & NFSV4ACE_WRITENAMEDATTR) {
223 		mask &= ~NFSV4ACE_WRITENAMEDATTR;
224 		perm |= ACL_WRITE_NAMED_ATTRS;
225 	}
226 	if (mask & NFSV4ACE_EXECUTE) {
227 		mask &= ~NFSV4ACE_EXECUTE;
228 		perm |= ACL_EXECUTE;
229 	}
230 	if (mask & NFSV4ACE_SEARCH) {
231 		mask &= ~NFSV4ACE_SEARCH;
232 		perm |= ACL_EXECUTE;
233 	}
234 	if (mask & NFSV4ACE_DELETECHILD) {
235 		mask &= ~NFSV4ACE_DELETECHILD;
236 		perm |= ACL_DELETE_CHILD;
237 	}
238 	if (mask & NFSV4ACE_READATTRIBUTES) {
239 		mask &= ~NFSV4ACE_READATTRIBUTES;
240 		perm |= ACL_READ_ATTRIBUTES;
241 	}
242 	if (mask & NFSV4ACE_WRITEATTRIBUTES) {
243 		mask &= ~NFSV4ACE_WRITEATTRIBUTES;
244 		perm |= ACL_WRITE_ATTRIBUTES;
245 	}
246 	if (mask & NFSV4ACE_DELETE) {
247 		mask &= ~NFSV4ACE_DELETE;
248 		perm |= ACL_DELETE;
249 	}
250 	if (mask & NFSV4ACE_READACL) {
251 		mask &= ~NFSV4ACE_READACL;
252 		perm |= ACL_READ_ACL;
253 	}
254 	if (mask & NFSV4ACE_WRITEACL) {
255 		mask &= ~NFSV4ACE_WRITEACL;
256 		perm |= ACL_WRITE_ACL;
257 	}
258 	if (mask & NFSV4ACE_WRITEOWNER) {
259 		mask &= ~NFSV4ACE_WRITEOWNER;
260 		perm |= ACL_WRITE_OWNER;
261 	}
262 	if (mask & NFSV4ACE_SYNCHRONIZE) {
263 		mask &= ~NFSV4ACE_SYNCHRONIZE;
264 		perm |= ACL_SYNCHRONIZE;
265 	}
266 	if (mask != 0) {
267 		error = NFSERR_ATTRNOTSUPP;
268 		goto out;
269 	}
270 	*permp = perm;
271 
272 out:
273 	NFSEXITCODE(error);
274 	return (error);
275 }
276 
277 /* local functions */
278 static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
279     enum vtype, int, int, struct acl_entry *);
280 
281 /*
282  * This function builds an NFS ace.
283  */
284 static int
nfsrv_buildace(struct nfsrv_descript * nd,u_char * name,int namelen,enum vtype type,int group,int owner,struct acl_entry * ace)285 nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
286     enum vtype type, int group, int owner, struct acl_entry *ace)
287 {
288 	u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
289 	int full_len;
290 
291 	full_len = NFSM_RNDUP(namelen);
292 	NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED + full_len);
293 
294 	/*
295 	 * Fill in the ace type.
296 	 */
297 	if (ace->ae_entry_type & ACL_ENTRY_TYPE_ALLOW)
298 		acetype = NFSV4ACE_ALLOWEDTYPE;
299 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_DENY)
300 		acetype = NFSV4ACE_DENIEDTYPE;
301 	else if (ace->ae_entry_type & ACL_ENTRY_TYPE_AUDIT)
302 		acetype = NFSV4ACE_AUDITTYPE;
303 	else
304 		acetype = NFSV4ACE_ALARMTYPE;
305 	*tl++ = txdr_unsigned(acetype);
306 
307 	/*
308 	 * Set the flag bits from the ACL.
309 	 */
310 	if (ace->ae_flags & ACL_ENTRY_FILE_INHERIT)
311 		aceflag |= NFSV4ACE_FILEINHERIT;
312 	if (ace->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT)
313 		aceflag |= NFSV4ACE_DIRECTORYINHERIT;
314 	if (ace->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)
315 		aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
316 	if (ace->ae_flags & ACL_ENTRY_INHERIT_ONLY)
317 		aceflag |= NFSV4ACE_INHERITONLY;
318 	if (ace->ae_flags & ACL_ENTRY_SUCCESSFUL_ACCESS)
319 		aceflag |= NFSV4ACE_SUCCESSFULACCESS;
320 	if (ace->ae_flags & ACL_ENTRY_FAILED_ACCESS)
321 		aceflag |= NFSV4ACE_FAILEDACCESS;
322 	if (group)
323 		aceflag |= NFSV4ACE_IDENTIFIERGROUP;
324 	*tl++ = txdr_unsigned(aceflag);
325 	if (type == VDIR) {
326 		if (ace->ae_perm & ACL_LIST_DIRECTORY)
327 			acemask |= NFSV4ACE_LISTDIRECTORY;
328 		if (ace->ae_perm & ACL_ADD_FILE)
329 			acemask |= NFSV4ACE_ADDFILE;
330 		if (ace->ae_perm & ACL_ADD_SUBDIRECTORY)
331 			acemask |= NFSV4ACE_ADDSUBDIRECTORY;
332 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
333 			acemask |= NFSV4ACE_READNAMEDATTR;
334 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
335 			acemask |= NFSV4ACE_WRITENAMEDATTR;
336 		if (ace->ae_perm & ACL_EXECUTE)
337 			acemask |= NFSV4ACE_SEARCH;
338 		if (ace->ae_perm & ACL_DELETE_CHILD)
339 			acemask |= NFSV4ACE_DELETECHILD;
340 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
341 			acemask |= NFSV4ACE_READATTRIBUTES;
342 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
343 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
344 		if (ace->ae_perm & ACL_DELETE)
345 			acemask |= NFSV4ACE_DELETE;
346 		if (ace->ae_perm & ACL_READ_ACL)
347 			acemask |= NFSV4ACE_READACL;
348 		if (ace->ae_perm & ACL_WRITE_ACL)
349 			acemask |= NFSV4ACE_WRITEACL;
350 		if (ace->ae_perm & ACL_WRITE_OWNER)
351 			acemask |= NFSV4ACE_WRITEOWNER;
352 		if (ace->ae_perm & ACL_SYNCHRONIZE)
353 			acemask |= NFSV4ACE_SYNCHRONIZE;
354 	} else {
355 		if (ace->ae_perm & ACL_READ_DATA)
356 			acemask |= NFSV4ACE_READDATA;
357 		if (ace->ae_perm & ACL_WRITE_DATA)
358 			acemask |= NFSV4ACE_WRITEDATA;
359 		if (ace->ae_perm & ACL_APPEND_DATA)
360 			acemask |= NFSV4ACE_APPENDDATA;
361 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
362 			acemask |= NFSV4ACE_READNAMEDATTR;
363 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
364 			acemask |= NFSV4ACE_WRITENAMEDATTR;
365 		if (ace->ae_perm & ACL_EXECUTE)
366 			acemask |= NFSV4ACE_EXECUTE;
367 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
368 			acemask |= NFSV4ACE_READATTRIBUTES;
369 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
370 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
371 		if (ace->ae_perm & ACL_DELETE)
372 			acemask |= NFSV4ACE_DELETE;
373 		if (ace->ae_perm & ACL_READ_ACL)
374 			acemask |= NFSV4ACE_READACL;
375 		if (ace->ae_perm & ACL_WRITE_ACL)
376 			acemask |= NFSV4ACE_WRITEACL;
377 		if (ace->ae_perm & ACL_WRITE_OWNER)
378 			acemask |= NFSV4ACE_WRITEOWNER;
379 		if (ace->ae_perm & ACL_SYNCHRONIZE)
380 			acemask |= NFSV4ACE_SYNCHRONIZE;
381 	}
382 	*tl++ = txdr_unsigned(acemask);
383 	*tl++ = txdr_unsigned(namelen);
384 	if (full_len - namelen)
385 		*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
386 	NFSBCOPY(name, (caddr_t)tl, namelen);
387 	return (full_len + 4 * NFSX_UNSIGNED);
388 }
389 
390 /*
391  * Build an NFSv4 ACL.
392  */
393 APPLESTATIC int
nfsrv_buildacl(struct nfsrv_descript * nd,NFSACL_T * aclp,enum vtype type,NFSPROC_T * p)394 nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, enum vtype type,
395     NFSPROC_T *p)
396 {
397 	int i, entrycnt = 0, retlen;
398 	u_int32_t *entrycntp;
399 	int isowner, isgroup, namelen, malloced;
400 	u_char *name, namestr[NFSV4_SMALLSTR];
401 
402 	NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
403 	retlen = NFSX_UNSIGNED;
404 	/*
405 	 * Loop through the acl entries, building each one.
406 	 */
407 	for (i = 0; i < aclp->acl_cnt; i++) {
408 		isowner = isgroup = malloced = 0;
409 		switch (aclp->acl_entry[i].ae_tag) {
410 		case ACL_USER_OBJ:
411 			isowner = 1;
412 			name = "OWNER@";
413 			namelen = 6;
414 			break;
415 		case ACL_GROUP_OBJ:
416 			isgroup = 1;
417 			name = "GROUP@";
418 			namelen = 6;
419 			break;
420 		case ACL_EVERYONE:
421 			name = "EVERYONE@";
422 			namelen = 9;
423 			break;
424 		case ACL_USER:
425 			name = namestr;
426 			nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
427 			    &namelen, p);
428 			if (name != namestr)
429 				malloced = 1;
430 			break;
431 		case ACL_GROUP:
432 			isgroup = 1;
433 			name = namestr;
434 			nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
435 			    &namelen, p);
436 			if (name != namestr)
437 				malloced = 1;
438 			break;
439 		default:
440 			continue;
441 		}
442 		retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
443 		    isowner, &aclp->acl_entry[i]);
444 		entrycnt++;
445 		if (malloced)
446 			free(name, M_NFSSTRING);
447 	}
448 	*entrycntp = txdr_unsigned(entrycnt);
449 	return (retlen);
450 }
451 
452 /*
453  * Set an NFSv4 acl.
454  */
455 APPLESTATIC int
nfsrv_setacl(vnode_t vp,NFSACL_T * aclp,struct ucred * cred,NFSPROC_T * p)456 nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
457     NFSPROC_T *p)
458 {
459 	int error;
460 
461 	if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
462 		error = NFSERR_ATTRNOTSUPP;
463 		goto out;
464 	}
465 	/*
466 	 * With NFSv4 ACLs, chmod(2) may need to add additional entries.
467 	 * Make sure it has enough room for that - splitting every entry
468 	 * into two and appending "canonical six" entries at the end.
469 	 * Cribbed out of kern/vfs_acl.c - Rick M.
470 	 */
471 	if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) {
472 		error = NFSERR_ATTRNOTSUPP;
473 		goto out;
474 	}
475 	error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
476 
477 out:
478 	NFSEXITCODE(error);
479 	return (error);
480 }
481 
482 /*
483  * Compare two NFSv4 acls.
484  * Return 0 if they are the same, 1 if not the same.
485  */
486 APPLESTATIC int
nfsrv_compareacl(NFSACL_T * aclp1,NFSACL_T * aclp2)487 nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
488 {
489 	int i;
490 	struct acl_entry *acep1, *acep2;
491 
492 	if (aclp1->acl_cnt != aclp2->acl_cnt)
493 		return (1);
494 	acep1 = aclp1->acl_entry;
495 	acep2 = aclp2->acl_entry;
496 	for (i = 0; i < aclp1->acl_cnt; i++) {
497 		if (acep1->ae_tag != acep2->ae_tag)
498 			return (1);
499 		switch (acep1->ae_tag) {
500 		case ACL_GROUP:
501 		case ACL_USER:
502 			if (acep1->ae_id != acep2->ae_id)
503 				return (1);
504 			/* fall through */
505 		case ACL_USER_OBJ:
506 		case ACL_GROUP_OBJ:
507 		case ACL_OTHER:
508 			if (acep1->ae_perm != acep2->ae_perm)
509 				return (1);
510 		}
511 		acep1++;
512 		acep2++;
513 	}
514 	return (0);
515 }
516