Skip to content

Commit 4bdac43

Browse files
committed
Use GetFileAttributesW in symlink_status if CreateFileW fails.
For some system files and folders like "System Volume Information" CreateFileW fails with ERROR_ACCESS_DENIED while GetFileAttributesW succeeds. GetFileAttributesW doesn't allow to discover whether the file is a symlink or some other kind of a reparse point, so this fallback will only work for files that are not reparse points, symlinks or not. For reparse points continue to report error. Closes boostorg#234.
1 parent d732ab0 commit 4bdac43

File tree

3 files changed

+45
-7
lines changed

3 files changed

+45
-7
lines changed

doc/release_history.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
</td>
4040
</table>
4141

42+
<h2>1.80.0</h2>
43+
<ul>
44+
<li>On Windows, added a fallback implementation for querying file attributes in case if the file cannot be opened with <code>ERROR_ACCESS_DENIED</code> error. This may allow <code>status</code> and <code>symlink_status</code> to succeed for system files and directories that are not reparse points or symlinks. (<a href="https://github.com/boostorg/filesystem/issues/234">#234</a>)</li>
45+
</ul>
46+
4247
<h2>1.79.0</h2>
4348
<ul>
4449
<li><b>v3:</b> <code>path::replace_extension</code> now works in terms of <b>v3</b> definition of <code>path::extension</code> rather than <b>v4</b>.</li>

src/operations.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,9 +1284,8 @@ inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t*
12841284
return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p));
12851285
}
12861286

1287-
inline fs::file_status process_status_failure(path const& p, error_code* ec)
1287+
inline fs::file_status process_status_failure(DWORD errval, path const& p, error_code* ec)
12881288
{
1289-
DWORD errval = ::GetLastError();
12901289
if (ec) // always report errval, even though some
12911290
ec->assign(errval, system_category()); // errval values are not status_errors
12921291

@@ -1305,6 +1304,11 @@ inline fs::file_status process_status_failure(path const& p, error_code* ec)
13051304
return fs::file_status(fs::status_error);
13061305
}
13071306

1307+
inline fs::file_status process_status_failure(path const& p, error_code* ec)
1308+
{
1309+
return process_status_failure(::GetLastError(), p, ec);
1310+
}
1311+
13081312
//! remove() implementation for Windows XP and older
13091313
bool remove_nt5_impl(path const& p, DWORD attrs, error_code* ec)
13101314
{
@@ -3855,21 +3859,38 @@ file_status symlink_status(path const& p, error_code* ec)
38553859
OPEN_EXISTING,
38563860
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
38573861

3862+
DWORD attrs;
38583863
if (h.handle == INVALID_HANDLE_VALUE)
38593864
{
3860-
return_status_failure:
3861-
return process_status_failure(p, ec);
3865+
// For some system files and folders like "System Volume Information" CreateFileW fails
3866+
// with ERROR_ACCESS_DENIED. GetFileAttributesW succeeds for such files, so try that.
3867+
// Though this will only help if the file is not a reparse point (symlink or not).
3868+
DWORD err = GetLastError();
3869+
if (err == ERROR_ACCESS_DENIED)
3870+
{
3871+
attrs = GetFileAttributesW(p.c_str());
3872+
if (attrs != INVALID_FILE_ATTRIBUTES)
3873+
{
3874+
if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0u)
3875+
return file_status((attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file, make_permissions(p, attrs));
3876+
}
3877+
else
3878+
{
3879+
err = GetLastError();
3880+
}
3881+
}
3882+
3883+
return process_status_failure(err, p, ec);
38623884
}
38633885

3864-
DWORD attrs;
38653886
fs::perms permissions;
38663887
GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
38673888
if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL))
38683889
{
38693890
file_attribute_tag_info info;
38703891
BOOL res = get_file_information_by_handle_ex(h.handle, file_attribute_tag_info_class, &info, sizeof(info));
38713892
if (BOOST_UNLIKELY(!res))
3872-
goto return_status_failure;
3893+
return process_status_failure(p, ec);
38733894

38743895
attrs = info.FileAttributes;
38753896
permissions = make_permissions(p, attrs);
@@ -3882,7 +3903,7 @@ file_status symlink_status(path const& p, error_code* ec)
38823903
BY_HANDLE_FILE_INFORMATION info;
38833904
BOOL res = ::GetFileInformationByHandle(h.handle, &info);
38843905
if (BOOST_UNLIKELY(!res))
3885-
goto return_status_failure;
3906+
return process_status_failure(p, ec);
38863907

38873908
attrs = info.dwFileAttributes;
38883909
permissions = make_permissions(p, attrs);

test/operations_test.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,18 @@ void predicate_and_status_tests()
10691069
BOOST_TEST(!fs::is_regular_file(stat));
10701070
BOOST_TEST(!fs::is_other(stat));
10711071
BOOST_TEST(!fs::is_symlink(stat));
1072+
1073+
#ifdef BOOST_WINDOWS_API
1074+
stat = fs::status(L"\\System Volume Information");
1075+
BOOST_TEST(fs::type_present(stat));
1076+
BOOST_TEST(fs::permissions_present(stat));
1077+
BOOST_TEST(fs::status_known(stat));
1078+
BOOST_TEST(fs::exists(stat));
1079+
BOOST_TEST(fs::is_directory(stat));
1080+
BOOST_TEST(!fs::is_regular_file(stat));
1081+
BOOST_TEST(!fs::is_other(stat));
1082+
BOOST_TEST(!fs::is_symlink(stat));
1083+
#endif // BOOST_WINDOWS_API
10721084
}
10731085

10741086
// create_directory_tests ----------------------------------------------------------//

0 commit comments

Comments
 (0)