Skip to content

<filesystem>: GetVolumePathNameW linker errors for UWP developers #322

@StephanTLavavej

Description

@StephanTLavavej

We've defined these functions in filesystem.cpp to implement std::filesystem::space():

STL/stl/src/filesystem.cpp

Lines 266 to 277 in 8f4c816

[[nodiscard]] __std_win_error _Fs_space_attempt(wchar_t* const _Temp_buffer, const DWORD _Temp_buffer_characters,
const wchar_t* const _Target, uintmax_t* const _Available, uintmax_t* const _Total_bytes,
uintmax_t* const _Free_bytes) noexcept {
if (GetVolumePathNameW(_Target, _Temp_buffer, _Temp_buffer_characters)) {
if (GetDiskFreeSpaceExW(_Temp_buffer, reinterpret_cast<PULARGE_INTEGER>(_Available),
reinterpret_cast<PULARGE_INTEGER>(_Total_bytes), reinterpret_cast<PULARGE_INTEGER>(_Free_bytes))) {
return __std_win_error::_Success;
}
}
return __std_win_error{GetLastError()};
}

STL/stl/src/filesystem.cpp

Lines 763 to 798 in 8f4c816

[[nodiscard]] __std_win_error __stdcall __std_fs_space(const wchar_t* const _Target, uintmax_t* const _Available,
uintmax_t* const _Total_bytes, uintmax_t* const _Free_bytes) noexcept {
// get capacity information for the volume on which the file _Target resides
__std_win_error _Last_error;
if (GetFileAttributesW(_Target) == INVALID_FILE_ATTRIBUTES) {
_Last_error = __std_win_error{GetLastError()};
} else {
{
constexpr DWORD _Static_size = MAX_PATH;
wchar_t _Temp_buf[_Static_size];
_Last_error = _Fs_space_attempt(_Temp_buf, _Static_size, _Target, _Available, _Total_bytes, _Free_bytes);
if (_Last_error == __std_win_error::_Success) {
return __std_win_error::_Success;
}
}
if (_Last_error == __std_win_error::_Filename_exceeds_range) {
constexpr DWORD _Dynamic_size = USHRT_MAX + 1; // assuming maximum NT path fits in a UNICODE_STRING
const auto _Temp_buf = _malloc_crt_t(wchar_t, _Dynamic_size);
if (_Temp_buf) {
_Last_error =
_Fs_space_attempt(_Temp_buf.get(), _Dynamic_size, _Target, _Available, _Total_bytes, _Free_bytes);
if (_Last_error == __std_win_error::_Success) {
return __std_win_error::_Success;
}
} else {
_Last_error = __std_win_error::_Not_enough_memory;
}
}
}
*_Available = ~0ull;
*_Total_bytes = ~0ull;
*_Free_bytes = ~0ull;
return _Last_error;
}

Because this is a single source file, whose object file is injected into the import lib (effectively linking statically), when a developer uses anything from std::filesystem that needs separately compiled support machinery, filesystem.obj is dragged in.

For UWP developers, there's an issue with all currently available versions of the Windows SDK where GetVolumePathNameW() isn't usable (without linking to onecoreuap.lib, which is an arcane workaround that nobody knows about). This has been fixed and will be available in a future version of the Windows SDK. However, at this time, UWP developers who attempt to use anything from std::filesystem that needs separately compiled support, like std::filesystem::create_directories(), are experiencing mysterious linker errors.

We can change how the STL is built in order to fix this scenario for the vast majority of UWP developers. (Only UWP developers specifically calling std::filesystem::space() would still be affected; they inherently need either the onecoreuap.lib workaround or the future Windows SDK.)

We simply need to move _Fs_space_attempt() and __std_fs_space() into a separate source/object file, so that the linker drags it in only when specifically needed. We can comment this as TRANSITION to remove the workaround later, although it's unclear how long this workaround will be needed.

I am unfamiliar with UWP environments, but this should be simple to validate with desktop compilations - use /link /verbose to ensure that the new object file is dragged in when, and only when, std::filesystem::space() is directly used.

Also tracked by Microsoft-internal VSO-1000285.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions