Skip to content
81 changes: 81 additions & 0 deletions PC/launcher2.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,21 @@ findArgv0End(const wchar_t *buffer, int bufferLength)
/******************************************************************************\
*** COMMAND-LINE PARSING ***
\******************************************************************************/
// Adapted from https://stackoverflow.com/a/65583702
typedef struct AppExecLinkFile { // For tag IO_REPARSE_TAG_APPEXECLINK
DWORD reparseTag;
WORD reparseDataLength;
WORD reserved;
ULONG version;
wchar_t stringList[MAX_PATH * 4]; // Multistring (Consecutive UTF-16 strings each ending with a NUL)
/* There are normally 4 strings here. Ex:
Package ID: L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"
Entry Point: L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe!PythonRedirector"
Executable: L"C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.17.106910_x64__8wekyb3d8bbwe\AppInstallerPythonRedirector.exe"
Applic. Type: L"0" // Integer as ASCII. "0" = Desktop bridge application; Else sandboxed UWP application
*/
} AppExecLinkFile;



int
Expand Down Expand Up @@ -826,6 +841,72 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength)
return RC_BAD_VIRTUAL_PATH;
}

// Make sure we didn't find a reparse point that will open the Microsoft Store
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start by refactoring all this out into another function ensure_no_redirector_stub that either returns 0 (okay) or the RC constant that should be returned from here.

// If we did, pretend there was no shebang and let normal handling take over
WIN32_FIND_DATAW findData;
HANDLE hFind = FindFirstFileW(buffer, &findData);
if (!hFind) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
debug(L"# %s on disappeared\n", buffer);
// The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over
RC_NO_SHEBANG;
}

// Other errors should cause us to break
winerror(0, L"Failed to find %s on PATH\n", filename);
return RC_BAD_VIRTUAL_PATH;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to FindClose here and not worry about cleaning it up later.

if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After refactoring, use this check to exit early from the new function. That will keep the overall indent level lower for the rest of it.

const size_t bufSize = sizeof(AppExecLinkFile);
wchar_t appExecLinkBuf[sizeof(AppExecLinkFile)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't necessary - just allocate the struct:

AppExecLinkFile appExecLink;

And then use sizeof(appExecLink) anywhere we need it, which ought to only be the DeviceIOControl call.


HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (!hReparsePoint) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
debug(L"# %s on disappeared\n", buffer);
// The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over
RC_NO_SHEBANG;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this error adds anything - we could lose this race at any time, so probably best to just return RC_NO_SHEBANG for them all and it can fail if/when we try to launch it.


// Other errors should cause us to break
winerror(0, L"Failed to find %s on PATH\n", filename);
return RC_BAD_VIRTUAL_PATH;
}

if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, appExecLinkBuf, bufSize, NULL, NULL)) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
debug(L"# %s on disappeared\n", buffer);
// The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over
RC_NO_SHEBANG;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto - no benefit to calling this one out separately. Normal handling for all errors should be okay


// Other errors should cause us to break
winerror(0, L"Failed to find %s on PATH\n", filename);
return RC_BAD_VIRTUAL_PATH;
}

CloseHandle(hReparsePoint);

AppExecLinkFile* appExecLinkFilePtr;

appExecLinkFilePtr = (AppExecLinkFile*)&appExecLinkBuf;

wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller";
const wchar_t *redirectorPackageId = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe";

int redirectorStrLen = wcsnlen_s(redirectorPackageId, 29);

wchar_t partialId[30];
wcsncpy_s(partialId, redirectorStrLen + 1, appExecLinkFilePtr->stringList, redirectorStrLen);

if (0 == wcsncmp(partialId, &redirectorPackageId, redirectorStrLen)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple wcscmp ought to be fine here if you use the full package name. As far as I'm aware, the publisher ID has never changed and never will (it's always some kind of hash of "Microsoft").

debug(L"# ignoring redirector that would launch store\n");
return RC_NO_SHEBANG;
}
}

FindClose(hFind);

// Check that we aren't going to call ourselves again
// If we are, pretend there was no shebang and let normal handling take over
if (GetModuleFileNameW(NULL, filename, MAXLEN) &&
Expand Down