#define _GNU_SOURCE
#include <errno.h>
+#include <fcntl.h> /* AT_FOO constants */
#include <ftw.h> /* nftw() et al. */
#include <getopt.h>
#include <libgen.h> /* dirname() */
}
struct stat s;
- int result = stat(path, &s);
+ int result = lstat(path, &s);
if (result == 0) {
return s.st_mode;
}
else {
- /* errno will be set already by stat() */
+ /* errno will be set already by lstat() */
return result;
}
}
}
struct stat s;
- int result = stat(path, &s);
+ int result = lstat(path, &s);
if (result == 0) {
return S_ISREG(s.st_mode);
}
+/**
+ * @brief Determine whether or not the given path is accessible.
+ *
+ * @param path
+ * The path to test.
+ *
+ * @return true if @c path is accessible to the current effective
+ * user/group, false otherwise.
+ */
+bool path_accessible(const char* path) {
+ if (path == NULL) {
+ return false;
+ }
+
+ /* Test for access using the effective user and group rather than
+ the real one. */
+ int flags = AT_EACCESS;
+
+ /* Don't follow symlinks when checking for a path's existence,
+ since we won't follow them to set its ACLs either. */
+ flags |= AT_SYMLINK_NOFOLLOW;
+
+ /* If the path is relative, interpret it relative to the current
+ working directory (just like the access() system call). */
+ int result = faccessat(AT_FDCWD, path, F_OK, flags);
+
+ if (result == 0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+
+
/**
* @brief Determine whether or not the given path is a directory.
*
}
struct stat s;
- int result = stat(path, &s);
+ int result = lstat(path, &s);
if (result == 0) {
return S_ISDIR(s.st_mode);
}
}
+
/**
* @brief Determine whether @c path is executable (by anyone) or a
* directory.
int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
while (ge_result == ACL_SUCCESS) {
+ /* The first thing we do is check to see if this is a mask
+ entry. If it is, we skip it entirely. */
+ acl_tag_t tag = ACL_UNDEFINED_TAG;
+ int tag_result = acl_get_tag_type(entry, &tag);
+
+ if (tag_result == ACL_ERROR) {
+ perror("any_can_execute_or_dir (acl_get_tag_type)");
+ result = ACL_ERROR;
+ goto cleanup;
+ }
+
+ if (tag == ACL_MASK) {
+ ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+ continue;
+ }
+
+ /* Ok, so it's not a mask entry. Check the execute perms. */
acl_permset_t permset;
int ps_result = acl_get_permset(entry, &permset);
}
if (gp_result == ACL_SUCCESS) {
- /* Only return one if this execute bit is not masked. */
+ /* Only return ACL_SUCCESS if this execute bit is not masked. */
if (acl_execute_masked(path) != ACL_SUCCESS) {
result = ACL_SUCCESS;
goto cleanup;
* The program name to use in the output.
*
*/
-void usage(char* program_name) {
+void usage(const char* program_name) {
printf("Apply any applicable default ACLs to the given files or "
"directories.\n\n");
printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
const char* target = argv[arg_index];
bool reapp_result = false;
+ /* Make sure we can access the given path before we go out of our
+ * way to please it. Doing this check outside of
+ * apply_default_acl() lets us spit out a better error message for
+ * typos, too.
+ */
+ if (!path_accessible(target)) {
+ fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], target);
+ result = EXIT_FAILURE;
+ continue;
+ }
+
if (recursive) {
reapp_result = apply_default_acl_recursive(target, no_exec_mask);
}
else {
- /* It's either normal file, or we're not operating recursively. */
+ /* It's either a normal file, or we're not operating recursively. */
reapp_result = apply_default_acl(target, no_exec_mask);
}