Forráskód Böngészése

* Added tests for the configuration of the binary crates.
* Rewrote the `BlockPath` struct to store paths in a single
contiguous buffer in memory to improve cache utilization.

Matthew Carr 1 éve
szülő
commit
1fd0c24d12

+ 100 - 3
Cargo.lock

@@ -826,6 +826,17 @@ dependencies = [
  "termcolor",
 ]
 
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "errno"
 version = "0.3.1"
@@ -847,6 +858,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
 [[package]]
 name = "fd-lock"
 version = "3.0.12"
@@ -854,7 +874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "39ae6b3d9530211fb3b12a95374b8b0823be812f53d09e18c5675c0146b09642"
 dependencies = [
  "cfg-if",
- "rustix",
+ "rustix 0.37.19",
  "windows-sys 0.48.0",
 ]
 
@@ -865,8 +885,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4e56602b469b2201400dec66a66aec5a9b8761ee97cd1b8c96ab2483fcc16cc9"
 dependencies = [
  "atomic",
+ "parking_lot",
  "pear",
  "serde",
+ "tempfile",
  "uncased",
  "version_check",
 ]
@@ -1402,6 +1424,15 @@ version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
 
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "io-lifetimes"
 version = "1.0.10"
@@ -1493,12 +1524,28 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
 [[package]]
 name = "linux-raw-sys"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
 
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.17"
@@ -1767,6 +1814,29 @@ version = "6.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
 
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys 0.42.0",
+]
+
 [[package]]
 name = "paste"
 version = "1.0.11"
@@ -2258,6 +2328,20 @@ dependencies = [
  "semver",
 ]
 
+[[package]]
+name = "rustix"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
+dependencies = [
+ "bitflags",
+ "errno 0.2.8",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.1.4",
+ "windows-sys 0.42.0",
+]
+
 [[package]]
 name = "rustix"
 version = "0.37.19"
@@ -2265,10 +2349,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
 dependencies = [
  "bitflags",
- "errno",
+ "errno 0.3.1",
  "io-lifetimes",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.3.7",
  "windows-sys 0.48.0",
 ]
 
@@ -2647,6 +2731,19 @@ dependencies = [
  "remove_dir_all",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix 0.36.7",
+ "windows-sys 0.42.0",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.3"

+ 32 - 0
crates/btconfig/src/lib.rs

@@ -1,3 +1,35 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//! This crate defines the common configuration system used by other Blocktree crates. To define a
+//! new configuration struct all you need to do is derive [Serialize] and [Deserialize] and
+//! implement the [Default] trait.
+//!
+//! Environment variables prefixed with "BT_" are used for setting configuration values. When a
+//! configuration struct contains another struct in one of its fields, then names of the fields
+//! in the sub-struct are separated from the struct's field name with `_`. In order for this
+//! to work you have to use a [serde] attribute to rename fields which contain `_` characters.
+//! For example:
+//! ```
+//! use btconfig::CredStoreConfig;
+//! use std::path::PathBuf;
+//! use serde::{Serialize, Deserialize};
+//!
+//! #[derive(Serialize, Deserialize)]
+//! struct AppConfig {
+//!     #[serde(rename = "credstore")]
+//!     cred_store: CredStoreConfig,
+//!     path: PathBuf,
+//! }
+//! ```
+//! For this struct, the path field is set using the `BT_PATH` environment variable. In order to set
+//! credential store, the `BT_CREDSTORE_TYPE` variable must be set to a valid variant name of the
+//! [CredStoreConfig] enum, and the
+//! required fields must be set using their variables. For example, if `BT_CREDSTORE_TYPE=File`,
+//! then `BT_CREDSTORE_PATH` must be set to the path to the credential store file.
+//!
+//! When an enum is used for configuration, serde must be configured to externally tag it in order
+//! for a variant to be specified using an environment variable. This is done by adding
+//! `#[serde(tag = "type")]` to the enum. See [CredStoreConfig] as an example.
+
 use btlib::{
     self,
     crypto::{file_cred_store::FileCredStore, tpm::TpmCredStore, CredStore, CredStoreMut, Creds},

+ 2 - 1
crates/btconsole/src/lib.rs

@@ -12,9 +12,10 @@ use tower::Service;
 use tower_http::services::ServeDir;
 use yew::ServerRenderer;
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
 pub struct BtConsoleConfig {
     addr: SocketAddr,
+    #[serde(rename = "distpath")]
     dist_path: PathBuf,
 }
 

+ 10 - 2
crates/btfproto-tests/src/lib.rs

@@ -17,7 +17,11 @@ lazy_static! {
     static ref ROOT_CREDS: ConcreteCreds = {
         let mut root_creds = ConcreteCreds::generate().unwrap();
         let writecap = root_creds
-            .issue_writecap(root_creds.principal(), vec![], one_hour_hence())
+            .issue_writecap(
+                root_creds.principal(),
+                &mut std::iter::empty(),
+                one_hour_hence(),
+            )
             .unwrap();
         root_creds.set_writecap(writecap).unwrap();
         root_creds
@@ -26,7 +30,11 @@ lazy_static! {
         let root_creds = &ROOT_CREDS;
         let mut node_creds = ConcreteCreds::generate().unwrap();
         let writecap = root_creds
-            .issue_writecap(node_creds.principal(), vec![], one_hour_hence())
+            .issue_writecap(
+                node_creds.principal(),
+                &mut std::iter::empty(),
+                one_hour_hence(),
+            )
             .unwrap();
         node_creds.set_writecap(writecap).unwrap();
         node_creds

+ 1 - 1
crates/btfproto-tests/src/local_fs_tests.rs

@@ -657,7 +657,7 @@ mod tests {
         let name = "file.txt";
         let owner = case.from();
         let mut other = owner.as_ref().clone();
-        other.push_component("subdir".to_owned());
+        other.push_component("subdir");
         let other = Arc::new(other);
 
         let create_msg = Create {

+ 2 - 1
crates/btfsd/Cargo.toml

@@ -24,4 +24,5 @@ swtpm-harness = { path = "../swtpm-harness" }
 btserde = { path = "../btserde" }
 tempdir = "0.3.7"
 ctor = { version = "0.1.22" }
-libc = { version = "0.2.137" }
+libc = { version = "0.2.137" }
+figment = { version = "0.10.8", features = ["test"] }

+ 135 - 26
crates/btfsd/src/main.rs

@@ -12,18 +12,21 @@ use serde::{Deserialize, Serialize};
 use std::{net::IpAddr, path::PathBuf, sync::Arc};
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
-struct AppConfig {
-    credstore: CredStoreConfig,
+struct BtfsdConfig {
+    #[serde(rename = "credstore")]
+    cred_store: CredStoreConfig,
     /// The IP address to listen for filesystem connection on.
-    ipaddr: IpAddr,
+    #[serde(rename = "ipaddr")]
+    ip_addr: IpAddr,
     /// The path in the local filesystem where blocks are stored.
-    blockdir: PathBuf,
+    #[serde(rename = "blockdir")]
+    block_dir: PathBuf,
     /// Configuration for the btconsole webapp.
     console: Option<BtConsoleConfig>,
 }
 
-impl AppConfig {
-    fn new() -> Result<AppConfig> {
+impl BtfsdConfig {
+    fn new() -> Result<BtfsdConfig> {
         Figment::new()
             .merge(Serialized::defaults(Self::default()))
             .btconfig()?
@@ -32,12 +35,12 @@ impl AppConfig {
     }
 }
 
-impl Default for AppConfig {
+impl Default for BtfsdConfig {
     fn default() -> Self {
         Self {
-            credstore: CredStoreConfig::default(),
-            ipaddr: IpAddr::from([127, 0, 0, 1]),
-            blockdir: btconfig::default_block_dir(),
+            cred_store: CredStoreConfig::default(),
+            ip_addr: IpAddr::from([127, 0, 0, 1]),
+            block_dir: btconfig::default_block_dir(),
             console: Some(BtConsoleConfig::default()),
         }
     }
@@ -52,16 +55,16 @@ async fn provider(block_dir: PathBuf, creds: Arc<dyn Creds>) -> Result<impl FsPr
     }
 }
 
-async fn receiver(config: AppConfig) -> Result<impl Receiver> {
-    let node_creds = config.credstore.consume(NodeCredConsumer)??;
-    let provider = Arc::new(provider(config.blockdir, node_creds.clone()).await?);
-    new_fs_server(config.ipaddr, Arc::new(node_creds), provider)
+async fn receiver(config: BtfsdConfig) -> Result<impl Receiver> {
+    let node_creds = config.cred_store.consume(NodeCredConsumer)??;
+    let provider = Arc::new(provider(config.block_dir, node_creds.clone()).await?);
+    new_fs_server(config.ip_addr, Arc::new(node_creds), provider)
 }
 
 #[tokio::main]
 async fn main() -> Result<()> {
     env_logger::Builder::from_default_env().btformat().init();
-    let mut config = AppConfig::new()?;
+    let mut config = BtfsdConfig::new()?;
     let console_config = config
         .console
         .take()
@@ -80,6 +83,8 @@ async fn main() -> Result<()> {
 mod tests {
     use super::*;
 
+    use btconfig::CredStoreConfig;
+    use btconsole::BtConsoleConfig;
     use btfproto::{client::FsClient, msg::*};
     use btlib::{
         crypto::{
@@ -124,10 +129,10 @@ mod tests {
             let cred_store = FileCredStore::new(file_store_path).unwrap();
             cred_store.provision(ROOT_PASSWD).unwrap();
         }
-        let config = AppConfig {
-            credstore,
-            ipaddr,
-            blockdir: dir.path().join(BT_DIR),
+        let config = BtfsdConfig {
+            cred_store: credstore,
+            ip_addr: ipaddr,
+            block_dir: dir.path().join(BT_DIR),
             console: Some(BtConsoleConfig::default()),
         };
         let rx = receiver(config).await.unwrap();
@@ -166,10 +171,10 @@ mod tests {
             path: swtpm.state_path().to_owned(),
             tabrmd: swtpm.tabrmd_config().to_owned(),
         };
-        let config = AppConfig {
-            credstore,
-            ipaddr,
-            blockdir: dir.path().join(BT_DIR),
+        let config = BtfsdConfig {
+            cred_store: credstore,
+            ip_addr: ipaddr,
+            block_dir: dir.path().join(BT_DIR),
             console: Some(BtConsoleConfig::default()),
         };
         let rx = receiver(config).await.unwrap();
@@ -731,7 +736,7 @@ mod tests {
         let writecap = root_creds
             .issue_writecap(
                 creds.principal(),
-                vec![],
+                &mut std::iter::empty(),
                 Epoch::now() + Duration::from_secs(3600),
             )
             .unwrap();
@@ -781,7 +786,7 @@ mod tests {
         let writecap = root_creds
             .issue_writecap(
                 creds.principal(),
-                vec![DIRNAME.to_owned()],
+                &mut [DIRNAME].into_iter(),
                 Epoch::now() + Duration::from_secs(3600),
             )
             .unwrap();
@@ -854,7 +859,7 @@ mod tests {
             let writecap = root_creds
                 .issue_writecap(
                     creds.principal(),
-                    vec![],
+                    &mut std::iter::empty(),
                     Epoch::now() + Duration::from_secs(3600),
                 )
                 .unwrap();
@@ -916,3 +921,107 @@ mod tests {
         assert_eq!("write access denied", err);
     }
 }
+
+#[cfg(test)]
+mod config_tests {
+    use super::BtfsdConfig;
+    use std::{
+        net::{IpAddr, SocketAddr},
+        path::PathBuf,
+    };
+
+    use btconfig::CredStoreConfig;
+    use btconsole::BtConsoleConfig;
+    use figment::Jail;
+
+    #[test]
+    fn cred_store_file_path() {
+        Jail::expect_with(|jail| {
+            let expected = PathBuf::from("./file_credstore");
+            jail.set_env("BT_CREDSTORE_TYPE", "File");
+            jail.set_env("BT_CREDSTORE_PATH", expected.display());
+
+            let config = BtfsdConfig::new().unwrap();
+
+            let success = if let CredStoreConfig::File { path: actual } = config.cred_store {
+                expected == actual
+            } else {
+                false
+            };
+            assert!(success);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn cred_store_tpm_path_tabrmd() {
+        Jail::expect_with(|jail| {
+            let expected_path = PathBuf::from("./tpm_credstore");
+            let expected_tabrmd = String::from("bus_type=session");
+            jail.set_env("BT_CREDSTORE_TYPE", "Tpm");
+            jail.set_env("BT_CREDSTORE_PATH", expected_path.display());
+            jail.set_env("BT_CREDSTORE_TABRMD", expected_tabrmd.as_str());
+
+            let config = BtfsdConfig::new().unwrap();
+
+            let success = if let CredStoreConfig::Tpm {
+                path: actual_path,
+                tabrmd: actual_tabrmd,
+            } = config.cred_store
+            {
+                expected_path == actual_path && expected_tabrmd == actual_tabrmd
+            } else {
+                false
+            };
+            assert!(success);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn ip_addr() {
+        Jail::expect_with(|jail| {
+            let expected = IpAddr::from([172, 0, 0, 1]);
+            jail.set_env("BT_IPADDR", expected);
+
+            let config = BtfsdConfig::new().unwrap();
+
+            assert_eq!(expected, config.ip_addr);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn block_dir() {
+        Jail::expect_with(|jail| {
+            let expected = PathBuf::from("/tmp/blocks");
+            jail.set_env("BT_BLOCKDIR", expected.display());
+
+            let config = BtfsdConfig::new().unwrap();
+
+            assert_eq!(expected, config.block_dir);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn console() {
+        Jail::expect_with(|jail| {
+            let expected_addr = SocketAddr::from(([127, 0, 0, 42], 4129));
+            let expected_dist_path = PathBuf::from("/var/www");
+            jail.set_env("BT_CONSOLE_ADDR", expected_addr);
+            jail.set_env("BT_CONSOLE_DISTPATH", expected_dist_path.display());
+            let expected = BtConsoleConfig::new(expected_addr, expected_dist_path);
+
+            let config = BtfsdConfig::new().unwrap();
+
+            assert_eq!(expected, config.console.unwrap());
+
+            Ok(())
+        })
+    }
+}

+ 1 - 0
crates/btfuse/Cargo.toml

@@ -29,3 +29,4 @@ btfproto-tests = { path = "../btfproto-tests" }
 tempdir = "0.3.7"
 libc = "0.2.137"
 ctor = { version = "0.1.22" }
+figment = { version = "0.10.8", features = ["test"] }

+ 169 - 32
crates/btfuse/src/main.rs

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 mod fuse_daemon;
 use btconfig::{CredStoreConfig, FigmentExt, NodeCredConsumer};
+use figment::{providers::Serialized, Figment};
 use fuse_daemon::FuseDaemon;
 mod fuse_fs;
 
@@ -11,7 +12,6 @@ use btlib::{
     Result,
 };
 use btmsg::{transmitter, BlockAddr};
-use figment::{providers::Serialized, Figment};
 use serde::{Deserialize, Serialize};
 use std::{
     fs::{self},
@@ -40,38 +40,42 @@ impl Default for FsKind {
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
-struct AppConfig {
+pub struct BtfuseConfig {
     /// Configures the credential store used.
-    credstore: CredStoreConfig,
+    #[serde(rename = "credstore")]
+    pub cred_store: CredStoreConfig,
     /// Configures the filesystem provider used.
-    fskind: FsKind,
+    #[serde(rename = "fskind")]
+    pub fs_kind: FsKind,
     /// Sets the directory where the filesystem will be mounted.
-    mntdir: PathBuf,
+    #[serde(rename = "mntdir")]
+    pub mnt_dir: PathBuf,
     /// Specifies the options to use for the filesystem mount.
-    mntoptions: String,
+    #[serde(rename = "mntoptions")]
+    pub mnt_options: String,
     /// Prescribes the number of threads to use for handling FUSE messages from the kernel. If
     /// not specified, then the number of threads will be equal to the number of logical cores on
     /// the system.
-    threads: Option<NonZeroUsize>,
+    pub threads: Option<NonZeroUsize>,
 }
 
-impl AppConfig {
-    fn new() -> Result<Self> {
+impl BtfuseConfig {
+    pub fn new() -> Result<Self> {
         Figment::new()
-            .merge(Serialized::defaults(AppConfig::default()))
+            .merge(Serialized::defaults(BtfuseConfig::default()))
             .btconfig()?
             .extract()
             .map_err(|err| err.into())
     }
 }
 
-impl Default for AppConfig {
+impl Default for BtfuseConfig {
     fn default() -> Self {
         Self {
-            credstore: CredStoreConfig::default(),
-            fskind: FsKind::default(),
-            mntdir: "./btfuse_mnt".into(),
-            mntoptions: "default_permissions".into(),
+            cred_store: CredStoreConfig::default(),
+            fs_kind: FsKind::default(),
+            mnt_dir: "./btfuse_mnt".into(),
+            mnt_options: "default_permissions".into(),
             threads: None,
         }
     }
@@ -112,8 +116,12 @@ async fn remote_provider<C: 'static + Creds + Send + Sync>(
     Ok(client)
 }
 
-async fn run_daemon(config: AppConfig, mounted_signal: Option<oneshot::Sender<()>>) {
-    let node_creds = config.credstore.consume(NodeCredConsumer).unwrap().unwrap();
+async fn run_daemon(config: BtfuseConfig, mounted_signal: Option<oneshot::Sender<()>>) {
+    let node_creds = config
+        .cred_store
+        .consume(NodeCredConsumer)
+        .unwrap()
+        .unwrap();
     let fallback_path = {
         let writecap = node_creds
             .writecap()
@@ -121,7 +129,7 @@ async fn run_daemon(config: AppConfig, mounted_signal: Option<oneshot::Sender<()
             .unwrap();
         Arc::new(writecap.bind_path())
     };
-    let mut daemon = match config.fskind {
+    let mut daemon = match config.fs_kind {
         FsKind::Local { path: btdir } => {
             log::info!("starting daemon with local provider using {:?}", btdir);
             let provider = local_provider(btdir.clone(), node_creds)
@@ -134,8 +142,8 @@ async fn run_daemon(config: AppConfig, mounted_signal: Option<oneshot::Sender<()
                 })
                 .unwrap();
             FuseDaemon::new(
-                config.mntdir,
-                &config.mntoptions,
+                config.mnt_dir,
+                &config.mnt_options,
                 config.threads,
                 fallback_path,
                 mounted_signal,
@@ -151,8 +159,8 @@ async fn run_daemon(config: AppConfig, mounted_signal: Option<oneshot::Sender<()
                 .await
                 .expect("failed to create remote provider");
             FuseDaemon::new(
-                config.mntdir,
-                &config.mntoptions,
+                config.mnt_dir,
+                &config.mnt_options,
                 config.threads,
                 fallback_path,
                 mounted_signal,
@@ -168,7 +176,7 @@ async fn run_daemon(config: AppConfig, mounted_signal: Option<oneshot::Sender<()
 #[tokio::main]
 async fn main() {
     env_logger::init();
-    let config = AppConfig::new().unwrap();
+    let config = BtfuseConfig::new().unwrap();
     run_daemon(config, None).await;
 }
 
@@ -176,6 +184,7 @@ async fn main() {
 mod test {
     use super::*;
 
+    use btconfig::CredStoreConfig;
     use btfproto::{local_fs::ModeAuthorizer, server::new_fs_server};
     use btlib::{
         crypto::{tpm::TpmCredStore, CredStore, CredStoreMut, Creds},
@@ -275,7 +284,7 @@ mod test {
     const ROOT_PASSWD: &str = "password";
 
     struct TestCase<R: Receiver> {
-        config: AppConfig,
+        config: BtfuseConfig,
         handle: Option<JoinHandle<()>>,
         node_principal: OsString,
         stop_flag: Option<()>,
@@ -315,15 +324,15 @@ mod test {
         } else {
             (FsKind::Local { path: block_dir }, None)
         };
-        let config = AppConfig {
+        let config = BtfuseConfig {
             threads: Some(NonZeroUsize::new(1).unwrap()),
-            mntdir: tmp.path().join("mnt"),
-            credstore: CredStoreConfig::Tpm {
+            mnt_dir: tmp.path().join("mnt"),
+            cred_store: CredStoreConfig::Tpm {
                 path: swtpm.state_path().to_owned().into(),
                 tabrmd: swtpm.tabrmd_config().to_owned(),
             },
-            fskind: fs_kind,
-            mntoptions: "default_permissions".to_string(),
+            fs_kind,
+            mnt_options: "default_permissions".to_string(),
         };
         let config_clone = config.clone();
         let handle = tokio::spawn(async move {
@@ -362,7 +371,7 @@ mod test {
         let mut node_creds = cred_store.node_creds().unwrap();
         let expires = Epoch::now() + Duration::from_secs(3600);
         let writecap = root_creds
-            .issue_writecap(node_creds.principal(), vec![], expires)
+            .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
             .unwrap();
         cred_store
             .assign_node_writecap(&mut node_creds, writecap)
@@ -372,13 +381,13 @@ mod test {
 
     impl<R: Receiver> TestCase<R> {
         fn mnt_dir(&self) -> &Path {
-            &self.config.mntdir
+            &self.config.mnt_dir
         }
 
         /// Signals to the daemon that it must stop.
         fn signal_stop(&mut self) {
             if let Some(_) = self.stop_flag.take() {
-                unmount(&self.config.mntdir)
+                unmount(&self.config.mnt_dir)
             }
         }
 
@@ -840,3 +849,131 @@ mod test {
         read_more_than_whats_buffered(new_remote().await).await
     }
 }
+
+#[cfg(test)]
+mod config_tests {
+    use super::{BtfuseConfig, FsKind};
+
+    use std::{net::IpAddr, num::NonZeroUsize, path::PathBuf, sync::Arc};
+
+    use btconfig::CredStoreConfig;
+    use btlib::BlockPath;
+    use btmsg::BlockAddr;
+    use figment::Jail;
+
+    #[test]
+    fn fs_kind_local() {
+        Jail::expect_with(|jail| {
+            const EXPECTED_PATH: &str = "/tmp/blocks";
+            let expected = FsKind::Local {
+                path: EXPECTED_PATH.into(),
+            };
+            jail.set_env("BT_FSKIND_TYPE", "Local");
+            jail.set_env("BT_FSKIND_PATH", EXPECTED_PATH);
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, config.fs_kind);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn fs_kind_remote() {
+        Jail::expect_with(|jail| {
+            let expected_ip = IpAddr::from([127, 0, 0, 42]);
+            let expected_path = Arc::new(BlockPath::try_from(
+                "/0!zX_LMUVQO2Y7mgDomQB8ZdNsXKlykpHs-zPX9C3ztII/0!vVB5rOb3NFjzaZl_wlH3jqhBaYV7uuxrk3_s42xLnzg"
+            ).unwrap());
+            let expected_addr = BlockAddr::new(expected_ip, expected_path.clone());
+            let expected = FsKind::Remote {
+                addr: expected_addr,
+            };
+            jail.set_env("BT_FSKIND_TYPE", "Remote");
+            jail.set_env("BT_FSKIND_ADDR_IPADDR", expected_ip);
+            jail.set_env("BT_FSKIND_ADDR_PATH", expected_path.as_ref());
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, config.fs_kind);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn mnt_dir() {
+        Jail::expect_with(|jail| {
+            let expected = PathBuf::from("/tmp/btfuse_mnt");
+            jail.set_env("BT_MNTDIR", expected.display());
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, config.mnt_dir);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn mnt_options() {
+        Jail::expect_with(|jail| {
+            let expected = "default_permissions";
+            jail.set_env("BT_MNTOPTIONS", expected);
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, &config.mnt_options);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn threads_is_set() {
+        Jail::expect_with(|jail| {
+            let expected = Some(NonZeroUsize::new(8).unwrap());
+            jail.set_env("BT_THREADS", expected.unwrap().get());
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, config.threads);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn threads_is_not_set() {
+        Jail::expect_with(|_jail| {
+            let expected = None;
+
+            let config = BtfuseConfig::new().unwrap();
+
+            assert_eq!(expected, config.threads);
+
+            Ok(())
+        })
+    }
+
+    #[test]
+    fn cred_store_path() {
+        Jail::expect_with(|jail| {
+            let expected = PathBuf::from("/tmp/secrets/file_credstore");
+            jail.set_env("BT_CREDSTORE_TYPE", "File");
+            jail.set_env("BT_CREDSTORE_PATH", expected.display());
+
+            let config = BtfuseConfig::new().unwrap();
+
+            let success = if let CredStoreConfig::File { path: actual } = config.cred_store {
+                expected == actual
+            } else {
+                false
+            };
+            assert!(success);
+
+            Ok(())
+        })
+    }
+}

+ 2 - 1
crates/btlib-tests/src/cred_store_testing_ext.rs

@@ -12,7 +12,8 @@ pub trait CredStoreTestingExt: CredStoreMut {
         let root_creds = self.gen_root_creds(root_password)?;
         let mut node_creds = self.node_creds()?;
         let expires = Epoch::now() + Duration::from_secs(3600);
-        let writecap = root_creds.issue_writecap(node_creds.principal(), vec![], expires)?;
+        let writecap =
+            root_creds.issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)?;
         self.assign_node_writecap(&mut node_creds, writecap)
     }
 }

+ 1 - 1
crates/btlib-tests/src/tpm_cred_store_harness.rs

@@ -30,7 +30,7 @@ impl TpmCredStoreHarness {
         let mut node_creds = cred_store.node_creds().unwrap();
         let expires = Epoch::now() + Duration::from_secs(3600);
         let writecap = root_creds
-            .issue_writecap(node_creds.principal(), vec![], expires)
+            .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
             .unwrap();
         cred_store
             .assign_node_writecap(&mut node_creds, writecap)

+ 3 - 3
crates/btlib/benches/block_benches.rs

@@ -15,11 +15,11 @@ fn block_write(c: &mut Criterion) {
     let temp_dir = TempDir::new("block_bench").expect("failed to create temp dir");
     let root_creds = ConcreteCreds::generate().expect("failed to generate root_creds");
     let mut node_creds = ConcreteCreds::generate().expect("failed to generate node_creds");
-    let components = vec!["nodes".to_string(), "phone".to_string()];
+    let components = ["nodes", "phone"];
     let writecap = root_creds
         .issue_writecap(
             node_creds.principal(),
-            components.clone(),
+            &mut components.into_iter(),
             Epoch::now() + Duration::from_secs(3600),
         )
         .expect("failed to issue writecap");
@@ -29,7 +29,7 @@ fn block_write(c: &mut Criterion) {
     fs_path.extend(components.iter());
     std::fs::create_dir_all(&fs_path).expect("failed to create fs_path");
 
-    let node_path = BlockPath::new(root_creds.principal(), components);
+    let node_path = BlockPath::from_components(root_creds.principal(), components.into_iter());
     let file_path = fs_path.join("file.txt");
 
     c.bench_function("block_write", |b| {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 634 - 214
crates/btlib/src/block_path.rs


+ 11 - 10
crates/btlib/src/crypto.rs

@@ -2299,7 +2299,7 @@ pub trait Creds: CredsPriv + CredsPub + Send + Sync {
     fn issue_writecap(
         &self,
         issued_to: Principal,
-        path_components: Vec<String>,
+        path_components: &mut dyn Iterator<Item = &str>,
         expires: Epoch,
     ) -> Result<Writecap> {
         // The root principal is given by the path in our writecap, or if we don't have a writecap,
@@ -2308,7 +2308,7 @@ pub trait Creds: CredsPriv + CredsPub + Send + Sync {
             .writecap()
             .map(|e| e.root_principal())
             .unwrap_or_else(|| self.principal());
-        let path = BlockPath::new(root_principal, path_components);
+        let path = BlockPath::from_components(root_principal, path_components);
         let body = WritecapBody {
             issued_to,
             path,
@@ -2419,7 +2419,8 @@ pub trait CredStoreMut: CredStore {
     /// returned.
     fn provision_root(&self, password: &str, expires: Epoch) -> Result<Self::CredHandle> {
         let mut root_creds = self.gen_root_creds(password)?;
-        let writecap = root_creds.issue_writecap(root_creds.principal(), vec![], expires)?;
+        let writecap =
+            root_creds.issue_writecap(root_creds.principal(), &mut std::iter::empty(), expires)?;
         self.assign_root_writecap(&mut root_creds, writecap)?;
         Ok(root_creds)
     }
@@ -2619,7 +2620,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_valid() {
-        let writecap = make_writecap(vec!["apps", "verse"]);
+        let writecap = make_writecap(["apps", "verse"].into_iter());
         writecap
             .assert_valid_for(&writecap.body.path)
             .expect("failed to verify writecap");
@@ -2627,7 +2628,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_signature() -> Result<()> {
-        let mut writecap = make_writecap(vec!["apps", "verse"]);
+        let mut writecap = make_writecap(["apps", "verse"].into_iter());
         writecap.signature = Signature::empty(Sign::RSA_PSS_3072_SHA_256);
         let result = writecap.assert_valid_for(&writecap.body.path);
         if let Err(ref err) = result {
@@ -2656,7 +2657,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_path_not_contained() -> Result<()> {
-        let writecap = make_writecap(vec!["apps", "verse"]);
+        let writecap = make_writecap(["apps", "verse"].into_iter());
         let mut path = writecap.body.path.clone();
         path.pop_component();
         // `path` is now a superpath of `writecap.path`, thus the writecap is not authorized to
@@ -2667,7 +2668,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_expired() -> Result<()> {
-        let mut writecap = make_writecap(vec!["apps", "verse"]);
+        let mut writecap = make_writecap(["apps", "verse"].into_iter());
         writecap.body.expires = Epoch::now() - Duration::from_secs(1);
         let result = writecap.assert_valid_for(&writecap.body.path);
         assert_authz_err(WritecapAuthzErr::Expired, result)
@@ -2683,7 +2684,7 @@ mod tests {
             root_writecap,
             &root_key,
             node_principal,
-            vec!["apps", "contacts"],
+            ["apps", "contacts"].into_iter(),
         );
         let result = writecap.assert_valid_for(&writecap.body.path);
         assert_authz_err(WritecapAuthzErr::NotChained, result)
@@ -2693,14 +2694,14 @@ mod tests {
     fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> {
         let (mut root_writecap, root_key) = make_self_signed_writecap();
         let owner = Principal(VarHash::from(HashKind::Sha2_256));
-        root_writecap.body.path = make_path_with_root(owner, vec![]);
+        root_writecap.body.path = make_path_with_root(owner, std::iter::empty());
         root_key.sign_writecap(&mut root_writecap)?;
         let node_principal = NODE_CREDS.principal();
         let writecap = make_writecap_trusted_by(
             root_writecap,
             &root_key,
             node_principal,
-            vec!["apps", "contacts"],
+            ["apps", "contacts"].into_iter(),
         );
         let result = writecap.assert_valid_for(&writecap.body.path);
         assert_authz_err(WritecapAuthzErr::RootDoesNotOwnPath, result)

+ 3 - 3
crates/btlib/src/crypto/file_cred_store.rs

@@ -392,7 +392,7 @@ mod test {
         let root_creds = case.gen_root_creds("password").unwrap();
         let expires = Epoch::now() + Duration::from_secs(3600);
         let expected = root_creds
-            .issue_writecap(node_creds.principal(), vec![], expires)
+            .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
             .unwrap();
         case.assign_node_writecap(&mut node_creds, expected.clone())
             .unwrap();
@@ -436,7 +436,7 @@ mod test {
             let root_creds = case.gen_root_creds("password").unwrap();
             let expires = Epoch::now() + Duration::from_secs(3600);
             let expected = root_creds
-                .issue_writecap(node_creds.principal(), vec![], expires)
+                .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
                 .unwrap();
             case.assign_node_writecap(&mut node_creds, expected.clone())
                 .unwrap();
@@ -459,7 +459,7 @@ mod test {
             let mut root_creds = case.gen_root_creds(PASSWORD).unwrap();
             let expires = Epoch::now() + Duration::from_secs(3600);
             let expected = root_creds
-                .issue_writecap(root_creds.principal(), vec![], expires)
+                .issue_writecap(root_creds.principal(), &mut std::iter::empty(), expires)
                 .unwrap();
             case.assign_root_writecap(&mut root_creds, expected.clone())
                 .unwrap();

+ 8 - 8
crates/btlib/src/crypto/tpm.rs

@@ -1282,7 +1282,7 @@ impl TpmCreds {
     }
 
     fn init_root_writecap(&mut self, expires: Epoch) -> Result<()> {
-        let writecap = self.issue_writecap(self.principal(), Vec::new(), expires)?;
+        let writecap = self.issue_writecap(self.principal(), &mut std::iter::empty(), expires)?;
         self.writecap = Some(writecap);
         Ok(())
     }
@@ -1748,7 +1748,7 @@ mod test {
             .gen_root_creds(&"TranslationInvariant")
             .expect("failed to gen root creds");
         let writecap = root_creds.writecap().expect("no root writecap was present");
-        let path = crate::BlockPath::new(root_creds.principal(), Vec::new());
+        let path = crate::BlockPath::from_components(root_creds.principal(), std::iter::empty());
         writecap
             .assert_valid_for(&path)
             .expect("failed to verify root writecap");
@@ -1760,15 +1760,15 @@ mod test {
         let root_creds = store
             .gen_root_creds(&"TranslationInvariant")
             .expect("failed to gen root creds");
-        let path = crate::BlockPath::new(
+        let path = crate::BlockPath::from_components(
             root_creds.principal(),
-            vec!["apps".to_string(), "comms".to_string()],
+            ["apps", "comms"].into_iter(),
         );
         let node_creds = store.node_creds().expect("failed to gen node creds");
         let writecap = root_creds
             .issue_writecap(
                 node_creds.principal(),
-                path.components().map(|e| e.to_string()).collect(),
+                &mut path.components(),
                 Epoch::now() + Duration::from_secs(3600),
             )
             .expect("failed to issue writecap");
@@ -1995,7 +1995,7 @@ mod test {
             let mut node_creds = store.node_creds().unwrap();
             let expires = Epoch::now() + Duration::from_secs(3600);
             let expected = root_creds
-                .issue_writecap(node_creds.principal(), vec![], expires)
+                .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
                 .unwrap();
             store
                 .assign_node_writecap(&mut node_creds, expected.clone())
@@ -2023,7 +2023,7 @@ mod test {
             let mut root_creds = store.gen_root_creds(PASSWORD).unwrap();
             let expires = Epoch::now() + Duration::from_secs(3600);
             let expected = root_creds
-                .issue_writecap(root_creds.principal(), vec![], expires)
+                .issue_writecap(root_creds.principal(), &mut std::iter::empty(), expires)
                 .unwrap();
             store
                 .assign_root_writecap(&mut root_creds, expected.clone())
@@ -2051,7 +2051,7 @@ mod test {
             let mut node_creds = store.node_creds().unwrap();
             let expires = Epoch::now() + Duration::from_secs(3600);
             let writecap = root_creds
-                .issue_writecap(node_creds.principal(), vec![], expires)
+                .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
                 .unwrap();
             store
                 .assign_node_writecap(&mut node_creds, writecap.clone())

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

@@ -466,7 +466,8 @@ mod private {
                     // principal's signing key.
                     let mut vec = Vec::with_capacity(2);
                     let root_principal = self.body.signing_key.principal();
-                    let path = BlockPath::new(root_principal.clone(), vec![]);
+                    let path =
+                        BlockPath::from_components(root_principal.clone(), std::iter::empty());
                     let writecap = Writecap {
                         body: WritecapBody {
                             issued_to: root_principal,
@@ -629,7 +630,7 @@ mod tests {
         let writecap = node_creds
             .issue_writecap(
                 process_creds.principal(),
-                vec!["console".to_string()],
+                &mut ["console"].into_iter(),
                 Epoch::now() + Duration::from_secs(3600),
             )
             .unwrap();

+ 15 - 11
crates/btlib/src/lib.rs

@@ -50,7 +50,7 @@ use std::{
 use strum_macros::{Display, EnumDiscriminants, FromRepr};
 
 use accessor::Accessor;
-pub use block_path::{BlockPath, BlockPathError, RelBlockPath};
+pub use block_path::{BlockPath, BlockPathError, BlockPathGen, BlockPathRef, RelBlockPath};
 use crypto::{
     AsymKeyPub, Ciphertext, ConcretePub, Creds, CredsPub, Decrypter, DecrypterExt, EncrypterExt,
     HashKind, MerkleStream, SecretStream, Sign, Signature, Signer, SignerExt, SymKey, SymKeyKind,
@@ -1175,7 +1175,7 @@ impl Writecap {
 
     /// 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![])
+        BlockPath::from_components(self.root_principal(), std::iter::empty())
     }
 
     /// Returns a reference to the path contained in this [Writecap].
@@ -1186,7 +1186,7 @@ impl Writecap {
     /// Returns the path that the [Principal] this [Writecap] was issued to is allowed to bind.
     pub fn bind_path(&self) -> BlockPath {
         let mut path = self.body.path.clone();
-        path.push_component(self.body.issued_to.to_string());
+        path.push_component(&self.body.issued_to);
         path
     }
 
@@ -1572,20 +1572,21 @@ mod tests {
 
     impl InMemTestCase {
         fn new() -> InMemTestCase {
-            let components = vec!["nodes".to_string(), "phone".to_string()];
+            let components = ["nodes", "phone"];
             let node_creds = {
                 let mut node_creds = node_creds().clone();
                 let writecap = root_creds()
                     .issue_writecap(
                         node_creds.principal(),
-                        components.clone(),
+                        &mut components.into_iter(),
                         Epoch::now() + Duration::from_secs(3600),
                     )
                     .expect("failed to issue writecap");
                 node_creds.set_writecap(writecap).unwrap();
                 node_creds
             };
-            let block_path = BlockPath::new(root_creds().principal(), components);
+            let block_path =
+                BlockPath::from_components(root_creds().principal(), components.into_iter());
             let block_id = BlockId::default();
             Self {
                 node_creds,
@@ -1673,19 +1674,22 @@ mod tests {
             let temp_dir = TempDir::new("block_test").expect("failed to create temp dir");
             let root_creds = test_helpers::ROOT_CREDS.clone();
             let mut node_creds = test_helpers::NODE_CREDS.clone();
-            let components = vec!["nodes".to_string(), "phone".to_string()];
+            let components = ["nodes", "phone"];
             let writecap = root_creds
                 .issue_writecap(
                     node_creds.principal(),
-                    components.clone(),
+                    &mut components.into_iter(),
                     Epoch::now() + Duration::from_secs(3600),
                 )
                 .expect("failed to issue writecap");
             node_creds.set_writecap(writecap).unwrap();
             let case = BlockTestCase {
                 temp_dir,
-                node_path: BlockPath::new(root_creds.principal(), components),
-                root_path: BlockPath::new(root_creds.principal(), vec![]),
+                node_path: BlockPath::from_components(
+                    root_creds.principal(),
+                    components.into_iter(),
+                ),
+                root_path: BlockPath::from_components(root_creds.principal(), std::iter::empty()),
                 node_creds,
                 root_creds,
             };
@@ -1812,7 +1816,7 @@ mod tests {
                 .root_creds
                 .issue_writecap(
                     app_creds.principal(),
-                    path.components().map(|e| e.to_string()).collect(),
+                    &mut path.components(),
                     Epoch::now() + Duration::from_secs(60),
                 )
                 .expect("failed to issue writecap");

+ 19 - 15
crates/btlib/src/test_helpers.rs

@@ -98,7 +98,7 @@ lazy_static! {
         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)
+            .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
             .expect("failed to issue writecap to test_helpers::NODE_CREDS");
         node_creds.set_writecap(writecap).unwrap();
         node_creds
@@ -122,19 +122,20 @@ pub fn make_principal() -> Principal {
     Principal(VarHash::Sha2_256(PRINCIPAL.into()))
 }
 
-pub fn make_path_with_root(root: Principal, rel_components: Vec<&str>) -> BlockPath {
-    let mut components = Vec::with_capacity(rel_components.len());
-    for component in rel_components {
-        components.push(component.to_string());
-    }
-    BlockPath::new(root, components)
+pub fn make_path_with_root<'a, I: Iterator<Item = &'a str>>(
+    root: Principal,
+    rel_components: I,
+) -> BlockPath {
+    BlockPath::from_components(root, rel_components)
 }
 
-pub fn make_path(rel_components: Vec<&str>) -> BlockPath {
+pub fn make_path<'a, I: Iterator<Item = &'a str>>(rel_components: I) -> BlockPath {
     make_path_with_root(make_principal(), rel_components)
 }
 
-pub fn make_writecap_and_creds(rel_components: Vec<&str>) -> (Writecap, impl Creds) {
+pub fn make_writecap_and_creds<'a, I: Iterator<Item = &'a str>>(
+    rel_components: I,
+) -> (Writecap, impl Creds) {
     let (root_writecap, root_key) = make_self_signed_writecap();
     let issued_to = Principal(VarHash::Sha2_256(PRINCIPAL.into()));
     (
@@ -143,16 +144,16 @@ pub fn make_writecap_and_creds(rel_components: Vec<&str>) -> (Writecap, impl Cre
     )
 }
 
-pub fn make_writecap(rel_components: Vec<&str>) -> Writecap {
+pub fn make_writecap<'a, I: Iterator<Item = &'a str>>(rel_components: I) -> Writecap {
     let (writecap, ..) = make_writecap_and_creds(rel_components);
     writecap
 }
 
-pub fn make_writecap_trusted_by<C: Creds>(
+pub fn make_writecap_trusted_by<'a, C: Creds, I: Iterator<Item = &'a str>>(
     next: Writecap,
     trusting_creds: &C,
     issued_to: Principal,
-    path_components: Vec<&str>,
+    path_components: I,
 ) -> Writecap {
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
@@ -187,7 +188,7 @@ pub fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Writecap {
     let mut writecap = Writecap {
         body: WritecapBody {
             issued_to: root_principal.clone(),
-            path: make_path_with_root(root_principal, vec![]),
+            path: make_path_with_root(root_principal, std::iter::empty()),
             expires: hour_hence,
             signing_key: key.public_sign().clone(),
         },
@@ -204,9 +205,12 @@ pub fn make_block_with() -> SectoredBuf<SecretStream<PioCursor<impl Block>>> {
     let block_key = SymKey::generate(SymKeyKind::default()).unwrap();
     // Notice that the writecap path contains the block path. If this were not the case, the block
     // would be invalid.
-    let (writecap, creds) = make_writecap_and_creds(vec!["apps"]);
+    let (writecap, creds) = make_writecap_and_creds(["apps"].into_iter());
     let root_writecap = writecap.next.as_ref().unwrap();
-    let path = make_path_with_root(root_writecap.body.issued_to.clone(), vec!["apps", "verse"]);
+    let path = make_path_with_root(
+        root_writecap.body.issued_to.clone(),
+        ["apps", "verse"].into_iter(),
+    );
     let header = BlockMetaBody::new(&creds).unwrap();
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
     let mut stream = BlockStream::new(

+ 6 - 8
crates/btmsg/src/lib.rs

@@ -90,21 +90,19 @@ pub trait SendMsg<'de>: CallMsg<'de> {}
 /// used to get a socket address for the block this address refers to.
 #[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize)]
 pub struct BlockAddr {
-    ipaddr: IpAddr,
+    #[serde(rename = "ipaddr")]
+    ip_addr: IpAddr,
     #[serde(with = "smart_ptr")]
     path: Arc<BlockPath>,
 }
 
 impl BlockAddr {
     pub fn new(ip_addr: IpAddr, path: Arc<BlockPath>) -> Self {
-        Self {
-            ipaddr: ip_addr,
-            path,
-        }
+        Self { ip_addr, path }
     }
 
     pub fn ip_addr(&self) -> IpAddr {
-        self.ipaddr
+        self.ip_addr
     }
 
     pub fn path(&self) -> &BlockPath {
@@ -117,13 +115,13 @@ impl BlockAddr {
 
     /// Returns the socket address of the block this instance refers to.
     pub fn socket_addr(&self) -> Result<SocketAddr> {
-        Ok(SocketAddr::new(self.ipaddr, self.port()?))
+        Ok(SocketAddr::new(self.ip_addr, self.port()?))
     }
 }
 
 impl Display for BlockAddr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}@{}", self.path, self.ipaddr)
+        write!(f, "{}@{}", self.path, self.ip_addr)
     }
 }
 

+ 2 - 2
crates/btmsg/tests/tests.rs

@@ -48,7 +48,7 @@ lazy_static! {
         let writecap = root_creds
             .issue_writecap(
                 creds.principal(),
-                vec![],
+                &mut std::iter::empty(),
                 Epoch::now() + Duration::from_secs(3600),
             )
             .unwrap();
@@ -131,7 +131,7 @@ fn proc_creds() -> impl Creds {
     let writecap = NODE_CREDS
         .issue_writecap(
             creds.principal(),
-            vec![],
+            &mut std::iter::empty(),
             Epoch::now() + Duration::from_secs(3600),
         )
         .unwrap();

+ 4 - 1
crates/btprovision/Cargo.toml

@@ -13,4 +13,7 @@ serde = { version = "^1.0.136", features = ["derive"] }
 anyhow = { version = "1.0.66", features = ["std", "backtrace"] }
 termion = "2.0.1"
 tempdir = { version = "0.3.7" }
-figment = "0.10.8"
+figment = "0.10.8"
+
+[dev-dependencies]
+figment = { version = "0.10.8", features = ["test"] }

+ 133 - 67
crates/btprovision/src/main.rs

@@ -4,7 +4,7 @@ use btconfig::{
 use btlib::{
     bterr,
     crypto::{CredStore, CredStoreMut, Creds},
-    Decompose, Epoch, Principal, Principaled, RelBlockPath, Result, Writecap,
+    BlockPath, Epoch, Principal, Principaled, Result, Writecap,
 };
 use btserde::{read_from, write_to};
 use figment::{providers::Serialized, Figment};
@@ -19,39 +19,44 @@ use tempdir::TempDir;
 use termion::input::TermRead;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
-struct AppConfig {
+pub struct BtprovisionConfig {
     /// The configuration for the [CredStore] to use.
-    credstore: CredStoreConfig,
+    #[serde(rename = "credstore")]
+    pub cred_store: CredStoreConfig,
     /// The root password.
-    password: Option<String>,
+    pub password: Option<String>,
     /// The number of seconds after the UNIX epoch when the credentials expire.
-    writecapexpires: Option<u64>,
-    writecappath: Option<String>,
-    writecapissuee: Option<String>,
-    writecapsavepath: Option<PathBuf>,
+    #[serde(rename = "writecapexpires")]
+    pub writecap_expires: Option<u64>,
+    #[serde(rename = "writecappath")]
+    pub writecap_path: Option<String>,
+    #[serde(rename = "writecapissuee")]
+    pub writecap_issuee: Option<String>,
+    #[serde(rename = "writecapsavepath")]
+    pub writecap_save_path: Option<PathBuf>,
 }
 
-impl AppConfig {
-    fn new() -> Result<Self> {
+impl BtprovisionConfig {
+    pub fn new() -> Result<Self> {
         Figment::new()
-            .merge(Serialized::defaults(AppConfig::default()))
+            .merge(Serialized::defaults(BtprovisionConfig::default()))
             .btconfig()?
             .extract()
             .map_err(|err| err.into())
     }
 }
 
-impl Default for AppConfig {
+impl Default for BtprovisionConfig {
     fn default() -> Self {
         const DEFAULT_VALID_FOR: u64 = 60 * 60 * 24 * 365;
         let expires = Epoch::now() + Duration::from_secs(DEFAULT_VALID_FOR);
         Self {
-            credstore: CredStoreConfig::default(),
+            cred_store: CredStoreConfig::default(),
             password: None,
-            writecapexpires: Some(expires.value()),
-            writecappath: None,
-            writecapissuee: None,
-            writecapsavepath: None,
+            writecap_expires: Some(expires.value()),
+            writecap_path: None,
+            writecap_issuee: None,
+            writecap_save_path: None,
         }
     }
 }
@@ -85,7 +90,7 @@ impl<'a> CredStoreMutConsumer for RootProvisionConsumer<'a> {
     }
 }
 
-fn gen_root_creds(config: AppConfig) -> Result<()> {
+fn gen_root_creds(config: BtprovisionConfig) -> Result<()> {
     let password = if let Some(password) = config.password {
         password
     } else {
@@ -96,15 +101,15 @@ fn gen_root_creds(config: AppConfig) -> Result<()> {
         }
         password
     };
-    let expires = get_setting!(config, writecapexpires);
-    config.credstore.consume_mut(RootProvisionConsumer {
+    let expires = get_setting!(config, writecap_expires);
+    config.cred_store.consume_mut(RootProvisionConsumer {
         password: &password,
         expires: Epoch::from_value(expires),
     })?
 }
 
-fn gen_node_creds(config: AppConfig) -> Result<()> {
-    let principal = config.credstore.consume(PrincipalConsumer)??;
+fn gen_node_creds(config: BtprovisionConfig) -> Result<()> {
+    let principal = config.cred_store.consume(PrincipalConsumer)??;
     eprint!("node principal: ");
     println!("{principal}");
     Ok(())
@@ -113,7 +118,7 @@ fn gen_node_creds(config: AppConfig) -> Result<()> {
 struct IssueWritecapConsumer<'a> {
     password: &'a str,
     issuee: Principal,
-    components: Vec<String>,
+    components: String,
     expires: Epoch,
 }
 
@@ -121,27 +126,32 @@ impl<'a> CredStoreConsumer for IssueWritecapConsumer<'a> {
     type Output = Result<Writecap>;
     fn consume<C: CredStore>(self, cred_store: C) -> Self::Output {
         let root_creds = cred_store.root_creds(self.password)?;
-        root_creds.issue_writecap(self.issuee, self.components, self.expires)
+        let mut components = self.components.split(BlockPath::SEP);
+        root_creds.issue_writecap(self.issuee, &mut components, self.expires)
     }
 }
 
-fn issue_node_writecap(config: AppConfig) -> Result<()> {
-    let (writecap_path, issuee, expires) =
-        get_settings!(config, writecapsavepath, writecapissuee, writecapexpires);
+fn issue_node_writecap(config: BtprovisionConfig) -> Result<()> {
+    let (writecap_path, issuee, expires) = get_settings!(
+        config,
+        writecap_save_path,
+        writecap_issuee,
+        writecap_expires
+    );
     let password = if let Some(password) = config.password {
         password
     } else {
         password_prompt("Please enter the root password: ")?
     };
-    let cred_components = if let Some(cred_path) = config.writecappath {
-        RelBlockPath::try_from(cred_path.as_str())?
+    let cred_components = if let Some(cred_path) = config.writecap_path {
+        cred_path
     } else {
-        RelBlockPath::empty()
+        String::new()
     };
     let issuee = Principal::try_from(issuee.as_str())?;
-    let writecap = config.credstore.consume(IssueWritecapConsumer {
+    let writecap = config.cred_store.consume(IssueWritecapConsumer {
         password: &password,
-        components: cred_components.into_inner(),
+        components: cred_components,
         expires: Epoch::from_value(expires),
         issuee,
     })??;
@@ -173,9 +183,9 @@ impl CredStoreMutConsumer for SaveNodeWritecapConsumer {
     }
 }
 
-fn save_node_writecap(config: AppConfig) -> Result<()> {
+fn save_node_writecap(config: BtprovisionConfig) -> Result<()> {
     let writecap = {
-        let writecap_path = get_setting!(config, writecapsavepath);
+        let writecap_path = get_setting!(config, writecap_save_path);
         let file = OpenOptions::new()
             .read(true)
             .write(false)
@@ -185,7 +195,7 @@ fn save_node_writecap(config: AppConfig) -> Result<()> {
         read_from::<Writecap, _>(&mut reader)?
     };
     config
-        .credstore
+        .cred_store
         .consume_mut(SaveNodeWritecapConsumer { writecap })?
 }
 
@@ -199,14 +209,14 @@ impl CredStoreConsumer for PrincipalConsumer {
 }
 
 /// Runs all of the provisioning steps on a single credential store.
-fn full(mut config: AppConfig) -> Result<()> {
+fn full(mut config: BtprovisionConfig) -> Result<()> {
     gen_root_creds(config.clone())?;
     gen_node_creds(config.clone())?;
-    let node_principal = config.credstore.clone().consume(PrincipalConsumer)??;
-    config.writecapissuee = Some(node_principal.to_string());
-    let _temp_dir = if config.writecapsavepath.is_none() {
+    let node_principal = config.cred_store.clone().consume(PrincipalConsumer)??;
+    config.writecap_issuee = Some(node_principal.to_string());
+    let _temp_dir = if config.writecap_save_path.is_none() {
         let temp_dir = TempDir::new("btprovision")?;
-        config.writecapsavepath = Some(temp_dir.path().join("writecap"));
+        config.writecap_save_path = Some(temp_dir.path().join("writecap"));
         Some(temp_dir)
     } else {
         None
@@ -215,7 +225,7 @@ fn full(mut config: AppConfig) -> Result<()> {
     save_node_writecap(config)
 }
 
-fn run(command: &str, config: AppConfig) -> Result<()> {
+fn run(command: &str, config: BtprovisionConfig) -> Result<()> {
     match command {
         "gen_root_creds" => gen_root_creds(config),
         "gen_node_creds" => gen_node_creds(config),
@@ -227,7 +237,7 @@ fn run(command: &str, config: AppConfig) -> Result<()> {
 }
 
 fn main() -> Result<()> {
-    let config = AppConfig::new()?;
+    let config = BtprovisionConfig::new()?;
     let mut args = std::env::args().skip(1);
     let command = args
         .next()
@@ -237,9 +247,12 @@ fn main() -> Result<()> {
 
 #[cfg(test)]
 mod tests {
+    use std::time::Duration;
+
     use super::*;
 
-    use btlib::{crypto::CredsPriv, BlockError};
+    use btconfig::CredStoreConfig;
+    use btlib::{crypto::CredsPriv, BlockError, RelBlockPath};
     use tempdir::TempDir;
 
     struct NodeWritecapConsumer;
@@ -289,13 +302,13 @@ mod tests {
 
         run(
             "gen_root_creds",
-            AppConfig {
-                credstore: root_store.clone(),
+            BtprovisionConfig {
+                cred_store: root_store.clone(),
                 password: Some(password.clone()),
-                writecapexpires: Some(expires_expected),
-                writecappath: None,
-                writecapissuee: None,
-                writecapsavepath: None,
+                writecap_expires: Some(expires_expected),
+                writecap_path: None,
+                writecap_issuee: None,
+                writecap_save_path: None,
             },
         )
         .unwrap();
@@ -313,13 +326,13 @@ mod tests {
         }
         run(
             "gen_node_creds",
-            AppConfig {
-                credstore: node_store.clone(),
+            BtprovisionConfig {
+                cred_store: node_store.clone(),
                 password: None,
-                writecapexpires: None,
-                writecappath: None,
-                writecapissuee: None,
-                writecapsavepath: None,
+                writecap_expires: None,
+                writecap_path: None,
+                writecap_issuee: None,
+                writecap_save_path: None,
             },
         )
         .unwrap();
@@ -331,25 +344,25 @@ mod tests {
             .to_string();
         run(
             "issue_node_writecap",
-            AppConfig {
-                credstore: root_store,
+            BtprovisionConfig {
+                cred_store: root_store,
                 password: Some(password),
-                writecapexpires: Some(expires_expected),
-                writecappath: Some(writecap_path.clone()),
-                writecapissuee: Some(issued_to_expected.clone()),
-                writecapsavepath: Some(writecap_save_path.clone()),
+                writecap_expires: Some(expires_expected),
+                writecap_path: Some(writecap_path.clone()),
+                writecap_issuee: Some(issued_to_expected.clone()),
+                writecap_save_path: Some(writecap_save_path.clone()),
             },
         )
         .unwrap();
         run(
             "save_node_writecap",
-            AppConfig {
-                credstore: node_store.clone(),
+            BtprovisionConfig {
+                cred_store: node_store.clone(),
                 password: None,
-                writecapexpires: None,
-                writecappath: None,
-                writecapissuee: None,
-                writecapsavepath: Some(writecap_save_path),
+                writecap_expires: None,
+                writecap_path: None,
+                writecap_issuee: None,
+                writecap_save_path: Some(writecap_save_path),
             },
         )
         .unwrap();
@@ -369,3 +382,56 @@ mod tests {
         }
     }
 }
+
+#[cfg(test)]
+mod config_tests {
+    use super::BtprovisionConfig;
+
+    use std::path::PathBuf;
+
+    use figment::Jail;
+
+    macro_rules! test_field {
+        ($field:ident, $expected:expr, $env_var:literal) => {
+            #[test]
+            fn $field() {
+                Jail::expect_with(|jail| {
+                    jail.set_env($env_var, $expected);
+
+                    let config = BtprovisionConfig::new().unwrap();
+
+                    assert_eq!(Some($expected), config.$field);
+
+                    Ok(())
+                })
+            }
+        };
+    }
+
+    test_field!(password, String::from("eldritch"), "BT_PASSWORD");
+    test_field!(writecap_expires, 1729, "BT_WRITECAPEXPIRES");
+    test_field!(
+        writecap_path,
+        String::from("foofercoorg"),
+        "BT_WRITECAPPATH"
+    );
+    test_field!(
+        writecap_issuee,
+        String::from("slammin"),
+        "BT_WRITECAPISSUEE"
+    );
+
+    #[test]
+    fn writecap_save_path() {
+        Jail::expect_with(|jail| {
+            let expected = PathBuf::from("./writecap");
+            jail.set_env("BT_WRITECAPSAVEPATH", expected.display());
+
+            let config = BtprovisionConfig::new().unwrap();
+
+            assert_eq!(Some(expected), config.writecap_save_path);
+
+            Ok(())
+        })
+    }
+}

+ 12 - 4
crates/btserde/src/de.rs

@@ -273,8 +273,12 @@ impl<'de, 'a, T: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer<T>
     }
 
     fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
-        let value = self.read_str()?;
-        visitor.visit_borrowed_str(value)
+        if T::can_borrow() {
+            let value = self.read_str()?;
+            visitor.visit_borrowed_str(value)
+        } else {
+            self.deserialize_str(visitor)
+        }
     }
 
     fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
@@ -283,8 +287,12 @@ impl<'de, 'a, T: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer<T>
     }
 
     fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
-        let value = self.read_bytes()?;
-        visitor.visit_borrowed_bytes(value)
+        if T::can_borrow() {
+            let value = self.read_bytes()?;
+            visitor.visit_borrowed_bytes(value)
+        } else {
+            self.deserialize_byte_buf(visitor)
+        }
     }
 
     fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {

+ 5 - 7
crates/btserde/src/reader.rs

@@ -3,7 +3,7 @@
 //! different input types.
 use crate::{error::MapError, Error, Result};
 
-use std::io::Read;
+use std::{io::Read, ops::DerefMut};
 
 pub trait Reader<'de> {
     /// Reads exactly enough bytes to fill the given buffer or returns an error.
@@ -14,9 +14,9 @@ pub trait Reader<'de> {
     fn borrow_bytes(&mut self, len: usize) -> Result<&'de [u8]>;
 }
 
-impl<'de, R: Reader<'de>> Reader<'de> for &mut R {
+impl<'de, R: ?Sized + Reader<'de>, P: DerefMut<Target = R>> Reader<'de> for P {
     fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
-        (*self).read_exact(buf)
+        self.deref_mut().read_exact(buf)
     }
 
     fn can_borrow() -> bool {
@@ -24,7 +24,7 @@ impl<'de, R: Reader<'de>> Reader<'de> for &mut R {
     }
 
     fn borrow_bytes(&mut self, len: usize) -> Result<&'de [u8]> {
-        (*self).borrow_bytes(len)
+        self.deref_mut().borrow_bytes(len)
     }
 }
 
@@ -47,9 +47,7 @@ impl<'de, R: Read> Reader<'de> for ReadAdapter<R> {
     }
 
     fn borrow_bytes(&mut self, _len: usize) -> Result<&'de [u8]> {
-        Err(Error::NotSupported(
-            "borrowing from a ReadAdapter is not supported",
-        ))
+        Err(Error::NotSupported("borrowing from a ReadAdapter"))
     }
 }
 

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott