aboutsummaryrefslogtreecommitdiffstats
path: root/main/boost1.80/boost-1.80-filesystem-fix-weakly-canonical-long-paths.patch
blob: 9312059a600000b5f406be4ff073a0bfdfcb5a3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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))
     {