FIX: Copy relative symbolic links under Windows (issue #2171)

(cherry picked from commit 52ee174822)
This commit is contained in:
Alexander Koblov 2025-02-22 13:54:03 +03:00
commit aa50acce68
6 changed files with 56 additions and 16 deletions

View file

@ -95,7 +95,7 @@ type
@param(ALinkName The name of the symbolic link)
@returns(The function returns @true if successful, @false otherwise)
}
function CreateSymLink(const ATargetName, ALinkName: UnicodeString): Boolean;
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.
@ -305,7 +305,7 @@ begin
end;
end;
function CreateSymLink(const ATargetName, ALinkName: UnicodeString): Boolean;
function CreateSymLink(const ATargetName, ALinkName: UnicodeString; Attr: UInt32): Boolean;
var
dwAttributes: DWORD;
lpFilePart: LPWSTR = nil;
@ -324,7 +324,11 @@ begin
AFullPathName:= ATargetName;
end;
end;
dwAttributes:= Windows.GetFileAttributesW(PWideChar(AFullPathName));
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

View file

@ -99,7 +99,7 @@ type
PCopyAttributesResult = ^TCopyAttributesResult;
const
faInvalidAttributes: TFileAttrs = TFileAttrs(-1);
faInvalidAttributes = TFileAttrs(-1);
CopyAttributesOptionCopyAll = [caoCopyAttributes, caoCopyTime, caoCopyOwnership];
{en
@ -308,7 +308,7 @@ function CreateHardLink(const Path, LinkName: String) : Boolean;
@param(LinkName Name of symbolic link)
@returns(The function returns @true if successful, @false otherwise)
}
function CreateSymLink(const Path, LinkName: string) : Boolean;
function CreateSymLink(const Path, LinkName: string; Attr: UInt32 = faInvalidAttributes) : Boolean;
{en
Read destination of symbolic link
@param(LinkName Name of symbolic link)
@ -2020,14 +2020,14 @@ begin
end;
{$ENDIF}
function CreateSymLink(const Path, LinkName: string) : Boolean;
function CreateSymLink(const Path, LinkName: string; Attr: UInt32): Boolean;
{$IFDEF MSWINDOWS}
var
wsPath, wsLinkName: UnicodeString;
begin
wsPath:= CeUtf8ToUtf16(Path);
wsLinkName:= UTF16LongName(LinkName);
Result:= DCNtfsLinks.CreateSymlink(wsPath, wsLinkName);
Result:= DCNtfsLinks.CreateSymlink(wsPath, wsLinkName, Attr);
end;
{$ELSE}
begin

View file

@ -1347,7 +1347,7 @@ begin
LinkTarget := CorrectedLink;
end;
if CreateSymbolicLinkUAC(LinkTarget, AbsoluteTargetFileName) then
if CreateSymbolicLinkUAC(LinkTarget, AbsoluteTargetFileName, aFile.Attributes) then
begin
CopyProperties(aFile, AbsoluteTargetFileName);
if (FMode = fsohmMove) then Result:= DeleteFile(aFile);

View file

@ -37,7 +37,7 @@ function CreateDirectoryUAC(const Directory: String): Boolean;
function RemoveDirectoryUAC(const Directory: String): Boolean;
function DirectoryExistsUAC(const Directory : String): Boolean;
function CreateSymbolicLinkUAC(const Path, LinkName: String) : Boolean;
function CreateSymbolicLinkUAC(const Path, LinkName: String; Attr: UInt32 = faInvalidAttributes) : Boolean;
function CreateHardLinkUAC(const Path, LinkName: String) : Boolean;
type
@ -367,16 +367,16 @@ begin
end;
end;
function CreateSymbolicLinkUAC(const Path, LinkName: String): Boolean;
function CreateSymbolicLinkUAC(const Path, LinkName: String; Attr: UInt32): Boolean;
var
LastError: Integer;
begin
Result:= CreateSymLink(Path, LinkName);
Result:= CreateSymLink(Path, LinkName, Attr);
if (not Result) and ElevationRequired then
begin
LastError:= GetLastOSError;
if RequestElevation(rsElevationRequiredSymLink, LinkName) then
Result:= TWorkerProxy.Instance.CreateSymbolicLink(Path, LinkName)
Result:= TWorkerProxy.Instance.CreateSymbolicLink(Path, LinkName, Attr)
else
SetLastOSError(LastError);
end;

View file

@ -33,6 +33,7 @@ type
function ProcessObject(ACommand: UInt32; const OldName, NewName: String): LongBool;
function ProcessObject(ACommand: UInt32; const ObjectName: String; Attr: UInt32): LongBool;
function ProcessObject(ACommand: UInt32; const ObjectName: String; Mode: Integer): THandle;
function ProcessObject(ACommand: UInt32; const OldName, NewName: String; Attr: UInt32): LongBool;
public
function Terminate: Boolean;
function FileExists(const FileName: String): LongBool; inline;
@ -54,7 +55,7 @@ type
function FindNext(var SearchRec: TSearchRecEx): Integer;
procedure FindClose(var SearchRec: TSearchRecEx);
function CreateHardLink(const Path, LinkName: String): LongBool; inline;
function CreateSymbolicLink(const Path, LinkName: String): LongBool; inline;
function CreateSymbolicLink(const Path, LinkName: String; Attr: UInt32): LongBool; inline;
function CreateDirectory(const Directory: String): LongBool; inline;
function RemoveDirectory(const Directory: String): LongBool; inline;
function DirectoryExists(const Directory: String): LongBool; inline;
@ -327,6 +328,40 @@ begin
end;
end;
function TWorkerProxy.ProcessObject(ACommand: UInt32; const OldName,
NewName: String; Attr: UInt32): LongBool;
var
LastError: Integer;
Stream: TMemoryStream;
begin
Result:= False;
try
Stream:= TMemoryStream.Create;
try
// Write header
Stream.WriteDWord(ACommand);
Stream.Seek(SizeOf(UInt32), soFromCurrent);
// Write arguments
Stream.WriteAnsiString(OldName);
Stream.WriteAnsiString(NewName);
Stream.WriteDWord(Attr);
// Write data size
Stream.Seek(SizeOf(UInt32), soFromBeginning);
Stream.WriteDWord(Stream.Size - SizeOf(UInt32) * 2);
// Send command
FClient.WriteBuffer(Stream.Memory^, Stream.Size);
// Receive command result
FClient.ReadBuffer(Result, SizeOf(Result));
FClient.ReadBuffer(LastError, SizeOf(LastError));
SetLastOSError(LastError);
finally
Stream.Free;
end;
except
on E: Exception do DCDebug(E.Message);
end;
end;
function TWorkerProxy.Terminate: Boolean;
var
Stream: TMemoryStream;
@ -653,9 +688,9 @@ begin
Result:= ProcessObject(RPC_CreateHardLink, Path, LinkName);
end;
function TWorkerProxy.CreateSymbolicLink(const Path, LinkName: String): LongBool;
function TWorkerProxy.CreateSymbolicLink(const Path, LinkName: String; Attr: UInt32): LongBool;
begin
Result:= ProcessObject(RPC_CreateSymbolicLink, Path, LinkName);
Result:= ProcessObject(RPC_CreateSymbolicLink, Path, LinkName, Attr);
end;
function TWorkerProxy.CreateDirectory(const Directory: String): LongBool;

View file

@ -322,8 +322,9 @@ begin
begin
FileName:= ARequest.ReadAnsiString;
NewName:= ARequest.ReadAnsiString;
Attr:= ARequest.ReadDWord;
DCDebug('CreateSymbolicLink ', NewName);
Result:= CreateSymLink(FileName, NewName);
Result:= CreateSymLink(FileName, NewName, Attr);
LastError:= GetLastOSError;
ATransport.WriteBuffer(Result, SizeOf(Result));
ATransport.WriteBuffer(LastError, SizeOf(LastError));