#include "copy.h"
#include "def.h"
#include "dirent-util.h"
+#include "dissect-image.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "main-func.h"
+#include "missing_stat.h"
+#include "missing_syscall.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "mountpoint-util.h"
#include "offline-passwd.h"
#include "pager.h"
static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
static char *arg_root = NULL;
+static char *arg_image = NULL;
static char *arg_replace = NULL;
#define MAX_DEPTH 256
STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
static struct Item* find_glob(OrderedHashmap *h, const char *match) {
ItemArray *j;
- Iterator i;
- ORDERED_HASHMAP_FOREACH(j, h, i) {
+ ORDERED_HASHMAP_FOREACH(j, h) {
size_t n;
for (n = 0; n < j->n_items; n++) {
return xopendirat_nomod(AT_FDCWD, path);
}
+static inline nsec_t load_statx_timestamp_nsec(const struct statx_timestamp *ts) {
+ assert(ts);
+
+ if (ts->tv_sec < 0)
+ return NSEC_INFINITY;
+
+ if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
+ return NSEC_INFINITY;
+
+ return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
+}
+
static int dir_cleanup(
Item *i,
const char *p,
DIR *d,
- const struct stat *ds,
- usec_t cutoff,
- dev_t rootdev,
+ nsec_t self_atime_nsec,
+ nsec_t self_mtime_nsec,
+ nsec_t cutoff_nsec,
+ dev_t rootdev_major,
+ dev_t rootdev_minor,
bool mountpoint,
int maxdepth,
bool keep_this_level) {
- struct dirent *dent;
bool deleted = false;
+ struct dirent *dent;
int r = 0;
FOREACH_DIRENT_ALL(dent, d, break) {
- struct stat s;
- usec_t age;
_cleanup_free_ char *sub_path = NULL;
+ nsec_t atime_nsec, mtime_nsec, ctime_nsec, btime_nsec;
if (dot_or_dot_dot(dent->d_name))
continue;
- if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
- if (errno == ENOENT)
- continue;
-
+ /* If statx() is supported, use it. It's preferable over fstatat() since it tells us
+ * explicitly where we are looking at a mount point, for free as side information. Determing
+ * the same information without statx() is hard, see the complexity of path_is_mount_point(),
+ * and also much slower as it requires a number of syscalls instead of just one. Hence, when
+ * we have modern statx() we use it instead of fstat() and do proper mount point checks,
+ * while on older kernels's well do traditional st_dev based detection of mount points.
+ *
+ * Using statx() for detecting mount points also has the benfit that we handle weird file
+ * systems such as overlayfs better where each file is originating from a different
+ * st_dev. */
+
+ STRUCT_STATX_DEFINE(sx);
+
+ r = statx_fallback(
+ dirfd(d), dent->d_name,
+ AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
+ STATX_TYPE|STATX_MODE|STATX_UID|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
+ &sx);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0) {
/* FUSE, NFS mounts, SELinux might return EACCES */
r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
- "stat(%s/%s) failed: %m", p, dent->d_name);
+ "statx(%s/%s) failed: %m", p, dent->d_name);
continue;
}
- /* Stay on the same filesystem */
- if (s.st_dev != rootdev) {
- log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
- continue;
- }
-
- /* Try to detect bind mounts of the same filesystem instance; they
- * do not differ in device major/minors. This type of query is not
- * supported on all kernels or filesystem types though. */
- if (S_ISDIR(s.st_mode)) {
- int q;
-
- q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
- if (q < 0)
- log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, dent->d_name);
- else if (q > 0) {
- log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", p, dent->d_name);
+ if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) {
+ /* Yay, we have the mount point API, use it */
+ if (FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT)) {
+ log_debug("Ignoring \"%s/%s\": different mount points.", p, dent->d_name);
+ continue;
+ }
+ } else {
+ /* So we might have statx() but the STATX_ATTR_MOUNT_ROOT flag is not supported, fall
+ * back to traditional stx_dev checking. */
+ if (sx.stx_dev_major != rootdev_major ||
+ sx.stx_dev_minor != rootdev_minor) {
+ log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
continue;
}
+
+ /* Try to detect bind mounts of the same filesystem instance; they do not differ in device
+ * major/minors. This type of query is not supported on all kernels or filesystem types
+ * though. */
+ if (S_ISDIR(sx.stx_mode)) {
+ int q;
+
+ q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
+ if (q < 0)
+ log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, dent->d_name);
+ else if (q > 0) {
+ log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", p, dent->d_name);
+ continue;
+ }
+ }
}
+ atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? load_statx_timestamp_nsec(&sx.stx_atime) : 0;
+ mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? load_statx_timestamp_nsec(&sx.stx_mtime) : 0;
+ ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? load_statx_timestamp_nsec(&sx.stx_ctime) : 0;
+ btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? load_statx_timestamp_nsec(&sx.stx_btime) : 0;
+
sub_path = path_join(p, dent->d_name);
if (!sub_path) {
r = log_oom();
continue;
}
- if (S_ISDIR(s.st_mode)) {
+ if (S_ISDIR(sx.stx_mode)) {
_cleanup_closedir_ DIR *sub_dir = NULL;
if (mountpoint &&
streq(dent->d_name, "lost+found") &&
- s.st_uid == 0) {
+ sx.stx_uid == 0) {
log_debug("Ignoring directory \"%s\".", sub_path);
continue;
}
continue;
}
- q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
+ q = dir_cleanup(i,
+ sub_path, sub_dir,
+ atime_nsec, mtime_nsec, cutoff_nsec,
+ rootdev_major, rootdev_minor,
+ false, maxdepth-1, false);
if (q < 0)
r = q;
}
}
/* Ignore ctime, we change it when deleting */
- age = timespec_load(&s.st_mtim);
- if (age >= cutoff) {
+ if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
char a[FORMAT_TIMESTAMP_MAX];
/* Follows spelling in stat(1). */
log_debug("Directory \"%s\": modify time %s is too new.",
sub_path,
- format_timestamp_us(a, sizeof(a), age));
+ format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
continue;
}
- age = timespec_load(&s.st_atim);
- if (age >= cutoff) {
+ if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
char a[FORMAT_TIMESTAMP_MAX];
log_debug("Directory \"%s\": access time %s is too new.",
sub_path,
- format_timestamp_us(a, sizeof(a), age));
+ format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
+ continue;
+ }
+
+ if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
+ char a[FORMAT_TIMESTAMP_MAX];
+ log_debug("Directory \"%s\": birth time %s is too new.",
+ sub_path,
+ format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
continue;
}
} else {
/* Skip files for which the sticky bit is set. These are semantics we define, and are
* unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */
- if (s.st_mode & S_ISVTX) {
+ if (sx.stx_mode & S_ISVTX) {
log_debug("Skipping \"%s\": sticky bit set.", sub_path);
continue;
}
if (mountpoint &&
- S_ISREG(s.st_mode) &&
- s.st_uid == 0 &&
+ S_ISREG(sx.stx_mode) &&
+ sx.stx_uid == 0 &&
STR_IN_SET(dent->d_name,
".journal",
"aquota.user",
}
/* Ignore sockets that are listed in /proc/net/unix */
- if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
+ if (S_ISSOCK(sx.stx_mode) && unix_socket_alive(sub_path)) {
log_debug("Skipping \"%s\": live socket.", sub_path);
continue;
}
/* Ignore device nodes */
- if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
+ if (S_ISCHR(sx.stx_mode) || S_ISBLK(sx.stx_mode)) {
log_debug("Skipping \"%s\": a device.", sub_path);
continue;
}
continue;
}
- age = timespec_load(&s.st_mtim);
- if (age >= cutoff) {
+ if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
char a[FORMAT_TIMESTAMP_MAX];
/* Follows spelling in stat(1). */
log_debug("File \"%s\": modify time %s is too new.",
sub_path,
- format_timestamp_us(a, sizeof(a), age));
+ format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
continue;
}
- age = timespec_load(&s.st_atim);
- if (age >= cutoff) {
+ if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
char a[FORMAT_TIMESTAMP_MAX];
log_debug("File \"%s\": access time %s is too new.",
sub_path,
- format_timestamp_us(a, sizeof(a), age));
+ format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
continue;
}
- age = timespec_load(&s.st_ctim);
- if (age >= cutoff) {
+ if (ctime_nsec != NSEC_INFINITY && ctime_nsec >= cutoff_nsec) {
char a[FORMAT_TIMESTAMP_MAX];
log_debug("File \"%s\": change time %s is too new.",
sub_path,
- format_timestamp_us(a, sizeof(a), age));
+ format_timestamp_style(a, sizeof(a), ctime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
+ continue;
+ }
+
+ if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
+ char a[FORMAT_TIMESTAMP_MAX];
+ log_debug("File \"%s\": birth time %s is too new.",
+ sub_path,
+ format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
continue;
}
finish:
if (deleted) {
- char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
- usec_t age1, age2;
-
- age1 = timespec_load(&ds->st_atim);
- age2 = timespec_load(&ds->st_mtim);
+ char a[FORMAT_TIMESTAMP_MAX], m[FORMAT_TIMESTAMP_MAX];
+ struct timespec ts[2];
log_debug("Restoring access and modification time on \"%s\": %s, %s",
p,
- format_timestamp_us(a, sizeof(a), age1),
- format_timestamp_us(b, sizeof(b), age2));
+ format_timestamp_style(a, sizeof(a), self_atime_nsec / NSEC_PER_USEC, TIMESTAMP_US),
+ format_timestamp_style(m, sizeof(m), self_mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
+
+ timespec_store_nsec(ts + 0, self_atime_nsec);
+ timespec_store_nsec(ts + 1, self_mtime_nsec);
/* Restore original directory timestamps */
- if (futimens(dirfd(d), (struct timespec[]) {
- ds->st_atim,
- ds->st_mtim }) < 0)
+ if (futimens(dirfd(d), ts) < 0)
log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
}
struct stat stbuf;
mode_t new_mode;
bool do_chown;
+ int r;
assert(i);
assert(fd);
log_debug("\"%s\" matches temporary mode %o already.", path, m);
else {
log_debug("Temporarily changing \"%s\" to mode %o.", path, m);
- if (fchmod_opath(fd, m) < 0)
- return log_error_errno(errno, "fchmod() of %s failed: %m", path);
+ r = fchmod_opath(fd, m);
+ if (r < 0)
+ return log_error_errno(r, "fchmod() of %s failed: %m", path);
}
}
}
log_debug("\"%s\" matches mode %o already.", path, new_mode);
else {
log_debug("Changing \"%s\" to mode %o.", path, new_mode);
- if (fchmod_opath(fd, new_mode) < 0)
- return log_error_errno(errno, "fchmod() of %s failed: %m", path);
+ r = fchmod_opath(fd, new_mode);
+ if (r < 0)
+ return log_error_errno(r, "fchmod() of %s failed: %m", path);
}
}
}
static int write_one_file(Item *i, const char *path) {
_cleanup_close_ int fd = -1, dir_fd = -1;
char *bn;
- int flags, r;
+ int r;
assert(i);
assert(path);
bn = basename(path);
- flags = O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY;
-
/* Follows symlinks */
- fd = openat(dir_fd, bn, i->append_or_force ? flags|O_APPEND : flags, i->mode);
+ fd = openat(dir_fd, bn,
+ O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY|(i->append_or_force ? O_APPEND : 0),
+ i->mode);
if (fd < 0) {
if (errno == ENOENT) {
log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
return 0;
}
+
+ if (i->allow_failure)
+ return log_debug_errno(errno, "Failed to open file \"%s\", ignoring: %m", path);
+
return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
}
dfd, bn,
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID,
- COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE);
+ COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS);
if (r < 0) {
struct stat a, b;
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
else if (r == -EROFS)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
- else if (r == -ENOPROTOOPT)
+ else if (r == -ENOTCONN)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
else if (r < 0)
q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
}
static int clean_item_instance(Item *i, const char* instance) {
+ char timestamp[FORMAT_TIMESTAMP_MAX];
_cleanup_closedir_ DIR *d = NULL;
- struct stat s, ps;
- bool mountpoint;
+ STRUCT_STATX_DEFINE(sx);
+ int mountpoint, r;
usec_t cutoff, n;
- char timestamp[FORMAT_TIMESTAMP_MAX];
assert(i);
return log_error_errno(errno, "Failed to open directory %s: %m", instance);
}
- if (fstat(dirfd(d), &s) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+ r = statx_fallback(dirfd(d), "", AT_EMPTY_PATH, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, &sx);
+ if (r < 0)
+ return log_error_errno(r, "statx(%s) failed: %m", instance);
- if (!S_ISDIR(s.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
- "%s is not a directory.", i->path);
+ if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
+ mountpoint = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
+ else {
+ struct stat ps;
- if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
- return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
+ if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
+ return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
- mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
+ mountpoint =
+ sx.stx_dev_major != major(ps.st_dev) ||
+ sx.stx_dev_minor != minor(ps.st_dev) ||
+ sx.stx_ino != ps.st_ino;
+ }
log_debug("Cleanup threshold for %s \"%s\" is %s",
mountpoint ? "mount point" : "directory",
instance,
- format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
+ format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
- return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
+ return dir_cleanup(i, instance, d,
+ load_statx_timestamp_nsec(&sx.stx_atime),
+ load_statx_timestamp_nsec(&sx.stx_mtime),
+ cutoff * NSEC_PER_USEC,
+ sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
MAX_DEPTH, i->keep_first_level);
}
/* Clean up all children first */
if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
- Iterator i;
ItemArray *c;
- SET_FOREACH(c, array->children, i) {
+ SET_FOREACH(c, array->children) {
int k;
k = process_item_array(c, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
return true;
}
- /* no matches, so we should include this path only if we
- * have no whitelist at all */
+ /* no matches, so we should include this path only if we have no allow list at all */
if (strv_isempty(arg_include_prefixes))
return true;
return cat_files(NULL, files, 0);
}
+static int exclude_default_prefixes(void) {
+ int r;
+
+ /* Provide an easy way to exclude virtual/memory file systems from what we do here. Useful in
+ * combination with --root= where we probably don't want to apply stuff to these dirs as they are
+ * likely over-mounted if the root directory is actually used, and it wouldbe less than ideal to have
+ * all kinds of files created/adjusted underneath these mount points. */
+
+ r = strv_extend_strv(
+ &arg_exclude_prefixes,
+ STRV_MAKE("/dev",
+ "/proc",
+ "/run",
+ "/sys"),
+ true);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
" --boot Execute actions only safe at boot\n"
" --prefix=PATH Only apply rules with the specified prefix\n"
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
+ " -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
" --root=PATH Operate on an alternate filesystem root\n"
+ " --image=PATH Operate on disk image as filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
" --no-pager Do not pipe output into a pager\n"
"\nSee the %s for details.\n"
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
+ ARG_IMAGE,
ARG_REPLACE,
ARG_NO_PAGER,
};
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "replace", required_argument, NULL, ARG_REPLACE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hE", options, NULL)) >= 0)
switch (c) {
return r;
break;
+ case ARG_IMAGE:
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_image);
+ if (r < 0)
+ return r;
+
+ /* Imply -E here since it makes little sense to create files persistently in the /run mointpoint of a disk image */
+ _fallthrough_;
+
+ case 'E':
+ r = exclude_default_prefixes();
+ if (r < 0)
+ return r;
+
+ break;
+
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
!endswith(optarg, ".conf"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"When --replace= is given, some configuration items must be specified");
+ if (arg_root && arg_user)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Combination of --user and --root= is not supported.");
+
+ if (arg_image && arg_root)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+
return 1;
}
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
_cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
- Iterator iterator;
unsigned v = 0;
FILE *f;
Item *i;
}
/* we have to determine age parameter for each entry of type X */
- ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
- Iterator iter;
+ ORDERED_HASHMAP_FOREACH(i, globs) {
Item *j, *candidate_item = NULL;
if (i->type != IGNORE_DIRECTORY_PATH)
continue;
- ORDERED_HASHMAP_FOREACH(j, items, iter) {
+ ORDERED_HASHMAP_FOREACH(j, items) {
if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
continue;
if (!j)
j = ordered_hashmap_get(globs, prefix);
if (j) {
- r = set_ensure_allocated(&j->children, NULL);
- if (r < 0)
- return log_oom();
-
- r = set_put(j->children, a);
+ r = set_ensure_put(&j->children, NULL, a);
if (r < 0)
return log_oom();
ItemArray, item_array_free);
static int run(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
_cleanup_strv_free_ char **config_dirs = NULL;
bool invalid_config = false;
- Iterator iterator;
ItemArray *a;
enum {
PHASE_REMOVE_AND_CLEAN,
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = NULL;
+ char **i;
- t = strv_join(config_dirs, "\n\t");
- if (t)
- log_debug("Looking for configuration files in (higher priority first):\n\t%s", t);
+ STRV_FOREACH(i, config_dirs) {
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(arg_root, *i);
+ if (!j)
+ return log_oom();
+
+ if (!strextend(&t, "\n\t", j, NULL))
+ return log_oom();
+ }
+
+ log_debug("Looking for configuration files in (higher priority first):%s", t);
}
if (arg_cat_config) {
umask(0022);
- mac_selinux_init();
+ r = mac_selinux_init();
+ if (r < 0)
+ return r;
+
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
items = ordered_hashmap_new(&item_array_hash_ops);
globs = ordered_hashmap_new(&item_array_hash_ops);
return r;
/* Let's now link up all child/parent relationships */
- ORDERED_HASHMAP_FOREACH(a, items, iterator) {
+ ORDERED_HASHMAP_FOREACH(a, items) {
r = link_parent(a);
if (r < 0)
return r;
}
- ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
+ ORDERED_HASHMAP_FOREACH(a, globs) {
r = link_parent(a);
if (r < 0)
return r;
continue;
/* The non-globbing ones usually create things, hence we apply them first */
- ORDERED_HASHMAP_FOREACH(a, items, iterator) {
+ ORDERED_HASHMAP_FOREACH(a, items) {
k = process_item_array(a, op);
if (k < 0 && r >= 0)
r = k;
}
/* The globbing ones usually alter things, hence we apply them second. */
- ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
+ ORDERED_HASHMAP_FOREACH(a, globs) {
k = process_item_array(a, op);
if (k < 0 && r >= 0)
r = k;
}
}
- if (ERRNO_IS_RESOURCE(-r))
+ if (ERRNO_IS_RESOURCE(r))
return r;
if (invalid_config)
return EX_DATAERR;