1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

df: fix calculation of Use% column

Change formula from: "Used/Size * 100" to "Used/(Used + Avail) * 100".
This formula also works if "Used" and "Avail" do not add up to "Size",
which is the case if there are reserved disk blocks.
This commit is contained in:
Daniel Hofstetter 2022-03-25 10:46:13 +01:00 committed by Sylvestre Ledru
parent d6fd701511
commit e152ebaead
2 changed files with 15 additions and 20 deletions

View file

@ -149,24 +149,28 @@ impl From<Filesystem> for Row {
ffree, ffree,
.. ..
} = fs.usage; } = fs.usage;
let bused = blocks - bfree;
Self { Self {
file: fs.file, file: fs.file,
fs_device: dev_name, fs_device: dev_name,
fs_type, fs_type,
fs_mount: mount_dir, fs_mount: mount_dir,
bytes: blocksize * blocks, bytes: blocksize * blocks,
bytes_used: blocksize * (blocks - bfree), bytes_used: blocksize * bused,
bytes_avail: blocksize * bavail, bytes_avail: blocksize * bavail,
bytes_usage: if blocks == 0 { bytes_usage: if blocks == 0 {
None None
} else { } else {
Some(((blocks - bfree) as f64) / blocks as f64) // We use "(bused + bavail)" instead of "blocks" because on some filesystems (e.g.
// ext4) "blocks" also includes reserved blocks we ignore for the usage calculation.
// https://www.gnu.org/software/coreutils/faq/coreutils-faq.html#df-Size-and-Used-and-Available-do-not-add-up
Some((bused as f64) / (bused + bavail) as f64)
}, },
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
bytes_capacity: if bavail == 0 { bytes_capacity: if bavail == 0 {
None None
} else { } else {
Some(bavail as f64 / ((blocks - bfree + bavail) as f64)) Some(bavail as f64 / ((bused + bavail) as f64))
}, },
inodes: files, inodes: files,
inodes_used: files - ffree, inodes_used: files - ffree,

View file

@ -1,4 +1,4 @@
// spell-checker:ignore udev // spell-checker:ignore udev pcent
use crate::common::util::*; use crate::common::util::*;
#[test] #[test]
@ -139,33 +139,24 @@ fn test_total() {
#[test] #[test]
fn test_use_percentage() { fn test_use_percentage() {
// Example output: let output = new_ucmd!()
// .args(&["--output=used,avail,pcent"])
// Filesystem 1K-blocks Used Available Use% Mounted on .succeeds()
// udev 3858016 0 3858016 0% /dev .stdout_move_str();
// ...
// /dev/loop14 63488 63488 0 100% /snap/core20/1361
let output = new_ucmd!().succeeds().stdout_move_str();
// Skip the header line. // Skip the header line.
let lines: Vec<&str> = output.lines().skip(1).collect(); let lines: Vec<&str> = output.lines().skip(1).collect();
for line in lines { for line in lines {
let mut iter = line.split_whitespace(); let mut iter = line.split_whitespace();
iter.next();
let reported_size = iter.next().unwrap().parse::<f64>().unwrap();
let reported_used = iter.next().unwrap().parse::<f64>().unwrap(); let reported_used = iter.next().unwrap().parse::<f64>().unwrap();
// Skip "Available" column let reported_avail = iter.next().unwrap().parse::<f64>().unwrap();
iter.next();
if cfg!(target_os = "macos") {
// Skip "Capacity" column
iter.next();
}
let reported_percentage = iter.next().unwrap(); let reported_percentage = iter.next().unwrap();
let reported_percentage = reported_percentage[..reported_percentage.len() - 1] let reported_percentage = reported_percentage[..reported_percentage.len() - 1]
.parse::<u8>() .parse::<u8>()
.unwrap(); .unwrap();
let computed_percentage = (100.0 * (reported_used / reported_size)).ceil() as u8; let computed_percentage =
(100.0 * (reported_used / (reported_used + reported_avail))).ceil() as u8;
assert_eq!(computed_percentage, reported_percentage); assert_eq!(computed_percentage, reported_percentage);
} }