@@ -428,21 +428,36 @@ impl<'a> VfsCtxView<'a> {
428428 }
429429
430430 /// Get node at given path.
431- fn node_at ( & self , res : Resource < Descriptor > , path : & str ) -> FsResult < SharedVfsNode > {
431+ fn node_at ( & self , res : Resource < Descriptor > , path : & str ) -> FsResult < Option < SharedVfsNode > > {
432432 let node = self . node ( res) ?;
433433 self . get_node_from_start ( path, node)
434434 }
435435
436436 /// Get node at given path from given starting node.
437- fn get_node_from_start ( & self , path : & str , node : SharedVfsNode ) -> FsResult < SharedVfsNode > {
437+ fn get_node_from_start (
438+ & self ,
439+ path : & str ,
440+ node : SharedVfsNode ,
441+ ) -> FsResult < Option < SharedVfsNode > > {
442+ if path. is_empty ( ) {
443+ return Err ( FsError :: trap ( ErrorCode :: Invalid ) ) ;
444+ }
445+
438446 let ( is_root, directions) = PathTraversal :: parse ( path, & self . vfs_state . limits ) ?;
439447
440448 let start = if is_root {
441449 Arc :: clone ( & self . vfs_state . root )
442450 } else {
443451 node
444452 } ;
445- VfsNode :: traverse ( start, directions)
453+
454+ match VfsNode :: traverse ( start, directions) {
455+ Ok ( node) => Ok ( Some ( node) ) ,
456+ Err ( e) => match e. downcast_ref ( ) {
457+ Some ( ErrorCode :: NoEntry ) => Ok ( None ) ,
458+ _ => Err ( e) ,
459+ } ,
460+ }
446461 }
447462
448463 /// Get the parent node and base name for a given path.
@@ -698,7 +713,7 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
698713 _path_flags : PathFlags ,
699714 path : String ,
700715 ) -> FsResult < DescriptorStat > {
701- Ok ( self . node_at ( self_, & path) ?. read ( ) . unwrap ( ) . stat ( ) )
716+ Ok ( self . node_at ( self_, & path) ?. unwrap ( ) . read ( ) . unwrap ( ) . stat ( ) )
702717 }
703718
704719 async fn set_times_at (
@@ -731,10 +746,6 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
731746 open_flags : OpenFlags ,
732747 flags : DescriptorFlags ,
733748 ) -> FsResult < Resource < Descriptor > > {
734- if path. is_empty ( ) {
735- return Err ( FsError :: trap ( ErrorCode :: Invalid ) ) ;
736- }
737-
738749 let base_desc = self . get_descriptor ( self_) ?;
739750 let base_node = Arc :: clone ( & base_desc. node ) ;
740751 let base_flags = base_desc. flags ;
@@ -744,30 +755,36 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
744755 let exclusive = open_flags. contains ( OpenFlags :: EXCLUSIVE ) ;
745756 let truncate = open_flags. contains ( OpenFlags :: TRUNCATE ) ;
746757
747- // Per POSIX: O_CREAT only creates regular files, not directories.
748- // https://github.com/WebAssembly/WASI/blob/184b0c0e9fd437e5e5601d6e327a28feddbbd7f7/proposals/filesystem/wit/types.wit#L145-L146
749- // "If O_CREAT and O_DIRECTORY are set and the requested access mode is neither
750- // O_WRONLY nor O_RDWR, the result is unspecified."
751- // We choose to disallow this combination entirely.
752- if create && directory {
753- return Err ( FsError :: trap ( ErrorCode :: Invalid ) ) ;
754- }
755-
756- if directory && flags. contains ( DescriptorFlags :: WRITE ) {
757- // Per POSIX: "O_DIRECTORY: "If path resolves to a non-directory file, fail and set errno to [ENOTDIR]."
758- return Err ( FsError :: trap ( ErrorCode :: IsDirectory ) ) ;
759- }
760-
761758 // Try to resolve the path to an existing node
762759 let existing = self . get_node_from_start ( & path, Arc :: clone ( & base_node) ) ;
763760
764761 let node = match ( existing, create, directory, exclusive, truncate) {
765- ( Ok ( node) , true , _, false , false ) => node, // Per POSIX: "If the file exists, O_CREAT has no effect except as noted under O_EXCL below.
766- ( Ok ( _) , true , _, true , _) => {
762+ ( _, true , true , _, _) => {
763+ // Per POSIX: O_CREAT only creates regular files, not directories.
764+ // https://github.com/WebAssembly/WASI/blob/184b0c0e9fd437e5e5601d6e327a28feddbbd7f7/proposals/filesystem/wit/types.wit#L145-L146
765+ // "If O_CREAT and O_DIRECTORY are set and the requested access mode is
766+ // neither O_WRONLY nor O_RDWR, the result is unspecified." We choose to
767+ // disallow this combination entirely.
768+ return Err ( FsError :: trap ( ErrorCode :: Invalid ) ) ;
769+ }
770+ ( Ok ( Some ( node) ) , _, true , _, _) if flags. contains ( DescriptorFlags :: WRITE ) => {
771+ if matches ! ( node. read( ) . unwrap( ) . kind, VfsNodeKind :: Directory { .. } ) {
772+ // Disallow opening directories with write permissions.
773+ // POSIX isn't clear here, so we choose to disallow this
774+ // combination entirely.
775+ return Err ( FsError :: trap ( ErrorCode :: IsDirectory ) ) ;
776+ } else {
777+ // Per POSIX: "O_DIRECTORY: "If path resolves to a
778+ // non-directory file, fail and set errno to [ENOTDIR]."
779+ return Err ( FsError :: trap ( ErrorCode :: NotDirectory ) ) ;
780+ }
781+ }
782+ ( Ok ( Some ( node) ) , true , _, false , false ) => node, // Per POSIX: "If the file exists, O_CREAT has no effect except as noted under O_EXCL below.
783+ ( Ok ( Some ( _) ) , true , _, true , _) => {
767784 // Per POSIX: "O_CREAT and O_EXCL are set, open() shall fail if the file exists"
768785 return Err ( FsError :: trap ( ErrorCode :: Exist ) ) ;
769786 }
770- ( Ok ( node) , false , true , false , false ) => {
787+ ( Ok ( Some ( node) ) , false , true , false , false ) => {
771788 // Per POSIX: "O_DIRECTORY: "If path resolves to a non-directory file, fail and set errno to [ENOTDIR]."
772789 let guard = node. read ( ) . unwrap ( ) ;
773790 if !matches ! ( guard. kind, VfsNodeKind :: Directory { .. } ) {
@@ -776,15 +793,19 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
776793 drop ( guard) ;
777794 node
778795 }
779- ( Ok ( node) , _, _, _, true ) => {
796+ ( Ok ( Some ( node) ) , _, _, _, true ) => {
780797 let mut guard = node. write ( ) . unwrap ( ) ;
781798 match & mut guard. kind {
782799 VfsNodeKind :: File { content } => {
783800 // Per POSIX: "The result of using O_TRUNC without either O_RDWR
784801 // or O_WRONLY is undefined." We allow it but it's a no-op
785802 // unless write permission is granted.
786803 if flags. contains ( DescriptorFlags :: WRITE ) {
787- content. clear ( ) ;
804+ self . vfs_state
805+ . limiter
806+ . shrink ( content. capacity ( ) )
807+ . map_err ( |_| FsError :: trap ( ErrorCode :: InsufficientMemory ) ) ?;
808+ * content = Vec :: new ( ) ;
788809 }
789810 }
790811 VfsNodeKind :: Directory { .. } => {
@@ -798,13 +819,13 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
798819 drop ( guard) ;
799820 node
800821 }
801- ( Ok ( node) , _, _, _, _) => node,
802- ( Err ( _ ) , false , _, _, _) => {
822+ ( Ok ( Some ( node) ) , _, _, _, _) => node,
823+ ( Ok ( None ) , false , _, _, _) => {
803824 // "If O_CREAT is not set and the file does not exist, open()
804825 // shall fail and set errno to [ENOENT]."
805826 return Err ( FsError :: trap ( ErrorCode :: NoEntry ) ) ;
806827 }
807- ( Err ( _ ) , true , _, _, _) => {
828+ ( Ok ( None ) , true , _, _, _) => {
808829 // "If O_CREAT is set and the file does not exist, it shall be
809830 // created as a regular file with permissions"
810831 if !base_flags. contains ( DescriptorFlags :: MUTATE_DIRECTORY ) {
@@ -821,19 +842,6 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
821842 parent : Some ( Arc :: downgrade ( & parent_node) ) ,
822843 } ) ) ;
823844
824- // Account for resource usage
825- self . vfs_state
826- . inodes_allocation
827- . inc ( 1 )
828- . map_err ( FsError :: trap) ?;
829- let growth = name. len ( ) + std:: mem:: size_of_val ( & new_file) ;
830- self . vfs_state . limiter . grow ( growth) . map_err ( |_| {
831- // Rollback inode allocation since we failed to account for
832- // the new file's name and node size
833- self . vfs_state . inodes_allocation . dec ( 1 ) ;
834- FsError :: trap ( ErrorCode :: InsufficientMemory )
835- } ) ?;
836-
837845 // Insert the new file into the parent directory
838846 match & mut parent_node. write ( ) . unwrap ( ) . kind {
839847 VfsNodeKind :: File { .. } => {
@@ -843,8 +851,20 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
843851 return Err ( FsError :: trap ( ErrorCode :: NotDirectory ) ) ;
844852 }
845853 VfsNodeKind :: Directory { children } => {
854+ let growth = name. len ( ) + std:: mem:: size_of_val ( & new_file) ;
846855 match children. entry ( name) {
847856 Entry :: Vacant ( entry) => {
857+ // Account for resource usage
858+ self . vfs_state
859+ . inodes_allocation
860+ . inc ( 1 )
861+ . map_err ( FsError :: trap) ?;
862+ self . vfs_state . limiter . grow ( growth) . map_err ( |_| {
863+ // Rollback inode allocation since we failed to account for
864+ // the new file's name and node size
865+ self . vfs_state . inodes_allocation . dec ( 1 ) ;
866+ FsError :: trap ( ErrorCode :: InsufficientMemory )
867+ } ) ?;
848868 entry. insert ( Arc :: clone ( & new_file) ) ;
849869 }
850870 Entry :: Occupied ( _) => {
@@ -860,6 +880,10 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
860880
861881 new_file
862882 }
883+ ( Err ( e) , _, _, _, _) => {
884+ // Path parsing error
885+ return Err ( FsError :: trap ( e) ) ;
886+ }
863887 } ;
864888
865889 let res = self
@@ -939,6 +963,7 @@ impl<'a> filesystem::types::HostDescriptor for VfsCtxView<'a> {
939963 ) -> FsResult < MetadataHashValue > {
940964 Ok ( self
941965 . node_at ( self_, & path) ?
966+ . unwrap ( )
942967 . read ( )
943968 . unwrap ( )
944969 . metadata_hash ( & self . vfs_state . metadata_hash_key ) )
@@ -1231,13 +1256,14 @@ mod tests {
12311256 let node = ctx. node_at ( desc, name) . unwrap ( ) ;
12321257
12331258 {
1259+ let node = node. unwrap ( ) ;
12341260 let mut guard = node. write ( ) . unwrap ( ) ;
12351261 if let VfsNodeKind :: File { content : c } = & mut guard. kind {
12361262 * c = content;
12371263 }
1264+ drop ( guard) ;
1265+ node
12381266 }
1239-
1240- node
12411267 }
12421268
12431269 // ==================== create_directory_at tests ====================
@@ -1290,7 +1316,7 @@ mod tests {
12901316
12911317 let desc = create_test_descriptor ( & mut ctx, DescriptorFlags :: READ ) ;
12921318 let node = ctx. node_at ( desc, "testdir" ) . unwrap ( ) ;
1293- assert_is_directory ( & node) ;
1319+ assert_is_directory ( & node. unwrap ( ) ) ;
12941320 }
12951321
12961322 #[ tokio:: test]
@@ -1567,7 +1593,7 @@ mod tests {
15671593
15681594 let desc = create_test_descriptor ( & mut ctx, DescriptorFlags :: READ ) ;
15691595 let node = ctx. node_at ( desc, "newfile" ) . unwrap ( ) ;
1570- assert_empty_file ( & node) ;
1596+ assert_empty_file ( & node. unwrap ( ) ) ;
15711597 }
15721598
15731599 #[ tokio:: test]
@@ -1845,7 +1871,7 @@ mod tests {
18451871
18461872 let desc = create_test_descriptor ( & mut ctx, DescriptorFlags :: READ ) ;
18471873 let node = ctx. node_at ( desc, "newfile" ) . unwrap ( ) ;
1848- assert_empty_file ( & node) ;
1874+ assert_empty_file ( & node. unwrap ( ) ) ;
18491875 }
18501876
18511877 #[ tokio:: test]
0 commit comments