|
@@ -92,6 +92,10 @@ implementation
|
|
sysconst,
|
|
sysconst,
|
|
windirs;
|
|
windirs;
|
|
|
|
|
|
|
|
+var
|
|
|
|
+ FindExInfoDefaults : TFINDEX_INFO_LEVELS = FindExInfoStandard;
|
|
|
|
+ FindFirstAdditionalFlags : DWord = 0;
|
|
|
|
+
|
|
function WinCheck(res:boolean):boolean;
|
|
function WinCheck(res:boolean):boolean;
|
|
begin
|
|
begin
|
|
if not res then
|
|
if not res then
|
|
@@ -407,28 +411,157 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
-Function FileExists (Const FileName : UnicodeString) : Boolean;
|
|
|
|
|
|
+function FileGetSymLinkTargetInt(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec; RaiseErrorOnMissing: Boolean): Boolean;
|
|
|
|
+{ reparse point specific declarations from Windows headers }
|
|
|
|
+const
|
|
|
|
+ IO_REPARSE_TAG_MOUNT_POINT = $A0000003;
|
|
|
|
+ IO_REPARSE_TAG_SYMLINK = $A000000C;
|
|
|
|
+ ERROR_REPARSE_TAG_INVALID = 4393;
|
|
|
|
+ FSCTL_GET_REPARSE_POINT = $900A8;
|
|
|
|
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
|
|
|
|
+ SYMLINK_FLAG_RELATIVE = 1;
|
|
|
|
+ FILE_FLAG_OPEN_REPARSE_POINT = $200000;
|
|
|
|
+ FILE_READ_EA = $8;
|
|
|
|
+type
|
|
|
|
+ TReparseDataBuffer = record
|
|
|
|
+ ReparseTag: ULONG;
|
|
|
|
+ ReparseDataLength: Word;
|
|
|
|
+ Reserved: Word;
|
|
|
|
+ SubstituteNameOffset: Word;
|
|
|
|
+ SubstituteNameLength: Word;
|
|
|
|
+ PrintNameOffset: Word;
|
|
|
|
+ PrintNameLength: Word;
|
|
|
|
+ case ULONG of
|
|
|
|
+ IO_REPARSE_TAG_MOUNT_POINT: (
|
|
|
|
+ PathBufferMount: array[0..4095] of WCHAR);
|
|
|
|
+ IO_REPARSE_TAG_SYMLINK: (
|
|
|
|
+ Flags: ULONG;
|
|
|
|
+ PathBufferSym: array[0..4095] of WCHAR);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+const
|
|
|
|
+ CShareAny = FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE;
|
|
|
|
+ COpenReparse = FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS;
|
|
var
|
|
var
|
|
- Attr:Dword;
|
|
|
|
|
|
+ HFile, Handle: THandle;
|
|
|
|
+ PBuffer: ^TReparseDataBuffer;
|
|
|
|
+ BytesReturned: DWORD;
|
|
begin
|
|
begin
|
|
|
|
+ SymLinkRec := Default(TUnicodeSymLinkRec);
|
|
|
|
|
|
- Attr:=GetFileAttributesW(PWideChar(FileName));
|
|
|
|
- if Attr <> $ffffffff then
|
|
|
|
- Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) = 0
|
|
|
|
- else
|
|
|
|
- Result:=False;
|
|
|
|
|
|
+ HFile := CreateFileW(PUnicodeChar(FileName), FILE_READ_EA, CShareAny, Nil, OPEN_EXISTING, COpenReparse, 0);
|
|
|
|
+ if HFile <> INVALID_HANDLE_VALUE then
|
|
|
|
+ try
|
|
|
|
+ GetMem(PBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
|
|
|
+ try
|
|
|
|
+ if DeviceIoControl(HFile, FSCTL_GET_REPARSE_POINT, Nil, 0,
|
|
|
|
+ PBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, @BytesReturned, Nil) then begin
|
|
|
|
+ case PBuffer^.ReparseTag of
|
|
|
|
+ IO_REPARSE_TAG_MOUNT_POINT: begin
|
|
|
|
+ SymLinkRec.TargetName := WideCharLenToString(
|
|
|
|
+ @PBuffer^.PathBufferMount[4 { skip start '\??\' } +
|
|
|
|
+ PBuffer^.SubstituteNameOffset div SizeOf(WCHAR)],
|
|
|
|
+ PBuffer^.SubstituteNameLength div SizeOf(WCHAR) - 4);
|
|
|
|
+ end;
|
|
|
|
+ IO_REPARSE_TAG_SYMLINK: begin
|
|
|
|
+ SymLinkRec.TargetName := WideCharLenToString(
|
|
|
|
+ @PBuffer^.PathBufferSym[PBuffer^.PrintNameOffset div SizeOf(WCHAR)],
|
|
|
|
+ PBuffer^.PrintNameLength div SizeOf(WCHAR));
|
|
|
|
+ if (PBuffer^.Flags and SYMLINK_FLAG_RELATIVE) <> 0 then
|
|
|
|
+ SymLinkRec.TargetName := ExpandFileName(ExtractFilePath(FileName) + SymLinkRec.TargetName);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ Handle := FindFirstFileExW(PUnicodeChar(SymLinkRec.TargetName), FindExInfoDefaults , @SymLinkRec.FindData,
|
|
|
|
+ FindExSearchNameMatch, Nil, 0);
|
|
|
|
+ if Handle <> INVALID_HANDLE_VALUE then begin
|
|
|
|
+ Windows.FindClose(Handle);
|
|
|
|
+ SymLinkRec.Attr := SymLinkRec.FindData.dwFileAttributes;
|
|
|
|
+ SymLinkRec.Size := QWord(SymLinkRec.FindData.nFileSizeHigh) shl 32 + QWord(SymLinkRec.FindData.nFileSizeLow);
|
|
|
|
+ end else if RaiseErrorOnMissing then
|
|
|
|
+ raise EDirectoryNotFoundException.Create(SysErrorMessage(GetLastOSError))
|
|
|
|
+ else
|
|
|
|
+ SymLinkRec.TargetName := '';
|
|
|
|
+ end else
|
|
|
|
+ SetLastError(ERROR_REPARSE_TAG_INVALID);
|
|
|
|
+ finally
|
|
|
|
+ FreeMem(PBuffer);
|
|
|
|
+ end;
|
|
|
|
+ finally
|
|
|
|
+ CloseHandle(HFile);
|
|
|
|
+ end;
|
|
|
|
+ Result := SymLinkRec.TargetName <> '';
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+function FileGetSymLinkTarget(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec): Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result := FileGetSymLinkTargetInt(FileName, SymLinkRec, True);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
-Function DirectoryExists (Const Directory : UnicodeString) : Boolean;
|
|
|
|
|
|
+function FileOrDirExists(const FileOrDirName: UnicodeString; CheckDir: Boolean; FollowLink: Boolean): Boolean;
|
|
|
|
+const
|
|
|
|
+ CDirAttributes: array[Boolean] of DWORD = (0, FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
+
|
|
|
|
+ function FoundByEnum: Boolean;
|
|
|
|
+ var
|
|
|
|
+ FindData: TWin32FindDataW;
|
|
|
|
+ Handle: THandle;
|
|
|
|
+ begin
|
|
|
|
+ { FindFirstFileEx is faster than FindFirstFile }
|
|
|
|
+ Handle := FindFirstFileExW(PUnicodeChar(FileOrDirName), FindExInfoDefaults , @FindData,
|
|
|
|
+ FindExSearchNameMatch, Nil, 0);
|
|
|
|
+ Result := Handle <> INVALID_HANDLE_VALUE;
|
|
|
|
+ if Result then begin
|
|
|
|
+ Windows.FindClose(Handle);
|
|
|
|
+ Result := (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = CDirAttributes[CheckDir];
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ function LinkFileExists: Boolean;
|
|
|
|
+ var
|
|
|
|
+ slr: TUnicodeSymLinkRec;
|
|
|
|
+ begin
|
|
|
|
+ Result := FileGetSymLinkTargetInt(FileOrDirName, slr, False) and
|
|
|
|
+ FileOrDirExists(slr.TargetName, CheckDir, False);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+const
|
|
|
|
+ CNotExistsErrors = [
|
|
|
|
+ ERROR_FILE_NOT_FOUND,
|
|
|
|
+ ERROR_PATH_NOT_FOUND,
|
|
|
|
+ ERROR_INVALID_NAME, // protects from names in the form of masks like '*'
|
|
|
|
+ ERROR_INVALID_DRIVE,
|
|
|
|
+ ERROR_NOT_READY,
|
|
|
|
+ ERROR_INVALID_PARAMETER,
|
|
|
|
+ ERROR_BAD_PATHNAME,
|
|
|
|
+ ERROR_BAD_NETPATH,
|
|
|
|
+ ERROR_BAD_NET_NAME
|
|
|
|
+ ];
|
|
var
|
|
var
|
|
- Attr:Dword;
|
|
|
|
|
|
+ Attr : DWord;
|
|
|
|
+begin
|
|
|
|
+ Attr := GetFileAttributesW(PUnicodeChar(FileOrDirName));
|
|
|
|
+ if Attr = INVALID_FILE_ATTRIBUTES then
|
|
|
|
+ Result := not (GetLastError in CNotExistsErrors) and FoundByEnum
|
|
|
|
+ else begin
|
|
|
|
+ Result := (Attr and FILE_ATTRIBUTE_DIRECTORY) = CDirAttributes[CheckDir];
|
|
|
|
+ if Result and FollowLink and ((Attr and FILE_ATTRIBUTE_REPARSE_POINT) <> 0) then
|
|
|
|
+ Result := LinkFileExists;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Function FileExists (Const FileName : UnicodeString; FollowLink : Boolean) : Boolean;
|
|
begin
|
|
begin
|
|
- Attr:=GetFileAttributesW(PWideChar(Directory));
|
|
|
|
- if Attr <> $ffffffff then
|
|
|
|
- Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) > 0
|
|
|
|
- else
|
|
|
|
- Result:=False;
|
|
|
|
|
|
+ Result := FileOrDirExists(FileName, False, FollowLink);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Function DirectoryExists (Const Directory : UnicodeString; FollowLink : Boolean) : Boolean;
|
|
|
|
+begin
|
|
|
|
+ Result := FileOrDirExists(Directory, True, FollowLink);
|
|
end;
|
|
end;
|
|
|
|
|
|
Function FindMatch(var f: TAbstractSearchRec; var Name: UnicodeString) : Longint;
|
|
Function FindMatch(var f: TAbstractSearchRec; var Name: UnicodeString) : Longint;
|
|
@@ -466,7 +599,9 @@ begin
|
|
Rslt.ExcludeAttr:=(not Attr) and ($1e);
|
|
Rslt.ExcludeAttr:=(not Attr) and ($1e);
|
|
{ $1e = faHidden or faSysFile or faVolumeID or faDirectory }
|
|
{ $1e = faHidden or faSysFile or faVolumeID or faDirectory }
|
|
{ FindFirstFile is a Win32 Call }
|
|
{ FindFirstFile is a Win32 Call }
|
|
- Rslt.FindHandle:=FindFirstFileW (PWideChar(Path),Rslt.FindData);
|
|
|
|
|
|
+ Rslt.FindHandle:=FindFirstFileExW(PUnicodeChar(Path), FindExInfoDefaults , @Rslt.FindData,
|
|
|
|
+ FindExSearchNameMatch, Nil, FindFirstAdditionalFlags);
|
|
|
|
+
|
|
If Rslt.FindHandle=Invalid_Handle_value then
|
|
If Rslt.FindHandle=Invalid_Handle_value then
|
|
begin
|
|
begin
|
|
Result:=GetLastError;
|
|
Result:=GetLastError;
|
|
@@ -1265,6 +1400,10 @@ begin
|
|
kernel32dll:=GetModuleHandle('kernel32');
|
|
kernel32dll:=GetModuleHandle('kernel32');
|
|
if kernel32dll<>0 then
|
|
if kernel32dll<>0 then
|
|
GetDiskFreeSpaceEx:=TGetDiskFreeSpaceEx(GetProcAddress(kernel32dll,'GetDiskFreeSpaceExA'));
|
|
GetDiskFreeSpaceEx:=TGetDiskFreeSpaceEx(GetProcAddress(kernel32dll,'GetDiskFreeSpaceExA'));
|
|
|
|
+ if Win32MajorVersion<6 then
|
|
|
|
+ FindExInfoDefaults := FindExInfoStandard; // also searches SFNs. XP only.
|
|
|
|
+ if (Win32MajorVersion>=6) and (Win32MinorVersion>=1) then
|
|
|
|
+ FindFirstAdditionalFlags := FIND_FIRST_EX_LARGE_FETCH; // win7 and 2008R2+
|
|
end;
|
|
end;
|
|
|
|
|
|
Function GetAppConfigDir(Global : Boolean) : String;
|
|
Function GetAppConfigDir(Global : Boolean) : String;
|
|
@@ -1527,6 +1666,6 @@ Initialization
|
|
InitSysConfigDir;
|
|
InitSysConfigDir;
|
|
OnBeep:=@SysBeep;
|
|
OnBeep:=@SysBeep;
|
|
Finalization
|
|
Finalization
|
|
- DoneExceptions;
|
|
|
|
FreeTerminateProcs;
|
|
FreeTerminateProcs;
|
|
|
|
+ DoneExceptions;
|
|
end.
|
|
end.
|