diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md index f82f1307cc8..d34eac1afa8 100644 --- a/docs/USER_RECORD.md +++ b/docs/USER_RECORD.md @@ -476,6 +476,9 @@ executed to make sure the image matches the selected option. to trim/allocate the file system/backing file when deactivating the home directory. +`luksExtraMountOptions` → A string with additional mount options to append to +the default mount options for the file system in the LUKS volume. + `luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism. `luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism. diff --git a/man/homectl.xml b/man/homectl.xml index 01e9c3b29b9..1b109938ce2 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -656,6 +656,14 @@ to on, to ensure disk space is minimized while a user is not logged in. + + OPTIONS + + Takes a string containing additional mount options to use when mounting the LUKS + volume. If specified, this string will be appended to the default, built-in mount + options. + + CIPHER MODE diff --git a/src/home/homectl.c b/src/home/homectl.c index c689de5b45e..21c12816c4d 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -2153,6 +2153,8 @@ static int help(int argc, char *argv[], void *userdata) { " Memory cost for PBKDF in bytes\n" " --luks-pbkdf-parallel-threads=NUMBER\n" " Number of parallel threads for PKBDF\n" + " --luks-extra-mount-options=OPTIONS\n" + " LUKS extra mount options\n" "\n%4$sMounting User Record Properties:%5$s\n" " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n" " --nodev=BOOL Control the 'nodev' flag of the home mount\n" @@ -2251,6 +2253,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_AND_RESIZE, ARG_AND_CHANGE_PASSWORD, ARG_DROP_CACHES, + ARG_LUKS_EXTRA_MOUNT_OPTIONS, }; static const struct option options[] = { @@ -2335,6 +2338,7 @@ static int parse_argv(int argc, char *argv[]) { { "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 }, + { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS }, {} }; @@ -2452,7 +2456,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_ICON_NAME: case ARG_CIFS_USER_NAME: case ARG_CIFS_DOMAIN: - case ARG_CIFS_EXTRA_MOUNT_OPTIONS: { + case ARG_CIFS_EXTRA_MOUNT_OPTIONS: + case ARG_LUKS_EXTRA_MOUNT_OPTIONS: { const char *field = c == ARG_EMAIL_ADDRESS ? "emailAddress" : @@ -2461,6 +2466,7 @@ static int parse_argv(int argc, char *argv[]) { c == ARG_CIFS_USER_NAME ? "cifsUserName" : c == ARG_CIFS_DOMAIN ? "cifsDomain" : c == ARG_CIFS_EXTRA_MOUNT_OPTIONS ? "cifsExtraMountOptions" : + c == ARG_LUKS_EXTRA_MOUNT_OPTIONS ? "luksExtraMountOptions" : NULL; assert(field); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 41cdac9cc40..1e2c3e55fde 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -1352,7 +1352,7 @@ int home_setup_luks( if (r < 0) return r; - r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h)); + r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); if (r < 0) return r; @@ -2223,7 +2223,7 @@ int home_create_luks( log_info("Formatting file system completed."); - r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h)); + r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); if (r < 0) return r; @@ -2413,7 +2413,13 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) { return CAN_RESIZE_ONLINE; } -static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard, unsigned long flags) { +static int ext4_offline_resize_fs( + HomeSetup *setup, + uint64_t new_size, + bool discard, + unsigned long flags, + const char *extra_mount_options) { + _cleanup_free_ char *size_str = NULL; bool re_open = false, re_mount = false; pid_t resize_pid, fsck_pid; @@ -2488,7 +2494,7 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc /* Re-establish mounts and reopen the directory */ if (re_mount) { - r = home_mount_node(setup->dm_node, "ext4", discard, flags); + r = home_mount_node(setup->dm_node, "ext4", discard, flags, extra_mount_options); if (r < 0) return r; @@ -2930,7 +2936,7 @@ int home_resize_luks( if (r < 0) return log_error_errno(r, "Failed to resize file system: %m"); } else { - r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h)); + r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h), h->luks_extra_mount_options); if (r < 0) return r; } diff --git a/src/home/homework-mount.c b/src/home/homework-mount.c index 234b965dc8e..1e63dbed410 100644 --- a/src/home/homework-mount.c +++ b/src/home/homework-mount.c @@ -40,28 +40,35 @@ static const char *mount_options_for_fstype(const char *fstype) { return NULL; } -int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags) { +int home_mount_node( + const char *node, + const char *fstype, + bool discard, + unsigned long flags, + const char *extra_mount_options) { + _cleanup_free_ char *joined = NULL; - const char *options, *discard_option; + const char *default_options; int r; assert(node); assert(fstype); - options = mount_options_for_fstype(fstype); - - discard_option = discard ? "discard" : "nodiscard"; - - if (options) { - joined = strjoin(options, ",", discard_option); - if (!joined) + default_options = mount_options_for_fstype(fstype); + if (default_options) { + if (!strextend_with_separator(&joined, ",", default_options)) return log_oom(); + } - options = joined; - } else - options = discard_option; + if (!strextend_with_separator(&joined, ",", discard ? "discard" : "nodiscard")) + return log_oom(); - r = mount_nofollow_verbose(LOG_ERR, node, HOME_RUNTIME_WORK_DIR, fstype, flags|MS_RELATIME, strempty(options)); + if (extra_mount_options) { + if (!strextend_with_separator(&joined, ",", extra_mount_options)) + return log_oom(); + } + + r = mount_nofollow_verbose(LOG_ERR, node, HOME_RUNTIME_WORK_DIR, fstype, flags|MS_RELATIME, joined); if (r < 0) return r; @@ -85,7 +92,13 @@ int home_unshare_and_mkdir(void) { return 0; } -int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags) { +int home_unshare_and_mount( + const char *node, + const char *fstype, + bool discard, + unsigned long flags, + const char *extra_mount_options) { + int r; assert(node); @@ -95,7 +108,7 @@ int home_unshare_and_mount(const char *node, const char *fstype, bool discard, u if (r < 0) return r; - r = home_mount_node(node, fstype, discard, flags); + r = home_mount_node(node, fstype, discard, flags, extra_mount_options); if (r < 0) return r; diff --git a/src/home/homework-mount.h b/src/home/homework-mount.h index c9190ae7228..255df26c9ae 100644 --- a/src/home/homework-mount.h +++ b/src/home/homework-mount.h @@ -3,8 +3,8 @@ #include -int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags); +int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags, const char *extra_mount_options); int home_unshare_and_mkdir(void); -int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags); +int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags, const char *extra_mount_options); int home_move_mount(const char *user_name_and_realm, const char *target); int home_shift_uid(int dir_fd, const char *target, uid_t stored_uid, uid_t exposed_uid, int *ret_mount_fd); diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index b7ea7e9f07d..c733654b20a 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -284,6 +284,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (hr->file_system_type) printf(" File System: %s\n", user_record_file_system_type(hr)); + if (hr->luks_extra_mount_options) + printf("LUKS MntOpts: %s\n", hr->luks_extra_mount_options); + if (hr->luks_cipher) printf(" LUKS Cipher: %s\n", hr->luks_cipher); if (hr->luks_cipher_mode) diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 49febade2a3..6413fc904d1 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -285,6 +285,7 @@ static UserRecord* user_record_free(UserRecord *h) { free(h->luks_cipher_mode); free(h->luks_pbkdf_hash_algorithm); free(h->luks_pbkdf_type); + free(h->luks_extra_mount_options); free(h->state); free(h->service); @@ -1287,6 +1288,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 }, + { "luksExtraMountOptions", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_extra_mount_options), 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 }, @@ -1634,6 +1636,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 }, + { "luksExtraMountOptions", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_extra_mount_options), 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 }, diff --git a/src/shared/user-record.h b/src/shared/user-record.h index acf2cdc9d4d..bc160a0a5ec 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -331,6 +331,7 @@ typedef struct UserRecord { uint64_t luks_pbkdf_time_cost_usec; uint64_t luks_pbkdf_memory_cost; uint64_t luks_pbkdf_parallel_threads; + char *luks_extra_mount_options; uint64_t disk_usage; uint64_t disk_free;