3

despite what is MSDN documentation say :

You can also enable the new long path behavior per app via the manifest:

in test i found that this is not a true. all test done on win10 1607.14393.447(and 14393.0) x64

when i test exe with and without manifest, i discover that manifest have no any effect for CreateFileW, CreateDirectoryW, (no sence test more function - why i explain later) really when LongPathsEnabled is 1 in registry - long paths worked (without \\?\ prefix). when it 0 - not worked, even if longPathAware true in manifest. (code below)

need understand next:

  • all file function in kernel work only with NT-paths

  • so before call kernel we need convert Win32-path no NT-path

  • for path transformations exist some close functions RtlDosPathNameTo*NtPathName* in ntdll

  • all transformations Win32->NT path done centrally in this functions, but not direct in CreateFileW, FindFirstFileW, GetCompressedFileSizeW... (other behavior would be extremely illogical)

  • so really enough test 1,2 function, but not all under article (but if somebody not believe and want - let test :) )

  • all this kernel32 (and kernelbase) api take Win32-path and convert it to NT here - so no any different - are say GetFileAttributesW called direct from native c/c++ code or from managed runtime or app container - result will be the same
  • ALL dependent only from RtlDosPathNameTo*NtPathName* internal implementation

ntdll now implement and export 2 new API

BOOLEAN NTAPI RtlIsLongPathAwareProcessByManifest();
BOOLEAN NTAPI RtlAreLongPathsEnabled();

RtlAreLongPathsEnabled - looks only in registry (on first call) HKLM\SYSTEM\CurrentControlSet\Control\FileSystem @ LongPathsEnabled cache result in static var and return true when LongPathsEnabled != 0. this function called from RtlDosPathNameTo*NtPathName* when Win32 path exceeds MAX_PATH and only if long paths enabled - path is converted, otherwise STATUS_NAME_TOO_LONG returned

RtlIsLongPathAwareProcessByManifest - looks only in application manifest (by call RtlQueryActivationContextApplicationSettings(0, 0, L"http://schemas.microsoft.com/SMI/2016/WindowsSettings", L"longPathAware");) when it first time called and again cache result in local static BOOLEAN. if i not skip something - this function never called from ntdll and called from kernebase.dll only from one place - GetTempPathW. so really result of RtlIsLongPathAwareProcessByManifest can have effect only for GetTempPathW result.

but when test this - i view that at all bug in current windows builds here - if length of TMP environment variable (or TEMP, etc.. but TMP queried first) exceeded MAX_PATH queried RtlAreLongPathsEnabled and RtlIsLongPathAwareProcessByManifest and if both returned true (so here AND logic) - system try got this long TMP path, but mistake with buffer size on one WCHAR - as result we hang in infinite loop - [ *- > RtlAllocateHeap -> STATUS_BUFFER_TOO_SMALL -> RtlFreeHeap -> RtlAllocateHeap -> *]

are somebody can test and confirm or refute this ? not pass link to documentation, but really test.

code which i use for test (with and without correct manifest - confirmed by RtlIsLongPathAwareProcessByManifest result)

BOOL CreateFolder(LPCWSTR lpPathName)
{
    return CreateDirectoryW(lpPathName, 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}

void LPT()
{
    // check manifest and registry

    BOOLEAN bByManifest = 0, bLongPathsEnabled = 0;

    if (HMODULE hmod = GetModuleHandle(L"ntdll"))
    {
        BOOLEAN (NTAPI * RtlIsLongPathAwareProcessByManifest)();
        BOOLEAN (NTAPI * RtlAreLongPathsEnabled)();

        if (*(FARPROC*)&RtlIsLongPathAwareProcessByManifest = GetProcAddress(hmod, "RtlIsLongPathAwareProcessByManifest"))
        {
            bByManifest = RtlIsLongPathAwareProcessByManifest();
        }

        if (*(FARPROC*)&RtlAreLongPathsEnabled = GetProcAddress(hmod, "RtlAreLongPathsEnabled"))
        {
            bLongPathsEnabled = RtlAreLongPathsEnabled();
        }
    }

    WCHAR name[128], path[0x8000], *c;

    if (bLongPathsEnabled && bByManifest)
    {
        // hung test in GetTempPathW

        __stosw((PUSHORT)path, 'x', MAX_PATH + 1);
        path[MAX_PATH + 1] = 0;

        if (SetEnvironmentVariable(L"TMP", path))
        {
            // hung here in infinite loop by windows error

            // ---> buffer allocated on sizeof(WCHAR) less than required, 
            // |    got STATUS_BUFFER_TOO_SMALL
            // | -< free buffer
            GetTempPathW(RTL_NUMBER_OF(path), path);// never return :)
        }
    }

    if (!SHGetFolderPath(0, CSIDL_PROFILE , 0, 0, path))
    {
        *name = '\\';
        __stosw((PUSHORT)name + 1, '3', RTL_NUMBER_OF(name) - 2);
        name[RTL_NUMBER_OF(name) - 1] = 0;

        c = path + wcslen(path);

        int n = 4;
        do 
        {
            memcpy(c, name, sizeof(name));
            c += RTL_NUMBER_OF(name) - 1;

            if (!CreateFolder(path))
            {
                break;
            }

        } while (--n);

        if (!n)
        {

            wcscpy(c, L"\\1.txt");

            HANDLE hFile = CreateFileW(path, FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0, OPEN_ALWAYS, 0, 0);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                CloseHandle(hFile);
                return ;
            }
        }
    }

    GetLastError();
}
RbMm
  • 31,280
  • 3
  • 35
  • 56

0 Answers0