Improve NativeNT RTL (tested on Windows 7)

* fix DirectoryExists which didn't yet work without System privileges
* fix FileExists which didn't use the correct access flags
+ implement FindFirst/FindNext/FindClose which is conceptually based on the Find-mechanism of the Unix RTL as for the object hierarchy I can't use the same mechanism that Windows provides for filesystems (the function NtQueryDirectoryFile provides the possibility to pass a pattern, but I'm not using that functionality; maybe I'll update FindNext in the future to use this for speed up...). Note: The PChar "workaround" is needed, because string reallocation does currently not work... (maybe Reallocmem is buggy)
- remove FindMatch which is only provided by Windows SysUtils and DOS units
+ add functions/constants/types which are used by Find* to the NDK includes
- remove "packed" from file information types (Windows 7 didn't like the size otherwise)

git-svn-id: trunk@21438 -
This commit is contained in:
svenbarth 2012-05-31 07:18:13 +00:00
parent 3b7a933e9d
commit 22d1a8bbab
9 changed files with 585 additions and 42 deletions

1
.gitattributes vendored
View File

@ -7944,6 +7944,7 @@ rtl/nativent/ndk/ketypes.inc svneol=native#text/plain
rtl/nativent/ndk/ntdef.inc svneol=native#text/plain
rtl/nativent/ndk/ntstatus.inc svneol=native#text/plain
rtl/nativent/ndk/obfuncs.inc svneol=native#text/plain
rtl/nativent/ndk/obtypes.inc svneol=native#text/plain
rtl/nativent/ndk/peb_teb.inc svneol=native#text/plain
rtl/nativent/ndk/pstypes.inc svneol=native#text/plain
rtl/nativent/ndk/rtlfuncs.inc svneol=native#text/plain

View File

@ -35,6 +35,7 @@ const
{$include iotypes.inc}
{$include rtltypes.inc}
{$include ketypes.inc}
{$include obtypes.inc}
{$include pstypes.inc}
{$include peb_teb.inc}

View File

@ -38,6 +38,20 @@ function NtOpenFile(
OpenOptions: ULONG
): NTSTATUS; external ntdll;
function NtQueryDirectoryFile(
FileHandle: HANDLE;
Event: HANDLE; {OPTIONAL}
ApcRoutine: PIO_APC_ROUTINE; {OPTIONAL}
ApcContext: PVOID; {OPTIONAL}
IoStatusBlock: PIO_STATUS_BLOCK;
FileInformation: PVOID;
Length: ULONG;
FileInformationClass: FILE_INFORMATION_CLASS;
ReturnSingleEntry: NT_BOOLEAN;
FileName: PUNICODE_STRING; {OPTIONAL}
RestartScan: NT_BOOLEAN
): NTSTATUS; external ntdll;
function NtQueryFullAttributesFile(
ObjectAttributes: POBJECT_ATTRIBUTES;
FileInformation: PFILE_NETWORK_OPEN_INFORMATION

View File

@ -104,8 +104,8 @@ type
//
// I/O Status Block
//
_IO_STATUS_BLOCK = packed record
union1: packed record
_IO_STATUS_BLOCK = record
union1: record
case Boolean of
True: (Status: NTSTATUS);
False: (Pointer: PVOID);
@ -118,7 +118,7 @@ type
//
// File Information structures for NtQueryInformationFile
//
_FILE_BASIC_INFORMATION = packed record
_FILE_BASIC_INFORMATION = record
CreationTime: LARGE_INTEGER;
LastAccessTime: LARGE_INTEGER;
LastWriteTime: LARGE_INTEGER;
@ -126,9 +126,9 @@ type
FileAttributes: ULONG;
end;
FILE_BASIC_INFORMATION = _FILE_BASIC_INFORMATION;
PFILE_BASIC_INFORMATION = FILE_BASIC_INFORMATION;
PFILE_BASIC_INFORMATION = ^FILE_BASIC_INFORMATION;
_FILE_STANDARD_INFORMATION = packed record
_FILE_STANDARD_INFORMATION = record
AllocationSize: LARGE_INTEGER;
EndOfFile: LARGE_INTEGER;
NumberOfLinks: ULONG;
@ -138,7 +138,7 @@ type
FILE_STANDARD_INFORMATION = _FILE_STANDARD_INFORMATION;
PFILE_STANDARD_INFORMATION = ^FILE_STANDARD_INFORMATION;
_FILE_NETWORK_OPEN_INFORMATION = packed record
_FILE_NETWORK_OPEN_INFORMATION = record
CreationTime: LARGE_INTEGER;
LastAccessTime: LARGE_INTEGER;
LastWriteTime: LARGE_INTEGER;
@ -150,19 +150,19 @@ type
FILE_NETWORK_OPEN_INFORMATION = _FILE_NETWORK_OPEN_INFORMATION;
PFILE_NETWORK_OPEN_INFORMATION = ^FILE_NETWORK_OPEN_INFORMATION;
_FILE_POSITION_INFORMATION = packed record
_FILE_POSITION_INFORMATION = record
CurrentByteOffset: LARGE_INTEGER;
end;
FILE_POSITION_INFORMATION = _FILE_POSITION_INFORMATION;
PFILE_POSITION_INFORMATION = ^FILE_POSITION_INFORMATION;
_FILE_DISPOSITION_INFORMATION = packed record
_FILE_DISPOSITION_INFORMATION = record
DeleteFile: NT_BOOLEAN;
end;
FILE_DISPOSITION_INFORMATION = _FILE_DISPOSITION_INFORMATION;
PFILE_DISPOSITION_INFORMATION = ^FILE_DISPOSITION_INFORMATION;
_FILE_RENAME_INFORMATION = packed record
_FILE_RENAME_INFORMATION = record
ReplaceIfExists: NT_BOOLEAN;
RootDirectory: HANDLE;
FileNameLength: ULONG;
@ -171,16 +171,65 @@ type
FILE_RENAME_INFORMATION = _FILE_RENAME_INFORMATION;
PFILE_RENAME_INFORMATION = ^FILE_RENAME_INFORMATION;
_FILE_ALLOCATION_INFORMATION = packed record
_FILE_BOTH_DIR_INFORMATION = record
NextEntryOffset: ULONG;
FileIndex: ULONG;
CreationTime: LARGE_INTEGER;
LastAccessTime: LARGE_INTEGER;
LastWriteTime: LARGE_INTEGER;
ChangeTime: LARGE_INTEGER;
EndOfFile: LARGE_INTEGER;
AllocationSize: LARGE_INTEGER;
FileAttributes: ULONG;
FileNameLength: ULONG;
EaSize: ULONG;
ShortNameLength: CCHAR;
ShortName: array[0..11] of WCHAR;
FileName: array[0..0] of WCHAR;
end;
FILE_BOTH_DIR_INFORMATION = _FILE_BOTH_DIR_INFORMATION;
PFILE_BOTH_DIR_INFORMATION = ^FILE_BOTH_DIR_INFORMATION;
_FILE_NAME_INFORMATION = record
FileNameLength: ULONG;
FileName: array[0..0] of WCHAR;
end;
FILE_NAME_INFORMATION = _FILE_NAME_INFORMATION;
PFILE_NAME_INFORMATION = ^FILE_NAME_INFORMATION;
_FILE_ALLOCATION_INFORMATION = record
AllocationSize: LARGE_INTEGER;
end;
FILE_ALLOCATION_INFORMATION = _FILE_ALLOCATION_INFORMATION;
PFILE_ALLOCATION_INFORMATION = ^FILE_ALLOCATION_INFORMATION;
_FILE_END_OF_FILE_INFORMATION = packed record
_FILE_END_OF_FILE_INFORMATION = record
EndOfFile: LARGE_INTEGER;
end;
FILE_END_OF_FILE_INFORMATION = _FILE_END_OF_FILE_INFORMATION;
PFILE_END_OF_FILE_INFORMATION = ^FILE_END_OF_FILE_INFORMATION;
_FILE_DIRECTORY_INFORMATION = record
NextEntryOffset: ULONG;
FileIndex: ULONG;
CreationTime: LARGE_INTEGER;
LastAccessTime: LARGE_INTEGER;
LastWriteTime: LARGE_INTEGER;
ChangeTime: LARGE_INTEGER;
EndOfFile: LARGE_INTEGER;
AllocationSize: LARGE_INTEGER;
FileAttributes: ULONG;
FileNameLength: ULONG;
FileName: array[0..0] of WCHAR;
end;
FILE_DIRECTORY_INFORMATION = _FILE_DIRECTORY_INFORMATION;
PFILE_DIRECTORY_INFORMATION = ^FILE_DIRECTORY_INFORMATION;
//
// APC Callback for NtCreateFile
//
PIO_APC_ROUTINE = procedure(
ApcContext: PVOID;
IoStatusBlock: PIO_STATUS_BLOCK;
Reserved: ULONG); stdcall;

View File

@ -20,6 +20,12 @@ const
STATUS_PENDING = NTSTATUS($00000103);
STATUS_NO_MORE_FILES = NTSTATUS($80000006);
STATUS_NO_MORE_ENTRIES = NTSTATUS($8000001A);
STATUS_INVALID_HANDLE = NTSTATUS($C0000008);
STATUS_INVALID_PARAMETER = NTSTATUS($C000000D);
STATUS_OBJECT_TYPE_MISMATCH = NTSTATUS($C0000024);
STATUS_OBJECT_NAME_COLLISION = NTSTATUS($C0000035);

View File

@ -21,6 +21,24 @@ function NtOpenDirectoryObject(
ObjectAttributes: POBJECT_ATTRIBUTES
): NTSTATUS; external ntdll;
function NtQueryDirectoryObject(
DirectoryHandle: HANDLE;
Buffer: PVOID;
BufferLength: ULONG;
ReturnSingleEntry: NT_BOOLEAN;
RestartScan: NT_BOOLEAN;
Context: PULONG;
ReturnLength: PULONG {OPTIONAL}
): NTSTATUS; external ntdll;
function NtQueryObject(
ObjectHandle: HANDLE;
ObjectInformationClass: OBJECT_INFORMATION_CLASS;
ObjectInformation: PVOID;
Length: ULONG;
ResultLength: PULONG {OPTIONAL}
): NTSTATUS; external ntdll;
function NtWaitForSingleObject(
_Object: HANDLE;
Alertable: NT_BOOLEAN;

View File

@ -0,0 +1,82 @@
{%MainUnit ndk.pas}
{
Native Development Kit for Native NT
This file is part of the Free Pascal run time library.
This unit contains types for use with the Object Manager.
Copyright (c) 2012 by Sven Barth
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
const
//
// Directory Object Access Rights
//
DIRECTORY_QUERY = $0001;
DIRECTORY_TRAVERSE = $0002;
DIRECTORY_CREATE_OBJECT = $0004;
DIRECTORY_CREATE_SUBDIRECTORY = $0008;
{ TODO }
//DIRECTORY_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or $F);
type
//
// Object Information Classes for NtQueryInformationObject
//
_OBJECT_INFORMATION_CLASS = (
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectTypesInformation,
ObjectHandleFlagInformation,
ObjectSessionInformation,
MaxObjectInfoClass
);
OBJECT_INFORMATION_CLASS = _OBJECT_INFORMATION_CLASS;
//
// Object Information Types for NtQueryInformationObject
//
_OBJECT_NAME_INFORMATION = record
Name: UNICODE_STRING;
end;
OBJECT_NAME_INFORMATION = _OBJECT_NAME_INFORMATION;
POBJECT_NAME_INFORMATION = ^OBJECT_NAME_INFORMATION;
_OBJECT_HANDLE_ATTRIBUTE_INFORMATION = record
Inherit: NT_BOOLEAN;
ProtectFromClose: NT_BOOLEAN;
end;
OBJECT_HANDLE_ATTRIBUTE_INFORMATION = _OBJECT_HANDLE_ATTRIBUTE_INFORMATION;
POBJECT_HANDLE_ATTRIBUTE_INFORMATION = ^OBJECT_HANDLE_ATTRIBUTE_INFORMATION;
_OBJECT_DIRECTORY_INFORMATION = record
Name: UNICODE_STRING;
TypeName: UNICODE_STRING;
end;
OBJECT_DIRECTORY_INFORMATION = _OBJECT_DIRECTORY_INFORMATION;
POBJECT_DIRECTORY_INFORMATION = ^OBJECT_DIRECTORY_INFORMATION;
_OBJECT_BASIC_INFORMATION = record
Attributes: ULONG;
GrantedAccess: ACCESS_MASK;
HandleCount: ULONG;
PointerCount: ULONG;
PagedPoolUsage: ULONG;
NonPagedPoolUsage: ULONG;
Reserved: array[0..2] of ULONG;
NameInformationLength: ULONG;
TypeInformationLength: ULONG;
SecurityDescriptorLength: ULONG;
CreateTime: LARGE_INTEGER;
end;
OBJECT_BASIC_INFORMATION = _OBJECT_BASIC_INFORMATION;
POBJECT_BASIC_INFORMATION = ^OBJECT_BASIC_INFORMATION;

View File

@ -18,6 +18,8 @@
type
WCHAR = WideChar;
PWSTR = PWideChar;
CCHAR = Byte;
PCCHAR = ^CCHAR;
const
NT_DELETE = $00010000;

View File

@ -28,6 +28,17 @@ uses
{$DEFINE HAS_SLEEP}
{$DEFINE HAS_CREATEGUID}
type
TNativeNTFindData = record
SearchSpec: String;
NamePos: LongInt;
Handle: THandle;
IsDirObj: Boolean;
SearchAttr: LongInt;
Context: ULONG;
LastRes: NTSTATUS;
end;
{ Include platform independent interface part }
{$i sysutilh.inc}
@ -56,7 +67,7 @@ const
0,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE);
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
var
ntstr: UNICODE_STRING;
objattr: OBJECT_ATTRIBUTES;
@ -72,6 +83,25 @@ end;
function FileCreate(const FileName : String) : THandle;
begin
FileCreate := FileCreate(FileName, fmShareDenyNone, 0);
end;
function FileCreate(const FileName : String; Rights: longint) : THandle;
begin
FileCreate := FileCreate(FileName, fmShareDenyNone, Rights);
end;
function FileCreate(const FileName : String; ShareMode : longint; Rights: longint) : THandle;
const
ShareModeFlags: array[0..4] of ULONG = (
0,
0,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
var
ntstr: UNICODE_STRING;
objattr: OBJECT_ATTRIBUTES;
@ -81,24 +111,13 @@ begin
AnsiStrToNTStr(FileName, ntstr);
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
NtCreateFile(@Result, GENERIC_READ or GENERIC_WRITE or NT_SYNCHRONIZE,
@objattr, @iostatus, Nil, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
@objattr, @iostatus, Nil, FILE_ATTRIBUTE_NORMAL,
ShareModeFlags[(ShareMode and $F0) shr 4], FILE_OVERWRITE_IF,
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
FreeNtStr(ntstr);
end;
function FileCreate(const FileName : String; Rights: longint) : THandle;
begin
FileCreate := FileCreate(FileName);
end;
function FileCreate(const FileName : String; ShareMode : longint; Rights: longint) : THandle;
begin
FileCreate := FileCreate(FileName);
end;
function FileRead(Handle : THandle; out Buffer; Count : longint) : Longint;
var
iostatus: IO_STATUS_BLOCK;
@ -288,6 +307,7 @@ end;
function FileAge(const FileName: String): Longint;
begin
{ TODO }
Result := -1;
end;
@ -302,8 +322,8 @@ var
begin
AnsiStrToNtStr(FileName, ntstr);
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
res := NtOpenFile(@h, 0, @objattr, @iostatus,
FILE_SHARE_READ or FILE_SHARE_WRITE,
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
Result := NT_SUCCESS(res);
@ -325,15 +345,15 @@ begin
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
{ first test wether this is a object directory }
res := NtOpenDirectoryObject(@h, 0, @objattr);
res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
if NT_SUCCESS(res) then
Result := True
else begin
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
{ this is a file object! }
res := NtOpenFile(@h, 0, @objattr, @iostatus,
FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
Result := NT_SUCCESS(res);
end else
Result := False;
@ -344,28 +364,378 @@ begin
FreeNtStr(ntstr);
end;
{ copied from rtl/unix/sysutils.pp }
Function FNMatch(const Pattern,Name:string):Boolean;
Var
LenPat,LenName : longint;
function FindMatch(var f: TSearchRec): Longint;
Function DoFNMatch(i,j:longint):Boolean;
Var
Found : boolean;
Begin
Found:=true;
While Found and (i<=LenPat) Do
Begin
Case Pattern[i] of
'?' : Found:=(j<=LenName);
'*' : Begin
{find the next character in pattern, different of ? and *}
while Found do
begin
inc(i);
if i>LenPat then Break;
case Pattern[i] of
'*' : ;
'?' : begin
if j>LenName then begin DoFNMatch:=false; Exit; end;
inc(j);
end;
else
Found:=false;
end;
end;
Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') ));
{Now, find in name the character which i points to, if the * or ?
wasn't the last character in the pattern, else, use up all the
chars in name}
Found:=false;
if (i<=LenPat) then
begin
repeat
{find a letter (not only first !) which maches pattern[i]}
while (j<=LenName) and (name[j]<>pattern[i]) do
inc (j);
if (j<LenName) then
begin
if DoFnMatch(i+1,j+1) then
begin
i:=LenPat;
j:=LenName;{we can stop}
Found:=true;
Break;
end else
inc(j);{We didn't find one, need to look further}
end else
if j=LenName then
begin
Found:=true;
Break;
end;
{ This 'until' condition must be j>LenName, not j>=LenName.
That's because when we 'need to look further' and
j = LenName then loop must not terminate. }
until (j>LenName);
end else
begin
j:=LenName;{we can stop}
Found:=true;
end;
end;
else {not a wildcard character in pattern}
Found:=(j<=LenName) and (pattern[i]=name[j]);
end;
inc(i);
inc(j);
end;
DoFnMatch:=Found and (j>LenName);
end;
Begin {start FNMatch}
LenPat:=Length(Pattern);
LenName:=Length(Name);
FNMatch:=DoFNMatch(1,1);
End;
function FindGetFileInfo(const s: String; var f: TSearchRec): Boolean;
var
ntstr: UNICODE_STRING;
objattr: OBJECT_ATTRIBUTES;
res: NTSTATUS;
h: THandle;
iostatus: IO_STATUS_BLOCK;
attr: LongInt;
filename: String;
isfileobj: Boolean;
buf: array of Byte;
objinfo: OBJECT_BASIC_INFORMATION;
fileinfo: FILE_BASIC_INFORMATION;
time: LongInt;
begin
Result := -1;
end;
AnsiStrToNtStr(s, ntstr);
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
filename := ExtractFileName(s);
function FindFirst(const Path: String; Attr: Longint; out Rslt: TSearchRec): Longint;
begin
Result := -1;
end;
{ TODO : handle symlinks }
{ If Assigned(F.FindHandle) and ((((PUnixFindData(f.FindHandle)^.searchattr)) and faSymlink) > 0) then
FindGetFileInfo:=(fplstat(pointer(s),st)=0)
else
FindGetFileInfo:=(fpstat(pointer(s),st)=0);}
attr := 0;
Result := False;
function FindNext(var Rslt: TSearchRec): Longint;
begin
Result := -1;
if (faDirectory and f.FindData.SearchAttr <> 0) and
((filename = '.') or (filename = '..')) then begin
attr := faDirectory;
res := STATUS_SUCCESS;
end else
res := STATUS_INVALID_PARAMETER;
isfileobj := False;
if not NT_SUCCESS(res) then begin
{ first check whether it's a directory }
res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
if not NT_SUCCESS(res) then
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
isfileobj := NT_SUCCESS(res);
end;
if NT_SUCCESS(res) then
attr := faDirectory;
end;
if not NT_SUCCESS(res) then begin
{ first try whether we have a file object }
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
isfileobj := NT_SUCCESS(res);
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
{ is this an object? }
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (res = STATUS_OBJECT_TYPE_MISMATCH)
and (f.FindData.SearchAttr and faSysFile <> 0) then begin
{ this is some other system file like an event or port, so we can only
provide it's name }
res := STATUS_SUCCESS;
attr := faSysFile;
end;
end;
end;
FreeNtStr(ntstr);
if not NT_SUCCESS(res) then
Exit;
time := 0;
if isfileobj then begin
res := NtQueryInformationFile(h, @iostatus, @fileinfo, SizeOf(fileinfo),
FileBasicInformation);
if NT_SUCCESS(res) then begin
time := NtToDosTime(fileinfo.LastWriteTime);
{ copy file attributes? }
end;
end else begin
res := NtQueryObject(h, ObjectBasicInformation, @objinfo, SizeOf(objinfo),
Nil);
if NT_SUCCESS(res) then begin
time := NtToDosTime(objinfo.CreateTime);
{ what about attributes? }
end;
end;
if (attr and not f.FindData.SearchAttr) = 0 then begin
f.Name := filename;
f.Attr := attr;
f.Size := 0;
{$ifndef FPUNONE}
if time = 0 then
{ for now we use "Now" as a fall back; ideally this should be the system
start time }
f.Time := DateTimeToFileDate(Now)
else
f.Time := time;
{$endif}
Result := True;
end else
Result := False;
NtClose(h);
end;
procedure FindClose(var F: TSearchrec);
begin
{ empty }
if f.FindData.Handle <> 0 then
NtClose(f.FindData.Handle);
end;
function FindNext(var Rslt: TSearchRec): LongInt;
{
re-opens dir if not already in array and calls FindGetFileInfo
}
Var
DirName : String;
FName,
SName : string;
Found,
Finished : boolean;
ntstr: UNICODE_STRING;
objattr: OBJECT_ATTRIBUTES;
buf: array of WideChar;
len: LongWord;
res: NTSTATUS;
i: LongInt;
dirinfo: POBJECT_DIRECTORY_INFORMATION;
filedirinfo: PFILE_DIRECTORY_INFORMATION;
pc: PChar;
name: AnsiString;
iostatus: IO_STATUS_BLOCK;
begin
{ TODO : relative directories }
Result := -1;
{ SearchSpec='' means that there were no wild cards, so only one file to
find.
}
if Rslt.FindData.SearchSpec = '' then
Exit;
{ relative directories not supported for now }
if Rslt.FindData.NamePos = 0 then
Exit;
if Rslt.FindData.Handle = 0 then begin
if Rslt.FindData.NamePos > 1 then
name := Copy(Rslt.FindData.SearchSpec, 1, Rslt.FindData.NamePos - 1)
else
if Rslt.FindData.NamePos = 1 then
name := Copy(Rslt.FindData.SearchSpec, 1, 1)
else
name := Rslt.FindData.SearchSpec;
AnsiStrToNtStr(name, ntstr);
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
res := NtOpenDirectoryObject(@Rslt.FindData.Handle,
DIRECTORY_QUERY or DIRECTORY_TRAVERSE, @objattr);
if not NT_SUCCESS(res) then begin
if res = STATUS_OBJECT_TYPE_MISMATCH then
res := NtOpenFile(@Rslt.FindData.Handle,
FILE_LIST_DIRECTORY or NT_SYNCHRONIZE, @objattr,
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
end else
Rslt.FindData.IsDirObj := True;
FreeNTStr(ntstr);
if not NT_SUCCESS(res) then
Exit;
end;
{ if (NTFindData^.SearchType = 0) and
(NTFindData^.Dirptr = Nil) then
begin
If NTFindData^.NamePos = 0 Then
DirName:='./'
Else
DirName:=Copy(NTFindData^.SearchSpec,1,NTFindData^.NamePos);
NTFindData^.DirPtr := fpopendir(Pchar(pointer(DirName)));
end;}
SName := Copy(Rslt.FindData.SearchSpec, Rslt.FindData.NamePos + 1,
Length(Rslt.FindData.SearchSpec));
Found := False;
Finished := not NT_SUCCESS(Rslt.FindData.LastRes)
or (Rslt.FindData.LastRes = STATUS_NO_MORE_ENTRIES);
SetLength(buf, 200);
dirinfo := @buf[0];
filedirinfo := @buf[0];
while not Finished do begin
if Rslt.FindData.IsDirObj then
res := NtQueryDirectoryObject(Rslt.FindData.Handle, @buf[0],
Length(buf) * SizeOf(buf[0]), True, False,
@Rslt.FindData.Context, @len)
else
res := NtQueryDirectoryFile(Rslt.FindData.Handle, 0, Nil, Nil, @iostatus,
@buf[0], Length(buf) * SizeOf(buf[0]), FileDirectoryInformation,
True, Nil, False);
if Rslt.FindData.IsDirObj then begin
Finished := (res = STATUS_NO_MORE_ENTRIES)
or (res = STATUS_NO_MORE_FILES)
or not NT_SUCCESS(res);
Rslt.FindData.LastRes := res;
if dirinfo^.Name.Length > 0 then begin
SetLength(FName, dirinfo^.Name.Length div 2);
pc := PChar(FName);
for i := 0 to dirinfo^.Name.Length div 2 - 1 do begin
if dirinfo^.Name.Buffer[i] < #256 then
pc^ := AnsiChar(Byte(dirinfo^.Name.Buffer[i]))
else
pc^ := '?';
pc := pc + 1;
end;
{$ifdef debug_findnext}
Write(FName, ' (');
for i := 0 to dirinfo^.TypeName.Length div 2 - 1 do
if dirinfo^.TypeName.Buffer[i] < #256 then
Write(AnsiChar(Byte(dirinfo^.TypeName.Buffer[i])))
else
Write('?');
Writeln(')');
{$endif debug_findnext}
end else
FName := '';
end else begin
SetLength(FName, filedirinfo^.FileNameLength div 2);
pc := PChar(FName);
for i := 0 to filedirinfo^.FileNameLength div 2 - 1 do begin
if filedirinfo^.FileName[i] < #256 then
pc^ := AnsiChar(Byte(filedirinfo^.FileName[i]))
else
pc^ := '?';
pc := pc + 1;
end;
end;
if FName = '' then
Finished := True
else begin
if FNMatch(SName, FName) then begin
Found := FindGetFileInfo(Copy(Rslt.FindData.SearchSpec, 1,
Rslt.FindData.NamePos) + FName, Rslt);
if Found then begin
Result := 0;
Exit;
end;
end;
end;
end;
end;
function FindFirst(const Path: String; Attr: Longint; out Rslt: TSearchRec): Longint;
{
opens dir and calls FindNext if needed.
}
Begin
Result := -1;
FillChar(Rslt, SizeOf(Rslt), 0);
if Path = '' then
Exit;
Rslt.FindData.SearchAttr := Attr;
{Wildcards?}
if (Pos('?', Path) = 0) and (Pos('*', Path) = 0) then begin
if FindGetFileInfo(Path, Rslt) then
Result := 0;
end else begin
{Create Info}
Rslt.FindData.SearchSpec := Path;
Rslt.FindData.NamePos := Length(Rslt.FindData.SearchSpec);
while (Rslt.FindData.NamePos > 0)
and (Rslt.FindData.SearchSpec[Rslt.FindData.NamePos] <> DirectorySeparator)
do
Dec(Rslt.FindData.NamePos);
Result := FindNext(Rslt);
end;
if Result <> 0 then
FindClose(Rslt);
end;