Skip to content

fsext: do not confuse f_bsize with f_frsize on Linux - #14192

Open
Socialpranker wants to merge 1 commit into
uutils:mainfrom
Socialpranker:fsext-frsize
Open

fsext: do not confuse f_bsize with f_frsize on Linux#14192
Socialpranker wants to merge 1 commit into
uutils:mainfrom
Socialpranker:fsext-frsize

Conversation

@Socialpranker

@Socialpranker Socialpranker commented Aug 27, 2026

Copy link
Copy Markdown

On Linux struct statfs carries two sizes: f_bsize, the preferred transfer
size, and f_frsize, the block size that f_blocks, f_bfree and f_bavail
are counted in. FsMeta had them the wrong way round — block_size() returned
f_bsize and io_size() returned f_frsize — and FsUsage::new scaled the
counts by f_bsize (with a // or statvfs.f_frsize ? comment next to it).

Almost every filesystem reports the two as equal, so nothing shows. virtiofs —
the filesystem Docker Desktop uses for bind mounts — does not: on a 461 GiB
host disk it reports f_bsize 1 MiB and f_frsize 4 KiB. In a
debian:stable-slim container with a bind-mounted directory:

$ df -h /w            # GNU
Filesystem      Size  Used Avail Use% Mounted on
virtiofs2       461G  343G  118G  75% /w

$ ./df -h /w          # this build
virtiofs2       116T   86T   30T  75% /w

Every size is 256x too large — exactly f_bsize / f_frsize. The raw counts
back this up: f_blocks is 120699413, and 120699413 x 4096 = 494384795648
bytes = 461 GiB, the actual size of the disk; multiplying by f_bsize gives
118 TiB, which no disk on the machine has.

The same swap made stat -f print %s and %S the other way round:

$ stat -f -c '%s %S' /w            # GNU
1048576 4096
$ ./stat -f -c '%s %S' /w          # this build
4096 1048576

which contradicts this crate's own help text — %s is documented as "block
size (for faster transfers)" and %S as "fundamental block size (for block
counts)".

This PR gives Linux and Android their own block_size() (the fragment size,
falling back to f_bsize when f_frsize is zero, as it is on pre-2.6 kernels)
and io_size() (the transfer size), and has FsUsage::new scale by
block_size() instead of reading the field directly. Other targets are
untouched: Apple, FreeBSD and OpenBSD have no f_frsize in struct statfs and
their f_bsize already is the block size the counts use, so the existing
cfg arms are kept verbatim minus the Android entries that moved.

Testing: three unit tests in fsext.rs. Two build a statfs holding
virtiofs's numbers and check that block_size() and io_size() come out the
right way round and that FsUsage scales 120699413 blocks to 494384795648
bytes; the third covers the f_frsize == 0 fallback. The first two fail on current main and pass here; the fallback test
passes either way. cargo test -p uucore --features fsext is green (9 passed),
as are the df (60 passed) and stat (38 passed) suites,
cargo clippy -p uucore --features fsext --all-targets and
cargo fmt --all --check. cargo check -p uucore --features fsext also passes
for aarch64-linux-android and on macOS, so both new cfg arms compile.

Against the system df in debian:stable-slim, df -B1 --output=size now
matches byte for byte on /, /dev, /dev/shm and the virtiofs mount, where
before the virtiofs row was off by 256x; stat -f -c '%s %S' matches on all of
them too.

Behaviour was established by observing GNU's output and the raw statfs values
on a Debian system — no GNU source was consulted.

On Linux `f_bsize` is the preferred transfer size and `f_frsize` the block
size the f_blocks/f_bfree/f_bavail counts are expressed in; the two were
swapped. They are equal on most filesystems, so this only shows up where
they differ - virtiofs reports a 1 MiB f_bsize next to a 4 KiB f_frsize,
and df then reported a 461 GiB mount as 116 TiB. `stat -f` had %s and %S
the other way round for the same reason.
@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 10.17%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 3 regressed benchmarks
✅ 358 untouched benchmarks
⏩ 50 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation split_lines 9.2 ms 10.6 ms -13.16%
Simulation split_numeric_suffix 9.7 ms 11.1 ms -12.55%
Simulation split_bytes 644.5 µs 675.3 µs -4.57%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Socialpranker:fsext-frsize (2aea7a2) with main (0d8310c)

Open in CodSpeed

Footnotes

  1. 50 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/symlink (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/csplit/csplit-heap is now passing!
Congrats! The gnu test tests/seq/seq-epipe is now passing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant