Skip to main content

network_mount/
network_mount.rs

1//! Connects to a `doublecrypt-server` over mTLS, mounts a network-backed
2//! encrypted filesystem with a write-back cache, and performs basic operations.
3//!
4//! Run with:
5//!   cargo run --example network_mount -- \
6//!       --addr 127.0.0.1:9100 \
7//!       --server-name localhost \
8//!       --cert certs/client.pem \
9//!       --key certs/client-key.pem \
10//!       --ca certs/ca.pem \
11//!       --master-key 0000000000000000000000000000000000000000000000000000000000000000
12
13use std::path::Path;
14use std::sync::Arc;
15
16use doublecrypt_core::block_store::BlockStore;
17use doublecrypt_core::cached_store::CachedBlockStore;
18use doublecrypt_core::crypto::ChaChaEngine;
19use doublecrypt_core::fs::FilesystemCore;
20use doublecrypt_core::network_store::NetworkBlockStore;
21
22fn main() {
23    let args: Vec<String> = std::env::args().collect();
24
25    let mut addr = "127.0.0.1:9100";
26    let mut server_name = "localhost";
27    let mut cert = "certs/client.pem";
28    let mut key = "certs/client-key.pem";
29    let mut ca = "certs/ca.pem";
30    let mut master_key_hex = "";
31    let mut init = false;
32
33    let mut i = 1;
34    while i < args.len() {
35        match args[i].as_str() {
36            "--addr" => {
37                addr = &args[i + 1];
38                i += 2;
39            }
40            "--server-name" => {
41                server_name = &args[i + 1];
42                i += 2;
43            }
44            "--cert" => {
45                cert = &args[i + 1];
46                i += 2;
47            }
48            "--key" => {
49                key = &args[i + 1];
50                i += 2;
51            }
52            "--ca" => {
53                ca = &args[i + 1];
54                i += 2;
55            }
56            "--master-key" => {
57                master_key_hex = &args[i + 1];
58                i += 2;
59            }
60            "--init" => {
61                init = true;
62                i += 1;
63            }
64            other => {
65                eprintln!("unknown argument: {other}");
66                std::process::exit(1);
67            }
68        }
69    }
70
71    if master_key_hex.is_empty() || master_key_hex.len() != 64 {
72        eprintln!("--master-key must be a 64-character hex string (32 bytes)");
73        std::process::exit(1);
74    }
75
76    let master_key: Vec<u8> = (0..32)
77        .map(|i| u8::from_str_radix(&master_key_hex[i * 2..i * 2 + 2], 16).unwrap())
78        .collect();
79
80    // ── Connect ─────────────────────────────────────────────
81    println!("Connecting to {addr} (SNI: {server_name})...");
82    let net = NetworkBlockStore::connect(
83        addr,
84        server_name,
85        Path::new(cert),
86        Path::new(key),
87        Path::new(ca),
88    )
89    .expect("failed to connect to server");
90
91    println!(
92        "Connected: {} blocks × {} bytes ({} MiB)",
93        net.total_blocks(),
94        net.block_size(),
95        net.total_blocks() as usize * net.block_size() / (1024 * 1024)
96    );
97
98    // ── Wrap with cache ─────────────────────────────────────
99    let store = Arc::new(CachedBlockStore::new(net, 1024));
100    let crypto = Arc::new(ChaChaEngine::new(&master_key).expect("invalid master key"));
101    let mut fs = FilesystemCore::new(store.clone(), crypto);
102
103    // ── Mount or init ───────────────────────────────────────
104    if init {
105        println!("Initializing new filesystem...");
106        fs.init_filesystem().expect("init_filesystem failed");
107    } else {
108        println!("Mounting existing filesystem...");
109        fs.open().expect("open failed");
110    }
111
112    // ── Demo operations ─────────────────────────────────────
113    println!("\nCreating file 'hello.txt'...");
114    match fs.create_file("hello.txt") {
115        Ok(()) => {}
116        Err(e) => println!("  (skipped: {e})"),
117    }
118
119    fs.write_file("hello.txt", 0, b"Hello from the network!")
120        .expect("write failed");
121
122    let data = fs.read_file("hello.txt", 0, 4096).expect("read failed");
123    println!("Read back: {:?}", String::from_utf8_lossy(&data));
124
125    println!("\nListing root directory:");
126    for entry in fs.list_directory().expect("list failed") {
127        println!(
128            "  {:?}  {:>10} bytes  {}",
129            entry.kind, entry.size, entry.name
130        );
131    }
132
133    // ── Sync ────────────────────────────────────────────────
134    fs.sync().expect("sync failed");
135    println!("\nAll data synced to server.");
136}