mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-05 09:17:44 +03:00
Merge pull request #21411 from poettering/homed-maximize
homed: add concept for "maximizing" home dirs
This commit is contained in:
commit
5c9da90d1d
@ -849,7 +849,11 @@
|
||||
<literal>xfs</literal> and <literal>btrfs</literal> the home directory may be grown while the user is
|
||||
logged in, and on the latter also shrunk while the user is logged in. If the
|
||||
<literal>subvolume</literal>, <literal>directory</literal>, <literal>fscrypt</literal> storage
|
||||
mechanisms are used, resizing will change file system quota.</para></listitem>
|
||||
mechanisms are used, resizing will change file system quota. The size parameter may make use of the
|
||||
usual suffixes B, K, M, G, T (to the base of 1024). The special strings <literal>min</literal> and
|
||||
<literal>max</literal> may be specified in place of a numeric size value, for minimizing or
|
||||
maximizing disk space assigned to the home area, taking constraints of the file system, disk usage inside
|
||||
the home area and on the backing storage into account.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -1763,6 +1763,32 @@ static int passwd_home(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_disk_size(const char *t, uint64_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(ret);
|
||||
|
||||
if (streq(t, "min"))
|
||||
*ret = 0;
|
||||
else if (streq(t, "max"))
|
||||
*ret = UINT64_MAX-1; /* Largest size that isn't UINT64_MAX special marker */
|
||||
else {
|
||||
uint64_t ds;
|
||||
|
||||
r = parse_size(t, 1024, &ds);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse disk size parameter: %s", t);
|
||||
|
||||
if (ds >= UINT64_MAX) /* UINT64_MAX has special meaning for us ("dont change"), refuse */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Disk size out of range: %s", t);
|
||||
|
||||
*ret = ds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize_home(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *secret = NULL;
|
||||
@ -1781,9 +1807,9 @@ static int resize_home(int argc, char *argv[], void *userdata) {
|
||||
"Relative disk size specification currently not supported when resizing.");
|
||||
|
||||
if (argc > 2) {
|
||||
r = parse_size(argv[2], 1024, &ds);
|
||||
r = parse_disk_size(argv[2], &ds);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse disk size parameter: %s", argv[2]);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_disk_size != UINT64_MAX) {
|
||||
@ -2907,9 +2933,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
r = parse_permyriad(optarg);
|
||||
if (r < 0) {
|
||||
r = parse_size(optarg, 1024, &arg_disk_size);
|
||||
r = parse_disk_size(optarg, &arg_disk_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size '%s' not valid.", optarg);
|
||||
return r;
|
||||
|
||||
r = drop_from_identity("diskSizeRelative");
|
||||
if (r < 0)
|
||||
|
@ -1164,55 +1164,67 @@ int home_setup_luks(
|
||||
PasswordCache *cache,
|
||||
UserRecord **ret_luks_home) {
|
||||
|
||||
sd_id128_t found_partition_uuid, found_luks_uuid, found_fs_uuid;
|
||||
sd_id128_t found_partition_uuid = SD_ID128_NULL, found_luks_uuid = SD_ID128_NULL, found_fs_uuid = SD_ID128_NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *luks_home = NULL;
|
||||
_cleanup_(erase_and_freep) void *volume_key = NULL;
|
||||
size_t volume_key_size = 0;
|
||||
uint64_t offset, size;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(setup);
|
||||
assert(setup->dm_name);
|
||||
assert(setup->dm_node);
|
||||
assert(setup->root_fd < 0);
|
||||
assert(!setup->crypt_device);
|
||||
assert(!setup->loop);
|
||||
|
||||
assert(user_record_storage(h) == USER_LUKS);
|
||||
|
||||
r = dlopen_cryptsetup();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = make_dm_names(h, setup);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Reuse the image fd if it has already been opened by an earlier step */
|
||||
if (setup->image_fd < 0) {
|
||||
setup->image_fd = open_image_file(h, force_image_path, &st);
|
||||
if (setup->image_fd < 0)
|
||||
return setup->image_fd;
|
||||
} else if (fstat(setup->image_fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat image: %m");
|
||||
|
||||
if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) {
|
||||
struct loop_info64 info;
|
||||
const char *n;
|
||||
|
||||
r = luks_open(h,
|
||||
setup,
|
||||
cache,
|
||||
&found_luks_uuid,
|
||||
&volume_key,
|
||||
&volume_key_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!setup->crypt_device) {
|
||||
r = luks_open(h,
|
||||
setup,
|
||||
cache,
|
||||
&found_luks_uuid,
|
||||
&volume_key,
|
||||
&volume_key_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (ret_luks_home) {
|
||||
r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
n = sym_crypt_get_device_name(setup->crypt_device);
|
||||
if (!n)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name);
|
||||
|
||||
r = loop_device_open(n, O_RDWR, &setup->loop);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open loopback device %s: %m", n);
|
||||
if (!setup->loop) {
|
||||
r = loop_device_open(n, O_RDWR, &setup->loop);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open loopback device %s: %m", n);
|
||||
}
|
||||
|
||||
if (ioctl(setup->loop->fd, LOOP_GET_STATUS64, &info) < 0) {
|
||||
_cleanup_free_ char *sysfs = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (!IN_SET(errno, ENOTTY, EINVAL))
|
||||
return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
|
||||
@ -1264,14 +1276,20 @@ int home_setup_luks(
|
||||
|
||||
log_info("Discovered used loopback device %s.", setup->loop->node);
|
||||
|
||||
setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
|
||||
if (setup->root_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open home directory: %m");
|
||||
if (setup->root_fd < 0) {
|
||||
setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
|
||||
if (setup->root_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open home directory: %m");
|
||||
}
|
||||
} else {
|
||||
_cleanup_free_ char *fstype = NULL, *subdir = NULL;
|
||||
bool has_stat = false;
|
||||
const char *ip;
|
||||
struct stat st;
|
||||
|
||||
/* When we aren't reopening the home directory we are allocating it fresh, hence the relevant
|
||||
* objects can't be allocated yet. */
|
||||
assert(setup->root_fd < 0);
|
||||
assert(!setup->crypt_device);
|
||||
assert(!setup->loop);
|
||||
|
||||
ip = force_image_path ?: user_record_image_path(h);
|
||||
|
||||
@ -1279,15 +1297,6 @@ int home_setup_luks(
|
||||
if (!subdir)
|
||||
return log_oom();
|
||||
|
||||
/* Reuse the image fd if it has already been opened by an earlier step */
|
||||
if (setup->image_fd < 0) {
|
||||
setup->image_fd = open_image_file(h, force_image_path, &st);
|
||||
if (setup->image_fd < 0)
|
||||
return setup->image_fd;
|
||||
|
||||
has_stat = true;
|
||||
}
|
||||
|
||||
r = luks_validate(setup->image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate disk label: %m");
|
||||
@ -1298,7 +1307,7 @@ int home_setup_luks(
|
||||
setup->do_mark_clean = true;
|
||||
|
||||
if (!user_record_luks_discard(h)) {
|
||||
r = run_fallocate(setup->image_fd, has_stat ? &st : NULL);
|
||||
r = run_fallocate(setup->image_fd, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1331,9 +1340,11 @@ int home_setup_luks(
|
||||
|
||||
setup->undo_dm = true;
|
||||
|
||||
r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (ret_luks_home) {
|
||||
r = luks_validate_home_record(setup->crypt_device, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fs_validate(setup->dm_node, h->file_system_uuid, &fstype, &found_fs_uuid);
|
||||
if (r < 0)
|
||||
@ -1359,13 +1370,21 @@ int home_setup_luks(
|
||||
setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h));
|
||||
}
|
||||
|
||||
setup->found_partition_uuid = found_partition_uuid;
|
||||
setup->found_luks_uuid = found_luks_uuid;
|
||||
setup->found_fs_uuid = found_fs_uuid;
|
||||
if (!sd_id128_is_null(found_partition_uuid))
|
||||
setup->found_partition_uuid = found_partition_uuid;
|
||||
if (!sd_id128_is_null(found_luks_uuid))
|
||||
setup->found_luks_uuid = found_luks_uuid;
|
||||
if (!sd_id128_is_null(found_fs_uuid))
|
||||
setup->found_fs_uuid = found_fs_uuid;
|
||||
|
||||
setup->partition_offset = offset;
|
||||
setup->partition_size = size;
|
||||
setup->volume_key = TAKE_PTR(volume_key);
|
||||
setup->volume_key_size = volume_key_size;
|
||||
|
||||
if (volume_key) {
|
||||
erase_and_free(setup->volume_key);
|
||||
setup->volume_key = TAKE_PTR(volume_key);
|
||||
setup->volume_key_size = volume_key_size;
|
||||
}
|
||||
|
||||
if (ret_luks_home)
|
||||
*ret_luks_home = TAKE_PTR(luks_home);
|
||||
@ -1941,7 +1960,6 @@ static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *
|
||||
static int home_truncate(
|
||||
UserRecord *h,
|
||||
int fd,
|
||||
const char *path,
|
||||
uint64_t size) {
|
||||
|
||||
bool trunc;
|
||||
@ -1949,7 +1967,6 @@ static int home_truncate(
|
||||
|
||||
assert(h);
|
||||
assert(fd >= 0);
|
||||
assert(path);
|
||||
|
||||
trunc = user_record_luks_discard(h);
|
||||
if (!trunc) {
|
||||
@ -1967,14 +1984,14 @@ static int home_truncate(
|
||||
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_DISK_SPACE(errno)) {
|
||||
log_error_errno(errno, "Not enough disk space to allocate home.");
|
||||
log_debug_errno(errno, "Not enough disk space to allocate home of size %s.", FORMAT_BYTES(size));
|
||||
return -ENOSPC; /* make recognizable */
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to truncate home image %s: %m", path);
|
||||
return log_error_errno(errno, "Failed to truncate home image: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return !trunc; /* Return == 0 if we managed to truncate, > 0 if we managed to allocate */
|
||||
}
|
||||
|
||||
int home_create_luks(
|
||||
@ -2156,7 +2173,7 @@ int home_create_luks(
|
||||
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
|
||||
"Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path);
|
||||
|
||||
r = home_truncate(h, setup->image_fd, setup->temporary_image_path, host_size);
|
||||
r = home_truncate(h, setup->image_fd, host_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2745,6 +2762,39 @@ static int get_smallest_fs_size(int fd, uint64_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_largest_image_size(int fd, const struct stat *st, uint64_t *ret) {
|
||||
uint64_t used, avail, sum;
|
||||
struct statfs sfs;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(st);
|
||||
assert(ret);
|
||||
|
||||
/* Determines the maximum file size we might be able to grow the image file referenced by the fd to. */
|
||||
|
||||
r = stat_verify_regular(st);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Image file is not a regular file, refusing: %m");
|
||||
|
||||
if (syncfs(fd) < 0)
|
||||
return log_error_errno(errno, "Failed to synchronize file system backing image file: %m");
|
||||
|
||||
if (fstatfs(fd, &sfs) < 0)
|
||||
return log_error_errno(errno, "Failed to statfs() image file: %m");
|
||||
|
||||
used = (uint64_t) st->st_blocks * 512;
|
||||
avail = (uint64_t) sfs.f_bsize * sfs.f_bavail;
|
||||
|
||||
if (avail > UINT64_MAX - used)
|
||||
sum = UINT64_MAX;
|
||||
else
|
||||
sum = avail + used;
|
||||
|
||||
*ret = DISK_SIZE_ROUND_DOWN(MIN(sum, USER_DISK_SIZE_MAX));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize_fs_loop(
|
||||
UserRecord *h,
|
||||
HomeSetup *setup,
|
||||
@ -2832,6 +2882,77 @@ static int resize_fs_loop(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize_image_loop(
|
||||
UserRecord *h,
|
||||
HomeSetup *setup,
|
||||
uint64_t old_image_size,
|
||||
uint64_t new_image_size,
|
||||
uint64_t *ret_image_size) {
|
||||
|
||||
uint64_t current_image_size;
|
||||
unsigned n_iterations = 0;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(setup);
|
||||
assert(setup->image_fd >= 0);
|
||||
|
||||
/* A bisection loop trying to find the closest size to what the user asked for. (Well, we bisect like
|
||||
* this only when we *grow* the image — if we shrink the image then there's no need to bisect.) */
|
||||
|
||||
current_image_size = old_image_size;
|
||||
for (uint64_t lower_boundary = old_image_size, upper_boundary = new_image_size, try_image_size = new_image_size;;) {
|
||||
bool worked;
|
||||
|
||||
n_iterations++;
|
||||
|
||||
r = home_truncate(h, setup->image_fd, try_image_size);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_DISK_SPACE(r) || new_image_size < old_image_size) /* Not a disk space issue? Not trying to grow? */
|
||||
return r;
|
||||
|
||||
log_debug_errno(r, "Growing from %s to %s didn't work, not enough space on backing disk.", FORMAT_BYTES(current_image_size), FORMAT_BYTES(try_image_size));
|
||||
worked = false;
|
||||
} else if (r > 0) { /* Success: allocation worked */
|
||||
log_debug("Resizing from %s to %s via allocation worked successfully.", FORMAT_BYTES(current_image_size), FORMAT_BYTES(try_image_size));
|
||||
current_image_size = try_image_size;
|
||||
worked = true;
|
||||
} else { /* Success, but through truncation, not allocation. */
|
||||
log_debug("Resizing from %s to %s via truncation worked successfully.", FORMAT_BYTES(old_image_size), FORMAT_BYTES(try_image_size));
|
||||
current_image_size = try_image_size;
|
||||
break; /* there's no point in the bisection logic if this was plain truncation and
|
||||
* not allocation, let's exit immediately. */
|
||||
}
|
||||
|
||||
if (new_image_size < old_image_size) /* If we are shrinking we are done after one iteration */
|
||||
break;
|
||||
|
||||
/* If we are growing then let's adjust our bisection boundaries and try again */
|
||||
if (worked)
|
||||
lower_boundary = MAX(lower_boundary, try_image_size);
|
||||
else
|
||||
upper_boundary = MIN(upper_boundary, try_image_size);
|
||||
|
||||
if (lower_boundary >= upper_boundary) {
|
||||
log_debug("Image can't be grown further (range to try is empty).");
|
||||
break;
|
||||
}
|
||||
|
||||
try_image_size = DISK_SIZE_ROUND_DOWN(lower_boundary + (upper_boundary - lower_boundary) / 2);
|
||||
if (try_image_size <= lower_boundary || try_image_size >= upper_boundary) {
|
||||
log_debug("Image can't be grown further (remaining range to try too small).");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Bisection loop completed after %u iterations.", n_iterations);
|
||||
|
||||
if (ret_image_size)
|
||||
*ret_image_size = current_image_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int home_resize_luks(
|
||||
UserRecord *h,
|
||||
HomeSetupFlags flags,
|
||||
@ -2969,11 +3090,17 @@ int home_resize_luks(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Old partition doesn't fit in backing storage, refusing.");
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
uint64_t partition_table_extra;
|
||||
uint64_t partition_table_extra, largest_size;
|
||||
|
||||
partition_table_extra = old_image_size - setup->partition_size;
|
||||
|
||||
if (new_image_size <= partition_table_extra)
|
||||
r = get_largest_image_size(setup->image_fd, &st, &largest_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (new_image_size > largest_size)
|
||||
new_image_size = largest_size;
|
||||
|
||||
if (new_image_size < partition_table_extra)
|
||||
new_image_size = partition_table_extra;
|
||||
|
||||
new_partition_size = DISK_SIZE_ROUND_DOWN(new_image_size - partition_table_extra);
|
||||
@ -3090,12 +3217,35 @@ int home_resize_luks(
|
||||
if (new_fs_size > old_fs_size) { /* → Grow */
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
uint64_t resized_image_size;
|
||||
|
||||
/* Grow file size */
|
||||
r = home_truncate(h, image_fd, ip, new_image_size);
|
||||
r = resize_image_loop(h, setup, old_image_size, new_image_size, &resized_image_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_info("Growing of image file completed.");
|
||||
if (resized_image_size == old_image_size) {
|
||||
log_info("Couldn't change image size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(resized_image_size > old_image_size);
|
||||
|
||||
log_info("Growing of image file from %s to %s completed.", FORMAT_BYTES(old_image_size), FORMAT_BYTES(resized_image_size));
|
||||
|
||||
if (resized_image_size < new_image_size) {
|
||||
uint64_t sub;
|
||||
|
||||
/* If the growing we managed to do is smaller than what we wanted we need to
|
||||
* adjust the partition/file system sizes we are going for, too */
|
||||
sub = new_image_size - resized_image_size;
|
||||
assert(new_partition_size >= sub);
|
||||
new_partition_size -= sub;
|
||||
assert(new_fs_size >= sub);
|
||||
new_fs_size -= sub;
|
||||
}
|
||||
|
||||
new_image_size = resized_image_size;
|
||||
} else {
|
||||
assert(S_ISBLK(st.st_mode));
|
||||
assert(new_image_size == old_image_size);
|
||||
@ -3240,9 +3390,11 @@ int home_resize_luks(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = home_setup_done(setup);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_UNDO)) {
|
||||
r = home_setup_done(setup);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
log_info("Everything completed.");
|
||||
|
||||
|
@ -63,6 +63,7 @@ typedef enum HomeSetupFlags {
|
||||
HOME_SETUP_RESIZE_MINIMIZE = 1 << 3, /* Shrink to minimal size */
|
||||
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 */
|
||||
} HomeSetupFlags;
|
||||
|
||||
int home_setup_done(HomeSetup *setup);
|
||||
|
@ -27,9 +27,19 @@ inspect() {
|
||||
systemd-analyze log-level debug
|
||||
systemd-analyze log-target console
|
||||
|
||||
# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
|
||||
mkdir -p /home-pool
|
||||
mount -t tmpfs tmpfs /home-pool -o size=290M
|
||||
|
||||
# we enable --luks-discard= since we run our tests in a tight VM, hence don't
|
||||
# needlessly pressure for storage
|
||||
NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=256M --luks-discard=yes
|
||||
# needlessly pressure for storage. We also set the cheapest KDF, since we don't
|
||||
# want to waste CI CPU cycles on it.
|
||||
NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
|
||||
--disk-size=256M \
|
||||
--luks-discard=yes \
|
||||
--image-path=/home-pool/test-user.home \
|
||||
--luks-pbkdf-type=pbkdf2 \
|
||||
--luks-pbkdf-time-cost=1ms
|
||||
inspect test-user
|
||||
|
||||
PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
|
||||
@ -77,14 +87,14 @@ if ! systemd-detect-virt -cq ; then
|
||||
inspect test-user
|
||||
|
||||
# minimize while inactive
|
||||
PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
|
||||
PASSWORD=xEhErW0ndafV4s homectl resize test-user min
|
||||
inspect test-user
|
||||
|
||||
PASSWORD=xEhErW0ndafV4s homectl activate test-user
|
||||
inspect test-user
|
||||
|
||||
# grow while active
|
||||
PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
|
||||
PASSWORD=xEhErW0ndafV4s homectl resize test-user max
|
||||
inspect test-user
|
||||
|
||||
# minimize while active
|
||||
|
Loading…
Reference in New Issue
Block a user