diff options
author | AlexSm <alex@ydb.tech> | 2023-12-27 23:31:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 23:31:58 +0100 |
commit | d67bfb4b4b7549081543e87a31bc6cb5c46ac973 (patch) | |
tree | 8674f2f1570877cb653e7ddcff37ba00288de15a /contrib/restricted/boost/filesystem/src/directory.cpp | |
parent | 1f6bef05ed441c3aa2d565ac792b26cded704ac7 (diff) | |
download | ydb-d67bfb4b4b7549081543e87a31bc6cb5c46ac973.tar.gz |
Import libs 4 (#758)
Diffstat (limited to 'contrib/restricted/boost/filesystem/src/directory.cpp')
-rw-r--r-- | contrib/restricted/boost/filesystem/src/directory.cpp | 209 |
1 files changed, 112 insertions, 97 deletions
diff --git a/contrib/restricted/boost/filesystem/src/directory.cpp b/contrib/restricted/boost/filesystem/src/directory.cpp index 6a3e1dc731..f769f8fc18 100644 --- a/contrib/restricted/boost/filesystem/src/directory.cpp +++ b/contrib/restricted/boost/filesystem/src/directory.cpp @@ -74,15 +74,6 @@ #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. -// -// TODO: find out what macros indicate dirent::d_type present in more libraries -#if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present -#define BOOST_FILESYSTEM_STATUS_CACHE -#endif - namespace fs = boost::filesystem; using boost::system::error_code; using boost::system::system_category; @@ -96,76 +87,34 @@ namespace filesystem { // // //--------------------------------------------------------------------------------------// -BOOST_FILESYSTEM_DECL -file_status directory_entry::get_status(system::error_code* ec) const +BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const { - if (!status_known(m_status)) + system::error_code local_ec; + m_symlink_status = detail::symlink_status(m_path, &local_ec); + + if (!filesystem::is_symlink(m_symlink_status)) { - // optimization: if the symlink status is known, and it isn't a symlink, - // then status and symlink_status are identical so just copy the - // symlink status to the regular status. - if (status_known(m_symlink_status) && !is_symlink(m_symlink_status)) - { - m_status = m_symlink_status; - if (ec) - ec->clear(); - } - else + // Also works if symlink_status fails - set m_status to status_error as well + m_status = m_symlink_status; + + if (BOOST_UNLIKELY(!!local_ec)) { - m_status = detail::status(m_path, ec); + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_entry::refresh", m_path, local_ec)); + + *ec = local_ec; + return; } + + if (ec) + ec->clear(); } - else if (ec) + else { - ec->clear(); + m_status = detail::status(m_path, ec); } - - return m_status; } -BOOST_FILESYSTEM_DECL -file_status directory_entry::get_symlink_status(system::error_code* ec) const -{ - if (!status_known(m_symlink_status)) - m_symlink_status = detail::symlink_status(m_path, ec); - else if (ec) - ec->clear(); - - return m_symlink_status; -} - -// dispatch directory_entry supplied here rather than in -// <boost/filesystem/path_traits.hpp>, thus avoiding header circularity. -// test cases are in operations_unit_test.cpp - -namespace path_traits { - -BOOST_FILESYSTEM_DECL -void dispatch(directory_entry const& de, -#ifdef BOOST_WINDOWS_API - std::wstring& to, -#else - std::string& to, -#endif - codecvt_type const&) -{ - to = de.path().native(); -} - -BOOST_FILESYSTEM_DECL -void dispatch(directory_entry const& de, -#ifdef BOOST_WINDOWS_API - std::wstring& to -#else - std::string& to -#endif -) -{ - to = de.path().native(); -} - -} // namespace path_traits - //--------------------------------------------------------------------------------------// // // // directory_iterator // @@ -353,24 +302,43 @@ error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_stat filename = result->d_name; -#ifdef BOOST_FILESYSTEM_STATUS_CACHE +#if defined(BOOST_FILESYSTEM_HAS_DIRENT_D_TYPE) if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value { sf = symlink_sf = fs::file_status(fs::status_error); } else // filesystem supplies d_type value { - if (result->d_type == DT_DIR) - sf = symlink_sf = fs::file_status(fs::directory_file); - else if (result->d_type == DT_REG) + if (result->d_type == DT_REG) sf = symlink_sf = fs::file_status(fs::regular_file); + else if (result->d_type == DT_DIR) + sf = symlink_sf = fs::file_status(fs::directory_file); else if (result->d_type == DT_LNK) { sf = fs::file_status(fs::status_error); symlink_sf = fs::file_status(fs::symlink_file); } else - sf = symlink_sf = fs::file_status(fs::status_error); + { + switch (result->d_type) + { + case DT_SOCK: + sf = symlink_sf = fs::file_status(fs::socket_file); + break; + case DT_FIFO: + sf = symlink_sf = fs::file_status(fs::fifo_file); + break; + case DT_BLK: + sf = symlink_sf = fs::file_status(fs::block_file); + break; + case DT_CHR: + sf = symlink_sf = fs::file_status(fs::character_file); + break; + default: + sf = symlink_sf = fs::file_status(fs::status_error); + break; + } + } } #else sf = symlink_sf = fs::file_status(fs::status_error); @@ -599,9 +567,12 @@ 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. + * NTFS, VFAT, exFAT and ReFS support filenames up to 255 UTF-16/UCS-2 characters. (For ReFS, there is no information + * on the on-disk format, and it is possible that it supports longer filenames, up to 32768 UTF-16/UCS-2 characters.) + * The buffer cannot be larger than 64k, because up to Windows 8.1, NtQueryDirectoryFile and GetFileInformationByHandleEx + * fail with ERROR_INVALID_PARAMETER when trying to retrieve the filenames from a network share. */ -BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u; +BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = 65536u; inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT { @@ -763,6 +734,28 @@ done: return error_code(); } +//! Returns \c true if the error code indicates that the OS or the filesystem does not support a particular directory info class +inline bool is_dir_info_class_not_supported(DWORD error) +{ + // Some mounted filesystems may not support FILE_ID_128 identifiers, which will cause + // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER, + // even though in general the operation is supported by the kernel. SMBv1 returns a special error + // code ERROR_INVALID_LEVEL in this case. + // Some other filesystems also don't implement other info classes and return ERROR_INVALID_PARAMETER + // (e.g. see https://github.com/boostorg/filesystem/issues/266), ERROR_GEN_FAILURE, ERROR_INVALID_FUNCTION + // or ERROR_INTERNAL_ERROR (https://github.com/boostorg/filesystem/issues/286). Treat these error codes + // as "non-permanent", even though ERROR_INVALID_PARAMETER is also returned if GetFileInformationByHandleEx + // in general does not support a certain info class. Worst case, we will make extra syscalls on directory + // iterator construction. + // Also note that Wine returns ERROR_CALL_NOT_IMPLEMENTED for unimplemented info classes, and + // up until 7.21 it didn't implement FileIdExtdDirectoryRestartInfo and FileFullDirectoryRestartInfo. + // (https://bugs.winehq.org/show_bug.cgi?id=53590) + return error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER || + error == ERROR_INVALID_LEVEL || error == ERROR_CALL_NOT_IMPLEMENTED || + error == ERROR_GEN_FAILURE || error == ERROR_INVALID_FUNCTION || + error == ERROR_INTERNAL_ERROR; +} + 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()); @@ -828,9 +821,14 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: 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 ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) { - if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_a_symlink_ioctl(h.handle)) + error_code ec; + const ULONG reparse_point_tag = detail::get_reparse_point_tag_ioctl(h.handle, dir, &ec); + if (BOOST_UNLIKELY(!!ec)) + return ec; + + if (detail::is_reparse_point_tag_a_symlink(reparse_point_tag)) return make_error_code(system::errc::too_many_symbolic_link_levels); } } @@ -845,14 +843,10 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: { DWORD error = ::GetLastError(); - if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) + if (is_dir_info_class_not_supported(error)) { // Fall back to file_full_dir_info_format. - // Note that some mounted filesystems may not support FILE_ID_128 identifiers, which will cause - // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER, - // even though in general the operation is supported by the kernel. So don't downgrade to - // FileFullDirectoryRestartInfo permanently in this case - only for this particular iterator. - if (error == ERROR_NOT_SUPPORTED) + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format); goto fallback_to_file_full_dir_info_format; } @@ -879,11 +873,12 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: { DWORD error = ::GetLastError(); - if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) + if (is_dir_info_class_not_supported(error)) { // Fall back to file_id_both_dir_info - filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format); - goto fallback_to_file_id_both_dir_info; + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) + filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format); + goto fallback_to_file_id_both_dir_info_format; } if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) @@ -902,12 +897,20 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: break; case file_id_both_dir_info_format: - fallback_to_file_id_both_dir_info: + fallback_to_file_id_both_dir_info_format: { 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(); + if (is_dir_info_class_not_supported(error)) + { + // Fall back to file_directory_information + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) + filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_directory_information_format); + goto fallback_to_file_directory_information_format; + } + if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) goto done; @@ -924,6 +927,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: break; default: + fallback_to_file_directory_information_format: { NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api); if (BOOST_UNLIKELY(!nt_query_directory_file)) @@ -1120,7 +1124,18 @@ void directory_iterator_construct(directory_iterator& it, path const& p, unsigne && (filename_str[1] == static_cast< path::string_type::value_type >('\0') || (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))) { - imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat); + path full_path(p); + path_algorithms::append_v4(full_path, filename); + imp->dir_entry.assign_with_status + ( +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + static_cast< path&& >(full_path), +#else + full_path, +#endif + file_stat, + symlink_file_stat + ); it.m_imp.swap(imp); return; } @@ -1180,7 +1195,7 @@ void directory_iterator_increment(directory_iterator& it, system::error_code* ec && (filename_str[1] == static_cast< path::string_type::value_type >('\0') || (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))) { - it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat); + it.m_imp->dir_entry.replace_filename_with_status(filename, file_stat, symlink_file_stat); return; } } @@ -1344,14 +1359,14 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: return result; } - file_status symlink_stat; + file_type symlink_ft = status_error; // 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) { - symlink_stat = imp->m_stack.back()->symlink_status(ec); + symlink_ft = imp->m_stack.back()->symlink_file_type(ec); if (ec) return result; } @@ -1364,12 +1379,12 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: // The predicate code has since been rewritten to pass error_code arguments, // per ticket #5653. - if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat)) + if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || symlink_ft != symlink_file) { - file_status stat = imp->m_stack.back()->status(ec); + file_type ft = imp->m_stack.back()->file_type(ec); if (BOOST_UNLIKELY(!!ec)) { - if (ec == make_error_condition(system::errc::no_such_file_or_directory) && fs::is_symlink(symlink_stat) && + if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file && (imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) { // Skip dangling symlink and continue iteration on the current depth level @@ -1379,7 +1394,7 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: return result; } - if (!fs::is_directory(stat)) + if (ft != directory_file) return result; if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)()))) |