Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix symlink bugs #70

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Next Next commit
Add and handle follow option in CopyOptions
GPlaczek committed Mar 27, 2023
commit 000ac6f383c4afdf7f7c5fac2904b2dbf71e9205
4 changes: 4 additions & 0 deletions src/dir.rs
Original file line number Diff line number Diff line change
@@ -619,6 +619,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};
let mut result_copy: Result<u64>;
let mut work = true;
@@ -931,6 +932,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

if let Some(file_name) = file_name.to_str() {
@@ -1126,6 +1128,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

let mut result_copy: Result<u64>;
@@ -1268,6 +1271,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

if let Some(file_name) = file_name.to_str() {
70 changes: 55 additions & 15 deletions src/file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::error::{Error, ErrorKind, Result};
use std;
use std::fs::{remove_file, File};
use std::fs::{read_link, remove_file, File};
use std::io::{Read, Write};
#[cfg(unix)]
use std::os::unix::fs::symlink;
use std::path::Path;

// Options and flags which can be used to configure how a file will be copied or moved.
@@ -12,6 +14,8 @@ pub struct CopyOptions {
pub skip_exist: bool,
/// Sets buffer size for copy/move work only with receipt information about process work.
pub buffer_size: usize,
/// Follows the last symbolic link in the path
pub follow: bool,
}

impl CopyOptions {
@@ -30,6 +34,7 @@ impl CopyOptions {
overwrite: false,
skip_exist: false,
buffer_size: 64000, //64kb
follow: true,
}
}

@@ -94,26 +99,54 @@ where
Q: AsRef<Path>,
{
let from = from.as_ref();
if !from.exists() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
err!(&msg, ErrorKind::NotFound);
match from.try_exists() {
Ok(false) => {
if options.follow {
err!(
&if let Some(msg) = from.to_str() {
format!("Path \"{}\" is a broken symlink or doesn't exist!", msg)
} else {
String::from("Path is a broken symlink or doesn't exist!")
},
if from.is_symlink() {
ErrorKind::InvalidFile
} else {
ErrorKind::NotFound
}
)
}
}
err!(
"Path does not exist or you don't have access!",
ErrorKind::NotFound
);
Err(_) => {
if let Some(msg) = from.to_str() {
let msg = format!("You don't have access to \"{}\" path!", msg);
err!(&msg, ErrorKind::PermissionDenied);
}
err!("You don't have access!", ErrorKind::PermissionDenied);
}
_ => {}
}

if !from.is_file() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
if options.follow {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" is not a file!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
} else if !from.is_symlink() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{}\" is not a file or symlink!", msg);
err!(&msg, ErrorKind::InvalidFile);
}
err!("Path is not a file or symlink!", ErrorKind::InvalidFile);
}
err!("Path is not a file!", ErrorKind::InvalidFile);
}

if !options.overwrite && to.as_ref().exists() {
if !options.overwrite
&& (to.as_ref().exists() ||
// If the target is a broken symlink, it should not be overwritten
to.as_ref().is_symlink())
{
if options.skip_exist {
return Ok(0);
}
@@ -122,9 +155,16 @@ where
let msg = format!("Path \"{}\" exists", msg);
err!(&msg, ErrorKind::AlreadyExists);
}
err!("Path exists!", ErrorKind::AlreadyExists);
}

Ok(std::fs::copy(from, to)?)
if options.follow || from.is_file() {
Ok(std::fs::copy(from, to)?)
} else {
#[cfg(unix)]
symlink(read_link(from)?, &to)?;
Ok(to.as_ref().as_os_str().len() as u64)
}
}

/// Copies the contents of one file to another file with information about progress.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -354,6 +354,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

if let Some(file_name) = item.file_name() {
@@ -541,6 +542,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

if let Some(file_name) = item.file_name() {
@@ -666,6 +668,7 @@ where
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
follow: false,
};

if let Some(file_name) = item.file_name() {
6 changes: 2 additions & 4 deletions tests/file.rs
Original file line number Diff line number Diff line change
@@ -168,8 +168,7 @@ fn it_copy_source_not_exist() {
Err(err) => match err.kind {
ErrorKind::NotFound => {
let wrong_path = format!(
"Path \"{}\" does not exist or you don't have \
access!",
"Path \"{}\" is a broken symlink or doesn't exist!",
test_file.to_str().unwrap()
);
assert_eq!(wrong_path, err.to_string());
@@ -643,8 +642,7 @@ fn it_move_source_not_exist() {
Err(err) => match err.kind {
ErrorKind::NotFound => {
let wrong_path = format!(
"Path \"{}\" does not exist or you don't have \
access!",
"Path \"{}\" is a broken symlink or doesn't exist!",
test_file.to_str().unwrap()
);