Home | History | Annotate | Line # | Download | only in common
nfs_commonacl.c revision 1.1
      1 /*	$NetBSD: nfs_commonacl.c,v 1.1 2013/09/30 07:19:35 dholland 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 240720 2012-09-20 02:49:25Z rmacklem "); */
     31 __RCSID("$NetBSD: nfs_commonacl.c,v 1.1 2013/09/30 07:19:35 dholland Exp $");
     32 
     33 #ifndef APPLEKEXT
     34 #include <fs/nfs/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 	} else {
    353 		if (ace->ae_perm & ACL_READ_DATA)
    354 			acemask |= NFSV4ACE_READDATA;
    355 		if (ace->ae_perm & ACL_WRITE_DATA)
    356 			acemask |= NFSV4ACE_WRITEDATA;
    357 		if (ace->ae_perm & ACL_APPEND_DATA)
    358 			acemask |= NFSV4ACE_APPENDDATA;
    359 		if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
    360 			acemask |= NFSV4ACE_READNAMEDATTR;
    361 		if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
    362 			acemask |= NFSV4ACE_WRITENAMEDATTR;
    363 		if (ace->ae_perm & ACL_EXECUTE)
    364 			acemask |= NFSV4ACE_EXECUTE;
    365 		if (ace->ae_perm & ACL_READ_ATTRIBUTES)
    366 			acemask |= NFSV4ACE_READATTRIBUTES;
    367 		if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
    368 			acemask |= NFSV4ACE_WRITEATTRIBUTES;
    369 		if (ace->ae_perm & ACL_DELETE)
    370 			acemask |= NFSV4ACE_DELETE;
    371 		if (ace->ae_perm & ACL_READ_ACL)
    372 			acemask |= NFSV4ACE_READACL;
    373 		if (ace->ae_perm & ACL_WRITE_ACL)
    374 			acemask |= NFSV4ACE_WRITEACL;
    375 		if (ace->ae_perm & ACL_WRITE_OWNER)
    376 			acemask |= NFSV4ACE_WRITEOWNER;
    377 		if (ace->ae_perm & ACL_SYNCHRONIZE)
    378 			acemask |= NFSV4ACE_SYNCHRONIZE;
    379 	}
    380 	*tl++ = txdr_unsigned(acemask);
    381 	*tl++ = txdr_unsigned(namelen);
    382 	if (full_len - namelen)
    383 		*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
    384 	NFSBCOPY(name, (caddr_t)tl, namelen);
    385 	return (full_len + 4 * NFSX_UNSIGNED);
    386 }
    387 
    388 /*
    389  * Build an NFSv4 ACL.
    390  */
    391 APPLESTATIC int
    392 nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, enum vtype type,
    393     NFSPROC_T *p)
    394 {
    395 	int i, entrycnt = 0, retlen;
    396 	u_int32_t *entrycntp;
    397 	int isowner, isgroup, namelen, malloced;
    398 	u_char *name, namestr[NFSV4_SMALLSTR];
    399 
    400 	NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
    401 	retlen = NFSX_UNSIGNED;
    402 	/*
    403 	 * Loop through the acl entries, building each one.
    404 	 */
    405 	for (i = 0; i < aclp->acl_cnt; i++) {
    406 		isowner = isgroup = malloced = 0;
    407 		switch (aclp->acl_entry[i].ae_tag) {
    408 		case ACL_USER_OBJ:
    409 			isowner = 1;
    410 			name = "OWNER@";
    411 			namelen = 6;
    412 			break;
    413 		case ACL_GROUP_OBJ:
    414 			isgroup = 1;
    415 			name = "GROUP@";
    416 			namelen = 6;
    417 			break;
    418 		case ACL_EVERYONE:
    419 			name = "EVERYONE@";
    420 			namelen = 9;
    421 			break;
    422 		case ACL_USER:
    423 			name = namestr;
    424 			nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
    425 			    &namelen, p);
    426 			if (name != namestr)
    427 				malloced = 1;
    428 			break;
    429 		case ACL_GROUP:
    430 			isgroup = 1;
    431 			name = namestr;
    432 			nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
    433 			    &namelen, p);
    434 			if (name != namestr)
    435 				malloced = 1;
    436 			break;
    437 		default:
    438 			continue;
    439 		};
    440 		retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
    441 		    isowner, &aclp->acl_entry[i]);
    442 		entrycnt++;
    443 		if (malloced)
    444 			free(name, M_NFSSTRING);
    445 	}
    446 	*entrycntp = txdr_unsigned(entrycnt);
    447 	return (retlen);
    448 }
    449 
    450 /*
    451  * Set an NFSv4 acl.
    452  */
    453 APPLESTATIC int
    454 nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
    455     NFSPROC_T *p)
    456 {
    457 	int error;
    458 
    459 	if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
    460 		error = NFSERR_ATTRNOTSUPP;
    461 		goto out;
    462 	}
    463 	/*
    464 	 * With NFSv4 ACLs, chmod(2) may need to add additional entries.
    465 	 * Make sure it has enough room for that - splitting every entry
    466 	 * into two and appending "canonical six" entries at the end.
    467 	 * Cribbed out of kern/vfs_acl.c - Rick M.
    468 	 */
    469 	if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) {
    470 		error = NFSERR_ATTRNOTSUPP;
    471 		goto out;
    472 	}
    473 	error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
    474 
    475 out:
    476 	NFSEXITCODE(error);
    477 	return (error);
    478 }
    479 
    480 /*
    481  * Compare two NFSv4 acls.
    482  * Return 0 if they are the same, 1 if not the same.
    483  */
    484 APPLESTATIC int
    485 nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
    486 {
    487 	int i;
    488 	struct acl_entry *acep1, *acep2;
    489 
    490 	if (aclp1->acl_cnt != aclp2->acl_cnt)
    491 		return (1);
    492 	acep1 = aclp1->acl_entry;
    493 	acep2 = aclp2->acl_entry;
    494 	for (i = 0; i < aclp1->acl_cnt; i++) {
    495 		if (acep1->ae_tag != acep2->ae_tag)
    496 			return (1);
    497 		switch (acep1->ae_tag) {
    498 		case ACL_GROUP:
    499 		case ACL_USER:
    500 			if (acep1->ae_id != acep2->ae_id)
    501 				return (1);
    502 			/* fall through */
    503 		case ACL_USER_OBJ:
    504 		case ACL_GROUP_OBJ:
    505 		case ACL_OTHER:
    506 			if (acep1->ae_perm != acep2->ae_perm)
    507 				return (1);
    508 		};
    509 		acep1++;
    510 		acep2++;
    511 	}
    512 	return (0);
    513 }
    514