diff options
-rw-r--r-- | man/homectl.xml | 11 | ||||
-rw-r--r-- | src/home/homectl.c | 23 | ||||
-rw-r--r-- | src/home/homework.c | 57 | ||||
-rw-r--r-- | src/home/homework.h | 1 | ||||
-rw-r--r-- | src/shared/user-record-show.c | 3 | ||||
-rw-r--r-- | src/shared/user-record.c | 15 | ||||
-rw-r--r-- | 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 @@ -611,6 +611,17 @@ </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> <listitem><para>When LUKS2 storage is used configures the file system type to use inside the home 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); |