@@ -29,6 +29,7 @@ pub const STATUS_END_OF_FILE: NtStatus = 0xC000_0011u32 as i32;
2929pub const STATUS_UNSUCCESSFUL : NtStatus = 0xC000_0001u32 as i32 ;
3030pub const STATUS_PENDING : NtStatus = 0x0000_0103u32 as i32 ;
3131pub const STATUS_TIMEOUT : NtStatus = 0x0000_0102u32 as i32 ;
32+ pub const STATUS_NO_MORE_ENTRIES : NtStatus = 0x8000_001Au32 as i32 ;
3233pub const STATUS_IMAGE_MACHINE_TYPE_MISMATCH : NtStatus = 0x4000_002Eu32 as i32 ;
3334pub const STATUS_INVALID_IMAGE_FORMAT : NtStatus = 0xC000_007Bu32 as i32 ;
3435pub const STATUS_NOT_SUPPORTED : NtStatus = 0xC000_00BBu32 as i32 ;
@@ -82,6 +83,12 @@ pub const FILE_STANDARD_INFORMATION_CLASS: u32 = 5;
8283pub const FILE_POSITION_INFORMATION_CLASS : u32 = 14 ;
8384pub const FILE_NAME_INFORMATION_CLASS : u32 = 9 ;
8485pub const MEMORY_BASIC_INFORMATION_CLASS : u32 = 0 ;
86+ pub const FILE_FS_SIZE_INFORMATION_CLASS : u32 = 3 ;
87+ pub const FILE_FS_DEVICE_INFORMATION_CLASS : u32 = 4 ;
88+ pub const FILE_FS_ATTRIBUTE_INFORMATION_CLASS : u32 = 5 ;
89+ pub const FILE_DEVICE_DISK : u32 = 0x00000007 ;
90+ pub const FILE_CASE_SENSITIVE_SEARCH : u32 = 0x00000001 ;
91+ pub const FILE_CASE_PRESERVED_NAMES : u32 = 0x00000002 ;
8592
8693pub const EVENT_TYPE_NOTIFICATION : u32 = 0 ;
8794pub const EVENT_TYPE_SYNCHRONIZATION : u32 = 1 ;
@@ -751,6 +758,128 @@ pub fn open_section(name: &str) -> Result<u32, NtStatus> {
751758 Err ( STATUS_OBJECT_NAME_NOT_FOUND )
752759}
753760
761+ pub fn open_directory ( name : & str ) -> Result < u32 , NtStatus > {
762+ init_namespace ( ) ;
763+ let canonical = canonicalize_nt_path ( name) ;
764+ let mut objects = OBJECTS . lock ( ) ;
765+ let Some ( id) = objects. named . get ( & canonical) . copied ( ) else {
766+ return Err ( STATUS_OBJECT_NAME_NOT_FOUND ) ;
767+ } ;
768+ let Some ( record) = objects. objects . get_mut ( & id) else {
769+ return Err ( STATUS_OBJECT_NAME_NOT_FOUND ) ;
770+ } ;
771+ if record. object_type != ObjectType :: Directory {
772+ return Err ( STATUS_OBJECT_TYPE_MISMATCH ) ;
773+ }
774+ record. refs = record. refs . saturating_add ( 1 ) ;
775+ Ok ( id)
776+ }
777+
778+ pub fn open_symbolic_link ( name : & str ) -> Result < u32 , NtStatus > {
779+ init_namespace ( ) ;
780+ let canonical = canonicalize_nt_path ( name) ;
781+ let mut objects = OBJECTS . lock ( ) ;
782+ let Some ( id) = objects. named . get ( & canonical) . copied ( ) else {
783+ return Err ( STATUS_OBJECT_NAME_NOT_FOUND ) ;
784+ } ;
785+ let Some ( record) = objects. objects . get_mut ( & id) else {
786+ return Err ( STATUS_OBJECT_NAME_NOT_FOUND ) ;
787+ } ;
788+ if record. object_type != ObjectType :: SymbolicLink {
789+ return Err ( STATUS_OBJECT_TYPE_MISMATCH ) ;
790+ }
791+ record. refs = record. refs . saturating_add ( 1 ) ;
792+ Ok ( id)
793+ }
794+
795+ pub fn query_symbolic_link_target ( object_id : u32 ) -> Result < String , NtStatus > {
796+ let objects = OBJECTS . lock ( ) ;
797+ let Some ( record) = objects. objects . get ( & object_id) else {
798+ return Err ( STATUS_INVALID_HANDLE ) ;
799+ } ;
800+ match & record. data {
801+ ObjectData :: SymbolicLink { target } => Ok ( target. clone ( ) ) ,
802+ _ => Err ( STATUS_OBJECT_TYPE_MISMATCH ) ,
803+ }
804+ }
805+
806+ fn object_type_name ( kind : ObjectType ) -> & ' static str {
807+ match kind {
808+ ObjectType :: Directory => "Directory" ,
809+ ObjectType :: SymbolicLink => "SymbolicLink" ,
810+ ObjectType :: File => "File" ,
811+ ObjectType :: Key => "Key" ,
812+ ObjectType :: Section => "Section" ,
813+ ObjectType :: Process => "Process" ,
814+ ObjectType :: Thread => "Thread" ,
815+ ObjectType :: Event => "Event" ,
816+ }
817+ }
818+
819+ pub fn query_directory_entries ( object_id : u32 ) -> Result < Vec < ( String , String ) > , NtStatus > {
820+ let objects = OBJECTS . lock ( ) ;
821+ let Some ( record) = objects. objects . get ( & object_id) else {
822+ return Err ( STATUS_INVALID_HANDLE ) ;
823+ } ;
824+ if record. object_type != ObjectType :: Directory {
825+ return Err ( STATUS_OBJECT_TYPE_MISMATCH ) ;
826+ }
827+ let Some ( dir_path) = record. name . as_ref ( ) else {
828+ return Err ( STATUS_OBJECT_NAME_NOT_FOUND ) ;
829+ } ;
830+
831+ let mut entries: BTreeMap < String , String > = BTreeMap :: new ( ) ;
832+ if dir_path == "\\ " {
833+ for ( name, child_id) in & objects. named {
834+ if name == "\\ " {
835+ continue ;
836+ }
837+ let Some ( rest) = name. strip_prefix ( '\\' ) else {
838+ continue ;
839+ } ;
840+ if rest. is_empty ( ) {
841+ continue ;
842+ }
843+ let child = rest. split ( '\\' ) . next ( ) . unwrap_or ( "" ) ;
844+ if child. is_empty ( ) {
845+ continue ;
846+ }
847+ let full = format ! ( "\\ {}" , child) ;
848+ let Some ( child_record) = objects. objects . get ( child_id) else {
849+ continue ;
850+ } ;
851+ entries. insert (
852+ child. to_string ( ) ,
853+ object_type_name ( child_record. object_type ) . to_string ( ) ,
854+ ) ;
855+ let _ = full;
856+ }
857+ } else {
858+ let prefix = format ! ( "{dir_path}\\ " ) ;
859+ for ( name, child_id) in & objects. named {
860+ if !name. starts_with ( & prefix) {
861+ continue ;
862+ }
863+ let rest = & name[ prefix. len ( ) ..] ;
864+ if rest. is_empty ( ) {
865+ continue ;
866+ }
867+ let child = rest. split ( '\\' ) . next ( ) . unwrap_or ( "" ) ;
868+ if child. is_empty ( ) {
869+ continue ;
870+ }
871+ let Some ( child_record) = objects. objects . get ( child_id) else {
872+ continue ;
873+ } ;
874+ entries. insert (
875+ child. to_string ( ) ,
876+ object_type_name ( child_record. object_type ) . to_string ( ) ,
877+ ) ;
878+ }
879+ }
880+ Ok ( entries. into_iter ( ) . collect ( ) )
881+ }
882+
754883pub fn create_process ( pid : u32 ) -> u32 {
755884 let mut objects = OBJECTS . lock ( ) ;
756885 insert_unnamed (
0 commit comments