+ recurse:
+ if (recursive && S_ISDIR(s.st_mode)) {
+ /* Recurse into subdirectories. Don't call closedir() on d! It
+ closes the open file descriptor as well, and subsequent calls
+ to close() then throw errors. */
+ DIR* d = fdopendir(fd);
+ if (d == NULL) {
+ perror("apply_default_acl_fds (fdopendir)");
+ result = ACL_ERROR;
+ goto cleanup;
+ }
+
+ struct dirent* de;
+ int new_fd = 0;
+ while ((de = readdir(d)) != NULL) {
+ if (de->d_type != DT_DIR && de->d_type != DT_REG) {
+ /* Hit a symlink or whatever. */
+ result = ACL_FAILURE;
+ continue;
+ }
+ if (strcmp(de->d_name, ".") == 0) { continue; }
+ if (strcmp(de->d_name, "..") == 0) { continue; }
+
+ /* Be careful not to "return" out of this loop and leave the
+ new_fd open! */
+ new_fd = openat(fd, de->d_name, O_NOFOLLOW);
+ if (new_fd == OPEN_ERROR) {
+ if (errno == ELOOP || errno == ENOTDIR) {
+ /* We hit a symlink, either in the last path component (ELOOP)
+ or higher up (ENOTDIR). */
+ if (result == ACL_SUCCESS) {
+ /* Don't overwrite an error result with success/failure. */
+ result = ACL_FAILURE;
+ }
+ continue;
+ }
+ else {
+ perror("apply_default_acl_fds (openat)");
+ result = ACL_ERROR;
+ continue;
+ }
+ }
+ switch (apply_default_acl_fds(fd, new_fd, recursive)) {
+ /* Don't overwrite an error result with success/failure. */
+ case ACL_FAILURE:
+ if (result == ACL_SUCCESS) {
+ result = ACL_FAILURE;
+ }
+ break;
+ case ACL_ERROR:
+ result = ACL_ERROR;
+ default:
+ if (close(new_fd) == CLOSE_ERROR) {
+ perror("apply_default_acl_fds (close)");
+ result = ACL_ERROR;
+ }
+ }
+ }
+ }
+
+ cleanup:
+ acl_free(new_acl);
+ acl_free(new_acl_unmasked);
+ return result;
+}
+
+
+/**
+ * @brief Apply parent default ACL to a path and optionally its children.
+ *
+ * This overwrites any existing ACLs on the target, and, if @c
+ * recursive is @c true, its children. When @c recursive is @c true,
+ * the "worst" result encountered is returned as the overall result.
+ *
+ * @param path
+ * The path whose ACL we would like to reset to its default.
+ *
+ * @param recursive
+ * Should we recurse into subdirectories?
+ *
+ * @return
+ * - @c ACL_SUCCESS - The parent default ACLs were inherited successfully.
+ * - @c ACL_FAILURE - If symlinks or hard links are encountered.
+ * - @c ACL_ERROR - Unexpected library error.
+ */
+int apply_default_acl(const char* path, bool recursive) {
+
+ if (path == NULL) {
+ errno = EINVAL;
+ perror("apply_default_acl (args)");
+ return ACL_ERROR;
+ }
+
+ /* Define these next three variables here because we may have to
+ * jump to the cleanup routine which expects them to exist.
+ */
+
+ /* Our return value. */
+ int result = ACL_SUCCESS;
+
+ /* The file descriptor corresponding to "path" */
+ int fd = 0;
+
+ /* The file descriptor for the directory containing "path" */
+ int parent_fd = 0;
+
+ /* dirname() and basename() mangle their arguments, so we need
+ to make copies of "path" before using them. */
+ char* dirname_path_copy = NULL;
+ char* basename_path_copy = NULL;
+
+ /* Get the parent directory of "path" with dirname(), which happens
+ * to murder its argument and necessitates a path_copy. */
+ dirname_path_copy = strdup(path);
+ if (dirname_path_copy == NULL) {
+ perror("apply_default_acl (strdup)");
+ return ACL_ERROR;
+ }
+ char* parent = dirname(dirname_path_copy);
+
+ /* Just kidding, if the path is ".", then dirname will do the wrong
+ * thing and give us "." as its parent, too. So, we handle that as a
+ * special case.
+ *
+ * WARNING: it is important that "parent" itself is not used after
+ * this point; otherwise we would need to store the correct parent
+ * path in there. But since everything uses file descriptors from
+ * now on, we only need to ensure that we get the correct parent_fd
+ * below. */
+ if (strcmp(path, ".") == 0 && strcmp(parent, ".") == 0) {
+ parent_fd = safe_open("..", O_DIRECTORY | O_NOFOLLOW);
+ }
+ else {
+ parent_fd = safe_open(parent, O_DIRECTORY | O_NOFOLLOW);
+ }
+ if (parent_fd == OPEN_ERROR) {
+ if (errno == ELOOP || errno == ENOTDIR) {
+ /* We hit a symlink, either in the last path component (ELOOP)
+ or higher up (ENOTDIR). */
+ result = ACL_FAILURE;
+ goto cleanup;
+ }
+ else {
+ perror("apply_default_acl (open parent fd)");
+ result = ACL_ERROR;
+ goto cleanup;
+ }
+ }
+
+ /* We already obtained the parent fd safely, so if we use the
+ basename of path here instead of the full thing, then we can get
+ away with using openat() and spare ourselves the slowness of
+ another safe_open(). */
+ basename_path_copy = strdup(path);
+ if (basename_path_copy == NULL) {
+ perror("apply_default_acl (strdup)");