Răsfoiți Sursa

Introduced the `WriteDual` trait and used it in `Blocktree::write`.

Matthew Carr 2 ani în urmă
părinte
comite
e487c5902a

+ 7 - 1
crates/btlib/src/accessor.rs

@@ -3,7 +3,7 @@ use std::io::{Read, Seek, SeekFrom, Write};
 
 use crate::{
     sectored_buf::SectoredBuf, BlockMeta, Cursor, Decompose, FlushMeta, MetaAccess, Positioned,
-    ReadDual, Result, SecretStream, Sectored, Split, TryCompose, TrySeek,
+    ReadDual, Result, SecretStream, Sectored, Split, TryCompose, TrySeek, WriteDual,
 };
 
 pub use private::Accessor;
@@ -94,6 +94,12 @@ mod private {
         }
     }
 
+    impl<T: ReadAt + WriteAt + MetaAccess> WriteDual for Accessor<T> {
+        fn write_from<R: Read>(&mut self, read: R, count: usize) -> std::io::Result<usize> {
+            self.inner.write_from(read, count)
+        }
+    }
+
     impl<T: ReadAt + AsRef<BlockMeta> + Size> ReadDual for Accessor<T> {
         fn read_into<W: Write>(&mut self, write: W, count: usize) -> std::io::Result<usize> {
             self.inner.read_into(write, count)

+ 7 - 32
crates/btlib/src/blocktree.rs

@@ -30,7 +30,7 @@ use crate::{
     error::{BtErr, DisplayErr, IoErr},
     BlockAccessor, BlockError, BlockMeta, BlockOpenOptions, BlockPath, BlockReader, BlockRecord,
     BoxInIoErr, DirEntry, DirEntryKind, Directory, Epoch, FileBlock, FlushMeta, MetaAccess,
-    MetaReader, Positioned, Principaled, ReadDual, Result, SeekFromExt, Split, TrySeek,
+    MetaReader, Positioned, Principaled, ReadDual, Result, SeekFromExt, Split, TrySeek, WriteDual,
 };
 
 pub use private::{Blocktree, ModeAuthorizer};
@@ -1038,7 +1038,7 @@ mod private {
             handle: Self::Handle,
             r: &mut dyn fuse_backend_rs::api::filesystem::ZeroCopyReader,
             size: u32,
-            _offset: u64,
+            offset: u64,
             _lock_owner: Option<u64>,
             _delayed_write: bool,
             // `flags` and `fuse_flags` are the arguments that were passed to `open` when this
@@ -1053,38 +1053,13 @@ mod private {
                     "file is readonly",
                 ));
             }
-            let mut size: usize = size.try_into().box_err()?;
+            let size: usize = size.try_into().box_err()?;
             self.access_block_mut(inode, handle, |block| {
-                let mut buf = [0u8; crate::SECTOR_SZ_DEFAULT];
-                let mut written = 0;
-                while size > 0 {
-                    let read = match r.read(&mut buf) {
-                        Ok(size) => size,
-                        Err(err) => {
-                            if written > 0 {
-                                error!("error while reading: {err}");
-                                return Ok(written);
-                            } else {
-                                return Err(err.into());
-                            }
-                        }
-                    };
-                    if 0 == read {
-                        break;
-                    }
-                    let filled = &buf[..read];
-                    size -= read;
-                    if let Err(err) = block.write_all(filled) {
-                        if written > 0 {
-                            error!("error while writing: {err}");
-                            return Ok(written);
-                        } else {
-                            return Err(err.into());
-                        }
-                    }
-                    written += filled.len();
+                let pos = block.pos() as u64;
+                if offset != pos {
+                    block.seek(SeekFrom::Start(offset))?;
                 }
-                Ok(written)
+                block.write_from(r, size).bterr()
             })
             .io_err()
         }

+ 4 - 0
crates/btlib/src/lib.rs

@@ -224,6 +224,10 @@ trait ReadDual: Read {
     fn read_into<W: Write>(&mut self, write: W, count: usize) -> io::Result<usize>;
 }
 
+trait WriteDual: Write {
+    fn write_from<R: Read>(&mut self, read: R, count: usize) -> io::Result<usize>;
+}
+
 /// Trait for streams which can efficiently and infallibly return their current position.
 trait Positioned {
     /// Returns the position of this stream (byte offset relative to the beginning).

+ 99 - 9
crates/btlib/src/sectored_buf.rs

@@ -4,15 +4,13 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
 
 use crate::{
     bterr, suppress_err_if_non_zero, BlockError, BlockMeta, BoxInIoErr, Decompose, MetaAccess,
-    Positioned, ReadDual, ReadExt, Result, Sectored, SizeExt, Split, TryCompose, TrySeek,
-    EMPTY_SLICE,
+    Positioned, ReadDual, ReadExt, Result, Sectored, SeekFromExt, SizeExt, Split, TryCompose,
+    TrySeek, WriteDual, EMPTY_SLICE,
 };
 
 pub use private::SectoredBuf;
 
 mod private {
-    use crate::SeekFromExt;
-
     use super::*;
 
     /// A stream which buffers writes and read such that the inner stream only sees reads and writes
@@ -51,7 +49,13 @@ mod private {
             let end = $self.buf_end();
             if pos == end {
                 match $self.fill_internal_buf() {
-                    Ok(byte_ct) => Ok(&$self.buf[..byte_ct]),
+                    Ok(nread) => {
+                        if nread > 0 {
+                            Ok(&$self.buf[..$self.buf_end()])
+                        } else {
+                            Ok(&$self.buf[..0])
+                        }
+                    }
                     Err(err) => Err(err),
                 }
             } else {
@@ -192,10 +196,7 @@ mod private {
             };
             while !src.is_empty() {
                 if dest.is_empty() {
-                    if let Err(err) = self.flush() {
-                        error!("A call to SectoredBuf::flush returned an error: {}", err);
-                        break;
-                    }
+                    suppress_err_if_non_zero!(src_len_start - src.len(), self.flush());
                     dest = &mut self.buf[..];
                 }
                 let sz = src.len().min(dest.len());
@@ -337,6 +338,39 @@ mod private {
         }
     }
 
+    impl<T: Read + Write + Seek + MetaAccess> WriteDual for SectoredBuf<T> {
+        fn write_from<R: Read>(&mut self, mut read: R, mut count: usize) -> io::Result<usize> {
+            let pos_start = self.pos;
+            let mut dest = {
+                let pos = self.buf_pos();
+                &mut self.buf[pos..]
+            };
+            let dest_len = dest.len();
+            dest = &mut dest[..dest_len.min(count)];
+            while count > 0 {
+                if dest.is_empty() {
+                    suppress_err_if_non_zero!(self.pos - pos_start, self.flush());
+                    dest = &mut self.buf[..];
+                    let dest_len = dest.len();
+                    dest = &mut dest[..dest_len.min(count)];
+                }
+                let nread = suppress_err_if_non_zero!(self.pos - pos_start, read.read(dest));
+                if 0 == nread {
+                    break;
+                }
+                self.dirty = true;
+                dest = &mut dest[nread..];
+                self.pos += nread;
+                count -= nread;
+                self.inner.mut_meta_body().access_secrets(|secrets| {
+                    secrets.size = secrets.size.max(self.pos as u64);
+                    Ok(())
+                })?;
+            }
+            Ok(self.pos - pos_start)
+        }
+    }
+
     impl<T: Read + Seek + AsRef<BlockMeta>> ReadDual for SectoredBuf<T> {
         fn read_into<W: Write>(&mut self, mut write: W, mut count: usize) -> io::Result<usize> {
             let pos_start = self.pos;
@@ -638,4 +672,60 @@ mod tests {
 
         assert_eq!([0, 1, 2, 3, 4, 5, 6, 7, 0], actual.into_inner());
     }
+
+    #[test]
+    fn write_from_full_cursor() {
+        const DATA: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+        let mut sectored = SectoredBuf::new()
+            .try_compose(SectoredCursor::new(Vec::new(), DATA.len()))
+            .unwrap();
+
+        let written = sectored
+            .write_from(BtCursor::new(DATA), DATA.len())
+            .unwrap();
+        assert_eq!(DATA.len(), written);
+        sectored.flush().unwrap();
+
+        assert_eq!(&DATA, sectored.into_inner().into_inner().as_slice());
+    }
+
+    #[test]
+    fn write_from_count_limits_bytes_read() {
+        const DATA: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+        let mut sectored = SectoredBuf::new()
+            .try_compose(SectoredCursor::new(Vec::new(), DATA.len()))
+            .unwrap();
+        let mut cursor = BtCursor::new(DATA);
+
+        let written = sectored.write_from(&mut cursor, DATA.len() / 2).unwrap();
+        assert_eq!(DATA.len() / 2, written);
+        sectored.flush().unwrap();
+
+        assert_eq!(
+            &[0, 1, 2, 3, 0, 0, 0, 0],
+            sectored.into_inner().into_inner().as_slice()
+        );
+        let mut remaining = Vec::new();
+        cursor.read_to_end(&mut remaining).unwrap();
+        assert_eq!(&[4, 5, 6, 7], remaining.as_slice());
+    }
+
+    #[test]
+    fn write_from_write_spans_multiple_sectors() {
+        const SECT_SZ: usize = 4;
+        const DATA: [u8; SECT_SZ + 1] = [0, 1, 2, 3, 4];
+        let mut sectored = SectoredBuf::new()
+            .try_compose(SectoredCursor::new(Vec::new(), SECT_SZ))
+            .unwrap();
+
+        let written = sectored
+            .write_from(BtCursor::new(DATA), DATA.len())
+            .unwrap();
+        assert_eq!(DATA.len(), written);
+        sectored.rewind().unwrap();
+        let mut actual = Vec::new();
+        sectored.read_to_end(&mut actual).unwrap();
+
+        assert_eq!(&[0, 1, 2, 3, 4], actual.as_slice());
+    }
 }