diff --git a/doc/release_history.html b/doc/release_history.html
index 1f4ee86..90a746b 100644
--- a/doc/release_history.html
+++ b/doc/release_history.html
@@ -42,9 +42,12 @@
1.79.0
- Fixed compilation of path appending and concatenation operators with arguments of types convertible to
path or compatible string type. (#223)
- - On POSIX systems that support
fdopendir and O_NOFOLLOW, remove_all is now protected against CVE-2022-21658. The vulnerability is a race condition that allows a third party process to replace a directory that is being concurrently processed by remove_all with a directory symlink and cause remove_all to follow the symlink and remove files in the linked directory instead of removing the symlink itself. (#224)
+ - On POSIX systems that support
fdopendir and O_NOFOLLOW and on Windows, remove_all is now protected against CVE-2022-21658. The vulnerability is a race condition that allows a third party process to replace a directory that is being concurrently processed by remove_all with a directory symlink and cause remove_all to follow the symlink and remove files in the linked directory instead of removing the symlink itself. (#224)
+ - On Windows,
directory_iterator internal implementation has been reworked to better utilize modern Windows APIs, which may improve performance while handling symlinks.
- 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.
+ - On Windows,
resize_file should no longer fail with an error if the file to be resized is opened.
- Deprecated:
boost/filesystem/string_file.hpp header is deprecated and will be removed in a future release. The header is no longer included by boost/filesystem.hpp by default. Users are advised to implement the functionality themselves or migrate to other implementations.
+ - Deprecated: 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.
1.78.0
diff --git a/include/boost/filesystem/directory.hpp b/include/boost/filesystem/directory.hpp
index fa1e69b..66e1dd9 100644
--- a/include/boost/filesystem/directory.hpp
+++ b/include/boost/filesystem/directory.hpp
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include
#include // std::move
@@ -258,40 +259,29 @@ class directory_iterator;
namespace detail {
-BOOST_FILESYSTEM_DECL
-system::error_code dir_itr_close( // never throws()
- void*& handle
-#if defined(BOOST_POSIX_API)
- , void*& buffer
-#endif
- ) BOOST_NOEXCEPT;
-
struct dir_itr_imp :
public boost::intrusive_ref_counter< dir_itr_imp >
{
+#ifdef BOOST_WINDOWS_API
+ unsigned char extra_data_format;
+ std::size_t current_offset;
+#endif
directory_entry dir_entry;
void* handle;
-#if defined(BOOST_POSIX_API)
- void* buffer; // see dir_itr_increment implementation
-#endif
-
dir_itr_imp() BOOST_NOEXCEPT :
- handle(0)
-#if defined(BOOST_POSIX_API)
- , buffer(0)
+#ifdef BOOST_WINDOWS_API
+ extra_data_format(0u),
+ current_offset(0u),
#endif
+ handle(NULL)
{
}
+ BOOST_FILESYSTEM_DECL ~dir_itr_imp() BOOST_NOEXCEPT;
- ~dir_itr_imp() BOOST_NOEXCEPT
- {
- dir_itr_close(handle
-#if defined(BOOST_POSIX_API)
- , buffer
-#endif
- );
- }
+ BOOST_FILESYSTEM_DECL static void* operator new(std::size_t class_size, std::size_t extra_size) BOOST_NOEXCEPT;
+ BOOST_FILESYSTEM_DECL static void operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT;
+ BOOST_FILESYSTEM_DECL static void operator delete(void* p) BOOST_NOEXCEPT;
};
BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec);
diff --git a/src/directory.cpp b/src/directory.cpp
index dd2f941..e7a4c1a 100644
--- a/src/directory.cpp
+++ b/src/directory.cpp
@@ -13,6 +13,7 @@
#include "platform_config.hpp"
+#include
#include
#include
#include
@@ -61,6 +62,7 @@
#include
#include
+#include // NTSTATUS_
#include "windows_tools.hpp"
@@ -170,10 +172,58 @@ void dispatch(directory_entry const& de,
namespace detail {
+BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u;
+
+BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t extra_size) BOOST_NOEXCEPT
+{
+ if (extra_size > 0)
+ class_size = (class_size + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
+ std::size_t total_size = class_size + extra_size;
+
+ // Return NULL on OOM
+ void* p = std::malloc(total_size);
+ if (BOOST_LIKELY(p != NULL))
+ std::memset(p, 0, total_size);
+ return p;
+}
+
+BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT
+{
+ std::free(p);
+}
+
+BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) BOOST_NOEXCEPT
+{
+ std::free(p);
+}
+
namespace {
+inline void* get_dir_itr_imp_extra_data(dir_itr_imp* imp) BOOST_NOEXCEPT
+{
+ BOOST_CONSTEXPR_OR_CONST std::size_t extra_data_offset = (sizeof(dir_itr_imp) + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
+ return reinterpret_cast< unsigned char* >(imp) + extra_data_offset;
+}
+
#ifdef BOOST_POSIX_API
+inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
+{
+ if (imp.handle != NULL)
+ {
+ DIR* h = static_cast< DIR* >(imp.handle);
+ imp.handle = NULL;
+ int err = 0;
+ if (BOOST_UNLIKELY(::closedir(h) != 0))
+ {
+ err = errno;
+ return error_code(err, system_category());
+ }
+ }
+
+ return error_code();
+}
+
#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
// Obtains maximum length of a path, not including the terminating zero
@@ -216,61 +266,15 @@ inline std::size_t path_max()
#endif // BOOST_FILESYSTEM_USE_READDIR_R
-error_code dir_itr_first(void*& handle, void*& buffer, const char* dir, std::string& target, unsigned int opts, fs::file_status&, fs::file_status&)
-{
-#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
- int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
- if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
- flags |= O_NOFOLLOW;
-
- int fd = ::open(dir, flags);
- if (BOOST_UNLIKELY(fd < 0))
- {
- const int err = errno;
- return error_code(err, system_category());
- }
-
-#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
- int res = ::fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (BOOST_UNLIKELY(res < 0))
- {
- const int err = errno;
- close_fd(fd);
- return error_code(err, system_category());
- }
-#endif
-
- handle = ::fdopendir(fd);
- if (BOOST_UNLIKELY(!handle))
- {
- const int err = errno;
- close_fd(fd);
- return error_code(err, system_category());
- }
-#else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
- handle = ::opendir(dir);
- if (BOOST_UNLIKELY(!handle))
- {
- const int err = errno;
- return error_code(err, system_category());
- }
-#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
-
- target.assign("."); // string was static but caused trouble
- // when iteration called from dtor, after
- // static had already been destroyed
- return error_code();
-}
-
// *result set to NULL on end of directory
#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
inline
#endif
-int readdir_impl(DIR* dirp, void*&, struct dirent** result)
+int readdir_impl(dir_itr_imp& imp, struct dirent** result)
{
errno = 0;
- struct dirent* p = ::readdir(dirp);
+ struct dirent* p = ::readdir(static_cast< DIR* >(imp.handle));
*result = p;
if (!p)
return errno;
@@ -279,37 +283,26 @@ int readdir_impl(DIR* dirp, void*&, struct dirent** result)
#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
-inline int invoke_readdir(DIR* dirp, void*& buffer, struct dirent** result)
+inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
{
- return readdir_impl(dirp, buffer, result);
+ return readdir_impl(imp, result);
}
#else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
-int readdir_r_impl(DIR* dirp, void*& buffer, struct dirent** result)
+int readdir_r_impl(dir_itr_imp& imp, struct dirent** result)
{
- struct dirent* storage = static_cast< struct dirent* >(buffer);
- if (BOOST_UNLIKELY(!storage))
- {
- // According to readdir description, there's no reliable way to predict the length of the d_name string.
- // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
- // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
- // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
- // in favor of readdir.
- const std::size_t name_size = path_max();
- const std::size_t buffer_size = (sizeof(dirent) - sizeof(dirent().d_name)) + name_size + 1; // + 1 for "\0"
- buffer = storage = static_cast< struct dirent* >(std::malloc(buffer_size));
- if (BOOST_UNLIKELY(!storage))
- return boost::system::errc::not_enough_memory;
- std::memset(storage, 0, buffer_size);
- }
-
- return ::readdir_r(dirp, storage, result);
+ return ::readdir_r
+ (
+ static_cast< DIR* >(imp.handle),
+ static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)),
+ result
+ );
}
-int readdir_select_impl(DIR* dirp, void*& buffer, struct dirent** result);
+int readdir_select_impl(dir_itr_imp& imp, struct dirent** result);
-typedef int readdir_impl_t(DIR* dirp, void*& buffer, struct dirent** result);
+typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result);
//! Pointer to the actual implementation of the copy_file_data implementation
readdir_impl_t* readdir_impl_ptr = &readdir_select_impl;
@@ -334,29 +327,29 @@ struct readdir_initializer
BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
const readdir_initializer readdir_init;
-int readdir_select_impl(DIR* dirp, void*& buffer, struct dirent** result)
+int readdir_select_impl(dir_itr_imp& imp, struct dirent** result)
{
init_readdir_impl();
- return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(dirp, buffer, result);
+ return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
}
-inline int invoke_readdir(DIR* dirp, void*& buffer, struct dirent** result)
+inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
{
- return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(dirp, buffer, result);
+ return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
}
#endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
-error_code dir_itr_increment(void*& handle, void*& buffer, std::string& target, fs::file_status& sf, fs::file_status& symlink_sf)
+error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
{
dirent* result = NULL;
- int err = invoke_readdir(static_cast< DIR* >(handle), buffer, &result);
+ int err = invoke_readdir(imp, &result);
if (BOOST_UNLIKELY(err != 0))
return error_code(err, system_category());
if (result == NULL)
- return fs::detail::dir_itr_close(handle, buffer);
+ return dir_itr_close(imp);
- target = result->d_name;
+ filename = result->d_name;
#ifdef BOOST_FILESYSTEM_STATUS_CACHE
if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
@@ -383,203 +376,709 @@ error_code dir_itr_increment(void*& handle, void*& buffer, std::string& target,
return error_code();
}
+error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status&, fs::file_status&)
+{
+ std::size_t extra_size = 0u;
+#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
+ {
+ readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
+ if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl))
+ {
+ init_readdir_impl();
+ rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
+ }
+
+ if (rdimpl == &readdir_r_impl)
+ {
+ // According to readdir description, there's no reliable way to predict the length of the d_name string.
+ // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
+ // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
+ // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
+ // in favor of readdir.
+ extra_size = (sizeof(dirent) - sizeof(dirent().d_name)) + path_max() + 1u; // + 1 for "\0"
+ }
+ }
+#endif // defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+ boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (extra_size) detail::dir_itr_imp());
+ if (BOOST_UNLIKELY(!pimpl))
+ return make_error_code(boost::system::errc::not_enough_memory);
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
+ if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
+ flags |= O_NOFOLLOW;
+
+ int fd = ::open(dir.c_str(), flags);
+ if (BOOST_UNLIKELY(fd < 0))
+ {
+ const int err = errno;
+ return error_code(err, system_category());
+ }
+
+#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
+ int res = ::fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ const int err = errno;
+ close_fd(fd);
+ return error_code(err, system_category());
+ }
+#endif
+
+ pimpl->handle = ::fdopendir(fd);
+ if (BOOST_UNLIKELY(!pimpl->handle))
+ {
+ const int err = errno;
+ close_fd(fd);
+ return error_code(err, system_category());
+ }
+#else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ pimpl->handle = ::opendir(dir.c_str());
+ if (BOOST_UNLIKELY(!pimpl->handle))
+ {
+ const int err = errno;
+ return error_code(err, system_category());
+ }
+#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+ // Force initial readdir call by the caller. This will initialize the actual first filename and statuses.
+ first_filename.assign(".");
+
+ imp.swap(pimpl);
+ return error_code();
+}
+
+BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT;
+
#else // BOOST_WINDOWS_API
-error_code dir_itr_first(void*& handle, fs::path const& dir, std::wstring& target, unsigned int opts, fs::file_status& sf, fs::file_status& symlink_sf)
-// Note: an empty root directory has no "." or ".." entries, so this
-// causes a ERROR_FILE_NOT_FOUND error which we do not considered an
-// error. It is treated as eof instead.
+inline void set_file_statuses(DWORD attrs, const ULONG* reparse_point_tag, fs::path const& filename, fs::file_status& sf, fs::file_status& symlink_sf)
{
+ if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ {
+ // Reparse points are complex, so don't try to resolve them here; instead just mark
+ // them as status_error which causes directory_entry caching to call status()
+ // and symlink_status() which do handle reparse points fully
+ if (reparse_point_tag)
+ {
+ // If we have a reparse point tag we can at least populate the symlink status,
+ // consistent with symlink_status() behavior
+ symlink_sf.type(is_reparse_point_tag_a_symlink(*reparse_point_tag) ? fs::symlink_file : fs::reparse_file);
+ symlink_sf.permissions(make_permissions(filename, attrs));
+ }
+ else
+ {
+ symlink_sf.type(fs::status_error);
+ }
+
+ sf.type(fs::status_error);
+ }
+ else
+ {
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
+ {
+ sf.type(fs::directory_file);
+ symlink_sf.type(fs::directory_file);
+ }
+ else
+ {
+ sf.type(fs::regular_file);
+ symlink_sf.type(fs::regular_file);
+ }
+
+ sf.permissions(make_permissions(filename, attrs));
+ symlink_sf.permissions(sf.permissions());
+ }
+}
+
+#if !defined(UNDER_CE)
+
+//! FILE_ID_128 definition from Windows SDK
+struct file_id_128
+{
+ BYTE Identifier[16];
+};
+
+//! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably).
+struct file_directory_information
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+};
+
+//! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista.
+struct file_id_both_dir_info
+{
+ DWORD NextEntryOffset;
+ DWORD FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ DWORD FileAttributes;
+ DWORD FileNameLength;
+ DWORD EaSize;
+ CCHAR ShortNameLength;
+ WCHAR ShortName[12];
+ LARGE_INTEGER FileId;
+ WCHAR FileName[1];
+};
+
+//! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8.
+struct file_full_dir_info
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ WCHAR FileName[1];
+};
+
+//! FILE_ID_EXTD_DIR_INFO definition from Windows SDK. Provides reparse point tag, which saves us querying it with a few separate syscalls. Supported since Windows 8.
+struct file_id_extd_dir_info
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ ULONG ReparsePointTag;
+ file_id_128 FileId;
+ WCHAR FileName[1];
+};
+
+//! Indicates format of the extra data in the directory iterator
+enum extra_data_format
+{
+ file_directory_information_format,
+ file_id_both_dir_info_format,
+ file_full_dir_info_format,
+ file_id_extd_dir_info_format
+};
+
+//! Indicates extra data format that should be used by directory iterator by default
+extra_data_format g_extra_data_format = file_directory_information_format;
+
+/*!
+ * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator.
+ *
+ * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename.
+ * NTFS, VFAT, exFAT support filenames up to 255 UTF-16/UCS-2 characters. ReFS supports filenames up to 32768 UTF-16 characters.
+ */
+BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u;
+
+inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
+{
+ imp.extra_data_format = 0u;
+ imp.current_offset = 0u;
+
+ if (imp.handle != NULL)
+ {
+ ::CloseHandle(imp.handle);
+ imp.handle = NULL;
+ }
+
+ return error_code();
+}
+
+error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ void* extra_data = get_dir_itr_imp_extra_data(&imp);
+ const void* current_data = static_cast< const unsigned char* >(extra_data) + imp.current_offset;
+ switch (imp.extra_data_format)
+ {
+ case file_id_extd_dir_info_format:
+ {
+ const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_extd_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_id_extd_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_id_extd_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_full_dir_info_format:
+ {
+ const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_full_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_full_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_id_both_dir_info_format:
+ {
+ const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_both_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_id_both_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_id_both_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
+ }
+ break;
+
+ default:
+ {
+ const file_directory_information* data = static_cast< const file_directory_information* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ io_status_block iosb;
+ boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api)
+ (
+ imp.handle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &iosb,
+ extra_data,
+ dir_itr_extra_size,
+ file_directory_information_class,
+ FALSE, // ReturnSingleEntry
+ NULL, // FileName
+ FALSE // RestartScan
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ dir_itr_close(imp);
+ if (status == STATUS_NO_MORE_FILES)
+ goto done;
+
+ return error_code(translate_ntstatus(status), system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_directory_information* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
+ }
+ break;
+ }
+
+done:
+ return error_code();
+}
+
+error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp());
+ if (BOOST_UNLIKELY(!pimpl))
+ return make_error_code(boost::system::errc::not_enough_memory);
+
+ DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ handle_wrapper h(create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags));
+ if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
+ {
+ return_last_error:
+ DWORD error = ::GetLastError();
+ return error_code(error, system_category());
+ }
+
+ if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
+ {
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
+ if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL))
+ {
+ file_attribute_tag_info info;
+ BOOL res = get_file_information_by_handle_ex(h.handle, file_attribute_tag_info_class, &info, sizeof(info));
+ if (BOOST_UNLIKELY(!res))
+ goto return_last_error;
+
+ if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag))
+ return make_error_code(boost::system::errc::too_many_symbolic_link_levels);
+ }
+ else
+ {
+ BY_HANDLE_FILE_INFORMATION info;
+ BOOL res = ::GetFileInformationByHandle(h.handle, &info);
+ if (BOOST_UNLIKELY(!res))
+ goto return_last_error;
+
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ return make_error_code(boost::system::errc::too_many_symbolic_link_levels);
+ }
+ }
+
+ void* extra_data = get_dir_itr_imp_extra_data(pimpl.get());
+ switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format))
+ {
+ case file_id_extd_dir_info_format:
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(h.handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER)
+ {
+ // Fall back to file_full_dir_info_format.
+ // Note that some mounted filesystems may not support FILE_ID_128 identifiers, which will cause
+ // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER,
+ // even though in general the operation is supported by the kernel. So don't downgrade to
+ // FileFullDirectoryRestartInfo permanently in this case - only for this particular iterator.
+ if (error == ERROR_NOT_SUPPORTED)
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format);
+ goto fallback_to_file_full_dir_info_format;
+ }
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ pimpl->extra_data_format = file_id_extd_dir_info_format;
+
+ const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_full_dir_info_format:
+ fallback_to_file_full_dir_info_format:
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(h.handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER)
+ {
+ // Fall back to file_id_both_dir_info
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format);
+ goto fallback_to_file_id_both_dir_info;
+ }
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ pimpl->extra_data_format = file_full_dir_info_format;
+
+ const file_full_dir_info* data = static_cast< const file_full_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_id_both_dir_info_format:
+ fallback_to_file_id_both_dir_info:
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(h.handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return error_code(error, system_category());
+ }
+
+ pimpl->extra_data_format = file_id_both_dir_info_format;
+
+ const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ default:
+ {
+ NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api);
+ if (BOOST_UNLIKELY(!nt_query_directory_file))
+ return error_code(ERROR_NOT_SUPPORTED, system_category());
+
+ io_status_block iosb;
+ boost::winapi::NTSTATUS_ status = nt_query_directory_file
+ (
+ h.handle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &iosb,
+ extra_data,
+ dir_itr_extra_size,
+ file_directory_information_class,
+ FALSE, // ReturnSingleEntry
+ NULL, // FileName
+ TRUE // RestartScan
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ // Note: an empty root directory has no "." or ".." entries, so this
+ // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW
+ // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we
+ // do not consider an error. It is treated as eof instead.
+ if (status == STATUS_NO_MORE_FILES || status == STATUS_NO_SUCH_FILE)
+ goto done;
+
+ return error_code(translate_ntstatus(status), system_category());
+ }
+
+ pimpl->extra_data_format = file_directory_information_format;
+
+ const file_directory_information* data = static_cast< const file_directory_information* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
+ }
+ break;
+ }
+
+ pimpl->handle = h.handle;
+ h.handle = INVALID_HANDLE_VALUE;
+
+done:
+ imp.swap(pimpl);
+ return error_code();
+}
+
+#else // !defined(UNDER_CE)
+
+inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
+{
+ if (imp.handle != NULL)
+ {
+ ::FindClose(imp.handle);
+ imp.handle = NULL;
+ }
+
+ return error_code();
+}
+
+error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ WIN32_FIND_DATAW data;
+ if (::FindNextFileW(imp.handle, &data) == 0) // fails
+ {
+ DWORD error = ::GetLastError();
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+ return error_code(error, system_category());
+ }
+
+ filename = data.cFileName;
+ set_file_statuses(data.dwFileAttributes, NULL, filename, sf, symlink_sf);
+
+done:
+ return error_code();
+}
+
+error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (static_cast< std::size_t >(0u)) detail::dir_itr_imp());
+ if (BOOST_UNLIKELY(!pimpl))
+ return make_error_code(boost::system::errc::not_enough_memory);
+
// use a form of search Sebastian Martel reports will work with Win98
- std::wstring dirpath(dir.wstring());
- dirpath += (dirpath.empty() || (dirpath[dirpath.size() - 1] != L'\\' && dirpath[dirpath.size() - 1] != L'/' && dirpath[dirpath.size() - 1] != L':')) ? L"\\*" : L"*";
+ fs::path dirpath(dir);
+ dirpath.make_preferred();
+ dirpath /= L"*";
WIN32_FIND_DATAW data;
- if ((handle = ::FindFirstFileW(dirpath.c_str(), &data)) == INVALID_HANDLE_VALUE)
+ pimpl->handle = ::FindFirstFileW(dirpath.c_str(), &data);
+ if (BOOST_UNLIKELY(pimpl->handle == INVALID_HANDLE_VALUE))
{
- handle = 0; // signal eof
+ pimpl->handle = NULL; // signal eof
+
+ // Note: an empty root directory has no "." or ".." entries, so this
+ // causes a ERROR_FILE_NOT_FOUND error which we do not consider an
+ // error. It is treated as eof instead.
+ // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551
DWORD error = ::GetLastError();
- return error_code((error == ERROR_FILE_NOT_FOUND
- // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551
- || error == ERROR_NO_MORE_FILES) ?
- 0 :
- error,
- system_category());
- }
- target = data.cFileName;
- if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
- // reparse points are complex, so don't try to handle them here; instead just mark
- // them as status_error which causes directory_entry caching to call status()
- // and symlink_status() which do handle reparse points fully
- {
- sf.type(fs::status_error);
- symlink_sf.type(fs::status_error);
- }
- else
- {
- if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- {
- sf.type(fs::directory_file);
- symlink_sf.type(fs::directory_file);
- }
- else
- {
- sf.type(fs::regular_file);
- symlink_sf.type(fs::regular_file);
- }
- sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
- symlink_sf.permissions(sf.permissions());
+ if (error == ERROR_FILE_NOT_FOUND || error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return error_code(error, system_category());
}
+
+ first_filename = data.cFileName;
+ set_file_statuses(data.dwFileAttributes, NULL, first_filename, sf, symlink_sf);
+
+done:
+ imp.swap(pimpl);
return error_code();
}
-error_code dir_itr_increment(void*& handle, std::wstring& target, fs::file_status& sf, fs::file_status& symlink_sf)
-{
- WIN32_FIND_DATAW data;
- if (::FindNextFileW(handle, &data) == 0) // fails
- {
- DWORD error = ::GetLastError();
- fs::detail::dir_itr_close(handle);
- return error_code(error == ERROR_NO_MORE_FILES ? 0 : error, system_category());
- }
- target = data.cFileName;
- if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
- // reparse points are complex, so don't try to handle them here; instead just mark
- // them as status_error which causes directory_entry caching to call status()
- // and symlink_status() which do handle reparse points fully
- {
- sf.type(fs::status_error);
- symlink_sf.type(fs::status_error);
- }
- else
- {
- if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- {
- sf.type(fs::directory_file);
- symlink_sf.type(fs::directory_file);
- }
- else
- {
- sf.type(fs::regular_file);
- symlink_sf.type(fs::regular_file);
- }
- sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
- symlink_sf.permissions(sf.permissions());
- }
- return error_code();
-}
-#endif
+#endif // !defined(UNDER_CE)
-BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code =
-#ifdef BOOST_WINDOWS_API
- ERROR_PATH_NOT_FOUND
-#else
- ENOENT
-#endif
- ;
+BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND;
+
+#endif // BOOST_WINDOWS_API
} // namespace
-// dir_itr_close is called both from the ~dir_itr_imp()destructor
-// and dir_itr_increment()
-BOOST_FILESYSTEM_DECL
-system::error_code dir_itr_close( // never throws
- void*& handle
-#if defined(BOOST_POSIX_API)
- , void*& buffer
-#endif
- ) BOOST_NOEXCEPT
+#if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
+
+//! Initializes directory iterator implementation
+void init_directory_iterator_impl() BOOST_NOEXCEPT
{
-#ifdef BOOST_POSIX_API
-
- if (buffer != NULL)
+ if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != NULL)
{
- std::free(buffer);
- buffer = NULL;
+ // Enable the latest format we support. It will get downgraded, if needed, as we attempt
+ // to create the directory iterator the first time.
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_extd_dir_info_format);
}
+}
- if (handle != NULL)
- {
- DIR* h = static_cast< DIR* >(handle);
- handle = NULL;
- int err = 0;
- if (BOOST_UNLIKELY(::closedir(h) != 0))
- {
- err = errno;
- return error_code(err, system_category());
- }
- }
+#endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
- return error_code();
-
-#else
-
- if (handle != NULL)
- {
- ::FindClose(handle);
- handle = NULL;
- }
- return error_code();
-
-#endif
+BOOST_FILESYSTEM_DECL
+dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT
+{
+ dir_itr_close(*this);
}
BOOST_FILESYSTEM_DECL
void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec)
{
- if (error(p.empty() ? not_found_error_code : 0, p, ec, "boost::filesystem::directory_iterator::construct"))
+ if (BOOST_UNLIKELY(p.empty()))
{
+ emit_error(not_found_error_code, p, ec, "boost::filesystem::directory_iterator::construct");
return;
}
- boost::intrusive_ptr< detail::dir_itr_imp > imp;
- if (!ec)
- {
- imp = new detail::dir_itr_imp();
- }
- else
- {
- imp = new (std::nothrow) detail::dir_itr_imp();
- if (BOOST_UNLIKELY(!imp))
- {
- *ec = make_error_code(system::errc::not_enough_memory);
- return;
- }
- }
+ if (ec)
+ ec->clear();
try
{
- path::string_type filename;
+ boost::intrusive_ptr< detail::dir_itr_imp > imp;
+ path filename;
file_status file_stat, symlink_file_stat;
- error_code result = dir_itr_first(imp->handle,
-#if defined(BOOST_POSIX_API)
- imp->buffer,
-#endif
- p.c_str(), filename, opts, file_stat, symlink_file_stat);
+ system::error_code result = dir_itr_create(imp, p, opts, filename, file_stat, symlink_file_stat);
- if (result)
+ while (true)
{
- if (result != make_error_condition(system::errc::permission_denied) ||
- (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u)
+ if (result)
{
- error(result.value(), p, ec, "boost::filesystem::directory_iterator::construct");
+ if (result != make_error_condition(system::errc::permission_denied) ||
+ (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u)
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p, result));
+ *ec = result;
+ }
+
+ return;
}
- return;
- }
+ if (imp->handle == NULL) // eof, make end
+ return;
- if (imp->handle)
- {
// Not eof
- it.m_imp.swap(imp);
- it.m_imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat);
const path::string_type::value_type* filename_str = filename.c_str();
- if (filename_str[0] == path::dot // dot or dot-dot
+ if (!(filename_str[0] == path::dot // dot or dot-dot
&& (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
- (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))
+ (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
{
- detail::directory_iterator_increment(it, ec);
+ imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat);
+ it.m_imp.swap(imp);
+ return;
}
+
+ // If dot or dot-dot name produced by the underlying API, skip it until the first actual file
+ result = dir_itr_increment(*imp, filename, file_stat, symlink_file_stat);
}
}
catch (std::bad_alloc&)
@@ -602,17 +1101,13 @@ void directory_iterator_increment(directory_iterator& it, system::error_code* ec
try
{
- path::string_type filename;
+ path filename;
file_status file_stat, symlink_file_stat;
system::error_code increment_ec;
- for (;;)
+ while (true)
{
- increment_ec = dir_itr_increment(it.m_imp->handle,
-#if defined(BOOST_POSIX_API)
- it.m_imp->buffer,
-#endif
- filename, file_stat, symlink_file_stat);
+ increment_ec = dir_itr_increment(*it.m_imp, filename, file_stat, symlink_file_stat);
if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
{
diff --git a/src/error_handling.hpp b/src/error_handling.hpp
index 91edde4..cd6a72e 100644
--- a/src/error_handling.hpp
+++ b/src/error_handling.hpp
@@ -49,6 +49,73 @@ typedef boost::winapi::DWORD_ err_t;
#define BOOST_ERROR_ALREADY_EXISTS boost::winapi::ERROR_ALREADY_EXISTS_
#define BOOST_ERROR_NOT_SUPPORTED boost::winapi::ERROR_NOT_SUPPORTED_
+// Note: Legacy MinGW doesn't have ntstatus.h and doesn't define NTSTATUS error codes other than STATUS_SUCCESS.
+#if !defined(NT_SUCCESS)
+#define NT_SUCCESS(Status) (((boost::winapi::NTSTATUS_)(Status)) >= 0)
+#endif
+#if !defined(STATUS_SUCCESS)
+#define STATUS_SUCCESS ((boost::winapi::NTSTATUS_)0x00000000l)
+#endif
+#if !defined(STATUS_NOT_IMPLEMENTED)
+#define STATUS_NOT_IMPLEMENTED ((boost::winapi::NTSTATUS_)0xC0000002l)
+#endif
+#if !defined(STATUS_INVALID_INFO_CLASS)
+#define STATUS_INVALID_INFO_CLASS ((boost::winapi::NTSTATUS_)0xC0000003l)
+#endif
+#if !defined(STATUS_INVALID_HANDLE)
+#define STATUS_INVALID_HANDLE ((boost::winapi::NTSTATUS_)0xC0000008l)
+#endif
+#if !defined(STATUS_INVALID_PARAMETER)
+#define STATUS_INVALID_PARAMETER ((boost::winapi::NTSTATUS_)0xC000000Dl)
+#endif
+#if !defined(STATUS_NO_SUCH_DEVICE)
+#define STATUS_NO_SUCH_DEVICE ((boost::winapi::NTSTATUS_)0xC000000El)
+#endif
+#if !defined(STATUS_NO_SUCH_FILE)
+#define STATUS_NO_SUCH_FILE ((boost::winapi::NTSTATUS_)0xC000000Fl)
+#endif
+#if !defined(STATUS_NO_MORE_FILES)
+#define STATUS_NO_MORE_FILES ((boost::winapi::NTSTATUS_)0x80000006l)
+#endif
+#if !defined(STATUS_BUFFER_OVERFLOW)
+#define STATUS_BUFFER_OVERFLOW ((boost::winapi::NTSTATUS_)0x80000005l)
+#endif
+#if !defined(STATUS_NO_MEMORY)
+#define STATUS_NO_MEMORY ((boost::winapi::NTSTATUS_)0xC0000017l)
+#endif
+#if !defined(STATUS_ACCESS_DENIED)
+#define STATUS_ACCESS_DENIED ((boost::winapi::NTSTATUS_)0xC0000022l)
+#endif
+
+//! Converts NTSTATUS error codes to Win32 error codes for reporting
+inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status)
+{
+ // We have to cast to unsigned integral type to avoid signed overflow and narrowing conversion in the constants.
+ switch (static_cast< boost::winapi::ULONG_ >(status))
+ {
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_MEMORY):
+ return boost::winapi::ERROR_OUTOFMEMORY_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_BUFFER_OVERFLOW):
+ return boost::winapi::ERROR_BUFFER_OVERFLOW_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_HANDLE):
+ return boost::winapi::ERROR_INVALID_HANDLE_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_PARAMETER):
+ return boost::winapi::ERROR_INVALID_PARAMETER_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_MORE_FILES):
+ return boost::winapi::ERROR_NO_MORE_FILES_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_SUCH_DEVICE):
+ return boost::winapi::ERROR_DEV_NOT_EXIST_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_SUCH_FILE):
+ return boost::winapi::ERROR_FILE_NOT_FOUND_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_ACCESS_DENIED):
+ return boost::winapi::ERROR_ACCESS_DENIED_;
+ // map "invalid info class" to "not supported" as this error likely indicates that the kernel does not support what we request
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_INFO_CLASS):
+ default:
+ return boost::winapi::ERROR_NOT_SUPPORTED_;
+ }
+}
+
#endif
// error handling helpers ----------------------------------------------------------//
diff --git a/src/operations.cpp b/src/operations.cpp
index 42bc078..29dc7d7 100644
--- a/src/operations.cpp
+++ b/src/operations.cpp
@@ -184,74 +184,6 @@ using boost::system::system_category;
#else // defined(BOOST_POSIX_API)
-// REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
-// Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
-// here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
-
-#if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) // mingw winnt.h does provide the defs
-
-#define SYMLINK_FLAG_RELATIVE 1
-
-typedef struct _REPARSE_DATA_BUFFER
-{
- ULONG ReparseTag;
- USHORT ReparseDataLength;
- USHORT Reserved;
- union
- {
- /*
- * In SymbolicLink and MountPoint reparse points, there are two names.
- * SubstituteName is the effective replacement path for the reparse point.
- * This is what should be used for path traversal.
- * PrintName is intended for presentation to the user and may omit some
- * elements of the path or be absent entirely.
- *
- * Examples of substitute and print names:
- * mklink /D ldrive c:\
- * SubstituteName: "\??\c:\"
- * PrintName: "c:\"
- *
- * mklink /J ldrive c:\
- * SubstituteName: "\??\C:\"
- * PrintName: "c:\"
- *
- * junction ldrive c:\
- * SubstituteName: "\??\C:\"
- * PrintName: ""
- *
- * box.com mounted cloud storage
- * SubstituteName: "\??\Volume{}\"
- * PrintName: ""
- */
- struct
- {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- ULONG Flags;
- WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
- struct
- {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
- struct
- {
- UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- };
-} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-
-#define REPARSE_DATA_BUFFER_HEADER_SIZE \
- FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
-
-#endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
-
#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
#endif
@@ -260,8 +192,8 @@ typedef struct _REPARSE_DATA_BUFFER
#define FSCTL_GET_REPARSE_POINT 0x900a8
#endif
-#ifndef IO_REPARSE_TAG_SYMLINK
-#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#ifndef SYMLINK_FLAG_RELATIVE
+#define SYMLINK_FLAG_RELATIVE 1
#endif
// Fallback for MinGW/Cygwin
@@ -273,13 +205,6 @@ typedef struct _REPARSE_DATA_BUFFER
#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
#endif
-// Our convenience type for allocating REPARSE_DATA_BUFFER along with sufficient space after it
-union reparse_data_buffer
-{
- REPARSE_DATA_BUFFER rdb;
- unsigned char storage[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
-};
-
#endif // defined(BOOST_POSIX_API)
// POSIX/Windows macros ----------------------------------------------------//
@@ -294,16 +219,14 @@ union reparse_data_buffer
#if defined(BOOST_POSIX_API)
#define BOOST_SET_CURRENT_DIRECTORY(P) (::chdir(P) == 0)
-#define BOOST_CREATE_HARD_LINK(F, T) (::link(T, F) == 0)
#define BOOST_MOVE_FILE(OLD, NEW) (::rename(OLD, NEW) == 0)
#define BOOST_RESIZE_FILE(P, SZ) (::truncate(P, SZ) == 0)
#else // BOOST_WINDOWS_API
#define BOOST_SET_CURRENT_DIRECTORY(P) (::SetCurrentDirectoryW(P) != 0)
-#define BOOST_CREATE_HARD_LINK(F, T) (create_hard_link_api(F, T, 0) != 0)
#define BOOST_MOVE_FILE(OLD, NEW) (::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0)
-#define BOOST_RESIZE_FILE(P, SZ) (resize_file_api(P, SZ) != 0)
+#define BOOST_RESIZE_FILE(P, SZ) (resize_file_impl(P, SZ) != 0)
#endif
@@ -316,6 +239,11 @@ namespace detail {
void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver);
#endif // defined(linux) || defined(__linux) || defined(__linux__)
+#if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
+//! Initializes directory iterator implementation. Implemented in directory.cpp.
+void init_directory_iterator_impl() BOOST_NOEXCEPT;
+#endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
+
//--------------------------------------------------------------------------------------//
// //
// helpers (all operating systems) //
@@ -324,6 +252,9 @@ void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsig
namespace {
+// The number of retries remove_all should make if it detects that the directory it is about to enter has been replaced with a symlink
+BOOST_CONSTEXPR_OR_CONST unsigned int remove_all_directory_replaced_retry_count = 5u;
+
#if defined(BOOST_POSIX_API)
// Size of a small buffer for a path that can be placed on stack, in character code units
@@ -945,7 +876,7 @@ inline bool remove_impl(path const& p, error_code* ec)
//! remove_all() implementation
uintmax_t remove_all_impl(path const& p, error_code* ec)
{
- for (unsigned int attempt = 0u; attempt < 5u; ++attempt)
+ for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
{
fs::file_type type;
{
@@ -966,7 +897,6 @@ uintmax_t remove_all_impl(path const& p, error_code* ec)
}
uintmax_t count = 0u;
-
if (type == fs::directory_file) // but not a directory symlink
{
fs::directory_iterator itr;
@@ -1022,6 +952,71 @@ uintmax_t remove_all_impl(path const& p, error_code* ec)
// //
//--------------------------------------------------------------------------------------//
+// REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
+// Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
+// here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
+struct reparse_data_buffer
+{
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union
+ {
+ /*
+ * In SymbolicLink and MountPoint reparse points, there are two names.
+ * SubstituteName is the effective replacement path for the reparse point.
+ * This is what should be used for path traversal.
+ * PrintName is intended for presentation to the user and may omit some
+ * elements of the path or be absent entirely.
+ *
+ * Examples of substitute and print names:
+ * mklink /D ldrive c:\
+ * SubstituteName: "\??\c:\"
+ * PrintName: "c:\"
+ *
+ * mklink /J ldrive c:\
+ * SubstituteName: "\??\C:\"
+ * PrintName: "c:\"
+ *
+ * junction ldrive c:\
+ * SubstituteName: "\??\C:\"
+ * PrintName: ""
+ *
+ * box.com mounted cloud storage
+ * SubstituteName: "\??\Volume{}\"
+ * PrintName: ""
+ */
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct
+ {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+};
+
+// Our convenience type for allocating REPARSE_DATA_BUFFER along with sufficient space after it
+union reparse_data_buffer_with_storage
+{
+ reparse_data_buffer rdb;
+ unsigned char storage[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+};
+
// Windows CE has no environment variables
#if !defined(UNDER_CE)
inline std::wstring wgetenv(const wchar_t* name)
@@ -1068,53 +1063,40 @@ inline void to_FILETIME(std::time_t t, FILETIME& ft) BOOST_NOEXCEPT
ft.dwHighDateTime = static_cast< DWORD >(temp >> 32);
}
-// Thanks to Jeremy Maitin-Shepard for much help and for permission to
-// base the equivalent()implementation on portions of his
-// file-equivalence-win32.cpp experimental code.
-
-struct handle_wrapper
+inline bool is_reparse_point_a_symlink(path const& p)
{
- HANDLE handle;
+ handle_wrapper h(create_file_handle(p, 0u, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
+ if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
+ return false;
- handle_wrapper() BOOST_NOEXCEPT : handle(INVALID_HANDLE_VALUE) {}
- explicit handle_wrapper(HANDLE h) BOOST_NOEXCEPT : handle(h) {}
- ~handle_wrapper() BOOST_NOEXCEPT
+ ULONG reparse_point_tag;
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
+ if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL))
{
- if (handle != INVALID_HANDLE_VALUE)
- ::CloseHandle(handle);
+ file_attribute_tag_info info;
+ BOOL result = get_file_information_by_handle_ex(h.handle, file_attribute_tag_info_class, &info, sizeof(info));
+ if (BOOST_UNLIKELY(!result))
+ return false;
+
+ if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0u)
+ return false;
+
+ reparse_point_tag = info.ReparseTag;
}
- BOOST_DELETED_FUNCTION(handle_wrapper(handle_wrapper const&))
- BOOST_DELETED_FUNCTION(handle_wrapper& operator=(handle_wrapper const&))
-};
+ else
+ {
+ boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage);
-inline HANDLE create_file_handle(path const& p, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
-{
- return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
-}
+ // Query the reparse data
+ DWORD dwRetLen = 0u;
+ BOOL result = ::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(), sizeof(*buf), &dwRetLen, NULL);
+ if (BOOST_UNLIKELY(!result))
+ return false;
-bool is_reparse_point_a_symlink(path const& p)
-{
- handle_wrapper h(create_file_handle(p, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL));
- if (h.handle == INVALID_HANDLE_VALUE)
- return false;
+ reparse_point_tag = buf->rdb.ReparseTag;
+ }
- boost::scoped_ptr< reparse_data_buffer > buf(new reparse_data_buffer);
-
- // Query the reparse data
- DWORD dwRetLen = 0u;
- BOOL result = ::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(), sizeof(*buf), &dwRetLen, NULL);
- if (!result)
- return false;
-
- return buf->rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK
- // Issue 9016 asked that NTFS directory junctions be recognized as directories.
- // That is equivalent to recognizing them as symlinks, and then the normal symlink
- // mechanism will take care of recognizing them as directories.
- //
- // Directory junctions are very similar to symlinks, but have some performance
- // and other advantages over symlinks. They can be created from the command line
- // with "mklink /j junction-name target-path".
- || buf->rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; // aka "directory junction" or "junction"
+ return is_reparse_point_tag_a_symlink(reparse_point_tag);
}
inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* buf, wchar_t** p)
@@ -1238,66 +1220,85 @@ inline bool remove_impl(path const& p, error_code* ec)
//! remove_all() implementation
uintmax_t remove_all_impl(path const& p, error_code* ec)
{
- const DWORD attrs = ::GetFileAttributesW(p.c_str());
- bool recurse;
- if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
+ for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
{
- error_code local_ec;
- file_type type = process_status_failure(p, &local_ec).type();
-
- if (type == fs::file_not_found)
- return 0u;
-
- if (BOOST_UNLIKELY(type == fs::status_error))
+ const DWORD attrs = ::GetFileAttributesW(p.c_str());
+ bool recurse;
+ if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
{
- if (!ec)
- BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+ error_code local_ec;
+ file_type type = process_status_failure(p, &local_ec).type();
- *ec = local_ec;
- return static_cast< uintmax_t >(-1);
+ if (type == fs::file_not_found)
+ return 0u;
+
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ // Some unknown file type
+ recurse = false;
+ }
+ else
+ {
+ // Recurse into directories, but not into junctions or directory symlinks
+ recurse = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
}
- // Some unknown file type
- recurse = false;
- }
- else
- {
- // Recurse into directories, but not into junctions or directory symlinks
- recurse = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
- }
+ uintmax_t count = 0u;
+ if (recurse)
+ {
+ fs::directory_iterator itr;
+ error_code local_ec;
+ fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::_detail_no_follow), &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ if (local_ec == make_error_condition(system::errc::too_many_symbolic_link_levels))
+ continue;
- uintmax_t count = 0u;
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
- if (recurse)
- {
- fs::directory_iterator itr;
- fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), ec);
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ count += remove_all_impl(itr->path(), ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+ count += remove_impl(p, attrs, ec);
if (ec && *ec)
return static_cast< uintmax_t >(-1);
- const fs::directory_iterator end_dit;
- while (itr != end_dit)
- {
- count += remove_all_impl(itr->path(), ec);
- if (ec && *ec)
- return static_cast< uintmax_t >(-1);
-
- fs::detail::directory_iterator_increment(itr, ec);
- if (ec && *ec)
- return static_cast< uintmax_t >(-1);
- }
+ return count;
}
- count += remove_impl(p, attrs, ec);
- if (ec && *ec)
- return static_cast< uintmax_t >(-1);
+ error_code local_ec(make_error_code(system::errc::too_many_symbolic_link_levels));
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all: path cannot be opened as a directory", p, local_ec));
- return count;
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
}
-inline BOOL resize_file_api(const wchar_t* p, uintmax_t size)
+inline BOOL resize_file_impl(const wchar_t* p, uintmax_t size)
{
- handle_wrapper h(CreateFileW(p, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+ handle_wrapper h(CreateFileW(p, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
LARGE_INTEGER sz;
sz.QuadPart = size;
return h.handle != INVALID_HANDLE_VALUE && ::SetFilePointerEx(h.handle, sz, 0, FILE_BEGIN) && ::SetEndOfFile(h.handle);
@@ -1452,19 +1453,29 @@ done:
// Windows kernel32.dll functions that may or may not be present
// must be accessed through pointers
-typedef BOOL (WINAPI* PtrCreateHardLinkW)(
+typedef BOOL (WINAPI CreateHardLinkW_t)(
/*__in*/ LPCWSTR lpFileName,
/*__in*/ LPCWSTR lpExistingFileName,
/*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-PtrCreateHardLinkW create_hard_link_api = NULL;
+CreateHardLinkW_t* create_hard_link_api = NULL;
-typedef BOOLEAN (WINAPI* PtrCreateSymbolicLinkW)(
+typedef BOOLEAN (WINAPI CreateSymbolicLinkW_t)(
/*__in*/ LPCWSTR lpSymlinkFileName,
/*__in*/ LPCWSTR lpTargetFileName,
/*__in*/ DWORD dwFlags);
-PtrCreateSymbolicLinkW create_symbolic_link_api = NULL;
+CreateSymbolicLinkW_t* create_symbolic_link_api = NULL;
+
+} // unnamed namespace
+
+GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api = NULL;
+
+#if !defined(UNDER_CE)
+NtQueryDirectoryFile_t* nt_query_directory_file_api = NULL;
+#endif // !defined(UNDER_CE)
+
+namespace {
//! Initializes WinAPI function pointers
BOOST_FILESYSTEM_INIT_FUNC init_winapi_func_ptrs()
@@ -1472,9 +1483,21 @@ BOOST_FILESYSTEM_INIT_FUNC init_winapi_func_ptrs()
boost::winapi::HMODULE_ h = boost::winapi::GetModuleHandleW(L"kernel32.dll");
if (BOOST_LIKELY(!!h))
{
- create_hard_link_api = (PtrCreateHardLinkW)boost::winapi::get_proc_address(h, "CreateHardLinkW");
- create_symbolic_link_api = (PtrCreateSymbolicLinkW)boost::winapi::get_proc_address(h, "CreateSymbolicLinkW");
+ filesystem::detail::atomic_store_relaxed(get_file_information_by_handle_ex_api, (GetFileInformationByHandleEx_t*)boost::winapi::get_proc_address(h, "GetFileInformationByHandleEx"));
+ filesystem::detail::atomic_store_relaxed(create_hard_link_api, (CreateHardLinkW_t*)boost::winapi::get_proc_address(h, "CreateHardLinkW"));
+ filesystem::detail::atomic_store_relaxed(create_symbolic_link_api, (CreateSymbolicLinkW_t*)boost::winapi::get_proc_address(h, "CreateSymbolicLinkW"));
}
+
+#if !defined(UNDER_CE)
+ h = boost::winapi::GetModuleHandleW(L"ntdll.dll");
+ if (BOOST_LIKELY(!!h))
+ {
+ filesystem::detail::atomic_store_relaxed(nt_query_directory_file_api, (NtQueryDirectoryFile_t*)boost::winapi::get_proc_address(h, "NtQueryDirectoryFile"));
+ }
+
+ init_directory_iterator_impl();
+#endif // !defined(UNDER_CE)
+
return BOOST_FILESYSTEM_INITRETSUCCESS_V;
}
@@ -2155,7 +2178,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod
// Create handle_wrappers here so that CloseHandle calls don't clobber error code returned by GetLastError
handle_wrapper hw_from, hw_to;
- hw_from.handle = create_file_handle(from.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
+ hw_from.handle = create_file_handle(from.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS);
FILETIME lwt_from;
if (hw_from.handle == INVALID_HANDLE_VALUE)
@@ -2169,7 +2192,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod
if (!::GetFileTime(hw_from.handle, 0, 0, &lwt_from))
goto fail_last_error;
- hw_to.handle = create_file_handle(to.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
+ hw_to.handle = create_file_handle(to.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS);
if (hw_to.handle != INVALID_HANDLE_VALUE)
{
@@ -2484,13 +2507,30 @@ void create_directory_symlink(path const& to, path const& from, system::error_co
BOOST_FILESYSTEM_DECL
void create_hard_link(path const& to, path const& from, error_code* ec)
{
-#if defined(BOOST_WINDOWS_API)
- // see if actually supported by Windows runtime dll
- if (error(!create_hard_link_api ? BOOST_ERROR_NOT_SUPPORTED : 0, to, from, ec, "boost::filesystem::create_hard_link"))
- return;
-#endif
+ if (ec)
+ ec->clear();
- error(!BOOST_CREATE_HARD_LINK(from.c_str(), to.c_str()) ? BOOST_ERRNO : 0, to, from, ec, "boost::filesystem::create_hard_link");
+#if defined(BOOST_POSIX_API)
+ int err = ::link(to.c_str(), from.c_str());
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ emit_error(err, to, from, ec, "boost::filesystem::create_hard_link");
+ }
+#else
+ // see if actually supported by Windows runtime dll
+ CreateHardLinkW_t* chl_api = filesystem::detail::atomic_load_relaxed(create_hard_link_api);
+ if (BOOST_UNLIKELY(!chl_api))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_hard_link");
+ return;
+ }
+
+ if (BOOST_UNLIKELY(!chl_api(from.c_str(), to.c_str(), NULL)))
+ {
+ emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_hard_link");
+ }
+#endif
}
BOOST_FILESYSTEM_DECL
@@ -2508,13 +2548,14 @@ void create_symlink(path const& to, path const& from, error_code* ec)
}
#else
// see if actually supported by Windows runtime dll
- if (!create_symbolic_link_api)
+ CreateSymbolicLinkW_t* csl_api = filesystem::detail::atomic_load_relaxed(create_symbolic_link_api);
+ if (BOOST_UNLIKELY(!csl_api))
{
emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_symlink");
return;
}
- if (!create_symbolic_link_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
+ if (BOOST_UNLIKELY(!csl_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)))
{
emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_symlink");
}
@@ -2647,6 +2688,10 @@ bool equivalent(path const& p1, path const& p2, system::error_code* ec)
#else // Windows
+ // Thanks to Jeremy Maitin-Shepard for much help and for permission to
+ // base the equivalent() implementation on portions of his
+ // file-equivalence-win32.cpp experimental code.
+
// Note well: Physical location on external media is part of the
// equivalence criteria. If there are no open handles, physical location
// can change due to defragmentation or other relocations. Thus handles
@@ -2657,22 +2702,20 @@ bool equivalent(path const& p1, path const& p2, system::error_code* ec)
handle_wrapper h2(
create_file_handle(
p2.c_str(),
- 0,
+ 0u,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- 0,
+ NULL,
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- 0));
+ FILE_FLAG_BACKUP_SEMANTICS));
handle_wrapper h1(
create_file_handle(
p1.c_str(),
- 0,
+ 0u,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- 0,
+ NULL,
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- 0));
+ FILE_FLAG_BACKUP_SEMANTICS));
if (BOOST_UNLIKELY(h1.handle == INVALID_HANDLE_VALUE || h2.handle == INVALID_HANDLE_VALUE))
{
@@ -2807,7 +2850,7 @@ uintmax_t hard_link_count(path const& p, system::error_code* ec)
#else // defined(BOOST_POSIX_API)
handle_wrapper h(
- create_file_handle(p.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
+ create_file_handle(p.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS));
if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
{
@@ -2929,7 +2972,7 @@ std::time_t creation_time(path const& p, system::error_code* ec)
#else // defined(BOOST_POSIX_API)
handle_wrapper hw(
- create_file_handle(p.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
+ create_file_handle(p.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS));
if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
{
@@ -2982,7 +3025,7 @@ std::time_t last_write_time(path const& p, system::error_code* ec)
#else // defined(BOOST_POSIX_API)
handle_wrapper hw(
- create_file_handle(p.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
+ create_file_handle(p.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS));
if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
{
@@ -3044,7 +3087,7 @@ void last_write_time(path const& p, const std::time_t new_time, system::error_co
#else // defined(BOOST_POSIX_API)
handle_wrapper hw(
- create_file_handle(p.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
+ create_file_handle(p.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS));
if (BOOST_UNLIKELY(hw.handle == INVALID_HANDLE_VALUE))
{
@@ -3156,6 +3199,9 @@ void permissions(path const& p, perms prms, system::error_code* ec)
BOOST_FILESYSTEM_DECL
path read_symlink(path const& p, system::error_code* ec)
{
+ if (ec)
+ ec->clear();
+
path symlink_path;
#ifdef BOOST_POSIX_API
@@ -3174,8 +3220,6 @@ path read_symlink(path const& p, system::error_code* ec)
else if (BOOST_LIKELY(static_cast< std::size_t >(result) < sizeof(small_buf)))
{
symlink_path.assign(small_buf, small_buf + result);
- if (ec)
- ec->clear();
}
else
{
@@ -3199,8 +3243,6 @@ path read_symlink(path const& p, system::error_code* ec)
else if (BOOST_LIKELY(static_cast< std::size_t >(result) < path_max))
{
symlink_path.assign(buf.get(), buf.get() + result);
- if (ec)
- ec->clear();
break;
}
}
@@ -3209,40 +3251,50 @@ path read_symlink(path const& p, system::error_code* ec)
#else
handle_wrapper h(
- create_file_handle(p.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0));
+ create_file_handle(p.c_str(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
- if (error(h.handle == INVALID_HANDLE_VALUE ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::read_symlink"))
- return symlink_path;
-
- boost::scoped_ptr< reparse_data_buffer > buf(new reparse_data_buffer);
- DWORD sz = 0u;
- if (!error(::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, 0, 0, buf.get(), sizeof(*buf), &sz, 0) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::read_symlink"))
+ DWORD error;
+ if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
{
- const wchar_t* buffer;
- std::size_t offset, len;
- switch (buf->rdb.ReparseTag)
- {
- case IO_REPARSE_TAG_MOUNT_POINT:
- buffer = buf->rdb.MountPointReparseBuffer.PathBuffer;
- offset = buf->rdb.MountPointReparseBuffer.SubstituteNameOffset;
- len = buf->rdb.MountPointReparseBuffer.SubstituteNameLength;
- break;
+ error = ::GetLastError();
- case IO_REPARSE_TAG_SYMLINK:
- buffer = buf->rdb.SymbolicLinkReparseBuffer.PathBuffer;
- offset = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameOffset;
- len = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameLength;
- // Note: iff info.rdb.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE
- // -> resulting path is relative to the source
- break;
-
- default:
- emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "Unknown ReparseTag in boost::filesystem::read_symlink");
- return symlink_path;
- }
-
- symlink_path = convert_nt_path_to_win32_path(buffer + offset / sizeof(wchar_t), len / sizeof(wchar_t));
+ return_error:
+ emit_error(error, p, ec, "boost::filesystem::read_symlink");
+ return symlink_path;
}
+
+ boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage);
+ DWORD sz = 0u;
+ if (BOOST_UNLIKELY(!::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, 0, 0, buf.get(), sizeof(*buf), &sz, 0)))
+ {
+ error = ::GetLastError();
+ goto return_error;
+ }
+
+ const wchar_t* buffer;
+ std::size_t offset, len;
+ switch (buf->rdb.ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ buffer = buf->rdb.MountPointReparseBuffer.PathBuffer;
+ offset = buf->rdb.MountPointReparseBuffer.SubstituteNameOffset;
+ len = buf->rdb.MountPointReparseBuffer.SubstituteNameLength;
+ break;
+
+ case IO_REPARSE_TAG_SYMLINK:
+ buffer = buf->rdb.SymbolicLinkReparseBuffer.PathBuffer;
+ offset = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameOffset;
+ len = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameLength;
+ // Note: iff info.rdb.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE
+ // -> resulting path is relative to the source
+ break;
+
+ default:
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "Unknown ReparseTag in boost::filesystem::read_symlink");
+ return symlink_path;
+ }
+
+ symlink_path = convert_nt_path_to_win32_path(buffer + offset / sizeof(wchar_t), len / sizeof(wchar_t));
#endif
return symlink_path;
@@ -3479,12 +3531,11 @@ file_status status(path const& p, error_code* ec)
handle_wrapper h(
create_file_handle(
p.c_str(),
- 0, // dwDesiredAccess; attributes only
+ 0u, // dwDesiredAccess; attributes only
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- 0, // lpSecurityAttributes
+ NULL, // lpSecurityAttributes
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- 0)); // hTemplateFile
+ FILE_FLAG_BACKUP_SEMANTICS));
if (h.handle == INVALID_HANDLE_VALUE)
{
diff --git a/src/unique_path.cpp b/src/unique_path.cpp
index d48f8ed..4815ca4 100644
--- a/src/unique_path.cpp
+++ b/src/unique_path.cpp
@@ -108,27 +108,6 @@ namespace detail {
namespace {
-#if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
-//! Converts NTSTATUS error codes to Win32 error codes for reporting
-inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status)
-{
- // Note: Legacy MinGW doesn't have ntstatus.h and doesn't define NTSTATUS error codes other than STATUS_SUCCESS.
- // Because of this we have to use hardcoded integer literals here. Also, we have to cast to unsigned
- // integral type to avoid signed overflow and narrowing conversion in the constants.
- switch (static_cast< boost::winapi::ULONG_ >(status))
- {
- case 0xC0000017ul: // STATUS_NO_MEMORY
- return boost::winapi::ERROR_OUTOFMEMORY_;
- case 0xC0000008ul: // STATUS_INVALID_HANDLE
- return boost::winapi::ERROR_INVALID_HANDLE_;
- case 0xC000000Dul: // STATUS_INVALID_PARAMETER
- return boost::winapi::ERROR_INVALID_PARAMETER_;
- default:
- return boost::winapi::ERROR_NOT_SUPPORTED_;
- }
-}
-#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
-
#if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
//! Fills buffer with cryptographically random data obtained from /dev/(u)random
diff --git a/src/windows_tools.hpp b/src/windows_tools.hpp
index b1fe3c3..d68f999 100644
--- a/src/windows_tools.hpp
+++ b/src/windows_tools.hpp
@@ -13,12 +13,22 @@
#ifndef BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_
#define BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_
+#include
#include
#include
#include
+#include // NTSTATUS_
#include
+#ifndef IO_REPARSE_TAG_MOUNT_POINT
+#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
+#endif
+
+#ifndef IO_REPARSE_TAG_SYMLINK
+#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+
namespace boost {
namespace filesystem {
namespace detail {
@@ -52,6 +62,121 @@ inline boost::filesystem::perms make_permissions(boost::filesystem::path const&
return prms;
}
+inline bool is_reparse_point_tag_a_symlink(ULONG reparse_point_tag)
+{
+ return reparse_point_tag == IO_REPARSE_TAG_SYMLINK
+ // Issue 9016 asked that NTFS directory junctions be recognized as directories.
+ // That is equivalent to recognizing them as symlinks, and then the normal symlink
+ // mechanism will take care of recognizing them as directories.
+ //
+ // Directory junctions are very similar to symlinks, but have some performance
+ // and other advantages over symlinks. They can be created from the command line
+ // with "mklink /J junction-name target-path".
+ //
+ // Note that mounted filesystems also have the same repartse point tag, which makes
+ // them look like directory symlinks in terms of Boost.Filesystem. read_symlink()
+ // may return a volume path or NT path for such symlinks.
+ || reparse_point_tag == IO_REPARSE_TAG_MOUNT_POINT; // aka "directory junction" or "junction"
+}
+
+#if !defined(UNDER_CE)
+
+//! IO_STATUS_BLOCK definition from Windows SDK.
+struct io_status_block
+{
+ union
+ {
+ boost::winapi::NTSTATUS_ Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+};
+
+//! UNICODE_STRING definition from Windows SDK
+struct unicode_string
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+};
+
+//! PIO_APC_ROUTINE definition from Windows SDK
+typedef VOID (NTAPI* pio_apc_routine) (PVOID ApcContext, io_status_block* IoStatusBlock, ULONG Reserved);
+
+//! FILE_INFORMATION_CLASS enum entries
+enum file_information_class
+{
+ file_directory_information_class = 1
+};
+
+//! NtQueryDirectoryFile signature. Available since Windows NT 4.0 (probably).
+typedef boost::winapi::NTSTATUS_ (NTAPI NtQueryDirectoryFile_t)(
+ /*in*/ HANDLE FileHandle,
+ /*in, optional*/ HANDLE Event,
+ /*in, optional*/ pio_apc_routine ApcRoutine,
+ /*in, optional*/ PVOID ApcContext,
+ /*out*/ io_status_block* IoStatusBlock,
+ /*out*/ PVOID FileInformation,
+ /*in*/ ULONG Length,
+ /*in*/ file_information_class FileInformationClass,
+ /*in*/ BOOLEAN ReturnSingleEntry,
+ /*in, optional*/ unicode_string* FileName,
+ /*in*/ BOOLEAN RestartScan);
+
+extern NtQueryDirectoryFile_t* nt_query_directory_file_api;
+
+#endif // !defined(UNDER_CE)
+
+//! FILE_INFO_BY_HANDLE_CLASS enum entries
+enum file_info_by_handle_class
+{
+ file_attribute_tag_info_class = 9,
+ file_id_both_directory_info_class = 10,
+ file_id_both_directory_restart_info_class = 11,
+ file_full_directory_info_class = 14,
+ file_full_directory_restart_info_class = 15,
+ file_id_extd_directory_info_class = 19,
+ file_id_extd_directory_restart_info_class = 20
+};
+
+//! FILE_ATTRIBUTE_TAG_INFO definition from Windows SDK
+struct file_attribute_tag_info
+{
+ DWORD FileAttributes;
+ DWORD ReparseTag;
+};
+
+//! GetFileInformationByHandleEx signature. Available since Windows Vista.
+typedef BOOL (WINAPI GetFileInformationByHandleEx_t)(
+ /*__in*/ HANDLE hFile,
+ /*__in*/ file_info_by_handle_class FileInformationClass, // the actual type is FILE_INFO_BY_HANDLE_CLASS enum
+ /*__out_bcount(dwBufferSize)*/ LPVOID lpFileInformation,
+ /*__in*/ DWORD dwBufferSize);
+
+extern GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api;
+
+//! HANDLE wrapper that automatically closes the handle
+struct handle_wrapper
+{
+ HANDLE handle;
+
+ handle_wrapper() BOOST_NOEXCEPT : handle(INVALID_HANDLE_VALUE) {}
+ explicit handle_wrapper(HANDLE h) BOOST_NOEXCEPT : handle(h) {}
+ ~handle_wrapper() BOOST_NOEXCEPT
+ {
+ if (handle != INVALID_HANDLE_VALUE)
+ ::CloseHandle(handle);
+ }
+ BOOST_DELETED_FUNCTION(handle_wrapper(handle_wrapper const&))
+ BOOST_DELETED_FUNCTION(handle_wrapper& operator=(handle_wrapper const&))
+};
+
+//! Creates a file handle
+inline HANDLE create_file_handle(boost::filesystem::path const& p, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile = NULL)
+{
+ return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+}
+
} // namespace detail
} // namespace filesystem
} // namespace boost