#include "def.h"
#include "device-nodes.h"
#include "device-util.h"
+#include "discover-image.h"
#include "dissect-image.h"
#include "dm-util.h"
#include "env-file.h"
+#include "extension-release.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "hostname-setup.h"
#include "id128-util.h"
+#include "import-util.h"
#include "mkdir.h"
#include "mount-util.h"
#include "mountpoint-util.h"
return false;
r = sd_device_get_sysattr_value(d, "partition", &v);
- if (r == -ENOENT) /* Not a partition device */
+ if (r == -ENOENT || /* Not a partition device */
+ ERRNO_IS_PRIVILEGE(r)) /* Not ready to access? */
return false;
if (r < 0)
return r;
assert(w);
- if (device_for_action(device, DEVICE_ACTION_REMOVE))
+ if (device_for_action(device, SD_DEVICE_REMOVE))
return 0;
r = sd_device_get_parent(device, &pp);
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
_cleanup_free_ char *generic_node = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
- const char *pttype = NULL;
+ const char *pttype = NULL, *sysname = NULL;
blkid_partlist pl;
int r, generic_nr, n_partitions;
struct stat st;
if (!S_ISBLK(st.st_mode))
return -ENOTBLK;
- r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0)
return r;
if (!m)
return -ENOMEM;
+ r = sd_device_get_sysname(d, &sysname);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get device sysname: %m");
+ if (startswith(sysname, "loop")) {
+ _cleanup_free_ char *name_stripped = NULL;
+ const char *full_path;
+
+ r = sd_device_get_sysattr_value(d, "loop/backing_file", &full_path);
+ if (r < 0)
+ log_debug_errno(r, "Failed to lookup image name via loop device backing file sysattr, ignoring: %m");
+ else {
+ r = raw_strip_suffixes(basename(full_path), &name_stripped);
+ if (r < 0)
+ return r;
+ }
+
+ free_and_replace(m->image_name, name_stripped);
+ } else {
+ r = free_and_strdup(&m->image_name, sysname);
+ if (r < 0)
+ return r;
+ }
+
+ if (!image_name_is_valid(m->image_name)) {
+ log_debug("Image name %s is not valid, ignoring", strempty(m->image_name));
+ m->image_name = mfree(m->image_name);
+ }
+
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) ||
(flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
free(m->partitions[i].mount_options);
}
+ free(m->image_name);
free(m->hostname);
strv_free(m->machine_info);
strv_free(m->os_release);
+ strv_free(m->extension_release);
return mfree(m);
}
/* Returns:
*
* -ENXIO → No root partition found
- * -EMEDIUMTYPE → DISSECT_IMAGE_VALIDATE_OS set but no os-release file found
+ * -EMEDIUMTYPE → DISSECT_IMAGE_VALIDATE_OS set but no os-release/extension-release file found
* -EUNATCH → Encrypted partition found for which no dm-crypt was set up yet
* -EUCLEAN → fsck for file system failed
* -EBUSY → File system already mounted/used elsewhere (kernel)
r = path_is_os_tree(where);
if (r < 0)
return r;
- if (r == 0)
- return -EMEDIUMTYPE;
+ if (r == 0) {
+ r = path_is_extension_tree(where, m->image_name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+ }
}
}
if (r == -ENXIO)
return log_error_errno(r, "Not root file system found in image.");
if (r == -EMEDIUMTYPE)
- return log_error_errno(r, "No suitable os-release file in image found.");
+ return log_error_errno(r, "No suitable os-release/extension-release file in image found.");
if (r == -EUNATCH)
return log_error_errno(r, "Encrypted file system discovered, but decryption not requested.");
if (r == -EUCLEAN)
DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
#if HAVE_LIBCRYPTSETUP
- size_t i;
int r;
if (!d)
return NULL;
- for (i = 0; i < d->n_decrypted; i++) {
+ for (size_t i = 0; i < d->n_decrypted; i++) {
DecryptedPartition *p = d->decrypted + i;
if (p->device && p->name && !p->relinquished) {
}
int decrypted_image_relinquish(DecryptedImage *d) {
-
-#if HAVE_LIBCRYPTSETUP
- size_t i;
- int r;
-#endif
-
assert(d);
- /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
- * that we don't clean it up ourselves either anymore */
+ /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a
+ * boolean so that we don't clean it up ourselves either anymore */
#if HAVE_LIBCRYPTSETUP
- for (i = 0; i < d->n_decrypted; i++) {
+ int r;
+
+ for (size_t i = 0; i < d->n_decrypted; i++) {
DecryptedPartition *p = d->decrypted + i;
if (p->relinquished)
META_MACHINE_ID,
META_MACHINE_INFO,
META_OS_RELEASE,
+ META_EXTENSION_RELEASE,
_META_MAX,
};
- static const char *const paths[_META_MAX] = {
- [META_HOSTNAME] = "/etc/hostname\0",
- [META_MACHINE_ID] = "/etc/machine-id\0",
- [META_MACHINE_INFO] = "/etc/machine-info\0",
- [META_OS_RELEASE] = ("/etc/os-release\0"
- "/usr/lib/os-release\0"),
+ static const char *paths[_META_MAX] = {
+ [META_HOSTNAME] = "/etc/hostname\0",
+ [META_MACHINE_ID] = "/etc/machine-id\0",
+ [META_MACHINE_INFO] = "/etc/machine-info\0",
+ [META_OS_RELEASE] = ("/etc/os-release\0"
+ "/usr/lib/os-release\0"),
+ [META_EXTENSION_RELEASE] = NULL,
};
- _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
+ _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
_cleanup_close_pair_ int error_pipe[2] = { -1, -1 };
_cleanup_(rmdir_and_freep) char *t = NULL;
_cleanup_(sigkill_waitp) pid_t child = 0;
sd_id128_t machine_id = SD_ID128_NULL;
_cleanup_free_ char *hostname = NULL;
- unsigned n_meta_initialized = 0, k;
+ unsigned n_meta_initialized = 0;
int fds[2 * _META_MAX], r, v;
ssize_t n;
assert(m);
- for (; n_meta_initialized < _META_MAX; n_meta_initialized ++)
+ /* As per the os-release spec, if the image is an extension it will have a file
+ * named after the image name in extension-release.d/ */
+ if (m->image_name)
+ paths[META_EXTENSION_RELEASE] = strjoina("/usr/lib/extension-release.d/extension-release.", m->image_name);
+ else
+ log_debug("No image name available, will skip extension-release metadata");
+
+ for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
+ if (!paths[n_meta_initialized]) {
+ fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -1;
+ continue;
+ }
+
if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
r = -errno;
goto finish;
}
+ }
r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
if (r < 0)
_exit(EXIT_FAILURE);
}
- for (k = 0; k < _META_MAX; k++) {
+ for (unsigned k = 0; k < _META_MAX; k++) {
_cleanup_close_ int fd = -ENOENT;
const char *p;
+ if (!paths[k])
+ continue;
+
fds[2*k] = safe_close(fds[2*k]);
NULSTR_FOREACH(p, paths[k]) {
continue;
}
- r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
+ r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
if (r < 0) {
(void) write(error_pipe[1], &r, sizeof(r));
_exit(EXIT_FAILURE);
error_pipe[1] = safe_close(error_pipe[1]);
- for (k = 0; k < _META_MAX; k++) {
+ for (unsigned k = 0; k < _META_MAX; k++) {
_cleanup_fclose_ FILE *f = NULL;
+ if (!paths[k])
+ continue;
+
fds[2*k+1] = safe_close(fds[2*k+1]);
f = take_fdopen(&fds[2*k], "r");
log_debug_errno(r, "Failed to read OS release file: %m");
break;
+
+ case META_EXTENSION_RELEASE:
+ r = load_env_file_pairs(f, "extension-release", &extension_release);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read extension release file: %m");
+
+ break;
}
}
m->machine_id = machine_id;
strv_free_and_replace(m->machine_info, machine_info);
strv_free_and_replace(m->os_release, os_release);
+ strv_free_and_replace(m->extension_release, extension_release);
finish:
- for (k = 0; k < n_meta_initialized; k++)
+ for (unsigned k = 0; k < n_meta_initialized; k++)
safe_close_pair(fds + 2*k);
return r;
[PARTITION_VAR] = "var",
};
-int verity_dissect_and_mount(const char *src, const char *dest, const MountOptions *options) {
+int verity_dissect_and_mount(
+ const char *src,
+ const char *dest,
+ const MountOptions *options,
+ const char *required_host_os_release_id,
+ const char *required_host_os_release_version_id,
+ const char *required_host_os_release_sysext_level) {
+
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
if (r < 0)
return log_debug_errno(r, "Failed to mount image: %m");
+ /* If we got os-release values from the caller, then we need to match them with the image's
+ * extension-release.d/ content. Return -EINVAL if there's any mismatch.
+ * First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
+ * available, or else fallback to VERSION_ID. */
+ if (required_host_os_release_id &&
+ (required_host_os_release_version_id || required_host_os_release_sysext_level)) {
+ _cleanup_strv_free_ char **extension_release = NULL;
+
+ r = load_extension_release_pairs(dest, dissected_image->image_name, &extension_release);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
+
+ r = extension_release_validate(
+ dissected_image->image_name,
+ required_host_os_release_id,
+ required_host_os_release_version_id,
+ required_host_os_release_sysext_level,
+ extension_release);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", dissected_image->image_name);
+ }
+
if (decrypted_image) {
r = decrypted_image_relinquish(decrypted_image);
if (r < 0)