From 22b54aebdbeadbe9a62a30667670c9837e1ec66c Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Mon, 15 Jan 2024 15:38:03 +0100 Subject: [PATCH 01/20] android x64: f_bsize and f_type are u64 to be casted into i64 --- src/uucore/src/lib/features/fsext.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 3a23682c160..606e61b12d9 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -642,7 +642,7 @@ impl FsMeta for StatFs { not(target_arch = "s390x"), target_pointer_width = "64" ))] - return self.f_bsize; + return self.f_bsize; // i64 -> i64 - OK #[cfg(all( not(target_env = "musl"), not(target_os = "freebsd"), @@ -655,7 +655,7 @@ impl FsMeta for StatFs { not(target_pointer_width = "64") ) ))] - return self.f_bsize.into(); + return self.f_bsize.into(); // i32 or u32 -> i64 - OK #[cfg(any( target_env = "musl", target_os = "freebsd", @@ -664,7 +664,7 @@ impl FsMeta for StatFs { target_os = "redox", all(target_os = "android", target_pointer_width = "64"), ))] - return self.f_bsize.try_into().unwrap(); + return self.f_bsize.try_into().unwrap(); // u64 -> i64 - might fail if number is too high } fn total_blocks(&self) -> u64 { #[cfg(target_pointer_width = "64")] From 69d8f13280d86a507d6433f954802242d60c1a5d Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Mon, 15 Jan 2024 15:39:17 +0100 Subject: [PATCH 02/20] androidx64: test_ls needs output adaption (rounding) - unclear if its file system rather then operating system --- tests/by-util/test_ls.rs | 43 +++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 8d4e5875951..de593d53266 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -115,13 +115,24 @@ fn test_ls_allocation_size() { .succeeds() .stdout_matches(&Regex::new("[^ ] 2 [^ ]").unwrap()); + #[cfg(all( + not(target_os = "freebsd"), + not(all(target_os = "android", target_pointer_width = "64")) + ))] + let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = + (4096, 1024, 8192, "4.0M"); + + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = + (4100, 1025, 8200, "4.1M"); // FIXME: Investigate where this difference comes from. Is it OS or filesystem? + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") .arg("some-dir1") .succeeds() - .stdout_is("total 4096\n 0 empty-file\n 0 file-with-holes\n4096 zero-file\n"); + .stdout_is(format!("total {zero_file_size_4k}\n 0 empty-file\n 0 file-with-holes\n{zero_file_size_4k} zero-file\n")); scene .ucmd() @@ -138,7 +149,7 @@ fn test_ls_allocation_size() { .arg("some-dir1") .succeeds() .stdout_contains("0 empty-file") - .stdout_contains("4096 zero-file"); + .stdout_contains(format!("{zero_file_size_4k} zero-file")); // Test alignment of different block sized files let res = scene.ucmd().arg("-si1").arg("some-dir1").succeeds(); @@ -185,10 +196,10 @@ fn test_ls_allocation_size() { .arg("-s1") .arg("some-dir1") .succeeds() - .stdout_contains("total 1024") + .stdout_contains(format!("total {zero_file_size_1k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("1024 zero-file"); + .stdout_contains(format!("{zero_file_size_1k} zero-file")); #[cfg(not(target_os = "freebsd"))] scene @@ -210,10 +221,10 @@ fn test_ls_allocation_size() { .arg("-s1") .arg("some-dir1") .succeeds() - .stdout_contains("total 1024") + .stdout_contains(format!("total {zero_file_size_1k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("1024 zero-file"); + .stdout_contains(format!("{zero_file_size_1k} zero-file")); #[cfg(not(target_os = "freebsd"))] scene @@ -222,10 +233,10 @@ fn test_ls_allocation_size() { .arg("-s1") .arg("some-dir1") .succeeds() - .stdout_contains("total 8192") + .stdout_contains(format!("total {zero_file_size_8k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("8192 zero-file"); + .stdout_contains(format!("{zero_file_size_8k} zero-file")); // -k should make 'ls' ignore the env var #[cfg(not(target_os = "freebsd"))] @@ -235,10 +246,10 @@ fn test_ls_allocation_size() { .arg("-s1k") .arg("some-dir1") .succeeds() - .stdout_contains("total 4096") + .stdout_contains(format!("total {zero_file_size_4k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("4096 zero-file"); + .stdout_contains(format!("{zero_file_size_4k} zero-file")); // but manually specified blocksize overrides -k #[cfg(not(target_os = "freebsd"))] @@ -248,10 +259,10 @@ fn test_ls_allocation_size() { .arg("--block-size=4K") .arg("some-dir1") .succeeds() - .stdout_contains("total 1024") + .stdout_contains(format!("total {zero_file_size_1k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("1024 zero-file"); + .stdout_contains(format!("{zero_file_size_1k} zero-file")); #[cfg(not(target_os = "freebsd"))] scene @@ -260,10 +271,10 @@ fn test_ls_allocation_size() { .arg("--block-size=4K") .arg("some-dir1") .succeeds() - .stdout_contains("total 1024") + .stdout_contains(format!("total {zero_file_size_1k}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("1024 zero-file"); + .stdout_contains(format!("{zero_file_size_1k} zero-file")); // si option should always trump the human-readable option #[cfg(not(target_os = "freebsd"))] @@ -285,10 +296,10 @@ fn test_ls_allocation_size() { .arg("--block-size=human-readable") .arg("some-dir1") .succeeds() - .stdout_contains("total 4.0M") + .stdout_contains(format!("total {zero_file_size_4m}")) .stdout_contains("0 empty-file") .stdout_contains("0 file-with-holes") - .stdout_contains("4.0M zero-file"); + .stdout_contains(format!("{zero_file_size_4m} zero-file")); #[cfg(not(target_os = "freebsd"))] scene From d861deb38cc9788242429bc3d8c66b1fefe22823 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Mon, 15 Jan 2024 15:40:24 +0100 Subject: [PATCH 03/20] androidx64: df is crashing due to int-overflow of u64 number of inodes --- src/uu/df/src/table.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index f6e09420482..e82873f3a2b 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -102,8 +102,8 @@ impl AddAssign for Row { let bytes = self.bytes + rhs.bytes; let bytes_used = self.bytes_used + rhs.bytes_used; let bytes_avail = self.bytes_avail + rhs.bytes_avail; - let inodes = self.inodes + rhs.inodes; - let inodes_used = self.inodes_used + rhs.inodes_used; + let inodes = self.inodes.saturating_add(rhs.inodes); + let inodes_used = self.inodes_used.saturating_add(rhs.inodes_used); *self = Self { file: None, fs_device: "total".into(), @@ -125,7 +125,7 @@ impl AddAssign for Row { bytes_capacity: None, inodes, inodes_used, - inodes_free: self.inodes_free + rhs.inodes_free, + inodes_free: self.inodes_free.saturating_add(rhs.inodes_free), inodes_usage: if inodes == 0 { None } else { From bf99cf75974ee642716cf59a6dd7f04cde49daee Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Mon, 15 Jan 2024 15:42:17 +0100 Subject: [PATCH 04/20] androidx64: test_df fails due to "." and "/" having different file systems - negative testing fails when selecting by chance the filesystem of "/" --- tests/by-util/test_df.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 0276f18c3c1..43b1651c6d3 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -264,13 +264,18 @@ fn test_type_option() { #[test] #[cfg(not(any(target_os = "freebsd", target_os = "windows")))] // FIXME: fix test for FreeBSD & Win fn test_type_option_with_file() { - let fs_type = new_ucmd!() + let fs_type_cwd = new_ucmd!() .args(&["--output=fstype", "."]) .succeeds() .stdout_move_str(); - let fs_type = fs_type.lines().nth(1).unwrap().trim(); + let fs_type_cwd = fs_type_cwd.lines().nth(1).unwrap().trim(); + let fs_type_root_dir = new_ucmd!() + .args(&["--output=fstype", "/"]) + .succeeds() + .stdout_move_str(); + let fs_type_root_dir = fs_type_root_dir.lines().nth(1).unwrap().trim(); - new_ucmd!().args(&["-t", fs_type, "."]).succeeds(); + new_ucmd!().args(&["-t", fs_type_cwd, "."]).succeeds(); new_ucmd!() .args(&["-t", "nonexisting", "."]) .fails() @@ -283,7 +288,7 @@ fn test_type_option_with_file() { let fs_types: Vec<_> = fs_types .lines() .skip(1) - .filter(|t| t.trim() != fs_type && t.trim() != "") + .filter(|t| t.trim() != fs_type_cwd && t.trim() != fs_type_root_dir && t.trim() != "") .collect(); if !fs_types.is_empty() { From 63a58137431281c1f32d702fb2a7de751708c1a9 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Fri, 8 Mar 2024 23:13:55 +0100 Subject: [PATCH 05/20] revert comments in fsext.rs --- src/uucore/src/lib/features/fsext.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 606e61b12d9..3a23682c160 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -642,7 +642,7 @@ impl FsMeta for StatFs { not(target_arch = "s390x"), target_pointer_width = "64" ))] - return self.f_bsize; // i64 -> i64 - OK + return self.f_bsize; #[cfg(all( not(target_env = "musl"), not(target_os = "freebsd"), @@ -655,7 +655,7 @@ impl FsMeta for StatFs { not(target_pointer_width = "64") ) ))] - return self.f_bsize.into(); // i32 or u32 -> i64 - OK + return self.f_bsize.into(); #[cfg(any( target_env = "musl", target_os = "freebsd", @@ -664,7 +664,7 @@ impl FsMeta for StatFs { target_os = "redox", all(target_os = "android", target_pointer_width = "64"), ))] - return self.f_bsize.try_into().unwrap(); // u64 -> i64 - might fail if number is too high + return self.f_bsize.try_into().unwrap(); } fn total_blocks(&self) -> u64 { #[cfg(target_pointer_width = "64")] From 42ad9f9364adb3038c3557d7a5af28e64bb7d8fd Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 13:22:36 +0100 Subject: [PATCH 06/20] extend AVD side debug information logging --- util/android-scripts/collect-info.sh | 7 +++++++ util/android-scripts/run-tests.sh | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/util/android-scripts/collect-info.sh b/util/android-scripts/collect-info.sh index 1ce8996b9e0..ae4c6443379 100644 --- a/util/android-scripts/collect-info.sh +++ b/util/android-scripts/collect-info.sh @@ -2,6 +2,13 @@ # spell-checker:ignore nextest watchplus PIPESTATUS +echo "system ressources - file systems:" +df -Th +echo "system ressources - RAM:" +free -hm +echo "system ressources - CPU:" +lscpu + echo "$HOME" PATH=$HOME/.cargo/bin:$PATH export PATH diff --git a/util/android-scripts/run-tests.sh b/util/android-scripts/run-tests.sh index 17eed8808e0..98ce29931f5 100644 --- a/util/android-scripts/run-tests.sh +++ b/util/android-scripts/run-tests.sh @@ -45,7 +45,7 @@ run_tests_in_subprocess() ( } # observe (log) every 2 seconds the system resource usage to judge if we are at a limit - watchplus 2 df -h & + watchplus 2 df -Th & watchplus 2 free -hm & nextest_params=(--profile ci --hide-progress-bar --features feat_os_unix_android) From 7099408fd6f381e63497b43715be9b3a3c0b9139 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 13:57:44 +0100 Subject: [PATCH 07/20] fix debug printing of filesystems types (use mount) --- util/android-commands.sh | 8 +++----- util/android-scripts/collect-info.sh | 4 ++-- util/android-scripts/run-tests.sh | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/util/android-commands.sh b/util/android-commands.sh index 0124710d8bf..2b400c7a2ab 100755 --- a/util/android-commands.sh +++ b/util/android-commands.sh @@ -509,7 +509,7 @@ snapshot() { apt_upgrade_all_packages - install_packages_via_ssh_using_apt "rust binutils openssl tar" + install_packages_via_ssh_using_apt "rust binutils openssl tar mount-utils" echo "Read /proc/cpuinfo" run_command_via_ssh "cat /proc/cpuinfo" @@ -573,8 +573,7 @@ build() { reinit_ssh_connection - echo "Read /proc/cpuinfo" - run_command_via_ssh "cat /proc/cpuinfo" + run_script_file_via_ssh "$this_repo/util/android-scripts/collect-info.sh" command="export CARGO_TERM_COLOR=always; export CARGO_INCREMENTAL=0; \ @@ -589,8 +588,7 @@ tests() { reinit_ssh_connection - echo "Read /proc/cpuinfo" - run_command_via_ssh "cat /proc/cpuinfo" + run_script_file_via_ssh "$this_repo/util/android-scripts/collect-info.sh" run_script_file_via_ssh "$this_repo/util/android-scripts/run-tests.sh" || return diff --git a/util/android-scripts/collect-info.sh b/util/android-scripts/collect-info.sh index ae4c6443379..bf4d4ef3813 100644 --- a/util/android-scripts/collect-info.sh +++ b/util/android-scripts/collect-info.sh @@ -2,12 +2,12 @@ # spell-checker:ignore nextest watchplus PIPESTATUS -echo "system ressources - file systems:" -df -Th echo "system ressources - RAM:" free -hm echo "system ressources - CPU:" lscpu +echo "system ressources - file systems:" +mount echo "$HOME" PATH=$HOME/.cargo/bin:$PATH diff --git a/util/android-scripts/run-tests.sh b/util/android-scripts/run-tests.sh index 98ce29931f5..17eed8808e0 100644 --- a/util/android-scripts/run-tests.sh +++ b/util/android-scripts/run-tests.sh @@ -45,7 +45,7 @@ run_tests_in_subprocess() ( } # observe (log) every 2 seconds the system resource usage to judge if we are at a limit - watchplus 2 df -Th & + watchplus 2 df -h & watchplus 2 free -hm & nextest_params=(--profile ci --hide-progress-bar --features feat_os_unix_android) From 9fc277f51a112e2c10abbdffb33e80b71062dbe2 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 14:06:55 +0100 Subject: [PATCH 08/20] force new image, remove free -mh from connection test --- .github/workflows/android.yml | 2 +- util/android-commands.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index c366c7172d9..f37ca2fbdc8 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -19,7 +19,7 @@ concurrency: env: TERMUX: v0.118.0 - KEY_POSTFIX: nextest+rustc-hash+adb+sshd+upgrade+XGB+inc15 + KEY_POSTFIX: nextest+rustc-hash+adb+sshd+upgrade+XGB+inc16 COMMON_EMULATOR_OPTIONS: -no-window -noaudio -no-boot-anim -camera-back none -gpu swiftshader_indirect EMULATOR_DISK_SIZE: 12GB EMULATOR_HEAP_SIZE: 2048M diff --git a/util/android-commands.sh b/util/android-commands.sh index 2b400c7a2ab..48fdd86d844 100755 --- a/util/android-commands.sh +++ b/util/android-commands.sh @@ -371,7 +371,6 @@ run_command_via_ssh() { test_ssh_connection() { run_command_via_ssh echo ssh connection is working - run_command_via_ssh free -mh } # takes a local (on runner side) script file and runs it via ssh on the virtual android device. forwards return code. From d83891ffdf46e4e6286c054bbe7984b1d9563d16 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 15:42:44 +0100 Subject: [PATCH 09/20] criterions depending on filesystem type remove FreeBSD skips --- tests/by-util/test_ls.rs | 41 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index de593d53266..4d970cba11c 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -20,6 +20,7 @@ use std::os::unix::io::IntoRawFd; use std::path::Path; #[cfg(not(windows))] use std::path::PathBuf; +use std::process::Stdio; use std::thread::sleep; use std::time::Duration; @@ -73,6 +74,18 @@ fn test_ls_ordering() { .stdout_matches(&Regex::new("some-dir1:\\ntotal 0").unwrap()); } +fn get_filesystem_type(path: &Path) -> String { + let mut cmd = std::process::Command::new("stat"); + cmd.arg("-f").arg(path); + cmd.stdout(Stdio::piped()); + let child = cmd.spawn().unwrap(); + let output = child.wait_with_output().unwrap(); + let stdout_str = String::from_utf8_lossy(&output.stdout); + let regex = Regex::new(r#"\sType: (?[^\s]+)"#).unwrap(); + let m = regex.captures(&stdout_str).unwrap(); + m["fstype"].to_owned() +} + #[cfg(all(feature = "truncate", feature = "dd"))] #[test] // FIXME: fix this test for FreeBSD fn test_ls_allocation_size() { @@ -115,18 +128,14 @@ fn test_ls_allocation_size() { .succeeds() .stdout_matches(&Regex::new("[^ ] 2 [^ ]").unwrap()); - #[cfg(all( - not(target_os = "freebsd"), - not(all(target_os = "android", target_pointer_width = "64")) - ))] + let fstype = get_filesystem_type(&scene.fixtures.subdir); let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = - (4096, 1024, 8192, "4.0M"); - - #[cfg(all(target_os = "android", target_pointer_width = "64"))] - let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = - (4100, 1025, 8200, "4.1M"); // FIXME: Investigate where this difference comes from. Is it OS or filesystem? + if fstype != "f2fs" { + (4096, 1024, 8192, "4.0M") + } else { + (4100, 1025, 8200, "4.1M") + }; - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -142,7 +151,6 @@ fn test_ls_allocation_size() { // block size is 0 whereas size/len is 4194304 .stdout_contains("4194304"); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -176,7 +184,6 @@ fn test_ls_allocation_size() { assert_eq!(empty_file_len, file_with_holes_len); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("LS_BLOCK_SIZE", "8K") @@ -189,7 +196,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("512 zero-file"); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -201,7 +207,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -214,7 +219,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("4.2M zero-file"); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4096") @@ -226,7 +230,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("POSIXLY_CORRECT", "true") @@ -239,7 +242,6 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_8k} zero-file")); // -k should make 'ls' ignore the env var - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -252,7 +254,6 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_4k} zero-file")); // but manually specified blocksize overrides -k - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1k") @@ -264,7 +265,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -277,7 +277,6 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_1k} zero-file")); // si option should always trump the human-readable option - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1h") @@ -289,7 +288,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("4.2M zero-file"); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -301,7 +299,6 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_4m} zero-file")); - #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") From 155f9dccff45e9177bb2334eab5b4c6a0c7ef4a7 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 16:10:21 +0100 Subject: [PATCH 10/20] debug logging of fstype detection --- tests/by-util/test_ls.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 4d970cba11c..04e05d8a79e 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -20,7 +20,6 @@ use std::os::unix::io::IntoRawFd; use std::path::Path; #[cfg(not(windows))] use std::path::PathBuf; -use std::process::Stdio; use std::thread::sleep; use std::time::Duration; @@ -77,13 +76,14 @@ fn test_ls_ordering() { fn get_filesystem_type(path: &Path) -> String { let mut cmd = std::process::Command::new("stat"); cmd.arg("-f").arg(path); - cmd.stdout(Stdio::piped()); - let child = cmd.spawn().unwrap(); - let output = child.wait_with_output().unwrap(); + let output = cmd.output().unwrap(); let stdout_str = String::from_utf8_lossy(&output.stdout); let regex = Regex::new(r#"\sType: (?[^\s]+)"#).unwrap(); + println!("output of stat call ({:?}):\n{}", cmd, stdout_str); let m = regex.captures(&stdout_str).unwrap(); - m["fstype"].to_owned() + let fstype = m["fstype"].to_owned(); + println!("detected fstype: {}", fstype); + fstype } #[cfg(all(feature = "truncate", feature = "dd"))] From 33f1e39c155e653f1ba9c98d5c4e0fc2f4c1aad4 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 16:49:35 +0100 Subject: [PATCH 11/20] implement seperate fs detection for freebsd --- tests/by-util/test_ls.rs | 16 ++++++++++++++-- util/android-scripts/collect-info.sh | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 04e05d8a79e..be012d12f23 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -73,13 +73,25 @@ fn test_ls_ordering() { .stdout_matches(&Regex::new("some-dir1:\\ntotal 0").unwrap()); } +#[cfg(not(target_os = "freebsd"))] // stat on freebsd doesn't support "-f" fn get_filesystem_type(path: &Path) -> String { let mut cmd = std::process::Command::new("stat"); - cmd.arg("-f").arg(path); + cmd.args(["-f", "-c", "%T"]).arg(path); let output = cmd.output().unwrap(); let stdout_str = String::from_utf8_lossy(&output.stdout); - let regex = Regex::new(r#"\sType: (?[^\s]+)"#).unwrap(); println!("output of stat call ({:?}):\n{}", cmd, stdout_str); + stdout_str.into_owned() +} + +#[cfg(target_os = "freebsd")] // df -T is not supported by android +fn get_filesystem_type(path: &Path) -> String { + let mut cmd = std::process::Command::new("df"); + cmd.args(["-PT"]).arg(path); + let output = cmd.output().unwrap(); + let stdout_str = String::from_utf8_lossy(&output.stdout); + println!("output of stat call ({:?}):\n{}", cmd, stdout_str); + let regex_str = r#"Filesystem\s+Type\s+.+[\r\n]+([^\s]+)\s+(?[^\s]+)\s+"#; + let regex = Regex::new(regex_str).unwrap(); let m = regex.captures(&stdout_str).unwrap(); let fstype = m["fstype"].to_owned(); println!("detected fstype: {}", fstype); diff --git a/util/android-scripts/collect-info.sh b/util/android-scripts/collect-info.sh index bf4d4ef3813..412423bd135 100644 --- a/util/android-scripts/collect-info.sh +++ b/util/android-scripts/collect-info.sh @@ -2,11 +2,11 @@ # spell-checker:ignore nextest watchplus PIPESTATUS -echo "system ressources - RAM:" +echo "system resources - RAM:" free -hm -echo "system ressources - CPU:" +echo "system resources - CPU:" lscpu -echo "system ressources - file systems:" +echo "system resources - file systems:" mount echo "$HOME" From 2fc269f9ed55838dfb7a7aeed85e9f47c84918e0 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 17:40:50 +0100 Subject: [PATCH 12/20] use uutils df to get fstype --- tests/by-util/test_ls.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index be012d12f23..780639f2c79 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -73,22 +73,11 @@ fn test_ls_ordering() { .stdout_matches(&Regex::new("some-dir1:\\ntotal 0").unwrap()); } -#[cfg(not(target_os = "freebsd"))] // stat on freebsd doesn't support "-f" -fn get_filesystem_type(path: &Path) -> String { - let mut cmd = std::process::Command::new("stat"); - cmd.args(["-f", "-c", "%T"]).arg(path); - let output = cmd.output().unwrap(); - let stdout_str = String::from_utf8_lossy(&output.stdout); - println!("output of stat call ({:?}):\n{}", cmd, stdout_str); - stdout_str.into_owned() -} - -#[cfg(target_os = "freebsd")] // df -T is not supported by android -fn get_filesystem_type(path: &Path) -> String { - let mut cmd = std::process::Command::new("df"); - cmd.args(["-PT"]).arg(path); - let output = cmd.output().unwrap(); - let stdout_str = String::from_utf8_lossy(&output.stdout); +fn get_filesystem_type(scene: &TestScenario, path: &Path) -> String { + let mut cmd = scene.ccmd("df"); + cmd.args(&["-PT"]).arg(path); + let output = cmd.succeeds(); + let stdout_str = String::from_utf8_lossy(&output.stdout()); println!("output of stat call ({:?}):\n{}", cmd, stdout_str); let regex_str = r#"Filesystem\s+Type\s+.+[\r\n]+([^\s]+)\s+(?[^\s]+)\s+"#; let regex = Regex::new(regex_str).unwrap(); @@ -98,7 +87,7 @@ fn get_filesystem_type(path: &Path) -> String { fstype } -#[cfg(all(feature = "truncate", feature = "dd"))] +#[cfg(all(feature = "truncate", feature = "dd", feature = "df"))] #[test] // FIXME: fix this test for FreeBSD fn test_ls_allocation_size() { let scene = TestScenario::new(util_name!()); @@ -140,7 +129,7 @@ fn test_ls_allocation_size() { .succeeds() .stdout_matches(&Regex::new("[^ ] 2 [^ ]").unwrap()); - let fstype = get_filesystem_type(&scene.fixtures.subdir); + let fstype = get_filesystem_type(&scene, &scene.fixtures.subdir); let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = if fstype != "f2fs" { (4096, 1024, 8192, "4.0M") From a0616577cf6a3384e17d8628dade5194300d21ab Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 17:49:54 +0100 Subject: [PATCH 13/20] special criterions for zfs --- tests/by-util/test_ls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 780639f2c79..8152f3325bb 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -131,10 +131,10 @@ fn test_ls_allocation_size() { let fstype = get_filesystem_type(&scene, &scene.fixtures.subdir); let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = - if fstype != "f2fs" { - (4096, 1024, 8192, "4.0M") - } else { - (4100, 1025, 8200, "4.1M") + match fstype.as_str() { + "f2fs" => (4100, 1025, 8200, "4.1M"), + "zfs" => (0, 1025, 8200, "4.1M"), + _ => (4096, 1024, 8192, "4.0M"), }; scene From 205d36aae12c44ddff11f655ca731d95b9a6f84c Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 18:03:22 +0100 Subject: [PATCH 14/20] fix space fillers check --- tests/by-util/test_ls.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 8152f3325bb..ca6832e5aa5 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -77,7 +77,7 @@ fn get_filesystem_type(scene: &TestScenario, path: &Path) -> String { let mut cmd = scene.ccmd("df"); cmd.args(&["-PT"]).arg(path); let output = cmd.succeeds(); - let stdout_str = String::from_utf8_lossy(&output.stdout()); + let stdout_str = String::from_utf8_lossy(output.stdout()); println!("output of stat call ({:?}):\n{}", cmd, stdout_str); let regex_str = r#"Filesystem\s+Type\s+.+[\r\n]+([^\s]+)\s+(?[^\s]+)\s+"#; let regex = Regex::new(regex_str).unwrap(); @@ -137,12 +137,15 @@ fn test_ls_allocation_size() { _ => (4096, 1024, 8192, "4.0M"), }; + let zero_file_size_4k_filler = " ".repeat(zero_file_size_4k.to_string().len() - 1); + scene .ucmd() .arg("-s1") .arg("some-dir1") .succeeds() - .stdout_is(format!("total {zero_file_size_4k}\n 0 empty-file\n 0 file-with-holes\n{zero_file_size_4k} zero-file\n")); + .stdout_is(format!("total {zero_file_size_4k}\n{zero_file_size_4k_filler}0 \ + empty-file\n{zero_file_size_4k_filler}0 file-with-holes\n{zero_file_size_4k} zero-file\n")); scene .ucmd() From 3c57515b45a0b941ac5f201dc5fc330a3ec05d20 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 18:07:08 +0100 Subject: [PATCH 15/20] use random numbers as file content to avoid compression of zfs --- tests/by-util/test_ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index ca6832e5aa5..53c082ae458 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -4071,7 +4071,7 @@ fn test_ls_block_size_override() { scene .ccmd("dd") - .arg("if=/dev/zero") + .arg("if=/dev/random") .arg("of=file") .arg("bs=1024") .arg("count=1") From 75b44240309f87c991241b09fe3ef2a2bc89f598 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 18:18:52 +0100 Subject: [PATCH 16/20] use random data to fill zero-file to avoid zfs compression --- tests/by-util/test_ls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 53c082ae458..0cdc53e3962 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -107,7 +107,7 @@ fn test_ls_allocation_size() { // fill empty file with zeros scene .ccmd("dd") - .arg("if=/dev/zero") + .arg("if=/dev/random") .arg("of=some-dir1/zero-file") .arg("bs=1024") .arg("count=4096") @@ -115,7 +115,7 @@ fn test_ls_allocation_size() { scene .ccmd("dd") - .arg("if=/dev/zero") + .arg("if=/dev/random") .arg("of=irregular-file") .arg("bs=1") .arg("count=777") @@ -133,7 +133,7 @@ fn test_ls_allocation_size() { let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = match fstype.as_str() { "f2fs" => (4100, 1025, 8200, "4.1M"), - "zfs" => (0, 1025, 8200, "4.1M"), + "zfs" => (4096, 1025, 8200, "4.1M"), _ => (4096, 1024, 8192, "4.0M"), }; From da6604e6f569e5fdc9ea641282d280e5394eff6e Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 18:57:41 +0100 Subject: [PATCH 17/20] remove zfs handling --- tests/by-util/test_ls.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 0cdc53e3962..e2f84e7eb45 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -133,7 +133,6 @@ fn test_ls_allocation_size() { let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = match fstype.as_str() { "f2fs" => (4100, 1025, 8200, "4.1M"), - "zfs" => (4096, 1025, 8200, "4.1M"), _ => (4096, 1024, 8192, "4.0M"), }; From 417022b7052d358f98c07b4a40553c15725e895e Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 19:02:54 +0100 Subject: [PATCH 18/20] revert freebsd related stuff --- tests/by-util/test_ls.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index e2f84e7eb45..3a98aad70a7 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -107,7 +107,7 @@ fn test_ls_allocation_size() { // fill empty file with zeros scene .ccmd("dd") - .arg("if=/dev/random") + .arg("if=/dev/zero") .arg("of=some-dir1/zero-file") .arg("bs=1024") .arg("count=4096") @@ -115,7 +115,7 @@ fn test_ls_allocation_size() { scene .ccmd("dd") - .arg("if=/dev/random") + .arg("if=/dev/zero") .arg("of=irregular-file") .arg("bs=1") .arg("count=777") @@ -132,12 +132,14 @@ fn test_ls_allocation_size() { let fstype = get_filesystem_type(&scene, &scene.fixtures.subdir); let (zero_file_size_4k, zero_file_size_1k, zero_file_size_8k, zero_file_size_4m) = match fstype.as_str() { + // apparently f2fs (flash friendly fs) accepts small overhead for better performance "f2fs" => (4100, 1025, 8200, "4.1M"), _ => (4096, 1024, 8192, "4.0M"), }; let zero_file_size_4k_filler = " ".repeat(zero_file_size_4k.to_string().len() - 1); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -154,6 +156,7 @@ fn test_ls_allocation_size() { // block size is 0 whereas size/len is 4194304 .stdout_contains("4194304"); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -187,6 +190,7 @@ fn test_ls_allocation_size() { assert_eq!(empty_file_len, file_with_holes_len); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("LS_BLOCK_SIZE", "8K") @@ -199,6 +203,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("512 zero-file"); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -210,6 +215,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -222,6 +228,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("4.2M zero-file"); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4096") @@ -233,6 +240,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("POSIXLY_CORRECT", "true") @@ -245,6 +253,7 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_8k} zero-file")); // -k should make 'ls' ignore the env var + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .env("BLOCK_SIZE", "4K") @@ -257,6 +266,7 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_4k} zero-file")); // but manually specified blocksize overrides -k + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1k") @@ -268,6 +278,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_1k} zero-file")); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -280,6 +291,7 @@ fn test_ls_allocation_size() { .stdout_contains(format!("{zero_file_size_1k} zero-file")); // si option should always trump the human-readable option + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1h") @@ -291,6 +303,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains("4.2M zero-file"); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -302,6 +315,7 @@ fn test_ls_allocation_size() { .stdout_contains("0 file-with-holes") .stdout_contains(format!("{zero_file_size_4m} zero-file")); + #[cfg(not(target_os = "freebsd"))] scene .ucmd() .arg("-s1") @@ -4070,7 +4084,7 @@ fn test_ls_block_size_override() { scene .ccmd("dd") - .arg("if=/dev/random") + .arg("if=/dev/zero") .arg("of=file") .arg("bs=1024") .arg("count=1") From 7de5a92a2f482358bba58167c3ed7ed080fefba3 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Wed, 13 Mar 2024 23:10:47 +0100 Subject: [PATCH 19/20] Option for inodes due to fs(erofs) uses u64::MAX numbers --- src/uu/df/src/table.rs | 83 +++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index e82873f3a2b..9bd9370c7c1 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -18,6 +18,9 @@ use uucore::fsext::{FsUsage, MountInfo}; use std::fmt; use std::ops::AddAssign; +use u128 as InodesIntT; +type MaybeInodesT = Option; + /// A row in the filesystem usage data table. /// /// A row comprises several pieces of information, including the @@ -58,13 +61,13 @@ pub(crate) struct Row { bytes_capacity: Option, /// Total number of inodes in the filesystem. - inodes: u64, + inodes: MaybeInodesT, /// Number of used inodes. - inodes_used: u64, + inodes_used: MaybeInodesT, /// Number of free inodes. - inodes_free: u64, + inodes_free: MaybeInodesT, /// Percentage of inodes that are used, given as a float between 0 and 1. /// @@ -85,14 +88,36 @@ impl Row { bytes_usage: None, #[cfg(target_os = "macos")] bytes_capacity: None, - inodes: 0, - inodes_used: 0, - inodes_free: 0, + inodes: Some(0), + inodes_used: Some(0), + inodes_free: Some(0), inodes_usage: None, } } } +fn checked_accumulation_op(accumulator: &MaybeInodesT, summand: &MaybeInodesT) -> MaybeInodesT { + let a = (*accumulator)?; + if let Some(s) = *summand { + if s > InodesIntT::MAX / 2 { + eprintln!("invalid inodes number ({s}) - filesystem will be ignored"); + *accumulator + } else { + a.checked_add(s) + } + } else { + *accumulator + } +} + +fn calc_inode_usage(inodes: MaybeInodesT, inodes_used: MaybeInodesT) -> Option { + if inodes? == 0 { + None + } else { + Some(inodes_used? as f64 / inodes? as f64) + } +} + impl AddAssign for Row { /// Sum the numeric values of two rows. /// @@ -102,8 +127,8 @@ impl AddAssign for Row { let bytes = self.bytes + rhs.bytes; let bytes_used = self.bytes_used + rhs.bytes_used; let bytes_avail = self.bytes_avail + rhs.bytes_avail; - let inodes = self.inodes.saturating_add(rhs.inodes); - let inodes_used = self.inodes_used.saturating_add(rhs.inodes_used); + let inodes = checked_accumulation_op(&self.inodes, &rhs.inodes); + let inodes_used = checked_accumulation_op(&self.inodes_used, &rhs.inodes_used); *self = Self { file: None, fs_device: "total".into(), @@ -125,12 +150,8 @@ impl AddAssign for Row { bytes_capacity: None, inodes, inodes_used, - inodes_free: self.inodes_free.saturating_add(rhs.inodes_free), - inodes_usage: if inodes == 0 { - None - } else { - Some(inodes_used as f64 / inodes as f64) - }, + inodes_free: checked_accumulation_op(&self.inodes_free, &rhs.inodes_free), + inodes_usage: calc_inode_usage(inodes, inodes_used), } } } @@ -178,9 +199,9 @@ impl From for Row { } else { Some(bavail as f64 / ((bused + bavail) as f64)) }, - inodes: files, - inodes_used: fused, - inodes_free: ffree, + inodes: Some(files as InodesIntT), + inodes_used: Some(fused as InodesIntT), + inodes_free: Some(ffree as InodesIntT), inodes_usage: if files == 0 { None } else { @@ -235,11 +256,15 @@ impl<'a> RowFormatter<'a> { /// Get a string giving the scaled version of the input number. /// /// The scaling factor is defined in the `options` field. - fn scaled_inodes(&self, size: u64) -> String { - if let Some(h) = self.options.human_readable { - to_magnitude_and_suffix(size.into(), SuffixType::HumanReadable(h)) + fn scaled_inodes(&self, maybe_size: MaybeInodesT) -> String { + if let Some(size) = maybe_size { + if let Some(h) = self.options.human_readable { + to_magnitude_and_suffix(size, SuffixType::HumanReadable(h)) + } else { + size.to_string() + } } else { - size.to_string() + "int_overflow".into() } } @@ -505,9 +530,9 @@ mod tests { #[cfg(target_os = "macos")] bytes_capacity: Some(0.5), - inodes: 10, - inodes_used: 2, - inodes_free: 8, + inodes: Some(10), + inodes_used: Some(2), + inodes_free: Some(8), inodes_usage: Some(0.2), } } @@ -698,9 +723,9 @@ mod tests { fs_device: "my_device".to_string(), fs_mount: "my_mount".to_string(), - inodes: 10, - inodes_used: 2, - inodes_free: 8, + inodes: Some(10), + inodes_used: Some(2), + inodes_free: Some(8), inodes_usage: Some(0.2), ..Default::default() @@ -721,7 +746,7 @@ mod tests { }; let row = Row { bytes: 100, - inodes: 10, + inodes: Some(10), ..Default::default() }; let fmt = RowFormatter::new(&row, &options, false); @@ -846,6 +871,6 @@ mod tests { let row = Row::from(d); - assert_eq!(row.inodes_used, 0); + assert_eq!(row.inodes_used, Some(0)); } } From 0332a42412ac1f33644dc48f6424362dfe889504 Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Thu, 14 Mar 2024 14:15:05 +0100 Subject: [PATCH 20/20] add unittests --- src/uu/df/src/table.rs | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 9bd9370c7c1..1c70eb19746 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -25,6 +25,7 @@ type MaybeInodesT = Option; /// /// A row comprises several pieces of information, including the /// filesystem device, the mountpoint, the number of bytes used, etc. +#[derive(Clone)] pub(crate) struct Row { /// The filename given on the command-line, if given. file: Option, @@ -873,4 +874,116 @@ mod tests { assert_eq!(row.inodes_used, Some(0)); } + + #[test] + fn test_row_accumulation_u64_overflow() { + let total = u64::MAX as super::InodesIntT; + let used1 = 3000 as super::InodesIntT; + let used2 = 50000 as super::InodesIntT; + + let mut row1 = Row { + inodes: Some(total), + inodes_used: Some(used1), + inodes_free: Some(total - used1), + ..Default::default() + }; + + let row2 = Row { + inodes: Some(total), + inodes_used: Some(used2), + inodes_free: Some(total - used2), + ..Default::default() + }; + + row1 += row2; + + assert_eq!(row1.inodes, Some(total * 2)); + assert_eq!(row1.inodes_used, Some(used1 + used2)); + assert_eq!(row1.inodes_free, Some(total * 2 - used1 - used2)); + } + + #[test] + fn test_row_accumulation_close_to_u128_overflow() { + let total = u128::MAX as super::InodesIntT / 2 - 1; + let used1 = total - 50000; + let used2 = total - 100000; + + let mut row1 = Row { + inodes: Some(total), + inodes_used: Some(used1), + inodes_free: Some(total - used1), + ..Default::default() + }; + + let row2 = Row { + inodes: Some(total), + inodes_used: Some(used2), + inodes_free: Some(total - used2), + ..Default::default() + }; + + row1 += row2; + + assert_eq!(row1.inodes, Some(total * 2)); + assert_eq!(row1.inodes_used, Some(used1 + used2)); + assert_eq!(row1.inodes_free, Some(total * 2 - used1 - used2)); + } + + #[test] + fn test_row_accumulation_and_usage_close_over_u128_overflow() { + let total = u128::MAX as super::InodesIntT / 2 - 1; + let used1 = total / 2; + let free1 = total - used1; + let used2 = total / 2 - 10; + let free2 = total - used2; + + let mut row1 = Row { + inodes: Some(total), + inodes_used: Some(used1), + inodes_free: Some(free1), + ..Default::default() + }; + + let row2 = Row { + inodes: Some(total), + inodes_used: Some(used2), + inodes_free: Some(free2), + ..Default::default() + }; + + row1 += row2.clone(); + + assert_eq!(row1.inodes, Some(total * 2)); + assert_eq!(row1.inodes_used, Some(used1 + used2)); + assert_eq!(row1.inodes_free, Some(free1 + free2)); + assert_eq!(row1.inodes_usage, Some(0.5)); + + row1 += row2.clone(); + + assert_eq!(row1.inodes, None); // total * 3 + assert_eq!(row1.inodes_used, Some(used1 + used2 * 2)); + assert_eq!(row1.inodes_free, Some(free1 + free2 * 2)); + assert_eq!(row1.inodes_usage, None); + + row1 += row2.clone(); + + assert_eq!(row1.inodes, None); // total * 4 + assert_eq!(row1.inodes_used, Some(used1 + used2 * 3)); // used * 4 + assert_eq!(row1.inodes_free, None); // free * 4 + assert_eq!(row1.inodes_usage, None); + + row1 += row2.clone(); + + assert_eq!(row1.inodes, None); // total * 5 + assert_eq!(row1.inodes_used, None); // used * 5 + assert_eq!(row1.inodes_free, None); // free * 5 + assert_eq!(row1.inodes_usage, None); + + row1 += row2.clone(); + + assert_eq!(row1.inodes, None); // total * 6 + assert_eq!(row1.inodes_used, None); // used * 6 + assert_eq!(row1.inodes_free, None); // free * 6 + assert_eq!(row1.inodes_usage, None); + } }