diff --git a/man/systemd-homed.service.xml b/man/systemd-homed.service.xml index a4561776c9f..42e5780cccc 100644 --- a/man/systemd-homed.service.xml +++ b/man/systemd-homed.service.xml @@ -46,6 +46,10 @@ url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink, and thus may be browsed with userdbctl1. + + systemd-homed.service also manages blob directories for each home directory + it manages. See User Record Blob Directories + for more details. diff --git a/src/home/home-util.c b/src/home/home-util.c index c777d7b0eb6..9c9c0ff78aa 100644 --- a/src/home/home-util.c +++ b/src/home/home-util.c @@ -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/"; +} diff --git a/src/home/home-util.h b/src/home/home-util.h index 36b301d2360..ead8f5637ee 100644 --- a/src/home/home-util.h +++ b/src/home/home-util.h @@ -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); diff --git a/src/home/homed-home.c b/src/home/homed-home.c index e1422475dce..7bb078ee2c8 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -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; } diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 810ecb23fcf..2e07e90f9c7 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -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); diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c index 8620371ac6a..b18a77d7407 100644 --- a/src/home/user-record-util.c +++ b/src/home/user-record-util.c @@ -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; } diff --git a/units/systemd-homed.service.in b/units/systemd-homed.service.in index ee72c75ac96..ff4f429498c 100644 --- a/units/systemd-homed.service.in +++ b/units/systemd-homed.service.in @@ -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