a677afb
From 529ab041d887b4e21be7a30d1187ac9bab92b55f Mon Sep 17 00:00:00 2001
a677afb
From: Jonathan Lebon <jonathan@jlebon.com>
a677afb
Date: Thu, 4 Jan 2024 11:14:39 -0500
a677afb
Subject: [PATCH 1/2] lib/deploy: Round to block size in early prune space
a677afb
 check
a677afb
a677afb
When we estimate how much space a new bootcsum dir will use, we
a677afb
weren't accounting for the space overhead from files not using the
a677afb
last filesystem block completely. This doesn't matter much if counting
a677afb
a few files, but e.g. on FCOS aarch64, we include lots of small
a677afb
devicetree blobs in the bootfs. That loss can add up to enough for the
a677afb
`fallocate()` check to pass but copying still hitting `ENOSPC` later on.
a677afb
a677afb
I think a better fix here is to change approach entirely and instead
a677afb
refactor `install_deployment_kernel()` so that we can call just the
a677afb
copying bits of it as part of the early prune logic. We'll get a more
a677afb
accurate assessment and it's not lost work since we won't need to
a677afb
recopy later on. Also this would not require having to keep in sync the
a677afb
estimator and the install bits.
a677afb
a677afb
That said, this is blocking FCOS releases, so I went with a more tactical
a677afb
fix for now.
a677afb
a677afb
Fixes: https://github.com/coreos/fedora-coreos-tracker/issues/1637
a677afb
---
a677afb
 src/libostree/ostree-sysroot-deploy.c | 42 +++++++++++++++++----------
a677afb
 src/libotutil/ot-fs-utils.c           | 18 ++++++++----
a677afb
 src/libotutil/ot-fs-utils.h           |  4 +--
a677afb
 3 files changed, 42 insertions(+), 22 deletions(-)
a677afb
a677afb
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
a677afb
index a809d560..78c47e76 100644
a677afb
--- a/src/libostree/ostree-sysroot-deploy.c
a677afb
+++ b/src/libostree/ostree-sysroot-deploy.c
a677afb
@@ -2450,7 +2450,8 @@ write_deployments_finish (OstreeSysroot *self, GCancellable *cancellable, GError
a677afb
 }
a677afb
 
a677afb
 static gboolean
a677afb
-add_file_size_if_nonnull (int dfd, const char *path, guint64 *inout_size, GError **error)
a677afb
+add_file_size_if_nonnull (int dfd, const char *path, guint64 blocksize, guint64 *inout_size,
a677afb
+                          GError **error)
a677afb
 {
a677afb
   if (path == NULL)
a677afb
     return TRUE;
a677afb
@@ -2460,14 +2461,21 @@ add_file_size_if_nonnull (int dfd, const char *path, guint64 *inout_size, GError
a677afb
     return FALSE;
a677afb
 
a677afb
   *inout_size += stbuf.st_size;
a677afb
+  if (blocksize > 0)
a677afb
+    {
a677afb
+      off_t rem = stbuf.st_size % blocksize;
a677afb
+      if (rem > 0)
a677afb
+        *inout_size += blocksize - rem;
a677afb
+    }
a677afb
+
a677afb
   return TRUE;
a677afb
 }
a677afb
 
a677afb
 /* calculates the total size of the bootcsum dir in /boot after we would copy
a677afb
  * it. This reflects the logic in  install_deployment_kernel(). */
a677afb
 static gboolean
a677afb
-get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint64 *out_size,
a677afb
-                        GCancellable *cancellable, GError **error)
a677afb
+get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint64 blocksize,
a677afb
+                        guint64 *out_size, GCancellable *cancellable, GError **error)
a677afb
 {
a677afb
   g_autofree char *deployment_dirpath = ostree_sysroot_get_deployment_dirpath (self, deployment);
a677afb
   glnx_autofd int deployment_dfd = -1;
a677afb
@@ -2479,11 +2487,11 @@ get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint
a677afb
     return FALSE;
a677afb
 
a677afb
   guint64 bootdir_size = 0;
a677afb
-  if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
a677afb
+  if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_srcpath, blocksize,
a677afb
                                  &bootdir_size, error))
a677afb
     return FALSE;
a677afb
   if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
a677afb
-                                 &bootdir_size, error))
a677afb
+                                 blocksize, &bootdir_size, error))
a677afb
     return FALSE;
a677afb
   if (kernel_layout->devicetree_srcpath)
a677afb
     {
a677afb
@@ -2491,22 +2499,22 @@ get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint
a677afb
       if (kernel_layout->devicetree_namever)
a677afb
         {
a677afb
           if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
a677afb
-                                         &bootdir_size, error))
a677afb
+                                         blocksize, &bootdir_size, error))
a677afb
             return FALSE;
a677afb
         }
a677afb
       else
a677afb
         {
a677afb
           guint64 dirsize = 0;
a677afb
           if (!ot_get_dir_size (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
a677afb
-                                &dirsize, cancellable, error))
a677afb
+                                blocksize, &dirsize, cancellable, error))
a677afb
             return FALSE;
a677afb
           bootdir_size += dirsize;
a677afb
         }
a677afb
     }
a677afb
   if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath,
a677afb
-                                 &bootdir_size, error))
a677afb
+                                 blocksize, &bootdir_size, error))
a677afb
     return FALSE;
a677afb
-  if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->aboot_srcpath,
a677afb
+  if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->aboot_srcpath, blocksize,
a677afb
                                  &bootdir_size, error))
a677afb
     return FALSE;
a677afb
 
a677afb
@@ -2583,6 +2591,11 @@ auto_early_prune_old_deployments (OstreeSysroot *self, GPtrArray *new_deployment
a677afb
   g_autoptr (GHashTable) new_bootcsums
a677afb
       = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
a677afb
 
a677afb
+  /* get bootfs block size */
a677afb
+  struct statvfs stvfsbuf;
a677afb
+  if (TEMP_FAILURE_RETRY (fstatvfs (self->boot_fd, &stvfsbuf)) < 0)
a677afb
+    return glnx_throw_errno_prefix (error, "fstatvfs(boot)");
a677afb
+
a677afb
   g_auto (GStrv) bootdirs = NULL;
a677afb
   if (!_ostree_sysroot_list_all_boot_directories (self, &bootdirs, cancellable, error))
a677afb
     return glnx_prefix_error (error, "listing bootcsum directories in bootfs");
a677afb
@@ -2597,7 +2610,8 @@ auto_early_prune_old_deployments (OstreeSysroot *self, GPtrArray *new_deployment
a677afb
 
a677afb
       guint64 bootdir_size;
a677afb
       g_autofree char *ostree_bootdir = g_build_filename ("ostree", bootdir, NULL);
a677afb
-      if (!ot_get_dir_size (self->boot_fd, ostree_bootdir, &bootdir_size, cancellable, error))
a677afb
+      if (!ot_get_dir_size (self->boot_fd, ostree_bootdir, stvfsbuf.f_bsize, &bootdir_size,
a677afb
+                            cancellable, error))
a677afb
         return FALSE;
a677afb
 
a677afb
       /* for our purposes of sizing bootcsums, it's highly unlikely we need a
a677afb
@@ -2609,10 +2623,7 @@ auto_early_prune_old_deployments (OstreeSysroot *self, GPtrArray *new_deployment
a677afb
            * that users report it and we tweak this code to handle this.
a677afb
            *
a677afb
            * An alternative is working with the block size instead, which would
a677afb
-           * be easier to handle. But ideally, `ot_get_dir_size` would be block
a677afb
-           * size aware too for better accuracy, which is awkward since the
a677afb
-           * function itself is generic over directories and doesn't consider
a677afb
-           * e.g. mount points from different filesystems. */
a677afb
+           * be easier to handle. */
a677afb
           g_printerr ("bootcsum %s size exceeds %u; disabling auto-prune optimization\n", bootdir,
a677afb
                       G_MAXUINT);
a677afb
           return TRUE;
a677afb
@@ -2640,7 +2651,8 @@ auto_early_prune_old_deployments (OstreeSysroot *self, GPtrArray *new_deployment
a677afb
         }
a677afb
 
a677afb
       guint64 bootdir_size = 0;
a677afb
-      if (!get_kernel_layout_size (self, deployment, &bootdir_size, cancellable, error))
a677afb
+      if (!get_kernel_layout_size (self, deployment, stvfsbuf.f_bsize, &bootdir_size, cancellable,
a677afb
+                                   error))
a677afb
         return FALSE;
a677afb
 
a677afb
       /* see similar logic in previous loop */
a677afb
diff --git a/src/libotutil/ot-fs-utils.c b/src/libotutil/ot-fs-utils.c
a677afb
index efbb2f20..1e961a98 100644
a677afb
--- a/src/libotutil/ot-fs-utils.c
a677afb
+++ b/src/libotutil/ot-fs-utils.c
a677afb
@@ -227,11 +227,12 @@ ot_parse_file_by_line (const char *path, gboolean (*cb) (const char *, void *, G
a677afb
   return TRUE;
a677afb
 }
a677afb
 
a677afb
-/* Calculate the size of the files contained in a directory. Symlinks are not
a677afb
- * followed. */
a677afb
+/* Calculate the size of the files contained in a directory. Symlinks are
a677afb
+ * not followed. If `blocksize` is nonzero, all sizes are rounded to its next
a677afb
+ * multiple. */
a677afb
 gboolean
a677afb
-ot_get_dir_size (int dfd, const char *path, guint64 *out_size, GCancellable *cancellable,
a677afb
-                 GError **error)
a677afb
+ot_get_dir_size (int dfd, const char *path, guint64 blocksize, guint64 *out_size,
a677afb
+                 GCancellable *cancellable, GError **error)
a677afb
 {
a677afb
   g_auto (GLnxDirFdIterator) dfd_iter = {
a677afb
     0,
a677afb
@@ -256,11 +257,18 @@ ot_get_dir_size (int dfd, const char *path, guint64 *out_size, GCancellable *can
a677afb
             return FALSE;
a677afb
 
a677afb
           *out_size += stbuf.st_size;
a677afb
+          if (blocksize > 0)
a677afb
+            {
a677afb
+              off_t rem = stbuf.st_size % blocksize;
a677afb
+              if (rem > 0)
a677afb
+                *out_size += blocksize - rem;
a677afb
+            }
a677afb
         }
a677afb
       else if (dent->d_type == DT_DIR)
a677afb
         {
a677afb
           guint64 subdir_size;
a677afb
-          if (!ot_get_dir_size (dfd_iter.fd, dent->d_name, &subdir_size, cancellable, error))
a677afb
+          if (!ot_get_dir_size (dfd_iter.fd, dent->d_name, blocksize, &subdir_size, cancellable,
a677afb
+                                error))
a677afb
             return FALSE;
a677afb
 
a677afb
           *out_size += subdir_size;
a677afb
diff --git a/src/libotutil/ot-fs-utils.h b/src/libotutil/ot-fs-utils.h
a677afb
index b64adce2..7df79ba2 100644
a677afb
--- a/src/libotutil/ot-fs-utils.h
a677afb
+++ b/src/libotutil/ot-fs-utils.h
a677afb
@@ -75,7 +75,7 @@ GBytes *ot_fd_readall_or_mmap (int fd, goffset offset, GError **error);
a677afb
 gboolean ot_parse_file_by_line (const char *path, gboolean (*cb) (const char *, void *, GError **),
a677afb
                                 void *cbdata, GCancellable *cancellable, GError **error);
a677afb
 
a677afb
-gboolean ot_get_dir_size (int dfd, const char *path, guint64 *out_size, GCancellable *cancellable,
a677afb
-                          GError **error);
a677afb
+gboolean ot_get_dir_size (int dfd, const char *path, guint64 blocksize, guint64 *out_size,
a677afb
+                          GCancellable *cancellable, GError **error);
a677afb
 
a677afb
 G_END_DECLS
a677afb
-- 
a677afb
2.43.0
a677afb