1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

wc: better handle files in pseudo-filesystems

This commit is contained in:
zhitkoff 2023-11-26 17:01:22 -05:00 committed by Yury Zhytkou
parent ca024abe31
commit 054ca4a6b5
2 changed files with 29 additions and 5 deletions

View file

@ -2,6 +2,8 @@
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// cSpell:ignore sysconf
use crate::word_count::WordCount; use crate::word_count::WordCount;
use super::WordCountable; use super::WordCountable;
@ -11,7 +13,7 @@ use std::fs::OpenOptions;
use std::io::{self, ErrorKind, Read}; use std::io::{self, ErrorKind, Read};
#[cfg(unix)] #[cfg(unix)]
use libc::S_IFREG; use libc::{sysconf, S_IFREG, _SC_PAGESIZE};
#[cfg(unix)] #[cfg(unix)]
use nix::sys::stat; use nix::sys::stat;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
@ -87,12 +89,26 @@ pub(crate) fn count_bytes_fast<T: WordCountable>(handle: &mut T) -> (usize, Opti
// If stat.st_size = 0 then // If stat.st_size = 0 then
// - either the size is 0 // - either the size is 0
// - or the size is unknown. // - or the size is unknown.
// The second case happens for files in pseudo-filesystems. For // The second case happens for files in pseudo-filesystems.
// example with /proc/version and /sys/kernel/profiling. So, // For example with /proc/version.
// if it is 0 we don't report that and instead do a full read. // So, if it is 0 we don't report that and instead do a full read.
//
// Another thing to consider for files in pseudo-filesystems like /proc, /sys
// and similar is that they could report `st_size` greater than actual content.
// For example /sys/kernel/profiling could report `st_size` equal to
// system page size (typically 4096 on 64bit system), while it's file content
// would count up only to a couple of bytes.
// This condition usually occurs for files in pseudo-filesystems like /proc, /sys
// that report `st_size` in the multiples of system page size.
// In such cases - fall back on full read
if (stat.st_mode as libc::mode_t & S_IFREG) != 0 && stat.st_size > 0 { if (stat.st_mode as libc::mode_t & S_IFREG) != 0 && stat.st_size > 0 {
let sys_page_size = unsafe { sysconf(_SC_PAGESIZE) as usize };
if stat.st_size as usize % sys_page_size > 0 {
// regular file or file from /proc, /sys and similar pseudo-filesystems
// with size that is NOT a multiple of system page size
return (stat.st_size as usize, None); return (stat.st_size as usize, None);
} }
}
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
{ {
// Else, if we're on Linux and our file is a FIFO pipe // Else, if we're on Linux and our file is a FIFO pipe

View file

@ -419,6 +419,14 @@ fn test_files_from_pseudo_filesystem() {
use pretty_assertions::assert_ne; use pretty_assertions::assert_ne;
let result = new_ucmd!().arg("-c").arg("/proc/cpuinfo").succeeds(); let result = new_ucmd!().arg("-c").arg("/proc/cpuinfo").succeeds();
assert_ne!(result.stdout_str(), "0 /proc/cpuinfo\n"); assert_ne!(result.stdout_str(), "0 /proc/cpuinfo\n");
let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-c").arg("/sys/kernel/profiling").succeeds();
let actual = at.read("/sys/kernel/profiling").len();
assert_eq!(
result.stdout_str(),
format!("{} /sys/kernel/profiling\n", actual)
);
} }
#[test] #[test]