1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 13:07:46 +00:00

mv: factor rename_with_fallback func into helpers

Factor out helper functions from the `rename_with_fallback()` function
so that there is one fallback helper per file type (symlink, directory,
or file). This doesn't change the functionality of `mv`, it is just a
re-organization of the code.
This commit is contained in:
Jeffrey Finkelstein 2025-01-28 22:35:17 -05:00
parent d99b7b31a6
commit 3b2db58a78

View file

@ -672,7 +672,7 @@ fn rename_with_fallback(
to: &Path, to: &Path,
multi_progress: Option<&MultiProgress>, multi_progress: Option<&MultiProgress>,
) -> io::Result<()> { ) -> io::Result<()> {
if let Err(err) = fs::rename(from, to) { fs::rename(from, to).or_else(|err| {
#[cfg(windows)] #[cfg(windows)]
const EXDEV: i32 = windows_sys::Win32::Foundation::ERROR_NOT_SAME_DEVICE as _; const EXDEV: i32 = windows_sys::Win32::Foundation::ERROR_NOT_SAME_DEVICE as _;
#[cfg(unix)] #[cfg(unix)]
@ -687,14 +687,59 @@ fn rename_with_fallback(
if !should_fallback { if !should_fallback {
return Err(err); return Err(err);
} }
// Get metadata without following symlinks // Get metadata without following symlinks
let metadata = from.symlink_metadata()?; let metadata = from.symlink_metadata()?;
let file_type = metadata.file_type(); let file_type = metadata.file_type();
if file_type.is_symlink() { if file_type.is_symlink() {
rename_symlink_fallback(from, to)?; rename_symlink_fallback(from, to)
} else if file_type.is_dir() { } else if file_type.is_dir() {
rename_dir_fallback(from, to, multi_progress)
} else {
rename_file_fallback(from, to)
}
})
}
/// Move the given symlink to the given destination. On Windows, dangling
/// symlinks return an error.
#[cfg(unix)]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
let path_symlink_points_to = fs::read_link(from)?;
unix::fs::symlink(path_symlink_points_to, to).and_then(|_| fs::remove_file(from))
}
#[cfg(windows)]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
let path_symlink_points_to = fs::read_link(from)?;
if path_symlink_points_to.exists() {
if path_symlink_points_to.is_dir() {
windows::fs::symlink_dir(&path_symlink_points_to, to)?;
} else {
windows::fs::symlink_file(&path_symlink_points_to, to)?;
}
fs::remove_file(from)
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"can't determine symlink type, since it is dangling",
))
}
}
#[cfg(not(any(windows, unix)))]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
let path_symlink_points_to = fs::read_link(from)?;
Err(io::Error::new(
io::ErrorKind::Other,
"your operating system does not support symlinks",
))
}
fn rename_dir_fallback(
from: &Path,
to: &Path,
multi_progress: Option<&MultiProgress>,
) -> io::Result<()> {
// We remove the destination directory if it exists to match the // We remove the destination directory if it exists to match the
// behavior of `fs::rename`. As far as I can tell, `fs_extra`'s // behavior of `fs::rename`. As far as I can tell, `fs_extra`'s
// `move_dir` would otherwise behave differently. // `move_dir` would otherwise behave differently.
@ -716,18 +761,14 @@ fn rename_with_fallback(
// (Move will probably fail due to permission error later?) // (Move will probably fail due to permission error later?)
let total_size = dir_get_size(from).ok(); let total_size = dir_get_size(from).ok();
let progress_bar = let progress_bar = match (multi_progress, total_size) {
if let (Some(multi_progress), Some(total_size)) = (multi_progress, total_size) { (Some(multi_progress), Some(total_size)) => {
let bar = ProgressBar::new(total_size).with_style( let template = "{msg}: [{elapsed_precise}] {wide_bar} {bytes:>7}/{total_bytes:7}";
ProgressStyle::with_template( let style = ProgressStyle::with_template(template).unwrap();
"{msg}: [{elapsed_precise}] {wide_bar} {bytes:>7}/{total_bytes:7}", let bar = ProgressBar::new(total_size).with_style(style);
)
.unwrap(),
);
Some(multi_progress.add(bar)) Some(multi_progress.add(bar))
} else { }
None (_, _) => None,
}; };
#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))] #[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
@ -747,16 +788,19 @@ fn rename_with_fallback(
#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))] #[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
fsxattr::apply_xattrs(to, xattrs)?; fsxattr::apply_xattrs(to, xattrs)?;
if let Err(err) = result { match result {
return match err.kind { Err(err) => match err.kind {
fs_extra::error::ErrorKind::PermissionDenied => Err(io::Error::new( fs_extra::error::ErrorKind::PermissionDenied => Err(io::Error::new(
io::ErrorKind::PermissionDenied, io::ErrorKind::PermissionDenied,
"Permission denied", "Permission denied",
)), )),
_ => Err(io::Error::other(format!("{err:?}"))), _ => Err(io::Error::new(io::ErrorKind::Other, format!("{err:?}"))),
}; },
_ => Ok(()),
} }
} else { }
fn rename_file_fallback(from: &Path, to: &Path) -> io::Result<()> {
if to.is_symlink() { if to.is_symlink() {
fs::remove_file(to).map_err(|err| { fs::remove_file(to).map_err(|err| {
let to = to.to_string_lossy(); let to = to.to_string_lossy();
@ -776,42 +820,6 @@ fn rename_with_fallback(
.and_then(|_| fs::remove_file(from))?; .and_then(|_| fs::remove_file(from))?;
#[cfg(any(target_os = "macos", target_os = "redox", not(unix)))] #[cfg(any(target_os = "macos", target_os = "redox", not(unix)))]
fs::copy(from, to).and_then(|_| fs::remove_file(from))?; fs::copy(from, to).and_then(|_| fs::remove_file(from))?;
}
}
Ok(())
}
/// Move the given symlink to the given destination. On Windows, dangling
/// symlinks return an error.
#[inline]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
let path_symlink_points_to = fs::read_link(from)?;
#[cfg(unix)]
{
unix::fs::symlink(path_symlink_points_to, to).and_then(|_| fs::remove_file(from))?;
}
#[cfg(windows)]
{
if path_symlink_points_to.exists() {
if path_symlink_points_to.is_dir() {
windows::fs::symlink_dir(&path_symlink_points_to, to)?;
} else {
windows::fs::symlink_file(&path_symlink_points_to, to)?;
}
fs::remove_file(from)?;
} else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"can't determine symlink type, since it is dangling",
));
}
}
#[cfg(not(any(windows, unix)))]
{
return Err(io::Error::other(
"your operating system does not support symlinks",
));
}
Ok(()) Ok(())
} }