mirror of
https://github.com/systemd/systemd
synced 2026-03-30 19:54:51 +02:00
Compare commits
27 Commits
40258ae061
...
de3ef2524e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de3ef2524e | ||
|
|
5c791053e3 | ||
|
|
f533135c6c | ||
|
|
10285219ea | ||
|
|
c4fb47365c | ||
|
|
2700fecdb3 | ||
|
|
86019efa44 | ||
|
|
2aaf565a2d | ||
|
|
23cff6d4fe | ||
|
|
0c71e3ef24 | ||
|
|
bdfe7ada0d | ||
|
|
678ca2133c | ||
|
|
a995ce4768 | ||
|
|
d8f1673700 | ||
|
|
6393b847f4 | ||
|
|
25d7a71774 | ||
|
|
aab35b1e59 | ||
|
|
11c8b1f103 | ||
|
|
95fe7b28d3 | ||
|
|
7fbae5b706 | ||
|
|
fe92eb795b | ||
|
|
899c1c0a34 | ||
|
|
2ccd598635 | ||
|
|
f0c4f94453 | ||
|
|
f8cc16fd53 | ||
|
|
39ddc32a86 | ||
|
|
52b6b35643 |
4
TODO
4
TODO
@ -1248,10 +1248,6 @@ Features:
|
||||
fallback logic to get a regular user created on uninitialized systems.
|
||||
- store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
|
||||
systemd-cryptsetup, so that it can unlock homed volumes
|
||||
- try to unmount in regular intervals when home dir was busy when we
|
||||
tried because idle.
|
||||
- keep an fd to the homedir open at all times, to keep the fs pinned
|
||||
(autofs and such) while user is logged in.
|
||||
|
||||
* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
|
||||
specified, synthesize a definition automatically if we can: enlarge last
|
||||
|
||||
@ -116,6 +116,21 @@
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>set-timeout</option> <replaceable>TIMEOUT</replaceable></term>
|
||||
<term><option>set-timeout-oneshot</option> <replaceable>TIMEOUT</replaceable></term>
|
||||
|
||||
<listitem><para>Sets the boot loader menu timeout in seconds. The <option>set-timeout-oneshot</option>
|
||||
command will set the timeout only for the next boot. See
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for details about the syntax of time spans.</para>
|
||||
|
||||
<para>If this is set to <option>menu-hidden</option> or <option>0</option> no menu is shown and
|
||||
the default entry will be booted immediately, while setting this to <option>menu-force</option>
|
||||
disables the timeout while always showing the menu. When an empty string ("") is specified the
|
||||
bootloader will revert to its default menu timeout.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@ -610,6 +610,17 @@
|
||||
node is not allowed if any of the other storage backends are used.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--drop-caches=</option><replaceable>BOOL</replaceable></term>
|
||||
|
||||
<listitem><para>Automatically flush OS file system caches on logout. This is useful in combination
|
||||
with the fscrypt storage backend to ensure the OS does not keep decrypted versions of the files and
|
||||
directories in memory (and accessible) after logout. This option is also supported on other backends,
|
||||
but should not bring any benefit there. Defaults to off, except if the selected storage backend is
|
||||
fscrypt, where it defaults to on. Note that flushing OS caches will negatively influence performance
|
||||
of the OS shortly after logout.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
|
||||
|
||||
|
||||
@ -106,9 +106,10 @@
|
||||
will be stored as an EFI variable in that case, overriding this option.
|
||||
</para>
|
||||
|
||||
<para>If the timeout is disabled, the default entry will be booted
|
||||
immediately. The menu can be shown by pressing and holding a key before
|
||||
systemd-boot is launched.</para>
|
||||
<para>If set to <literal>menu-hidden</literal> or <literal>0</literal> no menu
|
||||
is shown and the default entry will be booted immediately. The menu can be shown
|
||||
by pressing and holding a key before systemd-boot is launched. Setting this to
|
||||
<literal>menu-force</literal> disables the timeout while always showing the menu.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@ -546,6 +546,7 @@ foreach ident : [
|
||||
['mount_setattr', '''#include <sys/mount.h>'''],
|
||||
['move_mount', '''#include <sys/mount.h>'''],
|
||||
['open_tree', '''#include <sys/mount.h>'''],
|
||||
['getdents64', '''#include <dirent.h>'''],
|
||||
]
|
||||
|
||||
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
|
||||
|
||||
@ -57,7 +57,7 @@ _bootctl() {
|
||||
|
||||
local -A VERBS=(
|
||||
# systemd-efi-options takes an argument, but it is free-form, so we cannot complete it
|
||||
[STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list'
|
||||
[STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot'
|
||||
[BOOTENTRY]='set-default set-oneshot'
|
||||
[BOOLEAN]='reboot-to-firmware'
|
||||
)
|
||||
|
||||
@ -46,6 +46,8 @@ _bootctl_reboot-to-firmware() {
|
||||
"list:List boot loader entries"
|
||||
"set-default:Set the default boot loader entry"
|
||||
"set-oneshot:Set the default boot loader entry only for the next boot"
|
||||
"set-timeout:Set the menu timeout"
|
||||
"set-timeout-oneshot:Set the menu timeout for the next boot only"
|
||||
)
|
||||
if (( CURRENT == 1 )); then
|
||||
_describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@"
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
#define _public_ __attribute__((__visibility__("default")))
|
||||
#define _hidden_ __attribute__((__visibility__("hidden")))
|
||||
#define _weakref_(x) __attribute__((__weakref__(#x)))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
|
||||
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
|
||||
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
|
||||
|
||||
|
||||
@ -540,3 +540,19 @@ static inline int missing_move_mount(
|
||||
|
||||
# define move_mount missing_move_mount
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_GETDENTS64
|
||||
|
||||
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
|
||||
# if defined __NR_getdents64 && __NR_getdents64 >= 0
|
||||
return syscall(__NR_getdents64, fd, buffer, length);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define getdents64 missing_getdents64
|
||||
#endif
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "sort-util.h"
|
||||
@ -14,76 +15,130 @@ static int sort_func(struct dirent * const *a, struct dirent * const *b) {
|
||||
return strcmp((*a)->d_name, (*b)->d_name);
|
||||
}
|
||||
|
||||
struct dirent** readdir_all_free(struct dirent **array) {
|
||||
static bool ignore_dirent(const struct dirent *de, RecurseDirFlags flags) {
|
||||
assert(de);
|
||||
|
||||
/* Destructor that relies on the fact that the array of dirent structure pointers is NULL
|
||||
* terminated */
|
||||
/* Depending on flag either ignore everything starting with ".", or just "." itself and ".." */
|
||||
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
for (struct dirent **i = array; *i; i++)
|
||||
free(*i);
|
||||
|
||||
return mfree(array);
|
||||
return FLAGS_SET(flags, RECURSE_DIR_IGNORE_DOT) ?
|
||||
de->d_name[0] == '.' :
|
||||
dot_or_dot_dot(de->d_name);
|
||||
}
|
||||
|
||||
int readdir_all(DIR *d,
|
||||
/* Maximum space one direent structure might require at most */
|
||||
#define DIRENT_SIZE_MAX MAX(sizeof(struct dirent), offsetof(struct dirent, d_name) + NAME_MAX + 1)
|
||||
|
||||
int readdir_all(int dir_fd,
|
||||
RecurseDirFlags flags,
|
||||
struct dirent ***ret) {
|
||||
DirectoryEntries **ret) {
|
||||
|
||||
_cleanup_(readdir_all_freep) struct dirent **de_array = NULL;
|
||||
size_t n_de = 0;
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
DirectoryEntries *nde;
|
||||
size_t add, sz, j;
|
||||
|
||||
assert(d);
|
||||
assert(dir_fd >= 0);
|
||||
|
||||
/* Returns an array with pointers to "struct dirent" directory entries, optionally sorted. Free the
|
||||
* array with readdir_all_freep(). */
|
||||
|
||||
/* Only if 64bit off_t is enabled struct dirent + struct dirent64 are actually the same. We require
|
||||
* this, and we want them to be interchangable, hence verify that. */
|
||||
assert_cc(_FILE_OFFSET_BITS == 64);
|
||||
assert_cc(sizeof(struct dirent) == sizeof(struct dirent64));
|
||||
assert_cc(offsetof(struct dirent, d_ino) == offsetof(struct dirent64, d_ino));
|
||||
assert_cc(sizeof(((struct dirent*) NULL)->d_ino) == sizeof(((struct dirent64*) NULL)->d_ino));
|
||||
assert_cc(offsetof(struct dirent, d_off) == offsetof(struct dirent64, d_off));
|
||||
assert_cc(sizeof(((struct dirent*) NULL)->d_off) == sizeof(((struct dirent64*) NULL)->d_off));
|
||||
assert_cc(offsetof(struct dirent, d_reclen) == offsetof(struct dirent64, d_reclen));
|
||||
assert_cc(sizeof(((struct dirent*) NULL)->d_reclen) == sizeof(((struct dirent64*) NULL)->d_reclen));
|
||||
assert_cc(offsetof(struct dirent, d_type) == offsetof(struct dirent64, d_type));
|
||||
assert_cc(sizeof(((struct dirent*) NULL)->d_type) == sizeof(((struct dirent64*) NULL)->d_type));
|
||||
assert_cc(offsetof(struct dirent, d_name) == offsetof(struct dirent64, d_name));
|
||||
assert_cc(sizeof(((struct dirent*) NULL)->d_name) == sizeof(((struct dirent64*) NULL)->d_name));
|
||||
|
||||
/* Start with space for up to 8 directory entries. We expect at least 2 ("." + ".."), hence hopefully
|
||||
* 8 will cover most cases comprehensively. (Note that most likely a lot more entries will actually
|
||||
* fit in the buffer, given we calculate maximum file name length here.) */
|
||||
de = malloc(offsetof(DirectoryEntries, buffer) + DIRENT_SIZE_MAX * 8);
|
||||
if (!de)
|
||||
return -ENOMEM;
|
||||
|
||||
de->buffer_size = 0;
|
||||
for (;;) {
|
||||
_cleanup_free_ struct dirent *copy = NULL;
|
||||
struct dirent *de;
|
||||
size_t bs;
|
||||
ssize_t n;
|
||||
|
||||
errno = 0;
|
||||
de = readdir(d);
|
||||
if (!de) {
|
||||
if (errno == 0)
|
||||
break;
|
||||
bs = MIN(MALLOC_SIZEOF_SAFE(de) - offsetof(DirectoryEntries, buffer), (size_t) SSIZE_MAX);
|
||||
assert(bs > de->buffer_size);
|
||||
|
||||
n = getdents64(dir_fd, de->buffer + de->buffer_size, bs - de->buffer_size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
/* Depending on flag either ignore everything starting with ".", or just "." itself and ".." */
|
||||
if (FLAGS_SET(flags, RECURSE_DIR_IGNORE_DOT) ?
|
||||
de->d_name[0] == '.' :
|
||||
dot_or_dot_dot(de->d_name))
|
||||
de->buffer_size += n;
|
||||
|
||||
if (de->buffer_size < bs - DIRENT_SIZE_MAX) /* Still room for one more entry, then try to
|
||||
* fill it up without growing the structure. */
|
||||
continue;
|
||||
|
||||
if (n_de >= INT_MAX) /* Make sure we can return the number as 'int' return value */
|
||||
return -ERANGE;
|
||||
if (bs >= SSIZE_MAX - offsetof(DirectoryEntries, buffer))
|
||||
return -EFBIG;
|
||||
bs = bs >= (SSIZE_MAX - offsetof(DirectoryEntries, buffer))/2 ? SSIZE_MAX - offsetof(DirectoryEntries, buffer) : bs * 2;
|
||||
|
||||
if (!GREEDY_REALLOC(de_array, n_de+2))
|
||||
nde = realloc(de, bs);
|
||||
if (!nde)
|
||||
return -ENOMEM;
|
||||
|
||||
copy = memdup(de, de->d_reclen);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
de = nde;
|
||||
}
|
||||
|
||||
de_array[n_de++] = TAKE_PTR(copy);
|
||||
de_array[n_de] = NULL; /* guarantee array remains NUL terminated */
|
||||
de->n_entries = 0;
|
||||
for (struct dirent *entry = (struct dirent*) de->buffer;
|
||||
(uint8_t*) entry < de->buffer + de->buffer_size;
|
||||
entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
|
||||
|
||||
if (ignore_dirent(entry, flags))
|
||||
continue;
|
||||
|
||||
de->n_entries++;
|
||||
}
|
||||
|
||||
sz = ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size);
|
||||
add = sizeof(struct dirent*) * de->n_entries;
|
||||
if (add > SIZE_MAX - add)
|
||||
return -ENOMEM;
|
||||
|
||||
nde = realloc(de, sz + add);
|
||||
if (!nde)
|
||||
return -ENOMEM;
|
||||
|
||||
de = nde;
|
||||
de->entries = (struct dirent**) ((uint8_t*) de + ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size));
|
||||
|
||||
j = 0;
|
||||
for (struct dirent *entry = (struct dirent*) de->buffer;
|
||||
(uint8_t*) entry < de->buffer + de->buffer_size;
|
||||
entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
|
||||
|
||||
if (ignore_dirent(entry, flags))
|
||||
continue;
|
||||
|
||||
de->entries[j++] = entry;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(flags, RECURSE_DIR_SORT))
|
||||
typesafe_qsort(de_array, n_de, sort_func);
|
||||
typesafe_qsort(de->entries, de->n_entries, sort_func);
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(de_array);
|
||||
*ret = TAKE_PTR(de);
|
||||
|
||||
return (int) n_de;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int recurse_dir(
|
||||
DIR *d,
|
||||
int dir_fd,
|
||||
const char *path,
|
||||
unsigned statx_mask,
|
||||
unsigned n_depth_max,
|
||||
@ -91,51 +146,50 @@ int recurse_dir(
|
||||
recurse_dir_func_t func,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(readdir_all_freep) struct dirent **de = NULL;
|
||||
int r, n;
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
assert(dir_fd >= 0);
|
||||
assert(func);
|
||||
|
||||
/* This is a lot like ftw()/nftw(), but a lot more modern, i.e. built around openat()/statx(), and
|
||||
* under the assumption that fds are not as 'expensive' as they used to be. */
|
||||
/* This is a lot like ftw()/nftw(), but a lot more modern, i.e. built around openat()/statx()/O_PATH,
|
||||
* and under the assumption that fds are not as 'expensive' as they used to be. */
|
||||
|
||||
if (n_depth_max == 0)
|
||||
return -EOVERFLOW;
|
||||
if (n_depth_max == UINT_MAX) /* special marker for "default" */
|
||||
n_depth_max = DEFAULT_RECURSION_MAX;
|
||||
|
||||
n = readdir_all(d, flags, &de);
|
||||
if (n < 0)
|
||||
return n;
|
||||
r = readdir_all(dir_fd, flags, &de);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (size_t i = 0; i < de->n_entries; i++) {
|
||||
_cleanup_close_ int inode_fd = -1, subdir_fd = -1;
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
_cleanup_closedir_ DIR *subdir = NULL;
|
||||
_cleanup_close_ int inode_fd = -1;
|
||||
STRUCT_STATX_DEFINE(sx);
|
||||
bool sx_valid = false;
|
||||
const char *p;
|
||||
|
||||
/* For each directory entry we'll do one of the following:
|
||||
*
|
||||
* 1) If the entry refers to a directory, we'll open it as O_DIRECTORY 'subdir' and then statx() the opened directory if requested
|
||||
* 2) Otherwise and RECURSE_DIR_INODE_FD is set we'll open O_PATH 'inode_fd' and then statx() the opened inode
|
||||
* 3) Otherwise we'll statx() the directory entry via the directory we are currently looking at
|
||||
* 1) If the entry refers to a directory, we'll open it as O_DIRECTORY 'subdir_fd' and then statx() the opened directory via that new fd (if requested)
|
||||
* 2) Otherwise, if RECURSE_DIR_INODE_FD is set we'll open it as O_PATH 'inode_fd' and then statx() the opened inode via that new fd (if requested)
|
||||
* 3) Otherwise, we'll statx() the directory entry via the directory fd we are currently looking at (if requested)
|
||||
*/
|
||||
|
||||
if (path) {
|
||||
joined = path_join(path, de[i]->d_name);
|
||||
joined = path_join(path, de->entries[i]->d_name);
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
p = joined;
|
||||
} else
|
||||
p = de[i]->d_name;
|
||||
p = de->entries[i]->d_name;
|
||||
|
||||
if (IN_SET(de[i]->d_type, DT_UNKNOWN, DT_DIR)) {
|
||||
subdir = xopendirat(dirfd(d), de[i]->d_name, O_NOFOLLOW);
|
||||
if (!subdir) {
|
||||
if (IN_SET(de->entries[i]->d_type, DT_UNKNOWN, DT_DIR)) {
|
||||
subdir_fd = openat(dir_fd, de->entries[i]->d_name, O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
|
||||
if (subdir_fd < 0) {
|
||||
if (errno == ENOENT) /* Vanished by now, go for next file immediately */
|
||||
continue;
|
||||
|
||||
@ -147,9 +201,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE + errno,
|
||||
p,
|
||||
dirfd(d),
|
||||
dir_fd,
|
||||
-1,
|
||||
de[i],
|
||||
de->entries[i],
|
||||
NULL,
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -164,10 +218,10 @@ int recurse_dir(
|
||||
|
||||
} else {
|
||||
/* If we managed to get a DIR* off the inode, it's definitely a directory. */
|
||||
de[i]->d_type = DT_DIR;
|
||||
de->entries[i]->d_type = DT_DIR;
|
||||
|
||||
if (statx_mask != 0 || (flags & RECURSE_DIR_SAME_MOUNT)) {
|
||||
r = statx_fallback(dirfd(subdir), "", AT_EMPTY_PATH, statx_mask, &sx);
|
||||
r = statx_fallback(subdir_fd, "", AT_EMPTY_PATH, statx_mask, &sx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -176,12 +230,12 @@ int recurse_dir(
|
||||
}
|
||||
}
|
||||
|
||||
if (!subdir) {
|
||||
if (subdir_fd < 0) {
|
||||
/* It's not a subdirectory. */
|
||||
|
||||
if (flags & RECURSE_DIR_INODE_FD) {
|
||||
|
||||
inode_fd = openat(dirfd(d), de[i]->d_name, O_PATH|O_NOFOLLOW|O_CLOEXEC);
|
||||
inode_fd = openat(dir_fd, de->entries[i]->d_name, O_PATH|O_NOFOLLOW|O_CLOEXEC);
|
||||
if (inode_fd < 0) {
|
||||
if (errno == ENOENT) /* Vanished by now, go for next file immediately */
|
||||
continue;
|
||||
@ -192,9 +246,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE + errno,
|
||||
p,
|
||||
dirfd(d),
|
||||
dir_fd,
|
||||
-1,
|
||||
de[i],
|
||||
de->entries[i],
|
||||
NULL,
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -222,16 +276,16 @@ int recurse_dir(
|
||||
* directory fd — which sould be riskless now that we pinned the
|
||||
* inode. */
|
||||
|
||||
subdir = xopendirat(AT_FDCWD, FORMAT_PROC_FD_PATH(inode_fd), 0);
|
||||
if (!subdir)
|
||||
subdir_fd = openat(AT_FDCWD, FORMAT_PROC_FD_PATH(inode_fd), O_DIRECTORY|O_CLOEXEC);
|
||||
if (subdir_fd < 0)
|
||||
return -errno;
|
||||
|
||||
inode_fd = safe_close(inode_fd);
|
||||
}
|
||||
|
||||
} else if (statx_mask != 0 || (de[i]->d_type == DT_UNKNOWN && (flags & RECURSE_DIR_ENSURE_TYPE))) {
|
||||
} else if (statx_mask != 0 || (de->entries[i]->d_type == DT_UNKNOWN && (flags & RECURSE_DIR_ENSURE_TYPE))) {
|
||||
|
||||
r = statx_fallback(dirfd(d), de[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx);
|
||||
r = statx_fallback(dir_fd, de->entries[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx);
|
||||
if (r == -ENOENT) /* Vanished by now? Go for next file immediately */
|
||||
continue;
|
||||
if (r < 0) {
|
||||
@ -241,9 +295,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + -r,
|
||||
p,
|
||||
dirfd(d),
|
||||
dir_fd,
|
||||
-1,
|
||||
de[i],
|
||||
de->entries[i],
|
||||
NULL,
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -271,9 +325,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + EISDIR,
|
||||
p,
|
||||
dirfd(d),
|
||||
dir_fd,
|
||||
-1,
|
||||
de[i],
|
||||
de->entries[i],
|
||||
NULL,
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -289,22 +343,22 @@ int recurse_dir(
|
||||
if (sx_valid) {
|
||||
/* Copy over the data we acquired through statx() if we acquired any */
|
||||
if (sx.stx_mask & STATX_TYPE) {
|
||||
assert(!!subdir == !!S_ISDIR(sx.stx_mode));
|
||||
de[i]->d_type = IFTODT(sx.stx_mode);
|
||||
assert((subdir_fd < 0) == !S_ISDIR(sx.stx_mode));
|
||||
de->entries[i]->d_type = IFTODT(sx.stx_mode);
|
||||
}
|
||||
|
||||
if (sx.stx_mask & STATX_INO)
|
||||
de[i]->d_ino = sx.stx_ino;
|
||||
de->entries[i]->d_ino = sx.stx_ino;
|
||||
}
|
||||
|
||||
if (subdir) {
|
||||
if (subdir_fd >= 0) {
|
||||
if (FLAGS_SET(flags, RECURSE_DIR_SAME_MOUNT)) {
|
||||
bool is_mount;
|
||||
|
||||
if (sx_valid && FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
|
||||
is_mount = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
|
||||
else {
|
||||
r = fd_is_mount_point(dirfd(d), de[i]->d_name, 0);
|
||||
r = fd_is_mount_point(dir_fd, de->entries[i]->d_name, 0);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether %s is a submount, assuming not: %m", p);
|
||||
|
||||
@ -314,9 +368,9 @@ int recurse_dir(
|
||||
if (is_mount) {
|
||||
r = func(RECURSE_DIR_SKIP_MOUNT,
|
||||
p,
|
||||
dirfd(d),
|
||||
dirfd(subdir),
|
||||
de[i],
|
||||
dir_fd,
|
||||
subdir_fd,
|
||||
de->entries[i],
|
||||
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -333,9 +387,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_SKIP_DEPTH,
|
||||
p,
|
||||
dirfd(d),
|
||||
dirfd(subdir),
|
||||
de[i],
|
||||
dir_fd,
|
||||
subdir_fd,
|
||||
de->entries[i],
|
||||
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -348,9 +402,9 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_ENTER,
|
||||
p,
|
||||
dirfd(d),
|
||||
dirfd(subdir),
|
||||
de[i],
|
||||
dir_fd,
|
||||
subdir_fd,
|
||||
de->entries[i],
|
||||
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
|
||||
userdata);
|
||||
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
|
||||
@ -360,7 +414,7 @@ int recurse_dir(
|
||||
if (r != RECURSE_DIR_CONTINUE)
|
||||
return r;
|
||||
|
||||
r = recurse_dir(subdir,
|
||||
r = recurse_dir(subdir_fd,
|
||||
p,
|
||||
statx_mask,
|
||||
n_depth_max - 1,
|
||||
@ -372,18 +426,18 @@ int recurse_dir(
|
||||
|
||||
r = func(RECURSE_DIR_LEAVE,
|
||||
p,
|
||||
dirfd(d),
|
||||
dirfd(subdir),
|
||||
de[i],
|
||||
dir_fd,
|
||||
subdir_fd,
|
||||
de->entries[i],
|
||||
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
|
||||
userdata);
|
||||
} else
|
||||
/* Non-directory inode */
|
||||
r = func(RECURSE_DIR_ENTRY,
|
||||
p,
|
||||
dirfd(d),
|
||||
dir_fd,
|
||||
inode_fd,
|
||||
de[i],
|
||||
de->entries[i],
|
||||
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
|
||||
userdata);
|
||||
|
||||
@ -406,11 +460,17 @@ int recurse_dir_at(
|
||||
recurse_dir_func_t func,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
d = xopendirat(atfd, path, 0);
|
||||
if (!d)
|
||||
assert(atfd >= 0 || atfd == AT_FDCWD);
|
||||
assert(func);
|
||||
|
||||
if (!path)
|
||||
path = ".";
|
||||
|
||||
fd = openat(atfd, path, O_DIRECTORY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return recurse_dir(d, path, statx_mask, n_depth_max, flags, func, userdata);
|
||||
return recurse_dir(fd, path, statx_mask, n_depth_max, flags, func, userdata);
|
||||
}
|
||||
|
||||
@ -67,9 +67,14 @@ typedef enum RecurseDirFlags {
|
||||
RECURSE_DIR_INODE_FD = 1 << 4, /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
|
||||
} RecurseDirFlags;
|
||||
|
||||
struct dirent** readdir_all_free(struct dirent **array);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct dirent **, readdir_all_free);
|
||||
int readdir_all(DIR *d, RecurseDirFlags flags, struct dirent ***ret);
|
||||
typedef struct DirectoryEntries {
|
||||
size_t n_entries;
|
||||
struct dirent** entries;
|
||||
size_t buffer_size;
|
||||
uint8_t buffer[] _alignas_(struct dirent);
|
||||
} DirectoryEntries;
|
||||
|
||||
int recurse_dir(DIR *d, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
|
||||
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
|
||||
|
||||
int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
|
||||
int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
|
||||
|
||||
@ -119,7 +119,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const __signal_table[] = {
|
||||
static const char *const static_signal_table[] = {
|
||||
[SIGHUP] = "HUP",
|
||||
[SIGINT] = "INT",
|
||||
[SIGQUIT] = "QUIT",
|
||||
@ -155,13 +155,13 @@ static const char *const __signal_table[] = {
|
||||
[SIGSYS] = "SYS"
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
|
||||
|
||||
const char *signal_to_string(int signo) {
|
||||
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
|
||||
const char *name;
|
||||
|
||||
name = __signal_to_string(signo);
|
||||
name = static_signal_to_string(signo);
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
@ -190,7 +190,7 @@ int signal_from_string(const char *s) {
|
||||
s += 3;
|
||||
|
||||
/* Check that the input is a signal name. */
|
||||
signo = __signal_from_string(s);
|
||||
signo = static_signal_from_string(s);
|
||||
if (signo > 0)
|
||||
return signo;
|
||||
|
||||
|
||||
@ -551,7 +551,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) {
|
||||
return -errno;
|
||||
|
||||
if (sa.sa.sa_family == AF_UNIX) {
|
||||
struct ucred ucred = {};
|
||||
struct ucred ucred = UCRED_INVALID;
|
||||
|
||||
/* UNIX connection sockets are anonymous, so let's use
|
||||
* PID/UID as pretty credentials instead */
|
||||
|
||||
@ -327,3 +327,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) {
|
||||
}
|
||||
|
||||
int socket_get_mtu(int fd, int af, size_t *ret);
|
||||
|
||||
/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
|
||||
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
/* hey glibc, APIs with callbacks without a user pointer are so useless */
|
||||
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t compar, void *arg) {
|
||||
comparison_userdata_fn_t compar, void *arg) {
|
||||
size_t l, u, idx;
|
||||
const void *p;
|
||||
int comparison;
|
||||
|
||||
@ -5,14 +5,20 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the
|
||||
* external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that
|
||||
* ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if
|
||||
* they should ever add one. */
|
||||
typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *);
|
||||
|
||||
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t compar, void *arg);
|
||||
comparison_userdata_fn_t compar, void *arg);
|
||||
|
||||
#define typesafe_bsearch_r(k, b, n, func, userdata) \
|
||||
({ \
|
||||
const typeof(b[0]) *_k = k; \
|
||||
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
|
||||
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
|
||||
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
|
||||
})
|
||||
|
||||
/**
|
||||
@ -20,7 +26,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
* that only if nmemb > 0.
|
||||
*/
|
||||
static inline void* bsearch_safe(const void *key, const void *base,
|
||||
size_t nmemb, size_t size, __compar_fn_t compar) {
|
||||
size_t nmemb, size_t size, comparison_fn_t compar) {
|
||||
if (nmemb <= 0)
|
||||
return NULL;
|
||||
|
||||
@ -32,14 +38,14 @@ static inline void* bsearch_safe(const void *key, const void *base,
|
||||
({ \
|
||||
const typeof(b[0]) *_k = k; \
|
||||
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
|
||||
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
|
||||
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
|
||||
})
|
||||
|
||||
/**
|
||||
* Normal qsort requires base to be nonnull. Here were require
|
||||
* that only if nmemb > 0.
|
||||
*/
|
||||
static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
|
||||
static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
|
||||
if (nmemb <= 1)
|
||||
return;
|
||||
|
||||
@ -52,10 +58,10 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f
|
||||
#define typesafe_qsort(p, n, func) \
|
||||
({ \
|
||||
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
|
||||
_qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
|
||||
_qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
|
||||
})
|
||||
|
||||
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
|
||||
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
|
||||
if (nmemb <= 1)
|
||||
return;
|
||||
|
||||
@ -66,7 +72,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_
|
||||
#define typesafe_qsort_r(p, n, func, userdata) \
|
||||
({ \
|
||||
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
|
||||
qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
|
||||
qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
|
||||
})
|
||||
|
||||
int cmp_int(const int *a, const int *b);
|
||||
|
||||
@ -55,9 +55,9 @@ typedef uint64_t __sd_bitwise be64_t;
|
||||
#undef le64toh
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define bswap_16_on_le(x) __bswap_16(x)
|
||||
#define bswap_32_on_le(x) __bswap_32(x)
|
||||
#define bswap_64_on_le(x) __bswap_64(x)
|
||||
#define bswap_16_on_le(x) bswap_16(x)
|
||||
#define bswap_32_on_le(x) bswap_32(x)
|
||||
#define bswap_64_on_le(x) bswap_64(x)
|
||||
#define bswap_16_on_be(x) (x)
|
||||
#define bswap_32_on_be(x) (x)
|
||||
#define bswap_64_on_be(x) (x)
|
||||
@ -65,9 +65,9 @@ typedef uint64_t __sd_bitwise be64_t;
|
||||
#define bswap_16_on_le(x) (x)
|
||||
#define bswap_32_on_le(x) (x)
|
||||
#define bswap_64_on_le(x) (x)
|
||||
#define bswap_16_on_be(x) __bswap_16(x)
|
||||
#define bswap_32_on_be(x) __bswap_32(x)
|
||||
#define bswap_64_on_be(x) __bswap_64(x)
|
||||
#define bswap_16_on_be(x) bswap_16(x)
|
||||
#define bswap_32_on_be(x) bswap_32(x)
|
||||
#define bswap_64_on_be(x) bswap_64(x)
|
||||
#endif
|
||||
|
||||
static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
|
||||
|
||||
@ -1085,3 +1085,14 @@ int is_this_me(const char *username) {
|
||||
|
||||
return uid == getuid();
|
||||
}
|
||||
|
||||
const char *get_home_root(void) {
|
||||
const char *e;
|
||||
|
||||
/* For debug purposes allow overriding where we look for home dirs */
|
||||
e = secure_getenv("SYSTEMD_HOME_ROOT");
|
||||
if (e && path_is_absolute(e) && path_is_normalized(e))
|
||||
return e;
|
||||
|
||||
return "/home";
|
||||
}
|
||||
|
||||
@ -112,6 +112,8 @@ bool is_nologin_shell(const char *shell);
|
||||
|
||||
int is_this_me(const char *username);
|
||||
|
||||
const char *get_home_root(void);
|
||||
|
||||
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
|
||||
#define PASSWORD_LOCKED_AND_INVALID "!*"
|
||||
|
||||
|
||||
@ -1099,6 +1099,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" list List boot loader entries\n"
|
||||
" set-default ID Set default boot loader entry\n"
|
||||
" set-oneshot ID Set default boot loader entry, for next boot only\n"
|
||||
" set-timeout SECONDS Set the menu timeout\n"
|
||||
" set-timeout-oneshot SECONDS\n"
|
||||
" Set the menu timeout for the next boot only\n"
|
||||
"\n%3$ssystemd-boot Commands:%4$s\n"
|
||||
" install Install systemd-boot to the ESP and EFI variables\n"
|
||||
" update Update systemd-boot in the ESP and EFI variables\n"
|
||||
@ -1773,6 +1776,37 @@ static int verb_is_installed(int argc, char *argv[], void *userdata) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
||||
char utf8[DECIMAL_STR_MAX(usec_t)];
|
||||
char16_t *encoded;
|
||||
usec_t timeout;
|
||||
int r;
|
||||
|
||||
assert(arg1);
|
||||
assert(ret_timeout);
|
||||
assert(ret_timeout_size);
|
||||
|
||||
if (streq(arg1, "menu-force"))
|
||||
timeout = USEC_INFINITY;
|
||||
else if (streq(arg1, "menu-hidden"))
|
||||
timeout = 0;
|
||||
else {
|
||||
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
|
||||
if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
|
||||
log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
|
||||
}
|
||||
|
||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
||||
encoded = utf8_to_utf16(utf8, strlen(utf8));
|
||||
if (!encoded)
|
||||
return log_oom();
|
||||
*ret_timeout = encoded;
|
||||
*ret_timeout_size = char16_strlen(encoded) * 2 + 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
|
||||
int r;
|
||||
if (streq(arg1, "@current")) {
|
||||
@ -1798,7 +1832,7 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_set_default(int argc, char *argv[], void *userdata) {
|
||||
static int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot())
|
||||
@ -1821,24 +1855,39 @@ static int verb_set_default(int argc, char *argv[], void *userdata) {
|
||||
|
||||
if (!arg_touch_variables)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' operation cannot be combined with --touch-variables=no.",
|
||||
"'%s' operation cannot be combined with --no-variables.",
|
||||
argv[0]);
|
||||
|
||||
const char *variable = streq(argv[0], "set-default") ?
|
||||
EFI_LOADER_VARIABLE(LoaderEntryDefault) : EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
||||
const char *variable;
|
||||
int (* arg_parser)(const char *, char16_t **, size_t *);
|
||||
|
||||
if (streq(argv[0], "set-default")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-timeout")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
|
||||
arg_parser = parse_timeout;
|
||||
} else if (streq(argv[0], "set-timeout-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
|
||||
arg_parser = parse_timeout;
|
||||
} else
|
||||
assert_not_reached();
|
||||
|
||||
if (isempty(argv[1])) {
|
||||
r = efi_set_variable(variable, NULL, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
|
||||
} else {
|
||||
_cleanup_free_ char16_t *target = NULL;
|
||||
size_t target_size = 0;
|
||||
_cleanup_free_ char16_t *value = NULL;
|
||||
size_t value_size = 0;
|
||||
|
||||
r = parse_loader_entry_target_arg(argv[1], &target, &target_size);
|
||||
r = arg_parser(argv[1], &value, &value_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = efi_set_variable(variable, target, target_size);
|
||||
r = efi_set_variable(variable, value, value_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
|
||||
}
|
||||
@ -1943,8 +1992,10 @@ static int bootctl_main(int argc, char *argv[]) {
|
||||
{ "remove", VERB_ANY, 1, 0, verb_remove },
|
||||
{ "is-installed", VERB_ANY, 1, 0, verb_is_installed },
|
||||
{ "list", VERB_ANY, 1, 0, verb_list },
|
||||
{ "set-default", 2, 2, 0, verb_set_default },
|
||||
{ "set-oneshot", 2, 2, 0, verb_set_default },
|
||||
{ "set-default", 2, 2, 0, verb_set_efivar },
|
||||
{ "set-oneshot", 2, 2, 0, verb_set_efivar },
|
||||
{ "set-timeout", 2, 2, 0, verb_set_efivar },
|
||||
{ "set-timeout-oneshot", 2, 2, 0, verb_set_efivar },
|
||||
{ "random-seed", VERB_ANY, 1, 0, verb_random_seed },
|
||||
{ "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options },
|
||||
{ "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware },
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
#define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
|
||||
|
||||
/* magic string to find in the binary image */
|
||||
static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
|
||||
_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
|
||||
|
||||
enum loader_type {
|
||||
LOADER_UNDEFINED,
|
||||
@ -60,9 +60,9 @@ typedef struct {
|
||||
UINTN entry_count;
|
||||
INTN idx_default;
|
||||
INTN idx_default_efivar;
|
||||
UINTN timeout_sec;
|
||||
UINTN timeout_sec_config;
|
||||
INTN timeout_sec_efivar;
|
||||
UINT32 timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
|
||||
UINT32 timeout_sec_config;
|
||||
UINT32 timeout_sec_efivar;
|
||||
CHAR16 *entry_default_pattern;
|
||||
CHAR16 *entry_oneshot;
|
||||
CHAR16 *options_edit;
|
||||
@ -75,6 +75,18 @@ typedef struct {
|
||||
RandomSeedMode random_seed_mode;
|
||||
} Config;
|
||||
|
||||
/* These values have been chosen so that the transitions the user sees could
|
||||
* employ unsigned over-/underflow like this:
|
||||
* efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
|
||||
enum {
|
||||
TIMEOUT_MIN = 1,
|
||||
TIMEOUT_MAX = UINT32_MAX - 2U,
|
||||
TIMEOUT_UNSET = UINT32_MAX - 1U,
|
||||
TIMEOUT_MENU_FORCE = UINT32_MAX,
|
||||
TIMEOUT_MENU_HIDDEN = 0,
|
||||
TIMEOUT_TYPE_MAX = UINT32_MAX,
|
||||
};
|
||||
|
||||
static VOID cursor_left(UINTN *cursor, UINTN *first) {
|
||||
assert(cursor);
|
||||
assert(first);
|
||||
@ -366,6 +378,38 @@ static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) {
|
||||
assert(t);
|
||||
|
||||
switch (*t) {
|
||||
case TIMEOUT_MAX:
|
||||
*t = inc ? TIMEOUT_MAX : (*t - 1);
|
||||
break;
|
||||
case TIMEOUT_UNSET:
|
||||
*t = inc ? TIMEOUT_MENU_FORCE : TIMEOUT_UNSET;
|
||||
break;
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
*t = inc ? TIMEOUT_MENU_HIDDEN : TIMEOUT_UNSET;
|
||||
break;
|
||||
case TIMEOUT_MENU_HIDDEN:
|
||||
*t = inc ? TIMEOUT_MIN : TIMEOUT_MENU_FORCE;
|
||||
break;
|
||||
default:
|
||||
*t += inc ? 1 : -1;
|
||||
}
|
||||
|
||||
switch (*t) {
|
||||
case TIMEOUT_UNSET:
|
||||
return StrDuplicate(L"Menu timeout defined by configuration file.");
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
return StrDuplicate(L"Timeout disabled, menu will always be shown.");
|
||||
case TIMEOUT_MENU_HIDDEN:
|
||||
return StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
|
||||
default:
|
||||
return PoolPrint(L"Menu timeout set to %u s.", *t);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
|
||||
UINT64 key;
|
||||
UINTN timeout;
|
||||
@ -400,10 +444,11 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
|
||||
Print(L"\n--- press key ---\n\n");
|
||||
console_key_read(&key, 0);
|
||||
|
||||
Print(L"timeout: %u\n", config->timeout_sec);
|
||||
if (config->timeout_sec_efivar >= 0)
|
||||
Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
|
||||
Print(L"timeout (config): %u\n", config->timeout_sec_config);
|
||||
Print(L"timeout: %u s\n", config->timeout_sec);
|
||||
if (config->timeout_sec_efivar != TIMEOUT_UNSET)
|
||||
Print(L"timeout (EFI var): %u s\n", config->timeout_sec_efivar);
|
||||
if (config->timeout_sec_config != TIMEOUT_UNSET)
|
||||
Print(L"timeout (config): %u s\n", config->timeout_sec_config);
|
||||
if (config->entry_default_pattern)
|
||||
Print(L"default pattern: '%s'\n", config->entry_default_pattern);
|
||||
Print(L"editor: %s\n", yes_no(config->editor));
|
||||
@ -519,7 +564,8 @@ static BOOLEAN menu_run(
|
||||
UINTN x_max, y_max;
|
||||
CHAR16 **lines = NULL;
|
||||
_cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
|
||||
UINTN timeout_remain = config->timeout_sec;
|
||||
UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
|
||||
UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
|
||||
INT16 idx;
|
||||
BOOLEAN exit = FALSE, run = TRUE;
|
||||
INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
|
||||
@ -640,7 +686,7 @@ static BOOLEAN menu_run(
|
||||
|
||||
if (timeout_remain > 0) {
|
||||
FreePool(status);
|
||||
status = PoolPrint(L"Boot in %d s.", timeout_remain);
|
||||
status = PoolPrint(L"Boot in %u s.", timeout_remain);
|
||||
}
|
||||
|
||||
/* print status at last line of screen */
|
||||
@ -768,44 +814,12 @@ static BOOLEAN menu_run(
|
||||
|
||||
case KEYPRESS(0, 0, '-'):
|
||||
case KEYPRESS(0, 0, 'T'):
|
||||
if (config->timeout_sec_efivar > 0) {
|
||||
config->timeout_sec_efivar--;
|
||||
efivar_set_uint_string(
|
||||
LOADER_GUID,
|
||||
L"LoaderConfigTimeout",
|
||||
config->timeout_sec_efivar,
|
||||
EFI_VARIABLE_NON_VOLATILE);
|
||||
if (config->timeout_sec_efivar > 0)
|
||||
status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
|
||||
else
|
||||
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
|
||||
} else if (config->timeout_sec_efivar <= 0){
|
||||
config->timeout_sec_efivar = -1;
|
||||
efivar_set(
|
||||
LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
|
||||
if (config->timeout_sec_config > 0)
|
||||
status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
|
||||
config->timeout_sec_config);
|
||||
else
|
||||
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
|
||||
}
|
||||
status = update_timeout_efivar(&config->timeout_sec_efivar, FALSE);
|
||||
break;
|
||||
|
||||
case KEYPRESS(0, 0, '+'):
|
||||
case KEYPRESS(0, 0, 't'):
|
||||
if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
|
||||
config->timeout_sec_efivar++;
|
||||
config->timeout_sec_efivar++;
|
||||
efivar_set_uint_string(
|
||||
LOADER_GUID,
|
||||
L"LoaderConfigTimeout",
|
||||
config->timeout_sec_efivar,
|
||||
EFI_VARIABLE_NON_VOLATILE);
|
||||
if (config->timeout_sec_efivar > 0)
|
||||
status = PoolPrint(L"Menu timeout set to %d s.",
|
||||
config->timeout_sec_efivar);
|
||||
else
|
||||
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
|
||||
status = update_timeout_efivar(&config->timeout_sec_efivar, TRUE);
|
||||
break;
|
||||
|
||||
case KEYPRESS(0, 0, 'e'):
|
||||
@ -888,9 +902,8 @@ static BOOLEAN menu_run(
|
||||
|
||||
*chosen_entry = config->entries[idx_highlight];
|
||||
|
||||
/* The user is likely to cycle through several modes before
|
||||
* deciding to keep one. Therefore, we update the EFI var after
|
||||
* we left the menu to reduce nvram writes. */
|
||||
/* Update EFI vars after we left the menu to reduce NVRAM writes. */
|
||||
|
||||
if (console_mode_efivar_saved != config->console_mode_efivar) {
|
||||
if (config->console_mode_efivar == CONSOLE_MODE_KEEP)
|
||||
efivar_set(LOADER_GUID, L"LoaderConfigConsoleMode", NULL, EFI_VARIABLE_NON_VOLATILE);
|
||||
@ -899,6 +912,14 @@ static BOOLEAN menu_run(
|
||||
config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE);
|
||||
}
|
||||
|
||||
if (timeout_efivar_saved != config->timeout_sec_efivar) {
|
||||
if (config->timeout_sec_efivar == TIMEOUT_UNSET)
|
||||
efivar_set(LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
|
||||
else
|
||||
efivar_set_uint_string(LOADER_GUID, L"LoaderConfigTimeout",
|
||||
config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
|
||||
}
|
||||
|
||||
for (UINTN i = 0; i < config->entry_count; i++)
|
||||
FreePool(lines[i]);
|
||||
FreePool(lines);
|
||||
@ -1023,10 +1044,16 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
|
||||
|
||||
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
|
||||
if (strcmpa((CHAR8 *)"timeout", key) == 0) {
|
||||
_cleanup_freepool_ CHAR16 *s = NULL;
|
||||
if (strcmpa((CHAR8*) "menu-force", value) == 0)
|
||||
config->timeout_sec_config = TIMEOUT_MENU_FORCE;
|
||||
else if (strcmpa((CHAR8*) "menu-hidden", value) == 0)
|
||||
config->timeout_sec_config = TIMEOUT_MENU_HIDDEN;
|
||||
else {
|
||||
_cleanup_freepool_ CHAR16 *s = NULL;
|
||||
|
||||
s = stra_to_str(value);
|
||||
config->timeout_sec_config = Atoi(s);
|
||||
s = stra_to_str(value);
|
||||
config->timeout_sec_config = MIN(Atoi(s), TIMEOUT_TYPE_MAX);
|
||||
}
|
||||
config->timeout_sec = config->timeout_sec_config;
|
||||
continue;
|
||||
}
|
||||
@ -1439,6 +1466,8 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
|
||||
.idx_default_efivar = -1,
|
||||
.console_mode = CONSOLE_MODE_KEEP,
|
||||
.console_mode_efivar = CONSOLE_MODE_KEEP,
|
||||
.timeout_sec_config = TIMEOUT_UNSET,
|
||||
.timeout_sec_efivar = TIMEOUT_UNSET,
|
||||
};
|
||||
|
||||
err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
|
||||
@ -1447,17 +1476,16 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
|
||||
|
||||
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
|
||||
if (!EFI_ERROR(err)) {
|
||||
config->timeout_sec_efivar = value > INTN_MAX ? INTN_MAX : value;
|
||||
config->timeout_sec = value;
|
||||
} else
|
||||
config->timeout_sec_efivar = -1;
|
||||
config->timeout_sec_efivar = MIN(value, TIMEOUT_TYPE_MAX);
|
||||
config->timeout_sec = config->timeout_sec_efivar;
|
||||
}
|
||||
|
||||
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
|
||||
if (!EFI_ERROR(err)) {
|
||||
/* Unset variable now, after all it's "one shot". */
|
||||
(void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
|
||||
|
||||
config->timeout_sec = value;
|
||||
config->timeout_sec = MIN(value, TIMEOUT_TYPE_MAX);
|
||||
config->force_menu = TRUE; /* force the menu when this is set */
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
#include "util.h"
|
||||
|
||||
/* magic string to find in the binary image */
|
||||
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
|
||||
_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
|
||||
|
||||
static EFI_STATUS combine_initrd(
|
||||
EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
|
||||
|
||||
@ -126,7 +126,7 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
|
||||
|
||||
DISABLE_WARNING_FORMAT_NONLITERAL;
|
||||
log_internalv(LOG_AUTH | callback_type_to_priority(type),
|
||||
0, PROJECT_FILE, __LINE__, __FUNCTION__,
|
||||
0, PROJECT_FILE, __LINE__, __func__,
|
||||
fmt2, ap);
|
||||
REENABLE_WARNING;
|
||||
va_end(ap);
|
||||
|
||||
@ -2131,6 +2131,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
|
||||
" subvolume, cifs)\n"
|
||||
" --image-path=PATH Path to image file/directory\n"
|
||||
" --drop-caches=BOOL Whether to automatically drop caches on logout\n"
|
||||
"\n%4$sLUKS Storage User Record Properties:%5$s\n"
|
||||
" --fs-type=TYPE File system type to use in case of luks\n"
|
||||
" storage (btrfs, ext4, xfs)\n"
|
||||
@ -2245,6 +2246,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_RECOVERY_KEY,
|
||||
ARG_AND_RESIZE,
|
||||
ARG_AND_CHANGE_PASSWORD,
|
||||
ARG_DROP_CACHES,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -2327,6 +2329,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
|
||||
{ "and-resize", required_argument, NULL, ARG_AND_RESIZE },
|
||||
{ "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
|
||||
{ "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -3450,6 +3453,26 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_and_change_password = true;
|
||||
break;
|
||||
|
||||
case ARG_DROP_CACHES: {
|
||||
bool drop_caches;
|
||||
|
||||
if (isempty(optarg)) {
|
||||
r = drop_from_identity("dropCaches");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = parse_boolean_argument("--drop-caches=", optarg, &drop_caches);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set drop caches field: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@ -38,6 +38,9 @@
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
|
||||
/* Retry to deactivate home directories again and again every 15s until it works */
|
||||
#define RETRY_DEACTIVATE_USEC (15U * USEC_PER_SEC)
|
||||
|
||||
#define HOME_USERS_MAX 500
|
||||
#define PENDING_OPERATIONS_MAX 100
|
||||
|
||||
@ -130,6 +133,8 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
|
||||
.worker_stdout_fd = -1,
|
||||
.sysfs = TAKE_PTR(ns),
|
||||
.signed_locally = -1,
|
||||
.pin_fd = -1,
|
||||
.luks_lock_fd = -1,
|
||||
};
|
||||
|
||||
r = hashmap_put(m->homes_by_name, home->user_name, home);
|
||||
@ -203,6 +208,11 @@ Home *home_free(Home *h) {
|
||||
|
||||
h->current_operation = operation_unref(h->current_operation);
|
||||
|
||||
safe_close(h->pin_fd);
|
||||
safe_close(h->luks_lock_fd);
|
||||
|
||||
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
|
||||
|
||||
return mfree(h);
|
||||
}
|
||||
|
||||
@ -317,6 +327,141 @@ int home_unlink_record(Home *h) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void home_unpin(Home *h) {
|
||||
assert(h);
|
||||
|
||||
if (h->pin_fd < 0)
|
||||
return;
|
||||
|
||||
h->pin_fd = safe_close(h->pin_fd);
|
||||
log_debug("Successfully closed pin fd on home for %s.", h->user_name);
|
||||
}
|
||||
|
||||
static void home_pin(Home *h) {
|
||||
const char *path;
|
||||
|
||||
assert(h);
|
||||
|
||||
if (h->pin_fd >= 0) /* Already pinned? */
|
||||
return;
|
||||
|
||||
path = user_record_home_directory(h->record);
|
||||
if (!path) {
|
||||
log_warning("No home directory path to pin for %s, ignoring.", h->user_name);
|
||||
return;
|
||||
}
|
||||
|
||||
h->pin_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (h->pin_fd < 0) {
|
||||
log_warning_errno(errno, "Couldn't open home directory '%s' for pinning, ignoring: %m", path);
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Successfully pinned home directory '%s'.", path);
|
||||
}
|
||||
|
||||
static void home_update_pin_fd(Home *h, HomeState state) {
|
||||
assert(h);
|
||||
|
||||
if (state < 0)
|
||||
state = home_get_state(h);
|
||||
|
||||
return HOME_STATE_SHALL_PIN(state) ? home_pin(h) : home_unpin(h);
|
||||
}
|
||||
|
||||
static void home_maybe_close_luks_lock_fd(Home *h, HomeState state) {
|
||||
assert(h);
|
||||
|
||||
if (h->luks_lock_fd < 0)
|
||||
return;
|
||||
|
||||
if (state < 0)
|
||||
state = home_get_state(h);
|
||||
|
||||
/* Keep the lock as long as the home dir is active or has some operation going */
|
||||
if (HOME_STATE_IS_EXECUTING_OPERATION(state) || HOME_STATE_IS_ACTIVE(state) || state == HOME_LOCKED)
|
||||
return;
|
||||
|
||||
h->luks_lock_fd = safe_close(h->luks_lock_fd);
|
||||
log_debug("Successfully closed LUKS backing file lock for %s.", h->user_name);
|
||||
}
|
||||
|
||||
static void home_maybe_stop_retry_deactivate(Home *h, HomeState state) {
|
||||
assert(h);
|
||||
|
||||
/* Free the deactivation retry event source if we won't need it anymore. Specifically, we'll free the
|
||||
* event source whenever the home directory is already deactivated (and we thus where successful) or
|
||||
* if we start executing an operation that indicates that the home directory is going to be used or
|
||||
* operated on again. Also, if the home is referenced again stop the timer */
|
||||
|
||||
if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) &&
|
||||
!h->ref_event_source_dont_suspend &&
|
||||
!h->ref_event_source_please_suspend)
|
||||
return;
|
||||
|
||||
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
|
||||
}
|
||||
|
||||
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error);
|
||||
static void home_start_retry_deactivate(Home *h);
|
||||
|
||||
static int home_on_retry_deactivate(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
Home *h = userdata;
|
||||
HomeState state;
|
||||
|
||||
assert(s);
|
||||
assert(h);
|
||||
|
||||
/* 15s after the last attempt to deactivate the home directory passed. Let's try it one more time. */
|
||||
|
||||
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
|
||||
|
||||
state = home_get_state(h);
|
||||
if (!HOME_STATE_MAY_RETRY_DEACTIVATE(state))
|
||||
return 0;
|
||||
|
||||
if (IN_SET(state, HOME_ACTIVE, HOME_LINGERING)) {
|
||||
log_info("Again trying to deactivate home directory.");
|
||||
|
||||
/* If we are not executing any operation, let's start deactivating now. Note that this will
|
||||
* restart our timer again, we are gonna be called again if this doesn't work. */
|
||||
(void) home_deactivate_internal(h, /* force= */ false, NULL);
|
||||
} else
|
||||
/* if we are executing an operation (specifically, area already running a deactivation
|
||||
* operation), then simply reque the timer, so that we retry again. */
|
||||
home_start_retry_deactivate(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void home_start_retry_deactivate(Home *h) {
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(h->manager);
|
||||
|
||||
/* Alrady allocated? */
|
||||
if (h->retry_deactivate_event_source)
|
||||
return;
|
||||
|
||||
/* If the home directory is being used now don't start the timer */
|
||||
if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
|
||||
return;
|
||||
|
||||
r = sd_event_add_time_relative(
|
||||
h->manager->event,
|
||||
&h->retry_deactivate_event_source,
|
||||
CLOCK_MONOTONIC,
|
||||
RETRY_DEACTIVATE_USEC,
|
||||
1*USEC_PER_MINUTE,
|
||||
home_on_retry_deactivate,
|
||||
h);
|
||||
if (r < 0)
|
||||
return (void) log_warning_errno(r, "Failed to install retry-deactivate event source, ignoring: %m");
|
||||
|
||||
(void) sd_event_source_set_description(h->retry_deactivate_event_source, "retry-deactivate");
|
||||
}
|
||||
|
||||
static void home_set_state(Home *h, HomeState state) {
|
||||
HomeState old_state, new_state;
|
||||
|
||||
@ -331,6 +476,10 @@ static void home_set_state(Home *h, HomeState state) {
|
||||
home_state_to_string(old_state),
|
||||
home_state_to_string(new_state));
|
||||
|
||||
home_update_pin_fd(h, new_state);
|
||||
home_maybe_close_luks_lock_fd(h, new_state);
|
||||
home_maybe_stop_retry_deactivate(h, new_state);
|
||||
|
||||
if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
|
||||
/* If we just finished executing some operation, process the queue of pending operations. And
|
||||
* enqueue it for GC too. */
|
||||
@ -483,6 +632,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
|
||||
case -EKEYREVOKED:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
|
||||
case -EADDRINUSE:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_IN_USE, "Home %s is currently being used elsewhere.", h->user_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1029,6 +1180,12 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If we haven't locked the device yet, ask for a lock to be taken and be passed back to us via sd_notify(). */
|
||||
if (setenv("SYSTEMD_LUKS_LOCK", one_zero(h->luks_lock_fd < 0), 1) < 0) {
|
||||
log_error_errno(errno, "Failed to set $SYSTEMD_LUKS_LOCK: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (h->manager->default_storage >= 0)
|
||||
if (setenv("SYSTEMD_HOME_DEFAULT_STORAGE", user_storage_to_string(h->manager->default_storage), 1) < 0) {
|
||||
log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_STORAGE: %m");
|
||||
@ -1149,6 +1306,7 @@ int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
case HOME_INACTIVE:
|
||||
case HOME_DIRTY:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
case HOME_LOCKED:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
|
||||
case HOME_UNFIXATED:
|
||||
@ -1190,6 +1348,11 @@ int home_activate(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
|
||||
case HOME_ACTIVE:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
|
||||
case HOME_LINGERING:
|
||||
/* If we are lingering, i.e. active but are supposed to be deactivated, then cancel this
|
||||
* timer if the user explicitly asks us to be active */
|
||||
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
|
||||
return 0;
|
||||
case HOME_LOCKED:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
|
||||
case HOME_INACTIVE:
|
||||
@ -1236,6 +1399,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
case HOME_INACTIVE:
|
||||
case HOME_DIRTY:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1245,7 +1409,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
|
||||
return home_authenticate_internal(h, secret, HOME_STATE_IS_ACTIVE(state) ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
|
||||
}
|
||||
|
||||
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
|
||||
@ -1253,12 +1417,22 @@ static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
|
||||
|
||||
assert(h);
|
||||
|
||||
home_unpin(h); /* unpin so that we can deactivate */
|
||||
|
||||
r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Operation failed before it even started, reacquire pin fd, if state still dictates so */
|
||||
home_update_pin_fd(h, _HOME_STATE_INVALID);
|
||||
else {
|
||||
home_set_state(h, HOME_DEACTIVATING);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
home_set_state(h, HOME_DEACTIVATING);
|
||||
return 0;
|
||||
/* Let's start a timer to retry deactivation in 15. We'll stop the timer once we manage to deactivate
|
||||
* the home directory again, or we we start any other operation. */
|
||||
home_start_retry_deactivate(h);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int home_deactivate(Home *h, bool force, sd_bus_error *error) {
|
||||
@ -1273,6 +1447,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error) {
|
||||
case HOME_LOCKED:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1309,6 +1484,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
case HOME_ABSENT:
|
||||
break;
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
case HOME_LOCKED:
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
|
||||
@ -1347,6 +1523,7 @@ int home_remove(Home *h, sd_bus_error *error) {
|
||||
case HOME_DIRTY:
|
||||
break;
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
|
||||
}
|
||||
@ -1485,6 +1662,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
|
||||
case HOME_INACTIVE:
|
||||
case HOME_DIRTY:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1498,7 +1676,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
|
||||
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1520,6 +1698,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
|
||||
case HOME_INACTIVE:
|
||||
case HOME_DIRTY:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1568,7 +1747,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
|
||||
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1616,6 +1795,7 @@ int home_passwd(Home *h,
|
||||
case HOME_INACTIVE:
|
||||
case HOME_DIRTY:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1681,7 +1861,7 @@ int home_passwd(Home *h,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
|
||||
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1700,6 +1880,7 @@ int home_unregister(Home *h, sd_bus_error *error) {
|
||||
case HOME_DIRTY:
|
||||
break;
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
|
||||
}
|
||||
@ -1727,6 +1908,7 @@ int home_lock(Home *h, sd_bus_error *error) {
|
||||
case HOME_LOCKED:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
break;
|
||||
default:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
|
||||
@ -1767,6 +1949,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
|
||||
case HOME_ABSENT:
|
||||
case HOME_INACTIVE:
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
case HOME_DIRTY:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
|
||||
case HOME_LOCKED:
|
||||
@ -1789,7 +1972,7 @@ HomeState home_get_state(Home *h) {
|
||||
/* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
|
||||
* directory is active */
|
||||
if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
|
||||
return HOME_ACTIVE;
|
||||
return h->retry_deactivate_event_source ? HOME_LINGERING : HOME_ACTIVE;
|
||||
|
||||
/* And if we see the image being gone, we report this as absent */
|
||||
r = user_record_test_image_path(h->record);
|
||||
@ -1802,28 +1985,49 @@ HomeState home_get_state(Home *h) {
|
||||
return HOME_INACTIVE;
|
||||
}
|
||||
|
||||
void home_process_notify(Home *h, char **l) {
|
||||
void home_process_notify(Home *h, char **l, int fd) {
|
||||
_cleanup_close_ int taken_fd = TAKE_FD(fd);
|
||||
const char *e;
|
||||
int error;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
|
||||
e = strv_env_get(l, "ERRNO");
|
||||
if (!e) {
|
||||
log_debug("Got notify message lacking ERRNO= field, ignoring.");
|
||||
e = strv_env_get(l, "SYSTEMD_LUKS_LOCK_FD");
|
||||
if (e) {
|
||||
r = parse_boolean(e);
|
||||
if (r < 0)
|
||||
return (void) log_debug_errno(r, "Failed to parse SYSTEMD_LUKS_LOCK_FD value: %m");
|
||||
if (r > 0) {
|
||||
if (taken_fd < 0)
|
||||
return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=1 but no fd passed, ignoring: %m");
|
||||
|
||||
safe_close(h->luks_lock_fd);
|
||||
h->luks_lock_fd = TAKE_FD(taken_fd);
|
||||
|
||||
log_debug("Successfully acquired LUKS lock fd from worker.");
|
||||
|
||||
/* Immediately check if we actually want to keep it */
|
||||
home_maybe_close_luks_lock_fd(h, _HOME_STATE_INVALID);
|
||||
} else {
|
||||
if (taken_fd >= 0)
|
||||
return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=0 but fd passed, ignoring: %m");
|
||||
|
||||
h->luks_lock_fd = safe_close(h->luks_lock_fd);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
e = strv_env_get(l, "ERRNO");
|
||||
if (!e)
|
||||
return (void) log_debug("Got notify message lacking both ERRNO= and SYSTEMD_LUKS_LOCK_FD= field, ignoring.");
|
||||
|
||||
r = safe_atoi(e, &error);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
|
||||
return;
|
||||
}
|
||||
if (error <= 0) {
|
||||
log_debug("Error number is out of range: %i", error);
|
||||
return;
|
||||
}
|
||||
if (r < 0)
|
||||
return (void) log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
|
||||
if (error <= 0)
|
||||
return (void) log_debug("Error number is out of range: %i", error);
|
||||
|
||||
h->worker_error_code = error;
|
||||
}
|
||||
@ -2372,6 +2576,7 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
|
||||
break;
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
|
||||
call = home_authenticate_internal;
|
||||
break;
|
||||
@ -2426,6 +2631,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
|
||||
break;
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
r = home_deactivate_internal(h, false, &error);
|
||||
break;
|
||||
|
||||
@ -2470,6 +2676,7 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
|
||||
break;
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
log_info("Locking home %s.", h->user_name);
|
||||
r = home_lock(h, &error);
|
||||
break;
|
||||
@ -2514,6 +2721,7 @@ static int home_dispatch_deactivate_all(Home *h, Operation *o) {
|
||||
break;
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
log_info("Deactivating home %s.", h->user_name);
|
||||
r = home_deactivate_internal(h, false, &error);
|
||||
break;
|
||||
@ -2559,6 +2767,7 @@ static int home_dispatch_pipe_eof(Home *h, Operation *o) {
|
||||
break;
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LINGERING:
|
||||
r = home_deactivate_internal(h, false, &error);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
|
||||
@ -2600,6 +2809,7 @@ static int home_dispatch_deactivate_force(Home *h, Operation *o) {
|
||||
|
||||
case HOME_ACTIVE:
|
||||
case HOME_LOCKED:
|
||||
case HOME_LINGERING:
|
||||
r = home_deactivate_internal(h, true, &error);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
|
||||
@ -2820,6 +3030,7 @@ static const char* const home_state_table[_HOME_STATE_MAX] = {
|
||||
[HOME_ACTIVATING_FOR_ACQUIRE] = "activating-for-acquire",
|
||||
[HOME_DEACTIVATING] = "deactivating",
|
||||
[HOME_ACTIVE] = "active",
|
||||
[HOME_LINGERING] = "lingering",
|
||||
[HOME_LOCKING] = "locking",
|
||||
[HOME_LOCKED] = "locked",
|
||||
[HOME_UNLOCKING] = "unlocking",
|
||||
|
||||
@ -21,6 +21,7 @@ typedef enum HomeState {
|
||||
HOME_ACTIVATING_FOR_ACQUIRE, /* activating because Acquire() was called */
|
||||
HOME_DEACTIVATING,
|
||||
HOME_ACTIVE, /* logged in right now */
|
||||
HOME_LINGERING, /* not logged in anymore, but we didn't manage to deactivate (because some process keeps it busy?) but we'll keep trying */
|
||||
HOME_LOCKING,
|
||||
HOME_LOCKED,
|
||||
HOME_UNLOCKING,
|
||||
@ -43,6 +44,7 @@ typedef enum HomeState {
|
||||
static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
|
||||
return IN_SET(state,
|
||||
HOME_ACTIVE,
|
||||
HOME_LINGERING,
|
||||
HOME_UPDATING_WHILE_ACTIVE,
|
||||
HOME_RESIZING_WHILE_ACTIVE,
|
||||
HOME_PASSWD_WHILE_ACTIVE,
|
||||
@ -74,6 +76,33 @@ static inline bool HOME_STATE_IS_EXECUTING_OPERATION(HomeState state) {
|
||||
HOME_AUTHENTICATING_FOR_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline bool HOME_STATE_SHALL_PIN(HomeState state) {
|
||||
/* Like HOME_STATE_IS_ACTIVE() – but HOME_LINGERING is missing! */
|
||||
return IN_SET(state,
|
||||
HOME_ACTIVE,
|
||||
HOME_UPDATING_WHILE_ACTIVE,
|
||||
HOME_RESIZING_WHILE_ACTIVE,
|
||||
HOME_PASSWD_WHILE_ACTIVE,
|
||||
HOME_AUTHENTICATING_WHILE_ACTIVE,
|
||||
HOME_AUTHENTICATING_FOR_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline bool HOME_STATE_MAY_RETRY_DEACTIVATE(HomeState state) {
|
||||
/* Indicates when to leave the deactivate retry timer active */
|
||||
return IN_SET(state,
|
||||
HOME_ACTIVE,
|
||||
HOME_LINGERING,
|
||||
HOME_DEACTIVATING,
|
||||
HOME_LOCKING,
|
||||
HOME_UNLOCKING,
|
||||
HOME_UNLOCKING_FOR_ACQUIRE,
|
||||
HOME_UPDATING_WHILE_ACTIVE,
|
||||
HOME_RESIZING_WHILE_ACTIVE,
|
||||
HOME_PASSWD_WHILE_ACTIVE,
|
||||
HOME_AUTHENTICATING_WHILE_ACTIVE,
|
||||
HOME_AUTHENTICATING_FOR_ACQUIRE);
|
||||
}
|
||||
|
||||
struct Home {
|
||||
Manager *manager;
|
||||
char *user_name;
|
||||
@ -126,6 +155,15 @@ struct Home {
|
||||
|
||||
/* Used to coalesce bus PropertiesChanged events */
|
||||
sd_event_source *deferred_change_event_source;
|
||||
|
||||
/* An fd to the top-level home directory we keep while logged in, to keep the dir busy */
|
||||
int pin_fd;
|
||||
|
||||
/* A time event used to repeatedly try to unmount home dir after use if it didn't work on first try */
|
||||
sd_event_source *retry_deactivate_event_source;
|
||||
|
||||
/* An fd that locks the backing file of LUKS home dirs with a BSD lock. */
|
||||
int luks_lock_fd;
|
||||
};
|
||||
|
||||
int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);
|
||||
@ -152,7 +190,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error);
|
||||
|
||||
HomeState home_get_state(Home *h);
|
||||
|
||||
void home_process_notify(Home *h, char **l);
|
||||
void home_process_notify(Home *h, char **l, int fd);
|
||||
|
||||
int home_killall(Home *h);
|
||||
|
||||
|
||||
@ -83,35 +83,38 @@ static void manager_watch_home(Manager *m) {
|
||||
m->inotify_event_source = sd_event_source_disable_unref(m->inotify_event_source);
|
||||
m->scan_slash_home = false;
|
||||
|
||||
if (statfs("/home/", &sfs) < 0) {
|
||||
if (statfs(get_home_root(), &sfs) < 0) {
|
||||
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to statfs() /home/ directory, disabling automatic scanning.");
|
||||
"Failed to statfs() %s directory, disabling automatic scanning.", get_home_root());
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_network_fs(&sfs)) {
|
||||
log_info("/home/ is a network file system, disabling automatic scanning.");
|
||||
log_info("%s is a network file system, disabling automatic scanning.", get_home_root());
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_fs_type(&sfs, AUTOFS_SUPER_MAGIC)) {
|
||||
log_info("/home/ is on autofs, disabling automatic scanning.");
|
||||
log_info("%s is on autofs, disabling automatic scanning.", get_home_root());
|
||||
return;
|
||||
}
|
||||
|
||||
m->scan_slash_home = true;
|
||||
|
||||
r = sd_event_add_inotify(m->event, &m->inotify_event_source, "/home/",
|
||||
r = sd_event_add_inotify(m->event, &m->inotify_event_source, get_home_root(),
|
||||
IN_CREATE|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF|IN_ONLYDIR|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE,
|
||||
on_home_inotify, m);
|
||||
if (r < 0)
|
||||
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
|
||||
"Failed to create inotify watch on /home/, ignoring.");
|
||||
"Failed to create inotify watch on %s, ignoring.", get_home_root());
|
||||
|
||||
(void) sd_event_source_set_description(m->inotify_event_source, "home-inotify");
|
||||
|
||||
log_info("Watching %s.", get_home_root());
|
||||
}
|
||||
|
||||
static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
Manager *m = userdata;
|
||||
const char *e, *n;
|
||||
|
||||
@ -121,15 +124,15 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
|
||||
if ((event->mask & (IN_Q_OVERFLOW|IN_MOVE_SELF|IN_DELETE_SELF|IN_IGNORED|IN_UNMOUNT)) != 0) {
|
||||
|
||||
if (FLAGS_SET(event->mask, IN_Q_OVERFLOW))
|
||||
log_debug("/home/ inotify queue overflow, rescanning.");
|
||||
log_debug("%s inotify queue overflow, rescanning.", get_home_root());
|
||||
else if (FLAGS_SET(event->mask, IN_MOVE_SELF))
|
||||
log_info("/home/ moved or renamed, recreating watch and rescanning.");
|
||||
log_info("%s moved or renamed, recreating watch and rescanning.", get_home_root());
|
||||
else if (FLAGS_SET(event->mask, IN_DELETE_SELF))
|
||||
log_info("/home/ deleted, recreating watch and rescanning.");
|
||||
log_info("%s deleted, recreating watch and rescanning.", get_home_root());
|
||||
else if (FLAGS_SET(event->mask, IN_UNMOUNT))
|
||||
log_info("/home/ unmounted, recreating watch and rescanning.");
|
||||
log_info("%s unmounted, recreating watch and rescanning.", get_home_root());
|
||||
else if (FLAGS_SET(event->mask, IN_IGNORED))
|
||||
log_info("/home/ watch invalidated, recreating watch and rescanning.");
|
||||
log_info("%s watch invalidated, recreating watch and rescanning.", get_home_root());
|
||||
|
||||
manager_watch_home(m);
|
||||
(void) manager_gc_images(m);
|
||||
@ -150,15 +153,19 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
|
||||
if (!suitable_user_name(n))
|
||||
return 0;
|
||||
|
||||
j = path_join(get_home_root(), event->name);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
if ((event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) != 0) {
|
||||
if (FLAGS_SET(event->mask, IN_CREATE))
|
||||
log_debug("/home/%s has been created, having a look.", event->name);
|
||||
log_debug("%s has been created, having a look.", j);
|
||||
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
|
||||
log_debug("/home/%s has been modified, having a look.", event->name);
|
||||
log_debug("%s has been modified, having a look.", j);
|
||||
else if (FLAGS_SET(event->mask, IN_MOVED_TO))
|
||||
log_debug("/home/%s has been moved in, having a look.", event->name);
|
||||
log_debug("%s has been moved in, having a look.", j);
|
||||
|
||||
(void) manager_assess_image(m, -1, "/home/", event->name);
|
||||
(void) manager_assess_image(m, -1, get_home_root(), event->name);
|
||||
(void) bus_manager_emit_auto_login_changed(m);
|
||||
}
|
||||
|
||||
@ -166,11 +173,11 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
|
||||
Home *h;
|
||||
|
||||
if (FLAGS_SET(event->mask, IN_DELETE))
|
||||
log_debug("/home/%s has been deleted, revalidating.", event->name);
|
||||
log_debug("%s has been deleted, revalidating.", j);
|
||||
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
|
||||
log_debug("/home/%s has been closed after writing, revalidating.", event->name);
|
||||
log_debug("%s has been closed after writing, revalidating.", j);
|
||||
else if (FLAGS_SET(event->mask, IN_MOVED_FROM))
|
||||
log_debug("/home/%s has been moved away, revalidating.", event->name);
|
||||
log_debug("%s has been moved away, revalidating.", j);
|
||||
|
||||
h = hashmap_get(m->homes_by_name, n);
|
||||
if (h) {
|
||||
@ -487,7 +494,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
|
||||
* comprehensive, but should cover most cases. Note that in an ideal world every user would be
|
||||
* registered in NSS and avoid our own UID range, but for all other cases, it's a good idea to be
|
||||
* paranoid and check quota if we can. */
|
||||
FOREACH_STRING(where, "/home/", "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
|
||||
FOREACH_STRING(where, get_home_root(), "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
|
||||
struct dqblk req;
|
||||
struct stat st;
|
||||
|
||||
@ -914,13 +921,13 @@ int manager_enumerate_images(Manager *m) {
|
||||
if (!m->scan_slash_home)
|
||||
return 0;
|
||||
|
||||
d = opendir("/home/");
|
||||
d = opendir(get_home_root());
|
||||
if (!d)
|
||||
return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
|
||||
"Failed to open /home/: %m");
|
||||
"Failed to open %s: %m", get_home_root());
|
||||
|
||||
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read /home/ directory: %m"))
|
||||
(void) manager_assess_image(m, dirfd(d), "/home", de->d_name);
|
||||
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s directory: %m", get_home_root()))
|
||||
(void) manager_assess_image(m, dirfd(d), get_home_root(), de->d_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1010,13 +1017,25 @@ static int manager_bind_varlink(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
|
||||
static ssize_t read_datagram(
|
||||
int fd,
|
||||
struct ucred *ret_sender,
|
||||
void **ret,
|
||||
int *ret_passed_fd) {
|
||||
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))) control;
|
||||
_cleanup_free_ void *buffer = NULL;
|
||||
_cleanup_close_ int passed_fd = -1;
|
||||
struct ucred *sender = NULL;
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
ssize_t n, m;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret_sender);
|
||||
assert(ret);
|
||||
assert(ret_passed_fd);
|
||||
|
||||
n = next_datagram_size_fd(fd);
|
||||
if (n < 0)
|
||||
@ -1026,58 +1045,54 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ret_sender) {
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
|
||||
bool found_ucred = false;
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
/* Pass one extra byte, as a size check */
|
||||
iov = IOVEC_MAKE(buffer, n + 1);
|
||||
|
||||
/* Pass one extra byte, as a size check */
|
||||
iov = IOVEC_MAKE(buffer, n + 1);
|
||||
mh = (struct msghdr) {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
|
||||
mh = (struct msghdr) {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
|
||||
m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (m < 0)
|
||||
return m;
|
||||
m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (m < 0)
|
||||
return m;
|
||||
|
||||
/* Ensure the size matches what we determined before */
|
||||
if (m != n) {
|
||||
cmsg_close_all(&mh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* Ensure the size matches what we determined before */
|
||||
if (m != n)
|
||||
return -EMSGSIZE;
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
assert(!sender);
|
||||
sender = (struct ucred*) CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh)
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
|
||||
memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||
found_ucred = true;
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
||||
cmsg_close_all(&mh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (!found_ucred)
|
||||
*ret_sender = (struct ucred) {
|
||||
.pid = 0,
|
||||
.uid = UID_INVALID,
|
||||
.gid = GID_INVALID,
|
||||
};
|
||||
} else {
|
||||
m = recv(fd, buffer, n + 1, MSG_DONTWAIT);
|
||||
if (m < 0)
|
||||
return -errno;
|
||||
|
||||
/* Ensure the size matches what we determined before */
|
||||
if (m != n)
|
||||
return -EMSGSIZE;
|
||||
assert(passed_fd < 0);
|
||||
passed_fd = *(int*) CMSG_DATA(cmsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (sender)
|
||||
*ret_sender = *sender;
|
||||
else
|
||||
*ret_sender = (struct ucred) UCRED_INVALID;
|
||||
|
||||
*ret_passed_fd = TAKE_FD(passed_fd);
|
||||
|
||||
/* For safety reasons: let's always NUL terminate. */
|
||||
((char*) buffer)[n] = 0;
|
||||
*ret = TAKE_PTR(buffer);
|
||||
@ -1088,7 +1103,8 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
|
||||
static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_free_ void *datagram = NULL;
|
||||
struct ucred sender;
|
||||
_cleanup_close_ int passed_fd = -1;
|
||||
struct ucred sender = UCRED_INVALID;
|
||||
Manager *m = userdata;
|
||||
ssize_t n;
|
||||
Home *h;
|
||||
@ -1096,7 +1112,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *
|
||||
assert(s);
|
||||
assert(m);
|
||||
|
||||
n = read_datagram(fd, &sender, &datagram);
|
||||
n = read_datagram(fd, &sender, &datagram, &passed_fd);
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
return 0;
|
||||
if (n < 0)
|
||||
@ -1117,7 +1133,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *
|
||||
if (!l)
|
||||
return log_oom();
|
||||
|
||||
home_process_notify(h, l);
|
||||
home_process_notify(h, l, TAKE_FD(passed_fd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -8,11 +8,14 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#include "blkid-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "chattr-util.h"
|
||||
#include "dm-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -1042,6 +1045,40 @@ int run_fallocate_by_path(const char *backing_path) {
|
||||
return run_fallocate(backing_fd, NULL);
|
||||
}
|
||||
|
||||
static int lock_image_fd(int image_fd, const char *ip) {
|
||||
int r;
|
||||
|
||||
/* If the $SYSTEMD_LUKS_LOCK environment variable is set we'll take an exclusive BSD lock on the
|
||||
* image file, and send it to our parent. homed will keep it open to ensure no other instance of
|
||||
* homed (across the network or such) will also mount the file. */
|
||||
|
||||
r = getenv_bool("SYSTEMD_LUKS_LOCK");
|
||||
if (r == -ENXIO)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m");
|
||||
if (r > 0) {
|
||||
if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) {
|
||||
|
||||
if (errno == EWOULDBLOCK)
|
||||
log_error_errno(errno, "Image file '%s' already locked, can't use.", ip);
|
||||
else
|
||||
log_error_errno(errno, "Failed to lock image file '%s': %m", ip);
|
||||
|
||||
return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */
|
||||
}
|
||||
|
||||
log_info("Successfully locked image file '%s'.", ip);
|
||||
|
||||
/* Now send it to our parent to keep safe while the home dir is active */
|
||||
r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int home_prepare_luks(
|
||||
UserRecord *h,
|
||||
bool already_activated,
|
||||
@ -1176,6 +1213,10 @@ int home_prepare_luks(
|
||||
S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
|
||||
"Image file %s is not a regular file or block device: %m", ip);
|
||||
|
||||
r = lock_image_fd(image_fd, ip);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = luks_validate(image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate disk label: %m");
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
#include "strv.h"
|
||||
#include "sync-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
@ -283,6 +284,20 @@ int user_record_authenticate(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drop_caches_now(void) {
|
||||
int r;
|
||||
|
||||
/* Drop file system caches now. See https://www.kernel.org/doc/Documentation/sysctl/vm.txt for
|
||||
* details. We write "2" into /proc/sys/vm/drop_caches to ensure dentries/inodes are flushed, but not
|
||||
* more. */
|
||||
|
||||
r = write_string_file("/proc/sys/vm/drop_caches", "2\n", WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to drop caches, ignoring: %m");
|
||||
else
|
||||
log_debug("Dropped caches.");
|
||||
}
|
||||
|
||||
int home_setup_undo(HomeSetup *setup) {
|
||||
int r = 0, q;
|
||||
|
||||
@ -295,6 +310,9 @@ int home_setup_undo(HomeSetup *setup) {
|
||||
r = q;
|
||||
}
|
||||
|
||||
if (syncfs(setup->root_fd) < 0)
|
||||
log_debug_errno(errno, "Failed to synchronize home directory, ignoring: %m");
|
||||
|
||||
setup->root_fd = safe_close(setup->root_fd);
|
||||
}
|
||||
|
||||
@ -345,6 +363,9 @@ int home_setup_undo(HomeSetup *setup) {
|
||||
setup->volume_key = mfree(setup->volume_key);
|
||||
setup->volume_key_size = 0;
|
||||
|
||||
if (setup->do_drop_caches)
|
||||
drop_caches_now();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -367,6 +388,9 @@ int home_prepare(
|
||||
|
||||
/* Makes a home directory accessible (through the root_fd file descriptor, not by path!). */
|
||||
|
||||
if (!already_activated) /* If we set up the directory, we should also drop caches once we are done */
|
||||
setup->do_drop_caches = setup->do_drop_caches || user_record_drop_caches(h);
|
||||
|
||||
switch (user_record_storage(h)) {
|
||||
|
||||
case USER_LUKS:
|
||||
@ -827,6 +851,13 @@ static int home_deactivate(UserRecord *h, bool force) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Sync explicitly, so that the drop caches logic below can work as documented */
|
||||
r = syncfs_path(AT_FDCWD, user_record_home_directory(h));
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to synchronize home directory, ignoring: %m");
|
||||
else
|
||||
log_info("Syncing completed.");
|
||||
|
||||
if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
|
||||
return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
|
||||
|
||||
@ -846,6 +877,9 @@ static int home_deactivate(UserRecord *h, bool force) {
|
||||
if (!done)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
|
||||
|
||||
if (user_record_drop_caches(h))
|
||||
drop_caches_now();
|
||||
|
||||
log_info("Everything completed.");
|
||||
return 0;
|
||||
}
|
||||
@ -1095,12 +1129,12 @@ static int determine_default_storage(UserStorage *ret) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine whether we are in a container: %m");
|
||||
if (r == 0) {
|
||||
r = path_is_encrypted("/home");
|
||||
r = path_is_encrypted(get_home_root());
|
||||
if (r > 0)
|
||||
log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS));
|
||||
log_info("%s is encrypted, not using '%s' storage, in order to avoid double encryption.", get_home_root(), user_storage_to_string(USER_LUKS));
|
||||
else {
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m");
|
||||
log_warning_errno(r, "Failed to determine if %s is encrypted, ignoring: %m", get_home_root());
|
||||
|
||||
r = dlopen_cryptsetup();
|
||||
if (r < 0)
|
||||
@ -1114,14 +1148,14 @@ static int determine_default_storage(UserStorage *ret) {
|
||||
} else
|
||||
log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS));
|
||||
|
||||
r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
|
||||
r = path_is_fs_type(get_home_root(), BTRFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m");
|
||||
log_warning_errno(r, "Failed to determine file system of %s, ignoring: %m", get_home_root());
|
||||
if (r > 0) {
|
||||
log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME));
|
||||
log_info("%s is on btrfs, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_SUBVOLUME));
|
||||
*ret = USER_SUBVOLUME;
|
||||
} else {
|
||||
log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY));
|
||||
log_info("%s is on simple file system, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_DIRECTORY));
|
||||
*ret = USER_DIRECTORY;
|
||||
}
|
||||
|
||||
@ -1268,9 +1302,21 @@ static int home_remove(UserRecord *h) {
|
||||
if (unlink(ip) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to remove %s: %m", ip);
|
||||
} else
|
||||
} else {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
|
||||
deleted = true;
|
||||
|
||||
r = path_extract_directory(ip, &parent);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine parent directory of '%s': %m", ip);
|
||||
else {
|
||||
r = fsync_path_at(AT_FDCWD, parent);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to synchronize disk after deleting '%s', ignoring: %m", ip);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (S_ISBLK(st.st_mode))
|
||||
log_info("Not removing file system on block device %s.", ip);
|
||||
else
|
||||
@ -1285,7 +1331,7 @@ static int home_remove(UserRecord *h) {
|
||||
case USER_FSCRYPT:
|
||||
assert(ip);
|
||||
|
||||
r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||
r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_SYNCFS);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_warning_errno(r, "Failed to remove %s: %m", ip);
|
||||
@ -1316,9 +1362,12 @@ static int home_remove(UserRecord *h) {
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
if (deleted) {
|
||||
if (user_record_drop_caches(h))
|
||||
drop_caches_now();
|
||||
|
||||
log_info("Everything completed.");
|
||||
else
|
||||
} else
|
||||
return log_notice_errno(SYNTHETIC_ERRNO(EALREADY),
|
||||
"Nothing to remove.");
|
||||
|
||||
@ -1706,6 +1755,7 @@ static int run(int argc, char *argv[]) {
|
||||
* ENOEXEC → file system is currently not active
|
||||
* ENOSPC → not enough disk space for operation
|
||||
* EKEYREVOKED → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
|
||||
* EADDRINUSE → home image is already used elsewhere (lock taken)
|
||||
*/
|
||||
|
||||
if (streq(argv[1], "activate"))
|
||||
|
||||
@ -32,6 +32,7 @@ typedef struct HomeSetup {
|
||||
bool do_offline_fitrim;
|
||||
bool do_offline_fallocate;
|
||||
bool do_mark_clean;
|
||||
bool do_drop_caches;
|
||||
|
||||
uint64_t partition_offset;
|
||||
uint64_t partition_size;
|
||||
|
||||
@ -75,7 +75,7 @@ int user_record_synthesize(
|
||||
if (!ip)
|
||||
return -ENOMEM;
|
||||
|
||||
hd = path_join("/home/", user_name);
|
||||
hd = path_join(get_home_root(), user_name);
|
||||
if (!hd)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include "syslog-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
|
||||
#define STDOUT_STREAMS_MAX 4096
|
||||
|
||||
@ -663,6 +664,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
|
||||
*stream = (StdoutStream) {
|
||||
.fd = -1,
|
||||
.priority = LOG_INFO,
|
||||
.ucred = UCRED_INVALID,
|
||||
};
|
||||
|
||||
xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
|
||||
@ -727,9 +729,9 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
|
||||
}
|
||||
|
||||
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
|
||||
struct ucred u;
|
||||
struct ucred u = UCRED_INVALID;
|
||||
|
||||
r = getpeercred(fd, &u);
|
||||
(void) getpeercred(fd, &u);
|
||||
|
||||
/* By closing fd here we make sure that the client won't wait too long for journald to
|
||||
* gather all the data it adds to the error message to find out that the connection has
|
||||
@ -737,7 +739,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
|
||||
*/
|
||||
fd = safe_close(fd);
|
||||
|
||||
server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
|
||||
server_driver_message(s, u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -207,7 +207,7 @@ int dhcp_identifier_set_iaid(
|
||||
if (legacy_unstable_byteorder)
|
||||
/* for historical reasons (a bug), the bits were swapped and thus
|
||||
* the result was endianness dependent. Preserve that behavior. */
|
||||
id32 = __bswap_32(id32);
|
||||
id32 = bswap_32(id32);
|
||||
else
|
||||
/* the fixed behavior returns a stable byte order. Since LE is expected
|
||||
* to be more common, swap the bytes on LE to give the same as legacy
|
||||
|
||||
@ -45,7 +45,7 @@ static void test_request_basic(sd_event *e) {
|
||||
sd_dhcp_client *client;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
/* Initialize client without Anonymize settings. */
|
||||
r = sd_dhcp_client_new(&client, false);
|
||||
@ -105,7 +105,7 @@ static void test_request_anonymize(sd_event *e) {
|
||||
sd_dhcp_client *client;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
/* Initialize client with Anonymize settings. */
|
||||
r = sd_dhcp_client_new(&client, true);
|
||||
@ -137,7 +137,7 @@ static void test_checksum(void) {
|
||||
};
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
|
||||
}
|
||||
@ -158,7 +158,7 @@ static void test_dhcp_identifier_set_iaid(void) {
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
assert_se(iaid == iaid_legacy);
|
||||
#else
|
||||
assert_se(iaid == __bswap_32(iaid_legacy));
|
||||
assert_se(iaid == bswap_32(iaid_legacy));
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ static void test_discover_message(sd_event *e) {
|
||||
int res, r;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
r = sd_dhcp_client_new(&client, false);
|
||||
assert_se(r >= 0);
|
||||
@ -497,7 +497,7 @@ static void test_addr_acq(sd_event *e) {
|
||||
int res, r;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
r = sd_dhcp_client_new(&client, false);
|
||||
assert_se(r >= 0);
|
||||
|
||||
@ -81,7 +81,7 @@ static void test_public_api_setters(sd_event *e) {
|
||||
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(sd_ipv4ll_new(&ll) == 0);
|
||||
assert_se(ll);
|
||||
@ -130,7 +130,7 @@ static void test_basic_request(sd_event *e) {
|
||||
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(sd_ipv4ll_new(&ll) == 0);
|
||||
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
|
||||
|
||||
@ -111,7 +111,7 @@ static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
|
||||
static void test_radv_prefix(void) {
|
||||
sd_radv_prefix *p;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(sd_radv_prefix_new(&p) >= 0);
|
||||
|
||||
@ -153,7 +153,7 @@ static void test_radv_prefix(void) {
|
||||
static void test_radv(void) {
|
||||
sd_radv *ra;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(sd_radv_new(&ra) >= 0);
|
||||
assert_se(ra);
|
||||
@ -295,7 +295,7 @@ static void test_ra(void) {
|
||||
sd_radv *ra;
|
||||
unsigned i;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
|
||||
|
||||
|
||||
@ -273,7 +273,7 @@ static void test_rs(void) {
|
||||
sd_ndisc *nd;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
send_ra_function = send_ra;
|
||||
|
||||
@ -366,7 +366,7 @@ static void test_timeout(void) {
|
||||
sd_ndisc *nd;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
printf("* %s\n", __func__);
|
||||
|
||||
send_ra_function = test_timeout_value;
|
||||
|
||||
|
||||
@ -142,6 +142,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
|
||||
|
||||
SD_BUS_ERROR_MAP_END
|
||||
};
|
||||
|
||||
@ -126,5 +126,6 @@
|
||||
#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
|
||||
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
|
||||
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
|
||||
#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
|
||||
|
||||
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
|
||||
|
||||
@ -249,6 +249,7 @@ _public_ int sd_bus_new(sd_bus **ret) {
|
||||
.original_pid = getpid_cached(),
|
||||
.n_groups = SIZE_MAX,
|
||||
.close_on_exit = true,
|
||||
.ucred = UCRED_INVALID,
|
||||
};
|
||||
|
||||
/* We guarantee that wqueue always has space for at least one entry */
|
||||
|
||||
@ -136,7 +136,7 @@ _public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) {
|
||||
}
|
||||
|
||||
_public_ int sd_peer_get_session(int fd, char **session) {
|
||||
struct ucred ucred = {};
|
||||
struct ucred ucred = UCRED_INVALID;
|
||||
int r;
|
||||
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
|
||||
@ -560,7 +560,7 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char
|
||||
|
||||
fmt = strjoina("libxkbcommon: ", format);
|
||||
DISABLE_WARNING_FORMAT_NONLITERAL;
|
||||
log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
|
||||
log_internalv(LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__, fmt, args);
|
||||
REENABLE_WARNING;
|
||||
}
|
||||
|
||||
|
||||
@ -756,7 +756,7 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
|
||||
static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
|
||||
struct ecmd {
|
||||
struct ethtool_link_settings req;
|
||||
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
||||
uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
||||
} ecmd = {
|
||||
.req.cmd = ETHTOOL_GLINKSETTINGS,
|
||||
};
|
||||
@ -857,7 +857,7 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
|
||||
static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
|
||||
struct {
|
||||
struct ethtool_link_settings req;
|
||||
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
||||
uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
||||
} ecmd = {};
|
||||
unsigned offset;
|
||||
|
||||
|
||||
@ -250,6 +250,9 @@ int rm_rf_children(
|
||||
ret = r;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
|
||||
ret = -errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ typedef enum RemoveFlags {
|
||||
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
|
||||
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete or access something */
|
||||
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
|
||||
REMOVE_SYNCFS = 1 << 7, /* syncfs() the root of the specified directory after removing everything in it */
|
||||
} RemoveFlags;
|
||||
|
||||
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags);
|
||||
|
||||
@ -435,6 +435,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
|
||||
if (hr->password_change_now >= 0)
|
||||
printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
|
||||
|
||||
if (hr->drop_caches >= 0 || user_record_drop_caches(hr))
|
||||
printf(" Drop Caches: %s\n", yes_no(user_record_drop_caches(hr)));
|
||||
|
||||
if (!strv_isempty(hr->ssh_authorized_keys))
|
||||
printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
|
||||
|
||||
|
||||
@ -202,6 +202,7 @@ UserRecord* user_record_new(void) {
|
||||
.pkcs11_protected_authentication_path_permitted = -1,
|
||||
.fido2_user_presence_permitted = -1,
|
||||
.fido2_user_verification_permitted = -1,
|
||||
.drop_caches = -1,
|
||||
};
|
||||
|
||||
return h;
|
||||
@ -1284,6 +1285,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
|
||||
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
|
||||
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
|
||||
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
|
||||
{ "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
|
||||
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
|
||||
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
|
||||
{ "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
|
||||
@ -1406,11 +1408,11 @@ int user_record_build_image_path(UserStorage storage, const char *user_name_and_
|
||||
return 0;
|
||||
}
|
||||
|
||||
z = strjoin("/home/", user_name_and_realm, suffix);
|
||||
z = strjoin(get_home_root(), "/", user_name_and_realm, suffix);
|
||||
if (!z)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = z;
|
||||
*ret = path_simplify(z);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1435,7 +1437,7 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
|
||||
return 0;
|
||||
|
||||
if (!h->home_directory && !h->home_directory_auto) {
|
||||
h->home_directory_auto = path_join("/home/", h->user_name);
|
||||
h->home_directory_auto = path_join(get_home_root(), h->user_name);
|
||||
if (!h->home_directory_auto)
|
||||
return json_log_oom(h->json, json_flags);
|
||||
}
|
||||
@ -1620,7 +1622,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
|
||||
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
|
||||
{ "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
|
||||
{ "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 },
|
||||
{ "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
|
||||
{ "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
|
||||
{ "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
|
||||
{ "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
|
||||
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
|
||||
@ -1629,6 +1631,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
|
||||
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
|
||||
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
|
||||
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
|
||||
{ "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
|
||||
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
|
||||
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
|
||||
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
|
||||
@ -2021,6 +2024,16 @@ bool user_record_can_authenticate(UserRecord *h) {
|
||||
return !strv_isempty(h->hashed_password);
|
||||
}
|
||||
|
||||
bool user_record_drop_caches(UserRecord *h) {
|
||||
assert(h);
|
||||
|
||||
if (h->drop_caches >= 0)
|
||||
return h->drop_caches;
|
||||
|
||||
/* By default drop caches on fscrypt, not otherwise. */
|
||||
return user_record_storage(h) == USER_FSCRYPT;
|
||||
}
|
||||
|
||||
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
|
||||
assert(h);
|
||||
|
||||
|
||||
@ -353,6 +353,7 @@ typedef struct UserRecord {
|
||||
int removable;
|
||||
int enforce_password_policy;
|
||||
int auto_login;
|
||||
int drop_caches;
|
||||
|
||||
uint64_t stop_delay_usec; /* How long to leave systemd --user around on log-out */
|
||||
int kill_processes; /* Whether to kill user processes forcibly on log-out */
|
||||
@ -419,6 +420,7 @@ int user_record_removable(UserRecord *h);
|
||||
usec_t user_record_ratelimit_interval_usec(UserRecord *h);
|
||||
uint64_t user_record_ratelimit_burst(UserRecord *h);
|
||||
bool user_record_can_authenticate(UserRecord *h);
|
||||
bool user_record_drop_caches(UserRecord *h);
|
||||
|
||||
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);
|
||||
|
||||
|
||||
@ -258,8 +258,7 @@ static int varlink_new(Varlink **ret) {
|
||||
|
||||
.state = _VARLINK_STATE_INVALID,
|
||||
|
||||
.ucred.uid = UID_INVALID,
|
||||
.ucred.gid = GID_INVALID,
|
||||
.ucred = UCRED_INVALID,
|
||||
|
||||
.timestamp = USEC_INFINITY,
|
||||
.timeout = VARLINK_DEFAULT_TIMEOUT_USEC
|
||||
@ -2077,7 +2076,7 @@ static int validate_connection(VarlinkServer *server, const struct ucred *ucred)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int count_connection(VarlinkServer *server, struct ucred *ucred) {
|
||||
static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
|
||||
unsigned c;
|
||||
int r;
|
||||
|
||||
@ -2106,8 +2105,8 @@ static int count_connection(VarlinkServer *server, struct ucred *ucred) {
|
||||
|
||||
int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret) {
|
||||
_cleanup_(varlink_unrefp) Varlink *v = NULL;
|
||||
struct ucred ucred = UCRED_INVALID;
|
||||
bool ucred_acquired;
|
||||
struct ucred ucred;
|
||||
int r;
|
||||
|
||||
assert_return(server, -EINVAL);
|
||||
|
||||
@ -120,6 +120,7 @@ static int recurse_dir_callback(
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_strv_free_ char **list_recurse_dir = NULL;
|
||||
const char *p;
|
||||
usec_t t1, t2, t3, t4;
|
||||
int r;
|
||||
|
||||
log_show_color(true);
|
||||
@ -131,14 +132,20 @@ int main(int argc, char *argv[]) {
|
||||
p = "/usr/share/man"; /* something hopefully reasonably stable while we run (and limited in size) */
|
||||
|
||||
/* Enumerate the specified dirs in full, once via nftw(), and once via recurse_dir(), and ensure the results are identical */
|
||||
t1 = now(CLOCK_MONOTONIC);
|
||||
r = recurse_dir_at(AT_FDCWD, p, 0, UINT_MAX, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE|RECURSE_DIR_SAME_MOUNT, recurse_dir_callback, &list_recurse_dir);
|
||||
t2 = now(CLOCK_MONOTONIC);
|
||||
if (r == -ENOENT) {
|
||||
log_warning_errno(r, "Couldn't open directory %s, ignoring: %m", p);
|
||||
return EXIT_TEST_SKIP;
|
||||
}
|
||||
assert_se(r >= 0);
|
||||
|
||||
t3 = now(CLOCK_MONOTONIC);
|
||||
assert_se(nftw(p, nftw_cb, 64, FTW_PHYS|FTW_MOUNT) >= 0);
|
||||
t4 = now(CLOCK_MONOTONIC);
|
||||
|
||||
log_info("recurse_dir(): %s – nftw(): %s", FORMAT_TIMESPAN(t2 - t1, 1), FORMAT_TIMESPAN(t4 - t3, 1));
|
||||
|
||||
strv_sort(list_recurse_dir);
|
||||
strv_sort(list_nftw);
|
||||
|
||||
@ -237,7 +237,7 @@ static int scsi_dump(struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) {
|
||||
*/
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: called with no error",
|
||||
__FUNCTION__);
|
||||
__func__);
|
||||
|
||||
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x",
|
||||
dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status);
|
||||
@ -255,7 +255,7 @@ static int scsi_dump_v4(struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) {
|
||||
*/
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: called with no error",
|
||||
__FUNCTION__);
|
||||
__func__);
|
||||
|
||||
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x",
|
||||
dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user