1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #4216 from jfinkels/cp-preserve-hardlinks-readability

cp: improve readability of preserve_hardlinks()
This commit is contained in:
Sylvestre Ledru 2022-12-06 08:44:44 +01:00 committed by GitHub
commit 24340665ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 49 deletions

View file

@ -213,9 +213,8 @@ fn copy_direntry(
// If the source is not a directory, then we need to copy the file. // If the source is not a directory, then we need to copy the file.
if !source_absolute.is_dir() { if !source_absolute.is_dir() {
if preserve_hard_links { if preserve_hard_links {
let mut found_hard_link = false;
let dest = local_to_target.as_path().to_path_buf(); let dest = local_to_target.as_path().to_path_buf();
preserve_hardlinks(hard_links, &source_absolute, &dest, &mut found_hard_link)?; let found_hard_link = preserve_hardlinks(hard_links, &source_absolute, &dest)?;
if !found_hard_link { if !found_hard_link {
match copy_file( match copy_file(
progress_bar, progress_bar,

View file

@ -891,6 +891,16 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
Ok((paths, target)) Ok((paths, target))
} }
/// Get the inode information for a file.
fn get_inode(file_info: &FileInformation) -> u64 {
#[cfg(unix)]
let result = file_info.inode();
#[cfg(windows)]
let result = file_info.file_index();
result
}
#[cfg(target_os = "redox")]
fn preserve_hardlinks( fn preserve_hardlinks(
hard_links: &mut Vec<(String, u64)>, hard_links: &mut Vec<(String, u64)>,
source: &std::path::Path, source: &std::path::Path,
@ -898,53 +908,47 @@ fn preserve_hardlinks(
found_hard_link: &mut bool, found_hard_link: &mut bool,
) -> CopyResult<()> { ) -> CopyResult<()> {
// Redox does not currently support hard links // Redox does not currently support hard links
#[cfg(not(target_os = "redox"))] Ok(())
{ }
if !source.is_dir() {
let info = match FileInformation::from_path(source, false) {
Ok(info) => info,
Err(e) => {
return Err(format!("cannot stat {}: {}", source.quote(), e,).into());
}
};
#[cfg(unix)] /// Hard link a pair of files if needed _and_ record if this pair is a new hard link.
let inode = info.inode(); #[cfg(not(target_os = "redox"))]
fn preserve_hardlinks(
#[cfg(windows)] hard_links: &mut Vec<(String, u64)>,
let inode = info.file_index(); source: &std::path::Path,
dest: &std::path::Path,
let nlinks = info.number_of_links(); ) -> CopyResult<bool> {
let info = FileInformation::from_path(source, false)
for hard_link in hard_links.iter() { .context(format!("cannot stat {}", source.quote()))?;
if hard_link.1 == inode { let inode = get_inode(&info);
// Consider the following files: let nlinks = info.number_of_links();
// let mut found_hard_link = false;
// * `src/f` - a regular file for (link, link_inode) in hard_links.iter() {
// * `src/link` - a hard link to `src/f` if *link_inode == inode {
// * `dest/src/f` - a different regular file // Consider the following files:
// //
// In this scenario, if we do `cp -a src/ dest/`, it is // * `src/f` - a regular file
// possible that the order of traversal causes `src/link` // * `src/link` - a hard link to `src/f`
// to get copied first (to `dest/src/link`). In that case, // * `dest/src/f` - a different regular file
// in order to make sure `dest/src/link` is a hard link to //
// `dest/src/f` and `dest/src/f` has the contents of // In this scenario, if we do `cp -a src/ dest/`, it is
// `src/f`, we delete the existing file to allow the hard // possible that the order of traversal causes `src/link`
// linking. // to get copied first (to `dest/src/link`). In that case,
if file_or_link_exists(dest) && file_or_link_exists(Path::new(&hard_link.0)) { // in order to make sure `dest/src/link` is a hard link to
std::fs::remove_file(dest)?; // `dest/src/f` and `dest/src/f` has the contents of
} // `src/f`, we delete the existing file to allow the hard
// linking.
std::fs::hard_link(hard_link.0.clone(), dest).unwrap(); if file_or_link_exists(dest) && file_or_link_exists(Path::new(link)) {
*found_hard_link = true; std::fs::remove_file(dest)?;
}
}
if !(*found_hard_link) && nlinks > 1 {
hard_links.push((dest.to_str().unwrap().to_string(), inode));
} }
std::fs::hard_link(link, dest).unwrap();
found_hard_link = true;
} }
} }
Ok(()) if !found_hard_link && nlinks > 1 {
hard_links.push((dest.to_str().unwrap().to_string(), inode));
}
Ok(found_hard_link)
} }
/// Copy all `sources` to `target`. Returns an /// Copy all `sources` to `target`. Returns an
@ -986,11 +990,12 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
// FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases) // FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases)
show_warning!("source {} specified more than once", source.quote()); show_warning!("source {} specified more than once", source.quote());
} else { } else {
let mut found_hard_link = false; let found_hard_link = if preserve_hard_links && !source.is_dir() {
if preserve_hard_links {
let dest = construct_dest_path(source, target, &target_type, options)?; let dest = construct_dest_path(source, target, &target_type, options)?;
preserve_hardlinks(&mut hard_links, source, &dest, &mut found_hard_link)?; preserve_hardlinks(&mut hard_links, source, &dest)?
} } else {
false
};
if !found_hard_link { if !found_hard_link {
if let Err(error) = copy_source( if let Err(error) = copy_source(
&progress_bar, &progress_bar,