diff options
author | thegeorg <[email protected]> | 2022-08-29 07:42:14 +0300 |
---|---|---|
committer | thegeorg <[email protected]> | 2022-08-29 07:42:14 +0300 |
commit | 08b42c5732ff484354c4ca8adba3b002b117d122 (patch) | |
tree | e9757bdde5d9e8a10b187efffacbe8ea9282a0f5 /contrib/restricted/boost/filesystem/src | |
parent | 9f0fa80273f73bc1848572d7bd8a08ad530e438e (diff) |
Update contrib/restricted/boost/filesystem to 1.80.0
Diffstat (limited to 'contrib/restricted/boost/filesystem/src')
14 files changed, 1107 insertions, 470 deletions
diff --git a/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp index 501f78f103c..72db677a09c 100644 --- a/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp +++ b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp @@ -22,6 +22,8 @@ #include "private_config.hpp" +#include <boost/filesystem/detail/header.hpp> // must be the last #include + //--------------------------------------------------------------------------------------// namespace boost { @@ -29,6 +31,13 @@ namespace filesystem { namespace { +#if (defined(BOOST_GCC) && BOOST_GCC >= 40600) || defined(BOOST_CLANG) +#pragma GCC diagnostic push +// '(anonymous namespace)::codecvt_error_cat' has virtual functions but non-virtual destructor +// This is not a problem as instances of codecvt_error_cat are never destroyed through a pointer to base. +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + class codecvt_error_cat BOOST_FINAL : public boost::system::error_category { @@ -68,6 +77,10 @@ std::string codecvt_error_cat::message(int ev) const return str; } +#if (defined(BOOST_GCC) && BOOST_GCC >= 40600) || defined(BOOST_CLANG) +#pragma GCC diagnostic pop +#endif + } // unnamed namespace BOOST_FILESYSTEM_DECL boost::system::error_category const& codecvt_error_category() BOOST_NOEXCEPT @@ -103,3 +116,5 @@ const codecvt_error_category_initializer g_codecvt_error_category_initializer; } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/directory.cpp b/contrib/restricted/boost/filesystem/src/directory.cpp index cbe8319cf03..6a3e1dc7319 100644 --- a/contrib/restricted/boost/filesystem/src/directory.cpp +++ b/contrib/restricted/boost/filesystem/src/directory.cpp @@ -72,6 +72,8 @@ #include "error_handling.hpp" #include "private_config.hpp" +#include <boost/filesystem/detail/header.hpp> // must be the last #include + // BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in // dir_itr_increment. The config tests are placed here because some of the // macros being tested come from dirent.h. @@ -376,7 +378,7 @@ error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_stat 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&) +error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status&, fs::file_status&) { std::size_t extra_size = 0u; #if defined(BOOST_FILESYSTEM_USE_READDIR_R) @@ -409,7 +411,11 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) flags |= O_NOFOLLOW; +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + int fd = ::openat(params ? params->basedir_fd : AT_FDCWD, dir.c_str(), flags); +#else int fd = ::open(dir.c_str(), flags); +#endif if (BOOST_UNLIKELY(fd < 0)) { const int err = errno; @@ -445,6 +451,11 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: // Force initial readdir call by the caller. This will initialize the actual first filename and statuses. first_filename.assign("."); +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) + if (params) + params->iterator_fd = fd; +#endif + imp.swap(pimpl); return error_code(); } @@ -586,7 +597,7 @@ 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. */ @@ -599,7 +610,8 @@ inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT if (imp.handle != NULL) { - ::CloseHandle(imp.handle); + if (BOOST_LIKELY(imp.close_handle)) + ::CloseHandle(imp.handle); imp.handle = NULL; } @@ -751,54 +763,76 @@ 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) +error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, directory_iterator_params* params, 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(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)) + GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api); + + handle_wrapper h; + HANDLE iterator_handle; + bool close_handle = true; + if (params != NULL && params->use_handle != INVALID_HANDLE_VALUE) { - return_last_error: - DWORD error = ::GetLastError(); - return error_code(error, system_category()); + // Operate on externally provided handle, which must be a directory handle + iterator_handle = params->use_handle; + close_handle = params->close_handle; } - - 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)) + else { - 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 (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) - return make_error_code(system::errc::not_a_directory); - + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + + iterator_handle = h.handle = create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags); + if (BOOST_UNLIKELY(iterator_handle == INVALID_HANDLE_VALUE)) { - if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag)) - return make_error_code(system::errc::too_many_symbolic_link_levels); + return_last_error: + DWORD error = ::GetLastError(); + return error_code(error, system_category()); } - } - else - { - BY_HANDLE_FILE_INFORMATION info; - BOOL res = ::GetFileInformationByHandle(h.handle, &info); - if (BOOST_UNLIKELY(!res)) - goto return_last_error; - if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) - return make_error_code(system::errc::not_a_directory); + if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL)) + { + file_attribute_tag_info info; + BOOL res = get_file_information_by_handle_ex(iterator_handle, file_attribute_tag_info_class, &info, sizeof(info)); + if (BOOST_UNLIKELY(!res)) + { + // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER. See the comment in symlink_status. + DWORD error = ::GetLastError(); + if (error == ERROR_INVALID_PARAMETER || error == ERROR_NOT_SUPPORTED) + goto use_get_file_information_by_handle; - if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) + return error_code(error, system_category()); + } + + if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) + return make_error_code(system::errc::not_a_directory); + + if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) + { + if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag)) + return make_error_code(system::errc::too_many_symbolic_link_levels); + } + } + else { - if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_a_symlink_ioctl(h.handle)) - return make_error_code(system::errc::too_many_symbolic_link_levels); + use_get_file_information_by_handle: + BY_HANDLE_FILE_INFORMATION info; + BOOL res = ::GetFileInformationByHandle(iterator_handle, &info); + if (BOOST_UNLIKELY(!res)) + goto return_last_error; + + if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) + return make_error_code(system::errc::not_a_directory); + + if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) + { + if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_a_symlink_ioctl(h.handle)) + return make_error_code(system::errc::too_many_symbolic_link_levels); + } } } @@ -807,7 +841,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: { case file_id_extd_dir_info_format: { - if (!get_file_information_by_handle_ex(h.handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size)) + if (!get_file_information_by_handle_ex(iterator_handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size)) { DWORD error = ::GetLastError(); @@ -841,7 +875,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: case file_full_dir_info_format: fallback_to_file_full_dir_info_format: { - if (!get_file_information_by_handle_ex(h.handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size)) + if (!get_file_information_by_handle_ex(iterator_handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size)) { DWORD error = ::GetLastError(); @@ -870,7 +904,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: case file_id_both_dir_info_format: fallback_to_file_id_both_dir_info: { - if (!get_file_information_by_handle_ex(h.handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size)) + if (!get_file_information_by_handle_ex(iterator_handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size)) { DWORD error = ::GetLastError(); @@ -898,7 +932,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: io_status_block iosb; boost::winapi::NTSTATUS_ status = nt_query_directory_file ( - h.handle, + iterator_handle, NULL, // Event NULL, // ApcRoutine NULL, // ApcContext @@ -933,8 +967,10 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: break; } - pimpl->handle = h.handle; + + pimpl->handle = iterator_handle; h.handle = INVALID_HANDLE_VALUE; + pimpl->close_handle = close_handle; done: imp.swap(pimpl); @@ -947,7 +983,8 @@ inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT { if (imp.handle != NULL) { - ::FindClose(imp.handle); + if (BOOST_LIKELY(imp.close_handle)) + ::FindClose(imp.handle); imp.handle = NULL; } @@ -973,7 +1010,7 @@ 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) +error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, directory_iterator_params*, 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)) @@ -1001,6 +1038,8 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: return error_code(error, system_category()); } + pimpl->close_handle = true; + first_filename = data.cFileName; set_file_statuses(data.dwFileAttributes, NULL, first_filename, sf, symlink_sf); @@ -1039,7 +1078,7 @@ dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT } BOOST_FILESYSTEM_DECL -void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec) +void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, directory_iterator_params* params, system::error_code* ec) { if (BOOST_UNLIKELY(p.empty())) { @@ -1055,7 +1094,7 @@ void directory_iterator_construct(directory_iterator& it, path const& p, unsigne boost::intrusive_ptr< detail::dir_itr_imp > imp; path filename; file_status file_stat, symlink_file_stat; - system::error_code result = dir_itr_create(imp, p, opts, filename, file_stat, symlink_file_stat); + system::error_code result = dir_itr_create(imp, p, opts, params, filename, file_stat, symlink_file_stat); while (true) { @@ -1169,7 +1208,7 @@ void recursive_directory_iterator_construct(recursive_directory_iterator& it, pa ec->clear(); directory_iterator dir_it; - detail::directory_iterator_construct(dir_it, dir_path, opts, ec); + detail::directory_iterator_construct(dir_it, dir_path, opts, NULL, ec); if ((ec && *ec) || dir_it == directory_iterator()) return; @@ -1307,8 +1346,8 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: file_status symlink_stat; - // if we are not recursing into symlinks, we are going to have to know if the - // stack top is a symlink, so get symlink_status and verify no error occurred + // If we are not recursing into symlinks, we are going to have to know if the + // stack top is a symlink, so get symlink_status and verify no error occurred. if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) == 0u || (imp->m_options & static_cast< unsigned int >(directory_options::skip_dangling_symlinks)) != 0u) { @@ -1452,3 +1491,5 @@ void recursive_directory_iterator_increment(recursive_directory_iterator& it, sy } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/error_handling.hpp b/contrib/restricted/boost/filesystem/src/error_handling.hpp index cd6a72e17fd..97590a87120 100644 --- a/contrib/restricted/boost/filesystem/src/error_handling.hpp +++ b/contrib/restricted/boost/filesystem/src/error_handling.hpp @@ -24,6 +24,8 @@ #include <boost/winapi/error_codes.hpp> #endif +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace boost { namespace filesystem { @@ -86,6 +88,33 @@ typedef boost::winapi::DWORD_ err_t; #if !defined(STATUS_ACCESS_DENIED) #define STATUS_ACCESS_DENIED ((boost::winapi::NTSTATUS_)0xC0000022l) #endif +#if !defined(STATUS_OBJECT_NAME_NOT_FOUND) +#define STATUS_OBJECT_NAME_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC0000034l) +#endif +#if !defined(STATUS_OBJECT_PATH_NOT_FOUND) +#define STATUS_OBJECT_PATH_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC000003Al) +#endif +#if !defined(STATUS_NOT_SUPPORTED) +#define STATUS_NOT_SUPPORTED ((boost::winapi::NTSTATUS_)0xC00000BBl) +#endif +#if !defined(STATUS_BAD_NETWORK_PATH) +#define STATUS_BAD_NETWORK_PATH ((boost::winapi::NTSTATUS_)0xC00000BEl) +#endif +#if !defined(STATUS_DEVICE_DOES_NOT_EXIST) +#define STATUS_DEVICE_DOES_NOT_EXIST ((boost::winapi::NTSTATUS_)0xC00000C0l) +#endif +#if !defined(STATUS_BAD_NETWORK_NAME) +#define STATUS_BAD_NETWORK_NAME ((boost::winapi::NTSTATUS_)0xC00000CCl) +#endif +#if !defined(STATUS_DIRECTORY_NOT_EMPTY) +#define STATUS_DIRECTORY_NOT_EMPTY ((boost::winapi::NTSTATUS_)0xC0000101l) +#endif +#if !defined(STATUS_NOT_A_DIRECTORY) +#define STATUS_NOT_A_DIRECTORY ((boost::winapi::NTSTATUS_)0xC0000103l) +#endif +#if !defined(STATUS_NOT_FOUND) +#define STATUS_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC0000225l) +#endif //! Converts NTSTATUS error codes to Win32 error codes for reporting inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status) @@ -104,11 +133,24 @@ inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status) 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): + case static_cast< boost::winapi::ULONG_ >(STATUS_DEVICE_DOES_NOT_EXIST): return boost::winapi::ERROR_DEV_NOT_EXIST_; case static_cast< boost::winapi::ULONG_ >(STATUS_NO_SUCH_FILE): + case static_cast< boost::winapi::ULONG_ >(STATUS_OBJECT_NAME_NOT_FOUND): + case static_cast< boost::winapi::ULONG_ >(STATUS_OBJECT_PATH_NOT_FOUND): return boost::winapi::ERROR_FILE_NOT_FOUND_; case static_cast< boost::winapi::ULONG_ >(STATUS_ACCESS_DENIED): return boost::winapi::ERROR_ACCESS_DENIED_; + case static_cast< boost::winapi::ULONG_ >(STATUS_BAD_NETWORK_PATH): + return boost::winapi::ERROR_BAD_NETPATH_; + case static_cast< boost::winapi::ULONG_ >(STATUS_BAD_NETWORK_NAME): + return boost::winapi::ERROR_BAD_NET_NAME_; + case static_cast< boost::winapi::ULONG_ >(STATUS_DIRECTORY_NOT_EMPTY): + return boost::winapi::ERROR_DIR_NOT_EMPTY_; + case static_cast< boost::winapi::ULONG_ >(STATUS_NOT_A_DIRECTORY): + return boost::winapi::ERROR_DIRECTORY_; // The directory name is invalid + case static_cast< boost::winapi::ULONG_ >(STATUS_NOT_FOUND): + return boost::winapi::ERROR_NOT_FOUND_; // 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: @@ -173,4 +215,6 @@ inline bool error(err_t error_num, path const& p1, path const& p2, system::error } // namespace filesystem } // namespace boost +#include <boost/filesystem/detail/footer.hpp> + #endif // BOOST_FILESYSTEM_SRC_ERROR_HANDLING_HPP_ diff --git a/contrib/restricted/boost/filesystem/src/exception.cpp b/contrib/restricted/boost/filesystem/src/exception.cpp index b0480a8b8e6..91ee1c90a9b 100644 --- a/contrib/restricted/boost/filesystem/src/exception.cpp +++ b/contrib/restricted/boost/filesystem/src/exception.cpp @@ -19,7 +19,7 @@ #include "error_handling.hpp" -#include <boost/config/abi_prefix.hpp> // must be the last #include +#include <boost/filesystem/detail/header.hpp> // must be the last #include namespace boost { namespace filesystem { @@ -185,4 +185,4 @@ void emit_error(err_t error_num, path const& p1, path const& p2, system::error_c } // namespace filesystem } // namespace boost -#include <boost/config/abi_suffix.hpp> // pops abi_prefix.hpp pragmas +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/operations.cpp b/contrib/restricted/boost/filesystem/src/operations.cpp index 8d2f0adf963..dd636e90637 100644 --- a/contrib/restricted/boost/filesystem/src/operations.cpp +++ b/contrib/restricted/boost/filesystem/src/operations.cpp @@ -32,18 +32,21 @@ #include <cstddef> #include <cstdlib> // for malloc, free #include <cstring> -#include <cstdio> // for remove, rename -#if defined(__QNXNTO__) // see ticket #5355 -#include <stdio.h> -#endif #include <cerrno> +#include <stdio.h> // for rename + +// Default to POSIX under Emscripten +// If BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI is set, use WASI instead +#if defined(__wasm) && (!defined(__EMSCRIPTEN__) || defined(BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI)) +#define BOOST_FILESYSTEM_USE_WASI +#endif #ifdef BOOST_POSIX_API #include <sys/types.h> #include <sys/stat.h> -#if defined(__wasm) +#if defined(BOOST_FILESYSTEM_USE_WASI) // WASI does not have statfs or statvfs. #elif !defined(__APPLE__) && \ (!defined(__OpenBSD__) || BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(4, 4, 0)) && \ @@ -164,6 +167,8 @@ using std::time_t; #include "error_handling.hpp" #include "private_config.hpp" +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace fs = boost::filesystem; using boost::filesystem::path; using boost::filesystem::filesystem_error; @@ -286,7 +291,7 @@ BOOST_CONSTEXPR_OR_CONST unsigned int symloop_max = bool is_empty_directory(path const& p, error_code* ec) { fs::directory_iterator itr; - detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), ec); + detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), NULL, ec); return itr == fs::directory_iterator(); } @@ -472,6 +477,130 @@ inline std::size_t get_blksize(struct ::stat const& st) BOOST_NOEXCEPT #endif // defined(BOOST_FILESYSTEM_USE_STATX) +//! status() implementation +file_status status_impl +( + path const& p, + error_code* ec +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX) + , int basedir_fd = AT_FDCWD +#endif +) +{ +#if defined(BOOST_FILESYSTEM_USE_STATX) + struct ::statx path_stat; + int err = invoke_statx(basedir_fd, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat); +#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + struct ::stat path_stat; + int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_NO_AUTOMOUNT); +#else + struct ::stat path_stat; + int err = ::stat(p.c_str(), &path_stat); +#endif + + if (err != 0) + { + err = errno; + if (ec) // always report errno, even though some + ec->assign(err, system_category()); // errno values are not status_errors + + if (not_found_error(err)) + return fs::file_status(fs::file_not_found, fs::no_perms); + + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, error_code(err, system_category()))); + + return fs::file_status(fs::status_error); + } + +#if defined(BOOST_FILESYSTEM_USE_STATX) + if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE))) + { + emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::status"); + return fs::file_status(fs::status_error); + } +#endif + + const mode_t mode = get_mode(path_stat); + if (S_ISDIR(mode)) + return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISREG(mode)) + return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISBLK(mode)) + return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISCHR(mode)) + return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISFIFO(mode)) + return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISSOCK(mode)) + return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask); + + return fs::file_status(fs::type_unknown); +} + +//! symlink_status() implementation +file_status symlink_status_impl +( + path const& p, + error_code* ec +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX) + , int basedir_fd = AT_FDCWD +#endif +) +{ +#if defined(BOOST_FILESYSTEM_USE_STATX) + struct ::statx path_stat; + int err = invoke_statx(basedir_fd, p.c_str(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat); +#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + struct ::stat path_stat; + int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); +#else + struct ::stat path_stat; + int err = ::lstat(p.c_str(), &path_stat); +#endif + + if (err != 0) + { + err = errno; + if (ec) // always report errno, even though some + ec->assign(err, system_category()); // errno values are not status_errors + + if (not_found_error(err)) // these are not errors + return fs::file_status(fs::file_not_found, fs::no_perms); + + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::symlink_status", p, error_code(err, system_category()))); + + return fs::file_status(fs::status_error); + } + +#if defined(BOOST_FILESYSTEM_USE_STATX) + if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE))) + { + emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::symlink_status"); + return fs::file_status(fs::status_error); + } +#endif + + const mode_t mode = get_mode(path_stat); + if (S_ISREG(mode)) + return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISDIR(mode)) + return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISLNK(mode)) + return fs::file_status(fs::symlink_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISBLK(mode)) + return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISCHR(mode)) + return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISFIFO(mode)) + return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask); + if (S_ISSOCK(mode)) + return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask); + + return fs::file_status(fs::type_unknown); +} + //! Flushes buffered data and attributes written to the file to permanent storage inline int full_sync(int fd) { @@ -611,130 +740,144 @@ typedef int copy_file_data_t(int infile, int outfile, uintmax_t size, std::size_ //! Pointer to the actual implementation of the copy_file_data implementation copy_file_data_t* copy_file_data = ©_file_data_read_write; +#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) + +//! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem +template< typename CopyFileData > +int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize); + +#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) + #if defined(BOOST_FILESYSTEM_USE_SENDFILE) -//! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors. -int copy_file_data_sendfile(int infile, int outfile, uintmax_t size, std::size_t blksize) +struct copy_file_data_sendfile { - // sendfile will not send more than this amount of data in one call - BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u; - uintmax_t offset = 0u; - while (offset < size) - { - uintmax_t size_left = size - offset; - std::size_t size_to_copy = max_batch_size; - if (size_left < static_cast< uintmax_t >(max_batch_size)) - size_to_copy = static_cast< std::size_t >(size_left); - ssize_t sz = ::sendfile(outfile, infile, NULL, size_to_copy); - if (BOOST_UNLIKELY(sz < 0)) + //! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors. + static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize) + { + // sendfile will not send more than this amount of data in one call + BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u; + uintmax_t offset = 0u; + while (offset < size) { - int err = errno; - if (err == EINTR) - continue; - - if (offset == 0u) + uintmax_t size_left = size - offset; + std::size_t size_to_copy = max_batch_size; + if (size_left < static_cast< uintmax_t >(max_batch_size)) + size_to_copy = static_cast< std::size_t >(size_left); + ssize_t sz = ::sendfile(outfile, infile, NULL, size_to_copy); + if (BOOST_UNLIKELY(sz < 0)) { - // sendfile may fail with EINVAL if the underlying filesystem does not support it - if (err == EINVAL) - { - fallback_to_read_write: - return copy_file_data_read_write(infile, outfile, size, blksize); - } + int err = errno; + if (err == EINTR) + continue; - if (err == ENOSYS) + if (offset == 0u) { - filesystem::detail::atomic_store_relaxed(copy_file_data, ©_file_data_read_write); - goto fallback_to_read_write; + // sendfile may fail with EINVAL if the underlying filesystem does not support it + if (err == EINVAL) + { + fallback_to_read_write: + return copy_file_data_read_write(infile, outfile, size, blksize); + } + + if (err == ENOSYS) + { + filesystem::detail::atomic_store_relaxed(copy_file_data, ©_file_data_read_write); + goto fallback_to_read_write; + } } + + return err; } - return err; + offset += sz; } - offset += sz; + return 0; } - - return 0; -} +}; #endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) #if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) -//! copy_file implementation that uses copy_file_range loop. Requires copy_file_range to support cross-filesystem copying. -int copy_file_data_copy_file_range(int infile, int outfile, uintmax_t size, std::size_t blksize) +struct copy_file_data_copy_file_range { - // Although copy_file_range does not document any particular upper limit of one transfer, still use some upper bound to guarantee - // that size_t is not overflown in case if off_t is larger and the file size does not fit in size_t. - BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u; - uintmax_t offset = 0u; - while (offset < size) - { - uintmax_t size_left = size - offset; - std::size_t size_to_copy = max_batch_size; - if (size_left < static_cast< uintmax_t >(max_batch_size)) - size_to_copy = static_cast< std::size_t >(size_left); - // Note: Use syscall directly to avoid depending on libc version. copy_file_range is added in glibc 2.27. - // uClibc-ng does not have copy_file_range as of the time of this writing (the latest uClibc-ng release is 1.0.33). - loff_t sz = ::syscall(__NR_copy_file_range, infile, (loff_t*)NULL, outfile, (loff_t*)NULL, size_to_copy, (unsigned int)0u); - if (BOOST_UNLIKELY(sz < 0)) + //! copy_file implementation that uses copy_file_range loop. Requires copy_file_range to support cross-filesystem copying. + static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize) + { + // Although copy_file_range does not document any particular upper limit of one transfer, still use some upper bound to guarantee + // that size_t is not overflown in case if off_t is larger and the file size does not fit in size_t. + BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u; + uintmax_t offset = 0u; + while (offset < size) { - int err = errno; - if (err == EINTR) - continue; - - if (offset == 0u) + uintmax_t size_left = size - offset; + std::size_t size_to_copy = max_batch_size; + if (size_left < static_cast< uintmax_t >(max_batch_size)) + size_to_copy = static_cast< std::size_t >(size_left); + // Note: Use syscall directly to avoid depending on libc version. copy_file_range is added in glibc 2.27. + // uClibc-ng does not have copy_file_range as of the time of this writing (the latest uClibc-ng release is 1.0.33). + loff_t sz = ::syscall(__NR_copy_file_range, infile, (loff_t*)NULL, outfile, (loff_t*)NULL, size_to_copy, (unsigned int)0u); + if (BOOST_UNLIKELY(sz < 0)) { - // copy_file_range may fail with EINVAL if the underlying filesystem does not support it. - // In some RHEL/CentOS 7.7-7.8 kernel versions, copy_file_range on NFSv4 is also known to return EOPNOTSUPP - // if the remote server does not support COPY, despite that it is not a documented error code. - // See https://patchwork.kernel.org/project/linux-nfs/patch/[email protected]/ - // and https://bugzilla.redhat.com/show_bug.cgi?id=1783554. - if (err == EINVAL || err == EOPNOTSUPP) + int err = errno; + if (err == EINTR) + continue; + + if (offset == 0u) { + // copy_file_range may fail with EINVAL if the underlying filesystem does not support it. + // In some RHEL/CentOS 7.7-7.8 kernel versions, copy_file_range on NFSv4 is also known to return EOPNOTSUPP + // if the remote server does not support COPY, despite that it is not a documented error code. + // See https://patchwork.kernel.org/project/linux-nfs/patch/[email protected]/ + // and https://bugzilla.redhat.com/show_bug.cgi?id=1783554. + if (err == EINVAL || err == EOPNOTSUPP) + { #if !defined(BOOST_FILESYSTEM_USE_SENDFILE) - fallback_to_read_write: + fallback_to_read_write: #endif - return copy_file_data_read_write(infile, outfile, size, blksize); - } + return copy_file_data_read_write(infile, outfile, size, blksize); + } - if (err == EXDEV) - { + if (err == EXDEV) + { #if defined(BOOST_FILESYSTEM_USE_SENDFILE) - fallback_to_sendfile: - return copy_file_data_sendfile(infile, outfile, size, blksize); + fallback_to_sendfile: + return copy_file_data_sendfile::impl(infile, outfile, size, blksize); #else - goto fallback_to_read_write; + goto fallback_to_read_write; #endif - } + } - if (err == ENOSYS) - { + if (err == ENOSYS) + { #if defined(BOOST_FILESYSTEM_USE_SENDFILE) - filesystem::detail::atomic_store_relaxed(copy_file_data, ©_file_data_sendfile); - goto fallback_to_sendfile; + filesystem::detail::atomic_store_relaxed(copy_file_data, &check_fs_type< copy_file_data_sendfile >); + goto fallback_to_sendfile; #else - filesystem::detail::atomic_store_relaxed(copy_file_data, ©_file_data_read_write); - goto fallback_to_read_write; + filesystem::detail::atomic_store_relaxed(copy_file_data, ©_file_data_read_write); + goto fallback_to_read_write; #endif + } } + + return err; } - return err; + offset += sz; } - offset += sz; + return 0; } - - return 0; -} +}; #endif // defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) #if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) //! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem -template< copy_file_data_t* CopyFileData > +template< typename CopyFileData > int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize) { { @@ -768,7 +911,7 @@ int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize) } } - return CopyFileData(infile, outfile, size, blksize); + return CopyFileData::impl(infile, outfile, size, blksize); } #endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) @@ -784,14 +927,14 @@ inline void init_copy_file_data_impl(unsigned int major_ver, unsigned int minor_ #if defined(BOOST_FILESYSTEM_USE_SENDFILE) // sendfile started accepting file descriptors as the target in Linux 2.6.33 if (major_ver > 2u || (major_ver == 2u && (minor_ver > 6u || (minor_ver == 6u && patch_ver >= 33u)))) - cfd = &check_fs_type< ©_file_data_sendfile >; + cfd = &check_fs_type< copy_file_data_sendfile >; #endif #if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE) // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3. // copy_file_data_copy_file_range will fallback to copy_file_data_sendfile if copy_file_range returns EXDEV. if (major_ver > 4u || (major_ver == 4u && minor_ver >= 5u)) - cfd = &check_fs_type< ©_file_data_copy_file_range >; + cfd = &check_fs_type< copy_file_data_copy_file_range >; #endif filesystem::detail::atomic_store_relaxed(copy_file_data, cfd); @@ -827,16 +970,28 @@ const syscall_initializer syscall_init; #endif // defined(linux) || defined(__linux) || defined(__linux__) //! remove() implementation -inline bool remove_impl(path const& p, fs::file_type type, error_code* ec) +inline bool remove_impl +( + path const& p, + fs::file_type type, + error_code* ec +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + , int basedir_fd = AT_FDCWD +#endif +) { if (type == fs::file_not_found) return false; int res; +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + res = ::unlinkat(basedir_fd, p.c_str(), type == fs::directory_file ? AT_REMOVEDIR : 0); +#else if (type == fs::directory_file) res = ::rmdir(p.c_str()); else res = ::unlink(p.c_str()); +#endif if (res != 0) { @@ -860,7 +1015,7 @@ inline bool remove_impl(path const& p, error_code* ec) // to remove them. error_code local_ec; - fs::file_type type = fs::detail::symlink_status(p, &local_ec).type(); + fs::file_type type = fs::detail::symlink_status_impl(p, &local_ec).type(); if (BOOST_UNLIKELY(type == fs::status_error)) { if (!ec) @@ -874,15 +1029,35 @@ inline bool remove_impl(path const& p, error_code* ec) } //! remove_all() implementation -uintmax_t remove_all_impl(path const& p, error_code* ec) +uintmax_t remove_all_impl +( + path const& p, + error_code* ec +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + , int basedir_fd = AT_FDCWD +#endif +) { +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + fs::detail::directory_iterator_params params; + params.basedir_fd = basedir_fd; + params.iterator_fd = -1; +#endif + error_code dit_create_ec; for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt) { fs::file_type type; { error_code local_ec; - type = fs::detail::symlink_status(p, &local_ec).type(); + type = fs::detail::symlink_status_impl + ( + p, + &local_ec +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + , basedir_fd +#endif + ).type(); if (type == fs::file_not_found) return 0u; @@ -901,7 +1076,19 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) if (type == fs::directory_file) // but not a directory symlink { fs::directory_iterator itr; - fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::_detail_no_follow), &dit_create_ec); + fs::detail::directory_iterator_construct + ( + itr, + p, + static_cast< unsigned int >(directory_options::_detail_no_follow), +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + ¶ms, +#else + NULL, +#endif + &dit_create_ec + ); + if (BOOST_UNLIKELY(!!dit_create_ec)) { if (dit_create_ec == error_code(ENOTDIR, system_category())) @@ -926,7 +1113,18 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) const fs::directory_iterator end_dit; while (itr != end_dit) { - count += fs::detail::remove_all_impl(itr->path(), ec); + count += fs::detail::remove_all_impl + ( +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + itr->path().filename(), +#else + itr->path(), +#endif + ec +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + , params.iterator_fd +#endif + ); if (ec && *ec) return static_cast< uintmax_t >(-1); @@ -936,7 +1134,15 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) } } - count += fs::detail::remove_impl(p, type, ec); + count += fs::detail::remove_impl + ( + p, + type, + ec +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + , basedir_fd +#endif + ); if (ec && *ec) return static_cast< uintmax_t >(-1); @@ -1088,6 +1294,7 @@ SetFileInformationByHandle_t* set_file_information_by_handle_api = NULL; GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api = NULL; #if !defined(UNDER_CE) +NtCreateFile_t* nt_create_file_api = NULL; NtQueryDirectoryFile_t* nt_query_directory_file_api = NULL; #endif // !defined(UNDER_CE) @@ -1129,6 +1336,7 @@ BOOST_FILESYSTEM_INIT_FUNC init_winapi_func_ptrs() h = boost::winapi::GetModuleHandleW(L"ntdll.dll"); if (BOOST_LIKELY(!!h)) { + filesystem::detail::atomic_store_relaxed(nt_create_file_api, (NtCreateFile_t*)boost::winapi::get_proc_address(h, "NtCreateFile")); filesystem::detail::atomic_store_relaxed(nt_query_directory_file_api, (NtQueryDirectoryFile_t*)boost::winapi::get_proc_address(h, "NtQueryDirectoryFile")); } @@ -1235,6 +1443,74 @@ inline void to_FILETIME(std::time_t t, FILETIME& ft) BOOST_NOEXCEPT } // unnamed namespace +#if !defined(UNDER_CE) + +//! The flag indicates whether OBJ_DONT_REPARSE flag is not supported by the kernel +static bool g_no_obj_dont_reparse = false; + +//! Creates a file handle for a file relative to a previously opened base directory. The file path must be relative and in preferred format. +boost::winapi::NTSTATUS_ nt_create_file_handle_at(HANDLE& out, HANDLE basedir_handle, boost::filesystem::path const& p, ULONG FileAttributes, ACCESS_MASK DesiredAccess, ULONG ShareMode, ULONG CreateDisposition, ULONG CreateOptions) +{ + NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api); + if (BOOST_UNLIKELY(!nt_create_file)) + return STATUS_NOT_IMPLEMENTED; + + unicode_string obj_name = {}; + obj_name.Buffer = const_cast< wchar_t* >(p.c_str()); + obj_name.Length = obj_name.MaximumLength = static_cast< USHORT >(p.size() * sizeof(wchar_t)); + + object_attributes obj_attrs = {}; + obj_attrs.Length = sizeof(obj_attrs); + obj_attrs.RootDirectory = basedir_handle; + obj_attrs.ObjectName = &obj_name; + + obj_attrs.Attributes = OBJ_CASE_INSENSITIVE; + if ((CreateOptions & FILE_OPEN_REPARSE_POINT) != 0u && !filesystem::detail::atomic_load_relaxed(g_no_obj_dont_reparse)) + obj_attrs.Attributes |= OBJ_DONT_REPARSE; + + io_status_block iosb; + boost::winapi::NTSTATUS_ status = nt_create_file + ( + &out, + DesiredAccess, + &obj_attrs, + &iosb, + NULL, // AllocationSize + FileAttributes, + ShareMode, + CreateDisposition, + CreateOptions, + NULL, // EaBuffer + 0u // EaLength + ); + + if (BOOST_UNLIKELY(status == STATUS_INVALID_PARAMETER && (obj_attrs.Attributes & OBJ_DONT_REPARSE) != 0u)) + { + // OBJ_DONT_REPARSE is supported since Windows 10, retry without it + filesystem::detail::atomic_store_relaxed(g_no_obj_dont_reparse, true); + obj_attrs.Attributes &= ~static_cast< ULONG >(OBJ_DONT_REPARSE); + + status = nt_create_file + ( + &out, + DesiredAccess, + &obj_attrs, + &iosb, + NULL, // AllocationSize + FileAttributes, + ShareMode, + CreateDisposition, + CreateOptions, + NULL, // EaBuffer + 0u // EaLength + ); + } + + return status; +} + +#endif // !defined(UNDER_CE) + bool is_reparse_point_a_symlink_ioctl(HANDLE h) { boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage); @@ -1252,7 +1528,13 @@ namespace { inline bool is_reparse_point_a_symlink(path const& p) { - 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)); + handle_wrapper h(create_file_handle( + p, + FILE_READ_ATTRIBUTES, + 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; @@ -1278,9 +1560,8 @@ inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p)); } -inline fs::file_status process_status_failure(path const& p, error_code* ec) +inline fs::file_status process_status_failure(DWORD errval, path const& p, error_code* ec) { - DWORD errval = ::GetLastError(); if (ec) // always report errval, even though some ec->assign(errval, system_category()); // errval values are not status_errors @@ -1299,6 +1580,129 @@ inline fs::file_status process_status_failure(path const& p, error_code* ec) return fs::file_status(fs::status_error); } +inline fs::file_status process_status_failure(path const& p, error_code* ec) +{ + return process_status_failure(::GetLastError(), p, ec); +} + +//! (symlink_)status() by handle implementation +fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec) +{ + fs::file_type ftype; + DWORD attrs; + 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, file_attribute_tag_info_class, &info, sizeof(info)); + if (BOOST_UNLIKELY(!res)) + { + // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER. + // Presumably, this is because these filesystems don't support reparse points, so ReparseTag + // cannot be returned. Also check ERROR_NOT_SUPPORTED for good measure. Fall back to the legacy + // code path in this case. + DWORD err = ::GetLastError(); + if (err == ERROR_INVALID_PARAMETER || err == ERROR_NOT_SUPPORTED) + goto use_get_file_information_by_handle; + + return process_status_failure(err, p, ec); + } + + attrs = info.FileAttributes; + + if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) + { + ftype = is_reparse_point_tag_a_symlink(info.ReparseTag) ? fs::symlink_file : fs::reparse_file; + goto done; + } + } + else + { + use_get_file_information_by_handle: + BY_HANDLE_FILE_INFORMATION info; + BOOL res = ::GetFileInformationByHandle(h, &info); + if (BOOST_UNLIKELY(!res)) + return process_status_failure(p, ec); + + attrs = info.dwFileAttributes; + + if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) + { + ftype = is_reparse_point_a_symlink_ioctl(h) ? fs::symlink_file : fs::reparse_file; + goto done; + } + } + + ftype = (attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file; + +done: + return fs::file_status(ftype, make_permissions(p, attrs)); +} + +//! symlink_status() implementation +fs::file_status symlink_status_impl(path const& p, error_code* ec) +{ + handle_wrapper h(create_file_handle( + p.c_str(), + FILE_READ_ATTRIBUTES, // dwDesiredAccess; attributes only + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, // lpSecurityAttributes + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); + + if (h.handle == INVALID_HANDLE_VALUE) + { + // For some system files and folders like "System Volume Information" CreateFileW fails + // with ERROR_ACCESS_DENIED. GetFileAttributesW succeeds for such files, so try that. + // Though this will only help if the file is not a reparse point (symlink or not). + DWORD err = ::GetLastError(); + if (err == ERROR_ACCESS_DENIED) + { + DWORD attrs = ::GetFileAttributesW(p.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES) + { + if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0u) + return fs::file_status((attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file, make_permissions(p, attrs)); + } + else + { + err = ::GetLastError(); + } + } + + return process_status_failure(err, p, ec); + } + + return detail::status_by_handle(h.handle, p, ec); +} + +//! status() implementation +fs::file_status status_impl(path const& p, error_code* ec) +{ + // We should first test if the file is a symlink or a reparse point. Resolving some reparse + // points by opening the file may fail, and status() should return file_status(reparse_file) in this case. + // Which is what symlink_status() returns. + fs::file_status st(detail::symlink_status_impl(p, ec)); + if (st.type() == symlink_file) + { + // Resolve the symlink + handle_wrapper h(create_file_handle( + p.c_str(), + FILE_READ_ATTRIBUTES, // dwDesiredAccess; attributes only + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, // lpSecurityAttributes + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS)); + + if (h.handle == INVALID_HANDLE_VALUE) + return process_status_failure(p, ec); + + st = detail::status_by_handle(h.handle, p, ec); + } + + return st; +} + //! remove() implementation for Windows XP and older bool remove_nt5_impl(path const& p, DWORD attrs, error_code* ec) { @@ -1354,32 +1758,19 @@ bool remove_nt5_impl(path const& p, DWORD attrs, error_code* ec) return true; } -//! remove() implementation for Windows Vista and newer -bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) +//! remove() by handle implementation for Windows Vista and newer +DWORD remove_nt6_by_handle(HANDLE handle, remove_impl_type impl) { - handle_wrapper h(create_file_handle(p, DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); - DWORD err; - if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) - { - err = ::GetLastError(); - - return_error: - if (!not_found_error(err)) - emit_error(err, p, ec, "boost::filesystem::remove"); - - return false; - } - GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api); SetFileInformationByHandle_t* set_file_information_by_handle = filesystem::detail::atomic_load_relaxed(set_file_information_by_handle_api); + DWORD err = 0u; switch (impl) { case remove_disp_ex_flag_ignore_readonly: { file_disposition_info_ex info; info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE; - BOOL res = set_file_information_by_handle(h.handle, file_disposition_info_ex_class, &info, sizeof(info)); + BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info)); if (BOOST_LIKELY(!!res)) break; @@ -1392,7 +1783,7 @@ bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) } else { - goto return_error; + break; } } BOOST_FALLTHROUGH; @@ -1401,40 +1792,46 @@ bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) { file_disposition_info_ex info; info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS; - BOOL res = set_file_information_by_handle(h.handle, file_disposition_info_ex_class, &info, sizeof(info)); + BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info)); if (BOOST_LIKELY(!!res)) + { + err = 0u; break; + } err = ::GetLastError(); if (err == ERROR_ACCESS_DENIED) { // Check if the file is read-only and reset the attribute file_basic_info basic_info; - res = get_file_information_by_handle_ex(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)) - goto return_error; // return ERROR_ACCESS_DENIED + break; // return ERROR_ACCESS_DENIED basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; - res = set_file_information_by_handle(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); if (BOOST_UNLIKELY(!res)) { err = ::GetLastError(); - goto return_error; + break; } // Try to set the flag again - res = set_file_information_by_handle(h.handle, file_disposition_info_ex_class, &info, sizeof(info)); + res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info)); if (BOOST_LIKELY(!!res)) + { + err = 0u; break; + } err = ::GetLastError(); // Try to restore the read-only flag basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY; - set_file_information_by_handle(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); - goto return_error; + break; } else if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED)) { @@ -1444,7 +1841,7 @@ bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) } else { - goto return_error; + break; } } BOOST_FALLTHROUGH; @@ -1453,54 +1850,89 @@ bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) { file_disposition_info info; info.DeleteFile = true; - BOOL res = set_file_information_by_handle(h.handle, file_disposition_info_class, &info, sizeof(info)); + BOOL res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info)); if (BOOST_LIKELY(!!res)) + { + err = 0u; break; + } err = ::GetLastError(); if (err == ERROR_ACCESS_DENIED) { // Check if the file is read-only and reset the attribute file_basic_info basic_info; - res = get_file_information_by_handle_ex(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)) - goto return_error; // return ERROR_ACCESS_DENIED + break; // return ERROR_ACCESS_DENIED basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; - res = set_file_information_by_handle(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); if (BOOST_UNLIKELY(!res)) { err = ::GetLastError(); - goto return_error; + break; } // Try to set the flag again - res = set_file_information_by_handle(h.handle, file_disposition_info_class, &info, sizeof(info)); + res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info)); if (BOOST_LIKELY(!!res)) + { + err = 0u; break; + } err = ::GetLastError(); // Try to restore the read-only flag basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY; - set_file_information_by_handle(h.handle, file_basic_info_class, &basic_info, sizeof(basic_info)); + set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info)); } - goto return_error; + break; } } + return err; +} + +//! remove() implementation for Windows Vista and newer +inline bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec) +{ + handle_wrapper h(create_file_handle( + p, + DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); + DWORD err = 0u; + if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) + { + err = ::GetLastError(); + + return_error: + if (!not_found_error(err)) + emit_error(err, p, ec, "boost::filesystem::remove"); + + return false; + } + + err = fs::detail::remove_nt6_by_handle(h.handle, impl); + if (BOOST_UNLIKELY(err != 0u)) + goto return_error; + return true; } //! remove() implementation inline bool remove_impl(path const& p, error_code* ec) { - remove_impl_type impl = filesystem::detail::atomic_load_relaxed(g_remove_impl_type); + remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type); if (BOOST_LIKELY(impl != remove_nt5)) { - return remove_nt6_impl(p, impl, ec); + return fs::detail::remove_nt6_impl(p, impl, ec); } else { @@ -1514,14 +1946,131 @@ inline bool remove_impl(path const& p, error_code* ec) return false; } - return remove_nt5_impl(p, attrs, ec); + return fs::detail::remove_nt5_impl(p, attrs, ec); } } -//! remove_all() implementation -uintmax_t remove_all_impl(path const& p, error_code* ec) +#if !defined(UNDER_CE) + +//! remove_all() by handle implementation for Windows Vista and newer +uintmax_t remove_all_nt6_by_handle(HANDLE h, path const& p, error_code* ec) +{ + error_code local_ec; + fs::file_status st(fs::detail::status_by_handle(h, p, &local_ec)); + if (BOOST_UNLIKELY(st.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); + } + + uintmax_t count = 0u; + if (st.type() == fs::directory_file) + { + local_ec.clear(); + + fs::directory_iterator itr; + directory_iterator_params params; + params.use_handle = h; + params.close_handle = false; // the caller will close the handle + fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::_detail_no_follow), ¶ms, &local_ec); + if (BOOST_UNLIKELY(!!local_ec)) + { + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec)); + + *ec = local_ec; + return static_cast< uintmax_t >(-1); + } + + NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api); + const fs::directory_iterator end_dit; + while (itr != end_dit) + { + fs::path nested_path(itr->path()); + handle_wrapper hh; + if (BOOST_LIKELY(nt_create_file != NULL)) + { + // Note: WinAPI methods like CreateFileW implicitly request SYNCHRONIZE access but NtCreateFile doesn't. + // Without SYNCHRONIZE access querying file attributes via GetFileInformationByHandleEx fails with ERROR_ACCESS_DENIED. + boost::winapi::NTSTATUS_ status = nt_create_file_handle_at + ( + hh.handle, + h, + nested_path.filename(), + 0u, // FileAttributes + FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT + ); + + if (!NT_SUCCESS(status)) + { + if (status == STATUS_NO_SUCH_FILE || + status == STATUS_OBJECT_NAME_NOT_FOUND || + status == STATUS_OBJECT_PATH_NOT_FOUND || + status == STATUS_BAD_NETWORK_PATH || + status == STATUS_BAD_NETWORK_NAME) + { + goto next_entry; + } + + DWORD err = translate_ntstatus(status); + emit_error(err, nested_path, ec, "boost::filesystem::remove_all"); + return static_cast< uintmax_t >(-1); + } + } + else + { + hh.handle = create_file_handle( + nested_path, + FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); + + if (BOOST_UNLIKELY(hh.handle == INVALID_HANDLE_VALUE)) + { + DWORD err = ::GetLastError(); + if (not_found_error(err)) + goto next_entry; + + emit_error(err, nested_path, ec, "boost::filesystem::remove_all"); + return static_cast< uintmax_t >(-1); + } + } + + count += fs::detail::remove_all_nt6_by_handle(hh.handle, nested_path, ec); + if (ec && *ec) + return static_cast< uintmax_t >(-1); + + next_entry: + fs::detail::directory_iterator_increment(itr, ec); + if (ec && *ec) + return static_cast< uintmax_t >(-1); + } + } + + DWORD err = fs::detail::remove_nt6_by_handle(h, fs::detail::atomic_load_relaxed(g_remove_impl_type)); + if (BOOST_UNLIKELY(err != 0u)) + { + emit_error(err, p, ec, "boost::filesystem::remove_all"); + return static_cast< uintmax_t >(-1); + } + + ++count; + return count; +} + +#endif // !defined(UNDER_CE) + +//! remove_all() implementation for Windows XP and older +uintmax_t remove_all_nt5_impl(path const& p, error_code* ec) { - remove_impl_type impl = filesystem::detail::atomic_load_relaxed(g_remove_impl_type); error_code dit_create_ec; for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt) { @@ -1542,7 +2091,7 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) if (recurse) { fs::directory_iterator itr; - fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::_detail_no_follow), &dit_create_ec); + fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::_detail_no_follow), NULL, &dit_create_ec); if (BOOST_UNLIKELY(!!dit_create_ec)) { if (dit_create_ec == make_error_condition(system::errc::not_a_directory) || @@ -1561,7 +2110,7 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) const fs::directory_iterator end_dit; while (itr != end_dit) { - count += remove_all_impl(itr->path(), ec); + count += fs::detail::remove_all_nt5_impl(itr->path(), ec); if (ec && *ec) return static_cast< uintmax_t >(-1); @@ -1571,11 +2120,7 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) } } - bool removed; - if (BOOST_LIKELY(impl != remove_nt5)) - removed = remove_nt6_impl(p, impl, ec); - else - removed = remove_nt5_impl(p, attrs, ec); + bool removed = fs::detail::remove_nt5_impl(p, attrs, ec); if (ec && *ec) return static_cast< uintmax_t >(-1); @@ -1590,6 +2135,38 @@ uintmax_t remove_all_impl(path const& p, error_code* ec) return static_cast< uintmax_t >(-1); } +//! remove_all() implementation +inline uintmax_t remove_all_impl(path const& p, error_code* ec) +{ +#if !defined(UNDER_CE) + remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type); + if (BOOST_LIKELY(impl != remove_nt5)) + { + handle_wrapper h(create_file_handle( + p, + FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + 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)) + { + DWORD err = ::GetLastError(); + if (not_found_error(err)) + return 0u; + + emit_error(err, p, ec, "boost::filesystem::remove_all"); + return static_cast< uintmax_t >(-1); + } + + return fs::detail::remove_all_nt6_by_handle(h.handle, p, ec); + } +#endif // !defined(UNDER_CE) + + return fs::detail::remove_all_nt5_impl(p, ec); +} + inline BOOL resize_file_impl(const wchar_t* p, uintmax_t size) { handle_wrapper h(CreateFileW(p, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); @@ -1836,7 +2413,7 @@ path canonical(path const& p, path const& base, system::error_code* ec) } system::error_code local_ec; - file_status st(detail::status(source, &local_ec)); + file_status st(detail::status_impl(source, &local_ec)); if (st.type() == fs::file_not_found) { @@ -1890,7 +2467,7 @@ path canonical(path const& p, path const& base, system::error_code* ec) if (!result.is_absolute()) continue; - st = detail::symlink_status(result, ec); + st = detail::symlink_status_impl(result, ec); if (ec && *ec) goto return_empty_path; @@ -1964,16 +2541,19 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ ((options & static_cast< unsigned int >(copy_options::create_symlinks)) != 0u) + ((options & static_cast< unsigned int >(copy_options::create_hard_links)) != 0u)) <= 1); + if (ec) + ec->clear(); + file_status from_stat; if ((options & (static_cast< unsigned int >(copy_options::copy_symlinks) | static_cast< unsigned int >(copy_options::skip_symlinks) | static_cast< unsigned int >(copy_options::create_symlinks))) != 0u) { - from_stat = detail::symlink_status(from, ec); + from_stat = detail::symlink_status_impl(from, ec); } else { - from_stat = detail::status(from, ec); + from_stat = detail::status_impl(from, ec); } if (ec && *ec) @@ -2044,11 +2624,11 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ if ((options & (static_cast< unsigned int >(copy_options::skip_symlinks) | static_cast< unsigned int >(copy_options::create_symlinks))) != 0u) { - to_stat = detail::symlink_status(to, &local_ec); + to_stat = detail::symlink_status_impl(to, &local_ec); } else { - to_stat = detail::status(to, &local_ec); + to_stat = detail::status_impl(to, &local_ec); } // Note: local_ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist. @@ -2082,11 +2662,11 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ if ((options & (static_cast< unsigned int >(copy_options::skip_symlinks) | static_cast< unsigned int >(copy_options::create_symlinks))) != 0u) { - to_stat = detail::symlink_status(to, &local_ec); + to_stat = detail::symlink_status_impl(to, &local_ec); } else { - to_stat = detail::status(to, &local_ec); + to_stat = detail::status_impl(to, &local_ec); } // Note: ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist. @@ -2109,7 +2689,7 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ if ((options & static_cast< unsigned int >(copy_options::recursive)) != 0u || options == 0u) { fs::directory_iterator itr; - detail::directory_iterator_construct(itr, from, static_cast< unsigned int >(directory_options::none), ec); + detail::directory_iterator_construct(itr, from, static_cast< unsigned int >(directory_options::none), NULL, ec); if (ec && *ec) return; @@ -2205,7 +2785,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod } mode_t to_mode = from_mode; -#if !defined(__wasm) +#if !defined(BOOST_FILESYSTEM_USE_WASI) // Enable writing for the newly created files. Having write permission set is important e.g. for NFS, // which checks the file permission on the server, even if the client's file descriptor supports writing. to_mode |= S_IWUSR; @@ -2324,7 +2904,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod if (BOOST_UNLIKELY(err != 0)) goto fail; // err already contains the error code -#if !defined(__wasm) +#if !defined(BOOST_FILESYSTEM_USE_WASI) // If we created a new file with an explicitly added S_IWUSR permission, // we may need to update its mode bits to match the source file. if (to_mode != from_mode) @@ -2373,7 +2953,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(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); + hw_from.handle = create_file_handle(from.c_str(), FILE_READ_ATTRIBUTES, 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) @@ -2384,15 +2964,15 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod return false; } - if (!::GetFileTime(hw_from.handle, 0, 0, &lwt_from)) + if (!::GetFileTime(hw_from.handle, NULL, NULL, &lwt_from)) goto fail_last_error; - 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); + hw_to.handle = create_file_handle(to.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); if (hw_to.handle != INVALID_HANDLE_VALUE) { FILETIME lwt_to; - if (!::GetFileTime(hw_to.handle, 0, 0, &lwt_to)) + if (!::GetFileTime(hw_to.handle, NULL, NULL, &lwt_to)) goto fail_last_error; ULONGLONG tfrom = (static_cast< ULONGLONG >(lwt_from.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_from.dwLowDateTime); @@ -2512,7 +3092,7 @@ bool create_directories(path const& p, system::error_code* ec) { if (!fname.empty() && fname != dot_p && fname != dot_dot_p) { - file_status existing_status = detail::status(parent, &local_ec); + file_status existing_status = detail::status_impl(parent, &local_ec); if (existing_status.type() == directory_file) { @@ -2760,7 +3340,7 @@ void create_symlink(path const& to, path const& from, error_code* ec) BOOST_FILESYSTEM_DECL path current_path(error_code* ec) { -#if defined(UNDER_CE) || defined(__wasm) +#if defined(UNDER_CE) || defined(BOOST_FILESYSTEM_USE_WASI) // Windows CE has no current directory, so everything's relative to the root of the directory tree. // WASI also does not support current path. emit_error(BOOST_ERROR_NOT_SUPPORTED, ec, "boost::filesystem::current_path"); @@ -2830,7 +3410,7 @@ path current_path(error_code* ec) BOOST_FILESYSTEM_DECL void current_path(path const& p, system::error_code* ec) { -#if defined(UNDER_CE) || defined(__wasm) +#if defined(UNDER_CE) || defined(BOOST_FILESYSTEM_USE_WASI) emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::current_path"); #else error(!BOOST_SET_CURRENT_DIRECTORY(p.c_str()) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::current_path"); @@ -2894,23 +3474,21 @@ bool equivalent(path const& p1, path const& p2, system::error_code* ec) // been retrieved. // p2 is done first, so any error reported is for p1 - handle_wrapper h2( - create_file_handle( - p2.c_str(), - 0u, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS)); - - handle_wrapper h1( - create_file_handle( - p1.c_str(), - 0u, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS)); + handle_wrapper h2(create_file_handle( + p2.c_str(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS)); + + handle_wrapper h1(create_file_handle( + p1.c_str(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS)); if (BOOST_UNLIKELY(h1.handle == INVALID_HANDLE_VALUE || h2.handle == INVALID_HANDLE_VALUE)) { @@ -3044,8 +3622,13 @@ 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(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS)); + handle_wrapper h(create_file_handle( + p.c_str(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS)); if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) { @@ -3166,8 +3749,13 @@ 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(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS)); + handle_wrapper hw(create_file_handle( + p.c_str(), + FILE_READ_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)) { @@ -3219,8 +3807,13 @@ 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(), 0u, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS)); + handle_wrapper hw(create_file_handle( + p.c_str(), + FILE_READ_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)) { @@ -3247,7 +3840,7 @@ void last_write_time(path const& p, const std::time_t new_time, system::error_co #if defined(BOOST_POSIX_API) -#if _POSIX_C_SOURCE >= 200809L +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) struct timespec times[2] = {}; @@ -3262,7 +3855,7 @@ void last_write_time(path const& p, const std::time_t new_time, system::error_co return; } -#else // _POSIX_C_SOURCE >= 200809L +#else // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) struct ::stat st; if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0)) @@ -3272,17 +3865,22 @@ void last_write_time(path const& p, const std::time_t new_time, system::error_co } ::utimbuf buf; - buf.actime = st.st_atime; // utime()updates access time too:-( + buf.actime = st.st_atime; // utime() updates access time too :-( buf.modtime = new_time; if (BOOST_UNLIKELY(::utime(p.c_str(), &buf) < 0)) emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time"); -#endif // _POSIX_C_SOURCE >= 200809L +#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) #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, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS)); + handle_wrapper hw(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)) { @@ -3294,7 +3892,7 @@ void last_write_time(path const& p, const std::time_t new_time, system::error_co FILETIME lwt; to_FILETIME(new_time, lwt); - if (BOOST_UNLIKELY(!::SetFileTime(hw.handle, 0, 0, &lwt))) + if (BOOST_UNLIKELY(!::SetFileTime(hw.handle, NULL, NULL, &lwt))) goto fail; #endif // defined(BOOST_POSIX_API) @@ -3316,11 +3914,11 @@ void permissions(path const& p, perms prms, system::error_code* ec) if ((prms & add_perms) && (prms & remove_perms)) // precondition failed return; -#if defined(__wasm) +#if defined(BOOST_FILESYSTEM_USE_WASI) emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::permissions"); #elif defined(BOOST_POSIX_API) error_code local_ec; - file_status current_status((prms & symlink_perms) ? fs::symlink_status(p, local_ec) : fs::status(p, local_ec)); + file_status current_status((prms & symlink_perms) ? detail::symlink_status_impl(p, &local_ec) : detail::status_impl(p, &local_ec)); if (local_ec) { if (!ec) @@ -3346,11 +3944,12 @@ void permissions(path const& p, perms prms, system::error_code* ec) // "http://man7.org/linux/man-pages/man7/symlink.7.html" // - See the fchmodat() Linux man page: // "http://man7.org/linux/man-pages/man2/fchmodat.2.html" -#if defined(AT_FDCWD) && defined(AT_SYMLINK_NOFOLLOW) && \ +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) && \ !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) && \ !(defined(linux) || defined(__linux) || defined(__linux__)) && \ !(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000) && \ !(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000) && \ + !(defined(__rtems__)) && \ !(defined(__QNX__) && (_NTO_VERSION <= 700)) if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms), !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW)) #else // fallback if fchmodat() not supported @@ -3445,8 +4044,13 @@ path read_symlink(path const& p, system::error_code* ec) #else - handle_wrapper h( - 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)); + handle_wrapper h(create_file_handle( + p.c_str(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); DWORD error; if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) @@ -3460,7 +4064,7 @@ path read_symlink(path const& p, system::error_code* ec) 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))) + if (BOOST_UNLIKELY(!::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(), sizeof(*buf), &sz, NULL))) { error = ::GetLastError(); goto return_error; @@ -3575,7 +4179,7 @@ space_info space(path const& p, error_code* ec) if (ec) ec->clear(); -#if defined(__wasm) +#if defined(BOOST_FILESYSTEM_USE_WASI) emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::space"); @@ -3594,7 +4198,7 @@ space_info space(path const& p, error_code* ec) // GetDiskFreeSpaceExW requires a directory path, which is unlike statvfs, which accepts any file. // To work around this, test if the path refers to a directory and use the parent directory if not. error_code local_ec; - file_status status = detail::status(p, &local_ec); + file_status status = detail::status_impl(p, &local_ec); if (status.type() == fs::status_error || status.type() == fs::file_not_found) { fail_local_ec: @@ -3611,7 +4215,7 @@ space_info space(path const& p, error_code* ec) if (ec && *ec) return info; - status = detail::symlink_status(p, &local_ec); + status = detail::symlink_status_impl(p, &local_ec); if (status.type() == fs::status_error) goto fail_local_ec; if (is_symlink(status)) @@ -3654,106 +4258,7 @@ file_status status(path const& p, error_code* ec) if (ec) ec->clear(); -#if defined(BOOST_POSIX_API) - -#if defined(BOOST_FILESYSTEM_USE_STATX) - struct ::statx path_stat; - int err = invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat); -#else - struct ::stat path_stat; - int err = ::stat(p.c_str(), &path_stat); -#endif - - if (err != 0) - { - err = errno; - if (ec) // always report errno, even though some - ec->assign(err, system_category()); // errno values are not status_errors - - if (not_found_error(err)) - return fs::file_status(fs::file_not_found, fs::no_perms); - - if (!ec) - BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, error_code(err, system_category()))); - - return fs::file_status(fs::status_error); - } - -#if defined(BOOST_FILESYSTEM_USE_STATX) - if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE))) - { - emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::status"); - return fs::file_status(fs::status_error); - } -#endif - - const mode_t mode = get_mode(path_stat); - if (S_ISDIR(mode)) - return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISREG(mode)) - return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISBLK(mode)) - return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISCHR(mode)) - return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISFIFO(mode)) - return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISSOCK(mode)) - return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask); - return fs::file_status(fs::type_unknown); - -#else // defined(BOOST_POSIX_API) - - // We should first test if the file is a symlink or a reparse point. Resolving some reparse - // points by opening the file may fail, and status() should return file_status(reparse_file) in this case. - // Which is what symlink_status() returns. - fs::file_status st(detail::symlink_status(p, ec)); - if (st.type() == symlink_file) - { - // Resolve the symlink - handle_wrapper h( - create_file_handle( - p.c_str(), - 0u, // dwDesiredAccess; attributes only - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // lpSecurityAttributes - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS)); - - if (h.handle == INVALID_HANDLE_VALUE) - { - return_status_failure: - return process_status_failure(p, ec); - } - - DWORD attrs; - 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_status_failure; - - attrs = info.FileAttributes; - } - else - { - BY_HANDLE_FILE_INFORMATION info; - BOOL res = ::GetFileInformationByHandle(h.handle, &info); - if (BOOST_UNLIKELY(!res)) - goto return_status_failure; - - attrs = info.dwFileAttributes; - } - - st.permissions(make_permissions(p, attrs)); - st.type((attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file); - } - - return st; - -#endif // defined(BOOST_POSIX_API) + return detail::status_impl(p, ec); } BOOST_FILESYSTEM_DECL @@ -3762,106 +4267,7 @@ file_status symlink_status(path const& p, error_code* ec) if (ec) ec->clear(); -#if defined(BOOST_POSIX_API) - -#if defined(BOOST_FILESYSTEM_USE_STATX) - struct ::statx path_stat; - int err = invoke_statx(AT_FDCWD, p.c_str(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat); -#else - struct ::stat path_stat; - int err = ::lstat(p.c_str(), &path_stat); -#endif - - if (err != 0) - { - err = errno; - if (ec) // always report errno, even though some - ec->assign(err, system_category()); // errno values are not status_errors - - if (not_found_error(err)) // these are not errors - return fs::file_status(fs::file_not_found, fs::no_perms); - - if (!ec) - BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::symlink_status", p, error_code(err, system_category()))); - - return fs::file_status(fs::status_error); - } - -#if defined(BOOST_FILESYSTEM_USE_STATX) - if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE))) - { - emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::symlink_status"); - return fs::file_status(fs::status_error); - } -#endif - - const mode_t mode = get_mode(path_stat); - if (S_ISREG(mode)) - return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISDIR(mode)) - return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISLNK(mode)) - return fs::file_status(fs::symlink_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISBLK(mode)) - return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISCHR(mode)) - return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISFIFO(mode)) - return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask); - if (S_ISSOCK(mode)) - return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask); - return fs::file_status(fs::type_unknown); - -#else // defined(BOOST_POSIX_API) - - handle_wrapper h( - create_file_handle( - p.c_str(), - 0u, // dwDesiredAccess; attributes only - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, // lpSecurityAttributes - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); - - if (h.handle == INVALID_HANDLE_VALUE) - { - return_status_failure: - return process_status_failure(p, ec); - } - - DWORD attrs; - fs::perms permissions; - 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_status_failure; - - attrs = info.FileAttributes; - permissions = make_permissions(p, attrs); - - if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) - return is_reparse_point_tag_a_symlink(info.ReparseTag) ? fs::file_status(fs::symlink_file, permissions) : fs::file_status(fs::reparse_file, permissions); - } - else - { - BY_HANDLE_FILE_INFORMATION info; - BOOL res = ::GetFileInformationByHandle(h.handle, &info); - if (BOOST_UNLIKELY(!res)) - goto return_status_failure; - - attrs = info.dwFileAttributes; - permissions = make_permissions(p, attrs); - - if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) - return is_reparse_point_a_symlink_ioctl(h.handle) ? fs::file_status(fs::symlink_file, permissions) : fs::file_status(fs::reparse_file, permissions); - } - - return (attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::file_status(fs::directory_file, permissions) : fs::file_status(fs::regular_file, permissions); - -#endif // defined(BOOST_POSIX_API) + return detail::symlink_status_impl(p, ec); } // contributed by Jeff Flinn @@ -3893,7 +4299,7 @@ path temp_directory_path(system::error_code* ec) return p; } - file_status status = detail::status(p, ec); + file_status status = detail::status_impl(p, ec); if (BOOST_UNLIKELY(ec && *ec)) return path(); if (BOOST_UNLIKELY(!is_directory(status))) @@ -3969,7 +4375,7 @@ path temp_directory_path(system::error_code* ec) path p(buf.get()); p.remove_trailing_separator(); - file_status status = detail::status(p, ec); + file_status status = detail::status_impl(p, ec); if (ec && *ec) return path(); if (!is_directory(status)) @@ -4028,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(head, &local_ec); + file_status head_status = detail::status_impl(head, &local_ec); if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) { if (!ec) @@ -4069,7 +4475,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) head /= p_elem; } - file_status head_status = detail::status(head, &local_ec); + file_status head_status = detail::status_impl(head, &local_ec); if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) { if (!ec) @@ -4138,3 +4544,5 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) } // namespace detail } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/path.cpp b/contrib/restricted/boost/filesystem/src/path.cpp index f6cb3ba9a16..27b32e57c52 100644 --- a/contrib/restricted/boost/filesystem/src/path.cpp +++ b/contrib/restricted/boost/filesystem/src/path.cpp @@ -40,6 +40,8 @@ #include "atomic_tools.hpp" #include "private_config.hpp" +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace fs = boost::filesystem; using boost::filesystem::path; @@ -1678,3 +1680,5 @@ BOOST_FILESYSTEM_DECL path const& dot_dot_path() } // namespace detail } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/path_traits.cpp b/contrib/restricted/boost/filesystem/src/path_traits.cpp index d882ad89a08..aa4b8815f7b 100644 --- a/contrib/restricted/boost/filesystem/src/path_traits.cpp +++ b/contrib/restricted/boost/filesystem/src/path_traits.cpp @@ -22,6 +22,8 @@ #include <cwchar> // for wcslen #include <cstddef> +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace pt = boost::filesystem::path_traits; namespace fs = boost::filesystem; namespace bs = boost::system; @@ -195,3 +197,5 @@ void convert(const wchar_t* from, } // namespace path_traits } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/portability.cpp b/contrib/restricted/boost/filesystem/src/portability.cpp index 43e63b074ec..8e0e2e5d98d 100644 --- a/contrib/restricted/boost/filesystem/src/portability.cpp +++ b/contrib/restricted/boost/filesystem/src/portability.cpp @@ -17,6 +17,8 @@ #include <cstring> // SGI MIPSpro compilers need this #include <string> +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace boost { namespace filesystem { @@ -77,3 +79,5 @@ BOOST_FILESYSTEM_DECL bool portable_file_name(std::string const& name) } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/posix_tools.hpp b/contrib/restricted/boost/filesystem/src/posix_tools.hpp index 52426d4a36a..52cd87b0a02 100644 --- a/contrib/restricted/boost/filesystem/src/posix_tools.hpp +++ b/contrib/restricted/boost/filesystem/src/posix_tools.hpp @@ -19,10 +19,25 @@ #include <unistd.h> #endif +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace boost { namespace filesystem { namespace detail { +//! Platform-specific parameters for directory iterator construction +struct directory_iterator_params +{ +#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) + //! File descriptor of the base directory relative to which to interpret relative paths + int basedir_fd; +#endif +#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) + //! File descriptor of the directory over which the iterator iterates + int iterator_fd; +#endif +}; + /*! * Closes a file descriptor and returns the result, similar to close(2). Unlike close(2), guarantees that the file descriptor is closed even if EINTR error happens. * @@ -61,4 +76,6 @@ inline int close_fd(int fd) } // namespace filesystem } // namespace boost +#include <boost/filesystem/detail/footer.hpp> + #endif // BOOST_FILESYSTEM_SRC_POSIX_TOOLS_HPP_ diff --git a/contrib/restricted/boost/filesystem/src/unique_path.cpp b/contrib/restricted/boost/filesystem/src/unique_path.cpp index 4815ca43698..f0bc6058cb2 100644 --- a/contrib/restricted/boost/filesystem/src/unique_path.cpp +++ b/contrib/restricted/boost/filesystem/src/unique_path.cpp @@ -95,6 +95,8 @@ #include "atomic_tools.hpp" #include "error_handling.hpp" +#include <boost/filesystem/detail/header.hpp> // must be the last #include + #if defined(BOOST_POSIX_API) // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC #ifndef O_CLOEXEC @@ -325,3 +327,5 @@ path unique_path(path const& model, system::error_code* ec) } // namespace detail } // namespace filesystem } // namespace boost + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp b/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp index f7f9f7ef583..7e5939bb718 100644 --- a/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp +++ b/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp @@ -7,6 +7,8 @@ #include <boost/filesystem/config.hpp> +#include <boost/filesystem/detail/header.hpp> + #define BOOST_UTF8_BEGIN_NAMESPACE \ namespace boost { \ namespace filesystem { \ @@ -23,3 +25,5 @@ #undef BOOST_UTF8_BEGIN_NAMESPACE #undef BOOST_UTF8_END_NAMESPACE #undef BOOST_UTF8_DECL + +#include <boost/filesystem/detail/footer.hpp> diff --git a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp index 56d1d1d7b3b..f86ba5111b9 100644 --- a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp +++ b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp @@ -19,6 +19,8 @@ #include <windows.h> +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace boost { namespace filesystem { namespace detail { @@ -65,4 +67,6 @@ std::codecvt_base::result windows_file_codecvt::do_out( } // namespace filesystem } // namespace boost +#include <boost/filesystem/detail/footer.hpp> + #endif // BOOST_WINDOWS_API diff --git a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp index fbcf670b114..917dc2182fe 100644 --- a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp +++ b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp @@ -19,6 +19,8 @@ #include <cwchar> // std::mbstate_t #include <locale> +#include <boost/filesystem/detail/header.hpp> // must be the last #include + namespace boost { namespace filesystem { namespace detail { @@ -63,6 +65,8 @@ protected: } // namespace filesystem } // namespace boost +#include <boost/filesystem/detail/footer.hpp> + #endif // BOOST_WINDOWS_API #endif // BOOST_FILESYSTEM_WINDOWS_FILE_CODECVT_HPP diff --git a/contrib/restricted/boost/filesystem/src/windows_tools.hpp b/contrib/restricted/boost/filesystem/src/windows_tools.hpp index 469be041ec2..8a2de7f0c9f 100644 --- a/contrib/restricted/boost/filesystem/src/windows_tools.hpp +++ b/contrib/restricted/boost/filesystem/src/windows_tools.hpp @@ -21,6 +21,8 @@ #include <windows.h> +#include <boost/filesystem/detail/header.hpp> // must be the last #include + #ifndef IO_REPARSE_TAG_MOUNT_POINT #define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) #endif @@ -83,6 +85,18 @@ inline bool is_reparse_point_tag_a_symlink(ULONG reparse_point_tag) #if !defined(UNDER_CE) +//! Platform-specific parameters for directory iterator construction +struct directory_iterator_params +{ + //! Handle of the directory to iterate over. If not \c INVALID_HANDLE_VALUE, the directory path is ignored. + HANDLE use_handle; + /*! + * If \c use_handle is not \c INVALID_HANDLE_VALUE, specifies whether the directory iterator should close the handle upon destruction. + * If \c false, the handle must remain valid for the lifetime of the iterator. + */ + bool close_handle; +}; + //! IO_STATUS_BLOCK definition from Windows SDK. struct io_status_block { @@ -102,6 +116,69 @@ struct unicode_string PWSTR Buffer; }; +//! OBJECT_ATTRIBUTES definition from Windows SDK +struct object_attributes +{ + ULONG Length; + HANDLE RootDirectory; + unicode_string* ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +}; + +#ifndef OBJ_CASE_INSENSITIVE +#define OBJ_CASE_INSENSITIVE 0x00000040 +#endif +#ifndef OBJ_DONT_REPARSE +#define OBJ_DONT_REPARSE 0x00001000 +#endif + +#ifndef FILE_SUPERSEDE +#define FILE_SUPERSEDE 0x00000000 +#endif +#ifndef FILE_OPEN +#define FILE_OPEN 0x00000001 +#endif +#ifndef FILE_CREATE +#define FILE_CREATE 0x00000002 +#endif +#ifndef FILE_OPEN_IF +#define FILE_OPEN_IF 0x00000003 +#endif +#ifndef FILE_OVERWRITE +#define FILE_OVERWRITE 0x00000004 +#endif +#ifndef FILE_OVERWRITE_IF +#define FILE_OVERWRITE_IF 0x00000005 +#endif + +#ifndef FILE_DIRECTORY_FILE +#define FILE_DIRECTORY_FILE 0x00000001 +#endif +#ifndef FILE_OPEN_FOR_BACKUP_INTENT +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#endif +#ifndef FILE_OPEN_REPARSE_POINT +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#endif + +//! NtCreateFile signature. Available since Windows 2000 (probably). +typedef boost::winapi::NTSTATUS_ (NTAPI NtCreateFile_t)( + /*out*/ PHANDLE FileHandle, + /*in*/ ACCESS_MASK DesiredAccess, + /*in*/ object_attributes* ObjectAttributes, + /*out*/ io_status_block* IoStatusBlock, + /*in, optional*/ PLARGE_INTEGER AllocationSize, + /*in*/ ULONG FileAttributes, + /*in*/ ULONG ShareAccess, + /*in*/ ULONG CreateDisposition, + /*in*/ ULONG CreateOptions, + /*in, optional*/ PVOID EaBuffer, + /*in*/ ULONG EaLength); + +extern NtCreateFile_t* nt_create_file_api; + //! PIO_APC_ROUTINE definition from Windows SDK typedef VOID (NTAPI* pio_apc_routine) (PVOID ApcContext, io_status_block* IoStatusBlock, ULONG Reserved); @@ -113,17 +190,17 @@ enum file_information_class //! 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); + /*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; @@ -182,8 +259,15 @@ inline HANDLE create_file_handle(boost::filesystem::path const& p, DWORD dwDesir return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); } +#if !defined(UNDER_CE) +//! Creates a file handle for a file relative to a previously opened base directory. The file path must be relative and in preferred format. +boost::winapi::NTSTATUS_ nt_create_file_handle_at(HANDLE& out, HANDLE basedir_handle, boost::filesystem::path const& p, ULONG FileAttributes, ACCESS_MASK DesiredAccess, ULONG ShareMode, ULONG CreateDisposition, ULONG CreateOptions); +#endif // !defined(UNDER_CE) + } // namespace detail } // namespace filesystem } // namespace boost +#include <boost/filesystem/detail/footer.hpp> + #endif // BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_ |