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

Merge pull request #1998 from paulotten/issue1969

Consider device id when comparing files
This commit is contained in:
Sylvestre Ledru 2021-04-05 22:34:12 +02:00 committed by GitHub
commit 3f3b12ebcc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -32,7 +32,7 @@ use winapi::um::minwinbase::{FileIdInfo, FileStandardInfo};
#[cfg(windows)] #[cfg(windows)]
use winapi::um::winbase::GetFileInformationByHandleEx; use winapi::um::winbase::GetFileInformationByHandleEx;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::winnt::FILE_ID_128; use winapi::um::winnt::{FILE_ID_128, ULONGLONG};
const NAME: &str = "du"; const NAME: &str = "du";
const SUMMARY: &str = "estimate file space usage"; const SUMMARY: &str = "estimate file space usage";
@ -58,12 +58,18 @@ struct Options {
separate_dirs: bool, separate_dirs: bool,
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
struct FileInfo {
file_id: u128,
dev_id: u64,
}
struct Stat { struct Stat {
path: PathBuf, path: PathBuf,
is_dir: bool, is_dir: bool,
size: u64, size: u64,
blocks: u64, blocks: u64,
inode: Option<u128>, inode: Option<FileInfo>,
created: u64, created: u64,
accessed: u64, accessed: u64,
modified: u64, modified: u64,
@ -73,13 +79,18 @@ impl Stat {
fn new(path: PathBuf) -> Result<Stat> { fn new(path: PathBuf) -> Result<Stat> {
let metadata = fs::symlink_metadata(&path)?; let metadata = fs::symlink_metadata(&path)?;
#[cfg(not(windows))]
let file_info = FileInfo {
file_id: metadata.ino() as u128,
dev_id: metadata.dev(),
};
#[cfg(not(windows))] #[cfg(not(windows))]
return Ok(Stat { return Ok(Stat {
path, path,
is_dir: metadata.is_dir(), is_dir: metadata.is_dir(),
size: metadata.len(), size: metadata.len(),
blocks: metadata.blocks() as u64, blocks: metadata.blocks() as u64,
inode: Some(metadata.ino() as u128), inode: Some(file_info),
created: metadata.mtime() as u64, created: metadata.mtime() as u64,
accessed: metadata.atime() as u64, accessed: metadata.atime() as u64,
modified: metadata.mtime() as u64, modified: metadata.mtime() as u64,
@ -88,14 +99,14 @@ impl Stat {
#[cfg(windows)] #[cfg(windows)]
let size_on_disk = get_size_on_disk(&path); let size_on_disk = get_size_on_disk(&path);
#[cfg(windows)] #[cfg(windows)]
let inode = get_inode(&path); let file_info = get_file_info(&path);
#[cfg(windows)] #[cfg(windows)]
Ok(Stat { Ok(Stat {
path, path,
is_dir: metadata.is_dir(), is_dir: metadata.is_dir(),
size: metadata.len(), size: metadata.len(),
blocks: size_on_disk / 1024 * 2, blocks: size_on_disk / 1024 * 2,
inode: inode, inode: file_info,
created: windows_time_to_unix_time(metadata.creation_time()), created: windows_time_to_unix_time(metadata.creation_time()),
accessed: windows_time_to_unix_time(metadata.last_access_time()), accessed: windows_time_to_unix_time(metadata.last_access_time()),
modified: windows_time_to_unix_time(metadata.last_write_time()), modified: windows_time_to_unix_time(metadata.last_write_time()),
@ -143,12 +154,12 @@ fn get_size_on_disk(path: &PathBuf) -> u64 {
} }
#[cfg(windows)] #[cfg(windows)]
fn get_inode(path: &PathBuf) -> Option<u128> { fn get_file_info(path: &PathBuf) -> Option<FileInfo> {
let mut inode = None; let mut result = None;
let file = match fs::File::open(path) { let file = match fs::File::open(path) {
Ok(file) => file, Ok(file) => file,
Err(_) => return inode, Err(_) => return result,
}; };
let handle = file.as_raw_handle(); let handle = file.as_raw_handle();
@ -165,11 +176,14 @@ fn get_inode(path: &PathBuf) -> Option<u128> {
); );
if success != 0 { if success != 0 {
inode = Some(std::mem::transmute::<FILE_ID_128, u128>(file_info.FileId)); result = Some(FileInfo {
file_id: std::mem::transmute::<FILE_ID_128, u128>(file_info.FileId),
dev_id: std::mem::transmute::<ULONGLONG, u64>(file_info.VolumeSerialNumber),
});
} }
} }
inode result
} }
fn unit_string_to_number(s: &str) -> Option<u64> { fn unit_string_to_number(s: &str) -> Option<u64> {
@ -239,7 +253,7 @@ fn du(
mut my_stat: Stat, mut my_stat: Stat,
options: &Options, options: &Options,
depth: usize, depth: usize,
inodes: &mut HashSet<u128>, inodes: &mut HashSet<FileInfo>,
) -> Box<dyn DoubleEndedIterator<Item = Stat>> { ) -> Box<dyn DoubleEndedIterator<Item = Stat>> {
let mut stats = vec![]; let mut stats = vec![];
let mut futures = vec![]; let mut futures = vec![];
@ -526,7 +540,7 @@ Try '{} --help' for more information.",
let path = PathBuf::from(&path_str); let path = PathBuf::from(&path_str);
match Stat::new(path) { match Stat::new(path) {
Ok(stat) => { Ok(stat) => {
let mut inodes: HashSet<u128> = HashSet::new(); let mut inodes: HashSet<FileInfo> = HashSet::new();
let iter = du(stat, &options, 0, &mut inodes); let iter = du(stat, &options, 0, &mut inodes);
let (_, len) = iter.size_hint(); let (_, len) = iter.size_hint();