pull: Check free space when pulling deltas
authorColin Walters <walters@verbum.org>
Mon, 26 Jun 2017 19:10:57 +0000 (15:10 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Tue, 27 Jun 2017 18:42:00 +0000 (18:42 +0000)
Computing download/storage size for `archive` pulls is hard; there's
`OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES` which was from a
pre-static-deltas effort by Endless, but we aren't currently making use of this
much.

Static deltas were designed to solve this problem; we have the total
uncompressed size. Let's check free space before doing a delta pull.

Related: https://github.com/ostreedev/ostree/issues/962

Closes: #963
Approved by: jlebon

src/libostree/ostree-repo-pull.c

index 4607f2f158b9530226307fe255e5b013f5546766..acc5098a7c62bdef491516f1e52ec61a2170ff2e 100644 (file)
@@ -49,6 +49,7 @@
 #endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
 
 #include <gio/gunixinputstream.h>
+#include <sys/statvfs.h>
 
 #define OSTREE_REPO_PULL_CONTENT_PRIORITY  (OSTREE_FETCHER_DEFAULT_PRIORITY)
 #define OSTREE_REPO_PULL_METADATA_PRIORITY (OSTREE_REPO_PULL_CONTENT_PRIORITY - 100)
@@ -1814,6 +1815,11 @@ process_one_static_delta (OtPullData   *pull_data,
   headers = g_variant_get_child_value (delta_superblock, 6);
   fallback_objects = g_variant_get_child_value (delta_superblock, 7);
 
+  /* Gather free space so we can do a check below */
+  struct statvfs stvfsbuf;
+  if (TEMP_FAILURE_RETRY (fstatvfs (pull_data->repo->repo_dir_fd, &stvfsbuf)) < 0)
+    return glnx_throw_errno_prefix (error, "fstatvfs");
+
   /* First process the fallbacks */
   n = g_variant_n_children (fallback_objects);
   for (i = 0; i < n; i++)
@@ -1982,6 +1988,21 @@ process_one_static_delta (OtPullData   *pull_data,
         }
     }
 
+  /* The free space check is here since at this point we've parsed the delta not
+   * only the total size of the parts, but also whether or not we already have
+   * them. TODO: Ideally this free space check would be above, but we'd have to
+   * walk everything twice and keep track of state.
+   */
+  const guint64 delta_required_blocks = (pull_data->total_deltapart_usize / stvfsbuf.f_bsize);
+  if (delta_required_blocks > stvfsbuf.f_bfree)
+    {
+      g_autofree char *formatted_required = g_format_size (pull_data->total_deltapart_usize);
+      g_autofree char *formatted_avail = g_format_size (((guint64)stvfsbuf.f_bsize) * stvfsbuf.f_bfree);
+      glnx_throw (error, "Delta requires %s free space, but only %s available",
+                  formatted_required, formatted_avail);
+      goto out;
+    }
+
   ret = TRUE;
  out:
   return ret;