microsoft / STL

MSVC's implementation of the C++ Standard Library.
Other
10.25k stars 1.51k forks source link

<filesystem>: `weakly_canonical` fails for UNC path with `\\?\UNC` prefix #2823

Open manxorist opened 2 years ago

manxorist commented 2 years ago

Describe the bug std::filesystem::weakly_canonical fails for UNC path with \\?\UNC prefix.

Command-line test case

C:\Users\manx\stuff>type weakly_canonical.cpp
#include <filesystem>
#include <iostream>

int main() {
    try {
        std::wcout << std::filesystem::weakly_canonical(L"\\\\?\\UNC\\server\\share\\dir\\name.ext").wstring() << std::endl;
    } catch (const std::exception & e) {
        std::wcout << std::flush;
        std::cerr << e.what() << std::endl;
    }
    return 0;
}

C:\Users\manx\stuff>cl /std:c++20 /permissive- /EHsc /O2 /W4 weakly_canonical.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.32.31332 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

weakly_canonical.cpp
Microsoft (R) Incremental Linker Version 14.32.31332.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:weakly_canonical.exe
weakly_canonical.obj

C:\Users\manx\stuff>weakly_canonical.exe
weakly_canonical: Incorrect function.: "\\?\UNC\server\share\dir\name.ext"

C:\Users\manx\stuff>

(https://godbolt.org/z/v83fbPfrW)

Expected behavior weakly_canonical does work for plain UNC paths without \\?\UNC prefix, so I think it should also work for prefixed ones:

#include <filesystem>
#include <iostream>

void show(std::filesystem::path p) {
    std::wcout << L"path: " << p.wstring() << std::endl;
    std::wcout << L" absolute: " << std::filesystem::absolute(p).wstring() << std::endl;
    std::wcout << L" canonical: " << std::filesystem::weakly_canonical(p).wstring() << std::endl;
    std::wcout << L" root name " << p.root_name().wstring() << std::endl;
    std::wcout << L" root directory " << p.root_directory().wstring() << std::endl;
    std::wcout << L" relative path " << p.relative_path().wstring() << std::endl;
}

int main() {
    try {
        show(std::filesystem::current_path());
        show(L"C:\\dir\\name.ext");
        show(L"\\\\.\\C:\\dir\\name.ext");
        show(L"\\\\?\\C:\\dir\\name.ext");
        show(L"\\\\server\\share\\dir\\name.ext");
        show(L"\\\\?\\UNC\\server\\share\\dir\\name.ext");
    } catch (const std::exception & e) {
        std::wcout << std::flush;
        std::cerr << e.what() << std::endl;
    }
    return 0;
}

(https://godbolt.org/z/rEdMvWs7h)

https://github.com/microsoft/STL/blob/7f04137880e1c3df5125c1baf808f16f399dee2e/stl/inc/filesystem#L420 talks about this prefix, so I would assume std::filesystem to be aware of it.

StephanTLavavej commented 2 years ago

Improved readability with a raw string literal:

C:\Temp>type meow.cpp
#include <filesystem>
#include <iostream>
using namespace std;

int main() {
    try {
        cout << filesystem::weakly_canonical(R"(\\?\UNC\server\share\dir\name.ext)").string() << endl;
    } catch (const filesystem::filesystem_error& e) {
        cout << e.what() << endl;
    }
}
C:\Temp>cl /EHsc /nologo /W4 /MTd /std:c++17 meow.cpp && meow
meow.cpp
weakly_canonical: Incorrect function.: "\\?\UNC\server\share\dir\name.ext"

The error is happening here: https://github.com/microsoft/STL/blob/ad80eb79ba4953e7529d15b1bc8d0b540150bf7d/stl/inc/filesystem#L4075-L4089 We've called _Canonical() with LR"(\\?\UNC)" and it has failed with _Invalid_function.

StephanTLavavej commented 2 years ago

Potentially related / involving similar issues: #2256 and LWG-3699 "lexically_relative on UNC drive paths (\\?\C:\...) results in a default-constructed value".