diff --git a/dev/settings.html b/dev/settings.html index d66cdb06f..8241f8b4e 100644 --- a/dev/settings.html +++ b/dev/settings.html @@ -1 +1 @@ -
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::extra_unused_lifetimes)]
@@ -1912,15 +1708,11 @@
#[cfg(not(windows))]
use std::ffi::CString;
use std::fs::{self, File, OpenOptions};
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use std::io::Read;
use std::io::{self, stderr, stdin, Write};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, PermissionsExt};
-#[cfg(any(target_os = "linux", target_os = "android"))]
-use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use std::string::ToString;
@@ -1939,6 +1731,9 @@
};
use walkdir::WalkDir;
+mod platform;
+use platform::copy_on_write;
+
quick_error! {
#[derive(Debug)]
pub enum Error {
@@ -2114,16 +1909,6 @@
verbose: bool,
}
-// From /usr/include/linux/fs.h:
-// #define FICLONE _IOW(0x94, 9, int)
-#[cfg(any(target_os = "linux", target_os = "android"))]
-// Use a macro as libc::ioctl expects u32 or u64 depending on the arch
-macro_rules! FICLONE {
- () => {
- 0x40049409
- };
-}
-
static ABOUT: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.";
static LONG_HELP: &str = "";
static EXIT_ERR: i32 = 1;
@@ -3479,26 +3264,7 @@
} else if source_is_symlink {
copy_link(source, dest, symlinked_files)?;
} else {
- #[cfg(target_os = "macos")]
- copy_on_write_macos(
- source,
- dest,
- options.reflink_mode,
- options.sparse_mode,
- context,
- )?;
-
- #[cfg(any(target_os = "linux", target_os = "android"))]
- copy_on_write_linux(
- source,
- dest,
- options.reflink_mode,
- options.sparse_mode,
- context,
- )?;
-
- #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))]
- copy_no_cow_fallback(
+ copy_on_write(
source,
dest,
options.reflink_mode,
@@ -3554,180 +3320,6 @@
symlink_file(&link, &dest, &context_for(&link, &dest), symlinked_files)
}
-/// Copies `source` to `dest` for systems without copy-on-write
-#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))]
-fn copy_no_cow_fallback(
- source: &Path,
- dest: &Path,
- reflink_mode: ReflinkMode,
- sparse_mode: SparseMode,
- context: &str,
-) -> CopyResult<()> {
- if reflink_mode != ReflinkMode::Never {
- return Err("--reflink is only supported on linux and macOS"
- .to_string()
- .into());
- }
- if sparse_mode != SparseMode::Auto {
- return Err("--sparse is only supported on linux".to_string().into());
- }
-
- fs::copy(source, dest).context(context)?;
-
- Ok(())
-}
-
-/// Use the Linux `ioctl_ficlone` API to do a copy-on-write clone.
-///
-/// If `fallback` is true and there is a failure performing the clone,
-/// then this function performs a standard [`std::fs::copy`]. Otherwise,
-/// this function returns an error.
-#[cfg(any(target_os = "linux", target_os = "android"))]
-fn clone<P>(source: P, dest: P, fallback: bool) -> std::io::Result<()>
-where
- P: AsRef<Path>,
-{
- let src_file = File::open(&source)?;
- let dst_file = File::create(&dest)?;
- let src_fd = src_file.as_raw_fd();
- let dst_fd = dst_file.as_raw_fd();
- let result = unsafe { libc::ioctl(dst_fd, FICLONE!(), src_fd) };
- if result != 0 {
- if fallback {
- std::fs::copy(source, dest).map(|_| ())
- } else {
- Err(std::io::Error::last_os_error())
- }
- } else {
- Ok(())
- }
-}
-
-/// Perform a sparse copy from one file to another.
-#[cfg(any(target_os = "linux", target_os = "android"))]
-fn sparse_copy<P>(source: P, dest: P) -> std::io::Result<()>
-where
- P: AsRef<Path>,
-{
- use std::os::unix::prelude::MetadataExt;
-
- let mut src_file = File::open(source)?;
- let dst_file = File::create(dest)?;
- let dst_fd = dst_file.as_raw_fd();
-
- let size: usize = src_file.metadata()?.size().try_into().unwrap();
- if unsafe { libc::ftruncate(dst_fd, size.try_into().unwrap()) } < 0 {
- return Err(std::io::Error::last_os_error());
- }
-
- let blksize = dst_file.metadata()?.blksize();
- let mut buf: Vec<u8> = vec![0; blksize.try_into().unwrap()];
- let mut current_offset: usize = 0;
-
- // TODO Perhaps we can employ the "fiemap ioctl" API to get the
- // file extent mappings:
- // https://www.kernel.org/doc/html/latest/filesystems/fiemap.html
- while current_offset < size {
- let this_read = src_file.read(&mut buf)?;
- if buf.iter().any(|&x| x != 0) {
- unsafe {
- libc::pwrite(
- dst_fd,
- buf.as_ptr() as *const libc::c_void,
- this_read,
- current_offset.try_into().unwrap(),
- )
- };
- }
- current_offset += this_read;
- }
- Ok(())
-}
-
-/// Copies `source` to `dest` using copy-on-write if possible.
-#[cfg(any(target_os = "linux", target_os = "android"))]
-fn copy_on_write_linux(
- source: &Path,
- dest: &Path,
- reflink_mode: ReflinkMode,
- sparse_mode: SparseMode,
- context: &str,
-) -> CopyResult<()> {
- let result = match (reflink_mode, sparse_mode) {
- (ReflinkMode::Never, _) => std::fs::copy(source, dest).map(|_| ()),
- (ReflinkMode::Auto, SparseMode::Always) => sparse_copy(source, dest),
- (ReflinkMode::Auto, _) => clone(source, dest, true),
- (ReflinkMode::Always, SparseMode::Auto) => clone(source, dest, false),
- (ReflinkMode::Always, _) => {
- return Err("`--reflink=always` can be used only with --sparse=auto".into())
- }
- };
- result.context(context)?;
- Ok(())
-}
-
-/// Copies `source` to `dest` using copy-on-write if possible.
-#[cfg(target_os = "macos")]
-fn copy_on_write_macos(
- source: &Path,
- dest: &Path,
- reflink_mode: ReflinkMode,
- sparse_mode: SparseMode,
- context: &str,
-) -> CopyResult<()> {
- if sparse_mode != SparseMode::Auto {
- return Err("--sparse is only supported on linux".to_string().into());
- }
-
- // Extract paths in a form suitable to be passed to a syscall.
- // The unwrap() is safe because they come from the command-line and so contain non nul
- // character.
- let src = CString::new(source.as_os_str().as_bytes()).unwrap();
- let dst = CString::new(dest.as_os_str().as_bytes()).unwrap();
-
- // clonefile(2) was introduced in macOS 10.12 so we cannot statically link against it
- // for backward compatibility.
- let clonefile = CString::new("clonefile").unwrap();
- let raw_pfn = unsafe { libc::dlsym(libc::RTLD_NEXT, clonefile.as_ptr()) };
-
- let mut error = 0;
- if !raw_pfn.is_null() {
- // Call clonefile(2).
- // Safety: Casting a C function pointer to a rust function value is one of the few
- // blessed uses of `transmute()`.
- unsafe {
- let pfn: extern "C" fn(
- src: *const libc::c_char,
- dst: *const libc::c_char,
- flags: u32,
- ) -> libc::c_int = std::mem::transmute(raw_pfn);
- error = pfn(src.as_ptr(), dst.as_ptr(), 0);
- if std::io::Error::last_os_error().kind() == std::io::ErrorKind::AlreadyExists {
- // clonefile(2) fails if the destination exists. Remove it and try again. Do not
- // bother to check if removal worked because we're going to try to clone again.
- let _ = fs::remove_file(dest);
- error = pfn(src.as_ptr(), dst.as_ptr(), 0);
- }
- }
- }
-
- if raw_pfn.is_null() || error != 0 {
- // clonefile(2) is either not supported or it errored out (possibly because the FS does not
- // support COW).
- match reflink_mode {
- ReflinkMode::Always => {
- return Err(
- format!("failed to clone {:?} from {:?}: {}", source, dest, error).into(),
- )
- }
- ReflinkMode::Auto => fs::copy(source, dest).context(context)?,
- ReflinkMode::Never => fs::copy(source, dest).context(context)?,
- };
- }
-
- Ok(())
-}
-
/// Generate an error message if `target` is not the correct `target_type`
pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> {
match (target_type, target.is_dir()) {
diff --git a/dev/src/uu_cp/platform/linux.rs.html b/dev/src/uu_cp/platform/linux.rs.html
new file mode 100644
index 000000000..b6898c97a
--- /dev/null
+++ b/dev/src/uu_cp/platform/linux.rs.html
@@ -0,0 +1,222 @@
+linux.rs - source 1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+
// * This file is part of the uutils coreutils package.
+// *
+// * For the full copyright and license information, please view the LICENSE
+// * file that was distributed with this source code.
+// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap
+use std::fs::File;
+use std::io::Read;
+use std::os::unix::io::AsRawFd;
+use std::path::Path;
+
+use quick_error::ResultExt;
+
+use crate::{CopyResult, ReflinkMode, SparseMode};
+
+// From /usr/include/linux/fs.h:
+// #define FICLONE _IOW(0x94, 9, int)
+// Use a macro as libc::ioctl expects u32 or u64 depending on the arch
+macro_rules! FICLONE {
+ () => {
+ 0x40049409
+ };
+}
+
+/// Use the Linux `ioctl_ficlone` API to do a copy-on-write clone.
+///
+/// If `fallback` is true and there is a failure performing the clone,
+/// then this function performs a standard [`std::fs::copy`]. Otherwise,
+/// this function returns an error.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+fn clone<P>(source: P, dest: P, fallback: bool) -> std::io::Result<()>
+where
+ P: AsRef<Path>,
+{
+ let src_file = File::open(&source)?;
+ let dst_file = File::create(&dest)?;
+ let src_fd = src_file.as_raw_fd();
+ let dst_fd = dst_file.as_raw_fd();
+ let result = unsafe { libc::ioctl(dst_fd, FICLONE!(), src_fd) };
+ if result != 0 {
+ if fallback {
+ std::fs::copy(source, dest).map(|_| ())
+ } else {
+ Err(std::io::Error::last_os_error())
+ }
+ } else {
+ Ok(())
+ }
+}
+
+/// Perform a sparse copy from one file to another.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+fn sparse_copy<P>(source: P, dest: P) -> std::io::Result<()>
+where
+ P: AsRef<Path>,
+{
+ use std::os::unix::prelude::MetadataExt;
+
+ let mut src_file = File::open(source)?;
+ let dst_file = File::create(dest)?;
+ let dst_fd = dst_file.as_raw_fd();
+
+ let size: usize = src_file.metadata()?.size().try_into().unwrap();
+ if unsafe { libc::ftruncate(dst_fd, size.try_into().unwrap()) } < 0 {
+ return Err(std::io::Error::last_os_error());
+ }
+
+ let blksize = dst_file.metadata()?.blksize();
+ let mut buf: Vec<u8> = vec![0; blksize.try_into().unwrap()];
+ let mut current_offset: usize = 0;
+
+ // TODO Perhaps we can employ the "fiemap ioctl" API to get the
+ // file extent mappings:
+ // https://www.kernel.org/doc/html/latest/filesystems/fiemap.html
+ while current_offset < size {
+ let this_read = src_file.read(&mut buf)?;
+ if buf.iter().any(|&x| x != 0) {
+ unsafe {
+ libc::pwrite(
+ dst_fd,
+ buf.as_ptr() as *const libc::c_void,
+ this_read,
+ current_offset.try_into().unwrap(),
+ )
+ };
+ }
+ current_offset += this_read;
+ }
+ Ok(())
+}
+
+/// Copies `source` to `dest` using copy-on-write if possible.
+pub(crate) fn copy_on_write(
+ source: &Path,
+ dest: &Path,
+ reflink_mode: ReflinkMode,
+ sparse_mode: SparseMode,
+ context: &str,
+) -> CopyResult<()> {
+ let result = match (reflink_mode, sparse_mode) {
+ (ReflinkMode::Never, _) => std::fs::copy(source, dest).map(|_| ()),
+ (ReflinkMode::Auto, SparseMode::Always) => sparse_copy(source, dest),
+ (ReflinkMode::Auto, _) => clone(source, dest, true),
+ (ReflinkMode::Always, SparseMode::Auto) => clone(source, dest, false),
+ (ReflinkMode::Always, _) => {
+ return Err("`--reflink=always` can be used only with --sparse=auto".into())
+ }
+ };
+ result.context(context)?;
+ Ok(())
+}
+
+
\ No newline at end of file
diff --git a/dev/src/uu_cp/platform/mod.rs.html b/dev/src/uu_cp/platform/mod.rs.html
new file mode 100644
index 000000000..284aba676
--- /dev/null
+++ b/dev/src/uu_cp/platform/mod.rs.html
@@ -0,0 +1,38 @@
+mod.rs - source 1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
// * This file is part of the uutils coreutils package.
+// *
+// * For the full copyright and license information, please view the LICENSE
+// * file that was distributed with this source code.
+#[cfg(target_os = "macos")]
+mod macos;
+#[cfg(target_os = "macos")]
+pub(crate) use self::macos::copy_on_write;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux;
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub(crate) use self::linux::copy_on_write;
+
+#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))]
+mod other;
+#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))]
+pub(crate) use self::other::copy_on_write;
+
+
\ No newline at end of file
diff --git a/dev/src/uu_split/filenames.rs.html b/dev/src/uu_split/filenames.rs.html
index b5a80c91f..f34cb292f 100644
--- a/dev/src/uu_split/filenames.rs.html
+++ b/dev/src/uu_split/filenames.rs.html
@@ -209,6 +209,51 @@
209
210
211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
@@ -239,6 +284,7 @@
use crate::number::DynamicWidthNumber;
use crate::number::FixedWidthNumber;
use crate::number::Number;
+use uucore::error::{UResult, USimpleError};
/// The format to use for suffixes in the filename for each output chunk.
#[derive(Clone, Copy)]
@@ -330,19 +376,28 @@
additional_suffix: &'a str,
suffix_length: usize,
suffix_type: SuffixType,
- ) -> FilenameIterator<'a> {
+ suffix_start: usize,
+ ) -> UResult<FilenameIterator<'a>> {
let radix = suffix_type.radix();
let number = if suffix_length == 0 {
- Number::DynamicWidth(DynamicWidthNumber::new(radix))
+ Number::DynamicWidth(DynamicWidthNumber::new(radix, suffix_start))
} else {
- Number::FixedWidth(FixedWidthNumber::new(radix, suffix_length))
+ Number::FixedWidth(
+ FixedWidthNumber::new(radix, suffix_length, suffix_start).map_err(|_| {
+ USimpleError::new(
+ 1,
+ "numerical suffix start value is too large for the suffix length",
+ )
+ })?,
+ )
};
- FilenameIterator {
+
+ Ok(FilenameIterator {
prefix,
additional_suffix,
number,
first_iteration: true,
- }
+ })
}
}
@@ -372,36 +427,36 @@
#[test]
fn test_filename_iterator_alphabetic_fixed_width() {
- let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0).unwrap();
assert_eq!(it.next().unwrap(), "chunk_aa.txt");
assert_eq!(it.next().unwrap(), "chunk_ab.txt");
assert_eq!(it.next().unwrap(), "chunk_ac.txt");
- let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Alphabetic, 0).unwrap();
assert_eq!(it.nth(26 * 26 - 1).unwrap(), "chunk_zz.txt");
assert_eq!(it.next(), None);
}
#[test]
fn test_filename_iterator_numeric_fixed_width() {
- let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0).unwrap();
assert_eq!(it.next().unwrap(), "chunk_00.txt");
assert_eq!(it.next().unwrap(), "chunk_01.txt");
assert_eq!(it.next().unwrap(), "chunk_02.txt");
- let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 2, SuffixType::Decimal, 0).unwrap();
assert_eq!(it.nth(10 * 10 - 1).unwrap(), "chunk_99.txt");
assert_eq!(it.next(), None);
}
#[test]
fn test_filename_iterator_alphabetic_dynamic_width() {
- let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic, 0).unwrap();
assert_eq!(it.next().unwrap(), "chunk_aa.txt");
assert_eq!(it.next().unwrap(), "chunk_ab.txt");
assert_eq!(it.next().unwrap(), "chunk_ac.txt");
- let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Alphabetic, 0).unwrap();
assert_eq!(it.nth(26 * 25 - 1).unwrap(), "chunk_yz.txt");
assert_eq!(it.next().unwrap(), "chunk_zaaa.txt");
assert_eq!(it.next().unwrap(), "chunk_zaab.txt");
@@ -409,16 +464,51 @@
#[test]
fn test_filename_iterator_numeric_dynamic_width() {
- let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 0).unwrap();
assert_eq!(it.next().unwrap(), "chunk_00.txt");
assert_eq!(it.next().unwrap(), "chunk_01.txt");
assert_eq!(it.next().unwrap(), "chunk_02.txt");
- let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal);
+ let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 0).unwrap();
assert_eq!(it.nth(10 * 9 - 1).unwrap(), "chunk_89.txt");
assert_eq!(it.next().unwrap(), "chunk_9000.txt");
assert_eq!(it.next().unwrap(), "chunk_9001.txt");
}
+
+ #[test]
+ fn test_filename_iterator_numeric_suffix_decimal() {
+ let mut it = FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Decimal, 5).unwrap();
+ assert_eq!(it.next().unwrap(), "chunk_05.txt");
+ assert_eq!(it.next().unwrap(), "chunk_06.txt");
+ assert_eq!(it.next().unwrap(), "chunk_07.txt");
+ }
+
+ #[test]
+ fn test_filename_iterator_numeric_suffix_hex() {
+ let mut it =
+ FilenameIterator::new("chunk_", ".txt", 0, SuffixType::Hexadecimal, 9).unwrap();
+ assert_eq!(it.next().unwrap(), "chunk_09.txt");
+ assert_eq!(it.next().unwrap(), "chunk_0a.txt");
+ assert_eq!(it.next().unwrap(), "chunk_0b.txt");
+ }
+
+ #[test]
+ fn test_filename_iterator_numeric_suffix_err() {
+ let mut it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 999).unwrap();
+ assert_eq!(it.next().unwrap(), "chunk_999.txt");
+ assert!(it.next().is_none());
+
+ let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Decimal, 1000);
+ assert!(it.is_err());
+
+ let mut it =
+ FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0xfff).unwrap();
+ assert_eq!(it.next().unwrap(), "chunk_fff.txt");
+ assert!(it.next().is_none());
+
+ let it = FilenameIterator::new("chunk_", ".txt", 3, SuffixType::Hexadecimal, 0x1000);
+ assert!(it.is_err());
+ }
}