mirror of
https://github.com/CloudDelphi/Virtual-File-System
synced 2025-12-19 09:53:54 +01:00
Added crucial VFS modules and a few working tests for them
This commit is contained in:
parent
933e714566
commit
65e182822a
1
Tests/Fs/503.html
Normal file
1
Tests/Fs/503.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
Server temporarily non accessible.
|
||||||
BIN
Tests/Fs/Mods/A/eula.1028.txt
Normal file
BIN
Tests/Fs/Mods/A/eula.1028.txt
Normal file
Binary file not shown.
0
Tests/Fs/Mods/A/vcredist.bmp
Normal file
0
Tests/Fs/Mods/A/vcredist.bmp
Normal file
BIN
Tests/Fs/Mods/B/vcredist.bmp
Normal file
BIN
Tests/Fs/Mods/B/vcredist.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
2
Tests/Fs/Mods/FullyVirtual/Hobbots/mms.cfg
Normal file
2
Tests/Fs/Mods/FullyVirtual/Hobbots/mms.cfg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SilentAutoUpdateEnable=1
|
||||||
|
AutoUpdateDisable=0
|
||||||
13
Tests/Fs/default
Normal file
13
Tests/Fs/default
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
server {
|
||||||
|
#listen 80 default;
|
||||||
|
#server_name localhost;
|
||||||
|
#deny all;
|
||||||
|
access_log off;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_pass http://127.0.0.1:8080;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
Tests/VfsApiDiggerTest.pas
Normal file
49
Tests/VfsApiDiggerTest.pas
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
unit VfsApiDiggerTest;
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, SysUtils, TestFramework,
|
||||||
|
Utils, WinUtils, DataLib,
|
||||||
|
VfsApiDigger;
|
||||||
|
|
||||||
|
type
|
||||||
|
TestApiDigger = class (TTestCase)
|
||||||
|
published
|
||||||
|
procedure DetermineRealApiAddress;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
procedure TestApiDigger.DetermineRealApiAddress;
|
||||||
|
type
|
||||||
|
TGetCurrentProcessId = function (): integer; stdcall;
|
||||||
|
|
||||||
|
var
|
||||||
|
Kernel32Handle: THandle;
|
||||||
|
KernelBaseHandle: THandle;
|
||||||
|
NormalProc: TGetCurrentProcessId;
|
||||||
|
RealProc: TGetCurrentProcessId;
|
||||||
|
TestProc: TGetCurrentProcessId;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Kernel32Handle := Windows.GetModuleHandle('kernel32.dll');
|
||||||
|
KernelBaseHandle := Windows.GetModuleHandle('kernelbase.dll');
|
||||||
|
|
||||||
|
if (Kernel32Handle <> 0) and (KernelBaseHandle <> 0) then begin
|
||||||
|
NormalProc := Windows.GetProcAddress(Kernel32Handle, 'GetCurrentProcessId');
|
||||||
|
RealProc := Windows.GetProcAddress(KernelBaseHandle, 'GetCurrentProcessId');
|
||||||
|
|
||||||
|
if (@NormalProc <> nil) and (@RealProc <> nil) then begin
|
||||||
|
VfsApiDigger.FindOutRealSystemApiAddrs([Kernel32Handle]);
|
||||||
|
TestProc := VfsApiDigger.GetRealProcAddress(Kernel32Handle, 'GetCurrentProcessId');
|
||||||
|
Check(@TestProc = @RealProc, Format('Failed to get real api address. Normal address: %x, Real address: %x, Got address: %x', [Int(@NormalProc), Int(@RealProc), Int(@TestProc)]));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
RegisterTest(TestApiDigger.Suite);
|
||||||
|
end.
|
||||||
69
Tests/VfsBaseTest.pas
Normal file
69
Tests/VfsBaseTest.pas
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
unit VfsBaseTest;
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, TestFramework,
|
||||||
|
Utils, WinUtils,
|
||||||
|
VfsUtils, VfsBase;
|
||||||
|
|
||||||
|
type
|
||||||
|
TestBase = class (TTestCase)
|
||||||
|
protected
|
||||||
|
procedure SetUp; override;
|
||||||
|
procedure TearDown; override;
|
||||||
|
|
||||||
|
published
|
||||||
|
procedure TestVirtualDirMapping;
|
||||||
|
end;
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
procedure TestBase.SetUp;
|
||||||
|
begin
|
||||||
|
VfsBase.ResetVfs();
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestBase.TearDown;
|
||||||
|
begin
|
||||||
|
VfsBase.ResetVfs();
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestBase.TestVirtualDirMapping;
|
||||||
|
var
|
||||||
|
DirListing: TDirListing;
|
||||||
|
DirInfo: TNativeFileInfo;
|
||||||
|
RootDir: string;
|
||||||
|
FileInfo: TFileInfo;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
DirListing := TDirListing.Create;
|
||||||
|
FileInfo := nil;
|
||||||
|
// * * * * * //
|
||||||
|
RootDir := SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs';
|
||||||
|
VfsBase.MapDir(RootDir, RootDir + '\Mods\B', DONT_OVERWRITE_EXISTING);
|
||||||
|
VfsBase.MapDir(RootDir, RootDir + '\Mods\A', DONT_OVERWRITE_EXISTING);
|
||||||
|
VfsBase.RunVfs(SORT_FIFO);
|
||||||
|
VfsBase.GetVfsDirInfo(RootDir, '*', DirInfo, DirListing);
|
||||||
|
|
||||||
|
DirListing.Rewind;
|
||||||
|
Check(DirListing.GetDebugDump() = 'vcredist.bmp'#13#10'eula.1028.txt', 'Invalid virtual directoring listing. Got: ' + DirListing.GetDebugDump());
|
||||||
|
|
||||||
|
DirListing.Rewind;
|
||||||
|
|
||||||
|
for i := 0 to DirListing.Count - 1 do begin
|
||||||
|
DirListing.GetNextItem(FileInfo);
|
||||||
|
|
||||||
|
if FileInfo.Data.FileName = 'vcredist.bmp' then begin
|
||||||
|
Check(FileInfo.Data.GetFileSize() = 5686, 'File from A mod must not override same file from B mod');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(DirListing);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
RegisterTest(TestBase.Suite);
|
||||||
|
end.
|
||||||
69
Tests/VfsOpenFilesTest.pas
Normal file
69
Tests/VfsOpenFilesTest.pas
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
unit VfsOpenFilesTest;
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, SysUtils, TestFramework,
|
||||||
|
Utils, WinUtils, DataLib,
|
||||||
|
VfsBase, VfsUtils, VfsOpenFiles;
|
||||||
|
|
||||||
|
type
|
||||||
|
TestOpenFiles = class (TTestCase)
|
||||||
|
protected
|
||||||
|
procedure SetUp; override;
|
||||||
|
procedure TearDown; override;
|
||||||
|
|
||||||
|
published
|
||||||
|
procedure GetCombinedDirListing;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
procedure TestOpenFiles.SetUp;
|
||||||
|
begin
|
||||||
|
VfsBase.ResetVfs();
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestOpenFiles.TearDown;
|
||||||
|
begin
|
||||||
|
VfsBase.ResetVfs();
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TestOpenFiles.GetCombinedDirListing;
|
||||||
|
const
|
||||||
|
VALID_FULLY_VIRT_DIR_LISTING = 'mms.cfg'#13#10'.'#13#10'..';
|
||||||
|
VALID_COMBINED_LISTING = 'Hobbots'#13#10'vcredist.bmp'#13#10'.'#13#10'..'#13#10'503.html'#13#10'default'#13#10'Mods';
|
||||||
|
|
||||||
|
var
|
||||||
|
{O} OpenedFile: VfsOpenFiles.TOpenedFile;
|
||||||
|
DirPath: WideString;
|
||||||
|
RootDir: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
OpenedFile := nil;
|
||||||
|
// * * * * * //
|
||||||
|
RootDir := SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs';
|
||||||
|
VfsBase.MapDir(RootDir, RootDir + '\Mods\FullyVirtual', DONT_OVERWRITE_EXISTING);
|
||||||
|
VfsBase.MapDir(RootDir, RootDir + '\Mods\B', DONT_OVERWRITE_EXISTING);
|
||||||
|
VfsBase.RunVfs(SORT_FIFO);
|
||||||
|
|
||||||
|
DirPath := VfsUtils.NormalizePath(RootDir + '\Hobbots');
|
||||||
|
OpenedFile := VfsOpenFiles.TOpenedFile.Create(777, DirPath);
|
||||||
|
OpenedFile.FillDirListing('*');
|
||||||
|
Check(OpenedFile.DirListing <> nil, 'Directory listing must be assigned');
|
||||||
|
Check(OpenedFile.DirListing.GetDebugDump() = VALID_FULLY_VIRT_DIR_LISTING, 'Invalid listing for fully virtual directory "' + DirPath + '". Got: ' + OpenedFile.DirListing.GetDebugDump());
|
||||||
|
FreeAndNil(OpenedFile);
|
||||||
|
|
||||||
|
OpenedFile := VfsOpenFiles.TOpenedFile.Create(888, RootDir);
|
||||||
|
OpenedFile.FillDirListing('*');
|
||||||
|
Check(OpenedFile.DirListing <> nil, 'Directory listing must be assigned');
|
||||||
|
Check(OpenedFile.DirListing.GetDebugDump() = VALID_COMBINED_LISTING, 'Invalid combined listing for directory "' + RootDir + '". Got: ' + OpenedFile.DirListing.GetDebugDump());
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(OpenedFile);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
RegisterTest(TestOpenFiles.Suite);
|
||||||
|
end.
|
||||||
68
Tests/VfsUtilsTest.pas
Normal file
68
Tests/VfsUtilsTest.pas
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
unit VfsUtilsTest;
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, TestFramework,
|
||||||
|
Utils, WinUtils, DataLib,
|
||||||
|
VfsUtils;
|
||||||
|
|
||||||
|
type
|
||||||
|
TestUtils = class (TTestCase)
|
||||||
|
published
|
||||||
|
procedure TestNativeDirScanning;
|
||||||
|
procedure TestGetDirectoryListing;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
procedure TestUtils.TestNativeDirScanning;
|
||||||
|
var
|
||||||
|
RootDir: string;
|
||||||
|
FileInfo: VfsUtils.TNativeFileInfo;
|
||||||
|
DirItems: DataLib.TStrList;
|
||||||
|
DirContents: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
DirItems := DataLib.NewStrList(not Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
||||||
|
// * * * * * //
|
||||||
|
RootDir := SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs';
|
||||||
|
|
||||||
|
with SysScanDir(RootDir, '*') do begin
|
||||||
|
while IterNext(FileInfo.FileName, @FileInfo.Base) do begin
|
||||||
|
DirItems.Add(FileInfo.FileName);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
DirItems.Sort;
|
||||||
|
DirContents := DirItems.ToText(#13#10);
|
||||||
|
Check(DirContents = '.'#13#10'..'#13#10'503.html'#13#10'default'#13#10'Mods', 'Invalid directory listing. Got:'#13#10 + DirContents);
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(DirItems);
|
||||||
|
end; // .procedure TestNativeDirScanning
|
||||||
|
|
||||||
|
procedure TestUtils.TestGetDirectoryListing;
|
||||||
|
var
|
||||||
|
DirListing: VfsUtils.TDirListing;
|
||||||
|
Exclude: DataLib.TDict {of not nil};
|
||||||
|
RootDir: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
DirListing := VfsUtils.TDirListing.Create;
|
||||||
|
Exclude := DataLib.NewDict(not Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
||||||
|
// * * * * * //
|
||||||
|
RootDir := SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs';
|
||||||
|
Exclude[VfsUtils.WideStrToCaselessKey('..')] := Ptr(1);
|
||||||
|
|
||||||
|
VfsUtils.GetDirectoryListing(RootDir, '*', Exclude, DirListing);
|
||||||
|
Check(DirListing.GetDebugDump() = '.'#13#10'503.html'#13#10'default'#13#10'Mods', 'Invalid directory listing. Got:'#13#10 + DirListing.GetDebugDump());
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(DirListing);
|
||||||
|
SysUtils.FreeAndNil(Exclude);
|
||||||
|
end; // .procedure TestUtils.TestGetDirectoryListing
|
||||||
|
|
||||||
|
begin
|
||||||
|
RegisterTest(TestUtils.Suite);
|
||||||
|
end.
|
||||||
2
Vfs.dproj.local
Normal file
2
Vfs.dproj.local
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<BorlandProject/>
|
||||||
10
Vfs.drc
Normal file
10
Vfs.drc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* VER185
|
||||||
|
Generated by the CodeGear Delphi Pascal Compiler
|
||||||
|
because -GD or --drc was supplied to the compiler.
|
||||||
|
|
||||||
|
This file contains compiler-generated resources that
|
||||||
|
were bound to the executable.
|
||||||
|
If this file is empty, then no compiler-generated
|
||||||
|
resources were bound to the produced executable.
|
||||||
|
*/
|
||||||
|
|
||||||
164
VfsApiDigger.pas
Normal file
164
VfsApiDigger.pas
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
unit VfsApiDigger;
|
||||||
|
(*
|
||||||
|
Description: Provides means for detecting real WinAPI functions addresses, bypassing proxy dlls and
|
||||||
|
other low level code routines.
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, Windows,
|
||||||
|
Utils, DataLib, PatchForge;
|
||||||
|
|
||||||
|
|
||||||
|
(* Determines real exported API addresses for all specified DLL handles. If DLL imports function
|
||||||
|
with the same name, as the exported one, then imported one is treated as real function.
|
||||||
|
Example: kernel32.ReadProcessMemory can be a bridge to imported kernelbase.ReadProcessMemory.
|
||||||
|
If DLL handle was processed earlier, it's skipped *)
|
||||||
|
procedure FindOutRealSystemApiAddrs (const DllHandles: array of integer);
|
||||||
|
|
||||||
|
(* Returns real code address, bypassing possibly nested simple redirection stubs like JMP [...] or JMP XXX. *)
|
||||||
|
function GetRealAddress (CodeOrRedirStub: pointer): {n} pointer;
|
||||||
|
|
||||||
|
(* Enhanced version of kernel32.GetProcAddress, traversing bridge chains and using info, gained by FindOutRealSystemApiAddrs earlier *)
|
||||||
|
function GetRealProcAddress (DllHandle: integer; const ProcName: string): {n} pointer;
|
||||||
|
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
(* Map of DLL handle => API name => Real api address *)
|
||||||
|
{O} DllRealApiAddrs: {O} TObjDict {OF TDict};
|
||||||
|
|
||||||
|
|
||||||
|
procedure FindOutRealSystemApiAddrs (const DllHandles: array of integer);
|
||||||
|
const
|
||||||
|
PE_SIGNATURE_LEN = 4;
|
||||||
|
|
||||||
|
type
|
||||||
|
PImageImportDirectory = ^TImageImportDirectory;
|
||||||
|
TImageImportDirectory = packed record
|
||||||
|
RvaImportLookupTable: integer;
|
||||||
|
TimeDateStamp: integer;
|
||||||
|
ForwarderChain: integer;
|
||||||
|
RvaModuleName: integer;
|
||||||
|
RvaImportAddressTable: integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
PHintName = ^THintName;
|
||||||
|
THintName = packed record
|
||||||
|
Hint: word;
|
||||||
|
Name: array [0..MAXLONGINT - 5] of char;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
ImportDirInfo: PImageDataDirectory;
|
||||||
|
ImportDir: PImageImportDirectory;
|
||||||
|
ImportLookupTable: Utils.PEndlessIntArr;
|
||||||
|
ImportAddrTable: Utils.PEndlessIntArr;
|
||||||
|
DllApiRedirs: {U} TDict {of pointer};
|
||||||
|
DllHandle: integer;
|
||||||
|
i, j: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
ImportDirInfo := nil;
|
||||||
|
ImportDir := nil;
|
||||||
|
ImportLookupTable := nil;
|
||||||
|
ImportAddrTable := nil;
|
||||||
|
DllApiRedirs := nil;
|
||||||
|
// * * * * * //
|
||||||
|
for i := 0 to high(DllHandles) do begin
|
||||||
|
DllHandle := DllHandles[i];
|
||||||
|
ImportDirInfo := @PImageOptionalHeader(DllHandle + PImageDosHeader(DllHandle)._lfanew + PE_SIGNATURE_LEN + sizeof(TImageFileHeader)).DataDirectory[1];
|
||||||
|
DllApiRedirs := DllRealApiAddrs[Ptr(DllHandle)];
|
||||||
|
|
||||||
|
if DllApiRedirs = nil then begin
|
||||||
|
DllApiRedirs := DataLib.NewDict(NOT Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
||||||
|
DllRealApiAddrs[Ptr(DllHandle)] := DllApiRedirs;
|
||||||
|
|
||||||
|
// Found valid import directory in Win32 PE
|
||||||
|
if ((ImportDirInfo.Size > 0) and (ImportDirInfo.VirtualAddress <> 0)) then begin
|
||||||
|
ImportDir := pointer(DllHandle + integer(ImportDirInfo.VirtualAddress));
|
||||||
|
|
||||||
|
while ImportDir.RvaImportLookupTable <> 0 do begin
|
||||||
|
ImportLookupTable := pointer(DllHandle + ImportDir.RvaImportLookupTable);
|
||||||
|
ImportAddrTable := pointer(DllHandle + ImportDir.RvaImportAddressTable);
|
||||||
|
|
||||||
|
j := 0;
|
||||||
|
|
||||||
|
while (j >= 0) and (ImportLookupTable[j] <> 0) do begin
|
||||||
|
if ImportLookupTable[j] > 0 then begin
|
||||||
|
DllApiRedirs[pchar(@PHintName(DllHandle + ImportLookupTable[j]).Name)] := Ptr(ImportAddrTable[j]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Inc(j);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Inc(ImportDir);
|
||||||
|
end; // .while
|
||||||
|
end; // .if
|
||||||
|
end; // .if
|
||||||
|
end; // .for
|
||||||
|
end; // .procedure FindOutRealSystemApiAddrs
|
||||||
|
|
||||||
|
function GetRealAddress (CodeOrRedirStub: pointer): {n} pointer;
|
||||||
|
const
|
||||||
|
MAX_DEPTH = 100;
|
||||||
|
|
||||||
|
var
|
||||||
|
Depth: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
{!} Assert(CodeOrRedirStub <> nil);
|
||||||
|
result := CodeOrRedirStub;
|
||||||
|
Depth := 0;
|
||||||
|
|
||||||
|
while Depth < MAX_DEPTH do begin
|
||||||
|
// JMP DWORD [PTR]
|
||||||
|
if pword(result)^ = PatchForge.OPCODE_JMP_PTR_CONST32 then begin
|
||||||
|
result := ppointer(integer(result) + sizeof(word))^;
|
||||||
|
// JXX SHORT CONST8
|
||||||
|
end else if PatchForge.IsShortJumpConst8Opcode(pbyte(result)^) then begin
|
||||||
|
result := pointer(integer(result) + sizeof(byte) + pshortint(integer(result) + sizeof(byte))^);
|
||||||
|
// JMP NEAR CONST32
|
||||||
|
end else if pbyte(result)^ = PatchForge.OPCODE_JMP_CONST32 then begin
|
||||||
|
result := pointer(integer(result) + sizeof(PatchForge.TJumpCall32Rec) + pinteger(integer(result) + sizeof(byte))^);
|
||||||
|
// JXX (conditional) NEAR CONST32
|
||||||
|
end else if PatchForge.IsNearJumpConst32Opcode(pword(result)^) then begin
|
||||||
|
result := pointer(integer(result) + sizeof(word) + sizeof(integer) + pinteger(integer(result) + sizeof(word))^);
|
||||||
|
// Regular code
|
||||||
|
end else begin
|
||||||
|
break;
|
||||||
|
end; // .else
|
||||||
|
|
||||||
|
Inc(Depth);
|
||||||
|
end; // .while
|
||||||
|
end; // .function GetRealAddress
|
||||||
|
|
||||||
|
function GetRealProcAddress (DllHandle: integer; const ProcName: string): {n} pointer;
|
||||||
|
var
|
||||||
|
{Un} DllApiRedirs: {U} TDict {OF pointer};
|
||||||
|
|
||||||
|
begin
|
||||||
|
DllApiRedirs := DllRealApiAddrs[Ptr(DllHandle)];
|
||||||
|
result := nil;
|
||||||
|
// * * * * * //
|
||||||
|
|
||||||
|
if DllApiRedirs <> nil then begin
|
||||||
|
result := DllApiRedirs[ProcName];
|
||||||
|
end;
|
||||||
|
|
||||||
|
if result = nil then begin
|
||||||
|
result := Windows.GetProcAddress(DllHandle, pchar(ProcName));
|
||||||
|
end;
|
||||||
|
|
||||||
|
if result <> nil then begin
|
||||||
|
result := GetRealAddress(result);
|
||||||
|
end;
|
||||||
|
end; // .function GetRealProcAddress
|
||||||
|
|
||||||
|
begin
|
||||||
|
DllRealApiAddrs := DataLib.NewObjDict(Utils.OWNS_ITEMS);
|
||||||
|
end.
|
||||||
123
VfsBase.pas
123
VfsBase.pas
@ -22,6 +22,9 @@ type
|
|||||||
TList = Lists.TList;
|
TList = Lists.TList;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
OVERWRITE_EXISTING = true;
|
||||||
|
DONT_OVERWRITE_EXISTING = false;
|
||||||
|
|
||||||
AUTO_PRIORITY = MAXLONGINT div 2;
|
AUTO_PRIORITY = MAXLONGINT div 2;
|
||||||
INITIAL_OVERWRITING_PRIORITY = AUTO_PRIORITY + 1;
|
INITIAL_OVERWRITING_PRIORITY = AUTO_PRIORITY + 1;
|
||||||
INITIAL_ADDING_PRIORITY = AUTO_PRIORITY - 1;
|
INITIAL_ADDING_PRIORITY = AUTO_PRIORITY - 1;
|
||||||
@ -76,11 +79,24 @@ type
|
|||||||
procedure RestoreVfsForThread;
|
procedure RestoreVfsForThread;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
(* Global VFS access synchronizer *)
|
||||||
|
VfsCritSection: Concur.TCritSection;
|
||||||
|
|
||||||
|
|
||||||
function GetThreadVfsDisabler: TThreadVfsDisabler;
|
function GetThreadVfsDisabler: TThreadVfsDisabler;
|
||||||
procedure RunVfs (DirListingOrder: TDirListingSortType);
|
|
||||||
|
(* Runs VFS. Higher level API must install hooks in VfsCritSection protected area *)
|
||||||
|
function RunVfs (DirListingOrder: TDirListingSortType): boolean;
|
||||||
|
|
||||||
|
(* Stops VFS and clears all mappings *)
|
||||||
function ResetVfs: boolean;
|
function ResetVfs: boolean;
|
||||||
|
|
||||||
|
(* Returns real path for VFS item by its absolute virtual path or empty string. Optionally returns file info structure *)
|
||||||
|
function GetVfsItemRealPath (const AbsVirtPath: WideString; {n} FileInfo: PNativeFileInfo = nil): WideString;
|
||||||
|
|
||||||
|
(* Returns virtual directory info. Adds virtual entries to specified directory listing container *)
|
||||||
|
function GetVfsDirInfo (const AbsVirtPath, Mask: WideString; {OUT} var DirInfo: TNativeFileInfo; DirListing: TDirListing): boolean;
|
||||||
|
|
||||||
(* Maps real directory contents to virtual path. Target must exist for success *)
|
(* Maps real directory contents to virtual path. Target must exist for success *)
|
||||||
function MapDir (const VirtPath, RealPath: WideString; OverwriteExisting: boolean; Flags: integer = 0): boolean;
|
function MapDir (const VirtPath, RealPath: WideString; OverwriteExisting: boolean; Flags: integer = 0): boolean;
|
||||||
@ -97,9 +113,6 @@ var
|
|||||||
*)
|
*)
|
||||||
{O} VfsItems: {O} TDict {OF TVfsItem};
|
{O} VfsItems: {O} TDict {OF TVfsItem};
|
||||||
|
|
||||||
(* Global VFS access synchronizer *)
|
|
||||||
VfsCritSection: Concur.TCritSection;
|
|
||||||
|
|
||||||
(* Global VFS state indicator. If false, all VFS search operations must fail *)
|
(* Global VFS state indicator. If false, all VFS search operations must fail *)
|
||||||
VfsIsRunning: boolean = false;
|
VfsIsRunning: boolean = false;
|
||||||
|
|
||||||
@ -166,32 +179,6 @@ begin
|
|||||||
VfsCritSection.Leave;
|
VfsCritSection.Leave;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* Packs lower cased WideString bytes into AnsiString buffer *)
|
|
||||||
function WideStrToCaselessKey (const Str: WideString): string;
|
|
||||||
var
|
|
||||||
ProcessedPath: WideString;
|
|
||||||
|
|
||||||
begin
|
|
||||||
result := '';
|
|
||||||
|
|
||||||
if Str <> '' then begin
|
|
||||||
ProcessedPath := StrLib.WideLowerCase(Str);
|
|
||||||
SetLength(result, Length(ProcessedPath) * sizeof(ProcessedPath[1]) div sizeof(result[1]));
|
|
||||||
Utils.CopyMem(Length(result) * sizeof(result[1]), PWideChar(ProcessedPath), PChar(result));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
(* The opposite of WideStrToKey *)
|
|
||||||
function UnpackPath (const PackedPath: string): WideString;
|
|
||||||
begin
|
|
||||||
result := '';
|
|
||||||
|
|
||||||
if PackedPath <> '' then begin
|
|
||||||
SetLength(result, Length(PackedPath) * sizeof(PackedPath[1]) div sizeof(result[1]));
|
|
||||||
Utils.CopyMem(Length(result) * sizeof(result[1]), pchar(PackedPath), PWideChar(result));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function CompareVfsItemsByPriorityDescAndNameAsc (Item1, Item2: integer): integer;
|
function CompareVfsItemsByPriorityDescAndNameAsc (Item1, Item2: integer): integer;
|
||||||
begin
|
begin
|
||||||
result := TVfsItem(Item2).Priority - TVfsItem(Item1).Priority;
|
result := TVfsItem(Item2).Priority - TVfsItem(Item1).Priority;
|
||||||
@ -266,7 +253,7 @@ begin
|
|||||||
// * * * * * //
|
// * * * * * //
|
||||||
with DataLib.IterateDict(VfsItems) do begin
|
with DataLib.IterateDict(VfsItems) do begin
|
||||||
while IterNext() do begin
|
while IterNext() do begin
|
||||||
AbsDirPath := StrLib.ExtractDirPathW(UnpackPath(IterKey));
|
AbsDirPath := StrLib.ExtractDirPathW(CaselessKeyToWideStr(IterKey));
|
||||||
|
|
||||||
if FindVfsItemByNormalizedPath(AbsDirPath, DirVfsItem) then begin
|
if FindVfsItemByNormalizedPath(AbsDirPath, DirVfsItem) then begin
|
||||||
DirVfsItem.Children.Add(IterValue);
|
DirVfsItem.Children.Add(IterValue);
|
||||||
@ -275,8 +262,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end; // .procedure BuildVfsItemsTree
|
end; // .procedure BuildVfsItemsTree
|
||||||
|
|
||||||
procedure RunVfs (DirListingOrder: TDirListingSortType);
|
function RunVfs (DirListingOrder: TDirListingSortType): boolean;
|
||||||
begin
|
begin
|
||||||
|
result := not DisableVfsForThisThread;
|
||||||
|
|
||||||
|
if result then begin
|
||||||
with VfsCritSection do begin
|
with VfsCritSection do begin
|
||||||
Enter;
|
Enter;
|
||||||
|
|
||||||
@ -288,18 +278,77 @@ begin
|
|||||||
|
|
||||||
Leave;
|
Leave;
|
||||||
end;
|
end;
|
||||||
end; // .procedure RunVfs
|
end;
|
||||||
|
end; // .function RunVfs
|
||||||
|
|
||||||
function ResetVfs: boolean;
|
function ResetVfs: boolean;
|
||||||
begin
|
begin
|
||||||
|
result := not DisableVfsForThisThread;
|
||||||
|
|
||||||
|
if result then begin
|
||||||
|
with VfsCritSection do begin
|
||||||
|
Enter;
|
||||||
|
VfsIsRunning := false;
|
||||||
|
VfsItems.Clear();
|
||||||
|
Leave;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
(* Returns real path for vfs item by its absolute virtual path or empty string. Optionally returns file info structure *)
|
||||||
|
function GetVfsItemRealPath (const AbsVirtPath: WideString; {n} FileInfo: PNativeFileInfo = nil): WideString;
|
||||||
|
var
|
||||||
|
{n} VfsItem: TVfsItem;
|
||||||
|
|
||||||
|
begin
|
||||||
|
VfsItem := nil;
|
||||||
|
result := '';
|
||||||
|
// * * * * * //
|
||||||
|
if EnterVfs then begin
|
||||||
|
if FindVfsItemByNormalizedPath(AbsVirtPath, VfsItem) then begin
|
||||||
|
result := VfsItem.RealPath;
|
||||||
|
|
||||||
|
if FileInfo <> nil then begin
|
||||||
|
FileInfo^ := VfsItem.Info;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
LeaveVfs;
|
||||||
|
end; // .if
|
||||||
|
end; // .function GetVfsItemRealPath
|
||||||
|
|
||||||
|
function GetVfsDirInfo (const AbsVirtPath, Mask: WideString; {OUT} var DirInfo: TNativeFileInfo; DirListing: TDirListing): boolean;
|
||||||
|
var
|
||||||
|
{n} VfsItem: TVfsItem;
|
||||||
|
NormalizedMask: WideString;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
{!} Assert(DirListing <> nil);
|
||||||
|
VfsItem := nil;
|
||||||
|
// * * * * * //
|
||||||
result := EnterVfs;
|
result := EnterVfs;
|
||||||
|
|
||||||
if result then begin
|
if result then begin
|
||||||
VfsIsRunning := false;
|
result := FindVfsItemByNormalizedPath(AbsVirtPath, VfsItem) and VfsItem.IsDir;
|
||||||
VfsItems.Clear();
|
|
||||||
|
if result then begin
|
||||||
|
DirInfo := VfsItem.Info;
|
||||||
|
|
||||||
|
if VfsItem.Children <> nil then begin
|
||||||
|
NormalizedMask := StrLib.WideLowerCase(Mask);
|
||||||
|
|
||||||
|
for i := 0 to VfsItem.Children.Count - 1 do begin
|
||||||
|
if StrLib.MatchW(TVfsItem(VfsItem.Children[i]).SearchName, NormalizedMask) then begin
|
||||||
|
DirListing.AddItem(@TVfsItem(VfsItem.Children[i]).Info);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end; // .if
|
||||||
|
end; // .if
|
||||||
|
|
||||||
LeaveVfs;
|
LeaveVfs;
|
||||||
end;
|
end; // .if
|
||||||
end;
|
end; // .function GetVfsDirInfo
|
||||||
|
|
||||||
procedure CopyFileInfoWithoutNames (var Src, Dest: WinNative.FILE_ID_BOTH_DIR_INFORMATION);
|
procedure CopyFileInfoWithoutNames (var Src, Dest: WinNative.FILE_ID_BOTH_DIR_INFORMATION);
|
||||||
begin
|
begin
|
||||||
|
|||||||
172
VfsOpenFiles.pas
Normal file
172
VfsOpenFiles.pas
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
unit VfsOpenFiles;
|
||||||
|
(*
|
||||||
|
Author: Alexander Shostak aka Berserker aka EtherniDee.
|
||||||
|
Description: Provides concurrent storage for additional information for each file handle,
|
||||||
|
fully integrated to file handles life cycle.
|
||||||
|
The unit works independently of other VFS subsystems, guaranteeing relative paths
|
||||||
|
resolution capability (conversion of directory handle into directory path).
|
||||||
|
Most NT file APIs can work with pairs of [hDir, RelativePath] (@see WinNative.TObjectAttributes).
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
(***) interface (***)
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, SysUtils,
|
||||||
|
Utils, Concur, DataLib, StrLib,
|
||||||
|
VfsUtils, VfsBase;
|
||||||
|
|
||||||
|
type
|
||||||
|
(* Import *)
|
||||||
|
TVfsItem = VfsBase.TVfsItem;
|
||||||
|
|
||||||
|
type
|
||||||
|
(* Extra information for file handle. Working with structure must be protected by corresponding critical section *)
|
||||||
|
TOpenedFile = class
|
||||||
|
public
|
||||||
|
(* Handle for either virtual or real path *)
|
||||||
|
hFile: Windows.THandle;
|
||||||
|
|
||||||
|
(* Virtual path to file (path given to NtCreate API) *)
|
||||||
|
AbsPath: WideString;
|
||||||
|
|
||||||
|
(* Directory listing (both real and virtual children). Created on the fly on FillDirListing call *)
|
||||||
|
{On} DirListing: VfsUtils.TDirListing;
|
||||||
|
|
||||||
|
constructor Create (hFile: Windows.THandle; const AbsPath: WideString);
|
||||||
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
(* Makes complete directory listing, including real and virtual items. Does nothing if listing already exists *)
|
||||||
|
procedure FillDirListing (const Mask: WideString);
|
||||||
|
end; // .class TOpenedFile
|
||||||
|
|
||||||
|
var
|
||||||
|
OpenFilesCritSection: Concur.TCritSection;
|
||||||
|
|
||||||
|
|
||||||
|
(* Returns absolute virtual/real path to opened file by its handle in a thread-safe way. Empty string on failure. The result path is the one, passed to open file API *)
|
||||||
|
function GetOpenedFilePath (hFile: Windows.THandle): WideString;
|
||||||
|
|
||||||
|
(* Atomically replaces TOpenedFile record for given file handle *)
|
||||||
|
procedure SetOpenedFileInfo (hFile: Windows.THandle; {On} OpenedFile: TOpenedFile);
|
||||||
|
|
||||||
|
(* Atomically deletes TOpenedFile information by file handle *)
|
||||||
|
procedure DeleteOpenedFileInfo (hFile: Windows.THandle);
|
||||||
|
|
||||||
|
|
||||||
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
(* Map of all tracked file handles => TOpenedFile. Protected by corresponding critical section *)
|
||||||
|
{O} OpenedFiles: {O} TObjDict {of TOpenedFile};
|
||||||
|
|
||||||
|
|
||||||
|
constructor TOpenedFile.Create (hFile: Windows.THandle; const AbsPath: WideString);
|
||||||
|
begin
|
||||||
|
Self.hFile := hFile;
|
||||||
|
Self.AbsPath := AbsPath;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TOpenedFile.Destroy;
|
||||||
|
begin
|
||||||
|
FreeAndNil(Self.DirListing);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TOpenedFile.FillDirListing (const Mask: WideString);
|
||||||
|
var
|
||||||
|
{On} ExcludedItems: {U} TDict {OF not nil};
|
||||||
|
VfsItemFound: boolean;
|
||||||
|
NumVfsChildren: integer;
|
||||||
|
DirInfo: TNativeFileInfo;
|
||||||
|
ParentDirInfo: TNativeFileInfo;
|
||||||
|
DirItem: TFileInfo;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
ExcludedItems := nil;
|
||||||
|
// * * * * * //
|
||||||
|
if Self.DirListing <> nil then begin
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Self.DirListing := TDirListing.Create;
|
||||||
|
VfsItemFound := VfsBase.GetVfsDirInfo(Self.AbsPath, Mask, DirInfo, Self.DirListing);
|
||||||
|
ExcludedItems := DataLib.NewDict(not Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
||||||
|
|
||||||
|
if VfsItemFound then begin
|
||||||
|
while DirListing.GetNextItem(DirItem) do begin
|
||||||
|
ExcludedItems[WideStrToCaselessKey(DirItem.Data.FileName)] := Ptr(1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Self.DirListing.Rewind;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Add real items
|
||||||
|
NumVfsChildren := Self.DirListing.Count;
|
||||||
|
|
||||||
|
with VfsBase.GetThreadVfsDisabler do begin
|
||||||
|
DisableVfsForThread;
|
||||||
|
VfsUtils.GetDirectoryListing(Self.AbsPath, Mask, ExcludedItems, Self.DirListing);
|
||||||
|
RestoreVfsForThread;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// No real items added, maybe there is a need to add '.' and/or '..' manually
|
||||||
|
if VfsItemFound and (Self.DirListing.Count = NumVfsChildren) then begin
|
||||||
|
if StrLib.MatchW('.', Mask) then begin
|
||||||
|
Self.DirListing.AddItem(@DirInfo, '.');
|
||||||
|
end;
|
||||||
|
|
||||||
|
if StrLib.MatchW('..', Mask) and VfsUtils.GetFileInfo(Self.AbsPath + '\..', ParentDirInfo) then begin
|
||||||
|
Self.DirListing.AddItem(@ParentDirInfo, '..');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(ExcludedItems);
|
||||||
|
end; // .procedure TOpenedFile.FillDirListing
|
||||||
|
|
||||||
|
function GetOpenedFilePath (hFile: Windows.THandle): WideString;
|
||||||
|
var
|
||||||
|
{n} OpenedFile: TOpenedFile;
|
||||||
|
|
||||||
|
begin
|
||||||
|
OpenedFile := nil;
|
||||||
|
result := '';
|
||||||
|
// * * * * * //
|
||||||
|
with OpenFilesCritSection do begin
|
||||||
|
Enter;
|
||||||
|
|
||||||
|
OpenedFile := OpenedFiles[pointer(hFile)];
|
||||||
|
|
||||||
|
if OpenedFile <> nil then begin
|
||||||
|
result := OpenedFile.AbsPath;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Leave;
|
||||||
|
end;
|
||||||
|
end; // .function GetOpenedFilePath
|
||||||
|
|
||||||
|
procedure SetOpenedFileInfo (hFile: Windows.THandle; {On} OpenedFile: TOpenedFile);
|
||||||
|
begin
|
||||||
|
with OpenFilesCritSection do begin
|
||||||
|
Enter;
|
||||||
|
OpenedFiles[pointer(hFile)] := OpenedFile; OpenedFile := nil;
|
||||||
|
Leave;
|
||||||
|
end;
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(OpenedFile);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DeleteOpenedFileInfo (hFile: Windows.THandle);
|
||||||
|
begin
|
||||||
|
with OpenFilesCritSection do begin
|
||||||
|
Enter;
|
||||||
|
OpenedFiles.DeleteItem(pointer(hFile));
|
||||||
|
Leave;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
OpenFilesCritSection.Init;
|
||||||
|
OpenedFiles := DataLib.NewObjDict(Utils.OWNS_ITEMS);
|
||||||
|
end.
|
||||||
@ -2,8 +2,10 @@ program VfsTest;
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
TestFramework, GuiTestRunner,
|
TestFramework, GuiTestRunner,
|
||||||
VfsUtils, VfsBase, VfsDebug, VfsExport,
|
VfsUtils, VfsBase, VfsDebug,
|
||||||
VfsDebugTest;
|
VfsApiDigger, VfsExport, VfsOpenFiles,
|
||||||
|
VfsDebugTest, VfsUtilsTest, VfsBaseTest,
|
||||||
|
VfsApiDiggerTest, VfsOpenFilesTest;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
TGUITestRunner.RunRegisteredTests;
|
TGUITestRunner.RunRegisteredTests;
|
||||||
|
|||||||
@ -34,10 +34,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k100.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k100.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||||
<Excluded_Packages Name="$(BDS)\bin\dclofficexp100.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
<Excluded_Packages Name="$(BDS)\bin\dclofficexp100.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||||
</Excluded_Packages>
|
</Excluded_Packages></Delphi.Personality></BorlandProject></BorlandProject>
|
||||||
</Delphi.Personality></BorlandProject></BorlandProject>
|
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
<Import Project="$(MSBuildBinPath)\Borland.Delphi.Targets" />
|
<Import Project="$(MSBuildBinPath)\Borland.Delphi.Targets" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
109
VfsUtils.pas
109
VfsUtils.pas
@ -58,13 +58,15 @@ type
|
|||||||
|
|
||||||
function IsEnd: boolean;
|
function IsEnd: boolean;
|
||||||
procedure AddItem ({U} FileInfo: PNativeFileInfo; const FileName: WideString = ''; const InsertBefore: integer = High(integer));
|
procedure AddItem ({U} FileInfo: PNativeFileInfo; const FileName: WideString = ''; const InsertBefore: integer = High(integer));
|
||||||
function GetNextItem ({OUT} var Res: TFileInfo): boolean;
|
function GetNextItem ({OUT} var {U} Res: TFileInfo): boolean;
|
||||||
procedure Rewind;
|
procedure Rewind;
|
||||||
|
|
||||||
(* Always seeks as close as possible *)
|
(* Always seeks as close as possible *)
|
||||||
function Seek (SeekInd: integer): boolean;
|
function Seek (SeekInd: integer): boolean;
|
||||||
function SeekRel (RelInd: integer): boolean;
|
function SeekRel (RelInd: integer): boolean;
|
||||||
|
|
||||||
|
function GetDebugDump: string;
|
||||||
|
|
||||||
property FileInd: integer read fFileInd;
|
property FileInd: integer read fFileInd;
|
||||||
property Count: integer read GetCount;
|
property Count: integer read GetCount;
|
||||||
end; // .class TDirListing
|
end; // .class TDirListing
|
||||||
@ -95,6 +97,11 @@ type
|
|||||||
function IterNext ({OUT} var FileName: WideString; {n} FileInfo: WinNative.PFILE_ID_BOTH_DIR_INFORMATION = nil): boolean;
|
function IterNext ({OUT} var FileName: WideString; {n} FileInfo: WinNative.PFILE_ID_BOTH_DIR_INFORMATION = nil): boolean;
|
||||||
end; // .class TSysDirScanner
|
end; // .class TSysDirScanner
|
||||||
|
|
||||||
|
(* Packs lower cased WideString bytes into AnsiString buffer *)
|
||||||
|
function WideStrToCaselessKey (const Str: WideString): string;
|
||||||
|
|
||||||
|
(* The opposite of WideStrToKey *)
|
||||||
|
function CaselessKeyToWideStr (const CaselessKey: string): WideString;
|
||||||
|
|
||||||
(* Returns expanded unicode path, preserving trailing delimiter, or original path on error *)
|
(* Returns expanded unicode path, preserving trailing delimiter, or original path on error *)
|
||||||
function ExpandPath (const Path: WideString): WideString;
|
function ExpandPath (const Path: WideString): WideString;
|
||||||
@ -117,7 +124,7 @@ function StripNtAbsPathPrefix (const Path: WideString): WideString;
|
|||||||
function SaveAndRet (Res: integer; out ResCopy): integer;
|
function SaveAndRet (Res: integer; out ResCopy): integer;
|
||||||
|
|
||||||
(* Opens file/directory using absolute NT path and returns success flag *)
|
(* Opens file/directory using absolute NT path and returns success flag *)
|
||||||
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: integer = Int(GENERIC_READ) or SYNCHRONIZE): boolean;
|
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: ACCESS_MASK = GENERIC_READ or SYNCHRONIZE): boolean;
|
||||||
|
|
||||||
(* Returns TNativeFileInfo record for single file/directory. Short names and files indexes/ids in the result are always empty. *)
|
(* Returns TNativeFileInfo record for single file/directory. Short names and files indexes/ids in the result are always empty. *)
|
||||||
function GetFileInfo (const FilePath: WideString; {OUT} var Res: TNativeFileInfo): boolean;
|
function GetFileInfo (const FilePath: WideString; {OUT} var Res: TNativeFileInfo): boolean;
|
||||||
@ -125,9 +132,44 @@ function GetFileInfo (const FilePath: WideString; {OUT} var Res: TNativeFileInfo
|
|||||||
function SysScanDir (const hDir: Windows.THandle; const Mask: WideString): ISysDirScanner; overload;
|
function SysScanDir (const hDir: Windows.THandle; const Mask: WideString): ISysDirScanner; overload;
|
||||||
function SysScanDir (const DirPath, Mask: WideString): ISysDirScanner; overload;
|
function SysScanDir (const DirPath, Mask: WideString): ISysDirScanner; overload;
|
||||||
|
|
||||||
|
(* Scans specified directory and adds sorted entries to directory listing. Optionally exclude names from Exclude dictionary.
|
||||||
|
Excluded items must be preprocessed via WideStringToCaselessKey routine *)
|
||||||
|
procedure GetDirectoryListing (const SearchPath, FileMask: WideString; {Un} Exclude: TDict {OF CaselessKey => not NIL}; DirListing: TDirListing);
|
||||||
|
|
||||||
(***) implementation (***)
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TDirListingItem = class
|
||||||
|
SearchName: WideString;
|
||||||
|
Info: TNativeFileInfo;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function WideStrToCaselessKey (const Str: WideString): string;
|
||||||
|
var
|
||||||
|
ProcessedPath: WideString;
|
||||||
|
|
||||||
|
begin
|
||||||
|
result := '';
|
||||||
|
|
||||||
|
if Str <> '' then begin
|
||||||
|
ProcessedPath := StrLib.WideLowerCase(Str);
|
||||||
|
SetLength(result, Length(ProcessedPath) * sizeof(ProcessedPath[1]) div sizeof(result[1]));
|
||||||
|
Utils.CopyMem(Length(result) * sizeof(result[1]), PWideChar(ProcessedPath), PChar(result));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CaselessKeyToWideStr (const CaselessKey: string): WideString;
|
||||||
|
begin
|
||||||
|
result := '';
|
||||||
|
|
||||||
|
if CaselessKey <> '' then begin
|
||||||
|
SetLength(result, Length(CaselessKey) * sizeof(CaselessKey[1]) div sizeof(result[1]));
|
||||||
|
Utils.CopyMem(Length(result) * sizeof(result[1]), pchar(CaselessKey), PWideChar(result));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function ExpandPath (const Path: WideString): WideString;
|
function ExpandPath (const Path: WideString): WideString;
|
||||||
var
|
var
|
||||||
BufLen: integer;
|
BufLen: integer;
|
||||||
@ -309,7 +351,22 @@ begin
|
|||||||
result := Self.Seek(Self.fFileInd + RelInd);
|
result := Self.Seek(Self.fFileInd + RelInd);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: integer = Int(GENERIC_READ) or SYNCHRONIZE): boolean;
|
function TDirListing.GetDebugDump: string;
|
||||||
|
var
|
||||||
|
FileNames: Utils.TArrayOfStr;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
SetLength(FileNames, Self.fFileList.Count);
|
||||||
|
|
||||||
|
for i := 0 to Self.fFileList.Count - 1 do begin
|
||||||
|
FileNames[i] := TFileInfo(Self.fFileList[i]).Data.FileName;
|
||||||
|
end;
|
||||||
|
|
||||||
|
result := StrLib.Join(FileNames, #13#10);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: ACCESS_MASK = GENERIC_READ or SYNCHRONIZE): boolean;
|
||||||
var
|
var
|
||||||
FilePathU: WinNative.UNICODE_STRING;
|
FilePathU: WinNative.UNICODE_STRING;
|
||||||
hFile: Windows.THandle;
|
hFile: Windows.THandle;
|
||||||
@ -478,4 +535,50 @@ begin
|
|||||||
result := TSysDirScanner.Create(DirPath, Mask);
|
result := TSysDirScanner.Create(DirPath, Mask);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function CompareFileItemsByNameAsc (Item1, Item2: integer): integer;
|
||||||
|
begin
|
||||||
|
result := StrLib.CompareBinStringsW(TDirListingItem(Item1).SearchName, TDirListingItem(Item2).SearchName);
|
||||||
|
|
||||||
|
if result = 0 then begin
|
||||||
|
result := StrLib.CompareBinStringsW(TDirListingItem(Item1).Info.FileName, TDirListingItem(Item2).Info.FileName);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure SortDirListing ({U} List: TList {OF TDirListingItem});
|
||||||
|
begin
|
||||||
|
List.CustomSort(CompareFileItemsByNameAsc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure GetDirectoryListing (const SearchPath, FileMask: WideString; {Un} Exclude: TDict {OF CaselessKey => not NIL}; DirListing: TDirListing);
|
||||||
|
var
|
||||||
|
{O} Items: {O} TList {OF TDirListingItem};
|
||||||
|
{O} Item: {O} TDirListingItem;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
{!} Assert(DirListing <> nil);
|
||||||
|
// * * * * * //
|
||||||
|
Items := DataLib.NewList(Utils.OWNS_ITEMS);
|
||||||
|
Item := TDirListingItem.Create;
|
||||||
|
// * * * * * //
|
||||||
|
with VfsUtils.SysScanDir(SearchPath, FileMask) do begin
|
||||||
|
while IterNext(Item.Info.FileName, @Item.Info.Base) do begin
|
||||||
|
if (Exclude = nil) or (Exclude[WideStrToCaselessKey(Item.Info.FileName)] = nil) then begin
|
||||||
|
Item.SearchName := StrLib.WideLowerCase(Item.Info.FileName);
|
||||||
|
Items.Add(Item); Item := nil;
|
||||||
|
Item := TDirListingItem.Create;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
SortDirListing(Items);
|
||||||
|
|
||||||
|
for i := 0 to Items.Count - 1 do begin
|
||||||
|
DirListing.AddItem(@TDirListingItem(Items[i]).Info);
|
||||||
|
end;
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(Items);
|
||||||
|
SysUtils.FreeAndNil(Item);
|
||||||
|
end; // .procedure GetDirectoryListing
|
||||||
|
|
||||||
end.
|
end.
|
||||||
Loading…
Reference in New Issue
Block a user