]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect: add new --shift command 35932/head
authorLennart Poettering <lennart@poettering.net>
Tue, 12 Nov 2024 08:44:48 +0000 (09:44 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 8 Jan 2025 20:54:19 +0000 (21:54 +0100)
man/systemd-dissect.xml
src/dissect/dissect.c

index 3aaa1744f3ea4b3800ef8747d2c40af71bccca0a..2718feccb75da12ea1d73656ddd88f286a70c44b 100644 (file)
@@ -62,6 +62,9 @@
     <cmdsynopsis>
       <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--validate</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--shift</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>UIDBASE</replaceable></arg>
+    </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
         <xi:include href="version-info.xml" xpointer="v254"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--shift</option></term>
+
+        <listitem><para>Recursively iterates through all inodes of the specified image and shifts the UIDs
+        and GIDs the inodes are owned by into the specified UID range. Takes an image path and a UID base as
+        parameter. The UID base can be specified numerically (in which case it must be a multiple of 65536,
+        and either 0 or within the container or foreign UID range, as per <ulink
+        url="https://systemd.io/UIDS-GIDS/">Users, Groups, UIDs and GIDs on systemd Systems</ulink>), or as
+        the symbolic identifier <literal>foreign</literal> which is shorthand to the foreign UID base. This
+        command is useful for preparing directory container images for unprivileged use. Note that this
+        command is intended for images that use the 16bit UIDs/GIDs range only, and it always ignores the
+        upper 16bit of the current UID/GID ownership, combining the lower 16 bit with the target UID
+        base.</para>
+
+        <para>Use <command>systemd-dissect --shift /some/container/tree foreign</command> to shift a
+        container image into the foreign UID range, or <command>systemd-dissect --shift /some/container/tree
+        0</command> to shift it to host UID range.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
index cb51d7138435254a139b75bb679747b3e0dd831a..3ca1e17be4b6c8fd7ff7cfe7d2aa07693366bbd4 100644 (file)
@@ -45,6 +45,7 @@
 #include "process-util.h"
 #include "recurse-dir.h"
 #include "sha256.h"
+#include "shift-uid.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -68,6 +69,7 @@ static enum {
         ACTION_DISCOVER,
         ACTION_VALIDATE,
         ACTION_MAKE_ARCHIVE,
+        ACTION_SHIFT,
 } arg_action = ACTION_DISSECT;
 static char *arg_image = NULL;
 static char *arg_root = NULL;
@@ -97,6 +99,7 @@ static bool arg_mtree_hash = true;
 static bool arg_via_service = false;
 static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
 static bool arg_all = false;
+static uid_t arg_uid_base = UID_INVALID;
 
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -129,6 +132,7 @@ static int help(void) {
                "%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n"
                "%1$s [OPTIONS...] --discover\n"
                "%1$s [OPTIONS...] --validate IMAGE\n"
+               "%1$s [OPTIONS...] --shift IMAGE UIDBASE\n"
                "\n%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n"
                "%3$sOptions:%4$s\n"
                "     --no-pager           Do not pipe output into a pager\n"
@@ -174,6 +178,7 @@ static int help(void) {
                "     --make-archive       Convert the DDI to an archive file\n"
                "     --discover           Discover DDIs in well known directories\n"
                "     --validate           Validate image and image policy\n"
+               "     --shift              Shift UID range to selected base\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -279,6 +284,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VALIDATE,
                 ARG_MTREE_HASH,
                 ARG_MAKE_ARCHIVE,
+                ARG_SHIFT,
                 ARG_SYSTEM,
                 ARG_USER,
                 ARG_ALL,
@@ -315,6 +321,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "validate",      no_argument,       NULL, ARG_VALIDATE      },
                 { "mtree-hash",    required_argument, NULL, ARG_MTREE_HASH    },
                 { "make-archive",  no_argument,       NULL, ARG_MAKE_ARCHIVE  },
+                { "shift",         no_argument,       NULL, ARG_SHIFT         },
                 { "system",        no_argument,       NULL, ARG_SYSTEM        },
                 { "user",          no_argument,       NULL, ARG_USER          },
                 { "all",           no_argument,       NULL, ARG_ALL           },
@@ -550,6 +557,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_MAKE_ARCHIVE;
                         break;
 
+                case ARG_SHIFT:
+                        arg_action = ACTION_SHIFT;
+                        break;
+
                 case ARG_SYSTEM:
                         system_scope_requested = true;
                         break;
@@ -731,6 +742,33 @@ static int parse_argv(int argc, char *argv[]) {
                 arg_flags &= ~(DISSECT_IMAGE_PIN_PARTITION_DEVICES|DISSECT_IMAGE_ADD_PARTITION_DEVICES);
                 break;
 
+        case ACTION_SHIFT:
+                if (optind + 2 != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Expected an image path and a UID base as only argument.");
+
+                r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
+                if (r < 0)
+                        return r;
+
+                if (streq(argv[optind + 1], "foreign"))
+                        arg_uid_base = FOREIGN_UID_BASE;
+                else {
+                        r = parse_uid(argv[optind + 1], &arg_uid_base);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse UID base: %s", argv[optind + 1]);
+
+                        if ((arg_uid_base & 0xFFFF) != 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID base not a multiple of 64K: " UID_FMT, arg_uid_base);
+                        if (arg_uid_base != 0 &&
+                            !uid_is_container(arg_uid_base) &&
+                            !uid_is_foreign(arg_uid_base))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID range is not in the container range, nor the foreign one, refusing.");
+                }
+
+                arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
+                break;
+
         default:
                 assert_not_reached();
         }
@@ -1444,7 +1482,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
         const char *root;
         int r;
 
-        assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE));
+        assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE, ACTION_SHIFT));
 
         if (arg_image) {
                 assert(m);
@@ -1699,6 +1737,13 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
 #endif
         }
 
+        case ACTION_SHIFT:
+                r = path_patch_uid(root, arg_uid_base, 0x10000);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to shift UID base: %m");
+
+                return 0;
+
         default:
                 assert_not_reached();
         }
@@ -2121,7 +2166,7 @@ static int run(int argc, char *argv[]) {
                         else
                                 r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
                         if (r < 0) {
-                                if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO))
+                                if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_SHIFT))
                                         return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
 
                                 log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image);
@@ -2206,6 +2251,7 @@ static int run(int argc, char *argv[]) {
         case ACTION_COPY_FROM:
         case ACTION_COPY_TO:
         case ACTION_MAKE_ARCHIVE:
+        case ACTION_SHIFT:
                 return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd);
 
         case ACTION_WITH: