homed: Create & advertise blob directory

This ensures that a user-specific blob directory exists in
/var/cache/systemd/homed for as long as the user exists, and gets
deleted if the user gets deleted.

It also advertises this blob directory via the user record, so that
clients can find and use it.
This commit is contained in:
Adrian Vovk 2024-01-08 18:37:52 -05:00 committed by Luca Boccassi
parent 1b466c0940
commit c3d50255fc
7 changed files with 70 additions and 2 deletions

View File

@ -46,6 +46,10 @@
url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and thus may be
browsed with
<citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<para><filename>systemd-homed.service</filename> also manages blob directories for each home directory
it manages. See <ulink url="https://systemd.io/USER_RECORD_BLOB_DIRS">User Record Blob Directories</ulink>
for more details.</para>
</refsect1>
<refsect1>

View File

@ -137,3 +137,7 @@ int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
const char *home_record_dir(void) {
return secure_getenv("SYSTEMD_HOME_RECORD_DIR") ?: "/var/lib/systemd/home/";
}
const char *home_system_blob_dir(void) {
return secure_getenv("SYSTEMD_HOME_SYSTEM_BLOB_DIR") ?: "/var/cache/systemd/home/";
}

View File

@ -35,3 +35,4 @@ int bus_message_append_secret(sd_bus_message *m, UserRecord *secret);
#define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
const char *home_record_dir(void);
const char *home_system_blob_dir(void);

View File

@ -33,6 +33,7 @@
#include "process-util.h"
#include "quota-util.h"
#include "resize-fs.h"
#include "rm-rf.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
@ -96,7 +97,7 @@ static int suitable_home_record(UserRecord *hr) {
int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
_cleanup_(home_freep) Home *home = NULL;
_cleanup_free_ char *nm = NULL, *ns = NULL;
_cleanup_free_ char *nm = NULL, *ns = NULL, *blob = NULL;
int r;
assert(m);
@ -162,6 +163,13 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
if (r < 0)
return r;
blob = path_join(home_system_blob_dir(), hr->user_name);
if (!blob)
return -ENOMEM;
r = mkdir_safe(blob, 0755, 0, 0, MKDIR_IGNORE_EXISTING);
if (r < 0)
log_warning_errno(r, "Failed to create blob dir for user '%s': %m", home->user_name);
(void) bus_manager_emit_auto_login_changed(m);
(void) bus_home_emit_change(home);
(void) manager_schedule_rebalance(m, /* immediately= */ false);
@ -323,7 +331,9 @@ int home_save_record(Home *h) {
}
int home_unlink_record(Home *h) {
_cleanup_free_ char *blob = NULL;
const char *fn;
int r;
assert(h);
@ -335,6 +345,13 @@ int home_unlink_record(Home *h) {
if (unlink(fn) < 0 && errno != ENOENT)
return -errno;
blob = path_join(home_system_blob_dir(), h->user_name);
if (!blob)
return -ENOMEM;
r = rm_rf(blob, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
if (r < 0)
return r;
return 0;
}

View File

@ -42,6 +42,7 @@
#include "quota-util.h"
#include "random-util.h"
#include "resize-fs.h"
#include "rm-rf.h"
#include "socket-util.h"
#include "sort-util.h"
#include "stat-util.h"
@ -79,6 +80,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_sysfs_hash_ops, char, pat
static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata);
static int manager_gc_images(Manager *m);
static int manager_gc_blob(Manager *m);
static int manager_enumerate_images(Manager *m);
static int manager_assess_image(Manager *m, int dir_fd, const char *dir_path, const char *dentry_name);
static void manager_revalidate_image(Manager *m, Home *h);
@ -1627,6 +1629,9 @@ int manager_startup(Manager *m) {
/* Let's clean up home directories whose devices got removed while we were not running */
(void) manager_enqueue_gc(m, NULL);
/* Let's clean up blob directories for home dirs that no longer exist */
(void) manager_gc_blob(m);
return 0;
}
@ -1715,6 +1720,29 @@ int manager_gc_images(Manager *m) {
return 0;
}
static int manager_gc_blob(Manager *m) {
_cleanup_closedir_ DIR *d = NULL;
int r;
assert(m);
d = opendir(home_system_blob_dir());
if (!d) {
if (errno == ENOENT)
return 0;
return log_error_errno(errno, "Failed to open %s: %m", home_system_blob_dir());
}
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read system blob directory: %m"))
if (!hashmap_contains(m->homes_by_name, de->d_name)) {
r = rm_rf_at(dirfd(d), de->d_name, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (r < 0)
log_warning_errno(r, "Failed to delete blob dir for missing user '%s', ignoring: %m", de->d_name);
}
return 0;
}
static int on_deferred_rescan(sd_event_source *s, void *userdata) {
Manager *m = ASSERT_PTR(userdata);

View File

@ -282,7 +282,7 @@ int user_record_add_binding(
gid_t gid) {
_cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
_cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
_cleanup_free_ char *blob = NULL, *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
sd_id128_t mid;
int r;
@ -291,6 +291,10 @@ int user_record_add_binding(
if (!h->json)
return -EUNATCH;
blob = path_join(home_system_blob_dir(), h->user_name);
if (!blob)
return -ENOMEM;
r = sd_id128_get_machine(&mid);
if (r < 0)
return r;
@ -331,6 +335,7 @@ int user_record_add_binding(
r = json_build(&new_binding_entry,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("blobDirectory", JSON_BUILD_STRING(blob)),
JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(partition_uuid))),
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(luks_uuid))),
@ -370,6 +375,8 @@ int user_record_add_binding(
if (r < 0)
return r;
free_and_replace(h->blob_directory, blob);
if (storage >= 0)
h->storage = storage;
@ -1383,6 +1390,12 @@ int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
if (hr->service && !streq(hr->service, "io.systemd.Home"))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
if (hr->blob_directory) {
/* This function is always called w/o binding section, so if hr->blob_dir is set then the caller set it themselves */
assert((hr->mask & USER_RECORD_BINDING) == 0);
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage custom blob directories.");
}
return 0;
}

View File

@ -30,6 +30,7 @@ RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_ALG AF_INET AF_INET6
RestrictNamespaces=mnt user
RestrictRealtime=yes
StateDirectory=systemd/home
CacheDirectory=systemd/home
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service @mount quotactl