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:
commit
3f3b12ebcc
1 changed files with 26 additions and 12 deletions
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue