]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #4835 from poettering/unit-name-printf
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 10 Dec 2016 06:29:52 +0000 (01:29 -0500)
committerGitHub <noreply@github.com>
Sat, 10 Dec 2016 06:29:52 +0000 (01:29 -0500)
Various specifier resolution fixes.

63 files changed:
.gitignore
Makefile-man.am
Makefile.am
TODO
man/machine-id.xml
man/sd_id128_get_machine.xml
man/systemd-nspawn.xml
man/systemd.exec.xml
man/systemd.network.xml
src/basic/fd-util.c
src/basic/fileio.c
src/basic/fileio.h
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/khash.c [new file with mode: 0644]
src/basic/khash.h [new file with mode: 0644]
src/basic/missing.h
src/basic/rm-rf.c
src/basic/rm-rf.h
src/basic/util.c
src/core/killall.c
src/core/manager.c
src/core/service.c
src/core/service.h
src/core/umount.c
src/cryptsetup/cryptsetup.c
src/delta/delta.c
src/dissect/Makefile [new symlink]
src/dissect/dissect.c [new file with mode: 0644]
src/libsystemd-network/network-internal.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-id128/sd-id128.c
src/libsystemd/sd-login/sd-login.c
src/libudev/libudev.c
src/login/logind-dbus.c
src/machine/image-dbus.c
src/machine/image-dbus.h
src/machine/machine-dbus.c
src/machine/machine-dbus.h
src/machine/machinectl.c
src/machine/machined-dbus.c
src/machine/org.freedesktop.machine1.conf
src/nspawn/nspawn.c
src/shared/dissect-image.c [new file with mode: 0644]
src/shared/dissect-image.h [new file with mode: 0644]
src/shared/dropin.c
src/shared/fdset.c
src/shared/gpt.h
src/shared/loop-util.c [new file with mode: 0644]
src/shared/loop-util.h [new file with mode: 0644]
src/systemd/sd-id128.h
src/test/test-dissect-image.c [new file with mode: 0644]
src/test/test-hash.c [new file with mode: 0644]
src/test/test-id128.c
src/tmpfiles/tmpfiles.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-path_id.c
src/udev/udev-node.c
src/udev/udev-rules.c
src/udev/udev-watch.c
src/udev/udevadm-info.c
test/networkd-test.py

index 21fcf9841c0439672a09ae79803997e078607ce5..016ba625e36f58fc7db18945c719f1627dda532a 100644 (file)
@@ -67,6 +67,7 @@
 /systemd-debug-generator
 /systemd-delta
 /systemd-detect-virt
+/systemd-dissect
 /systemd-escape
 /systemd-export
 /systemd-firstboot
 /test-dhcp-option
 /test-dhcp-server
 /test-dhcp6-client
+/test-dissect-image
 /test-dns-domain
 /test-dns-packet
 /test-dnssec
 /test-fs-util
 /test-fstab-util
 /test-glob-util
+/test-hash
 /test-hashmap
 /test-hexdecoct
 /test-hostname
index 013e0d796711be2d369b9189713cc0571f43d114..228e29fc4f4ef035e5353a4d563c34a178cff75f 100644 (file)
@@ -397,6 +397,7 @@ MANPAGES_ALIAS += \
        man/sd_id128_from_string.3 \
        man/sd_id128_get_boot.3 \
        man/sd_id128_get_invocation.3 \
+       man/sd_id128_get_machine_app_specific.3 \
        man/sd_id128_is_null.3 \
        man/sd_id128_t.3 \
        man/sd_is_mq.3 \
@@ -750,6 +751,7 @@ man/sd_id128_equal.3: man/sd-id128.3
 man/sd_id128_from_string.3: man/sd_id128_to_string.3
 man/sd_id128_get_boot.3: man/sd_id128_get_machine.3
 man/sd_id128_get_invocation.3: man/sd_id128_get_machine.3
+man/sd_id128_get_machine_app_specific.3: man/sd_id128_get_machine.3
 man/sd_id128_is_null.3: man/sd-id128.3
 man/sd_id128_t.3: man/sd-id128.3
 man/sd_is_mq.3: man/sd_is_fifo.3
@@ -1531,6 +1533,9 @@ man/sd_id128_get_boot.html: man/sd_id128_get_machine.html
 man/sd_id128_get_invocation.html: man/sd_id128_get_machine.html
        $(html-alias)
 
+man/sd_id128_get_machine_app_specific.html: man/sd_id128_get_machine.html
+       $(html-alias)
+
 man/sd_id128_is_null.html: man/sd-id128.html
        $(html-alias)
 
index 124e1867cde2c568869c3f76e052335c13ae6c1d..9bc18d39d6c391bf06b0cb631e5b649d3a572a05 100644 (file)
@@ -404,6 +404,11 @@ rootlibexec_PROGRAMS = \
        systemd-socket-proxyd \
        systemd-update-done
 
+if HAVE_BLKID
+rootlibexec_PROGRAMS += \
+       systemd-dissect
+endif
+
 if HAVE_UTMP
 rootlibexec_PROGRAMS += \
        systemd-update-utmp
@@ -938,7 +943,9 @@ libbasic_la_SOURCES = \
        src/basic/alloc-util.h \
        src/basic/alloc-util.c \
        src/basic/format-util.h \
-       src/basic/nss-util.h
+       src/basic/nss-util.h \
+       src/basic/khash.h \
+       src/basic/khash.c
 
 nodist_libbasic_la_SOURCES = \
        src/basic/errno-from-name.h \
@@ -1042,6 +1049,8 @@ libshared_la_SOURCES = \
        src/shared/machine-image.h \
        src/shared/machine-pool.c \
        src/shared/machine-pool.h \
+       src/shared/loop-util.c \
+       src/shared/loop-util.h \
        src/shared/resolve-util.c \
        src/shared/resolve-util.h \
        src/shared/bus-unit-util.c \
@@ -1053,7 +1062,9 @@ libshared_la_SOURCES = \
        src/shared/fdset.c \
        src/shared/fdset.h \
        src/shared/nsflags.h \
-       src/shared/nsflags.c
+       src/shared/nsflags.c \
+       src/shared/dissect-image.c \
+       src/shared/dissect-image.h
 
 if HAVE_UTMP
 libshared_la_SOURCES += \
@@ -1076,7 +1087,9 @@ libshared_la_CFLAGS = \
        $(AM_CFLAGS) \
        $(ACL_CFLAGS) \
        $(LIBIDN_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(BLKID_CFLAGS) \
+       $(LIBCRYPTSETUP_CFLAGS)
 
 libshared_la_LIBADD = \
        libsystemd-internal.la \
@@ -1085,7 +1098,9 @@ libshared_la_LIBADD = \
        libudev-internal.la \
        $(ACL_LIBS) \
        $(LIBIDN_LIBS) \
-       $(SECCOMP_LIBS)
+       $(SECCOMP_LIBS) \
+       $(BLKID_LIBS) \
+       $(LIBCRYPTSETUP_LIBS)
 
 rootlibexec_LTLIBRARIES += \
        libsystemd-shared.la
@@ -1107,6 +1122,8 @@ libsystemd_shared_la_CFLAGS = \
        $(ACL_CFLAGS) \
        $(LIBIDN_CFLAGS) \
        $(SECCOMP_CFLAGS) \
+       $(BLKID_CFLAGS) \
+       $(LIBCRYPTSETUP_CFLAGS) \
        -fvisibility=default
 
 # We can't use libshared_la_LIBADD here because it would
@@ -1118,7 +1135,9 @@ libsystemd_shared_la_LIBADD = \
        $(libudev_internal_la_LIBADD) \
        $(ACL_LIBS) \
        $(LIBIDN_LIBS) \
-       $(SECCOMP_LIBS)
+       $(SECCOMP_LIBS) \
+       $(BLKID_LIBS) \
+       $(LIBCRYPTSETUP_LIBS)
 
 libsystemd_shared_la_LDFLAGS = \
        $(AM_LDFLAGS) \
@@ -1456,7 +1475,8 @@ manual_tests += \
        test-btrfs \
        test-acd \
        test-ipv4ll-manual \
-       test-ask-password-api
+       test-ask-password-api \
+       test-dissect-image
 
 unsafe_tests = \
        test-hostname \
@@ -2067,6 +2087,17 @@ test_ask_password_api_SOURCES = \
 test_ask_password_api_LDADD = \
        libsystemd-shared.la
 
+test_dissect_image_SOURCES = \
+       src/test/test-dissect-image.c
+
+test_dissect_image_CFLAGS = \
+       $(AM_CFLAGS) \
+       $(BLKID_CFLAGS)
+
+test_dissect_image_LDADD = \
+       libsystemd-shared.la \
+       $(BLKID_LIBS)
+
 test_signal_util_SOURCES = \
        src/test/test-signal-util.c
 
@@ -3053,6 +3084,13 @@ systemd_notify_SOURCES = \
 systemd_notify_LDADD = \
        libsystemd-shared.la
 
+# ------------------------------------------------------------------------------
+systemd_dissect_SOURCES = \
+       src/dissect/dissect.c
+
+systemd_dissect_LDADD = \
+       libsystemd-shared.la
+
 # ------------------------------------------------------------------------------
 systemd_path_SOURCES = \
        src/path/path.c
@@ -4045,6 +4083,16 @@ test_id128_LDADD = \
 tests += \
        test-id128
 
+# ------------------------------------------------------------------------------
+test_hash_SOURCES = \
+       src/test/test-hash.c
+
+test_hash_LDADD = \
+       libsystemd-shared.la
+
+tests += \
+       test-hash
+
 # ------------------------------------------------------------------------------
 
 bin_PROGRAMS += \
diff --git a/TODO b/TODO
index d89242ec6ecf7ecf55892bc34da7c03008e33b29..a4cad5757fc83652ca7bb5a44c954a9d31e32d5a 100644 (file)
--- a/TODO
+++ b/TODO
@@ -23,8 +23,6 @@ External:
 
 Janitorial Clean-ups:
 
-* replace manual readdir() loops with FOREACH_DIRENT or FOREACH_DIRENT_ALL
-
 * Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again
 
 Features:
index a722649de4e27135a5e5e77259b270a700a90d0b..3c261bffcc3856d585d41c3fe6ba3f0f6e844f1f 100644 (file)
     <para>This machine ID adheres to the same format and logic as the
     D-Bus machine ID.</para>
 
-    <para>This ID uniquely identifies the host. It should be considered "confidential", and must not
-    be exposed in untrusted environments, in particular on the network. If a stable unique
-    identifier that is tied to the machine is needed for some application, the machine ID or any
-    part of it must not be used directly. Instead the machine ID should be hashed with a
-    cryptographic, keyed hash function, using a fixed, application-specific key. That way the ID
-    will be properly unique, and derived in a constant way from the machine ID but there will be no
-    way to retrieve the original machine ID from the application-specific one.</para>
+    <para>This ID uniquely identifies the host. It should be considered "confidential", and must not be exposed in
+    untrusted environments, in particular on the network. If a stable unique identifier that is tied to the machine is
+    needed for some application, the machine ID or any part of it must not be used directly. Instead the machine ID
+    should be hashed with a cryptographic, keyed hash function, using a fixed, application-specific key. That way the
+    ID will be properly unique, and derived in a constant way from the machine ID but there will be no way to retrieve
+    the original machine ID from the application-specific one. The
+    <citerefentry><refentrytitle>sd_id128_get_machine_app_specific</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    API provides an implementation of such an algorithm.</para>
 
     <para>The
     <citerefentry><refentrytitle>systemd-machine-id-setup</refentrytitle><manvolnum>1</manvolnum></citerefentry>
index 9a86c24aed1b5d824d6c6810c17717b9010fe3d0..3938c6d836c199633b84423489d5e1a3697bfb62 100644 (file)
@@ -44,6 +44,7 @@
 
   <refnamediv>
     <refname>sd_id128_get_machine</refname>
+    <refname>sd_id128_get_machine_app_specific</refname>
     <refname>sd_id128_get_boot</refname>
     <refname>sd_id128_get_invocation</refname>
     <refpurpose>Retrieve 128-bit IDs</refpurpose>
         <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_id128_get_machine_app_specific</function></funcdef>
+        <paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
+        <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_id128_get_boot</function></funcdef>
         <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_id128_get_machine()</function> returns the
-    machine ID of the executing host. This reads and parses the
-    <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-    file. This function caches the machine ID internally to make
-    retrieving the machine ID a cheap operation.</para>
+    <para><function>sd_id128_get_machine()</function> returns the machine ID of the executing host. This reads and
+    parses the <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    file. This function caches the machine ID internally to make retrieving the machine ID a cheap operation. This ID
+    may be used wherever a unique identifier for the local system is needed. However, it is recommended to use this ID
+    as-is only in trusted environments. In untrusted environments it is recommended to derive an application specific
+    ID from this machine ID, in an irreversable (cryptographically secure) way. To make this easy
+    <function>sd_id128_get_machine_app_specific()</function> is provided, see below.</para>
+
+    <para><function>sd_id128_get_machine_app_specific()</function> is similar to
+    <function>sd_id128_get_machine()</function>, but retrieves a machine ID that is specific to the application that is
+    identified by the indicated application ID. It is recommended to use this function instead of
+    <function>sd_id128_get_machine()</function> when passing an ID to untrusted environments, in order to make sure
+    that the original machine ID may not be determined externally. The application-specific ID should be generated via
+    a tool like <command>journalctl --new-id128</command>, and may be compiled into the application. This function will
+    return the same application-specific ID for each combination of machine ID and application ID. Internally, this
+    function calculates HMAC-SHA256 of the application ID, keyed by the machine ID.</para>
 
     <para><function>sd_id128_get_boot()</function> returns the boot ID
     of the executing kernel. This reads and parses the
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
     ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
 
-    <para>Note that <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> always
-    return UUID v4 compatible IDs.  <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
-    ID on new installations but might not on older.  It is possible to convert the machine ID into a UUID v4-compatible
-    one. For more information, see
+    <para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>
+    and <function>sd_id128_get_invocation()</function> always return UUID v4 compatible IDs.
+    <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible ID on new installations but might
+    not on older.  It is possible to convert the machine ID into a UUID v4-compatible one. For more information, see
     <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
     <para>For more information about the <literal>sd_id128_t</literal>
   <refsect1>
     <title>Notes</title>
 
-    <para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_boot()</function> and
-    <function>sd_id128_get_invocation()</function> interfaces are available as a shared library, which can be compiled
-    and linked to with the <literal>libsystemd</literal> <citerefentry
+    <para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_machine_app_specific()</function>
+    <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> interfaces are
+    available as a shared library, which can be compiled and linked to with the
+    <literal>libsystemd</literal> <citerefentry
     project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Application-specific machine ID</title>
+
+      <para>Here's a simple example for an application specific machine ID:</para>
+
+      <programlisting>#include &lt;systemd/sd-id128.h&gt;
+#include &lt;stdio.h&gt;
+
+#define OUR_APPLICATION_ID SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97)
+
+int main(int argc, char *argv[]) {
+        sd_id128_t id;
+        sd_id128_get_machine_app_specific(OUR_APPLICATION_ID, &amp;id);
+        printf("Our application ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
+        return 0;
+}</programlisting>
+    </example>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
 
index cd0a90d82f3fe6dcbccac5f0b2337be1aa9fff28..2bc81ea1aa285e6ada0780cc57c83c98c2865b23 100644 (file)
           identified by the partition types defined by the <ulink
           url="http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable
           Partitions Specification</ulink>.</para></listitem>
+
+          <listitem><para>No partition table, and a single file system spanning the whole image.</para></listitem>
         </itemizedlist>
 
         <para>On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to
         <filename>/efi</filename> (or <filename>/boot</filename> as fallback) in case a directory by this name exists
         and is empty.</para>
 
+        <para>Partitions encrypted with LUKS are automatically decrypted. Also, on GPT images dm-verity data integrity
+        hash partitions are set up if the root hash for them is specified using the <option>--root-hash=</option>
+        option.</para>
+
         <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
         together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--root-hash=</option></term>
+
+        <listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data
+        integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The
+        specified hash must match the root hash of integrity data, and is usually at least 256bits (and hence 64
+        hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but a file with
+        the <filename>.roothash</filename> suffix is found next to the image file, bearing otherwise the same name the
+        root hash is read from it and automatically used.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-a</option></term>
         <term><option>--as-pid2</option></term>
index ab83876eba5fd277844463fea97843f3830db1aa..f27e4a5c04cc17a3f332e966f5783bd0ceea788c 100644 (file)
           <title>Summary of possible service result variable values</title>
           <tgroup cols='3'>
             <colspec colname='result' />
-            <colspec colname='status' />
             <colspec colname='code' />
+            <colspec colname='status' />
             <thead>
               <row>
                 <entry><varname>$SERVICE_RESULT</varname></entry>
-                <entry><varname>$EXIT_STATUS</varname></entry>
                 <entry><varname>$EXIT_CODE</varname></entry>
+                <entry><varname>$EXIT_STATUS</varname></entry>
               </row>
             </thead>
 
             <tbody>
+              <row>
+                <entry morerows="1" valign="top"><literal>protocol</literal></entry>
+                <entry valign="top">not set</entry>
+                <entry>not set</entry>
+              </row>
+              <row>
+                <entry><literal>exited</literal></entry>
+                <entry><literal>0</literal></entry>
+              </row>
+
               <row>
                 <entry morerows="1" valign="top"><literal>timeout</literal></entry>
                 <entry valign="top"><literal>killed</literal></entry>
                 <entry><literal>TERM</literal>, <literal>KILL</literal></entry>
               </row>
-
               <row>
                 <entry valign="top"><literal>exited</literal></entry>
                 <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal
index 53c49f817fd79771add7afcae5e9c257f546e575..0fa68b7623f9ca5da06a0ba468a7ae5af88aa29c 100644 (file)
           <listitem>
             <para>A whitespace-separated list of shell-style globs
             matching the persistent path, as exposed by the udev
-            property <literal>ID_PATH</literal>.</para>
+            property <literal>ID_PATH</literal>. If the list is
+            prefixed with a "!", the test is inverted; i.e. it is
+            true when <literal>ID_PATH</literal> does not match any
+            item in the list.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             exposed by the udev property <literal>DRIVER</literal>
             of its parent device, or if that is not set the driver
             as exposed by <literal>ethtool -i</literal> of the
-            device itself.</para>
+            device itself. If the list is prefixed with a "!", the
+            test is inverted.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <listitem>
             <para>A whitespace-separated list of shell-style globs
             matching the device type, as exposed by the udev property
-            <literal>DEVTYPE</literal>.</para>
+            <literal>DEVTYPE</literal>. If the list is prefixed with
+            a "!", the test is inverted.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <listitem>
             <para>A whitespace-separated list of shell-style globs
             matching the device name, as exposed by the udev property
-            <literal>INTERFACE</literal>.</para>
+            <literal>INTERFACE</literal>. If the list is prefixed
+            with a "!", the test is inverted.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
index 5c820332a5dd1417663c31a4f269a3d5c440b796..19ad20789b7cb9d8e2f558f16d4768576b8510e5 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "macro.h"
@@ -234,12 +235,9 @@ int close_all_fds(const int except[], unsigned n_except) {
                 return r;
         }
 
-        while ((de = readdir(d))) {
+        FOREACH_DIRENT(de, d, return -errno) {
                 int fd = -1;
 
-                if (hidden_or_backup_file(de->d_name))
-                        continue;
-
                 if (safe_atoi(de->d_name, &fd) < 0)
                         /* Let's better ignore this, just in case */
                         continue;
index 1615456659ef29b6a3d6507516ea6574e6315fc2..c43b0583a4a93e6f28658d9de84ae063e4efc98b 100644 (file)
@@ -1409,3 +1409,22 @@ int read_nul_string(FILE *f, char **ret) {
 
         return 0;
 }
+
+int mkdtemp_malloc(const char *template, char **ret) {
+        char *p;
+
+        assert(template);
+        assert(ret);
+
+        p = strdup(template);
+        if (!p)
+                return -ENOMEM;
+
+        if (!mkdtemp(p)) {
+                free(p);
+                return -errno;
+        }
+
+        *ret = p;
+        return 0;
+}
index b58c83e64ad501a7322aeff754e2b9f7239cf1fd..17b38a5d60e5567a8fb6e9ea379de644676eabe1 100644 (file)
@@ -88,3 +88,5 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
 int link_tmpfile(int fd, const char *path, const char *target);
 
 int read_nul_string(FILE *f, char **ret);
+
+int mkdtemp_malloc(const char *template, char **ret);
index b30cec4f925154ae96d7142441b12d1a5887226c..5b232691090acbb405d7ba137d56d6652af05a32 100644 (file)
@@ -17,7 +17,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -446,6 +445,7 @@ int mkfifo_atomic(const char *path, mode_t mode) {
 
 int get_files_in_directory(const char *path, char ***list) {
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
         size_t bufsize = 0, n = 0;
         _cleanup_strv_free_ char **l = NULL;
 
@@ -459,16 +459,7 @@ int get_files_in_directory(const char *path, char ***list) {
         if (!d)
                 return -errno;
 
-        for (;;) {
-                struct dirent *de;
-
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno > 0)
-                        return -errno;
-                if (!de)
-                        break;
-
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 dirent_ensure_type(d, de);
 
                 if (!dirent_is_file(de))
index 0d925c6b84db82250601e3a21fe6cc1cfc5d8c04..5fe5c71ff029cc953b669fd2e0d3ac427f14dd13 100644 (file)
@@ -84,3 +84,10 @@ enum {
 };
 
 int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
+
+/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
+static inline void rmdir_and_free(char *p) {
+        (void) rmdir(p);
+        free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
diff --git a/src/basic/khash.c b/src/basic/khash.c
new file mode 100644 (file)
index 0000000..9a2a3ed
--- /dev/null
@@ -0,0 +1,275 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/if_alg.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "khash.h"
+#include "macro.h"
+#include "missing.h"
+#include "string-util.h"
+#include "util.h"
+
+/* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
+ * let's add some extra room, the few wasted bytes don't really matter... */
+#define LONGEST_DIGEST 128
+
+struct khash {
+        int fd;
+        char *algorithm;
+        uint8_t digest[LONGEST_DIGEST+1];
+        size_t digest_size;
+        bool digest_valid;
+};
+
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
+        union {
+                struct sockaddr sa;
+                struct sockaddr_alg alg;
+        } sa = {
+                .alg.salg_family = AF_ALG,
+                .alg.salg_type = "hash",
+        };
+
+        _cleanup_(khash_unrefp) khash *h = NULL;
+        _cleanup_close_ int fd = -1;
+        ssize_t n;
+
+        assert(ret);
+        assert(key || key_size == 0);
+
+        /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
+        if (isempty(algorithm))
+                return -EINVAL;
+
+        /* Overly long hash algorithm names we definitely do not support */
+        if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
+                return -EOPNOTSUPP;
+
+        fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return -errno;
+
+        strcpy((char*) sa.alg.salg_name, algorithm);
+        if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
+                if (errno == ENOENT)
+                        return -EOPNOTSUPP;
+                return -errno;
+        }
+
+        if (key) {
+                if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
+                        return -errno;
+        }
+
+        h = new0(khash, 1);
+        if (!h)
+                return -ENOMEM;
+
+        h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
+        if (h->fd < 0)
+                return -errno;
+
+        h->algorithm = strdup(algorithm);
+        if (!h->algorithm)
+                return -ENOMEM;
+
+        /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+        (void) send(h->fd, NULL, 0, 0);
+
+        /* Figure out the digest size */
+        n = recv(h->fd, h->digest, sizeof(h->digest), 0);
+        if (n < 0)
+                return -errno;
+        if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
+                return -EOPNOTSUPP;
+
+        h->digest_size = (size_t) n;
+        h->digest_valid = true;
+
+        /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+        (void) send(h->fd, NULL, 0, 0);
+
+        *ret = h;
+        h = NULL;
+
+        return 0;
+}
+
+int khash_new(khash **ret, const char *algorithm) {
+        return khash_new_with_key(ret, algorithm, NULL, 0);
+}
+
+khash* khash_unref(khash *h) {
+        if (!h)
+                return NULL;
+
+        safe_close(h->fd);
+        free(h->algorithm);
+        free(h);
+
+        return NULL;
+}
+
+int khash_dup(khash *h, khash **ret) {
+        _cleanup_(khash_unrefp) khash *copy = NULL;
+
+        assert(h);
+        assert(ret);
+
+        copy = newdup(khash, h, 1);
+        if (!copy)
+                return -ENOMEM;
+
+        copy->fd = -1;
+        copy->algorithm = strdup(h->algorithm);
+        if (!copy)
+                return -ENOMEM;
+
+        copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
+        if (copy->fd < 0)
+                return -errno;
+
+        *ret = copy;
+        copy = NULL;
+
+        return 0;
+}
+
+const char *khash_get_algorithm(khash *h) {
+        assert(h);
+
+        return h->algorithm;
+}
+
+size_t khash_get_size(khash *h) {
+        assert(h);
+
+        return h->digest_size;
+}
+
+int khash_reset(khash *h) {
+        ssize_t n;
+
+        assert(h);
+
+        n = send(h->fd, NULL, 0, 0);
+        if (n < 0)
+                return -errno;
+
+        h->digest_valid = false;
+
+        return 0;
+}
+
+int khash_put(khash *h, const void *buffer, size_t size) {
+        ssize_t n;
+
+        assert(h);
+        assert(buffer || size == 0);
+
+        if (size <= 0)
+                return 0;
+
+        n = send(h->fd, buffer, size, MSG_MORE);
+        if (n < 0)
+                return -errno;
+
+        h->digest_valid = false;
+
+        return 0;
+}
+
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
+        struct msghdr mh = {
+                mh.msg_iov = (struct iovec*) iovec,
+                mh.msg_iovlen = n,
+        };
+        ssize_t k;
+
+        assert(h);
+        assert(iovec || n == 0);
+
+        if (n <= 0)
+                return 0;
+
+        k = sendmsg(h->fd, &mh, MSG_MORE);
+        if (k < 0)
+                return -errno;
+
+        h->digest_valid = false;
+
+        return 0;
+}
+
+static int retrieve_digest(khash *h) {
+        ssize_t n;
+
+        assert(h);
+
+        if (h->digest_valid)
+                return 0;
+
+        n = recv(h->fd, h->digest, h->digest_size, 0);
+        if (n < 0)
+                return n;
+        if ((size_t) n != h->digest_size) /* digest size changed? */
+                return -EIO;
+
+        h->digest_valid = true;
+
+        return 0;
+}
+
+int khash_digest_data(khash *h, const void **ret) {
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        r = retrieve_digest(h);
+        if (r < 0)
+                return r;
+
+        *ret = h->digest;
+        return 0;
+}
+
+int khash_digest_string(khash *h, char **ret) {
+        int r;
+        char *p;
+
+        assert(h);
+        assert(ret);
+
+        r = retrieve_digest(h);
+        if (r < 0)
+                return r;
+
+        p = hexmem(h->digest, h->digest_size);
+        if (!p)
+                return -ENOMEM;
+
+        *ret = p;
+        return 0;
+}
diff --git a/src/basic/khash.h b/src/basic/khash.h
new file mode 100644 (file)
index 0000000..f404a68
--- /dev/null
@@ -0,0 +1,53 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "macro.h"
+
+typedef struct khash khash;
+
+/* For plain hash functions. Hash functions commonly supported on today's kernels are: crc32c, crct10dif, crc32,
+ * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more.*/
+int khash_new(khash **ret, const char *algorithm);
+
+/* For keyed hash functions. Hash functions commonly supported on today's kernels are: hmac(sha256), cmac(aes),
+ * cmac(des3_ede), hmac(sha3-512), hmac(sha3-384), hmac(sha3-256), hmac(sha3-224), hmac(rmd160), hmac(rmd128),
+ * hmac(sha224), hmac(sha512), hmac(sha384), hmac(sha1), hmac(md5), and more. */
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size);
+
+int khash_dup(khash *h, khash **ret);
+khash* khash_unref(khash *h);
+
+const char *khash_get_algorithm(khash *h);
+size_t khash_get_size(khash *h);
+
+int khash_reset(khash *h);
+
+int khash_put(khash *h, const void *buffer, size_t size);
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n);
+
+int khash_digest_data(khash *h, const void **ret);
+int khash_digest_string(khash *h, char **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(khash*, khash_unref);
index 8833617dc6c9079c4370b2713c67803fe9026d6c..1502b3f4f43f3876222db252c0397c10208c3a9d 100644 (file)
@@ -1109,4 +1109,8 @@ struct ethtool_link_settings {
 
 #endif
 
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
 #include "missing_syscall.h"
index baa70c2c8d136a0de6cc3f04d0f13346856aa2fa..07d42f78dd20926b22123acb08b024f3af3560f1 100644 (file)
@@ -17,7 +17,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -28,6 +27,7 @@
 
 #include "btrfs-util.h"
 #include "cgroup-util.h"
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "log.h"
 #include "macro.h"
@@ -43,6 +43,7 @@ static bool is_physical_fs(const struct statfs *sfs) {
 
 int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
         int ret = 0, r;
         struct statfs sfs;
 
@@ -78,19 +79,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                 return errno == ENOENT ? 0 : -errno;
         }
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 bool is_dir;
                 struct stat st;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de) {
-                        if (errno > 0 && ret == 0)
-                                ret = -errno;
-                        return ret;
-                }
-
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
@@ -178,6 +170,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                         }
                 }
         }
+        return ret;
 }
 
 int rm_rf(const char *path, RemoveFlags flags) {
index f693a5bb7c52bbadbf8424f9edde4d8f65952b02..e13f7003e3e73d515466ff91bfc15e9be58376fc 100644 (file)
@@ -33,8 +33,6 @@ int rm_rf(const char *path, RemoveFlags flags);
 
 /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
 static inline void rm_rf_physical_and_free(char *p) {
-        if (!p)
-                return;
         (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
         free(p);
 }
index c1b5ca1ef7b7d6f9c0b0717be62746edb2787a34..8a630049d7c26ac313c802be93e0972bd4953d30 100644 (file)
@@ -18,7 +18,6 @@
 ***/
 
 #include <alloca.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sched.h>
@@ -508,28 +507,17 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
 int on_ac_power(void) {
         bool found_offline = false, found_online = false;
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
 
         d = opendir("/sys/class/power_supply");
         if (!d)
                 return errno == ENOENT ? true : -errno;
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT(de, d, return -errno) {
                 _cleanup_close_ int fd = -1, device = -1;
                 char contents[6];
                 ssize_t n;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno > 0)
-                        return -errno;
-
-                if (!de)
-                        break;
-
-                if (hidden_or_backup_file(de->d_name))
-                        continue;
-
                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
                 if (device < 0) {
                         if (errno == ENOENT || errno == ENOTDIR)
index 3bc19e9c842ba802f83d9acbb9dde998f9461d8a..b3aa22adc55d7af5a5d882e9fc1240d65f066fc8 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "alloc-util.h"
 #include "def.h"
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "killall.h"
@@ -172,7 +173,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
         if (!dir)
                 return -errno;
 
-        while ((d = readdir(dir))) {
+        FOREACH_DIRENT_ALL(d, dir, break) {
                 pid_t pid;
                 int r;
 
index 1f663d3c1df3a1a73b332d7fbacfa7ea8eb02634..21cd6062c66fef2056d389d8fdb23b0c020a5f87 100644 (file)
@@ -17,7 +17,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/kd.h>
@@ -233,6 +232,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
 
 static int have_ask_password(void) {
         _cleanup_closedir_ DIR *dir;
+        struct dirent *de;
 
         dir = opendir("/run/systemd/ask-password");
         if (!dir) {
@@ -242,19 +242,11 @@ static int have_ask_password(void) {
                         return -errno;
         }
 
-        for (;;) {
-                struct dirent *de;
-
-                errno = 0;
-                de = readdir(dir);
-                if (!de && errno > 0)
-                        return -errno;
-                if (!de)
-                        return false;
-
+        FOREACH_DIRENT_ALL(de, dir, return -errno) {
                 if (startswith(de->d_name, "ask."))
                         return true;
         }
+        return false;
 }
 
 static int manager_dispatch_ask_password_fd(sd_event_source *source,
index d5b8decc2042645960b3ce08b8d55c86bbe06d0a..576416ad29117cc6baf4fadb4f27d546198ade6e 100644 (file)
@@ -1709,7 +1709,7 @@ static void service_enter_running(Service *s, ServiceResult f) {
                 }
 
         } else if (f != SERVICE_SUCCESS)
-                service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
         else if (s->remain_after_exit)
                 service_set_state(s, SERVICE_EXITED);
         else
@@ -1846,7 +1846,7 @@ static void service_enter_start(Service *s) {
 
 fail:
         log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m");
-        service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+        service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
 }
 
 static void service_enter_start_pre(Service *s) {
@@ -1992,9 +1992,7 @@ static void service_run_next_control(Service *s) {
 fail:
         log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m");
 
-        if (s->state == SERVICE_START_PRE)
-                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
-        else if (s->state == SERVICE_STOP)
+        if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_STOP))
                 service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
         else if (s->state == SERVICE_STOP_POST)
                 service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
@@ -2595,7 +2593,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
         case SERVICE_START:
                 if (s->type == SERVICE_NOTIFY) {
                         /* No chance of getting a ready notification anymore */
-                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+                        service_enter_stop_post(s, SERVICE_FAILURE_PROTOCOL);
                         break;
                 }
 
@@ -2608,7 +2606,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
 
                         service_unwatch_pid_file(s);
                         if (s->state == SERVICE_START)
-                                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+                                service_enter_stop_post(s, SERVICE_FAILURE_PROTOCOL);
                         else
                                 service_enter_stop(s, SERVICE_FAILURE_PROTOCOL);
                 }
@@ -2742,17 +2740,17 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         if (f == SERVICE_SUCCESS)
                                                 service_enter_start_post(s);
                                         else
-                                                service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                                                service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
                                         break;
                                 } else if (s->type == SERVICE_NOTIFY) {
                                         /* Only enter running through a notification, so that the
                                          * SERVICE_START state signifies that no ready notification
                                          * has been received */
                                         if (f != SERVICE_SUCCESS)
-                                                service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                                                service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
                                         else if (!s->remain_after_exit)
                                                 /* The service has never been active */
-                                                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+                                                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
                                         break;
                                 }
 
@@ -2832,7 +2830,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 if (f == SERVICE_SUCCESS)
                                         service_enter_start(s);
                                 else
-                                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                                        service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
                                 break;
 
                         case SERVICE_START:
@@ -2841,7 +2839,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         break;
 
                                 if (f != SERVICE_SUCCESS) {
-                                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                                        service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
                                         break;
                                 }
 
@@ -2858,7 +2856,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         if (!has_start_post && r < 0) {
                                                 r = service_demand_pid_file(s);
                                                 if (r < 0 || !cgroup_good(s))
-                                                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+                                                        service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
                                                 break;
                                         }
                                 } else
@@ -2954,7 +2952,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
         case SERVICE_START_PRE:
         case SERVICE_START:
                 log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre");
-                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
                 break;
 
         case SERVICE_START_POST:
index e09722a952fd7da5148efb0c86049eef53afc504..ff9cfaeb88e7498808c6e3f32409578387bca61b 100644 (file)
@@ -79,6 +79,8 @@ typedef enum NotifyState {
         _NOTIFY_STATE_INVALID = -1
 } NotifyState;
 
+/* The values of this enum are referenced in man/systemd.exec.xml and src/shared/bus-unit-util.c.
+ * Update those sources for each change to this enum. */
 typedef enum ServiceResult {
         SERVICE_SUCCESS,
         SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
index 1e5459ed80c83ba532d7d6bd301d4d335d71b40e..2f4b12bdb9ca29a117cf8711e29fe04226238c56 100644 (file)
@@ -344,24 +344,29 @@ static int delete_loopback(const char *device) {
 }
 
 static int delete_dm(dev_t devnum) {
-        _cleanup_close_ int fd = -1;
-        int r;
+
         struct dm_ioctl dm = {
-                .version = {DM_VERSION_MAJOR,
-                            DM_VERSION_MINOR,
-                            DM_VERSION_PATCHLEVEL},
+                .version = {
+                        DM_VERSION_MAJOR,
+                        DM_VERSION_MINOR,
+                        DM_VERSION_PATCHLEVEL
+                },
                 .data_size = sizeof(dm),
                 .dev = devnum,
         };
 
+        _cleanup_close_ int fd = -1;
+
         assert(major(devnum) != 0);
 
         fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
-        r = ioctl(fd, DM_DEV_REMOVE, &dm);
-        return r >= 0 ? 0 : -errno;
+        if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
+                return -errno;
+
+        return 0;
 }
 
 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
index 01e7ee9973e605dd3c537865196d53228bea0ef4..c7fec609dfde4ebb1b30e6a663213f7fb0b4d3bf 100644 (file)
@@ -651,7 +651,7 @@ int main(int argc, char *argv[]) {
                         k = crypt_init(&cd, arg_header);
                 } else
                         k = crypt_init(&cd, argv[3]);
-                if (k) {
+                if (k != 0) {
                         log_error_errno(k, "crypt_init() failed: %m");
                         goto finish;
                 }
index 107b105fde8f99288df129c737124e6607f3dd45..9a44b15da71d238ebbf49ffa9a91f77829cbbb41 100644 (file)
@@ -297,6 +297,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
 
 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
         _cleanup_closedir_ DIR *d;
+        struct dirent *de;
 
         assert(top);
         assert(bottom);
@@ -313,16 +314,10 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
                 return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 int k;
                 char *p;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de)
-                        return -errno;
-
                 dirent_ensure_type(d, de);
 
                 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
@@ -354,6 +349,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
                         return k;
                 }
         }
+        return 0;
 }
 
 static int should_skip_prefix(const char* p) {
diff --git a/src/dissect/Makefile b/src/dissect/Makefile
new file mode 120000 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
new file mode 100644 (file)
index 0000000..e3c96b7
--- /dev/null
@@ -0,0 +1,271 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include "architecture.h"
+#include "dissect-image.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "loop-util.h"
+#include "string-util.h"
+#include "util.h"
+
+static enum {
+        ACTION_DISSECT,
+        ACTION_MOUNT,
+} arg_action = ACTION_DISSECT;
+static const char *arg_image = NULL;
+static const char *arg_path = NULL;
+static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
+
+static void help(void) {
+        printf("%s [OPTIONS...] IMAGE\n"
+               "%s [OPTIONS...] --mount IMAGE PATH\n"
+               "Dissect a file system OS image.\n\n"
+               "  -h --help            Show this help\n"
+               "     --version         Show package version\n"
+               "  -m --mount           Mount the image to the specified directory\n"
+               "  -r --read-only       Mount read-only\n"
+               "     --discard=MODE    Choose 'discard' mode (disabled, loop, all, crypto)\n"
+               "     --root-hash=HASH  Specify root hash for verity\n",
+               program_invocation_short_name,
+               program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_DISCARD,
+                ARG_ROOT_HASH,
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "mount",     no_argument,       NULL, 'm'           },
+                { "read-only", no_argument,       NULL, 'r'           },
+                { "discard",   required_argument, NULL, ARG_DISCARD   },
+                { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'm':
+                        arg_action = ACTION_MOUNT;
+                        break;
+
+                case 'r':
+                        arg_flags |= DISSECT_IMAGE_READ_ONLY;
+                        break;
+
+                case ARG_DISCARD:
+                        if (streq(optarg, "disabled"))
+                                arg_flags &= ~(DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO);
+                        else if (streq(optarg, "loop"))
+                                arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP;
+                        else if (streq(optarg, "all"))
+                                arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
+                        else if (streq(optarg, "crypt"))
+                                arg_flags |= DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD | DISSECT_IMAGE_DISCARD_ON_CRYPTO;
+                        else {
+                                log_error("Unknown --discard= parameter: %s", optarg);
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case ARG_ROOT_HASH: {
+                        void *p;
+                        size_t l;
+
+                        r = unhexmem(optarg, strlen(optarg), &p, &l);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+                        if (l < sizeof(sd_id128_t)) {
+                                log_error("Root hash must be at least 128bit long: %s", optarg);
+                                free(p);
+                                return -EINVAL;
+                        }
+
+                        free(arg_root_hash);
+                        arg_root_hash = p;
+                        arg_root_hash_size = l;
+                        break;
+                }
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+
+        }
+
+        switch (arg_action) {
+
+        case ACTION_DISSECT:
+                if (optind + 1 != argc) {
+                        log_error("Expected a file path as only argument.");
+                        return -EINVAL;
+                }
+
+                arg_image = argv[optind];
+                arg_flags |= DISSECT_IMAGE_READ_ONLY;
+                break;
+
+        case ACTION_MOUNT:
+                if (optind + 2 != argc) {
+                        log_error("Expected a file path and mount point path as only arguments.");
+                        return -EINVAL;
+                }
+
+                arg_image = argv[optind];
+                arg_path = argv[optind + 1];
+                break;
+
+        default:
+                assert_not_reached("Unknown action.");
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+        int r;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                goto finish;
+
+        r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
+        if (r < 0) {
+                log_error_errno(r, "Failed to set up loopback device: %m");
+                goto finish;
+        }
+
+        r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
+        if (r == -ENOPKG) {
+                log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
+                goto finish;
+        }
+        if (r == -EADDRNOTAVAIL) {
+                log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
+                goto finish;
+        }
+        if (r < 0) {
+                log_error_errno(r, "Failed to dissect image: %m");
+                goto finish;
+        }
+
+        switch (arg_action) {
+
+        case ACTION_DISSECT: {
+                unsigned i;
+
+                for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                        DissectedPartition *p = m->partitions + i;
+                        int k;
+
+                        if (!p->found)
+                                continue;
+
+                        printf("Found %s '%s' partition",
+                               p->rw ? "writable" : "read-only",
+                               partition_designator_to_string(i));
+
+                        if (p->fstype)
+                                printf(" of type %s", p->fstype);
+
+                        if (p->architecture != _ARCHITECTURE_INVALID)
+                                printf(" for %s", architecture_to_string(p->architecture));
+
+                        k = PARTITION_VERITY_OF(i);
+                        if (k >= 0)
+                                printf(" %s verity", m->partitions[k].found ? "with" : "without");
+
+                        if (p->partno >= 0)
+                                printf(" on partition #%i", p->partno);
+
+                        if (p->node)
+                                printf(" (%s)", p->node);
+
+                        putchar('\n');
+                }
+
+                break;
+        }
+
+        case ACTION_MOUNT:
+                r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
+                if (r < 0)
+                        goto finish;
+
+                r = dissected_image_mount(m, arg_path, arg_flags);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to mount image: %m");
+                        goto finish;
+                }
+
+                if (di) {
+                        r = decrypted_image_relinquish(di);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to relinquish DM devices: %m");
+                                goto finish;
+                        }
+                }
+
+                loop_device_relinquish(d);
+                break;
+
+        default:
+                assert_not_reached("Unknown action.");
+        }
+
+finish:
+        free(arg_root_hash);
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
index 9d78b953fc11841a3efce69800b650578e79961c..092a1eabb07dd5eb79a6bdecc22b0dba4bc95161 100644 (file)
@@ -86,6 +86,28 @@ int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result
         return 0;
 }
 
+static bool net_condition_test_strv(char * const *raw_patterns,
+                                    const char *string) {
+        if (strv_isempty(raw_patterns))
+                return true;
+
+        /* If the patterns begin with "!", edit it out and negate the test. */
+        if (raw_patterns[0][0] == '!') {
+                char **patterns;
+                unsigned i, length;
+
+                length = strv_length(raw_patterns) + 1; /* Include the NULL. */
+                patterns = newa(char*, length);
+                patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */
+                for (i = 1; i < length; i++)
+                        patterns[i] = raw_patterns[i];
+
+                return !string || !strv_fnmatch(patterns, string, 0);
+        }
+
+        return string && strv_fnmatch(raw_patterns, string, 0);
+}
+
 bool net_match_config(const struct ether_addr *match_mac,
                       char * const *match_paths,
                       char * const *match_drivers,
@@ -117,20 +139,16 @@ bool net_match_config(const struct ether_addr *match_mac,
         if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
                 return false;
 
-        if (!strv_isempty(match_paths) &&
-            (!dev_path || !strv_fnmatch(match_paths, dev_path, 0)))
+        if (!net_condition_test_strv(match_paths, dev_path))
                 return false;
 
-        if (!strv_isempty(match_drivers) &&
-            (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0)))
+        if (!net_condition_test_strv(match_drivers, dev_driver))
                 return false;
 
-        if (!strv_isempty(match_types) &&
-            (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0)))
+        if (!net_condition_test_strv(match_types, dev_type))
                 return false;
 
-        if (!strv_isempty(match_names) &&
-            (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0)))
+        if (!net_condition_test_strv(match_names, dev_name))
                 return false;
 
         return true;
index d48ef6bbe2ab7e9061cf6e79fb931ae6994461c8..46c4dac7d71128b5f685eac11150d640f2253b9f 100644 (file)
@@ -511,3 +511,8 @@ global:
         sd_bus_get_exit_on_disconnect;
         sd_id128_get_invocation;
 } LIBSYSTEMD_231;
+
+LIBSYSTEMD_233 {
+global:
+        sd_id128_get_machine_app_specific;
+} LIBSYSTEMD_232;
index 1081979bf931b47850bb737d29ba54633efeeb8a..bc5e92f8fe62c28e3abc9785f05a061b4dbb6f43 100644 (file)
@@ -28,6 +28,7 @@
 #include "device-internal.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -1627,7 +1628,7 @@ static int device_sysattrs_read_all(sd_device *device) {
         if (r < 0)
                 return r;
 
-        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+        FOREACH_DIRENT_ALL(dent, dir, return -errno) {
                 char *path;
                 struct stat statbuf;
 
index d4450c70a003c357b0dafce92a542e2f67f87459..0d673ba655e18905b4ea1742b98ef9b81ba25d0e 100644 (file)
@@ -27,6 +27,7 @@
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "khash.h"
 #include "macro.h"
 #include "random-util.h"
 #include "util.h"
@@ -181,3 +182,34 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
         *ret = make_v4_uuid(t);
         return 0;
 }
+
+_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+        _cleanup_(khash_unrefp) khash *h = NULL;
+        sd_id128_t m, result;
+        const void *p;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = sd_id128_get_machine(&m);
+        if (r < 0)
+                return r;
+
+        r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m));
+        if (r < 0)
+                return r;
+
+        r = khash_put(h, &app_id, sizeof(app_id));
+        if (r < 0)
+                return r;
+
+        r = khash_digest_data(h, &p);
+        if (r < 0)
+                return r;
+
+        /* We chop off the trailing 16 bytes */
+        memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
+
+        *ret = make_v4_uuid(result);
+        return 0;
+}
index 42ea0badfc6a0d6dc269a8cea7ac7a2fa744c3fe..d2cfbdf5b0aa8f5a74dbcee047efbcde56a2f182 100644 (file)
@@ -793,6 +793,7 @@ _public_ int sd_get_sessions(char ***sessions) {
 
 _public_ int sd_get_uids(uid_t **users) {
         _cleanup_closedir_ DIR *d;
+        struct dirent *de;
         int r = 0;
         unsigned n = 0;
         _cleanup_free_ uid_t *l = NULL;
@@ -801,19 +802,10 @@ _public_ int sd_get_uids(uid_t **users) {
         if (!d)
                 return -errno;
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 int k;
                 uid_t uid;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno > 0)
-                        return -errno;
-
-                if (!de)
-                        break;
-
                 dirent_ensure_type(d, de);
 
                 if (!dirent_is_file(de))
index 57ce749e072151ec6f542b397c9c291c0a58d0cd..d8e13288b025ca533dd67fb168e729d40b778db7 100644 (file)
@@ -97,8 +97,10 @@ _public_ struct udev *udev_new(void) {
         _cleanup_fclose_ FILE *f = NULL;
 
         udev = new0(struct udev, 1);
-        if (udev == NULL)
+        if (!udev) {
+                errno = -ENOMEM;
                 return NULL;
+        }
         udev->refcount = 1;
 
         f = fopen("/etc/udev/udev.conf", "re");
index 23ad5d7c6a1d0299d5c482bba1116b60f0949487..3873bf3e96cfe64c319dd519891b4ddb0f25e6df 100644 (file)
@@ -1286,8 +1286,7 @@ static int flush_devices(Manager *m) {
         } else {
                 struct dirent *de;
 
-                while ((de = readdir(d))) {
-
+                FOREACH_DIRENT_ALL(de, d, break) {
                         if (!dirent_is_file(de))
                                 continue;
 
index 867bbc467bbd978318d0b52a726bd57ee670e017..e2fb8823933deab125bd3913bf94b5f884f540ca 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sys/mount.h>
+
 #include "alloc-util.h"
 #include "bus-label.h"
 #include "bus-util.h"
+#include "copy.h"
+#include "dissect-image.h"
 #include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
 #include "image-dbus.h"
 #include "io-util.h"
+#include "loop-util.h"
 #include "machine-image.h"
+#include "mount-util.h"
 #include "process-util.h"
+#include "raw-clone.h"
 #include "strv.h"
 #include "user-util.h"
 
@@ -279,6 +288,161 @@ int bus_image_method_set_limit(
         return sd_bus_reply_method_return(message, NULL);
 }
 
+#define EXIT_NOT_FOUND 2
+
+static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
+
+        _cleanup_free_ char *path = NULL;
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert(image);
+        assert(ret);
+
+        r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path);
+        if (r == -ENOENT)
+                r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path);
+        if (r == -ENOENT)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path);
+
+        r = load_env_file_pairs(NULL, path, NULL, ret);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path);
+
+        return 0;
+}
+
+static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
+        _cleanup_(rmdir_and_freep) char *t = NULL;
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+        _cleanup_(sigkill_waitp) pid_t child = 0;
+        _cleanup_close_pair_ int pair[2] = { -1, -1 };
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_strv_free_ char **v = NULL;
+        siginfo_t si;
+        int r;
+
+        assert(image);
+        assert(ret);
+
+        r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m");
+
+        r = loop_device_make_by_path(image->path, O_RDONLY, &d);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
+
+        r = dissect_image(d->fd, NULL, 0, &m);
+        if (r == -ENOPKG)
+                return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path);
+
+        if (pipe2(pair, O_CLOEXEC) < 0)
+                return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m");
+
+        child = raw_clone(SIGCHLD|CLONE_NEWNS);
+        if (child < 0)
+                return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+        if (child == 0) {
+                int fd;
+
+                pair[0] = safe_close(pair[0]);
+
+                /* Make sure we never propagate to the host */
+                if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
+                        _exit(EXIT_FAILURE);
+
+                r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                r = mount_move_root(t);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0 && errno == ENOENT) {
+                        fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (fd < 0 && errno == ENOENT)
+                                _exit(EXIT_NOT_FOUND);
+                }
+                if (fd < 0)
+                        _exit(EXIT_FAILURE);
+
+                r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        f = fdopen(pair[0], "re");
+        if (!f)
+                return -errno;
+
+        pair[0] = -1;
+
+        r = load_env_file_pairs(f, "os-release", NULL, &v);
+        if (r < 0)
+                return r;
+
+        r = wait_for_terminate(child, &si);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+        child = 0;
+        if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
+        if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+
+        *ret = v;
+        v = NULL;
+
+        return 0;
+}
+
+int bus_image_method_get_os_release(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
+        _cleanup_strv_free_ char **v = NULL;
+        Image *image = userdata;
+        int r;
+
+        r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m");
+
+        switch (image->type) {
+
+        case IMAGE_DIRECTORY:
+        case IMAGE_SUBVOLUME:
+                r = directory_image_get_os_release(image, &v, error);
+                break;
+
+        case IMAGE_RAW:
+                r = raw_image_get_os_release(image, &v, error);
+                break;
+
+        default:
+                assert_not_reached("Unknown image type");
+        }
+        if (r < 0)
+                return r;
+
+        return bus_reply_pair_array(message, v);
+}
+
 const sd_bus_vtable image_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
@@ -296,6 +460,7 @@ const sd_bus_vtable image_vtable[] = {
         SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_VTABLE_END
 };
 
index b62da996c68e183f08cf28385112238e587f4f38..bc8a6c34006dcac855ce17d1bf359010eaed11fe 100644 (file)
@@ -33,3 +33,4 @@ int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_erro
 int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
index 28e4867cb32abc8c4d3619c6937bc2c5822a6fd5..af745b65677b67d74d5d26d6ff00feefae8db15a 100644 (file)
@@ -356,11 +356,11 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
         return sd_bus_send(NULL, reply, NULL);
 }
 
+#define EXIT_NOT_FOUND 2
+
 int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_strv_free_ char **l = NULL;
         Machine *m = userdata;
-        char **k, **v;
         int r;
 
         assert(message);
@@ -394,7 +394,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
                         return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
 
                 if (child == 0) {
-                        _cleanup_close_ int fd = -1;
+                        int fd = -1;
 
                         pair[0] = safe_close(pair[0]);
 
@@ -402,12 +402,14 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
                         if (r < 0)
                                 _exit(EXIT_FAILURE);
 
-                        fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
-                        if (fd < 0) {
-                                fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
-                                if (fd < 0)
-                                        _exit(EXIT_FAILURE);
+                        fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (fd < 0 && errno == ENOENT) {
+                                fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                                if (fd < 0 && errno == ENOENT)
+                                        _exit(EXIT_NOT_FOUND);
                         }
+                        if (fd < 0)
+                                _exit(EXIT_FAILURE);
 
                         r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
                         if (r < 0)
@@ -431,6 +433,8 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
                 r = wait_for_terminate(child, &si);
                 if (r < 0)
                         return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+                if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
                 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
                         return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
 
@@ -441,25 +445,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
         }
 
-        r = sd_bus_message_new_method_return(message, &reply);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_open_container(reply, 'a', "{ss}");
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH_PAIR(k, v, l) {
-                r = sd_bus_message_append(reply, "{ss}", *k, *v);
-                if (r < 0)
-                        return r;
-        }
-
-        r = sd_bus_message_close_container(reply);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send(NULL, reply, NULL);
+        return bus_reply_pair_array(message, l);
 }
 
 int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
index 241b23c7ecc55de3f7e586246fa6eefce7093f7d..c51378348034e146f1c338ec0cad98dbbc68cb6c 100644 (file)
@@ -42,3 +42,5 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
 
 int machine_send_signal(Machine *m, bool new_machine);
 int machine_send_create_reply(Machine *m, sd_bus_error *error);
+
+int bus_reply_pair_array(sd_bus_message *m, char **l);
index 9c754b43278c2879a87fe314ae8ea1e5d25b5172..3294ea7821fae9a04488c6a88f2b4ce98003db49 100644 (file)
@@ -138,7 +138,7 @@ static void clean_machine_info(MachineInfo *machines, size_t n_machines) {
         free(machines);
 }
 
-static int get_os_release_property(sd_bus *bus, const char *name, const char *query, ...) {
+static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         const char *k, *v, *iter, **query_res = NULL;
         size_t count = 0, awaited_args = 0;
@@ -153,12 +153,13 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
                 awaited_args++;
         query_res = newa0(const char *, awaited_args);
 
-        r = sd_bus_call_method(bus,
-                "org.freedesktop.machine1",
-                "/org/freedesktop/machine1",
-                "org.freedesktop.machine1.Manager",
-                "GetMachineOSRelease",
-                NULL, &reply, "s", name);
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        "/org/freedesktop/machine1",
+                        "org.freedesktop.machine1.Manager",
+                        method,
+                        NULL, &reply, "s", name);
         if (r < 0)
                 return r;
 
@@ -193,7 +194,7 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
                         val = strdup(query_res[count]);
                         if (!val) {
                                 va_end(ap);
-                                return log_oom();
+                                return -ENOMEM;
                         }
                         *out = val;
                 }
@@ -249,8 +250,12 @@ static int list_machines(int argc, char *argv[], void *userdata) {
 
                 machines[n_machines].os = NULL;
                 machines[n_machines].version_id = NULL;
-                r = get_os_release_property(bus, name,
-                                "ID\0" "VERSION_ID\0",
+                r = call_get_os_release(
+                                bus,
+                                "GetMachineOSRelease",
+                                name,
+                                "ID\0"
+                                "VERSION_ID\0",
                                 &machines[n_machines].os,
                                 &machines[n_machines].version_id);
                 if (r < 0)
@@ -610,7 +615,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
         return 0;
 }
 
-static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
+static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
         _cleanup_free_ char *pretty = NULL;
         int r;
 
@@ -618,7 +623,7 @@ static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
         assert(name);
         assert(prefix);
 
-        r = get_os_release_property(bus, name, "PRETTY_NAME\0", &pretty, NULL);
+        r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
         if (r < 0)
                 return r;
 
@@ -729,7 +734,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
                        "\n\t          ",
                        ALL_IP_ADDRESSES);
 
-        print_os_release(bus, i->name, "\t      OS: ");
+        print_os_release(bus, "GetMachineOSRelease", i->name, "\t      OS: ");
 
         if (i->unit) {
                 printf("\t    Unit: %s\n", i->unit);
@@ -927,6 +932,8 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
         if (i->path)
                 printf("\t    Path: %s\n", i->path);
 
+        print_os_release(bus, "GetImageOSRelease", i->name, "\t      OS: ");
+
         printf("\t      RO: %s%s%s\n",
                i->read_only ? ansi_highlight_red() : "",
                i->read_only ? "read-only" : "writable",
index 3ee3938ebb779f3b7d4f9bb9aae7a6c01fcdb655..fd9e5b56fcaa62a8e38802ecb520f28245272397 100644 (file)
@@ -825,6 +825,30 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
         return bus_image_method_mark_read_only(message, i, error);
 }
 
+static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(image_unrefp) Image *i = NULL;
+        const char *name;
+        int r;
+
+        assert(message);
+
+        r = sd_bus_message_read(message, "s", &name);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
+
+        r = image_find(name, &i);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
+
+        i->userdata = userdata;
+        return bus_image_method_get_os_release(message, i, error);
+}
+
 static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -1396,6 +1420,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1804,3 +1829,30 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
 
         return 0;
 }
+
+int bus_reply_pair_array(sd_bus_message *m, char **l) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        char **k, **v;
+        int r;
+
+        r = sd_bus_message_new_method_return(m, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "{ss}");
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH_PAIR(k, v, l) {
+                r = sd_bus_message_append(reply, "{ss}", *k, *v);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return sd_bus_send(NULL, reply, NULL);
+
+}
index 562b9d3cc0790cb13f47e124e38529f58e96210c..82ebfba50c922dd8176e2e303b70dba72eca9aa2 100644 (file)
                        send_interface="org.freedesktop.machine1.Manager"
                        send_member="SetImageLimit"/>
 
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Manager"
+                       send_member="GetImageOSRelease"/>
+
                 <allow send_destination="org.freedesktop.machine1"
                        send_interface="org.freedesktop.machine1.Manager"
                        send_member="CleanPool"/>
                        send_interface="org.freedesktop.machine1.Image"
                        send_member="MarkReadOnly"/>
 
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Image"
+                       send_member="GetOSRelease"/>
+
                 <allow receive_sender="org.freedesktop.machine1"/>
         </policy>
 
index ddd6a64ec6669027ef55f773263564322d2c3494..d701f2158d9a99ee6d6bed99422507ce35ffa341 100644 (file)
@@ -53,6 +53,7 @@
 #include "cgroup-util.h"
 #include "copy.h"
 #include "dev-setup.h"
+#include "dissect-image.h"
 #include "env-util.h"
 #include "fd-util.h"
 #include "fdset.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "gpt.h"
+#include "hexdecoct.h"
 #include "hostname-util.h"
 #include "id128-util.h"
 #include "log.h"
+#include "loop-util.h"
 #include "loopback-setup.h"
 #include "machine-image.h"
 #include "macro.h"
@@ -198,6 +201,8 @@ static bool arg_notify_ready = false;
 static bool arg_use_cgns = true;
 static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
 static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
 
 static void help(void) {
         printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -211,6 +216,7 @@ static void help(void) {
                "  -x --ephemeral            Run container with snapshot of root directory, and\n"
                "                            remove it after exit\n"
                "  -i --image=PATH           File system device or disk image for the container\n"
+               "     --root-hash=HASH       Specify verity root hash\n"
                "  -a --as-pid2              Maintain a stub init as PID1, invoke binary as PID2\n"
                "  -b --boot                 Boot up full system (i.e. invoke init)\n"
                "     --chdir=PATH           Set working directory in the container\n"
@@ -422,6 +428,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_CHDIR,
                 ARG_PRIVATE_USERS_CHOWN,
                 ARG_NOTIFY_READY,
+                ARG_ROOT_HASH,
         };
 
         static const struct option options[] = {
@@ -471,6 +478,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "settings",              required_argument, NULL, ARG_SETTINGS            },
                 { "chdir",                 required_argument, NULL, ARG_CHDIR               },
                 { "notify-ready",          required_argument, NULL, ARG_NOTIFY_READY        },
+                { "root-hash",             required_argument, NULL, ARG_ROOT_HASH           },
                 {}
         };
 
@@ -482,7 +490,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -1014,6 +1022,25 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_settings_mask |= SETTING_NOTIFY_READY;
                         break;
 
+                case ARG_ROOT_HASH: {
+                        void *k;
+                        size_t l;
+
+                        r = unhexmem(optarg, strlen(optarg), &k, &l);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+                        if (l < sizeof(sd_id128_t)) {
+                                log_error("Root hash must be at least 128bit long: %s", optarg);
+                                free(k);
+                                return -EINVAL;
+                        }
+
+                        free(arg_root_hash);
+                        arg_root_hash = k;
+                        arg_root_hash_size = l;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
@@ -1299,6 +1326,8 @@ static int setup_resolv_conf(const char *dest) {
                  * advantage that the container will be able to follow the host's DNS server configuration changes
                  * transparently. */
 
+                (void) touch(where);
+
                 r = mount_verbose(LOG_WARNING, "/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL);
                 if (r >= 0)
                         return mount_verbose(LOG_ERR, NULL, where, NULL,
@@ -1769,546 +1798,6 @@ static int setup_propagate(const char *root) {
         return mount_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
 }
 
-static int setup_image(char **device_path, int *loop_nr) {
-        struct loop_info64 info = {
-                .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN
-        };
-        _cleanup_close_ int fd = -1, control = -1, loop = -1;
-        _cleanup_free_ char* loopdev = NULL;
-        struct stat st;
-        int r, nr;
-
-        assert(device_path);
-        assert(loop_nr);
-        assert(arg_image);
-
-        fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open %s: %m", arg_image);
-
-        if (fstat(fd, &st) < 0)
-                return log_error_errno(errno, "Failed to stat %s: %m", arg_image);
-
-        if (S_ISBLK(st.st_mode)) {
-                char *p;
-
-                p = strdup(arg_image);
-                if (!p)
-                        return log_oom();
-
-                *device_path = p;
-
-                *loop_nr = -1;
-
-                r = fd;
-                fd = -1;
-
-                return r;
-        }
-
-        if (!S_ISREG(st.st_mode)) {
-                log_error("%s is not a regular file or block device.", arg_image);
-                return -EINVAL;
-        }
-
-        control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
-        if (control < 0)
-                return log_error_errno(errno, "Failed to open /dev/loop-control: %m");
-
-        nr = ioctl(control, LOOP_CTL_GET_FREE);
-        if (nr < 0)
-                return log_error_errno(errno, "Failed to allocate loop device: %m");
-
-        if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
-                return log_oom();
-
-        loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
-        if (loop < 0)
-                return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev);
-
-        if (ioctl(loop, LOOP_SET_FD, fd) < 0)
-                return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev);
-
-        if (arg_read_only)
-                info.lo_flags |= LO_FLAGS_READ_ONLY;
-
-        if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
-                return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev);
-
-        *device_path = loopdev;
-        loopdev = NULL;
-
-        *loop_nr = nr;
-
-        r = loop;
-        loop = -1;
-
-        return r;
-}
-
-#define PARTITION_TABLE_BLURB \
-        "Note that the disk image needs to either contain only a single MBR partition of\n" \
-        "type 0x83 that is marked bootable, or a single GPT partition of type " \
-        "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \
-        "    http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
-        "to be bootable with systemd-nspawn."
-
-static int dissect_image(
-                int fd,
-                char **root_device, bool *root_device_rw,
-                char **home_device, bool *home_device_rw,
-                char **srv_device, bool *srv_device_rw,
-                char **esp_device,
-                bool *secondary) {
-
-#ifdef HAVE_BLKID
-        int home_nr = -1, srv_nr = -1, esp_nr = -1;
-#ifdef GPT_ROOT_NATIVE
-        int root_nr = -1;
-#endif
-#ifdef GPT_ROOT_SECONDARY
-        int secondary_root_nr = -1;
-#endif
-        _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *esp = NULL, *generic = NULL;
-        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
-        _cleanup_udev_device_unref_ struct udev_device *d = NULL;
-        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
-        _cleanup_udev_unref_ struct udev *udev = NULL;
-        struct udev_list_entry *first, *item;
-        bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true;
-        bool is_gpt, is_mbr, multiple_generic = false;
-        const char *pttype = NULL;
-        blkid_partlist pl;
-        struct stat st;
-        unsigned i;
-        int r;
-
-        assert(fd >= 0);
-        assert(root_device);
-        assert(home_device);
-        assert(srv_device);
-        assert(esp_device);
-        assert(secondary);
-        assert(arg_image);
-
-        b = blkid_new_probe();
-        if (!b)
-                return log_oom();
-
-        errno = 0;
-        r = blkid_probe_set_device(b, fd, 0, 0);
-        if (r != 0) {
-                if (errno == 0)
-                        return log_oom();
-
-                return log_error_errno(errno, "Failed to set device on blkid probe: %m");
-        }
-
-        blkid_probe_enable_partitions(b, 1);
-        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
-
-        errno = 0;
-        r = blkid_do_safeprobe(b);
-        if (r == -2 || r == 1) {
-                log_error("Failed to identify any partition table on\n"
-                          "    %s\n"
-                          PARTITION_TABLE_BLURB, arg_image);
-                return -EINVAL;
-        } else if (r != 0) {
-                if (errno == 0)
-                        errno = EIO;
-                return log_error_errno(errno, "Failed to probe: %m");
-        }
-
-        (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
-
-        is_gpt = streq_ptr(pttype, "gpt");
-        is_mbr = streq_ptr(pttype, "dos");
-
-        if (!is_gpt && !is_mbr) {
-                log_error("No GPT or MBR partition table discovered on\n"
-                          "    %s\n"
-                          PARTITION_TABLE_BLURB, arg_image);
-                return -EINVAL;
-        }
-
-        errno = 0;
-        pl = blkid_probe_get_partitions(b);
-        if (!pl) {
-                if (errno == 0)
-                        return log_oom();
-
-                log_error("Failed to list partitions of %s", arg_image);
-                return -errno;
-        }
-
-        udev = udev_new();
-        if (!udev)
-                return log_oom();
-
-        if (fstat(fd, &st) < 0)
-                return log_error_errno(errno, "Failed to stat block device: %m");
-
-        d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
-        if (!d)
-                return log_oom();
-
-        for (i = 0;; i++) {
-                int n, m;
-
-                if (i >= 10) {
-                        log_error("Kernel partitions never appeared.");
-                        return -ENXIO;
-                }
-
-                e = udev_enumerate_new(udev);
-                if (!e)
-                        return log_oom();
-
-                r = udev_enumerate_add_match_parent(e, d);
-                if (r < 0)
-                        return log_oom();
-
-                r = udev_enumerate_scan_devices(e);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image);
-
-                /* Count the partitions enumerated by the kernel */
-                n = 0;
-                first = udev_enumerate_get_list_entry(e);
-                udev_list_entry_foreach(item, first)
-                        n++;
-
-                /* Count the partitions enumerated by blkid */
-                m = blkid_partlist_numof_partitions(pl);
-                if (n == m + 1)
-                        break;
-                if (n > m + 1) {
-                        log_error("blkid and kernel partition list do not match.");
-                        return -EIO;
-                }
-                if (n < m + 1) {
-                        unsigned j;
-
-                        /* The kernel has probed fewer partitions than
-                         * blkid? Maybe the kernel prober is still
-                         * running or it got EBUSY because udev
-                         * already opened the device. Let's reprobe
-                         * the device, which is a synchronous call
-                         * that waits until probing is complete. */
-
-                        for (j = 0; j < 20; j++) {
-
-                                r = ioctl(fd, BLKRRPART, 0);
-                                if (r < 0)
-                                        r = -errno;
-                                if (r >= 0 || r != -EBUSY)
-                                        break;
-
-                                /* If something else has the device
-                                 * open, such as an udev rule, the
-                                 * ioctl will return EBUSY. Since
-                                 * there's no way to wait until it
-                                 * isn't busy anymore, let's just wait
-                                 * a bit, and try again.
-                                 *
-                                 * This is really something they
-                                 * should fix in the kernel! */
-
-                                usleep(50 * USEC_PER_MSEC);
-                        }
-
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to reread partition table: %m");
-                }
-
-                e = udev_enumerate_unref(e);
-        }
-
-        first = udev_enumerate_get_list_entry(e);
-        udev_list_entry_foreach(item, first) {
-                _cleanup_udev_device_unref_ struct udev_device *q;
-                const char *node;
-                unsigned long long flags;
-                blkid_partition pp;
-                dev_t qn;
-                int nr;
-
-                errno = 0;
-                q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
-                if (!q) {
-                        if (!errno)
-                                errno = ENOMEM;
-
-                        return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image);
-                }
-
-                qn = udev_device_get_devnum(q);
-                if (major(qn) == 0)
-                        continue;
-
-                if (st.st_rdev == qn)
-                        continue;
-
-                node = udev_device_get_devnode(q);
-                if (!node)
-                        continue;
-
-                pp = blkid_partlist_devno_to_partition(pl, qn);
-                if (!pp)
-                        continue;
-
-                flags = blkid_partition_get_flags(pp);
-
-                nr = blkid_partition_get_partno(pp);
-                if (nr < 0)
-                        continue;
-
-                if (is_gpt) {
-                        sd_id128_t type_id;
-                        const char *stype;
-
-                        if (flags & GPT_FLAG_NO_AUTO)
-                                continue;
-
-                        stype = blkid_partition_get_type_string(pp);
-                        if (!stype)
-                                continue;
-
-                        if (sd_id128_from_string(stype, &type_id) < 0)
-                                continue;
-
-                        if (sd_id128_equal(type_id, GPT_HOME)) {
-
-                                if (home && nr >= home_nr)
-                                        continue;
-
-                                home_nr = nr;
-                                home_rw = !(flags & GPT_FLAG_READ_ONLY);
-
-                                r = free_and_strdup(&home, node);
-                                if (r < 0)
-                                        return log_oom();
-
-                        } else if (sd_id128_equal(type_id, GPT_SRV)) {
-
-                                if (srv && nr >= srv_nr)
-                                        continue;
-
-                                srv_nr = nr;
-                                srv_rw = !(flags & GPT_FLAG_READ_ONLY);
-
-                                r = free_and_strdup(&srv, node);
-                                if (r < 0)
-                                        return log_oom();
-                        } else if (sd_id128_equal(type_id, GPT_ESP)) {
-
-                                if (esp && nr >= esp_nr)
-                                        continue;
-
-                                esp_nr = nr;
-
-                                r = free_and_strdup(&esp, node);
-                                if (r < 0)
-                                        return log_oom();
-                        }
-#ifdef GPT_ROOT_NATIVE
-                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
-
-                                if (root && nr >= root_nr)
-                                        continue;
-
-                                root_nr = nr;
-                                root_rw = !(flags & GPT_FLAG_READ_ONLY);
-
-                                r = free_and_strdup(&root, node);
-                                if (r < 0)
-                                        return log_oom();
-                        }
-#endif
-#ifdef GPT_ROOT_SECONDARY
-                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
-
-                                if (secondary_root && nr >= secondary_root_nr)
-                                        continue;
-
-                                secondary_root_nr = nr;
-                                secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
-
-                                r = free_and_strdup(&secondary_root, node);
-                                if (r < 0)
-                                        return log_oom();
-                        }
-#endif
-                        else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
-
-                                if (generic)
-                                        multiple_generic = true;
-                                else {
-                                        generic_rw = !(flags & GPT_FLAG_READ_ONLY);
-
-                                        r = free_and_strdup(&generic, node);
-                                        if (r < 0)
-                                                return log_oom();
-                                }
-                        }
-
-                } else if (is_mbr) {
-                        int type;
-
-                        if (flags != 0x80) /* Bootable flag */
-                                continue;
-
-                        type = blkid_partition_get_type(pp);
-                        if (type != 0x83) /* Linux partition */
-                                continue;
-
-                        if (generic)
-                                multiple_generic = true;
-                        else {
-                                generic_rw = true;
-
-                                r = free_and_strdup(&root, node);
-                                if (r < 0)
-                                        return log_oom();
-                        }
-                }
-        }
-
-        if (root) {
-                *root_device = root;
-                root = NULL;
-
-                *root_device_rw = root_rw;
-                *secondary = false;
-        } else if (secondary_root) {
-                *root_device = secondary_root;
-                secondary_root = NULL;
-
-                *root_device_rw = secondary_root_rw;
-                *secondary = true;
-        } else if (generic) {
-
-                /* There were no partitions with precise meanings
-                 * around, but we found generic partitions. In this
-                 * case, if there's only one, we can go ahead and boot
-                 * it, otherwise we bail out, because we really cannot
-                 * make any sense of it. */
-
-                if (multiple_generic) {
-                        log_error("Identified multiple bootable Linux partitions on\n"
-                                  "    %s\n"
-                                  PARTITION_TABLE_BLURB, arg_image);
-                        return -EINVAL;
-                }
-
-                *root_device = generic;
-                generic = NULL;
-
-                *root_device_rw = generic_rw;
-                *secondary = false;
-        } else {
-                log_error("Failed to identify root partition in disk image\n"
-                          "    %s\n"
-                          PARTITION_TABLE_BLURB, arg_image);
-                return -EINVAL;
-        }
-
-        if (home) {
-                *home_device = home;
-                home = NULL;
-
-                *home_device_rw = home_rw;
-        }
-
-        if (srv) {
-                *srv_device = srv;
-                srv = NULL;
-
-                *srv_device_rw = srv_rw;
-        }
-
-        if (esp) {
-                *esp_device = esp;
-                esp = NULL;
-        }
-
-        return 0;
-#else
-        log_error("--image= is not supported, compiled without blkid support.");
-        return -EOPNOTSUPP;
-#endif
-}
-
-static int mount_device(const char *what, const char *where, const char *directory, bool rw) {
-#ifdef HAVE_BLKID
-        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
-        const char *fstype, *p, *options;
-        int r;
-
-        assert(what);
-        assert(where);
-
-        if (arg_read_only)
-                rw = false;
-
-        if (directory)
-                p = strjoina(where, directory);
-        else
-                p = where;
-
-        errno = 0;
-        b = blkid_new_probe_from_filename(what);
-        if (!b) {
-                if (errno == 0)
-                        return log_oom();
-                return log_error_errno(errno, "Failed to allocate prober for %s: %m", what);
-        }
-
-        blkid_probe_enable_superblocks(b, 1);
-        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
-
-        errno = 0;
-        r = blkid_do_safeprobe(b);
-        if (r == -1 || r == 1) {
-                log_error("Cannot determine file system type of %s", what);
-                return -EINVAL;
-        } else if (r != 0) {
-                if (errno == 0)
-                        errno = EIO;
-                return log_error_errno(errno, "Failed to probe %s: %m", what);
-        }
-
-        errno = 0;
-        if (blkid_probe_lookup_value(b, "TYPE", &fstype, NULL) < 0) {
-                if (errno == 0)
-                        errno = EINVAL;
-                log_error("Failed to determine file system type of %s", what);
-                return -errno;
-        }
-
-        if (streq(fstype, "crypto_LUKS")) {
-                log_error("nspawn currently does not support LUKS disk images.");
-                return -EOPNOTSUPP;
-        }
-
-        /* If this is a loopback device then let's mount the image with discard, so that the underlying file remains
-         * sparse when possible. */
-        if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs")) {
-                const char *l;
-
-                l = path_startswith(what, "/dev");
-                if (l && startswith(l, "loop"))
-                        options = "discard";
-        }
-
-        return mount_verbose(LOG_ERR, what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
-#else
-        log_error("--image= is not supported, compiled without blkid support.");
-        return -EOPNOTSUPP;
-#endif
-}
-
 static int setup_machine_id(const char *directory) {
         const char *etc_machine_id;
         sd_id128_t id;
@@ -2368,83 +1857,6 @@ static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
         return r;
 }
 
-static int mount_devices(
-                const char *where,
-                const char *root_device, bool root_device_rw,
-                const char *home_device, bool home_device_rw,
-                const char *srv_device, bool srv_device_rw,
-                const char *esp_device) {
-        int r;
-
-        assert(where);
-
-        if (root_device) {
-                r = mount_device(root_device, arg_directory, NULL, root_device_rw);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to mount root directory: %m");
-        }
-
-        if (home_device) {
-                r = mount_device(home_device, arg_directory, "/home", home_device_rw);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to mount home directory: %m");
-        }
-
-        if (srv_device) {
-                r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to mount server data directory: %m");
-        }
-
-        if (esp_device) {
-                const char *mp, *x;
-
-                /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
-
-                mp = "/efi";
-                x = strjoina(arg_directory, mp);
-                r = dir_is_empty(x);
-                if (r == -ENOENT) {
-                        mp = "/boot";
-                        x = strjoina(arg_directory, mp);
-                        r = dir_is_empty(x);
-                }
-
-                if (r > 0) {
-                        r = mount_device(esp_device, arg_directory, mp, true);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to  mount ESP: %m");
-                }
-        }
-
-        return 0;
-}
-
-static void loop_remove(int nr, int *image_fd) {
-        _cleanup_close_ int control = -1;
-        int r;
-
-        if (nr < 0)
-                return;
-
-        if (image_fd && *image_fd >= 0) {
-                r = ioctl(*image_fd, LOOP_CLR_FD);
-                if (r < 0)
-                        log_debug_errno(errno, "Failed to close loop image: %m");
-                *image_fd = safe_close(*image_fd);
-        }
-
-        control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
-        if (control < 0) {
-                log_warning_errno(errno, "Failed to open /dev/loop-control: %m");
-                return;
-        }
-
-        r = ioctl(control, LOOP_CTL_REMOVE, nr);
-        if (r < 0)
-                log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
-}
-
 /*
  * Return values:
  * < 0 : wait_for_terminate() failed to get the state of the
@@ -2566,10 +1978,22 @@ static int determine_names(void) {
         }
 
         if (!arg_machine) {
+
                 if (arg_directory && path_equal(arg_directory, "/"))
                         arg_machine = gethostname_malloc();
-                else
-                        arg_machine = strdup(basename(arg_image ?: arg_directory));
+                else {
+                        if (arg_image) {
+                                char *e;
+
+                                arg_machine = strdup(basename(arg_image));
+
+                                /* Truncate suffix if there is one */
+                                e = endswith(arg_machine, ".raw");
+                                if (e)
+                                        *e = 0;
+                        } else
+                                arg_machine = strdup(basename(arg_directory));
+                }
                 if (!arg_machine)
                         return log_oom();
 
@@ -2919,10 +2343,7 @@ static int outer_child(
                 Barrier *barrier,
                 const char *directory,
                 const char *console,
-                const char *root_device, bool root_device_rw,
-                const char *home_device, bool home_device_rw,
-                const char *srv_device, bool srv_device_rw,
-                const char *esp_device,
+                DissectedImage *dissected_image,
                 bool interactive,
                 bool secondary,
                 int pid_socket,
@@ -2982,13 +2403,11 @@ static int outer_child(
         if (r < 0)
                 return r;
 
-        r = mount_devices(directory,
-                          root_device, root_device_rw,
-                          home_device, home_device_rw,
-                          srv_device, srv_device_rw,
-                          esp_device);
-        if (r < 0)
-                return r;
+        if (dissected_image) {
+                r = dissected_image_mount(dissected_image, directory, DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+                if (r < 0)
+                        return r;
+        }
 
         r = determine_uid_shift(directory);
         if (r < 0)
@@ -3605,10 +3024,7 @@ static int load_settings(void) {
 
 static int run(int master,
                const char* console,
-               const char *root_device, bool root_device_rw,
-               const char *home_device, bool home_device_rw,
-               const char *srv_device, bool srv_device_rw,
-               const char *esp_device,
+               DissectedImage *dissected_image,
                bool interactive,
                bool secondary,
                FDSet *fds,
@@ -3715,10 +3131,7 @@ static int run(int master,
                 r = outer_child(&barrier,
                                 arg_directory,
                                 console,
-                                root_device, root_device_rw,
-                                home_device, home_device_rw,
-                                srv_device, srv_device_rw,
-                                esp_device,
+                                dissected_image,
                                 interactive,
                                 secondary,
                                 pid_socket_pair[1],
@@ -4023,13 +3436,59 @@ static int run(int master,
         return 1; /* loop again */
 }
 
+static int load_root_hash(const char *image) {
+        _cleanup_free_ char *text = NULL;
+        char *fn, *n, *e;
+        void *k;
+        size_t l;
+        int r;
+
+        assert_se(image);
+
+        /* Try to load the root hash from a file next to the image file if it exists. */
+
+        if (arg_root_hash)
+                return 0;
+
+        fn = new(char, strlen(image) + strlen(".roothash") + 1);
+        if (!fn)
+                return log_oom();
+
+        n = stpcpy(fn, image);
+        e = endswith(fn, ".raw");
+        if (e)
+                n = e;
+
+        strcpy(n, ".roothash");
+
+        r = read_one_line_file(fn, &text);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0) {
+                log_warning_errno(r, "Failed to read %s, ignoring: %m", fn);
+                return 0;
+        }
+
+        r = unhexmem(text, strlen(text), &k, &l);
+        if (r < 0)
+                return log_error_errno(r, "Invalid root hash: %s", text);
+        if (l < sizeof(sd_id128_t)) {
+                free(k);
+                return log_error_errno(r, "Root hash too short: %s", text);
+        }
+
+        arg_root_hash = k;
+        arg_root_hash_size = l;
+
+        return 0;
+}
+
 int main(int argc, char *argv[]) {
 
-        _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
-        bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
-        _cleanup_close_ int master = -1, image_fd = -1;
+        _cleanup_free_ char *console = NULL;
+        _cleanup_close_ int master = -1;
         _cleanup_fdset_free_ FDSet *fds = NULL;
-        int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS;
+        int r, n_fd_passed, ret = EXIT_SUCCESS;
         char veth_name[IFNAMSIZ] = "";
         bool secondary = false, remove_directory = false, remove_image = false;
         pid_t pid = 0;
@@ -4037,6 +3496,9 @@ int main(int argc, char *argv[]) {
         _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
         bool interactive, veth_created = false, remove_tmprootdir = false;
         char tmprootdir[] = "/tmp/nspawn-root-XXXXXX";
+        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
 
         log_parse_environment();
         log_open();
@@ -4235,6 +3697,10 @@ int main(int argc, char *argv[]) {
                                 r = log_error_errno(r, "Failed to create image lock: %m");
                                 goto finish;
                         }
+
+                        r = load_root_hash(arg_image);
+                        if (r < 0)
+                                goto finish;
                 }
 
                 if (!mkdtemp(tmprootdir)) {
@@ -4250,18 +3716,41 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                image_fd = setup_image(&device_path, &loop_nr);
-                if (image_fd < 0) {
-                        r = image_fd;
+                r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to set up loopback block device: %m");
+                        goto finish;
+                }
+
+                r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
+                if (r == -ENOPKG) {
+                        log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);
+
+                        log_notice("Note that the disk image needs to\n"
+                                   "    a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
+                                   "    b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
+                                   "    c) or follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n"
+                                   "    d) or contain a file system without a partition table\n"
+                                   "in order to be bootable with systemd-nspawn.");
+                        goto finish;
+                }
+                if (r == -EADDRNOTAVAIL) {
+                        log_error_errno(r, "No root partition for specified root hash found.");
+                        goto finish;
+                }
+                if (r == -EOPNOTSUPP) {
+                        log_error_errno(r, "--image= is not supported, compiled without blkid support.");
+                        goto finish;
+                }
+                if (r < 0) {
+                        log_error_errno(r, "Failed to dissect image: %m");
                         goto finish;
                 }
 
-                r = dissect_image(image_fd,
-                                  &root_device, &root_device_rw,
-                                  &home_device, &home_device_rw,
-                                  &srv_device, &srv_device_rw,
-                                  &esp_device,
-                                  &secondary);
+                if (!arg_root_hash && dissected_image->can_verity)
+                        log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
+
+                r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image);
                 if (r < 0)
                         goto finish;
 
@@ -4315,10 +3804,7 @@ int main(int argc, char *argv[]) {
         for (;;) {
                 r = run(master,
                         console,
-                        root_device, root_device_rw,
-                        home_device, home_device_rw,
-                        srv_device, srv_device_rw,
-                        esp_device,
+                        dissected_image,
                         interactive, secondary,
                         fds,
                         veth_name, &veth_created,
@@ -4345,8 +3831,6 @@ finish:
         if (pid > 0)
                 (void) wait_for_terminate(pid, NULL);
 
-        loop_remove(loop_nr, &image_fd);
-
         if (remove_directory && arg_directory) {
                 int k;
 
@@ -4393,6 +3877,7 @@ finish:
         strv_free(arg_parameters);
         custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
         expose_port_free_all(arg_expose_ports);
+        free(arg_root_hash);
 
         return r < 0 ? EXIT_FAILURE : ret;
 }
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
new file mode 100644 (file)
index 0000000..257af78
--- /dev/null
@@ -0,0 +1,1078 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_LIBCRYPTSETUP
+#include <libcryptsetup.h>
+#endif
+#include <linux/dm-ioctl.h>
+#include <sys/mount.h>
+
+#include "architecture.h"
+#include "ask-password-api.h"
+#include "blkid-util.h"
+#include "dissect-image.h"
+#include "fd-util.h"
+#include "gpt.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "udev-util.h"
+
+static int probe_filesystem(const char *node, char **ret_fstype) {
+#ifdef HAVE_BLKID
+        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+        const char *fstype;
+        int r;
+
+        b = blkid_new_probe_from_filename(node);
+        if (!b)
+                return -ENOMEM;
+
+        blkid_probe_enable_superblocks(b, 1);
+        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (r == -2 || r == 1) {
+                log_debug("Failed to identify any partition type on partition %s", node);
+                goto not_found;
+        }
+        if (r != 0) {
+                if (errno == 0)
+                        return -EIO;
+
+                return -errno;
+        }
+
+        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+
+        if (fstype) {
+                char *t;
+
+                t = strdup(fstype);
+                if (!t)
+                        return -ENOMEM;
+
+                *ret_fstype = t;
+                return 1;
+        }
+
+not_found:
+        *ret_fstype = NULL;
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
+
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
+
+#ifdef HAVE_BLKID
+        sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
+        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+        bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
+        _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+        _cleanup_udev_unref_ struct udev *udev = NULL;
+        _cleanup_free_ char *generic_node = NULL;
+        const char *pttype = NULL, *usage = NULL;
+        struct udev_list_entry *first, *item;
+        blkid_partlist pl;
+        int r, generic_nr;
+        struct stat st;
+        unsigned i;
+
+        assert(fd >= 0);
+        assert(ret);
+        assert(root_hash || root_hash_size == 0);
+
+        /* Probes a disk image, and returns information about what it found in *ret.
+         *
+         * Returns -ENOPKG if no suitable partition table or file system could be found.
+         * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
+
+        if (root_hash) {
+                /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
+                 * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
+                 * 128bit. */
+
+                if (root_hash_size < sizeof(sd_id128_t))
+                        return -EINVAL;
+
+                memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
+                memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
+
+                if (sd_id128_is_null(root_uuid))
+                        return -EINVAL;
+                if (sd_id128_is_null(verity_uuid))
+                        return -EINVAL;
+        }
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (!S_ISBLK(st.st_mode))
+                return -ENOTBLK;
+
+        b = blkid_new_probe();
+        if (!b)
+                return -ENOMEM;
+
+        errno = 0;
+        r = blkid_probe_set_device(b, fd, 0, 0);
+        if (r != 0) {
+                if (errno == 0)
+                        return -ENOMEM;
+
+                return -errno;
+        }
+
+        blkid_probe_enable_superblocks(b, 1);
+        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+        blkid_probe_enable_partitions(b, 1);
+        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (r == -2 || r == 1) {
+                log_debug("Failed to identify any partition table.");
+                return -ENOPKG;
+        }
+        if (r != 0) {
+                if (errno == 0)
+                        return -EIO;
+
+                return -errno;
+        }
+
+        m = new0(DissectedImage, 1);
+        if (!m)
+                return -ENOMEM;
+
+        (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
+        if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
+                _cleanup_free_ char *t = NULL, *n = NULL;
+                const char *fstype = NULL;
+
+                /* OK, we have found a file system, that's our root partition then. */
+                (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+
+                if (fstype) {
+                        t = strdup(fstype);
+                        if (!t)
+                                return -ENOMEM;
+                }
+
+                if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+                        return -ENOMEM;
+
+                m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+                        .found = true,
+                        .rw = true,
+                        .partno = -1,
+                        .architecture = _ARCHITECTURE_INVALID,
+                        .fstype = t,
+                        .node = n,
+                };
+
+                t = n = NULL;
+
+                m->encrypted = streq(fstype, "crypto_LUKS");
+
+                *ret = m;
+                m = NULL;
+
+                return 0;
+        }
+
+        (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+        if (!pttype)
+                return -ENOPKG;
+
+        is_gpt = streq_ptr(pttype, "gpt");
+        is_mbr = streq_ptr(pttype, "dos");
+
+        if (!is_gpt && !is_mbr)
+                return -ENOPKG;
+
+        errno = 0;
+        pl = blkid_probe_get_partitions(b);
+        if (!pl) {
+                if (errno == 0)
+                        return -ENOMEM;
+
+                return -errno;
+        }
+
+        udev = udev_new();
+        if (!udev)
+                return -errno;
+
+        d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+        if (!d)
+                return -ENOMEM;
+
+        for (i = 0;; i++) {
+                int n, z;
+
+                if (i >= 10) {
+                        log_debug("Kernel partitions never appeared.");
+                        return -ENXIO;
+                }
+
+                e = udev_enumerate_new(udev);
+                if (!e)
+                        return -errno;
+
+                r = udev_enumerate_add_match_parent(e, d);
+                if (r < 0)
+                        return r;
+
+                r = udev_enumerate_scan_devices(e);
+                if (r < 0)
+                        return r;
+
+                /* Count the partitions enumerated by the kernel */
+                n = 0;
+                first = udev_enumerate_get_list_entry(e);
+                udev_list_entry_foreach(item, first)
+                        n++;
+
+                /* Count the partitions enumerated by blkid */
+                z = blkid_partlist_numof_partitions(pl);
+                if (n == z + 1)
+                        break;
+                if (n > z + 1) {
+                        log_debug("blkid and kernel partition list do not match.");
+                        return -EIO;
+                }
+                if (n < z + 1) {
+                        unsigned j;
+
+                        /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
+                         * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
+                         * synchronous call that waits until probing is complete. */
+
+                        for (j = 0; j < 20; j++) {
+
+                                r = ioctl(fd, BLKRRPART, 0);
+                                if (r < 0)
+                                        r = -errno;
+                                if (r >= 0 || r != -EBUSY)
+                                        break;
+
+                                /* If something else has the device open, such as an udev rule, the ioctl will return
+                                 * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
+                                 * bit, and try again.
+                                 *
+                                 * This is really something they should fix in the kernel! */
+
+                                usleep(50 * USEC_PER_MSEC);
+                        }
+
+                        if (r < 0)
+                                return r;
+                }
+
+                e = udev_enumerate_unref(e);
+        }
+
+        first = udev_enumerate_get_list_entry(e);
+        udev_list_entry_foreach(item, first) {
+                _cleanup_udev_device_unref_ struct udev_device *q;
+                unsigned long long flags;
+                blkid_partition pp;
+                const char *node;
+                dev_t qn;
+                int nr;
+
+                q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+                if (!q)
+                        return -errno;
+
+                qn = udev_device_get_devnum(q);
+                if (major(qn) == 0)
+                        continue;
+
+                if (st.st_rdev == qn)
+                        continue;
+
+                node = udev_device_get_devnode(q);
+                if (!node)
+                        continue;
+
+                pp = blkid_partlist_devno_to_partition(pl, qn);
+                if (!pp)
+                        continue;
+
+                flags = blkid_partition_get_flags(pp);
+
+                nr = blkid_partition_get_partno(pp);
+                if (nr < 0)
+                        continue;
+
+                if (is_gpt) {
+                        int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
+                        const char *stype, *sid, *fstype = NULL;
+                        sd_id128_t type_id, id;
+                        bool rw = true;
+
+                        if (flags & GPT_FLAG_NO_AUTO)
+                                continue;
+
+                        sid = blkid_partition_get_uuid(pp);
+                        if (!sid)
+                                continue;
+                        if (sd_id128_from_string(sid, &id) < 0)
+                                continue;
+
+                        stype = blkid_partition_get_type_string(pp);
+                        if (!stype)
+                                continue;
+                        if (sd_id128_from_string(stype, &type_id) < 0)
+                                continue;
+
+                        if (sd_id128_equal(type_id, GPT_HOME)) {
+                                designator = PARTITION_HOME;
+                                rw = !(flags & GPT_FLAG_READ_ONLY);
+                        } else if (sd_id128_equal(type_id, GPT_SRV)) {
+                                designator = PARTITION_SRV;
+                                rw = !(flags & GPT_FLAG_READ_ONLY);
+                        } else if (sd_id128_equal(type_id, GPT_ESP)) {
+                                designator = PARTITION_ESP;
+                                fstype = "vfat";
+                        }
+#ifdef GPT_ROOT_NATIVE
+                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+
+                                /* If a root ID is specified, ignore everything but the root id */
+                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT;
+                                architecture = native_architecture();
+                                rw = !(flags & GPT_FLAG_READ_ONLY);
+                        }
+#ifdef GPT_ROOT_NATIVE_VERITY
+                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless a root hash is specified */
+                                if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = native_architecture();
+                                rw = false;
+                        }
+#endif
+#endif
+#ifdef GPT_ROOT_SECONDARY
+                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+
+                                /* If a root ID is specified, ignore everything but the root id */
+                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT_SECONDARY;
+                                architecture = SECONDARY_ARCHITECTURE;
+                                rw = !(flags & GPT_FLAG_READ_ONLY);
+                        }
+#ifdef GPT_ROOT_SECONDARY_VERITY
+                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless root has is specified */
+                                if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT_SECONDARY_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = SECONDARY_ARCHITECTURE;
+                                rw = false;
+                        }
+#endif
+#endif
+                        else if (sd_id128_equal(type_id, GPT_SWAP)) {
+                                designator = PARTITION_SWAP;
+                                fstype = "swap";
+                        } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+
+                                if (generic_node)
+                                        multiple_generic = true;
+                                else {
+                                        generic_nr = nr;
+                                        generic_rw = !(flags & GPT_FLAG_READ_ONLY);
+                                        generic_node = strdup(node);
+                                        if (!generic_node)
+                                                return -ENOMEM;
+                                }
+                        }
+
+                        if (designator != _PARTITION_DESIGNATOR_INVALID) {
+                                _cleanup_free_ char *t = NULL, *n = NULL;
+
+                                /* First one wins */
+                                if (m->partitions[designator].found)
+                                        continue;
+
+                                if (fstype) {
+                                        t = strdup(fstype);
+                                        if (!t)
+                                                return -ENOMEM;
+                                }
+
+                                n = strdup(node);
+                                if (!n)
+                                        return -ENOMEM;
+
+                                m->partitions[designator] = (DissectedPartition) {
+                                        .found = true,
+                                        .partno = nr,
+                                        .rw = rw,
+                                        .architecture = architecture,
+                                        .node = n,
+                                        .fstype = t,
+                                };
+
+                                n = t = NULL;
+                        }
+
+                } else if (is_mbr) {
+
+                        if (flags != 0x80) /* Bootable flag */
+                                continue;
+
+                        if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
+                                continue;
+
+                        if (generic_node)
+                                multiple_generic = true;
+                        else {
+                                generic_nr = nr;
+                                generic_rw = true;
+                                generic_node = strdup(node);
+                                if (!generic_node)
+                                        return -ENOMEM;
+                        }
+                }
+        }
+
+        if (!m->partitions[PARTITION_ROOT].found) {
+                /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
+                 * either, then check if there's a single generic one, and use that. */
+
+                if (m->partitions[PARTITION_ROOT_VERITY].found)
+                        return -ENXIO;
+
+                if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
+                        m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
+                        zero(m->partitions[PARTITION_ROOT_SECONDARY]);
+
+                        m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
+                        zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
+
+                } else if (generic_node && !root_hash) {
+
+                        if (multiple_generic)
+                                return -ENOTUNIQ;
+
+                        m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+                                .found = true,
+                                .rw = generic_rw,
+                                .partno = generic_nr,
+                                .architecture = _ARCHITECTURE_INVALID,
+                                .node = generic_node,
+                        };
+
+                        generic_node = NULL;
+                } else
+                        return -ENXIO;
+        }
+
+        assert(m->partitions[PARTITION_ROOT].found);
+
+        if (root_hash) {
+                if (!m->partitions[PARTITION_ROOT_VERITY].found)
+                        return -EADDRNOTAVAIL;
+
+                /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
+                 * (which would be weird, after all the root hash should only be assigned to one pair of
+                 * partitions... */
+                m->partitions[PARTITION_ROOT_SECONDARY].found = false;
+                m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
+
+                /* If we found a verity setup, then the root partition is necessarily read-only. */
+                m->partitions[PARTITION_ROOT].rw = false;
+
+                m->verity = true;
+        }
+
+        blkid_free_probe(b);
+        b = NULL;
+
+        /* Fill in file system types if we don't know them yet. */
+        for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                DissectedPartition *p = m->partitions + i;
+
+                if (!p->found)
+                        continue;
+
+                if (!p->fstype && p->node) {
+                        r = probe_filesystem(p->node, &p->fstype);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (streq_ptr(p->fstype, "crypto_LUKS"))
+                        m->encrypted = true;
+        }
+
+        *ret = m;
+        m = NULL;
+
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
+
+DissectedImage* dissected_image_unref(DissectedImage *m) {
+        unsigned i;
+
+        if (!m)
+                return NULL;
+
+        for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                free(m->partitions[i].fstype);
+                free(m->partitions[i].node);
+                free(m->partitions[i].decrypted_fstype);
+                free(m->partitions[i].decrypted_node);
+        }
+
+        free(m);
+        return NULL;
+}
+
+static int is_loop_device(const char *path) {
+        char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
+        struct stat st;
+
+        assert(path);
+
+        if (stat(path, &st) < 0)
+                return -errno;
+
+        if (!S_ISBLK(st.st_mode))
+                return -ENOTBLK;
+
+        xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
+        if (access(s, F_OK) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
+                xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
+                if (access(s, F_OK) < 0)
+                        return errno == ENOENT ? false : -errno;
+        }
+
+        return true;
+}
+
+static int mount_partition(
+                DissectedPartition *m,
+                const char *where,
+                const char *directory,
+                DissectImageFlags flags) {
+
+        const char *p, *options = NULL, *node, *fstype;
+        bool rw;
+
+        assert(m);
+        assert(where);
+
+        node = m->decrypted_node ?: m->node;
+        fstype = m->decrypted_fstype ?: m->fstype;
+
+        if (!m->found || !node || !fstype)
+                return 0;
+
+        /* Stacked encryption? Yuck */
+        if (streq_ptr(fstype, "crypto_LUKS"))
+                return -ELOOP;
+
+        rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
+
+        if (directory)
+                p = strjoina(where, directory);
+        else
+                p = where;
+
+        /* If requested, turn on discard support. */
+        if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs") &&
+            ((flags & DISSECT_IMAGE_DISCARD) ||
+             ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
+                options = "discard";
+
+        return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
+}
+
+int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
+        int r;
+
+        assert(m);
+        assert(where);
+
+        if (!m->partitions[PARTITION_ROOT].found)
+                return -ENXIO;
+
+        r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
+        if (r < 0)
+                return r;
+
+        r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
+        if (r < 0)
+                return r;
+
+        r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
+        if (r < 0)
+                return r;
+
+        if (m->partitions[PARTITION_ESP].found) {
+                const char *mp, *x;
+
+                /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
+
+                mp = "/efi";
+                x = strjoina(where, mp);
+                r = dir_is_empty(x);
+                if (r == -ENOENT) {
+                        mp = "/boot";
+                        x = strjoina(where, mp);
+                        r = dir_is_empty(x);
+                }
+                if (r > 0) {
+                        r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+typedef struct DecryptedPartition {
+        struct crypt_device *device;
+        char *name;
+        bool relinquished;
+} DecryptedPartition;
+
+struct DecryptedImage {
+        DecryptedPartition *decrypted;
+        size_t n_decrypted;
+        size_t n_allocated;
+};
+#endif
+
+DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
+#ifdef HAVE_LIBCRYPTSETUP
+        size_t i;
+        int r;
+
+        if (!d)
+                return NULL;
+
+        for (i = 0; i < d->n_decrypted; i++) {
+                DecryptedPartition *p = d->decrypted + i;
+
+                if (p->device && p->name && !p->relinquished) {
+                        r = crypt_deactivate(p->device, p->name);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
+                }
+
+                if (p->device)
+                        crypt_free(p->device);
+                free(p->name);
+        }
+
+        free(d);
+#endif
+        return NULL;
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+
+static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
+        _cleanup_free_ char *name = NULL, *node = NULL;
+        const char *base;
+
+        assert(original_node);
+        assert(suffix);
+        assert(ret_name);
+        assert(ret_node);
+
+        base = strrchr(original_node, '/');
+        if (!base)
+                return -EINVAL;
+        base++;
+        if (isempty(base))
+                return -EINVAL;
+
+        name = strjoin(base, suffix);
+        if (!name)
+                return -ENOMEM;
+        if (!filename_is_valid(name))
+                return -EINVAL;
+
+        node = strjoin(crypt_get_dir(), "/", name);
+        if (!node)
+                return -ENOMEM;
+
+        *ret_name = name;
+        *ret_node = node;
+
+        name = node = NULL;
+        return 0;
+}
+
+static int decrypt_partition(
+                DissectedPartition *m,
+                const char *passphrase,
+                DissectImageFlags flags,
+                DecryptedImage *d) {
+
+        _cleanup_free_ char *node = NULL, *name = NULL;
+        struct crypt_device *cd;
+        int r;
+
+        assert(m);
+        assert(d);
+
+        if (!m->found || !m->node || !m->fstype)
+                return 0;
+
+        if (!streq(m->fstype, "crypto_LUKS"))
+                return 0;
+
+        r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
+        if (r < 0)
+                return r;
+
+        if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+                return -ENOMEM;
+
+        r = crypt_init(&cd, m->node);
+        if (r < 0)
+                return r;
+
+        r = crypt_load(cd, CRYPT_LUKS1, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
+                                         ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
+                                         ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
+        if (r == -EPERM) {
+                r = -EKEYREJECTED;
+                goto fail;
+        }
+        if (r < 0)
+                goto fail;
+
+        d->decrypted[d->n_decrypted].name = name;
+        name = NULL;
+
+        d->decrypted[d->n_decrypted].device = cd;
+        d->n_decrypted++;
+
+        m->decrypted_node = node;
+        node = NULL;
+
+        return 0;
+
+fail:
+        crypt_free(cd);
+        return r;
+}
+
+static int verity_partition(
+                DissectedPartition *m,
+                DissectedPartition *v,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DecryptedImage *d) {
+
+        _cleanup_free_ char *node = NULL, *name = NULL;
+        struct crypt_device *cd;
+        int r;
+
+        assert(m);
+        assert(v);
+
+        if (!root_hash)
+                return 0;
+
+        if (!m->found || !m->node || !m->fstype)
+                return 0;
+        if (!v->found || !v->node || !v->fstype)
+                return 0;
+
+        if (!streq(v->fstype, "DM_verity_hash"))
+                return 0;
+
+        r = make_dm_name_and_node(m->node, "-verity", &name, &node);
+        if (r < 0)
+                return r;
+
+        if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+                return -ENOMEM;
+
+        r = crypt_init(&cd, v->node);
+        if (r < 0)
+                return r;
+
+        r = crypt_load(cd, CRYPT_VERITY, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = crypt_set_data_device(cd, m->node);
+        if (r < 0)
+                goto fail;
+
+        r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
+        if (r < 0)
+                goto fail;
+
+        d->decrypted[d->n_decrypted].name = name;
+        name = NULL;
+
+        d->decrypted[d->n_decrypted].device = cd;
+        d->n_decrypted++;
+
+        m->decrypted_node = node;
+        node = NULL;
+
+        return 0;
+
+fail:
+        crypt_free(cd);
+        return r;
+}
+#endif
+
+int dissected_image_decrypt(
+                DissectedImage *m,
+                const char *passphrase,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DecryptedImage **ret) {
+
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
+#ifdef HAVE_LIBCRYPTSETUP
+        unsigned i;
+        int r;
+#endif
+
+        assert(m);
+        assert(root_hash || root_hash_size == 0);
+
+        /* Returns:
+         *
+         *      = 0           â†’ There was nothing to decrypt
+         *      > 0           â†’ Decrypted successfully
+         *      -ENOKEY       â†’ There's some to decrypt but no key was supplied
+         *      -EKEYREJECTED â†’ Passed key was not correct
+         */
+
+        if (root_hash && root_hash_size < sizeof(sd_id128_t))
+                return -EINVAL;
+
+        if (!m->encrypted && !m->verity) {
+                *ret = NULL;
+                return 0;
+        }
+
+#ifdef HAVE_LIBCRYPTSETUP
+        if (m->encrypted && !passphrase)
+                return -ENOKEY;
+
+        d = new0(DecryptedImage, 1);
+        if (!d)
+                return -ENOMEM;
+
+        for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                DissectedPartition *p = m->partitions + i;
+                int k;
+
+                if (!p->found)
+                        continue;
+
+                r = decrypt_partition(p, passphrase, flags, d);
+                if (r < 0)
+                        return r;
+
+                k = PARTITION_VERITY_OF(i);
+                if (k >= 0) {
+                        r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!p->decrypted_fstype && p->decrypted_node) {
+                        r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret = d;
+        d = NULL;
+
+        return 1;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
+
+int dissected_image_decrypt_interactively(
+                DissectedImage *m,
+                const char *passphrase,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DecryptedImage **ret) {
+
+        _cleanup_strv_free_erase_ char **z = NULL;
+        int n = 3, r;
+
+        if (passphrase)
+                n--;
+
+        for (;;) {
+                r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
+                if (r >= 0)
+                        return r;
+                if (r == -EKEYREJECTED)
+                        log_error_errno(r, "Incorrect passphrase, try again!");
+                else if (r != -ENOKEY) {
+                        log_error_errno(r, "Failed to decrypt image: %m");
+                        return r;
+                }
+
+                if (--n < 0) {
+                        log_error("Too many retries.");
+                        return -EKEYREJECTED;
+                }
+
+                z = strv_free(z);
+
+                r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to query for passphrase: %m");
+
+                passphrase = z[0];
+        }
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+static int deferred_remove(DecryptedPartition *p) {
+
+        struct dm_ioctl dm = {
+                .version = {
+                        DM_VERSION_MAJOR,
+                        DM_VERSION_MINOR,
+                        DM_VERSION_PATCHLEVEL
+                },
+                .data_size = sizeof(dm),
+                .flags = DM_DEFERRED_REMOVE,
+        };
+
+        _cleanup_close_ int fd = -1;
+
+        assert(p);
+
+        /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
+
+        fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        strncpy(dm.name, p->name, sizeof(dm.name));
+
+        if (ioctl(fd, DM_DEV_REMOVE, &dm))
+                return -errno;
+
+        return 0;
+}
+#endif
+
+int decrypted_image_relinquish(DecryptedImage *d) {
+
+#ifdef HAVE_LIBCRYPTSETUP
+        size_t i;
+        int r;
+#endif
+
+        assert(d);
+
+        /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
+         * that we don't clean it up ourselves either anymore */
+
+#ifdef HAVE_LIBCRYPTSETUP
+        for (i = 0; i < d->n_decrypted; i++) {
+                DecryptedPartition *p = d->decrypted + i;
+
+                if (p->relinquished)
+                        continue;
+
+                r = deferred_remove(p);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
+
+                p->relinquished = true;
+        }
+#endif
+
+        return 0;
+}
+
+static const char *const partition_designator_table[] = {
+        [PARTITION_ROOT] = "root",
+        [PARTITION_ROOT_SECONDARY] = "root-secondary",
+        [PARTITION_HOME] = "home",
+        [PARTITION_SRV] = "srv",
+        [PARTITION_ESP] = "esp",
+        [PARTITION_SWAP] = "swap",
+        [PARTITION_ROOT_VERITY] = "root-verity",
+        [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
new file mode 100644 (file)
index 0000000..902c8d4
--- /dev/null
@@ -0,0 +1,90 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+typedef struct DissectedImage DissectedImage;
+typedef struct DissectedPartition DissectedPartition;
+typedef struct DecryptedImage DecryptedImage;
+
+struct DissectedPartition {
+        bool found:1;
+        bool rw:1;
+        int partno;        /* -1 if there was no partition and the images contains a file system directly */
+        int architecture;  /* Intended architecture: either native, secondary or unset (-1). */
+        char *fstype;
+        char *node;
+        char *decrypted_node;
+        char *decrypted_fstype;
+};
+
+enum  {
+        PARTITION_ROOT,
+        PARTITION_ROOT_SECONDARY,  /* Secondary architecture */
+        PARTITION_HOME,
+        PARTITION_SRV,
+        PARTITION_ESP,
+        PARTITION_SWAP,
+        PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
+        PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
+        _PARTITION_DESIGNATOR_MAX,
+        _PARTITION_DESIGNATOR_INVALID = -1
+};
+
+static inline int PARTITION_VERITY_OF(int p) {
+        if (p == PARTITION_ROOT)
+                return PARTITION_ROOT_VERITY;
+        if (p == PARTITION_ROOT_SECONDARY)
+                return PARTITION_ROOT_SECONDARY_VERITY;
+        return _PARTITION_DESIGNATOR_INVALID;
+}
+
+typedef enum DissectImageFlags {
+        DISSECT_IMAGE_READ_ONLY = 1,
+        DISSECT_IMAGE_DISCARD_ON_LOOP = 2,   /* Turn on "discard" if on loop device and file system supports it */
+        DISSECT_IMAGE_DISCARD = 4,           /* Turn on "discard" if file system supports it, on all block devices */
+        DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */
+} DissectImageFlags;
+
+struct DissectedImage {
+        bool encrypted:1;
+        bool verity:1;     /* verity available and usable */
+        bool can_verity:1; /* verity available, but not necessarily used */
+        DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
+};
+
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
+
+DissectedImage* dissected_image_unref(DissectedImage *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
+
+int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
+
+DecryptedImage* decrypted_image_unref(DecryptedImage *p);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
+int decrypted_image_relinquish(DecryptedImage *d);
+
+const char* partition_designator_to_string(int i) _const_;
+int partition_designator_from_string(const char *name) _pure_;
index 2c1cd84df5cdc70229b917ceeb63422d0ad404bf..3cbfe13f4c49248e5c2423caa13777923bc93786 100644 (file)
@@ -17,7 +17,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -25,6 +24,7 @@
 
 #include "alloc-util.h"
 #include "conf-files.h"
+#include "dirent-util.h"
 #include "dropin.h"
 #include "escape.h"
 #include "fd-util.h"
@@ -124,6 +124,7 @@ static int iterate_dir(
                 char ***strv) {
 
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
         int r;
 
         assert(path);
@@ -148,21 +149,9 @@ static int iterate_dir(
                 return log_error_errno(errno, "Failed to open directory %s: %m", path);
         }
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory %s: %m", path)) {
                 _cleanup_free_ char *f = NULL;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno > 0)
-                        return log_error_errno(errno, "Failed to read directory %s: %m", path);
-
-                if (!de)
-                        break;
-
-                if (hidden_or_backup_file(de->d_name))
-                        continue;
-
                 f = strjoin(path, "/", de->d_name);
                 if (!f)
                         return log_oom();
index 527f27bc67a730a5ef596ca20facb079cf46b8c1..090f3fdcdd70d8d6ebcee5c4300149d6a2c0c521 100644 (file)
 ***/
 
 #include <alloca.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stddef.h>
 
 #include "sd-daemon.h"
 
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "fdset.h"
 #include "log.h"
@@ -148,12 +148,9 @@ int fdset_new_fill(FDSet **_s) {
                 goto finish;
         }
 
-        while ((de = readdir(d))) {
+        FOREACH_DIRENT(de, d, return -errno) {
                 int fd = -1;
 
-                if (hidden_or_backup_file(de->d_name))
-                        continue;
-
                 r = safe_atoi(de->d_name, &fd);
                 if (r < 0)
                         goto finish;
index 55b41bbcd83babdb8bcf64911f324339788b4f63..13d80d611c3aab9b0a3b8d5b72f2f1795515c57e 100644 (file)
 #define GPT_ROOT_ARM    SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
 #define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
 #define GPT_ROOT_IA64   SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
-
 #define GPT_ESP         SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
 #define GPT_SWAP        SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
 #define GPT_HOME        SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
 #define GPT_SRV         SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
 
+/* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
+ * are commonly read-only and hence suitable for verity). */
+#define GPT_ROOT_X86_VERITY    SD_ID128_MAKE(d1,3c,5d,3b,b5,d1,42,2a,b2,9f,94,54,fd,c8,9d,76)
+#define GPT_ROOT_X86_64_VERITY SD_ID128_MAKE(2c,73,57,ed,eb,d2,46,d9,ae,c1,23,d4,37,ec,2b,f5)
+#define GPT_ROOT_ARM_VERITY    SD_ID128_MAKE(73,86,cd,f2,20,3c,47,a9,a4,98,f2,ec,ce,45,a2,d6)
+#define GPT_ROOT_ARM_64_VERITY SD_ID128_MAKE(df,33,00,ce,d6,9f,4c,92,97,8c,9b,fb,0f,38,d8,20)
+#define GPT_ROOT_IA64_VERITY   SD_ID128_MAKE(86,ed,10,d5,b6,07,45,bb,89,57,d3,50,f2,3d,05,71)
+
+
 #if defined(__x86_64__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_X86_64
 #  define GPT_ROOT_SECONDARY GPT_ROOT_X86
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_64_VERITY
+#  define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_X86_VERITY
 #elif defined(__i386__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_X86
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_VERITY
 #endif
 
 #if defined(__ia64__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_IA64
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_IA64_VERITY
 #endif
 
 #if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
 #  define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
 #  define GPT_ROOT_SECONDARY GPT_ROOT_ARM
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_64_VERITY
+#  define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_ARM_VERITY
 #elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN)
 #  define GPT_ROOT_NATIVE GPT_ROOT_ARM
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
 #endif
 
 /* Flags we recognize on the root, swap, home and srv partitions when
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
new file mode 100644 (file)
index 0000000..047e213
--- /dev/null
@@ -0,0 +1,166 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "loop-util.h"
+
+int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
+        const struct loop_info64 info = {
+                .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
+        };
+
+        _cleanup_close_ int control = -1, loop = -1;
+        _cleanup_free_ char *loopdev = NULL;
+        struct stat st;
+        LoopDevice *d;
+        int nr;
+
+        assert(fd >= 0);
+        assert(ret);
+        assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (S_ISBLK(st.st_mode)) {
+                int copy;
+
+                /* If this is already a block device, store a copy of the fd as it is */
+
+                copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+                if (copy < 0)
+                        return -errno;
+
+                d = new0(LoopDevice, 1);
+                if (!d)
+                        return -ENOMEM;
+
+                *d = (LoopDevice) {
+                        .fd = copy,
+                        .nr = -1,
+                };
+
+                *ret = d;
+
+                return 0;
+        }
+
+        if (!S_ISREG(st.st_mode))
+                return -EINVAL;
+
+        control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (control < 0)
+                return -errno;
+
+        nr = ioctl(control, LOOP_CTL_GET_FREE);
+        if (nr < 0)
+                return -errno;
+
+        if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
+                return -ENOMEM;
+
+        loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+        if (loop < 0)
+                return -errno;
+
+        if (ioctl(loop, LOOP_SET_FD, fd) < 0)
+                return -errno;
+
+        if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
+                return -errno;
+
+        d = new(LoopDevice, 1);
+        if (!d)
+                return -ENOMEM;
+
+        *d = (LoopDevice) {
+                .fd = loop,
+                .node = loopdev,
+                .nr = nr,
+        };
+
+        loop = -1;
+        loopdev = NULL;
+
+        *ret = d;
+
+        return (*ret)->fd;
+}
+
+int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
+        _cleanup_close_ int fd = -1;
+
+        assert(path);
+        assert(ret);
+        assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+
+        fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+        if (fd < 0)
+                return -errno;
+
+        return loop_device_make(fd, open_flags, ret);
+}
+
+LoopDevice* loop_device_unref(LoopDevice *d) {
+        if (!d)
+                return NULL;
+
+        if (d->fd >= 0) {
+
+                if (d->nr >= 0 && !d->relinquished) {
+                        if (ioctl(d->fd, LOOP_CLR_FD) < 0)
+                                log_debug_errno(errno, "Failed to clear loop device: %m");
+
+                }
+
+                safe_close(d->fd);
+        }
+
+        if (d->nr >= 0 && !d->relinquished) {
+                _cleanup_close_ int control = -1;
+
+                control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                if (control < 0)
+                        log_debug_errno(errno, "Failed to open loop control device: %m");
+                else {
+                        if (ioctl(control, LOOP_CTL_REMOVE, d->nr) < 0)
+                                log_debug_errno(errno, "Failed to remove loop device: %m");
+                }
+        }
+
+        free(d->node);
+        free(d);
+
+        return NULL;
+}
+
+void loop_device_relinquish(LoopDevice *d) {
+        assert(d);
+
+        /* Don't attempt to clean up the loop device anymore from this point on. Leave the clean-ing up to the kernel
+         * itself, using the loop device "auto-clear" logic we already turned on when creating the device. */
+
+        d->relinquished = true;
+}
diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h
new file mode 100644 (file)
index 0000000..45fead5
--- /dev/null
@@ -0,0 +1,41 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+typedef struct LoopDevice LoopDevice;
+
+/* Some helpers for setting up loopback block devices */
+
+struct LoopDevice {
+        int fd;
+        int nr;
+        char *node;
+        bool relinquished;
+};
+
+int loop_device_make(int fd, int open_flags, LoopDevice **ret);
+int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret);
+
+LoopDevice* loop_device_unref(LoopDevice *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
+
+void loop_device_relinquish(LoopDevice *d);
index ee011b1861b7531d83d67486cb74865a2dd5fbd8..6cc8e4ac0e9f2968b4630d251a1dfa4b1c3b7395 100644 (file)
@@ -39,12 +39,12 @@ union sd_id128 {
 #define SD_ID128_STRING_MAX 33
 
 char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]);
-
 int sd_id128_from_string(const char *s, sd_id128_t *ret);
 
 int sd_id128_randomize(sd_id128_t *ret);
 
 int sd_id128_get_machine(sd_id128_t *ret);
+int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 int sd_id128_get_boot(sd_id128_t *ret);
 int sd_id128_get_invocation(sd_id128_t *ret);
 
diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c
new file mode 100644 (file)
index 0000000..0512a15
--- /dev/null
@@ -0,0 +1,66 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "dissect-image.h"
+#include "log.h"
+#include "loop-util.h"
+#include "string-util.h"
+
+int main(int argc, char *argv[]) {
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+        int r, i;
+
+        log_set_max_level(LOG_DEBUG);
+
+        if (argc < 2) {
+                log_error("Requires one command line argument.");
+                return EXIT_FAILURE;
+        }
+
+        r = loop_device_make_by_path(argv[1], O_RDONLY, &d);
+        if (r < 0) {
+                log_error_errno(r, "Failed to set up loopback device: %m");
+                return EXIT_FAILURE;
+        }
+
+        r = dissect_image(d->fd, NULL, 0, &m);
+        if (r < 0) {
+                log_error_errno(r, "Failed to dissect image: %m");
+                return EXIT_FAILURE;
+        }
+
+        for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+
+                if (!m->partitions[i].found)
+                        continue;
+
+                printf("Found %s partition, %s of type %s at #%i (%s)\n",
+                       partition_designator_to_string(i),
+                       m->partitions[i].rw ? "writable" : "read-only",
+                       strna(m->partitions[i].fstype),
+                       m->partitions[i].partno,
+                       strna(m->partitions[i].node));
+        }
+
+        return EXIT_SUCCESS;
+}
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
new file mode 100644 (file)
index 0000000..1972b94
--- /dev/null
@@ -0,0 +1,82 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "khash.h"
+
+int main(int argc, char *argv[]) {
+        _cleanup_(khash_unrefp) khash *h = NULL, *copy = NULL;
+        _cleanup_free_ char *s = NULL;
+
+        log_set_max_level(LOG_DEBUG);
+
+        assert_se(khash_new(&h, NULL) == -EINVAL);
+        assert_se(khash_new(&h, "") == -EINVAL);
+        assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP);
+
+        assert_se(khash_new(&h, "sha256") >= 0);
+        assert_se(khash_get_size(h) == 32);
+        assert_se(streq(khash_get_algorithm(h), "sha256"));
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foobar", 6) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "piep", 4) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "f114d872b5ea075d3be9040d0b7a429514b3f9324a8e8e3dc3fb24c34ee56bea"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foo", 3) >= 0);
+        assert_se(khash_dup(h, &copy) >= 0);
+
+        assert_se(khash_put(h, "bar", 3) >= 0);
+        assert_se(khash_put(copy, "bar", 3) >= 0);
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        assert_se(khash_digest_string(copy, &s) >= 0);
+        assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+        s = mfree(s);
+
+        h = khash_unref(h);
+
+        assert_se(khash_new_with_key(&h, "hmac(sha256)", "quux", 4) >= 0);
+        assert_se(khash_get_size(h) == 32);
+        assert_se(streq(khash_get_algorithm(h), "hmac(sha256)"));
+
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "abed9f8218ab473f77218a6a7d39abf1d21fa46d0700c4898e330ba88309d5ae"));
+        s = mfree(s);
+
+        assert_se(khash_put(h, "foobar", 6) >= 0);
+        assert_se(khash_digest_string(h, &s) >= 0);
+        assert_se(streq(s, "33f6c70a60db66007d5325d5d1dea37c371354e5b83347a59ad339ce9f4ba3dc"));
+
+        return 0;
+}
index 1c8e5549daf983bbc10d25c10511e1e6e6bcc320..ab5a111ba94d5756dfb4af36aa45a4d72cbd1d12 100644 (file)
@@ -153,5 +153,11 @@ int main(int argc, char *argv[]) {
         assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
+        assert_se(sd_id128_equal(id, id2));
+        assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0);
+        assert_se(!sd_id128_equal(id, id2));
+
         return 0;
 }
index b881d774a0ece70e23755d2449d8250720634762..79f75e165b46c1133ac967e66e60ff04d86e11ca 100644 (file)
@@ -18,7 +18,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fnmatch.h>
@@ -44,6 +43,7 @@
 #include "conf-files.h"
 #include "copy.h"
 #include "def.h"
+#include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -380,7 +380,7 @@ static int dir_cleanup(
         bool deleted = false;
         int r = 0;
 
-        while ((dent = readdir(d))) {
+        FOREACH_DIRENT_ALL(dent, d, break) {
                 struct stat s;
                 usec_t age;
                 _cleanup_free_ char *sub_path = NULL;
@@ -1053,6 +1053,7 @@ typedef int (*action_t)(Item *, const char *);
 
 static int item_do_children(Item *i, const char *path, action_t action) {
         _cleanup_closedir_ DIR *d;
+        struct dirent *de;
         int r = 0;
 
         assert(i);
@@ -1065,19 +1066,11 @@ static int item_do_children(Item *i, const char *path, action_t action) {
         if (!d)
                 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
 
-        for (;;) {
+        FOREACH_DIRENT_ALL(de, d, r = -errno) {
                 _cleanup_free_ char *p = NULL;
-                struct dirent *de;
                 int q;
 
                 errno = 0;
-                de = readdir(d);
-                if (!de) {
-                        if (errno > 0 && r == 0)
-                                r = -errno;
-
-                        break;
-                }
 
                 if (STR_IN_SET(de->d_name, ".", ".."))
                         continue;
index fe9d6f44829745d2fae911c9a6426d2d043b1a5b..5be158f5272d3999f9a95bf9a104b410e7fbc97d 100644 (file)
 #include <unistd.h>
 #include <linux/pci_regs.h>
 
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "stdio-util.h"
@@ -256,7 +257,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
                 goto out;
         }
 
-        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+        FOREACH_DIRENT_ALL(dent, dir, break) {
                 int i;
                 char *rest, *address, str[PATH_MAX];
 
index 1825ee75a7367903ef8ad860f8852652138c54e2..527f0bff2dcbb57663faeb111b557e8ccaa08e0a 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -31,6 +30,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "dirent-util.h"
 #include "string-util.h"
 #include "udev.h"
 
@@ -405,7 +405,7 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
                 parent = NULL;
                 goto out;
         }
-        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+        FOREACH_DIRENT_ALL(dent, dir, break) {
                 char *rest;
                 int i;
 
index e94a8143887092d86e5fbf0db97bc2f2231ffe3e..53cfd9c053f9c90956586d2aca43d15b796deb3d 100644 (file)
@@ -15,7 +15,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -25,6 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "dirent-util.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "selinux-util.h"
@@ -129,6 +129,7 @@ exit:
 static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) {
         struct udev *udev = udev_device_get_udev(dev);
         DIR *dir;
+        struct dirent *dent;
         int priority = 0;
         const char *target = NULL;
 
@@ -141,12 +142,10 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons
         dir = opendir(stackdir);
         if (dir == NULL)
                 return target;
-        for (;;) {
+        FOREACH_DIRENT_ALL(dent, dir, break) {
                 struct udev_device *dev_db;
-                struct dirent *dent;
 
-                dent = readdir(dir);
-                if (dent == NULL || dent->d_name[0] == '\0')
+                if (dent->d_name[0] == '\0')
                         break;
                 if (dent->d_name[0] == '.')
                         continue;
index d88687e9c2b83b720f7e908ef9e93dba5609b226..b0238220e47065ae30e307e8a3f4185cb855db89 100644 (file)
@@ -16,7 +16,6 @@
  */
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fnmatch.h>
@@ -31,6 +30,7 @@
 
 #include "alloc-util.h"
 #include "conf-files.h"
+#include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fs-util.h"
@@ -703,7 +703,7 @@ static void attr_subst_subdir(char *attr, size_t len) {
         if (dir == NULL)
                 return;
 
-        for (dent = readdir(dir); dent != NULL; dent = readdir(dir))
+        FOREACH_DIRENT_ALL(dent, dir, break)
                 if (dent->d_name[0] != '.') {
                         char n[strlen(dent->d_name) + strlen(tail) + 1];
 
index bc9096ed0c170e087c1aed1e48e94ec90b274a89..aa432bb90a80e8aa5bd8ea60e7e54ee9c701fac6 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <dirent.h>
 #include <errno.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/inotify.h>
 #include <unistd.h>
 
+#include "dirent-util.h"
 #include "stdio-util.h"
 #include "udev.h"
 
@@ -57,7 +57,7 @@ void udev_watch_restore(struct udev *udev) {
                         return;
                 }
 
-                for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
+                FOREACH_DIRENT_ALL(ent, dir, break) {
                         char device[UTIL_PATH_SIZE];
                         ssize_t len;
                         struct udev_device *dev;
index 6753c52005ad1c4d8b3f5a1a7d2e86bc1426b6c5..90cdfa16c7630a58a9ac84e66237e61e707b904a 100644 (file)
@@ -16,7 +16,6 @@
  */
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -26,6 +25,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "dirent-util.h"
 #include "fd-util.h"
 #include "string-util.h"
 #include "udev-util.h"
@@ -196,7 +196,7 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
         if (depth <= 0)
                 return;
 
-        for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+        FOREACH_DIRENT_ALL(dent, dir, break) {
                 struct stat stats;
 
                 if (dent->d_name[0] == '.')
index a932d32b92a42ef52f73734444e74db93ad486f2..39bd4f5b1b3c291ded470f06fdd8444fae468387 100755 (executable)
@@ -74,6 +74,14 @@ class NetworkdTestingUtilities:
     some required methods.
     """
 
+    def add_veth_pair(self, veth, peer, veth_options=(), peer_options=()):
+        """Add a veth interface pair, and queue them to be removed."""
+        subprocess.check_call(['ip', 'link', 'add', 'name', veth] +
+                              list(veth_options) +
+                              ['type', 'veth', 'peer', 'name', peer] +
+                              list(peer_options))
+        self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', peer])
+
     def write_network(self, unit_name, contents):
         """Write a network unit file, and queue it to be removed."""
         unit_path = os.path.join(NETWORK_UNITDIR, unit_name)
@@ -439,9 +447,7 @@ IPv6AcceptRA=False''' % self.iface)
 
         # create second device/dnsmasq for a .company/.lab VPN interface
         # static IPs for simplicity
-        subprocess.check_call(['ip', 'link', 'add', 'name', 'testvpnclient', 'type',
-                               'veth', 'peer', 'name', 'testvpnrouter'])
-        self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', 'testvpnrouter'])
+        self.add_veth_pair('testvpnclient', 'testvpnrouter')
         subprocess.check_call(['ip', 'a', 'flush', 'dev', 'testvpnrouter'])
         subprocess.check_call(['ip', 'a', 'add', '10.241.3.1/24', 'dev', 'testvpnrouter'])
         subprocess.check_call(['ip', 'link', 'set', 'testvpnrouter', 'up'])
@@ -768,6 +774,42 @@ DNS=127.0.0.1''')
             raise
 
 
+class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
+    """Test [Match] sections in .network files.
+
+    Be aware that matching the test host's interfaces will wipe their
+    configuration, so as a precaution, all network files should have a
+    restrictive [Match] section to only ever interfere with the
+    temporary veth interfaces created here.
+    """
+
+    def tearDown(self):
+        """Stop networkd."""
+        subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+
+    def test_basic_matching(self):
+        """Verify the Name= line works throughout this class."""
+        self.add_veth_pair('test_if1', 'fake_if2')
+        self.write_network('test.network', "[Match]\nName=test_*\n[Network]")
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.assert_link_states(test_if1='managed', fake_if2='unmanaged')
+
+    def test_inverted_matching(self):
+        """Verify that a '!'-prefixed value inverts the match."""
+        # Use a MAC address as the interfaces' common matching attribute
+        # to avoid depending on udev, to support testing in containers.
+        mac = '00:01:02:03:98:99'
+        self.add_veth_pair('test_veth', 'test_peer',
+                           ['addr', mac], ['addr', mac])
+        self.write_network('no-veth.network', """\
+[Match]
+MACAddress=%s
+Name=!nonexistent *peer*
+[Network]""" % mac)
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.assert_link_states(test_veth='managed', test_peer='unmanaged')
+
+
 class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities):
     """Test if networkd manages the correct interfaces."""
 
@@ -798,11 +840,7 @@ class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities):
     def create_iface(self):
         """Create temporary veth pairs for interface matching."""
         for veth, peer in self.veths.items():
-            subprocess.check_call(['ip', 'link', 'add',
-                                   'name', veth, 'type', 'veth',
-                                   'peer', 'name', peer])
-            self.addCleanup(subprocess.call,
-                            ['ip', 'link', 'del', 'dev', peer])
+            self.add_veth_pair(veth, peer)
 
     def test_unmanaged_setting(self):
         """Verify link states with Unmanaged= settings, hot-plug."""