mirror of
https://github.com/CloudDelphi/Virtual-File-System
synced 2025-12-19 09:53:54 +01:00
Fixed bug: MapDir on non-existent directories reported TRUE. Improved generated mappings report
This commit is contained in:
parent
7cb8c94406
commit
e9ef40a404
141
VfsBase.pas
141
VfsBase.pas
@ -126,6 +126,10 @@ function MapDir (const VirtPath, RealPath: WideString; OverwriteExisting: boolea
|
|||||||
(* Calls specified function with a single argument and returns its result. VFS is disabled for current thread during function exection *)
|
(* Calls specified function with a single argument and returns its result. VFS is disabled for current thread during function exection *)
|
||||||
function CallWithoutVfs (Func: TSingleArgExternalFunc; Arg: pointer = nil): integer; stdcall;
|
function CallWithoutVfs (Func: TSingleArgExternalFunc; Arg: pointer = nil): integer; stdcall;
|
||||||
|
|
||||||
|
(* Returns text with all applied mappings, separated via #13#10. If ShortenPaths is true, common part
|
||||||
|
of real and virtual paths is stripped *)
|
||||||
|
function GetMappingsReport: WideString;
|
||||||
|
|
||||||
|
|
||||||
(***) implementation (***)
|
(***) implementation (***)
|
||||||
|
|
||||||
@ -133,12 +137,13 @@ function CallWithoutVfs (Func: TSingleArgExternalFunc; Arg: pointer = nil): inte
|
|||||||
type
|
type
|
||||||
(* Applied and remembered mapping. Used to refresh or report VFS *)
|
(* Applied and remembered mapping. Used to refresh or report VFS *)
|
||||||
TMapping = class
|
TMapping = class
|
||||||
|
Applied: LONGBOOL;
|
||||||
AbsVirtPath: WideString;
|
AbsVirtPath: WideString;
|
||||||
AbsRealPath: WideString;
|
AbsRealPath: WideString;
|
||||||
OverwriteExisting: boolean;
|
OverwriteExisting: LONGBOOL;
|
||||||
Flags: integer;
|
Flags: integer;
|
||||||
|
|
||||||
class function Make (const AbsVirtPath, AbsRealPath: WideString; OverwriteExisting: boolean; Flags: integer): TMapping;
|
class function Make (Applied: boolean; const AbsVirtPath, AbsRealPath: WideString; OverwriteExisting: boolean; Flags: integer): TMapping;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -332,9 +337,10 @@ begin
|
|||||||
end;
|
end;
|
||||||
end; // .procedure BuildVfsItemsTree
|
end; // .procedure BuildVfsItemsTree
|
||||||
|
|
||||||
class function TMapping.Make (const AbsVirtPath, AbsRealPath: WideString; OverwriteExisting: boolean; Flags: integer): {O} TMapping;
|
class function TMapping.Make (Applied: boolean; const AbsVirtPath, AbsRealPath: WideString; OverwriteExisting: boolean; Flags: integer): {O} TMapping;
|
||||||
begin
|
begin
|
||||||
result := TMapping.Create;
|
result := TMapping.Create;
|
||||||
|
result.Applied := Applied;
|
||||||
result.AbsVirtPath := AbsVirtPath;
|
result.AbsVirtPath := AbsVirtPath;
|
||||||
result.AbsRealPath := AbsRealPath;
|
result.AbsRealPath := AbsRealPath;
|
||||||
result.OverwriteExisting := OverwriteExisting;
|
result.OverwriteExisting := OverwriteExisting;
|
||||||
@ -468,7 +474,8 @@ begin
|
|||||||
Dest.EaSize := Src.EaSize;
|
Dest.EaSize := Src.EaSize;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* Redirects single file/directory path (not including directory contents). Target must exist for success *)
|
(* Redirects single file/directory path (not including directory contents). Returns redirected VFS item
|
||||||
|
for given real path if VFS item was successfully created/overwritten or it already existed and OverwriteExisting = false. *)
|
||||||
function RedirectFile (const AbsVirtPath, AbsRealPath: WideString; {n} FileInfoPtr: WinNative.PFILE_ID_BOTH_DIR_INFORMATION; OverwriteExisting: boolean; Priority: integer): {Un} TVfsItem;
|
function RedirectFile (const AbsVirtPath, AbsRealPath: WideString; {n} FileInfoPtr: WinNative.PFILE_ID_BOTH_DIR_INFORMATION; OverwriteExisting: boolean; Priority: integer): {Un} TVfsItem;
|
||||||
const
|
const
|
||||||
WIDE_NULL_CHAR_LEN = Length(#0);
|
WIDE_NULL_CHAR_LEN = Length(#0);
|
||||||
@ -550,7 +557,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
DirVfsItem := RedirectFile(AbsVirtPath, AbsRealPath, FileInfoPtr, OverwriteExisting, Priority);
|
DirVfsItem := RedirectFile(AbsVirtPath, AbsRealPath, FileInfoPtr, OverwriteExisting, Priority);
|
||||||
Success := DirVfsItem <> nil;
|
Success := (DirVfsItem <> nil) and ((DirVfsItem.RealPath = AbsRealPath) or VfsUtils.IsDir(AbsRealPath));
|
||||||
|
|
||||||
if Success then begin
|
if Success then begin
|
||||||
VirtPathPrefix := AddBackslash(AbsVirtPath);
|
VirtPathPrefix := AddBackslash(AbsVirtPath);
|
||||||
@ -599,8 +606,8 @@ begin
|
|||||||
result := (AbsVirtPath <> '') and (AbsRealPath <> '');
|
result := (AbsVirtPath <> '') and (AbsRealPath <> '');
|
||||||
|
|
||||||
if result then begin
|
if result then begin
|
||||||
Mappings.Add(TMapping.Make(AbsVirtPath, AbsRealPath, OverwriteExisting, Flags));
|
|
||||||
result := _MapDir(AbsVirtPath, AbsRealPath, nil, OverwriteExisting, Flags, AUTO_PRIORITY) <> nil;
|
result := _MapDir(AbsVirtPath, AbsRealPath, nil, OverwriteExisting, Flags, AUTO_PRIORITY) <> nil;
|
||||||
|
Mappings.Add(TMapping.Make(result, AbsVirtPath, AbsRealPath, OverwriteExisting, Flags));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
LeaveVfsConfig;
|
LeaveVfsConfig;
|
||||||
@ -638,7 +645,7 @@ begin
|
|||||||
|
|
||||||
for i := 0 to Mappings.Count - 1 do begin
|
for i := 0 to Mappings.Count - 1 do begin
|
||||||
with TMapping(Mappings[i]) do begin
|
with TMapping(Mappings[i]) do begin
|
||||||
MapDir(AbsVirtPath, AbsRealPath, OverwriteExisting, Flags);
|
TMapping(Mappings[i]).Applied := MapDir(AbsVirtPath, AbsRealPath, OverwriteExisting, Flags);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -686,6 +693,126 @@ begin
|
|||||||
end; // .with
|
end; // .with
|
||||||
end; // .function RefreshMappedFile
|
end; // .function RefreshMappedFile
|
||||||
|
|
||||||
|
function GetMappingsReport: WideString;
|
||||||
|
const
|
||||||
|
COL_PATHS = 0;
|
||||||
|
COL_META = 1;
|
||||||
|
|
||||||
|
var
|
||||||
|
{O} Buf: StrLib.TStrBuilder;
|
||||||
|
{O} Line: StrLib.TStrBuilder;
|
||||||
|
Cols: array [0..1] of array of WideString;
|
||||||
|
MaxPathColWidth: integer;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
procedure WriteMapping (Mapping: TMapping; LineN: integer);
|
||||||
|
var
|
||||||
|
StartPathPos: integer;
|
||||||
|
MaxCommonPathLen: integer;
|
||||||
|
ShortestPath: WideString;
|
||||||
|
LongestPath: WideString;
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
{!} Assert(Mapping <> nil);
|
||||||
|
StartPathPos := 1;
|
||||||
|
|
||||||
|
if Length(Mapping.AbsRealPath) > Length(Mapping.AbsVirtPath) then begin
|
||||||
|
LongestPath := Mapping.AbsRealPath;
|
||||||
|
ShortestPath := Mapping.AbsVirtPath;
|
||||||
|
end else begin
|
||||||
|
LongestPath := Mapping.AbsVirtPath;
|
||||||
|
ShortestPath := Mapping.AbsRealPath;
|
||||||
|
end;
|
||||||
|
|
||||||
|
i := 1;
|
||||||
|
MaxCommonPathLen := Length(ShortestPath);
|
||||||
|
|
||||||
|
while (i <= MaxCommonPathLen) and (ShortestPath[i] = LongestPath[i]) do begin
|
||||||
|
Inc(i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Handle case: [xxx\yyy] zzz and [xxx\yyy]. Common part is [xxx]
|
||||||
|
if (Length(LongestPath) > MaxCommonPathLen) and (LongestPath[i] <> '\') then begin
|
||||||
|
while (i >= 2) and (LongestPath[i] <> '\') do begin
|
||||||
|
Dec(i);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
// Handle case: D:\App <= D:\Mods. Common part is D:
|
||||||
|
else if ShortestPath[i] = '\' then begin
|
||||||
|
Dec(i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
StartPathPos := i;
|
||||||
|
Line.Clear;
|
||||||
|
|
||||||
|
if StartPathPos > 1 then begin
|
||||||
|
Line.AppendWide('$');
|
||||||
|
end;
|
||||||
|
|
||||||
|
Line.AppendWide(Copy(Mapping.AbsVirtPath, StartPathPos));
|
||||||
|
Line.AppendWide(' <= ');
|
||||||
|
|
||||||
|
if StartPathPos > 1 then begin
|
||||||
|
Line.AppendWide('$');
|
||||||
|
end;
|
||||||
|
|
||||||
|
Line.AppendWide(Copy(Mapping.AbsRealPath, StartPathPos));
|
||||||
|
|
||||||
|
if not Mapping.Applied then begin
|
||||||
|
Line.AppendWide(' *MISS*');
|
||||||
|
end;
|
||||||
|
|
||||||
|
Cols[COL_PATHS][LineN] := Line.BuildWideStr;
|
||||||
|
MaxPathColWidth := Max(MaxPathColWidth, Length(Cols[COL_PATHS][LineN]));
|
||||||
|
|
||||||
|
Line.Clear;
|
||||||
|
Line.AppendWide('[Overwrite = ' + IntToStr(ord(Mapping.OverwriteExisting)) + ', Flags = ' + IntToStr(Mapping.Flags));
|
||||||
|
|
||||||
|
if StartPathPos > 1 then begin
|
||||||
|
Line.AppendWide(', $ = "' + Copy(ShortestPath, 1, StartPathPos - 1) + '"]');
|
||||||
|
end else begin
|
||||||
|
Line.AppendWide(']');
|
||||||
|
end;
|
||||||
|
|
||||||
|
Cols[COL_META][LineN] := Line.BuildWideStr;
|
||||||
|
end; // .procedure WriteMapping
|
||||||
|
|
||||||
|
function FormatResultTable: WideString;
|
||||||
|
var
|
||||||
|
i: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
for i := 0 to Mappings.Count - 1 do begin
|
||||||
|
Buf.AppendWide(Cols[COL_PATHS][i] + StringOfChar(WideChar(' '), MaxPathColWidth - Length(Cols[COL_PATHS][i]) + 1));
|
||||||
|
Buf.AppendWide(Cols[COL_META][i]);
|
||||||
|
|
||||||
|
if i < Mappings.Count - 1 then begin
|
||||||
|
Buf.AppendWide(#13#10);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
result := Buf.BuildWideStr;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Buf := StrLib.TStrBuilder.Create;
|
||||||
|
Line := StrLib.TStrBuilder.Create;
|
||||||
|
// * * * * * //
|
||||||
|
SetLength(Cols[COL_PATHS], Mappings.Count);
|
||||||
|
SetLength(Cols[COL_META], Mappings.Count);
|
||||||
|
MaxPathColWidth := 0;
|
||||||
|
|
||||||
|
for i := 0 to Mappings.Count - 1 do begin
|
||||||
|
WriteMapping(TMapping(Mappings[i]), i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
result := FormatResultTable;
|
||||||
|
// * * * * * //
|
||||||
|
SysUtils.FreeAndNil(Buf);
|
||||||
|
SysUtils.FreeAndNil(Line);
|
||||||
|
end; // .function GetMappingsReport
|
||||||
|
|
||||||
begin
|
begin
|
||||||
VfsCritSection.Init;
|
VfsCritSection.Init;
|
||||||
VfsItems := DataLib.NewDict(Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
VfsItems := DataLib.NewDict(Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ unit VfsExport;
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Windows,
|
Windows,
|
||||||
|
Utils,
|
||||||
VfsDebug, VfsBase, VfsControl, VfsWatching;
|
VfsDebug, VfsBase, VfsControl, VfsWatching;
|
||||||
|
|
||||||
exports
|
exports
|
||||||
@ -48,6 +49,36 @@ begin
|
|||||||
result := VfsWatching.RunWatcher(WatchDir, DebounceInterval);
|
result := VfsWatching.RunWatcher(WatchDir, DebounceInterval);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
(* Frees buffer, that was transfered to client earlier using other VFS API *)
|
||||||
|
procedure MemFree ({O} Buf: pointer); stdcall;
|
||||||
|
begin
|
||||||
|
FreeMem(Buf);
|
||||||
|
end;
|
||||||
|
|
||||||
|
(* Returns text with all applied mappings, separated via #13#10. If ShortenPaths is true, common part
|
||||||
|
of real and virtual paths is stripped. Call MemFree to release result buffer *)
|
||||||
|
function GetMappingsReport: {O} PWideChar; stdcall;
|
||||||
|
var
|
||||||
|
Res: WideString;
|
||||||
|
|
||||||
|
begin
|
||||||
|
result := nil;
|
||||||
|
Res := VfsBase.GetMappingsReport;
|
||||||
|
GetMem(result, (Length(Res) + 1) * sizeof(WideChar));
|
||||||
|
Utils.CopyMem((Length(Res) + 1) * sizeof(WideChar), PWideChar(Res), result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function GetMappingsReportA: {O} pchar; stdcall;
|
||||||
|
var
|
||||||
|
Res: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
result := nil;
|
||||||
|
Res := VfsBase.GetMappingsReport;
|
||||||
|
GetMem(result, Length(Res) + 1);
|
||||||
|
Utils.CopyMem(Length(Res) + 1, pchar(Res), result);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure ConsoleLoggingProc (Operation, Message: pchar); stdcall;
|
procedure ConsoleLoggingProc (Operation, Message: pchar); stdcall;
|
||||||
begin
|
begin
|
||||||
WriteLn('>> ', string(Operation), ': ', string(Message), #13#10);
|
WriteLn('>> ', string(Operation), ': ', string(Message), #13#10);
|
||||||
@ -88,6 +119,9 @@ exports
|
|||||||
MapModsFromList,
|
MapModsFromList,
|
||||||
MapModsFromListA,
|
MapModsFromListA,
|
||||||
RunWatcher,
|
RunWatcher,
|
||||||
|
GetMappingsReport,
|
||||||
|
GetMappingsReportA,
|
||||||
|
MemFree,
|
||||||
InstallConsoleLogger;
|
InstallConsoleLogger;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@ -33,9 +33,18 @@ function RunVfs (DirListingOrder: TDirListingSortType): LONGBOOL; stdcall; exter
|
|||||||
time in msec to wait after last change before running full VFS rescanning routine *)
|
time in msec to wait after last change before running full VFS rescanning routine *)
|
||||||
function RunWatcher (const WatchDir: PWideChar; DebounceInterval: integer): LONGBOOL; stdcall; external 'vfs.dll';
|
function RunWatcher (const WatchDir: PWideChar; DebounceInterval: integer): LONGBOOL; stdcall; external 'vfs.dll';
|
||||||
|
|
||||||
|
(* Frees buffer, that was transfered to client earlier using other VFS API *)
|
||||||
|
procedure MemFree ({O} Buf: pointer); stdcall; external 'vfs.dll';
|
||||||
|
|
||||||
|
(* Returns text with all applied mappings, separated via #13#10. If ShortenPaths is true, common part
|
||||||
|
of real and virtual paths is stripped. Call MemFree to release result buffer *)
|
||||||
|
function GetMappingsReport: {O} PWideChar; stdcall; external 'vfs.dll';
|
||||||
|
function GetMappingsReportA: {O} pchar; stdcall; external 'vfs.dll';
|
||||||
|
|
||||||
(* Allocates console and install logger, writing messages to console *)
|
(* Allocates console and install logger, writing messages to console *)
|
||||||
procedure InstallConsoleLogger; stdcall; external 'vfs.dll';
|
procedure InstallConsoleLogger; stdcall; external 'vfs.dll';
|
||||||
|
|
||||||
|
|
||||||
(***) implementation (***)
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
33
VfsUtils.pas
33
VfsUtils.pas
@ -140,6 +140,12 @@ function StripNtAbsPathPrefix (const Path: WideString): WideString;
|
|||||||
(* Saves API result in external variable and returns result as is *)
|
(* Saves API result in external variable and returns result as is *)
|
||||||
function SaveAndRet (Res: integer; out ResCopy): integer;
|
function SaveAndRet (Res: integer; out ResCopy): integer;
|
||||||
|
|
||||||
|
(* Returns attributes for file at given path *)
|
||||||
|
function GetFileAttrs (const Path: WideString; {out} var Attrs: integer): boolean;
|
||||||
|
|
||||||
|
(* Returns true if directory with given path exists *)
|
||||||
|
function IsDir (const Path: WideString): boolean;
|
||||||
|
|
||||||
(* 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: ACCESS_MASK = FILE_GENERIC_READ): boolean;
|
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: ACCESS_MASK = FILE_GENERIC_READ): boolean;
|
||||||
|
|
||||||
@ -155,6 +161,7 @@ function SysScanDir (const DirPath, Mask: WideString): ISysDirScanner; overload;
|
|||||||
tests were run on network drive *)
|
tests were run on network drive *)
|
||||||
procedure GetDirectoryListing (const SearchPath, FileMask: WideString; {Un} Exclude: TDict {OF CaselessKey => not NIL}; DirListing: TDirListing);
|
procedure GetDirectoryListing (const SearchPath, FileMask: WideString; {Un} Exclude: TDict {OF CaselessKey => not NIL}; DirListing: TDirListing);
|
||||||
|
|
||||||
|
|
||||||
(***) implementation (***)
|
(***) implementation (***)
|
||||||
|
|
||||||
|
|
||||||
@ -478,6 +485,30 @@ begin
|
|||||||
result := StrLib.Join(FileNames, #13#10);
|
result := StrLib.Join(FileNames, #13#10);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function GetFileAttrs (const Path: WideString; {out} var Attrs: integer): boolean;
|
||||||
|
const
|
||||||
|
INVALID_FILE_ATTRIBUTES = -1;
|
||||||
|
|
||||||
|
var
|
||||||
|
Res: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Res := integer(Windows.GetFileAttributesW(PWideChar(Path)));
|
||||||
|
result := Res <> INVALID_FILE_ATTRIBUTES;
|
||||||
|
|
||||||
|
if result then begin
|
||||||
|
Attrs := Res;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function IsDir (const Path: WideString): boolean;
|
||||||
|
var
|
||||||
|
FileAttrs: integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
result := GetFileAttrs(Path, FileAttrs) and Utils.HasFlag(Windows.FILE_ATTRIBUTE_DIRECTORY, FileAttrs);
|
||||||
|
end;
|
||||||
|
|
||||||
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: ACCESS_MASK = FILE_GENERIC_READ): boolean;
|
function SysOpenFile (const NtAbsPath: WideString; {OUT} var Res: Windows.THandle; const OpenMode: TSysOpenFileMode = OPEN_AS_ANY; const AccessMode: ACCESS_MASK = FILE_GENERIC_READ): boolean;
|
||||||
var
|
var
|
||||||
FilePathU: WinNative.UNICODE_STRING;
|
FilePathU: WinNative.UNICODE_STRING;
|
||||||
@ -521,7 +552,7 @@ begin
|
|||||||
|
|
||||||
if IsNtRootDriveAbsPath(NtAbsPath) then begin
|
if IsNtRootDriveAbsPath(NtAbsPath) then begin
|
||||||
// Return fake info for root drive
|
// Return fake info for root drive
|
||||||
result := SaveAndRet(Windows.GetFileAttributesW(PWideChar(StripNtAbsPathPrefix(NtAbsPath))), FileAllInfo.BasicInformation.FileAttributes) <> integer(Windows.INVALID_HANDLE_VALUE);
|
result := GetFileAttrs(StripNtAbsPathPrefix(NtAbsPath), integer(FileAllInfo.BasicInformation.FileAttributes));
|
||||||
|
|
||||||
if result then begin
|
if result then begin
|
||||||
FillChar(Res.Base, sizeof(Res.Base), 0);
|
FillChar(Res.Base, sizeof(Res.Base), 0);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user