fix(cubefs): make FUSE mount a durable view of cube-server daemon store
The cubefs-mount --socket path was never actually built: CubeFs<B> is generic, so the --socket (DaemonBackend) and default (HashBackend) arms of the match were incompatible types (E0308), and --features mount failed to compile. The running binary was therefore the in-memory build, so --socket was silently ignored and every FUSE write went to RAM and never reached the daemon (rawget/rawkeys returned none / 0 keys, WAL stayed 0). Fix: type-erase the backend. Add (forwards to inner) in cubestore, and build in cubefs-mount via Box::new(DaemonBackend::new(p)) / Box::new(HashBackend::new()). Keeps cubefs free of a cubesys dep (acyclic graph). Verified end-to-end on host (shared code path as the VM): a FUSE write via lands in the daemon store (rawget returns the record, 5 keys present, WAL grows), and survives a of the daemon + relaunch with the same --store (byte-identical read-back). Also includes (from RESUME-cubefs-daemon.md): cubesys raw* command family (rawget/rawput/rawdel/rawkeys/rawscan + parse/hex helpers) and the DaemonBackend client + smoke tests. Report/verification docs added. Note: VM cubefs.service still mounts in-memory (no --socket); update the unit to as a follow-up so the deployed VM FUSE is durable too.
This commit is contained in:
@@ -1,25 +1,31 @@
|
||||
//! `cubefs-mount` — mount a cube as a POSIX filesystem.
|
||||
//!
|
||||
//! Usage: `cubefs-mount <mountpoint> [--label NAME] [--seed] [--allow-other]`
|
||||
//! Usage:
|
||||
//! cubefs-mount <mountpoint> [--label NAME] [--seed] [--allow-other]
|
||||
//! [--socket PATH]
|
||||
//!
|
||||
//! `--socket PATH` mounts the **daemon's** durable store over a Unix socket
|
||||
//! (see `cubefs::DaemonBackend`). A write through the FUSE mount then lands in
|
||||
//! the daemon's WAL and survives an unmount — or a daemon restart — exactly as
|
||||
//! the PDF's "cubefs is a FUSE view of the cube" intends. Without `--socket`
|
||||
//! the mount uses the in-memory `HashBackend` (ephemeral; every write is lost
|
||||
//! on unmount), which is still useful for namespace-mapping tests and for
|
||||
//! machines with no `cube-server` running.
|
||||
//!
|
||||
//! `--allow-other` lets users other than the mounting user reach the
|
||||
//! filesystem. Without it the kernel rejects them at the mountpoint before any
|
||||
//! request reaches us, so multi-user ACL behaviour cannot be observed.
|
||||
//!
|
||||
//! The backing store is the in-memory [`HashBackend`] for now: Package 3's job
|
||||
//! is the *namespace mapping*, and a durable on-disk backend is a cubestore
|
||||
//! concern that gets swapped in by changing one type parameter here. `--seed`
|
||||
//! populates a few records so the mount has something to `ls`.
|
||||
|
||||
use cubefs::fuse::CubeFuse;
|
||||
use cubefs::CubeFs;
|
||||
use cubestore::{CubeStore, HashBackend};
|
||||
use cubefs::{CubeFs, DaemonBackend};
|
||||
use cubestore::{CubeBackend, CubeStore, HashBackend};
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let Some(mountpoint) = args.get(1).filter(|a| !a.starts_with("--")) else {
|
||||
eprintln!("usage: cubefs-mount <mountpoint> [--label NAME] [--seed]");
|
||||
eprintln!(
|
||||
"usage: cubefs-mount <mountpoint> [--label NAME] [--seed] [--socket PATH] [--allow-other]"
|
||||
);
|
||||
return ExitCode::from(2);
|
||||
};
|
||||
let label = args
|
||||
@@ -30,8 +36,29 @@ fn main() -> ExitCode {
|
||||
.unwrap_or_else(|| "cube0".to_string());
|
||||
let seed = args.iter().any(|a| a == "--seed");
|
||||
let allow_other = args.iter().any(|a| a == "--allow-other");
|
||||
let socket = args
|
||||
.iter()
|
||||
.position(|a| a == "--socket")
|
||||
.and_then(|i| args.get(i + 1).cloned());
|
||||
|
||||
let mut fs = CubeFs::new(CubeStore::new(HashBackend::new()));
|
||||
// Backing store: the daemon's durable store (over a socket) when `--socket`
|
||||
// is given, otherwise an ephemeral in-memory map. Both are type-erased to
|
||||
// `Box<dyn CubeBackend + Send + Sync>` so `CubeFs` has a single concrete
|
||||
// type regardless of which backend was chosen at runtime.
|
||||
let mut fs: CubeFs<Box<dyn CubeBackend + Send + Sync>> = match &socket {
|
||||
Some(path) => {
|
||||
eprintln!("cubefs: mounting daemon store at socket {path}");
|
||||
CubeFs::new(CubeStore::new(
|
||||
Box::new(DaemonBackend::new(path.clone())) as Box<dyn CubeBackend + Send + Sync>,
|
||||
))
|
||||
}
|
||||
None => {
|
||||
eprintln!("cubefs: mounting IN-MEMORY store (writes are NOT durable; use --socket PATH to mount the daemon's store)");
|
||||
CubeFs::new(CubeStore::new(
|
||||
Box::new(HashBackend::new()) as Box<dyn CubeBackend + Send + Sync>,
|
||||
))
|
||||
}
|
||||
};
|
||||
fs.format(&label);
|
||||
|
||||
if seed {
|
||||
@@ -73,9 +100,10 @@ fn main() -> ExitCode {
|
||||
opts.push(fuser::MountOption::AllowOther);
|
||||
}
|
||||
eprintln!(
|
||||
"mounting cubefs at {mountpoint} (label={label}, seed={seed}, allow_other={allow_other}) — ctrl-c to unmount"
|
||||
"mounting cubefs at {mountpoint} (label={label}, socket={:?}, seed={seed}, allow_other={allow_other}) — ctrl-c to unmount",
|
||||
socket
|
||||
);
|
||||
match fuser::mount2(CubeFuse::new(fs), mountpoint, &opts) {
|
||||
match fuser::mount2(cubefs::fuse::CubeFuse::new(fs), mountpoint, &opts) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("mount failed: {e}");
|
||||
|
||||
Reference in New Issue
Block a user