@@ -413,6 +413,178 @@ impl FileSystem for PseudoFs {
413
413
}
414
414
}
415
415
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
+
416
588
#[ cfg( test) ]
417
589
mod tests {
418
590
use super :: * ;
0 commit comments