import: support in --user mode (#39322)
This is the first half of #38728, just the parts that immediately touch importd to add a --user mode for it.
This commit is contained in:
@@ -2302,7 +2302,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
/* Don't run things in private userns, if the mount shall be attached to the host */
|
||||
if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH)) {
|
||||
userns_fd = nsresource_allocate_userns(/* name= */ NULL, UINT64_C(0x10000)); /* allocate 64K users by default */
|
||||
userns_fd = nsresource_allocate_userns(/* name= */ NULL, NSRESOURCE_UIDS_64K); /* allocate 64K users by default */
|
||||
if (userns_fd < 0)
|
||||
return log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
|
||||
}
|
||||
|
||||
+15
-1
@@ -207,7 +207,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --version Show package version\n"
|
||||
" --format=FORMAT Select format\n"
|
||||
" --class=CLASS Select image class (machine, sysext, confext,\n"
|
||||
" portable)\n",
|
||||
" portable)\n"
|
||||
" --system Operate in per-system mode\n"
|
||||
" --user Operate in per-user mode\n",
|
||||
program_invocation_short_name,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
@@ -223,6 +225,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_FORMAT,
|
||||
ARG_CLASS,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -230,6 +234,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "format", required_argument, NULL, ARG_FORMAT },
|
||||
{ "class", required_argument, NULL, ARG_CLASS },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -262,6 +268,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
+161
-4
@@ -6,21 +6,26 @@
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "capability-util.h"
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "import-common.h"
|
||||
#include "libarchive-util.h"
|
||||
#include "log.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "os-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "selinux-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "tar-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
int import_fork_tar_x(int tree_fd, PidRef *ret_pid) {
|
||||
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
||||
int r;
|
||||
|
||||
assert(tree_fd >= 0);
|
||||
@@ -41,7 +46,7 @@ int import_fork_tar_x(int tree_fd, PidRef *ret_pid) {
|
||||
r = pidref_safe_fork_full(
|
||||
"tar-x",
|
||||
/* stdio_fds= */ NULL,
|
||||
(int[]) { tree_fd, pipefd[0] }, 2,
|
||||
(int[]) { tree_fd, pipefd[0], userns_fd }, userns_fd >= 0 ? 3 : 2,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG,
|
||||
ret_pid);
|
||||
if (r < 0)
|
||||
@@ -58,12 +63,20 @@ int import_fork_tar_x(int tree_fd, PidRef *ret_pid) {
|
||||
|
||||
/* Child */
|
||||
|
||||
if (userns_fd >= 0) {
|
||||
r = detach_mount_namespace_userns(userns_fd);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to join user namespace: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) < 0)
|
||||
log_warning_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
||||
log_debug_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
||||
|
||||
r = capability_bounding_set_drop(retain, true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to drop capabilities, ignoring: %m");
|
||||
log_debug_errno(r, "Failed to drop capabilities, ignoring: %m");
|
||||
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
|
||||
log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m");
|
||||
@@ -291,3 +304,147 @@ int import_allocate_event_with_signals(sd_event **ret) {
|
||||
*ret = TAKE_PTR(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_make_foreign_userns(int *userns_fd) {
|
||||
assert(userns_fd);
|
||||
|
||||
if (*userns_fd >= 0)
|
||||
return 0;
|
||||
|
||||
*userns_fd = nsresource_allocate_userns(/* name= */ NULL, NSRESOURCE_UIDS_64K); /* allocate 64K users */
|
||||
if (*userns_fd < 0)
|
||||
return log_error_errno(*userns_fd, "Failed to allocate transient user namespace: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int import_copy_foreign(
|
||||
int source_fd,
|
||||
int target_fd,
|
||||
int *userns_fd) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(source_fd >= 0);
|
||||
assert(target_fd >= 0);
|
||||
assert(userns_fd);
|
||||
|
||||
/* Copies dir referenced by source_fd into dir referenced by source_fd, moves to the specified userns
|
||||
* for that (allocated if needed), which should be foreign UID range */
|
||||
|
||||
r = import_make_foreign_userns(userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = safe_fork_full(
|
||||
"copy-tree",
|
||||
/* stdio_fds= */ NULL,
|
||||
(int[]) { *userns_fd, source_fd, target_fd }, 3,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
|
||||
/* ret_pid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
r = namespace_enter(
|
||||
/* pidns_fd= */ -EBADF,
|
||||
/* mntns_fd= */ -EBADF,
|
||||
/* netns_fd= */ -EBADF,
|
||||
*userns_fd,
|
||||
/* root_fd= */ -EBADF);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to join user namespace: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = copy_tree_at(
|
||||
source_fd, /* from= */ NULL,
|
||||
target_fd, /* to = */ NULL,
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_REFLINK|COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to copy tree: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_remove_tree_foreign(const char *path, int *userns_fd) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(userns_fd);
|
||||
|
||||
r = import_make_foreign_userns(userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int tree_fd = -EBADF;
|
||||
r = mountfsd_mount_directory(
|
||||
path,
|
||||
*userns_fd,
|
||||
DISSECT_IMAGE_FOREIGN_UID,
|
||||
&tree_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = safe_fork_full(
|
||||
"rm-tree",
|
||||
/* stdio_fds= */ NULL,
|
||||
(int[]) { *userns_fd, tree_fd }, 2,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
|
||||
/* ret_pid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* child */
|
||||
|
||||
r = namespace_enter(
|
||||
/* pidns_fd= */ -EBADF,
|
||||
/* mntns_fd= */ -EBADF,
|
||||
/* netns_fd= */ -EBADF,
|
||||
*userns_fd,
|
||||
/* root_fd= */ -EBADF);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to join user namespace: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
_cleanup_close_ int dfd = fd_reopen(tree_fd, O_DIRECTORY|O_CLOEXEC);
|
||||
if (dfd < 0) {
|
||||
log_error_errno(r, "Failed to reopen tree fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = rm_rf_children(dfd, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, /* root_dev= */ NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to empty '%s' directory in foreign UID mode, ignoring: %m", path);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(userns_fd);
|
||||
|
||||
/* Try the userns dance first, to remove foreign UID range owned trees */
|
||||
if (FLAGS_SET(flags, IMPORT_FOREIGN_UID))
|
||||
(void) import_remove_tree_foreign(path, userns_fd);
|
||||
|
||||
r = rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_MISSING_OK|REMOVE_CHMOD);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to remove '%s': %m", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,16 @@ typedef enum ImportFlags {
|
||||
IMPORT_CONVERT_QCOW2 = 1 << 5, /* raw: if we detect a qcow2 image, unpack it */
|
||||
IMPORT_DIRECT = 1 << 6, /* import without rename games */
|
||||
IMPORT_SYNC = 1 << 7, /* fsync() right before we are done */
|
||||
IMPORT_FOREIGN_UID = 1 << 8, /* tar: go via nsresourced/mountfsd and make owned by foreign UID */
|
||||
|
||||
/* When pulling these flags are defined too */
|
||||
IMPORT_PULL_SETTINGS = 1 << 8, /* download .nspawn settings file */
|
||||
IMPORT_PULL_ROOTHASH = 1 << 9, /* only for raw: download .roothash file for verity */
|
||||
IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 10, /* only for raw: download .roothash.p7s file for verity */
|
||||
IMPORT_PULL_VERITY = 1 << 11, /* only for raw: download .verity file for verity */
|
||||
IMPORT_PULL_SETTINGS = 1 << 9, /* download .nspawn settings file */
|
||||
IMPORT_PULL_ROOTHASH = 1 << 10, /* only for raw: download .roothash file for verity */
|
||||
IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 11, /* only for raw: download .roothash.p7s file for verity */
|
||||
IMPORT_PULL_VERITY = 1 << 12, /* only for raw: download .verity file for verity */
|
||||
|
||||
/* The supported flags for the tar and the raw importing */
|
||||
IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
|
||||
IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC|IMPORT_FOREIGN_UID,
|
||||
IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
|
||||
|
||||
/* The supported flags for the tar and the raw pulling */
|
||||
@@ -34,7 +35,7 @@ typedef enum ImportFlags {
|
||||
} ImportFlags;
|
||||
|
||||
int import_fork_tar_c(const char *path, PidRef *ret);
|
||||
int import_fork_tar_x(int tree_fd, PidRef *ret_pid);
|
||||
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
|
||||
|
||||
int import_mangle_os_tree(const char *path);
|
||||
|
||||
@@ -42,4 +43,11 @@ bool import_validate_local(const char *name, ImportFlags flags);
|
||||
|
||||
int import_allocate_event_with_signals(sd_event **ret);
|
||||
|
||||
int import_make_foreign_userns(int *userns_fd);
|
||||
|
||||
int import_copy_foreign(int source_fd, int target_fd, int *userns_fd);
|
||||
|
||||
int import_remove_tree_foreign(const char *path, int *userns_fd);
|
||||
int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags);
|
||||
|
||||
#define IMPORT_BUFFER_SIZE (128U*1024U)
|
||||
|
||||
+27
-5
@@ -35,10 +35,12 @@ static bool arg_btrfs_subvol = true;
|
||||
static bool arg_btrfs_quota = true;
|
||||
static bool arg_sync = true;
|
||||
static bool arg_direct = false;
|
||||
static const char *arg_image_root = NULL;
|
||||
static char *arg_image_root = NULL;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep);
|
||||
|
||||
typedef struct ProgressInfo {
|
||||
RateLimit limit;
|
||||
char *path;
|
||||
@@ -280,7 +282,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" subvolume\n"
|
||||
" --sync=BOOL Controls whether to sync() before completing\n"
|
||||
" --class=CLASS Select image class (machine, sysext, confext,\n"
|
||||
" portable)\n",
|
||||
" portable)\n"
|
||||
" --system Operate in per-system mode\n"
|
||||
" --user Operate in per-user mode\n",
|
||||
program_invocation_short_name,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
@@ -302,6 +306,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_BTRFS_QUOTA,
|
||||
ARG_SYNC,
|
||||
ARG_CLASS,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -315,6 +321,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
|
||||
{ "sync", required_argument, NULL, ARG_SYNC },
|
||||
{ "class", required_argument, NULL, ARG_CLASS },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -338,7 +346,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_ROOT:
|
||||
arg_image_root = optarg;
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_READ_ONLY:
|
||||
@@ -377,6 +388,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -384,8 +403,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!arg_image_root)
|
||||
arg_image_root = image_root_to_string(arg_class);
|
||||
if (!arg_image_root) {
|
||||
r = image_root_pick(arg_runtime_scope < 0 ? RUNTIME_SCOPE_SYSTEM : arg_runtime_scope, arg_class, /* runtime= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick image root: %m");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "specifier.h"
|
||||
#include "string-util.h"
|
||||
#include "unit-name.h"
|
||||
@@ -200,7 +201,10 @@ static int parse_pull_expression(const char *v) {
|
||||
if (!GREEDY_REALLOC(arg_transfers, arg_n_transfers + 1))
|
||||
return log_oom();
|
||||
|
||||
const char *image_root = runtime ? image_root_runtime_to_string(class) : image_root_to_string(class);
|
||||
_cleanup_free_ char *image_root = NULL;
|
||||
r = image_root_pick(RUNTIME_SCOPE_SYSTEM, class, runtime, &image_root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick image root: %m");
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
|
||||
r = sd_json_buildo(
|
||||
@@ -220,7 +224,7 @@ static int parse_pull_expression(const char *v) {
|
||||
.type = type,
|
||||
.local = TAKE_PTR(local),
|
||||
.remote = TAKE_PTR(remote),
|
||||
.image_root = image_root,
|
||||
.image_root = TAKE_PTR(image_root),
|
||||
.json = TAKE_PTR(j),
|
||||
.blockdev = blockdev,
|
||||
};
|
||||
|
||||
+43
-21
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
@@ -46,6 +47,7 @@ typedef struct TarImport {
|
||||
int input_fd;
|
||||
int tar_fd;
|
||||
int tree_fd;
|
||||
int userns_fd;
|
||||
|
||||
ImportCompress compress;
|
||||
|
||||
@@ -73,7 +75,10 @@ TarImport* tar_import_unref(TarImport *i) {
|
||||
|
||||
pidref_done_sigkill_wait(&i->tar_pid);
|
||||
|
||||
rm_rf_subvolume_and_free(i->temp_path);
|
||||
if (i->temp_path) {
|
||||
import_remove_tree(i->temp_path, &i->userns_fd, i->flags);
|
||||
free(i->temp_path);
|
||||
}
|
||||
|
||||
import_compress_free(&i->compress);
|
||||
|
||||
@@ -81,6 +86,7 @@ TarImport* tar_import_unref(TarImport *i) {
|
||||
|
||||
safe_close(i->tar_fd);
|
||||
safe_close(i->tree_fd);
|
||||
safe_close(i->userns_fd);
|
||||
|
||||
free(i->final_path);
|
||||
free(i->image_root);
|
||||
@@ -114,6 +120,7 @@ int tar_import_new(
|
||||
.input_fd = -EBADF,
|
||||
.tar_fd = -EBADF,
|
||||
.tree_fd = -EBADF,
|
||||
.userns_fd = -EBADF,
|
||||
.on_finished = on_finished,
|
||||
.userdata = userdata,
|
||||
.last_percent = UINT_MAX,
|
||||
@@ -200,8 +207,8 @@ static int tar_import_finish(TarImport *i) {
|
||||
AT_FDCWD, d,
|
||||
AT_FDCWD, i->final_path,
|
||||
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY|INSTALL_GRACEFUL : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS|INSTALL_GRACEFUL : 0));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to move '%s' into place: %m", i->final_path ?: i->local);
|
||||
|
||||
@@ -244,26 +251,41 @@ static int tar_import_fork_tar(TarImport *i) {
|
||||
if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
|
||||
(void) rm_rf(d, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_make_fallback(AT_FDCWD, d, 0755);
|
||||
else
|
||||
r = RET_NERRNO(mkdir(d, 0755));
|
||||
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
|
||||
* because in that case our temporary path collided */
|
||||
r = 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", d);
|
||||
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
|
||||
if (!(i->flags & IMPORT_DIRECT))
|
||||
(void) import_assign_pool_quota_and_warn(root);
|
||||
(void) import_assign_pool_quota_and_warn(d);
|
||||
if (FLAGS_SET(i->flags, IMPORT_FOREIGN_UID)) {
|
||||
r = import_make_foreign_userns(&i->userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int directory_fd = -EBADF;
|
||||
r = mountfsd_make_directory(d, /* flags= */ 0, &directory_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mountfsd_mount_directory_fd(directory_fd, i->userns_fd, DISSECT_IMAGE_FOREIGN_UID, &i->tree_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_make_fallback(AT_FDCWD, d, 0755);
|
||||
else
|
||||
r = RET_NERRNO(mkdir(d, 0755));
|
||||
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
|
||||
* because in that case our temporary path collided */
|
||||
r = 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", d);
|
||||
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
|
||||
if (!(i->flags & IMPORT_DIRECT))
|
||||
(void) import_assign_pool_quota_and_warn(root);
|
||||
(void) import_assign_pool_quota_and_warn(d);
|
||||
}
|
||||
|
||||
i->tree_fd = open(d, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (i->tree_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", d);
|
||||
}
|
||||
|
||||
i->tree_fd = open(d, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (i->tree_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", d);
|
||||
|
||||
i->tar_fd = import_fork_tar_x(i->tree_fd, &i->tar_pid);
|
||||
i->tar_fd = import_fork_tar_x(i->tree_fd, i->userns_fd, &i->tar_pid);
|
||||
if (i->tar_fd < 0)
|
||||
return i->tar_fd;
|
||||
|
||||
|
||||
+30
-5
@@ -26,12 +26,14 @@
|
||||
#include "string-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
static const char *arg_image_root = NULL;
|
||||
static char *arg_image_root = NULL;
|
||||
static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
|
||||
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep);
|
||||
|
||||
static int normalize_local(const char *local, char **ret) {
|
||||
_cleanup_free_ char *ll = NULL;
|
||||
int r;
|
||||
@@ -290,7 +292,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --offset=BYTES Offset to seek to in destination\n"
|
||||
" --size-max=BYTES Maximum number of bytes to write to destination\n"
|
||||
" --class=CLASS Select image class (machine, sysext, confext,\n"
|
||||
" portable)\n",
|
||||
" portable)\n"
|
||||
" --system Operate in per-system mode\n"
|
||||
" --user Operate in per-user mode\n",
|
||||
program_invocation_short_name,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
@@ -315,6 +319,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_OFFSET,
|
||||
ARG_SIZE_MAX,
|
||||
ARG_CLASS,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -331,6 +337,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "offset", required_argument, NULL, ARG_OFFSET },
|
||||
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
|
||||
{ "class", required_argument, NULL, ARG_CLASS },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -354,7 +362,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_ROOT:
|
||||
arg_image_root = optarg;
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_READ_ONLY:
|
||||
@@ -430,6 +441,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -446,8 +465,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
|
||||
|
||||
if (!arg_image_root)
|
||||
arg_image_root = image_root_to_string(arg_class);
|
||||
if (!arg_image_root) {
|
||||
r = image_root_pick(arg_runtime_scope < 0 ? RUNTIME_SCOPE_SYSTEM : arg_runtime_scope, arg_class, /* runtime= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick image root: %m");
|
||||
}
|
||||
|
||||
if (arg_runtime_scope == RUNTIME_SCOPE_USER)
|
||||
arg_import_flags |= IMPORT_FOREIGN_UID;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
+27
-4
@@ -45,19 +45,28 @@ static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
|
||||
static const char* arg_format = NULL;
|
||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||
static ImageClass arg_image_class = _IMAGE_CLASS_INVALID;
|
||||
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
|
||||
#define PROGRESS_PREFIX "Total:"
|
||||
|
||||
static int settle_image_class(void) {
|
||||
int r;
|
||||
|
||||
if (arg_image_class < 0) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
|
||||
for (ImageClass class = 0; class < _IMAGE_CLASS_MAX; class++)
|
||||
for (ImageClass class = 0; class < _IMAGE_CLASS_MAX; class++) {
|
||||
_cleanup_free_ char *root = NULL;
|
||||
|
||||
r = image_root_pick(arg_runtime_scope, class, /* runtime= */ false, &root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick image root: %m");
|
||||
|
||||
if (strextendf_with_separator(&j, ", ", "%s (downloads to %s/)",
|
||||
image_class_to_string(class),
|
||||
image_root_to_string(class)) < 0)
|
||||
root) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"No image class specified, retry with --class= set to one of: %s.", j);
|
||||
@@ -1014,6 +1023,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --no-ask-password Do not ask for system passwords\n"
|
||||
" -H --host=[USER@]HOST Operate on remote host\n"
|
||||
" -M --machine=CONTAINER Operate on local container\n"
|
||||
" --system Connect to system machine manager\n"
|
||||
" --user Connect to user machine manager\n"
|
||||
" --read-only Create read-only image\n"
|
||||
" -q --quiet Suppress output\n"
|
||||
" --json=pretty|short|off Generate JSON output\n"
|
||||
@@ -1055,6 +1066,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_FORMAT,
|
||||
ARG_CLASS,
|
||||
ARG_KEEP_DOWNLOAD,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -1073,6 +1086,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "format", required_argument, NULL, ARG_FORMAT },
|
||||
{ "class", required_argument, NULL, ARG_CLASS },
|
||||
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1198,6 +1213,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1240,9 +1263,9 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
|
||||
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
|
||||
if (r < 0)
|
||||
return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
|
||||
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
|
||||
|
||||
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
|
||||
|
||||
|
||||
+117
-78
@@ -32,6 +32,7 @@
|
||||
#include "notify-recv.h"
|
||||
#include "os-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-lookup.h"
|
||||
#include "percent-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
@@ -100,7 +101,8 @@ typedef struct Transfer {
|
||||
|
||||
typedef struct Manager {
|
||||
sd_event *event;
|
||||
sd_bus *bus;
|
||||
sd_bus *api_bus;
|
||||
sd_bus *system_bus;
|
||||
sd_varlink_server *varlink_server;
|
||||
|
||||
uint32_t current_transfer_id;
|
||||
@@ -113,7 +115,7 @@ typedef struct Manager {
|
||||
bool use_btrfs_subvol;
|
||||
bool use_btrfs_quota;
|
||||
|
||||
RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
|
||||
RuntimeScope runtime_scope;
|
||||
} Manager;
|
||||
|
||||
#define TRANSFERS_MAX 64
|
||||
@@ -223,7 +225,7 @@ static void transfer_send_log_line(Transfer *t, const char *line) {
|
||||
log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
|
||||
|
||||
r = sd_bus_emit_signal(
|
||||
t->manager->bus,
|
||||
t->manager->api_bus,
|
||||
t->object_path,
|
||||
"org.freedesktop.import1.Transfer",
|
||||
"LogMessage",
|
||||
@@ -254,7 +256,7 @@ static void transfer_send_progress_update(Transfer *t) {
|
||||
double progress = transfer_percent_as_double(t);
|
||||
|
||||
r = sd_bus_emit_signal(
|
||||
t->manager->bus,
|
||||
t->manager->api_bus,
|
||||
t->object_path,
|
||||
"org.freedesktop.import1.Transfer",
|
||||
"ProgressUpdate",
|
||||
@@ -335,7 +337,7 @@ static int transfer_finalize(Transfer *t, bool success) {
|
||||
transfer_send_logs(t, true);
|
||||
|
||||
r = sd_bus_emit_signal(
|
||||
t->manager->bus,
|
||||
t->manager->api_bus,
|
||||
"/org/freedesktop/import1",
|
||||
"org.freedesktop.import1.Manager",
|
||||
"TransferRemoved",
|
||||
@@ -443,6 +445,7 @@ static int transfer_start(Transfer *t) {
|
||||
const char *cmd[] = {
|
||||
NULL, /* systemd-import, systemd-import-fs, systemd-export or systemd-pull */
|
||||
NULL, /* tar, raw */
|
||||
NULL, /* --system or --user */
|
||||
NULL, /* --verify= */
|
||||
NULL, /* verify argument */
|
||||
NULL, /* --class= */
|
||||
@@ -524,6 +527,8 @@ static int transfer_start(Transfer *t) {
|
||||
;
|
||||
}
|
||||
|
||||
cmd[k++] = runtime_scope_cmdline_option_to_string(t->manager->runtime_scope);
|
||||
|
||||
if (t->verify != _IMPORT_VERIFY_INVALID) {
|
||||
cmd[k++] = "--verify";
|
||||
cmd[k++] = import_verify_to_string(t->verify);
|
||||
@@ -602,7 +607,7 @@ static int transfer_start(Transfer *t) {
|
||||
return r;
|
||||
|
||||
r = sd_bus_emit_signal(
|
||||
t->manager->bus,
|
||||
t->manager->api_bus,
|
||||
"/org/freedesktop/import1",
|
||||
"org.freedesktop.import1.Manager",
|
||||
"TransferNew",
|
||||
@@ -630,7 +635,8 @@ static Manager *manager_unref(Manager *m) {
|
||||
|
||||
hashmap_free(m->polkit_registry);
|
||||
|
||||
m->bus = sd_bus_flush_close_unref(m->bus);
|
||||
m->api_bus = sd_bus_flush_close_unref(m->api_bus);
|
||||
m->system_bus = sd_bus_flush_close_unref(m->system_bus);
|
||||
m->varlink_server = sd_varlink_server_unref(m->varlink_server);
|
||||
|
||||
sd_event_unref(m->event);
|
||||
@@ -681,7 +687,7 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_new(Manager **ret) {
|
||||
static int manager_new(RuntimeScope scope, Manager **ret) {
|
||||
_cleanup_(manager_unrefp) Manager *m = NULL;
|
||||
int r;
|
||||
|
||||
@@ -694,7 +700,7 @@ static int manager_new(Manager **ret) {
|
||||
*m = (Manager) {
|
||||
.use_btrfs_subvol = true,
|
||||
.use_btrfs_quota = true,
|
||||
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
|
||||
.runtime_scope = scope,
|
||||
};
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
@@ -757,16 +763,18 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
|
||||
|
||||
assert(msg);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.import",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.import",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
}
|
||||
|
||||
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
|
||||
const char *sclass;
|
||||
@@ -858,16 +866,18 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
|
||||
|
||||
assert(msg);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.import",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.import",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
}
|
||||
|
||||
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
|
||||
const char *sclass;
|
||||
@@ -956,16 +966,18 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
|
||||
|
||||
assert(msg);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.export",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.export",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
}
|
||||
|
||||
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
|
||||
const char *sclass;
|
||||
@@ -1054,16 +1066,18 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
|
||||
|
||||
assert(msg);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.pull",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
|
||||
r = bus_verify_polkit_async(
|
||||
msg,
|
||||
"org.freedesktop.import1.pull",
|
||||
/* details= */ NULL,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
}
|
||||
|
||||
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
|
||||
const char *sclass;
|
||||
@@ -1702,28 +1716,43 @@ static int manager_connect_bus(Manager *m) {
|
||||
|
||||
assert(m);
|
||||
assert(m->event);
|
||||
assert(!m->bus);
|
||||
assert(!m->system_bus);
|
||||
assert(!m->api_bus);
|
||||
|
||||
r = bus_open_system_watch_bind(&m->bus);
|
||||
r = bus_open_system_watch_bind(&m->system_bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get system bus connection: %m");
|
||||
|
||||
r = bus_add_implementation(m->bus, &manager_object, m);
|
||||
r = sd_bus_attach_event(m->system_bus, m->event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach system bus to event loop: %m");
|
||||
|
||||
if (m->runtime_scope == RUNTIME_SCOPE_SYSTEM)
|
||||
m->api_bus = sd_bus_ref(m->system_bus);
|
||||
else {
|
||||
assert(m->runtime_scope == RUNTIME_SCOPE_USER);
|
||||
|
||||
r = sd_bus_default_user(&m->api_bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get user bus connection: %m");
|
||||
|
||||
r = sd_bus_attach_event(m->api_bus, m->event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach user bus to event loop: %m");
|
||||
}
|
||||
|
||||
r = bus_add_implementation(m->api_bus, &manager_object, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_log_control_api_register(m->bus);
|
||||
r = bus_log_control_api_register(m->api_bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL);
|
||||
r = sd_bus_request_name_async(m->api_bus, NULL, "org.freedesktop.import1", 0, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request name: %m");
|
||||
|
||||
r = sd_bus_attach_event(m->bus, m->event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1867,19 +1896,21 @@ static int vl_method_pull(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
if (manager_find(m, tt, p.remote))
|
||||
return sd_varlink_errorbo(link, "io.systemd.Import.AlreadyInProgress", SD_JSON_BUILD_PAIR_STRING("remote", p.remote));
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
m->bus,
|
||||
"org.freedesktop.import1.pull",
|
||||
(const char**) STRV_MAKE(
|
||||
"remote", p.remote,
|
||||
"local", p.local,
|
||||
"class", image_class_to_string(p.class),
|
||||
"type", import_type_to_string(p.type),
|
||||
"verify", import_verify_to_string(p.verify)),
|
||||
&m->polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
m->system_bus,
|
||||
"org.freedesktop.import1.pull",
|
||||
(const char**) STRV_MAKE(
|
||||
"remote", p.remote,
|
||||
"local", p.local,
|
||||
"class", image_class_to_string(p.class),
|
||||
"type", import_type_to_string(p.type),
|
||||
"verify", import_verify_to_string(p.verify)),
|
||||
&m->polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(transfer_unrefp) Transfer *t = NULL;
|
||||
|
||||
@@ -1938,9 +1969,11 @@ static int manager_connect_varlink(Manager *m) {
|
||||
assert(m->event);
|
||||
assert(!m->varlink_server);
|
||||
|
||||
r = varlink_server_new(&m->varlink_server,
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA,
|
||||
m);
|
||||
r = varlink_server_new(
|
||||
&m->varlink_server,
|
||||
(m->runtime_scope != RUNTIME_SCOPE_USER ? SD_VARLINK_SERVER_ACCOUNT_UID : 0)|
|
||||
SD_VARLINK_SERVER_INHERIT_USERDATA,
|
||||
m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate varlink server object: %m");
|
||||
|
||||
@@ -1969,7 +2002,12 @@ static int manager_connect_varlink(Manager *m) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind to passed Varlink sockets: %m");
|
||||
if (r == 0) {
|
||||
r = sd_varlink_server_listen_address(m->varlink_server, "/run/systemd/io.systemd.Import", 0666);
|
||||
_cleanup_free_ char *socket_path = NULL;
|
||||
r = runtime_directory_generic(m->runtime_scope, "systemd/io.systemd.Import", &socket_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine socket path: %m");
|
||||
|
||||
r = sd_varlink_server_listen_address(m->varlink_server, socket_path, runtime_scope_to_socket_mode(m->runtime_scope) | SD_VARLINK_SERVER_MODE_MKDIR_0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind to Varlink socket: %m");
|
||||
}
|
||||
@@ -2009,6 +2047,7 @@ static void manager_parse_env(Manager *m) {
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(manager_unrefp) Manager *m = NULL;
|
||||
RuntimeScope scope = RUNTIME_SCOPE_SYSTEM;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
@@ -2017,7 +2056,7 @@ static int run(int argc, char *argv[]) {
|
||||
"VM and container image import and export service.",
|
||||
BUS_IMPLEMENTATIONS(&manager_object,
|
||||
&log_control_object),
|
||||
/* runtime_scope= */ NULL,
|
||||
&scope,
|
||||
argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
@@ -2026,7 +2065,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
|
||||
|
||||
r = manager_new(&m);
|
||||
r = manager_new(scope, &m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate manager object: %m");
|
||||
|
||||
@@ -2046,7 +2085,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = bus_event_loop_with_idle(
|
||||
m->event,
|
||||
m->bus,
|
||||
m->api_bus,
|
||||
"org.freedesktop.import1",
|
||||
DEFAULT_EXIT_USEC,
|
||||
manager_check_idle,
|
||||
|
||||
@@ -111,6 +111,9 @@ install_data('org.freedesktop.import1.conf',
|
||||
install_dir : dbuspolicydir)
|
||||
install_data('org.freedesktop.import1.service',
|
||||
install_dir : dbussystemservicedir)
|
||||
install_data('org.freedesktop.import1.service-for-session',
|
||||
install_dir : dbussessionservicedir,
|
||||
rename : 'org.freedesktop.import1.service')
|
||||
install_data('org.freedesktop.import1.policy',
|
||||
install_dir : polkitpolicydir)
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[D-BUS Service]
|
||||
Name=org.freedesktop.import1
|
||||
Exec=/bin/false
|
||||
SystemdService=dbus-org.freedesktop.import1.service
|
||||
+108
-40
@@ -9,11 +9,10 @@
|
||||
#include "btrfs-util.h"
|
||||
#include "copy.h"
|
||||
#include "curl-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "import-common.h"
|
||||
#include "import-util.h"
|
||||
#include "install-file.h"
|
||||
#include "log.h"
|
||||
#include "mkdir-label.h"
|
||||
@@ -26,6 +25,7 @@
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "uid-classification.h"
|
||||
#include "web-util.h"
|
||||
|
||||
typedef enum TarProgress {
|
||||
@@ -64,6 +64,7 @@ typedef struct TarPull {
|
||||
char *checksum;
|
||||
|
||||
int tree_fd;
|
||||
int userns_fd;
|
||||
} TarPull;
|
||||
|
||||
TarPull* tar_pull_unref(TarPull *i) {
|
||||
@@ -80,7 +81,10 @@ TarPull* tar_pull_unref(TarPull *i) {
|
||||
curl_glue_unref(i->glue);
|
||||
sd_event_unref(i->event);
|
||||
|
||||
rm_rf_subvolume_and_free(i->temp_path);
|
||||
if (i->temp_path) {
|
||||
import_remove_tree(i->temp_path, &i->userns_fd, i->flags);
|
||||
free(i->temp_path);
|
||||
}
|
||||
unlink_and_free(i->settings_temp_path);
|
||||
|
||||
free(i->final_path);
|
||||
@@ -90,6 +94,7 @@ TarPull* tar_pull_unref(TarPull *i) {
|
||||
free(i->checksum);
|
||||
|
||||
safe_close(i->tree_fd);
|
||||
safe_close(i->userns_fd);
|
||||
|
||||
return mfree(i);
|
||||
}
|
||||
@@ -138,6 +143,7 @@ int tar_pull_new(
|
||||
.glue = TAKE_PTR(g),
|
||||
.tar_pid = PIDREF_NULL,
|
||||
.tree_fd = -EBADF,
|
||||
.userns_fd = -EBADF,
|
||||
};
|
||||
|
||||
i->glue->on_finished = pull_job_curl_on_finished;
|
||||
@@ -233,6 +239,9 @@ static int tar_pull_make_local_copy(TarPull *i) {
|
||||
if (!i->local)
|
||||
return 0;
|
||||
|
||||
/* Creates a copy/clone of the original downloaded version (which is supposed to remain untouched)
|
||||
* under a local image name (which may then be modified) */
|
||||
|
||||
assert(i->final_path);
|
||||
|
||||
p = path_join(i->image_root, i->local);
|
||||
@@ -244,18 +253,61 @@ static int tar_pull_make_local_copy(TarPull *i) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
|
||||
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_snapshot_at(
|
||||
AT_FDCWD, i->final_path,
|
||||
AT_FDCWD, t,
|
||||
(i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
|
||||
BTRFS_SNAPSHOT_FALLBACK_COPY|
|
||||
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
|
||||
BTRFS_SNAPSHOT_RECURSIVE);
|
||||
else
|
||||
r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create local image: %m");
|
||||
if (FLAGS_SET(i->flags, IMPORT_FOREIGN_UID)) {
|
||||
/* Copy in userns */
|
||||
|
||||
r = import_make_foreign_userns(&i->userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Usually, tar_pull_job_on_open_disk_tar() would allocate ->tree_fd for us, but if
|
||||
* already downloaded the image before, and are just making a copy of the original
|
||||
* download, we need to open ->tree_fd now */
|
||||
if (i->tree_fd < 0) {
|
||||
_cleanup_close_ int directory_fd = open(i->final_path, O_DIRECTORY|O_CLOEXEC);
|
||||
if (directory_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", i->final_path);
|
||||
|
||||
struct stat st;
|
||||
if (fstat(directory_fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat '%s': %m", i->final_path);
|
||||
|
||||
if (uid_is_foreign(st.st_uid)) {
|
||||
r = mountfsd_mount_directory_fd(directory_fd, i->userns_fd, DISSECT_IMAGE_FOREIGN_UID, &i->tree_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
i->tree_fd = TAKE_FD(directory_fd);
|
||||
}
|
||||
|
||||
_cleanup_close_ int directory_fd = -EBADF;
|
||||
r = mountfsd_make_directory(t, /* flags= */ 0, &directory_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int copy_fd = -EBADF;
|
||||
r = mountfsd_mount_directory_fd(directory_fd, i->userns_fd, DISSECT_IMAGE_FOREIGN_UID, ©_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = import_copy_foreign(i->tree_fd, copy_fd, &i->userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
/* Copy locally */
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_snapshot_at(
|
||||
AT_FDCWD, i->final_path,
|
||||
AT_FDCWD, t,
|
||||
(i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
|
||||
BTRFS_SNAPSHOT_FALLBACK_COPY|
|
||||
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
|
||||
BTRFS_SNAPSHOT_RECURSIVE);
|
||||
else
|
||||
r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create original download image: %m");
|
||||
}
|
||||
|
||||
source = t;
|
||||
} else
|
||||
@@ -264,8 +316,8 @@ static int tar_pull_make_local_copy(TarPull *i) {
|
||||
r = install_file(AT_FDCWD, source,
|
||||
AT_FDCWD, p,
|
||||
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY|INSTALL_GRACEFUL : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS|INSTALL_GRACEFUL : 0));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to install local image '%s': %m", p);
|
||||
|
||||
@@ -427,8 +479,8 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
||||
r = install_file(
|
||||
AT_FDCWD, i->local,
|
||||
AT_FDCWD, NULL,
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
|
||||
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY|INSTALL_GRACEFUL : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS|INSTALL_GRACEFUL : 0));
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to finalize '%s': %m", i->local);
|
||||
goto finish;
|
||||
@@ -453,8 +505,8 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
||||
r = install_file(
|
||||
AT_FDCWD, i->temp_path,
|
||||
AT_FDCWD, i->final_path,
|
||||
(i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
|
||||
(i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY|INSTALL_GRACEFUL : 0) |
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS|INSTALL_GRACEFUL : 0));
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
|
||||
goto finish;
|
||||
@@ -480,7 +532,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
||||
r = install_file(
|
||||
AT_FDCWD, i->settings_temp_path,
|
||||
AT_FDCWD, i->settings_path,
|
||||
INSTALL_READ_ONLY|
|
||||
INSTALL_READ_ONLY|INSTALL_GRACEFUL|
|
||||
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
|
||||
@@ -537,26 +589,42 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
|
||||
if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
|
||||
(void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_make_fallback(AT_FDCWD, where, 0755);
|
||||
else
|
||||
r = RET_NERRNO(mkdir(where, 0755));
|
||||
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
|
||||
* because in that case our temporary path collided */
|
||||
r = 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where);
|
||||
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
|
||||
if (!(i->flags & IMPORT_DIRECT))
|
||||
(void) import_assign_pool_quota_and_warn(i->image_root);
|
||||
(void) import_assign_pool_quota_and_warn(where);
|
||||
if (FLAGS_SET(i->flags, IMPORT_FOREIGN_UID)) {
|
||||
r = import_make_foreign_userns(&i->userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int directory_fd = -EBADF;
|
||||
r = mountfsd_make_directory(where, /* flags= */ 0, &directory_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mountfsd_mount_directory_fd(directory_fd, i->userns_fd, DISSECT_IMAGE_FOREIGN_UID, &i->tree_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||
r = btrfs_subvol_make_fallback(AT_FDCWD, where, 0755);
|
||||
else
|
||||
r = RET_NERRNO(mkdir(where, 0755));
|
||||
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
|
||||
* because in that case our temporary path collided */
|
||||
r = 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where);
|
||||
|
||||
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
|
||||
if (!(i->flags & IMPORT_DIRECT))
|
||||
(void) import_assign_pool_quota_and_warn(i->image_root);
|
||||
(void) import_assign_pool_quota_and_warn(where);
|
||||
}
|
||||
|
||||
i->tree_fd = open(where, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (i->tree_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", where);
|
||||
}
|
||||
|
||||
i->tree_fd = open(where, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (i->tree_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", where);
|
||||
|
||||
j->disk_fd = import_fork_tar_x(i->tree_fd, &i->tar_pid);
|
||||
j->disk_fd = import_fork_tar_x(i->tree_fd, i->userns_fd, &i->tar_pid);
|
||||
if (j->disk_fd < 0)
|
||||
return j->disk_fd;
|
||||
|
||||
|
||||
+29
-5
@@ -28,7 +28,7 @@
|
||||
#include "verbs.h"
|
||||
#include "web-util.h"
|
||||
|
||||
static const char *arg_image_root = NULL;
|
||||
static char *arg_image_root = NULL;
|
||||
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
|
||||
static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHASH | IMPORT_PULL_ROOTHASH_SIGNATURE | IMPORT_PULL_VERITY | IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
|
||||
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
|
||||
@@ -37,6 +37,7 @@ static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep);
|
||||
|
||||
static int normalize_local(const char *local, const char *url, char **ret) {
|
||||
_cleanup_free_ char *ll = NULL;
|
||||
@@ -275,7 +276,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --class=CLASS Select image class (machine, sysext, confext,\n"
|
||||
" portable)\n"
|
||||
" --keep-download=BOOL Keep a copy pristine copy of the downloaded file\n"
|
||||
" around\n",
|
||||
" around\n"
|
||||
" --system Operate in per-system mode\n"
|
||||
" --user Operate in per-user mode\n",
|
||||
program_invocation_short_name,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
@@ -306,6 +309,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_SIZE_MAX,
|
||||
ARG_CLASS,
|
||||
ARG_KEEP_DOWNLOAD,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -328,6 +333,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
|
||||
{ "class", required_argument, NULL, ARG_CLASS },
|
||||
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -352,7 +359,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_ROOT:
|
||||
arg_image_root = optarg;
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_VERIFY: {
|
||||
@@ -508,6 +518,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
auto_keep_download = false;
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -527,8 +545,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_checksum && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
|
||||
|
||||
if (!arg_image_root)
|
||||
arg_image_root = image_root_to_string(arg_class);
|
||||
if (!arg_image_root) {
|
||||
r = image_root_pick(arg_runtime_scope < 0 ? RUNTIME_SCOPE_SYSTEM : arg_runtime_scope, arg_class, /* runtime= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pick image root: %m");
|
||||
}
|
||||
|
||||
/* .nspawn settings files only really make sense for machine images, not for sysext/confext/portable */
|
||||
if (auto_settings && arg_class != IMAGE_MACHINE)
|
||||
@@ -539,6 +560,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (auto_keep_download)
|
||||
SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, arg_class == IMAGE_MACHINE);
|
||||
|
||||
if (arg_runtime_scope == RUNTIME_SCOPE_USER)
|
||||
arg_import_flags |= IMPORT_FOREIGN_UID;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -6007,7 +6007,7 @@ static int run(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
userns_fd = nsresource_allocate_userns(userns_name, UINT64_C(0x10000));
|
||||
userns_fd = nsresource_allocate_userns(userns_name, NSRESOURCE_UIDS_64K); /* allocate 64K UIDs */
|
||||
if (userns_fd < 0) {
|
||||
r = log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
|
||||
goto finish;
|
||||
@@ -6019,7 +6019,7 @@ static int run(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
arg_uid_range = UINT32_C(0x10000);
|
||||
arg_uid_range = NSRESOURCE_UIDS_64K;
|
||||
}
|
||||
|
||||
if (arg_directory) {
|
||||
|
||||
+9
-4
@@ -657,7 +657,10 @@ static int hardlink_context_setup(
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = tempfn_random_child(to, "hardlink", &c->subdir);
|
||||
if (to)
|
||||
r = tempfn_random_child(to, "hardlink", &c->subdir);
|
||||
else
|
||||
r = tempfn_random("hardlink", /* extra= */ NULL, &c->subdir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1127,7 +1130,6 @@ static int fd_copy_directory(
|
||||
int r;
|
||||
|
||||
assert(st);
|
||||
assert(to);
|
||||
|
||||
if (depth_left == 0)
|
||||
return -ENAMETOOLONG;
|
||||
@@ -1257,7 +1259,7 @@ static int fd_copy_directory(
|
||||
}
|
||||
|
||||
finish:
|
||||
if (!exists) {
|
||||
if (FLAGS_SET(copy_flags, COPY_MERGE_APPLY_STAT) || !exists) {
|
||||
if (fchown(fdt,
|
||||
uid_is_valid(override_uid) ? override_uid : st->st_uid,
|
||||
gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
|
||||
@@ -1345,6 +1347,10 @@ static int fd_copy_tree_generic(
|
||||
override_gid, copy_flags, denylist, subvolumes, hardlink_context,
|
||||
display_path, progress_path, progress_bytes, userdata);
|
||||
|
||||
/* Only if we are copying a directory we are fine if the target dir is referenced by fd only */
|
||||
if (!to)
|
||||
return -ENOTDIR;
|
||||
|
||||
DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
|
||||
if (t == DENY_INODE) {
|
||||
log_debug("%s is in the denylist, ignoring", from ?: "file to copy");
|
||||
@@ -1382,7 +1388,6 @@ int copy_tree_at_full(
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(to);
|
||||
assert(!FLAGS_SET(copy_flags, COPY_LOCK_BSD));
|
||||
|
||||
if (fstatat(fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW | (isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef enum CopyFlags {
|
||||
*/
|
||||
COPY_NOCOW_AFTER = 1 << 20,
|
||||
COPY_PRESERVE_FS_VERITY = 1 << 21, /* Preserve fs-verity when copying. */
|
||||
COPY_MERGE_APPLY_STAT = 1 << 22, /* When we reuse an existing directory inode, apply source ownership/mode/xattrs/timestamps */
|
||||
} CopyFlags;
|
||||
|
||||
typedef enum DenyType {
|
||||
|
||||
@@ -110,7 +110,7 @@ static const char *const image_root_table[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_CONFEXT] = "/var/lib/confexts",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root, ImageClass);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_root, ImageClass);
|
||||
|
||||
static const char *const image_root_runtime_table[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_MACHINE] = "/run/machines",
|
||||
@@ -119,7 +119,7 @@ static const char *const image_root_runtime_table[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_CONFEXT] = "/run/confexts",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root_runtime, ImageClass);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_root_runtime, ImageClass);
|
||||
|
||||
static const char *const image_dirname_table[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_MACHINE] = "machines",
|
||||
@@ -2022,3 +2022,45 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
|
||||
|
||||
int image_root_pick(
|
||||
RuntimeScope scope,
|
||||
ImageClass c,
|
||||
bool runtime,
|
||||
char **ret) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(scope >= 0);
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
assert(c >= 0);
|
||||
assert(c < _IMAGE_CLASS_MAX);
|
||||
assert(ret);
|
||||
|
||||
/* Picks the primary target directory for downloads, depending on invocation contexts */
|
||||
|
||||
_cleanup_free_ char *s = NULL;
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM: {
|
||||
s = strdup(runtime ? image_root_runtime_to_string(c) : image_root_to_string(c));
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
r = sd_path_lookup(runtime ? SD_PATH_USER_RUNTIME : SD_PATH_USER_STATE_PRIVATE, "machines", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,7 @@ bool image_is_host(const struct Image *i);
|
||||
|
||||
int image_to_json(const struct Image *i, sd_json_variant **ret);
|
||||
|
||||
const char* image_root_to_string(ImageClass c) _const_;
|
||||
const char* image_root_runtime_to_string(ImageClass c) _const_;
|
||||
int image_root_pick(RuntimeScope scope, ImageClass c, bool runtime, char **ret);
|
||||
|
||||
extern const struct hash_ops image_hash_ops;
|
||||
|
||||
|
||||
+105
-6
@@ -4848,14 +4848,17 @@ int mountfsd_mount_image(
|
||||
#endif
|
||||
}
|
||||
|
||||
int mountfsd_mount_directory(
|
||||
const char *path,
|
||||
int mountfsd_mount_directory_fd(
|
||||
int directory_fd,
|
||||
int userns_fd,
|
||||
DissectImageFlags flags,
|
||||
int *ret_mount_fd) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(directory_fd >= 0);
|
||||
assert(ret_mount_fd);
|
||||
|
||||
/* Pick one identity, not both, that makes no sense. */
|
||||
assert(!FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID|DISSECT_IMAGE_IDENTITY_UID));
|
||||
|
||||
@@ -4872,10 +4875,6 @@ int mountfsd_mount_directory(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
|
||||
|
||||
_cleanup_close_ int directory_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH);
|
||||
if (directory_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
||||
|
||||
r = sd_varlink_push_dup_fd(vl, directory_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to push directory fd into varlink connection: %m");
|
||||
@@ -4919,3 +4918,103 @@ int mountfsd_mount_directory(
|
||||
*ret_mount_fd = TAKE_FD(fsmount_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mountfsd_mount_directory(
|
||||
const char *path,
|
||||
int userns_fd,
|
||||
DissectImageFlags flags,
|
||||
int *ret_mount_fd) {
|
||||
|
||||
assert(path);
|
||||
assert(ret_mount_fd);
|
||||
|
||||
_cleanup_close_ int directory_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH);
|
||||
if (directory_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
||||
|
||||
return mountfsd_mount_directory_fd(directory_fd, userns_fd, flags, ret_mount_fd);
|
||||
}
|
||||
|
||||
int mountfsd_make_directory_fd(
|
||||
int parent_fd,
|
||||
const char *name,
|
||||
DissectImageFlags flags,
|
||||
int *ret_directory_fd) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(parent_fd >= 0);
|
||||
assert(name);
|
||||
assert(ret_directory_fd);
|
||||
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to mountfsd: %m");
|
||||
|
||||
r = sd_varlink_set_allow_fd_passing_input(vl, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
|
||||
|
||||
r = sd_varlink_set_allow_fd_passing_output(vl, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
|
||||
|
||||
r = sd_varlink_push_dup_fd(vl, parent_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to push parent fd into varlink connection: %m");
|
||||
|
||||
sd_json_variant *reply = NULL;
|
||||
const char *error_id = NULL;
|
||||
r = varlink_callbo_and_log(
|
||||
vl,
|
||||
"io.systemd.MountFileSystem.MakeDirectory",
|
||||
&reply,
|
||||
&error_id,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("parentFileDescriptor", 0),
|
||||
SD_JSON_BUILD_PAIR_STRING("name", name),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "directoryFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
unsigned directory_fd_idx = UINT_MAX;
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &directory_fd_idx);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse MountImage() reply: %m");
|
||||
|
||||
_cleanup_close_ int directory_fd = sd_varlink_take_fd(vl, directory_fd_idx);
|
||||
if (directory_fd < 0)
|
||||
return log_error_errno(directory_fd, "Failed to take directory fd from Varlink connection: %m");
|
||||
|
||||
*ret_directory_fd = TAKE_FD(directory_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mountfsd_make_directory(
|
||||
const char *path,
|
||||
DissectImageFlags flags,
|
||||
int *ret_directory_fd) {
|
||||
|
||||
int r;
|
||||
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
r = path_extract_directory(path, &parent);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract parent directory from '%s': %m", path);
|
||||
|
||||
_cleanup_free_ char *dirname = NULL;
|
||||
r = path_extract_filename(path, &dirname);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract directory name from '%s': %m", path);
|
||||
|
||||
_cleanup_close_ int fd = open(parent, O_DIRECTORY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(r, "Failed to open '%s': %m", parent);
|
||||
|
||||
return mountfsd_make_directory_fd(fd, dirname, flags, ret_directory_fd);
|
||||
}
|
||||
|
||||
@@ -258,4 +258,8 @@ static inline const char* dissected_partition_fstype(const DissectedPartition *m
|
||||
int get_common_dissect_directory(char **ret);
|
||||
|
||||
int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret);
|
||||
int mountfsd_mount_directory_fd(int directory_fd, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
|
||||
int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
|
||||
|
||||
int mountfsd_make_directory_fd(int parent_fd, const char *name, DissectImageFlags flags, int *ret_directory_fd);
|
||||
int mountfsd_make_directory(const char *path, DissectImageFlags flags, int *ret_directory_fd);
|
||||
|
||||
+69
-37
@@ -95,6 +95,11 @@ static int unlinkat_maybe_dir(int dirfd, const char *pathname) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool need_opath(InstallFileFlags flags) {
|
||||
/* Returns true when we need to pin the source file via an O_PATH fd */
|
||||
return (flags & (INSTALL_FSYNC|INSTALL_FSYNC_FULL|INSTALL_SYNCFS|INSTALL_READ_ONLY)) != 0;
|
||||
}
|
||||
|
||||
int install_file(int source_atfd, const char *source_name,
|
||||
int target_atfd, const char *target_name,
|
||||
InstallFileFlags flags) {
|
||||
@@ -118,7 +123,7 @@ int install_file(int source_atfd, const char *source_name,
|
||||
* already in place, and only the syncing/read-only marking shall be applied. Note that with
|
||||
* target_name=NULL and flags=0 this call is a NOP */
|
||||
|
||||
if ((flags & (INSTALL_FSYNC|INSTALL_FSYNC_FULL|INSTALL_SYNCFS|INSTALL_READ_ONLY)) != 0) {
|
||||
if (need_opath(flags)) {
|
||||
_cleanup_close_ int pfd = -EBADF;
|
||||
struct stat st;
|
||||
|
||||
@@ -137,24 +142,32 @@ int install_file(int source_atfd, const char *source_name,
|
||||
_cleanup_close_ int regfd = -EBADF;
|
||||
|
||||
regfd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
|
||||
if (regfd < 0)
|
||||
return regfd;
|
||||
if (regfd < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(regfd, "Failed to open referenced inode: %m");
|
||||
|
||||
if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
|
||||
/* If this is just a regular file (as oppose to a fully populated directory)
|
||||
* let's downgrade INSTALL_SYNCFS to INSTALL_FSYNC_FULL, after all this is
|
||||
* going to be a single inode we install */
|
||||
r = fsync_full(regfd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (flags & INSTALL_FSYNC) {
|
||||
if (fsync(regfd) < 0)
|
||||
return -errno;
|
||||
log_debug_errno(regfd, "Failed to open referenced inode, ignoring: %m");
|
||||
} else {
|
||||
if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0)
|
||||
/* If this is just a regular file (as opposed to a fully populated
|
||||
* directory) let's downgrade INSTALL_SYNCFS to INSTALL_FSYNC_FULL,
|
||||
* after all this is going to be a single inode we install */
|
||||
r = fsync_full(regfd);
|
||||
else if (flags & INSTALL_FSYNC)
|
||||
r = RET_NERRNO(fsync(regfd));
|
||||
else
|
||||
r = 0;
|
||||
if (r < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(r, "Failed to sync source inode: %m");
|
||||
|
||||
log_debug_errno(r, "Failed to sync source inode, ignoring: %m");
|
||||
}
|
||||
|
||||
if (flags & INSTALL_READ_ONLY)
|
||||
rofd = TAKE_FD(regfd);
|
||||
}
|
||||
|
||||
if (flags & INSTALL_READ_ONLY)
|
||||
rofd = TAKE_FD(regfd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -162,24 +175,31 @@ int install_file(int source_atfd, const char *source_name,
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
|
||||
dfd = fd_reopen(pfd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (dfd < 0)
|
||||
return dfd;
|
||||
if (dfd < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(dfd, "Failed to open referenced inode: %m");
|
||||
|
||||
if (flags & INSTALL_SYNCFS) {
|
||||
if (syncfs(dfd) < 0)
|
||||
return -errno;
|
||||
} else if (flags & INSTALL_FSYNC_FULL) {
|
||||
r = fsync_full(dfd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (flags & INSTALL_FSYNC) {
|
||||
if (fsync(dfd) < 0)
|
||||
return -errno;
|
||||
log_debug_errno(dfd, "Failed to open referenced inode, ignoring: %m");
|
||||
} else {
|
||||
if (flags & INSTALL_SYNCFS)
|
||||
r = RET_NERRNO(syncfs(dfd));
|
||||
else if (flags & INSTALL_FSYNC_FULL)
|
||||
r = fsync_full(dfd);
|
||||
else if (flags & INSTALL_FSYNC)
|
||||
r = RET_NERRNO(fsync(dfd));
|
||||
else
|
||||
r = 0;
|
||||
if (r < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(r, "Failed to sync source inode: %m");
|
||||
|
||||
log_debug_errno(r, "Failed to sync source inode, ignoring: %m");
|
||||
}
|
||||
|
||||
if (flags & INSTALL_READ_ONLY)
|
||||
rofd = TAKE_FD(dfd);
|
||||
}
|
||||
|
||||
if (flags & INSTALL_READ_ONLY)
|
||||
rofd = TAKE_FD(dfd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -190,8 +210,12 @@ int install_file(int source_atfd, const char *source_name,
|
||||
|
||||
if (target_name && (flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
|
||||
r = fsync_directory_of_file(pfd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(r, "Failed to sync source inode: %m");
|
||||
|
||||
log_debug_errno(r, "Failed to sync source inode, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -254,8 +278,12 @@ int install_file(int source_atfd, const char *source_name,
|
||||
|
||||
if (rofd >= 0) {
|
||||
r = fs_make_very_read_only(rofd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(r, "Failed to make inode read-only: %m");
|
||||
|
||||
log_debug_errno(r, "Failed to make inode read-only, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
|
||||
@@ -263,8 +291,12 @@ int install_file(int source_atfd, const char *source_name,
|
||||
r = fsync_parent_at(target_atfd, target_name);
|
||||
else
|
||||
r = fsync_parent_at(source_atfd, source_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
if (!FLAGS_SET(flags, INSTALL_GRACEFUL))
|
||||
return log_debug_errno(r, "Failed to sync inode: %m");
|
||||
|
||||
log_debug_errno(r, "Failed to sync inode, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -9,6 +9,7 @@ typedef enum InstallFileFlags {
|
||||
INSTALL_FSYNC = 1 << 2, /* fsync() file contents before moving file in */
|
||||
INSTALL_FSYNC_FULL = 1 << 3, /* like INSTALL_FSYNC, but also fsync() parent dir before+after moving file in */
|
||||
INSTALL_SYNCFS = 1 << 4, /* syncfs() before moving file in, fsync() parent dir after moving file in */
|
||||
INSTALL_GRACEFUL = 1 << 5, /* don't fail if we cannot sync or mark read-only */
|
||||
} InstallFileFlags;
|
||||
|
||||
int install_file(int source_atfd, const char *source_name, int target_atfd, const char *target_name, InstallFileFlags flags);
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "shared-forward.h"
|
||||
|
||||
/* Helpful constants for the only numbers of UIDs that can currently be allocated */
|
||||
#define NSRESOURCE_UIDS_64K 0x10000U
|
||||
#define NSRESOURCE_UIDS_1 1U
|
||||
|
||||
int nsresource_allocate_userns(const char *name, uint64_t size);
|
||||
int nsresource_register_userns(const char *name, int userns_fd);
|
||||
int nsresource_add_mount(int userns_fd, int mount_fd);
|
||||
|
||||
@@ -28,6 +28,16 @@ units = [
|
||||
'conditions' : ['ENABLE_MACHINED'],
|
||||
'symlinks' : ['sockets.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-importd.service.in',
|
||||
'conditions' : ['ENABLE_IMPORTD'],
|
||||
'symlinks' : ['dbus-org.freedesktop.import1.service'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-importd.socket',
|
||||
'conditions' : ['ENABLE_IMPORTD'],
|
||||
'symlinks' : ['sockets.target.wants/'],
|
||||
},
|
||||
{ 'file' : 'paths.target' },
|
||||
{ 'file' : 'printer.target' },
|
||||
{ 'file' : 'session.slice' },
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Disk Image Download Service
|
||||
Documentation=man:systemd-importd.service(8)
|
||||
Documentation=man:org.freedesktop.import1(5)
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart={{LIBEXECDIR}}/systemd-importd --user
|
||||
BusName=org.freedesktop.import1
|
||||
KillMode=mixed
|
||||
NoNewPrivileges=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
SystemCallFilter=@system-service @mount
|
||||
SystemCallErrorNumber=EPERM
|
||||
SystemCallArchitectures=native
|
||||
LockPersonality=yes
|
||||
{{SERVICE_WATCHDOG}}
|
||||
@@ -0,0 +1,18 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Disk Image Download Service Socket
|
||||
Documentation=man:systemd-importd.service(8)
|
||||
Documentation=man:org.freedesktop.import1(5)
|
||||
|
||||
[Socket]
|
||||
ListenStream=%t/systemd/io.systemd.Import
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Reference in New Issue
Block a user