Merge pull request #15713 from poettering/home-discard-when-offline
homed: optionally, issue FITRIM ioctl when logging out
This commit is contained in:
commit
7c5137329d
1
TODO
1
TODO
|
@ -226,7 +226,6 @@ Features:
|
||||||
- rollback when resize fails mid-operation
|
- rollback when resize fails mid-operation
|
||||||
- GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid)
|
- GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid)
|
||||||
- resize on login?
|
- resize on login?
|
||||||
- fstrim on logout?
|
|
||||||
- shrink fs on logout?
|
- shrink fs on logout?
|
||||||
- update LUKS password on login if we find there's a password that unlocks the JSON record but not the LUKS device.
|
- update LUKS password on login if we find there's a password that unlocks the JSON record but not the LUKS device.
|
||||||
- create on activate?
|
- create on activate?
|
||||||
|
|
|
@ -168,6 +168,10 @@ If the UID assigned to a user does not match the owner of the home directory in
|
||||||
the file system, the home directory is automatically and recursively `chown()`ed
|
the file system, the home directory is automatically and recursively `chown()`ed
|
||||||
to the correct UID.
|
to the correct UID.
|
||||||
|
|
||||||
Depending on the `discard` setting of the user record either the backing
|
Depending on the `luksDiscard` setting of the user record either the backing
|
||||||
loopback file is `fallocate()`ed during activation, or the mounted file system
|
loopback file is `fallocate()`ed during activation, or the mounted file system
|
||||||
is `FITRIM`ed after mounting, to ensure the setting is correctly enforced.
|
is `FITRIM`ed after mounting, to ensure the setting is correctly enforced.
|
||||||
|
|
||||||
|
When deactivating a home directory, the file system or block device is trimmed
|
||||||
|
or extended as configured in the `luksOfflineDiscard` setting of the user
|
||||||
|
record.
|
||||||
|
|
|
@ -455,6 +455,10 @@ storage. If false and `luks` storage is used turns this behavior off. In
|
||||||
addition, depending on this setting an `FITRIM` or `fallocate()` operation is
|
addition, depending on this setting an `FITRIM` or `fallocate()` operation is
|
||||||
executed to make sure the image matches the selected option.
|
executed to make sure the image matches the selected option.
|
||||||
|
|
||||||
|
`luksOfflineDiscard` → A boolean. Similar to `luksDiscard`, it controls whether
|
||||||
|
to trim/allocate the file system/backing file when deactivating the home
|
||||||
|
directory.
|
||||||
|
|
||||||
`luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism.
|
`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.
|
`luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism.
|
||||||
|
@ -648,11 +652,12 @@ that may be used in this section are identical to the equally named ones in the
|
||||||
`mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
|
`mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
|
||||||
`cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
|
`cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
|
||||||
`fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
|
`fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
|
||||||
`luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
|
`luksOfflineDiscard`, `luksOfflineDiscard`, `luksCipher`, `luksCipherMode`,
|
||||||
`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
|
`luksVolumeKeySize`, `luksPbkdfHashAlgorithm`, `luksPbkdfType`,
|
||||||
`luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, `rateLimitBurst`,
|
`luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`,
|
||||||
`enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`,
|
`rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
|
||||||
`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
|
`autoLogin`, `stopDelayUSec`, `killProcesses`, `passwordChangeMinUSec`,
|
||||||
|
`passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
|
||||||
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
|
`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
|
||||||
|
|
||||||
## Fields in the `binding` section
|
## Fields in the `binding` section
|
||||||
|
|
|
@ -544,6 +544,16 @@
|
||||||
loopback file) the discard logic defaults to on.</para></listitem>
|
loopback file) the discard logic defaults to on.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--luks-offline-discard=</option><replaceable>BOOL</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Similar to <option>--luks-discard=</option>, controls the trimming of the file
|
||||||
|
system. However, while <option>--luks-discard=</option> controls what happens when the home directory
|
||||||
|
is active, <option>--luks-offline-discard=</option> controls what happens when it becomes inactive,
|
||||||
|
i.e. whether to trim/allocate the storage when deactivating the home directory. This option defaults
|
||||||
|
to on, to ensure disk space is minimized while a user is not logged in.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
|
<term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
|
||||||
<term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
|
<term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
|
||||||
|
|
|
@ -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"
|
" --fs-type=TYPE File system type to use in case of luks\n"
|
||||||
" storage (ext4, xfs, btrfs)\n"
|
" storage (ext4, xfs, btrfs)\n"
|
||||||
" --luks-discard=BOOL Whether to use 'discard' feature of file system\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=CIPHER Cipher to use for LUKS encryption\n"
|
||||||
" --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
|
" --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
|
||||||
" --luks-volume-key-size=BITS\n"
|
" --luks-volume-key-size=BITS\n"
|
||||||
|
@ -2279,6 +2282,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_IMAGE_PATH,
|
ARG_IMAGE_PATH,
|
||||||
ARG_UMASK,
|
ARG_UMASK,
|
||||||
ARG_LUKS_DISCARD,
|
ARG_LUKS_DISCARD,
|
||||||
|
ARG_LUKS_OFFLINE_DISCARD,
|
||||||
ARG_JSON,
|
ARG_JSON,
|
||||||
ARG_SETENV,
|
ARG_SETENV,
|
||||||
ARG_TIMEZONE,
|
ARG_TIMEZONE,
|
||||||
|
@ -2372,6 +2376,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "image-path", required_argument, NULL, ARG_IMAGE_PATH },
|
{ "image-path", required_argument, NULL, ARG_IMAGE_PATH },
|
||||||
{ "fs-type", required_argument, NULL, ARG_FS_TYPE },
|
{ "fs-type", required_argument, NULL, ARG_FS_TYPE },
|
||||||
{ "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
|
{ "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", required_argument, NULL, ARG_LUKS_CIPHER },
|
||||||
{ "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
|
{ "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
|
||||||
{ "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
|
{ "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;
|
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_VOLUME_KEY_SIZE:
|
||||||
case ARG_LUKS_PBKDF_PARALLEL_THREADS:
|
case ARG_LUKS_PBKDF_PARALLEL_THREADS:
|
||||||
case ARG_RATE_LIMIT_BURST: {
|
case ARG_RATE_LIMIT_BURST: {
|
||||||
|
|
|
@ -1002,6 +1002,8 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
|
const char *homework;
|
||||||
|
|
||||||
/* Child */
|
/* Child */
|
||||||
|
|
||||||
if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
|
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() */
|
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");
|
log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -893,19 +893,19 @@ int home_store_header_identity_luks(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run_fitrim(int root_fd) {
|
int run_fitrim(int root_fd) {
|
||||||
char buf[FORMAT_BYTES_MAX];
|
char buf[FORMAT_BYTES_MAX];
|
||||||
struct fstrim_range range = {
|
struct fstrim_range range = {
|
||||||
.len = UINT64_MAX,
|
.len = UINT64_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* If discarding is on, discard everything right after mounting, so that the discard setting takes
|
/* 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);
|
assert(root_fd >= 0);
|
||||||
|
|
||||||
if (ioctl(root_fd, FITRIM, &range) < 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.");
|
log_debug_errno(errno, "File system does not support FITRIM, not trimming.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -918,15 +918,32 @@ static int run_fitrim(int root_fd) {
|
||||||
return 1;
|
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];
|
char buf[FORMAT_BYTES_MAX];
|
||||||
|
struct stat stbuf;
|
||||||
|
|
||||||
assert(backing_fd >= 0);
|
assert(backing_fd >= 0);
|
||||||
assert(st);
|
|
||||||
|
|
||||||
/* If discarding is off, let's allocate the whole image before mounting, so that the setting takes
|
/* If discarding is off, let's allocate the whole image before mounting, so that the setting takes
|
||||||
* effect on activation */
|
* 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))
|
if (!S_ISREG(st->st_mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -955,6 +972,16 @@ static int run_fallocate(int backing_fd, const struct stat *st) {
|
||||||
return 1;
|
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(
|
int home_prepare_luks(
|
||||||
UserRecord *h,
|
UserRecord *h,
|
||||||
bool already_activated,
|
bool already_activated,
|
||||||
|
@ -1111,7 +1138,7 @@ int home_prepare_luks(
|
||||||
h->luks_volume_key_size,
|
h->luks_volume_key_size,
|
||||||
h->password,
|
h->password,
|
||||||
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
|
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
|
||||||
user_record_luks_discard(h),
|
user_record_luks_discard(h) || user_record_luks_offline_discard(h),
|
||||||
&cd,
|
&cd,
|
||||||
&found_luks_uuid,
|
&found_luks_uuid,
|
||||||
&volume_key,
|
&volume_key,
|
||||||
|
@ -1147,6 +1174,9 @@ int home_prepare_luks(
|
||||||
|
|
||||||
if (user_record_luks_discard(h))
|
if (user_record_luks_discard(h))
|
||||||
(void) run_fitrim(root_fd);
|
(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);
|
setup->loop = TAKE_PTR(loop);
|
||||||
|
@ -1259,6 +1289,7 @@ int home_activate_luks(
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
setup.undo_mount = false;
|
setup.undo_mount = false;
|
||||||
|
setup.do_offline_fitrim = false;
|
||||||
|
|
||||||
loop_device_relinquish(setup.loop);
|
loop_device_relinquish(setup.loop);
|
||||||
|
|
||||||
|
@ -1267,6 +1298,7 @@ int home_activate_luks(
|
||||||
log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
|
log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
|
||||||
|
|
||||||
setup.undo_dm = false;
|
setup.undo_dm = false;
|
||||||
|
setup.do_offline_fallocate = false;
|
||||||
|
|
||||||
log_info("Everything completed.");
|
log_info("Everything completed.");
|
||||||
|
|
||||||
|
@ -1279,6 +1311,7 @@ int home_activate_luks(
|
||||||
int home_deactivate_luks(UserRecord *h) {
|
int home_deactivate_luks(UserRecord *h) {
|
||||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||||
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
|
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
|
||||||
|
bool we_detached;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we
|
/* 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);
|
r = crypt_init_by_name(&cd, dm_name);
|
||||||
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
|
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
|
||||||
log_debug_errno(r, "LUKS device %s is already detached.", dm_name);
|
log_debug_errno(r, "LUKS device %s has already been detached.", dm_name);
|
||||||
return false;
|
we_detached = false;
|
||||||
} else if (r < 0)
|
} else if (r < 0)
|
||||||
return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
|
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);
|
||||||
|
|
||||||
log_info("Discovered used LUKS device %s.", dm_node);
|
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
r = crypt_deactivate(cd, dm_name);
|
if (user_record_luks_offline_discard(h))
|
||||||
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT))
|
log_debug("Not allocating on logout.");
|
||||||
log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
|
else
|
||||||
else if (r < 0)
|
(void) run_fallocate_by_path(user_record_image_path(h));
|
||||||
return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
|
|
||||||
|
|
||||||
log_info("LUKS device detaching completed.");
|
return we_detached;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
int home_trim_luks(UserRecord *h) {
|
||||||
|
assert(h);
|
||||||
|
|
||||||
|
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(
|
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)
|
if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0)
|
||||||
return log_oom();
|
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)
|
if (ioctl(image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0)
|
||||||
log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno,
|
log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno,
|
||||||
"Failed to issue full-device BLKDISCARD on device, ignoring: %m");
|
"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),
|
user_record_user_name_and_realm(h),
|
||||||
pkcs11_decrypted_passwords,
|
pkcs11_decrypted_passwords,
|
||||||
effective_passwords,
|
effective_passwords,
|
||||||
user_record_luks_discard(h),
|
user_record_luks_discard(h) || user_record_luks_offline_discard(h),
|
||||||
h,
|
h,
|
||||||
&cd);
|
&cd);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -2084,6 +2141,12 @@ int home_create_luks(
|
||||||
goto fail;
|
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);
|
root_fd = safe_close(root_fd);
|
||||||
|
|
||||||
r = umount_verbose("/run/systemd/user-home-mount");
|
r = umount_verbose("/run/systemd/user-home-mount");
|
||||||
|
@ -2102,6 +2165,12 @@ int home_create_luks(
|
||||||
|
|
||||||
loop = loop_device_unref(loop);
|
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)
|
if (disk_uuid_path)
|
||||||
(void) ioctl(image_fd, BLKRRPART, 0);
|
(void) ioctl(image_fd, BLKRRPART, 0);
|
||||||
|
|
||||||
|
|
|
@ -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_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
|
||||||
int home_deactivate_luks(UserRecord *h);
|
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);
|
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;
|
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);
|
||||||
|
|
|
@ -163,7 +163,15 @@ int home_setup_undo(HomeSetup *setup) {
|
||||||
|
|
||||||
assert(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) {
|
if (setup->undo_mount) {
|
||||||
q = umount_verbose("/run/systemd/user-home-mount");
|
q = umount_verbose("/run/systemd/user-home-mount");
|
||||||
|
@ -177,8 +185,20 @@ int home_setup_undo(HomeSetup *setup) {
|
||||||
r = q;
|
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_mount = false;
|
||||||
setup->undo_dm = false;
|
setup->undo_dm = false;
|
||||||
|
setup->do_offline_fitrim = false;
|
||||||
|
setup->do_offline_fallocate = false;
|
||||||
|
|
||||||
setup->dm_name = mfree(setup->dm_name);
|
setup->dm_name = mfree(setup->dm_name);
|
||||||
setup->dm_node = mfree(setup->dm_node);
|
setup->dm_node = mfree(setup->dm_node);
|
||||||
|
@ -666,6 +686,12 @@ static int home_deactivate(UserRecord *h, bool force) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == USER_TEST_MOUNTED) {
|
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)
|
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));
|
return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ typedef struct HomeSetup {
|
||||||
LoopDevice *loop;
|
LoopDevice *loop;
|
||||||
struct crypt_device *crypt_device;
|
struct crypt_device *crypt_device;
|
||||||
int root_fd;
|
int root_fd;
|
||||||
|
int image_fd;
|
||||||
sd_id128_t found_partition_uuid;
|
sd_id128_t found_partition_uuid;
|
||||||
sd_id128_t found_luks_uuid;
|
sd_id128_t found_luks_uuid;
|
||||||
sd_id128_t found_fs_uuid;
|
sd_id128_t found_fs_uuid;
|
||||||
|
@ -28,6 +29,8 @@ typedef struct HomeSetup {
|
||||||
|
|
||||||
bool undo_dm;
|
bool undo_dm;
|
||||||
bool undo_mount;
|
bool undo_mount;
|
||||||
|
bool do_offline_fitrim;
|
||||||
|
bool do_offline_fallocate;
|
||||||
|
|
||||||
uint64_t partition_offset;
|
uint64_t partition_offset;
|
||||||
uint64_t partition_size;
|
uint64_t partition_size;
|
||||||
|
@ -36,6 +39,7 @@ typedef struct HomeSetup {
|
||||||
#define HOME_SETUP_INIT \
|
#define HOME_SETUP_INIT \
|
||||||
{ \
|
{ \
|
||||||
.root_fd = -1, \
|
.root_fd = -1, \
|
||||||
|
.image_fd = -1, \
|
||||||
.partition_offset = UINT64_MAX, \
|
.partition_offset = UINT64_MAX, \
|
||||||
.partition_size = UINT64_MAX, \
|
.partition_size = UINT64_MAX, \
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
|
||||||
printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
|
printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
|
||||||
|
|
||||||
if (storage == USER_LUKS) {
|
if (storage == USER_LUKS) {
|
||||||
printf("LUKS Discard: %s\n", yes_no(user_record_luks_discard(hr)));
|
printf("LUKS Discard: online=%s offline=%s\n", yes_no(user_record_luks_discard(hr)), yes_no(user_record_luks_offline_discard(hr)));
|
||||||
|
|
||||||
if (!sd_id128_is_null(hr->luks_uuid))
|
if (!sd_id128_is_null(hr->luks_uuid))
|
||||||
printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
|
printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
|
||||||
|
|
|
@ -52,6 +52,7 @@ UserRecord* user_record_new(void) {
|
||||||
.nodev = true,
|
.nodev = true,
|
||||||
.nosuid = true,
|
.nosuid = true,
|
||||||
.luks_discard = -1,
|
.luks_discard = -1,
|
||||||
|
.luks_offline_discard = -1,
|
||||||
.luks_volume_key_size = UINT64_MAX,
|
.luks_volume_key_size = UINT64_MAX,
|
||||||
.luks_pbkdf_time_cost_usec = UINT64_MAX,
|
.luks_pbkdf_time_cost_usec = UINT64_MAX,
|
||||||
.luks_pbkdf_memory_cost = UINT64_MAX,
|
.luks_pbkdf_memory_cost = UINT64_MAX,
|
||||||
|
@ -944,6 +945,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
|
||||||
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
|
{ "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 },
|
{ "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, },
|
{ "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, },
|
||||||
{ "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
|
{ "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 },
|
{ "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 },
|
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
|
||||||
|
@ -1276,6 +1278,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
|
||||||
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
|
{ "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 },
|
{ "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 },
|
{ "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 },
|
||||||
{ "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
|
{ "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 },
|
{ "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 },
|
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
|
||||||
|
@ -1500,6 +1503,27 @@ bool user_record_luks_discard(UserRecord *h) {
|
||||||
return path_startswith(ip, "/dev/");
|
return path_startswith(ip, "/dev/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool user_record_luks_offline_discard(UserRecord *h) {
|
||||||
|
const char *ip;
|
||||||
|
|
||||||
|
assert(h);
|
||||||
|
|
||||||
|
if (h->luks_offline_discard >= 0)
|
||||||
|
return h->luks_offline_discard;
|
||||||
|
|
||||||
|
/* Discard while we are logged out should generally be a good idea, except when operating directly on
|
||||||
|
* physical media, where we should just bind it to the online discard mode. */
|
||||||
|
|
||||||
|
ip = user_record_image_path(h);
|
||||||
|
if (!ip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (path_startswith(ip, "/dev/"))
|
||||||
|
return user_record_luks_discard(h);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const char *user_record_luks_cipher(UserRecord *h) {
|
const char *user_record_luks_cipher(UserRecord *h) {
|
||||||
assert(h);
|
assert(h);
|
||||||
|
|
||||||
|
|
|
@ -261,6 +261,7 @@ typedef struct UserRecord {
|
||||||
sd_id128_t file_system_uuid;
|
sd_id128_t file_system_uuid;
|
||||||
|
|
||||||
int luks_discard;
|
int luks_discard;
|
||||||
|
int luks_offline_discard;
|
||||||
char *luks_cipher;
|
char *luks_cipher;
|
||||||
char *luks_cipher_mode;
|
char *luks_cipher_mode;
|
||||||
uint64_t luks_volume_key_size;
|
uint64_t luks_volume_key_size;
|
||||||
|
@ -332,6 +333,7 @@ const char *user_record_cifs_user_name(UserRecord *h);
|
||||||
const char *user_record_shell(UserRecord *h);
|
const char *user_record_shell(UserRecord *h);
|
||||||
const char *user_record_real_name(UserRecord *h);
|
const char *user_record_real_name(UserRecord *h);
|
||||||
bool user_record_luks_discard(UserRecord *h);
|
bool user_record_luks_discard(UserRecord *h);
|
||||||
|
bool user_record_luks_offline_discard(UserRecord *h);
|
||||||
const char *user_record_luks_cipher(UserRecord *h);
|
const char *user_record_luks_cipher(UserRecord *h);
|
||||||
const char *user_record_luks_cipher_mode(UserRecord *h);
|
const char *user_record_luks_cipher_mode(UserRecord *h);
|
||||||
uint64_t user_record_luks_volume_key_size(UserRecord *h);
|
uint64_t user_record_luks_volume_key_size(UserRecord *h);
|
||||||
|
|
Loading…
Reference in New Issue