Search code examples
winapintfsreparsepoint

How to read files in a junction point directory, using GetFileInformationByHandleEx?


I have this code which runs fine if path points to a regular directory:

#include <windows.h>
#include <stdio.h>

int main()
{
    wchar_t path[1024] = L"C:\\MyPath\\MyDir";
    
    auto h = CreateFile(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (h == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile error: 0x%08X\n", GetLastError());
        return 0;
    }

    FILE_ID_INFO id = {};
    if (!GetFileInformationByHandleEx(h, FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &id, sizeof(id)))
    {
        printf("FileIdInfo error: 0x%08X\n", GetLastError());
    }

    unsigned char buffer[4096];
    do
    {
        if (!GetFileInformationByHandleEx(h, FILE_INFO_BY_HANDLE_CLASS::FileIdExtdDirectoryInfo, buffer, ARRAYSIZE(buffer)))
        {
            auto gle = GetLastError();
            if (gle == ERROR_NO_MORE_FILES)
                break;

            printf("FileIdExtdDirectoryInfo error: 0x%08X\n", gle);
            break;
        }

        auto current = buffer;
        do
        {
            auto info = (FILE_ID_EXTD_DIR_INFO*)current;
            wprintf(L"name: %.*s\n", info->FileNameLength / 2, info->FileName);
            if (!info->NextEntryOffset)
                break;

            current += info->NextEntryOffset;
        } while (true);
    } while (true);
    CloseHandle(h);
}

But if the directory points to a junction point, it returns (ERROR_INVALID_PARAMETER invalid parameter)

FileIdInfo error: 0x00000057
FileIdExtdDirectoryInfo error: 0x00000057

So, I've tried this for CreateFile

auto h = CreateFile(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);

and now, I have no error, but it only shows "." and ".." entries, not all the files in the directory which I can read using cmd.exe

C:\MyPath\MyDir>dir
 Volume in drive C has no label.
 Volume Serial Number is AEA6-688A

 Directory of C:\MyPath\MyDir

20/10/2017  14:08             (157) My Icon.ico
11/04/2018  09:08               321 My File.cpp
30/04/2018  15:14    <DIR>          My dossier
19/09/2019  10:40          (41 650) New Rich Text Document.rtf
10/10/2021  11:06                 0 New Text Document.txt
               4 File(s)         46 224 bytes
               1 Dir(s)  544 625 274 880 bytes free

Solution

  • It turns out my code in itself is correct, but it can fail if the file system below doesn't support NTFS object ID, since this is what it's trying to read (using FILE_INFO_BY_HANDLE_CLASS::FileIdInfo and FILE_INFO_BY_HANDLE_CLASS::FileIdExtdDirectoryInfo).

    This happens for example if the directory is an NTFS mount point with a substitute name that points to a volume that doesn't have the FILE_SUPPORTS_OBJECT_IDS flag set (seen in virtual drive scenarios).

    The error here (ERROR_INVALID_PARAMETER) is kind of misleading as there's no problem with any parameter (except maybe the FileInformationClass one), I would have expected some "unsupported" error instead.

    As for cmd.exe, well, it just doesn't read/need that information so it works fine.