|
@@ -408,8 +408,83 @@ end;
|
|
|
|
|
|
|
|
|
|
function FileGetSymLinkTarget(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec): Boolean;
|
|
function FileGetSymLinkTarget(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec): 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
|
|
|
|
+ HFile, Handle: THandle;
|
|
|
|
+ PBuffer: ^TReparseDataBuffer;
|
|
|
|
+ BytesReturned: DWORD;
|
|
begin
|
|
begin
|
|
- Result := False;
|
|
|
|
|
|
+ SymLinkRec := Default(TUnicodeSymLinkRec);
|
|
|
|
+
|
|
|
|
+ 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^.PrintNameOffset div SizeOf(WCHAR));
|
|
|
|
+ if (PBuffer^.Flags and SYMLINK_FLAG_RELATIVE) <> 0 then
|
|
|
|
+ SymLinkRec.TargetName := ExpandFileName(ExtractFilePath(FileName) + SymLinkRec.TargetName);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ Handle := FindFirstFileExW(PUnicodeChar(FileName), FindExInfoBasic, @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
|
|
|
|
+ SymLinkRec.TargetName := '';
|
|
|
|
+ end else
|
|
|
|
+ SetLastError(ERROR_REPARSE_TAG_INVALID);
|
|
|
|
+ finally
|
|
|
|
+ FreeMem(PBuffer);
|
|
|
|
+ end;
|
|
|
|
+ finally
|
|
|
|
+ CloseHandle(HFile);
|
|
|
|
+ end;
|
|
|
|
+ Result := SymLinkRec.TargetName <> '';
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|