Compare commits
21 Commits
c53da7ed02
...
830171936c
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | 830171936c | |
Lennart Poettering | 1af83e7c37 | |
Lennart Poettering | 35afe47abe | |
Lennart Poettering | de8231b007 | |
Lennart Poettering | 0b9481cf2e | |
Lennart Poettering | 61f403a14f | |
Lennart Poettering | 5a151082d7 | |
Lennart Poettering | af8219d562 | |
Lennart Poettering | af187ab237 | |
Lennart Poettering | fa45d12c1c | |
Lennart Poettering | 37e44c3f95 | |
Lennart Poettering | f5ea63a5e1 | |
Lennart Poettering | 89d00f2e3f | |
Lennart Poettering | e3659eb236 | |
Lennart Poettering | 16b7459280 | |
Lennart Poettering | 33973b841d | |
Lennart Poettering | bacf21e9e9 | |
Lennart Poettering | 5c05f06264 | |
Lennart Poettering | 1ffd93683b | |
Lennart Poettering | 140788f75f | |
Lennart Poettering | ee72df1c7b |
11
TODO
11
TODO
|
@ -42,9 +42,6 @@ Features:
|
|||
mounting a subdir of the root fs as actual root. This can be used as
|
||||
fstype-agnostic version of btrfs' rootflags=subvol=foobar.
|
||||
|
||||
* add --copy-from and --copy-to command to systemd-dissect which copies stuff
|
||||
in and out of a disk image
|
||||
|
||||
* Support ProtectProc= or so, using: https://patchwork.kernel.org/cover/11310197/
|
||||
|
||||
* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
|
||||
|
@ -112,10 +109,6 @@ Features:
|
|||
* systemd-path: add ESP and XBOOTLDR path. Add "private" runtime/state/cache dir enum,
|
||||
mapping to $RUNTIME_DIRECTORY, $STATE_DIRECTORY and such
|
||||
|
||||
* make "systemd-dissect" an official supported tool, i.e. move to /usr/bin/ and
|
||||
provide man page. Given that we now have a tool that can generate images like
|
||||
this, it's useful to have one that can dump contents of them, too.
|
||||
|
||||
* All tools that support --root= should also learn --image= so that they can
|
||||
operate on disk images directly. Specifically: bootctl, systemctl,
|
||||
coredumpctl. (Already done: systemd-nspawn, systemd-firstboot,
|
||||
|
@ -1153,10 +1146,6 @@ Features:
|
|||
- optionally automatically add FORWARD rules to iptables whenever nspawn is
|
||||
running, remove them when shut down.
|
||||
|
||||
* dissect
|
||||
- refuse mounting over a mount point
|
||||
- automatically discover .roothash files in dissect, similarly to nspawn
|
||||
|
||||
* machined:
|
||||
- add an API so that libvirt-lxc can inform us about network interfaces being
|
||||
removed or added to an existing machine
|
||||
|
|
|
@ -818,6 +818,7 @@ manpages = [
|
|||
['systemd-debug-generator', '8', [], ''],
|
||||
['systemd-delta', '1', [], ''],
|
||||
['systemd-detect-virt', '1', [], ''],
|
||||
['systemd-dissect', '1', [], ''],
|
||||
['systemd-environment-d-generator',
|
||||
'8',
|
||||
['30-systemd-environment-d-generator'],
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
|
||||
|
||||
<refentry id="systemd-dissect"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-dissect</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-dissect</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-dissect</refname>
|
||||
<refpurpose>Dissect file system OS images</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--mount</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--copy-from</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg> <arg choice="opt"><replaceable>TARGET</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--copy-to</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="opt"><replaceable>SOURCE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-dissect</command> is a tool for introspecting and interacting with file system OS
|
||||
disk images. It supports four different operations:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>Show general OS image information, including the image's
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> data,
|
||||
machine ID, partition information and more.</para></listitem>
|
||||
|
||||
<listitem><para>Mount an OS image to a local directory. In this mode it will dissect the OS image and
|
||||
mount the included partitions according to their designation onto a directory and possibly
|
||||
sub-directories.</para></listitem>
|
||||
|
||||
<listitem><para>Copy files and directories in and out of an OS image.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>The tool may operate on three types of OS images:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>OS disk images containing a GPT partition table envelope, with partitions marked
|
||||
according to the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
|
||||
Specification</ulink>.</para></listitem>
|
||||
|
||||
<listitem><para>OS disk images containing just a plain file-system without an enveloping partition
|
||||
table. (This file system is assumed to be the root file system of the OS.)</para></listitem>
|
||||
|
||||
<listitem><para>OS disk images containing a GPT or MBR partition table, with a single
|
||||
partition only. (This partition is assumed to contain the root file system of the OS.)</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>OS images may use any kind of Linux-supported file systems. In addition they may make use of LUKS
|
||||
disk encryption, and contain Verity integrity information. Note that qualifying OS images may be booted
|
||||
with <citerefentry><refentrytitle>system-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<option>--image=</option> switch, and be used as root file system for system service using the
|
||||
<varname>RootImage=</varname> unit file setting, see
|
||||
<citerefentry><refentrytitle>system.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Commands</title>
|
||||
|
||||
<para>If neither of the command switches listed below are passed the specified disk image is opened and
|
||||
general information about the image and the contained partitions and their use is shown.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--mount</option></term>
|
||||
<term><option>-m</option></term>
|
||||
|
||||
<listitem><para>Mount the specified OS image to the specified directory. This will dissect the image,
|
||||
determine the OS root file system — as well as possibly other partitions — and mount them to the
|
||||
specified directory. If the OS image contains multiple partitions marked with the <ulink
|
||||
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>
|
||||
multiple nested mounts are established. This command expects two arguments: a path to an image file
|
||||
and a path to a directory where to mount the image.</para>
|
||||
|
||||
<para>To unmount an OS image mounted like this use <citerefentry
|
||||
project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
|
||||
<option>-R</option> switch (for recursive operation), so that the OS image and all nested partition
|
||||
mounts are unmounted.</para>
|
||||
|
||||
<para>When the OS image contains LUKS encrypted or Verity integrity protected file systems
|
||||
appropriate volumes are automatically set up and marked for automatic disassembly when the image is
|
||||
unmounted.</para>
|
||||
|
||||
<para>The OS image may either be specified as path to an OS image stored in a regular file or may
|
||||
refer to block device node (in the latter case the block device must be the "whole" device, i.e. not
|
||||
a partition device). (The other supported commands described here support this, too.)</para>
|
||||
|
||||
<para>All mounted file systems are checked with the appropriate <citerefentry
|
||||
project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
implementation in automatic fixing mode, unless explicitly turned off (<option>--fsck=no</option>) or
|
||||
read-only operation is requested (<option>--read-only</option>).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-M</option></term>
|
||||
|
||||
<listitem><para>This is a shortcut for <option>--mount --mkdir</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--copy-from</option></term>
|
||||
<term><option>-x</option></term>
|
||||
|
||||
<listitem><para>Copies a file or directory from the specified OS image into the specified location on
|
||||
the host file system. Expects three arguments: a path to an image file, a source path (relative to
|
||||
the image's root directory) and a destination path (relative to the current working directory, or an
|
||||
absolute path, both outside of the image). If the destination path is omitted or specified as dash
|
||||
(<literal>-</literal>), the specified file is written to standard output. If the source path in the
|
||||
image file system refers to a regular file it is copied to the destination path. In this case access
|
||||
mode, extended attributes and timestamps are copied as well, but file ownership is not. If the source
|
||||
path in the image refers to a directory, it is copied to the destination path, recursively with all
|
||||
containing files and directories. In this case the file ownership is copied too.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--copy-to</option></term>
|
||||
<term><option>-a</option></term>
|
||||
|
||||
<listitem><para>Copies a file or directory from the specified location in the host file system into
|
||||
the specified OS image. Expects three arguments: a path to an image file, a source path (relative to
|
||||
the current working directory, or an absolute path, both outside of the image) and a destination path
|
||||
(relative to the image's root directory). If the source path is omitted or specified as dash
|
||||
(<literal>-</literal>), the data to write is read from standard input. If the source path in the host
|
||||
file system refers to a regular file, it is copied to the destination path. In this case access mode,
|
||||
extended attributes and timestamps are copied as well, but file ownership is not. If the source path
|
||||
in the host file system refers to a directory it is copied to the destination path, recursively with
|
||||
all containing files and directories. In this case the file ownership is copied
|
||||
too.</para>
|
||||
|
||||
<para>As with <option>--mount</option> file system checks are implicitly run before the copy
|
||||
operation begins.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--json=</option><replaceable>MODE</replaceable></term>
|
||||
|
||||
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
|
||||
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
|
||||
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
|
||||
off json output).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--read-only</option></term>
|
||||
<term><option>-r</option></term>
|
||||
|
||||
<listitem><para>Operate in read-only mode. By default <option>--mount</option> will establish
|
||||
writable mount points. If this option is specified they are established in read-only mode
|
||||
instead.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--fsck=no</option></term>
|
||||
|
||||
<listitem><para>Turn off automatic file system checking. By default when an image is accessed for
|
||||
writing (by <option>--mount</option> or <option>--add</option>) the file systems contained in the OS
|
||||
image are automatically checked using the appropriate <citerefentry
|
||||
project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
command, in automatic fixing mode. This behavior may be switched off using
|
||||
<option>--fsck=no</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--mkdir</option></term>
|
||||
|
||||
<listitem><para>If combined with <option>--mount</option> the directory to mount the OS image to is
|
||||
created if it is missing. Note that the directory is not automatically removed when the disk image is
|
||||
unmounted again.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--discard=</option></term>
|
||||
|
||||
<listitem><para>Takes one of <literal>disabled</literal>, <literal>loop</literal>,
|
||||
<literal>all</literal>, <literal>crypto</literal>. If <literal>disabled</literal> the image is
|
||||
accessed with empty block discarding turned off. if <literal>loop</literal> discarding is enabled if
|
||||
operating on a regular file. If <literal>crypt</literal> discarding is enabled even on encrypted file
|
||||
systems. If <literal>all</literal> discarding is unconditionally enabled.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--root-hash=</option></term>
|
||||
<term><option>--root-hash-sig=</option></term>
|
||||
<term><option>--verity-data=</option></term>
|
||||
|
||||
<listitem><para>Configure various aspects of Verity data integrity for the OS
|
||||
image. <option>--root-hash=</option> expects a hex-encoding top-level Verity hash to use for setting
|
||||
up the Verity integrity protection. <option>--root-hash-sig=</option> expects the path to a file
|
||||
containing a PKCS#7 signature file for the hash. This signature is passed to the kernel during
|
||||
activation, which will match it against signature keys available in the kernel
|
||||
keyring. <option>--verity-data=</option> expects the path to a file with the Verity data to use for
|
||||
the OS image, in case it is stored in a detached file. It is recommended to embed the Verity data
|
||||
directly in the image, using the Verity mechanisms in the <ulink
|
||||
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, a non-zero failure code
|
||||
otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>system-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>system.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>,
|
||||
<citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
|
@ -1898,8 +1898,7 @@ if conf.get('HAVE_BLKID') == 1
|
|||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
install : true)
|
||||
endif
|
||||
|
||||
if conf.get('ENABLE_RESOLVE') == 1
|
||||
|
|
|
@ -969,6 +969,21 @@ int copy_times(int fdf, int fdt, CopyFlags flags) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int copy_access(int fdf, int fdt) {
|
||||
struct stat st;
|
||||
|
||||
assert(fdf >= 0);
|
||||
assert(fdt >= 0);
|
||||
|
||||
if (fstat(fdf, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (fchmod(fdt, st.st_mode & 07777) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int copy_xattr(int fdf, int fdt) {
|
||||
_cleanup_free_ char *names = NULL;
|
||||
int ret = 0, r;
|
||||
|
|
|
@ -61,4 +61,5 @@ static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags cop
|
|||
}
|
||||
|
||||
int copy_times(int fdf, int fdt, CopyFlags flags);
|
||||
int copy_access(int fdf, int fdt);
|
||||
int copy_xattr(int fdf, int fdt);
|
||||
|
|
|
@ -106,7 +106,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui
|
|||
/* return immediately if directory exists */
|
||||
e = strrchr(path, '/');
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
|
||||
if (e == path)
|
||||
return 0;
|
||||
|
|
|
@ -4,26 +4,44 @@
|
|||
#include <getopt.h>
|
||||
#include <linux/loop.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "architecture.h"
|
||||
#include "copy.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir.h"
|
||||
#include "mount-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static enum {
|
||||
ACTION_DISSECT,
|
||||
ACTION_MOUNT,
|
||||
ACTION_COPY_FROM,
|
||||
ACTION_COPY_TO,
|
||||
} arg_action = ACTION_DISSECT;
|
||||
static const char *arg_image = NULL;
|
||||
static const char *arg_path = NULL;
|
||||
static const char *arg_source = NULL;
|
||||
static const char *arg_target = NULL;
|
||||
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
|
||||
static void *arg_root_hash = NULL;
|
||||
static char *arg_verity_data = NULL;
|
||||
|
@ -31,21 +49,31 @@ static size_t arg_root_hash_size = 0;
|
|||
static char *arg_root_hash_sig_path = NULL;
|
||||
static void *arg_root_hash_sig = NULL;
|
||||
static size_t arg_root_hash_sig_size = 0;
|
||||
static bool arg_json = false;
|
||||
static JsonFormatFlags arg_json_format_flags = 0;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
|
||||
|
||||
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"
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-dissect", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%1$s [OPTIONS...] IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mount IMAGE PATH\n"
|
||||
"%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n"
|
||||
"%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n\n"
|
||||
"%5$sDissect a file system OS image.%6$s\n\n"
|
||||
"%3$sOptions:%4$s\n"
|
||||
" -r --read-only Mount read-only\n"
|
||||
" --fsck=BOOL Run fsck before mounting\n"
|
||||
" --mkdir Make mount directory before mounting, if missing\n"
|
||||
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
|
||||
" --root-hash=HASH Specify root hash for verity\n"
|
||||
" --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
|
||||
|
@ -53,9 +81,23 @@ static void help(void) {
|
|||
" or as an ASCII base64 encoded string prefixed by\n"
|
||||
" 'base64:'\n"
|
||||
" --verity-data=PATH Specify data file with hash tree for verity if it is\n"
|
||||
" not embedded in IMAGE\n",
|
||||
program_invocation_short_name,
|
||||
program_invocation_short_name);
|
||||
" not embedded in IMAGE\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" -m --mount Mount the image to the specified directory\n"
|
||||
" -M Shortcut for --mount --mkdir\n"
|
||||
" -x --copy-from Copy files from image to host\n"
|
||||
" -a --copy-to Copy files from host to image\n"
|
||||
"\nSee the %2$s for details.\n"
|
||||
, program_invocation_short_name
|
||||
, link
|
||||
, ansi_underline(), ansi_normal()
|
||||
, ansi_highlight(), ansi_normal());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
@ -67,6 +109,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_FSCK,
|
||||
ARG_VERITY_DATA,
|
||||
ARG_ROOT_HASH_SIG,
|
||||
ARG_MKDIR,
|
||||
ARG_JSON,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -79,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "fsck", required_argument, NULL, ARG_FSCK },
|
||||
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
|
||||
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
|
||||
{ "mkdir", no_argument, NULL, ARG_MKDIR },
|
||||
{ "copy-from", no_argument, NULL, 'x' },
|
||||
{ "copy-to", no_argument, NULL, 'a' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -87,13 +135,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
|
||||
while ((c = getopt_long(argc, argv, "hmrMxa", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
@ -102,6 +149,25 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_action = ACTION_MOUNT;
|
||||
break;
|
||||
|
||||
case ARG_MKDIR:
|
||||
arg_flags |= DISSECT_IMAGE_MKDIR;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
/* Shortcut combination of the above two */
|
||||
arg_action = ACTION_MOUNT;
|
||||
arg_flags |= DISSECT_IMAGE_MKDIR;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
arg_action = ACTION_COPY_FROM;
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
arg_action = ACTION_COPY_TO;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
@ -117,7 +183,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
|
||||
else if (streq(optarg, "crypt"))
|
||||
flags = DISSECT_IMAGE_DISCARD_ANY;
|
||||
else
|
||||
else if (streq(optarg, "list")) {
|
||||
puts("disabled\n"
|
||||
"all\n"
|
||||
"crypt\n"
|
||||
"loop");
|
||||
return 0;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown --discard= parameter: %s",
|
||||
optarg);
|
||||
|
@ -184,6 +256,26 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
if (streq(optarg, "pretty")) {
|
||||
arg_json = true;
|
||||
arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
|
||||
} else if (streq(optarg, "short")) {
|
||||
arg_json = true;
|
||||
arg_json_format_flags = JSON_FORMAT_NEWLINE;
|
||||
} else if (streq(optarg, "off")) {
|
||||
arg_json = false;
|
||||
arg_json_format_flags = 0;
|
||||
} else if (streq(optarg, "help")) {
|
||||
puts("pretty\n"
|
||||
"short\n"
|
||||
"off");
|
||||
return 0;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -198,7 +290,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
case ACTION_DISSECT:
|
||||
if (optind + 1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected a file path as only argument.");
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
|
@ -207,12 +299,41 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
case ACTION_MOUNT:
|
||||
if (optind + 2 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected a file path and mount point path as only arguments.");
|
||||
"Expected an image file path and mount point path as only arguments.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_path = argv[optind + 1];
|
||||
break;
|
||||
|
||||
case ACTION_COPY_FROM:
|
||||
if (argc < optind + 2 || argc > optind + 3)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path, a source path and an optional destination path as only arguments.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_source = argv[optind + 1];
|
||||
arg_target = argc > optind + 2 ? argv[optind + 2] : "-" /* this means stdout */ ;
|
||||
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case ACTION_COPY_TO:
|
||||
if (argc < optind + 2 || argc > optind + 3)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path, an optional source path and a destination path as only arguments.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
|
||||
if (argc > optind + 2) {
|
||||
arg_source = argv[optind + 1];
|
||||
arg_target = argv[optind + 2];
|
||||
} else {
|
||||
arg_source = "-"; /* this means stdin */
|
||||
arg_target = argv[optind + 1];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown action.");
|
||||
}
|
||||
|
@ -220,74 +341,60 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int run(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;
|
||||
static int strv_pair_to_json(char **l, JsonVariant **ret) {
|
||||
_cleanup_strv_free_ char **jl = NULL;
|
||||
char **a, **b;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
STRV_FOREACH_PAIR(a, b, l) {
|
||||
char *j;
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
j = strjoin(*a, "=", *b);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
|
||||
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
|
||||
arg_verity_data ? NULL : &arg_verity_data,
|
||||
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
|
||||
arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
|
||||
|
||||
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, NULL, arg_flags, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT: {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
printf("Found %s '%s' partition",
|
||||
p->rw ? "writable" : "read-only",
|
||||
partition_designator_to_string(i));
|
||||
|
||||
if (!sd_id128_is_null(p->uuid))
|
||||
printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid));
|
||||
|
||||
if (p->fstype)
|
||||
printf(" of type %s", p->fstype);
|
||||
|
||||
if (p->architecture != _ARCHITECTURE_INVALID)
|
||||
printf(" for %s", architecture_to_string(p->architecture));
|
||||
|
||||
if (dissected_image_can_do_verity(m, i))
|
||||
printf(" %s verity", dissected_image_has_verity(m, i) ? "with" : "without");
|
||||
|
||||
if (p->partno >= 0)
|
||||
printf(" on partition #%i", p->partno);
|
||||
|
||||
if (p->node)
|
||||
printf(" (%s)", p->node);
|
||||
|
||||
putchar('\n');
|
||||
if (strv_consume(&jl, j) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = dissected_image_acquire_metadata(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire image metadata: %m");
|
||||
return json_variant_new_array_strv(ret, jl);
|
||||
}
|
||||
|
||||
static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
uint64_t size = UINT64_MAX;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
if (!arg_json)
|
||||
printf(" Name: %s\n", basename(arg_image));
|
||||
|
||||
if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
|
||||
log_debug_errno(errno, "Failed to query size of loopback device: %m");
|
||||
else if (!arg_json) {
|
||||
char s[FORMAT_BYTES_MAX];
|
||||
printf(" Size: %s\n", format_bytes(s, sizeof(s), size));
|
||||
}
|
||||
|
||||
if (!arg_json)
|
||||
putc('\n', stdout);
|
||||
|
||||
r = dissected_image_acquire_metadata(m);
|
||||
if (r == -ENXIO)
|
||||
return log_error_errno(r, "No root partition discovered.");
|
||||
if (r == -EMEDIUMTYPE)
|
||||
return log_error_errno(r, "Not a valid OS image, no os-release file included.");
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check of image failed.");
|
||||
if (r == -EUNATCH)
|
||||
log_warning_errno(r, "OS image is encrypted, proceeding without showing OS image metadata.");
|
||||
else if (r == -EBUSY)
|
||||
log_warning_errno(r, "OS image is currently in use, proceeding without showing OS image metadata.");
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire image metadata: %m");
|
||||
else if (!arg_json) {
|
||||
if (m->hostname)
|
||||
printf(" Hostname: %s\n", m->hostname);
|
||||
|
||||
|
@ -311,20 +418,138 @@ static int run(int argc, char *argv[]) {
|
|||
p == m->os_release ? "OS Release:" : " ",
|
||||
*p, *q);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_MOUNT:
|
||||
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, arg_flags, &di);
|
||||
if (arg_json) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL;
|
||||
|
||||
if (!strv_isempty(m->machine_info)) {
|
||||
r = strv_pair_to_json(m->machine_info, &mi);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!strv_isempty(m->os_release)) {
|
||||
r = strv_pair_to_json(m->os_release, &osr);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = json_build(&v, JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(basename(arg_image))),
|
||||
JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
|
||||
JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)),
|
||||
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
|
||||
JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)),
|
||||
JSON_BUILD_PAIR_CONDITION(osr, "osRelease", JSON_BUILD_VARIANT(osr))));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!arg_json)
|
||||
putc('\n', stdout);
|
||||
|
||||
t = table_new("rw", "designator", "partition uuid", "fstype", "architecture", "verity", "node", "partno");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_empty_string(t, "-");
|
||||
(void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
|
||||
|
||||
for (unsigned i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, p->rw ? "rw" : "ro",
|
||||
TABLE_STRING, partition_designator_to_string(i));
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (sd_id128_is_null(p->uuid))
|
||||
r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
|
||||
else
|
||||
r = table_add_cell(t, NULL, TABLE_UUID, &p->uuid);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, p->fstype,
|
||||
TABLE_STRING, architecture_to_string(p->architecture));
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (arg_verity_data)
|
||||
r = table_add_cell(t, NULL, TABLE_STRING, "external");
|
||||
else if (dissected_image_can_do_verity(m, i))
|
||||
r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_has_verity(m, i)));
|
||||
else
|
||||
r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (p->partno < 0) /* no partition table, naked file system */ {
|
||||
r = table_add_cell(t, NULL, TABLE_STRING, arg_image);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
|
||||
} else {
|
||||
r = table_add_cell(t, NULL, TABLE_STRING, p->node);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_add_cell(t, NULL, TABLE_INT, &p->partno);
|
||||
}
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
if (arg_json) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *jt = NULL;
|
||||
|
||||
r = table_to_json(t, &jt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to convert table to JSON: %m");
|
||||
|
||||
r = json_variant_set_field(&v, "mounts", jt);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
json_variant_dump(v, arg_json_format_flags, stdout, NULL);
|
||||
} else {
|
||||
r = table_print(t, stdout);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dump table: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_mount(DissectedImage *m, LoopDevice *d) {
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
r = dissected_image_decrypt_interactively(
|
||||
m, NULL,
|
||||
arg_root_hash, arg_root_hash_size,
|
||||
arg_verity_data,
|
||||
arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
|
||||
arg_flags,
|
||||
&di);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check on image failed: %m");
|
||||
r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, arg_flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount image: %m");
|
||||
return r;
|
||||
|
||||
if (di) {
|
||||
r = decrypted_image_relinquish(di);
|
||||
|
@ -333,13 +558,241 @@ static int run(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
loop_device_relinquish(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_copy(DissectedImage *m, LoopDevice *d) {
|
||||
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
||||
_cleanup_(rmdir_and_freep) char *created_dir = NULL;
|
||||
_cleanup_free_ char *temp = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
r = dissected_image_decrypt_interactively(
|
||||
m, NULL,
|
||||
arg_root_hash, arg_root_hash_size,
|
||||
arg_verity_data,
|
||||
arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
|
||||
arg_flags,
|
||||
&di);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = detach_mount_namespace();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to detach mount namespace: %m");
|
||||
|
||||
r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate temporary mount directory: %m");
|
||||
|
||||
r = mkdir_p(temp, 0700);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create mount point: %m");
|
||||
|
||||
created_dir = TAKE_PTR(temp);
|
||||
|
||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
mounted_dir = TAKE_PTR(created_dir);
|
||||
|
||||
if (di) {
|
||||
r = decrypted_image_relinquish(di);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to relinquish DM devices: %m");
|
||||
}
|
||||
|
||||
loop_device_relinquish(d);
|
||||
|
||||
if (arg_action == ACTION_COPY_FROM) {
|
||||
_cleanup_close_ int source_fd = -1, target_fd = -1;
|
||||
|
||||
source_fd = chase_symlinks_and_open(arg_source, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(source_fd, "Failed to open source path '%s' in image '%s': %m", arg_source, arg_image);
|
||||
|
||||
/* Copying to stdout? */
|
||||
if (streq(arg_target, "-")) {
|
||||
r = copy_bytes(source_fd, STDOUT_FILENO, (uint64_t) -1, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to stdout: %m", arg_source, arg_image);
|
||||
|
||||
/* When we copy to stdou we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to copy as directory? */
|
||||
r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (r != -ENOTDIR)
|
||||
return log_error_errno(r, "Failed to copy %s in image '%s' to '%s': %m", arg_source, arg_image, arg_target);
|
||||
|
||||
r = fd_verify_regular(source_fd);
|
||||
if (r == -EISDIR)
|
||||
return log_error_errno(r, "Target '%s' exists already and is not a directory.", arg_target);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Source path %s in image '%s' is neither regular file nor directory, refusing: %m", arg_source, arg_image);
|
||||
|
||||
/* Nah, it's a plain file! */
|
||||
target_fd = open(arg_target, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create regular file at target path '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, (uint64_t) -1, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
|
||||
|
||||
(void) copy_xattr(source_fd, target_fd);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
/* When this is a regular file we don't copy ownership! */
|
||||
|
||||
} else {
|
||||
_cleanup_close_ int source_fd = -1, target_fd = -1;
|
||||
_cleanup_close_ int dfd = -1;
|
||||
_cleanup_free_ char *dn = NULL;
|
||||
|
||||
assert(arg_action == ACTION_COPY_TO);
|
||||
|
||||
dn = dirname_malloc(arg_target);
|
||||
if (!dn)
|
||||
return log_oom();
|
||||
|
||||
r = chase_symlinks(dn, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open '%s': %m", dn);
|
||||
|
||||
/* Are we reading from stdin? */
|
||||
if (streq(arg_source, "-")) {
|
||||
target_fd = openat(dfd, basename(arg_target), O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0644);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(STDIN_FILENO, target_fd, (uint64_t) -1, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from stdin to '%s' in image '%s': %m", arg_target, arg_image);
|
||||
|
||||
/* When we copy from stdin we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
source_fd = open(arg_source, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(source_fd, "Failed to open source path '%s': %m", arg_source);
|
||||
|
||||
r = fd_verify_regular(source_fd);
|
||||
if (r < 0) {
|
||||
if (r != -EISDIR)
|
||||
return log_error_errno(r, "Source '%s' is neither regular file nor directory: %m", arg_source);
|
||||
|
||||
/* We are looking at a directory. */
|
||||
|
||||
target_fd = openat(dfd, basename(arg_target), O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (target_fd < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
|
||||
|
||||
r = copy_tree_at(source_fd, ".", dfd, basename(arg_target), UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT);
|
||||
} else
|
||||
r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We area looking at a regular file */
|
||||
target_fd = openat(dfd, basename(arg_target), O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0600);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, (uint64_t) -1, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
|
||||
|
||||
(void) copy_xattr(source_fd, target_fd);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
/* When this is a regular file we don't copy ownership! */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = verity_metadata_load(
|
||||
arg_image, NULL,
|
||||
arg_root_hash ? NULL : &arg_root_hash,
|
||||
&arg_root_hash_size,
|
||||
arg_verity_data ? NULL : &arg_verity_data,
|
||||
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
|
||||
|
||||
r = loop_device_make_by_path(
|
||||
arg_image,
|
||||
(arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
|
||||
arg_verity_data ? 0 : LO_FLAGS_PARTSCAN,
|
||||
&d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
|
||||
if (arg_verity_data)
|
||||
arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system,
|
||||
* hence if there's external Verity data
|
||||
* available we turn off partition table
|
||||
* support */
|
||||
r = dissect_image_and_warn(
|
||||
d->fd,
|
||||
arg_image,
|
||||
arg_root_hash,
|
||||
arg_root_hash_size,
|
||||
arg_verity_data,
|
||||
NULL,
|
||||
arg_flags,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT:
|
||||
r = action_dissect(m, d);
|
||||
break;
|
||||
|
||||
case ACTION_MOUNT:
|
||||
r = action_mount(m, d);
|
||||
break;
|
||||
|
||||
case ACTION_COPY_FROM:
|
||||
case ACTION_COPY_TO:
|
||||
r = action_copy(m, d);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown action.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "memory-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "mount-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "os-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
|
|
|
@ -3369,14 +3369,13 @@ static int outer_child(
|
|||
* uid shift known. That way we can mount VFAT file systems shifted to the right place right away. This
|
||||
* makes sure ESP partitions and userns are compatible. */
|
||||
|
||||
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
|
||||
r = dissected_image_mount_and_warn(
|
||||
dissected_image, directory, arg_uid_shift,
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
|
||||
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check for image failed: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount image root file system: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = determine_uid_shift(directory);
|
||||
|
|
|
@ -2826,7 +2826,7 @@ static int help(void) {
|
|||
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
|
||||
" --size=BYTES Grow loopback file to specified size\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate json output\n"
|
||||
" Generate JSON output\n"
|
||||
"\nSee the %s for details.\n"
|
||||
, program_invocation_short_name
|
||||
, ansi_highlight(), ansi_normal()
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
#include "sd-id128.h"
|
||||
|
@ -1003,9 +1004,9 @@ static int mount_partition(
|
|||
if (!m->found || !node || !fstype)
|
||||
return 0;
|
||||
|
||||
/* Stacked encryption? Yuck */
|
||||
/* We are looking at an encrypted partition? This either means stacked encryption, or the caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error for this case. */
|
||||
if (streq_ptr(fstype, "crypto_LUKS"))
|
||||
return -ELOOP;
|
||||
return -EUNATCH;
|
||||
|
||||
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
|
||||
|
||||
|
@ -1047,6 +1048,12 @@ static int mount_partition(
|
|||
if (!strextend_with_separator(&options, ",", m->mount_options, NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
|
||||
r = mkdir_p(p, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1060,6 +1067,15 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
|||
assert(m);
|
||||
assert(where);
|
||||
|
||||
/* Returns:
|
||||
*
|
||||
* -ENXIO → No root partition found
|
||||
* -EMEDIUMTYPE → DISSECT_IMAGE_VALIDATE_OS set but no os-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)
|
||||
*/
|
||||
|
||||
if (!m->partitions[PARTITION_ROOT].found)
|
||||
return -ENXIO;
|
||||
|
||||
|
@ -1080,6 +1096,10 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
|||
if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
|
||||
return 0;
|
||||
|
||||
/* Mask DISSECT_IMAGE_MKDIR for all subdirs: the idea is that only the top-level mount point is
|
||||
* created if needed, but the image itself not modified. */
|
||||
flags &= ~DISSECT_IMAGE_MKDIR;
|
||||
|
||||
r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1125,6 +1145,29 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
|
||||
r = dissected_image_mount(m, where, uid_shift, flags);
|
||||
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.");
|
||||
if (r == -EUNATCH)
|
||||
return log_error_errno(r, "Encrypted file system discovered, but decryption not requested.");
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check on image failed.");
|
||||
if (r == -EBUSY)
|
||||
return log_error_errno(r, "File system already mounted elsewhere.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount image: %m");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
typedef struct DecryptedPartition {
|
||||
struct crypt_device *device;
|
||||
|
@ -1586,7 +1629,14 @@ int decrypted_image_relinquish(DecryptedImage *d) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig) {
|
||||
int verity_metadata_load(
|
||||
const char *image,
|
||||
const char *root_hash_path,
|
||||
void **ret_roothash,
|
||||
size_t *ret_roothash_size,
|
||||
char **ret_verity_data,
|
||||
char **ret_roothashsig) {
|
||||
|
||||
_cleanup_free_ char *verity_filename = NULL, *roothashsig_filename = NULL;
|
||||
_cleanup_free_ void *roothash_decoded = NULL;
|
||||
size_t roothash_decoded_size = 0;
|
||||
|
@ -1722,12 +1772,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
};
|
||||
|
||||
_cleanup_strv_free_ char **machine_info = NULL, **os_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;
|
||||
int fds[2 * _META_MAX], r;
|
||||
int fds[2 * _META_MAX], r, v;
|
||||
ssize_t n;
|
||||
|
||||
BLOCK_SIGNALS(SIGCHLD);
|
||||
|
||||
|
@ -1743,18 +1795,28 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
if (pipe2(error_pipe, O_CLOEXEC) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0) {
|
||||
error_pipe[0] = safe_close(error_pipe[0]);
|
||||
|
||||
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
|
||||
if (r < 0) {
|
||||
/* Let parent know the error */
|
||||
(void) write(error_pipe[1], &r, sizeof(r));
|
||||
|
||||
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (k = 0; k < _META_MAX; k++) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_close_ int fd = -ENOENT;
|
||||
const char *p;
|
||||
|
||||
fds[2*k] = safe_close(fds[2*k]);
|
||||
|
@ -1766,12 +1828,15 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
}
|
||||
if (fd < 0) {
|
||||
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
(void) write(error_pipe[1], &r, sizeof(r));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
}
|
||||
|
@ -1779,6 +1844,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
error_pipe[1] = safe_close(error_pipe[1]);
|
||||
|
||||
for (k = 0; k < _META_MAX; k++) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
|
@ -1836,7 +1903,16 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
|
||||
child = 0;
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
return r;
|
||||
|
||||
n = read(error_pipe[0], &v, sizeof(v));
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n == sizeof(v))
|
||||
return v; /* propagate error sent to us from child */
|
||||
if (n != 0)
|
||||
return -EIO;
|
||||
|
||||
if (r != EXIT_SUCCESS)
|
||||
return -EPROTO;
|
||||
|
||||
|
@ -1995,11 +2071,9 @@ int mount_image_privately_interactively(
|
|||
|
||||
created_dir = TAKE_PTR(temp);
|
||||
|
||||
r = dissected_image_mount(dissected_image, created_dir, UID_INVALID, flags);
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check on image failed: %m");
|
||||
r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mount image: %m");
|
||||
return r;
|
||||
|
||||
if (decrypted_image) {
|
||||
r = decrypted_image_relinquish(decrypted_image);
|
||||
|
|
|
@ -69,6 +69,7 @@ typedef enum DissectImageFlags {
|
|||
DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
|
||||
DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */
|
||||
DISSECT_IMAGE_VERITY_SHARE = 1 << 13, /* When activating a verity device, reuse existing one if already open */
|
||||
DISSECT_IMAGE_MKDIR = 1 << 14, /* Make directory to mount right before mounting, if missing */
|
||||
} DissectImageFlags;
|
||||
|
||||
struct DissectedImage {
|
||||
|
@ -105,6 +106,7 @@ 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, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags);
|
||||
|
||||
int dissected_image_acquire_metadata(DissectedImage *m);
|
||||
|
||||
|
|
|
@ -430,6 +430,12 @@ int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n) {
|
|||
return json_variant_new_stringn(ret, s, k);
|
||||
}
|
||||
|
||||
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
|
||||
char s[SD_ID128_STRING_MAX];
|
||||
|
||||
return json_variant_new_string(ret, sd_id128_to_string(id, s));
|
||||
}
|
||||
|
||||
static void json_variant_set(JsonVariant *a, JsonVariant *b) {
|
||||
assert(a);
|
||||
|
||||
|
@ -1964,6 +1970,17 @@ int json_variant_set_field_boolean(JsonVariant **v, const char *field, bool b) {
|
|||
return json_variant_set_field(v, field, m);
|
||||
}
|
||||
|
||||
int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *m = NULL;
|
||||
int r;
|
||||
|
||||
r = json_variant_new_array_strv(&m, l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return json_variant_set_field(v, field, m);
|
||||
}
|
||||
|
||||
int json_variant_merge(JsonVariant **v, JsonVariant *m) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
|
||||
_cleanup_free_ JsonVariant **array = NULL;
|
||||
|
@ -3579,6 +3596,34 @@ int json_buildv(JsonVariant **ret, va_list ap) {
|
|||
break;
|
||||
}
|
||||
|
||||
case _JSON_BUILD_ID128: {
|
||||
sd_id128_t id;
|
||||
|
||||
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
id = va_arg(ap, sd_id128_t);
|
||||
|
||||
if (current->n_suppress == 0) {
|
||||
r = json_variant_new_id128(&add, id);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
n_subtract = 1;
|
||||
|
||||
if (current->expect == EXPECT_TOPLEVEL)
|
||||
current->expect = EXPECT_END;
|
||||
else if (current->expect == EXPECT_OBJECT_VALUE)
|
||||
current->expect = EXPECT_OBJECT_KEY;
|
||||
else
|
||||
assert(current->expect == EXPECT_ARRAY_ELEMENT);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case _JSON_BUILD_OBJECT_BEGIN:
|
||||
|
||||
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "log.h"
|
||||
|
@ -65,6 +67,7 @@ int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n);
|
|||
int json_variant_new_array_strv(JsonVariant **ret, char **l);
|
||||
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
|
||||
int json_variant_new_null(JsonVariant **ret);
|
||||
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id);
|
||||
|
||||
static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
|
||||
return json_variant_new_stringn(ret, s, (size_t) -1);
|
||||
|
@ -183,6 +186,7 @@ int json_variant_set_field_string(JsonVariant **v, const char *field, const char
|
|||
int json_variant_set_field_integer(JsonVariant **v, const char *field, intmax_t value);
|
||||
int json_variant_set_field_unsigned(JsonVariant **v, const char *field, uintmax_t value);
|
||||
int json_variant_set_field_boolean(JsonVariant **v, const char *field, bool b);
|
||||
int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l);
|
||||
|
||||
int json_variant_append_array(JsonVariant **v, JsonVariant *element);
|
||||
|
||||
|
@ -223,6 +227,7 @@ enum {
|
|||
_JSON_BUILD_LITERAL,
|
||||
_JSON_BUILD_STRV,
|
||||
_JSON_BUILD_BASE64,
|
||||
_JSON_BUILD_ID128,
|
||||
_JSON_BUILD_MAX,
|
||||
};
|
||||
|
||||
|
@ -243,6 +248,7 @@ enum {
|
|||
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
|
||||
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
|
||||
#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
|
||||
#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, ({ sd_id128_t _x = id; _x; })
|
||||
|
||||
int json_build(JsonVariant **ret, ...);
|
||||
int json_buildv(JsonVariant **ret, va_list ap);
|
||||
|
|
|
@ -28,25 +28,25 @@ cp /usr/share/minimal.* "${image_dir}/"
|
|||
image="${image_dir}/minimal"
|
||||
roothash="$(cat ${image}.roothash)"
|
||||
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
|
||||
systemd-dissect --json=short ${image}.raw | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
|
||||
systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
|
||||
|
||||
mv ${image}.verity ${image}.fooverity
|
||||
mv ${image}.roothash ${image}.foohash
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
|
||||
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
|
||||
systemd-dissect --json=short ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
|
||||
systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
|
||||
mv ${image}.fooverity ${image}.verity
|
||||
mv ${image}.foohash ${image}.roothash
|
||||
|
||||
mkdir -p ${image_dir}/mount ${image_dir}/mount2
|
||||
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount
|
||||
systemd-dissect --mount ${image}.raw ${image_dir}/mount
|
||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
|
||||
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
|
||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
||||
# Verity volume should be shared (opened only once)
|
||||
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount2
|
||||
systemd-dissect --mount ${image}.raw ${image_dir}/mount2
|
||||
verity_count=$(ls -1 /dev/mapper/ | grep -c verity)
|
||||
# In theory we should check that count is exactly one. In practice, libdevmapper
|
||||
# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
|
||||
|
@ -111,12 +111,16 @@ dd if=${image}.raw of=${loop}p1
|
|||
dd if=${image}.verity of=${loop}p2
|
||||
losetup -d ${loop}
|
||||
|
||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1"
|
||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2"
|
||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
|
||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
|
||||
# Derive partition UUIDs from root hash, in UUID syntax
|
||||
ROOT_UUID=$(systemd-id128 -u show $(head -c 32 ${image}.roothash) -u | tail -n 1 | cut -b 6-)
|
||||
VERITY_UUID=$(systemd-id128 -u show $(tail -c 32 ${image}.roothash) -u | tail -n 1 | cut -b 6-)
|
||||
|
||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'$ROOT_UUID'","fstype":"squashfs","architecture":"x86-64","verity":"yes","node":'
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'$VERITY_UUID'","fstype":"DM_verity_hash","architecture":"x86-64","verity":null,"node":'
|
||||
systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
|
||||
systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
|
||||
|
||||
systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
|
||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
|
||||
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
|
||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
||||
|
|
Loading…
Reference in New Issue