Skip to content

Commit f803579

Browse files
committed
Reworked directory_iterator on Windows to add support for O_NOFOLLOW.
directory_iterator implementation now explicitly opens a directory handle and relies on GetFileInformationByHandleEx or NtQueryDirectoryFile to query information of the files in the directory. With GetFileInformationByHandleEx, there are at least three ways to request the information, each supported on different Windows versions and different filesystems and providing different sets of information. We support all three (FILE_ID_BOTH_DIR_INFO, FILE_FULL_DIR_INFO and FILE_ID_EXTD_DIR_INFO), and we fall back to older ones if the newer ones are not supported. GetFileInformationByHandleEx is available since Windows Vista. NtQueryDirectoryFile is an NT API that provides similar information to GetFileInformationByHandleEx, but exists since at least Windows XP, but some sources state it has existed since NT 4.0. This API is now used when GetFileInformationByHandleEx is not available and it replaces FindFirstFileW/FindNextFileW/FindClose based implementation (which supposedly uses NtQueryDirectoryFile internally). Being an NT API, we have to handle NTSTATUS error codes returned from it, so we had to refactor unique path code that also used NTSTATUS previously. Opening a directory handle explicitly allows to support O_NOFOLLOW semantics on Windows, as we are able to prevent CreateFile from following to the reparse point target. This is needed for remove_all implementation to fix CVE-2022-21658. Similar to POSIX, remove_all_impl on Windows now requests the directory iterator to not follow symlinks when it is used to dive into a directory. FindFirstFileW/FindNextFileW/FindClose based implementation is still preserved for now for Windows CE, as it is unlikely to support neither GetFileInformationByHandleEx nor NtQueryDirectoryFile. Given that Windows CE has been untested for many years and is probably broken anyway, its support is now declared deprecated. The related code, including Find*-based directory iterator, will be removed in a future release. Closes boostorg#224. And a couple cleanup changes: - Since we now may use GetFileInformationByHandleEx, we are now using it to query FILE_ATTRIBUTE_TAG_INFO for testing whether a reparse point is a symlink. - In resize_file_impl, we now specify all sharing flags to allow the operation to succeed if the file is already opened by someone.
1 parent 0346889 commit f803579

File tree

7 files changed

+1184
-474
lines changed

7 files changed

+1184
-474
lines changed

doc/release_history.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@
4242
<h2>1.79.0</h2>
4343
<ul>
4444
<li>Fixed compilation of path appending and concatenation operators with arguments of types convertible to <code>path</code> or compatible string type. (<a href="https://github.com/boostorg/filesystem/issues/223">#223</a>)</li>
45-
<li>On POSIX systems that support <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html"><code>fdopendir</code></a> and <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html"><code>O_NOFOLLOW</code></a>, <code>remove_all</code> is now protected against <a href="https://www.cve.org/CVERecord?id=CVE-2022-21658">CVE-2022-21658</a>. The vulnerability is a race condition that allows a third party process to replace a directory that is being concurrently processed by <code>remove_all</code> with a directory symlink and cause <code>remove_all</code> to follow the symlink and remove files in the linked directory instead of removing the symlink itself. (<a href="https://github.com/boostorg/filesystem/issues/224">#224</a>)</li>
45+
<li>On POSIX systems that support <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html"><code>fdopendir</code></a> and <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html"><code>O_NOFOLLOW</code></a> and on Windows, <code>remove_all</code> is now protected against <a href="https://www.cve.org/CVERecord?id=CVE-2022-21658">CVE-2022-21658</a>. The vulnerability is a race condition that allows a third party process to replace a directory that is being concurrently processed by <code>remove_all</code> with a directory symlink and cause <code>remove_all</code> to follow the symlink and remove files in the linked directory instead of removing the symlink itself. (<a href="https://github.com/boostorg/filesystem/issues/224">#224</a>)</li>
46+
<li>On Windows, <code>directory_iterator</code> internal implementation has been reworked to better utilize modern Windows APIs, which may improve performance while handling symlinks.</li>
4647
<li>On Windows, initialize internal WinAPI function pointers early, if possible, to allow Boost.Filesystem operations to be invoked in global constructors. This is only supported on MSVC, GCC, Clang and compatible compilers.</li>
48+
<li>On Windows, <code>resize_file</code> should no longer fail with an error if the file to be resized is opened.</li>
4749
<li><b>Deprecated:</b> <code>boost/filesystem/string_file.hpp</code> header is deprecated and will be removed in a future release. The header is no longer included by <code>boost/filesystem.hpp</code> by default. Users are advised to implement the functionality themselves or migrate to other implementations.</li>
50+
<li><b>Deprecated:</b> Windows CE support is deprecated and will be removed in a future release. Windows CE has been untested for many years and is likely non-functional.</li>
4851
</ul>
4952

5053
<h2>1.78.0</h2>

include/boost/filesystem/directory.hpp

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <boost/filesystem/path.hpp>
2121
#include <boost/filesystem/file_status.hpp>
2222

23+
#include <cstddef>
2324
#include <string>
2425
#include <vector>
2526
#include <utility> // std::move
@@ -258,40 +259,29 @@ class directory_iterator;
258259

259260
namespace detail {
260261

261-
BOOST_FILESYSTEM_DECL
262-
system::error_code dir_itr_close( // never throws()
263-
void*& handle
264-
#if defined(BOOST_POSIX_API)
265-
, void*& buffer
266-
#endif
267-
) BOOST_NOEXCEPT;
268-
269262
struct dir_itr_imp :
270263
public boost::intrusive_ref_counter< dir_itr_imp >
271264
{
265+
#ifdef BOOST_WINDOWS_API
266+
unsigned char extra_data_format;
267+
std::size_t current_offset;
268+
#endif
272269
directory_entry dir_entry;
273270
void* handle;
274271

275-
#if defined(BOOST_POSIX_API)
276-
void* buffer; // see dir_itr_increment implementation
277-
#endif
278-
279272
dir_itr_imp() BOOST_NOEXCEPT :
280-
handle(0)
281-
#if defined(BOOST_POSIX_API)
282-
, buffer(0)
273+
#ifdef BOOST_WINDOWS_API
274+
extra_data_format(0u),
275+
current_offset(0u),
283276
#endif
277+
handle(NULL)
284278
{
285279
}
280+
BOOST_FILESYSTEM_DECL ~dir_itr_imp() BOOST_NOEXCEPT;
286281

287-
~dir_itr_imp() BOOST_NOEXCEPT
288-
{
289-
dir_itr_close(handle
290-
#if defined(BOOST_POSIX_API)
291-
, buffer
292-
#endif
293-
);
294-
}
282+
BOOST_FILESYSTEM_DECL static void* operator new(std::size_t class_size, std::size_t extra_size) BOOST_NOEXCEPT;
283+
BOOST_FILESYSTEM_DECL static void operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT;
284+
BOOST_FILESYSTEM_DECL static void operator delete(void* p) BOOST_NOEXCEPT;
295285
};
296286

297287
BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec);

0 commit comments

Comments
 (0)