mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
535 lines
19 KiB
ObjectPascal
535 lines
19 KiB
ObjectPascal
{
|
|
Double Commander
|
|
-------------------------------------------------------------------------
|
|
This unit contains functions to work with hard and symbolic links
|
|
on the NTFS file system.
|
|
|
|
Copyright (C) 2012-2025 Alexander Koblov (alexx2000@mail.ru)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
|
}
|
|
|
|
unit DCNtfsLinks;
|
|
|
|
{$mode delphi}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, SysUtils;
|
|
|
|
const
|
|
// CreateSymbolicLink flags
|
|
SYMBOLIC_LINK_FLAG_FILE = 0;
|
|
SYMBOLIC_LINK_FLAG_DIRECTORY = 1;
|
|
// CreateFile flags
|
|
FILE_FLAG_OPEN_REPARSE_POINT = $00200000;
|
|
// DeviceIoControl control codes
|
|
FSCTL_SET_REPARSE_POINT = $000900A4;
|
|
FSCTL_GET_REPARSE_POINT = $000900A8;
|
|
FSCTL_DELETE_REPARSE_POINT = $000900AC;
|
|
// WSL and Cygwin symbolic link
|
|
IO_REPARSE_TAG_LX_SYMLINK = $A000001D;
|
|
|
|
const
|
|
LX_SYMLINK_HEADER_SIZE = 4;
|
|
REPARSE_DATA_HEADER_SIZE = 8;
|
|
MOUNT_POINT_HEADER_SIZE = 8;
|
|
FILE_DOES_NOT_EXIST = DWORD(-1);
|
|
wsLongFileNamePrefix = UnicodeString('\\?\');
|
|
wsNativeFileNamePrefix = UnicodeString('\??\');
|
|
wsNetworkFileNamePrefix = UnicodeString('\??\UNC\');
|
|
|
|
type
|
|
{$packrecords c}
|
|
TSymbolicLinkReparseBuffer = record
|
|
SubstituteNameOffset: USHORT;
|
|
SubstituteNameLength: USHORT;
|
|
PrintNameOffset: USHORT;
|
|
PrintNameLength: USHORT;
|
|
Flags: ULONG;
|
|
PathBuffer: array[0..0] of WCHAR;
|
|
end;
|
|
|
|
TMountPointReparseBuffer = record
|
|
SubstituteNameOffset: USHORT;
|
|
SubstituteNameLength: USHORT;
|
|
PrintNameOffset: USHORT;
|
|
PrintNameLength: USHORT;
|
|
PathBuffer: array[0..0] of WCHAR;
|
|
end;
|
|
|
|
TLxSymlinkReparseBuffer = record
|
|
FileType: DWORD;
|
|
PathBuffer: array[0..0] of AnsiChar;
|
|
end;
|
|
|
|
TGenericReparseBuffer = record
|
|
DataBuffer: array[0..0] of UCHAR;
|
|
end;
|
|
|
|
REPARSE_DATA_BUFFER = record
|
|
ReparseTag: ULONG;
|
|
ReparseDataLength: USHORT;
|
|
Reserved: USHORT;
|
|
case Integer of
|
|
0: (SymbolicLinkReparseBuffer: TSymbolicLinkReparseBuffer);
|
|
1: (MountPointReparseBuffer: TMountPointReparseBuffer);
|
|
2: (LxSymlinkReparseBuffer: TLxSymlinkReparseBuffer);
|
|
3: (GenericReparseBuffer: TGenericReparseBuffer);
|
|
end;
|
|
TReparseDataBuffer = REPARSE_DATA_BUFFER;
|
|
PReparseDataBuffer = ^REPARSE_DATA_BUFFER;
|
|
{$packrecords default}
|
|
|
|
{en
|
|
Creates a symbolic link.
|
|
This function is only supported on the NTFS file system.
|
|
On Windows 2000/XP it works for directories only
|
|
On Windows Vista/Seven it works for directories and files
|
|
(for files it works only with Administrator rights)
|
|
@param(AFileName The name of the existing file)
|
|
@param(ALinkName The name of the symbolic link)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function CreateSymLink(const ATargetName, ALinkName: UnicodeString; Attr: UInt32): Boolean;
|
|
{en
|
|
Established a hard link beetwen an existing file and new file. This function
|
|
is only supported on the NTFS file system, and only for files, not directories.
|
|
@param(AFileName The name of the existing file)
|
|
@param(ALinkName The name of the new hard link)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function CreateHardLink(const AFileName, ALinkName: UnicodeString): Boolean;
|
|
{en
|
|
Reads a symbolic link target.
|
|
This function is only supported on the NTFS file system.
|
|
@param(aSymlinkFileName The name of the symbolic link)
|
|
@param(aTargetFileName The name of the target file/directory)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function ReadSymLink(const aSymlinkFileName: UnicodeString; out aTargetFileName: UnicodeString): Boolean;
|
|
{en
|
|
Creates a WSL/Cygwin symbolic link.
|
|
@param(aTargetFileName The name of the existing file)
|
|
@param(aSymlinkFileName The name of the symbolic link)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function CreateSymLinkUnix(const aTargetFileName: String; const aSymlinkFileName: UnicodeString): Boolean;
|
|
|
|
implementation
|
|
|
|
const
|
|
ERROR_DIRECTORY_NOT_SUPPORTED = 336;
|
|
|
|
type
|
|
TCreateSymbolicLinkW = function(
|
|
pwcSymlinkFileName,
|
|
pwcTargetFileName: PWideChar;
|
|
dwFlags: DWORD): BOOL; stdcall;
|
|
|
|
TCreateHardLinkW = function (
|
|
lpFileName,
|
|
lpExistingFileName: LPCWSTR;
|
|
lpSecurityAttributes: LPSECURITY_ATTRIBUTES): BOOL; stdcall;
|
|
|
|
var
|
|
HasNewApi: Boolean = False;
|
|
MayCreateSymLink: Boolean = False;
|
|
CreateHardLinkW: TCreateHardLinkW = nil;
|
|
CreateSymbolicLinkW: TCreateSymbolicLinkW = nil;
|
|
|
|
function _CreateHardLink_New(AFileName : UnicodeString; ALinkName: UnicodeString): Boolean;
|
|
begin
|
|
if Assigned(CreateHardLinkW) then
|
|
Result:= CreateHardLinkW(PWideChar(ALinkName), PWideChar(AFileName), nil)
|
|
else begin
|
|
Result:= False;
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
end;
|
|
end;
|
|
|
|
function _CreateHardLink_Old(aExistingFileName, aFileName: UnicodeString): Boolean;
|
|
var
|
|
hFile: THandle;
|
|
lpBuffer: TWin32StreamId;
|
|
wcFileName: array[0..MAX_PATH] of WideChar;
|
|
dwNumberOfBytesWritten: DWORD = 0;
|
|
lpContext: LPVOID = nil;
|
|
lpFilePart: LPWSTR = nil;
|
|
begin
|
|
Result:= GetFullPathNameW(PWideChar(aFileName), MAX_PATH, wcFileName, lpFilePart) > 0;
|
|
if Result then
|
|
begin
|
|
hFile:= CreateFileW(PWideChar(aExistingFileName),
|
|
GENERIC_READ or GENERIC_WRITE,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil, OPEN_EXISTING, 0, 0);
|
|
Result:= (hFile <> INVALID_HANDLE_VALUE);
|
|
end;
|
|
if Result then
|
|
try
|
|
ZeroMemory(@lpBuffer, SizeOf(TWin32StreamId));
|
|
with lpBuffer do
|
|
begin
|
|
dwStreamId:= BACKUP_LINK;
|
|
Size.LowPart:= (Length(aFileName) + 1) * SizeOf(WideChar);
|
|
end;
|
|
// Write stream header
|
|
Result:= BackupWrite(hFile,
|
|
@lpBuffer,
|
|
SizeOf(TWin32StreamId) - SizeOf(PWideChar),
|
|
dwNumberOfBytesWritten,
|
|
False,
|
|
False,
|
|
lpContext);
|
|
if not Result then Exit;
|
|
// Write file name buffer
|
|
Result:= BackupWrite(hFile,
|
|
@wcFileName,
|
|
lpBuffer.Size.LowPart,
|
|
dwNumberOfBytesWritten,
|
|
False,
|
|
False,
|
|
lpContext);
|
|
if not Result then Exit;
|
|
// Finish write operation
|
|
Result:= BackupWrite(hFile,
|
|
nil,
|
|
0,
|
|
dwNumberOfBytesWritten,
|
|
True,
|
|
False,
|
|
lpContext);
|
|
finally
|
|
CloseHandle(hFile);
|
|
end;
|
|
end;
|
|
|
|
function CreateHardLink(const AFileName, ALinkName: UnicodeString): Boolean;
|
|
var
|
|
dwAttributes: DWORD;
|
|
begin
|
|
dwAttributes := Windows.GetFileAttributesW(PWideChar(AFileName));
|
|
if dwAttributes = FILE_DOES_NOT_EXIST then Exit(False);
|
|
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
|
|
begin
|
|
SetLastError(ERROR_DIRECTORY_NOT_SUPPORTED);
|
|
Exit(False);
|
|
end;
|
|
|
|
dwAttributes := Windows.GetFileAttributesW(PWideChar(ALinkName));
|
|
if dwAttributes <> FILE_DOES_NOT_EXIST then
|
|
begin
|
|
SetLastError(ERROR_FILE_EXISTS);
|
|
Exit(False);
|
|
end;
|
|
|
|
if HasNewApi then
|
|
Result:= _CreateHardLink_New(AFileName, ALinkName)
|
|
else
|
|
Result:= _CreateHardLink_Old(AFileName, ALinkName)
|
|
end;
|
|
|
|
function _CreateSymLink_New(const ATargetFileName, ASymlinkFileName: UnicodeString; dwFlags: DWORD): Boolean;
|
|
begin
|
|
if not Assigned(CreateSymbolicLinkW) then
|
|
begin
|
|
Result:= False;
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
end
|
|
// CreateSymbolicLinkW under Windows 10 1903 does not return error if user doesn't have
|
|
// SeCreateSymbolicLinkPrivilege, so we make manual check and return error in this case
|
|
else begin
|
|
if MayCreateSymLink then
|
|
Result:= CreateSymbolicLinkW(PWideChar(ASymlinkFileName), PWideChar(ATargetFileName), dwFlags)
|
|
else begin
|
|
Result:= False;
|
|
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
|
end
|
|
end;
|
|
end;
|
|
|
|
function _CreateSymLink_Old(aTargetFileName, aSymlinkFileName: UnicodeString): Boolean;
|
|
var
|
|
hDevice: THandle;
|
|
lpInBuffer: PReparseDataBuffer;
|
|
dwLastError,
|
|
nInBufferSize,
|
|
dwPathBufferSize: DWORD;
|
|
wsNativeFileName: UnicodeString;
|
|
lpBytesReturned: DWORD = 0;
|
|
begin
|
|
Result:= CreateDirectoryW(PWideChar(aSymlinkFileName), nil);
|
|
if Result then
|
|
try
|
|
hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
|
|
GENERIC_WRITE, 0, nil, OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
|
if hDevice = INVALID_HANDLE_VALUE then
|
|
begin
|
|
dwLastError:= GetLastError;
|
|
Exit(False);
|
|
end;
|
|
if Pos(wsLongFileNamePrefix, aTargetFileName) <> 1 then
|
|
wsNativeFileName:= wsNativeFileNamePrefix + aTargetFileName
|
|
else begin
|
|
wsNativeFileName:= wsNativeFileNamePrefix + Copy(aTargetFileName, 5, MaxInt);
|
|
end;
|
|
// File name length with trailing zero and zero for empty PrintName
|
|
dwPathBufferSize:= Length(wsNativeFileName) * SizeOf(WideChar) + 4;
|
|
nInBufferSize:= REPARSE_DATA_HEADER_SIZE + MOUNT_POINT_HEADER_SIZE + dwPathBufferSize;
|
|
lpInBuffer:= GetMem(nInBufferSize);
|
|
ZeroMemory(lpInBuffer, nInBufferSize);
|
|
with lpInBuffer^, lpInBuffer^.MountPointReparseBuffer do
|
|
begin
|
|
ReparseTag:= IO_REPARSE_TAG_MOUNT_POINT;
|
|
ReparseDataLength:= MOUNT_POINT_HEADER_SIZE + dwPathBufferSize;
|
|
SubstituteNameLength:= Length(wsNativeFileName) * SizeOf(WideChar);
|
|
PrintNameOffset:= SubstituteNameOffset + SubstituteNameLength + SizeOf(WideChar);
|
|
CopyMemory(@PathBuffer[0], @wsNativeFileName[1], SubstituteNameLength);
|
|
end;
|
|
Result:= DeviceIoControl(hDevice, // handle to file or directory
|
|
FSCTL_SET_REPARSE_POINT, // dwIoControlCode
|
|
lpInBuffer, // input buffer
|
|
nInBufferSize, // size of input buffer
|
|
nil, // lpOutBuffer
|
|
0, // nOutBufferSize
|
|
lpBytesReturned, // lpBytesReturned
|
|
nil); // OVERLAPPED structure
|
|
if not Result then dwLastError:= GetLastError;
|
|
FreeMem(lpInBuffer);
|
|
CloseHandle(hDevice);
|
|
finally
|
|
if not Result then
|
|
begin
|
|
RemoveDirectoryW(PWideChar(aSymlinkFileName));
|
|
SetLastError(dwLastError);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CreateSymLink(const ATargetName, ALinkName: UnicodeString; Attr: UInt32): Boolean;
|
|
var
|
|
dwAttributes: DWORD;
|
|
lpFilePart: LPWSTR = nil;
|
|
AFileName, AFullPathName: UnicodeString;
|
|
begin
|
|
Result:= False;
|
|
if (Length(ATargetName) > 1) and CharInSet(ATargetName[2], [':', '\']) then
|
|
AFullPathName:= ATargetName
|
|
else begin
|
|
SetLength(AFullPathName, MaxSmallint);
|
|
AFileName:= ExtractFilePath(ALinkName) + ATargetName;
|
|
dwAttributes:= GetFullPathNameW(PWideChar(AFileName), MaxSmallint, PWideChar(AFullPathName), lpFilePart);
|
|
if dwAttributes > 0 then
|
|
SetLength(AFullPathName, dwAttributes)
|
|
else begin
|
|
AFullPathName:= ATargetName;
|
|
end;
|
|
end;
|
|
if (Attr <> FILE_DOES_NOT_EXIST) then
|
|
dwAttributes:= Attr
|
|
else begin
|
|
dwAttributes:= Windows.GetFileAttributesW(PWideChar(AFullPathName));
|
|
end;
|
|
if dwAttributes = FILE_DOES_NOT_EXIST then Exit;
|
|
if HasNewApi = False then
|
|
begin
|
|
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
|
|
Result:= _CreateSymLink_Old(AFullPathName, ALinkName)
|
|
else
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
end
|
|
else begin
|
|
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
|
|
Result:= _CreateSymLink_New(ATargetName, ALinkName, SYMBOLIC_LINK_FLAG_FILE)
|
|
else begin
|
|
if (not MayCreateSymLink) and (Pos('\\', AFullPathName) = 0) then
|
|
Result:= _CreateSymLink_Old(AFullPathName, ALinkName)
|
|
else begin
|
|
Result:= _CreateSymLink_New(ATargetName, ALinkName, SYMBOLIC_LINK_FLAG_DIRECTORY);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CreateSymLinkUnix(const aTargetFileName: String; const aSymlinkFileName: UnicodeString): Boolean;
|
|
var
|
|
hDevice: THandle;
|
|
dwLastError: DWORD;
|
|
nInBufferSize: DWORD;
|
|
dwPathBufferSize: DWORD;
|
|
lpBytesReturned: DWORD = 0;
|
|
lpInBuffer: PReparseDataBuffer;
|
|
begin
|
|
hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
|
|
GENERIC_WRITE, 0, nil, CREATE_NEW,
|
|
FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
|
if hDevice = INVALID_HANDLE_VALUE then Exit(False);
|
|
dwPathBufferSize:= Length(aTargetFileName);
|
|
nInBufferSize:= REPARSE_DATA_HEADER_SIZE + LX_SYMLINK_HEADER_SIZE + dwPathBufferSize;
|
|
lpInBuffer:= GetMem(nInBufferSize);
|
|
ZeroMemory(lpInBuffer, nInBufferSize);
|
|
with lpInBuffer^, lpInBuffer^.LxSymlinkReparseBuffer do
|
|
begin
|
|
FileType:= 2; // symbolic link
|
|
ReparseTag:= IO_REPARSE_TAG_LX_SYMLINK;
|
|
ReparseDataLength:= LX_SYMLINK_HEADER_SIZE + dwPathBufferSize;
|
|
CopyMemory(@PathBuffer[0], @aTargetFileName[1], Length(aTargetFileName));
|
|
end;
|
|
Result:= DeviceIoControl(hDevice, // handle to file or directory
|
|
FSCTL_SET_REPARSE_POINT, // dwIoControlCode
|
|
lpInBuffer, // input buffer
|
|
nInBufferSize, // size of input buffer
|
|
nil, // lpOutBuffer
|
|
0, // nOutBufferSize
|
|
lpBytesReturned, // lpBytesReturned
|
|
nil); // OVERLAPPED structure
|
|
// File system does not support reparse points
|
|
// Create a normal file with the link target inside
|
|
if (not Result) and (GetLastError = ERROR_INVALID_FUNCTION) then
|
|
begin
|
|
Result:= (FileWrite(hDevice, aTargetFileName[1], dwPathBufferSize) = dwPathBufferSize);
|
|
if Result then SetFileAttributesW(PWideChar(aSymlinkFileName), FILE_ATTRIBUTE_SYSTEM);
|
|
end;
|
|
if not Result then dwLastError:= GetLastError;
|
|
FreeMem(lpInBuffer);
|
|
CloseHandle(hDevice);
|
|
if not Result then
|
|
begin
|
|
DeleteFileW(PWideChar(aSymlinkFileName));
|
|
SetLastError(dwLastError);
|
|
end;
|
|
end;
|
|
|
|
function ReadSymLink(const aSymlinkFileName: UnicodeString; out aTargetFileName: UnicodeString): Boolean;
|
|
var
|
|
L: Integer;
|
|
hDevice: THandle;
|
|
dwFileAttributes: DWORD;
|
|
caOutBuffer: array[0..MaxSmallint] of Byte;
|
|
lpOutBuffer: TReparseDataBuffer absolute caOutBuffer;
|
|
pwcTargetFileName: PWideChar;
|
|
lpBytesReturned: DWORD = 0;
|
|
dwFlagsAndAttributes: DWORD;
|
|
begin
|
|
dwFileAttributes:= GetFileAttributesW(PWideChar(aSymlinkFileName));
|
|
Result:= dwFileAttributes <> FILE_DOES_NOT_EXIST;
|
|
if Result then
|
|
begin
|
|
if (dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
|
|
dwFlagsAndAttributes:= FILE_FLAG_OPEN_REPARSE_POINT
|
|
else
|
|
dwFlagsAndAttributes:= FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT;
|
|
// Open reparse point
|
|
hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
|
|
0, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
nil, OPEN_EXISTING, dwFlagsAndAttributes, 0);
|
|
Result:= hDevice <> INVALID_HANDLE_VALUE;
|
|
if not Result then Exit;
|
|
Result:= DeviceIoControl(hDevice, // handle to file or directory
|
|
FSCTL_GET_REPARSE_POINT, // dwIoControlCode
|
|
nil, // input buffer
|
|
0, // size of input buffer
|
|
@caOutBuffer, // lpOutBuffer
|
|
SizeOf(caOutBuffer), // nOutBufferSize
|
|
lpBytesReturned, // lpBytesReturned
|
|
nil); // OVERLAPPED structure
|
|
CloseHandle(hDevice);
|
|
if Result then
|
|
begin
|
|
case lpOutBuffer.ReparseTag of
|
|
IO_REPARSE_TAG_SYMLINK:
|
|
with lpOutBuffer.SymbolicLinkReparseBuffer do
|
|
begin
|
|
pwcTargetFileName:= @PathBuffer[0];
|
|
pwcTargetFileName:= pwcTargetFileName + SubstituteNameOffset div SizeOf(WideChar);
|
|
SetLength(aTargetFileName, SubstituteNameLength div SizeOf(WideChar));
|
|
CopyMemory(PWideChar(aTargetFileName), pwcTargetFileName, SubstituteNameLength);
|
|
end;
|
|
IO_REPARSE_TAG_MOUNT_POINT:
|
|
with lpOutBuffer.MountPointReparseBuffer do
|
|
begin
|
|
pwcTargetFileName:= @PathBuffer[0];
|
|
pwcTargetFileName:= pwcTargetFileName + SubstituteNameOffset div SizeOf(WideChar);
|
|
SetLength(aTargetFileName, SubstituteNameLength div SizeOf(WideChar));
|
|
CopyMemory(PWideChar(aTargetFileName), pwcTargetFileName, SubstituteNameLength);
|
|
end;
|
|
IO_REPARSE_TAG_LX_SYMLINK:
|
|
with lpOutBuffer.LxSymlinkReparseBuffer do
|
|
begin
|
|
L:= lpOutBuffer.ReparseDataLength - SizeOf(FileType);
|
|
SetLength(aTargetFileName, L + 1);
|
|
SetLength(aTargetFileName, MultiByteToWideChar(CP_UTF8, 0, @PathBuffer[0], L, PWideChar(aTargetFileName), L + 1));
|
|
end;
|
|
end;
|
|
if Pos(wsNetworkFileNamePrefix, aTargetFileName) = 1 then
|
|
Delete(aTargetFileName, 2, Length(wsNetworkFileNamePrefix) - 2)
|
|
else if Pos(wsNativeFileNamePrefix, aTargetFileName) = 1 then
|
|
Delete(aTargetFileName, 1, Length(wsNativeFileNamePrefix));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function MayCreateSymbolicLink: Boolean;
|
|
const
|
|
SE_CREATE_SYMBOLIC_LINK_NAME = 'SeCreateSymbolicLinkPrivilege';
|
|
var
|
|
I: Integer;
|
|
hProcess: HANDLE;
|
|
dwLength: DWORD = 0;
|
|
seCreateSymbolicLink: LUID = 0;
|
|
TokenInformation: array [0..1023] of Byte;
|
|
Privileges: TTokenPrivileges absolute TokenInformation;
|
|
begin
|
|
hProcess:= GetCurrentProcess();
|
|
if (OpenProcessToken(hProcess, TOKEN_READ, hProcess)) then
|
|
try
|
|
if (LookupPrivilegeValueW(nil, SE_CREATE_SYMBOLIC_LINK_NAME, seCreateSymbolicLink)) then
|
|
begin
|
|
if (GetTokenInformation(hProcess, TokenPrivileges, @Privileges, SizeOf(TokenInformation), dwLength)) then
|
|
begin
|
|
{$PUSH}{$R-}
|
|
for I:= 0 to Int32(Privileges.PrivilegeCount) - 1 do
|
|
begin
|
|
if Privileges.Privileges[I].Luid = seCreateSymbolicLink then
|
|
Exit(True);
|
|
end;
|
|
{$POP}
|
|
end;
|
|
end;
|
|
finally
|
|
CloseHandle(hProcess);
|
|
end;
|
|
Result:= False;
|
|
end;
|
|
|
|
procedure Initialize;
|
|
var
|
|
AHandle: HMODULE;
|
|
begin
|
|
MayCreateSymLink:= MayCreateSymbolicLink;
|
|
HasNewApi:= (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 6);
|
|
if HasNewApi then begin
|
|
AHandle:= GetModuleHandle('kernel32.dll');
|
|
CreateHardLinkW:= TCreateHardLinkW(GetProcAddress(AHandle, 'CreateHardLinkW'));
|
|
CreateSymbolicLinkW:= TCreateSymbolicLinkW(GetProcAddress(AHandle, 'CreateSymbolicLinkW'));
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
Initialize;
|
|
|
|
end.
|