Bläddra i källkod

Added encrypted metadata to blocks and used this to store
POSIX file system metadata.

Matthew Carr 2 år sedan
förälder
incheckning
91c5e1765d

+ 14 - 6
TODO.txt

@@ -20,15 +20,12 @@ Remove TryCompose?
 !- 5, 1, mdcarr941@gmail.com, bd6904, mdcarr941@gmail.com, bd6904
 !- 5, 1, mdcarr941@gmail.com, bd6904, mdcarr941@gmail.com, bd6904
 Move crypto::{encrypt, decrypt} into corresponding {EncrypterExt, DecrypterExt}.
 Move crypto::{encrypt, decrypt} into corresponding {EncrypterExt, DecrypterExt}.
 
 
-- 6
-Create an enum to eliminate the use of Block trait objects?
-
 !- 7, 2, mdcarr941@gmail.com, ?, mdcarr941@gmail.com, fd4356
 !- 7, 2, mdcarr941@gmail.com, ?, mdcarr941@gmail.com, fd4356
 Add a ser_sign_into method to SignerExt which serializes a value into a provided Vec<u8> and returns
 Add a ser_sign_into method to SignerExt which serializes a value into a provided Vec<u8> and returns
 a signature over this data. Update BlockStream::flush_integ to use this method.
 a signature over this data. Update BlockStream::flush_integ to use this method.
 
 
 - 8
 - 8
-Convert all sector sizes to u64 for portability.
+Convert all sector sizes to u32 for portability.
 
 
 - 9
 - 9
 Create an extension trait for u64 with a method for adding an i64 to it. Use this in
 Create an extension trait for u64 with a method for adding an i64 to it. Use this in
@@ -58,7 +55,7 @@ and helper functions are visible to.
 - 15, 13, mdcarr941@gmail.com, 58d1f6, 
 - 15, 13, mdcarr941@gmail.com, 58d1f6, 
 Create a new crate which implements a FUSE daemon.
 Create a new crate which implements a FUSE daemon.
 
 
-- 16, 5, mdcarr941@gmail.com, 866533,
+!- 16, 5, mdcarr941@gmail.com, 866533,
 Add the inherit field, which contains the crypto link from the parent block key to the current
 Add the inherit field, which contains the crypto link from the parent block key to the current
 block key, to the block metadata.
 block key, to the block metadata.
 
 
@@ -66,7 +63,7 @@ block key, to the block metadata.
 SECURITY: Design and implement a mechanism to protect the keys in block's metadata dictionary from
 SECURITY: Design and implement a mechanism to protect the keys in block's metadata dictionary from
 being correlated with one another. This mechanism must allow a principal with a readcap to be able
 being correlated with one another. This mechanism must allow a principal with a readcap to be able
 to find their readcap and to rotate the block and create new readcaps for each of the principals in
 to find their readcap and to rotate the block and create new readcaps for each of the principals in
-the dictionary, but prevent an attacker from being able to identify when two blocks contains
+the dictionary, but prevent an attacker from being able to identify when two blocks contain
 readcaps for the same principal.
 readcaps for the same principal.
 
 
 - 18, 3, mdcarr941@gmail.com, 8665339,
 - 18, 3, mdcarr941@gmail.com, 8665339,
@@ -85,3 +82,14 @@ Create a type to hold a pipeline of streams. New traits will need to be added fo
 components of the pipeline to implement. Require that the last pipeline component be a particular
 components of the pipeline to implement. Require that the last pipeline component be a particular
 sub-type of this trait. This will allow me to get rid of the dyn Block objects, as they can be
 sub-type of this trait. This will allow me to get rid of the dyn Block objects, as they can be
 replaced by instances of this pipeline type.
 replaced by instances of this pipeline type.
+
+!- 22, 8, mdcarr941@gmail.com, fe2ffc, mdcarr941@gmail.com, fe2ffc
+Add a new fields to BlockMeta which stores data encrypted using the block key. This information must
+include:
+  * mode bits as u32
+  * Unix timestamps
+  * owner UID and GID
+  * size of block data in bytes as u64
+  * number of hardlinks to the block
+Also include a dictionary for user data, which is indexed using a String and whose values are
+Vec<u8> structs.

+ 154 - 44
crates/btlib/src/blocktree.rs

@@ -5,7 +5,7 @@ mod private {
     use fuse_backend_rs::{
     use fuse_backend_rs::{
         abi::fuse_abi::{Attr, CreateIn},
         abi::fuse_abi::{Attr, CreateIn},
         api::filesystem::{
         api::filesystem::{
-            Context, Entry, FileSystem, OpenOptions, ZeroCopyReader, ZeroCopyWriter,
+            Context, Entry, FileSystem, FsOptions, OpenOptions, ZeroCopyReader, ZeroCopyWriter,
         },
         },
     };
     };
     use serde::{Deserialize, Serialize};
     use serde::{Deserialize, Serialize};
@@ -16,13 +16,15 @@ mod private {
         io::{self, SeekFrom, Write},
         io::{self, SeekFrom, Write},
         path::{Path, PathBuf},
         path::{Path, PathBuf},
         sync::{
         sync::{
-            atomic::{AtomicU64, Ordering},
+            atomic::{AtomicU64, AtomicUsize, Ordering},
             RwLock,
             RwLock,
         },
         },
         time::Duration,
         time::Duration,
     };
     };
 
 
-    use crate::{crypto::Creds, Block, BlockOpenOptions, BoxInIoErr, Directory, Error, Result};
+    use crate::{
+        crypto::Creds, Block, BlockOpenOptions, BoxInIoErr, DirEntry, Directory, Error, Result,
+    };
 
 
     type Inode = u64;
     type Inode = u64;
 
 
@@ -39,7 +41,7 @@ mod private {
         }
         }
     }
     }
 
 
-    type InodeTableValue = Box<dyn Block>;
+    type InodeTableValue = (AtomicUsize, Box<dyn Block>);
     type InodeTable = HashMap<Inode, InodeTableValue>;
     type InodeTable = HashMap<Inode, InodeTableValue>;
     type InodeTableEntry<'a> = hash_map::Entry<'a, Inode, InodeTableValue>;
     type InodeTableEntry<'a> = hash_map::Entry<'a, Inode, InodeTableValue>;
 
 
@@ -104,8 +106,11 @@ mod private {
             creds: C,
             creds: C,
         ) -> Result<Blocktree<C>> {
         ) -> Result<Blocktree<C>> {
             let mut inodes = HashMap::with_capacity(1);
             let mut inodes = HashMap::with_capacity(1);
-            inodes.insert(SpecInodes::Sb.into(), sb_block);
-            inodes.insert(SpecInodes::RootDir.into(), root_block);
+            inodes.insert(SpecInodes::Sb.into(), (AtomicUsize::new(1), sb_block));
+            inodes.insert(
+                SpecInodes::RootDir.into(),
+                (AtomicUsize::new(1), root_block),
+            );
             Ok(Blocktree {
             Ok(Blocktree {
                 path: btdir,
                 path: btdir,
                 inodes: RwLock::new(inodes),
                 inodes: RwLock::new(inodes),
@@ -151,15 +156,6 @@ mod private {
                 .open()
                 .open()
         }
         }
 
 
-        fn inode_release(&self, inode: Inode) -> io::Result<()> {
-            let mut inodes = self
-                .inodes
-                .write()
-                .map_err(|err| Error::custom(err.to_string()))?;
-            inodes.remove(&inode);
-            Ok(())
-        }
-
         fn access_entry<T, F: FnOnce(InodeTableEntry) -> io::Result<T>>(
         fn access_entry<T, F: FnOnce(InodeTableEntry) -> io::Result<T>>(
             &self,
             &self,
             inode: Inode,
             inode: Inode,
@@ -180,30 +176,43 @@ mod private {
         ) -> io::Result<T> {
         ) -> io::Result<T> {
             self.access_entry(inode, |mut entry| match &mut entry {
             self.access_entry(inode, |mut entry| match &mut entry {
                 InodeTableEntry::Vacant(_) => Err(Error::custom("Inode is not open").into()),
                 InodeTableEntry::Vacant(_) => Err(Error::custom("Inode is not open").into()),
-                InodeTableEntry::Occupied(entry) => cb(entry.get_mut()),
+                InodeTableEntry::Occupied(entry) => cb(&mut entry.get_mut().1),
             })
             })
         }
         }
 
 
-        /// Updates the superblock file with the next valid inode value.
-        fn update_sb(&self) -> io::Result<()> {
-            let sb = Superblock {
-                generation: self.generation,
-                next_inode: self.next_inode.load(Ordering::SeqCst),
-            };
-            self.ser_write(SpecInodes::Sb.into(), &sb)
-        }
-
-        fn open(&self, inode: Inode) -> io::Result<()> {
+        fn inode_open(&self, inode: Inode) -> io::Result<()> {
             self.access_entry(inode, |entry| match entry {
             self.access_entry(inode, |entry| match entry {
                 InodeTableEntry::Vacant(entry) => {
                 InodeTableEntry::Vacant(entry) => {
                     let block = Self::open_block(&self.path, inode, self.creds.clone())?;
                     let block = Self::open_block(&self.path, inode, self.creds.clone())?;
-                    entry.insert(block);
+                    entry.insert((AtomicUsize::new(1), block));
+                    Ok(())
+                }
+                InodeTableEntry::Occupied(entry) => {
+                    entry.get().0.fetch_add(1, Ordering::SeqCst);
                     Ok(())
                     Ok(())
                 }
                 }
-                InodeTableEntry::Occupied(_) => Ok(()),
             })
             })
         }
         }
 
 
+        fn inode_release(&self, inode: Inode) -> io::Result<()> {
+            let prev = {
+                let inodes = self
+                    .inodes
+                    .read()
+                    .map_err(|err| Error::custom(err.to_string()))?;
+                let pair = inodes.get(&inode).unwrap();
+                pair.0.fetch_sub(1, Ordering::SeqCst)
+            };
+            if 1 == prev {
+                let mut inodes = self
+                    .inodes
+                    .write()
+                    .map_err(|err| Error::custom(err.to_string()))?;
+                inodes.remove(&inode);
+            }
+            Ok(())
+        }
+
         fn inode_write(
         fn inode_write(
             &self,
             &self,
             inode: Inode,
             inode: Inode,
@@ -216,14 +225,6 @@ mod private {
             })
             })
         }
         }
 
 
-        fn ser_write<T: Serialize>(&self, inode: Inode, value: &T) -> io::Result<()> {
-            self.access_block(inode, |block| {
-                block.seek(SeekFrom::Start(0))?;
-                write_to(&value, block)?;
-                Ok(())
-            })
-        }
-
         fn inode_read(
         fn inode_read(
             &self,
             &self,
             inode: Inode,
             inode: Inode,
@@ -235,12 +236,90 @@ mod private {
                 writer.write_from(&mut block.as_mut(), count, off)
                 writer.write_from(&mut block.as_mut(), count, off)
             })
             })
         }
         }
+
+        fn ser_write<T: Serialize>(&self, inode: Inode, value: &T) -> io::Result<()> {
+            self.access_block(inode, |block| {
+                block.seek(SeekFrom::Start(0))?;
+                write_to(&value, block)?;
+                Ok(())
+            })
+        }
+
+        fn directory_read(&self, inode: Inode) -> io::Result<(Directory, Attr)> {
+            self.access_block(inode, |block| {
+                block.seek(SeekFrom::Start(0))?;
+                let dir = read_from(block)?;
+                let attr = block
+                    .mut_meta_body()
+                    .access_secrets(|secrets| Ok(secrets.attr()))?;
+                Ok((dir, attr))
+            })
+        }
+
+        /// Updates the superblock file with the next valid inode value.
+        fn update_sb(&self) -> io::Result<()> {
+            let sb = Superblock {
+                generation: self.generation,
+                next_inode: self.next_inode.load(Ordering::SeqCst),
+            };
+            self.ser_write(SpecInodes::Sb.into(), &sb)
+        }
+
+        fn attr_timeout(&self) -> Duration {
+            Duration::from_secs(5)
+        }
+
+        fn entry_timeout(&self) -> Duration {
+            Duration::from_secs(5)
+        }
     }
     }
 
 
     impl<C: Creds + Clone + 'static> FileSystem for Blocktree<C> {
     impl<C: Creds + Clone + 'static> FileSystem for Blocktree<C> {
         type Inode = Inode;
         type Inode = Inode;
         type Handle = u64;
         type Handle = u64;
 
 
+        fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
+            let options = FsOptions::empty()
+                .union(FsOptions::ASYNC_READ.complement())
+                .union(FsOptions::BIG_WRITES)
+                .union(FsOptions::FLOCK_LOCKS)
+                .union(FsOptions::WRITEBACK_CACHE)
+                .union(FsOptions::POSIX_ACL.complement());
+            Ok(options)
+        }
+
+        fn lookup(&self, _ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<Entry> {
+            let (dir, ..) = self.directory_read(parent)?;
+            let name = name.to_str().box_err()?;
+            let entry = dir.entries.get(name).ok_or_else(|| {
+                io::Error::new(
+                    io::ErrorKind::NotFound,
+                    format!("no entry for {name} found"),
+                )
+            })?;
+            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 mut stat =
+                self.access_block(inode, |block| Ok(block.meta_body().secrets()?.stat()))?;
+            stat.st_ino = inode;
+            Ok(Entry {
+                inode,
+                generation: self.generation,
+                attr: stat,
+                attr_flags: 0,
+                attr_timeout: self.attr_timeout(),
+                entry_timeout: self.entry_timeout(),
+            })
+        }
+
         fn open(
         fn open(
             &self,
             &self,
             _ctx: &Context,
             _ctx: &Context,
@@ -248,7 +327,7 @@ mod private {
             _flags: u32,
             _flags: u32,
             _fuse_flags: u32,
             _fuse_flags: u32,
         ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
         ) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
-            self.open(inode)?;
+            self.inode_open(inode)?;
             Ok((None, OpenOptions::empty()))
             Ok((None, OpenOptions::empty()))
         }
         }
 
 
@@ -266,10 +345,10 @@ mod private {
             self.update_sb()?;
             self.update_sb()?;
 
 
             // Create the new file block.
             // Create the new file block.
-            self.open(inode)?;
+            self.inode_open(inode)?;
 
 
             // Add a directory entry to the parent for the new inode.
             // Add a directory entry to the parent for the new inode.
-            self.access_block(parent, |dir_block| {
+            let attr = self.access_block(parent, |dir_block| {
                 dir_block.seek(SeekFrom::Start(0))?;
                 dir_block.seek(SeekFrom::Start(0))?;
                 let mut dir: Directory = read_from(dir_block).box_err()?;
                 let mut dir: Directory = read_from(dir_block).box_err()?;
                 let name_str = name
                 let name_str = name
@@ -278,17 +357,19 @@ mod private {
                 dir.add_file(name_str.to_owned(), inode)?;
                 dir.add_file(name_str.to_owned(), inode)?;
                 dir_block.seek(SeekFrom::Start(0))?;
                 dir_block.seek(SeekFrom::Start(0))?;
                 write_to(&dir, dir_block).box_err()?;
                 write_to(&dir, dir_block).box_err()?;
-                Ok(())
+                dir_block.flush()?;
+                Ok(dir_block
+                    .mut_meta_body()
+                    .access_secrets(|secrets| Ok(secrets.attr()))?)
             })?;
             })?;
 
 
             let entry = Entry {
             let entry = Entry {
                 inode,
                 inode,
                 generation: self.generation,
                 generation: self.generation,
-                // TODO: These fields need to be initialized properly.
-                attr: Attr::default().into(),
+                attr: attr.into(),
                 attr_flags: 0,
                 attr_flags: 0,
-                attr_timeout: Duration::from_secs(5),
-                entry_timeout: Duration::from_secs(5),
+                attr_timeout: self.attr_timeout(),
+                entry_timeout: self.entry_timeout(),
             };
             };
             // TODO: What is OpenOptions used for?
             // TODO: What is OpenOptions used for?
             let options = OpenOptions::empty();
             let options = OpenOptions::empty();
@@ -410,4 +491,33 @@ mod tests {
 
 
         assert_eq!(expected, actual)
         assert_eq!(expected, actual)
     }
     }
+
+    #[test]
+    fn lookup() {
+        let dir = TempDir::new("fuse").expect("failed to create temp dir");
+        let creds = test_helpers::NODE_CREDS.clone();
+        let bt = Blocktree::new_empty(dir.path().to_owned(), 0, creds)
+            .expect("failed to create empty blocktree");
+        let name = CString::new("README.md").unwrap();
+        let (expected, ..) = bt
+            .create(
+                &Default::default(),
+                SpecInodes::RootDir.into(),
+                name.as_c_str(),
+                Default::default(),
+            )
+            .expect("failed to create file");
+
+        let actual = bt
+            .lookup(&Default::default(), SpecInodes::RootDir.into(), &name)
+            .expect("lookup failed");
+
+        assert_eq!(expected.generation, actual.generation);
+        assert_eq!(expected.inode, actual.inode);
+        assert_eq!(expected.attr.st_size, actual.attr.st_size);
+        assert_eq!(expected.attr.st_uid, actual.attr.st_uid);
+        assert_eq!(expected.attr.st_gid, actual.attr.st_gid);
+        assert_eq!(expected.attr.st_blksize, actual.attr.st_blksize);
+        assert_eq!(expected.attr.st_blocks, actual.attr.st_blocks);
+    }
 }
 }

+ 11 - 18
crates/btlib/src/crypto/merkle_stream.rs

@@ -5,10 +5,9 @@ pub use private::{
 
 
 mod private {
 mod private {
     use crate::{
     use crate::{
-        crypto::{Encrypter, Error, HashKind, Result, SymKey},
+        crypto::{Error, HashKind, Result},
         trailered::Trailered,
         trailered::Trailered,
-        BlockPath, BoxInIoErr, Decompose, MetaAccess, Principal, Sectored, TryCompose, WriteInteg,
-        SECTOR_SZ_DEFAULT,
+        BoxInIoErr, Decompose, MetaAccess, Sectored, TryCompose, WriteInteg, SECTOR_SZ_DEFAULT,
     };
     };
     use fuse_backend_rs::file_traits::FileReadWriteVolatile;
     use fuse_backend_rs::file_traits::FileReadWriteVolatile;
     use serde::{Deserialize, Serialize};
     use serde::{Deserialize, Serialize};
@@ -554,15 +553,17 @@ mod private {
     }
     }
 
 
     impl<T: MetaAccess> MerkleStream<T> {
     impl<T: MetaAccess> MerkleStream<T> {
-        /// Asserts that the root merkle node contains the integrity value given by the inner stream.
+        /// Asserts that the root merkle node contains the integrity value given by the inner
+        /// stream.
         pub fn assert_root_integrity(&mut self) -> Result<()> {
         pub fn assert_root_integrity(&mut self) -> Result<()> {
-            let hash_data = self.trailered.integrity();
+            let hash_data = self.trailered.meta_body().integrity();
             self.tree.assert_root_contains(hash_data)
             self.tree.assert_root_contains(hash_data)
         }
         }
     }
     }
 
 
     impl<T: Read + Seek> MerkleStream<T> {
     impl<T: Read + Seek> MerkleStream<T> {
-        /// Reads a `MerkleTree` from the end of the given stream and returns a stream which uses it.
+        /// Reads a `MerkleTree` from the end of the given stream and returns a stream which uses
+        /// it.
         pub fn new(inner: T) -> Result<MerkleStream<T>> {
         pub fn new(inner: T) -> Result<MerkleStream<T>> {
             let (trailered, tree) = Trailered::new(inner)?;
             let (trailered, tree) = Trailered::new(inner)?;
             Ok(MerkleStream {
             Ok(MerkleStream {
@@ -645,20 +646,12 @@ mod private {
     }
     }
 
 
     impl<T: MetaAccess> MetaAccess for MerkleStream<T> {
     impl<T: MetaAccess> MetaAccess for MerkleStream<T> {
-        fn block_key(&self) -> crate::Result<SymKey> {
-            self.trailered.block_key()
+        fn meta(&self) -> &crate::BlockMeta {
+            self.trailered.meta()
         }
         }
 
 
-        fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> crate::Result<()> {
-            self.trailered.add_readcap_for(owner, key)
-        }
-
-        fn integrity(&self) -> Option<&[u8]> {
-            self.trailered.integrity()
-        }
-
-        fn set_path(&mut self, path: BlockPath) {
-            self.trailered.set_path(path)
+        fn mut_meta(&mut self) -> &mut crate::BlockMeta {
+            self.trailered.mut_meta()
         }
         }
     }
     }
 
 

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

@@ -592,6 +592,14 @@ impl VarHash {
     pub fn kind(&self) -> HashKind {
     pub fn kind(&self) -> HashKind {
         self.into()
         self.into()
     }
     }
+
+    pub fn as_slice(&self) -> &[u8] {
+        self.as_ref()
+    }
+
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.as_mut()
+    }
 }
 }
 
 
 impl From<HashKind> for VarHash {
 impl From<HashKind> for VarHash {
@@ -2071,8 +2079,7 @@ mod tests {
         const SECT_SZ: usize = 16;
         const SECT_SZ: usize = 16;
         const SECT_CT: usize = 8;
         const SECT_CT: usize = 8;
         let creds = make_key_pair();
         let creds = make_key_pair();
-        let readcap = make_readcap_for(&creds);
-        let mut block = make_block_with(readcap);
+        let mut block = make_block_with(&creds);
         write_fill(&mut block, SECT_SZ, SECT_CT);
         write_fill(&mut block, SECT_SZ, SECT_CT);
         block.seek(SeekFrom::Start(0)).expect("seek failed");
         block.seek(SeekFrom::Start(0)).expect("seek failed");
         read_check(block, SECT_SZ, SECT_CT);
         read_check(block, SECT_SZ, SECT_CT);

+ 6 - 14
crates/btlib/src/crypto/secret_stream.rs

@@ -4,8 +4,8 @@ mod private {
     use fuse_backend_rs::file_traits::FileReadWriteVolatile;
     use fuse_backend_rs::file_traits::FileReadWriteVolatile;
 
 
     use crate::{
     use crate::{
-        crypto::{Encrypter, Error, Result, SymKey},
-        Block, BlockPath, Decompose, MetaAccess, Principal, Sectored, TryCompose,
+        crypto::{Error, Result, SymKey},
+        Block, Decompose, MetaAccess, Sectored, TryCompose,
     };
     };
     use std::io::{self, Read, Seek, SeekFrom, Write};
     use std::io::{self, Read, Seek, SeekFrom, Write};
 
 
@@ -167,20 +167,12 @@ mod private {
     }
     }
 
 
     impl<T: MetaAccess> MetaAccess for SecretStream<T> {
     impl<T: MetaAccess> MetaAccess for SecretStream<T> {
-        fn block_key(&self) -> crate::Result<SymKey> {
-            self.inner.block_key()
+        fn meta(&self) -> &crate::BlockMeta {
+            self.inner.meta()
         }
         }
 
 
-        fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> crate::Result<()> {
-            self.inner.add_readcap_for(owner, key)
-        }
-
-        fn integrity(&self) -> Option<&[u8]> {
-            self.inner.integrity()
-        }
-
-        fn set_path(&mut self, path: BlockPath) {
-            self.inner.set_path(path)
+        fn mut_meta(&mut self) -> &mut crate::BlockMeta {
+            self.inner.mut_meta()
         }
         }
     }
     }
 
 

+ 303 - 45
crates/btlib/src/lib.rs

@@ -26,16 +26,19 @@ use crypto::{
     MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
     MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
 };
 };
 
 
-use fuse_backend_rs::file_traits::FileReadWriteVolatile;
+use fuse_backend_rs::{
+    abi::fuse_abi::{stat64, Attr},
+    file_traits::FileReadWriteVolatile,
+};
 use log::error;
 use log::error;
 use sectored_buf::SectoredBuf;
 use sectored_buf::SectoredBuf;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 use serde_big_array::BigArray;
 use serde_big_array::BigArray;
 use std::{
 use std::{
-    collections::{btree_map, BTreeMap, HashMap},
+    collections::{btree_map, hash_map::DefaultHasher, BTreeMap, HashMap},
     convert::{Infallible, TryFrom},
     convert::{Infallible, TryFrom},
     fmt::{self, Display, Formatter},
     fmt::{self, Display, Formatter},
-    hash::Hash as Hashable,
+    hash::{Hash as Hashable, Hasher},
     io::{self, Read, Seek, SeekFrom, Write},
     io::{self, Read, Seek, SeekFrom, Write},
     net::SocketAddr,
     net::SocketAddr,
     ops::{Add, Sub},
     ops::{Add, Sub},
@@ -51,6 +54,7 @@ pub enum Error {
     Serde(btserde::Error),
     Serde(btserde::Error),
     Crypto(crypto::Error),
     Crypto(crypto::Error),
     IncorrectSize { expected: usize, actual: usize },
     IncorrectSize { expected: usize, actual: usize },
+    NoBlockKey,
     Custom(Box<dyn std::fmt::Debug + Send + Sync>),
     Custom(Box<dyn std::fmt::Debug + Send + Sync>),
 }
 }
 
 
@@ -70,6 +74,7 @@ impl Display for Error {
             Error::IncorrectSize { expected, actual } => {
             Error::IncorrectSize { expected, actual } => {
                 write!(f, "incorrect size {actual}, expected {expected}")
                 write!(f, "incorrect size {actual}, expected {expected}")
             }
             }
+            Error::NoBlockKey => write!(f, "no block key is present"),
             Error::Custom(err) => err.fmt(f),
             Error::Custom(err) => err.fmt(f),
         }
         }
     }
     }
@@ -184,12 +189,18 @@ impl<T, U: Decompose<T>, S: TryCompose<T, U, Error = Infallible>> Compose<T, U>
     }
     }
 }
 }
 
 
+/// Trait for accessing the metadata associated with a block.
 pub trait MetaAccess {
 pub trait MetaAccess {
-    fn block_key(&self) -> Result<SymKey>;
-    fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> Result<()>;
-    /// Returns the integrity value used to protect the contents of the block.
-    fn integrity(&self) -> Option<&[u8]>;
-    fn set_path(&mut self, path: BlockPath);
+    fn meta(&self) -> &BlockMeta;
+    fn mut_meta(&mut self) -> &mut BlockMeta;
+
+    fn meta_body(&self) -> &BlockMetaBody {
+        self.meta().body()
+    }
+
+    fn mut_meta_body(&mut self) -> &mut BlockMetaBody {
+        self.mut_meta().mut_body()
+    }
 }
 }
 
 
 /// Extensions to the `Read` trait.
 /// Extensions to the `Read` trait.
@@ -225,6 +236,108 @@ trait ReadExt: Read {
 
 
 impl<T: Read> ReadExt for T {}
 impl<T: Read> ReadExt for T {}
 
 
+trait HashExt: Hashable {
+    /// Returns the hash produced by the `DefaultHasher`.
+    fn default_hash(&self) -> u64 {
+        let mut hasher = DefaultHasher::new();
+        self.hash(&mut hasher);
+        hasher.finish()
+    }
+}
+
+impl<T: Hashable> HashExt for T {}
+
+/// Metadata that is encrypted.
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash)]
+pub struct BlockMetaSecrets {
+    /// The inode number of the file.
+    inode: u64,
+    /// Mode of file.
+    mode: u32,
+    /// Owner UID of file.
+    uid: u32,
+    /// Owner GID of file.
+    gid: u32,
+    /// Last access time.
+    atime: Epoch,
+    /// Last data modification.
+    mtime: Epoch,
+    /// Last status change.
+    ctime: Epoch,
+    /// Size of the file in bytes.
+    size: u64,
+    /// Number of hard links to the file.
+    nlink: u32,
+    /// The sector size used by the block.
+    sect_sz: u32,
+    tags: BTreeMap<String, Vec<u8>>,
+}
+
+impl BlockMetaSecrets {
+    pub fn new() -> BlockMetaSecrets {
+        Self::default()
+    }
+
+    pub fn attr(&self) -> Attr {
+        self.into()
+    }
+
+    pub fn stat(&self) -> stat64 {
+        self.attr().into()
+    }
+
+    /// Returns the number of sectors occupied by the block's data.
+    pub fn sectors(&self) -> u64 {
+        let sect_sz = self.sect_sz as u64;
+        if self.size % sect_sz == 0 {
+            self.size / sect_sz
+        } else {
+            self.size / sect_sz + 1
+        }
+    }
+}
+
+impl Default for BlockMetaSecrets {
+    fn default() -> Self {
+        Self {
+            inode: 0,
+            mode: 0,
+            uid: 0,
+            gid: 0,
+            atime: Epoch::default(),
+            mtime: Epoch::default(),
+            ctime: Epoch::default(),
+            size: 0,
+            nlink: 0,
+            sect_sz: SECTOR_SZ_DEFAULT.try_into().unwrap(),
+            tags: BTreeMap::new(),
+        }
+    }
+}
+
+impl From<&BlockMetaSecrets> for Attr {
+    fn from(value: &BlockMetaSecrets) -> Self {
+        Attr {
+            ino: value.inode,
+            size: value.size,
+            atime: value.atime.value(),
+            mtime: value.mtime.value(),
+            ctime: value.ctime.value(),
+            atimensec: 0,
+            mtimensec: 0,
+            ctimensec: 0,
+            mode: value.mode,
+            nlink: value.nlink,
+            uid: value.uid,
+            gid: value.gid,
+            rdev: 0,
+            blksize: value.sect_sz,
+            blocks: value.sectors(),
+            flags: 0,
+        }
+    }
+}
+
 /// This struct contains all of the metadata fields associated with a block, except for its
 /// This struct contains all of the metadata fields associated with a block, except for its
 /// signature. Since this struct implements `Serialize`, this allows for convenient signature
 /// signature. Since this struct implements `Serialize`, this allows for convenient signature
 /// calculations.
 /// calculations.
@@ -240,33 +353,142 @@ pub struct BlockMetaBody {
     integrity: Option<VarHash>,
     integrity: Option<VarHash>,
     /// The public key that corresponds to the private key used to sign these metadata.
     /// The public key that corresponds to the private key used to sign these metadata.
     signing_key: AsymKeyPub<Sign>,
     signing_key: AsymKeyPub<Sign>,
+    /// Additional metadata which is subject to confidentiality protection.
+    secrets: Ciphertext<BlockMetaSecrets>,
+
+    #[serde(skip)]
+    /// The cleartext block key.
+    block_key: Option<SymKey>,
+    #[serde(skip)]
+    /// The clear text secret metadata.
+    secrets_struct: Option<BlockMetaSecrets>,
 }
 }
 
 
 impl BlockMetaBody {
 impl BlockMetaBody {
-    fn new<C: Creds>(creds: &C) -> BlockMetaBody {
-        BlockMetaBody {
+    fn new<C: Creds>(creds: &C) -> Result<BlockMetaBody> {
+        let block_key = SymKey::generate(SymKeyKind::default())?;
+        let mut readcaps = BTreeMap::new();
+        readcaps.insert(creds.principal(), creds.ser_encrypt(&block_key)?);
+        Ok(BlockMetaBody {
             path: BlockPath::default(),
             path: BlockPath::default(),
             inherit: None,
             inherit: None,
-            readcaps: BTreeMap::new(),
+            readcaps,
             writecap: creds.writecap().map(|e| e.to_owned()),
             writecap: creds.writecap().map(|e| e.to_owned()),
             integrity: None,
             integrity: None,
             signing_key: creds.public_sign().to_owned(),
             signing_key: creds.public_sign().to_owned(),
+            secrets: block_key.ser_encrypt(&BlockMetaSecrets::default())?,
+            block_key: Some(block_key),
+            secrets_struct: None,
+        })
+    }
+
+    pub fn unlock_block_key<C: Creds>(&mut self, creds: &C) -> Result<()> {
+        if self.block_key.is_some() {
+            return Ok(());
+        }
+        let readcap = self
+            .readcaps
+            .get(&creds.principal())
+            .ok_or(crypto::Error::NoReadCap)?;
+        self.block_key = Some(creds.ser_decrypt(readcap)?);
+        Ok(())
+    }
+
+    pub fn access_secrets<T, F: FnOnce(&mut BlockMetaSecrets) -> Result<T>>(
+        &mut self,
+        accessor: F,
+    ) -> Result<T> {
+        let secrets = match self.secrets_struct.as_mut() {
+            Some(secrets) => secrets,
+            None => {
+                let block_key = self.block_key()?;
+                self.secrets_struct = Some(block_key.ser_decrypt(&self.secrets)?);
+                self.secrets_struct.as_mut().unwrap()
+            }
+        };
+        let prev = secrets.default_hash();
+        let output = accessor(secrets)?;
+        if prev != secrets.default_hash() {
+            self.secrets = self
+                .block_key
+                .as_ref()
+                .ok_or(Error::NoBlockKey)?
+                .ser_encrypt(secrets)?;
         }
         }
+        Ok(output)
+    }
+
+    pub fn secrets(&self) -> Result<&BlockMetaSecrets> {
+        self.secrets_struct
+            .as_ref()
+            .ok_or_else(|| Error::custom("secrets have not been decrypted"))
+    }
+
+    pub fn block_key(&self) -> Result<&SymKey> {
+        self.block_key.as_ref().ok_or(Error::NoBlockKey)
+    }
+
+    pub fn use_block_key_for<C: Creds>(&mut self, creds: &C) -> Result<&SymKey> {
+        let readcap = self
+            .readcaps
+            .get(&creds.principal())
+            .ok_or(crypto::Error::NoReadCap)?;
+        let block_key = creds.ser_decrypt(readcap)?;
+        self.block_key = Some(block_key);
+        self.block_key()
+    }
+
+    pub fn mut_block_key(&mut self) -> &mut Option<SymKey> {
+        &mut self.block_key
+    }
+
+    pub fn integrity(&self) -> Option<&[u8]> {
+        self.integrity.as_ref().map(|hash| hash.as_slice())
+    }
+
+    pub fn add_readcap_for<E: Encrypter>(&mut self, owner: Principal, key: &E) -> Result<()> {
+        let block_key = self.block_key()?;
+        self.readcaps.insert(owner, key.ser_encrypt(block_key)?);
+        Ok(())
+    }
+
+    pub fn set_path(&mut self, path: BlockPath) {
+        self.path = path
     }
     }
 }
 }
 
 
 /// Signed metadata associated with a block.
 /// Signed metadata associated with a block.
 #[derive(Serialize, Deserialize)]
 #[derive(Serialize, Deserialize)]
-struct BlockMeta {
+pub struct BlockMeta {
     body: BlockMetaBody,
     body: BlockMetaBody,
     sig: Signature,
     sig: Signature,
 }
 }
 
 
 impl BlockMeta {
 impl BlockMeta {
-    fn new<C: Creds>(creds: &C) -> BlockMeta {
-        let body = BlockMetaBody::new(creds);
+    fn new<C: Creds>(creds: &C) -> Result<BlockMeta> {
+        let body = BlockMetaBody::new(creds)?;
         let sig = Signature::empty(body.signing_key.scheme());
         let sig = Signature::empty(body.signing_key.scheme());
-        BlockMeta { body, sig }
+        Ok(BlockMeta { body, sig })
+    }
+
+    pub fn body(&self) -> &BlockMetaBody {
+        self.as_ref()
+    }
+
+    pub fn mut_body(&mut self) -> &mut BlockMetaBody {
+        self.as_mut()
+    }
+}
+
+impl AsRef<BlockMetaBody> for BlockMeta {
+    fn as_ref(&self) -> &BlockMetaBody {
+        &self.body
+    }
+}
+
+impl AsMut<BlockMetaBody> for BlockMeta {
+    fn as_mut(&mut self) -> &mut BlockMetaBody {
+        &mut self.body
     }
     }
 }
 }
 
 
@@ -286,14 +508,12 @@ impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
                 // We need to use the writecap and signing_key provided by the current credentials.
                 // We need to use the writecap and signing_key provided by the current credentials.
                 meta.body.writecap = creds.writecap().map(|e| e.to_owned());
                 meta.body.writecap = creds.writecap().map(|e| e.to_owned());
                 meta.body.signing_key = creds.public_sign().to_owned();
                 meta.body.signing_key = creds.public_sign().to_owned();
+                meta.body.use_block_key_for(&creds)?;
                 meta
                 meta
             }
             }
             None => {
             None => {
-                let mut meta = BlockMeta::new(&creds);
-                let block_key = SymKey::generate(SymKeyKind::default())?;
-                meta.body
-                    .readcaps
-                    .insert(creds.principal(), creds.ser_encrypt(&block_key)?);
+                let mut meta = BlockMeta::new(&creds)?;
+                meta.mut_body().add_readcap_for(creds.principal(), &creds)?;
                 meta.body.writecap = creds.writecap().map(|e| e.to_owned());
                 meta.body.writecap = creds.writecap().map(|e| e.to_owned());
                 meta
                 meta
             }
             }
@@ -349,29 +569,12 @@ impl<T: Seek, C> Seek for BlockStream<T, C> {
 }
 }
 
 
 impl<T, C: Decrypter + Principaled> MetaAccess for BlockStream<T, C> {
 impl<T, C: Decrypter + Principaled> MetaAccess for BlockStream<T, C> {
-    fn block_key(&self) -> Result<SymKey> {
-        let readcap = self
-            .meta
-            .body
-            .readcaps
-            .get(&self.creds.principal())
-            .ok_or(Error::Crypto(crypto::Error::NoReadCap))?;
-        Ok(self.creds.ser_decrypt(readcap)?)
+    fn meta(&self) -> &BlockMeta {
+        &self.meta
     }
     }
 
 
-    fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> Result<()> {
-        let block_key = self.block_key()?;
-        let readcap = key.ser_encrypt(&block_key)?;
-        self.meta.body.readcaps.insert(owner, readcap);
-        Ok(())
-    }
-
-    fn integrity(&self) -> Option<&[u8]> {
-        self.meta.body.integrity.as_ref().map(|hash| hash.as_ref())
-    }
-
-    fn set_path(&mut self, path: BlockPath) {
-        self.meta.body.path = path;
+    fn mut_meta(&mut self) -> &mut BlockMeta {
+        &mut self.meta
     }
     }
 }
 }
 
 
@@ -465,9 +668,14 @@ impl<T: Read + Write + Seek + FileReadWriteVolatile + 'static, C: Creds + 'stati
 {
 {
     pub fn open(self) -> Result<Box<dyn Block>> {
     pub fn open(self) -> Result<Box<dyn Block>> {
         let stream = BlockStream::new(self.inner, self.creds)?;
         let stream = BlockStream::new(self.inner, self.creds)?;
-        let block_key = stream.block_key()?;
+        let block_key = stream.meta_body().block_key().map(|e| e.to_owned())?;
         let mut stream = MerkleStream::new(stream)?;
         let mut stream = MerkleStream::new(stream)?;
         stream.assert_root_integrity()?;
         stream.assert_root_integrity()?;
+        let sect_sz = stream.sector_sz();
+        stream.mut_meta_body().access_secrets(|secrets| {
+            secrets.sect_sz = sect_sz.try_into()?;
+            Ok(())
+        })?;
         if self.encrypt {
         if self.encrypt {
             let stream = SecretStream::new(block_key).try_compose(stream)?;
             let stream = SecretStream::new(block_key).try_compose(stream)?;
             let stream = SectoredBuf::new().try_compose(stream)?;
             let stream = SectoredBuf::new().try_compose(stream)?;
@@ -721,21 +929,41 @@ pub trait Principaled {
 }
 }
 
 
 /// An instant in time represented by the number of seconds since January 1st 1970, 00:00:00 UTC.
 /// An instant in time represented by the number of seconds since January 1st 1970, 00:00:00 UTC.
-#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
 pub struct Epoch(u64);
 pub struct Epoch(u64);
 
 
 impl Epoch {
 impl Epoch {
     /// Returns the current epoch time.
     /// Returns the current epoch time.
-    fn now() -> Epoch {
+    pub fn now() -> Epoch {
         let now = SystemTime::now();
         let now = SystemTime::now();
         // If the system clock is before the unix epoch, just panic.
         // If the system clock is before the unix epoch, just panic.
         let epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
         let epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
         Epoch(epoch.as_secs())
         Epoch(epoch.as_secs())
     }
     }
+
+    pub fn from_value(value: u64) -> Self {
+        value.into()
+    }
+
+    pub fn value(self) -> u64 {
+        self.into()
+    }
 }
 }
 
 
 impl Copy for Epoch {}
 impl Copy for Epoch {}
 
 
+impl From<u64> for Epoch {
+    fn from(value: u64) -> Self {
+        Self(value)
+    }
+}
+
+impl From<Epoch> for u64 {
+    fn from(value: Epoch) -> Self {
+        value.0
+    }
+}
+
 impl Add<Duration> for Epoch {
 impl Add<Duration> for Epoch {
     type Output = Self;
     type Output = Self;
     fn add(self, other: Duration) -> Self {
     fn add(self, other: Duration) -> Self {
@@ -786,6 +1014,7 @@ mod tests {
     };
     };
 
 
     use super::*;
     use super::*;
+    use btserde::{from_vec, to_vec};
     use tempdir::TempDir;
     use tempdir::TempDir;
     use test_helpers::*;
     use test_helpers::*;
 
 
@@ -826,6 +1055,32 @@ mod tests {
             .expect("failed to open block");
             .expect("failed to open block");
     }
     }
 
 
+    /// Tests that the `BlockMetaBody` struct has an updated secrets struct after it is modified
+    /// in the `access_secrets` method.
+    #[test]
+    fn block_meta_body_secrets_updated_after_access() {
+        const UID: u32 = 1000;
+        let creds = test_helpers::NODE_CREDS.clone();
+
+        let vec = {
+            let mut body = BlockMetaBody::new(&creds).expect("failed to create meta body");
+            body.access_secrets(|secrets| {
+                secrets.uid = UID;
+                Ok(())
+            })
+            .expect("access secrets failed");
+            to_vec(&body).expect("to_vec failed")
+        };
+
+        let mut body = from_vec::<BlockMetaBody>(&vec).expect("from_vec failed");
+        body.unlock_block_key(&creds)
+            .expect("unlock_block_key failed");
+        let actual_uid = body
+            .access_secrets(|secrets| Ok(secrets.uid))
+            .expect("access_secrets failed");
+        assert_eq!(UID, actual_uid);
+    }
+
     struct BlockTestCase {
     struct BlockTestCase {
         root_creds: TpmCreds,
         root_creds: TpmCreds,
         node_creds: TpmCreds,
         node_creds: TpmCreds,
@@ -881,7 +1136,9 @@ mod tests {
                 .with_encrypt(true)
                 .with_encrypt(true)
                 .open()
                 .open()
                 .expect("failed to open block");
                 .expect("failed to open block");
-            block.set_path(self.node_creds.writecap().unwrap().body.path.clone());
+            block
+                .mut_meta_body()
+                .set_path(self.node_creds.writecap().unwrap().body.path.clone());
             block
             block
         }
         }
 
 
@@ -968,6 +1225,7 @@ mod tests {
         {
         {
             let mut block = case.open_new(&path);
             let mut block = case.open_new(&path);
             block
             block
+                .mut_meta_body()
                 .add_readcap_for(app_creds.principal(), &app_creds)
                 .add_readcap_for(app_creds.principal(), &app_creds)
                 .expect("failed to add readcap");
                 .expect("failed to add readcap");
             block.write(&EXPECTED[..MID]).expect("first write failed");
             block.write(&EXPECTED[..MID]).expect("first write failed");

+ 5 - 15
crates/btlib/src/sectored_buf.rs

@@ -8,9 +8,7 @@ mod private {
     use btserde::{read_from, write_to};
     use btserde::{read_from, write_to};
 
 
     use crate::{
     use crate::{
-        crypto::{Encrypter, SymKey},
-        Block, BlockPath, BoxInIoErr, Decompose, Error, MetaAccess, Principal, ReadExt, Result,
-        Sectored, TryCompose,
+        Block, BoxInIoErr, Decompose, Error, MetaAccess, ReadExt, Result, Sectored, TryCompose,
     };
     };
 
 
     /// A stream which buffers writes and read such that the inner stream only sees reads and writes
     /// A stream which buffers writes and read such that the inner stream only sees reads and writes
@@ -312,20 +310,12 @@ mod private {
     }
     }
 
 
     impl<T: MetaAccess> MetaAccess for SectoredBuf<T> {
     impl<T: MetaAccess> MetaAccess for SectoredBuf<T> {
-        fn block_key(&self) -> Result<SymKey> {
-            self.inner.block_key()
+        fn meta(&self) -> &crate::BlockMeta {
+            self.inner.meta()
         }
         }
 
 
-        fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> Result<()> {
-            self.inner.add_readcap_for(owner, key)
-        }
-
-        fn integrity(&self) -> Option<&[u8]> {
-            self.inner.integrity()
-        }
-
-        fn set_path(&mut self, path: BlockPath) {
-            self.inner.set_path(path)
+        fn mut_meta(&mut self) -> &mut crate::BlockMeta {
+            self.inner.mut_meta()
         }
         }
     }
     }
 
 

+ 6 - 12
crates/btlib/src/test_helpers.rs

@@ -196,18 +196,10 @@ pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Writecap {
     writecap
     writecap
 }
 }
 
 
-pub(crate) fn make_readcap_for<C: Encrypter + Principaled>(creds: &C) -> Readcap {
-    Readcap {
-        issued_to: creds.principal(),
-        key: creds
-            .ser_encrypt(&BLOCK_KEY)
-            .expect("failed to encrypt block key"),
-    }
-}
-
-pub(crate) fn make_block_with(readcap: Readcap) -> Box<dyn Block> {
+pub(crate) fn make_block_with<C: CredsPub>(creds: &C) -> Box<dyn Block> {
+    let block_key = SymKey::generate(SymKeyKind::default()).unwrap();
     let mut readcaps = BTreeMap::new();
     let mut readcaps = BTreeMap::new();
-    readcaps.insert(readcap.issued_to, readcap.key);
+    readcaps.insert(creds.principal(), creds.ser_encrypt(&block_key).unwrap());
     // Notice that the writecap path contains the block path. If this were not the case, the block
     // Notice that the writecap path contains the block path. If this were not the case, the block
     // would be invalid.
     // would be invalid.
     let (writecap, creds) = make_writecap_and_creds(vec!["apps"]);
     let (writecap, creds) = make_writecap_and_creds(vec!["apps"]);
@@ -219,13 +211,15 @@ pub(crate) fn make_block_with(readcap: Readcap) -> Box<dyn Block> {
         writecap: Some(writecap),
         writecap: Some(writecap),
         integrity: Some(VarHash::from(HashKind::Sha2_256)),
         integrity: Some(VarHash::from(HashKind::Sha2_256)),
         signing_key: creds.public_sign().to_owned(),
         signing_key: creds.public_sign().to_owned(),
+        secrets: block_key.ser_encrypt(&BlockMetaSecrets::default()).unwrap(),
+        block_key: Some(block_key.clone()),
+        secrets_struct: None,
     };
     };
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
     let mut stream =
     let mut stream =
         BlockStream::new(BtCursor::new(Vec::new()), creds).expect("create block stream failed");
         BlockStream::new(BtCursor::new(Vec::new()), creds).expect("create block stream failed");
     stream.meta.body = header;
     stream.meta.body = header;
     stream.meta.sig = sig;
     stream.meta.sig = sig;
-    let block_key = stream.block_key().expect("get block key failed");
     let stream = MerkleStream::new(stream).expect("create merkle stream failed");
     let stream = MerkleStream::new(stream).expect("create merkle stream failed");
     let stream = SecretStream::new(block_key)
     let stream = SecretStream::new(block_key)
         .try_compose(stream)
         .try_compose(stream)

+ 4 - 16
crates/btlib/src/trailered.rs

@@ -142,24 +142,12 @@ mod private {
     }
     }
 
 
     impl<T: MetaAccess, D> MetaAccess for Trailered<T, D> {
     impl<T: MetaAccess, D> MetaAccess for Trailered<T, D> {
-        fn add_readcap_for(
-            &mut self,
-            owner: crate::Principal,
-            key: &dyn crate::crypto::Encrypter,
-        ) -> Result<()> {
-            self.inner.add_readcap_for(owner, key)
-        }
-
-        fn block_key(&self) -> Result<crate::crypto::SymKey> {
-            self.inner.block_key()
-        }
-
-        fn integrity(&self) -> Option<&[u8]> {
-            self.inner.integrity()
+        fn meta(&self) -> &crate::BlockMeta {
+            self.inner.meta()
         }
         }
 
 
-        fn set_path(&mut self, path: crate::BlockPath) {
-            self.inner.set_path(path)
+        fn mut_meta(&mut self) -> &mut crate::BlockMeta {
+            self.inner.mut_meta()
         }
         }
     }
     }