summaryrefslogtreecommitdiff
path: root/src/home
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2020-05-07 19:17:48 +0200
committerGitHub <noreply@github.com>2020-05-07 19:17:48 +0200
commit7c5137329d70a52ba20c9d31f04ad3ca0a7e06a5 (patch)
treed37301e99ceae334d7d068997aace218c50ed1da /src/home
parentbb5da6c38543c59cf458e27a2f5fa47d3e72d7f3 (diff)
parent6a49603e08fbabe989e60e16b4673b6a795c9933 (diff)
downloadsystemd-7c5137329d70a52ba20c9d31f04ad3ca0a7e06a5.tar.gz
Merge pull request #15713 from poettering/home-discard-when-offline
homed: optionally, issue FITRIM ioctl when logging out
Diffstat (limited to 'src/home')
-rw-r--r--src/home/homectl.c24
-rw-r--r--src/home/homed-home.c8
-rw-r--r--src/home/homework-luks.c107
-rw-r--r--src/home/homework-luks.h6
-rw-r--r--src/home/homework.c28
-rw-r--r--src/home/homework.h4
6 files changed, 156 insertions, 21 deletions
diff --git a/src/home/homectl.c b/src/home/homectl.c
index 7c1c69495c..66cbcfd9e4 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -2226,6 +2226,9 @@ static int help(int argc, char *argv[], void *userdata) {
" --fs-type=TYPE File system type to use in case of luks\n"
" storage (ext4, xfs, btrfs)\n"
" --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
+ " when activated (mounted)\n"
+ " --luks-offline-discard=BOOL\n"
+ " Whether to trim file on logout\n"
" --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
" --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
" --luks-volume-key-size=BITS\n"
@@ -2279,6 +2282,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IMAGE_PATH,
ARG_UMASK,
ARG_LUKS_DISCARD,
+ ARG_LUKS_OFFLINE_DISCARD,
ARG_JSON,
ARG_SETENV,
ARG_TIMEZONE,
@@ -2372,6 +2376,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "image-path", required_argument, NULL, ARG_IMAGE_PATH },
{ "fs-type", required_argument, NULL, ARG_FS_TYPE },
{ "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
+ { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD },
{ "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
{ "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
{ "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
@@ -2941,6 +2946,25 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_LUKS_OFFLINE_DISCARD:
+ if (isempty(optarg)) {
+ r = drop_from_identity("luksOfflineDiscard");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
+
+ r = json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set offline discard field: %m");
+
+ break;
+
case ARG_LUKS_VOLUME_KEY_SIZE:
case ARG_LUKS_PBKDF_PARALLEL_THREADS:
case ARG_RATE_LIMIT_BURST: {
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index c98e18c686..07356e60c4 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -1002,6 +1002,8 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
if (r < 0)
return r;
if (r == 0) {
+ const char *homework;
+
/* Child */
if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
@@ -1017,7 +1019,11 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */
- execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL);
+ /* Allow overriding the homework path via an environment variable, to make debugging
+ * easier. */
+ homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH;
+
+ execl(homework, homework, verb, NULL);
log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
_exit(EXIT_FAILURE);
}
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index 5eb67bc2b3..694f215b30 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -893,19 +893,19 @@ int home_store_header_identity_luks(
return 1;
}
-static int run_fitrim(int root_fd) {
+int run_fitrim(int root_fd) {
char buf[FORMAT_BYTES_MAX];
struct fstrim_range range = {
.len = UINT64_MAX,
};
/* If discarding is on, discard everything right after mounting, so that the discard setting takes
- * effect on activation. */
+ * effect on activation. (Also, optionally, trim on logout) */
assert(root_fd >= 0);
if (ioctl(root_fd, FITRIM, &range) < 0) {
- if (IN_SET(errno, ENOTTY, EOPNOTSUPP, EBADF)) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EBADF) {
log_debug_errno(errno, "File system does not support FITRIM, not trimming.");
return 0;
}
@@ -918,15 +918,32 @@ static int run_fitrim(int root_fd) {
return 1;
}
-static int run_fallocate(int backing_fd, const struct stat *st) {
+int run_fitrim_by_path(const char *root_path) {
+ _cleanup_close_ int root_fd = -1;
+
+ root_fd = open(root_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (root_fd < 0)
+ return log_error_errno(errno, "Failed to open file system '%s' for trimming: %m", root_path);
+
+ return run_fitrim(root_fd);
+}
+
+int run_fallocate(int backing_fd, const struct stat *st) {
char buf[FORMAT_BYTES_MAX];
+ struct stat stbuf;
assert(backing_fd >= 0);
- assert(st);
/* If discarding is off, let's allocate the whole image before mounting, so that the setting takes
* effect on activation */
+ if (!st) {
+ if (fstat(backing_fd, &stbuf) < 0)
+ return log_error_errno(errno, "Failed to fstat(): %m");
+
+ st = &stbuf;
+ }
+
if (!S_ISREG(st->st_mode))
return 0;
@@ -955,6 +972,16 @@ static int run_fallocate(int backing_fd, const struct stat *st) {
return 1;
}
+int run_fallocate_by_path(const char *backing_path) {
+ _cleanup_close_ int backing_fd = -1;
+
+ backing_fd = open(backing_path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (backing_fd < 0)
+ return log_error_errno(errno, "Failed to open '%s' for fallocate(): %m", backing_path);
+
+ return run_fallocate(backing_fd, NULL);
+}
+
int home_prepare_luks(
UserRecord *h,
bool already_activated,
@@ -1111,7 +1138,7 @@ int home_prepare_luks(
h->luks_volume_key_size,
h->password,
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
- user_record_luks_discard(h),
+ user_record_luks_discard(h) || user_record_luks_offline_discard(h),
&cd,
&found_luks_uuid,
&volume_key,
@@ -1147,6 +1174,9 @@ int home_prepare_luks(
if (user_record_luks_discard(h))
(void) run_fitrim(root_fd);
+
+ setup->image_fd = TAKE_FD(fd);
+ setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h));
}
setup->loop = TAKE_PTR(loop);
@@ -1259,6 +1289,7 @@ int home_activate_luks(
return r;
setup.undo_mount = false;
+ setup.do_offline_fitrim = false;
loop_device_relinquish(setup.loop);
@@ -1267,6 +1298,7 @@ int home_activate_luks(
log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
setup.undo_dm = false;
+ setup.do_offline_fallocate = false;
log_info("Everything completed.");
@@ -1279,6 +1311,7 @@ int home_activate_luks(
int home_deactivate_luks(UserRecord *h) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+ bool we_detached;
int r;
/* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we
@@ -1293,23 +1326,45 @@ int home_deactivate_luks(UserRecord *h) {
r = crypt_init_by_name(&cd, dm_name);
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
- log_debug_errno(r, "LUKS device %s is already detached.", dm_name);
- return false;
+ log_debug_errno(r, "LUKS device %s has already been detached.", dm_name);
+ we_detached = false;
} else if (r < 0)
return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+ else {
+ log_info("Discovered used LUKS device %s.", dm_node);
+
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ r = crypt_deactivate(cd, dm_name);
+ if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
+ log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
+ we_detached = false;
+ } else if (r < 0)
+ return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+ else {
+ log_info("LUKS device detaching completed.");
+ we_detached = true;
+ }
+ }
- log_info("Discovered used LUKS device %s.", dm_node);
+ if (user_record_luks_offline_discard(h))
+ log_debug("Not allocating on logout.");
+ else
+ (void) run_fallocate_by_path(user_record_image_path(h));
- crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+ return we_detached;
+}
- r = crypt_deactivate(cd, dm_name);
- if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT))
- log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
- else if (r < 0)
- return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+int home_trim_luks(UserRecord *h) {
+ assert(h);
- log_info("LUKS device detaching completed.");
- return true;
+ if (!user_record_luks_offline_discard(h)) {
+ log_debug("Not trimming on logout.");
+ return 0;
+ }
+
+ (void) run_fitrim_by_path(user_record_home_directory(h));
+ return 0;
}
static int run_mkfs(
@@ -1918,7 +1973,9 @@ int home_create_luks(
if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0)
return log_oom();
- if (user_record_luks_discard(h)) {
+ if (user_record_luks_discard(h) || user_record_luks_offline_discard(h)) {
+ /* If we want online or offline discard, discard once before we start using things. */
+
if (ioctl(image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0)
log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to issue full-device BLKDISCARD on device, ignoring: %m");
@@ -2004,7 +2061,7 @@ int home_create_luks(
user_record_user_name_and_realm(h),
pkcs11_decrypted_passwords,
effective_passwords,
- user_record_luks_discard(h),
+ user_record_luks_discard(h) || user_record_luks_offline_discard(h),
h,
&cd);
if (r < 0)
@@ -2084,6 +2141,12 @@ int home_create_luks(
goto fail;
}
+ if (user_record_luks_offline_discard(h)) {
+ r = run_fitrim(root_fd);
+ if (r < 0)
+ goto fail;
+ }
+
root_fd = safe_close(root_fd);
r = umount_verbose("/run/systemd/user-home-mount");
@@ -2102,6 +2165,12 @@ int home_create_luks(
loop = loop_device_unref(loop);
+ if (!user_record_luks_offline_discard(h)) {
+ r = run_fallocate(image_fd, NULL /* refresh stat() data */);
+ if (r < 0)
+ goto fail;
+ }
+
if (disk_uuid_path)
(void) ioctl(image_fd, BLKRRPART, 0);
diff --git a/src/home/homework-luks.h b/src/home/homework-luks.h
index 581255a223..bd51f5da50 100644
--- a/src/home/homework-luks.h
+++ b/src/home/homework-luks.h
@@ -9,6 +9,7 @@ int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_i
int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
int home_deactivate_luks(UserRecord *h);
+int home_trim_luks(UserRecord *h);
int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
@@ -36,3 +37,8 @@ static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
return (uint64_t) k;
}
+
+int run_fitrim(int root_fd);
+int run_fitrim_by_path(const char *root_path);
+int run_fallocate(int backing_fd, const struct stat *st);
+int run_fallocate_by_path(const char *backing_path);
diff --git a/src/home/homework.c b/src/home/homework.c
index 38b740729c..77afc402d3 100644
--- a/src/home/homework.c
+++ b/src/home/homework.c
@@ -163,7 +163,15 @@ int home_setup_undo(HomeSetup *setup) {
assert(setup);
- setup->root_fd = safe_close(setup->root_fd);
+ if (setup->root_fd >= 0) {
+ if (setup->do_offline_fitrim) {
+ q = run_fitrim(setup->root_fd);
+ if (q < 0)
+ r = q;
+ }
+
+ setup->root_fd = safe_close(setup->root_fd);
+ }
if (setup->undo_mount) {
q = umount_verbose("/run/systemd/user-home-mount");
@@ -177,8 +185,20 @@ int home_setup_undo(HomeSetup *setup) {
r = q;
}
+ if (setup->image_fd >= 0) {
+ if (setup->do_offline_fallocate) {
+ q = run_fallocate(setup->image_fd, NULL);
+ if (q < 0)
+ r = q;
+ }
+
+ setup->image_fd = safe_close(setup->image_fd);
+ }
+
setup->undo_mount = false;
setup->undo_dm = false;
+ setup->do_offline_fitrim = false;
+ setup->do_offline_fallocate = false;
setup->dm_name = mfree(setup->dm_name);
setup->dm_node = mfree(setup->dm_node);
@@ -666,6 +686,12 @@ static int home_deactivate(UserRecord *h, bool force) {
if (r < 0)
return r;
if (r == USER_TEST_MOUNTED) {
+ if (user_record_storage(h) == USER_LUKS) {
+ r = home_trim_luks(h);
+ if (r < 0)
+ return r;
+ }
+
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));
diff --git a/src/home/homework.h b/src/home/homework.h
index 81698b7601..3bcf3ad9b0 100644
--- a/src/home/homework.h
+++ b/src/home/homework.h
@@ -17,6 +17,7 @@ typedef struct HomeSetup {
LoopDevice *loop;
struct crypt_device *crypt_device;
int root_fd;
+ int image_fd;
sd_id128_t found_partition_uuid;
sd_id128_t found_luks_uuid;
sd_id128_t found_fs_uuid;
@@ -28,6 +29,8 @@ typedef struct HomeSetup {
bool undo_dm;
bool undo_mount;
+ bool do_offline_fitrim;
+ bool do_offline_fallocate;
uint64_t partition_offset;
uint64_t partition_size;
@@ -36,6 +39,7 @@ typedef struct HomeSetup {
#define HOME_SETUP_INIT \
{ \
.root_fd = -1, \
+ .image_fd = -1, \
.partition_offset = UINT64_MAX, \
.partition_size = UINT64_MAX, \
}