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
|
mounting a subdir of the root fs as actual root. This can be used as
|
||||||
fstype-agnostic version of btrfs' rootflags=subvol=foobar.
|
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/
|
* 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
|
* 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,
|
* systemd-path: add ESP and XBOOTLDR path. Add "private" runtime/state/cache dir enum,
|
||||||
mapping to $RUNTIME_DIRECTORY, $STATE_DIRECTORY and such
|
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
|
* All tools that support --root= should also learn --image= so that they can
|
||||||
operate on disk images directly. Specifically: bootctl, systemctl,
|
operate on disk images directly. Specifically: bootctl, systemctl,
|
||||||
coredumpctl. (Already done: systemd-nspawn, systemd-firstboot,
|
coredumpctl. (Already done: systemd-nspawn, systemd-firstboot,
|
||||||
|
@ -1153,10 +1146,6 @@ Features:
|
||||||
- optionally automatically add FORWARD rules to iptables whenever nspawn is
|
- optionally automatically add FORWARD rules to iptables whenever nspawn is
|
||||||
running, remove them when shut down.
|
running, remove them when shut down.
|
||||||
|
|
||||||
* dissect
|
|
||||||
- refuse mounting over a mount point
|
|
||||||
- automatically discover .roothash files in dissect, similarly to nspawn
|
|
||||||
|
|
||||||
* machined:
|
* machined:
|
||||||
- add an API so that libvirt-lxc can inform us about network interfaces being
|
- add an API so that libvirt-lxc can inform us about network interfaces being
|
||||||
removed or added to an existing machine
|
removed or added to an existing machine
|
||||||
|
|
|
@ -818,6 +818,7 @@ manpages = [
|
||||||
['systemd-debug-generator', '8', [], ''],
|
['systemd-debug-generator', '8', [], ''],
|
||||||
['systemd-delta', '1', [], ''],
|
['systemd-delta', '1', [], ''],
|
||||||
['systemd-detect-virt', '1', [], ''],
|
['systemd-detect-virt', '1', [], ''],
|
||||||
|
['systemd-dissect', '1', [], ''],
|
||||||
['systemd-environment-d-generator',
|
['systemd-environment-d-generator',
|
||||||
'8',
|
'8',
|
||||||
['30-systemd-environment-d-generator'],
|
['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,
|
include_directories : includes,
|
||||||
link_with : [libshared],
|
link_with : [libshared],
|
||||||
install_rpath : rootlibexecdir,
|
install_rpath : rootlibexecdir,
|
||||||
install : true,
|
install : true)
|
||||||
install_dir : rootlibexecdir)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if conf.get('ENABLE_RESOLVE') == 1
|
if conf.get('ENABLE_RESOLVE') == 1
|
||||||
|
|
|
@ -969,6 +969,21 @@ int copy_times(int fdf, int fdt, CopyFlags flags) {
|
||||||
return 0;
|
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) {
|
int copy_xattr(int fdf, int fdt) {
|
||||||
_cleanup_free_ char *names = NULL;
|
_cleanup_free_ char *names = NULL;
|
||||||
int ret = 0, r;
|
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_times(int fdf, int fdt, CopyFlags flags);
|
||||||
|
int copy_access(int fdf, int fdt);
|
||||||
int copy_xattr(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 */
|
/* return immediately if directory exists */
|
||||||
e = strrchr(path, '/');
|
e = strrchr(path, '/');
|
||||||
if (!e)
|
if (!e)
|
||||||
return -EINVAL;
|
return 0;
|
||||||
|
|
||||||
if (e == path)
|
if (e == path)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -4,26 +4,44 @@
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <linux/loop.h>
|
#include <linux/loop.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
#include "architecture.h"
|
#include "architecture.h"
|
||||||
|
#include "copy.h"
|
||||||
#include "dissect-image.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 "hexdecoct.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "loop-util.h"
|
#include "loop-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
|
#include "mkdir.h"
|
||||||
|
#include "mount-util.h"
|
||||||
|
#include "namespace-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
|
#include "pretty-print.h"
|
||||||
|
#include "stat-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
#include "terminal-util.h"
|
||||||
|
#include "tmpfile-util.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static enum {
|
static enum {
|
||||||
ACTION_DISSECT,
|
ACTION_DISSECT,
|
||||||
ACTION_MOUNT,
|
ACTION_MOUNT,
|
||||||
|
ACTION_COPY_FROM,
|
||||||
|
ACTION_COPY_TO,
|
||||||
} arg_action = ACTION_DISSECT;
|
} arg_action = ACTION_DISSECT;
|
||||||
static const char *arg_image = NULL;
|
static const char *arg_image = NULL;
|
||||||
static const char *arg_path = 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 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 void *arg_root_hash = NULL;
|
||||||
static char *arg_verity_data = 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 char *arg_root_hash_sig_path = NULL;
|
||||||
static void *arg_root_hash_sig = NULL;
|
static void *arg_root_hash_sig = NULL;
|
||||||
static size_t arg_root_hash_sig_size = 0;
|
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_root_hash, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
|
||||||
|
|
||||||
static void help(void) {
|
static int help(void) {
|
||||||
printf("%s [OPTIONS...] IMAGE\n"
|
_cleanup_free_ char *link = NULL;
|
||||||
"%s [OPTIONS...] --mount IMAGE PATH\n"
|
int r;
|
||||||
"Dissect a file system OS image.\n\n"
|
|
||||||
" -h --help Show this help\n"
|
r = terminal_urlify_man("systemd-dissect", "1", &link);
|
||||||
" --version Show package version\n"
|
if (r < 0)
|
||||||
" -m --mount Mount the image to the specified directory\n"
|
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"
|
" -r --read-only Mount read-only\n"
|
||||||
" --fsck=BOOL Run fsck before mounting\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"
|
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
|
||||||
" --root-hash=HASH Specify root hash for verity\n"
|
" --root-hash=HASH Specify root hash for verity\n"
|
||||||
" --root-hash-sig=SIG Specify pkcs7 signature of 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"
|
" or as an ASCII base64 encoded string prefixed by\n"
|
||||||
" 'base64:'\n"
|
" 'base64:'\n"
|
||||||
" --verity-data=PATH Specify data file with hash tree for verity if it is\n"
|
" --verity-data=PATH Specify data file with hash tree for verity if it is\n"
|
||||||
" not embedded in IMAGE\n",
|
" not embedded in IMAGE\n"
|
||||||
program_invocation_short_name,
|
" --json=pretty|short|off\n"
|
||||||
program_invocation_short_name);
|
" 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[]) {
|
static int parse_argv(int argc, char *argv[]) {
|
||||||
|
@ -67,6 +109,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_FSCK,
|
ARG_FSCK,
|
||||||
ARG_VERITY_DATA,
|
ARG_VERITY_DATA,
|
||||||
ARG_ROOT_HASH_SIG,
|
ARG_ROOT_HASH_SIG,
|
||||||
|
ARG_MKDIR,
|
||||||
|
ARG_JSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -79,6 +123,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "fsck", required_argument, NULL, ARG_FSCK },
|
{ "fsck", required_argument, NULL, ARG_FSCK },
|
||||||
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
|
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
|
||||||
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
|
{ "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(argc >= 0);
|
||||||
assert(argv);
|
assert(argv);
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
|
while ((c = getopt_long(argc, argv, "hmrMxa", options, NULL)) >= 0) {
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
help();
|
return help();
|
||||||
return 0;
|
|
||||||
|
|
||||||
case ARG_VERSION:
|
case ARG_VERSION:
|
||||||
return version();
|
return version();
|
||||||
|
@ -102,6 +149,25 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
arg_action = ACTION_MOUNT;
|
arg_action = ACTION_MOUNT;
|
||||||
break;
|
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':
|
case 'r':
|
||||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||||
break;
|
break;
|
||||||
|
@ -117,7 +183,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
|
flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
|
||||||
else if (streq(optarg, "crypt"))
|
else if (streq(optarg, "crypt"))
|
||||||
flags = DISSECT_IMAGE_DISCARD_ANY;
|
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),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Unknown --discard= parameter: %s",
|
"Unknown --discard= parameter: %s",
|
||||||
optarg);
|
optarg);
|
||||||
|
@ -184,6 +256,26 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
|
SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
|
||||||
break;
|
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 '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -198,7 +290,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
case ACTION_DISSECT:
|
case ACTION_DISSECT:
|
||||||
if (optind + 1 != argc)
|
if (optind + 1 != argc)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
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_image = argv[optind];
|
||||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||||
|
@ -207,12 +299,41 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
case ACTION_MOUNT:
|
case ACTION_MOUNT:
|
||||||
if (optind + 2 != argc)
|
if (optind + 2 != argc)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
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_image = argv[optind];
|
||||||
arg_path = argv[optind + 1];
|
arg_path = argv[optind + 1];
|
||||||
break;
|
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:
|
default:
|
||||||
assert_not_reached("Unknown action.");
|
assert_not_reached("Unknown action.");
|
||||||
}
|
}
|
||||||
|
@ -220,74 +341,60 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run(int argc, char *argv[]) {
|
static int strv_pair_to_json(char **l, JsonVariant **ret) {
|
||||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
_cleanup_strv_free_ char **jl = NULL;
|
||||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
char **a, **b;
|
||||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
|
||||||
|
STRV_FOREACH_PAIR(a, b, l) {
|
||||||
|
char *j;
|
||||||
|
|
||||||
|
j = strjoin(*a, "=", *b);
|
||||||
|
if (!j)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (strv_consume(&jl, j) < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
int r;
|
||||||
|
|
||||||
log_parse_environment();
|
assert(m);
|
||||||
log_open();
|
assert(d);
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
if (!arg_json)
|
||||||
if (r <= 0)
|
printf(" Name: %s\n", basename(arg_image));
|
||||||
return r;
|
|
||||||
|
|
||||||
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &d);
|
if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
|
||||||
if (r < 0)
|
log_debug_errno(errno, "Failed to query size of loopback device: %m");
|
||||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
else if (!arg_json) {
|
||||||
|
char s[FORMAT_BYTES_MAX];
|
||||||
|
printf(" Size: %s\n", format_bytes(s, sizeof(s), size));
|
||||||
|
}
|
||||||
|
|
||||||
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
|
if (!arg_json)
|
||||||
arg_verity_data ? NULL : &arg_verity_data,
|
putc('\n', stdout);
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
r = dissected_image_acquire_metadata(m);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to acquire image metadata: %m");
|
|
||||||
|
|
||||||
|
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)
|
if (m->hostname)
|
||||||
printf(" Hostname: %s\n", m->hostname);
|
printf(" Hostname: %s\n", m->hostname);
|
||||||
|
|
||||||
|
@ -311,35 +418,381 @@ static int run(int argc, char *argv[]) {
|
||||||
p == m->os_release ? "OS Release:" : " ",
|
p == m->os_release ? "OS Release:" : " ",
|
||||||
*p, *q);
|
*p, *q);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTION_MOUNT:
|
if (arg_json) {
|
||||||
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);
|
_cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL;
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
|
if (!strv_isempty(m->machine_info)) {
|
||||||
if (r == -EUCLEAN)
|
r = strv_pair_to_json(m->machine_info, &mi);
|
||||||
return log_error_errno(r, "File system check on image failed: %m");
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to mount image: %m");
|
|
||||||
|
|
||||||
if (di) {
|
|
||||||
r = decrypted_image_relinquish(di);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to relinquish DM devices: %m");
|
return log_oom();
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_device_relinquish(d);
|
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_and_warn(m, arg_path, UID_INVALID, arg_flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert_not_reached("Unknown action.");
|
assert_not_reached("Unknown action.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_MAIN_FUNCTION(run);
|
DEFINE_MAIN_FUNCTION(run);
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
#include "namespace-util.h"
|
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-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
|
* 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. */
|
* makes sure ESP partitions and userns are compatible. */
|
||||||
|
|
||||||
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
|
r = dissected_image_mount_and_warn(
|
||||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
|
dissected_image, directory, arg_uid_shift,
|
||||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
|
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||||
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
|
||||||
if (r == -EUCLEAN)
|
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
|
||||||
return log_error_errno(r, "File system check for image failed: %m");
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to mount image root file system: %m");
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = determine_uid_shift(directory);
|
r = determine_uid_shift(directory);
|
||||||
|
|
|
@ -2826,7 +2826,7 @@ static int help(void) {
|
||||||
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
|
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
|
||||||
" --size=BYTES Grow loopback file to specified size\n"
|
" --size=BYTES Grow loopback file to specified size\n"
|
||||||
" --json=pretty|short|off\n"
|
" --json=pretty|short|off\n"
|
||||||
" Generate json output\n"
|
" Generate JSON output\n"
|
||||||
"\nSee the %s for details.\n"
|
"\nSee the %s for details.\n"
|
||||||
, program_invocation_short_name
|
, program_invocation_short_name
|
||||||
, ansi_highlight(), ansi_normal()
|
, ansi_highlight(), ansi_normal()
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
|
||||||
#include "sd-device.h"
|
#include "sd-device.h"
|
||||||
#include "sd-id128.h"
|
#include "sd-id128.h"
|
||||||
|
@ -1003,9 +1004,9 @@ static int mount_partition(
|
||||||
if (!m->found || !node || !fstype)
|
if (!m->found || !node || !fstype)
|
||||||
return 0;
|
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"))
|
if (streq_ptr(fstype, "crypto_LUKS"))
|
||||||
return -ELOOP;
|
return -EUNATCH;
|
||||||
|
|
||||||
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
|
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))
|
if (!strextend_with_separator(&options, ",", m->mount_options, NULL))
|
||||||
return -ENOMEM;
|
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);
|
r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1060,6 +1067,15 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(where);
|
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)
|
if (!m->partitions[PARTITION_ROOT].found)
|
||||||
return -ENXIO;
|
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)
|
if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
|
||||||
return 0;
|
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);
|
r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1125,6 +1145,29 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
||||||
return 0;
|
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
|
#if HAVE_LIBCRYPTSETUP
|
||||||
typedef struct DecryptedPartition {
|
typedef struct DecryptedPartition {
|
||||||
struct crypt_device *device;
|
struct crypt_device *device;
|
||||||
|
@ -1586,7 +1629,14 @@ int decrypted_image_relinquish(DecryptedImage *d) {
|
||||||
return 0;
|
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_ char *verity_filename = NULL, *roothashsig_filename = NULL;
|
||||||
_cleanup_free_ void *roothash_decoded = NULL;
|
_cleanup_free_ void *roothash_decoded = NULL;
|
||||||
size_t roothash_decoded_size = 0;
|
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_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_(rmdir_and_freep) char *t = NULL;
|
||||||
_cleanup_(sigkill_waitp) pid_t child = 0;
|
_cleanup_(sigkill_waitp) pid_t child = 0;
|
||||||
sd_id128_t machine_id = SD_ID128_NULL;
|
sd_id128_t machine_id = SD_ID128_NULL;
|
||||||
_cleanup_free_ char *hostname = NULL;
|
_cleanup_free_ char *hostname = NULL;
|
||||||
unsigned n_meta_initialized = 0, k;
|
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);
|
BLOCK_SIGNALS(SIGCHLD);
|
||||||
|
|
||||||
|
@ -1743,18 +1795,28 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
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);
|
r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
if (r == 0) {
|
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);
|
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
|
||||||
if (r < 0) {
|
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");
|
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (k = 0; k < _META_MAX; k++) {
|
for (k = 0; k < _META_MAX; k++) {
|
||||||
_cleanup_close_ int fd = -1;
|
_cleanup_close_ int fd = -ENOENT;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
fds[2*k] = safe_close(fds[2*k]);
|
fds[2*k] = safe_close(fds[2*k]);
|
||||||
|
@ -1766,12 +1828,15 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||||
}
|
}
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
|
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);
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||||
}
|
}
|
||||||
|
@ -1779,6 +1844,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_pipe[1] = safe_close(error_pipe[1]);
|
||||||
|
|
||||||
for (k = 0; k < _META_MAX; k++) {
|
for (k = 0; k < _META_MAX; k++) {
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_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);
|
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
|
||||||
child = 0;
|
child = 0;
|
||||||
if (r < 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)
|
if (r != EXIT_SUCCESS)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
|
@ -1995,11 +2071,9 @@ int mount_image_privately_interactively(
|
||||||
|
|
||||||
created_dir = TAKE_PTR(temp);
|
created_dir = TAKE_PTR(temp);
|
||||||
|
|
||||||
r = dissected_image_mount(dissected_image, created_dir, UID_INVALID, flags);
|
r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, flags);
|
||||||
if (r == -EUCLEAN)
|
|
||||||
return log_error_errno(r, "File system check on image failed: %m");
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to mount image: %m");
|
return r;
|
||||||
|
|
||||||
if (decrypted_image) {
|
if (decrypted_image) {
|
||||||
r = decrypted_image_relinquish(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_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_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_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;
|
} DissectImageFlags;
|
||||||
|
|
||||||
struct DissectedImage {
|
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(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_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(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);
|
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);
|
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) {
|
static void json_variant_set(JsonVariant *a, JsonVariant *b) {
|
||||||
assert(a);
|
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);
|
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) {
|
int json_variant_merge(JsonVariant **v, JsonVariant *m) {
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
|
||||||
_cleanup_free_ JsonVariant **array = NULL;
|
_cleanup_free_ JsonVariant **array = NULL;
|
||||||
|
@ -3579,6 +3596,34 @@ int json_buildv(JsonVariant **ret, va_list ap) {
|
||||||
break;
|
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:
|
case _JSON_BUILD_OBJECT_BEGIN:
|
||||||
|
|
||||||
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
|
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "sd-id128.h"
|
||||||
|
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "log.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_array_strv(JsonVariant **ret, char **l);
|
||||||
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
|
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
|
||||||
int json_variant_new_null(JsonVariant **ret);
|
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) {
|
static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
|
||||||
return json_variant_new_stringn(ret, s, (size_t) -1);
|
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_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_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_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);
|
int json_variant_append_array(JsonVariant **v, JsonVariant *element);
|
||||||
|
|
||||||
|
@ -223,6 +227,7 @@ enum {
|
||||||
_JSON_BUILD_LITERAL,
|
_JSON_BUILD_LITERAL,
|
||||||
_JSON_BUILD_STRV,
|
_JSON_BUILD_STRV,
|
||||||
_JSON_BUILD_BASE64,
|
_JSON_BUILD_BASE64,
|
||||||
|
_JSON_BUILD_ID128,
|
||||||
_JSON_BUILD_MAX,
|
_JSON_BUILD_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,6 +248,7 @@ enum {
|
||||||
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
|
#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_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_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_build(JsonVariant **ret, ...);
|
||||||
int json_buildv(JsonVariant **ret, va_list ap);
|
int json_buildv(JsonVariant **ret, va_list ap);
|
||||||
|
|
|
@ -28,25 +28,25 @@ cp /usr/share/minimal.* "${image_dir}/"
|
||||||
image="${image_dir}/minimal"
|
image="${image_dir}/minimal"
|
||||||
roothash="$(cat ${image}.roothash)"
|
roothash="$(cat ${image}.roothash)"
|
||||||
|
|
||||||
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
|
systemd-dissect --json=short ${image}.raw | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||||
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
|
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 ${image}.raw | grep -q -F -f /usr/lib/os-release
|
||||||
|
|
||||||
mv ${image}.verity ${image}.fooverity
|
mv ${image}.verity ${image}.fooverity
|
||||||
mv ${image}.roothash ${image}.foohash
|
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"
|
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"'
|
||||||
/usr/lib/systemd/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 "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 ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
|
||||||
mv ${image}.fooverity ${image}.verity
|
mv ${image}.fooverity ${image}.verity
|
||||||
mv ${image}.foohash ${image}.roothash
|
mv ${image}.foohash ${image}.roothash
|
||||||
|
|
||||||
mkdir -p ${image_dir}/mount ${image_dir}/mount2
|
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/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/etc/os-release | grep -q -F -f /usr/lib/os-release
|
||||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
||||||
# Verity volume should be shared (opened only once)
|
# 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)
|
verity_count=$(ls -1 /dev/mapper/ | grep -c verity)
|
||||||
# In theory we should check that count is exactly one. In practice, libdevmapper
|
# 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
|
# 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
|
dd if=${image}.verity of=${loop}p2
|
||||||
losetup -d ${loop}
|
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"
|
# Derive partition UUIDs from root hash, in UUID syntax
|
||||||
/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"
|
ROOT_UUID=$(systemd-id128 -u show $(head -c 32 ${image}.roothash) -u | tail -n 1 | cut -b 6-)
|
||||||
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
|
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} ${image}.gpt | grep -q -F -f /usr/lib/os-release
|
|
||||||
|
|
||||||
/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/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/etc/os-release | grep -q -F -f /usr/lib/os-release
|
||||||
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
|
||||||
|
|
Loading…
Reference in New Issue