Skip to content

<filesystem>: equivalent() gives false positives on Remote Desktop shared folders #3571

@mstorsjo

Description

@mstorsjo

Describe the bug
When connecting to a Windows machine with Remote Desktop and sharing a local folder with the remote machine, filesystem operations on the remote mounted folder can give spurious results for equivalent().

Command-line test case

C:\Temp>type fs-equivalent.cpp
#include <filesystem>
#include <iostream>

int main(int argc, char *argv[]) {
    const char *dir = ".";
    if (argc > 1)
        dir = argv[1];
    std::cout << "Iterating over " << dir << std::endl;
    std::filesystem::path firstPath;
    int duplicates = 0;
    int unique = 0; 
    for (const auto &entry : std::filesystem::directory_iterator(dir)) {
        if (firstPath.empty()) {
            firstPath = entry.path();
            unique++; 
        } else {
            if (std::filesystem::equivalent(firstPath, entry.path())) {
                std::cout << firstPath << " and " << entry.path() << " seem equivalent" << std::endl;
                duplicates++;
            } else {
                unique++;
            }   
        }
    } 
    std::cout << "Iterated over " << unique << " unique files, found " << duplicates << " duplicates" << std::endl;
    return 0;
}

C:\Temp>cl /std:c++17 /EHsc /W4 /WX .\fs-equivalent.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32323 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

fs-equivalent.cpp
Microsoft (R) Incremental Linker Version 14.36.32323.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:fs-equivalent.exe  
fs-equivalent.obj 

C:\Temp>.\fs-equivalent.exe z:\directory
"z:\\directory\\file1" and "z:\\directory\\file42" seem equivalent
[...]
Iterated over 830 unique files, found 88 duplicates

Expected behavior
std::filesystem::equivalent shouldn't give false positives on distinct different files. When run on a directory with distinct files, without symlinks/hardlinks, the tool should print something like this: Iterated over 918 unique files, found 0 duplicates

STL version
Tested with MSVC 17.6 preview 1.

Additional context
To test this, connect to a remote Windows machine with Microsoft Remote Desktop (tested on macOS). In Remote Desktop, choose to share a local folder with the remote machine. Pick a directory that contains many distinct files (no symlinks, no hardlinks, etc). For the purposes of this test, I've tested with https://github.com/mstorsjo/llvm-mingw/releases/download/20230130/llvm-mingw-20230130-ucrt-x86_64.zip unpacked, iterating over files in the include or x86_64-w64-mingw32\lib subdirectories - but any directory with more than a few dozens files should do.

The reason for the issue, is that the unique file ID as returned by GetFileInformationByHandle in the nFileIndexHigh / nFileIndexLow fields actually aren't properly unique on some filesystem mounts. This seems to be the case with shared folders mounted by Remote Desktop and folders shared to a VM in VirtualBox.

Is it the case that the supposedly unique file ID fields from GetFileInformationByHandle are supposed to be unique only as long as the handles are open?

This same root cause (file IDs from GetFileInformationByHandle aren't unique on such filesystems) is hitting other tools such as LLVM, as reported in llvm/llvm-project#22079 and llvm/llvm-project#61401.

I.e. the issue can be reframed into this: Is STL's use of GetFileInformationByHandle in __std_fs_get_file_id (used later in std::filesystem::equivalent) correct (opening a handle, inspecting it and grabbing the unique ID, closing the handle, then later comparing the IDs)? Or are the file IDs only valid as long as the corresponding HANDLE is open? I.e. is this a fault in the users of GetFileInformationByHandle (STL and LLVM), or in the host of the shared directories, giving non-unique IDs?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfilesystemC++17 filesystemfixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions