summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-11-23 16:44:26 +0100
committerGitHub <noreply@github.com>2021-11-23 16:44:26 +0100
commit5c682b788188f213f095daf82fbf1c732166aca2 (patch)
tree872f593f78114e278381ac7813bb963efb45ecf9
parent76eec0649936d9ae2f9087769f463feaf0cf5cb4 (diff)
parent75f6ae064e80b830d3320e1c01c4ab052b3298a1 (diff)
downloadsystemd-5c682b788188f213f095daf82fbf1c732166aca2.tar.gz
Merge pull request #21440 from poettering/homed-initial-fs-size
homed: also support minimizing/maximizing home dirs when creating them
-rw-r--r--man/homectl.xml11
-rw-r--r--src/home/homework-luks.c75
-rwxr-xr-xtest/units/testsuite-46.sh2
3 files changed, 49 insertions, 39 deletions
diff --git a/man/homectl.xml b/man/homectl.xml
index b94d52b094..0bf4803d9a 100644
--- a/man/homectl.xml
+++ b/man/homectl.xml
@@ -504,10 +504,13 @@
<varlistentry>
<term><option>--disk-size=</option><replaceable>BYTES</replaceable></term>
<listitem><para>Either takes a size in bytes as argument (possibly using the usual K, M, G, …
- suffixes for 1024 base values), or a percentage value and configures the disk space to assign to the
- user. If a percentage value is specified (i.e. the argument suffixed with <literal>%</literal>) it is
- taken relative to the available disk space of the backing file system. If the LUKS2 backend is used
- this configures the size of the loopback file and file system contained therein. For the other
+ suffixes for 1024 base values), a percentage value, or the special strings <literal>min</literal> or
+ <literal>max</literal>, and configures the disk space to assign to the user. If a percentage value is
+ specified (i.e. the argument suffixed with <literal>%</literal>) it is taken relative to the
+ available disk space of the backing file system. If specified as <literal>min</literal> assigns the
+ minimal disk space permitted by the constraints of the backing file system and other limits, when
+ specified as <literal>max</literal> assigns the maximum disk space available. If the LUKS2 backend is
+ used this configures the size of the loopback file and file system contained therein. For the other
storage backends configures disk quota using the filesystem's native quota logic, if available. If
not specified, defaults to 85% of the available disk space for the LUKS2 backend and to no quota for
the others.</para></listitem>
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index fd8f252dac..ac379c6d07 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -64,6 +64,11 @@
_x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \
})
+/* How much larger will the image on disk be than the fs inside it, i.e. the space we pay for the GPT and
+ * LUKS2 envelope. (As measured on cryptsetup 2.4.1) */
+#define GPT_LUKS2_OVERHEAD UINT64_C(18874368)
+
+static int resize_image_loop(UserRecord *h, HomeSetup *setup, uint64_t old_image_size, uint64_t new_image_size, uint64_t *ret_image_size);
int run_mark_dirty(int fd, bool b) {
char x = '1';
@@ -1891,6 +1896,8 @@ static int make_partition_table(
assert(first_lba <= UINT64_MAX/512);
start = DISK_SIZE_ROUND_UP(first_lba * 512); /* Round up to multiple of 4K */
+ log_debug("Starting partition at offset %" PRIu64, start);
+
if (start == UINT64_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Overflow while rounding up start LBA.");
@@ -2024,36 +2031,33 @@ static int wait_for_devlink(const char *path) {
}
}
-static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *ret) {
+static int calculate_initial_image_size(UserRecord *h, int image_fd, const char *fstype, uint64_t *ret) {
+ uint64_t upper_boundary, lower_boundary;
struct statfs sfs;
- uint64_t m;
assert(h);
- assert(parent_dir);
+ assert(image_fd >= 0);
assert(ret);
- if (h->disk_size != UINT64_MAX) {
- *ret = DISK_SIZE_ROUND_DOWN(h->disk_size);
- return 0;
- }
+ if (fstatfs(image_fd, &sfs) < 0)
+ return log_error_errno(errno, "statfs() on image failed: %m");
- if (statfs(parent_dir, &sfs) < 0)
- return log_error_errno(errno, "statfs() on %s failed: %m", parent_dir);
+ upper_boundary = DISK_SIZE_ROUND_DOWN((uint64_t) sfs.f_bsize * sfs.f_bavail);
- m = sfs.f_bsize * sfs.f_bavail;
+ if (h->disk_size != UINT64_MAX)
+ *ret = MIN(DISK_SIZE_ROUND_DOWN(h->disk_size), upper_boundary);
+ else if (h->disk_size_relative == UINT64_MAX) {
- if (h->disk_size_relative == UINT64_MAX) {
-
- if (m > UINT64_MAX / USER_DISK_SIZE_DEFAULT_PERCENT)
+ if (upper_boundary > UINT64_MAX / USER_DISK_SIZE_DEFAULT_PERCENT)
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Disk size too large.");
- *ret = DISK_SIZE_ROUND_DOWN(m * USER_DISK_SIZE_DEFAULT_PERCENT / 100);
+ *ret = DISK_SIZE_ROUND_DOWN(upper_boundary * USER_DISK_SIZE_DEFAULT_PERCENT / 100);
log_info("Sizing home to %u%% of available disk space, which is %s.",
USER_DISK_SIZE_DEFAULT_PERCENT,
FORMAT_BYTES(*ret));
} else {
- *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) m * (double) h->disk_size_relative / (double) UINT32_MAX));
+ *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) upper_boundary * (double) CLAMP(h->disk_size_relative, 0U, UINT32_MAX) / (double) UINT32_MAX));
log_info("Sizing home to %" PRIu64 ".%01" PRIu64 "%% of available disk space, which is %s.",
(h->disk_size_relative * 100) / UINT32_MAX,
@@ -2061,8 +2065,16 @@ static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *
FORMAT_BYTES(*ret));
}
- if (*ret < USER_DISK_SIZE_MIN)
- *ret = USER_DISK_SIZE_MIN;
+ lower_boundary = minimal_size_by_fs_name(fstype);
+ if (lower_boundary != UINT64_MAX) {
+ assert(GPT_LUKS2_OVERHEAD < UINT64_MAX - lower_boundary);
+ lower_boundary += GPT_LUKS2_OVERHEAD;
+ }
+ if (lower_boundary == UINT64_MAX || lower_boundary < USER_DISK_SIZE_MIN)
+ lower_boundary = USER_DISK_SIZE_MIN;
+
+ if (*ret < lower_boundary)
+ *ret = lower_boundary;
return 0;
}
@@ -2231,7 +2243,7 @@ int home_create_luks(
else
host_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
- if (!supported_fs_size(fstype, host_size))
+ if (!supported_fs_size(fstype, LESS_BY(host_size, GPT_LUKS2_OVERHEAD)))
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"Selected file system size too small for %s.", fstype);
@@ -2251,22 +2263,11 @@ int home_create_luks(
log_info("Full device discard completed.");
}
} else {
- _cleanup_free_ char *parent = NULL, *t = NULL;
-
- parent = dirname_malloc(ip);
- if (!parent)
- return log_oom();
-
- r = mkdir_p(parent, 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to create parent directory %s: %m", parent);
+ _cleanup_free_ char *t = NULL;
- r = calculate_disk_size(h, parent, &host_size);
+ r = mkdir_parents(ip, 0755);
if (r < 0)
- return r;
-
- if (!supported_fs_size(fstype, host_size))
- return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", fstype);
+ return log_error_errno(r, "Failed to create parent directory of %s: %m", ip);
r = tempfn_random(ip, "homework", &t);
if (r < 0)
@@ -2283,7 +2284,11 @@ 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, host_size);
+ r = calculate_initial_image_size(h, setup->image_fd, fstype, &host_size);
+ if (r < 0)
+ return r;
+
+ r = resize_image_loop(h, setup, 0, host_size, &host_size);
if (r < 0)
return r;
@@ -2461,10 +2466,12 @@ int home_create_luks(
if (disk_uuid_path)
(void) wait_for_devlink(disk_uuid_path);
- log_info("Everything completed.");
+ log_info("Creation completed.");
print_size_summary(host_size, encrypted_size, &sfs);
+ log_debug("GPT + LUKS2 overhead is %" PRIu64 " (expected %" PRIu64 ")", host_size - encrypted_size, GPT_LUKS2_OVERHEAD);
+
*ret_home = TAKE_PTR(new_home);
return 0;
}
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
index 16329fec36..daec1536b0 100755
--- a/test/units/testsuite-46.sh
+++ b/test/units/testsuite-46.sh
@@ -35,7 +35,7 @@ mount -t tmpfs tmpfs /home-pool -o size=290M
# 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 \
+ --disk-size=min \
--luks-discard=yes \
--image-path=/home-pool/test-user.home \
--luks-pbkdf-type=pbkdf2 \