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>
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/";
+}
#define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
const char *home_record_dir(void);
+const char *home_system_blob_dir(void);
#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"
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);
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);
}
int home_unlink_record(Home *h) {
+ _cleanup_free_ char *blob = NULL;
const char *fn;
+ int r;
assert(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;
}
#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"
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);
/* 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;
}
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);
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;
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;
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))),
if (r < 0)
return r;
+ free_and_replace(h->blob_directory, blob);
+
if (storage >= 0)
h->storage = storage;
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;
}
RestrictNamespaces=mnt user
RestrictRealtime=yes
StateDirectory=systemd/home
+CacheDirectory=systemd/home
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service @mount quotactl