Browse Source

* Fixed a bug in the FuseFs::read method.
* Modified LocalFs::flush so that a flush with a read-only
handle returns successfully but does nothing.

Matthew Carr 2 years ago
parent
commit
ab989534a8

+ 13 - 3
crates/btfproto/src/local_fs.rs

@@ -1102,7 +1102,7 @@ mod private {
             offset: u64,
             size: u64,
         ) -> Result<BufGuard<C>> {
-            let table = table.clone().read_owned().await;
+            let table = table.read_owned().await;
             let entry = table.get(&inode).ok_or(Error::NotOpen(inode))?;
             let inode_guard = {
                 let inode_guard = entry.clone().read_owned().await;
@@ -1372,6 +1372,7 @@ mod private {
                     offset,
                     size,
                 } = msg;
+                debug!("read: inode {inode}, handle {handle}, offset {offset}, size {size}");
                 BufGuard::new(self.inodes.clone(), from, inode, handle, offset, size).await
             }
         }
@@ -1410,8 +1411,17 @@ mod private {
                 debug!("flush: inode {inode}, handle {handle}");
                 let table_guard = self.table_guard().await;
                 let mut value_guard = table_guard.write(inode).await?;
-                let mut block = value_guard.handle_guard_mut(from, handle).await?;
-                block.flush()?;
+                let mut handle_guard = match value_guard.handle_guard_mut(from, handle).await {
+                    Ok(guard) => guard,
+                    Err(err) => match err.downcast_ref::<Error>() {
+                        Some(Error::ReadOnlyHandle(..)) => {
+                            // We ignore attempts to flush read-only handles.
+                            return Ok(());
+                        }
+                        _ => return Err(err),
+                    },
+                };
+                handle_guard.flush()?;
                 Ok(())
             }
         }

+ 4 - 1
crates/btfproto/src/msg.rs

@@ -161,7 +161,8 @@ impl BitOr<libc::mode_t> for FileType {
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
 #[repr(i32)]
 /// The generators for the group of [Flag]s. These are mostly copied from [libc], save for
-/// several custom values.
+/// several custom values. Note that the presence of a flag in this enum does not guarantee
+/// it's supported.
 pub enum FlagValue {
     // Standard flags.
     ReadOnly = libc::O_RDONLY,
@@ -182,6 +183,8 @@ pub enum FlagValue {
     NoAtime = libc::O_NOATIME,
     CloseExec = libc::O_CLOEXEC,
     Rsync = libc::O_RSYNC,
+    Path = libc::O_PATH,
+    TmpFile = libc::O_TMPFILE,
     // Custom flags.
     /// Indicates that a process block should be created.
     Process = 0x01000000,

+ 18 - 8
crates/btfuse/src/fuse_fs.rs

@@ -279,14 +279,24 @@ mod private {
         ) -> IoResult<usize> {
             block_on(async move {
                 let path = self.path_from_luid(ctx.uid);
-                let msg = Read {
-                    inode,
-                    handle,
-                    offset,
-                    size: size as u64,
-                };
-                let guard = self.provider.read(path, msg).await?;
-                w.write(&guard)
+                let total_size: usize = size.try_into().display_err()?;
+                let mut total_written = 0;
+                while total_written < total_size {
+                    let msg = Read {
+                        inode,
+                        handle,
+                        offset,
+                        size: size as u64,
+                    };
+                    let guard = self.provider.read(path, msg).await?;
+                    let slice: &[u8] = &guard;
+                    if slice.is_empty() {
+                        break;
+                    }
+                    w.write_all(&guard)?;
+                    total_written += slice.len();
+                }
+                Ok(total_written)
             })
         }
 

+ 67 - 1
crates/btfuse/src/main.rs

@@ -119,8 +119,9 @@ mod test {
         ffi::{OsStr, OsString},
         fs::{
             create_dir, hard_link, metadata, read, read_dir, remove_dir, remove_file, rename,
-            set_permissions, write, Permissions, ReadDir,
+            set_permissions, write, OpenOptions, Permissions, ReadDir,
         },
+        io::{Read, Seek, SeekFrom, Write},
         os::unix::fs::PermissionsExt,
         thread::JoinHandle,
         time::Duration,
@@ -498,4 +499,69 @@ mod test {
         let actual = read(&dst_path).unwrap();
         assert_eq!(EXPECTED, actual)
     }
+
+    #[test]
+    fn write_read_with_file_struct() {
+        const FILE_NAME: &str = "big.dat";
+        const LEN: usize = btlib::SECTOR_SZ_DEFAULT + 1;
+        fn fill(buf: &mut Vec<u8>, value: u8) {
+            buf.clear();
+            buf.extend(std::iter::repeat(value).take(buf.capacity()));
+        }
+
+        let case = TestCase::new();
+        let file_path = case.mnt_dir().join(FILE_NAME);
+        let mut buf = vec![1u8; LEN];
+        let mut file = OpenOptions::new()
+            .create(true)
+            .read(true)
+            .write(true)
+            .open(&file_path)
+            .unwrap();
+
+        file.write_all(&buf).unwrap();
+        fill(&mut buf, 2);
+        file.write_all(&buf).unwrap();
+        file.rewind().unwrap();
+        let mut actual = vec![0u8; LEN];
+        file.read_exact(&mut actual).unwrap();
+
+        fill(&mut buf, 1);
+        assert_eq!(buf, actual);
+    }
+
+    //#[test]
+    #[allow(dead_code)]
+    /// KMC: This test is currently not working, and I've not been able to figure out why, not
+    /// reproduce it at a lower layer of the stack.
+    fn read_more_than_whats_buffered() {
+        const FILE_NAME: &str = "big.dat";
+        const SECT_SZ: usize = btlib::SECTOR_SZ_DEFAULT;
+        const DIVISOR: usize = 8;
+        const READ_SZ: usize = SECT_SZ / DIVISOR;
+
+        let case = TestCase::new();
+        let file_path = case.mnt_dir().join(FILE_NAME);
+        let mut file = OpenOptions::new()
+            .create(true)
+            .read(true)
+            .write(true)
+            .open(&file_path)
+            .unwrap();
+
+        let mut buf = vec![1u8; 2 * SECT_SZ];
+        file.write_all(&buf).unwrap();
+        file.flush().unwrap();
+        let mut file = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .open(&file_path)
+            .unwrap();
+        file.seek(SeekFrom::Start(SECT_SZ as u64)).unwrap();
+        let mut actual = vec![0u8; READ_SZ];
+        file.read_exact(&mut actual).unwrap();
+
+        buf.truncate(READ_SZ);
+        assert!(buf == actual);
+    }
 }

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

@@ -1823,6 +1823,31 @@ mod tests {
         }
     }
 
+    #[test]
+    fn block_try_seek_and_get_buf() {
+        const DIVISOR: usize = 8;
+        let mut case = BlockTestCase::new();
+        let path = case.node_path("blob.dat");
+        let mut block = case.open_new(path);
+        let sect_sz = block.sector_sz();
+        let read_sz = sect_sz / DIVISOR;
+        let mut expected = vec![0u8; read_sz];
+
+        for index in 0..(DIVISOR as u8 + 2) {
+            expected.fill(index + 1);
+            block.write(&expected).unwrap();
+        }
+
+        block.rewind().unwrap();
+        for index in 0..(DIVISOR as u8 + 2) {
+            let offset = (read_sz * index as usize) as u64;
+            block.try_seek(SeekFrom::Start(offset)).unwrap();
+            let actual = block.get_buf(offset, read_sz as u64).unwrap();
+            expected.fill(index + 1);
+            assert!(actual == expected);
+        }
+    }
+
     /// Tests that the last component of a [Writecap]'s bind path is the string representation of
     /// the [Principal] to which the [Writecap] was issued.
     #[test]

+ 25 - 0
crates/btlib/src/sectored_buf.rs

@@ -969,4 +969,29 @@ mod tests {
         assert_eq!(&[1, 1, 1, 1], &actual[..(SECT_SZ / 2)]);
         assert_eq!(&[0u8; EXPECTED_LEN], &actual[(SECT_SZ / 2)..]);
     }
+
+    #[test]
+    fn get_buf() {
+        const SECT_SZ: usize = crate::SECTOR_SZ_DEFAULT;
+        const DIVISOR: usize = 8;
+        const READ_SZ: usize = SECT_SZ / DIVISOR;
+        let mut sectored = SectoredBuf::new()
+            .try_compose(SectoredCursor::new(Vec::new(), SECT_SZ))
+            .unwrap();
+        let mut expected = vec![0u8; READ_SZ];
+
+        for index in 0..(DIVISOR as u8 + 1) {
+            expected.fill(index + 1);
+            sectored.write(&expected).unwrap();
+        }
+
+        sectored.rewind().unwrap();
+        for index in 0..(DIVISOR as u8 + 1) {
+            let offset = (READ_SZ * index as usize) as u64;
+            sectored.try_seek(SeekFrom::Start(offset)).unwrap();
+            let actual = sectored.get_buf(offset, READ_SZ as u64).unwrap();
+            expected.fill(index + 1);
+            assert!(actual == expected);
+        }
+    }
 }