From 538fee8e5435d078efeb42cabd64523299c3ea46 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 9 Sep 2022 22:59:14 +0100 Subject: [PATCH] installer: CustomActions: InstallJunctions --- .../installer/CustomActions/CustomActions.cpp | 186 +++++++++++++++++- .../installer/CustomActions/CustomActions.def | 1 + build/VStudio/installer/Product.wxs | 4 +- 3 files changed, 182 insertions(+), 9 deletions(-) diff --git a/build/VStudio/installer/CustomActions/CustomActions.cpp b/build/VStudio/installer/CustomActions/CustomActions.cpp index db33920e..28e924af 100644 --- a/build/VStudio/installer/CustomActions/CustomActions.cpp +++ b/build/VStudio/installer/CustomActions/CustomActions.cpp @@ -167,14 +167,17 @@ LExit: return WcaFinalize(err); } -static DWORD CreateSymlink(PWSTR Symlink, PWSTR Target); +static DWORD MakeSymlink(PWSTR Symlink, PWSTR Target); +static DWORD MakeJunction(PWSTR Junction, PWSTR Target); +static DWORD CreateJunction(PWSTR Junction, PWSTR Target); static DWORD RemoveFile(PWSTR FileName); DWORD InstallSymlinks(int Argc, PWSTR *Argv) { - /* usage: InstallSymlinks SourceDir TargetDir Name... */ + /* usage: InstallSymlinks/InstallJunctions SourceDir TargetDir Name... */ DWORD Result; + BOOL Junctions; PWSTR SourceDir, TargetDir; WCHAR SourcePath[MAX_PATH], TargetPath[MAX_PATH]; int SourceDirLen, TargetDirLen, Len; @@ -185,6 +188,7 @@ DWORD InstallSymlinks(int Argc, PWSTR *Argv) goto exit; } + Junctions = 0 == lstrcmpW(L"InstallJunctions", Argv[0]); SourceDir = Argv[1]; TargetDir = Argv[2]; SourceDirLen = lstrlenW(SourceDir); @@ -207,10 +211,13 @@ DWORD InstallSymlinks(int Argc, PWSTR *Argv) memcpy(TargetPath + TargetDirLen, Argv[Argi], Len * sizeof(WCHAR)); TargetPath[TargetDirLen + Len] = L'\0'; - Result = CreateSymlink(SourcePath, TargetPath); + if (!Junctions) + Result = MakeSymlink(SourcePath, TargetPath); + else + Result = MakeJunction(SourcePath, TargetPath); #if 0 WCHAR MessageBuf[1024]; - wsprintfW(MessageBuf, L"CreateSymlink(\"%s\", \"%s\") = %lu", SourcePath, TargetPath, Result); + wsprintfW(MessageBuf, L"MakeSymlink(\"%s\", \"%s\") = %lu", SourcePath, TargetPath, Result); MessageBoxW(0, MessageBuf, L"TRACE", MB_OK); #endif if (ERROR_SUCCESS != Result) @@ -223,6 +230,11 @@ exit: return Result; } +DWORD InstallJunctions(int Argc, PWSTR *Argv) +{ + return InstallSymlinks(Argc, Argv); +} + DWORD RemoveFiles(int Argc, PWSTR *Argv) { /* usage: RemoveFiles Dir Name... */ @@ -270,11 +282,13 @@ exit: return Result; } -static DWORD CreateSymlink(PWSTR Symlink, PWSTR Target) +static DWORD MakeSymlink(PWSTR Symlink, PWSTR Target) { DWORD Result; DWORD FileAttributes, Flags; + RemoveFile(Symlink); + FileAttributes = GetFileAttributesW(Target); if (INVALID_FILE_ATTRIBUTES == FileAttributes) { @@ -283,9 +297,161 @@ static DWORD CreateSymlink(PWSTR Symlink, PWSTR Target) } Flags = 0 != (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; - RemoveFile(Symlink); - if (!CreateSymbolicLinkW(Symlink, Target, Flags)) + { + Result = GetLastError(); + RemoveFile(Symlink); + goto exit; + } + + Result = ERROR_SUCCESS; + +exit: + return Result; +} + +static DWORD MakeJunction(PWSTR Junction, PWSTR Target) +{ + DWORD Result; + DWORD FileAttributes; + + RemoveFile(Junction); + + FileAttributes = GetFileAttributesW(Target); + if (INVALID_FILE_ATTRIBUTES == FileAttributes) + { + Result = GetLastError(); + goto exit; + } + if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + Result = ERROR_DIRECTORY; + goto exit; + } + + Result = CreateJunction(Junction, Target); + if (ERROR_SUCCESS != Result) + { + RemoveFile(Junction); + goto exit; + } + + Result = ERROR_SUCCESS; + +exit: + return Result; +} + +static DWORD CreateJunction(PWSTR Junction, PWSTR Target) +{ + /* + * The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers. + */ + typedef struct _REPARSE_DATA_BUFFER + { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; + } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + const LONG REPARSE_DATA_BUFFER_HEADER_SIZE = + FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer); + const DWORD FSCTL_SET_REPARSE_POINT = 0x000900a4; + + DWORD Result; + HANDLE Handle = INVALID_HANDLE_VALUE; + USHORT TargetLength, ReparseDataLength; + PREPARSE_DATA_BUFFER ReparseData = 0; + PWSTR PathBuffer; + DWORD Bytes; + + if (!( + ((L'A' <= Target[0] && Target[0] <= L'Z') || (L'a' <= Target[0] && Target[0] <= L'z')) && + L':' == Target[1] + )) + { + Result = ERROR_INVALID_NAME; + goto exit; + } + + Handle = CreateFileW(Junction, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + CREATE_NEW, + FILE_ATTRIBUTE_DIRECTORY | + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, + 0); + if (INVALID_HANDLE_VALUE == Handle) + { + Result = GetLastError(); + goto exit; + } + + TargetLength = (USHORT)lstrlenW(Target) * sizeof(WCHAR); + ReparseDataLength = (USHORT)( + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer)) + + 4 * sizeof(WCHAR) + 2 * (TargetLength + sizeof(WCHAR)); + ReparseData = (PREPARSE_DATA_BUFFER) + HeapAlloc(GetProcessHeap(), 0, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength); + if (0 == ReparseData) + { + Result = ERROR_NO_SYSTEM_RESOURCES; + goto exit; + } + + ReparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + ReparseData->ReparseDataLength = ReparseDataLength; + ReparseData->Reserved = 0; + ReparseData->MountPointReparseBuffer.SubstituteNameOffset = 0; + ReparseData->MountPointReparseBuffer.SubstituteNameLength = + 4 * sizeof(WCHAR) + TargetLength; + ReparseData->MountPointReparseBuffer.PrintNameOffset = + ReparseData->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR); + ReparseData->MountPointReparseBuffer.PrintNameLength = + TargetLength; + + PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer; + PathBuffer[0] = L'\\'; + PathBuffer[1] = L'?'; + PathBuffer[2] = L'?'; + PathBuffer[3] = L'\\'; + memcpy(PathBuffer + 4, Target, TargetLength); + PathBuffer[4 + TargetLength / sizeof(WCHAR)] = L'\0'; + + PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer + + (ReparseData->MountPointReparseBuffer.PrintNameOffset) / sizeof(WCHAR); + memcpy(PathBuffer, Target, TargetLength); + PathBuffer[TargetLength / sizeof(WCHAR)] = L'\0'; + + if (!DeviceIoControl(Handle, FSCTL_SET_REPARSE_POINT, + ReparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseData->ReparseDataLength, + 0, 0, + &Bytes, 0)) { Result = GetLastError(); goto exit; @@ -294,6 +460,12 @@ static DWORD CreateSymlink(PWSTR Symlink, PWSTR Target) Result = ERROR_SUCCESS; exit: + if (INVALID_HANDLE_VALUE != Handle) + CloseHandle(Handle); + + if (0 != ReparseData) + HeapFree(GetProcessHeap(), 0, ReparseData); + return Result; } diff --git a/build/VStudio/installer/CustomActions/CustomActions.def b/build/VStudio/installer/CustomActions/CustomActions.def index 50d7d6d8..c3670a5d 100644 --- a/build/VStudio/installer/CustomActions/CustomActions.def +++ b/build/VStudio/installer/CustomActions/CustomActions.def @@ -3,4 +3,5 @@ EXPORTS ServiceRunning DeferredAction InstallSymlinks + InstallJunctions RemoveFiles diff --git a/build/VStudio/installer/Product.wxs b/build/VStudio/installer/Product.wxs index cefbfa1c..b62f648d 100644 --- a/build/VStudio/installer/Product.wxs +++ b/build/VStudio/installer/Product.wxs @@ -1074,7 +1074,7 @@