mirror of
https://github.com/systemd/systemd
synced 2025-11-20 17:24:45 +01:00
Compare commits
14 Commits
aad0d11e7c
...
de2276cdcd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de2276cdcd | ||
|
|
9dd61cfbe7 | ||
|
|
3eb7b881bd | ||
|
|
4cae0e9a78 | ||
|
|
9f69ff69f7 | ||
|
|
0d87de0b8e | ||
|
|
566a4bbbbf | ||
|
|
ce7a5d6026 | ||
|
|
a9b1e35a32 | ||
|
|
d29a2cd2d4 | ||
|
|
a7c8f92d1f | ||
|
|
8be204df2b | ||
|
|
90fae0b46c | ||
|
|
b2fa6d0945 |
@ -3992,6 +3992,34 @@ ServerAddress=192.168.0.1/24</programlisting>
|
|||||||
<xi:include href="version-info.xml" xpointer="v226"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v226"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>EmitDomain=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a boolean. Configures whether the DHCP leases handed out
|
||||||
|
to clients shall contain domain name information (DHCP option 15). Defaults to
|
||||||
|
<literal>no</literal>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>Domain=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a domain name (such as <literal>example.com</literal>)
|
||||||
|
to pass to DHCP clients. This configures the DNS default domain for DHCP clients.
|
||||||
|
When set, DHCP clients will use this as their DNS search domain.</para>
|
||||||
|
|
||||||
|
<para>When <varname>EmitDomain=yes</varname> is set but <varname>Domain=</varname>
|
||||||
|
is not configured, the domain name will be automatically derived from the system's
|
||||||
|
fully qualified hostname. For example, if the system's hostname is
|
||||||
|
<literal>host.example.com</literal>, the domain <literal>example.com</literal>
|
||||||
|
will be sent to clients. If the system's hostname does not contain a domain part
|
||||||
|
(e.g., hostname is just <literal>host</literal>), no domain name will be sent to
|
||||||
|
DHCP clients. When empty or unset, defaults to no domain name.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>BootServerAddress=</varname></term>
|
<term><varname>BootServerAddress=</varname></term>
|
||||||
|
|
||||||
|
|||||||
@ -1438,6 +1438,10 @@ libarchive = dependency('libarchive',
|
|||||||
version : '>= 3.0',
|
version : '>= 3.0',
|
||||||
required : get_option('libarchive'))
|
required : get_option('libarchive'))
|
||||||
conf.set10('HAVE_LIBARCHIVE', libarchive.found())
|
conf.set10('HAVE_LIBARCHIVE', libarchive.found())
|
||||||
|
conf.set10('HAVE_LIBARCHIVE_UID_IS_SET',
|
||||||
|
libblkid.found() and cc.has_function('archive_entry_uid_is_set', dependencies : libarchive))
|
||||||
|
conf.set10('HAVE_LIBARCHIVE_HARDLINK_IS_SET',
|
||||||
|
libblkid.found() and cc.has_function('archive_entry_hardlink_is_set', dependencies : libarchive))
|
||||||
|
|
||||||
libxkbcommon = dependency('xkbcommon',
|
libxkbcommon = dependency('xkbcommon',
|
||||||
version : '>= 0.3.0',
|
version : '>= 0.3.0',
|
||||||
|
|||||||
@ -108,6 +108,10 @@ int stat_verify_symlink(const struct stat *st) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fd_verify_symlink(int fd) {
|
||||||
|
return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_symlink, /* verify= */ true);
|
||||||
|
}
|
||||||
|
|
||||||
int is_symlink(const char *path) {
|
int is_symlink(const char *path) {
|
||||||
assert(!isempty(path));
|
assert(!isempty(path));
|
||||||
return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
|
return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
|
||||||
|
|||||||
@ -16,6 +16,7 @@ int is_dir_at(int fd, const char *path, bool follow);
|
|||||||
int is_dir(const char *path, bool follow);
|
int is_dir(const char *path, bool follow);
|
||||||
|
|
||||||
int stat_verify_symlink(const struct stat *st);
|
int stat_verify_symlink(const struct stat *st);
|
||||||
|
int fd_verify_symlink(int fd);
|
||||||
int is_symlink(const char *path);
|
int is_symlink(const char *path);
|
||||||
|
|
||||||
int stat_verify_linked(const struct stat *st);
|
int stat_verify_linked(const struct stat *st);
|
||||||
@ -110,3 +111,10 @@ static inline bool stat_is_set(const struct stat *st) {
|
|||||||
static inline bool statx_is_set(const struct statx *sx) {
|
static inline bool statx_is_set(const struct statx *sx) {
|
||||||
return sx && sx->stx_mask != 0;
|
return sx && sx->stx_mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool inode_type_can_hardlink(mode_t m) {
|
||||||
|
/* returns true for all inode types that support hardlinks on linux. Note this is effectively all
|
||||||
|
* inode types except for directories (and those weird misc fds such as eventfds() that have no inode
|
||||||
|
* type). */
|
||||||
|
return IN_SET(m & S_IFMT, S_IFSOCK, S_IFLNK, S_IFREG, S_IFBLK, S_IFCHR, S_IFIFO);
|
||||||
|
}
|
||||||
|
|||||||
@ -477,3 +477,14 @@ int fd_setcrtime(int fd, usec_t usec) {
|
|||||||
"user.crtime_usec", (const char*) &le, sizeof(le),
|
"user.crtime_usec", (const char*) &le, sizeof(le),
|
||||||
/* xattr_flags = */ 0);
|
/* xattr_flags = */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool xattr_is_acl(const char *name) {
|
||||||
|
return STR_IN_SET(
|
||||||
|
ASSERT_PTR(name),
|
||||||
|
"system.posix_acl_access",
|
||||||
|
"system.posix_acl_default");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool xattr_is_selinux(const char *name) {
|
||||||
|
return streq(ASSERT_PTR(name), "security.selinux");
|
||||||
|
}
|
||||||
|
|||||||
@ -54,3 +54,6 @@ int getcrtime_at(int fd, const char *path, int at_flags, usec_t *ret);
|
|||||||
static inline int fd_getcrtime(int fd, usec_t *ret) {
|
static inline int fd_getcrtime(int fd, usec_t *ret) {
|
||||||
return getcrtime_at(fd, NULL, 0, ret);
|
return getcrtime_at(fd, NULL, 0, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool xattr_is_acl(const char *name);
|
||||||
|
bool xattr_is_selinux(const char *name);
|
||||||
|
|||||||
@ -1,71 +1,60 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/prctl.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "sd-event.h"
|
#include "sd-event.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "capability-util.h"
|
#include "capability-util.h"
|
||||||
#include "dirent-util.h"
|
#include "dirent-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "import-common.h"
|
#include "import-common.h"
|
||||||
|
#include "libarchive-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
#include "pidref.h"
|
#include "pidref.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
#include "selinux-util.h"
|
#include "selinux-util.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
|
#include "tar-util.h"
|
||||||
#include "tmpfile-util.h"
|
#include "tmpfile-util.h"
|
||||||
|
|
||||||
int import_fork_tar_x(const char *path, PidRef *ret) {
|
int import_fork_tar_x(int tree_fd, PidRef *ret_pid) {
|
||||||
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
|
|
||||||
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
|
|
||||||
bool use_selinux;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(path);
|
assert(tree_fd >= 0);
|
||||||
assert(ret);
|
assert(ret_pid);
|
||||||
|
|
||||||
|
r = dlopen_libarchive();
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
|
||||||
|
|
||||||
|
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
|
||||||
if (pipe2(pipefd, O_CLOEXEC) < 0)
|
if (pipe2(pipefd, O_CLOEXEC) < 0)
|
||||||
return log_error_errno(errno, "Failed to create pipe for tar: %m");
|
return log_error_errno(errno, "Failed to create pipe for tar: %m");
|
||||||
|
|
||||||
(void) fcntl(pipefd[0], F_SETPIPE_SZ, IMPORT_BUFFER_SIZE);
|
(void) fcntl(pipefd[0], F_SETPIPE_SZ, IMPORT_BUFFER_SIZE);
|
||||||
|
|
||||||
use_selinux = mac_selinux_use();
|
|
||||||
|
|
||||||
r = pidref_safe_fork_full(
|
r = pidref_safe_fork_full(
|
||||||
"(tar)",
|
"tar-x",
|
||||||
(int[]) { pipefd[0], -EBADF, STDERR_FILENO },
|
/* stdio_fds= */ NULL,
|
||||||
NULL, 0,
|
(int[]) { tree_fd, pipefd[0] }, 2,
|
||||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG,
|
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG,
|
||||||
&pid);
|
ret_pid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
const char *cmdline[] = {
|
static const uint64_t retain =
|
||||||
"tar",
|
|
||||||
"--ignore-zeros",
|
|
||||||
"--numeric-owner",
|
|
||||||
"-C", path,
|
|
||||||
"-pxf",
|
|
||||||
"-",
|
|
||||||
"--xattrs",
|
|
||||||
"--xattrs-include=*",
|
|
||||||
use_selinux ? "--selinux" : "--no-selinux",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t retain =
|
|
||||||
(1ULL << CAP_CHOWN) |
|
(1ULL << CAP_CHOWN) |
|
||||||
(1ULL << CAP_FOWNER) |
|
(1ULL << CAP_FOWNER) |
|
||||||
(1ULL << CAP_FSETID) |
|
(1ULL << CAP_FSETID) |
|
||||||
(1ULL << CAP_MKNOD) |
|
(1ULL << CAP_MKNOD) |
|
||||||
(1ULL << CAP_SETFCAP) |
|
(1ULL << CAP_SETFCAP) |
|
||||||
(1ULL << CAP_DAC_OVERRIDE);
|
(1ULL << CAP_DAC_OVERRIDE) |
|
||||||
|
(1ULL << CAP_DAC_READ_SEARCH);
|
||||||
|
|
||||||
/* Child */
|
/* Child */
|
||||||
|
|
||||||
@ -76,21 +65,15 @@ int import_fork_tar_x(const char *path, PidRef *ret) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_warning_errno(r, "Failed to drop capabilities, ignoring: %m");
|
log_warning_errno(r, "Failed to drop capabilities, ignoring: %m");
|
||||||
|
|
||||||
/* Try "gtar" before "tar". We only test things upstream with GNU tar. Some distros appear to
|
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
|
||||||
* install a different implementation as "tar" (in particular some that do not support the
|
log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m");
|
||||||
* same command line switches), but then provide "gtar" as alias for the real thing, hence
|
|
||||||
* let's prefer that. (Yes, it's a bad idea they do that, given they don't provide equivalent
|
|
||||||
* command line support, but we are not here to argue, let's just expose the same
|
|
||||||
* behaviour/implementation everywhere.) */
|
|
||||||
execvp("gtar", (char* const*) cmdline);
|
|
||||||
execvp("tar", (char* const*) cmdline);
|
|
||||||
|
|
||||||
log_error_errno(errno, "Failed to execute tar: %m");
|
if (tar_x(pipefd[0], tree_fd, flags) < 0)
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
*ret = TAKE_PIDREF(pid);
|
|
||||||
|
|
||||||
return TAKE_FD(pipefd[1]);
|
return TAKE_FD(pipefd[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ typedef enum ImportFlags {
|
|||||||
} ImportFlags;
|
} ImportFlags;
|
||||||
|
|
||||||
int import_fork_tar_c(const char *path, PidRef *ret);
|
int import_fork_tar_c(const char *path, PidRef *ret);
|
||||||
int import_fork_tar_x(const char *path, PidRef *ret);
|
int import_fork_tar_x(int tree_fd, PidRef *ret_pid);
|
||||||
|
|
||||||
int import_mangle_os_tree(const char *path);
|
int import_mangle_os_tree(const char *path);
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,7 @@ typedef struct TarImport {
|
|||||||
|
|
||||||
int input_fd;
|
int input_fd;
|
||||||
int tar_fd;
|
int tar_fd;
|
||||||
|
int tree_fd;
|
||||||
|
|
||||||
ImportCompress compress;
|
ImportCompress compress;
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ TarImport* tar_import_unref(TarImport *i) {
|
|||||||
sd_event_unref(i->event);
|
sd_event_unref(i->event);
|
||||||
|
|
||||||
safe_close(i->tar_fd);
|
safe_close(i->tar_fd);
|
||||||
|
safe_close(i->tree_fd);
|
||||||
|
|
||||||
free(i->final_path);
|
free(i->final_path);
|
||||||
free(i->image_root);
|
free(i->image_root);
|
||||||
@ -111,6 +113,7 @@ int tar_import_new(
|
|||||||
*i = (TarImport) {
|
*i = (TarImport) {
|
||||||
.input_fd = -EBADF,
|
.input_fd = -EBADF,
|
||||||
.tar_fd = -EBADF,
|
.tar_fd = -EBADF,
|
||||||
|
.tree_fd = -EBADF,
|
||||||
.on_finished = on_finished,
|
.on_finished = on_finished,
|
||||||
.userdata = userdata,
|
.userdata = userdata,
|
||||||
.last_percent = UINT_MAX,
|
.last_percent = UINT_MAX,
|
||||||
@ -172,6 +175,7 @@ static int tar_import_finish(TarImport *i) {
|
|||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
assert(i->tar_fd >= 0);
|
assert(i->tar_fd >= 0);
|
||||||
|
assert(i->tree_fd >= 0);
|
||||||
|
|
||||||
i->tar_fd = safe_close(i->tar_fd);
|
i->tar_fd = safe_close(i->tar_fd);
|
||||||
|
|
||||||
@ -215,6 +219,7 @@ static int tar_import_fork_tar(TarImport *i) {
|
|||||||
assert(!i->final_path);
|
assert(!i->final_path);
|
||||||
assert(!i->temp_path);
|
assert(!i->temp_path);
|
||||||
assert(i->tar_fd < 0);
|
assert(i->tar_fd < 0);
|
||||||
|
assert(i->tree_fd < 0);
|
||||||
|
|
||||||
if (i->flags & IMPORT_DIRECT) {
|
if (i->flags & IMPORT_DIRECT) {
|
||||||
d = i->local;
|
d = i->local;
|
||||||
@ -254,7 +259,11 @@ static int tar_import_fork_tar(TarImport *i) {
|
|||||||
(void) import_assign_pool_quota_and_warn(d);
|
(void) import_assign_pool_quota_and_warn(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
i->tar_fd = import_fork_tar_x(d, &i->tar_pid);
|
i->tree_fd = open(d, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||||
|
if (i->tree_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open '%s': %m", d);
|
||||||
|
|
||||||
|
i->tar_fd = import_fork_tar_x(i->tree_fd, &i->tar_pid);
|
||||||
if (i->tar_fd < 0)
|
if (i->tar_fd < 0)
|
||||||
return i->tar_fd;
|
return i->tar_fd;
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
#include "curl-util.h"
|
#include "curl-util.h"
|
||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "import-common.h"
|
#include "import-common.h"
|
||||||
#include "import-util.h"
|
#include "import-util.h"
|
||||||
@ -61,6 +62,8 @@ typedef struct TarPull {
|
|||||||
char *settings_temp_path;
|
char *settings_temp_path;
|
||||||
|
|
||||||
char *checksum;
|
char *checksum;
|
||||||
|
|
||||||
|
int tree_fd;
|
||||||
} TarPull;
|
} TarPull;
|
||||||
|
|
||||||
TarPull* tar_pull_unref(TarPull *i) {
|
TarPull* tar_pull_unref(TarPull *i) {
|
||||||
@ -86,6 +89,8 @@ TarPull* tar_pull_unref(TarPull *i) {
|
|||||||
free(i->local);
|
free(i->local);
|
||||||
free(i->checksum);
|
free(i->checksum);
|
||||||
|
|
||||||
|
safe_close(i->tree_fd);
|
||||||
|
|
||||||
return mfree(i);
|
return mfree(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +137,7 @@ int tar_pull_new(
|
|||||||
.event = TAKE_PTR(e),
|
.event = TAKE_PTR(e),
|
||||||
.glue = TAKE_PTR(g),
|
.glue = TAKE_PTR(g),
|
||||||
.tar_pid = PIDREF_NULL,
|
.tar_pid = PIDREF_NULL,
|
||||||
|
.tree_fd = -EBADF,
|
||||||
};
|
};
|
||||||
|
|
||||||
i->glue->on_finished = pull_job_curl_on_finished;
|
i->glue->on_finished = pull_job_curl_on_finished;
|
||||||
@ -512,6 +518,7 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
|
|||||||
i = j->userdata;
|
i = j->userdata;
|
||||||
assert(i->tar_job == j);
|
assert(i->tar_job == j);
|
||||||
assert(!pidref_is_set(&i->tar_pid));
|
assert(!pidref_is_set(&i->tar_pid));
|
||||||
|
assert(i->tree_fd < 0);
|
||||||
|
|
||||||
if (i->flags & IMPORT_DIRECT)
|
if (i->flags & IMPORT_DIRECT)
|
||||||
where = i->local;
|
where = i->local;
|
||||||
@ -545,7 +552,11 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
|
|||||||
(void) import_assign_pool_quota_and_warn(where);
|
(void) import_assign_pool_quota_and_warn(where);
|
||||||
}
|
}
|
||||||
|
|
||||||
j->disk_fd = import_fork_tar_x(where, &i->tar_pid);
|
i->tree_fd = open(where, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||||
|
if (i->tree_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open '%s': %m", where);
|
||||||
|
|
||||||
|
j->disk_fd = import_fork_tar_x(i->tree_fd, &i->tar_pid);
|
||||||
if (j->disk_fd < 0)
|
if (j->disk_fd < 0)
|
||||||
return j->disk_fd;
|
return j->disk_fd;
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ typedef struct sd_dhcp_server {
|
|||||||
uint32_t pool_size;
|
uint32_t pool_size;
|
||||||
|
|
||||||
char *timezone;
|
char *timezone;
|
||||||
|
char *domain_name;
|
||||||
|
|
||||||
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
||||||
struct in_addr boot_server_address;
|
struct in_addr boot_server_address;
|
||||||
|
|||||||
@ -128,6 +128,7 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
|
|||||||
free(server->boot_server_name);
|
free(server->boot_server_name);
|
||||||
free(server->boot_filename);
|
free(server->boot_filename);
|
||||||
free(server->timezone);
|
free(server->timezone);
|
||||||
|
free(server->domain_name);
|
||||||
|
|
||||||
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
|
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
|
||||||
free(server->servers[i].addr);
|
free(server->servers[i].addr);
|
||||||
@ -625,6 +626,15 @@ static int server_send_offer_or_ack(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server->domain_name) {
|
||||||
|
r = dhcp_option_append(
|
||||||
|
&packet->dhcp, req->max_optlen, &offset, 0,
|
||||||
|
SD_DHCP_OPTION_DOMAIN_NAME,
|
||||||
|
strlen(server->domain_name), server->domain_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* RFC 8925 section 3.3. DHCPv4 Server Behavior
|
/* RFC 8925 section 3.3. DHCPv4 Server Behavior
|
||||||
* The server MUST NOT include the IPv6-Only Preferred option in the DHCPOFFER or DHCPACK message if
|
* The server MUST NOT include the IPv6-Only Preferred option in the DHCPOFFER or DHCPACK message if
|
||||||
* the option was not present in the Parameter Request List sent by the client. */
|
* the option was not present in the Parameter Request List sent by the client. */
|
||||||
@ -1415,6 +1425,22 @@ int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sd_dhcp_server_set_domain_name(sd_dhcp_server *server, const char *domain_name) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_return(server, -EINVAL);
|
||||||
|
|
||||||
|
if (domain_name) {
|
||||||
|
r = dns_name_is_valid(domain_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return free_and_strdup(&server->domain_name, domain_name);
|
||||||
|
}
|
||||||
|
|
||||||
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t) {
|
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t) {
|
||||||
assert_return(server, -EINVAL);
|
assert_return(server, -EINVAL);
|
||||||
|
|
||||||
|
|||||||
@ -316,6 +316,44 @@ static void test_static_lease(void) {
|
|||||||
(uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t)));
|
(uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_domain_name(void) {
|
||||||
|
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
|
||||||
|
|
||||||
|
log_debug("/* %s */", __func__);
|
||||||
|
|
||||||
|
ASSERT_OK(sd_dhcp_server_new(&server, 1));
|
||||||
|
|
||||||
|
/* Test setting domain name */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, "example.com"));
|
||||||
|
|
||||||
|
/* Test setting same domain name (should return 0 - no change) */
|
||||||
|
ASSERT_OK_ZERO(sd_dhcp_server_set_domain_name(server, "example.com"));
|
||||||
|
|
||||||
|
/* Test changing domain name */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, "test.local"));
|
||||||
|
|
||||||
|
/* Test clearing domain name */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, NULL));
|
||||||
|
|
||||||
|
/* Test clearing again (should return 0 - already cleared) */
|
||||||
|
ASSERT_OK_ZERO(sd_dhcp_server_set_domain_name(server, NULL));
|
||||||
|
|
||||||
|
/* Test invalid domain name */
|
||||||
|
ASSERT_ERROR(sd_dhcp_server_set_domain_name(server, "invalid..domain"), EINVAL);
|
||||||
|
|
||||||
|
/* Test empty string (treated differently from NULL) */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, ""));
|
||||||
|
|
||||||
|
/* Test clearing domain name with NULL */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, NULL));
|
||||||
|
|
||||||
|
/* Test valid domain with subdomain */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, "sub.example.com"));
|
||||||
|
|
||||||
|
/* Test single-label domain */
|
||||||
|
ASSERT_OK_POSITIVE(sd_dhcp_server_set_domain_name(server, "local"));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -323,6 +361,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
test_client_id_hash();
|
test_client_id_hash();
|
||||||
test_static_lease();
|
test_static_lease();
|
||||||
|
test_domain_name();
|
||||||
|
|
||||||
r = test_basic(true);
|
r = test_basic(true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@ -108,7 +108,7 @@
|
|||||||
<defaults>
|
<defaults>
|
||||||
<allow_any>auth_admin</allow_any>
|
<allow_any>auth_admin</allow_any>
|
||||||
<allow_inactive>auth_admin</allow_inactive>
|
<allow_inactive>auth_admin</allow_inactive>
|
||||||
<allow_active>auth_admin_keep</allow_active>
|
<allow_active>yes</allow_active>
|
||||||
</defaults>
|
</defaults>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
#include "loop-util.h"
|
#include "loop-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
|
#include "mount-util.h"
|
||||||
#include "namespace-util.h"
|
#include "namespace-util.h"
|
||||||
#include "nsresource.h"
|
#include "nsresource.h"
|
||||||
#include "nulstr-util.h"
|
#include "nulstr-util.h"
|
||||||
@ -41,6 +42,7 @@
|
|||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
#include "uid-classification.h"
|
#include "uid-classification.h"
|
||||||
#include "uid-range.h"
|
#include "uid-range.h"
|
||||||
|
#include "user-util.h"
|
||||||
#include "varlink-io.systemd.MountFileSystem.h"
|
#include "varlink-io.systemd.MountFileSystem.h"
|
||||||
#include "varlink-util.h"
|
#include "varlink-util.h"
|
||||||
|
|
||||||
@ -89,6 +91,7 @@ typedef struct MountImageParameters {
|
|||||||
int growfs;
|
int growfs;
|
||||||
char *password;
|
char *password;
|
||||||
ImagePolicy *image_policy;
|
ImagePolicy *image_policy;
|
||||||
|
bool verity_sharing;
|
||||||
} MountImageParameters;
|
} MountImageParameters;
|
||||||
|
|
||||||
static void mount_image_parameters_done(MountImageParameters *p) {
|
static void mount_image_parameters_done(MountImageParameters *p) {
|
||||||
@ -283,12 +286,13 @@ static int vl_method_mount_image(
|
|||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "imageFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MountImageParameters, image_fd_idx), SD_JSON_MANDATORY },
|
{ "imageFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MountImageParameters, image_fd_idx), SD_JSON_MANDATORY },
|
||||||
{ "userNamespaceFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MountImageParameters, userns_fd_idx), 0 },
|
{ "userNamespaceFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(MountImageParameters, userns_fd_idx), 0 },
|
||||||
{ "readOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MountImageParameters, read_only), 0 },
|
{ "readOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MountImageParameters, read_only), 0 },
|
||||||
{ "growFileSystems", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MountImageParameters, growfs), 0 },
|
{ "growFileSystems", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MountImageParameters, growfs), 0 },
|
||||||
{ "password", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MountImageParameters, password), 0 },
|
{ "password", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MountImageParameters, password), 0 },
|
||||||
{ "imagePolicy", SD_JSON_VARIANT_STRING, json_dispatch_image_policy, offsetof(MountImageParameters, image_policy), 0 },
|
{ "imagePolicy", SD_JSON_VARIANT_STRING, json_dispatch_image_policy, offsetof(MountImageParameters, image_policy), 0 },
|
||||||
|
{ "veritySharing", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(MountImageParameters, verity_sharing), 0 },
|
||||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -403,6 +407,7 @@ static int vl_method_mount_image(
|
|||||||
DISSECT_IMAGE_FSCK |
|
DISSECT_IMAGE_FSCK |
|
||||||
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
|
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
|
||||||
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
|
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
|
||||||
|
(p.verity_sharing ? DISSECT_IMAGE_VERITY_SHARE : 0) |
|
||||||
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
|
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
|
||||||
|
|
||||||
/* Let's see if we have acquired the privilege to mount untrusted images already */
|
/* Let's see if we have acquired the privilege to mount untrusted images already */
|
||||||
@ -561,8 +566,8 @@ static int vl_method_mount_image(
|
|||||||
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(pp->uuid), "partitionUuid", SD_JSON_BUILD_UUID(pp->uuid)),
|
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(pp->uuid), "partitionUuid", SD_JSON_BUILD_UUID(pp->uuid)),
|
||||||
SD_JSON_BUILD_PAIR("fileSystemType", SD_JSON_BUILD_STRING(dissected_partition_fstype(pp))),
|
SD_JSON_BUILD_PAIR("fileSystemType", SD_JSON_BUILD_STRING(dissected_partition_fstype(pp))),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!!pp->label, "partitionLabel", SD_JSON_BUILD_STRING(pp->label)),
|
SD_JSON_BUILD_PAIR_CONDITION(!!pp->label, "partitionLabel", SD_JSON_BUILD_STRING(pp->label)),
|
||||||
SD_JSON_BUILD_PAIR("size", SD_JSON_BUILD_INTEGER(pp->size)),
|
SD_JSON_BUILD_PAIR("size", SD_JSON_BUILD_UNSIGNED(pp->size)),
|
||||||
SD_JSON_BUILD_PAIR("offset", SD_JSON_BUILD_INTEGER(pp->offset)),
|
SD_JSON_BUILD_PAIR("offset", SD_JSON_BUILD_UNSIGNED(pp->offset)),
|
||||||
SD_JSON_BUILD_PAIR("mountFileDescriptor", SD_JSON_BUILD_INTEGER(fd_idx)),
|
SD_JSON_BUILD_PAIR("mountFileDescriptor", SD_JSON_BUILD_INTEGER(fd_idx)),
|
||||||
JSON_BUILD_PAIR_STRV_NON_EMPTY("mountPoint", l));
|
JSON_BUILD_PAIR_STRV_NON_EMPTY("mountPoint", l));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -575,8 +580,8 @@ static int vl_method_mount_image(
|
|||||||
link,
|
link,
|
||||||
SD_JSON_BUILD_PAIR("partitions", SD_JSON_BUILD_VARIANT(aj)),
|
SD_JSON_BUILD_PAIR("partitions", SD_JSON_BUILD_VARIANT(aj)),
|
||||||
SD_JSON_BUILD_PAIR("imagePolicy", SD_JSON_BUILD_STRING(ps)),
|
SD_JSON_BUILD_PAIR("imagePolicy", SD_JSON_BUILD_STRING(ps)),
|
||||||
SD_JSON_BUILD_PAIR("imageSize", SD_JSON_BUILD_INTEGER(di->image_size)),
|
SD_JSON_BUILD_PAIR("imageSize", SD_JSON_BUILD_UNSIGNED(di->image_size)),
|
||||||
SD_JSON_BUILD_PAIR("sectorSize", SD_JSON_BUILD_INTEGER(di->sector_size)),
|
SD_JSON_BUILD_PAIR("sectorSize", SD_JSON_BUILD_UNSIGNED(di->sector_size)),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(di->image_uuid), "imageUuid", SD_JSON_BUILD_UUID(di->image_uuid)));
|
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(di->image_uuid), "imageUuid", SD_JSON_BUILD_UUID(di->image_uuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,10 +642,16 @@ static MountMapMode default_mount_map_mode(DirectoryOwnership ownership) {
|
|||||||
|
|
||||||
static JSON_DISPATCH_ENUM_DEFINE(dispatch_mount_directory_mode, MountMapMode, mount_map_mode_from_string);
|
static JSON_DISPATCH_ENUM_DEFINE(dispatch_mount_directory_mode, MountMapMode, mount_map_mode_from_string);
|
||||||
|
|
||||||
static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
static DirectoryOwnership validate_directory_fd(
|
||||||
|
int fd,
|
||||||
|
uid_t peer_uid,
|
||||||
|
uid_t *ret_current_owner_uid) {
|
||||||
|
|
||||||
int r, fl;
|
int r, fl;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
|
assert(uid_is_valid(peer_uid));
|
||||||
|
assert(ret_current_owner_uid);
|
||||||
|
|
||||||
/* Checks if the specified directory fd looks sane. Returns a DirectoryOwnership that categorizes the
|
/* Checks if the specified directory fd looks sane. Returns a DirectoryOwnership that categorizes the
|
||||||
* ownership situation in comparison to the peer's UID.
|
* ownership situation in comparison to the peer's UID.
|
||||||
@ -665,6 +676,7 @@ static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
|||||||
return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m");
|
return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m");
|
||||||
|
|
||||||
if (st.st_uid == 0) {
|
if (st.st_uid == 0) {
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
if (peer_uid == 0) {
|
if (peer_uid == 0) {
|
||||||
log_debug("Directory file descriptor points to root owned directory, who is also the peer.");
|
log_debug("Directory file descriptor points to root owned directory, who is also the peer.");
|
||||||
return DIRECTORY_IS_ROOT_PEER_OWNED;
|
return DIRECTORY_IS_ROOT_PEER_OWNED;
|
||||||
@ -674,6 +686,7 @@ static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
|||||||
}
|
}
|
||||||
if (st.st_uid == peer_uid) {
|
if (st.st_uid == peer_uid) {
|
||||||
log_debug("Directory file descriptor points to peer owned directory.");
|
log_debug("Directory file descriptor points to peer owned directory.");
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_PEER_OWNED;
|
return DIRECTORY_IS_PEER_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,12 +700,14 @@ static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
|||||||
/* Stop iteration if we find a directory up the tree that is neither owned by the user, nor is from the foreign UID range */
|
/* Stop iteration if we find a directory up the tree that is neither owned by the user, nor is from the foreign UID range */
|
||||||
if (!uid_is_foreign(st.st_uid) || !gid_is_foreign(st.st_gid)) {
|
if (!uid_is_foreign(st.st_uid) || !gid_is_foreign(st.st_gid)) {
|
||||||
log_debug("Directory file descriptor points to directory which itself or its parents is neither owned by foreign UID range nor by the user.");
|
log_debug("Directory file descriptor points to directory which itself or its parents is neither owned by foreign UID range nor by the user.");
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_OTHERWISE_OWNED;
|
return DIRECTORY_IS_OTHERWISE_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the peer is root, then it doesn't matter if we find a parent owned by root, let's shortcut things. */
|
/* If the peer is root, then it doesn't matter if we find a parent owned by root, let's shortcut things. */
|
||||||
if (peer_uid == 0) {
|
if (peer_uid == 0) {
|
||||||
log_debug("Directory file descriptor is owned by foreign UID range, and peer is root.");
|
log_debug("Directory file descriptor is owned by foreign UID range, and peer is root.");
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_FOREIGN_OWNED;
|
return DIRECTORY_IS_FOREIGN_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,11 +723,13 @@ static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
|||||||
/* Safety check to see if we hit the root dir */
|
/* Safety check to see if we hit the root dir */
|
||||||
if (stat_inode_same(&st, &new_st)) {
|
if (stat_inode_same(&st, &new_st)) {
|
||||||
log_debug("Directory file descriptor is owned by foreign UID range, but didn't find parent directory that is owned by peer among ancestors.");
|
log_debug("Directory file descriptor is owned by foreign UID range, but didn't find parent directory that is owned by peer among ancestors.");
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_OTHERWISE_OWNED;
|
return DIRECTORY_IS_OTHERWISE_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_st.st_uid == peer_uid) { /* Parent inode is owned by the peer. That's good! Everything's fine. */
|
if (new_st.st_uid == peer_uid) { /* Parent inode is owned by the peer. That's good! Everything's fine. */
|
||||||
log_debug("Directory file descriptor is owned by foreign UID range, and ancestor is owned by peer.");
|
log_debug("Directory file descriptor is owned by foreign UID range, and ancestor is owned by peer.");
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_FOREIGN_OWNED;
|
return DIRECTORY_IS_FOREIGN_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,6 +738,7 @@ static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_debug("Failed to find peer owned parent directory after %u levels, refusing.", n_level);
|
log_debug("Failed to find peer owned parent directory after %u levels, refusing.", n_level);
|
||||||
|
*ret_current_owner_uid = st.st_uid;
|
||||||
return DIRECTORY_IS_OTHERWISE_OWNED;
|
return DIRECTORY_IS_OTHERWISE_OWNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,7 +789,8 @@ static int vl_method_mount_directory(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to get client UID: %m");
|
return log_debug_errno(r, "Failed to get client UID: %m");
|
||||||
|
|
||||||
DirectoryOwnership owned_by = validate_directory_fd(directory_fd, peer_uid);
|
uid_t current_owner_uid;
|
||||||
|
DirectoryOwnership owned_by = validate_directory_fd(directory_fd, peer_uid, ¤t_owner_uid);
|
||||||
if (owned_by == -EREMOTEIO)
|
if (owned_by == -EREMOTEIO)
|
||||||
return sd_varlink_errorbo(link, "io.systemd.MountFileSystem.BadFileDescriptorFlags", SD_JSON_BUILD_PAIR_STRING("parameter", "directoryFileDescriptor"));
|
return sd_varlink_errorbo(link, "io.systemd.MountFileSystem.BadFileDescriptorFlags", SD_JSON_BUILD_PAIR_STRING("parameter", "directoryFileDescriptor"));
|
||||||
if (owned_by < 0)
|
if (owned_by < 0)
|
||||||
@ -836,10 +855,28 @@ static int vl_method_mount_directory(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
_cleanup_close_ int mount_fd = open_tree(directory_fd, "", OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH);
|
_cleanup_close_ int mount_fd = open_tree_try_drop_idmap(
|
||||||
|
directory_fd,
|
||||||
|
"",
|
||||||
|
OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH);
|
||||||
if (mount_fd < 0)
|
if (mount_fd < 0)
|
||||||
return log_debug_errno(errno, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path));
|
return log_debug_errno(errno, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path));
|
||||||
|
|
||||||
|
/* MOUNT_ATTR_IDMAP has possibly been cleared. Let's verify that the underlying data matches our expectations. */
|
||||||
|
struct stat unmapped_st;
|
||||||
|
if (fstat(mount_fd, &unmapped_st) < 0)
|
||||||
|
return log_debug_errno(errno, "Failed to stat unmapped inode: %m");
|
||||||
|
|
||||||
|
r = stat_verify_directory(&unmapped_st);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* For now, let's simply refuse things if dropping the idmapping changed anything. For now that
|
||||||
|
* should be good enough, because the primary usecase for this (homed) will mount the foreign UID
|
||||||
|
* range 1:1. */
|
||||||
|
if (unmapped_st.st_uid != current_owner_uid)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Owner UID of mount after clearing ID mapping not the same anymore, refusing.");
|
||||||
|
|
||||||
if (p.read_only > 0 && mount_setattr(
|
if (p.read_only > 0 && mount_setattr(
|
||||||
mount_fd, "", AT_EMPTY_PATH,
|
mount_fd, "", AT_EMPTY_PATH,
|
||||||
&(struct mount_attr) {
|
&(struct mount_attr) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
#include "hostname-setup.h"
|
||||||
#include "network-common.h"
|
#include "network-common.h"
|
||||||
#include "networkd-address.h"
|
#include "networkd-address.h"
|
||||||
#include "networkd-dhcp-server.h"
|
#include "networkd-dhcp-server.h"
|
||||||
@ -31,6 +32,30 @@
|
|||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
|
||||||
|
static int get_hostname_domain(char **ret) {
|
||||||
|
_cleanup_free_ char *hostname = NULL;
|
||||||
|
const char *domain;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
/* Get the full hostname (FQDN if available) */
|
||||||
|
r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &hostname);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Find the first dot to extract the domain part */
|
||||||
|
domain = strchr(hostname, '.');
|
||||||
|
if (!domain)
|
||||||
|
return -ENOENT; /* No domain part in hostname */
|
||||||
|
|
||||||
|
domain++; /* Skip the dot */
|
||||||
|
if (isempty(domain))
|
||||||
|
return -ENOENT; /* Empty domain after dot */
|
||||||
|
|
||||||
|
return strdup_to(ret, domain);
|
||||||
|
}
|
||||||
|
|
||||||
static bool link_dhcp4_server_enabled(Link *link) {
|
static bool link_dhcp4_server_enabled(Link *link) {
|
||||||
assert(link);
|
assert(link);
|
||||||
|
|
||||||
@ -678,6 +703,29 @@ static int dhcp4_server_configure(Link *link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link->network->dhcp_server_emit_domain) {
|
||||||
|
_cleanup_free_ char *buffer = NULL;
|
||||||
|
const char *domain = NULL;
|
||||||
|
|
||||||
|
if (link->network->dhcp_server_domain)
|
||||||
|
domain = link->network->dhcp_server_domain;
|
||||||
|
else {
|
||||||
|
r = get_hostname_domain(&buffer);
|
||||||
|
if (r < 0)
|
||||||
|
log_link_warning_errno(link, r, "Failed to determine domain name from host's hostname, will not send domain in DHCP leases: %m");
|
||||||
|
else {
|
||||||
|
domain = buffer;
|
||||||
|
log_link_debug(link, "Using autodetected domain name '%s' for DHCP server.", domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain) {
|
||||||
|
r = sd_dhcp_server_set_domain_name(link->dhcp_server, domain);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_error_errno(link, r, "Failed to set domain name for DHCP server: %m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) {
|
ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) {
|
||||||
r = sd_dhcp_server_add_option(link->dhcp_server, p);
|
r = sd_dhcp_server_add_option(link->dhcp_server, p);
|
||||||
if (r == -EEXIST)
|
if (r == -EEXIST)
|
||||||
|
|||||||
@ -381,6 +381,8 @@ DHCPServer.EmitRouter, config_parse_bool,
|
|||||||
DHCPServer.Router, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_router)
|
DHCPServer.Router, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_router)
|
||||||
DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
|
DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
|
||||||
DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
|
DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
|
||||||
|
DHCPServer.EmitDomain, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_domain)
|
||||||
|
DHCPServer.Domain, config_parse_dns_name, 0, offsetof(Network, dhcp_server_domain)
|
||||||
DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
|
DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
|
||||||
DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
|
DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
|
||||||
DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options)
|
DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options)
|
||||||
|
|||||||
@ -755,6 +755,7 @@ static Network *network_free(Network *network) {
|
|||||||
free(network->dhcp_server_boot_server_name);
|
free(network->dhcp_server_boot_server_name);
|
||||||
free(network->dhcp_server_boot_filename);
|
free(network->dhcp_server_boot_filename);
|
||||||
free(network->dhcp_server_timezone);
|
free(network->dhcp_server_timezone);
|
||||||
|
free(network->dhcp_server_domain);
|
||||||
free(network->dhcp_server_uplink_name);
|
free(network->dhcp_server_uplink_name);
|
||||||
for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
|
for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
|
||||||
free(network->dhcp_server_emit[t].addresses);
|
free(network->dhcp_server_emit[t].addresses);
|
||||||
|
|||||||
@ -220,6 +220,8 @@ typedef struct Network {
|
|||||||
struct in_addr dhcp_server_router;
|
struct in_addr dhcp_server_router;
|
||||||
bool dhcp_server_emit_timezone;
|
bool dhcp_server_emit_timezone;
|
||||||
char *dhcp_server_timezone;
|
char *dhcp_server_timezone;
|
||||||
|
bool dhcp_server_emit_domain;
|
||||||
|
char *dhcp_server_domain;
|
||||||
usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec;
|
usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec;
|
||||||
uint32_t dhcp_server_pool_offset;
|
uint32_t dhcp_server_pool_offset;
|
||||||
uint32_t dhcp_server_pool_size;
|
uint32_t dhcp_server_pool_size;
|
||||||
|
|||||||
@ -826,17 +826,16 @@ static int mount_bind(const char *dest, CustomMount *m, uid_t uid_shift, uid_t u
|
|||||||
* caller's userns *without* any mount idmapping in place. To get that uid, we clone the
|
* caller's userns *without* any mount idmapping in place. To get that uid, we clone the
|
||||||
* mount source tree and clear any existing idmapping and temporarily mount that tree over
|
* mount source tree and clear any existing idmapping and temporarily mount that tree over
|
||||||
* the mount source before we stat the mount source to figure out the source uid. */
|
* the mount source before we stat the mount source to figure out the source uid. */
|
||||||
_cleanup_close_ int fd_clone = open_tree_attr_with_fallback(
|
_cleanup_close_ int fd_clone =
|
||||||
|
idmapping == REMOUNT_IDMAPPING_NONE ?
|
||||||
|
RET_NERRNO(open_tree(
|
||||||
AT_FDCWD,
|
AT_FDCWD,
|
||||||
m->source,
|
m->source,
|
||||||
open_tree_flags,
|
open_tree_flags)) :
|
||||||
&(struct mount_attr) {
|
open_tree_try_drop_idmap(
|
||||||
.attr_clr = idmapping != REMOUNT_IDMAPPING_NONE ? MOUNT_ATTR_IDMAP : 0,
|
AT_FDCWD,
|
||||||
});
|
m->source,
|
||||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd_clone))
|
open_tree_flags);
|
||||||
/* We can only clear idmapped mounts with open_tree_attr(), but there might not be one in
|
|
||||||
* the first place, so we keep going if we get a not supported error. */
|
|
||||||
fd_clone = open_tree(AT_FDCWD, m->source, open_tree_flags);
|
|
||||||
if (fd_clone < 0)
|
if (fd_clone < 0)
|
||||||
return log_error_errno(errno, "Failed to clone %s: %m", m->source);
|
return log_error_errno(errno, "Failed to clone %s: %m", m->source);
|
||||||
|
|
||||||
|
|||||||
@ -3830,6 +3830,7 @@ static DissectImageFlags determine_dissect_image_flags(void) {
|
|||||||
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
|
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
|
||||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS) |
|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS) |
|
||||||
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
|
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
|
||||||
|
DISSECT_IMAGE_VERITY_SHARE |
|
||||||
(arg_console_mode == CONSOLE_INTERACTIVE && arg_ask_password ? DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH : 0) |
|
(arg_console_mode == CONSOLE_INTERACTIVE && arg_ask_password ? DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH : 0) |
|
||||||
((arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_FOREIGN) ? DISSECT_IMAGE_FOREIGN_UID :
|
((arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_FOREIGN) ? DISSECT_IMAGE_FOREIGN_UID :
|
||||||
(arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_AUTO) ? DISSECT_IMAGE_IDENTITY_UID : 0);
|
(arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_AUTO) ? DISSECT_IMAGE_IDENTITY_UID : 0);
|
||||||
|
|||||||
@ -4727,6 +4727,7 @@ int mountfsd_mount_image(
|
|||||||
SD_JSON_BUILD_PAIR("readOnly", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
|
SD_JSON_BUILD_PAIR("readOnly", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
|
||||||
SD_JSON_BUILD_PAIR("growFileSystems", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
|
SD_JSON_BUILD_PAIR("growFileSystems", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)),
|
SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)),
|
||||||
|
SD_JSON_BUILD_PAIR("veritySharing", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))),
|
||||||
SD_JSON_BUILD_PAIR("allowInteractiveAuthentication", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH))));
|
SD_JSON_BUILD_PAIR("allowInteractiveAuthentication", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH))));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -7,8 +7,24 @@
|
|||||||
#if HAVE_LIBARCHIVE
|
#if HAVE_LIBARCHIVE
|
||||||
static void *libarchive_dl = NULL;
|
static void *libarchive_dl = NULL;
|
||||||
|
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_filetype) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_free) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_free) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_gid) = NULL;
|
||||||
|
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_gid_is_set) = NULL;
|
||||||
|
#endif
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_hardlink) = NULL;
|
||||||
|
#if HAVE_LIBARCHIVE_HARDLINK_IS_SET
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_hardlink_is_set) = NULL;
|
||||||
|
#endif
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_mode) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_mtime) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_mtime_is_set) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_mtime_nsec) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_new) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_new) = NULL;
|
||||||
|
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_ctime) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_filetype) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_set_filetype) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_gid) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_set_gid) = NULL;
|
||||||
@ -17,10 +33,24 @@ DLSYM_PROTOTYPE(archive_entry_set_pathname) = NULL;
|
|||||||
DLSYM_PROTOTYPE(archive_entry_set_perm) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_set_perm) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_rdevmajor) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_set_rdevmajor) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_rdevminor) = NULL;
|
DLSYM_PROTOTYPE(archive_entry_set_rdevminor) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_symlink) = NULL;
|
|
||||||
DLSYM_PROTOTYPE(archive_entry_set_size) = 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_set_uid) = 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_next) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_entry_xattr_reset) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_error_string) = NULL;
|
DLSYM_PROTOTYPE(archive_error_string) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_data_into_fd) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_free) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_new) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_next_header) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_open_fd) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_support_format_cpio) = NULL;
|
||||||
|
DLSYM_PROTOTYPE(archive_read_support_format_tar) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_write_close) = NULL;
|
DLSYM_PROTOTYPE(archive_write_close) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_write_data) = NULL;
|
DLSYM_PROTOTYPE(archive_write_data) = NULL;
|
||||||
DLSYM_PROTOTYPE(archive_write_free) = NULL;
|
DLSYM_PROTOTYPE(archive_write_free) = NULL;
|
||||||
@ -41,8 +71,24 @@ int dlopen_libarchive(void) {
|
|||||||
&libarchive_dl,
|
&libarchive_dl,
|
||||||
"libarchive.so.13",
|
"libarchive.so.13",
|
||||||
LOG_DEBUG,
|
LOG_DEBUG,
|
||||||
|
DLSYM_ARG(archive_entry_filetype),
|
||||||
DLSYM_ARG(archive_entry_free),
|
DLSYM_ARG(archive_entry_free),
|
||||||
|
DLSYM_ARG(archive_entry_gid),
|
||||||
|
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||||
|
DLSYM_ARG(archive_entry_gid_is_set),
|
||||||
|
#endif
|
||||||
|
DLSYM_ARG(archive_entry_hardlink),
|
||||||
|
#if HAVE_LIBARCHIVE_HARDLINK_IS_SET
|
||||||
|
DLSYM_ARG(archive_entry_hardlink_is_set),
|
||||||
|
#endif
|
||||||
|
DLSYM_ARG(archive_entry_mode),
|
||||||
|
DLSYM_ARG(archive_entry_mtime),
|
||||||
|
DLSYM_ARG(archive_entry_mtime_is_set),
|
||||||
|
DLSYM_ARG(archive_entry_mtime_nsec),
|
||||||
DLSYM_ARG(archive_entry_new),
|
DLSYM_ARG(archive_entry_new),
|
||||||
|
DLSYM_ARG(archive_entry_pathname),
|
||||||
|
DLSYM_ARG(archive_entry_rdevmajor),
|
||||||
|
DLSYM_ARG(archive_entry_rdevminor),
|
||||||
DLSYM_ARG(archive_entry_set_ctime),
|
DLSYM_ARG(archive_entry_set_ctime),
|
||||||
DLSYM_ARG(archive_entry_set_filetype),
|
DLSYM_ARG(archive_entry_set_filetype),
|
||||||
DLSYM_ARG(archive_entry_set_gid),
|
DLSYM_ARG(archive_entry_set_gid),
|
||||||
@ -54,7 +100,21 @@ int dlopen_libarchive(void) {
|
|||||||
DLSYM_ARG(archive_entry_set_size),
|
DLSYM_ARG(archive_entry_set_size),
|
||||||
DLSYM_ARG(archive_entry_set_symlink),
|
DLSYM_ARG(archive_entry_set_symlink),
|
||||||
DLSYM_ARG(archive_entry_set_uid),
|
DLSYM_ARG(archive_entry_set_uid),
|
||||||
|
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_next),
|
||||||
|
DLSYM_ARG(archive_entry_xattr_reset),
|
||||||
DLSYM_ARG(archive_error_string),
|
DLSYM_ARG(archive_error_string),
|
||||||
|
DLSYM_ARG(archive_read_data_into_fd),
|
||||||
|
DLSYM_ARG(archive_read_free),
|
||||||
|
DLSYM_ARG(archive_read_new),
|
||||||
|
DLSYM_ARG(archive_read_next_header),
|
||||||
|
DLSYM_ARG(archive_read_open_fd),
|
||||||
|
DLSYM_ARG(archive_read_support_format_cpio),
|
||||||
|
DLSYM_ARG(archive_read_support_format_tar),
|
||||||
DLSYM_ARG(archive_write_close),
|
DLSYM_ARG(archive_write_close),
|
||||||
DLSYM_ARG(archive_write_data),
|
DLSYM_ARG(archive_write_data),
|
||||||
DLSYM_ARG(archive_write_free),
|
DLSYM_ARG(archive_write_free),
|
||||||
@ -63,7 +123,18 @@ int dlopen_libarchive(void) {
|
|||||||
DLSYM_ARG(archive_write_open_FILE),
|
DLSYM_ARG(archive_write_open_FILE),
|
||||||
DLSYM_ARG(archive_write_open_fd),
|
DLSYM_ARG(archive_write_open_fd),
|
||||||
DLSYM_ARG(archive_write_set_format_filter_by_ext),
|
DLSYM_ARG(archive_write_set_format_filter_by_ext),
|
||||||
DLSYM_ARG(archive_write_set_format_gnutar));
|
DLSYM_ARG(archive_write_set_format_gnutar)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* libarchive uses its own file type macros. They happen to be defined the same way as the Linux ones, and
|
||||||
|
* we'd like to rely on it. Let's verify this first though. */
|
||||||
|
assert_cc(S_IFDIR == AE_IFDIR);
|
||||||
|
assert_cc(S_IFREG == AE_IFREG);
|
||||||
|
assert_cc(S_IFLNK == AE_IFLNK);
|
||||||
|
assert_cc(S_IFBLK == AE_IFBLK);
|
||||||
|
assert_cc(S_IFCHR == AE_IFCHR);
|
||||||
|
assert_cc(S_IFIFO == AE_IFIFO);
|
||||||
|
assert_cc(S_IFSOCK == AE_IFSOCK);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -9,8 +9,18 @@
|
|||||||
|
|
||||||
#include "dlfcn-util.h"
|
#include "dlfcn-util.h"
|
||||||
|
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_filetype);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_free);
|
extern DLSYM_PROTOTYPE(archive_entry_free);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_gid);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_hardlink);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_mode);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_mtime);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_mtime_is_set);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_mtime_nsec);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_new);
|
extern DLSYM_PROTOTYPE(archive_entry_new);
|
||||||
|
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_ctime);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_filetype);
|
extern DLSYM_PROTOTYPE(archive_entry_set_filetype);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_gid);
|
extern DLSYM_PROTOTYPE(archive_entry_set_gid);
|
||||||
@ -19,10 +29,21 @@ extern DLSYM_PROTOTYPE(archive_entry_set_pathname);
|
|||||||
extern DLSYM_PROTOTYPE(archive_entry_set_perm);
|
extern DLSYM_PROTOTYPE(archive_entry_set_perm);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_rdevmajor);
|
extern DLSYM_PROTOTYPE(archive_entry_set_rdevmajor);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_rdevminor);
|
extern DLSYM_PROTOTYPE(archive_entry_set_rdevminor);
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_symlink);
|
|
||||||
extern DLSYM_PROTOTYPE(archive_entry_set_size);
|
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_set_uid);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_symlink);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_uid);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_xattr_next);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_xattr_reset);
|
||||||
extern DLSYM_PROTOTYPE(archive_error_string);
|
extern DLSYM_PROTOTYPE(archive_error_string);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_data_into_fd);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_free);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_new);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_next_header);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_open_fd);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_support_format_cpio);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_read_support_format_tar);
|
||||||
extern DLSYM_PROTOTYPE(archive_write_close);
|
extern DLSYM_PROTOTYPE(archive_write_close);
|
||||||
extern DLSYM_PROTOTYPE(archive_write_data);
|
extern DLSYM_PROTOTYPE(archive_write_data);
|
||||||
extern DLSYM_PROTOTYPE(archive_write_free);
|
extern DLSYM_PROTOTYPE(archive_write_free);
|
||||||
@ -33,10 +54,32 @@ extern DLSYM_PROTOTYPE(archive_write_open_fd);
|
|||||||
extern DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext);
|
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_gnutar);
|
||||||
|
|
||||||
|
#if HAVE_LIBARCHIVE_UID_IS_SET
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_gid_is_set);
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_uid_is_set);
|
||||||
|
#else
|
||||||
|
#include "user-util.h"
|
||||||
|
static inline int sym_archive_entry_gid_is_set(struct archive_entry *e) {
|
||||||
|
return gid_is_valid(sym_archive_entry_gid(e));
|
||||||
|
}
|
||||||
|
static inline int sym_archive_entry_uid_is_set(struct archive_entry *e) {
|
||||||
|
return uid_is_valid(sym_archive_entry_uid(e));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_LIBARCHIVE_HARDLINK_IS_SET
|
||||||
|
extern DLSYM_PROTOTYPE(archive_entry_hardlink_is_set);
|
||||||
|
#else
|
||||||
|
static inline int sym_archive_entry_hardlink_is_set(struct archive_entry *e) {
|
||||||
|
return !!sym_archive_entry_hardlink(e);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int dlopen_libarchive(void);
|
int dlopen_libarchive(void);
|
||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive_entry*, sym_archive_entry_free, NULL);
|
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive_entry*, sym_archive_entry_free, NULL);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive*, sym_archive_write_free, NULL);
|
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive*, sym_archive_write_free, NULL);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive*, sym_archive_read_free, NULL);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|||||||
@ -177,6 +177,7 @@ shared_sources = files(
|
|||||||
'socket-netlink.c',
|
'socket-netlink.c',
|
||||||
'specifier.c',
|
'specifier.c',
|
||||||
'switch-root.c',
|
'switch-root.c',
|
||||||
|
'tar-util.c',
|
||||||
'tmpfile-util-label.c',
|
'tmpfile-util-label.c',
|
||||||
'tomoyo-util.c',
|
'tomoyo-util.c',
|
||||||
'tpm2-util.c',
|
'tpm2-util.c',
|
||||||
|
|||||||
@ -1477,7 +1477,7 @@ int make_userns(uid_t uid_shift,
|
|||||||
return TAKE_FD(userns_fd);
|
return TAKE_FD(userns_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int open_tree_attr_with_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr) {
|
int open_tree_attr_with_fallback(int dir_fd, const char *path, unsigned flags, struct mount_attr *attr) {
|
||||||
_cleanup_close_ int fd = -EBADF;
|
_cleanup_close_ int fd = -EBADF;
|
||||||
|
|
||||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||||
@ -1507,6 +1507,42 @@ int open_tree_attr_with_fallback(int dir_fd, const char *path, unsigned int flag
|
|||||||
return TAKE_FD(fd);
|
return TAKE_FD(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int open_tree_try_drop_idmap(int dir_fd, const char *path, unsigned flags) {
|
||||||
|
/* Tries to drop MOUNT_ATTR_IDMAP while calling open_tree_attr(), but if that doesn't work just uses
|
||||||
|
* a regular open_tree() */
|
||||||
|
|
||||||
|
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||||
|
|
||||||
|
if (isempty(path)) {
|
||||||
|
path = "";
|
||||||
|
flags |= AT_EMPTY_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = open_tree_attr_with_fallback(
|
||||||
|
dir_fd,
|
||||||
|
path,
|
||||||
|
flags,
|
||||||
|
&(struct mount_attr) {
|
||||||
|
.attr_clr = MOUNT_ATTR_IDMAP,
|
||||||
|
});
|
||||||
|
if (fd < 0) {
|
||||||
|
if (!ERRNO_IS_NEG_NOT_SUPPORTED(fd))
|
||||||
|
return log_debug_errno(fd, "Failed to clear idmap of directory with open_tree_attr(): %m");
|
||||||
|
|
||||||
|
log_debug_errno(fd, "Failed to clear idmap with open_tree_attr(), retrying open_tree() without clearing idmap: %m");
|
||||||
|
|
||||||
|
fd = RET_NERRNO(open_tree(dir_fd, path, flags));
|
||||||
|
if (fd < 0)
|
||||||
|
return log_debug_errno(fd, "Both open_tree() and open_tree_attr() failed, giving up: %m");
|
||||||
|
|
||||||
|
log_debug("open_tree() without clearing idmap worked.");
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("Successfully acquired mount fd with cleared idmap.");
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
int remount_idmap_fd(
|
int remount_idmap_fd(
|
||||||
char **paths,
|
char **paths,
|
||||||
int userns_fd,
|
int userns_fd,
|
||||||
|
|||||||
@ -145,7 +145,8 @@ typedef enum RemountIdmapping {
|
|||||||
_REMOUNT_IDMAPPING_INVALID = -EINVAL,
|
_REMOUNT_IDMAPPING_INVALID = -EINVAL,
|
||||||
} RemountIdmapping;
|
} RemountIdmapping;
|
||||||
|
|
||||||
int open_tree_attr_with_fallback(int dir_fd, const char *path, unsigned int flags, struct mount_attr *attr);
|
int open_tree_attr_with_fallback(int dir_fd, const char *path, unsigned flags, struct mount_attr *attr);
|
||||||
|
int open_tree_try_drop_idmap(int dir_fd, const char *path, unsigned flags);
|
||||||
|
|
||||||
int make_userns(uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping);
|
int make_userns(uid_t uid_shift, uid_t uid_range, uid_t host_owner, uid_t dest_owner, RemountIdmapping idmapping);
|
||||||
int remount_idmap_fd(char **p, int userns_fd, uint64_t extra_mount_attr_set);
|
int remount_idmap_fd(char **p, int userns_fd, uint64_t extra_mount_attr_set);
|
||||||
|
|||||||
@ -400,8 +400,8 @@ int nsresource_add_netif_tap(
|
|||||||
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
|
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
|
||||||
|
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, host_interface_name), SD_JSON_MANDATORY },
|
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, host_interface_name), SD_JSON_MANDATORY },
|
||||||
{ "interfaceFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(InterfaceParams, namespace_interface_name), SD_JSON_MANDATORY },
|
{ "interfaceFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(InterfaceParams, interface_fd_index), SD_JSON_MANDATORY },
|
||||||
};
|
};
|
||||||
|
|
||||||
_cleanup_(interface_params_done) InterfaceParams p = {};
|
_cleanup_(interface_params_done) InterfaceParams p = {};
|
||||||
|
|||||||
686
src/shared/tar-util.c
Normal file
686
src/shared/tar-util.c
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "errno-util.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "tar-util.h"
|
||||||
|
|
||||||
|
#if HAVE_LIBARCHIVE
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "chase.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fs-util.h"
|
||||||
|
#include "iovec-util.h"
|
||||||
|
#include "libarchive-util.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "stat-util.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "tmpfile-util.h"
|
||||||
|
#include "user-util.h"
|
||||||
|
#include "xattr-util.h"
|
||||||
|
|
||||||
|
#define DEPTH_MAX 128U
|
||||||
|
|
||||||
|
typedef struct XAttr {
|
||||||
|
char *name;
|
||||||
|
struct iovec data;
|
||||||
|
} XAttr;
|
||||||
|
|
||||||
|
typedef struct OpenInode {
|
||||||
|
int fd;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
/* File properties to apply when we are done with the inode, i.e. right before closing it */
|
||||||
|
mode_t filetype;
|
||||||
|
mode_t mode;
|
||||||
|
struct timespec mtime;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
XAttr *xattr;
|
||||||
|
size_t n_xattr;
|
||||||
|
} OpenInode;
|
||||||
|
|
||||||
|
static void xattr_done(XAttr *xa) {
|
||||||
|
assert(xa);
|
||||||
|
|
||||||
|
free(xa->name);
|
||||||
|
iovec_done(&xa->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xattr_done_many(XAttr *xa, size_t n) {
|
||||||
|
assert(xa || n == 0);
|
||||||
|
|
||||||
|
FOREACH_ARRAY(i, xa, n)
|
||||||
|
xattr_done(i);
|
||||||
|
|
||||||
|
free(xa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_inode_done(OpenInode *of) {
|
||||||
|
assert(of);
|
||||||
|
|
||||||
|
if (of->path) {
|
||||||
|
/* Only close the stored fd if the path field is set. We'll set the path to NULL for the root
|
||||||
|
* inode, and we don't want the fd for that closed, as it's owned by the caller. */
|
||||||
|
of->fd = safe_close(of->fd);
|
||||||
|
of->path = mfree(of->path);
|
||||||
|
}
|
||||||
|
xattr_done_many(of->xattr, of->n_xattr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_inode_done_many(OpenInode *array, size_t n) {
|
||||||
|
assert(array || n == 0);
|
||||||
|
|
||||||
|
FOREACH_ARRAY(i, array, n)
|
||||||
|
open_inode_done(i);
|
||||||
|
|
||||||
|
free(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_inode_finalize(OpenInode *of) {
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
assert(of);
|
||||||
|
|
||||||
|
if (of->fd >= 0) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
/* We adjust the UID/GID right before the mode, since doing this might affect the mode (drops
|
||||||
|
* suid/sgid bits).
|
||||||
|
*
|
||||||
|
* We adjust the mode only when leaving a dir, because if we are unpriv we might lose the
|
||||||
|
* ability to enter it once we do this. */
|
||||||
|
|
||||||
|
if (uid_is_valid(of->uid) || gid_is_valid(of->gid) || of->mode != MODE_INVALID) {
|
||||||
|
k = fchmod_and_chown_with_fallback(of->fd, /* path= */ NULL, of->mode, of->uid, of->gid);
|
||||||
|
if (k < 0)
|
||||||
|
RET_GATHER(r, log_error_errno(k, "Failed to adjust ownership/mode 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) {
|
||||||
|
k = futimens_opath(of->fd, (const struct timespec[2]) {
|
||||||
|
{ .tv_nsec = UTIME_OMIT },
|
||||||
|
of->mtime,
|
||||||
|
});
|
||||||
|
if (k < 0)
|
||||||
|
RET_GATHER(r, log_error_errno(k, "Failed to adjust mtime of '%s': %m", of->path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setting certain xattrs might cause us to lose access to the inode, hence set this last */
|
||||||
|
FOREACH_ARRAY(i, of->xattr, of->n_xattr) {
|
||||||
|
k = xsetxattr_full(
|
||||||
|
of->fd,
|
||||||
|
/* path= */ NULL,
|
||||||
|
AT_EMPTY_PATH,
|
||||||
|
i->name,
|
||||||
|
i->data.iov_base,
|
||||||
|
i->data.iov_len,
|
||||||
|
/* xattr_flags= */ 0);
|
||||||
|
if (k < 0)
|
||||||
|
RET_GATHER(r, log_error_errno(k, "Failed to set xattr '%s' of '%s': %m", i->name, of->path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open_inode_done(of); /* free this item even on failure */
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_inode_finalize_many(OpenInode **array, size_t *n) {
|
||||||
|
int r = 0;
|
||||||
|
assert(array);
|
||||||
|
assert(n);
|
||||||
|
assert(*array || *n == 0);
|
||||||
|
|
||||||
|
/* Go backwards, so that we adjust innermost first */
|
||||||
|
for (size_t i = *n; i > 0; i--)
|
||||||
|
RET_GATHER(r, open_inode_finalize(*array + i - 1));
|
||||||
|
|
||||||
|
*array = mfree(*array);
|
||||||
|
*n = 0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_unpack_regular(
|
||||||
|
struct archive *a,
|
||||||
|
struct archive_entry *entry,
|
||||||
|
int parent_fd,
|
||||||
|
const char *filename,
|
||||||
|
const char *path) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(entry);
|
||||||
|
assert(parent_fd >= 0);
|
||||||
|
assert(filename);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
_cleanup_free_ char *tmp = NULL;
|
||||||
|
_cleanup_close_ int fd = open_tmpfile_linkable_at(parent_fd, filename, O_CLOEXEC|O_WRONLY, &tmp);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(fd, "Failed to create regular file '%s': %m", path);
|
||||||
|
|
||||||
|
r = sym_archive_read_data_into_fd(a, fd);
|
||||||
|
if (r != ARCHIVE_OK) {
|
||||||
|
r = log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||||
|
"Failed to unpack regular file '%s': %s", path, sym_archive_error_string(a));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this is a sparse file, then libarchive's archive_read_data_into_fd() won't insert the final
|
||||||
|
* hole. We need to manually truncate. */
|
||||||
|
off_t l = lseek(fd, 0, SEEK_CUR);
|
||||||
|
if (l < 0) {
|
||||||
|
r = log_error_errno(errno, "Failed to determine current file position in '%s': %m", path);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (ftruncate(fd, l) < 0) {
|
||||||
|
r = log_error_errno(errno, "Failed to truncate regular file '%s' to %" PRIu64 ": %m", path, (uint64_t) l);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = link_tmpfile_at(fd, parent_fd, tmp, filename, LINK_TMPFILE_REPLACE);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to install regular file '%s': %m", path);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (tmp)
|
||||||
|
(void) unlinkat(parent_fd, tmp, /* flags= */ 0);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_unpack_directory(
|
||||||
|
struct archive *a,
|
||||||
|
struct archive_entry *entry,
|
||||||
|
int parent_fd,
|
||||||
|
const char *filename,
|
||||||
|
const char *path) {
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(entry);
|
||||||
|
assert(parent_fd >= 0);
|
||||||
|
assert(filename);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
/* For the other inode types we operate in an atomic replace fashion, but not for the directories,
|
||||||
|
* they are more of a "shared" concept, and we try to reuse existing inodes. Note that we create the
|
||||||
|
* dir inode in mode 0700, so that we can fully access it (but others cannot). We'll adjust the modes
|
||||||
|
* right before closing the inode. */
|
||||||
|
_cleanup_close_ int fd = open_mkdir_at(parent_fd, filename, O_CLOEXEC, 0700);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(fd, "Failed to create directory '%s': %m", path);
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_unpack_symlink(
|
||||||
|
struct archive *a,
|
||||||
|
struct archive_entry *entry,
|
||||||
|
int parent_fd,
|
||||||
|
const char *filename,
|
||||||
|
const char *path) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(entry);
|
||||||
|
assert(parent_fd >= 0);
|
||||||
|
assert(filename);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
const char *target = sym_archive_entry_symlink(entry);
|
||||||
|
if (!target)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get symlink target for '%s': %m", path);
|
||||||
|
|
||||||
|
r = symlinkat_atomic_full(target, parent_fd, filename, /* flags= */ 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to create symlink '%s' → '%s': %m", path, target);
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = openat(parent_fd, filename, O_CLOEXEC|O_PATH|O_NOFOLLOW);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open symlink '%s' we just created: %m", path);
|
||||||
|
|
||||||
|
r = fd_verify_symlink(fd);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Symlink '%s' we just created is not a symlink: %m", path);
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_unpack_special_inode(
|
||||||
|
struct archive *a,
|
||||||
|
struct archive_entry *entry,
|
||||||
|
int parent_fd,
|
||||||
|
const char *filename,
|
||||||
|
const char *path,
|
||||||
|
mode_t filetype) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(entry);
|
||||||
|
assert(parent_fd >= 0);
|
||||||
|
assert(filename);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
dev_t major = 0, minor = 0;
|
||||||
|
if (IN_SET(filetype, S_IFCHR, S_IFBLK)) {
|
||||||
|
major = sym_archive_entry_rdevmajor(entry);
|
||||||
|
minor = sym_archive_entry_rdevminor(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mknodat_atomic(parent_fd, filename, filetype | 0000, makedev(major, minor));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to create special node '%s': %m", path);
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = openat(parent_fd, filename, O_CLOEXEC|O_PATH|O_NOFOLLOW);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open special node '%s' we just created: %m", path);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to fstat() '%s': %m", path);
|
||||||
|
|
||||||
|
if (((st.st_mode ^ filetype) & S_IFMT) != 0)
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"Special node '%s' we just created is of a wrong type: %m", path);
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_entry_pathname_safe(struct archive_entry *entry, const char **ret) {
|
||||||
|
/* libarchive prefixes all paths with "./", let's chop that off. Note that we'll return a path of
|
||||||
|
* NULL for the root inode here! */
|
||||||
|
|
||||||
|
assert(entry);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
const char *p = sym_archive_entry_pathname(entry);
|
||||||
|
if (!p)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
const char *e = startswith(p, "./") ?: p;
|
||||||
|
if (isempty(e))
|
||||||
|
*ret = NULL;
|
||||||
|
else if (path_is_safe(e))
|
||||||
|
*ret = e;
|
||||||
|
else
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archive_entry_read_stat(
|
||||||
|
struct archive_entry *entry,
|
||||||
|
mode_t *filetype,
|
||||||
|
mode_t *mode,
|
||||||
|
struct timespec *mtime,
|
||||||
|
uid_t *uid,
|
||||||
|
gid_t *gid,
|
||||||
|
XAttr **xa,
|
||||||
|
size_t *n_xa,
|
||||||
|
TarFlags flags) {
|
||||||
|
|
||||||
|
assert(entry);
|
||||||
|
|
||||||
|
/* Fills in all fields that are present in the archive entry. Doesn't change the fields if the entry
|
||||||
|
* doesn't contain the relevant data */
|
||||||
|
|
||||||
|
if (filetype)
|
||||||
|
*filetype = sym_archive_entry_filetype(entry);
|
||||||
|
|
||||||
|
if (mode)
|
||||||
|
*mode = sym_archive_entry_mode(entry);
|
||||||
|
|
||||||
|
if (mtime && sym_archive_entry_mtime_is_set(entry))
|
||||||
|
*mtime = (struct timespec) {
|
||||||
|
sym_archive_entry_mtime(entry),
|
||||||
|
sym_archive_entry_mtime_nsec(entry),
|
||||||
|
};
|
||||||
|
if (uid && sym_archive_entry_uid_is_set(entry))
|
||||||
|
*uid = sym_archive_entry_uid(entry);
|
||||||
|
if (gid && sym_archive_entry_gid_is_set(entry))
|
||||||
|
*gid = sym_archive_entry_gid(entry);
|
||||||
|
|
||||||
|
(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)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (xattr_is_acl(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(flags, TAR_SELINUX) && xattr_is_selinux(name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_cleanup_free_ char *n = strdup(name);
|
||||||
|
if (!n)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_(iovec_done) struct iovec iovec_copy = {};
|
||||||
|
if (!iovec_memdup(&data, &iovec_copy))
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(*xa, *n_xa+1))
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
(*xa)[(*n_xa)++] = (XAttr) {
|
||||||
|
.name = TAKE_PTR(n),
|
||||||
|
.data = TAKE_STRUCT(iovec_copy),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||||
|
int ar, r;
|
||||||
|
|
||||||
|
assert(input_fd >= 0);
|
||||||
|
assert(tree_fd >= 0);
|
||||||
|
|
||||||
|
_cleanup_(sym_archive_read_freep) struct archive *a = NULL;
|
||||||
|
a = sym_archive_read_new();
|
||||||
|
if (!a)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
ar = sym_archive_read_support_format_tar(a);
|
||||||
|
if (ar != ARCHIVE_OK)
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||||
|
"Failed to enable tar unpacking: %s", sym_archive_error_string(a));
|
||||||
|
|
||||||
|
ar = sym_archive_read_support_format_cpio(a);
|
||||||
|
if (ar != ARCHIVE_OK)
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||||
|
"Failed to enable cpio unpacking: %s", sym_archive_error_string(a));
|
||||||
|
|
||||||
|
ar = sym_archive_read_open_fd(a, input_fd, 64 * 1024);
|
||||||
|
if (ar != ARCHIVE_OK)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to initialize archive context: %s", sym_archive_error_string(a));
|
||||||
|
|
||||||
|
|
||||||
|
OpenInode *open_inodes = NULL;
|
||||||
|
if (!GREEDY_REALLOC(open_inodes, 2)) /* the minimal case is a single file in an archive, which would
|
||||||
|
* mean two inodes, the root dir inode, and he regular file
|
||||||
|
* inode, hence start with 2 here */
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
size_t n_open_inodes = 0;
|
||||||
|
CLEANUP_ARRAY(open_inodes, n_open_inodes, open_inode_done_many);
|
||||||
|
|
||||||
|
/* Fill in the root inode. (Note: we leave the .path field as NULL to mark it as root inode.) */
|
||||||
|
open_inodes[0] = (OpenInode) {
|
||||||
|
.fd = tree_fd,
|
||||||
|
.filetype = S_IFDIR,
|
||||||
|
.mode = MODE_INVALID,
|
||||||
|
.mtime = { .tv_nsec = UTIME_OMIT },
|
||||||
|
.uid = UID_INVALID,
|
||||||
|
.gid = GID_INVALID,
|
||||||
|
};
|
||||||
|
n_open_inodes = 1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct archive_entry *entry = NULL;
|
||||||
|
|
||||||
|
ar = sym_archive_read_next_header(a, &entry);
|
||||||
|
if (ar == ARCHIVE_EOF)
|
||||||
|
break;
|
||||||
|
if (ar != ARCHIVE_OK)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse archive: %s", sym_archive_error_string(a));
|
||||||
|
|
||||||
|
const char *p = NULL;
|
||||||
|
r = archive_entry_pathname_safe(entry, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Invalid path name in entry, refusing.");
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
/* This is the root inode */
|
||||||
|
r = archive_entry_read_stat(
|
||||||
|
entry,
|
||||||
|
&open_inodes[0].filetype,
|
||||||
|
&open_inodes[0].mode,
|
||||||
|
&open_inodes[0].mtime,
|
||||||
|
&open_inodes[0].uid,
|
||||||
|
&open_inodes[0].gid,
|
||||||
|
&open_inodes[0].xattr,
|
||||||
|
&open_inodes[0].n_xattr,
|
||||||
|
flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (open_inodes[0].filetype != S_IFDIR)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Archives root inode is not a directory, refusing.");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find common prefix with path elements we were looking at so far. */
|
||||||
|
const char *rest = p;
|
||||||
|
size_t i;
|
||||||
|
for (i = 1; i < n_open_inodes; i++) {
|
||||||
|
const char *e = path_startswith(p, open_inodes[i].path);
|
||||||
|
if (isempty(e))
|
||||||
|
break;
|
||||||
|
|
||||||
|
rest = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize all inodes we won't need anymore now (go backwards, i.e. close inner fds first) */
|
||||||
|
while (n_open_inodes > i) {
|
||||||
|
r = open_inode_finalize(open_inodes + n_open_inodes - 1);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
n_open_inodes--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And now create all remaining components */
|
||||||
|
for (;;) {
|
||||||
|
const char *element;
|
||||||
|
|
||||||
|
r = path_find_first_component(&rest, /* accept_dot_dot= */ false, &element);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to extract next element from path: %m");
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Safety check, before we add another level to our stack */
|
||||||
|
if (n_open_inodes >= DEPTH_MAX)
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(E2BIG),
|
||||||
|
"Archive's directory tree nested too deeply, refusing to descend more than %u levels.", DEPTH_MAX);
|
||||||
|
|
||||||
|
_cleanup_free_ char *e = strndup(element, r);
|
||||||
|
if (!e)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
const char *parent_path = NULL;
|
||||||
|
int parent_fd = -EBADF;
|
||||||
|
assert(n_open_inodes > 0);
|
||||||
|
parent_fd = open_inodes[n_open_inodes-1].fd;
|
||||||
|
parent_path = open_inodes[n_open_inodes-1].path;
|
||||||
|
|
||||||
|
_cleanup_free_ char *j = parent_path ? path_join(parent_path, e) : strdup(e);
|
||||||
|
if (!j)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(open_inodes, n_open_inodes+1))
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = -EBADF;
|
||||||
|
mode_t filetype = MODE_INVALID;
|
||||||
|
mode_t mode = MODE_INVALID;
|
||||||
|
uid_t uid = UID_INVALID;
|
||||||
|
gid_t gid = GID_INVALID;
|
||||||
|
struct timespec mtime = { .tv_nsec = UTIME_OMIT };
|
||||||
|
XAttr *xa = NULL;
|
||||||
|
size_t n_xa = 0;
|
||||||
|
CLEANUP_ARRAY(xa, n_xa, xattr_done_many);
|
||||||
|
|
||||||
|
if (isempty(rest)) {
|
||||||
|
/* This is the final node in the path, create it */
|
||||||
|
|
||||||
|
if (sym_archive_entry_hardlink_is_set(entry)) {
|
||||||
|
/* If this is a hardlink, act on it */
|
||||||
|
const char *h = sym_archive_entry_hardlink(entry);
|
||||||
|
if (!h)
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(EBADMSG),
|
||||||
|
"No hardlink target in hardlink entry, refusing.");
|
||||||
|
|
||||||
|
/* libarchive prefixes all paths with "./", let's chop that off */
|
||||||
|
const char *target = startswith(h, "./") ?: h;
|
||||||
|
if (!path_is_safe(target))
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(EBADMSG),
|
||||||
|
"Invalid hardlink path name '%s' in entry, refusing.", target);
|
||||||
|
|
||||||
|
_cleanup_close_ int target_fd = -EBADF;
|
||||||
|
r = chaseat(tree_fd, target, CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT, /* ret_path= */ NULL, &target_fd);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(
|
||||||
|
r,
|
||||||
|
"Failed to find inode '%s' which shall be hardlinked as '%s': %m", target, j);
|
||||||
|
|
||||||
|
struct stat verify_st;
|
||||||
|
if (fstat(target_fd, &verify_st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat inode '%s': %m", target);
|
||||||
|
|
||||||
|
/* Refuse hardlinking directories early. */
|
||||||
|
if (!inode_type_can_hardlink(verify_st.st_mode))
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(EBADF),
|
||||||
|
"Refusing to hardlink inode '%s' of type '%s': %m", target, inode_type_to_string(verify_st.st_mode));
|
||||||
|
|
||||||
|
if (linkat(target_fd, "", parent_fd, e, AT_EMPTY_PATH) < 0) {
|
||||||
|
if (errno != ENOENT)
|
||||||
|
return log_error_errno(
|
||||||
|
errno,
|
||||||
|
"Failed to hardlink inode '%s' as '%s': %m", target, j);
|
||||||
|
|
||||||
|
/* To be able to link by inode fd we might have needed
|
||||||
|
* CAP_DAC_READ_SEARCH which we lacked. Let's retry with the
|
||||||
|
* parent. Yes, glibc/kernel report this as ENOENT. Kinda
|
||||||
|
* annoying. */
|
||||||
|
|
||||||
|
_cleanup_close_ int target_parent_fd = -EBADF;
|
||||||
|
_cleanup_free_ char *target_filename = NULL;
|
||||||
|
r = chaseat(tree_fd, target, CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_EXTRACT_FILENAME, &target_filename, &target_parent_fd);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(
|
||||||
|
r,
|
||||||
|
"Failed to find inode '%s' which shall be hardlinked as '%s': %m", target, j);
|
||||||
|
|
||||||
|
if (linkat(target_parent_fd, target_filename, parent_fd, e, /* flags= */ 0) < 0)
|
||||||
|
return log_error_errno(
|
||||||
|
errno,
|
||||||
|
"Failed to hardlink inode '%s' as '%s': %m", target, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = archive_entry_read_stat(
|
||||||
|
entry,
|
||||||
|
&filetype,
|
||||||
|
&mode,
|
||||||
|
&mtime,
|
||||||
|
&uid,
|
||||||
|
&gid,
|
||||||
|
&xa,
|
||||||
|
&n_xa,
|
||||||
|
flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
switch (filetype) {
|
||||||
|
|
||||||
|
case S_IFREG:
|
||||||
|
fd = archive_unpack_regular(a, entry, parent_fd, e, j);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_IFDIR:
|
||||||
|
fd = archive_unpack_directory(a, entry, parent_fd, e, j);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_IFLNK:
|
||||||
|
fd = archive_unpack_symlink(a, entry, parent_fd, e, j);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_IFCHR:
|
||||||
|
case S_IFBLK:
|
||||||
|
case S_IFIFO:
|
||||||
|
case S_IFSOCK:
|
||||||
|
fd = archive_unpack_special_inode(a, entry, parent_fd, e, j, filetype);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return log_error_errno(
|
||||||
|
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||||
|
"Unexpected file type %i of '%s', refusing.", (int) filetype, j);
|
||||||
|
}
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* This is some intermediary node in the path that we haven't opened yet. Create it with default attributes */
|
||||||
|
fd = open_mkdir_at(parent_fd, e, O_CLOEXEC, 0700);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(fd, "Failed to create directory '%s': %m", j);
|
||||||
|
|
||||||
|
filetype = S_IFDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now store a reference to the inode we just created in our stack array. Note that
|
||||||
|
* we have not applied file ownership, access mode, mtime here, we'll do that only
|
||||||
|
* when we are finished with the inode, since we have to apply them *after* we are
|
||||||
|
* fully done with the inode (i.e. after creating further inodes inside of dir inodes
|
||||||
|
* for example), due to permission problems this might create or that the mtime
|
||||||
|
* changes we do might still be affected by our changes. */
|
||||||
|
open_inodes[n_open_inodes++] = (OpenInode) {
|
||||||
|
.fd = TAKE_FD(fd),
|
||||||
|
.path = TAKE_PTR(j),
|
||||||
|
.filetype = filetype,
|
||||||
|
.mode = mode,
|
||||||
|
.mtime = mtime,
|
||||||
|
.uid = uid,
|
||||||
|
.gid = gid,
|
||||||
|
.xattr = TAKE_PTR(xa),
|
||||||
|
.n_xattr = n_xa,
|
||||||
|
};
|
||||||
|
|
||||||
|
n_xa = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = open_inode_finalize_many(&open_inodes, &n_open_inodes);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||||
|
assert(input_fd >= 0);
|
||||||
|
assert(tree_fd >= 0);
|
||||||
|
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support not available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
8
src/shared/tar-util.h
Normal file
8
src/shared/tar-util.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum TarFlags {
|
||||||
|
TAR_SELINUX = 1 << 0,
|
||||||
|
} TarFlags;
|
||||||
|
|
||||||
|
int tar_x(int input_fd, int tree_fd, TarFlags flags);
|
||||||
@ -60,6 +60,8 @@ static SD_VARLINK_DEFINE_METHOD(
|
|||||||
SD_VARLINK_DEFINE_INPUT(password, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
SD_VARLINK_DEFINE_INPUT(password, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
SD_VARLINK_FIELD_COMMENT("Takes an image policy string (see systemd.image-policy(7) for details) to apply while mounting the image"),
|
SD_VARLINK_FIELD_COMMENT("Takes an image policy string (see systemd.image-policy(7) for details) to apply while mounting the image"),
|
||||||
SD_VARLINK_DEFINE_INPUT(imagePolicy, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
SD_VARLINK_DEFINE_INPUT(imagePolicy, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Whether to automatically reuse already set up dm-verity devices that share the same roothash."),
|
||||||
|
SD_VARLINK_DEFINE_INPUT(veritySharing, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||||
VARLINK_DEFINE_POLKIT_INPUT,
|
VARLINK_DEFINE_POLKIT_INPUT,
|
||||||
SD_VARLINK_FIELD_COMMENT("An array with information about contained partitions that have been prepared for mounting, as well as their mount file descriptors."),
|
SD_VARLINK_FIELD_COMMENT("An array with information about contained partitions that have been prepared for mounting, as well as their mount file descriptors."),
|
||||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(partitions, PartitionInfo, SD_VARLINK_ARRAY),
|
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(partitions, PartitionInfo, SD_VARLINK_ARRAY),
|
||||||
|
|||||||
@ -61,6 +61,7 @@ int sd_dhcp_server_set_boot_server_name(sd_dhcp_server *server, const char *name
|
|||||||
int sd_dhcp_server_set_boot_filename(sd_dhcp_server *server, const char *filename);
|
int sd_dhcp_server_set_boot_filename(sd_dhcp_server *server, const char *filename);
|
||||||
int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled);
|
int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled);
|
||||||
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
|
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
|
||||||
|
int sd_dhcp_server_set_domain_name(sd_dhcp_server *server, const char *domain_name);
|
||||||
int sd_dhcp_server_set_router(sd_dhcp_server *server, const struct in_addr *address);
|
int sd_dhcp_server_set_router(sd_dhcp_server *server, const struct in_addr *address);
|
||||||
|
|
||||||
int sd_dhcp_server_set_servers(
|
int sd_dhcp_server_set_servers(
|
||||||
|
|||||||
@ -29,9 +29,16 @@ ProtectHostname=yes
|
|||||||
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
|
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
|
||||||
RestrictRealtime=yes
|
RestrictRealtime=yes
|
||||||
RestrictSUIDSGID=yes
|
RestrictSUIDSGID=yes
|
||||||
SystemCallArchitectures=native
|
|
||||||
SystemCallErrorNumber=EPERM
|
# FIXME: libseccomp (as of 2.6.0) doesn't know the open_tree_attr() system call
|
||||||
SystemCallFilter=@system-service @mount
|
# yet which we need. Thus, applying any system call filter would block this
|
||||||
|
# system call unconditionally, which is not desirable. This should be added
|
||||||
|
# back once libseccomp is updated. See
|
||||||
|
# https://github.com/seccomp/libseccomp/issues/465
|
||||||
|
|
||||||
|
#SystemCallArchitectures=native
|
||||||
|
#SystemCallErrorNumber=EPERM
|
||||||
|
#SystemCallFilter=@system-service @mount
|
||||||
Type=notify
|
Type=notify
|
||||||
NotifyAccess=all
|
NotifyAccess=all
|
||||||
FileDescriptorStoreMax=4096
|
FileDescriptorStoreMax=4096
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user