Bläddra i källkod

Implemented hard linking in Blocktree.

Matthew Carr 2 år sedan
förälder
incheckning
188a466136
5 ändrade filer med 142 tillägg och 42 borttagningar
  1. 4 1
      TODO.txt
  2. 38 1
      crates/btfuse/src/main.rs
  3. 81 35
      crates/btlib/src/blocktree.rs
  4. 3 2
      crates/btlib/src/crypto/mod.rs
  5. 16 3
      crates/btlib/src/lib.rs

+ 4 - 1
TODO.txt

@@ -98,4 +98,7 @@ Vec<u8> structs.
 Manually implement the Serialize trait for BlockMetaBody so that the secrets field can be lazily
 Manually implement the Serialize trait for BlockMetaBody so that the secrets field can be lazily
 updated upon serialization if the secrets_struct field has been modified. In order to detect
 updated upon serialization if the secrets_struct field has been modified. In order to detect
 modifications, a new field with the serde(skip) attribute needs to be added to BlockMetaBody to
 modifications, a new field with the serde(skip) attribute needs to be added to BlockMetaBody to
-store the hash of BlockMetaSecrets that was computed just after decryption. 
+store the hash of BlockMetaSecrets that was computed just after decryption. 
+
+- 24, 3, mdcarr941@gmail.com, 7dbb358,
+Move `BlockRecord.frags` into `BlockMetaSecrets`.

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

@@ -179,7 +179,7 @@ fn main() {
 mod test {
 mod test {
     use std::{
     use std::{
         ffi::{OsStr, OsString},
         ffi::{OsStr, OsString},
-        fs::{read, read_dir, remove_file, write, ReadDir},
+        fs::{hard_link, read, read_dir, remove_file, write, ReadDir},
         sync::mpsc::{channel, Receiver},
         sync::mpsc::{channel, Receiver},
         thread::JoinHandle,
         thread::JoinHandle,
         time::Duration,
         time::Duration,
@@ -352,4 +352,41 @@ mod test {
         let expected: [&OsStr; 0] = [];
         let expected: [&OsStr; 0] = [];
         assert!(file_names(read_dir(&mnt_path).expect("read_dir failed")).eq(expected))
         assert!(file_names(read_dir(&mnt_path).expect("read_dir failed")).eq(expected))
     }
     }
+
+    #[test]
+    fn hard_link_then_remove() {
+        const EXPECTED: &[u8] = b"And the lives we've reclaimed";
+        let name1 = OsStr::new("refugee_lyrics.txt");
+        let name2 = OsStr::new("rise_against_lyrics.txt");
+        let mut case = TestCase::new();
+        let mnt_path = case.mnt_path();
+        let path1 = mnt_path.join(name1);
+        let path2 = mnt_path.join(name2);
+        write(&path1, EXPECTED).expect("write failed");
+
+        hard_link(&path1, &path2).expect("hard_link failed");
+        remove_file(&path1).expect("remove_file failed");
+
+        let actual = read(&path2).expect("read failed");
+        assert_eq!(EXPECTED, actual);
+    }
+
+    #[test]
+    fn hard_link_then_remove_both() {
+        const EXPECTED: &[u8] = b"And the lives we've reclaimed";
+        let name1 = OsStr::new("refugee_lyrics.txt");
+        let name2 = OsStr::new("rise_against_lyrics.txt");
+        let mut case = TestCase::new();
+        let mnt_path = case.mnt_path();
+        let path1 = mnt_path.join(name1);
+        let path2 = mnt_path.join(name2);
+        write(&path1, EXPECTED).expect("write failed");
+
+        hard_link(&path1, &path2).expect("hard_link failed");
+        remove_file(&path1).expect("remove_file on path1 failed");
+        remove_file(&path2).expect("remove_file on path2 failed");
+
+        let expected: [&OsStr; 0] = [];
+        assert!(file_names(read_dir(&mnt_path).expect("read_dir failed")).eq(expected));
+    }
 }
 }

+ 81 - 35
crates/btlib/src/blocktree.rs

@@ -24,8 +24,8 @@ mod private {
     };
     };
 
 
     use crate::{
     use crate::{
-        crypto::Creds, Block, BlockMeta, BlockOpenOptions, BlockPath, BoxInIoErr, DirEntry,
-        Directory, Epoch, Error, Result, ToStringInIoErr,
+        crypto::Creds, Block, BlockMeta, BlockOpenOptions, BlockPath, BlockRecord, BoxInIoErr,
+        DirEntry, DirEntryKind, Directory, Epoch, Error, Result, ToStringInIoErr,
     };
     };
 
 
     type Inode = u64;
     type Inode = u64;
@@ -70,6 +70,13 @@ mod private {
             }
             }
             Err(Error::custom(format!("unknown file type: 0o{value:0o}")))
             Err(Error::custom(format!("unknown file type: 0o{value:0o}")))
         }
         }
+
+        fn dir_entry_kind(self) -> DirEntryKind {
+            match self {
+                Self::Dir => DirEntryKind::Directory,
+                Self::Reg => DirEntryKind::File,
+            }
+        }
     }
     }
 
 
     impl From<FileType> for libc::mode_t {
     impl From<FileType> for libc::mode_t {
@@ -85,6 +92,12 @@ mod private {
         }
         }
     }
     }
 
 
+    impl From<FileType> for DirEntryKind {
+        fn from(value: FileType) -> Self {
+            value.dir_entry_kind()
+        }
+    }
+
     trait SeekFromExt {
     trait SeekFromExt {
         /// Converts a C-style `(whence, offset)` pair into a [SeekFrom] enum value.
         /// Converts a C-style `(whence, offset)` pair into a [SeekFrom] enum value.
         /// See the POSIX man page of `lseek` for more details.
         /// See the POSIX man page of `lseek` for more details.
@@ -318,7 +331,7 @@ mod private {
             self.value(handle)?.access_block_mut(cb)
             self.value(handle)?.access_block_mut(cb)
         }
         }
 
 
-        fn try_borrow_block<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
+        fn borrow_block<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
             &mut self,
             &mut self,
             cb: F,
             cb: F,
         ) -> io::Result<T> {
         ) -> io::Result<T> {
@@ -816,19 +829,11 @@ mod private {
                 .entries
                 .entries
                 .get(name)
                 .get(name)
                 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
                 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
-            let inode = match entry {
-                DirEntry::File(entry) => entry.inode,
-                DirEntry::Directory(entry) => entry.inode,
-                DirEntry::Server(_) => {
-                    return Err(io::Error::new(
-                        io::ErrorKind::Unsupported,
-                        "can't lookup server entry",
-                    ))
-                }
-            };
+            let inode = entry.inode().ok_or_else(|| {
+                io::Error::new(io::ErrorKind::Unsupported, "can't lookup server entry")
+            })?;
             let stat = self.open_value(inode, block_path, |value| {
             let stat = self.open_value(inode, block_path, |value| {
-                let stat =
-                    value.try_borrow_block(|block| Ok(block.meta_body().secrets()?.stat()))?;
+                let stat = value.borrow_block(|block| Ok(block.meta_body().secrets()?.stat()))?;
                 value.incr_lookup_count();
                 value.incr_lookup_count();
                 Ok(stat)
                 Ok(stat)
             })?;
             })?;
@@ -901,7 +906,7 @@ mod private {
         ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
         ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
             debug!("Blocktree::opendir called on inode {inode}");
             debug!("Blocktree::opendir called on inode {inode}");
             let handle = self.access_value_mut(inode, |value| {
             let handle = self.access_value_mut(inode, |value| {
-                value.try_borrow_block(|block| {
+                value.borrow_block(|block| {
                     let ctx = AuthzContext::new(ctx, block.meta());
                     let ctx = AuthzContext::new(ctx, block.meta());
                     self.authorizer.can_exec(&ctx)?;
                     self.authorizer.can_exec(&ctx)?;
                     Ok(())
                     Ok(())
@@ -1192,15 +1197,16 @@ mod private {
 
 
                 block.seek(SeekFrom::Start(0))?;
                 block.seek(SeekFrom::Start(0))?;
                 let mut dir: Directory = read_from(block)?;
                 let mut dir: Directory = read_from(block)?;
-                let inode = match dir.entries.remove(name) {
+                let entry = match dir.entries.remove(name) {
                     None => return Err(io::Error::from_raw_os_error(libc::ENOENT)),
                     None => return Err(io::Error::from_raw_os_error(libc::ENOENT)),
-                    Some(entry) => entry.inode().ok_or_else(|| {
-                        io::Error::new(
-                            io::ErrorKind::InvalidInput,
-                            format!("name {name} does not refer to a file or directory"),
-                        )
-                    })?,
+                    Some(entry) => entry,
                 };
                 };
+                let inode = entry.inode().ok_or_else(|| {
+                    io::Error::new(
+                        io::ErrorKind::Other,
+                        "no inode associated with the given name",
+                    )
+                })?;
                 block.seek(SeekFrom::Start(0))?;
                 block.seek(SeekFrom::Start(0))?;
                 write_to(&dir, block)?;
                 write_to(&dir, block)?;
 
 
@@ -1210,7 +1216,7 @@ mod private {
             })?;
             })?;
             self.open_value(inode, block_path, |value| {
             self.open_value(inode, block_path, |value| {
                 // We mark the block for deletion if `nlink` drops to zero.
                 // We mark the block for deletion if `nlink` drops to zero.
-                value.delete = value.try_borrow_block(|block| {
+                value.delete = value.borrow_block(|block| {
                     let nlink = block.mut_meta_body().access_secrets(|secrets| {
                     let nlink = block.mut_meta_body().access_secrets(|secrets| {
                         secrets.nlink -= 1;
                         secrets.nlink -= 1;
                         Ok(secrets.nlink)
                         Ok(secrets.nlink)
@@ -1222,6 +1228,57 @@ mod private {
             })
             })
         }
         }
 
 
+        fn link(
+            &self,
+            ctx: &Context,
+            inode: Self::Inode,
+            newparent: Self::Inode,
+            newname: &CStr,
+        ) -> io::Result<Entry> {
+            debug!("Blocktree::link called for inode {inode}");
+            let newname = newname.to_str().box_err()?;
+            self.borrow_block(newparent, |block| {
+                let ctx = AuthzContext::new(ctx, block.meta());
+                self.authorizer.can_write(&ctx)?;
+
+                block.seek(SeekFrom::Start(0))?;
+                let mut dir: Directory = read_from(block)?;
+                if dir.entries.contains_key(newname) {
+                    return Err(io::Error::from_raw_os_error(libc::EEXIST));
+                }
+
+                let (file_type, attr) = self.access_value_mut(inode, |value| {
+                    let (file_type, attr) = value.borrow_block(|block| {
+                        let meta = block.mut_meta_body();
+                        let (mode, attr) = meta.access_secrets(|secrets| {
+                            secrets.nlink += 1;
+                            Ok((secrets.mode, secrets.attr()))
+                        })?;
+                        let file_type = FileType::from_value(mode)?;
+                        block.flush_meta()?;
+                        Ok((file_type, attr))
+                    })?;
+                    value.incr_lookup_count();
+                    Ok((file_type, attr))
+                })?;
+                let entry = match file_type {
+                    FileType::Reg => DirEntry::File(BlockRecord::new(inode)),
+                    FileType::Dir => DirEntry::Directory(BlockRecord::new(inode)),
+                };
+                dir.entries.insert(newname.to_owned(), entry);
+                block.seek(SeekFrom::Start(0))?;
+                write_to(&dir, block)?;
+                Ok(Entry {
+                    inode,
+                    generation: self.generation,
+                    attr: attr.into(),
+                    attr_flags: 0,
+                    attr_timeout: self.attr_timeout(),
+                    entry_timeout: self.entry_timeout(),
+                })
+            })
+        }
+
         //////////////////////////////////
         //////////////////////////////////
         // METHODS WHICH ARE NOT SUPPORTED
         // METHODS WHICH ARE NOT SUPPORTED
         //////////////////////////////////
         //////////////////////////////////
@@ -1320,17 +1377,6 @@ mod private {
             Self::not_supported()
             Self::not_supported()
         }
         }
 
 
-        fn link(
-            &self,
-            _ctx: &Context,
-            inode: Self::Inode,
-            _newparent: Self::Inode,
-            _newname: &CStr,
-        ) -> io::Result<Entry> {
-            debug!("Blocktree::link called for inode {inode}");
-            Self::not_supported()
-        }
-
         fn listxattr(
         fn listxattr(
             &self,
             &self,
             _ctx: &Context,
             _ctx: &Context,

+ 3 - 2
crates/btlib/src/crypto/mod.rs

@@ -5,8 +5,8 @@ pub mod secret_stream;
 pub use secret_stream::SecretStream;
 pub use secret_stream::SecretStream;
 
 
 use crate::{
 use crate::{
-    fmt, io, BigArray, BlockMeta, BlockPath, Deserialize, Display, Epoch, Formatter, Hashable,
-    Principal, Principaled, Serialize, Writecap, WritecapBody,
+    fmt, io, BigArray, BlockMeta, BlockPath, Deserialize, Epoch, Formatter, Hashable, Principal,
+    Principaled, Serialize, Writecap, WritecapBody,
 };
 };
 
 
 use btserde::{self, from_vec, to_vec, write_to};
 use btserde::{self, from_vec, to_vec, write_to};
@@ -28,6 +28,7 @@ use serde::{
     ser::{SerializeStruct, Serializer},
     ser::{SerializeStruct, Serializer},
 };
 };
 use std::{
 use std::{
+    fmt::Display,
     io::{Read, Write},
     io::{Read, Write},
     marker::PhantomData,
     marker::PhantomData,
     num::TryFromIntError,
     num::TryFromIntError,

+ 16 - 3
crates/btlib/src/lib.rs

@@ -18,13 +18,14 @@ extern crate static_assertions;
 #[cfg(test)]
 #[cfg(test)]
 extern crate lazy_static;
 extern crate lazy_static;
 
 
-use brotli::{CompressorWriter, Decompressor};
 use crypto::{
 use crypto::{
     AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, HashKind,
     AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, HashKind,
     MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
     MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
 };
 };
+use trailered::Trailered;
 
 
 use ::log::error;
 use ::log::error;
+use brotli::{CompressorWriter, Decompressor};
 use fuse_backend_rs::abi::fuse_abi::{stat64, Attr};
 use fuse_backend_rs::abi::fuse_abi::{stat64, Attr};
 use sectored_buf::SectoredBuf;
 use sectored_buf::SectoredBuf;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
@@ -40,7 +41,7 @@ use std::{
     sync::PoisonError,
     sync::PoisonError,
     time::{Duration, SystemTime},
     time::{Duration, SystemTime},
 };
 };
-use trailered::Trailered;
+use strum_macros::{Display, EnumDiscriminants, FromRepr};
 
 
 #[derive(Debug)]
 #[derive(Debug)]
 pub enum Error {
 pub enum Error {
@@ -882,9 +883,19 @@ pub struct BlockRecord {
     pub inode: u64,
     pub inode: u64,
     /// A mapping from fragment serial numbers to fragment records.
     /// A mapping from fragment serial numbers to fragment records.
     /// TODO: Does this need to have a consistent order?
     /// TODO: Does this need to have a consistent order?
+    /// TODO: Move this into the block metadata.
     pub frags: HashMap<FragmentSerial, FragmentRecord>,
     pub frags: HashMap<FragmentSerial, FragmentRecord>,
 }
 }
 
 
+impl BlockRecord {
+    pub fn new(inode: u64) -> BlockRecord {
+        BlockRecord {
+            inode,
+            frags: HashMap::new(),
+        }
+    }
+}
+
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 /// Structure for keeping track of server information in a directory.
 /// Structure for keeping track of server information in a directory.
 pub struct ServerRecord {
 pub struct ServerRecord {
@@ -894,7 +905,9 @@ pub struct ServerRecord {
     pub_creds: PublicCreds,
     pub_creds: PublicCreds,
 }
 }
 
 
-#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize, EnumDiscriminants)]
+#[strum_discriminants(derive(FromRepr, Display, Serialize, Deserialize))]
+#[strum_discriminants(name(DirEntryKind))]
 pub enum DirEntry {
 pub enum DirEntry {
     Directory(BlockRecord),
     Directory(BlockRecord),
     File(BlockRecord),
     File(BlockRecord),