@@ -29,10 +29,10 @@ use crate::fs::{
2929 self , AccessPermission , DirectoryEntry , FileAttr , NodeKind , ObjectInterface , OpenOption ,
3030 SeekWhence , VfsNode ,
3131} ;
32+ use crate :: io;
3233use crate :: mm:: device_alloc:: DeviceAlloc ;
3334use crate :: syscalls:: Dirent64 ;
3435use crate :: time:: { time_t, timespec} ;
35- use crate :: { arch, io} ;
3636
3737// response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44
3838// op in/out sizes/layout: https://github.com/hanwen/go-fuse/blob/204b45dba899dfa147235c255908236d5fde2d32/fuse/opcode.go#L439
@@ -1093,61 +1093,99 @@ impl ObjectInterface for FuseDirectoryHandle {
10931093 }
10941094}
10951095
1096- #[ derive( Debug ) ]
1096+ #[ derive( Clone , Debug ) ]
1097+ // The `original_prefix` is the one the directory was originally initialized with,
1098+ // `prefix` is the current prefix (normally, `prefix` has `original_prefix` as its prefix).
1099+ // This distinction is used for symlink resolution and directory traversal.
10971100pub ( crate ) struct FuseDirectory {
1098- prefix : Option < String > ,
1099- attr : FileAttr ,
1101+ original_prefix : Arc < str > ,
1102+ prefix : String ,
11001103}
11011104
11021105impl FuseDirectory {
11031106 pub fn new ( prefix : Option < String > ) -> Self {
1104- let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
1105- let t = timespec:: from_usec ( microseconds as i64 ) ;
1106-
11071107 FuseDirectory {
1108- prefix,
1109- attr : FileAttr {
1110- st_mode : AccessPermission :: from_bits ( 0o777 ) . unwrap ( ) | AccessPermission :: S_IFDIR ,
1111- st_atim : t,
1112- st_mtim : t,
1113- st_ctim : t,
1114- ..Default :: default ( )
1115- } ,
1108+ original_prefix : Arc :: from ( prefix. as_deref ( ) . unwrap_or_default ( ) ) ,
1109+ prefix : prefix. unwrap_or_default ( ) ,
11161110 }
11171111 }
11181112
1119- fn traversal_path ( & self , components : & [ & str ] ) -> CString {
1120- let prefix_deref = self . prefix . as_deref ( ) ;
1121- let components_with_prefix = prefix_deref. iter ( ) . chain ( components. iter ( ) . rev ( ) ) ;
1122- let path: String = components_with_prefix
1123- . flat_map ( |component| [ "/" , component] )
1124- . collect ( ) ;
1125- if path. is_empty ( ) {
1126- CString :: new ( "/" ) . unwrap ( )
1127- } else {
1128- CString :: new ( path) . unwrap ( )
1113+ fn traversal_this ( & self ) -> CString {
1114+ let mut path = String :: new ( ) ;
1115+ path. push ( '/' ) ;
1116+ path. push_str ( & self . prefix ) ;
1117+ CString :: new ( path) . unwrap ( )
1118+ }
1119+
1120+ fn traversal_path ( & self , component : & str ) -> CString {
1121+ let mut path = String :: new ( ) ;
1122+ if !self . prefix . is_empty ( ) {
1123+ path. push ( '/' ) ;
1124+ path. push_str ( & self . prefix ) ;
11291125 }
1126+ path. push ( '/' ) ;
1127+ path. push_str ( component) ;
1128+ CString :: new ( path) . unwrap ( )
11301129 }
11311130}
11321131
1132+ #[ async_trait]
11331133impl VfsNode for FuseDirectory {
11341134 /// Returns the node type
11351135 fn get_kind ( & self ) -> NodeKind {
11361136 NodeKind :: Directory
11371137 }
11381138
1139- fn get_file_attributes ( & self ) -> io:: Result < FileAttr > {
1140- Ok ( self . attr )
1141- }
1142-
11431139 fn get_object ( & self ) -> io:: Result < Arc < async_lock:: RwLock < dyn ObjectInterface > > > {
11441140 Ok ( Arc :: new ( async_lock:: RwLock :: new ( FuseDirectoryHandle :: new (
1145- self . prefix . clone ( ) ,
1141+ if self . prefix . is_empty ( ) {
1142+ None
1143+ } else {
1144+ Some ( self . prefix . clone ( ) )
1145+ } ,
11461146 ) ) ) )
11471147 }
11481148
1149- fn traverse_readdir ( & self , components : & mut Vec < & str > ) -> io:: Result < Vec < DirectoryEntry > > {
1150- let path = self . traversal_path ( components) ;
1149+ fn dup ( & self ) -> Box < dyn VfsNode > {
1150+ Box :: new ( self . clone ( ) )
1151+ }
1152+
1153+ async fn traverse_once ( & self , component : & str ) -> io:: Result < Box < dyn VfsNode > > {
1154+ let mut prefix = self . prefix . clone ( ) ;
1155+ if !prefix. is_empty ( ) {
1156+ prefix. push ( '/' ) ;
1157+ }
1158+ prefix. push_str ( component) ;
1159+
1160+ Ok ( Box :: new ( Self {
1161+ original_prefix : Arc :: clone ( & self . original_prefix ) ,
1162+ prefix,
1163+ } ) )
1164+ }
1165+
1166+ async fn traverse_multiple ( & self , mut path : & str ) -> io:: Result < Box < dyn VfsNode > > {
1167+ let mut prefix = self . prefix . clone ( ) ;
1168+ // this part prevents inserting double-slashes or no slashes between prefix and path
1169+ if !path. is_empty ( ) {
1170+ if let Some ( x) = path. strip_prefix ( "/" ) {
1171+ path = x;
1172+ } else {
1173+ return Err ( Errno :: Nosys ) ;
1174+ }
1175+ if !prefix. is_empty ( ) {
1176+ prefix. push ( '/' ) ;
1177+ }
1178+ }
1179+ prefix. push_str ( path) ;
1180+
1181+ Ok ( Box :: new ( Self {
1182+ original_prefix : Arc :: clone ( & self . original_prefix ) ,
1183+ prefix,
1184+ } ) )
1185+ }
1186+
1187+ async fn readdir ( & self ) -> io:: Result < Vec < DirectoryEntry > > {
1188+ let path = self . traversal_this ( ) ;
11511189
11521190 debug ! ( "FUSE opendir: {path:#?}" ) ;
11531191
@@ -1226,36 +1264,55 @@ impl VfsNode for FuseDirectory {
12261264 Ok ( entries)
12271265 }
12281266
1229- fn traverse_stat ( & self , components : & mut Vec < & str > ) -> io:: Result < FileAttr > {
1230- let path = self . traversal_path ( components ) ;
1267+ async fn stat ( & self ) -> io:: Result < FileAttr > {
1268+ let mut prefix = alloc :: borrow :: Cow :: Borrowed ( & * self . prefix ) ;
12311269
1232- debug ! ( "FUSE stat: {path:#?}" ) ;
1270+ // handle infinite recursion
1271+ let mut encountered = alloc:: collections:: BTreeSet :: < String > :: new ( ) ;
12331272
1234- // Is there a better way to implement this?
1235- let ( cmd, rsp_payload_len) = ops:: Lookup :: create ( path) ;
1236- let rsp = get_filesystem_driver ( )
1237- . unwrap ( )
1238- . lock ( )
1239- . send_command ( cmd, rsp_payload_len) ?;
1273+ while encountered. insert ( ( * prefix) . to_owned ( ) ) {
1274+ let mut path = String :: new ( ) ;
1275+ path. push ( '/' ) ;
1276+ path. push_str ( & prefix) ;
1277+ let path = CString :: new ( path) . unwrap ( ) ;
12401278
1241- if rsp. headers . out_header . error != 0 {
1242- return Err ( Errno :: try_from ( -rsp. headers . out_header . error ) . unwrap ( ) ) ;
1243- }
1279+ debug ! ( "FUSE stat: {path:#?}" ) ;
12441280
1245- let entry_out = rsp. headers . op_header ;
1246- let attr = entry_out. attr ;
1281+ // Is there a better way to implement this?
1282+ let ( cmd, rsp_payload_len) = ops:: Lookup :: create ( path) ;
1283+ let rsp = get_filesystem_driver ( )
1284+ . unwrap ( )
1285+ . lock ( )
1286+ . send_command ( cmd, rsp_payload_len) ?;
1287+
1288+ if rsp. headers . out_header . error != 0 {
1289+ return Err ( Errno :: try_from ( -rsp. headers . out_header . error ) . unwrap ( ) ) ;
1290+ }
1291+
1292+ let entry_out = rsp. headers . op_header ;
1293+ let attr = entry_out. attr ;
1294+
1295+ if attr. mode & S_IFMT != S_IFLNK {
1296+ return Ok ( FileAttr :: from ( attr) ) ;
1297+ }
1298+
1299+ let path = readlink ( entry_out. nodeid ) ?;
1300+
1301+ // rewind and re-traverse
1302+ let mut new_prefix: String = ( * self . original_prefix ) . to_owned ( ) ;
1303+ if !path. starts_with ( '/' ) {
1304+ new_prefix. push ( '/' ) ;
1305+ }
12471306
1248- if attr . mode & S_IFMT != S_IFLNK {
1249- return Ok ( FileAttr :: from ( attr ) ) ;
1307+ new_prefix . push_str ( & path ) ;
1308+ prefix = alloc :: borrow :: Cow :: Owned ( new_prefix ) ;
12501309 }
12511310
1252- let path = readlink ( entry_out. nodeid ) ?;
1253- let mut components: Vec < & str > = path. split ( '/' ) . collect ( ) ;
1254- self . traverse_stat ( & mut components)
1311+ Err ( Errno :: Loop )
12551312 }
12561313
1257- fn traverse_lstat ( & self , components : & mut Vec < & str > ) -> io:: Result < FileAttr > {
1258- let path = self . traversal_path ( components ) ;
1314+ async fn lstat ( & self ) -> io:: Result < FileAttr > {
1315+ let path = self . traversal_this ( ) ;
12591316
12601317 debug ! ( "FUSE lstat: {path:#?}" ) ;
12611318
@@ -1267,13 +1324,13 @@ impl VfsNode for FuseDirectory {
12671324 Ok ( FileAttr :: from ( rsp. headers . op_header . attr ) )
12681325 }
12691326
1270- fn traverse_open (
1327+ async fn open (
12711328 & self ,
1272- components : & mut Vec < & str > ,
1329+ component : & str ,
12731330 opt : OpenOption ,
12741331 mode : AccessPermission ,
12751332 ) -> io:: Result < Arc < async_lock:: RwLock < dyn ObjectInterface > > > {
1276- let path = self . traversal_path ( components ) ;
1333+ let path = self . traversal_path ( component ) ;
12771334
12781335 debug ! ( "FUSE open: {path:#?}, {opt:?} {mode:?}" ) ;
12791336
@@ -1345,8 +1402,8 @@ impl VfsNode for FuseDirectory {
13451402 }
13461403 }
13471404
1348- fn traverse_unlink ( & self , components : & mut Vec < & str > ) -> io:: Result < ( ) > {
1349- let path = self . traversal_path ( components ) ;
1405+ async fn unlink ( & self , component : & str ) -> io:: Result < ( ) > {
1406+ let path = self . traversal_path ( component ) ;
13501407
13511408 let ( cmd, rsp_payload_len) = ops:: Unlink :: create ( path) ;
13521409 let rsp = get_filesystem_driver ( )
@@ -1358,8 +1415,8 @@ impl VfsNode for FuseDirectory {
13581415 Ok ( ( ) )
13591416 }
13601417
1361- fn traverse_rmdir ( & self , components : & mut Vec < & str > ) -> io:: Result < ( ) > {
1362- let path = self . traversal_path ( components ) ;
1418+ async fn rmdir ( & self , component : & str ) -> io:: Result < ( ) > {
1419+ let path = self . traversal_path ( component ) ;
13631420
13641421 let ( cmd, rsp_payload_len) = ops:: Rmdir :: create ( path) ;
13651422 let rsp = get_filesystem_driver ( )
@@ -1371,8 +1428,8 @@ impl VfsNode for FuseDirectory {
13711428 Ok ( ( ) )
13721429 }
13731430
1374- fn traverse_mkdir ( & self , components : & mut Vec < & str > , mode : AccessPermission ) -> io:: Result < ( ) > {
1375- let path = self . traversal_path ( components ) ;
1431+ async fn mkdir ( & self , component : & str , mode : AccessPermission ) -> io:: Result < ( ) > {
1432+ let path = self . traversal_path ( component ) ;
13761433 let ( cmd, rsp_payload_len) = ops:: Mkdir :: create ( path, mode. bits ( ) ) ;
13771434
13781435 let rsp = get_filesystem_driver ( )
0 commit comments