mirror of
https://github.com/systemd/systemd.git
synced 2025-03-22 06:50:18 +03:00
homework: also add logic for "maximizing" size of home
This commit is contained in:
parent
34081f6be7
commit
2b02eb0591
@ -1960,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;
|
||||
@ -1968,7 +1967,6 @@ static int home_truncate(
|
||||
|
||||
assert(h);
|
||||
assert(fd >= 0);
|
||||
assert(path);
|
||||
|
||||
trunc = user_record_luks_discard(h);
|
||||
if (!trunc) {
|
||||
@ -1986,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(
|
||||
@ -2175,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;
|
||||
|
||||
@ -2764,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,
|
||||
@ -2851,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,
|
||||
@ -2988,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);
|
||||
@ -3109,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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user