summaryrefslogtreecommitdiffstats
path: root/src/portable/portable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/portable/portable.c')
-rw-r--r--src/portable/portable.c213
1 files changed, 151 insertions, 62 deletions
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 3b2a379..53418c4 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -33,6 +33,7 @@
#include "path-lookup.h"
#include "portable.h"
#include "process-util.h"
+#include "rm-rf.h"
#include "selinux-util.h"
#include "set.h"
#include "signal-util.h"
@@ -42,6 +43,7 @@
#include "strv.h"
#include "tmpfile-util.h"
#include "user-util.h"
+#include "vpick.h"
/* Markers used in the first line of our 20-portable.conf unit file drop-in to determine, that a) the unit file was
* dropped there by the portable service logic and b) for which image it was dropped there. */
@@ -181,7 +183,7 @@ static int extract_now(
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
_cleanup_close_ int os_release_fd = -EBADF;
_cleanup_free_ char *os_release_path = NULL;
const char *os_release_id;
@@ -361,7 +363,13 @@ static int portable_extract_by_path(
assert(path);
- r = loop_device_make_by_path(path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
+ r = loop_device_make_by_path(
+ path,
+ O_RDONLY,
+ /* sector_size= */ UINT32_MAX,
+ LO_FLAGS_PARTSCAN,
+ LOCK_SH,
+ &d);
if (r == -EISDIR) {
_cleanup_free_ char *image_name = NULL;
@@ -383,6 +391,21 @@ static int portable_extract_by_path(
_cleanup_(rmdir_and_freep) char *tmpdir = NULL;
_cleanup_close_pair_ int seq[2] = EBADF_PAIR;
_cleanup_(sigkill_waitp) pid_t child = 0;
+ DissectImageFlags flags =
+ DISSECT_IMAGE_READ_ONLY |
+ DISSECT_IMAGE_GENERIC_ROOT |
+ DISSECT_IMAGE_REQUIRE_ROOT |
+ DISSECT_IMAGE_DISCARD_ON_LOOP |
+ DISSECT_IMAGE_RELAX_VAR_CHECK |
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES |
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
+
+ if (path_is_extension)
+ flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0);
+ else
+ flags |= DISSECT_IMAGE_VALIDATE_OS;
/* We now have a loopback block device, let's fork off a child in its own mount namespace, mount it
* there, and extract the metadata we need. The metadata is sent from the child back to us. */
@@ -398,14 +421,7 @@ static int portable_extract_by_path(
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
- DISSECT_IMAGE_READ_ONLY |
- DISSECT_IMAGE_GENERIC_ROOT |
- DISSECT_IMAGE_REQUIRE_ROOT |
- DISSECT_IMAGE_DISCARD_ON_LOOP |
- DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_USR_NO_ROOT |
- DISSECT_IMAGE_ADD_PARTITION_DEVICES |
- DISSECT_IMAGE_PIN_PARTITION_DEVICES,
+ flags,
&m);
if (r == -ENOPKG)
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
@@ -427,15 +443,8 @@ static int portable_extract_by_path(
if (r < 0)
return r;
if (r == 0) {
- DissectImageFlags flags = DISSECT_IMAGE_READ_ONLY;
-
seq[0] = safe_close(seq[0]);
- if (path_is_extension)
- flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0);
- else
- flags |= DISSECT_IMAGE_VALIDATE_OS;
-
r = dissected_image_mount(
m,
tmpdir,
@@ -556,6 +565,7 @@ static int extract_image_and_extensions(
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
@@ -564,7 +574,27 @@ static int extract_image_and_extensions(
assert(name_or_path);
- r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
+ /* If we get a path, then check if it can be resolved with vpick. We need this as we might just
+ * get a simple image name, which would make vpick error out. */
+ if (path_is_absolute(name_or_path)) {
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ name_or_path,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ name_or_path);
+
+ name_or_path = result.path;
+ }
+
+ r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
if (r < 0)
return r;
@@ -580,9 +610,29 @@ static int extract_image_and_extensions(
}
STRV_FOREACH(p, extension_image_paths) {
+ _cleanup_(pick_result_done) PickResult ext_result = PICK_RESULT_NULL;
_cleanup_(image_unrefp) Image *new = NULL;
+ const char *path = *p;
+
+ if (path_is_absolute(*p)) {
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ *p,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &ext_result);
+ if (r < 0)
+ return r;
+ if (!ext_result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ *p);
- r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new);
+ path = ext_result.path;
+ }
+
+ r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new);
if (r < 0)
return r;
@@ -1341,7 +1391,7 @@ static int attach_unit_file(
return 0;
}
-static int image_symlink(
+static int image_target_path(
const char *image_path,
PortableFlags flags,
char **ret) {
@@ -1367,37 +1417,66 @@ static int image_symlink(
return 0;
}
-static int install_image_symlink(
+static int install_image(
const char *image_path,
PortableFlags flags,
PortableChange **changes,
size_t *n_changes) {
- _cleanup_free_ char *sl = NULL;
+ _cleanup_free_ char *target = NULL;
int r;
assert(image_path);
- /* If the image is outside of the image search also link it into it, so that it can be found with short image
- * names and is listed among the images. */
+ /* If the image is outside of the image search also link it into it, so that it can be found with
+ * short image names and is listed among the images. If we are operating in mixed mode, the image is
+ * copied instead. */
if (image_in_search_path(IMAGE_PORTABLE, NULL, image_path))
return 0;
- r = image_symlink(image_path, flags, &sl);
+ r = image_target_path(image_path, flags, &target);
if (r < 0)
return log_debug_errno(r, "Failed to generate image symlink path: %m");
- (void) mkdir_parents(sl, 0755);
+ (void) mkdir_parents(target, 0755);
+
+ if (flags & PORTABLE_MIXED_COPY_LINK) {
+ r = copy_tree(image_path,
+ target,
+ UID_INVALID,
+ GID_INVALID,
+ COPY_REFLINK | COPY_FSYNC | COPY_FSYNC_FULL | COPY_SYNCFS,
+ /* denylist= */ NULL,
+ /* subvolumes= */ NULL);
+ if (r < 0)
+ return log_debug_errno(
+ r,
+ "Failed to copy %s %s %s: %m",
+ image_path,
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ target);
- if (symlink(image_path, sl) < 0)
- return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), sl);
+ } else {
+ if (symlink(image_path, target) < 0)
+ return log_debug_errno(
+ errno,
+ "Failed to link %s %s %s: %m",
+ image_path,
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ target);
+ }
- (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, sl, image_path);
+ (void) portable_changes_add(
+ changes,
+ n_changes,
+ (flags & PORTABLE_MIXED_COPY_LINK) ? PORTABLE_COPY : PORTABLE_SYMLINK,
+ target,
+ image_path);
return 0;
}
-static int install_image_and_extensions_symlinks(
+static int install_image_and_extensions(
const Image *image,
OrderedHashmap *extension_images,
PortableFlags flags,
@@ -1410,12 +1489,12 @@ static int install_image_and_extensions_symlinks(
assert(image);
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
- r = install_image_symlink(ext->path, flags, changes, n_changes);
+ r = install_image(ext->path, flags, changes, n_changes);
if (r < 0)
return r;
}
- r = install_image_symlink(image->path, flags, changes, n_changes);
+ r = install_image(image->path, flags, changes, n_changes);
if (r < 0)
return r;
@@ -1436,6 +1515,7 @@ static void log_portable_verb(
const char *verb,
const char *message_id,
const char *image_path,
+ const char *profile,
OrderedHashmap *extension_images,
char **extension_image_paths,
PortableFlags flags) {
@@ -1494,12 +1574,14 @@ static void log_portable_verb(
LOG_CONTEXT_PUSH_STRV(extension_base_names);
log_struct(LOG_INFO,
- LOG_MESSAGE("Successfully %s%s '%s%s%s'",
+ LOG_MESSAGE("Successfully %s%s '%s%s%s%s%s'",
verb,
FLAGS_SET(flags, PORTABLE_RUNTIME) ? " ephemeral" : "",
image_path,
isempty(extensions_joined) ? "" : "' and its extension(s) '",
- strempty(extensions_joined)),
+ strempty(extensions_joined),
+ isempty(profile) ? "" : "' using profile '",
+ strempty(profile)),
message_id,
"PORTABLE_ROOT=%s", strna(root_base_name));
}
@@ -1519,7 +1601,7 @@ int portable_attach(
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
_cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
PortableMetadata *item;
@@ -1608,14 +1690,15 @@ int portable_attach(
return sd_bus_error_set_errnof(error, r, "Failed to attach unit '%s': %m", item->name);
}
- /* We don't care too much for the image symlink, it's just a convenience thing, it's not necessary for proper
- * operation otherwise. */
- (void) install_image_and_extensions_symlinks(image, extension_images, flags, changes, n_changes);
+ /* We don't care too much for the image symlink/copy, it's just a convenience thing, it's not necessary for
+ * proper operation otherwise. */
+ (void) install_image_and_extensions(image, extension_images, flags, changes, n_changes);
log_portable_verb(
"attached",
"MESSAGE_ID=" SD_MESSAGE_PORTABLE_ATTACHED_STR,
image->path,
+ profile,
extension_images,
/* extension_image_paths= */ NULL,
flags);
@@ -1650,6 +1733,7 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
while (!isempty(marker))
STRV_FOREACH(image_name_or_path, root_and_extensions) {
_cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
@@ -1661,9 +1745,23 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
if (r < 0)
return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
- r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ *image_name_or_path,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ *image_name_or_path);
+
+ r = path_extract_image_name(result.path, &base_image_name_or_path);
if (r < 0)
- return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", result.path);
if (!streq(base_image, base_image_name_or_path)) {
if (match_all)
@@ -1746,7 +1844,7 @@ int portable_detach(
size_t *n_changes,
sd_bus_error *error) {
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
_cleanup_set_free_ Set *unit_files = NULL, *markers = NULL;
_cleanup_free_ char *extensions = NULL;
_cleanup_closedir_ DIR *d = NULL;
@@ -1875,34 +1973,24 @@ int portable_detach(
portable_changes_add_with_prefix(changes, n_changes, PORTABLE_UNLINK, where, md, NULL);
}
- /* Now, also drop any image symlink, for images outside of the sarch path */
+ /* Now, also drop any image symlink or copy, for images outside of the sarch path */
SET_FOREACH(item, markers) {
- _cleanup_free_ char *sl = NULL;
- struct stat st;
+ _cleanup_free_ char *target = NULL;
- r = image_symlink(item, flags, &sl);
+ r = image_target_path(item, flags, &target);
if (r < 0) {
- log_debug_errno(r, "Failed to determine image symlink for '%s', ignoring: %m", item);
+ log_debug_errno(r, "Failed to determine image path for '%s', ignoring: %m", item);
continue;
}
- if (lstat(sl, &st) < 0) {
- log_debug_errno(errno, "Failed to stat '%s', ignoring: %m", sl);
- continue;
- }
-
- if (!S_ISLNK(st.st_mode)) {
- log_debug("Image '%s' is not a symlink, ignoring.", sl);
- continue;
- }
-
- if (unlink(sl) < 0) {
- log_debug_errno(errno, "Can't remove image symlink '%s': %m", sl);
+ r = rm_rf(target, REMOVE_ROOT | REMOVE_PHYSICAL | REMOVE_MISSING_OK | REMOVE_SYNCFS);
+ if (r < 0) {
+ log_debug_errno(r, "Can't remove image '%s': %m", target);
- if (errno != ENOENT && ret >= 0)
- ret = -errno;
+ if (r != -ENOENT)
+ RET_GATHER(ret, r);
} else
- portable_changes_add(changes, n_changes, PORTABLE_UNLINK, sl, NULL);
+ portable_changes_add(changes, n_changes, PORTABLE_UNLINK, target, NULL);
}
/* Try to remove the unit file directory, if we can */
@@ -1913,6 +2001,7 @@ int portable_detach(
"detached",
"MESSAGE_ID=" SD_MESSAGE_PORTABLE_DETACHED_STR,
name_or_path,
+ /* profile= */ NULL,
/* extension_images= */ NULL,
extension_image_paths,
flags);
@@ -1941,7 +2030,7 @@ static int portable_get_state_internal(
PortableState *ret,
sd_bus_error *error) {
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
bool found_enabled = false, found_running = false;
_cleanup_set_free_ Set *unit_files = NULL;
_cleanup_closedir_ DIR *d = NULL;