Browse Source

Got reads and writes working in the FUSE daemon.

Matthew Carr 2 years ago
parent
commit
63eec7c42b

+ 1 - 1
.vscode/launch.json

@@ -11,7 +11,7 @@
             "cargo": {
                 "args": [ "build" ]
             },
-            "args": [],
+            "args": ["--features", "testing"],
             "cwd": "${workspaceFolder}"
         },
         {

+ 216 - 1
Cargo.lock

@@ -26,6 +26,15 @@ dependencies = [
  "alloc-no-stdlib",
 ]
 
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "ansi_term"
 version = "0.12.1"
@@ -148,6 +157,7 @@ dependencies = [
  "base64-url",
  "brotli",
  "btserde",
+ "chrono",
  "ctor",
  "dbus",
  "env_logger",
@@ -155,6 +165,7 @@ dependencies = [
  "fuse-backend-rs",
  "harness",
  "lazy_static",
+ "libc",
  "log",
  "nix 0.25.0",
  "openssl",
@@ -191,6 +202,12 @@ dependencies = [
  "serde-big-array",
 ]
 
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
 [[package]]
 name = "byteorder"
 version = "1.4.3"
@@ -228,6 +245,21 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-integer",
+ "num-traits",
+ "time",
+ "wasm-bindgen",
+ "winapi",
+]
+
 [[package]]
 name = "clang-sys"
 version = "1.4.0"
@@ -254,6 +286,16 @@ dependencies = [
  "vec_map",
 ]
 
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.3"
@@ -270,6 +312,50 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "cxx"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "dbus"
 version = "0.9.6"
@@ -409,6 +495,30 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
 [[package]]
 name = "io-uring"
 version = "0.5.7"
@@ -419,6 +529,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -456,6 +575,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "link-cplusplus"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "log"
 version = "0.4.17"
@@ -505,7 +633,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
 dependencies = [
  "libc",
  "log",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "windows-sys",
 ]
 
@@ -556,6 +684,16 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.15"
@@ -816,6 +954,12 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
 
+[[package]]
+name = "scratch"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+
 [[package]]
 name = "semver"
 version = "0.11.0"
@@ -1022,6 +1166,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
 [[package]]
 name = "tokio"
 version = "1.21.2"
@@ -1139,12 +1294,72 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
 [[package]]
 name = "which"
 version = "4.3.0"

+ 7 - 1
TODO.txt

@@ -92,4 +92,10 @@ include:
   * 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.
+Vec<u8> structs.
+
+- 23, 5, mdcarr941@gmail.com, 7f33fa,
+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
+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. 

+ 31 - 6
crates/btfuse/src/main.rs

@@ -2,7 +2,10 @@ use btlib::{
     blocktree::Blocktree,
     crypto::{tpm::TpmCredStore, CredStore},
 };
-use fuse_backend_rs::{api::server::Server, transport::{FuseSession, Error}};
+use fuse_backend_rs::{
+    api::server::Server,
+    transport::{Error, FuseSession},
+};
 use log::error;
 use std::{
     ffi::{c_char, CString},
@@ -60,6 +63,7 @@ struct FuseDaemon<'a> {
     path: &'a Path,
     /// The configuration string to use to connect to a Tabrmd instance.
     tabrmd_config: &'a str,
+    tpm_state_path: PathBuf,
 }
 
 impl<'a> FuseDaemon<'a> {
@@ -67,6 +71,7 @@ impl<'a> FuseDaemon<'a> {
         FuseDaemon {
             path,
             tabrmd_config,
+            tpm_state_path: path.join("tpm_state"),
         }
     }
 
@@ -86,7 +91,7 @@ impl<'a> FuseDaemon<'a> {
             TabrmdConfig::from_str(self.tabrmd_config).expect("failed to parse Tabrmd config"),
         ))
         .expect("failed to connect to Tabrmd");
-        let cred_store = TpmCredStore::new(context, self.path.join("tpm_state"))
+        let cred_store = TpmCredStore::new(context, self.tpm_state_path.to_owned())
             .expect("failed to create TpmCredStore");
         let node_creds = cred_store
             .node_creds()
@@ -144,19 +149,39 @@ fn main() {
 
 #[cfg(test)]
 mod test {
-    use btlib::test_helpers;
+    use std::time::Duration;
+
+    use btlib::{crypto::Creds, test_helpers, Epoch, Principaled, btlog::BuilderExt};
     use tempdir::TempDir;
 
     use super::*;
 
-    /// Starts the `FuseDaemon` in a new temporary directory prefixed by "btfuse".
+    /// Creates a new file system and mounts it at `/tmp/btfuse.<random>/mnt`.
     #[test]
+    #[allow(dead_code)]
     fn server_start() {
-        std::env::set_var("RUST_LOG", "info");
-        env_logger::init();
+        const ROOT_PASSWD: &str = "Gnurlingwheel";
+        std::env::set_var("RUST_LOG", "debug");
+        env_logger::Builder::from_default_env().btformat().init();
         let temp_dir = TempDir::new("btfuse").expect("failed to create TempDir");
         let swtpm = test_helpers::SwtpmHarness::new().expect("failed to start swtpm");
         let daemon = FuseDaemon::new(temp_dir.path(), swtpm.tabrmd_config());
+        {
+            let context = swtpm.context().expect("failed to create TPM context");
+            let cred_store = TpmCredStore::new(context, daemon.tpm_state_path.to_owned())
+                .expect("failed to create TpmCredStore");
+            let root_creds = cred_store
+                .gen_root_creds(ROOT_PASSWD)
+                .expect("failed to gen root creds");
+            let mut node_creds = cred_store.node_creds().expect("failed to get node creds");
+            let expires = Epoch::now() + Duration::from_secs(3600);
+            let writecap = root_creds
+                .issue_writecap(node_creds.principal(), vec![], expires)
+                .expect("failed to issue writecap to node creds");
+            cred_store
+                .assign_node_writecap(&mut node_creds, writecap)
+                .expect("failed to assign writecap");
+        }
         daemon.start();
     }
 }

+ 3 - 2
crates/btlib/Cargo.toml

@@ -13,7 +13,6 @@ testing=[
     "dep:tempdir",
     "dep:ctor",
     "dep:nix",
-    "dep:env_logger",
     "dep:lazy_static",
     "dep:dbus",
     "dep:vm-memory"
@@ -38,6 +37,9 @@ brotli = "3.3.4"
 os_pipe = { version = "1.1.1", features = ["io_safety"] }
 zerocopy = "0.6.1"
 fuse-backend-rs = "0.9.6"
+libc = "0.2.137"
+env_logger = { version = "0.9.0" }
+chrono = "0.4.23"
 # These are dev-dependencies. They are declared this way so test_helpers can be used in other
 # crates. The build.rs script also supports this by turning on "cfg(test)" when
 # the testing feature is enabled. This is a hack and should be removed once this issue is resolved:
@@ -45,7 +47,6 @@ fuse-backend-rs = "0.9.6"
 tempdir = { version = "0.3.7", optional = true }
 ctor = { version = "0.1.22", optional = true }
 nix = { version = "0.25.0", optional = true }
-env_logger = { version = "0.9.0", optional = true }
 lazy_static = { version = "1.4.0", optional = true }
 dbus = { version = "0.9.6", optional = true }
 vm-memory = { version = "0.9.0", optional = true }

File diff suppressed because it is too large
+ 748 - 116
crates/btlib/src/blocktree.rs


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

@@ -0,0 +1,25 @@
+use env_logger;
+use std::io::Write;
+use chrono;
+
+pub trait BuilderExt {
+    /// Uses a standard format for log messages which includes the source file and line number
+    /// a logging statement occurred on.
+    fn btformat(&mut self) -> &mut Self;
+}
+
+impl BuilderExt for env_logger::Builder {
+    fn btformat(&mut self) -> &mut Self {
+        self.format(|fmt, record| {
+            writeln!(
+                fmt,
+                "[{} {} {}:{}] {}",
+                chrono::Utc::now().to_rfc3339(),
+                record.level(),
+                record.file().unwrap_or("(unknown)"),
+                record.line().unwrap_or(u32::MAX),
+                record.args(),
+            )
+        })
+    }
+}

+ 10 - 12
crates/btlib/src/crypto/mod.rs

@@ -1584,7 +1584,7 @@ impl ConcreteCreds {
     }
 
     #[cfg(feature = "testing")]
-    pub(crate) fn generate() -> Result<ConcreteCreds> {
+    pub fn generate() -> Result<ConcreteCreds> {
         let encrypt = Encrypt::RSA_OAEP_3072_SHA_256.generate()?;
         let sign = Sign::RSA_PSS_3072_SHA_256.generate()?;
         Ok(ConcreteCreds {
@@ -1593,6 +1593,10 @@ impl ConcreteCreds {
             writecap: None,
         })
     }
+
+    pub fn set_writecap(&mut self, writecap: Writecap) {
+        self.writecap = Some(writecap)
+    }
 }
 
 impl Verifier for ConcreteCreds {
@@ -1647,10 +1651,6 @@ impl CredsPriv for ConcreteCreds {
     fn writecap(&self) -> Option<&Writecap> {
         self.writecap.as_ref()
     }
-
-    fn set_writecap(&mut self, writecap: Writecap) {
-        self.writecap = Some(writecap);
-    }
 }
 
 impl Creds for ConcreteCreds {}
@@ -1784,8 +1784,7 @@ pub trait Signer {
 
     fn ser_sign_into<T: Serialize>(&self, value: &T, buf: &mut Vec<u8>) -> Result<Signature> {
         write_to(value, buf)?;
-        let sig = self.sign(std::iter::once(buf.as_slice()))?;
-        Ok(sig)
+        self.sign(std::iter::once(buf.as_slice()))
     }
 }
 
@@ -1906,9 +1905,6 @@ pub trait CredsPriv: Decrypter + Signer {
     /// Returns a reference to the writecap associated with these credentials, if one has been
     /// issued.
     fn writecap(&self) -> Option<&Writecap>;
-
-    /// Sets the writecap used by these creds to the given instance.
-    fn set_writecap(&mut self, writecap: Writecap);
 }
 
 /// Trait for types which contain both public and private credentials.
@@ -1961,19 +1957,21 @@ pub trait CredStore {
         password: &str,
         exported: Self::ExportedCreds,
     ) -> Result<Self::CredHandle>;
+    fn assign_node_writecap(&self, handle: &mut Self::CredHandle, writecap: Writecap)
+        -> Result<()>;
 }
 
 impl BlockMeta {
     /// Validates that this metadata struct contains a valid writecap, that this writecap is
     /// permitted to write to the path of this block and that the signature in this metadata struct
     /// is valid and matches the key the writecap was issued to.
-    pub fn assert_valid(&self) -> Result<()> {
+    pub fn assert_valid(&self, path: &BlockPath) -> Result<()> {
         let body = &self.body;
         let writecap = body
             .writecap
             .as_ref()
             .ok_or(crate::Error::MissingWritecap)?;
-        writecap.assert_valid_for(&body.path)?;
+        writecap.assert_valid_for(path)?;
         let signed_by = body.signing_key.principal();
         if writecap.body.issued_to != signed_by {
             return Err(Error::signature_mismatch(

+ 28 - 13
crates/btlib/src/crypto/tpm.rs

@@ -856,8 +856,8 @@ impl TpmCredStore {
     const ENCRYPT_SCHEME: Encrypt = Encrypt::RSA_OAEP_2048_SHA_256;
     const DEFAULT_WRITECAP_EXP: Duration = Duration::from_secs(60 * 60 * 24 * 365 * 10);
 
-    pub fn new<P: AsRef<Path>>(mut context: Context, state_path: P) -> Result<TpmCredStore> {
-        let storage = Storage::load_or_init(state_path.as_ref())?;
+    pub fn new(mut context: Context, state_path: PathBuf) -> Result<TpmCredStore> {
+        let storage = Storage::load_or_init(&state_path)?;
         let session = context.start_default_auth_session()?;
         context.set_sessions((Some(session), None, None));
         let cookie = storage.cookie.clone();
@@ -868,7 +868,7 @@ impl TpmCredStore {
         }
         Ok(TpmCredStore {
             state,
-            storage_path: state_path.as_ref().to_owned(),
+            storage_path: state_path,
             cookie,
         })
     }
@@ -1186,6 +1186,25 @@ impl CredStore for TpmCredStore {
         self.persist(&creds, |storage, handles| storage.root = Some(handles))?;
         Ok(creds)
     }
+
+    fn assign_node_writecap(
+        &self,
+        handle: &mut Self::CredHandle,
+        writecap: Writecap,
+    ) -> Result<()> {
+        handle.writecap = Some(writecap.clone());
+        let mut state = self.state.write()?;
+        state
+            .node_creds
+            .as_mut()
+            .map(|e| e.writecap = Some(writecap.clone()));
+        state
+            .storage
+            .node
+            .as_mut()
+            .map(|e| e.writecap = Some(writecap));
+        self.save_storage(&mut state)
+    }
 }
 
 impl<S: Scheme> AsymKeyPub<S> {
@@ -1459,10 +1478,6 @@ impl CredsPriv for TpmCreds {
     fn writecap(&self) -> Option<&Writecap> {
         self.writecap.as_ref()
     }
-
-    fn set_writecap(&mut self, writecap: Writecap) {
-        self.writecap = Some(writecap)
-    }
 }
 
 impl Creds for TpmCreds {}
@@ -1611,7 +1626,7 @@ mod test {
     fn tpm_cred_store_new() -> Result<()> {
         let harness = SwtpmHarness::new()?;
         let cookie_path = harness.dir_path().join("cookie.bin");
-        let store = TpmCredStore::new(harness.context()?, &cookie_path)?;
+        let store = TpmCredStore::new(harness.context()?, cookie_path.to_owned())?;
         let cookie = File::open(&cookie_path)?;
         let metadata = cookie.metadata()?;
         let actual = metadata.permissions().mode();
@@ -1625,7 +1640,7 @@ mod test {
     fn gen_creds() -> Result<()> {
         let harness = SwtpmHarness::new()?;
         let cookie_path = harness.dir_path().join("cookie.bin");
-        let store = TpmCredStore::new(harness.context()?, &cookie_path)?;
+        let store = TpmCredStore::new(harness.context()?, cookie_path)?;
         store.gen_node_creds()?;
         Ok(())
     }
@@ -1668,7 +1683,7 @@ mod test {
     /// in the returned tuple is significant, as TpmCredStore must be dropped _before_ SwtpmHarness.
     fn test_store() -> Result<(SwtpmHarness, TpmCredStore)> {
         let harness = SwtpmHarness::new()?;
-        let store = TpmCredStore::new(harness.context()?, &harness.state_path())?;
+        let store = TpmCredStore::new(harness.context()?, harness.state_path().to_owned())?;
         Ok((harness, store))
     }
 
@@ -1747,7 +1762,7 @@ mod test {
             creds.principal()
         };
         drop(store);
-        let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
+        let store = TpmCredStore::new(harness.context()?, harness.state_path().to_owned())?;
         let creds = store.node_creds()?;
         let actual = creds.principal();
         assert_eq!(expected, actual);
@@ -1822,7 +1837,7 @@ mod test {
             creds.principal()
         };
         drop(store);
-        let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
+        let store = TpmCredStore::new(harness.context()?, harness.state_path().to_owned())?;
         let creds = store.root_creds(PASSWORD)?;
         let actual = creds.principal();
         assert_eq!(expected, actual);
@@ -1836,7 +1851,7 @@ mod test {
         let (harness, store) = test_store()?;
         store.gen_root_creds("Galileo")?;
         drop(store);
-        let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
+        let store = TpmCredStore::new(harness.context()?, harness.state_path().to_owned())?;
         let creds = store.root_creds("Figaro")?;
         assert!(sign_verify_test(&creds).is_err());
         assert!(encrypt_decrypt_test(&creds).is_err());

+ 127 - 43
crates/btlib/src/lib.rs

@@ -9,6 +9,7 @@ pub mod crypto;
 pub mod msg;
 mod sectored_buf;
 mod trailered;
+pub mod btlog;
 
 #[cfg(feature = "testing")]
 pub mod test_helpers;
@@ -55,6 +56,9 @@ pub enum Error {
     Crypto(crypto::Error),
     IncorrectSize { expected: usize, actual: usize },
     NoBlockKey,
+    NotOpen(u64),
+    NoBlockPath,
+    InodeNotFound(u64),
     Custom(Box<dyn std::fmt::Debug + Send + Sync>),
 }
 
@@ -75,6 +79,9 @@ impl Display for Error {
                 write!(f, "incorrect size {actual}, expected {expected}")
             }
             Error::NoBlockKey => write!(f, "no block key is present"),
+            Error::NotOpen(inode) => write!(f, "inode {inode} is not open"),
+            Error::NoBlockPath => write!(f, "no block path was specified"),
+            Error::InodeNotFound(inode) => write!(f, "inode {inode} could not be found"),
             Error::Custom(err) => err.fmt(f),
         }
     }
@@ -343,7 +350,6 @@ impl From<&BlockMetaSecrets> for Attr {
 /// calculations.
 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
 pub struct BlockMetaBody {
-    path: BlockPath,
     /// A copy of the block key encrypted using this block's parent's key. If this is None, then
     /// this block is not encrypted.
     inherit: Option<Ciphertext<SymKey>>,
@@ -356,6 +362,9 @@ pub struct BlockMetaBody {
     /// Additional metadata which is subject to confidentiality protection.
     secrets: Ciphertext<BlockMetaSecrets>,
 
+    #[serde(skip)]
+    /// The path in the blocktree where this block is located.
+    path: BlockPath,
     #[serde(skip)]
     /// The cleartext block key.
     block_key: Option<SymKey>,
@@ -458,7 +467,7 @@ impl BlockMetaBody {
 }
 
 /// Signed metadata associated with a block.
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct BlockMeta {
     body: BlockMetaBody,
     sig: Signature,
@@ -500,21 +509,25 @@ struct BlockStream<T, C> {
 }
 
 impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
-    fn new(inner: T, creds: C) -> Result<BlockStream<T, C>> {
+    fn new(inner: T, creds: C, block_path: BlockPath) -> Result<BlockStream<T, C>> {
         let (trailered, meta) = Trailered::<_, BlockMeta>::new(inner)?;
         let meta = match meta {
             Some(mut meta) => {
-                meta.assert_valid()?;
+                meta.assert_valid(&block_path)?;
+                meta.body.path = block_path;
                 // 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 =
+                    Some(creds.writecap().ok_or(Error::MissingWritecap)?.to_owned());
                 meta.body.signing_key = creds.public_sign().to_owned();
                 meta.body.use_block_key_for(&creds)?;
                 meta
             }
             None => {
                 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.path = block_path;
+                meta.body.add_readcap_for(creds.principal(), &creds)?;
+                meta.body.writecap =
+                    Some(creds.writecap().ok_or(Error::MissingWritecap)?.to_owned());
                 meta
             }
         };
@@ -527,7 +540,7 @@ impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
     }
 }
 
-impl<T: Write + Seek, C: Signer> Write for BlockStream<T, C> {
+impl<T: Write + Seek, C: Signer + Principaled + Decrypter> Write for BlockStream<T, C> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.trailered.write(buf)
     }
@@ -540,7 +553,7 @@ impl<T: Write + Seek, C: Signer> Write for BlockStream<T, C> {
     }
 }
 
-impl<T: Write + Seek, C: Signer> WriteInteg for BlockStream<T, C> {
+impl<T: Write + Seek, C: Signer + Principaled + Decrypter> WriteInteg for BlockStream<T, C> {
     fn flush_integ(&mut self, integrity: &[u8]) -> io::Result<()> {
         let meta_body = &mut self.meta.body;
         let integ = meta_body.integrity.get_or_insert_with(VarHash::default);
@@ -620,6 +633,7 @@ pub struct BlockOpenOptions<T, C> {
     creds: C,
     encrypt: bool,
     compress: bool,
+    block_path: Option<BlockPath>,
 }
 
 impl BlockOpenOptions<(), ()> {
@@ -629,6 +643,7 @@ impl BlockOpenOptions<(), ()> {
             creds: (),
             encrypt: true,
             compress: true,
+            block_path: Default::default(),
         }
     }
 }
@@ -640,6 +655,7 @@ impl<T, C> BlockOpenOptions<T, C> {
             creds: self.creds,
             encrypt: self.encrypt,
             compress: self.compress,
+            block_path: self.block_path,
         }
     }
 
@@ -649,6 +665,7 @@ impl<T, C> BlockOpenOptions<T, C> {
             creds,
             encrypt: self.encrypt,
             compress: self.compress,
+            block_path: self.block_path,
         }
     }
 
@@ -661,13 +678,19 @@ impl<T, C> BlockOpenOptions<T, C> {
         self.compress = compress;
         self
     }
+
+    pub fn with_block_path(mut self, block_path: BlockPath) -> Self {
+        self.block_path = Some(block_path);
+        self
+    }
 }
 
 impl<T: Read + Write + Seek + FileReadWriteVolatile + 'static, C: Creds + 'static>
     BlockOpenOptions<T, C>
 {
     pub fn open(self) -> Result<Box<dyn Block>> {
-        let stream = BlockStream::new(self.inner, self.creds)?;
+        let block_path = self.block_path.ok_or(Error::NoBlockPath)?;
+        let stream = BlockStream::new(self.inner, self.creds, block_path)?;
         let block_key = stream.meta_body().block_key().map(|e| e.to_owned())?;
         let mut stream = MerkleStream::new(stream)?;
         stream.assert_root_integrity()?;
@@ -782,6 +805,27 @@ pub struct Writecap {
     next: Option<Box<Writecap>>,
 }
 
+impl Writecap {
+    /// Returns the root key that was used to sign this writecap.
+    pub fn root_signing_key(&self) -> &AsymKeyPub<Sign> {
+        let mut writecap = self;
+        while writecap.next.is_some() {
+            writecap = writecap.next.as_ref().unwrap();
+        }
+        &writecap.body.signing_key
+    }
+
+    /// Returns the principal of the root key which was used to sign this writecap.
+    pub fn root_principal(&self) -> Principal {
+        self.root_signing_key().principal()
+    }
+
+    /// Returns the path to the root block of the blocktree that the root principal owns.
+    pub fn root_block_path(&self) -> BlockPath {
+        BlockPath::new(self.root_principal(), vec![])
+    }
+}
+
 /// Fragments are created from blocks using Erasure Encoding and stored with other nodes in the
 /// network to provide availability and redundancy of data.
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -838,6 +882,24 @@ pub enum DirEntry {
     Server(ServerRecord),
 }
 
+impl DirEntry {
+    pub fn inode(&self) -> Option<u64> {
+        match self {
+            Self::Directory(record) => Some(record.inode),
+            Self::File(record) => Some(record.inode),
+            Self::Server(..) => None,
+        }
+    }
+
+    pub fn kind(&self) -> u8 {
+        match self {
+            Self::Directory(..) => libc::DT_DIR,
+            Self::File(..) => libc::DT_REG,
+            Self::Server(..) => libc::DT_UNKNOWN,
+        }
+    }
+}
+
 /// This is the body contained in directory blocks.
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct Directory {
@@ -871,6 +933,10 @@ impl Directory {
             _ => Err(Error::custom("DirEntry was not the File variant")),
         }
     }
+
+    pub fn entries(&self) -> impl Iterator<Item = (&str, &DirEntry)> {
+        self.entries.iter().map(|kv| (kv.0.as_str(), kv.1))
+    }
 }
 
 impl Default for Directory {
@@ -1041,21 +1107,6 @@ mod tests {
         }
     }
 
-    #[test]
-    fn block_can_create_empty() {
-        let harness = SwtpmHarness::new().expect("failed to start swtpm");
-        let context = harness.context().expect("failed to retrieve context");
-        let cred_store = TpmCredStore::new(context, harness.state_path())
-            .expect("failed to create TpmCredStore");
-        let creds = cred_store.node_creds().expect("failed to get node creds");
-        BlockOpenOptions::new()
-            .with_inner(BtCursor::new(Vec::<u8>::new()))
-            .with_creds(creds)
-            .with_encrypt(true)
-            .open()
-            .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]
@@ -1087,6 +1138,8 @@ mod tests {
         node_creds: TpmCreds,
         _swtpm: SwtpmHarness,
         temp_dir: TempDir,
+        root_path: BlockPath,
+        node_path: BlockPath,
     }
 
     impl BlockTestCase {
@@ -1096,26 +1149,34 @@ mod tests {
             let temp_dir = TempDir::new("block_test").expect("failed to create temp dir");
             let swtpm = SwtpmHarness::new().expect("failed to start swtpm");
             let context = swtpm.context().expect("failed to retrieve context");
-            let cred_store = TpmCredStore::new(context, swtpm.state_path())
+            let cred_store = TpmCredStore::new(context, swtpm.state_path().to_owned())
                 .expect("failed to create TpmCredStore");
             let root_creds = cred_store
                 .gen_root_creds(Self::ROOT_PASSWORD)
                 .expect("failed to get root creds");
             let mut node_creds = cred_store.node_creds().expect("failed to get node creds");
+            let components = vec!["nodes".to_string(), "phone".to_string()];
             let writecap = root_creds
                 .issue_writecap(
                     node_creds.principal(),
-                    vec!["nodes".to_string(), "phone".to_string()],
+                    components.clone(),
                     Epoch::now() + Duration::from_secs(3600),
                 )
                 .expect("failed to issue writecap");
-            node_creds.set_writecap(writecap);
-            BlockTestCase {
+            cred_store
+                .assign_node_writecap(&mut node_creds, writecap)
+                .expect("failed to assign node writecap");
+            let case = BlockTestCase {
                 temp_dir,
                 _swtpm: swtpm,
                 node_creds,
+                node_path: BlockPath::new(root_creds.principal(), components),
+                root_path: BlockPath::new(root_creds.principal(), vec![]),
                 root_creds,
-            }
+            };
+            std::fs::create_dir_all(case.fs_path(&case.node_path))
+                .expect("failed to create node path");
+            case
         }
 
         fn fs_path(&self, path: &crate::BlockPath) -> PathBuf {
@@ -1124,17 +1185,18 @@ mod tests {
             fs_path
         }
 
-        fn open_new(&mut self, path: &crate::BlockPath) -> Box<dyn Block> {
+        fn open_new(&mut self, path: crate::BlockPath) -> Box<dyn Block> {
             let file = OpenOptions::new()
                 .create_new(true)
                 .read(true)
                 .write(true)
-                .open(&self.fs_path(path))
+                .open(&self.fs_path(&path))
                 .expect("failed to open file");
             let mut block = BlockOpenOptions::new()
                 .with_inner(file)
                 .with_creds(self.node_creds.clone())
                 .with_encrypt(true)
+                .with_block_path(path)
                 .open()
                 .expect("failed to open block");
             block
@@ -1143,19 +1205,39 @@ mod tests {
             block
         }
 
-        fn open_existing(&mut self, path: &crate::BlockPath) -> Box<dyn Block> {
+        fn open_existing(&mut self, path: crate::BlockPath) -> Box<dyn Block> {
             let file = OpenOptions::new()
                 .read(true)
                 .write(true)
-                .open(&self.fs_path(path))
+                .open(&self.fs_path(&path))
                 .expect("failed to reopen file");
             BlockOpenOptions::new()
                 .with_inner(file)
                 .with_creds(self.node_creds.clone())
                 .with_encrypt(true)
+                .with_block_path(path)
                 .open()
                 .expect("failed to reopen block")
         }
+
+        /// Returns a path in the directory which the node creds have permission to write to.
+        fn node_path(&self, file_name: &str) -> BlockPath {
+            let mut path = self.node_path.clone();
+            path.push_component(file_name.to_owned());
+            path
+        }
+    }
+
+    #[test]
+    fn block_can_create_empty() {
+        let case = BlockTestCase::new();
+        BlockOpenOptions::new()
+            .with_inner(BtCursor::new(Vec::<u8>::new()))
+            .with_creds(case.node_creds)
+            .with_encrypt(true)
+            .with_block_path(case.root_path)
+            .open()
+            .expect("failed to open block");
     }
 
     #[test]
@@ -1163,13 +1245,13 @@ mod tests {
         const EXPECTED: &[u8] = b"Silly sordid sulking sultans.";
 
         let mut case = BlockTestCase::new();
-        let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
+        let path = case.node_path("test.blk");
         {
-            let mut block = case.open_new(&path);
+            let mut block = case.open_new(path.clone());
             block.write(EXPECTED).expect("failed to write");
             block.flush().expect("flush failed");
         }
-        let mut block = case.open_existing(&path);
+        let mut block = case.open_existing(path);
         let mut actual = [0u8; EXPECTED.len()];
         block.read(&mut actual).expect("read failed");
         assert_eq!(EXPECTED, actual);
@@ -1181,14 +1263,14 @@ mod tests {
         const MID: usize = EXPECTED.len() / 2;
 
         let mut case = BlockTestCase::new();
-        let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
+        let path = case.node_path("test.blk");
         {
-            let mut block = case.open_new(&path);
+            let mut block = case.open_new(path.clone());
             block.write(&EXPECTED[..MID]).expect("first write failed");
             block.flush().expect("first flush failed");
         }
         {
-            let mut block = case.open_existing(&path);
+            let mut block = case.open_existing(path.clone());
             block
                 .seek(SeekFrom::Start(MID.try_into().unwrap()))
                 .expect("seek failed");
@@ -1196,7 +1278,7 @@ mod tests {
             block.flush().expect("second flush failed");
         }
         {
-            let mut block = case.open_existing(&path);
+            let mut block = case.open_existing(path);
             let mut actual = [0u8; EXPECTED.len()];
             block.read(&mut actual).expect("read failed");
             assert_eq!(EXPECTED, actual);
@@ -1209,7 +1291,7 @@ mod tests {
         const MID: usize = EXPECTED.len() / 2;
 
         let mut case = BlockTestCase::new();
-        let path = BlockPath::new(case.root_creds.principal(), vec!["test.blk".to_string()]);
+        let path = case.node_path("test.blk");
         let app_creds = {
             let mut app_creds = ConcreteCreds::generate().expect("failed to generate app creds");
             let writecap = case
@@ -1224,7 +1306,7 @@ mod tests {
             app_creds
         };
         {
-            let mut block = case.open_new(&path);
+            let mut block = case.open_new(path.clone());
             block
                 .mut_meta_body()
                 .add_readcap_for(app_creds.principal(), &app_creds)
@@ -1243,6 +1325,7 @@ mod tests {
                 // Note that this write is performed using app_creds.
                 .with_creds(app_creds)
                 .with_encrypt(true)
+                .with_block_path(path.clone())
                 .open()
                 .expect("failed to reopen block");
             block
@@ -1261,6 +1344,7 @@ mod tests {
                 .with_inner(file)
                 .with_creds(case.node_creds)
                 .with_encrypt(true)
+                .with_block_path(path)
                 .open()
                 .expect("failed to reopen block");
             let mut actual = [0u8; EXPECTED.len()];

+ 6 - 2
crates/btlib/src/sectored_buf.rs

@@ -162,7 +162,7 @@ mod private {
         }
     }
 
-    impl<T: Seek + Read + Write> Write for SectoredBuf<T> {
+    impl<T: Seek + Read + Write + MetaAccess> Write for SectoredBuf<T> {
         fn write(&mut self, mut src: &[u8]) -> io::Result<usize> {
             let src_len_start = src.len();
             let mut dest = {
@@ -219,6 +219,10 @@ mod private {
 
             // Update the stored length.
             self.len = self.len.max(self.pos);
+            self.inner.mut_meta_body().access_secrets(|secrets| {
+                secrets.size = (self.len - Self::RESERVED).try_into().box_err()?;
+                Ok(())
+            })?;
             self.inner.seek(SeekFrom::Start(0))?;
             self.fill_internal_buf()?;
             let len: u64 = self.len.try_into().box_err()?;
@@ -275,7 +279,7 @@ mod private {
         }
     }
 
-    impl<T: Seek + Read + Write> Seek for SectoredBuf<T> {
+    impl<T: Seek + Read + Write + MetaAccess> Seek for SectoredBuf<T> {
         fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
             let inner_pos = self.inner.stream_position()?;
             let inner_pos_new = match pos {

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

@@ -104,8 +104,15 @@ pub static BLOCK_KEY: SymKey = {
 lazy_static! {
     pub static ref ROOT_CREDS: ConcreteCreds =
         ConcreteCreds::generate().expect("root cred generation failed");
-    pub static ref NODE_CREDS: ConcreteCreds =
-        ConcreteCreds::generate().expect("node cred generation failed");
+    pub static ref NODE_CREDS: ConcreteCreds = {
+        let mut node_creds = ConcreteCreds::generate().expect("node cred generation failed");
+        let expires = Epoch::now() + Duration::from_secs(3600);
+        let writecap = ROOT_CREDS
+            .issue_writecap(node_creds.principal(), vec![], expires)
+            .expect("failed to issue writecap to test_helpers::NODE_CREDS");
+        node_creds.set_writecap(writecap);
+        node_creds
+    };
 }
 
 /// Converts the given error to a serde_block_tree error by turning it into a message.
@@ -169,7 +176,7 @@ pub fn make_writecap_trusted_by<C: Creds>(
 }
 
 pub fn make_key_pair() -> impl Creds {
-    ROOT_CREDS.clone()
+    NODE_CREDS.clone()
 }
 
 pub fn make_self_signed_writecap() -> (Writecap, impl Creds) {
@@ -204,8 +211,9 @@ pub fn make_block_with<C: CredsPub>(creds: &C) -> Box<dyn Block> {
     // would be invalid.
     let (writecap, creds) = make_writecap_and_creds(vec!["apps"]);
     let root_writecap = writecap.next.as_ref().unwrap();
+    let path = make_path_with_root(root_writecap.body.issued_to.clone(), vec!["apps", "verse"]);
     let header = BlockMetaBody {
-        path: make_path_with_root(root_writecap.body.issued_to.clone(), vec!["apps", "verse"]),
+        path: path.clone(),
         inherit: Some(INHERIT.clone()),
         readcaps,
         writecap: Some(writecap),
@@ -216,8 +224,8 @@ pub fn make_block_with<C: CredsPub>(creds: &C) -> Box<dyn Block> {
         secrets_struct: None,
     };
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
-    let mut stream =
-        BlockStream::new(BtCursor::new(Vec::new()), creds).expect("create block stream failed");
+    let mut stream = BlockStream::new(BtCursor::new(Vec::new()), creds, path)
+        .expect("create block stream failed");
     stream.meta.body = header;
     stream.meta.sig = sig;
     let stream = MerkleStream::new(stream).expect("create merkle stream failed");
@@ -580,6 +588,7 @@ impl<const N: usize> ZeroCopyWriter for BtCursor<[u8; N]> {
 pub struct SectoredCursor<T: FromVec> {
     cursor: BtCursor<T>,
     sect_sz: usize,
+    meta: BlockMeta,
 }
 
 impl<T: FromVec> SectoredCursor<T> {
@@ -587,6 +596,7 @@ impl<T: FromVec> SectoredCursor<T> {
         SectoredCursor {
             cursor: BtCursor::new(inner),
             sect_sz,
+            meta: BlockMeta::new(&*NODE_CREDS).unwrap(),
         }
     }
 }
@@ -632,6 +642,16 @@ impl<T: FromVec> Seek for SectoredCursor<T> {
     }
 }
 
+impl<T: FromVec> MetaAccess for SectoredCursor<T> {
+    fn meta(&self) -> &BlockMeta {
+        &self.meta
+    }
+
+    fn mut_meta(&mut self) -> &mut BlockMeta {
+        &mut self.meta
+    }
+}
+
 trait ExitStatusExt {
     fn success_or_err(&self) -> Result<()>;
 }

Some files were not shown because too many files changed in this diff