TFileSearcher: first try at detecting circular links (outside the current tree).

This commit is contained in:
Bart 2024-02-06 20:00:23 +01:00
parent de6b521614
commit 65e0930568
2 changed files with 49 additions and 11 deletions

View File

@ -795,6 +795,7 @@ begin
FMaskSeparator := ';'; FMaskSeparator := ';';
FPathSeparator := ';'; FPathSeparator := ';';
FFollowSymLink := True; FFollowSymLink := True;
FCircularLinkDetection := False;
FFileAttribute := faAnyFile; FFileAttribute := faAnyFile;
FDirectoryAttribute := faDirectory; FDirectoryAttribute := faDirectory;
FSearching := False; FSearching := False;
@ -819,7 +820,7 @@ var
end; end;
var var
P: String; P, FQDir, LinkTarget: String;
PathInfo: TSearchRec; PathInfo: TSearchRec;
begin begin
P := APath + AllDirectoryEntriesMask; P := APath + AllDirectoryEntriesMask;
@ -862,22 +863,49 @@ var
repeat repeat
if (PathInfo.Name = '.') or (PathInfo.Name = '..') or if (PathInfo.Name = '.') or (PathInfo.Name = '..') or
(PathInfo.Name = '') or ((PathInfo.Attr and faDirectory) = 0) or (PathInfo.Name = '') or ((PathInfo.Attr and faDirectory) = 0) or
(not FFollowSymLink and FileIsSymlink(APath + PathInfo.Name)) or
(FileIsSymlink(APath + PathInfo.Name) and
( //try to detect the simples case of a circular link
//is the link "up or down the current tree": infinite loop if we follow
FileIsInPath(ExpandFilenameUTF8(ReadAllLinks(APath+PathInfo.Name,False)),ExpandFilenameUTF8(APath)) or
FileIsInPath(ExpandFilenameUTF8(APath),ExpandFilenameUTF8(ReadAllLinks(APath+PathInfo.Name,False))))
) or
(not AcceptDir(AppendPathDelim(ExpandFilenameUtf8(APath + PathInfo.Name)))) (not AcceptDir(AppendPathDelim(ExpandFilenameUtf8(APath + PathInfo.Name))))
then Continue; then
Continue;
if Assigned(VisitedDirs) then
begin
FQDir := ExpandFilenameUTF8(APath + PathInfo.Name);
{$ifdef CaseInsensitiveFilenames}
FQDir := Utf8LowerCase(FQDir);
{$endif}
end;
// deal with symlinks separately, avoid excessive calls to ExpandFilenameUTF8 and
// have a less convoluted boolean expression above here
if (FileIsSymlink(APath + PathInfo.Name)) then
begin
if not FFollowSymlink then
Continue;
LinkTarget := ReadAllLinks(APath+PathInfo.Name,False);
if (LinkTarget = '') then //broken link
Continue;
LinkTarget := ExpandFilenameUTF8(LinkTarget);
{$ifdef CaseInsensitiveFilenames}
LinkTarget := Utf8LowerCase(LinkTarget);
{$endif}
if
//is the link "up or down the current tree": infinite loop if we follow
FileIsInPath(LinkTarget,ExpandFilenameUTF8(APath)) or
FileIsInPath(ExpandFilenameUTF8(APath),LinkTarget) or
//did we already visit that directory, assuming we actually do check for that
(Assigned(VisitedDirs) and Assigned(VisitedDirs.Find(LinkTarget)))
then
Continue;
if Assigned(VisitedDirs) then
FQDir := LinkTarget;
end;
FPath := APath; FPath := APath;
FLevel := ALevel; FLevel := ALevel;
FFileInfo := PathInfo; FFileInfo := PathInfo;
DoDirectoryEnter; DoDirectoryEnter;
if not FSearching then Break; if not FSearching then Break;
if Assigned(VisitedDirs) then
VisitedDirs.Add(FQDir,'');
DoSearch(AppendPathDelim(APath + PathInfo.Name), Succ(ALevel)); DoSearch(AppendPathDelim(APath + PathInfo.Name), Succ(ALevel));
until (FindNextUTF8(PathInfo) <> 0); until (FindNextUTF8(PathInfo) <> 0);
@ -893,6 +921,10 @@ var
OtherDir: String; OtherDir: String;
begin begin
if FSearching then RaiseSearchingError; if FSearching then RaiseSearchingError;
if FCircularLinkDetection and FFollowSymLink then
VisitedDirs := TFPStringHashTable.Create
else
VisitedDirs := nil;
{$ifdef windows} {$ifdef windows}
MaskList := TWindowsMaskList.Create(ASearchMask, FMaskSeparator, CaseSensitive); MaskList := TWindowsMaskList.Create(ASearchMask, FMaskSeparator, CaseSensitive);
{$else} {$else}
@ -944,6 +976,8 @@ begin
SearchDirectories.Free; SearchDirectories.Free;
FSearching := False; FSearching := False;
MaskList.Free; MaskList.Free;
if Assigned(VisitedDirs) then
VisitedDirs.Free;
end; end;
end; end;

View File

@ -29,7 +29,8 @@ interface
uses uses
Classes, SysUtils, StrUtils, Classes, SysUtils, StrUtils,
// LazUtils // LazUtils
Masks, LazUTF8, LazFileUtils; Masks, LazUTF8, LazFileUtils,
Contnrs;
{$IF defined(Windows) or defined(darwin) or defined(HASAMIGA)} {$IF defined(Windows) or defined(darwin) or defined(HASAMIGA)}
{$define CaseInsensitiveFilenames} {$define CaseInsensitiveFilenames}
@ -138,6 +139,8 @@ type
FDirectoryAttribute: Word; FDirectoryAttribute: Word;
FOnQueryFileFound: TQueryFileFoundEvent; FOnQueryFileFound: TQueryFileFoundEvent;
FOnQueryDirectoryFound: TQueryDirectoryFoundEvent; FOnQueryDirectoryFound: TQueryDirectoryFoundEvent;
FCircularLinkDetection: Boolean;
VisitedDirs: TFPStringHashTable;
procedure RaiseSearchingError; procedure RaiseSearchingError;
protected protected
procedure DoDirectoryEnter; virtual; procedure DoDirectoryEnter; virtual;
@ -155,6 +158,7 @@ type
property FollowSymLink: Boolean read FFollowSymLink write FFollowSymLink; property FollowSymLink: Boolean read FFollowSymLink write FFollowSymLink;
property FileAttribute: Word read FFileAttribute write FFileAttribute default faAnyfile; property FileAttribute: Word read FFileAttribute write FFileAttribute default faAnyfile;
property DirectoryAttribute: Word read FDirectoryAttribute write FDirectoryAttribute default faDirectory; property DirectoryAttribute: Word read FDirectoryAttribute write FDirectoryAttribute default faDirectory;
property CircularLinkdetection: Boolean read FCircularLinkdetection write FCircularLinkdetection default False;
property OnDirectoryFound: TDirectoryFoundEvent read FOnDirectoryFound write FOnDirectoryFound; property OnDirectoryFound: TDirectoryFoundEvent read FOnDirectoryFound write FOnDirectoryFound;
property OnFileFound: TFileFoundEvent read FOnFileFound write FOnFileFound; property OnFileFound: TFileFoundEvent read FOnFileFound write FOnFileFound;
property OnDirectoryEnter: TDirectoryEnterEvent read FOnDirectoryEnter write FOnDirectoryEnter; property OnDirectoryEnter: TDirectoryEnterEvent read FOnDirectoryEnter write FOnDirectoryEnter;