@@ -3,7 +3,7 @@ use std::{
3
3
ffi:: { OsStr , OsString } ,
4
4
fmt,
5
5
path:: Path ,
6
- sync:: LazyLock ,
6
+ sync:: { atomic :: AtomicBool , Arc , LazyLock , Mutex } ,
7
7
time:: { Duration , UNIX_EPOCH } ,
8
8
u64,
9
9
} ;
@@ -306,24 +306,33 @@ pub struct Trick {
306
306
/// Note that inodes are 1-based and this vector is 0-based.
307
307
inodes : Vec < InodeData > ,
308
308
freelist : Vec < Inode > ,
309
+ trigger_enospc : Arc < AtomicBool > ,
309
310
}
310
311
311
312
impl Trick {
312
- pub fn new ( ) -> Self {
313
+ fn new ( ) -> ( Self , TrickHandle ) {
314
+ let trigger_enospc = Arc :: new ( AtomicBool :: new ( false ) ) ;
315
+
313
316
let tree = Tree :: new ( ) ;
314
317
let inodes = Vec :: new ( ) ;
315
318
let freelist = Vec :: new ( ) ;
316
319
let mut fs = Trick {
317
320
tree,
318
321
inodes,
319
322
freelist,
323
+ trigger_enospc : trigger_enospc. clone ( ) ,
320
324
} ;
321
325
// Initialize the root directory. Parent of the ROOT is ROOT.
322
326
fs. register_inode ( InodeData :: new_dir ( Inode :: ROOT ) ) ;
323
327
fs. tree
324
328
. ino_to_container
325
329
. insert ( Inode :: ROOT , Container :: new ( ) ) ;
326
- fs
330
+
331
+ let handle = TrickHandle {
332
+ bg_sess : None . into ( ) ,
333
+ trigger_enospc,
334
+ } ;
335
+ ( fs, handle)
327
336
}
328
337
329
338
fn lookup_inode ( & self , ino : Inode ) -> Option < & InodeData > {
@@ -478,6 +487,13 @@ impl fuser::Filesystem for Trick {
478
487
) {
479
488
// we don't really care about these parameters.
480
489
let _ = ( mode, umask, flags) ;
490
+ if self
491
+ . trigger_enospc
492
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
493
+ {
494
+ reply. error ( libc:: ENOSPC ) ;
495
+ return ;
496
+ }
481
497
let Some ( parent_container) = self . tree . ino_to_container . get ( & Inode ( parent) ) else {
482
498
log:: trace!( "parent inode doesn't exist" ) ;
483
499
reply. error ( libc:: ENOENT ) ;
@@ -568,6 +584,9 @@ impl fuser::Filesystem for Trick {
568
584
reply : fuser:: ReplyWrite ,
569
585
) {
570
586
let _ = ( fh, flags, write_flags, lock_owner) ;
587
+ let trigger_enospc = self
588
+ . trigger_enospc
589
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
571
590
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( ino) ) else {
572
591
reply. error ( libc:: ENOENT ) ;
573
592
return ;
@@ -580,7 +599,12 @@ impl fuser::Filesystem for Trick {
580
599
reply. error ( libc:: EFBIG ) ;
581
600
return ;
582
601
}
602
+ // Extension: if the file size is less than the new size, we should extend the file.
583
603
if inode_data. size ( ) < new_file_sz as u64 {
604
+ if trigger_enospc {
605
+ reply. error ( libc:: ENOSPC ) ;
606
+ return ;
607
+ }
584
608
inode_data. set_size ( new_file_sz as u64 ) ;
585
609
}
586
610
inode_data. content_mut ( ) [ offset..offset + len] . copy_from_slice ( data) ;
@@ -597,6 +621,13 @@ impl fuser::Filesystem for Trick {
597
621
reply : fuser:: ReplyEntry ,
598
622
) {
599
623
let _ = ( mode, umask) ;
624
+ if self
625
+ . trigger_enospc
626
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
627
+ {
628
+ reply. error ( libc:: ENOSPC ) ;
629
+ return ;
630
+ }
600
631
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( parent) ) else {
601
632
reply. error ( libc:: ENOENT ) ;
602
633
return ;
@@ -757,14 +788,21 @@ impl fuser::Filesystem for Trick {
757
788
reply : fuser:: ReplyEmpty ,
758
789
) {
759
790
let _ = ( fh, mode) ;
791
+ let trigger_enospc = self
792
+ . trigger_enospc
793
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
760
794
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( ino) ) else {
761
795
reply. error ( libc:: ENOENT ) ;
762
796
return ;
763
797
} ;
764
798
// fallocate should preallocate stuff. We here just gon pretend that we are preallocating.
765
- // we should set the length though .
799
+ // Here we should extend the file if the offset + length is greater than the current file .
766
800
let new_size = u64:: try_from ( offset) . unwrap ( ) + u64:: try_from ( length) . unwrap ( ) ;
767
801
if inode_data. size ( ) < new_size {
802
+ if trigger_enospc {
803
+ reply. error ( libc:: ENOSPC ) ;
804
+ return ;
805
+ }
768
806
inode_data. set_size ( new_size) ;
769
807
}
770
808
reply. ok ( ) ;
@@ -780,6 +818,13 @@ impl fuser::Filesystem for Trick {
780
818
) {
781
819
// fsync doesn't do anything since we are working in-memory, so just return OK.
782
820
let _ = ( ino, fh, datasync) ;
821
+ if self
822
+ . trigger_enospc
823
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
824
+ {
825
+ reply. error ( libc:: ENOSPC ) ;
826
+ return ;
827
+ }
783
828
reply. ok ( ) ;
784
829
}
785
830
@@ -793,6 +838,13 @@ impl fuser::Filesystem for Trick {
793
838
) {
794
839
// just like fsync, fsyncdir doesn't do anything.
795
840
let _ = ( req, ino, fh, datasync) ;
841
+ if self
842
+ . trigger_enospc
843
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
844
+ {
845
+ reply. error ( libc:: ENOSPC ) ;
846
+ return ;
847
+ }
796
848
reply. ok ( ) ;
797
849
}
798
850
}
@@ -802,13 +854,21 @@ fn has_odirect(flags: i32) -> bool {
802
854
}
803
855
804
856
pub struct TrickHandle {
805
- bg_sess : fuser:: BackgroundSession ,
857
+ bg_sess : Mutex < Option < fuser:: BackgroundSession > > ,
858
+ trigger_enospc : Arc < AtomicBool > ,
806
859
}
807
860
808
861
impl TrickHandle {
809
862
/// Sets whether the file system should return ENOSPC on the subsequent write operations.
863
+ pub fn set_trigger_enospc ( & self , on : bool ) {
864
+ self . trigger_enospc
865
+ . store ( on, std:: sync:: atomic:: Ordering :: Relaxed ) ;
866
+ }
867
+
810
868
pub fn unmount_and_join ( self ) {
811
- self . bg_sess . join ( ) ;
869
+ if let Some ( bg_sess) = self . bg_sess . lock ( ) . unwrap ( ) . take ( ) {
870
+ bg_sess. join ( ) ;
871
+ }
812
872
}
813
873
}
814
874
@@ -822,9 +882,9 @@ pub fn spawn_trick<P: AsRef<Path>>(mountpoint: P) -> std::io::Result<TrickHandle
822
882
MountOption :: AutoUnmount ,
823
883
MountOption :: FSName ( "trick" . to_string ( ) ) ,
824
884
] ;
825
- let fs = Trick :: new ( ) ;
826
- let bg_sess = fuser:: spawn_mount2 ( fs, & mountpoint, options) ?;
827
- Ok ( TrickHandle { bg_sess } )
885
+ let ( fs , mut handle ) = Trick :: new ( ) ;
886
+ handle . bg_sess = Some ( fuser:: spawn_mount2 ( fs, & mountpoint, options) ?) . into ( ) ;
887
+ Ok ( handle )
828
888
}
829
889
830
890
#[ cfg( test) ]
@@ -852,7 +912,7 @@ mod tests {
852
912
init_log ( ) ;
853
913
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
854
914
let options = & [ MountOption :: RO , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
855
- let fs = Trick :: new ( ) ;
915
+ let ( fs , _handle ) = Trick :: new ( ) ;
856
916
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
857
917
drop ( mount_handle) ;
858
918
}
@@ -863,7 +923,7 @@ mod tests {
863
923
init_log ( ) ;
864
924
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
865
925
let options = & [ MountOption :: RW , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
866
- let fs = Trick :: new ( ) ;
926
+ let ( fs , _handle ) = Trick :: new ( ) ;
867
927
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
868
928
let filename = mountpoint. path ( ) . join ( "file" ) ;
869
929
let file = fs:: File :: create ( & filename) . unwrap ( ) ;
@@ -876,7 +936,7 @@ mod tests {
876
936
init_log ( ) ;
877
937
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
878
938
let options = & [ MountOption :: RW , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
879
- let fs = Trick :: new ( ) ;
939
+ let ( fs , _handle ) = Trick :: new ( ) ;
880
940
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
881
941
let filename = mountpoint. path ( ) . join ( "file" ) ;
882
942
let file = fs:: File :: create ( & filename) . unwrap ( ) ;
@@ -895,7 +955,7 @@ mod tests {
895
955
MountOption :: AutoUnmount ,
896
956
MountOption :: FSName ( "trick" . to_string ( ) ) ,
897
957
] ;
898
- let fs = Trick :: new ( ) ;
958
+ let ( fs , _handle ) = Trick :: new ( ) ;
899
959
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
900
960
let filename = mountpoint. path ( ) . join ( "file" ) ;
901
961
let mut file = fs:: File :: options ( )
@@ -928,7 +988,7 @@ mod tests {
928
988
MountOption :: AutoUnmount ,
929
989
MountOption :: FSName ( "trick" . to_string ( ) ) ,
930
990
] ;
931
- let fs = Trick :: new ( ) ;
991
+ let ( fs , _handle ) = Trick :: new ( ) ;
932
992
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
933
993
let mut files = Vec :: new ( ) ;
934
994
for i in 0 ..100 {
@@ -959,7 +1019,7 @@ mod tests {
959
1019
MountOption :: AutoUnmount ,
960
1020
MountOption :: FSName ( "trick" . to_string ( ) ) ,
961
1021
] ;
962
- let fs = Trick :: new ( ) ;
1022
+ let ( fs , _handle ) = Trick :: new ( ) ;
963
1023
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
964
1024
965
1025
// Create a file
@@ -981,7 +1041,7 @@ mod tests {
981
1041
MountOption :: AutoUnmount ,
982
1042
MountOption :: FSName ( "trick" . to_string ( ) ) ,
983
1043
] ;
984
- let fs = Trick :: new ( ) ;
1044
+ let ( fs , _handle ) = Trick :: new ( ) ;
985
1045
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
986
1046
987
1047
let filename = mountpoint. path ( ) . join ( "file" ) ;
@@ -1013,4 +1073,33 @@ mod tests {
1013
1073
drop ( file) ;
1014
1074
drop ( mount_handle) ;
1015
1075
}
1076
+
1077
+ #[ test]
1078
+ fn out_of_space ( ) {
1079
+ init_log ( ) ;
1080
+ let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
1081
+ let options = & [
1082
+ MountOption :: RW ,
1083
+ MountOption :: AutoUnmount ,
1084
+ MountOption :: FSName ( "trick" . to_string ( ) ) ,
1085
+ ] ;
1086
+ let ( fs, handle) = Trick :: new ( ) ;
1087
+ let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
1088
+
1089
+ let filename = mountpoint. path ( ) . join ( "file" ) ;
1090
+ let mut file = fs:: File :: options ( )
1091
+ . create_new ( true )
1092
+ . write ( true )
1093
+ . open ( & filename)
1094
+ . unwrap ( ) ;
1095
+
1096
+ let test_data = b"hello world" ;
1097
+ handle. set_trigger_enospc ( true ) ;
1098
+ let _ = file. write_all ( test_data) . unwrap_err ( ) ;
1099
+ handle. set_trigger_enospc ( false ) ;
1100
+ let _ = file. write_all ( test_data) . unwrap ( ) ;
1101
+
1102
+ drop ( file) ;
1103
+ drop ( mount_handle) ;
1104
+ }
1016
1105
}
0 commit comments