#include "alloc-util.h"
#include "btrfs-util.h"
-#include "chase-symlinks.h"
+#include "chase.h"
#include "chattr-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "env-file.h"
#include "env-util.h"
+#include "extension-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "hostname-setup.h"
#include "id128-util.h"
-#include "lockfile-util.h"
+#include "lock-util.h"
#include "log.h"
#include "loop-util.h"
#include "macro.h"
"/usr/local/lib/portables\0"
"/usr/lib/portables\0",
+ /* Note that we don't allow storing extensions under /usr/, unlike with other image types. That's
+ * because extension images are supposed to extend /usr/, so you get into recursive races, especially
+ * with directory-based extensions, as the kernel's OverlayFS explicitly checks for this and errors
+ * out with -ELOOP if it finds that a lowerdir= is a child of another lowerdir=. */
[IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
"/run/extensions\0" /* and here too */
- "/var/lib/extensions\0" /* the main place for images */
- "/usr/local/lib/extensions\0"
- "/usr/lib/extensions\0",
+ "/var/lib/extensions\0", /* the main place for images */
};
static Image *image_free(Image *i) {
static char **image_settings_path(Image *image) {
_cleanup_strv_free_ char **l = NULL;
- const char *fn;
- unsigned i = 0;
+ _cleanup_free_ char *fn = NULL;
+ size_t i = 0;
+ int r;
assert(image);
if (!l)
return NULL;
- fn = strjoina(image->name, ".nspawn");
+ fn = strjoin(image->name, ".nspawn");
+ if (!fn)
+ return NULL;
FOREACH_STRING(s, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
l[i] = path_join(s, fn);
i++;
}
- l[i] = file_in_same_dir(image->path, fn);
- if (!l[i])
+ r = file_in_same_dir(image->path, fn, l + i);
+ if (r == -ENOMEM)
return NULL;
+ if (r < 0)
+ log_debug_errno(r, "Failed to generate .nspawn settings path from image path, ignoring: %m");
+
+ strv_uniq(l);
return TAKE_PTR(l);
}
-static char *image_roothash_path(Image *image) {
- const char *fn;
+static int image_roothash_path(Image *image, char **ret) {
+ _cleanup_free_ char *fn = NULL;
assert(image);
- fn = strjoina(image->name, ".roothash");
+ fn = strjoin(image->name, ".roothash");
+ if (!fn)
+ return -ENOMEM;
- return file_in_same_dir(image->path, fn);
+ return file_in_same_dir(image->path, fn, ret);
}
static int image_new(
static int extract_pretty(const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *name = NULL;
const char *p;
- size_t n;
assert(path);
assert(ret);
p = last_path_component(path);
- n = strcspn(p, "/");
- name = strndup(p, n);
+ name = strdupcspn(p, "/");
if (!name)
return -ENOMEM;
(faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
if (S_ISDIR(st->st_mode)) {
- _cleanup_close_ int fd = -1;
+ _cleanup_close_ int fd = -EBADF;
unsigned file_attr = 0;
usec_t crtime = 0;
return 0;
} else if (S_ISBLK(st->st_mode)) {
- _cleanup_close_ int block_fd = -1;
+ _cleanup_close_ int block_fd = -EBADF;
uint64_t size = UINT64_MAX;
/* A block device */
struct stat st;
int flags;
- r = chase_symlinks_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
+ r = chase_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
if (r == -ENOENT)
continue;
if (r < 0)
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
- r = chase_symlinks_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
+ r = chase_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
if (r == -ENOENT)
continue;
if (r < 0)
if (!settings)
return -ENOMEM;
- roothash = image_roothash_path(i);
- if (!roothash)
- return -ENOMEM;
+ r = image_roothash_path(i, &roothash);
+ if (r < 0)
+ return r;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
}
static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
- _cleanup_free_ char *rs = NULL;
- const char *fn;
-
- fn = strjoina(new_name, suffix);
+ _cleanup_free_ char *fn = NULL, *rs = NULL;
+ int r;
- rs = file_in_same_dir(path, fn);
- if (!rs)
+ fn = strjoin(new_name, suffix);
+ if (!fn)
return -ENOMEM;
+ r = file_in_same_dir(path, fn, &rs);
+ if (r < 0)
+ return r;
+
return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs);
}
if (!settings)
return -ENOMEM;
- roothash = image_roothash_path(i);
- if (!roothash)
- return -ENOMEM;
+ r = image_roothash_path(i, &roothash);
+ if (r < 0)
+ return r;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
_fallthrough_;
case IMAGE_SUBVOLUME:
- new_path = file_in_same_dir(i->path, new_name);
+ r = file_in_same_dir(i->path, new_name, &new_path);
break;
case IMAGE_BLOCK:
if (path_startswith(i->path, "/dev"))
return -EROFS;
- new_path = file_in_same_dir(i->path, new_name);
+ r = file_in_same_dir(i->path, new_name, &new_path);
break;
case IMAGE_RAW: {
const char *fn;
fn = strjoina(new_name, ".raw");
- new_path = file_in_same_dir(i->path, fn);
+
+ r = file_in_same_dir(i->path, fn, &new_path);
break;
}
default:
return -EOPNOTSUPP;
}
-
- if (!new_path)
- return -ENOMEM;
+ if (r < 0)
+ return r;
nn = strdup(new_name);
if (!nn)
}
static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
- _cleanup_free_ char *rs = NULL;
- const char *fn;
-
- fn = strjoina(new_name, suffix);
+ _cleanup_free_ char *fn = NULL, *rs = NULL;
+ int r;
- rs = file_in_same_dir(path, fn);
- if (!rs)
+ fn = strjoin(new_name, suffix);
+ if (!fn)
return -ENOMEM;
- return copy_file_atomic(path, rs, 0664, 0, 0, COPY_REFLINK);
+ r = file_in_same_dir(path, fn, &rs);
+ if (r < 0)
+ return r;
+
+ return copy_file_atomic(path, rs, 0664, COPY_REFLINK);
}
int image_clone(Image *i, const char *new_name, bool read_only) {
if (!settings)
return -ENOMEM;
- roothash = image_roothash_path(i);
- if (!roothash)
- return -ENOMEM;
+ r = image_roothash_path(i, &roothash);
+ if (r < 0)
+ return r;
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
case IMAGE_RAW:
new_path = strjoina("/var/lib/machines/", new_name, ".raw");
- r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME);
+ r = copy_file_atomic_full(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, FS_NOCOW_FL,
+ COPY_REFLINK|COPY_CRTIME, NULL, NULL);
break;
case IMAGE_BLOCK:
}
case IMAGE_BLOCK: {
- _cleanup_close_ int fd = -1;
+ _cleanup_close_ int fd = -EBADF;
struct stat st;
int state = b;
return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
}
-int image_read_metadata(Image *i) {
+int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
int r;
_cleanup_free_ char *hostname = NULL;
_cleanup_free_ char *path = NULL;
- r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
+ if (i->class == IMAGE_EXTENSION) {
+ r = extension_has_forbidden_content(i->path);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+ "Conflicting content found in image %s, refusing.",
+ i->name);
+ }
+
+ r = chase("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
else if (r >= 0) {
path = mfree(path);
- r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
- if (r < 0 && r != -ENOENT)
- log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name);
- else if (r >= 0) {
- _cleanup_close_ int fd = -1;
-
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- log_debug_errno(errno, "Failed to open %s: %m", path);
- else {
- r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id);
- if (r < 0)
- log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
- }
- }
-
- path = mfree(path);
+ r = id128_get_machine(i->path, &machine_id);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read machine ID in image %s, ignoring: %m", i->name);
- r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
+ r = chase("/etc/machine-info", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path, NULL);
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
else if (r >= 0) {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
- r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
+ r = loop_device_make_by_path(i->path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
if (r < 0)
return r;
r = dissect_loop_device(
d,
- NULL, NULL,
+ /* verity= */ NULL,
+ /* mount_options= */ NULL,
+ image_policy,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |