Home | History | Annotate | Line # | Download | only in common
      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
     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
    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
    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
    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
    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
    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