Index: kern/kern_acl.c
===================================================================
RCS file: /data/ncvs/src/sys/kern/kern_acl.c,v
retrieving revision 1.42
diff -u -r1.42 kern_acl.c
--- kern/kern_acl.c	22 Jun 2003 08:41:42 -0000	1.42
+++ kern/kern_acl.c	29 Jul 2003 16:45:40 -0000
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
+ * Copyright (c) 1999, 2000, 2001, 2002, 2003 Robert N. M. Watson
  * All rights reserved.
  *
  * This software was developed by Robert Watson for the TrustedBSD Project.
@@ -473,6 +473,64 @@
 }
 
 /*
+ * Utility function to generate a file mode given a complete POSIX.1e
+ * access ACL.  Note that if the ACL is improperly formed, this may
+ * result in a panic.
+ */
+mode_t
+acl_posix1e_acl_to_mode(struct acl *acl)
+{
+	struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other;
+	int i;
+
+	/*
+	 * Find the ACL entries relevant to a POSIX permission mode.
+	 */
+	acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL;
+	for (i = 0; i < acl->acl_cnt; i++) {
+		switch (acl->acl_entry[i].ae_tag) {
+		case ACL_USER_OBJ:
+			acl_user_obj = &acl->acl_entry[i];
+			break;
+
+		case ACL_GROUP_OBJ:
+			acl_group_obj = &acl->acl_entry[i];
+			break;
+
+		case ACL_OTHER:
+			acl_other = &acl->acl_entry[i];
+			break;
+
+		case ACL_MASK:
+			acl_mask = &acl->acl_entry[i];
+			break;
+
+		case ACL_USER:
+		case ACL_GROUP:
+			break;
+
+		default:
+			panic("acl_posix1e_acl_to_mode: bad ae_tag");
+		}
+	}
+
+	if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL)
+		panic("acl_posix1e_acl_to_mode: missing base ae_tags");
+
+	/*
+	 * POSIX.1e specifies that if there is an ACL_MASK entry, we replace
+	 * the mode "group" bits with its permissions.  If there isn't, we
+	 * use the ACL_GROUP_OBJ permissions.
+	 */
+	if (acl_mask != NULL)
+		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask,
+		    acl_other));
+	else
+		return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj,
+		    acl_other));
+}
+
+/*
  * Perform a syntactic check of the ACL, sufficient to allow an
  * implementing filesystem to determine if it should accept this and
  * rely on the POSIX.1e ACL properties.
@@ -557,6 +615,31 @@
 	    (num_acl_mask != 1))
 		return (EINVAL);
 	return (0);
+}
+
+/*
+ * Given a requested mode for a new object, and a default ACL, combine
+ * the two to produce a new mode.  Be careful not to clear any bits that
+ * aren't intended to be affected by the POSIX.1e ACL.  Eventually,
+ * this might also take the cmask as an argument, if we push that down
+ * into per-filesystem-code.
+ */
+mode_t
+acl_posix1e_newfilemode(mode_t cmode, struct acl *dacl)
+{
+	mode_t mode;
+
+	mode = cmode;
+	/*
+	 * The current composition policy is that a permission bit must
+	 * be set in *both* the ACL and the requested creation mode for
+	 * it to appear in the resulting mode/ACL.  First clear any
+	 * possibly effected bits, then reconstruct.
+	 */
+	mode &= ACL_PRESERVE_MASK;
+	mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl));
+
+	return (mode);
 }
 
 /*
Index: sys/acl.h
===================================================================
RCS file: /data/ncvs/src/sys/sys/acl.h,v
retrieving revision 1.25
diff -u -r1.25 acl.h
--- sys/acl.h	29 Dec 2002 20:30:00 -0000	1.25
+++ sys/acl.h	29 Jul 2003 16:39:09 -0000
@@ -117,18 +117,40 @@
 #ifdef _KERNEL
 
 /*
+ * POSIX.1e ACLs are capable of expressing the read, write, and execute
+ * bits of the POSIX mode field.  We provide two masks: one that defines
+ * the bits the ACL will replace in the mode, and the other that defines
+ * the bits that must be preseved when an ACL is updating a mode.
+ */
+#define	ACL_OVERRIDE_MASK	(S_IRWXU | S_IRWXG | S_IRWXO)
+#define	ACL_PRESERVE_MASK	(~ACL_OVERRIDE_MASK)
+
+/*
  * Storage for ACLs and support structures.
  */
 #ifdef MALLOC_DECLARE
 MALLOC_DECLARE(M_ACL);
 #endif
 
-acl_perm_t	acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode);
+/*
+ * File system independent code to move back and forth between POSIX mode
+ * and POSIX.1e ACL representations.
+ */
+acl_perm_t		acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode);
 struct acl_entry	acl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid,
-    gid_t gid, mode_t mode);
-mode_t	acl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry,
-    struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry);
-int	acl_posix1e_check(struct acl *acl);
+			    gid_t gid, mode_t mode);
+mode_t			acl_posix1e_perms_to_mode(
+			    struct acl_entry *acl_user_obj_entry,
+			    struct acl_entry *acl_group_obj_entry,
+			    struct acl_entry *acl_other_entry);
+mode_t			acl_posix1e_acl_to_mode(struct acl *acl);
+mode_t			acl_posix1e_newfilemode(mode_t cmode,
+			    struct acl *dacl);
+
+/*
+ * File system independent syntax check for a POSIX.1e ACL.
+ */
+int			acl_posix1e_check(struct acl *acl);
 
 #else /* !_KERNEL */
 
Index: ufs/ufs/acl.h
===================================================================
RCS file: /data/ncvs/src/sys/ufs/ufs/acl.h,v
retrieving revision 1.4
diff -u -r1.4 acl.h
--- ufs/ufs/acl.h	19 Mar 2002 22:40:48 -0000	1.4
+++ ufs/ufs/acl.h	29 Jul 2003 16:00:15 -0000
@@ -38,8 +38,7 @@
 #ifdef _KERNEL
 
 void	ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl);
-void	ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip,
-	    mode_t preserve_mask);
+void	ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip);
 
 int	ufs_getacl(struct vop_getacl_args *);
 int	ufs_setacl(struct vop_setacl_args *);
Index: ufs/ufs/ufs_acl.c
===================================================================
RCS file: /data/ncvs/src/sys/ufs/ufs/ufs_acl.c,v
retrieving revision 1.17
diff -u -r1.17 ufs_acl.c
--- ufs/ufs/ufs_acl.c	11 Jun 2003 06:34:30 -0000	1.17
+++ ufs/ufs/ufs_acl.c	29 Jul 2003 15:57:22 -0000
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1999-2001 Robert N. M. Watson
+ * Copyright (c) 1999-2001, 2003 Robert N. M. Watson
  * All rights reserved.
  *
  * This software was developed by Robert Watson for the TrustedBSD Project.
@@ -126,83 +126,16 @@
 }
 
 /*
- * Synchronize an inode and an ACL by copying over appropriate ACL fields to
- * the passed inode.  Assumes an ACL that would satisfy acl_posix1e_check(),
- * and may panic if not.  This code will preserve existing use of the
- * sticky, setugid, and non-permission bits in the mode field.  It may
- * be that the caller wishes to have previously authorized these changes,
- * and may also want to clear the setugid bits in some situations.
+ * Calculate what the inode mode should look like based on an authoritative
+ * ACL for the inode.  Replace only the fields in the inode that the ACL
+ * can represent.
  */
 void
-ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip,
-    mode_t preserve_mask)
+ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
 {
-	struct acl_entry	*acl_mask, *acl_user_obj, *acl_group_obj;
-	struct acl_entry	*acl_other;
-	mode_t	preserve_mode;
-	int	i;
-
-	/*
-	 * Preserve old mode so we can restore appropriate bits of it.
-	 */
-	preserve_mode = (ip->i_mode & preserve_mask);
-
-	/*
-	 * Identify the ACL_MASK and all other entries appearing in the
-	 * inode mode.
-	 */
-	acl_user_obj = NULL;
-	acl_group_obj = NULL;
-	acl_other = NULL;
-	acl_mask = NULL;
-	for (i = 0; i < acl->acl_cnt; i++) {
-		switch (acl->acl_entry[i].ae_tag) {
-		case ACL_USER_OBJ:
-			acl_user_obj = &acl->acl_entry[i];
-			break;
-
-		case ACL_GROUP_OBJ:
-			acl_group_obj = &acl->acl_entry[i];
-			break;
-
-		case ACL_OTHER:
-			acl_other = &acl->acl_entry[i];
-			break;
 
-		case ACL_MASK:
-			acl_mask = &acl->acl_entry[i];
-			break;
-
-		case ACL_USER:
-		case ACL_GROUP:
-			break;
-
-		default:
-			panic("ufs_sync_inode_from_acl(): bad ae_tag");
-		}
-	}
-
-	if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL)
-		panic("ufs_sync_inode_from_acl(): missing ae_tags");
-
-	if (acl_mask == NULL) {
-		/*
-		 * There is no ACL_MASK, so use the ACL_GROUP_OBJ entry.
-		 */
-		ip->i_mode &= ~(S_IRWXU|S_IRWXG|S_IRWXO);
-		ip->i_mode |= acl_posix1e_perms_to_mode(acl_user_obj,
-		    acl_group_obj, acl_other);
-		DIP(ip, i_mode) = ip->i_mode;
-	} else {
-		/*
-		 * Use the ACL_MASK entry.
-		 */
-		ip->i_mode &= ~(S_IRWXU|S_IRWXG|S_IRWXO);
-		ip->i_mode |= acl_posix1e_perms_to_mode(acl_user_obj,
-		    acl_mask, acl_other);
-		DIP(ip, i_mode) = ip->i_mode;
-	}
-	ip->i_mode |= preserve_mode;
+	ip->i_mode &= ACL_PRESERVE_MASK;
+	ip->i_mode |= acl_posix1e_acl_to_mode(acl);
 	DIP(ip, i_mode) = ip->i_mode;
 }
 
@@ -366,7 +299,6 @@
 	} */ *ap;
 {
 	struct inode *ip = VTOI(ap->a_vp);
-	mode_t old_mode, preserve_mask;
 	int error;
 
 	if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
@@ -461,9 +393,7 @@
 		 * Now that the EA is successfully updated, update the
 		 * inode and mark it as changed.
 		 */
-		old_mode = ip->i_mode;
-		preserve_mask = ISVTX | ISGID | ISUID;
-		ufs_sync_inode_from_acl(ap->a_aclp, ip, preserve_mask);
+		ufs_sync_inode_from_acl(ap->a_aclp, ip);
 		ip->i_flag |= IN_CHANGE;
 	}
 
Index: ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /data/ncvs/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.229
diff -u -r1.229 ufs_vnops.c
--- ufs/ufs/ufs_vnops.c	4 Jul 2003 12:16:33 -0000	1.229
+++ ufs/ufs/ufs_vnops.c	29 Jul 2003 16:41:00 -0000
@@ -1454,16 +1454,11 @@
 		case 0:
 			/*
 			 * Retrieved a default ACL, so merge mode and ACL if
-			 * necessary.
+			 * necessary.  If the ACL is empty, fall through to
+			 * the "not defined or available" case.
 			 */
 			if (acl->acl_cnt != 0) {
-				/*
-				 * Two possible ways for default ACL to not
-				 * be present.  First, the EA can be
-				 * undefined, or second, the default ACL can
-				 * be blank.  If it's blank, fall through to
-				 * the it's not defined case.
-				 */
+				dmode = acl_posix1e_newfilemode(dmode, acl);
 				ip->i_mode = dmode;
 				DIP(ip, i_mode) = dmode;
 				*dacl = *acl;
@@ -2445,6 +2440,7 @@
 				 * be blank.  If it's blank, fall through to
 				 * the it's not defined case.
 				 */
+				mode = acl_posix1e_newfilemode(mode, acl);
 				ip->i_mode = mode;
 				DIP(ip, i_mode) = mode;
 				ufs_sync_acl_from_inode(ip, acl);
