@@ -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
} ;
@@ -302,24 +302,33 @@ pub struct Trick {
302
302
/// Note that inodes are 1-based and this vector is 0-based.
303
303
inodes : Vec < InodeData > ,
304
304
freelist : Vec < Inode > ,
305
+ trigger_enospc : Arc < AtomicBool > ,
305
306
}
306
307
307
308
impl Trick {
308
- pub fn new ( ) -> Self {
309
+ fn new ( ) -> ( Self , TrickHandle ) {
310
+ let trigger_enospc = Arc :: new ( AtomicBool :: new ( false ) ) ;
311
+
309
312
let tree = Tree :: new ( ) ;
310
313
let inodes = Vec :: new ( ) ;
311
314
let freelist = Vec :: new ( ) ;
312
315
let mut fs = Trick {
313
316
tree,
314
317
inodes,
315
318
freelist,
319
+ trigger_enospc : trigger_enospc. clone ( ) ,
316
320
} ;
317
321
// Initialize the root directory. Parent of the ROOT is ROOT.
318
322
fs. register_inode ( InodeData :: new_dir ( Inode :: ROOT ) ) ;
319
323
fs. tree
320
324
. ino_to_container
321
325
. insert ( Inode :: ROOT , Container :: new ( ) ) ;
322
- fs
326
+
327
+ let handle = TrickHandle {
328
+ bg_sess : None . into ( ) ,
329
+ trigger_enospc,
330
+ } ;
331
+ ( fs, handle)
323
332
}
324
333
325
334
fn lookup_inode ( & self , ino : Inode ) -> Option < & InodeData > {
@@ -474,6 +483,13 @@ impl fuser::Filesystem for Trick {
474
483
) {
475
484
// we don't really care about these parameters.
476
485
let _ = ( mode, umask, flags) ;
486
+ if self
487
+ . trigger_enospc
488
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
489
+ {
490
+ reply. error ( libc:: ENOSPC ) ;
491
+ return ;
492
+ }
477
493
let Some ( parent_container) = self . tree . ino_to_container . get ( & Inode ( parent) ) else {
478
494
log:: trace!( "parent inode doesn't exist" ) ;
479
495
reply. error ( libc:: ENOENT ) ;
@@ -564,6 +580,9 @@ impl fuser::Filesystem for Trick {
564
580
reply : fuser:: ReplyWrite ,
565
581
) {
566
582
let _ = ( fh, flags, write_flags, lock_owner) ;
583
+ let trigger_enospc = self
584
+ . trigger_enospc
585
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
567
586
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( ino) ) else {
568
587
reply. error ( libc:: ENOENT ) ;
569
588
return ;
@@ -576,7 +595,12 @@ impl fuser::Filesystem for Trick {
576
595
reply. error ( libc:: EFBIG ) ;
577
596
return ;
578
597
}
598
+ // Extension: if the file size is less than the new size, we should extend the file.
579
599
if inode_data. size ( ) < new_file_sz as u64 {
600
+ if trigger_enospc {
601
+ reply. error ( libc:: ENOSPC ) ;
602
+ return ;
603
+ }
580
604
inode_data. set_size ( new_file_sz as u64 ) ;
581
605
}
582
606
inode_data. content_mut ( ) [ offset..offset + len] . copy_from_slice ( data) ;
@@ -593,6 +617,13 @@ impl fuser::Filesystem for Trick {
593
617
reply : fuser:: ReplyEntry ,
594
618
) {
595
619
let _ = ( mode, umask) ;
620
+ if self
621
+ . trigger_enospc
622
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
623
+ {
624
+ reply. error ( libc:: ENOSPC ) ;
625
+ return ;
626
+ }
596
627
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( parent) ) else {
597
628
reply. error ( libc:: ENOENT ) ;
598
629
return ;
@@ -753,14 +784,21 @@ impl fuser::Filesystem for Trick {
753
784
reply : fuser:: ReplyEmpty ,
754
785
) {
755
786
let _ = ( fh, mode) ;
787
+ let trigger_enospc = self
788
+ . trigger_enospc
789
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
756
790
let Some ( inode_data) = self . lookup_inode_mut ( Inode ( ino) ) else {
757
791
reply. error ( libc:: ENOENT ) ;
758
792
return ;
759
793
} ;
760
794
// fallocate should preallocate stuff. We here just gon pretend that we are preallocating.
761
- // we should set the length though .
795
+ // Here we should extend the file if the offset + length is greater than the current file .
762
796
let new_size = u64:: try_from ( offset) . unwrap ( ) + u64:: try_from ( length) . unwrap ( ) ;
763
797
if inode_data. size ( ) < new_size {
798
+ if trigger_enospc {
799
+ reply. error ( libc:: ENOSPC ) ;
800
+ return ;
801
+ }
764
802
inode_data. set_size ( new_size) ;
765
803
}
766
804
reply. ok ( ) ;
@@ -776,6 +814,13 @@ impl fuser::Filesystem for Trick {
776
814
) {
777
815
// fsync doesn't do anything since we are working in-memory, so just return OK.
778
816
let _ = ( ino, fh, datasync) ;
817
+ if self
818
+ . trigger_enospc
819
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
820
+ {
821
+ reply. error ( libc:: ENOSPC ) ;
822
+ return ;
823
+ }
779
824
reply. ok ( ) ;
780
825
}
781
826
@@ -789,6 +834,13 @@ impl fuser::Filesystem for Trick {
789
834
) {
790
835
// just like fsync, fsyncdir doesn't do anything.
791
836
let _ = ( req, ino, fh, datasync) ;
837
+ if self
838
+ . trigger_enospc
839
+ . load ( std:: sync:: atomic:: Ordering :: Relaxed )
840
+ {
841
+ reply. error ( libc:: ENOSPC ) ;
842
+ return ;
843
+ }
792
844
reply. ok ( ) ;
793
845
}
794
846
}
@@ -798,13 +850,21 @@ fn has_odirect(flags: i32) -> bool {
798
850
}
799
851
800
852
pub struct TrickHandle {
801
- bg_sess : fuser:: BackgroundSession ,
853
+ bg_sess : Mutex < Option < fuser:: BackgroundSession > > ,
854
+ trigger_enospc : Arc < AtomicBool > ,
802
855
}
803
856
804
857
impl TrickHandle {
805
858
/// Sets whether the file system should return ENOSPC on the subsequent write operations.
859
+ pub fn set_trigger_enospc ( & self , on : bool ) {
860
+ self . trigger_enospc
861
+ . store ( on, std:: sync:: atomic:: Ordering :: Relaxed ) ;
862
+ }
863
+
806
864
pub fn unmount_and_join ( self ) {
807
- self . bg_sess . join ( ) ;
865
+ if let Some ( bg_sess) = self . bg_sess . lock ( ) . unwrap ( ) . take ( ) {
866
+ bg_sess. join ( ) ;
867
+ }
808
868
}
809
869
}
810
870
@@ -818,9 +878,9 @@ pub fn spawn_trick<P: AsRef<Path>>(mountpoint: P) -> std::io::Result<TrickHandle
818
878
MountOption :: AutoUnmount ,
819
879
MountOption :: FSName ( "trick" . to_string ( ) ) ,
820
880
] ;
821
- let fs = Trick :: new ( ) ;
822
- let bg_sess = fuser:: spawn_mount2 ( fs, & mountpoint, options) ?;
823
- Ok ( TrickHandle { bg_sess } )
881
+ let ( fs , mut handle ) = Trick :: new ( ) ;
882
+ handle . bg_sess = Some ( fuser:: spawn_mount2 ( fs, & mountpoint, options) ?) . into ( ) ;
883
+ Ok ( handle )
824
884
}
825
885
826
886
#[ cfg( test) ]
@@ -848,7 +908,7 @@ mod tests {
848
908
init_log ( ) ;
849
909
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
850
910
let options = & [ MountOption :: RO , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
851
- let fs = Trick :: new ( ) ;
911
+ let ( fs , _handle ) = Trick :: new ( ) ;
852
912
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
853
913
drop ( mount_handle) ;
854
914
}
@@ -859,7 +919,7 @@ mod tests {
859
919
init_log ( ) ;
860
920
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
861
921
let options = & [ MountOption :: RW , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
862
- let fs = Trick :: new ( ) ;
922
+ let ( fs , _handle ) = Trick :: new ( ) ;
863
923
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
864
924
let filename = mountpoint. path ( ) . join ( "file" ) ;
865
925
let file = fs:: File :: create ( & filename) . unwrap ( ) ;
@@ -872,7 +932,7 @@ mod tests {
872
932
init_log ( ) ;
873
933
let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
874
934
let options = & [ MountOption :: RW , MountOption :: FSName ( "trick" . to_string ( ) ) ] ;
875
- let fs = Trick :: new ( ) ;
935
+ let ( fs , _handle ) = Trick :: new ( ) ;
876
936
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
877
937
let filename = mountpoint. path ( ) . join ( "file" ) ;
878
938
let file = fs:: File :: create ( & filename) . unwrap ( ) ;
@@ -891,7 +951,7 @@ mod tests {
891
951
MountOption :: AutoUnmount ,
892
952
MountOption :: FSName ( "trick" . to_string ( ) ) ,
893
953
] ;
894
- let fs = Trick :: new ( ) ;
954
+ let ( fs , _handle ) = Trick :: new ( ) ;
895
955
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
896
956
let filename = mountpoint. path ( ) . join ( "file" ) ;
897
957
let mut file = fs:: File :: options ( )
@@ -924,7 +984,7 @@ mod tests {
924
984
MountOption :: AutoUnmount ,
925
985
MountOption :: FSName ( "trick" . to_string ( ) ) ,
926
986
] ;
927
- let fs = Trick :: new ( ) ;
987
+ let ( fs , _handle ) = Trick :: new ( ) ;
928
988
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
929
989
let mut files = Vec :: new ( ) ;
930
990
for i in 0 ..100 {
@@ -955,7 +1015,7 @@ mod tests {
955
1015
MountOption :: AutoUnmount ,
956
1016
MountOption :: FSName ( "trick" . to_string ( ) ) ,
957
1017
] ;
958
- let fs = Trick :: new ( ) ;
1018
+ let ( fs , _handle ) = Trick :: new ( ) ;
959
1019
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
960
1020
961
1021
// Create a file
@@ -977,7 +1037,7 @@ mod tests {
977
1037
MountOption :: AutoUnmount ,
978
1038
MountOption :: FSName ( "trick" . to_string ( ) ) ,
979
1039
] ;
980
- let fs = Trick :: new ( ) ;
1040
+ let ( fs , _handle ) = Trick :: new ( ) ;
981
1041
let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
982
1042
983
1043
let filename = mountpoint. path ( ) . join ( "file" ) ;
@@ -1009,4 +1069,33 @@ mod tests {
1009
1069
drop ( file) ;
1010
1070
drop ( mount_handle) ;
1011
1071
}
1072
+
1073
+ #[ test]
1074
+ fn out_of_space ( ) {
1075
+ init_log ( ) ;
1076
+ let mountpoint = tempfile:: tempdir ( ) . unwrap ( ) ;
1077
+ let options = & [
1078
+ MountOption :: RW ,
1079
+ MountOption :: AutoUnmount ,
1080
+ MountOption :: FSName ( "trick" . to_string ( ) ) ,
1081
+ ] ;
1082
+ let ( fs, handle) = Trick :: new ( ) ;
1083
+ let mount_handle = fuser:: spawn_mount2 ( fs, & mountpoint, options) . unwrap ( ) ;
1084
+
1085
+ let filename = mountpoint. path ( ) . join ( "file" ) ;
1086
+ let mut file = fs:: File :: options ( )
1087
+ . create_new ( true )
1088
+ . write ( true )
1089
+ . open ( & filename)
1090
+ . unwrap ( ) ;
1091
+
1092
+ let test_data = b"hello world" ;
1093
+ handle. set_trigger_enospc ( true ) ;
1094
+ let _ = file. write_all ( test_data) . unwrap_err ( ) ;
1095
+ handle. set_trigger_enospc ( false ) ;
1096
+ let _ = file. write_all ( test_data) . unwrap ( ) ;
1097
+
1098
+ drop ( file) ;
1099
+ drop ( mount_handle) ;
1100
+ }
1012
1101
}
0 commit comments