From ec601d947f0a3e56b7f6af9acb2a9bdde603ea86 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 5 Nov 2019 17:54:11 +0100 Subject: [PATCH] installer: use locale-independent way to create Cygwin symlinks Since https://github.com/git-for-windows/build-extra/pull/255, we create the `/dev/{fd,stdin,stdout,stderr}` symlinks in the installer explicitly, without relying on Git Bash's initial `post-install` scripts. However, the method chosen to create those (Cygwin-style) symlinks seems to work only in US locales. Let's jump through another hoop and make sure that it works also in non-US locales. The approach implemented in this patch was inspired by https://stackoverflow.com/questions/38617829/writing-binary-file-in-inno-setup This fixes https://github.com/git-for-windows/git/issues/2388 Signed-off-by: Johannes Schindelin --- installer/install.iss | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/installer/install.iss b/installer/install.iss index e86df630ff..b68bf7bb7d 100644 --- a/installer/install.iss +++ b/installer/install.iss @@ -2368,10 +2368,25 @@ function GetFileAttributes(Path:PAnsiChar):DWORD; function SetFileAttributes(Path:PAnsiChar;dwFileAttributes:DWORD):BOOL; external 'SetFileAttributesA@kernel32.dll stdcall'; +function CryptStringToBinary(sz:string;cch:LongWord;flags:LongWord;binary:string;var size:LongWord;skip:LongWord;flagsused:LongWord):Integer; +external 'CryptStringToBinaryW@crypt32.dll stdcall'; + +const + CRYPT_STRING_HEX = $04; + HEX_CHARS = '0123456789abcdef'; + +function CharToHex(C:Integer):string; +begin + Result:=HEX_CHARS[((C div 16) and 15)+1]+HEX_CHARS[(C and 15)+1]; +end; + function CreateCygwinSymlink(SymlinkPath,TargetPath:String):Boolean; var Attribute:DWord; i:Integer; + Hex,Buffer:string; + Stream:TStream; + Size:LongWord; begin Result:=True; @@ -2379,13 +2394,24 @@ begin for i:=Length(TargetPath) downto 1 do TargetPath:=Copy(TargetPath,1,i)+#0+Copy(TargetPath,i+1,Length(TargetPath)-i); - // insert `!\xff\xfe` prefix, and append `\0\0` - TargetPath:='!'+#255+#254+TargetPath+#0+#0; + Hex:='213c73796d6c696e6b3efffe'; // "!\xff\xfe" + for i:=1 to Length(TargetPath) do + Hex:=Hex+CharToHex(Ord(TargetPath[i])); // append wide characters as hex + Hex:=Hex+'0000'; // append a wide NUL // write the file - if not SaveStringToFile(SymlinkPath,TargetPath,False) then begin - LogError('Could not write "'+SymlinkPath+'"'); + Stream:=TFileStream.Create(SymlinkPath,fmCreate); + try + Size:=Length(Hex) div 2; + SetLength(Buffer,Size); + if (CryptStringToBinary(Hex,Length(Hex),CRYPT_STRING_HEX,Buffer,Size,0,0)=0) or (Size<>Length(Hex) div 2) then + RaiseException('could not decode hex '+Hex); + Stream.WriteBuffer(Buffer,Size); + except + LogError('Could not write "'+SymlinkPath+'" '+GetExceptionMessage()); Result:=False; + finally + Stream.Free end; // Set system bit (required for Cygwin to interpret this as a symlink)