mirror of
https://github.com/systemd/systemd
synced 2025-11-06 10:24:44 +01:00
Compare commits
15 Commits
9218e4eacc
...
05b880ac46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05b880ac46 | ||
|
|
9d129ea34e | ||
|
|
6971798864 | ||
|
|
22311a1291 | ||
|
|
9a1d72fe99 | ||
|
|
4248b02c44 | ||
|
|
78a738f4cf | ||
|
|
4ded7f7a43 | ||
|
|
e1e170feca | ||
|
|
a54f4520f3 | ||
|
|
63bf3ca8b0 | ||
|
|
d4d94fceba | ||
|
|
f89c914968 | ||
|
|
030f239a19 | ||
|
|
79dd24cf14 |
25
TODO
25
TODO
@ -63,6 +63,12 @@ Regularly:
|
||||
|
||||
Janitorial Clean-ups:
|
||||
|
||||
* machined: make remaining machine bus calls compatible with unpriv machined +
|
||||
unpriv npsawn: GetAddresses(), GetSSHInfo(), GetOSRelease(), OpenPTY(),
|
||||
OpenLogin(), OpenShell(), BindMount(), CopyFrom(), CopyTo(),
|
||||
OpenRootDirectory(). Similar for images: GetHostname(), GetMachineID(),
|
||||
GetMachineInfo(), GetOSRelease().
|
||||
|
||||
* rework mount.c and swap.c to follow proper state enumeration/deserialization
|
||||
semantics, like we do for device.c now
|
||||
|
||||
@ -469,18 +475,10 @@ Features:
|
||||
* allow dynamic modifications of ConcurrencyHardMax= and ConcurrencySoftMax=
|
||||
via DBus (and with that also by daemon-reload)
|
||||
|
||||
* importd: introduce a per-user instance, that downloads into per-user DDI dirs
|
||||
|
||||
* sysupdated: similar
|
||||
* sysupdated: introduce per-user version that can update per-user installed dDIs
|
||||
|
||||
* portabled: similar
|
||||
|
||||
* machined: implement a per-user instance, that manages per-user DDI dirs for
|
||||
images. systemd-nspawn/systemd-vmspawn should probably register with both the
|
||||
system and the user scoped machined instance. The former to get the machine
|
||||
name registered as hostname, and the latter so that the image stuff is nicely
|
||||
per-user managed.
|
||||
|
||||
* resolved: make resolved process DNR DHCP info
|
||||
|
||||
* maybe introduce an OSC sequence that signals when we ask for a password, so
|
||||
@ -490,14 +488,8 @@ Features:
|
||||
* start using STATX_SUBVOL in btrfs_is_subvol(). Also, make use of it
|
||||
generically, so that image discovery recognizes bcachefs subvols too.
|
||||
|
||||
* "systemd-export tar" should reuse the libarchive export code from systemd-dissect
|
||||
--archive.
|
||||
|
||||
* "systemd-import tar" should be moved to libarchive
|
||||
|
||||
* foreign uid:
|
||||
- add support to export-fs, import-fs, import-tar, export-tar
|
||||
- add tool for deleting foreign UID held container images
|
||||
- add support to export-fs, import-fs
|
||||
- systemd-dissect should learn mappings, too, when doing mtree and such
|
||||
|
||||
* format-table: introduce new cell type for strings with ansi sequences in
|
||||
@ -720,7 +712,6 @@ Features:
|
||||
from, tracing through overlayfs, DM, loopback block device.
|
||||
|
||||
* importd/importctl
|
||||
- port tar handling to libarchive
|
||||
- complete varlink interface
|
||||
- download images into .v/ dirs
|
||||
|
||||
|
||||
@ -3408,6 +3408,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s IPCNamespacePath = '...';
|
||||
@ -4019,6 +4021,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
|
||||
<!--property IPCNamespacePath is not documented!-->
|
||||
@ -4759,6 +4763,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IPCNamespacePath"/>
|
||||
@ -5655,6 +5661,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s IPCNamespacePath = '...';
|
||||
@ -6286,6 +6294,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
|
||||
<!--property IPCNamespacePath is not documented!-->
|
||||
@ -7006,6 +7016,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IPCNamespacePath"/>
|
||||
@ -7726,6 +7738,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s IPCNamespacePath = '...';
|
||||
@ -8279,6 +8293,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
|
||||
<!--property IPCNamespacePath is not documented!-->
|
||||
@ -8907,6 +8923,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IPCNamespacePath"/>
|
||||
@ -9760,6 +9778,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s IPCNamespacePath = '...';
|
||||
@ -10295,6 +10315,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
|
||||
<!--property IPCNamespacePath is not documented!-->
|
||||
@ -10905,6 +10927,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IPCNamespacePath"/>
|
||||
@ -12519,7 +12543,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>LogsDirectoryQuotaUsage</varname>,
|
||||
<varname>LogsDirectoryAccounting</varname>, and
|
||||
<function>KillSubgroup()</function> were added in version 258.</para>
|
||||
<para><varname>OOMKills</varname>, and
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
@ -12587,6 +12612,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>LogsDirectoryAccounting</varname>, and
|
||||
<function>KillSubgroup()</function> were added in version 258.</para>
|
||||
<para><varname>OOMKills</varname>, and
|
||||
<varname>UserNamespacePath</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
@ -12648,7 +12674,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>LogsDirectoryQuotaUsage</varname>,
|
||||
<varname>LogsDirectoryAccounting</varname>, and
|
||||
<function>KillSubgroup()</function> were added in version 258.</para>
|
||||
<para><varname>OOMKills</varname>, and
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
@ -12708,7 +12735,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>LogsDirectoryQuotaUsage</varname>,
|
||||
<varname>LogsDirectoryAccounting</varname>, and
|
||||
<function>KillSubgroup()</function> were added in version 258.</para>
|
||||
<para><varname>OOMKills</varname>, and
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
|
||||
@ -2026,6 +2026,20 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
|
||||
<xi:include href="system-or-user-ns.xml" xpointer="singular"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>UserNamespacePath=</varname></term>
|
||||
|
||||
<listitem><para>Takes an absolute file system path referring to a Linux user namespace
|
||||
pseudo-file (i.e. a file like <filename>/proc/$PID/ns/user</filename> or a bind mount or symlink to
|
||||
one). When set the invoked processes are added to the user namespace referenced by that path. The
|
||||
path has to point to a valid namespace file at the moment the processes are forked off. If this
|
||||
option is used <varname>PrivateUsers=</varname> has no effect.</para>
|
||||
|
||||
<para>This option is only available for system services.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>NetworkNamespacePath=</varname></term>
|
||||
|
||||
|
||||
@ -1407,6 +1407,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("BPFDelegatePrograms", "s", property_get_bpf_delegate_programs, offsetof(ExecContext, bpf_delegate_programs), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BPFDelegateAttachments", "s", property_get_bpf_delegate_attachments, offsetof(ExecContext, bpf_delegate_attachments), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MemoryKSM", "b", bus_property_get_tristate, offsetof(ExecContext, memory_ksm), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UserNamespacePath", "s", NULL, offsetof(ExecContext, user_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootImagePolicy", "s", property_get_image_policy, offsetof(ExecContext, root_image_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -2500,6 +2501,9 @@ int bus_exec_context_set_transient_property(
|
||||
if (streq(name, "NetworkNamespacePath"))
|
||||
return bus_set_transient_path(u, name, &c->network_namespace_path, message, flags, error);
|
||||
|
||||
if (streq(name, "UserNamespacePath"))
|
||||
return bus_set_transient_path(u, name, &c->user_namespace_path, message, flags, error);
|
||||
|
||||
if (streq(name, "IPCNamespacePath"))
|
||||
return bus_set_transient_path(u, name, &c->ipc_namespace_path, message, flags, error);
|
||||
|
||||
|
||||
@ -4159,6 +4159,7 @@ static int close_remaining_fds(
|
||||
append_socket_pair(dont_close, &n_dont_close, runtime->ephemeral_storage_socket);
|
||||
|
||||
if (runtime->shared) {
|
||||
append_socket_pair(dont_close, &n_dont_close, runtime->shared->userns_storage_socket);
|
||||
append_socket_pair(dont_close, &n_dont_close, runtime->shared->netns_storage_socket);
|
||||
append_socket_pair(dont_close, &n_dont_close, runtime->shared->ipcns_storage_socket);
|
||||
}
|
||||
@ -4464,6 +4465,7 @@ static bool exec_needs_cap_sys_admin(const ExecContext *context, const ExecParam
|
||||
context->private_tmp != PRIVATE_TMP_NO ||
|
||||
context->private_devices ||
|
||||
context->private_network ||
|
||||
context->user_namespace_path ||
|
||||
context->network_namespace_path ||
|
||||
context->private_ipc ||
|
||||
context->ipc_namespace_path ||
|
||||
@ -4768,6 +4770,7 @@ static void exec_shared_runtime_close(ExecSharedRuntime *shared) {
|
||||
if (!shared)
|
||||
return;
|
||||
|
||||
safe_close_pair(shared->userns_storage_socket);
|
||||
safe_close_pair(shared->netns_storage_socket);
|
||||
safe_close_pair(shared->ipcns_storage_socket);
|
||||
}
|
||||
@ -5322,6 +5325,14 @@ int exec_invoke(
|
||||
}
|
||||
}
|
||||
|
||||
if (context->user_namespace_path && runtime->shared && runtime->shared->userns_storage_socket[0] >= 0) {
|
||||
r = open_shareable_ns_path(runtime->shared->userns_storage_socket, context->user_namespace_path, CLONE_NEWUSER);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_NAMESPACE;
|
||||
return log_error_errno(r, "Failed to open user namespace path %s: %m", context->user_namespace_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (context->network_namespace_path && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
|
||||
r = open_shareable_ns_path(runtime->shared->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
|
||||
if (r < 0) {
|
||||
@ -5758,6 +5769,10 @@ int exec_invoke(
|
||||
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
|
||||
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
|
||||
* set up all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
||||
|
||||
if (context->user_namespace_path && runtime->shared->userns_storage_socket[0] >= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "UserNamespacePath= is configured, but user namespace setup not permitted");
|
||||
|
||||
PrivateUsers pu = exec_context_get_effective_private_users(context, params);
|
||||
if (pu == PRIVATE_USERS_NO)
|
||||
pu = PRIVATE_USERS_SELF;
|
||||
@ -5828,8 +5843,20 @@ int exec_invoke(
|
||||
* restricted by rules pertaining to combining user namespaces with other namespaces (e.g. in the
|
||||
* case of mount namespaces being less privileged when the mount point list is copied from a
|
||||
* different user namespace). */
|
||||
if (needs_sandboxing && context->user_namespace_path && runtime->shared && runtime->shared->userns_storage_socket[0] >= 0) {
|
||||
if (!namespace_type_supported(NAMESPACE_USER))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "UserNamespacePath= is not supported, refusing.");
|
||||
|
||||
if (needs_sandboxing && !userns_set_up) {
|
||||
r = setup_shareable_ns(runtime->shared->userns_storage_socket, CLONE_NEWUSER);
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(r))
|
||||
return log_notice_errno(r, "PrivateUsers= is configured, but user namespace setup not permitted, refusing.");
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return log_error_errno(r, "Failed to set up user namespacing: %m");
|
||||
}
|
||||
|
||||
log_debug("Set up existing user namespace");
|
||||
} else if (needs_sandboxing && !userns_set_up) {
|
||||
PrivateUsers pu = exec_context_get_effective_private_users(context, params);
|
||||
|
||||
r = setup_private_users(pu, saved_uid, saved_gid, uid, gid,
|
||||
|
||||
@ -938,6 +938,12 @@ static int exec_runtime_serialize(const ExecRuntime *rt, FILE *f, FDSet *fds) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rt->shared->userns_storage_socket[0] >= 0 && rt->shared->userns_storage_socket[1] >= 0) {
|
||||
r = serialize_fd_many(f, fds, "exec-runtime-userns-storage-socket", rt->shared->userns_storage_socket, 2);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (rt->shared->netns_storage_socket[0] >= 0 && rt->shared->netns_storage_socket[1] >= 0) {
|
||||
r = serialize_fd_many(f, fds, "exec-runtime-netns-storage-socket", rt->shared->netns_storage_socket, 2);
|
||||
if (r < 0)
|
||||
@ -1013,6 +1019,12 @@ static int exec_runtime_deserialize(ExecRuntime *rt, FILE *f, FDSet *fds) {
|
||||
r = free_and_strdup(&rt->shared->var_tmp_dir, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if ((val = startswith(l, "exec-runtime-userns-storage-socket="))) {
|
||||
|
||||
r = deserialize_fd_many(fds, val, 2, rt->shared->userns_storage_socket);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
} else if ((val = startswith(l, "exec-runtime-netns-storage-socket="))) {
|
||||
|
||||
r = deserialize_fd_many(fds, val, 2, rt->shared->netns_storage_socket);
|
||||
@ -2362,6 +2374,10 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = serialize_item(f, "exec-context-user-namespace-path", c->user_namespace_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = serialize_item(f, "exec-context-network-namespace-path", c->network_namespace_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -3493,6 +3509,10 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
r = free_and_strdup(&c->network_namespace_path, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if ((val = startswith(l, "exec-context-user-namespace-path="))) {
|
||||
r = free_and_strdup(&c->user_namespace_path, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if ((val = startswith(l, "exec-context-ipc-namespace-path="))) {
|
||||
r = free_and_strdup(&c->ipc_namespace_path, val);
|
||||
if (r < 0)
|
||||
|
||||
@ -742,6 +742,7 @@ void exec_context_done(ExecContext *c) {
|
||||
c->stdin_data = mfree(c->stdin_data);
|
||||
c->stdin_data_size = 0;
|
||||
|
||||
c->user_namespace_path = mfree(c->user_namespace_path);
|
||||
c->network_namespace_path = mfree(c->network_namespace_path);
|
||||
c->ipc_namespace_path = mfree(c->ipc_namespace_path);
|
||||
|
||||
@ -1554,6 +1555,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (c->user_namespace_path)
|
||||
fprintf(f,
|
||||
"%sUserNamespacePath: %s\n",
|
||||
prefix, c->user_namespace_path);
|
||||
|
||||
if (c->network_namespace_path)
|
||||
fprintf(f,
|
||||
"%sNetworkNamespacePath: %s\n",
|
||||
@ -2286,6 +2292,7 @@ void exec_shared_runtime_done(ExecSharedRuntime *rt) {
|
||||
rt->id = mfree(rt->id);
|
||||
rt->tmp_dir = mfree(rt->tmp_dir);
|
||||
rt->var_tmp_dir = mfree(rt->var_tmp_dir);
|
||||
safe_close_pair(rt->userns_storage_socket);
|
||||
safe_close_pair(rt->netns_storage_socket);
|
||||
safe_close_pair(rt->ipcns_storage_socket);
|
||||
}
|
||||
@ -2333,6 +2340,7 @@ static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id)
|
||||
|
||||
*n = (ExecSharedRuntime) {
|
||||
.id = TAKE_PTR(id_copy),
|
||||
.userns_storage_socket = EBADF_PAIR,
|
||||
.netns_storage_socket = EBADF_PAIR,
|
||||
.ipcns_storage_socket = EBADF_PAIR,
|
||||
};
|
||||
@ -2346,6 +2354,7 @@ static int exec_shared_runtime_add(
|
||||
const char *id,
|
||||
char **tmp_dir,
|
||||
char **var_tmp_dir,
|
||||
int userns_storage_socket[2],
|
||||
int netns_storage_socket[2],
|
||||
int ipcns_storage_socket[2],
|
||||
ExecSharedRuntime **ret) {
|
||||
@ -2370,6 +2379,11 @@ static int exec_shared_runtime_add(
|
||||
rt->tmp_dir = TAKE_PTR(*tmp_dir);
|
||||
rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
|
||||
|
||||
if (userns_storage_socket) {
|
||||
rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
|
||||
rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
|
||||
}
|
||||
|
||||
if (netns_storage_socket) {
|
||||
rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
|
||||
rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
|
||||
@ -2396,7 +2410,7 @@ static int exec_shared_runtime_make(
|
||||
ExecSharedRuntime **ret) {
|
||||
|
||||
_cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
|
||||
_cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
|
||||
_cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -2418,6 +2432,10 @@ static int exec_shared_runtime_make(
|
||||
return r;
|
||||
}
|
||||
|
||||
if (c->user_namespace_path)
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
|
||||
return -errno;
|
||||
|
||||
if (exec_needs_network_namespace(c))
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
|
||||
return -errno;
|
||||
@ -2426,7 +2444,7 @@ static int exec_shared_runtime_make(
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
|
||||
return -errno;
|
||||
|
||||
r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
|
||||
r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2484,6 +2502,26 @@ int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
|
||||
if (rt->var_tmp_dir)
|
||||
fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
|
||||
|
||||
if (rt->userns_storage_socket[0] >= 0) {
|
||||
int copy;
|
||||
|
||||
copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
|
||||
if (copy < 0)
|
||||
return copy;
|
||||
|
||||
fprintf(f, " userns-socket-0=%i", copy);
|
||||
}
|
||||
|
||||
if (rt->userns_storage_socket[1] >= 0) {
|
||||
int copy;
|
||||
|
||||
copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
|
||||
if (copy < 0)
|
||||
return copy;
|
||||
|
||||
fprintf(f, " userns-socket-1=%i", copy);
|
||||
}
|
||||
|
||||
if (rt->netns_storage_socket[0] >= 0) {
|
||||
int copy;
|
||||
|
||||
@ -2608,7 +2646,7 @@ int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char
|
||||
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
|
||||
_cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
|
||||
char *id = NULL;
|
||||
int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
|
||||
int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
|
||||
const char *p, *v = ASSERT_PTR(value);
|
||||
size_t n;
|
||||
|
||||
@ -2643,6 +2681,36 @@ int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fd
|
||||
p = v + n + 1;
|
||||
}
|
||||
|
||||
v = startswith(p, "userns-socket-0=");
|
||||
if (v) {
|
||||
char *buf;
|
||||
|
||||
n = strcspn(v, " ");
|
||||
buf = strndupa_safe(v, n);
|
||||
|
||||
userns_fdpair[0] = deserialize_fd(fds, buf);
|
||||
if (userns_fdpair[0] < 0)
|
||||
return userns_fdpair[0];
|
||||
if (v[n] != ' ')
|
||||
goto finalize;
|
||||
p = v + n + 1;
|
||||
}
|
||||
|
||||
v = startswith(p, "userns-socket-1=");
|
||||
if (v) {
|
||||
char *buf;
|
||||
|
||||
n = strcspn(v, " ");
|
||||
buf = strndupa_safe(v, n);
|
||||
|
||||
userns_fdpair[1] = deserialize_fd(fds, buf);
|
||||
if (userns_fdpair[1] < 0)
|
||||
return userns_fdpair[1];
|
||||
if (v[n] != ' ')
|
||||
goto finalize;
|
||||
p = v + n + 1;
|
||||
}
|
||||
|
||||
v = startswith(p, "netns-socket-0=");
|
||||
if (v) {
|
||||
char *buf;
|
||||
@ -2701,7 +2769,7 @@ int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fd
|
||||
}
|
||||
|
||||
finalize:
|
||||
r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
|
||||
r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add exec-runtime: %m");
|
||||
return 0;
|
||||
|
||||
@ -111,6 +111,9 @@ typedef struct ExecSharedRuntime {
|
||||
|
||||
/* Like netns_storage_socket, but the file descriptor is referring to the IPC namespace. */
|
||||
int ipcns_storage_socket[2];
|
||||
|
||||
/* Like netns_storage_socket, but the file descriptor is referring to the user namespace. */
|
||||
int userns_storage_socket[2];
|
||||
} ExecSharedRuntime;
|
||||
|
||||
typedef struct ExecRuntime {
|
||||
@ -353,6 +356,7 @@ typedef struct ExecContext {
|
||||
bool address_families_allow_list:1;
|
||||
Set *address_families;
|
||||
|
||||
char *user_namespace_path;
|
||||
char *network_namespace_path;
|
||||
char *ipc_namespace_path;
|
||||
|
||||
|
||||
@ -183,6 +183,7 @@ static int run(int argc, char *argv[]) {
|
||||
_cleanup_(exec_command_done) ExecCommand command = {};
|
||||
_cleanup_(exec_params_deep_clear) ExecParameters params = EXEC_PARAMETERS_INIT(/* flags= */ 0);
|
||||
_cleanup_(exec_shared_runtime_done) ExecSharedRuntime shared = {
|
||||
.userns_storage_socket = EBADF_PAIR,
|
||||
.netns_storage_socket = EBADF_PAIR,
|
||||
.ipcns_storage_socket = EBADF_PAIR,
|
||||
};
|
||||
|
||||
@ -35,6 +35,7 @@ static void exec_fuzz_one(FILE *f, FDSet *fdset) {
|
||||
DynamicCreds dynamic_creds = {};
|
||||
ExecCommand command = {};
|
||||
ExecSharedRuntime shared = {
|
||||
.userns_storage_socket = EBADF_PAIR,
|
||||
.netns_storage_socket = EBADF_PAIR,
|
||||
.ipcns_storage_socket = EBADF_PAIR,
|
||||
};
|
||||
|
||||
@ -132,6 +132,7 @@
|
||||
{{type}}.ProtectKernelLogs, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_logs)
|
||||
{{type}}.ProtectClock, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_clock)
|
||||
{{type}}.ProtectControlGroups, config_parse_protect_control_groups, 0, offsetof({{type}}, exec_context.protect_control_groups)
|
||||
{{type}}.UserNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.user_namespace_path)
|
||||
{{type}}.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.network_namespace_path)
|
||||
{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
|
||||
{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
|
||||
|
||||
@ -1580,6 +1580,15 @@ static int socket_address_listen_in_cgroup(
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(UNIT(s), r, "Failed to acquire runtime: %m");
|
||||
|
||||
if (s->exec_context.user_namespace_path &&
|
||||
s->exec_runtime &&
|
||||
s->exec_runtime->shared &&
|
||||
s->exec_runtime->shared->userns_storage_socket[0] >= 0) {
|
||||
r = open_shareable_ns_path(s->exec_runtime->shared->userns_storage_socket, s->exec_context.user_namespace_path, CLONE_NEWUSER);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(UNIT(s), r, "Failed to open user namespace path %s: %m", s->exec_context.user_namespace_path);
|
||||
}
|
||||
|
||||
if (s->exec_context.network_namespace_path &&
|
||||
s->exec_runtime &&
|
||||
s->exec_runtime->shared &&
|
||||
|
||||
@ -885,6 +885,7 @@ int unit_exec_context_build_json(sd_json_variant **ret, const char *name, void *
|
||||
JSON_BUILD_PAIR_TRISTATE_NON_NULL("MemoryKSM", c->memory_ksm),
|
||||
SD_JSON_BUILD_PAIR_STRING("PrivatePIDs", private_pids_to_string(c->private_pids)),
|
||||
SD_JSON_BUILD_PAIR_STRING("PrivateUsers", private_users_to_string(c->private_users)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("UserNamespacePath", c->user_namespace_path),
|
||||
SD_JSON_BUILD_PAIR_STRING("ProtectHostname", protect_hostname_to_string(c->protect_hostname)),
|
||||
JSON_BUILD_PAIR_YES_NO("ProtectClock", c->protect_clock),
|
||||
JSON_BUILD_PAIR_YES_NO("ProtectKernelTunables", c->protect_kernel_tunables),
|
||||
|
||||
@ -35,7 +35,9 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
|
||||
TarFlags flags =
|
||||
(userns_fd >= 0 ? TAR_SQUASH_UIDS_ABOVE_64K : 0) |
|
||||
(mac_selinux_use() ? TAR_SELINUX : 0);
|
||||
|
||||
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
|
||||
if (pipe2(pipefd, O_CLOEXEC) < 0)
|
||||
@ -100,7 +102,9 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
|
||||
TarFlags flags =
|
||||
(userns_fd >= 0 ? TAR_SQUASH_UIDS_ABOVE_64K : 0) |
|
||||
(mac_selinux_use() ? TAR_SELINUX : 0);
|
||||
|
||||
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
|
||||
if (pipe2(pipefd, O_CLOEXEC) < 0)
|
||||
|
||||
@ -64,11 +64,11 @@ struct json_variant_foreach_state {
|
||||
type cc = func(sd_json_variant_string(variant)); \
|
||||
if (cc < 0) { \
|
||||
/* Maybe this enum is recognizable if we replace "_" (i.e. Varlink syntax) with "-" (how we usually prefer it). */ \
|
||||
_cleanup_free_ char *z = strreplace(sd_json_variant_string(variant), "_", "-"); \
|
||||
_cleanup_free_ char *z = strdup(sd_json_variant_string(variant)); \
|
||||
if (!z) \
|
||||
return json_log_oom(variant, flags); \
|
||||
\
|
||||
cc = func(z); \
|
||||
cc = func(json_dashify(z)); \
|
||||
if (cc < 0) \
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Value of JSON field '%s' not recognized: %s", strna(n), sd_json_variant_string(variant)); \
|
||||
} \
|
||||
@ -261,3 +261,6 @@ enum {
|
||||
int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref);
|
||||
int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum);
|
||||
int json_variant_new_fd_info(sd_json_variant **ret, int fd);
|
||||
|
||||
char *json_underscorify(char *p);
|
||||
char *json_dashify(char *p);
|
||||
|
||||
@ -3474,8 +3474,9 @@ _public_ int sd_json_parse_file(
|
||||
return sd_json_parse_file_at(f, AT_FDCWD, path, flags, ret, reterr_line, reterr_column);
|
||||
}
|
||||
|
||||
static char *underscorify(char *p) {
|
||||
assert(p);
|
||||
char *json_underscorify(char *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
/* Replaces "-", "+" by "_", to deal with the usual enum naming rules we have. */
|
||||
|
||||
@ -3485,6 +3486,18 @@ static char *underscorify(char *p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
char *json_dashify(char *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
/* Replaces "-", "+" by "-", to (somewhat) undo what json_underscorify() does */
|
||||
|
||||
for (char *q = p; *q; q++)
|
||||
*q = IN_SET(*q, '_', '-', '+') ? '-' : *q;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
_public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
|
||||
JsonStack *stack = NULL;
|
||||
size_t n_stack = 1;
|
||||
@ -3538,7 +3551,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
p = underscorify(c);
|
||||
p = json_underscorify(c);
|
||||
}
|
||||
|
||||
r = sd_json_variant_new_string(&add, p);
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "errno-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "local-addresses.h"
|
||||
#include "nss-util.h"
|
||||
#include "resolve-util.h"
|
||||
@ -116,7 +117,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
|
||||
r_tuple->next = r_tuple_prev;
|
||||
r_tuple->name = r_name;
|
||||
r_tuple->family = AF_INET6;
|
||||
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
|
||||
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6));
|
||||
r_tuple->scopeid = 0;
|
||||
|
||||
idx += ALIGN(sizeof(struct gaih_addrtuple));
|
||||
@ -144,7 +145,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
|
||||
r_tuple->name = r_name;
|
||||
r_tuple->family = a->family;
|
||||
r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0;
|
||||
memcpy(r_tuple->addr, &a->address, 16);
|
||||
memcpy(r_tuple->addr, &a->address, FAMILY_ADDRESS_SIZE(a->family));
|
||||
|
||||
idx += ALIGN(sizeof(struct gaih_addrtuple));
|
||||
r_tuple_prev = r_tuple;
|
||||
@ -263,7 +264,7 @@ static enum nss_status fill_in_hostent(
|
||||
*(uint32_t*) r_addr = local_address_ipv4;
|
||||
idx += ALIGN(alen);
|
||||
} else if (socket_ipv6_is_enabled()) {
|
||||
memcpy(r_addr, LOCALADDRESS_IPV6, 16);
|
||||
memcpy(r_addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6));
|
||||
idx += ALIGN(alen);
|
||||
}
|
||||
|
||||
@ -463,7 +464,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
|
||||
if (!socket_ipv6_is_enabled())
|
||||
goto not_found;
|
||||
|
||||
if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
|
||||
if (memcmp(addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)) == 0) {
|
||||
canonical = "localhost";
|
||||
additional_from_hostname = true;
|
||||
goto found;
|
||||
|
||||
@ -23,6 +23,7 @@ DLSYM_PROTOTYPE(acl_delete_entry);
|
||||
DLSYM_PROTOTYPE(acl_delete_perm);
|
||||
DLSYM_PROTOTYPE(acl_dup);
|
||||
DLSYM_PROTOTYPE(acl_entries);
|
||||
DLSYM_PROTOTYPE(acl_extended_file);
|
||||
DLSYM_PROTOTYPE(acl_free);
|
||||
DLSYM_PROTOTYPE(acl_from_mode);
|
||||
DLSYM_PROTOTYPE(acl_from_text);
|
||||
@ -36,6 +37,7 @@ DLSYM_PROTOTYPE(acl_get_tag_type);
|
||||
DLSYM_PROTOTYPE(acl_init);
|
||||
DLSYM_PROTOTYPE(acl_set_fd);
|
||||
DLSYM_PROTOTYPE(acl_set_file);
|
||||
DLSYM_PROTOTYPE(acl_set_permset);
|
||||
DLSYM_PROTOTYPE(acl_set_qualifier);
|
||||
DLSYM_PROTOTYPE(acl_set_tag_type);
|
||||
DLSYM_PROTOTYPE(acl_to_any_text);
|
||||
@ -58,6 +60,7 @@ int dlopen_libacl(void) {
|
||||
DLSYM_ARG(acl_delete_perm),
|
||||
DLSYM_ARG(acl_dup),
|
||||
DLSYM_ARG(acl_entries),
|
||||
DLSYM_ARG(acl_extended_file),
|
||||
DLSYM_ARG(acl_free),
|
||||
DLSYM_ARG(acl_from_mode),
|
||||
DLSYM_ARG(acl_from_text),
|
||||
@ -71,6 +74,7 @@ int dlopen_libacl(void) {
|
||||
DLSYM_ARG(acl_init),
|
||||
DLSYM_ARG(acl_set_fd),
|
||||
DLSYM_ARG(acl_set_file),
|
||||
DLSYM_ARG(acl_set_permset),
|
||||
DLSYM_ARG(acl_set_qualifier),
|
||||
DLSYM_ARG(acl_set_tag_type),
|
||||
DLSYM_ARG(acl_to_any_text));
|
||||
|
||||
@ -20,6 +20,7 @@ extern DLSYM_PROTOTYPE(acl_delete_entry);
|
||||
extern DLSYM_PROTOTYPE(acl_delete_perm);
|
||||
extern DLSYM_PROTOTYPE(acl_dup);
|
||||
extern DLSYM_PROTOTYPE(acl_entries);
|
||||
extern DLSYM_PROTOTYPE(acl_extended_file);
|
||||
extern DLSYM_PROTOTYPE(acl_free);
|
||||
extern DLSYM_PROTOTYPE(acl_from_mode);
|
||||
extern DLSYM_PROTOTYPE(acl_from_text);
|
||||
@ -33,6 +34,7 @@ extern DLSYM_PROTOTYPE(acl_get_tag_type);
|
||||
extern DLSYM_PROTOTYPE(acl_init);
|
||||
extern DLSYM_PROTOTYPE(acl_set_fd);
|
||||
extern DLSYM_PROTOTYPE(acl_set_file);
|
||||
extern DLSYM_PROTOTYPE(acl_set_permset);
|
||||
extern DLSYM_PROTOTYPE(acl_set_qualifier);
|
||||
extern DLSYM_PROTOTYPE(acl_set_tag_type);
|
||||
extern DLSYM_PROTOTYPE(acl_to_any_text);
|
||||
@ -62,6 +64,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(char*, sym_acl_free, acl_free_charpp, NU
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(uid_t*, sym_acl_free, acl_free_uid_tpp, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(gid_t*, sym_acl_free, acl_free_gid_tpp, NULL);
|
||||
|
||||
static inline int acl_set_perm(acl_permset_t ps, acl_perm_t p, bool b) {
|
||||
return (b ? sym_acl_add_perm : sym_acl_delete_perm)(ps, p);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ACL_READ 0x04
|
||||
|
||||
@ -2419,6 +2419,7 @@ static const BusProperty execute_properties[] = {
|
||||
{ "ProtectProc", bus_append_string },
|
||||
{ "ProcSubset", bus_append_string },
|
||||
{ "NetworkNamespacePath", bus_append_string },
|
||||
{ "UserNamespacePath", bus_append_string },
|
||||
{ "IPCNamespacePath", bus_append_string },
|
||||
{ "LogNamespace", bus_append_string },
|
||||
{ "RootImagePolicy", bus_append_string },
|
||||
|
||||
@ -7,6 +7,10 @@
|
||||
#if HAVE_LIBARCHIVE
|
||||
static void *libarchive_dl = NULL;
|
||||
|
||||
DLSYM_PROTOTYPE(archive_entry_acl_add_entry) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_acl_next) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_acl_reset) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_fflags) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_filetype) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_free) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_gid) = NULL;
|
||||
@ -26,8 +30,10 @@ DLSYM_PROTOTYPE(archive_entry_pathname) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_rdevmajor) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_rdevminor) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_ctime) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_fflags) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_filetype) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_gid) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_hardlink) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_mtime) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_pathname) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_perm) = NULL;
|
||||
@ -36,11 +42,13 @@ DLSYM_PROTOTYPE(archive_entry_set_rdevminor) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_size) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_symlink) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_set_uid) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_sparse_add_entry) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_symlink) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_uid) = NULL;
|
||||
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||
DLSYM_PROTOTYPE(archive_entry_uid_is_set) = NULL;
|
||||
#endif
|
||||
DLSYM_PROTOTYPE(archive_entry_xattr_add_entry) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_xattr_next) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_entry_xattr_reset) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_error_string) = NULL;
|
||||
@ -59,7 +67,7 @@ DLSYM_PROTOTYPE(archive_write_new) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_write_open_FILE) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_write_open_fd) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_write_set_format_gnutar) = NULL;
|
||||
DLSYM_PROTOTYPE(archive_write_set_format_pax) = NULL;
|
||||
|
||||
int dlopen_libarchive(void) {
|
||||
ELF_NOTE_DLOPEN("archive",
|
||||
@ -71,6 +79,10 @@ int dlopen_libarchive(void) {
|
||||
&libarchive_dl,
|
||||
"libarchive.so.13",
|
||||
LOG_DEBUG,
|
||||
DLSYM_ARG(archive_entry_acl_add_entry),
|
||||
DLSYM_ARG(archive_entry_acl_next),
|
||||
DLSYM_ARG(archive_entry_acl_reset),
|
||||
DLSYM_ARG(archive_entry_fflags),
|
||||
DLSYM_ARG(archive_entry_filetype),
|
||||
DLSYM_ARG(archive_entry_free),
|
||||
DLSYM_ARG(archive_entry_gid),
|
||||
@ -90,8 +102,10 @@ int dlopen_libarchive(void) {
|
||||
DLSYM_ARG(archive_entry_rdevmajor),
|
||||
DLSYM_ARG(archive_entry_rdevminor),
|
||||
DLSYM_ARG(archive_entry_set_ctime),
|
||||
DLSYM_ARG(archive_entry_set_fflags),
|
||||
DLSYM_ARG(archive_entry_set_filetype),
|
||||
DLSYM_ARG(archive_entry_set_gid),
|
||||
DLSYM_ARG(archive_entry_set_hardlink),
|
||||
DLSYM_ARG(archive_entry_set_mtime),
|
||||
DLSYM_ARG(archive_entry_set_pathname),
|
||||
DLSYM_ARG(archive_entry_set_perm),
|
||||
@ -100,11 +114,13 @@ int dlopen_libarchive(void) {
|
||||
DLSYM_ARG(archive_entry_set_size),
|
||||
DLSYM_ARG(archive_entry_set_symlink),
|
||||
DLSYM_ARG(archive_entry_set_uid),
|
||||
DLSYM_ARG(archive_entry_sparse_add_entry),
|
||||
DLSYM_ARG(archive_entry_symlink),
|
||||
DLSYM_ARG(archive_entry_uid),
|
||||
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||
DLSYM_ARG(archive_entry_uid_is_set),
|
||||
#endif
|
||||
DLSYM_ARG(archive_entry_xattr_add_entry),
|
||||
DLSYM_ARG(archive_entry_xattr_next),
|
||||
DLSYM_ARG(archive_entry_xattr_reset),
|
||||
DLSYM_ARG(archive_error_string),
|
||||
@ -123,8 +139,7 @@ int dlopen_libarchive(void) {
|
||||
DLSYM_ARG(archive_write_open_FILE),
|
||||
DLSYM_ARG(archive_write_open_fd),
|
||||
DLSYM_ARG(archive_write_set_format_filter_by_ext),
|
||||
DLSYM_ARG(archive_write_set_format_gnutar)
|
||||
);
|
||||
DLSYM_ARG(archive_write_set_format_pax));
|
||||
}
|
||||
|
||||
/* libarchive uses its own file type macros. They happen to be defined the same way as the Linux ones, and
|
||||
|
||||
@ -9,6 +9,10 @@
|
||||
|
||||
#include "dlfcn-util.h"
|
||||
|
||||
extern DLSYM_PROTOTYPE(archive_entry_acl_add_entry);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_acl_next);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_acl_reset);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_fflags);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_filetype);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_free);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_gid);
|
||||
@ -22,8 +26,10 @@ extern DLSYM_PROTOTYPE(archive_entry_pathname);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_rdevmajor);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_rdevminor);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_ctime);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_fflags);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_filetype);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_gid);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_hardlink);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_mtime);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_pathname);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_perm);
|
||||
@ -32,8 +38,10 @@ extern DLSYM_PROTOTYPE(archive_entry_set_rdevminor);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_size);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_symlink);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_set_uid);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_sparse_add_entry);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_symlink);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_uid);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_xattr_add_entry);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_xattr_next);
|
||||
extern DLSYM_PROTOTYPE(archive_entry_xattr_reset);
|
||||
extern DLSYM_PROTOTYPE(archive_error_string);
|
||||
@ -52,7 +60,7 @@ extern DLSYM_PROTOTYPE(archive_write_new);
|
||||
extern DLSYM_PROTOTYPE(archive_write_open_FILE);
|
||||
extern DLSYM_PROTOTYPE(archive_write_open_fd);
|
||||
extern DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext);
|
||||
extern DLSYM_PROTOTYPE(archive_write_set_format_gnutar);
|
||||
extern DLSYM_PROTOTYPE(archive_write_set_format_pax);
|
||||
|
||||
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||
extern DLSYM_PROTOTYPE(archive_entry_gid_is_set);
|
||||
|
||||
@ -5,16 +5,25 @@
|
||||
#include "tar-util.h"
|
||||
|
||||
#if HAVE_LIBARCHIVE
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
#include "acl-util.h"
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "chattr-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "iovec-util.h"
|
||||
#include "libarchive-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "path-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "rm-rf.h"
|
||||
#include "sha256.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
@ -23,6 +32,15 @@
|
||||
|
||||
#define DEPTH_MAX 128U
|
||||
|
||||
/* We are a bit conservative with the flags we save/restore in tar files */
|
||||
#define CHATTR_TAR_FL \
|
||||
(FS_NOATIME_FL | \
|
||||
FS_NOCOW_FL | \
|
||||
FS_PROJINHERIT_FL | \
|
||||
FS_NODUMP_FL | \
|
||||
FS_SYNC_FL | \
|
||||
FS_DIRSYNC_FL)
|
||||
|
||||
typedef struct XAttr {
|
||||
char *name;
|
||||
struct iovec data;
|
||||
@ -38,8 +56,10 @@ typedef struct OpenInode {
|
||||
struct timespec mtime;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
unsigned fflags;
|
||||
XAttr *xattr;
|
||||
size_t n_xattr;
|
||||
acl_t acl_access, acl_default;
|
||||
} OpenInode;
|
||||
|
||||
static void xattr_done(XAttr *xa) {
|
||||
@ -68,6 +88,10 @@ static void open_inode_done(OpenInode *of) {
|
||||
of->path = mfree(of->path);
|
||||
}
|
||||
xattr_done_many(of->xattr, of->n_xattr);
|
||||
if (of->acl_access)
|
||||
sym_acl_free(of->acl_access);
|
||||
if (of->acl_default)
|
||||
sym_acl_free(of->acl_default);
|
||||
}
|
||||
|
||||
static void open_inode_done_many(OpenInode *array, size_t n) {
|
||||
@ -79,6 +103,29 @@ static void open_inode_done_many(OpenInode *array, size_t n) {
|
||||
free(array);
|
||||
}
|
||||
|
||||
static int open_inode_apply_acl(OpenInode *of) {
|
||||
int r = 0;
|
||||
|
||||
assert(of);
|
||||
assert(of->fd >= 0);
|
||||
|
||||
if (!inode_type_can_acl(of->filetype))
|
||||
return 0;
|
||||
|
||||
if (of->acl_access) {
|
||||
if (sym_acl_set_fd(of->fd, of->acl_access) < 0)
|
||||
RET_GATHER(r, log_error_errno(errno, "Failed to adjust ACLs of '%s': %m", of->path));
|
||||
}
|
||||
|
||||
if (of->filetype == S_IFDIR && of->acl_default) {
|
||||
/* There's no API to set default ACLs by fd, hence go by /proc/self/fd/ path */
|
||||
if (sym_acl_set_file(FORMAT_PROC_FD_PATH(of->fd), ACL_TYPE_DEFAULT, of->acl_default) < 0)
|
||||
RET_GATHER(r, log_error_errno(errno, "Failed to adjust default ACLs of '%s': %m", of->path));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int open_inode_finalize(OpenInode *of) {
|
||||
int r = 0;
|
||||
|
||||
@ -99,6 +146,24 @@ static int open_inode_finalize(OpenInode *of) {
|
||||
RET_GATHER(r, log_error_errno(k, "Failed to adjust ownership/mode of '%s': %m", of->path));
|
||||
}
|
||||
|
||||
k = open_inode_apply_acl(of);
|
||||
if (k < 0)
|
||||
RET_GATHER(r, log_error_errno(k, "Failed to adjust ACL of '%s': %m", of->path));
|
||||
|
||||
if ((of->fflags & ~CHATTR_EARLY_FL) != 0 && inode_type_can_chattr(of->filetype)) {
|
||||
k = chattr_full(of->fd,
|
||||
/* path= */ NULL,
|
||||
/* value= */ of->fflags,
|
||||
/* mask= */ of->fflags & ~CHATTR_EARLY_FL,
|
||||
/* ret_previous= */ NULL,
|
||||
/* ret_final= */ NULL,
|
||||
CHATTR_FALLBACK_BITWISE);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(k))
|
||||
log_warning_errno(k, "Failed to apply chattr of '%s', ignoring: %m", of->path);
|
||||
else if (k < 0)
|
||||
RET_GATHER(r, log_error_errno(k, "Failed to adjust chattr of '%s': %m", of->path));
|
||||
}
|
||||
|
||||
/* We also adjust the mtime only after leaving a dir, since it might otherwise change again
|
||||
* because we make modifications inside it */
|
||||
if (of->mtime.tv_nsec != UTIME_OMIT) {
|
||||
@ -149,7 +214,8 @@ static int archive_unpack_regular(
|
||||
struct archive_entry *entry,
|
||||
int parent_fd,
|
||||
const char *filename,
|
||||
const char *path) {
|
||||
const char *path,
|
||||
unsigned fflags) {
|
||||
|
||||
int r;
|
||||
|
||||
@ -164,6 +230,22 @@ static int archive_unpack_regular(
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to create regular file '%s': %m", path);
|
||||
|
||||
if ((fflags & CHATTR_EARLY_FL) != 0) {
|
||||
r = chattr_full(fd,
|
||||
/* path= */ NULL,
|
||||
/* value= */ fflags,
|
||||
/* mask= */ fflags & CHATTR_EARLY_FL,
|
||||
/* ret_previous= */ NULL,
|
||||
/* ret_final= */ NULL,
|
||||
CHATTR_FALLBACK_BITWISE);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
log_warning_errno(r, "Failed to apply chattr of '%s', ignoring: %m", path);
|
||||
else if (r < 0) {
|
||||
log_error_errno(r, "Failed to adjust chattr of '%s': %m", path);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
r = sym_archive_read_data_into_fd(a, fd);
|
||||
if (r != ARCHIVE_OK) {
|
||||
r = log_error_errno(
|
||||
@ -204,7 +286,10 @@ static int archive_unpack_directory(
|
||||
struct archive_entry *entry,
|
||||
int parent_fd,
|
||||
const char *filename,
|
||||
const char *path) {
|
||||
const char *path,
|
||||
unsigned fflags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(entry);
|
||||
@ -220,6 +305,20 @@ static int archive_unpack_directory(
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to create directory '%s': %m", path);
|
||||
|
||||
if ((fflags & CHATTR_EARLY_FL) != 0) {
|
||||
r = chattr_full(fd,
|
||||
/* path= */ NULL,
|
||||
/* value= */ fflags,
|
||||
/* mask= */ fflags & CHATTR_EARLY_FL,
|
||||
/* ret_previous= */ NULL,
|
||||
/* ret_final= */ NULL,
|
||||
CHATTR_FALLBACK_BITWISE);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
log_warning_errno(r, "Failed to apply chattr of '%s', ignoring: %m", path);
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Failed to adjust chattr of '%s': %m", path);
|
||||
}
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
@ -321,6 +420,145 @@ static int archive_entry_pathname_safe(struct archive_entry *entry, const char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int archive_entry_read_acl(
|
||||
struct archive_entry *entry,
|
||||
acl_type_t ntype,
|
||||
acl_t *acl,
|
||||
TarFlags flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(entry);
|
||||
assert(acl);
|
||||
|
||||
int type;
|
||||
if (ntype == ACL_TYPE_ACCESS)
|
||||
type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
|
||||
else if (ntype == ACL_TYPE_DEFAULT)
|
||||
type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
|
||||
else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unexpected ACL type");
|
||||
|
||||
int c = sym_archive_entry_acl_reset(entry, type);
|
||||
if (c == 0)
|
||||
return 0;
|
||||
assert(c > 0);
|
||||
|
||||
r = dlopen_libacl();
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Not restoring ACL data on inode as libacl is not available: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_(acl_freep) acl_t a = NULL;
|
||||
a = sym_acl_init(c);
|
||||
if (!a)
|
||||
return log_oom();
|
||||
|
||||
for (;;) {
|
||||
int rtype, permset, tag, qual;
|
||||
const char *name;
|
||||
r = sym_archive_entry_acl_next(
|
||||
entry,
|
||||
type,
|
||||
&rtype,
|
||||
&permset,
|
||||
&tag,
|
||||
&qual,
|
||||
&name);
|
||||
if (r == ARCHIVE_EOF)
|
||||
break;
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unexpected error while iterating through ACLs.");
|
||||
|
||||
assert(rtype == type);
|
||||
|
||||
acl_entry_t e;
|
||||
if (sym_acl_create_entry(&a, &e) < 0)
|
||||
return log_error_errno(errno, "Failed to create ACL entry: %m");
|
||||
|
||||
static const struct {
|
||||
int libarchive;
|
||||
acl_tag_t libacl;
|
||||
} tag_map[] = {
|
||||
{ ARCHIVE_ENTRY_ACL_USER, ACL_USER },
|
||||
{ ARCHIVE_ENTRY_ACL_GROUP, ACL_GROUP },
|
||||
{ ARCHIVE_ENTRY_ACL_USER_OBJ, ACL_USER_OBJ },
|
||||
{ ARCHIVE_ENTRY_ACL_GROUP_OBJ, ACL_GROUP_OBJ },
|
||||
{ ARCHIVE_ENTRY_ACL_MASK, ACL_MASK },
|
||||
{ ARCHIVE_ENTRY_ACL_OTHER, ACL_OTHER },
|
||||
};
|
||||
|
||||
acl_tag_t ntag = ACL_UNDEFINED_TAG;
|
||||
FOREACH_ELEMENT(t, tag_map)
|
||||
if (t->libarchive == tag) {
|
||||
ntag = t->libacl;
|
||||
break;
|
||||
}
|
||||
if (ntag == ACL_UNDEFINED_TAG)
|
||||
continue;
|
||||
|
||||
if (sym_acl_set_tag_type(e, ntag) < 0)
|
||||
return log_error_errno(errno, "Failed to set ACL entry tag: %m");
|
||||
|
||||
if (IN_SET(ntag, ACL_USER, ACL_GROUP)) {
|
||||
id_t id = qual;
|
||||
/* Suppress ACL entries for invalid UIDs/GIDS */
|
||||
if (!uid_is_valid(id))
|
||||
continue;
|
||||
|
||||
/* Suppress ACL entries for UIDs/GIDs to squash */
|
||||
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) && id >= NSRESOURCE_UIDS_64K)
|
||||
continue;
|
||||
|
||||
if (sym_acl_set_qualifier(e, &id) < 0)
|
||||
return log_error_errno(errno, "Failed to set ACL entry qualifier: %m");
|
||||
}
|
||||
|
||||
acl_permset_t p;
|
||||
if (sym_acl_get_permset(e, &p) < 0)
|
||||
return log_error_errno(errno, "Failed to get ACL entry permission set: %m");
|
||||
|
||||
r = acl_set_perm(p, ACL_READ, permset & ARCHIVE_ENTRY_ACL_READ);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set ACL entry read bit: %m");
|
||||
|
||||
r = acl_set_perm(p, ACL_WRITE, permset & ARCHIVE_ENTRY_ACL_WRITE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set ACL entry write bit: %m");
|
||||
|
||||
r = acl_set_perm(p, ACL_EXECUTE, permset & ARCHIVE_ENTRY_ACL_EXECUTE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set ACL entry excute bit: %m");
|
||||
|
||||
if (sym_acl_set_permset(e, p) < 0)
|
||||
return log_error_errno(errno, "Failed to set ACL entry permission set: %m");
|
||||
}
|
||||
|
||||
if (*acl)
|
||||
sym_acl_free(*acl);
|
||||
*acl = TAKE_PTR(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uid_t maybe_squash_uid(uid_t uid, TarFlags flags) {
|
||||
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) &&
|
||||
uid_is_valid(uid) &&
|
||||
uid >= NSRESOURCE_UIDS_64K)
|
||||
return UID_NOBODY;
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
static uid_t maybe_squash_gid(uid_t gid, TarFlags flags) {
|
||||
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) &&
|
||||
gid_is_valid(gid) &&
|
||||
gid >= NSRESOURCE_UIDS_64K)
|
||||
return GID_NOBODY;
|
||||
|
||||
return gid;
|
||||
}
|
||||
|
||||
static int archive_entry_read_stat(
|
||||
struct archive_entry *entry,
|
||||
mode_t *filetype,
|
||||
@ -328,10 +566,15 @@ static int archive_entry_read_stat(
|
||||
struct timespec *mtime,
|
||||
uid_t *uid,
|
||||
gid_t *gid,
|
||||
unsigned *fflags,
|
||||
acl_t *acl_access,
|
||||
acl_t *acl_default,
|
||||
XAttr **xa,
|
||||
size_t *n_xa,
|
||||
TarFlags flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(entry);
|
||||
|
||||
/* Fills in all fields that are present in the archive entry. Doesn't change the fields if the entry
|
||||
@ -349,24 +592,40 @@ static int archive_entry_read_stat(
|
||||
sym_archive_entry_mtime_nsec(entry),
|
||||
};
|
||||
if (uid && sym_archive_entry_uid_is_set(entry))
|
||||
*uid = sym_archive_entry_uid(entry);
|
||||
*uid = maybe_squash_uid(sym_archive_entry_uid(entry), flags);
|
||||
if (gid && sym_archive_entry_gid_is_set(entry))
|
||||
*gid = sym_archive_entry_gid(entry);
|
||||
*gid = maybe_squash_gid(sym_archive_entry_gid(entry), flags);
|
||||
|
||||
if (fflags) {
|
||||
unsigned long fs = 0, fc = 0;
|
||||
sym_archive_entry_fflags(entry, &fs, &fc);
|
||||
*fflags = (fs & ~fc) & CHATTR_TAR_FL;
|
||||
}
|
||||
|
||||
(void) sym_archive_entry_xattr_reset(entry);
|
||||
for (;;) {
|
||||
const char *name = NULL;
|
||||
struct iovec data;
|
||||
(void) sym_archive_entry_xattr_next(entry, &name, (const void**) &data.iov_base, &data.iov_len);
|
||||
if (!name)
|
||||
r = sym_archive_entry_xattr_next(entry, &name, (const void**) &data.iov_base, &data.iov_len);
|
||||
if (r != ARCHIVE_OK)
|
||||
break;
|
||||
|
||||
assert(name);
|
||||
if (xattr_is_acl(name))
|
||||
continue;
|
||||
|
||||
if (!FLAGS_SET(flags, TAR_SELINUX) && xattr_is_selinux(name))
|
||||
continue;
|
||||
|
||||
bool duplicate = false;
|
||||
FOREACH_ARRAY(i, *xa, *n_xa)
|
||||
if (streq(i->name, name)) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
if (duplicate)
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *n = strdup(name);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
@ -384,6 +643,18 @@ static int archive_entry_read_stat(
|
||||
};
|
||||
}
|
||||
|
||||
if (acl_access) {
|
||||
r = archive_entry_read_acl(entry, ACL_TYPE_ACCESS, acl_access, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (acl_default) {
|
||||
r = archive_entry_read_acl(entry, ACL_TYPE_DEFAULT, acl_default, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -458,6 +729,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
&open_inodes[0].mtime,
|
||||
&open_inodes[0].uid,
|
||||
&open_inodes[0].gid,
|
||||
&open_inodes[0].fflags,
|
||||
&open_inodes[0].acl_access,
|
||||
&open_inodes[0].acl_default,
|
||||
&open_inodes[0].xattr,
|
||||
&open_inodes[0].n_xattr,
|
||||
flags);
|
||||
@ -528,6 +802,8 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
uid_t uid = UID_INVALID;
|
||||
gid_t gid = GID_INVALID;
|
||||
struct timespec mtime = { .tv_nsec = UTIME_OMIT };
|
||||
unsigned fflags = 0;
|
||||
_cleanup_(acl_freep) acl_t acl_access = NULL, acl_default = NULL;
|
||||
XAttr *xa = NULL;
|
||||
size_t n_xa = 0;
|
||||
CLEANUP_ARRAY(xa, n_xa, xattr_done_many);
|
||||
@ -602,6 +878,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
&mtime,
|
||||
&uid,
|
||||
&gid,
|
||||
&fflags,
|
||||
&acl_access,
|
||||
&acl_default,
|
||||
&xa,
|
||||
&n_xa,
|
||||
flags);
|
||||
@ -611,11 +890,11 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
switch (filetype) {
|
||||
|
||||
case S_IFREG:
|
||||
fd = archive_unpack_regular(a, entry, parent_fd, e, j);
|
||||
fd = archive_unpack_regular(a, entry, parent_fd, e, j, fflags);
|
||||
break;
|
||||
|
||||
case S_IFDIR:
|
||||
fd = archive_unpack_directory(a, entry, parent_fd, e, j);
|
||||
fd = archive_unpack_directory(a, entry, parent_fd, e, j, fflags);
|
||||
break;
|
||||
|
||||
case S_IFLNK:
|
||||
@ -660,6 +939,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
.mtime = mtime,
|
||||
.uid = uid,
|
||||
.gid = gid,
|
||||
.fflags = fflags,
|
||||
.acl_access = TAKE_PTR(acl_access),
|
||||
.acl_default = TAKE_PTR(acl_default),
|
||||
.xattr = TAKE_PTR(xa),
|
||||
.n_xattr = n_xa,
|
||||
};
|
||||
@ -675,6 +957,258 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_tmpfs(void) {
|
||||
/* Creates a tmpfs superblock to store our hardlink db in. We can do this if we run in our own
|
||||
* userns, or if we are privileged. This is preferable, since it means the db is cleaned up
|
||||
* automatically once we are done. Moreover, since this is a new superblock owned by us, we do not
|
||||
* need to set up any uid mapping shenanigans */
|
||||
|
||||
_cleanup_close_ int superblock_fd = fsopen("tmpfs", FSOPEN_CLOEXEC);
|
||||
if (superblock_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to allocate tmpfs superblock: %m");
|
||||
|
||||
(void) fsconfig(superblock_fd, FSCONFIG_SET_STRING, "source", "hardlink", /* aux= */ 0);
|
||||
(void) fsconfig(superblock_fd, FSCONFIG_SET_STRING, "mode", "0700", /* aux= */ 0);
|
||||
|
||||
if (fsconfig(superblock_fd, FSCONFIG_CMD_CREATE, /* key= */ NULL, /* value= */ NULL, /* aux= */ 0) < 0)
|
||||
return log_debug_errno(errno, "Failed to finalize superblock: %m");
|
||||
|
||||
_cleanup_close_ int mount_fd = fsmount(superblock_fd, FSMOUNT_CLOEXEC, MS_NODEV|MS_NOEXEC|MS_NOSUID);
|
||||
if (mount_fd < 0)
|
||||
return log_debug_errno(errno, "Failed to turn tmpfs superblock into mount: %m");
|
||||
|
||||
return TAKE_FD(mount_fd);
|
||||
}
|
||||
|
||||
struct make_archive_data {
|
||||
struct archive *archive;
|
||||
TarFlags flags;
|
||||
int hardlink_db_fd;
|
||||
char *hardlink_db_path;
|
||||
};
|
||||
|
||||
static int hardlink_lookup(
|
||||
struct make_archive_data *d,
|
||||
int inode_fd,
|
||||
const struct statx *sx,
|
||||
const char *path,
|
||||
char **ret) {
|
||||
|
||||
_cleanup_free_ struct file_handle *handle = NULL;
|
||||
_cleanup_free_ char *m = NULL, *n = NULL;
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
assert(inode_fd >= 0);
|
||||
assert(sx);
|
||||
|
||||
/* If we know the hardlink count, and it's 1, then don't bother */
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_NLINK) && sx->stx_nlink == 1)
|
||||
goto bypass;
|
||||
|
||||
/* If this is a directory, then don't bother */
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_TYPE) && !inode_type_can_hardlink(sx->stx_mode))
|
||||
goto bypass;
|
||||
|
||||
int mnt_id;
|
||||
r = name_to_handle_at_try_fid(inode_fd, /* path= */ NULL, &handle, &mnt_id, /* flags= */ AT_EMPTY_PATH);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get file handle of file: %m");
|
||||
|
||||
m = hexmem(SHA256_DIRECT(handle->f_handle, handle->handle_bytes), SHA256_DIGEST_SIZE);
|
||||
if (!m)
|
||||
return log_oom();
|
||||
|
||||
if (asprintf(&n, "%i:%i:%s", mnt_id, handle->handle_type, m) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (d->hardlink_db_fd < 0) {
|
||||
assert(!d->hardlink_db_path);
|
||||
|
||||
/* We first try to create our own superblock, which works if we are in a userns, and which
|
||||
* doesn't require explicit clean-up */
|
||||
d->hardlink_db_fd = make_tmpfs();
|
||||
if (d->hardlink_db_fd < 0) {
|
||||
log_debug_errno(d->hardlink_db_fd, "Failed to allocate tmpfs superblock for hardlink db, falling back to temporary directory: %m");
|
||||
|
||||
const char *vt;
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine /var/tmp/ directory: %m");
|
||||
|
||||
_cleanup_free_ char *j = path_join(vt, "make-tar-XXXXXX");
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
d->hardlink_db_fd = mkdtemp_open(j, /* flags= */ 0, &d->hardlink_db_path);
|
||||
if (d->hardlink_db_fd < 0)
|
||||
return log_error_errno(d->hardlink_db_fd, "Failed to make hardlink database directory: %m");
|
||||
}
|
||||
} else {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = readlinkat_malloc(d->hardlink_db_fd, n, &p);
|
||||
if (r >= 0) {
|
||||
/* Found previous hit! */
|
||||
log_debug("hardlinkdb: found %s → %s", n, p);
|
||||
*ret = TAKE_PTR(p);
|
||||
return 1;
|
||||
}
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to read symlink '%s': %m", n);
|
||||
}
|
||||
|
||||
/* Store information about this inode */
|
||||
if (symlinkat(path, d->hardlink_db_fd, n) < 0)
|
||||
return log_error_errno(errno, "Failed to create symlink '%s' → '%s': %m", n, path);
|
||||
|
||||
log_debug("hardlinkdb: created %s → %s", n, path);
|
||||
|
||||
bypass:
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int archive_generate_sparse(struct archive_entry *entry, int fd) {
|
||||
assert(entry);
|
||||
assert(fd);
|
||||
|
||||
off_t c = 0;
|
||||
for (;;) {
|
||||
/* Look for the next hole */
|
||||
off_t h = lseek(fd, c, SEEK_HOLE);
|
||||
if (h < 0) {
|
||||
if (errno != ENXIO)
|
||||
return log_error_errno(errno, "Failed to issue SEEK_HOLE: %m");
|
||||
|
||||
/* If errno == ENXIO, that means we've reached the final data of the file and
|
||||
* that data isn't followed by anything more */
|
||||
|
||||
/* Figure out where the end of the file is */
|
||||
off_t e = lseek(fd, 0, SEEK_END);
|
||||
if (e < 0)
|
||||
return log_error_errno(errno, "Failed to issue SEEK_END: %m");
|
||||
|
||||
/* Generate sparse entry for final block */
|
||||
if (e > c && c != 0) {
|
||||
log_debug("final sparse block %" PRIu64 "…%" PRIu64, (uint64_t) c, (uint64_t) e);
|
||||
sym_archive_entry_sparse_add_entry(entry, c, e - c);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (h > c) {
|
||||
log_debug("inner sparse block %" PRIu64 "…%" PRIu64 " (%" PRIu64 ")", (uint64_t) c, (uint64_t) h, (uint64_t) h - (uint64_t) c);
|
||||
sym_archive_entry_sparse_add_entry(entry, c, h - c);
|
||||
}
|
||||
|
||||
/* Now look for the next data after the hole */
|
||||
c = lseek(fd, h, SEEK_DATA);
|
||||
if (c < 0) {
|
||||
if (errno != ENXIO)
|
||||
return log_error_errno(errno, "Failed to issue SEEK_DATA: %m");
|
||||
|
||||
/* No data anymore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return log_error_errno(errno, "Failed to reset seek offset: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int archive_write_acl(
|
||||
struct archive_entry *entry,
|
||||
acl_type_t ntype,
|
||||
acl_t acl,
|
||||
TarFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(entry);
|
||||
assert(acl);
|
||||
|
||||
int type;
|
||||
if (ntype == ACL_TYPE_ACCESS)
|
||||
type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
|
||||
else if (ntype == ACL_TYPE_DEFAULT)
|
||||
type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
|
||||
else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unexpected ACL type");
|
||||
|
||||
acl_entry_t e;
|
||||
r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &e);
|
||||
for (;;) {
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to get ACL entry: %m");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
acl_tag_t ntag;
|
||||
if (sym_acl_get_tag_type(e, &ntag) < 0)
|
||||
return log_error_errno(errno, "Failed to get ACL entry tag: %m");
|
||||
|
||||
static const int tag_map[] = {
|
||||
[ACL_USER] = ARCHIVE_ENTRY_ACL_USER,
|
||||
[ACL_GROUP] = ARCHIVE_ENTRY_ACL_GROUP,
|
||||
[ACL_USER_OBJ] = ARCHIVE_ENTRY_ACL_USER_OBJ,
|
||||
[ACL_GROUP_OBJ] = ARCHIVE_ENTRY_ACL_GROUP_OBJ,
|
||||
[ACL_MASK] = ARCHIVE_ENTRY_ACL_MASK,
|
||||
[ACL_OTHER] = ARCHIVE_ENTRY_ACL_OTHER,
|
||||
};
|
||||
assert_cc(ACL_UNDEFINED_TAG == 0); /* safety check, we assume that holes are filled with ACL_UNDEFINED_TAG */
|
||||
assert_cc(ELEMENTSOF(tag_map) <= 64); /* safety check, we assume that the tag ids are all packed and low */
|
||||
|
||||
int tag = ntag >= 0 && ntag <= (acl_tag_t) ELEMENTSOF(tag_map) ? tag_map[ntag] : ACL_UNDEFINED_TAG;
|
||||
|
||||
bool skip = false;
|
||||
id_t qualifier = UID_INVALID;
|
||||
if (IN_SET(ntag, ACL_USER, ACL_GROUP)) {
|
||||
id_t *q = sym_acl_get_qualifier(e);
|
||||
if (!q)
|
||||
return log_error_errno(errno, "Failed to get ACL entry qualifier: %m");
|
||||
|
||||
qualifier = *q;
|
||||
sym_acl_free(q);
|
||||
|
||||
/* Suppress invalid UIDs or those that shall be squashed */
|
||||
skip = !(uid_is_valid(qualifier) &&
|
||||
(!FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) || qualifier < NSRESOURCE_UIDS_64K));
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
acl_permset_t p;
|
||||
if (sym_acl_get_permset(e, &p) < 0)
|
||||
return log_error_errno(errno, "Failed to get ACL entry permission set: %m");
|
||||
|
||||
int permset = 0;
|
||||
r = sym_acl_get_perm(p, ACL_READ);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get ACL entry read bit: %m");
|
||||
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_READ, r);
|
||||
|
||||
r = sym_acl_get_perm(p, ACL_WRITE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get ACL entry write bit: %m");
|
||||
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_WRITE, r);
|
||||
|
||||
r = sym_acl_get_perm(p, ACL_EXECUTE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get ACL entry execute bit: %m");
|
||||
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_EXECUTE, r);
|
||||
|
||||
r = sym_archive_entry_acl_add_entry(entry, type, permset, tag, qualifier, /* name= */ NULL);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to add ACL entry.");
|
||||
}
|
||||
|
||||
r = sym_acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int archive_item(
|
||||
RecurseDirEvent event,
|
||||
const char *path,
|
||||
@ -684,7 +1218,7 @@ static int archive_item(
|
||||
const struct statx *sx,
|
||||
void *userdata) {
|
||||
|
||||
struct archive *a = ASSERT_PTR(userdata);
|
||||
struct make_archive_data *d = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
@ -702,17 +1236,31 @@ static int archive_item(
|
||||
if (!entry)
|
||||
return log_oom();
|
||||
|
||||
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
|
||||
sym_archive_entry_set_pathname(entry, path);
|
||||
|
||||
_cleanup_free_ char *hardlink = NULL;
|
||||
r = hardlink_lookup(d, inode_fd, sx, path, &hardlink);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
sym_archive_entry_set_hardlink(entry, hardlink);
|
||||
|
||||
if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));
|
||||
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
}
|
||||
|
||||
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
|
||||
sym_archive_entry_set_filetype(entry, sx->stx_mode);
|
||||
|
||||
if (!S_ISLNK(sx->stx_mode))
|
||||
sym_archive_entry_set_perm(entry, sx->stx_mode);
|
||||
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_UID))
|
||||
sym_archive_entry_set_uid(entry, sx->stx_uid);
|
||||
sym_archive_entry_set_uid(entry, maybe_squash_uid(sx->stx_uid, d->flags));
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_GID))
|
||||
sym_archive_entry_set_gid(entry, sx->stx_gid);
|
||||
sym_archive_entry_set_gid(entry, maybe_squash_gid(sx->stx_gid, d->flags));
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
|
||||
@ -745,17 +1293,90 @@ static int archive_item(
|
||||
sym_archive_entry_set_symlink(entry, s);
|
||||
}
|
||||
|
||||
if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
|
||||
if (inode_type_can_acl(sx->stx_mode)) {
|
||||
|
||||
r = dlopen_libacl();
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "No trying to read ACL off inode, as libacl support is not available: %m");
|
||||
else {
|
||||
r = sym_acl_extended_file(FORMAT_PROC_FD_PATH(inode_fd));
|
||||
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return log_error_errno(errno, "Failed check if '%s' has ACLs: %m", path);
|
||||
if (r > 0) {
|
||||
_cleanup_(acl_freep) acl_t acl = NULL;
|
||||
acl = sym_acl_get_file(FORMAT_PROC_FD_PATH(inode_fd), ACL_TYPE_ACCESS);
|
||||
if (!acl)
|
||||
return log_error_errno(errno, "Failed read access ACLs of '%s': %m", path);
|
||||
|
||||
archive_write_acl(entry, ACL_TYPE_ACCESS, acl, d->flags);
|
||||
|
||||
if (S_ISDIR(sx->stx_mode)) {
|
||||
sym_acl_free(acl);
|
||||
|
||||
acl = sym_acl_get_file(FORMAT_PROC_FD_PATH(inode_fd), ACL_TYPE_DEFAULT);
|
||||
if (!acl)
|
||||
return log_error_errno(errno, "Failed to read default ACLs of '%s': %m", path);
|
||||
|
||||
archive_write_acl(entry, ACL_TYPE_DEFAULT, acl, d->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup_free_ char *xattrs = NULL;
|
||||
r = flistxattr_malloc(inode_fd, &xattrs);
|
||||
if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r) && r != -ENODATA)
|
||||
return log_error_errno(r, "Failed to read xattr list of '%s': %m", path);
|
||||
|
||||
NULSTR_FOREACH(xa, xattrs) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t size;
|
||||
|
||||
if (xattr_is_acl(xa))
|
||||
continue;
|
||||
|
||||
if (!FLAGS_SET(d->flags, TAR_SELINUX) && xattr_is_selinux(xa))
|
||||
continue;
|
||||
|
||||
r = fgetxattr_malloc(inode_fd, xa, &buf, &size);
|
||||
if (r == -ENODATA) /* deleted by now? ignore... */
|
||||
continue;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read xattr '%s' of '%s': %m", xa, path);
|
||||
|
||||
sym_archive_entry_xattr_add_entry(entry, xa, buf, size);
|
||||
}
|
||||
|
||||
_cleanup_close_ int data_fd = -EBADF;
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
_cleanup_close_ int data_fd = -EBADF;
|
||||
|
||||
/* Convert the O_PATH fd in a proper fd */
|
||||
/* Convert the O_PATH fd into a proper fd */
|
||||
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
|
||||
if (data_fd < 0)
|
||||
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
|
||||
|
||||
r = archive_generate_sparse(entry, data_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (inode_type_can_chattr(sx->stx_mode)) {
|
||||
unsigned f = 0;
|
||||
|
||||
r = read_attr_fd(data_fd >= 0 ? data_fd : inode_fd, &f);
|
||||
if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return log_error_errno(r, "Failed to read file flags of '%s': %m", path);
|
||||
|
||||
f &= CHATTR_TAR_FL;
|
||||
if (f != 0)
|
||||
sym_archive_entry_set_fflags(entry, f, /* clear= */ 0);
|
||||
}
|
||||
|
||||
if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
assert(data_fd >= 0);
|
||||
|
||||
for (;;) {
|
||||
char buffer[64*1024];
|
||||
ssize_t l;
|
||||
@ -767,15 +1388,24 @@ static int archive_item(
|
||||
break;
|
||||
|
||||
la_ssize_t k;
|
||||
k = sym_archive_write_data(a, buffer, l);
|
||||
k = sym_archive_write_data(d->archive, buffer, l);
|
||||
if (k < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(d->archive));
|
||||
}
|
||||
}
|
||||
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
}
|
||||
|
||||
static void make_archive_data_done(struct make_archive_data *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->hardlink_db_fd >= 0)
|
||||
(void) rm_rf_children(d->hardlink_db_fd, REMOVE_PHYSICAL, /* root_dev= */ NULL);
|
||||
|
||||
unlink_and_free(d->hardlink_db_path);
|
||||
}
|
||||
|
||||
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
|
||||
int r;
|
||||
|
||||
@ -789,7 +1419,7 @@ int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
|
||||
if (filename)
|
||||
r = sym_archive_write_set_format_filter_by_ext(a, filename);
|
||||
else
|
||||
r = sym_archive_write_set_format_gnutar(a);
|
||||
r = sym_archive_write_set_format_pax(a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
|
||||
|
||||
@ -797,13 +1427,19 @@ int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
|
||||
|
||||
_cleanup_(make_archive_data_done) struct make_archive_data data = {
|
||||
.archive = a,
|
||||
.flags = flags,
|
||||
.hardlink_db_fd = -EBADF,
|
||||
};
|
||||
|
||||
r = recurse_dir(tree_fd,
|
||||
".",
|
||||
STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
|
||||
UINT_MAX,
|
||||
RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
|
||||
archive_item,
|
||||
a);
|
||||
&data);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make archive: %m");
|
||||
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum TarFlags {
|
||||
TAR_SELINUX = 1 << 0,
|
||||
TAR_SELINUX = 1 << 0, /* Include SELinux xattr in tarball, or unpack it */
|
||||
TAR_SQUASH_UIDS_ABOVE_64K = 1 << 1, /* Squash UIDs/GIDs above 64K when packing/unpacking to the nobody user */
|
||||
} TarFlags;
|
||||
|
||||
int tar_x(int input_fd, int tree_fd, TarFlags flags);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "varlink-io.systemd.BootControl.h"
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
BootEntryType,
|
||||
SD_VARLINK_FIELD_COMMENT("Boot Loader Specification Type #1 entries (.conf files)"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(type1),
|
||||
@ -13,7 +13,7 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
SD_VARLINK_FIELD_COMMENT("Automatically generated entries"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(auto));
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
BootEntrySource,
|
||||
SD_VARLINK_FIELD_COMMENT("Boot entry found in EFI system partition (ESP)"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(esp),
|
||||
|
||||
@ -3,4 +3,7 @@
|
||||
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
extern const sd_varlink_symbol vl_type_BootEntryType;
|
||||
extern const sd_varlink_symbol vl_type_BootEntrySource;
|
||||
|
||||
extern const sd_varlink_interface vl_interface_io_systemd_BootControl;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include "bus-polkit.h"
|
||||
#include "varlink-io.systemd.MountFileSystem.h"
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
PartitionDesignator,
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(root),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(usr),
|
||||
|
||||
@ -3,4 +3,6 @@
|
||||
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
extern const sd_varlink_symbol vl_type_PartitionDesignator;
|
||||
|
||||
extern const sd_varlink_interface vl_interface_io_systemd_MountFileSystem;
|
||||
|
||||
@ -582,6 +582,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD(PrivatePIDs, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#PrivateUsers="),
|
||||
SD_VARLINK_DEFINE_FIELD(PrivateUsers, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#UserNamespacePath="),
|
||||
SD_VARLINK_DEFINE_FIELD(UserNamespacePath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#ProtectHostname="),
|
||||
SD_VARLINK_DEFINE_FIELD(ProtectHostname, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#ProtectClock="),
|
||||
|
||||
@ -7,7 +7,10 @@
|
||||
#include "sd-varlink.h"
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
#include "bootspec.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "tests.h"
|
||||
#include "varlink-idl-util.h"
|
||||
@ -457,4 +460,58 @@ TEST(validate_method_call) {
|
||||
assert_se(pthread_join(t, NULL) == 0);
|
||||
}
|
||||
|
||||
static void test_enum_to_string_name(const char *n, const sd_varlink_symbol *symbol) {
|
||||
assert(n);
|
||||
assert(symbol);
|
||||
|
||||
assert(symbol->symbol_type == SD_VARLINK_ENUM_TYPE);
|
||||
_cleanup_free_ char *m = ASSERT_PTR(json_underscorify(strdup(n)));
|
||||
|
||||
bool found = false;
|
||||
for (const sd_varlink_field *f = symbol->fields; f->name; f++) {
|
||||
if (f->field_type == _SD_VARLINK_FIELD_COMMENT)
|
||||
continue;
|
||||
|
||||
assert(f->field_type == SD_VARLINK_ENUM_VALUE);
|
||||
if (streq(m, f->name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("'%s' found in '%s': %s", m, strna(symbol->name), yes_no(found));
|
||||
assert(found);
|
||||
}
|
||||
|
||||
#define TEST_IDL_ENUM_TO_STRING(type, ename, symbol) \
|
||||
for (type t = 0;; t++) { \
|
||||
const char *n = ename##_to_string(t); \
|
||||
if (!n) \
|
||||
break; \
|
||||
test_enum_to_string_name(n, &(symbol)); \
|
||||
}
|
||||
|
||||
#define TEST_IDL_ENUM_FROM_STRING(type, ename, symbol) \
|
||||
for (const sd_varlink_field *f = (symbol).fields; f->name; f++) { \
|
||||
if (f->field_type == _SD_VARLINK_FIELD_COMMENT) \
|
||||
continue; \
|
||||
assert(f->field_type == SD_VARLINK_ENUM_VALUE); \
|
||||
_cleanup_free_ char *m = ASSERT_PTR(json_dashify(strdup(f->name))); \
|
||||
type t = ename##_from_string(m); \
|
||||
log_debug("'%s' of '%s' translates: %s", f->name, strna((symbol).name), yes_no(t >= 0)); \
|
||||
assert(t >= 0); \
|
||||
}
|
||||
|
||||
#define TEST_IDL_ENUM(type, name, symbol) \
|
||||
do { \
|
||||
TEST_IDL_ENUM_TO_STRING(type, name, symbol); \
|
||||
TEST_IDL_ENUM_FROM_STRING(type, name, symbol); \
|
||||
} while (false)
|
||||
|
||||
TEST(enums_idl) {
|
||||
TEST_IDL_ENUM(BootEntryType, boot_entry_type, vl_type_BootEntryType);
|
||||
TEST_IDL_ENUM_TO_STRING(BootEntrySource, boot_entry_source, vl_type_BootEntrySource);
|
||||
TEST_IDL_ENUM(PartitionDesignator, partition_designator, vl_type_PartitionDesignator);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
@ -276,6 +276,7 @@ Unit=
|
||||
UpheldBy=
|
||||
Upholds=
|
||||
User=
|
||||
UserNamespacePath=
|
||||
WakeSystem=
|
||||
WantedBy=
|
||||
Wants=
|
||||
|
||||
52
test/units/TEST-07-PID1.user-namespace-path.sh
Executable file
52
test/units/TEST-07-PID1.user-namespace-path.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
# When sanitizers are used, export LD_PRELOAD with the sanitizers path,
|
||||
# lsns doesn't work otherwise.
|
||||
if [ -f /usr/lib/systemd/systemd-asan-env ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. /usr/lib/systemd/systemd-asan-env
|
||||
export LD_PRELOAD
|
||||
export ASAN_OPTIONS
|
||||
fi
|
||||
|
||||
# Only reuse the user namespace
|
||||
systemd-run --unit=oldservice --property=Type=exec --property=PrivateUsers=true sleep 3600
|
||||
OLD_PID=$(systemctl show oldservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
systemd-run --unit=newservice --property=Type=exec --property=UserNamespacePath=/proc/"$OLD_PID"/ns/user --property=PrivateNetwork=true sleep 3600
|
||||
NEW_PID=$(systemctl show newservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
assert_neq "$(lsns -p "$OLD_PID" -o NS -t net -n)" "$(lsns -p "$NEW_PID" -o NS -t net -n)"
|
||||
assert_eq "$(lsns -p "$OLD_PID" -o NS -t user -n)" "$(lsns -p "$NEW_PID" -o NS -t user -n)"
|
||||
|
||||
systemctl stop oldservice newservice
|
||||
|
||||
# Reuse the user and network namespaces
|
||||
systemd-run --unit=oldservice --property=Type=exec --property=PrivateUsers=true --property=PrivateNetwork=true sleep 3600
|
||||
OLD_PID=$(systemctl show oldservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
systemd-run --unit=newservice --property=Type=exec --property=UserNamespacePath=/proc/"$OLD_PID"/ns/user --property=NetworkNamespacePath=/proc/"$OLD_PID"/ns/net sleep 3600
|
||||
NEW_PID=$(systemctl show newservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
assert_eq "$(lsns -p "$OLD_PID" -o NS -t net -n)" "$(lsns -p "$NEW_PID" -o NS -t net -n)"
|
||||
assert_eq "$(lsns -p "$OLD_PID" -o NS -t user -n)" "$(lsns -p "$NEW_PID" -o NS -t user -n)"
|
||||
|
||||
systemctl stop oldservice newservice
|
||||
|
||||
# Delegate the network namespace
|
||||
systemd-run --unit=oldservice --property=Type=exec --property=PrivateUsers=true sleep 3600
|
||||
OLD_PID=$(systemctl show oldservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
systemd-run --unit=newservice --property=Type=exec --property=UserNamespacePath=/proc/"$OLD_PID"/ns/user --property=DelegateNamespaces=net --property=PrivateNetwork=true sleep 3600
|
||||
NEW_PID=$(systemctl show newservice -p MainPID | awk -F= '{print $2}')
|
||||
|
||||
assert_neq "$(lsns -p "$OLD_PID" -o NS -t net -n)" "$(lsns -p "$NEW_PID" -o NS -t net -n)"
|
||||
assert_eq "$(lsns -p "$OLD_PID" -o NS -t user -n)" "$(lsns -p "$NEW_PID" -o NS -t user -n)"
|
||||
|
||||
systemctl stop oldservice newservice
|
||||
@ -14,8 +14,21 @@ fi
|
||||
|
||||
at_exit() {
|
||||
rm -rf /home/testuser/.local/state/machines/zurps ||:
|
||||
rm -rf /home/testuser/.local/state/machines/nurps ||:
|
||||
rm -rf /home/testuser/.local/state/machines/kurps ||:
|
||||
rm -rf /home/testuser/.local/state/machines/wumms ||:
|
||||
rm -rf /home/testuser/.local/state/machines/wamms ||:
|
||||
rm -rf /home/testuser/.local/state/machines/inodetest ||:
|
||||
rm -rf /home/testuser/.local/state/machines/inodetest2 ||:
|
||||
machinectl terminate zurps ||:
|
||||
rm -f /etc/polkit-1/rules.d/registermachinetest.rules
|
||||
machinectl terminate nurps ||:
|
||||
machinectl terminate kurps ||:
|
||||
machinectl terminate wumms ||:
|
||||
machinectl terminate wamms ||:
|
||||
rm -f /usr/share/polkit-1/rules.d/registermachinetest.rules
|
||||
rm -rf /var/tmp/mangletest
|
||||
rm -f /var/tmp/mangletest.tar.gz
|
||||
}
|
||||
|
||||
trap at_exit EXIT
|
||||
@ -45,6 +58,8 @@ EOF
|
||||
|
||||
loginctl enable-linger testuser
|
||||
|
||||
run0 -u testuser mkdir -p .config/systemd/nspawn/
|
||||
run0 -u testuser -i "echo -e \"[Exec]\nKillSignal=SIGKILL\n\" > .config/systemd/nspawn/zurps.nspawn"
|
||||
run0 -u testuser systemctl start --user systemd-nspawn@zurps.service
|
||||
|
||||
machinectl status zurps
|
||||
@ -87,4 +102,114 @@ run0 -u testuser \
|
||||
(! run0 -u testuser machinectl shell 0@shouldnotwork2 /usr/bin/id -u)
|
||||
(! run0 -u testuser machinectl shell testuser@shouldnotwork2 /usr/bin/id -u)
|
||||
|
||||
run0 -u testuser mkdir /var/tmp/image-tar
|
||||
run0 -u testuser importctl --user export-tar zurps /var/tmp/image-tar/kurps.tar.gz -m
|
||||
run0 -u testuser importctl --user import-tar /var/tmp/image-tar/kurps.tar.gz -m
|
||||
|
||||
run0 -u testuser -i "echo -e \"[Exec]\nKillSignal=SIGKILL\n\" > .config/systemd/nspawn/kurps.nspawn"
|
||||
run0 -u testuser systemctl start --user systemd-nspawn@kurps.service
|
||||
machinectl terminate kurps
|
||||
|
||||
run0 -u testuser -D /var/tmp/image-tar/ bash -c 'sha256sum kurps.tar.gz > SHA256SUMS'
|
||||
run0 -u testuser importctl --user pull-tar file:///var/tmp/image-tar/kurps.tar.gz nurps --verify=checksum -m
|
||||
|
||||
run0 -u testuser -i "echo -e \"[Exec]\nKillSignal=SIGKILL\n\" > .config/systemd/nspawn/nurps.nspawn"
|
||||
run0 -u testuser systemctl start --user systemd-nspawn@nurps.service
|
||||
machinectl terminate nurps
|
||||
|
||||
run0 -u testuser rm -r /var/tmp/image-tar
|
||||
|
||||
run0 -u testuser importctl --user list-images
|
||||
run0 -u testuser machinectl --user list-images
|
||||
|
||||
assert_in 'zurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
assert_in 'nurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
assert_in 'kurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
|
||||
run0 -u testuser machinectl --user image-status zurps
|
||||
run0 -u testuser machinectl --user image-status nurps
|
||||
run0 -u testuser machinectl --user image-status kurps
|
||||
|
||||
run0 -u testuser machinectl --user show-image zurps
|
||||
run0 -u testuser machinectl --user show-image nurps
|
||||
run0 -u testuser machinectl --user show-image kurps
|
||||
|
||||
run0 -u testuser machinectl --user clone zurps wumms
|
||||
|
||||
assert_in 'wumms' "$(run0 -u testuser machinectl --user list-images)"
|
||||
run0 -u testuser machinectl --user image-status wumms
|
||||
run0 -u testuser machinectl --user show-image wumms
|
||||
|
||||
run0 -u testuser machinectl --user rename wumms wamms
|
||||
assert_not_in 'wumms' "$(run0 -u testuser machinectl --user list-images)"
|
||||
assert_in 'wamms' "$(run0 -u testuser machinectl --user list-images)"
|
||||
run0 -u testuser machinectl --user image-status wamms
|
||||
run0 -u testuser machinectl --user show-image wamms
|
||||
|
||||
run0 -u testuser -i "echo -e \"[Exec]\nKillSignal=SIGKILL\n\" > .config/systemd/nspawn/wamms.nspawn"
|
||||
run0 -u testuser systemctl start --user systemd-nspawn@wamms.service
|
||||
|
||||
run0 -u testuser systemctl stop --user systemd-nspawn@zurps.service
|
||||
run0 -u testuser systemctl stop --user systemd-nspawn@nurps.service
|
||||
run0 -u testuser systemctl stop --user systemd-nspawn@kurps.service
|
||||
run0 -u testuser systemctl stop --user systemd-nspawn@wamms.service
|
||||
|
||||
run0 -u testuser machinectl --user remove zurps
|
||||
run0 -u testuser machinectl --user remove kurps
|
||||
run0 -u testuser machinectl --user remove nurps
|
||||
run0 -u testuser machinectl --user remove wamms
|
||||
|
||||
assert_not_in 'zurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
assert_not_in 'nurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
assert_not_in 'kurps' "$(run0 --pipe -u testuser machinectl --user list-images)"
|
||||
|
||||
mkdir /home/testuser/.local/state/machines/inodetest
|
||||
echo hallo > /home/testuser/.local/state/machines/inodetest/testfile
|
||||
|
||||
# Make the file sparse, set an xattr, set an ACL, set a chattr flag, and make it hardlink
|
||||
ln /home/testuser/.local/state/machines/inodetest/testfile /home/testuser/.local/state/machines/inodetest/testfile.hard
|
||||
truncate -s 1M /home/testuser/.local/state/machines/inodetest/testfile
|
||||
setfattr -n "user.piff" -v "paff" /home/testuser/.local/state/machines/inodetest/testfile
|
||||
setfacl -m g:foreign-47:rwx /home/testuser/.local/state/machines/inodetest/testfile
|
||||
chattr +A /home/testuser/.local/state/machines/inodetest/testfile
|
||||
chown foreign-0:foreign-0 /home/testuser/.local/state/machines/inodetest/testfile.hard /home/testuser/.local/state/machines/inodetest
|
||||
ls -al /home/testuser/.local/state/machines/inodetest
|
||||
|
||||
echo gaga > /home/testuser/.local/state/machines/inodetest/squashtest
|
||||
chown 1000:1000 /home/testuser/.local/state/machines/inodetest/squashtest
|
||||
|
||||
run0 --pipe -u testuser importctl -m --user export-tar inodetest |
|
||||
run0 --pipe -u testuser importctl -m --user import-tar - inodetest2
|
||||
|
||||
ls -al /home/testuser/.local/state/machines/inodetest2
|
||||
|
||||
cmp /home/testuser/.local/state/machines/inodetest/testfile /home/testuser/.local/state/machines/inodetest2/testfile.hard
|
||||
cmp <(stat -c"%s %b %B" /home/testuser/.local/state/machines/inodetest/testfile) <(stat -c"%s %b %B" /home/testuser/.local/state/machines/inodetest2/testfile)
|
||||
cmp <(stat -c"%i" /home/testuser/.local/state/machines/inodetest2/testfile) <(stat -c"%i" /home/testuser/.local/state/machines/inodetest2/testfile.hard)
|
||||
getfattr -d /home/testuser/.local/state/machines/inodetest/testfile
|
||||
getfattr -d /home/testuser/.local/state/machines/inodetest2/testfile
|
||||
getfacl /home/testuser/.local/state/machines/inodetest/testfile
|
||||
getfacl /home/testuser/.local/state/machines/inodetest2/testfile
|
||||
lsattr /home/testuser/.local/state/machines/inodetest/testfile
|
||||
lsattr /home/testuser/.local/state/machines/inodetest2/testfile
|
||||
cmp <(getfattr -d /home/testuser/.local/state/machines/inodetest/testfile | grep -v ^#) <(getfattr -d /home/testuser/.local/state/machines/inodetest2/testfile | grep -v ^#)
|
||||
cmp <(getfacl /home/testuser/.local/state/machines/inodetest/testfile | grep -v ^#) <(getfacl /home/testuser/.local/state/machines/inodetest2/testfile | grep -v ^#)
|
||||
cmp <(lsattr /home/testuser/.local/state/machines/inodetest/testfile | cut -d " " -f1) <(lsattr /home/testuser/.local/state/machines/inodetest2/testfile | cut -d " " -f1)
|
||||
|
||||
# verify that squashing outside of 64K works
|
||||
test "$(stat -c'%U:%G' /home/testuser/.local/state/machines/inodetest2/squashtest)" = "foreign-65534:foreign-65534"
|
||||
|
||||
# chown to foreing UID range, so that removal works
|
||||
chown foreign-4711:foreign-4711 /home/testuser/.local/state/machines/inodetest/squashtest
|
||||
|
||||
run0 -u testuser machinectl --user remove inodetest
|
||||
run0 -u testuser machinectl --user remove inodetest2
|
||||
|
||||
# Test tree mangling (i.e. moving the root dir one level up on extract)
|
||||
mkdir -p /var/tmp/mangletest/mangletest-0.1/usr/lib
|
||||
echo "ID=brumm" > /var/tmp/mangletest/mangletest-0.1/usr/lib/os-release
|
||||
tar -C /var/tmp/mangletest/ -cvzf /var/tmp/mangletest.tar.gz mangletest-0.1
|
||||
run0 --pipe -u testuser importctl -m --user import-tar /var/tmp/mangletest.tar.gz
|
||||
cmp /var/tmp/mangletest/mangletest-0.1/usr/lib/os-release /home/testuser/.local/state/machines/mangletest/usr/lib/os-release
|
||||
|
||||
loginctl disable-linger testuser
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
(! journalctl -q -o short-monotonic --grep "didn't pass validation" | grep -v "test-varlink-idl" >>/failed)
|
||||
(! journalctl -q -o short-monotonic --grep "didn't pass validation" | grep -v "test-varlink-idl")
|
||||
|
||||
# Here, the redundant '[ ]' in the pattern is required in order not to match the logged command itself.
|
||||
(! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork' >>/failed)
|
||||
(! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork')
|
||||
|
||||
# Check if sd-executor doesn't complain about not being able to (de)serialize stuff
|
||||
(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line" >>/failed)
|
||||
(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+" >>/failed)
|
||||
(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line")
|
||||
(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user