]> gitweb.michael.orlitzky.com - apply-default-acl.git/commitdiff
Add another test.
authorMichael Orlitzky <michael@orlitzky.com>
Wed, 15 Aug 2012 06:51:08 +0000 (02:51 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Wed, 15 Aug 2012 06:51:08 +0000 (02:51 -0400)
Settle on the new reapplication algorithm.
Remove a ton of dead code.

run-tests.sh
src/aclq.c

index 4bfd36461bbe3cd944792df67785938046326986..a01d47c1922df9481ecb7213405593b8956776d5 100755 (executable)
@@ -139,8 +139,8 @@ compare
 
 
 
-# Make sure execute permission is removed for group/other after the
-# reapplication.
+# Since the default ACL will grant r-x to group/other, they will wind
+# up with it.
 TESTNUM=6
 touch "${TARGET}"
 chmod 744 "${TARGET}"
@@ -151,9 +151,9 @@ setfacl -d -m user:mail:rwx "${TESTDIR}"
 EXPECTED=$(cat <<EOF
 user::rwx
 user:mail:rwx
-group::r--
+group::r-x
 mask::rwx
-other::r--
+other::r-x
 
 EOF
 )
@@ -162,9 +162,8 @@ ACTUAL=`getfacl --omit-header "${TARGET}"`
 compare
 
 
-# In fact, no existing named entries without execute permissions
-# should be granted execute permissions as the result of
-# reapplication.
+# Some named entries can be granted execute permissions as the result
+# of reapplication.
 TESTNUM=7
 touch "${TARGET}"
 chmod 744 "${TARGET}"
@@ -177,9 +176,32 @@ setfacl -d -m user:news:rwx "${TESTDIR}"
 EXPECTED=$(cat <<EOF
 user::rwx
 user:mail:rwx
-user:news:rw-
-group::r--
+user:news:rwx
+group::r-x
 mask::rwx
+other::r-x
+
+EOF
+)
+
+ACTUAL=`getfacl --omit-header "${TARGET}"`
+compare
+
+
+# We should not retain any entries that aren't in the default.
+TESTNUM=8
+touch "${TARGET}"
+chmod 644 "${TARGET}"
+setfacl -m user:news:rw "${TARGET}"
+setfacl -d -m user:mail:rwx "${TESTDIR}"
+./aclq "${TARGET}"
+
+
+EXPECTED=$(cat <<EOF
+user::rw-
+user:mail:rwx  #effective:rw-
+group::r--
+mask::rw-
 other::r--
 
 EOF
@@ -187,3 +209,23 @@ EOF
 
 ACTUAL=`getfacl --omit-header "${TARGET}"`
 compare
+
+
+# A slightly modified test #1 to make sure it works right.
+TESTNUM=9
+TARGET="${TESTDIR}"/foo
+touch "${TARGET}"
+chmod 777 "${TARGET}"
+setfacl -d -m user::r--  "${TESTDIR}"
+./aclq "${TARGET}"
+
+EXPECTED=$(cat <<EOF
+user::r--
+group::r-x
+other::r-x
+
+EOF
+)
+
+ACTUAL=`getfacl --omit-header "${TARGET}"`
+compare
index 19749cdf671b1de3e6e2aa8eb3180e7614aa4159..a50d33f8249971d6a54c187be65351b7defb4ae5 100644 (file)
@@ -32,15 +32,6 @@ mode_t get_mode(const char* path) {
   }
 }
 
-bool mode_has_perm(mode_t mode, int perm) {
-  if (mode & perm) {
-    return true;
-  }
-  else {
-    return false;
-  }
-}
-
 
 bool is_regular_file(const char* path) {
   if (path == NULL) {
@@ -73,521 +64,22 @@ bool is_directory(const char* path) {
 }
 
 
-int has_type_tag_acl(const char* path,
-                    acl_type_t type,
-                    acl_tag_t desired_tag) {
-  /* Returns one if the given path has a default ACL for the supplied
-     tag, zero if it doesn't, and -1 on error. */
-  acl_t defacl = acl_get_file(path, type);
-
-  if (defacl == (acl_t)NULL) {
-    return 0;
-  }
-
-  acl_entry_t entry;
-  int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
-
-  while (result == 1) {
-    acl_tag_t tag = ACL_UNDEFINED_TAG;
-    int tag_result = acl_get_tag_type(entry, &tag);
-
-    if (tag_result == -1) {
-       perror("has_default_tag_acl (acl_get_tag_type)");
-       return -1;
-    }
-    else {
-      if (tag == desired_tag) {
-       return 1;
-      }
-    }
-
-    result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
-  }
-
-  if (result == -1) {
-    perror("has_default_tag_acl (acl_get_entry)");
-    return -1;
-  }
-
-  return 0;
-}
-
-int has_default_tag_acl(const char* path, acl_tag_t desired_tag) {
-  return has_type_tag_acl(path, ACL_TYPE_DEFAULT, desired_tag);
-}
-
-int has_access_tag_acl(const char* path, acl_tag_t desired_tag) {
-  return has_type_tag_acl(path, ACL_TYPE_ACCESS, desired_tag);
-}
-
-int has_default_user_obj_acl(const char* path) {
-  return has_default_tag_acl(path, ACL_USER_OBJ);
-}
-
-int has_default_group_obj_acl(const char* path) {
-  return has_default_tag_acl(path, ACL_GROUP_OBJ);
-}
-
-int has_default_other_acl(const char* path) {
-  return has_default_tag_acl(path, ACL_OTHER);
-}
-
-int has_default_mask_acl(const char* path) {
-  return has_default_tag_acl(path, ACL_MASK);
-}
-
-
-int get_type_tag_entry(const char* path,
-                      acl_type_t type,
-                      acl_tag_t desired_tag,
-                      acl_entry_t* entry) {
-    /* Returns one if successful, zero when the ACL doesn't exist, and
-     -1 on unexpected errors. */
-  acl_t acl = acl_get_file(path, type);
-
-  if (acl == (acl_t)NULL) {
-    /* Follow the acl_foo convention of -1 == error. */
-    return 0;
-  }
-
-  int result = acl_get_entry(acl, ACL_FIRST_ENTRY, entry);
-
-  while (result == 1) {
-    acl_tag_t tag = ACL_UNDEFINED_TAG;
-    int tag_result = acl_get_tag_type(*entry, &tag);
-
-    if (tag_result == -1) {
-       perror("get_type_tag_entry (acl_get_tag_type)");
-       return -1;
-    }
-
-    if (tag == desired_tag) {
-      /* We found the right tag, so return successfully. */
-      return 1;
-    }
-
-    result = acl_get_entry(acl, ACL_NEXT_ENTRY, entry);
-  }
-
-  /* This catches both the initial acl_get_entry and the ones at the
-     end of the loop. */
-  if (result == -1) {
-    perror("get_type_tag_entry (acl_get_entry)");
-    return -1;
-  }
-
-  return 0;
-}
-
-int get_default_tag_entry(const char* path,
-                         acl_tag_t desired_tag,
-                         acl_entry_t* entry) {
-  return get_type_tag_entry(path, ACL_TYPE_DEFAULT, desired_tag, entry);
-}
-
-int get_access_tag_entry(const char* path,
-                         acl_tag_t desired_tag,
-                         acl_entry_t* entry) {
-  return get_type_tag_entry(path, ACL_TYPE_ACCESS, desired_tag, entry);
-}
-
-
-
-int get_type_tag_permset(const char* path,
-                        acl_type_t type,
-                        acl_tag_t desired_tag,
-                        acl_permset_t* output_perms) {
-  /* Returns one if successful, zero when the ACL doesn't exist, and
-     -1 on unexpected errors. */
-  acl_t defacl = acl_get_file(path, type);
-
-  if (defacl == (acl_t)NULL) {
-    /* Follow the acl_foo convention of -1 == error. */
-    return 0;
-  }
-
-  acl_entry_t entry;
-  int result = get_type_tag_entry(path, type, desired_tag, &entry);
-
-  if (result == 1) {
-    /* We found the right tag, now get the permset. */
-    int ps_result = acl_get_permset(entry, output_perms);
-    if (ps_result == -1) {
-      perror("get_type_tag_permset (acl_get_permset)");
-      return -1;
-    }
-
-    if (ps_result == 0) {
-      return 1;
-    }
-    else {
-      return 0;
-    }
-  }
-  else {
-    return result;
-  }
-}
-
-int get_default_tag_permset(const char* path,
-                           acl_tag_t desired_tag,
-                           acl_permset_t* output_perms) {
-  return get_type_tag_permset(path,
-                             ACL_TYPE_DEFAULT,
-                             desired_tag,
-                             output_perms);
-}
-
-int get_access_tag_permset(const char* path,
-                          acl_tag_t desired_tag,
-                          acl_permset_t* output_perms) {
-  return get_type_tag_permset(path, ACL_TYPE_ACCESS, desired_tag, output_perms);
-}
-
-int has_default_tag_perm(const char* path,
-                         acl_tag_t tag,
-                         acl_perm_t perm) {
-  /* Check path to see if tag has the given perm. Returns one if it
-     does, zero if it doesn't (or there's no ACL), and -1 on unexpected
-     errors. */
-
-  if (!has_default_tag_acl(path, tag)) {
-    return 0;
-  }
-
-  acl_permset_t permset;
-  bool ps_result = get_default_tag_permset(path, tag, &permset);
-
-  if (ps_result != 1) {
-    /* Failure or error. */
-    return ps_result;
-  }
-
-  int p_result = acl_get_perm(permset, perm);
-  if (p_result == -1) {
-    perror("has_default_tag_perm (acl_get_perm)");
-  }
-
-  return p_result;
-}
-
-int remove_access_tag_perm(const char* path,
-                          acl_tag_t desired_tag,
-                          acl_perm_t perm) {
-  /* Attempt to remove perm from tag. Returns one if successful, zero
-     if there was nothing to do, and -1 on errors. */
-  acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
-  if (acl == (acl_t)NULL) {
-    /* Error. */
-    return -1;
-  }
-
-  acl_permset_t permset;
-  bool ps_result = get_access_tag_permset(path, desired_tag, &permset);
-
-  if (ps_result != 1) {
-    /* Failure or error. */
-    return ps_result;
-  }
-
-  int d_result = acl_delete_perm(permset, perm);
-  if (d_result == -1) {
-    perror("remove_access_tag_perm (acl_delete_perm)");
-    return -1;
-  }
-
-  /* We've only removed perm from the permset; now we have to replace
-     the permset. */
-  acl_entry_t entry;
-  int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
-
-  while (result == 1) {
-    acl_tag_t tag = ACL_UNDEFINED_TAG;
-    int tag_result = acl_get_tag_type(entry, &tag);
-
-    if (tag_result == -1) {
-       perror("remove_access_tag_perm (acl_get_tag_type)");
-       return -1;
-    }
-
-    if (tag == desired_tag) {
-      /* We found the right tag. Update the permset. */
-      int s_result = acl_set_permset(entry, permset);
-      if (s_result == -1) {
-       perror("remove_access_tag_perm (acl_set_permset)");
-       return -1;
-      }
-
-      int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl);
-      if (sf_result == -1) {
-       perror("remove_access_tag_perm (acl_set_file)");
-       return -1;
-      }
-
-      return 1;
-    }
-
-    result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
-  }
-
-  /* This catches both the initial acl_get_entry and the ones at the
-     end of the loop. */
-  if (result == -1) {
-    perror("remove_access_tag_perm (acl_get_entry)");
-    return -1;
-  }
-
-  return 0;
-}
-
-int remove_access_group_obj_execute(const char* path) {
-  return remove_access_tag_perm(path, ACL_GROUP_OBJ, ACL_EXECUTE);
-}
-
-
-int has_default_user_obj_read(const char* path) {
-  return has_default_tag_perm(path, ACL_USER_OBJ, ACL_READ);
-}
-
-int has_default_user_obj_write(const char* path) {
-  return has_default_tag_perm(path, ACL_USER_OBJ, ACL_WRITE);
-}
-
-int has_default_user_obj_execute(const char* path) {
-  return has_default_tag_perm(path, ACL_USER_OBJ, ACL_EXECUTE);
-}
-
-int has_default_group_obj_read(const char* path) {
-  return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_READ);
-}
-
-int has_default_group_obj_write(const char* path) {
-  return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_WRITE);
-}
-
-int has_default_group_obj_execute(const char* path) {
-  return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_EXECUTE);
-}
-
-int has_default_other_read(const char* path) {
-  return has_default_tag_perm(path, ACL_OTHER, ACL_READ);
-}
-
-int has_default_other_write(const char* path) {
-  return has_default_tag_perm(path, ACL_OTHER, ACL_WRITE);
-}
-
-int has_default_other_execute(const char* path) {
-  return has_default_tag_perm(path, ACL_OTHER, ACL_EXECUTE);
-}
-
-int has_default_mask_read(const char* path) {
-  return has_default_tag_perm(path, ACL_MASK, ACL_READ);
-}
-
-int has_default_mask_write(const char* path) {
-  return has_default_tag_perm(path, ACL_MASK, ACL_WRITE);
-}
-
-int has_default_mask_execute(const char* path) {
-  return has_default_tag_perm(path, ACL_MASK, ACL_EXECUTE);
-}
-
-
-int reapply_default_acl(const char* path) {
-  /* If this is a normal file or directory (i.e. that has just been
-     created), we proceed to find its parent directory which will have
-     a default ACL.
-
-     Returns one for success, zero for failure (i.e. no ACL), and -1
-     on unexpected errors. */
-  if (path == NULL) {
-    return 0;
-  }
-
-  if (!is_regular_file(path) && !is_directory(path)) {
-    return 0;
-  }
-
-  /* dirname mangles its argument */
-  char path_copy[PATH_MAX];
-  strncpy(path_copy, path, PATH_MAX-1);
-  path_copy[PATH_MAX-1] = 0;
-
-  char* parent = dirname(path_copy);
-  if (!is_directory(parent)) {
-    /* Make sure dirname() did what we think it did. */
-    return 0;
-  }
-
-  /* This is the original mode of path. We will simply add permissions
-     to it, and then later reapply the result via chmod. */
-  mode_t path_mode = get_mode(path);
-
-  if (has_default_mask_acl(parent)) {
-    /* The parent has an extended ACL. Extended ACLs use the mask
-       entry. */
-
-    /* For the group bits, we'll use the ACL's mask instead of the group
-       object bits. If the default ACL had a group entry, it should
-       already have propagated (but might be masked). */
-    if (has_default_mask_read(parent)) {
-      path_mode |= S_IRGRP;
-    }
-    else {
-      path_mode &= ~S_IRGRP;
-    }
-
-    if (has_default_mask_write(parent)) {
-      path_mode |= S_IWGRP;
-    }
-    else {
-      path_mode &= ~S_IWGRP;
-    }
-
-    if (!mode_has_perm(path_mode, S_IXGRP)) {
-      /* The group ACL entry should already have been inherited from the
-        default ACL. If the source was not group executable, we want to
-        modify the destination so that it is not group executable
-        either. In the presence of ACLs, the group permissions come not
-        from the mode bits, but from the group:: ACL entry. So, to do
-        this, we remove the group::x entry. */
-      remove_access_group_obj_execute(path);
-    }
-
-    /* We need to determine whether or not to mask the execute
-       bit. This applies not only to the user/group/other entries, but
-       also to all other named entries. If the original file wasn't
-       executable, then the result probably should not be. To
-       determine whether or not "it was executable", we rely on the
-       user execute bits. Obviously this should be done before we
-       twiddle that bit. */
-    if (has_default_mask_execute(parent)) {
-      if (mode_has_perm(path_mode, S_IXUSR)) {
-       /* This just adds the group execute bit, and doesn't actually
-            grant group execute permissions. */
-       path_mode |= S_IXGRP;
-      }
-    }
-    else {
-      path_mode &= ~S_IXGRP;
-    }
-
-  }
-  else {
-    /* It's a minimal ACL. We'll repeat for the group bits what we
-       already did for the owner/other bits. */
-    if (has_default_group_obj_acl(parent)) {
-      if (has_default_group_obj_read(parent)) {
-       path_mode |= S_IRGRP;
-      }
-      else {
-       path_mode &= ~S_IRGRP;
-      }
-
-
-      if (has_default_group_obj_write(parent)) {
-       path_mode |= S_IWGRP;
-      }
-      else {
-       path_mode &= ~S_IWGRP;
-      }
-
-      /* We don't want to set the execute bit on via the ACL unless it
-        was on originally. */
-      if (!has_default_group_obj_execute(parent)) {
-       path_mode &= ~S_IXGRP;
-      }
-    }
-  }
-
-
-  /* If parent has a default user ACL, apply it. */
-  if (has_default_user_obj_acl(parent)) {
-
-    if (has_default_user_obj_read(parent)) {
-      /* Add user read. */
-      path_mode |= S_IRUSR;
-    }
-    else {
-      /* Remove user read. */
-      path_mode &= ~S_IRUSR;
-    }
-
-
-    if (has_default_user_obj_write(parent)) {
-      /* Add user write. */
-      path_mode |= S_IWUSR;
-    }
-    else {
-      /* Remove user write. */
-      path_mode &= ~S_IWUSR;
-    }
-
-
-    /* We don't want to set the execute bit on via the ACL unless it
-       was on originally. */
-    if (!has_default_user_obj_execute(parent)) {
-      /* Remove user execute. */
-      path_mode &= ~S_IXUSR;
-    }
-  }
-
-
-  /* Do the same thing with the other perms/ACL. */
-  if (has_default_other_acl(parent)) {
-
-    if (has_default_other_read(parent)) {
-      path_mode |= S_IROTH;
-    }
-    else {
-      path_mode &= ~S_IROTH;
-    }
-
-
-    if (has_default_other_write(parent)) {
-      path_mode |= S_IWOTH;
-    }
-    else {
-      path_mode &= ~S_IWOTH;
-    }
-
-
-    /* We don't want to set the execute bit on via the ACL unless it
-       was on originally. */
-    if (!has_default_other_execute(parent)) {
-      path_mode &= ~S_IXOTH;
-    }
-  }
-
-  int chmod_result = chmod(path, path_mode);
-  if (chmod_result == 0) {
-    return 1;
-  }
-  else {
-    return 0;
-  }
-}
-
-
-
 
-
-int set_acl_entry(acl_t* aclp,
+int acl_set_entry(acl_t* aclp,
                  acl_entry_t entry) {
   /* Update or create the given entry. */
 
   acl_tag_t entry_tag;
   int gt_result = acl_get_tag_type(entry, &entry_tag);
   if (gt_result == -1) {
-    perror("set_acl_entry (acl_get_tag_type)");
+    perror("acl_set_entry (acl_get_tag_type)");
     return -1;
   }
 
   acl_permset_t entry_permset;
   int ps_result = acl_get_permset(entry, &entry_permset);
   if (ps_result == -1) {
-    perror("set_acl_entry (acl_get_permset)");
+    perror("acl_set_entry (acl_get_permset)");
     return -1;
   }
 
@@ -605,69 +97,21 @@ int set_acl_entry(acl_t* aclp,
     }
 
     if (existing_tag == entry_tag) {
-      bool update_it = true;
-
-      if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) {
-       void* entry_qual = acl_get_qualifier(entry);
-       if (entry_qual == (void*)NULL) {
-         perror("set_acl_entry (acl_get_qualifier - entry_qual)");
-         return -1;
-       }
-
-       void* existing_qual = acl_get_qualifier(existing_entry);
-       if (existing_qual == (void*)NULL) {
-         perror("set_acl_entry (acl_get_qualifier - existing_qual)");
-         return -1;
-       }
-
-       if (entry_tag == ACL_USER) {
-         uid_t* u1p = (uid_t *)entry_qual;
-         uid_t* u2p = (uid_t *)existing_qual;
-         if (*u1p != *u2p) {
-           update_it = false;
-         }
-       }
-       else {
-         gid_t* g1p = (gid_t *)entry_qual;
-         gid_t* g2p = (gid_t *)existing_qual;
-         if (*g1p != *g2p) {
-           update_it = false;
-         }
-       }
-
-       acl_free(entry_qual);
-       acl_free(existing_qual);
-      }
-
-      if (update_it) {
+      if (entry_tag == ACL_USER_OBJ ||
+         entry_tag == ACL_GROUP_OBJ ||
+         entry_tag == ACL_OTHER) {
+       /* Only update for these three since all other tags will have
+          been wiped. */
        acl_permset_t existing_permset;
        int gep_result = acl_get_permset(existing_entry, &existing_permset);
        if (gep_result == -1) {
-         perror("set_acl_entry (acl_get_permset)");
-         return -1;
-       }
-
-       /* If an existing entry doesn't have its execute bits set, we
-          don't want to set them from a default ACL. Unless it's the
-          mask entry! */
-       int gp_result = acl_get_perm(existing_permset, ACL_EXECUTE);
-       if (gp_result == -1) {
-         perror("set_acl_entry (acl_get_perm)");
+         perror("acl_set_entry (acl_get_permset)");
          return -1;
        }
 
-       if (gp_result == 0 && existing_tag != ACL_MASK) {
-         /* It doesn't already have execute perms */
-         int d_result = acl_delete_perm(entry_permset, ACL_EXECUTE);
-         if (d_result == -1) {
-           perror("set_acl_entry (acl_delete_perm)");
-           return -1;
-         }
-       }
-
        int s_result = acl_set_permset(existing_entry, entry_permset);
        if (s_result == -1) {
-         perror("set_acl_entry (acl_set_permset)");
+         perror("acl_set_entry (acl_set_permset)");
          return -1;
        }
 
@@ -682,7 +126,7 @@ int set_acl_entry(acl_t* aclp,
   /* This catches both the initial acl_get_entry and the ones at the
      end of the loop. */
   if (result == -1) {
-    perror("set_acl_entry (acl_get_entry)");
+    perror("acl_set_entry (acl_get_entry)");
     return -1;
   }
 
@@ -691,19 +135,19 @@ int set_acl_entry(acl_t* aclp,
   acl_entry_t new_entry;
   int c_result = acl_create_entry(aclp, &new_entry);
   if (c_result == -1) {
-    perror("set_acl_entry (acl_create_entry)");
+    perror("acl_set_entry (acl_create_entry)");
     return -1;
   }
 
   int st_result = acl_set_tag_type(new_entry, entry_tag);
   if (st_result == -1) {
-    perror("set_acl_entry (acl_set_tag_type)");
+    perror("acl_set_entry (acl_set_tag_type)");
     return -1;
   }
 
   int s_result = acl_set_permset(new_entry, entry_permset);
   if (s_result == -1) {
-    perror("set_acl_entry (acl_set_permset)");
+    perror("acl_set_entry (acl_set_permset)");
     return -1;
   }
 
@@ -711,13 +155,13 @@ int set_acl_entry(acl_t* aclp,
     /* We need to set the qualifier too. */
     void* entry_qual = acl_get_qualifier(entry);
     if (entry_qual == (void*)NULL) {
-      perror("set_acl_entry (acl_get_qualifier)");
+      perror("acl_set_entry (acl_get_qualifier)");
       return -1;
     }
 
     int sq_result = acl_set_qualifier(new_entry, entry_qual);
     if (sq_result == -1) {
-      perror("set_acl_entry (acl_set_qualifier)");
+      perror("acl_set_entry (acl_set_qualifier)");
       return -1;
     }
   }
@@ -727,8 +171,8 @@ int set_acl_entry(acl_t* aclp,
 
 
 
-int acl_is_minimal(acl_t* acl) {
-  /* An ACL is minimal if it has fewer than four entries. */
+int acl_entry_count(acl_t* acl) {
+  /* Return the number of entries in ACL, or -1 on error. */
   acl_entry_t entry;
   int entry_count = 0;
   int result = acl_get_entry(*acl, ACL_FIRST_ENTRY, &entry);
@@ -743,7 +187,21 @@ int acl_is_minimal(acl_t* acl) {
     return -1;
   }
 
-  if (entry_count > 3) {
+  return entry_count;
+}
+
+
+int acl_is_minimal(acl_t* acl) {
+  /* An ACL is minimal if it has fewer than four entries. Return 0 for
+     false, 1 for true, and -1 on error. */
+
+  int ec = acl_entry_count(acl);
+  if (ec == -1) {
+    perror("acl_is_minimal (acl_entry_count)");
+    return -1;
+  }
+
+  if (ec < 4) {
     return 1;
   }
   else {
@@ -751,7 +209,10 @@ int acl_is_minimal(acl_t* acl) {
   }
 }
 
+
 int any_can_execute(const char* path) {
+  /* Returns 1 if any ACL entry has execute access, 0 if none do, and
+     -1 on error. */
   acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
 
   if (acl_is_minimal(&acl)) {
@@ -802,7 +263,86 @@ int any_can_execute(const char* path) {
 }
 
 
-int reapply_default_acl_ng(const char* path) {
+int inherit_default_acl(const char* path, const char* parent) {
+  /* Inherit the default ACL from parent to path. This overwrites any
+     existing default ACL. Returns 1 for success, 0 for failure, and
+     -1 on error. */
+
+  if (path == NULL) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  if (!is_directory(path) || !is_directory(parent)) {
+    return 0;
+  }
+
+  acl_t parent_acl = acl_get_file(parent, ACL_TYPE_DEFAULT);
+  acl_t path_acl = acl_dup(parent_acl);
+
+  if (path_acl == (acl_t)NULL) {
+    perror("inherit_default_acl (acl_dup)");
+    return -1;
+  }
+
+  int sf_result = acl_set_file(path, ACL_TYPE_DEFAULT, path_acl);
+  if (sf_result == -1) {
+    perror("inherit_default_acl (acl_set_file)");
+    return -1;
+  }
+
+  acl_free(path_acl);
+  return 1;
+}
+
+
+int wipe_acls(const char* path) {
+  /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from
+     path. Returns 1 for success, 0 for failure, and -1 on error. */
+
+  if (path == NULL) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  /* Finally, remove individual named/mask entries. */
+  acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
+  if (acl == (acl_t)NULL) {
+    perror("wipe_acls (acl_get_file)");
+    return -1;
+  }
+
+  acl_entry_t entry;
+  int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+
+  while (result == 1) {
+    int d_result = acl_delete_entry(acl, entry);
+    if (d_result == -1) {
+      perror("wipe_acls (acl_delete_entry)");
+      return -1;
+    }
+
+    result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+  }
+
+  /* Catches the first acl_get_entry as well as the ones at the end of
+     the loop. */
+  if (result == -1) {
+    perror("reapply_default_acl_ng (acl_get_entry)");
+    return -1;
+  }
+
+  int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl);
+  if (sf_result == -1) {
+    perror("wipe_acls (acl_set_file)");
+    return -1;
+  }
+
+  return 1;
+}
+
+
+int reapply_default_acl(const char* path) {
   /* Really reapply the default ACL by looping through it. Returns one
      for success, zero for failure (i.e. no ACL), and -1 on unexpected
      errors. */
@@ -834,10 +374,31 @@ int reapply_default_acl_ng(const char* path) {
   bool allow_exec = (bool)ace_result;
 
   acl_t defacl = acl_get_file(parent, ACL_TYPE_DEFAULT);
+
+  if (defacl == (acl_t)NULL) {
+    perror("reapply_default_acl_ng (acl_get_file)");
+    return -1;
+  }
+
+  int wipe_result = wipe_acls(path);
+  if (wipe_result == -1) {
+    perror("reapply_default_acl_ng (wipe_acls)");
+    return -1;
+  }
+
+  /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
+     ACL with this one. */
   acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
+  if (acl == (acl_t)NULL) {
+    perror("reapply_default_acl_ng (acl_get_file)");
+    return -1;
+  }
 
-  if (defacl == (acl_t)NULL || acl == (acl_t)NULL) {
-    return 0;
+  /* If it's a directory, inherit the parent's default. */
+  int inherit_result = inherit_default_acl(path, parent);
+  if (inherit_result == -1) {
+    perror("reapply_default_acl_ng (inherit_acls)");
+    return -1;
   }
 
   acl_entry_t entry;
@@ -885,9 +446,9 @@ int reapply_default_acl_ng(const char* path) {
     }
 
     /* Finally, add the permset to the access ACL. */
-    int set_result = set_acl_entry(&acl, entry);
+    int set_result = acl_set_entry(&acl, entry);
     if (set_result == -1) {
-      perror("reapply_default_acl_ng (set_acl_entry)");
+      perror("reapply_default_acl_ng (acl_set_entry)");
       return -1;
     }
 
@@ -915,7 +476,7 @@ int reapply_default_acl_ng(const char* path) {
 int main(int argc, char* argv[]) {
    const char* target = argv[1];
 
-   bool result = reapply_default_acl_ng(target);
+   bool result = reapply_default_acl(target);
 
    if (result) {
      return EXIT_SUCCESS;