}
static gboolean
-add_file_size_if_nonnull (int dfd, const char *path, guint64 *inout_size, GError **error)
+add_file_size_if_nonnull (int dfd, const char *path, guint64 blocksize, guint64 *inout_size,
+ GError **error)
{
if (path == NULL)
return TRUE;
return FALSE;
*inout_size += stbuf.st_size;
+ if (blocksize > 0)
+ {
+ off_t rem = stbuf.st_size % blocksize;
+ if (rem > 0)
+ *inout_size += blocksize - rem;
+ }
+
return TRUE;
}
/* calculates the total size of the bootcsum dir in /boot after we would copy
* it. This reflects the logic in install_deployment_kernel(). */
static gboolean
-get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint64 *out_size,
- GCancellable *cancellable, GError **error)
+get_kernel_layout_size (OstreeSysroot *self, OstreeDeployment *deployment, guint64 blocksize,
+ guint64 *out_size, GCancellable *cancellable, GError **error)
{
g_autofree char *deployment_dirpath = ostree_sysroot_get_deployment_dirpath (self, deployment);
glnx_autofd int deployment_dfd = -1;
return FALSE;
guint64 bootdir_size = 0;
- if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
+ if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_srcpath, blocksize,
&bootdir_size, error))
return FALSE;
if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
- &bootdir_size, error))
+ blocksize, &bootdir_size, error))
return FALSE;
if (kernel_layout->devicetree_srcpath)
{
if (kernel_layout->devicetree_namever)
{
if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
- &bootdir_size, error))
+ blocksize, &bootdir_size, error))
return FALSE;
}
else
{
guint64 dirsize = 0;
if (!ot_get_dir_size (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
- &dirsize, cancellable, error))
+ blocksize, &dirsize, cancellable, error))
return FALSE;
bootdir_size += dirsize;
}
}
if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath,
- &bootdir_size, error))
+ blocksize, &bootdir_size, error))
return FALSE;
- if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->aboot_srcpath,
+ if (!add_file_size_if_nonnull (kernel_layout->boot_dfd, kernel_layout->aboot_srcpath, blocksize,
&bootdir_size, error))
return FALSE;
g_autoptr (GHashTable) new_bootcsums
= g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ /* get bootfs block size */
+ struct statvfs stvfsbuf;
+ if (TEMP_FAILURE_RETRY (fstatvfs (self->boot_fd, &stvfsbuf)) < 0)
+ return glnx_throw_errno_prefix (error, "fstatvfs(boot)");
+
g_auto (GStrv) bootdirs = NULL;
if (!_ostree_sysroot_list_all_boot_directories (self, &bootdirs, cancellable, error))
return glnx_prefix_error (error, "listing bootcsum directories in bootfs");
guint64 bootdir_size;
g_autofree char *ostree_bootdir = g_build_filename ("ostree", bootdir, NULL);
- if (!ot_get_dir_size (self->boot_fd, ostree_bootdir, &bootdir_size, cancellable, error))
+ if (!ot_get_dir_size (self->boot_fd, ostree_bootdir, stvfsbuf.f_bsize, &bootdir_size,
+ cancellable, error))
return FALSE;
/* for our purposes of sizing bootcsums, it's highly unlikely we need a
* that users report it and we tweak this code to handle this.
*
* An alternative is working with the block size instead, which would
- * be easier to handle. But ideally, `ot_get_dir_size` would be block
- * size aware too for better accuracy, which is awkward since the
- * function itself is generic over directories and doesn't consider
- * e.g. mount points from different filesystems. */
+ * be easier to handle. */
g_printerr ("bootcsum %s size exceeds %u; disabling auto-prune optimization\n", bootdir,
G_MAXUINT);
return TRUE;
}
guint64 bootdir_size = 0;
- if (!get_kernel_layout_size (self, deployment, &bootdir_size, cancellable, error))
+ if (!get_kernel_layout_size (self, deployment, stvfsbuf.f_bsize, &bootdir_size, cancellable,
+ error))
return FALSE;
/* see similar logic in previous loop */
return TRUE;
}
-/* Calculate the size of the files contained in a directory. Symlinks are not
- * followed. */
+/* Calculate the size of the files contained in a directory. Symlinks are
+ * not followed. If `blocksize` is nonzero, all sizes are rounded to its next
+ * multiple. */
gboolean
-ot_get_dir_size (int dfd, const char *path, guint64 *out_size, GCancellable *cancellable,
- GError **error)
+ot_get_dir_size (int dfd, const char *path, guint64 blocksize, guint64 *out_size,
+ GCancellable *cancellable, GError **error)
{
g_auto (GLnxDirFdIterator) dfd_iter = {
0,
return FALSE;
*out_size += stbuf.st_size;
+ if (blocksize > 0)
+ {
+ off_t rem = stbuf.st_size % blocksize;
+ if (rem > 0)
+ *out_size += blocksize - rem;
+ }
}
else if (dent->d_type == DT_DIR)
{
guint64 subdir_size;
- if (!ot_get_dir_size (dfd_iter.fd, dent->d_name, &subdir_size, cancellable, error))
+ if (!ot_get_dir_size (dfd_iter.fd, dent->d_name, blocksize, &subdir_size, cancellable,
+ error))
return FALSE;
*out_size += subdir_size;
# to not actually fit (because some filesystems like ext4 include reserved
# overhead in their f_bfree count for some reason) will still trigger the auto-
# prune logic.
+# https://github.com/ostreedev/ostree/pull/2866
unconsume_bootfs_space
unshare -m bash -c \
"mount -o rw,remount /boot && \
cp /usr/lib/modules/`uname -r`/{vmlinuz,initramfs.img} /boot"
-free_blocks=$(stat --file-system /boot -c '%f')
+free_blocks_kernel_and_initrd=$(stat --file-system /boot -c '%f')
unshare -m bash -c \
"mount -o rw,remount /boot && rm /boot/{vmlinuz,initramfs.img}"
-consume_bootfs_space "$((free_blocks))"
+consume_bootfs_space "$((free_blocks_kernel_and_initrd))"
rpm-ostree rebase :modkernel1
# Disable auto-pruning to verify we reproduce the bug
assert_file_has_content out.txt "updating bootloader in two steps"
rm out.txt
+# Below, we test that the size estimator is blocksize aware. This catches the
+# case where the dtb contains many small files such that there's a lot of wasted
+# block space we need to account for.
+# https://github.com/coreos/fedora-coreos-tracker/issues/1637
+
+unconsume_bootfs_space
+
+mkdir -p rootfs/usr/lib/modules/`uname -r`/dtb
+(set +x; for i in {1..10000}; do echo -n x > rootfs/usr/lib/modules/`uname -r`/dtb/$i; done)
+ostree commit --base modkernel1 -P --tree=dir=rootfs -b modkernel3
+
+# a naive estimator would think all those files just take 10000 bytes
+consume_bootfs_space "$((free_blocks_kernel_and_initrd - 10000))"
+
+rpm-ostree rebase :modkernel3
+# Disable auto-pruning to verify we reproduce the bug
+if OSTREE_SYSROOT_OPTS=no-early-prune ostree admin finalize-staged |& tee out.txt; then
+ assert_not_reached "successfully wrote kernel without auto-pruning"
+fi
+assert_file_has_content out.txt "No space left on device"
+rm out.txt
+
+# now, try again but with (now default) auto-pruning enabled
+rpm-ostree rebase :modkernel3
+ostree admin finalize-staged |& tee out.txt
+assert_file_has_content out.txt "updating bootloader in two steps"
+rm out.txt
+
echo "ok bootfs auto-prune"