SUPERCILEX / fuc

Modern, performance focused unix commands
Apache License 2.0
340 stars 8 forks source link

Neverending copy to sshfs mount #26

Closed zDEFz closed 10 months ago

zDEFz commented 11 months ago

I tried cpz locally, to find it was faster. When I tried to copy to a sshfs mount, it just never finished.

My command issued was cpz mainrig-MS-7D51_amd64_2023-10-10_2141.iso /sshfsmounts/20tb-1-raid1c3/iso/ And I used the cargo build of cpz 1.1.9 Rsync does for instance finish in like 10 minutes.

I went to the shower, had a workout and came back to see it hadn't sent any signal that it was finished. And the utilization was at 0.0% too.

What can I do to provide more info?

SUPERCILEX commented 11 months ago

This might be a rust bug because if you're copying a single file then that call is supposed to simply be forwarded to the rust stdlib.

Can you run cargo uninstall cpz; cargo install --git https://github.com/SUPERCILEX/fuc.git --branch tmp cpz? Then try copying the file again and paste the output.

zDEFz commented 11 months ago

It does not like, stop copying.

After 20 minutes however I stopped the copy. It only had around 5.8 GB transferred. In rsync, thats worth around one minute of time.

And I'm pretty sure normal cp is faster, too...

Maybe might be important to mention that the buffer on the receiving end is small so it ranges from 280MB/s to 13MB/s in bursts

cpz mainrig-MS-7D51_amd64_2023-10-10_2141.iso /sshfsmounts/20tb-1-raid1c3/iso/ [fuc_engine/src/ops/copy.rs:77] &from = "mainrig-MS-7D51_amd64_2023-10-10_2141.iso" [fuc_engine/src/ops/copy.rs:77] &to = "/sshfsmounts/20tb-1-raid1c3/iso/mainrig-MS-7D51_amd64_2023-10-10_2141.iso" [fuc_engine/src/ops/copy.rs:77] force = false [fuc_engine/src/ops/copy.rs:98] &from_metadata = Metadata { file_type: FileType( FileType { mode: 33188, }, ), is_dir: false, is_file: true, permissions: Permissions( FilePermissions { mode: 33188, }, ), modified: Ok( SystemTime { tv_sec: 1696967004, tv_nsec: 760134226, }, ), accessed: Ok( SystemTime { tv_sec: 1696967174, tv_nsec: 118295173, }, ), created: Ok( SystemTime { tv_sec: 1696966916, tv_nsec: 311065690, }, ), .. } [fuc_engine/src/ops/copy.rs:100] "create parent" = "create parent" [fuc_engine/src/ops/copy.rs:105] "before" = "before" [fuc_engine/src/ops/copy.rs:127] "expected" = "expected" ^C^C

SUPERCILEX commented 11 months ago

Fascinating, so we hit this line: https://github.com/SUPERCILEX/fuc/commit/dfb15c5900899513f2b0858671e489e1151a8b02#diff-4a92015d39fa1a593641703241942da796fccb42a9caa65889d886d3227de526R128

That means a rust bug I'm pretty sure. Need to double check.

SUPERCILEX commented 11 months ago

Ok, I assume you're on linux x86-64? I've built a binary with a custom version of the rust stdlib to see if we can find where things freeze.

cpz.zip

Same deal as last time: run and post logs. :)


Patch for reference:

diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index c44223a2f32..1f9fc00ad94 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2050,6 +2050,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
+    println!("inside stdlib copy");
     fs_imp::copy(from.as_ref(), to.as_ref())
 }

diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index 57d226a3771..fb82ad85cdc 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -79,10 +79,13 @@ pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W)
     R: Read,
     W: Write,
 {
+    println!("inside generic_copy");
     let read_buf = BufferedReaderSpec::buffer_size(reader);
     let write_buf = BufferedWriterSpec::buffer_size(writer);
+    println!("made buffers");

     if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf {
+        println!("copy_to");
         return BufferedReaderSpec::copy_to(reader, writer);
     }

@@ -121,6 +124,7 @@ fn buffer_size(&self) -> usize {
     }

     fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
+        println!("nah");
         let len = self.len();
         to.write_all(self)?;
         *self = &self[len..];
@@ -162,6 +166,7 @@ fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
             // if the buffer is empty, even for empty slices.
             // It can't be called directly here since specialization prevents us
             // from adding I: Read
+            println!("don't think this will be executed");
             match self.read(&mut []) {
                 Ok(_) => {}
                 Err(e) if e.is_interrupted() => continue,
@@ -209,6 +214,7 @@ fn buffer_size(&self) -> usize {
     }

     fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
+        println!("don't think this will be executed 2");
         if self.capacity() < DEFAULT_BUF_SIZE {
             return stack_buffer_copy(reader, self);
         }
@@ -300,6 +306,7 @@ fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
     reader: &mut R,
     writer: &mut W,
 ) -> Result<u64> {
+    println!("stack_buffer_copy");
     let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
     let mut buf: BorrowedBuf<'_> = buf.into();

diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index e1c58f2ba3c..3899fa91178 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1788,9 +1788,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {

 #[cfg(any(target_os = "linux", target_os = "android"))]
 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    println!("inside sys linux cfg copy");
+
     let (mut reader, reader_metadata) = open_from(from)?;
+    println!("rm: {reader_metadata:?}");
+
     let max_len = u64::MAX;
     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
+    println!("opened dest");

     use super::kernel_copy::{copy_regular_files, CopyResult};

diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
index 18acd5ecccd..6ebaa43e630 100644
--- a/library/std/src/sys/unix/kernel_copy.rs
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -570,6 +570,7 @@ fn copy_file_range(
             flags: libc::c_uint
         ) -> libc::ssize_t
     }
+    println!("inside copy_regular_files");

     match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) {
         NOT_PROBED => {
@@ -590,10 +591,12 @@ fn copy_file_range(
         UNAVAILABLE => return CopyResult::Fallback(0),
         _ => {}
     };
+    println!("finished probe");

     let mut written = 0u64;
     while written < max_len {
         let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
+        println!("bytes_to_copy: {bytes_to_copy:?}, {max_len:?}, {written:?}");
         // cap to 1GB chunks in case u64::MAX is passed as max_len and the file has a non-zero seek position
         // this allows us to copy large chunks without hitting EOVERFLOW,
         // unless someone sets a file offset close to u64::MAX - 1GB, in which case a fallback would be required
@@ -603,6 +606,8 @@ fn copy_file_range(
             // because copy_file_range adjusts the file offset automatically
             cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0))
         };
+        println!("bytes_to_copy after copy: {bytes_to_copy:?}");
+        println!("result: {copy_result:?}");

         match copy_result {
             Ok(0) if written == 0 => {
@@ -616,6 +621,7 @@ fn copy_file_range(
             Ok(0) => return CopyResult::Ended(written), // reached EOF
             Ok(ret) => written += ret as u64,
             Err(err) => {
+                println!("err: {:?}", err.raw_os_error());
                 return match err.raw_os_error() {
                     // when file offset + max_length > u64::MAX
                     Some(EOVERFLOW) => CopyResult::Fallback(written),
SUPERCILEX commented 10 months ago

Closing due to inactivity. Since this bug occurs inside Rust, my guess is that there is a bug with the copy_file_range syscall on older kernels.