Skip to content

Commit 6708951

Browse files
loheagnjiangliu
authored andcommitted
feat: add "persist" feature
This patch add a new sub module `persist` for the `fuse_backend_rs::api::vfs` module. The `persist` module export two functions to help to save and restore the `Vfs` state: - `save_to_bytes`, which save the mete data of the `Vfs` struct into a byte array; - `restore_from_bytes`, which restore the meta data of the `Vfs` struct from a byte array. As the two above functions only save and restore the vfs meta data, but don't save and restore the backend file systems, this patch also provides a method named `restore_mount` to re-mount the backend file system into the `Vfs` using the same index as before. For more information and usage, please refer to the doc of the `save_to_bytes` and `restore_from_bytes` functions. Signed-off-by: Nan Li <[email protected]>
1 parent a8d8cd9 commit 6708951

File tree

3 files changed

+545
-4
lines changed

3 files changed

+545
-4
lines changed

Cargo.toml

+21-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ build = "build.rs"
2020
arc-swap = "1.5"
2121
async-trait = { version = "0.1.42", optional = true }
2222
bitflags = "1.1"
23+
dbs-snapshot = { version = "1.5.0", optional = true }
2324
io-uring = { version = "0.5.8", optional = true }
2425
libc = "0.2.68"
2526
log = "0.4.6"
26-
mio = { version = "0.8", features = ["os-poll", "os-ext"]}
27+
mio = { version = "0.8", features = ["os-poll", "os-ext"] }
2728
nix = "0.24"
2829
lazy_static = "1.4"
2930
tokio = { version = "1", optional = true }
@@ -32,6 +33,8 @@ vmm-sys-util = { version = "0.11", optional = true }
3233
vm-memory = { version = "0.10", features = ["backend-mmap"] }
3334
virtio-queue = { version = "0.7", optional = true }
3435
vhost = { version = "0.6", features = ["vhost-user-slave"], optional = true }
36+
versionize_derive = { version = "0.1.6", optional = true }
37+
versionize = { version = "0.1.10", optional = true }
3538

3639
[target.'cfg(target_os = "macos")'.dependencies]
3740
core-foundation-sys = { version = ">=0.8", optional = true }
@@ -47,12 +50,26 @@ vm-memory = { version = "0.10", features = ["backend-mmap", "backend-bitmap"] }
4750

4851
[features]
4952
default = ["fusedev"]
50-
async-io = ["async-trait", "tokio-uring", "tokio/fs", "tokio/net", "tokio/sync", "tokio/rt", "tokio/macros", "io-uring"]
53+
async-io = [
54+
"async-trait",
55+
"tokio-uring",
56+
"tokio/fs",
57+
"tokio/net",
58+
"tokio/sync",
59+
"tokio/rt",
60+
"tokio/macros",
61+
"io-uring",
62+
]
5163
fusedev = ["vmm-sys-util", "caps", "core-foundation-sys"]
5264
virtiofs = ["virtio-queue", "caps", "vmm-sys-util"]
5365
vhost-user-fs = ["virtiofs", "vhost", "caps"]
54-
fuse-t=[]
66+
persist = ["dbs-snapshot", "versionize", "versionize_derive"]
67+
fuse-t = []
5568

5669
[package.metadata.docs.rs]
5770
all-features = true
58-
targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "aarch64-apple-darwin"]
71+
targets = [
72+
"x86_64-unknown-linux-gnu",
73+
"aarch64-unknown-linux-gnu",
74+
"aarch64-apple-darwin",
75+
]

src/api/pseudo_fs.rs

+172
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,178 @@ impl FileSystem for PseudoFs {
413413
}
414414
}
415415

416+
/// Save and restore PseudoFs state.
417+
#[cfg(feature = "persist")]
418+
pub mod persist {
419+
use std::collections::HashMap;
420+
use std::io::{Error as IoError, ErrorKind, Result};
421+
use std::sync::atomic::Ordering;
422+
use std::sync::Arc;
423+
424+
use dbs_snapshot::Snapshot;
425+
use versionize::{VersionMap, Versionize, VersionizeResult};
426+
use versionize_derive::Versionize;
427+
428+
use super::{PseudoFs, PseudoInode};
429+
use crate::api::filesystem::ROOT_ID;
430+
431+
#[derive(Versionize, PartialEq, Debug, Default, Clone)]
432+
struct PseudoInodeState {
433+
ino: u64,
434+
parent: u64,
435+
name: String,
436+
}
437+
438+
#[derive(Versionize, PartialEq, Debug, Default)]
439+
pub struct PseudoFsState {
440+
next_inode: u64,
441+
inodes: Vec<PseudoInodeState>,
442+
}
443+
444+
impl PseudoFs {
445+
fn get_version_map() -> VersionMap {
446+
let mut vm = VersionMap::new();
447+
vm.set_type_version(PseudoFsState::type_id(), 1);
448+
449+
// more versions for the future
450+
451+
vm
452+
}
453+
454+
/// Saves part of the PseudoFs into a byte array.
455+
/// The upper layer caller can use this method to save
456+
/// and transfer metadata for the reloading in the future.
457+
pub fn save_to_bytes(&self) -> Result<Vec<u8>> {
458+
let mut inodes = Vec::new();
459+
let next_inode = self.next_inode.load(Ordering::Relaxed);
460+
461+
let _guard = self.lock.lock().unwrap();
462+
for inode in self.inodes.load().values() {
463+
if inode.ino == ROOT_ID {
464+
// no need to save the root inode
465+
continue;
466+
}
467+
468+
inodes.push(PseudoInodeState {
469+
ino: inode.ino,
470+
parent: inode.parent,
471+
name: inode.name.clone(),
472+
});
473+
}
474+
let state = PseudoFsState { next_inode, inodes };
475+
476+
let vm = PseudoFs::get_version_map();
477+
let target_version = vm.latest_version();
478+
let mut s = Snapshot::new(vm, target_version);
479+
let mut buf = Vec::new();
480+
s.save(&mut buf, &state).map_err(|e| {
481+
IoError::new(
482+
ErrorKind::Other,
483+
format!("Failed to save PseudoFs to bytes: {:?}", e),
484+
)
485+
})?;
486+
487+
Ok(buf)
488+
}
489+
490+
/// Restores the PseudoFs from a byte array.
491+
pub fn restore_from_bytes(&self, buf: &mut Vec<u8>) -> Result<()> {
492+
let state: PseudoFsState =
493+
Snapshot::load(&mut buf.as_slice(), buf.len(), PseudoFs::get_version_map())
494+
.map_err(|e| {
495+
IoError::new(
496+
ErrorKind::Other,
497+
format!("Failed to load PseudoFs from bytes: {:?}", e),
498+
)
499+
})?
500+
.0;
501+
self.restore_from_state(&state)
502+
}
503+
504+
fn restore_from_state(&self, state: &PseudoFsState) -> Result<()> {
505+
// first, reconstruct all the inodes
506+
let mut inode_map = HashMap::new();
507+
let mut state_inodes = state.inodes.clone();
508+
for inode in state_inodes.iter() {
509+
let inode = Arc::new(PseudoInode::new(
510+
inode.ino,
511+
inode.parent,
512+
inode.name.clone(),
513+
));
514+
inode_map.insert(inode.ino, inode);
515+
}
516+
517+
// insert root inode to make sure the others inodes can find their parents
518+
inode_map.insert(self.root_inode.ino, self.root_inode.clone());
519+
520+
// then, connect the inodes
521+
state_inodes.sort_by(|a, b| a.ino.cmp(&b.ino));
522+
for inode in state_inodes.iter() {
523+
let inode = inode_map
524+
.get(&inode.ino)
525+
.ok_or_else(|| {
526+
IoError::new(
527+
ErrorKind::InvalidData,
528+
format!("invalid inode {}", inode.ino),
529+
)
530+
})?
531+
.clone();
532+
let parent = inode_map.get_mut(&inode.parent).ok_or_else(|| {
533+
IoError::new(
534+
ErrorKind::InvalidData,
535+
format!(
536+
"invalid parent inode {} for inode {}",
537+
inode.parent, inode.ino
538+
),
539+
)
540+
})?;
541+
parent.insert_child(inode);
542+
}
543+
self.inodes.store(Arc::new(inode_map));
544+
545+
// last, restore next_inode
546+
self.next_inode.store(state.next_inode, Ordering::Relaxed);
547+
548+
Ok(())
549+
}
550+
}
551+
552+
mod test {
553+
554+
#[test]
555+
fn save_restore_test() {
556+
use crate::api::pseudo_fs::PseudoFs;
557+
558+
let fs = &PseudoFs::new();
559+
let paths = vec!["/a", "/a/b", "/a/b/c", "/b", "/b/a/c", "/d"];
560+
561+
for path in paths.iter() {
562+
fs.mount(path).unwrap();
563+
}
564+
565+
// save fs
566+
let mut buf = fs.save_to_bytes().unwrap();
567+
568+
// restore fs
569+
let restored_fs = &PseudoFs::new();
570+
restored_fs.restore_from_bytes(&mut buf).unwrap();
571+
572+
// check fs and restored_fs
573+
let next_inode = fs.next_inode.load(std::sync::atomic::Ordering::Relaxed);
574+
let restored_next_inode = restored_fs
575+
.next_inode
576+
.load(std::sync::atomic::Ordering::Relaxed);
577+
assert_eq!(next_inode, restored_next_inode);
578+
579+
for path in paths.iter() {
580+
let inode = fs.path_walk(path).unwrap();
581+
let restored_inode = restored_fs.path_walk(path).unwrap();
582+
assert_eq!(inode, restored_inode);
583+
}
584+
}
585+
}
586+
}
587+
416588
#[cfg(test)]
417589
mod tests {
418590
use super::*;

0 commit comments

Comments
 (0)