Skip to content

Commit 32b5591

Browse files
authored
df: switch from u64 to u128 to handle fs with large inodes nr (#6071)
1 parent c9922ba commit 32b5591

File tree

1 file changed

+106
-15
lines changed

1 file changed

+106
-15
lines changed

src/uu/df/src/table.rs

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ pub(crate) struct Row {
5858
bytes_capacity: Option<f64>,
5959

6060
/// Total number of inodes in the filesystem.
61-
inodes: u64,
61+
inodes: u128,
6262

6363
/// Number of used inodes.
64-
inodes_used: u64,
64+
inodes_used: u128,
6565

6666
/// Number of free inodes.
67-
inodes_free: u64,
67+
inodes_free: u128,
6868

6969
/// Percentage of inodes that are used, given as a float between 0 and 1.
7070
///
@@ -178,9 +178,9 @@ impl From<Filesystem> for Row {
178178
} else {
179179
Some(bavail as f64 / ((bused + bavail) as f64))
180180
},
181-
inodes: files,
182-
inodes_used: fused,
183-
inodes_free: ffree,
181+
inodes: files as u128,
182+
inodes_used: fused as u128,
183+
inodes_free: ffree as u128,
184184
inodes_usage: if files == 0 {
185185
None
186186
} else {
@@ -235,9 +235,9 @@ impl<'a> RowFormatter<'a> {
235235
/// Get a string giving the scaled version of the input number.
236236
///
237237
/// The scaling factor is defined in the `options` field.
238-
fn scaled_inodes(&self, size: u64) -> String {
238+
fn scaled_inodes(&self, size: u128) -> String {
239239
if let Some(h) = self.options.human_readable {
240-
to_magnitude_and_suffix(size.into(), SuffixType::HumanReadable(h))
240+
to_magnitude_and_suffix(size, SuffixType::HumanReadable(h))
241241
} else {
242242
size.to_string()
243243
}
@@ -395,12 +395,6 @@ impl Table {
395395
let values = fmt.get_values();
396396
total += row;
397397

398-
for (i, value) in values.iter().enumerate() {
399-
if UnicodeWidthStr::width(value.as_str()) > widths[i] {
400-
widths[i] = UnicodeWidthStr::width(value.as_str());
401-
}
402-
}
403-
404398
rows.push(values);
405399
}
406400
}
@@ -410,6 +404,16 @@ impl Table {
410404
rows.push(total_row.get_values());
411405
}
412406

407+
// extend the column widths (in chars) for long values in rows
408+
// do it here, after total row was added to the list of rows
409+
for row in &rows {
410+
for (i, value) in row.iter().enumerate() {
411+
if UnicodeWidthStr::width(value.as_str()) > widths[i] {
412+
widths[i] = UnicodeWidthStr::width(value.as_str());
413+
}
414+
}
415+
}
416+
413417
Self {
414418
rows,
415419
widths,
@@ -466,9 +470,11 @@ impl fmt::Display for Table {
466470
#[cfg(test)]
467471
mod tests {
468472

473+
use std::vec;
474+
469475
use crate::blocks::HumanReadable;
470476
use crate::columns::Column;
471-
use crate::table::{Header, HeaderMode, Row, RowFormatter};
477+
use crate::table::{Header, HeaderMode, Row, RowFormatter, Table};
472478
use crate::{BlockSize, Options};
473479

474480
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
@@ -848,4 +854,89 @@ mod tests {
848854

849855
assert_eq!(row.inodes_used, 0);
850856
}
857+
858+
#[test]
859+
fn test_table_column_width_computation_include_total_row() {
860+
let d1 = crate::Filesystem {
861+
file: None,
862+
mount_info: crate::MountInfo {
863+
dev_id: "28".to_string(),
864+
dev_name: "none".to_string(),
865+
fs_type: "9p".to_string(),
866+
mount_dir: "/usr/lib/wsl/drivers".to_string(),
867+
mount_option: "ro,nosuid,nodev,noatime".to_string(),
868+
mount_root: "/".to_string(),
869+
remote: false,
870+
dummy: false,
871+
},
872+
usage: crate::table::FsUsage {
873+
blocksize: 4096,
874+
blocks: 244029695,
875+
bfree: 125085030,
876+
bavail: 125085030,
877+
bavail_top_bit_set: false,
878+
files: 99999999999,
879+
ffree: 999999,
880+
},
881+
};
882+
883+
let filesystems = vec![d1.clone(), d1];
884+
885+
let mut options = Options {
886+
show_total: true,
887+
columns: vec![
888+
Column::Source,
889+
Column::Itotal,
890+
Column::Iused,
891+
Column::Iavail,
892+
],
893+
..Default::default()
894+
};
895+
896+
let table_w_total = Table::new(&options, filesystems.clone());
897+
assert_eq!(
898+
table_w_total.to_string(),
899+
"Filesystem Inodes IUsed IFree\n\
900+
none 99999999999 99999000000 999999\n\
901+
none 99999999999 99999000000 999999\n\
902+
total 199999999998 199998000000 1999998"
903+
);
904+
905+
options.show_total = false;
906+
907+
let table_w_o_total = Table::new(&options, filesystems);
908+
assert_eq!(
909+
table_w_o_total.to_string(),
910+
"Filesystem Inodes IUsed IFree\n\
911+
none 99999999999 99999000000 999999\n\
912+
none 99999999999 99999000000 999999"
913+
);
914+
}
915+
916+
#[test]
917+
fn test_row_accumulation_u64_overflow() {
918+
let total = u64::MAX as u128;
919+
let used1 = 3000u128;
920+
let used2 = 50000u128;
921+
922+
let mut row1 = Row {
923+
inodes: total,
924+
inodes_used: used1,
925+
inodes_free: total - used1,
926+
..Default::default()
927+
};
928+
929+
let row2 = Row {
930+
inodes: total,
931+
inodes_used: used2,
932+
inodes_free: total - used2,
933+
..Default::default()
934+
};
935+
936+
row1 += row2;
937+
938+
assert_eq!(row1.inodes, total * 2);
939+
assert_eq!(row1.inodes_used, used1 + used2);
940+
assert_eq!(row1.inodes_free, total * 2 - used1 - used2);
941+
}
851942
}

0 commit comments

Comments
 (0)