mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-10 01:17:44 +03:00
Merge pull request #4795 from poettering/dissect
Generalize image dissection logic of nspawn, and make it useful for other tools.
This commit is contained in:
commit
4a5567d5d6
2
.gitignore
vendored
2
.gitignore
vendored
@ -67,6 +67,7 @@
|
||||
/systemd-debug-generator
|
||||
/systemd-delta
|
||||
/systemd-detect-virt
|
||||
/systemd-dissect
|
||||
/systemd-escape
|
||||
/systemd-export
|
||||
/systemd-firstboot
|
||||
@ -180,6 +181,7 @@
|
||||
/test-dhcp-option
|
||||
/test-dhcp-server
|
||||
/test-dhcp6-client
|
||||
/test-dissect-image
|
||||
/test-dns-domain
|
||||
/test-dns-packet
|
||||
/test-dnssec
|
||||
|
46
Makefile.am
46
Makefile.am
@ -404,6 +404,11 @@ rootlibexec_PROGRAMS = \
|
||||
systemd-socket-proxyd \
|
||||
systemd-update-done
|
||||
|
||||
if HAVE_BLKID
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-dissect
|
||||
endif
|
||||
|
||||
if HAVE_UTMP
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-update-utmp
|
||||
@ -1044,6 +1049,8 @@ libshared_la_SOURCES = \
|
||||
src/shared/machine-image.h \
|
||||
src/shared/machine-pool.c \
|
||||
src/shared/machine-pool.h \
|
||||
src/shared/loop-util.c \
|
||||
src/shared/loop-util.h \
|
||||
src/shared/resolve-util.c \
|
||||
src/shared/resolve-util.h \
|
||||
src/shared/bus-unit-util.c \
|
||||
@ -1055,7 +1062,9 @@ libshared_la_SOURCES = \
|
||||
src/shared/fdset.c \
|
||||
src/shared/fdset.h \
|
||||
src/shared/nsflags.h \
|
||||
src/shared/nsflags.c
|
||||
src/shared/nsflags.c \
|
||||
src/shared/dissect-image.c \
|
||||
src/shared/dissect-image.h
|
||||
|
||||
if HAVE_UTMP
|
||||
libshared_la_SOURCES += \
|
||||
@ -1078,7 +1087,9 @@ libshared_la_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(ACL_CFLAGS) \
|
||||
$(LIBIDN_CFLAGS) \
|
||||
$(SECCOMP_CFLAGS)
|
||||
$(SECCOMP_CFLAGS) \
|
||||
$(BLKID_CFLAGS) \
|
||||
$(LIBCRYPTSETUP_CFLAGS)
|
||||
|
||||
libshared_la_LIBADD = \
|
||||
libsystemd-internal.la \
|
||||
@ -1087,7 +1098,9 @@ libshared_la_LIBADD = \
|
||||
libudev-internal.la \
|
||||
$(ACL_LIBS) \
|
||||
$(LIBIDN_LIBS) \
|
||||
$(SECCOMP_LIBS)
|
||||
$(SECCOMP_LIBS) \
|
||||
$(BLKID_LIBS) \
|
||||
$(LIBCRYPTSETUP_LIBS)
|
||||
|
||||
rootlibexec_LTLIBRARIES += \
|
||||
libsystemd-shared.la
|
||||
@ -1109,6 +1122,8 @@ libsystemd_shared_la_CFLAGS = \
|
||||
$(ACL_CFLAGS) \
|
||||
$(LIBIDN_CFLAGS) \
|
||||
$(SECCOMP_CFLAGS) \
|
||||
$(BLKID_CFLAGS) \
|
||||
$(LIBCRYPTSETUP_CFLAGS) \
|
||||
-fvisibility=default
|
||||
|
||||
# We can't use libshared_la_LIBADD here because it would
|
||||
@ -1120,7 +1135,9 @@ libsystemd_shared_la_LIBADD = \
|
||||
$(libudev_internal_la_LIBADD) \
|
||||
$(ACL_LIBS) \
|
||||
$(LIBIDN_LIBS) \
|
||||
$(SECCOMP_LIBS)
|
||||
$(SECCOMP_LIBS) \
|
||||
$(BLKID_LIBS) \
|
||||
$(LIBCRYPTSETUP_LIBS)
|
||||
|
||||
libsystemd_shared_la_LDFLAGS = \
|
||||
$(AM_LDFLAGS) \
|
||||
@ -1458,7 +1475,8 @@ manual_tests += \
|
||||
test-btrfs \
|
||||
test-acd \
|
||||
test-ipv4ll-manual \
|
||||
test-ask-password-api
|
||||
test-ask-password-api \
|
||||
test-dissect-image
|
||||
|
||||
unsafe_tests = \
|
||||
test-hostname \
|
||||
@ -2069,6 +2087,17 @@ test_ask_password_api_SOURCES = \
|
||||
test_ask_password_api_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_dissect_image_SOURCES = \
|
||||
src/test/test-dissect-image.c
|
||||
|
||||
test_dissect_image_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(BLKID_CFLAGS)
|
||||
|
||||
test_dissect_image_LDADD = \
|
||||
libsystemd-shared.la \
|
||||
$(BLKID_LIBS)
|
||||
|
||||
test_signal_util_SOURCES = \
|
||||
src/test/test-signal-util.c
|
||||
|
||||
@ -3055,6 +3084,13 @@ systemd_notify_SOURCES = \
|
||||
systemd_notify_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
systemd_dissect_SOURCES = \
|
||||
src/dissect/dissect.c
|
||||
|
||||
systemd_dissect_LDADD = \
|
||||
libsystemd-shared.la
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
systemd_path_SOURCES = \
|
||||
src/path/path.c
|
||||
|
@ -235,16 +235,33 @@
|
||||
identified by the partition types defined by the <ulink
|
||||
url="http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable
|
||||
Partitions Specification</ulink>.</para></listitem>
|
||||
|
||||
<listitem><para>No partition table, and a single file system spanning the whole image.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to
|
||||
<filename>/efi</filename> (or <filename>/boot</filename> as fallback) in case a directory by this name exists
|
||||
and is empty.</para>
|
||||
|
||||
<para>Partitions encrypted with LUKS are automatically decrypted. Also, on GPT images dm-verity data integrity
|
||||
hash partitions are set up if the root hash for them is specified using the <option>--root-hash=</option>
|
||||
option.</para>
|
||||
|
||||
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
|
||||
together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--root-hash=</option></term>
|
||||
|
||||
<listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data
|
||||
integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The
|
||||
specified hash must match the root hash of integrity data, and is usually at least 256bits (and hence 64
|
||||
hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but a file with
|
||||
the <filename>.roothash</filename> suffix is found next to the image file, bearing otherwise the same name the
|
||||
root hash is read from it and automatically used.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-a</option></term>
|
||||
<term><option>--as-pid2</option></term>
|
||||
|
@ -1409,3 +1409,22 @@ int read_nul_string(FILE *f, char **ret) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret) {
|
||||
char *p;
|
||||
|
||||
assert(template);
|
||||
assert(ret);
|
||||
|
||||
p = strdup(template);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!mkdtemp(p)) {
|
||||
free(p);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*ret = p;
|
||||
return 0;
|
||||
}
|
||||
|
@ -88,3 +88,5 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
|
||||
int link_tmpfile(int fd, const char *path, const char *target);
|
||||
|
||||
int read_nul_string(FILE *f, char **ret);
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret);
|
||||
|
@ -84,3 +84,10 @@ enum {
|
||||
};
|
||||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
|
||||
|
||||
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
|
||||
static inline void rmdir_and_free(char *p) {
|
||||
(void) rmdir(p);
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
|
||||
|
@ -33,8 +33,6 @@ int rm_rf(const char *path, RemoveFlags flags);
|
||||
|
||||
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
|
||||
static inline void rm_rf_physical_and_free(char *p) {
|
||||
if (!p)
|
||||
return;
|
||||
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||
free(p);
|
||||
}
|
||||
|
@ -344,24 +344,29 @@ static int delete_loopback(const char *device) {
|
||||
}
|
||||
|
||||
static int delete_dm(dev_t devnum) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
struct dm_ioctl dm = {
|
||||
.version = {DM_VERSION_MAJOR,
|
||||
DM_VERSION_MINOR,
|
||||
DM_VERSION_PATCHLEVEL},
|
||||
.version = {
|
||||
DM_VERSION_MAJOR,
|
||||
DM_VERSION_MINOR,
|
||||
DM_VERSION_PATCHLEVEL
|
||||
},
|
||||
.data_size = sizeof(dm),
|
||||
.dev = devnum,
|
||||
};
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(major(devnum) != 0);
|
||||
|
||||
fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = ioctl(fd, DM_DEV_REMOVE, &dm);
|
||||
return r >= 0 ? 0 : -errno;
|
||||
if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
|
||||
|
@ -651,7 +651,7 @@ int main(int argc, char *argv[]) {
|
||||
k = crypt_init(&cd, arg_header);
|
||||
} else
|
||||
k = crypt_init(&cd, argv[3]);
|
||||
if (k) {
|
||||
if (k != 0) {
|
||||
log_error_errno(k, "crypt_init() failed: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
1
src/dissect/Makefile
Symbolic link
1
src/dissect/Makefile
Symbolic link
@ -0,0 +1 @@
|
||||
../Makefile
|
271
src/dissect/dissect.c
Normal file
271
src/dissect/dissect.c
Normal file
@ -0,0 +1,271 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "architecture.h"
|
||||
#include "dissect-image.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static enum {
|
||||
ACTION_DISSECT,
|
||||
ACTION_MOUNT,
|
||||
} arg_action = ACTION_DISSECT;
|
||||
static const char *arg_image = NULL;
|
||||
static const char *arg_path = NULL;
|
||||
static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
|
||||
static void *arg_root_hash = NULL;
|
||||
static size_t arg_root_hash_size = 0;
|
||||
|
||||
static void help(void) {
|
||||
printf("%s [OPTIONS...] IMAGE\n"
|
||||
"%s [OPTIONS...] --mount IMAGE PATH\n"
|
||||
"Dissect a file system OS image.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" -m --mount Mount the image to the specified directory\n"
|
||||
" -r --read-only Mount read-only\n"
|
||||
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
|
||||
" --root-hash=HASH Specify root hash for verity\n",
|
||||
program_invocation_short_name,
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_DISCARD,
|
||||
ARG_ROOT_HASH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "mount", no_argument, NULL, 'm' },
|
||||
{ "read-only", no_argument, NULL, 'r' },
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case 'm':
|
||||
arg_action = ACTION_MOUNT;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case ARG_DISCARD:
|
||||
if (streq(optarg, "disabled"))
|
||||
arg_flags &= ~(DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO);
|
||||
else if (streq(optarg, "loop"))
|
||||
arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP;
|
||||
else if (streq(optarg, "all"))
|
||||
arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
|
||||
else if (streq(optarg, "crypt"))
|
||||
arg_flags |= DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD | DISSECT_IMAGE_DISCARD_ON_CRYPTO;
|
||||
else {
|
||||
log_error("Unknown --discard= parameter: %s", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ARG_ROOT_HASH: {
|
||||
void *p;
|
||||
size_t l;
|
||||
|
||||
r = unhexmem(optarg, strlen(optarg), &p, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse root hash: %s", optarg);
|
||||
if (l < sizeof(sd_id128_t)) {
|
||||
log_error("Root hash must be at least 128bit long: %s", optarg);
|
||||
free(p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(arg_root_hash);
|
||||
arg_root_hash = p;
|
||||
arg_root_hash_size = l;
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unhandled option");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT:
|
||||
if (optind + 1 != argc) {
|
||||
log_error("Expected a file path as only argument.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case ACTION_MOUNT:
|
||||
if (optind + 2 != argc) {
|
||||
log_error("Expected a file path and mount point path as only arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_path = argv[optind + 1];
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown action.");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
goto finish;
|
||||
|
||||
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
|
||||
if (r == -ENOPKG) {
|
||||
log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
|
||||
goto finish;
|
||||
}
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
|
||||
goto finish;
|
||||
}
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to dissect image: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT: {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
int k;
|
||||
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
printf("Found %s '%s' partition",
|
||||
p->rw ? "writable" : "read-only",
|
||||
partition_designator_to_string(i));
|
||||
|
||||
if (p->fstype)
|
||||
printf(" of type %s", p->fstype);
|
||||
|
||||
if (p->architecture != _ARCHITECTURE_INVALID)
|
||||
printf(" for %s", architecture_to_string(p->architecture));
|
||||
|
||||
k = PARTITION_VERITY_OF(i);
|
||||
if (k >= 0)
|
||||
printf(" %s verity", m->partitions[k].found ? "with" : "without");
|
||||
|
||||
if (p->partno >= 0)
|
||||
printf(" on partition #%i", p->partno);
|
||||
|
||||
if (p->node)
|
||||
printf(" (%s)", p->node);
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_MOUNT:
|
||||
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = dissected_image_mount(m, arg_path, arg_flags);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to mount image: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (di) {
|
||||
r = decrypted_image_relinquish(di);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to relinquish DM devices: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
loop_device_relinquish(d);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown action.");
|
||||
}
|
||||
|
||||
finish:
|
||||
free(arg_root_hash);
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
@ -97,8 +97,10 @@ _public_ struct udev *udev_new(void) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
udev = new0(struct udev, 1);
|
||||
if (udev == NULL)
|
||||
if (!udev) {
|
||||
errno = -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
udev->refcount = 1;
|
||||
|
||||
f = fopen("/etc/udev/udev.conf", "re");
|
||||
|
@ -17,14 +17,23 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-label.h"
|
||||
#include "bus-util.h"
|
||||
#include "copy.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "image-dbus.h"
|
||||
#include "io-util.h"
|
||||
#include "loop-util.h"
|
||||
#include "machine-image.h"
|
||||
#include "mount-util.h"
|
||||
#include "process-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
@ -279,6 +288,161 @@ int bus_image_method_set_limit(
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
#define EXIT_NOT_FOUND 2
|
||||
|
||||
static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
|
||||
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(image);
|
||||
assert(ret);
|
||||
|
||||
r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r == -ENOENT)
|
||||
r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path);
|
||||
|
||||
r = load_env_file_pairs(NULL, path, NULL, ret);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
|
||||
_cleanup_(rmdir_and_freep) char *t = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_(sigkill_waitp) pid_t child = 0;
|
||||
_cleanup_close_pair_ int pair[2] = { -1, -1 };
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **v = NULL;
|
||||
siginfo_t si;
|
||||
int r;
|
||||
|
||||
assert(image);
|
||||
assert(ret);
|
||||
|
||||
r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m");
|
||||
|
||||
r = loop_device_make_by_path(image->path, O_RDONLY, &d);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, &m);
|
||||
if (r == -ENOPKG)
|
||||
return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path);
|
||||
|
||||
if (pipe2(pair, O_CLOEXEC) < 0)
|
||||
return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m");
|
||||
|
||||
child = raw_clone(SIGCHLD|CLONE_NEWNS);
|
||||
if (child < 0)
|
||||
return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
|
||||
|
||||
if (child == 0) {
|
||||
int fd;
|
||||
|
||||
pair[0] = safe_close(pair[0]);
|
||||
|
||||
/* Make sure we never propagate to the host */
|
||||
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = mount_move_root(t);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0 && errno == ENOENT) {
|
||||
fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0 && errno == ENOENT)
|
||||
_exit(EXIT_NOT_FOUND);
|
||||
}
|
||||
if (fd < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
pair[1] = safe_close(pair[1]);
|
||||
|
||||
f = fdopen(pair[0], "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
pair[0] = -1;
|
||||
|
||||
r = load_env_file_pairs(f, "os-release", NULL, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = wait_for_terminate(child, &si);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
|
||||
child = 0;
|
||||
if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
|
||||
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
|
||||
|
||||
*ret = v;
|
||||
v = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_image_method_get_os_release(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
|
||||
_cleanup_strv_free_ char **v = NULL;
|
||||
Image *image = userdata;
|
||||
int r;
|
||||
|
||||
r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m");
|
||||
|
||||
switch (image->type) {
|
||||
|
||||
case IMAGE_DIRECTORY:
|
||||
case IMAGE_SUBVOLUME:
|
||||
r = directory_image_get_os_release(image, &v, error);
|
||||
break;
|
||||
|
||||
case IMAGE_RAW:
|
||||
r = raw_image_get_os_release(image, &v, error);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown image type");
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return bus_reply_pair_array(message, v);
|
||||
}
|
||||
|
||||
const sd_bus_vtable image_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
|
||||
@ -296,6 +460,7 @@ const sd_bus_vtable image_vtable[] = {
|
||||
SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
|
@ -33,3 +33,4 @@ int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_erro
|
||||
int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
|
@ -356,11 +356,11 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
}
|
||||
|
||||
#define EXIT_NOT_FOUND 2
|
||||
|
||||
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Machine *m = userdata;
|
||||
char **k, **v;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
@ -394,7 +394,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
|
||||
return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
|
||||
|
||||
if (child == 0) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int fd = -1;
|
||||
|
||||
pair[0] = safe_close(pair[0]);
|
||||
|
||||
@ -402,12 +402,14 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0 && errno == ENOENT) {
|
||||
fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0 && errno == ENOENT)
|
||||
_exit(EXIT_NOT_FOUND);
|
||||
}
|
||||
if (fd < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
|
||||
if (r < 0)
|
||||
@ -431,6 +433,8 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
|
||||
r = wait_for_terminate(child, &si);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
|
||||
if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
|
||||
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
|
||||
|
||||
@ -441,25 +445,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "{ss}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(k, v, l) {
|
||||
r = sd_bus_message_append(reply, "{ss}", *k, *v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
return bus_reply_pair_array(message, l);
|
||||
}
|
||||
|
||||
int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
|
@ -42,3 +42,5 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
|
||||
|
||||
int machine_send_signal(Machine *m, bool new_machine);
|
||||
int machine_send_create_reply(Machine *m, sd_bus_error *error);
|
||||
|
||||
int bus_reply_pair_array(sd_bus_message *m, char **l);
|
||||
|
@ -138,7 +138,7 @@ static void clean_machine_info(MachineInfo *machines, size_t n_machines) {
|
||||
free(machines);
|
||||
}
|
||||
|
||||
static int get_os_release_property(sd_bus *bus, const char *name, const char *query, ...) {
|
||||
static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
const char *k, *v, *iter, **query_res = NULL;
|
||||
size_t count = 0, awaited_args = 0;
|
||||
@ -153,12 +153,13 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
|
||||
awaited_args++;
|
||||
query_res = newa0(const char *, awaited_args);
|
||||
|
||||
r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.machine1",
|
||||
"/org/freedesktop/machine1",
|
||||
"org.freedesktop.machine1.Manager",
|
||||
"GetMachineOSRelease",
|
||||
NULL, &reply, "s", name);
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.machine1",
|
||||
"/org/freedesktop/machine1",
|
||||
"org.freedesktop.machine1.Manager",
|
||||
method,
|
||||
NULL, &reply, "s", name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -193,7 +194,7 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
|
||||
val = strdup(query_res[count]);
|
||||
if (!val) {
|
||||
va_end(ap);
|
||||
return log_oom();
|
||||
return -ENOMEM;
|
||||
}
|
||||
*out = val;
|
||||
}
|
||||
@ -249,8 +250,12 @@ static int list_machines(int argc, char *argv[], void *userdata) {
|
||||
|
||||
machines[n_machines].os = NULL;
|
||||
machines[n_machines].version_id = NULL;
|
||||
r = get_os_release_property(bus, name,
|
||||
"ID\0" "VERSION_ID\0",
|
||||
r = call_get_os_release(
|
||||
bus,
|
||||
"GetMachineOSRelease",
|
||||
name,
|
||||
"ID\0"
|
||||
"VERSION_ID\0",
|
||||
&machines[n_machines].os,
|
||||
&machines[n_machines].version_id);
|
||||
if (r < 0)
|
||||
@ -610,7 +615,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
|
||||
static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
|
||||
_cleanup_free_ char *pretty = NULL;
|
||||
int r;
|
||||
|
||||
@ -618,7 +623,7 @@ static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
|
||||
assert(name);
|
||||
assert(prefix);
|
||||
|
||||
r = get_os_release_property(bus, name, "PRETTY_NAME\0", &pretty, NULL);
|
||||
r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -729,7 +734,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
|
||||
"\n\t ",
|
||||
ALL_IP_ADDRESSES);
|
||||
|
||||
print_os_release(bus, i->name, "\t OS: ");
|
||||
print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
|
||||
|
||||
if (i->unit) {
|
||||
printf("\t Unit: %s\n", i->unit);
|
||||
@ -927,6 +932,8 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
|
||||
if (i->path)
|
||||
printf("\t Path: %s\n", i->path);
|
||||
|
||||
print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
|
||||
|
||||
printf("\t RO: %s%s%s\n",
|
||||
i->read_only ? ansi_highlight_red() : "",
|
||||
i->read_only ? "read-only" : "writable",
|
||||
|
@ -825,6 +825,30 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
|
||||
return bus_image_method_mark_read_only(message, i, error);
|
||||
}
|
||||
|
||||
static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!image_name_is_valid(name))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
|
||||
|
||||
r = image_find(name, &i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
|
||||
|
||||
i->userdata = userdata;
|
||||
return bus_image_method_get_os_release(message, i, error);
|
||||
}
|
||||
|
||||
static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
@ -1396,6 +1420,7 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
@ -1804,3 +1829,30 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_reply_pair_array(sd_bus_message *m, char **l) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
char **k, **v;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_new_method_return(m, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "{ss}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(k, v, l) {
|
||||
r = sd_bus_message_append(reply, "{ss}", *k, *v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
|
||||
}
|
||||
|
@ -116,6 +116,10 @@
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="SetImageLimit"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="GetImageOSRelease"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="CleanPool"/>
|
||||
@ -192,6 +196,10 @@
|
||||
send_interface="org.freedesktop.machine1.Image"
|
||||
send_member="MarkReadOnly"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Image"
|
||||
send_member="GetOSRelease"/>
|
||||
|
||||
<allow receive_sender="org.freedesktop.machine1"/>
|
||||
</policy>
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "copy.h"
|
||||
#include "dev-setup.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdset.h"
|
||||
@ -60,9 +61,11 @@
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "loopback-setup.h"
|
||||
#include "machine-image.h"
|
||||
#include "macro.h"
|
||||
@ -198,6 +201,8 @@ static bool arg_notify_ready = false;
|
||||
static bool arg_use_cgns = true;
|
||||
static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
|
||||
static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO;
|
||||
static void *arg_root_hash = NULL;
|
||||
static size_t arg_root_hash_size = 0;
|
||||
|
||||
static void help(void) {
|
||||
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
|
||||
@ -211,6 +216,7 @@ static void help(void) {
|
||||
" -x --ephemeral Run container with snapshot of root directory, and\n"
|
||||
" remove it after exit\n"
|
||||
" -i --image=PATH File system device or disk image for the container\n"
|
||||
" --root-hash=HASH Specify verity root hash\n"
|
||||
" -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n"
|
||||
" -b --boot Boot up full system (i.e. invoke init)\n"
|
||||
" --chdir=PATH Set working directory in the container\n"
|
||||
@ -422,6 +428,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CHDIR,
|
||||
ARG_PRIVATE_USERS_CHOWN,
|
||||
ARG_NOTIFY_READY,
|
||||
ARG_ROOT_HASH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -471,6 +478,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "settings", required_argument, NULL, ARG_SETTINGS },
|
||||
{ "chdir", required_argument, NULL, ARG_CHDIR },
|
||||
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
|
||||
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1014,6 +1022,25 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_settings_mask |= SETTING_NOTIFY_READY;
|
||||
break;
|
||||
|
||||
case ARG_ROOT_HASH: {
|
||||
void *k;
|
||||
size_t l;
|
||||
|
||||
r = unhexmem(optarg, strlen(optarg), &k, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse root hash: %s", optarg);
|
||||
if (l < sizeof(sd_id128_t)) {
|
||||
log_error("Root hash must be at least 128bit long: %s", optarg);
|
||||
free(k);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(arg_root_hash);
|
||||
arg_root_hash = k;
|
||||
arg_root_hash_size = l;
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -1771,546 +1798,6 @@ static int setup_propagate(const char *root) {
|
||||
return mount_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
|
||||
}
|
||||
|
||||
static int setup_image(char **device_path, int *loop_nr) {
|
||||
struct loop_info64 info = {
|
||||
.lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN
|
||||
};
|
||||
_cleanup_close_ int fd = -1, control = -1, loop = -1;
|
||||
_cleanup_free_ char* loopdev = NULL;
|
||||
struct stat st;
|
||||
int r, nr;
|
||||
|
||||
assert(device_path);
|
||||
assert(loop_nr);
|
||||
assert(arg_image);
|
||||
|
||||
fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open %s: %m", arg_image);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", arg_image);
|
||||
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
char *p;
|
||||
|
||||
p = strdup(arg_image);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
*device_path = p;
|
||||
|
||||
*loop_nr = -1;
|
||||
|
||||
r = fd;
|
||||
fd = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
log_error("%s is not a regular file or block device.", arg_image);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||
if (control < 0)
|
||||
return log_error_errno(errno, "Failed to open /dev/loop-control: %m");
|
||||
|
||||
nr = ioctl(control, LOOP_CTL_GET_FREE);
|
||||
if (nr < 0)
|
||||
return log_error_errno(errno, "Failed to allocate loop device: %m");
|
||||
|
||||
if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
|
||||
return log_oom();
|
||||
|
||||
loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
|
||||
if (loop < 0)
|
||||
return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev);
|
||||
|
||||
if (ioctl(loop, LOOP_SET_FD, fd) < 0)
|
||||
return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev);
|
||||
|
||||
if (arg_read_only)
|
||||
info.lo_flags |= LO_FLAGS_READ_ONLY;
|
||||
|
||||
if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
|
||||
return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev);
|
||||
|
||||
*device_path = loopdev;
|
||||
loopdev = NULL;
|
||||
|
||||
*loop_nr = nr;
|
||||
|
||||
r = loop;
|
||||
loop = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define PARTITION_TABLE_BLURB \
|
||||
"Note that the disk image needs to either contain only a single MBR partition of\n" \
|
||||
"type 0x83 that is marked bootable, or a single GPT partition of type " \
|
||||
"0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \
|
||||
" http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
|
||||
"to be bootable with systemd-nspawn."
|
||||
|
||||
static int dissect_image(
|
||||
int fd,
|
||||
char **root_device, bool *root_device_rw,
|
||||
char **home_device, bool *home_device_rw,
|
||||
char **srv_device, bool *srv_device_rw,
|
||||
char **esp_device,
|
||||
bool *secondary) {
|
||||
|
||||
#ifdef HAVE_BLKID
|
||||
int home_nr = -1, srv_nr = -1, esp_nr = -1;
|
||||
#ifdef GPT_ROOT_NATIVE
|
||||
int root_nr = -1;
|
||||
#endif
|
||||
#ifdef GPT_ROOT_SECONDARY
|
||||
int secondary_root_nr = -1;
|
||||
#endif
|
||||
_cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *esp = NULL, *generic = NULL;
|
||||
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
|
||||
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
|
||||
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
|
||||
_cleanup_udev_unref_ struct udev *udev = NULL;
|
||||
struct udev_list_entry *first, *item;
|
||||
bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true;
|
||||
bool is_gpt, is_mbr, multiple_generic = false;
|
||||
const char *pttype = NULL;
|
||||
blkid_partlist pl;
|
||||
struct stat st;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(root_device);
|
||||
assert(home_device);
|
||||
assert(srv_device);
|
||||
assert(esp_device);
|
||||
assert(secondary);
|
||||
assert(arg_image);
|
||||
|
||||
b = blkid_new_probe();
|
||||
if (!b)
|
||||
return log_oom();
|
||||
|
||||
errno = 0;
|
||||
r = blkid_probe_set_device(b, fd, 0, 0);
|
||||
if (r != 0) {
|
||||
if (errno == 0)
|
||||
return log_oom();
|
||||
|
||||
return log_error_errno(errno, "Failed to set device on blkid probe: %m");
|
||||
}
|
||||
|
||||
blkid_probe_enable_partitions(b, 1);
|
||||
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
|
||||
|
||||
errno = 0;
|
||||
r = blkid_do_safeprobe(b);
|
||||
if (r == -2 || r == 1) {
|
||||
log_error("Failed to identify any partition table on\n"
|
||||
" %s\n"
|
||||
PARTITION_TABLE_BLURB, arg_image);
|
||||
return -EINVAL;
|
||||
} else if (r != 0) {
|
||||
if (errno == 0)
|
||||
errno = EIO;
|
||||
return log_error_errno(errno, "Failed to probe: %m");
|
||||
}
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
|
||||
|
||||
is_gpt = streq_ptr(pttype, "gpt");
|
||||
is_mbr = streq_ptr(pttype, "dos");
|
||||
|
||||
if (!is_gpt && !is_mbr) {
|
||||
log_error("No GPT or MBR partition table discovered on\n"
|
||||
" %s\n"
|
||||
PARTITION_TABLE_BLURB, arg_image);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
pl = blkid_probe_get_partitions(b);
|
||||
if (!pl) {
|
||||
if (errno == 0)
|
||||
return log_oom();
|
||||
|
||||
log_error("Failed to list partitions of %s", arg_image);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
udev = udev_new();
|
||||
if (!udev)
|
||||
return log_oom();
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat block device: %m");
|
||||
|
||||
d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
for (i = 0;; i++) {
|
||||
int n, m;
|
||||
|
||||
if (i >= 10) {
|
||||
log_error("Kernel partitions never appeared.");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
e = udev_enumerate_new(udev);
|
||||
if (!e)
|
||||
return log_oom();
|
||||
|
||||
r = udev_enumerate_add_match_parent(e, d);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = udev_enumerate_scan_devices(e);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image);
|
||||
|
||||
/* Count the partitions enumerated by the kernel */
|
||||
n = 0;
|
||||
first = udev_enumerate_get_list_entry(e);
|
||||
udev_list_entry_foreach(item, first)
|
||||
n++;
|
||||
|
||||
/* Count the partitions enumerated by blkid */
|
||||
m = blkid_partlist_numof_partitions(pl);
|
||||
if (n == m + 1)
|
||||
break;
|
||||
if (n > m + 1) {
|
||||
log_error("blkid and kernel partition list do not match.");
|
||||
return -EIO;
|
||||
}
|
||||
if (n < m + 1) {
|
||||
unsigned j;
|
||||
|
||||
/* The kernel has probed fewer partitions than
|
||||
* blkid? Maybe the kernel prober is still
|
||||
* running or it got EBUSY because udev
|
||||
* already opened the device. Let's reprobe
|
||||
* the device, which is a synchronous call
|
||||
* that waits until probing is complete. */
|
||||
|
||||
for (j = 0; j < 20; j++) {
|
||||
|
||||
r = ioctl(fd, BLKRRPART, 0);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
if (r >= 0 || r != -EBUSY)
|
||||
break;
|
||||
|
||||
/* If something else has the device
|
||||
* open, such as an udev rule, the
|
||||
* ioctl will return EBUSY. Since
|
||||
* there's no way to wait until it
|
||||
* isn't busy anymore, let's just wait
|
||||
* a bit, and try again.
|
||||
*
|
||||
* This is really something they
|
||||
* should fix in the kernel! */
|
||||
|
||||
usleep(50 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to reread partition table: %m");
|
||||
}
|
||||
|
||||
e = udev_enumerate_unref(e);
|
||||
}
|
||||
|
||||
first = udev_enumerate_get_list_entry(e);
|
||||
udev_list_entry_foreach(item, first) {
|
||||
_cleanup_udev_device_unref_ struct udev_device *q;
|
||||
const char *node;
|
||||
unsigned long long flags;
|
||||
blkid_partition pp;
|
||||
dev_t qn;
|
||||
int nr;
|
||||
|
||||
errno = 0;
|
||||
q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
|
||||
if (!q) {
|
||||
if (!errno)
|
||||
errno = ENOMEM;
|
||||
|
||||
return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image);
|
||||
}
|
||||
|
||||
qn = udev_device_get_devnum(q);
|
||||
if (major(qn) == 0)
|
||||
continue;
|
||||
|
||||
if (st.st_rdev == qn)
|
||||
continue;
|
||||
|
||||
node = udev_device_get_devnode(q);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
pp = blkid_partlist_devno_to_partition(pl, qn);
|
||||
if (!pp)
|
||||
continue;
|
||||
|
||||
flags = blkid_partition_get_flags(pp);
|
||||
|
||||
nr = blkid_partition_get_partno(pp);
|
||||
if (nr < 0)
|
||||
continue;
|
||||
|
||||
if (is_gpt) {
|
||||
sd_id128_t type_id;
|
||||
const char *stype;
|
||||
|
||||
if (flags & GPT_FLAG_NO_AUTO)
|
||||
continue;
|
||||
|
||||
stype = blkid_partition_get_type_string(pp);
|
||||
if (!stype)
|
||||
continue;
|
||||
|
||||
if (sd_id128_from_string(stype, &type_id) < 0)
|
||||
continue;
|
||||
|
||||
if (sd_id128_equal(type_id, GPT_HOME)) {
|
||||
|
||||
if (home && nr >= home_nr)
|
||||
continue;
|
||||
|
||||
home_nr = nr;
|
||||
home_rw = !(flags & GPT_FLAG_READ_ONLY);
|
||||
|
||||
r = free_and_strdup(&home, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else if (sd_id128_equal(type_id, GPT_SRV)) {
|
||||
|
||||
if (srv && nr >= srv_nr)
|
||||
continue;
|
||||
|
||||
srv_nr = nr;
|
||||
srv_rw = !(flags & GPT_FLAG_READ_ONLY);
|
||||
|
||||
r = free_and_strdup(&srv, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
} else if (sd_id128_equal(type_id, GPT_ESP)) {
|
||||
|
||||
if (esp && nr >= esp_nr)
|
||||
continue;
|
||||
|
||||
esp_nr = nr;
|
||||
|
||||
r = free_and_strdup(&esp, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
#ifdef GPT_ROOT_NATIVE
|
||||
else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
|
||||
|
||||
if (root && nr >= root_nr)
|
||||
continue;
|
||||
|
||||
root_nr = nr;
|
||||
root_rw = !(flags & GPT_FLAG_READ_ONLY);
|
||||
|
||||
r = free_and_strdup(&root, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
#endif
|
||||
#ifdef GPT_ROOT_SECONDARY
|
||||
else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
|
||||
|
||||
if (secondary_root && nr >= secondary_root_nr)
|
||||
continue;
|
||||
|
||||
secondary_root_nr = nr;
|
||||
secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
|
||||
|
||||
r = free_and_strdup(&secondary_root, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
#endif
|
||||
else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
|
||||
|
||||
if (generic)
|
||||
multiple_generic = true;
|
||||
else {
|
||||
generic_rw = !(flags & GPT_FLAG_READ_ONLY);
|
||||
|
||||
r = free_and_strdup(&generic, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (is_mbr) {
|
||||
int type;
|
||||
|
||||
if (flags != 0x80) /* Bootable flag */
|
||||
continue;
|
||||
|
||||
type = blkid_partition_get_type(pp);
|
||||
if (type != 0x83) /* Linux partition */
|
||||
continue;
|
||||
|
||||
if (generic)
|
||||
multiple_generic = true;
|
||||
else {
|
||||
generic_rw = true;
|
||||
|
||||
r = free_and_strdup(&root, node);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root) {
|
||||
*root_device = root;
|
||||
root = NULL;
|
||||
|
||||
*root_device_rw = root_rw;
|
||||
*secondary = false;
|
||||
} else if (secondary_root) {
|
||||
*root_device = secondary_root;
|
||||
secondary_root = NULL;
|
||||
|
||||
*root_device_rw = secondary_root_rw;
|
||||
*secondary = true;
|
||||
} else if (generic) {
|
||||
|
||||
/* There were no partitions with precise meanings
|
||||
* around, but we found generic partitions. In this
|
||||
* case, if there's only one, we can go ahead and boot
|
||||
* it, otherwise we bail out, because we really cannot
|
||||
* make any sense of it. */
|
||||
|
||||
if (multiple_generic) {
|
||||
log_error("Identified multiple bootable Linux partitions on\n"
|
||||
" %s\n"
|
||||
PARTITION_TABLE_BLURB, arg_image);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*root_device = generic;
|
||||
generic = NULL;
|
||||
|
||||
*root_device_rw = generic_rw;
|
||||
*secondary = false;
|
||||
} else {
|
||||
log_error("Failed to identify root partition in disk image\n"
|
||||
" %s\n"
|
||||
PARTITION_TABLE_BLURB, arg_image);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (home) {
|
||||
*home_device = home;
|
||||
home = NULL;
|
||||
|
||||
*home_device_rw = home_rw;
|
||||
}
|
||||
|
||||
if (srv) {
|
||||
*srv_device = srv;
|
||||
srv = NULL;
|
||||
|
||||
*srv_device_rw = srv_rw;
|
||||
}
|
||||
|
||||
if (esp) {
|
||||
*esp_device = esp;
|
||||
esp = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
log_error("--image= is not supported, compiled without blkid support.");
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int mount_device(const char *what, const char *where, const char *directory, bool rw) {
|
||||
#ifdef HAVE_BLKID
|
||||
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
|
||||
const char *fstype, *p, *options;
|
||||
int r;
|
||||
|
||||
assert(what);
|
||||
assert(where);
|
||||
|
||||
if (arg_read_only)
|
||||
rw = false;
|
||||
|
||||
if (directory)
|
||||
p = strjoina(where, directory);
|
||||
else
|
||||
p = where;
|
||||
|
||||
errno = 0;
|
||||
b = blkid_new_probe_from_filename(what);
|
||||
if (!b) {
|
||||
if (errno == 0)
|
||||
return log_oom();
|
||||
return log_error_errno(errno, "Failed to allocate prober for %s: %m", what);
|
||||
}
|
||||
|
||||
blkid_probe_enable_superblocks(b, 1);
|
||||
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
|
||||
|
||||
errno = 0;
|
||||
r = blkid_do_safeprobe(b);
|
||||
if (r == -1 || r == 1) {
|
||||
log_error("Cannot determine file system type of %s", what);
|
||||
return -EINVAL;
|
||||
} else if (r != 0) {
|
||||
if (errno == 0)
|
||||
errno = EIO;
|
||||
return log_error_errno(errno, "Failed to probe %s: %m", what);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
if (blkid_probe_lookup_value(b, "TYPE", &fstype, NULL) < 0) {
|
||||
if (errno == 0)
|
||||
errno = EINVAL;
|
||||
log_error("Failed to determine file system type of %s", what);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (streq(fstype, "crypto_LUKS")) {
|
||||
log_error("nspawn currently does not support LUKS disk images.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* If this is a loopback device then let's mount the image with discard, so that the underlying file remains
|
||||
* sparse when possible. */
|
||||
if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs")) {
|
||||
const char *l;
|
||||
|
||||
l = path_startswith(what, "/dev");
|
||||
if (l && startswith(l, "loop"))
|
||||
options = "discard";
|
||||
}
|
||||
|
||||
return mount_verbose(LOG_ERR, what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||
#else
|
||||
log_error("--image= is not supported, compiled without blkid support.");
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int setup_machine_id(const char *directory) {
|
||||
const char *etc_machine_id;
|
||||
sd_id128_t id;
|
||||
@ -2370,83 +1857,6 @@ static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int mount_devices(
|
||||
const char *where,
|
||||
const char *root_device, bool root_device_rw,
|
||||
const char *home_device, bool home_device_rw,
|
||||
const char *srv_device, bool srv_device_rw,
|
||||
const char *esp_device) {
|
||||
int r;
|
||||
|
||||
assert(where);
|
||||
|
||||
if (root_device) {
|
||||
r = mount_device(root_device, arg_directory, NULL, root_device_rw);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount root directory: %m");
|
||||
}
|
||||
|
||||
if (home_device) {
|
||||
r = mount_device(home_device, arg_directory, "/home", home_device_rw);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount home directory: %m");
|
||||
}
|
||||
|
||||
if (srv_device) {
|
||||
r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount server data directory: %m");
|
||||
}
|
||||
|
||||
if (esp_device) {
|
||||
const char *mp, *x;
|
||||
|
||||
/* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
|
||||
|
||||
mp = "/efi";
|
||||
x = strjoina(arg_directory, mp);
|
||||
r = dir_is_empty(x);
|
||||
if (r == -ENOENT) {
|
||||
mp = "/boot";
|
||||
x = strjoina(arg_directory, mp);
|
||||
r = dir_is_empty(x);
|
||||
}
|
||||
|
||||
if (r > 0) {
|
||||
r = mount_device(esp_device, arg_directory, mp, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount ESP: %m");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loop_remove(int nr, int *image_fd) {
|
||||
_cleanup_close_ int control = -1;
|
||||
int r;
|
||||
|
||||
if (nr < 0)
|
||||
return;
|
||||
|
||||
if (image_fd && *image_fd >= 0) {
|
||||
r = ioctl(*image_fd, LOOP_CLR_FD);
|
||||
if (r < 0)
|
||||
log_debug_errno(errno, "Failed to close loop image: %m");
|
||||
*image_fd = safe_close(*image_fd);
|
||||
}
|
||||
|
||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||
if (control < 0) {
|
||||
log_warning_errno(errno, "Failed to open /dev/loop-control: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = ioctl(control, LOOP_CTL_REMOVE, nr);
|
||||
if (r < 0)
|
||||
log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return values:
|
||||
* < 0 : wait_for_terminate() failed to get the state of the
|
||||
@ -2568,10 +1978,22 @@ static int determine_names(void) {
|
||||
}
|
||||
|
||||
if (!arg_machine) {
|
||||
|
||||
if (arg_directory && path_equal(arg_directory, "/"))
|
||||
arg_machine = gethostname_malloc();
|
||||
else
|
||||
arg_machine = strdup(basename(arg_image ?: arg_directory));
|
||||
else {
|
||||
if (arg_image) {
|
||||
char *e;
|
||||
|
||||
arg_machine = strdup(basename(arg_image));
|
||||
|
||||
/* Truncate suffix if there is one */
|
||||
e = endswith(arg_machine, ".raw");
|
||||
if (e)
|
||||
*e = 0;
|
||||
} else
|
||||
arg_machine = strdup(basename(arg_directory));
|
||||
}
|
||||
if (!arg_machine)
|
||||
return log_oom();
|
||||
|
||||
@ -2921,10 +2343,7 @@ static int outer_child(
|
||||
Barrier *barrier,
|
||||
const char *directory,
|
||||
const char *console,
|
||||
const char *root_device, bool root_device_rw,
|
||||
const char *home_device, bool home_device_rw,
|
||||
const char *srv_device, bool srv_device_rw,
|
||||
const char *esp_device,
|
||||
DissectedImage *dissected_image,
|
||||
bool interactive,
|
||||
bool secondary,
|
||||
int pid_socket,
|
||||
@ -2984,13 +2403,11 @@ static int outer_child(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mount_devices(directory,
|
||||
root_device, root_device_rw,
|
||||
home_device, home_device_rw,
|
||||
srv_device, srv_device_rw,
|
||||
esp_device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (dissected_image) {
|
||||
r = dissected_image_mount(dissected_image, directory, DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = determine_uid_shift(directory);
|
||||
if (r < 0)
|
||||
@ -3607,10 +3024,7 @@ static int load_settings(void) {
|
||||
|
||||
static int run(int master,
|
||||
const char* console,
|
||||
const char *root_device, bool root_device_rw,
|
||||
const char *home_device, bool home_device_rw,
|
||||
const char *srv_device, bool srv_device_rw,
|
||||
const char *esp_device,
|
||||
DissectedImage *dissected_image,
|
||||
bool interactive,
|
||||
bool secondary,
|
||||
FDSet *fds,
|
||||
@ -3717,10 +3131,7 @@ static int run(int master,
|
||||
r = outer_child(&barrier,
|
||||
arg_directory,
|
||||
console,
|
||||
root_device, root_device_rw,
|
||||
home_device, home_device_rw,
|
||||
srv_device, srv_device_rw,
|
||||
esp_device,
|
||||
dissected_image,
|
||||
interactive,
|
||||
secondary,
|
||||
pid_socket_pair[1],
|
||||
@ -4025,13 +3436,59 @@ static int run(int master,
|
||||
return 1; /* loop again */
|
||||
}
|
||||
|
||||
static int load_root_hash(const char *image) {
|
||||
_cleanup_free_ char *text = NULL;
|
||||
char *fn, *n, *e;
|
||||
void *k;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert_se(image);
|
||||
|
||||
/* Try to load the root hash from a file next to the image file if it exists. */
|
||||
|
||||
if (arg_root_hash)
|
||||
return 0;
|
||||
|
||||
fn = new(char, strlen(image) + strlen(".roothash") + 1);
|
||||
if (!fn)
|
||||
return log_oom();
|
||||
|
||||
n = stpcpy(fn, image);
|
||||
e = endswith(fn, ".raw");
|
||||
if (e)
|
||||
n = e;
|
||||
|
||||
strcpy(n, ".roothash");
|
||||
|
||||
r = read_one_line_file(fn, &text);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read %s, ignoring: %m", fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = unhexmem(text, strlen(text), &k, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Invalid root hash: %s", text);
|
||||
if (l < sizeof(sd_id128_t)) {
|
||||
free(k);
|
||||
return log_error_errno(r, "Root hash too short: %s", text);
|
||||
}
|
||||
|
||||
arg_root_hash = k;
|
||||
arg_root_hash_size = l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
|
||||
bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
|
||||
_cleanup_close_ int master = -1, image_fd = -1;
|
||||
_cleanup_free_ char *console = NULL;
|
||||
_cleanup_close_ int master = -1;
|
||||
_cleanup_fdset_free_ FDSet *fds = NULL;
|
||||
int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS;
|
||||
int r, n_fd_passed, ret = EXIT_SUCCESS;
|
||||
char veth_name[IFNAMSIZ] = "";
|
||||
bool secondary = false, remove_directory = false, remove_image = false;
|
||||
pid_t pid = 0;
|
||||
@ -4039,6 +3496,9 @@ int main(int argc, char *argv[]) {
|
||||
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
|
||||
bool interactive, veth_created = false, remove_tmprootdir = false;
|
||||
char tmprootdir[] = "/tmp/nspawn-root-XXXXXX";
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
@ -4237,6 +3697,10 @@ int main(int argc, char *argv[]) {
|
||||
r = log_error_errno(r, "Failed to create image lock: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = load_root_hash(arg_image);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!mkdtemp(tmprootdir)) {
|
||||
@ -4252,18 +3716,41 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
image_fd = setup_image(&device_path, &loop_nr);
|
||||
if (image_fd < 0) {
|
||||
r = image_fd;
|
||||
r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up loopback block device: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = dissect_image(image_fd,
|
||||
&root_device, &root_device_rw,
|
||||
&home_device, &home_device_rw,
|
||||
&srv_device, &srv_device_rw,
|
||||
&esp_device,
|
||||
&secondary);
|
||||
r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
|
||||
if (r == -ENOPKG) {
|
||||
log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);
|
||||
|
||||
log_notice("Note that the disk image needs to\n"
|
||||
" a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
|
||||
" b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
|
||||
" c) or follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n"
|
||||
" d) or contain a file system without a partition table\n"
|
||||
"in order to be bootable with systemd-nspawn.");
|
||||
goto finish;
|
||||
}
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
log_error_errno(r, "No root partition for specified root hash found.");
|
||||
goto finish;
|
||||
}
|
||||
if (r == -EOPNOTSUPP) {
|
||||
log_error_errno(r, "--image= is not supported, compiled without blkid support.");
|
||||
goto finish;
|
||||
}
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to dissect image: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!arg_root_hash && dissected_image->can_verity)
|
||||
log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
|
||||
|
||||
r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
@ -4317,10 +3804,7 @@ int main(int argc, char *argv[]) {
|
||||
for (;;) {
|
||||
r = run(master,
|
||||
console,
|
||||
root_device, root_device_rw,
|
||||
home_device, home_device_rw,
|
||||
srv_device, srv_device_rw,
|
||||
esp_device,
|
||||
dissected_image,
|
||||
interactive, secondary,
|
||||
fds,
|
||||
veth_name, &veth_created,
|
||||
@ -4347,8 +3831,6 @@ finish:
|
||||
if (pid > 0)
|
||||
(void) wait_for_terminate(pid, NULL);
|
||||
|
||||
loop_remove(loop_nr, &image_fd);
|
||||
|
||||
if (remove_directory && arg_directory) {
|
||||
int k;
|
||||
|
||||
@ -4395,6 +3877,7 @@ finish:
|
||||
strv_free(arg_parameters);
|
||||
custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
|
||||
expose_port_free_all(arg_expose_ports);
|
||||
free(arg_root_hash);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : ret;
|
||||
}
|
||||
|
1078
src/shared/dissect-image.c
Normal file
1078
src/shared/dissect-image.c
Normal file
File diff suppressed because it is too large
Load Diff
90
src/shared/dissect-image.h
Normal file
90
src/shared/dissect-image.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct DissectedImage DissectedImage;
|
||||
typedef struct DissectedPartition DissectedPartition;
|
||||
typedef struct DecryptedImage DecryptedImage;
|
||||
|
||||
struct DissectedPartition {
|
||||
bool found:1;
|
||||
bool rw:1;
|
||||
int partno; /* -1 if there was no partition and the images contains a file system directly */
|
||||
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
|
||||
char *fstype;
|
||||
char *node;
|
||||
char *decrypted_node;
|
||||
char *decrypted_fstype;
|
||||
};
|
||||
|
||||
enum {
|
||||
PARTITION_ROOT,
|
||||
PARTITION_ROOT_SECONDARY, /* Secondary architecture */
|
||||
PARTITION_HOME,
|
||||
PARTITION_SRV,
|
||||
PARTITION_ESP,
|
||||
PARTITION_SWAP,
|
||||
PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
|
||||
PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
|
||||
_PARTITION_DESIGNATOR_MAX,
|
||||
_PARTITION_DESIGNATOR_INVALID = -1
|
||||
};
|
||||
|
||||
static inline int PARTITION_VERITY_OF(int p) {
|
||||
if (p == PARTITION_ROOT)
|
||||
return PARTITION_ROOT_VERITY;
|
||||
if (p == PARTITION_ROOT_SECONDARY)
|
||||
return PARTITION_ROOT_SECONDARY_VERITY;
|
||||
return _PARTITION_DESIGNATOR_INVALID;
|
||||
}
|
||||
|
||||
typedef enum DissectImageFlags {
|
||||
DISSECT_IMAGE_READ_ONLY = 1,
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on loop device and file system supports it */
|
||||
DISSECT_IMAGE_DISCARD = 4, /* Turn on "discard" if file system supports it, on all block devices */
|
||||
DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */
|
||||
} DissectImageFlags;
|
||||
|
||||
struct DissectedImage {
|
||||
bool encrypted:1;
|
||||
bool verity:1; /* verity available and usable */
|
||||
bool can_verity:1; /* verity available, but not necessarily used */
|
||||
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
|
||||
};
|
||||
|
||||
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
|
||||
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
||||
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
|
||||
|
||||
DecryptedImage* decrypted_image_unref(DecryptedImage *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
|
||||
int decrypted_image_relinquish(DecryptedImage *d);
|
||||
|
||||
const char* partition_designator_to_string(int i) _const_;
|
||||
int partition_designator_from_string(const char *name) _pure_;
|
@ -32,28 +32,43 @@
|
||||
#define GPT_ROOT_ARM SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
|
||||
#define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
|
||||
#define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
|
||||
|
||||
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
|
||||
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
|
||||
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
|
||||
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
|
||||
|
||||
/* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
|
||||
* are commonly read-only and hence suitable for verity). */
|
||||
#define GPT_ROOT_X86_VERITY SD_ID128_MAKE(d1,3c,5d,3b,b5,d1,42,2a,b2,9f,94,54,fd,c8,9d,76)
|
||||
#define GPT_ROOT_X86_64_VERITY SD_ID128_MAKE(2c,73,57,ed,eb,d2,46,d9,ae,c1,23,d4,37,ec,2b,f5)
|
||||
#define GPT_ROOT_ARM_VERITY SD_ID128_MAKE(73,86,cd,f2,20,3c,47,a9,a4,98,f2,ec,ce,45,a2,d6)
|
||||
#define GPT_ROOT_ARM_64_VERITY SD_ID128_MAKE(df,33,00,ce,d6,9f,4c,92,97,8c,9b,fb,0f,38,d8,20)
|
||||
#define GPT_ROOT_IA64_VERITY SD_ID128_MAKE(86,ed,10,d5,b6,07,45,bb,89,57,d3,50,f2,3d,05,71)
|
||||
|
||||
|
||||
#if defined(__x86_64__)
|
||||
# define GPT_ROOT_NATIVE GPT_ROOT_X86_64
|
||||
# define GPT_ROOT_SECONDARY GPT_ROOT_X86
|
||||
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_64_VERITY
|
||||
# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_X86_VERITY
|
||||
#elif defined(__i386__)
|
||||
# define GPT_ROOT_NATIVE GPT_ROOT_X86
|
||||
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_VERITY
|
||||
#endif
|
||||
|
||||
#if defined(__ia64__)
|
||||
# define GPT_ROOT_NATIVE GPT_ROOT_IA64
|
||||
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_IA64_VERITY
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
|
||||
# define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
|
||||
# define GPT_ROOT_SECONDARY GPT_ROOT_ARM
|
||||
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_64_VERITY
|
||||
# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_ARM_VERITY
|
||||
#elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN)
|
||||
# define GPT_ROOT_NATIVE GPT_ROOT_ARM
|
||||
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
|
||||
#endif
|
||||
|
||||
/* Flags we recognize on the root, swap, home and srv partitions when
|
||||
|
166
src/shared/loop-util.c
Normal file
166
src/shared/loop-util.c
Normal file
@ -0,0 +1,166 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/loop.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "loop-util.h"
|
||||
|
||||
int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
|
||||
const struct loop_info64 info = {
|
||||
.lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
|
||||
};
|
||||
|
||||
_cleanup_close_ int control = -1, loop = -1;
|
||||
_cleanup_free_ char *loopdev = NULL;
|
||||
struct stat st;
|
||||
LoopDevice *d;
|
||||
int nr;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
int copy;
|
||||
|
||||
/* If this is already a block device, store a copy of the fd as it is */
|
||||
|
||||
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||
if (copy < 0)
|
||||
return -errno;
|
||||
|
||||
d = new0(LoopDevice, 1);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
*d = (LoopDevice) {
|
||||
.fd = copy,
|
||||
.nr = -1,
|
||||
};
|
||||
|
||||
*ret = d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return -EINVAL;
|
||||
|
||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||
if (control < 0)
|
||||
return -errno;
|
||||
|
||||
nr = ioctl(control, LOOP_CTL_GET_FREE);
|
||||
if (nr < 0)
|
||||
return -errno;
|
||||
|
||||
if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
|
||||
if (loop < 0)
|
||||
return -errno;
|
||||
|
||||
if (ioctl(loop, LOOP_SET_FD, fd) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
|
||||
return -errno;
|
||||
|
||||
d = new(LoopDevice, 1);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
*d = (LoopDevice) {
|
||||
.fd = loop,
|
||||
.node = loopdev,
|
||||
.nr = nr,
|
||||
};
|
||||
|
||||
loop = -1;
|
||||
loopdev = NULL;
|
||||
|
||||
*ret = d;
|
||||
|
||||
return (*ret)->fd;
|
||||
}
|
||||
|
||||
int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
|
||||
|
||||
fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return loop_device_make(fd, open_flags, ret);
|
||||
}
|
||||
|
||||
LoopDevice* loop_device_unref(LoopDevice *d) {
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
if (d->fd >= 0) {
|
||||
|
||||
if (d->nr >= 0 && !d->relinquished) {
|
||||
if (ioctl(d->fd, LOOP_CLR_FD) < 0)
|
||||
log_debug_errno(errno, "Failed to clear loop device: %m");
|
||||
|
||||
}
|
||||
|
||||
safe_close(d->fd);
|
||||
}
|
||||
|
||||
if (d->nr >= 0 && !d->relinquished) {
|
||||
_cleanup_close_ int control = -1;
|
||||
|
||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||
if (control < 0)
|
||||
log_debug_errno(errno, "Failed to open loop control device: %m");
|
||||
else {
|
||||
if (ioctl(control, LOOP_CTL_REMOVE, d->nr) < 0)
|
||||
log_debug_errno(errno, "Failed to remove loop device: %m");
|
||||
}
|
||||
}
|
||||
|
||||
free(d->node);
|
||||
free(d);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void loop_device_relinquish(LoopDevice *d) {
|
||||
assert(d);
|
||||
|
||||
/* Don't attempt to clean up the loop device anymore from this point on. Leave the clean-ing up to the kernel
|
||||
* itself, using the loop device "auto-clear" logic we already turned on when creating the device. */
|
||||
|
||||
d->relinquished = true;
|
||||
}
|
41
src/shared/loop-util.h
Normal file
41
src/shared/loop-util.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct LoopDevice LoopDevice;
|
||||
|
||||
/* Some helpers for setting up loopback block devices */
|
||||
|
||||
struct LoopDevice {
|
||||
int fd;
|
||||
int nr;
|
||||
char *node;
|
||||
bool relinquished;
|
||||
};
|
||||
|
||||
int loop_device_make(int fd, int open_flags, LoopDevice **ret);
|
||||
int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret);
|
||||
|
||||
LoopDevice* loop_device_unref(LoopDevice *d);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
|
||||
|
||||
void loop_device_relinquish(LoopDevice *d);
|
66
src/test/test-dissect-image.c
Normal file
66
src/test/test-dissect-image.c
Normal file
@ -0,0 +1,66 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dissect-image.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
int r, i;
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
|
||||
if (argc < 2) {
|
||||
log_error("Requires one command line argument.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = loop_device_make_by_path(argv[1], O_RDONLY, &d);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, &m);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to dissect image: %m");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
|
||||
if (!m->partitions[i].found)
|
||||
continue;
|
||||
|
||||
printf("Found %s partition, %s of type %s at #%i (%s)\n",
|
||||
partition_designator_to_string(i),
|
||||
m->partitions[i].rw ? "writable" : "read-only",
|
||||
strna(m->partitions[i].fstype),
|
||||
m->partitions[i].partno,
|
||||
strna(m->partitions[i].node));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user