[MKSHELLLINK] Fix a bug in the special shell folder handling code (#8936)

Addendum to commit 7b081be46d (PR #7158).

CORE-15156, CORE-19691, CORE-19692

Since commit 7b081be46d (PR #7158), one can create shell links that
point to a file in a subdirectory of SystemRoot (e.g. X:\reactos) or
SystemRoot\system32 using a shell "special shell folder" syntax, for
example:
  `shell:windows\Readme.txt` (--> X:\reactos\Readme.txt) ,
  `shell:windows\system32\cmd.exe` , or:
  `shell:system\cmd.exe` (--> X:\reactos\system32\cmd.exe) .

An `EXP_SPECIAL_FOLDER` data block allows parts of a shortcuts pidl
to be overridden by such a special folder.

- In these cases, try to build the shortcut pidl such that it faithfully
  mirrors the intended path. For example, when using `shell:system\cmd.exe`,
  resolve the `shell:system` part to `X:\reactos\system32` instead of
  just `X:\reactos` ; this helps understanding what happens when
  spelunking into such a shell link. This also helps when converting
  such a path to one containing unexpanded environment variables, for
  the purposes of making the Windows explorer shell show the correct
  shortcut target path, or resolve it independently of the SystemRoot
  being used; and, to be able to use the path for the shortcut icon.

- Get rid of the `index`/`specialindex` variables.
  These were used for making the `EXP_SPECIAL_FOLDER` data block point
  to the correct path suffix part that follows the special path prefix
  in the ID list.

  Hardcoding its value caused problems when the special path prefix is
  made to contain more path elements than just "X:\reactos". For example,
  "X:\reactos\system32" in the case of the `shell:system` prefix.
  Instead, retrieve the length of the special path prefix, then, compare
  it with the length of the path elements being parsed in the loop.

  ----

  This problem was made explicit when the the following code path is run:
  ```
  shell32!CShellLink.cpp:CShellLink::Load(IStream *stm)
  --> ILCombine(folder, m_pPidl + pSpecial->cbOffset)
  ```
This commit is contained in:
Hermès Bélusca-Maïto
2026-04-27 20:24:24 +02:00
parent 55092de280
commit d6f4c0887a

View File

@@ -194,10 +194,11 @@ static const struct SPECIALFOLDER
{
unsigned char csidl;
const char* name;
const char* dummyPath;
} g_specialfolders[] = {
{ CSIDL_WINDOWS, "windows" },
{ CSIDL_SYSTEM, "system" },
{ 0, NULL}
{ CSIDL_WINDOWS, "windows", "X:\\reactos" },
{ CSIDL_SYSTEM, "system", "X:\\reactos\\system32" },
{ 0, NULL, NULL }
};
@@ -338,7 +339,7 @@ int main(int argc, const char *argv[])
ID_LIST_DRIVE IdListDrive;
unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName;
const char *pszName = pszTarget;
int index = 1, specialindex = -1;
size_t specialPathLen = 0;
const struct SPECIALFOLDER *special = get_special_folder(pszTarget);
/* ID list. It appears explorer does not accept links
@@ -349,14 +350,13 @@ int main(int argc, const char *argv[])
CsidlBlock.cbSize = sizeof(CsidlBlock);
CsidlBlock.dwSignature = EXP_SPECIAL_FOLDER_SIG;
CsidlBlock.idSpecialFolder = special->csidl;
specialindex = 3; // Skip GUID, drive and fake windows/reactos folder
sprintf(targetpath, "x:\\reactos\\%s", pszTarget + sizeof("shell:") + strlen(special->name));
specialPathLen = strlen(special->dummyPath);
sprintf(targetpath, "%s\\%s", special->dummyPath, pszTarget + sizeof("shell:") + strlen(special->name));
pszName = pszTarget = targetpath;
}
if (pszName[0] && pszName[0] != ':' && pszName[1] == ':')
{
++index;
cbListSize += sizeof(IdListDrive);
pszName += 2;
while (is_path_separator(*pszName))
@@ -372,8 +372,9 @@ int main(int argc, const char *argv[])
if (cchName != 1 || pszName[0] != '.')
cbListSize += sizeof(IdListFile) + 2 * (cchName + 1);
if (++index == specialindex)
if (special && ((pszName+cchName)-pszTarget == specialPathLen))
{
/* Point the special folder block to the rest of the path in the ID list */
CsidlBlock.cbOffset = cbListSize - sizeof(uint16_t);
pCsidlBlock = &CsidlBlock;
}