fix: measure the install target's own capacity, not the devtmpfs holding its node - #433
Open
otavio wants to merge 1 commit into
Open
fix: measure the install target's own capacity, not the devtmpfs holding its node#433otavio wants to merge 1 commit into
otavio wants to merge 1 commit into
Conversation
…ing its node ensure_disk_space() asked statvfs about the install target, but every caller hands it a device node: /dev/mmcblk0p2 for raw and copy, an mtd character device for flash, a ubi volume for ubifs. statvfs reports on the filesystem holding the path it is given, and for anything under /dev that is the devtmpfs the kernel sizes after the available RAM. The check therefore compared the object against a fraction of RAM and never once looked at the partition the object was about to be written to. The same number came back for every device on a machine here, 3.2G, which is what df reports for /dev, while the devices themselves are 1.8T, 511M and 14.5G. A device large enough for the update was rejected, and, worse, one too small was accepted whenever RAM happened to exceed it. Device nodes are now asked for their own capacity by seeking to the end, which block devices, mtd character devices and ubi volumes all answer with their size. That is portable in a way the ioctls are not: BLKGETSIZE64 is declared in terms of size_t, so its request number differs between 32 and 64 bit userspace while the kernel writes back 64 bits either way, and mtd and ubi would each need an ioctl of their own besides. Paths that really do live on a filesystem keep going through statvfs. Widening the statvfs fields before multiplying fixes a second way the same line could mislead. They are c_ulong and fsblkcnt_t, 32 bit on the armv7 targets this runs on, so the product wrapped for any filesystem past 4GiB and the conversion to u64 came too late to help. The count is also taken in units of f_frsize rather than f_bsize, which is what f_bfree is expressed in. The loop device test needs root, as creating one does, so it is marked ignored alongside the mtd tests that already are. Its fixture is built on the loopdev crate and on the mutex the copy and tarball tests already serialize next_free() with, both of which had to be reachable from here, rather than on a second way of doing what the crate can already do. Left alone: copy and tarball mount a filesystem onto the target and write files into it, so the device's capacity is an upper bound for them rather than an answer, ignoring both what is already there and the filesystem's own overhead. Measuring those properly means a statvfs on the mount point once it is mounted, which is a change of its own.
otavio
force-pushed
the
fix/disk-space-check-measures-devtmpfs
branch
from
July 28, 2026 21:04
a5810e0 to
ea8b884
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #409.
The bug
ensure_disk_space()checked free space withstatvfs(target), but every caller passes a device node —/dev/mmcblk0p2forraw/copy, an MTD character device forflash, a UBI volume forubifs.statvfsreports on the filesystem holding the path it is given, and for anything under/devthat is the devtmpfs the kernel sizes after available RAM. The check never once looked at the partition being written to.Measured on a development machine — three very different devices, one identical number, and that number is
df /dev:This confirms the diagnosis @je-s reached in #409. It also explains why the reporter only hit it after growing their partitions: the rootfs used to be smaller than devtmpfs, so the meaningless comparison happened to pass. At a 10 GiB
required_uncompressed_sizeit no longer did.The failure is symmetric, and the other direction is the more dangerous one: a device too small for the update is accepted whenever RAM happens to exceed the object size.
The fix
Device nodes are asked for their own capacity by seeking to the end, which block devices, MTD character devices and UBI volumes all answer with their size. That is portable in a way the ioctls are not —
BLKGETSIZE64is declared in terms ofsize_t, so its request number differs between 32- and 64-bit userspace while the kernel writes back 64 bits either way, and MTD and UBI would each need an ioctl of their own besides. Paths that really do live on a filesystem keep going throughstatvfs.A second fix on the same line: the
statvfsfields arec_ulong/fsblkcnt_t, 32-bit on the armv7 targets this runs on, so the product wrapped for any filesystem past 4 GiB and theu64conversion came too late to help. Both operands are widened first now, and the count is taken in units off_frsizerather thanf_bsize, which is whatf_bfreeis expressed in.Tests
Two unit tests cover the
statvfspath and the rejection behaviour. A third sets up a loop device and asserts the reported capacity is the device's own 64 MiB and not devtmpfs's free space — it needs root, as creating a loop device does, so it is marked#[ignore]alongside the MTD tests that already are. Run undersudo:cargo fmt --check,cargo clippy --all-features --all --testsand the full suite (157 passed) are clean.Left for later
copyandtarballmount a filesystem onto the target and write files into it, so the device's capacity is an upper bound for them rather than an answer — it ignores both what is already there and the filesystem's own overhead. Measuring those properly means astatvfson the mount point once mounted, which is a change of its own. They are still far better off than measuring devtmpfs.