From a995ce4768928843b4d37ecf46f32fe9e635e38a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 11 Oct 2021 15:25:14 +0200 Subject: [PATCH 1/9] util: define initializer for 'struct ucred' that properly invalidates all fields i.e. let's make sure to invalid uid/gid to UID_INVAID + GID_INVALID instead of zero. --- src/basic/socket-util.c | 2 +- src/basic/socket-util.h | 3 +++ src/journal/journald-stream.c | 8 +++++--- src/libsystemd/sd-bus/sd-bus.c | 1 + src/libsystemd/sd-login/sd-login.c | 2 +- src/shared/varlink.c | 5 ++--- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 1e66f8700b..94ae90929a 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -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 */ diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index cb4a92236f..c4fafa084b 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -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 } diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 0a90091a86..cbff5036a4 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -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; } diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 80f2bdd87f..d8a7c81764 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -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 */ diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index d127443c4c..4a35e61425 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -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); diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 07a1b96f60..984dea1ff5 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -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 @@ -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); From 678ca2133cd65c8f4e273b45df6dae4c6f5e6303 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 11 Oct 2021 15:37:18 +0200 Subject: [PATCH 2/9] varlink: make one more parameter const --- src/shared/varlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 984dea1ff5..c34a08cf57 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -2076,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; From bdfe7ada0d4d66e6d6e65f2822acbb1ec230f9c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 10:32:56 +0200 Subject: [PATCH 3/9] rm-rf: optionally fsync() after removing directory tree --- src/shared/rm-rf.c | 3 +++ src/shared/rm-rf.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index d9cd8fb2b2..a8693660fa 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -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; } diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h index 577a2795e0..24fd9a2aa2 100644 --- a/src/shared/rm-rf.h +++ b/src/shared/rm-rf.h @@ -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); From 0c71e3ef247aaa74dde6f3e017c4478c6a1ed7e4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 28 Aug 2021 07:36:25 +0200 Subject: [PATCH 4/9] homed: keep "pinning" fd open while home dir active The pin fd keeps the mount busy, ensuring that unmount requests need to go through us. Note that this doesn't change too much IRL, since a logged in user generally has processes keeping the home dir busy anyway. However, in some corner cases it is safer to protect from accidental unmounts this way. (e.g. if user manually called "homectl activate" first). --- src/home/homed-home.c | 54 ++++++++++++++++++++++++++++++++++++++++++- src/home/homed-home.h | 3 +++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/home/homed-home.c b/src/home/homed-home.c index bbdc6940f3..41f8ea1299 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -130,6 +130,7 @@ 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, }; r = hashmap_put(m->homes_by_name, home->user_name, home); @@ -203,6 +204,8 @@ Home *home_free(Home *h) { h->current_operation = operation_unref(h->current_operation); + safe_close(h->pin_fd); + return mfree(h); } @@ -317,6 +320,48 @@ 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_IS_ACTIVE(state) ? home_pin(h) : home_unpin(h); +} + static void home_set_state(Home *h, HomeState state) { HomeState old_state, new_state; @@ -331,6 +376,8 @@ 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); + 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. */ @@ -1253,9 +1300,14 @@ 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) + if (r < 0) { + /* Operation failed before it even started, reacquire pin fd, if state still dictates so */ + home_update_pin_fd(h, _HOME_STATE_INVALID); return r; + } home_set_state(h, HOME_DEACTIVATING); return 0; diff --git a/src/home/homed-home.h b/src/home/homed-home.h index 7f531a234c..cd3b01ba72 100644 --- a/src/home/homed-home.h +++ b/src/home/homed-home.h @@ -126,6 +126,9 @@ 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; }; int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret); From 23cff6d4febea714b854996193f8ab93643ef5ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 30 Aug 2021 13:23:20 +0200 Subject: [PATCH 5/9] homed: retry deactivation every 15s until successful Fixes: #17445 --- src/home/homed-home.c | 130 ++++++++++++++++++++++++++++++++++++++---- src/home/homed-home.h | 32 +++++++++++ 2 files changed, 152 insertions(+), 10 deletions(-) diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 41f8ea1299..0260b8041c 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -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 @@ -206,6 +209,8 @@ Home *home_free(Home *h) { safe_close(h->pin_fd); + h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source); + return mfree(h); } @@ -359,7 +364,83 @@ static void home_update_pin_fd(Home *h, HomeState state) { if (state < 0) state = home_get_state(h); - return HOME_STATE_IS_ACTIVE(state) ? home_pin(h) : home_unpin(h); + return HOME_STATE_SHALL_PIN(state) ? home_pin(h) : home_unpin(h); +} + +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) { @@ -377,6 +458,7 @@ static void home_set_state(Home *h, HomeState state) { home_state_to_string(new_state)); home_update_pin_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 @@ -1196,6 +1278,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: @@ -1237,6 +1320,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: @@ -1283,6 +1371,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); @@ -1292,7 +1381,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) { @@ -1303,14 +1392,19 @@ static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) { home_unpin(h); /* unpin so that we can deactivate */ r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL); - if (r < 0) { + if (r < 0) /* Operation failed before it even started, reacquire pin fd, if state still dictates so */ home_update_pin_fd(h, _HOME_STATE_INVALID); - return r; + 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) { @@ -1325,6 +1419,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); @@ -1361,6 +1456,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); @@ -1399,6 +1495,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); } @@ -1537,6 +1634,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); @@ -1550,7 +1648,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; } @@ -1572,6 +1670,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); @@ -1620,7 +1719,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; } @@ -1668,6 +1767,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); @@ -1733,7 +1833,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; } @@ -1752,6 +1852,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); } @@ -1779,6 +1880,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); @@ -1819,6 +1921,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: @@ -1841,7 +1944,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); @@ -2424,6 +2527,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; @@ -2478,6 +2582,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; @@ -2522,6 +2627,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; @@ -2566,6 +2672,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; @@ -2611,6 +2718,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)); @@ -2652,6 +2760,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)); @@ -2872,6 +2981,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", diff --git a/src/home/homed-home.h b/src/home/homed-home.h index cd3b01ba72..e5a70e4092 100644 --- a/src/home/homed-home.h +++ b/src/home/homed-home.h @@ -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; @@ -129,6 +158,9 @@ struct Home { /* 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; }; int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret); From 2aaf565a2da8eb0ea9adf84028c9d0ab7a90e53e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 10:26:25 +0200 Subject: [PATCH 6/9] homed: take BSD file lock on LUKS file while activated Fixes: #19758 --- src/home/homed-home.c | 73 ++++++++++++++--- src/home/homed-home.h | 5 +- src/home/homed-manager.c | 99 ++++++++++++----------- src/home/homework-luks.c | 41 ++++++++++ src/home/homework.c | 1 + src/libsystemd/sd-bus/bus-common-errors.c | 1 + src/libsystemd/sd-bus/bus-common-errors.h | 1 + 7 files changed, 163 insertions(+), 58 deletions(-) diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 0260b8041c..d8f192650e 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -134,6 +134,7 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) { .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); @@ -208,6 +209,7 @@ 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); @@ -367,6 +369,23 @@ static void home_update_pin_fd(Home *h, HomeState state) { 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); @@ -458,6 +477,7 @@ static void home_set_state(Home *h, HomeState 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)) { @@ -612,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; @@ -1158,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"); @@ -1957,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; } diff --git a/src/home/homed-home.h b/src/home/homed-home.h index e5a70e4092..7cd8a9edb4 100644 --- a/src/home/homed-home.h +++ b/src/home/homed-home.h @@ -161,6 +161,9 @@ struct Home { /* 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); @@ -187,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); diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 070fd97d69..39782304f6 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -1010,13 +1010,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 +1038,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 +1096,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 +1105,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 +1126,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; } diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index c9c2476ed6..30b63e3481 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -8,11 +8,14 @@ #include #include +#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"); diff --git a/src/home/homework.c b/src/home/homework.c index ee1d4068ba..d35712c351 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -1706,6 +1706,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")) diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 43f9a7bdaa..61c5509e1c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -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 }; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index fd8f0c240e..348cd5094b 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -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); From 86019efa4416d7b548cab321c15bc22a65463786 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Oct 2021 10:32:25 +0200 Subject: [PATCH 7/9] homed: optionally, drop caches on logout Fixes: #20857 --- man/homectl.xml | 11 +++++++ src/home/homectl.c | 23 ++++++++++++++ src/home/homework.c | 57 ++++++++++++++++++++++++++++++++--- src/home/homework.h | 1 + src/shared/user-record-show.c | 3 ++ src/shared/user-record.c | 15 ++++++++- src/shared/user-record.h | 2 ++ 7 files changed, 107 insertions(+), 5 deletions(-) diff --git a/man/homectl.xml b/man/homectl.xml index 245ebcee00..c2b1ec6c9b 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -610,6 +610,17 @@ node is not allowed if any of the other storage backends are used. + + BOOL + + 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. + + TYPE diff --git a/src/home/homectl.c b/src/home/homectl.c index d5b699a242..3af1381eb4 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -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; diff --git a/src/home/homework.c b/src/home/homework.c index d35712c351..49533b2422 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -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; } @@ -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."); diff --git a/src/home/homework.h b/src/home/homework.h index fb53fd49b0..f20a23a918 100644 --- a/src/home/homework.h +++ b/src/home/homework.h @@ -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; diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index 29aa5c0c7c..dee6c4e5ee 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -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)); diff --git a/src/shared/user-record.c b/src/shared/user-record.c index f4e509e13e..44f629a910 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -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 }, @@ -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); diff --git a/src/shared/user-record.h b/src/shared/user-record.h index fa58dfdb6e..975e3e175b 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.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); From 2700fecdb33058a4a6119880978f92a01499f31a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Oct 2021 22:20:25 +0200 Subject: [PATCH 8/9] homed: allow overriding the root directory for home dirs via env var (i.e. use a different path than /home/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a debugging feature. It's sometimes incredibly useful to be able to run a second instance of homed that operates on another dir than /home/. Specifically, if you build homed from the source tree you can now run an instance of it pretty reasonably directly from the build tree via: sudo SYSTEMD_HOME_DEBUG_SUFFIX=foo SYSTEMD_HOMEWORK_PATH=$(pwd)/build/systemd-homework SYSTEMD_HOME_ROOT=/home/foo ./build/systemd-homed And then talk to it via sudo SYSTEMD_HOME_DEBUG_SUFFIX=foo homectl … (you might need to tweak your dbus policy for this to work fully though) --- src/basic/user-util.c | 11 ++++++++ src/basic/user-util.h | 2 ++ src/home/homed-manager.c | 53 +++++++++++++++++++++---------------- src/home/homework.c | 14 +++++----- src/home/user-record-util.c | 2 +- src/shared/user-record.c | 6 ++--- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 385ec514b4..aee2d9dc59 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -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"; +} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index fd00b47b76..ab1ce48b2d 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -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 "!*" diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 39782304f6..38283ff1ed 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -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; } diff --git a/src/home/homework.c b/src/home/homework.c index 49533b2422..ae3fffed02 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -1129,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) @@ -1148,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; } diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c index bc0c4171b4..464f1dbb7a 100644 --- a/src/home/user-record-util.c +++ b/src/home/user-record-util.c @@ -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; diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 44f629a910..9b2029bfcf 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -1408,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; } @@ -1437,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); } From c4fb47365cbd0fc572038d9bdad3837c79566288 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 28 Aug 2021 07:39:47 +0200 Subject: [PATCH 9/9] update TODO --- TODO | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TODO b/TODO index f670370a8e..efef0a5a37 100644 --- a/TODO +++ b/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