STL
STL copied to clipboard
<filesystem>: std::filesystem::exists fails when testing path naming unix socket
Description
std::filesystem::exists(p), where p is a path pointing to a unix socket (e.g. p = "./test.sock"), throws an exception with the message exists: unknown error: "./test.sock".
Command-line test case
C:\temp> cat test.cpp
#include <exception>
#include <filesystem>
#include <iostream>
#include <cassert>
#ifndef _WINSOCKAPI_
# define _WINSOCKAPI_
#endif
#include <WinSock2.h>
#include <ws2ipdef.h>
#include <afunix.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsa_data;
assert(WSAStartup(MAKEWORD(2, 2), &wsa_data) == 0);
auto socket = WSASocketW(AF_UNIX, SOCK_STREAM, 0, 0, 0, 0);
assert(socket != SOCKET_ERROR);
sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "./test.sock", sizeof(addr.sun_path));
assert(bind(socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != SOCKET_ERROR);
assert(listen(socket, 1) != SOCKET_ERROR);
try
{
(void)std::filesystem::exists("./test.sock");
}
catch (std::exception const& e)
{
std::cout << e.what() << std::endl;
}
}
C:\temp> cl /std:c++17 /EHsc /Gr test.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.37.32822 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
test.cpp
Microsoft (R) Incremental Linker Version 14.37.32822.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
C:\temp> ./test.exe
exists: unknown error: "./test.sock"
Expected behavior
I expect the call to std::filesystem::exists to return true.
STL version
Microsoft Visual Studio Community 2022
Version 17.8.0 Preview 1.0
@strega-nil-ms
The error code is 1920 ("The file cannot be accessed by the system.") on my machine, reported by CreateFileW.
Can we just return true when error code 1920 (and it friends) is encountered?
Does std::filesystem::exists even call CreateFileW? If so, it should not -- should use GetFileAttributesW or FindFirstFileW.
Does
std::filesystem::existseven callCreateFileW? If so, it should not -- should useGetFileAttributesWorFindFirstFileW.
It must, because for some reason, unix sockets are considered reparse points. I'm not sure how to solve this, to be quite honest. This seems to be a failing of the standard's interaction with Windows' APIs.
We talked about this at the weekly maintainer meeting. Nicole debugged into this and says that the problem is happening here:
https://github.com/microsoft/STL/blob/f362f7d7bea87bd199c927a89718e929ed89b187/stl/src/filesystem.cpp#L910-L918
This is where __std_fs_get_stats() calls CreateFileW() (via _Fs_file). It appears that we need to do something like add extra logic here to detect reparse points, and then to further detect Unix sockets. From quickly searching ~~MSDN~~ Microsoft Learn, https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags mentions something that looks like it might be relevant: IO_REPARSE_TAG_AF_UNIX resembles the AF_UNIX used in the repro (which curiously isn't mentioned in WSASocketW documentation).
This would add extra calls to the filesystem::exists() codepath, but we currently don't see a better way to avoid this, if we want to handle this aspect of the filesystem.
We might want to ask internal mailing lists (e.g. win32prg) if there's some better way to achieve this with the public APIs that we can use.
You may just need to use IsReparseTagNameSurrogate on the reparse tag to test if it's some kind of link or not.