@@ -137,6 +137,10 @@ fn is_safe_inode(mode: u32) -> bool {
137
137
matches ! ( mode & libc:: S_IFMT , libc:: S_IFREG | libc:: S_IFDIR )
138
138
}
139
139
140
+ fn is_dir ( mode : u32 ) -> bool {
141
+ ( mode & libc:: S_IFMT ) == libc:: S_IFDIR
142
+ }
143
+
140
144
impl InodeData {
141
145
fn new ( inode : Inode , f : FileOrHandle , refcount : u64 , altkey : InodeAltKey , mode : u32 ) -> Self {
142
146
InodeData {
@@ -391,6 +395,15 @@ pub struct Config {
391
395
/// The default value for this option is 5 seconds.
392
396
pub attr_timeout : Duration ,
393
397
398
+ /// Same as `entry_timeout`, override `entry_timeout` config, but only take effect on
399
+ /// directories when specified. This is useful to set different timeouts for directories and
400
+ /// regular files.
401
+ pub dir_entry_timeout : Option < Duration > ,
402
+
403
+ /// Same as `attr_timeout`, override `attr_timeout` config, but only take effect on directories
404
+ /// when specified. This is useful to set different timeouts for directories and regular files.
405
+ pub dir_attr_timeout : Option < Duration > ,
406
+
394
407
/// The caching policy the file system should use. See the documentation of `CachePolicy` for
395
408
/// more details.
396
409
pub cache_policy : CachePolicy ,
@@ -499,6 +512,8 @@ impl Default for Config {
499
512
seal_size : false ,
500
513
enable_mntid : false ,
501
514
dax_file_size : None ,
515
+ dir_entry_timeout : None ,
516
+ dir_attr_timeout : None ,
502
517
}
503
518
}
504
519
}
@@ -555,6 +570,9 @@ pub struct PassthroughFs<S: BitmapSlice + Send + Sync = ()> {
555
570
// Init from guest kernel Init cmd of fuse fs.
556
571
perfile_dax : AtomicBool ,
557
572
573
+ dir_entry_timeout : Duration ,
574
+ dir_attr_timeout : Duration ,
575
+
558
576
cfg : Config ,
559
577
560
578
phantom : PhantomData < S > ,
@@ -572,6 +590,13 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
572
590
0 ,
573
591
) ?;
574
592
593
+ let ( dir_entry_timeout, dir_attr_timeout) =
594
+ match ( cfg. dir_entry_timeout , cfg. dir_attr_timeout ) {
595
+ ( Some ( e) , Some ( a) ) => ( e, a) ,
596
+ ( Some ( e) , None ) => ( e, cfg. attr_timeout ) ,
597
+ ( None , Some ( a) ) => ( cfg. entry_timeout , a) ,
598
+ ( None , None ) => ( cfg. entry_timeout , cfg. attr_timeout ) ,
599
+ } ;
575
600
Ok ( PassthroughFs {
576
601
inode_map : InodeMap :: new ( ) ,
577
602
next_inode : AtomicU64 :: new ( fuse:: ROOT_ID + 1 ) ,
@@ -589,6 +614,8 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
589
614
no_readdir : AtomicBool :: new ( cfg. no_readdir ) ,
590
615
seal_size : AtomicBool :: new ( cfg. seal_size ) ,
591
616
perfile_dax : AtomicBool :: new ( false ) ,
617
+ dir_entry_timeout,
618
+ dir_attr_timeout,
592
619
cfg,
593
620
594
621
phantom : PhantomData ,
@@ -970,13 +997,19 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
970
997
}
971
998
} ;
972
999
1000
+ let attr = st. get_stat ( ) ;
1001
+ let ( entry_timeout, attr_timeout) = if is_dir ( attr. st_mode ) {
1002
+ ( self . dir_entry_timeout , self . dir_attr_timeout )
1003
+ } else {
1004
+ ( self . cfg . entry_timeout , self . cfg . attr_timeout )
1005
+ } ;
973
1006
Ok ( Entry {
974
1007
inode,
975
1008
generation : 0 ,
976
- attr : st . get_stat ( ) ,
1009
+ attr,
977
1010
attr_flags,
978
- attr_timeout : self . cfg . attr_timeout ,
979
- entry_timeout : self . cfg . entry_timeout ,
1011
+ attr_timeout,
1012
+ entry_timeout,
980
1013
} )
981
1014
}
982
1015
@@ -1515,4 +1548,76 @@ mod tests {
1515
1548
let n = f. read ( & mut buf) . unwrap ( ) ;
1516
1549
assert_eq ! ( n, 0 ) ;
1517
1550
}
1551
+
1552
+ #[ test]
1553
+ fn test_is_dir ( ) {
1554
+ let mode = libc:: S_IFREG ;
1555
+ assert ! ( !is_dir( mode) ) ;
1556
+
1557
+ let mode = libc:: S_IFDIR ;
1558
+ assert ! ( is_dir( mode) ) ;
1559
+ }
1560
+
1561
+ #[ test]
1562
+ fn test_passthroughfs_dir_timeout ( ) {
1563
+ log:: set_max_level ( log:: LevelFilter :: Trace ) ;
1564
+
1565
+ let source = TempDir :: new ( ) . expect ( "Cannot create temporary directory." ) ;
1566
+ let parent_path =
1567
+ TempDir :: new_in ( source. as_path ( ) ) . expect ( "Cannot create temporary directory." ) ;
1568
+ let child_path =
1569
+ TempFile :: new_in ( parent_path. as_path ( ) ) . expect ( "Cannot create temporary file." ) ;
1570
+
1571
+ // passthroughfs with cache=none, but non-zero dir entry/attr timeout.
1572
+ let fs_cfg = Config {
1573
+ writeback : false ,
1574
+ do_import : true ,
1575
+ no_open : false ,
1576
+ root_dir : source
1577
+ . as_path ( )
1578
+ . to_str ( )
1579
+ . expect ( "source path to string" )
1580
+ . to_string ( ) ,
1581
+ cache_policy : CachePolicy :: Never ,
1582
+ entry_timeout : Duration :: from_secs ( 0 ) ,
1583
+ attr_timeout : Duration :: from_secs ( 0 ) ,
1584
+ dir_entry_timeout : Some ( Duration :: from_secs ( 1 ) ) ,
1585
+ dir_attr_timeout : Some ( Duration :: from_secs ( 2 ) ) ,
1586
+ ..Default :: default ( )
1587
+ } ;
1588
+ let fs = PassthroughFs :: < ( ) > :: new ( fs_cfg) . unwrap ( ) ;
1589
+ fs. import ( ) . unwrap ( ) ;
1590
+
1591
+ let ctx = Context :: default ( ) ;
1592
+
1593
+ // parent entry should have non-zero timeouts
1594
+ let parent = CString :: new (
1595
+ parent_path
1596
+ . as_path ( )
1597
+ . file_name ( )
1598
+ . unwrap ( )
1599
+ . to_str ( )
1600
+ . expect ( "path to string" ) ,
1601
+ )
1602
+ . unwrap ( ) ;
1603
+ let p_entry = fs. lookup ( & ctx, ROOT_ID , & parent) . unwrap ( ) ;
1604
+ assert_eq ! ( p_entry. entry_timeout, Duration :: from_secs( 1 ) ) ;
1605
+ assert_eq ! ( p_entry. attr_timeout, Duration :: from_secs( 2 ) ) ;
1606
+
1607
+ // regular file has zero timeout value
1608
+ let child = CString :: new (
1609
+ child_path
1610
+ . as_path ( )
1611
+ . file_name ( )
1612
+ . unwrap ( )
1613
+ . to_str ( )
1614
+ . expect ( "path to string" ) ,
1615
+ )
1616
+ . unwrap ( ) ;
1617
+ let c_entry = fs. lookup ( & ctx, p_entry. inode , & child) . unwrap ( ) ;
1618
+ assert_eq ! ( c_entry. entry_timeout, Duration :: from_secs( 0 ) ) ;
1619
+ assert_eq ! ( c_entry. attr_timeout, Duration :: from_secs( 0 ) ) ;
1620
+
1621
+ fs. destroy ( ) ;
1622
+ }
1518
1623
}
0 commit comments