]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homework: when creating home dir also treat specified size as hint
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Nov 2021 20:54:46 +0000 (21:54 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 23 Nov 2021 09:02:35 +0000 (10:02 +0100)
The resize logic is now able to handle resize requests that cannot be
fulfilled in full gracefully. Let's do the same when allocating new home
directories.

This means "homectl create foo --disk-size=min" and "homectl create foo
--disk-size=max" may now be used to create the smallest or largest home
directory we support.

src/home/homework-luks.c

index 57b41d26b831ef409b38d295c186ed1d51dde860..0beee8ecfd697e589c9772c0f0273fe8bb83b8f3 100644 (file)
@@ -63,6 +63,7 @@
                 _x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \
         })
 
+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';
@@ -1914,36 +1915,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 (statfs(parent_dir, &sfs) < 0)
-                return log_error_errno(errno, "statfs() on %s failed: %m", parent_dir);
+        if (fstatfs(image_fd, &sfs) < 0)
+                return log_error_errno(errno, "statfs() on image failed: %m");
 
-        m = sfs.f_bsize * sfs.f_bavail;
+        upper_boundary = DISK_SIZE_ROUND_DOWN((uint64_t) sfs.f_bsize * sfs.f_bavail);
 
-        if (h->disk_size_relative == UINT64_MAX) {
+        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 (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,
@@ -1951,8 +1949,12 @@ 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 || lower_boundary < USER_DISK_SIZE_MIN)
+                lower_boundary = USER_DISK_SIZE_MIN;
+
+        if (*ret < lower_boundary)
+                *ret = lower_boundary;
 
         return 0;
 }
@@ -2141,22 +2143,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)
@@ -2173,7 +2164,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;
 
@@ -2351,7 +2346,7 @@ 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);