#include <string.h>
#include <limits.h>
#include <dirent.h>
-#include <grp.h>
-#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <glob.h>
#include <fnmatch.h>
#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/param.h>
#include <sys/xattr.h>
#include "log.h"
return r;
}
+static DIR* xopendirat_nomod(int dirfd, const char *path) {
+ DIR *dir;
+
+ dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
+ if (!dir) {
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
+ dirfd == AT_FDCWD ? "" : "sub", path);
+ if (errno == EPERM) {
+ dir = xopendirat(dirfd, path, O_NOFOLLOW);
+ if (!dir)
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
+ dirfd == AT_FDCWD ? "" : "sub", path);
+ }
+ }
+
+ return dir;
+}
+
+static DIR* opendir_nomod(const char *path) {
+ return xopendirat_nomod(AT_FDCWD, path);
+}
+
static int dir_cleanup(
Item *i,
const char *p,
_cleanup_closedir_ DIR *sub_dir;
int q;
- sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
+ sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
if (!sub_dir) {
if (errno != ENOENT)
r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
continue;
}
- if (i->type == IGNORE_DIRECTORY_PATH && streq(dent->d_name, p))
- log_debug("Ignoring directory \"%s\"", sub_path);
- else {
- log_debug("Removing directory \"%s\".", sub_path);
-
- if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
- if (errno != ENOENT && errno != ENOTEMPTY) {
- log_error_errno(errno, "rmdir(%s): %m", sub_path);
- r = -errno;
- }
+ log_debug("Removing directory \"%s\".", sub_path);
+ if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
+ if (errno != ENOENT && errno != ENOTEMPTY) {
+ log_error_errno(errno, "rmdir(%s): %m", sub_path);
+ r = -errno;
}
- }
} else {
/* Skip files for which the sticky bit is
static int get_acls_from_arg(Item *item) {
#ifdef HAVE_ACL
int r;
- _cleanup_(acl_freep) acl_t a = NULL, d = NULL;
assert(item);
* afterwards, so the mask can be added now if necessary. */
r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
if (r < 0)
- log_warning_errno(errno, "Failed to parse ACL \"%s\": %m. Ignoring",
+ log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring",
item->argument);
#else
log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
return 0;
}
+#ifdef HAVE_ACL
static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
_cleanup_(acl_freep) acl_t dup = NULL;
int r;
_cleanup_(acl_free_charpp) char *t = NULL;
+ /* Returns 0 for success, positive error if already warned,
+ * negative error otherwise. */
+
if (modify) {
r = acls_for_file(path, type, acl, &dup);
if (r < 0)
r = acl_set_file(path, type, dup);
if (r < 0)
- return log_error_errno(-errno,
- "Setting %s ACL \"%s\" on %s failed: %m",
- type == ACL_TYPE_ACCESS ? "access" : "default",
- strna(t), path);
+ return -log_error_errno(errno,
+ "Setting %s ACL \"%s\" on %s failed: %m",
+ type == ACL_TYPE_ACCESS ? "access" : "default",
+ strna(t), path);
+
return 0;
}
+#endif
static int path_set_acls(Item *item, const char *path) {
+ int r = 0;
#ifdef HAVE_ACL
- int r;
-
assert(item);
assert(path);
- if (item->acl_access) {
+ if (item->acl_access)
r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
- if (r < 0)
- return r;
- }
- if (item->acl_default) {
+ if (r == 0 && item->acl_default)
r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
- if (r < 0)
- return r;
- }
-#endif
- return 0;
+ if (r > 0)
+ return -r; /* already warned */
+ else if (r == -ENOTSUP) {
+ log_debug_errno(r, "ACLs not supported by file system at %s", path);
+ return 0;
+ } else if (r < 0)
+ log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
+#endif
+ return r;
}
static int write_one_file(Item *i, const char *path) {
/* This returns the first error we run into, but nevertheless
* tries to go on */
- d = opendir(path);
- if (!d) {
- log_debug_errno(errno, "Cannot open directory \"%s\": %m", path);
+ d = opendir_nomod(path);
+ if (!d)
return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
- }
for (;;) {
_cleanup_free_ char *p = NULL;
}
static int glob_item(Item *i, action_t action, bool recursive) {
- _cleanup_globfree_ glob_t g = {};
+ _cleanup_globfree_ glob_t g = {
+ .gl_closedir = (void (*)(void *)) closedir,
+ .gl_readdir = (struct dirent *(*)(void *)) readdir,
+ .gl_opendir = (void *(*)(const char *)) opendir_nomod,
+ .gl_lstat = lstat,
+ .gl_stat = stat,
+ };
int r = 0, k;
char **fn;
errno = 0;
- k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+ k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
if (k != 0 && k != GLOB_NOMATCH)
return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
return r;
}
+typedef enum {
+ CREATION_NORMAL,
+ CREATION_EXISTING,
+ CREATION_FORCE,
+ _CREATION_MODE_MAX,
+ _CREATION_MODE_INVALID = -1
+} CreationMode;
+
+static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
+ [CREATION_NORMAL] = "Created",
+ [CREATION_EXISTING] = "Found existing",
+ [CREATION_FORCE] = "Created replacement",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
+
static int create_item(Item *i) {
struct stat st;
int r = 0;
+ CreationMode creation;
assert(i);
log_debug("\"%s\" already exists and is not a directory.", i->path);
return 0;
}
- }
- log_debug("Created directory \"%s\".", i->path);
+
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+ log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
r = path_set_perms(i, i->path);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
+ creation = CREATION_FORCE;
} else {
log_debug("%s is not a fifo.", i->path);
return 0;
}
- }
- }
- log_debug("Created fifo \"%s\".", i->path);
+ } else
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+ log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
r = path_set_perms(i, i->path);
if (r < 0)
if (r < 0)
return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
+ creation = CREATION_FORCE;
} else {
log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
return 0;
}
- }
- }
- log_debug("Created symlink \"%s\".", i->path);
+ } else
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+ log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
break;
}
if (r < 0)
- return log_error_errno(r, "Failed to create device node %s: %m", i->path);
+ return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
+ creation = CREATION_FORCE;
} else {
log_debug("%s is not a device node.", i->path);
return 0;
}
- }
- }
- log_debug("Created %s device node \"%s\" %u:%u.",
+ } else
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+ log_debug("%s %s device node \"%s\" %u:%u.",
+ creation_mode_verb_to_string(creation),
i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
i->path, major(i->mode), minor(i->mode));
cutoff = n - i->age;
- d = opendir(instance);
+ d = opendir_nomod(instance);
if (!d) {
if (errno == ENOENT || errno == ENOTDIR) {
log_debug_errno(errno, "Directory \"%s\": %m", instance);
unsigned n;
for (n = 0; n < existing->count; n++) {
- if (!item_compatible(existing->items + n, &i))
+ if (!item_compatible(existing->items + n, &i)) {
log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
fname, line, i.path);
+ return 0;
+ }
}
} else {
existing = new0(ItemArray, 1);
" --clean Clean up marked directories\n"
" --remove Remove marked files/directories\n"
" --boot Execute actions only safe at boot\n"
- " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n"
- " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n"
+ " --prefix=PATH Only apply rules with the specified prefix\n"
+ " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
" --root=PATH Operate on an alternate filesystem root\n",
program_invocation_short_name);
}