mirror of
https://github.com/systemd/systemd
synced 2026-03-11 23:54:46 +01:00
Compare commits
12 Commits
5bc11cd4e6
...
83b4a5bb3d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83b4a5bb3d | ||
|
|
8732896857 | ||
|
|
3bba7fee4f | ||
|
|
01fb2d4a9e | ||
|
|
f933e959b1 | ||
|
|
dbd1a6e34f | ||
|
|
ba37ed9634 | ||
|
|
ef155c909a | ||
|
|
fe0114db8e | ||
|
|
a0a4e9d7d2 | ||
|
|
53d1d6e49c | ||
|
|
09e38c2709 |
@ -699,6 +699,26 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read ...");
|
||||
```
|
||||
|
||||
- When generating log messages that contain filenames, user controlled strings,
|
||||
or similar, please enclose them in single ticks.
|
||||
|
||||
- Think about the log level you choose: for functions that are of the "logging"
|
||||
kind (see above), please ensure that failures we propagate should be logged
|
||||
about at `LOG_ERR` level. Failures that are noteworthy, but we proceed anyway,
|
||||
should be loged at `LOG_WARN` level. Important informational messages should
|
||||
use `LOG_NOTICE` and regular informational messages should use
|
||||
`LOG_INFO`. Note that the latter is the default maximum log level, i.e. only
|
||||
`LOG_DEBUG` messages are hidden by default.
|
||||
|
||||
- All log messages that show some failure which is not fatal for the immediate
|
||||
operation (i.e. generally those you'd log at `LOG_WARN` level, as described
|
||||
above) should be suffixed with a `…, ignoring: %m"` or similar. Or in other
|
||||
words, they should make clear not only in log level but also in English
|
||||
language that the issue is not fatal, but ignored. Depending on context you
|
||||
can also use `…, proceeding anyway: %m"`, `…, skipping: %m` or other language
|
||||
that makes clear that the failure is not actionable and doesn't strictly
|
||||
require immediate administrator attention.
|
||||
|
||||
## Memory Allocation
|
||||
|
||||
- Always check OOM. There is no excuse. In program code, you can use
|
||||
|
||||
@ -1194,6 +1194,10 @@ sensor:modalias:acpi:INVN6500*:dmi:*:svnTOSHIBA:pnTOSHIBAWT10-A-103:*
|
||||
sensor:modalias:acpi:INVN6500*:dmi:*:svnTOSHIBA:pnTOSHIBAWT10-A-102:*
|
||||
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
|
||||
|
||||
# Toshiba Encore WT10A-108 tablet
|
||||
sensor:modalias:acpi:INVN6500*:dmi:*:svnTOSHIBA:pnTOSHIBAWT10-A-108:*
|
||||
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
|
||||
|
||||
#########################################
|
||||
# Trekstor
|
||||
#########################################
|
||||
|
||||
@ -551,6 +551,19 @@
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--copy-ownership=</option></term>
|
||||
|
||||
<listitem><para>Controls whether file ownership (user and group) is preserved when copying files
|
||||
with <option>--copy-from</option> or <option>--copy-to</option>. Takes a boolean. If
|
||||
<literal>yes</literal>, ownership is always preserved. If <literal>no</literal>, ownership is never
|
||||
preserved and the current user's UID/GID is used instead. If not specified, ownership is preserved
|
||||
when copying directory trees, but not when copying individual regular files.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--system</option></term>
|
||||
<term><option>--user</option></term>
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "argv-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "build.h"
|
||||
#include "capability-util.h"
|
||||
#include "chase.h"
|
||||
#include "copy.h"
|
||||
#include "device-util.h"
|
||||
@ -108,6 +109,7 @@ static bool arg_all = false;
|
||||
static uid_t arg_uid_base = UID_INVALID;
|
||||
static bool arg_quiet = false;
|
||||
static ImageFilter *arg_image_filter = NULL;
|
||||
static int arg_copy_ownership = -1;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
@ -169,6 +171,8 @@ static int help(void) {
|
||||
" --loop-ref=NAME Set reference string for loopback device\n"
|
||||
" --loop-ref-auto Derive reference string from image file name\n"
|
||||
" --mtree-hash=BOOL Whether to include SHA256 hash in the mtree output\n"
|
||||
" --copy-ownership=BOOL\n"
|
||||
" Whether to copy ownership when copying files\n"
|
||||
" --user Discover user images\n"
|
||||
" --system Discover system images\n"
|
||||
" --all Show hidden images too\n"
|
||||
@ -305,48 +309,50 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_USER,
|
||||
ARG_ALL,
|
||||
ARG_IMAGE_FILTER,
|
||||
ARG_COPY_OWNERSHIP,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "mount", no_argument, NULL, 'm' },
|
||||
{ "umount", no_argument, NULL, 'u' },
|
||||
{ "attach", no_argument, NULL, ARG_ATTACH },
|
||||
{ "detach", no_argument, NULL, ARG_DETACH },
|
||||
{ "with", no_argument, NULL, ARG_WITH },
|
||||
{ "read-only", no_argument, NULL, 'r' },
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
{ "fsck", required_argument, NULL, ARG_FSCK },
|
||||
{ "growfs", required_argument, NULL, ARG_GROWFS },
|
||||
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
|
||||
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
|
||||
{ "usr-hash", required_argument, NULL, ARG_USR_HASH },
|
||||
{ "usr-hash-sig", required_argument, NULL, ARG_USR_HASH_SIG },
|
||||
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
|
||||
{ "mkdir", no_argument, NULL, ARG_MKDIR },
|
||||
{ "rmdir", no_argument, NULL, ARG_RMDIR },
|
||||
{ "in-memory", no_argument, NULL, ARG_IN_MEMORY },
|
||||
{ "list", no_argument, NULL, 'l' },
|
||||
{ "mtree", no_argument, NULL, ARG_MTREE },
|
||||
{ "copy-from", no_argument, NULL, 'x' },
|
||||
{ "copy-to", no_argument, NULL, 'a' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "loop-ref", required_argument, NULL, ARG_LOOP_REF },
|
||||
{ "loop-ref-auto", no_argument, NULL, ARG_LOOP_REF_AUTO },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "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 },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "image-filter", required_argument, NULL, ARG_IMAGE_FILTER },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "mount", no_argument, NULL, 'm' },
|
||||
{ "umount", no_argument, NULL, 'u' },
|
||||
{ "attach", no_argument, NULL, ARG_ATTACH },
|
||||
{ "detach", no_argument, NULL, ARG_DETACH },
|
||||
{ "with", no_argument, NULL, ARG_WITH },
|
||||
{ "read-only", no_argument, NULL, 'r' },
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
{ "fsck", required_argument, NULL, ARG_FSCK },
|
||||
{ "growfs", required_argument, NULL, ARG_GROWFS },
|
||||
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
|
||||
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
|
||||
{ "usr-hash", required_argument, NULL, ARG_USR_HASH },
|
||||
{ "usr-hash-sig", required_argument, NULL, ARG_USR_HASH_SIG },
|
||||
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
|
||||
{ "mkdir", no_argument, NULL, ARG_MKDIR },
|
||||
{ "rmdir", no_argument, NULL, ARG_RMDIR },
|
||||
{ "in-memory", no_argument, NULL, ARG_IN_MEMORY },
|
||||
{ "list", no_argument, NULL, 'l' },
|
||||
{ "mtree", no_argument, NULL, ARG_MTREE },
|
||||
{ "copy-from", no_argument, NULL, 'x' },
|
||||
{ "copy-to", no_argument, NULL, 'a' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "loop-ref", required_argument, NULL, ARG_LOOP_REF },
|
||||
{ "loop-ref-auto", no_argument, NULL, ARG_LOOP_REF_AUTO },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "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 },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "image-filter", required_argument, NULL, ARG_IMAGE_FILTER },
|
||||
{ "copy-ownership", required_argument, NULL, ARG_COPY_OWNERSHIP },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -630,6 +636,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_COPY_OWNERSHIP:
|
||||
r = parse_tristate_argument("--copy-ownership=", optarg, &arg_copy_ownership);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -837,8 +849,25 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
} else
|
||||
arg_via_service = r;
|
||||
|
||||
if (!IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_DISCOVER, ACTION_VALIDATE) && geteuid() != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
|
||||
r = have_effective_cap(CAP_SYS_ADMIN);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if we have CAP_SYS_ADMIN: %m");
|
||||
|
||||
if (IN_SET(arg_action, ACTION_MOUNT, ACTION_UMOUNT) && r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to have CAP_SYS_ADMIN to mount/unmount images");
|
||||
|
||||
r = have_effective_cap(CAP_CHOWN);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if we have CAP_CHOWN: %m");
|
||||
|
||||
if (arg_action == ACTION_SHIFT && r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to have CAP_CHOWN to shift UID ranges");
|
||||
|
||||
if (IN_SET(arg_action, ACTION_ATTACH, ACTION_DETACH)) {
|
||||
r = must_be_root();
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty_safe(STDIN_FILENO));
|
||||
|
||||
@ -1439,6 +1468,13 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
|
||||
assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE, ACTION_SHIFT));
|
||||
|
||||
/* Determine whether to copy ownership:
|
||||
* --copy-ownership=yes: always try to preserve ownership
|
||||
* --copy-ownership=no: never preserve ownership, use current user
|
||||
* --copy-ownership=auto (default): preserve ownership for directory trees,
|
||||
* but not for regular files (since DDI password tables are typically
|
||||
* distinct from the host ones, individual file ownership is less meaningful) */
|
||||
|
||||
if (arg_image) {
|
||||
assert(m);
|
||||
|
||||
@ -1501,7 +1537,12 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
}
|
||||
|
||||
/* Try to copy as directory? */
|
||||
r = copy_directory_at(source_fd, NULL, AT_FDCWD, arg_target, COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
|
||||
r = copy_directory_at(
|
||||
source_fd, /* from= */ NULL,
|
||||
AT_FDCWD, arg_target,
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (r != -ENOTDIR)
|
||||
@ -1524,9 +1565,10 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
|
||||
(void) copy_xattr(source_fd, NULL, target_fd, NULL, 0);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
if (arg_copy_ownership > 0)
|
||||
(void) copy_owner(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
/* When this is a regular file we don't copy ownership! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1580,9 +1622,23 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
|
||||
|
||||
r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL, NULL);
|
||||
r = copy_tree_at(
|
||||
source_fd, ".",
|
||||
dfd, bn,
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
} else
|
||||
r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL, NULL);
|
||||
r = copy_tree_at(
|
||||
source_fd, ".",
|
||||
target_fd, ".",
|
||||
arg_copy_ownership == 0 ? getuid() : UID_INVALID,
|
||||
arg_copy_ownership == 0 ? getgid() : GID_INVALID,
|
||||
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
|
||||
|
||||
@ -1603,9 +1659,10 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
|
||||
(void) copy_xattr(source_fd, NULL, target_fd, NULL, 0);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
if (arg_copy_ownership > 0)
|
||||
(void) copy_owner(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
/* When this is a regular file we don't copy ownership! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1721,7 +1778,7 @@ static int action_umount(const char *path) {
|
||||
return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
|
||||
|
||||
r = loop_device_open(dev, 0, LOCK_EX, &d);
|
||||
if (r < 0)
|
||||
if (r < 0 && !ERRNO_IS_PRIVILEGE(r))
|
||||
return log_device_error_errno(dev, r, "Failed to open loopback block device: %m");
|
||||
|
||||
/* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
|
||||
@ -1733,7 +1790,8 @@ static int action_umount(const char *path) {
|
||||
return log_error_errno(r, "Failed to unmount '%s': %m", canonical);
|
||||
|
||||
/* We managed to lock and unmount successfully? That means we can try to remove the loop device. */
|
||||
loop_device_unrelinquish(d);
|
||||
if (d)
|
||||
loop_device_unrelinquish(d);
|
||||
|
||||
if (arg_rmdir) {
|
||||
r = RET_NERRNO(rmdir(canonical));
|
||||
@ -1785,7 +1843,7 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
|
||||
return log_error_errno(r, "Failed to unlock loopback block device: %m");
|
||||
}
|
||||
|
||||
rcode = pidref_safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, /* ret= */ NULL);
|
||||
rcode = pidref_safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT, /* ret= */ NULL);
|
||||
if (rcode == 0) {
|
||||
/* Child */
|
||||
|
||||
@ -1799,7 +1857,7 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setenv("SYSTEMD_DISSECT_DEVICE", d->node, /* overwrite= */ true) < 0) {
|
||||
if (d && setenv("SYSTEMD_DISSECT_DEVICE", d->node, /* overwrite= */ true) < 0) {
|
||||
log_error_errno(errno, "Failed to set $SYSTEMD_DISSECT_DEVICE: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
@ -1839,7 +1897,7 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
|
||||
created_dir = TAKE_PTR(mounted_dir);
|
||||
|
||||
if (rmdir(created_dir) < 0)
|
||||
log_warning_errno(r, "Failed to remove directory '%s', ignoring: %m", created_dir);
|
||||
log_warning_errno(errno, "Failed to remove directory '%s', ignoring: %m", created_dir);
|
||||
|
||||
temp = TAKE_PTR(created_dir);
|
||||
|
||||
@ -2114,7 +2172,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, ACTION_SHIFT))
|
||||
if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_MOUNT, ACTION_UMOUNT, ACTION_WITH, 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);
|
||||
@ -2180,8 +2238,9 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_loop_ref || arg_loop_ref_auto) /* yes, the 2nd check is strictly speaking redundant, given the normalization we did above, but let's be explicit here */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref=/--loop-ref-auto not supported when operating via systemd-mountfsd.");
|
||||
|
||||
/* 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)) {
|
||||
/* Don't run things in private userns, if the mount shall be attached to the host
|
||||
* or if we're copying from/to the host. */
|
||||
if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH, ACTION_COPY_FROM, ACTION_COPY_TO)) {
|
||||
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");
|
||||
|
||||
@ -223,6 +223,8 @@ static int import_fs(int argc, char *argv[], void *userdata) {
|
||||
r = copy_directory_at_full(
|
||||
fd, NULL,
|
||||
AT_FDCWD, dest,
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_REFLINK|
|
||||
COPY_SAME_MOUNT|
|
||||
COPY_HARDLINKS|
|
||||
|
||||
@ -1480,6 +1480,8 @@ int btrfs_subvol_snapshot_at_full(
|
||||
r = copy_directory_at_full(
|
||||
dir_fdf, from,
|
||||
new_fd, subvolume,
|
||||
/* override_uid= */ UID_INVALID,
|
||||
/* override_gid= */ GID_INVALID,
|
||||
COPY_MERGE_EMPTY|
|
||||
COPY_REFLINK|
|
||||
COPY_SAME_MOUNT|
|
||||
|
||||
@ -1442,6 +1442,8 @@ int copy_directory_at_full(
|
||||
const char *from,
|
||||
int dir_fdt,
|
||||
const char *to,
|
||||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
@ -1468,9 +1470,13 @@ int copy_directory_at_full(
|
||||
dir_fdt, to,
|
||||
st.st_dev,
|
||||
COPY_DEPTH_MAX,
|
||||
UID_INVALID, GID_INVALID,
|
||||
override_uid,
|
||||
override_gid,
|
||||
copy_flags,
|
||||
NULL, NULL, NULL, NULL,
|
||||
/* denylist= */ NULL,
|
||||
/* subvolumes= */ NULL,
|
||||
/* progress_path= */ NULL,
|
||||
/* progress_bytes= */ NULL,
|
||||
progress_path,
|
||||
progress_bytes,
|
||||
userdata);
|
||||
@ -1750,6 +1756,18 @@ int copy_access(int fdf, int fdt) {
|
||||
return RET_NERRNO(fchmod(fdt, st.st_mode & 07777));
|
||||
}
|
||||
|
||||
int copy_owner(int fdf, int fdt) {
|
||||
struct stat st;
|
||||
|
||||
assert(fdf >= 0);
|
||||
assert(fdt >= 0);
|
||||
|
||||
if (fstat(fdf, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
return RET_NERRNO(fchown(fdt, st.st_uid, st.st_gid));
|
||||
}
|
||||
|
||||
int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
|
||||
struct stat st;
|
||||
|
||||
|
||||
@ -88,9 +88,9 @@ static inline int copy_tree(const char *from, const char *to, uid_t override_uid
|
||||
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, subvolumes, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory_at(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags) {
|
||||
return copy_directory_at_full(dir_fdf, from, dir_fdt, to, copy_flags, NULL, NULL, NULL);
|
||||
int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_directory_at(int dir_fdf, const char *from, int dir_fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
|
||||
return copy_directory_at_full(dir_fdf, from, dir_fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
|
||||
@ -100,6 +100,7 @@ static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags cop
|
||||
|
||||
int copy_times(int fdf, int fdt, CopyFlags flags);
|
||||
int copy_access(int fdf, int fdt);
|
||||
int copy_owner(int fdf, int fdt);
|
||||
int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
|
||||
static inline int copy_rights(int fdf, int fdt) {
|
||||
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
|
||||
|
||||
@ -387,12 +387,13 @@ read_only:
|
||||
int path_patch_uid(const char *path, uid_t shift, uid_t range) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
return log_debug_errno(errno, "Failed to open '%s': %m", path);
|
||||
|
||||
/* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an
|
||||
* OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges
|
||||
@ -401,21 +402,30 @@ int path_patch_uid(const char *path, uid_t shift, uid_t range) {
|
||||
|
||||
/* We only support containers where the shift starts at a 2^16 boundary */
|
||||
if ((shift & 0xFFFF) != 0)
|
||||
return -EOPNOTSUPP;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"UID shift 0x%"PRIx32" is not at a 2^16 boundary.",
|
||||
(uint32_t) shift);
|
||||
|
||||
if (shift == UID_BUSY_BASE)
|
||||
return -EINVAL;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"UID shift 0x%"PRIx32" conflicts with busy base.",
|
||||
(uint32_t) shift);
|
||||
|
||||
/* We only support containers with 16-bit UID ranges for the patching logic */
|
||||
if (range != 0x10000)
|
||||
return -EOPNOTSUPP;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"UID range 0x%"PRIx32" is not supported, must be 0x10000.",
|
||||
(uint32_t) range);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
return log_debug_errno(errno, "Failed to stat '%s': %m", path);
|
||||
|
||||
/* We only support containers where the uid/gid container ID match */
|
||||
if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16)
|
||||
return -EBADE;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EBADE),
|
||||
"UID container ID 0x%"PRIx32" does not match GID container ID 0x%"PRIx32".",
|
||||
(uint32_t) st.st_uid >> 16,
|
||||
(uint32_t) st.st_gid >> 16);
|
||||
|
||||
/* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume
|
||||
* that if the top-level dir has the right upper 16-bit assigned, then everything below will have too... */
|
||||
@ -430,7 +440,11 @@ int path_patch_uid(const char *path, uid_t shift, uid_t range) {
|
||||
if (fchown(fd,
|
||||
UID_BUSY_BASE | (st.st_uid & ~UID_BUSY_MASK),
|
||||
(gid_t) UID_BUSY_BASE | (st.st_gid & ~(gid_t) UID_BUSY_MASK)) < 0)
|
||||
return -errno;
|
||||
log_debug_errno(errno, "Failed to mark '%s' as busy, ignoring: %m", path);
|
||||
|
||||
return recurse_fd(TAKE_FD(fd), &st, shift, true);
|
||||
r = recurse_fd(TAKE_FD(fd), &st, shift, /* is_toplevel= */ true);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to recursively patch UID/GID of '%s': %m", path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -560,7 +560,7 @@ TEST(copy_lock) {
|
||||
assert_se(mkdirat(tfd, "abc", 0755) >= 0);
|
||||
assert_se(write_string_file_at(tfd, "abc/def", "abc", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
|
||||
assert_se((fd = copy_directory_at(tfd, "abc", tfd, "qed", COPY_LOCK_BSD)) >= 0);
|
||||
assert_se((fd = copy_directory_at(tfd, "abc", tfd, "qed", UID_INVALID, GID_INVALID, COPY_LOCK_BSD)) >= 0);
|
||||
assert_se(faccessat(tfd, "qed", F_OK, 0) >= 0);
|
||||
assert_se(faccessat(tfd, "qed/def", F_OK, 0) >= 0);
|
||||
assert_se(xopenat_lock(tfd, "qed", 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
|
||||
|
||||
@ -1497,7 +1497,7 @@ static int prepare_ns(const char *process_name) {
|
||||
|
||||
/* Copy unit files to make them accessible even when unprivileged. */
|
||||
ASSERT_OK(get_testdata_dir("test-execute/", &unit_dir));
|
||||
ASSERT_OK(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY));
|
||||
ASSERT_OK(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, UID_INVALID, GID_INVALID, COPY_MERGE_EMPTY));
|
||||
|
||||
/* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
|
||||
ASSERT_OK_OR(get_build_exec_dir(&build_dir), -ENOEXEC);
|
||||
|
||||
@ -1050,6 +1050,45 @@ echo abc >abc
|
||||
systemd-dissect --copy-to /tmp/img abc /abc
|
||||
test -f /tmp/img/abc
|
||||
|
||||
# Test --copy-ownership= option
|
||||
rm -rf /tmp/copychown-test
|
||||
mkdir -p /tmp/copychown-test/srcdir
|
||||
echo "test file" >/tmp/copychown-test/srcdir/testfile
|
||||
chown 1234:5678 /tmp/copychown-test/srcdir/testfile
|
||||
chown 1234:5678 /tmp/copychown-test/srcdir
|
||||
|
||||
# Test --copy-ownership=yes preserves ownership for regular files
|
||||
systemd-dissect --copy-from /tmp/img etc/os-release /tmp/copychown-test/os-release-chown-yes --copy-ownership=yes
|
||||
test "$(stat -c %u:%g /tmp/copychown-test/os-release-chown-yes)" = "0:0"
|
||||
|
||||
# Test --copy-ownership=no uses current user for regular files
|
||||
systemd-dissect --copy-from /tmp/img etc/os-release /tmp/copychown-test/os-release-chown-no --copy-ownership=no
|
||||
test "$(stat -c %u:%g /tmp/copychown-test/os-release-chown-no)" = "0:0"
|
||||
|
||||
# Test --copy-ownership=auto (default) does not preserve ownership for regular files
|
||||
systemd-dissect --copy-from /tmp/img etc/os-release /tmp/copychown-test/os-release-chown-auto
|
||||
test "$(stat -c %u:%g /tmp/copychown-test/os-release-chown-auto)" = "0:0"
|
||||
|
||||
# Test --copy-ownership=yes preserves ownership for directories
|
||||
systemd-dissect --copy-to /tmp/img /tmp/copychown-test/srcdir /copychown-dir-yes --copy-ownership=yes
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-yes)" = "1234:5678"
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-yes/testfile)" = "1234:5678"
|
||||
rm -rf /tmp/img/copychown-dir-yes
|
||||
|
||||
# Test --copy-ownership=no overrides ownership for directories
|
||||
systemd-dissect --copy-to /tmp/img /tmp/copychown-test/srcdir /copychown-dir-no --copy-ownership=no
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-no)" = "0:0"
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-no/testfile)" = "0:0"
|
||||
rm -rf /tmp/img/copychown-dir-no
|
||||
|
||||
# Test --copy-ownership=auto (default) preserves ownership for directories
|
||||
systemd-dissect --copy-to /tmp/img /tmp/copychown-test/srcdir /copychown-dir-auto
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-auto)" = "1234:5678"
|
||||
test "$(stat -c %u:%g /tmp/img/copychown-dir-auto/testfile)" = "1234:5678"
|
||||
rm -rf /tmp/img/copychown-dir-auto
|
||||
|
||||
rm -rf /tmp/copychown-test
|
||||
|
||||
# Test for dissect tool support with systemd-sysext
|
||||
mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
|
||||
echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user