diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index f51b6f6fbf..7132c7e389 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -1430,7 +1430,7 @@ int home_setup_luks( if (run_mark_dirty(setup->image_fd, true) > 0) setup->do_mark_clean = true; - if (!user_record_luks_discard(h)) { + if (!FLAGS_SET(flags, HOME_SETUP_LUKS_DONT_FALLOCATE) && !user_record_luks_discard(h)) { r = run_fallocate(setup->image_fd, &st); if (r < 0) return r; @@ -1505,7 +1505,9 @@ int home_setup_luks( if (user_record_luks_discard(h)) (void) run_fitrim(setup->root_fd); - setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h)); + setup->do_offline_fitrim = user_record_luks_offline_discard(h); + setup->do_offline_fallocate = !setup->do_offline_fitrim; + setup->tolerate_offline_fallocate_enospc = FLAGS_SET(flags, HOME_SETUP_LUKS_DONT_FALLOCATE); } if (!sd_id128_is_null(found_partition_uuid)) @@ -1609,7 +1611,7 @@ int home_activate_luks( r = home_setup_luks( h, - 0, + flags, NULL, setup, cache, diff --git a/src/home/homework.c b/src/home/homework.c index 2aebe46f95..08762a8022 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -436,7 +436,10 @@ int home_setup_done(HomeSetup *setup) { if (setup->image_fd >= 0) { if (setup->do_offline_fallocate) { q = run_fallocate(setup->image_fd, NULL); - if (q < 0) + if (setup->tolerate_offline_fallocate_enospc && + ERRNO_IS_NEG_DISK_SPACE(q)) + log_warning_errno(q, "Failed to allocate backing file, ignoring: %m"); + else if (q < 0) r = q; } @@ -468,6 +471,7 @@ int home_setup_done(HomeSetup *setup) { setup->undo_dm = false; setup->do_offline_fitrim = false; setup->do_offline_fallocate = false; + setup->tolerate_offline_fallocate_enospc = false; setup->do_mark_clean = false; setup->dm_name = mfree(setup->dm_name); @@ -1686,7 +1690,7 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) { return user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE, ret); } - r = home_setup(h, flags, &setup, &cache, &header_home); + r = home_setup(h, flags | HOME_SETUP_LUKS_DONT_FALLOCATE, &setup, &cache, &header_home); if (r < 0) return r; @@ -1788,7 +1792,7 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) { if (r < 0) return r; - r = home_setup(h, flags, &setup, &cache, &header_home); + r = home_setup(h, flags | HOME_SETUP_LUKS_DONT_FALLOCATE, &setup, &cache, &header_home); if (r < 0) return r; @@ -1866,7 +1870,7 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) { if (r < 0) return r; - r = home_setup(h, flags, &setup, &cache, &header_home); + r = home_setup(h, flags | HOME_SETUP_LUKS_DONT_FALLOCATE, &setup, &cache, &header_home); if (r < 0) return r; diff --git a/src/home/homework.h b/src/home/homework.h index 493ddbf294..75eea1835d 100644 --- a/src/home/homework.h +++ b/src/home/homework.h @@ -48,6 +48,7 @@ typedef struct HomeSetup { bool undo_mount:1; /* Whether to unmount /run/systemd/user-home-mount */ bool do_offline_fitrim:1; bool do_offline_fallocate:1; + bool tolerate_offline_fallocate_enospc:1; bool do_mark_clean:1; bool do_drop_caches:1; @@ -82,6 +83,9 @@ typedef enum HomeSetupFlags { HOME_SETUP_RESIZE_DONT_GROW = 1 << 4, /* If the resize would grow, gracefully terminate operation */ HOME_SETUP_RESIZE_DONT_SHRINK = 1 << 5, /* If the resize would shrink, gracefully terminate operation */ HOME_SETUP_RESIZE_DONT_UNDO = 1 << 6, /* Leave loopback/DM device context open after successful operation */ + + /* LUKS backend: */ + HOME_SETUP_LUKS_DONT_FALLOCATE = 1 << 7, /* Skip setup-time backing file allocation */ } HomeSetupFlags; int home_setup_done(HomeSetup *setup); diff --git a/test/units/TEST-46-HOMED.sh b/test/units/TEST-46-HOMED.sh index ee0b249db0..a7cc3139d1 100755 --- a/test/units/TEST-46-HOMED.sh +++ b/test/units/TEST-46-HOMED.sh @@ -40,6 +40,22 @@ wait_for_state() { timeout 2m bash -c "until homectl inspect '${1:?}' | grep -F 'State: $2' >/dev/null; do sleep 2; done" } +cleanup_update_auto_resize_mode_without_space() ( + set +e + + rm -f /tmp/no-space-update.create /home/no-space-update.filler /home/no-space-update.home + + if homectl inspect no-space-update >/dev/null 2>&1; then + homectl deactivate no-space-update 2>/dev/null + wait_for_state no-space-update inactive 2>/dev/null + homectl remove no-space-update 2>/dev/null + fi + + mount /home -o remount,size=290M || : + systemctl restart systemd-homed.service || : + return 0 +) + FSTYPE="$(stat --file-system --format "%T" /)" systemctl start systemd-homed.service systemd-userdbd.socket @@ -337,6 +353,105 @@ testcase_recovery_key_file() ( rm -rf "$KEY_DIR" ) +testcase_update_auto_resize_mode_without_space() { + local blocks bsize create_output fill_mbytes fill_size filler home_size image + local fs_stat image_stat image_blocks image_size image_unallocated + + create_output="/tmp/no-space-update.create" + filler="/home/no-space-update.filler" + home_size=512M + image="/home/no-space-update.home" + + trap cleanup_update_auto_resize_mode_without_space RETURN ERR EXIT + + mount /home -o remount,size="$home_size" + + if ! NEWPASSWORD=xEhErW0ndafV4s \ + homectl create no-space-update \ + --storage=luks \ + --disk-size=300M \ + --auto-resize-mode=shrink-and-grow \ + --luks-discard=no \ + --luks-offline-discard=yes \ + --image-path="$image" \ + --luks-pbkdf-type=pbkdf2 \ + --luks-pbkdf-time-cost=1ms \ + --rate-limit-interval=1s \ + --rate-limit-burst=1000 \ + >"$create_output" 2>&1; then + + if grep -F "System does not support selected storage backend" "$create_output" >/dev/null; then + cat "$create_output" + echo "LUKS storage backend not supported, skipping update without space test." + return 0 + fi + + cat "$create_output" >&2 + return 1 + fi + cat "$create_output" + + PASSWORD=xEhErW0ndafV4s homectl update no-space-update --disk-size=max + wait_for_state no-space-update inactive + + # Activation still fully allocates luksDiscard=no images while space is available. + PASSWORD=xEhErW0ndafV4s homectl activate no-space-update + wait_for_state no-space-update active + homectl deactivate no-space-update + wait_for_state no-space-update inactive + + PASSWORD=xEhErW0ndafV4s homectl resize no-space-update 256M + wait_for_state no-space-update inactive + + PASSWORD=xEhErW0ndafV4s homectl update no-space-update --disk-size=max + wait_for_state no-space-update inactive + + image_stat="$(stat -c '%s %b' "$image")" + read -r image_size image_blocks <<<"$image_stat" + [[ "$image_size" =~ ^[0-9]+$ && "$image_blocks" =~ ^[0-9]+$ ]] + image_unallocated=$((image_size - image_blocks * 512)) + fs_stat="$(stat --file-system --format "%a %S" /home)" + read -r blocks bsize <<<"$fs_stat" + [[ "$blocks" =~ ^[0-9]+$ && "$bsize" =~ ^[0-9]+$ ]] + fill_size=$((blocks * bsize - 128 * 1024 * 1024)) + fill_mbytes=$((fill_size / 1024 / 1024)) + if [[ "$fill_mbytes" -le 0 ]]; then + echo "Not enough free space to set up update without space test, skipping." + return 0 + fi + + # Leave enough room for the small identity update below, but not enough for the old full-image fallocate(). + dd if=/dev/zero of="$filler" bs=1M count="$fill_mbytes" + + fs_stat="$(stat --file-system --format "%a %S" /home)" + read -r blocks bsize <<<"$fs_stat" + [[ "$blocks" =~ ^[0-9]+$ && "$bsize" =~ ^[0-9]+$ ]] + if [[ "$image_unallocated" -le $((blocks * bsize)) ]]; then + printf 'Not enough backing space for the update test: ' + printf 'image_unallocated=%s free_space=%s fill_mbytes=%s\n' \ + "$image_unallocated" "$((blocks * bsize))" "$fill_mbytes" + return 0 + fi + + systemctl restart systemd-homed.service + wait_for_exist no-space-update + + PASSWORD=xEhErW0ndafV4s homectl authenticate no-space-update + + PASSWORD=xEhErW0ndafV4s homectl update no-space-update \ + --luks-discard=yes \ + --luks-offline-discard=no + + PASSWORD=xEhErW0ndafV4s homectl update no-space-update \ + --luks-discard=no \ + --luks-offline-discard=yes + + PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd no-space-update + PASSWORD=yPN4N0fYNKUkOq homectl update no-space-update --auto-resize-mode=off + + homectl inspect no-space-update | grep -F "Auto Resize: off" >/dev/null +} + testcase_blob() { # blob directory tests # See docs/USER_RECORD_BLOB_DIRS.md