2
0
mirror of https://github.com/boostorg/website.git synced 2026-01-19 04:42:17 +00:00

Added a new Filesystem patch to fix weakly_canonical on Windows.

This commit is contained in:
Andrey Semashev
2022-08-10 13:43:39 +03:00
parent 597458a6d3
commit 9baf48a683
2 changed files with 173 additions and 0 deletions

View File

@@ -26,6 +26,8 @@ in the release.
* Boost.Filesystem directory iterators may fail to construct for a network share on Windows prior to 10, see [github_pr filesystem 246] and [github filesystem 245].
[@/patches/1_80_0/0001-filesystem-win-fix-dir-it-net-share.patch Patch].
* In Boost.Filesystem on Windows, `weakly_canonical` fails to process paths that start with the "\\\\?\\" prefix, see [github filesystem 247].
[@/patches/1_80_0/0002-filesystem-fix-weakly-canonical-long-paths.patch Patch].
[endsect]

View File

@@ -0,0 +1,171 @@
commit 476ca7b6c1d37a5d796f8525813a9a64c9e54ffc
Author: Andrey Semashev <andrey.semashev@gmail.com>
Date: Wed Aug 10 04:57:21 2022 +0300
Fix weakly_canonical on Windows with long paths prefix.
During its operation, weakly_canonical would call status() on the path
consisting only from the root name of the input path. This would fail
with ERROR_INVALID_FUNCTION if the root name starts with the "\\?\" prefix,
as the root name path is not absolute.
To fix this, we don't check the status of the root name path (which is
not the correct check anyways as it tests the current directory on the
corresponding drive for existence, which is not what we want). Additionally,
avoid calling status() on the paths containing dot and dot-dot elements
during the weakly_canonical execution for the same reason - the "\\?\"
prefix disables most of the path processing in Windows APIs, including
dot and dot-dot elements resolution.
Fixes https://github.com/boostorg/filesystem/issues/247.
diff --git a/libs/filesystem/src/operations.cpp b/libs/filesystem/src/operations.cpp
index dd636e9..ca2fff3 100644
--- a/libs/filesystem/src/operations.cpp
+++ b/libs/filesystem/src/operations.cpp
@@ -4434,7 +4434,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec)
path head(p);
for (; !head.empty(); --itr)
{
- file_status head_status = detail::status_impl(head, &local_ec);
+ file_status head_status(detail::status_impl(head, &local_ec));
if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
{
if (!ec)
@@ -4450,32 +4450,83 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec)
head.remove_filename();
}
+ if (head.empty())
+ return p.lexically_normal();
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+
#else
- // On Windows, filesystem APIs such as GetFileAttributesW perform lexical path normalization internally.
- // As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would break
- // canonical, as symlink_status that it calls internally would report an error that the file at the intermediate
- // path does not exist. To avoid this, scan the initial path in the forward direction.
- // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW,
- // which is called in status() may return "file not found" for paths to network shares and mounted cloud
- // storages that have forward slashes as separators.
+ // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization
+ // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would
+ // break canonical, as symlink_status that it calls internally would report an error that the file at the
+ // intermediate path does not exist. To avoid this, scan the initial path in the forward direction.
+ // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW
+ // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and
+ // mounted cloud storages that have forward slashes as separators.
+ // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
+ // such path. Querying the status of a root name such as c: is also not right as this path refers to the current
+ // directory on drive C:, which is not what we want to test for existence anyway.
path::iterator itr(p.begin());
path head;
- for (; itr != p_end; ++itr)
+ if (p.has_root_name())
{
- path const& p_elem = *itr;
- if (p_elem.size() == 1u && detail::is_directory_separator(p_elem.native()[0]))
+ BOOST_ASSERT(itr != p_end);
+ head = *itr;
+ ++itr;
+ }
+
+ if (p.has_root_directory())
+ {
+ BOOST_ASSERT(itr != p_end);
+ // Convert generic separator returned by the iterator for the root directory to
+ // the preferred separator.
+ head += path::preferred_separator;
+ ++itr;
+ }
+
+ if (!head.empty())
+ {
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
{
- // Convert generic separator returned by the iterator for the root directory to
- // the preferred separator.
- head += path::preferred_separator;
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ return path();
}
- else
+
+ if (head_status.type() == fs::file_not_found)
+ {
+ // If the root path does not exist then no path element exists
+ return p.lexically_normal();
+ }
+ }
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+ for (; itr != p_end; ++itr)
+ {
+ path const& p_elem = *itr;
+
+ // Avoid querying status of paths containing dot and dot-dot elements, as this will break
+ // if the root name starts with "\\?\".
+ if (p_elem == dot_p)
+ continue;
+
+ if (p_elem == dot_dot_p)
{
- head /= p_elem;
+ if (head.has_relative_path())
+ head.remove_filename();
+
+ continue;
}
- file_status head_status = detail::status_impl(head, &local_ec);
+ head /= p_elem;
+
+ file_status head_status(detail::status_impl(head, &local_ec));
if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
{
if (!ec)
@@ -4492,33 +4543,22 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec)
}
}
+ if (head.empty())
+ return p.lexically_normal();
+
#endif
- path const& dot_p = dot_path();
- path const& dot_dot_p = dot_dot_path();
path tail;
bool tail_has_dots = false;
for (; itr != p_end; ++itr)
{
path const& tail_elem = *itr;
-#if defined(BOOST_WINDOWS_API)
- if (tail_elem.size() == 1u && detail::is_directory_separator(tail_elem.native()[0]))
- {
- // Convert generic separator returned by the iterator for the root directory to
- // the preferred separator.
- tail += path::preferred_separator;
- continue;
- }
-#endif
tail /= tail_elem;
// for a later optimization, track if any dot or dot-dot elements are present
if (!tail_has_dots && (tail_elem == dot_p || tail_elem == dot_dot_p))
tail_has_dots = true;
}
- if (head.empty())
- return p.lexically_normal();
-
head = detail::canonical(head, base, &local_ec);
if (BOOST_UNLIKELY(!!local_ec))
{