Fix filesystem::directory_entry::refresh on Win11 24H2 by StephanTLavavej · Pull Request #5077 · microsoft/STL (original) (raw)

After upgrading to Win11 24H2, I observed a very sporadic failure in P0218R1_filesystem. (#4844 was also a behavioral change in 24H2, but that one was consistent.) Eventually I was able to reproduce this with focused testing:

Click to expand reduced test case:

D:\GitHub\STL\out\x64>type meow.cpp

#include #include #include #include #include #include #include using namespace std; using namespace std::filesystem;

bool good(const error_code& ec) { bool overall = true; if (ec.value() != 0) { wcout << L"Unexpected error " << ec.value() << L" " << ec.message().c_str() << L"\n"; overall = false; }

if (ec.category() != system_category()) {
    wcout << L"Unexpected category " << ec.category().name() << L"\n";
    overall = false;
}

return overall;

}

int main() { for (int repeat = 0; repeat < 10'000; ++repeat) { try { random_device rd;

        wstring hexits;

        if ((rd() & 0xFF) == 0) {
            hexits = L"woof";
        } else {
            for (int i = 0; i < 32; ++i) {
                hexits.push_back(L"0123456789abcdef"[rd() & 0xF]);
            }
        }

        error_code ec;

        const path nonexistent{L"//this_path_does_not_exist_on_the_network_" + hexits + L"/docs"};

        directory_entry nonexistentEntry(nonexistent);
        assert(nonexistentEntry.path() == nonexistent);
        // Test VSO-892890 "std::filesystem::directory_entry constructor initializes wrong state"
        assert(!nonexistentEntry.exists());

        directory_entry nonexistentEntryEc(nonexistent, ec);
        assert(nonexistentEntryEc.path() == nonexistent);
        // Test VSO-892890 "std::filesystem::directory_entry constructor initializes wrong state"
        assert(!nonexistentEntryEc.exists());
        assert(good(ec));

        // Also test GH-232 "<filesystem>: directory_entry(const path& p, error_code& ec) does not return error
        // code"
        nonexistentEntry.refresh();

        nonexistentEntryEc.refresh(ec);
        assert(good(ec));
    } catch (const filesystem_error& fe) {
        cout << "Caught filesystem_error." << endl;
        cout << "    what: " << fe.what() << endl;
        cout << "   value: " << fe.code().value() << endl;
        cout << "category: " << fe.code().category().name() << endl;
    } catch (const exception& e) {
        cout << "Caught exception." << endl;
        cout << "what: " << e.what() << endl;
    } catch (...) {
        cout << "Caught unknown exception." << endl;
    }
}

}

D:\GitHub\STL\out\x64>cl /EHsc /nologo /W4 /std:c++latest /MTd /Od meow.cpp && meow
meow.cpp

D:\GitHub\STL\out\x64>meow
Caught filesystem_error.
    what: directory_entry::refresh: The specified network name is no longer available.: "//this_path_does_not_exist_on_the_network_woof/docs"
   value: 64
category: system

According to System Error Codes (0-499), this is:

ERROR_NETNAME_DELETED
64 (0x40)
The specified network name is no longer available.

Like in #4844, we need to teach __std_is_file_not_found to recognize this error code.

(I don't know, and don't really care, what the root cause is. In my reduced test case, I randomly switch between random hexits and "woof" in the nonexistent server name, and only the "woof" case seems to fail. I suspect that this is somehow related to filesystem caching, where repeatedly querying for a network name's existence can sometimes return this ERROR_NETNAME_DELETED error code.)

Finally, #4844 enhanced P0218R1_filesystem, which was very helpful here, but not quite enough:

Additionally, this uncaught exception from a non-error_code overload manifested itself as an abort() with no useful logs. To aid in future investigations, I'm including a simple change to the filesystem test. Now, we wrap the whole test in try ... catch and print the contents of any exception before failing.

In this case, I really needed to know the numeric error code, not just the message, so I'm further enhancing the test coverage to print the filesystem_error's error code value and category (expected to be the system category). I verified that with this test change, but without the product code fix, the sporadic failure prints:

Caught filesystem_error.
    what: directory_entry::refresh: The specified network name is no longer available.: "//this_path_does_not_exist_on_the_network_e9da301701f70ead24c65bd30f600d15/docs"
   value: 64
category: system