From d508d4950f7b51c84d2819a5b643b9406523027c Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 4 Jun 2023 04:53:44 +0300 Subject: [PATCH] Add dir_entry::refresh and file type observers. Use them in recursive dir_it. This commit changes behavior of directory_entry constructors and modifiers that change the stored path in v4: the methods will now automatically query the filesystem for the file status instead of leaving the cached data default-initialized. This means that the paths passed to directory_entry must be valid, otherwise an error will be returned. Filesystem querying is implemented in the new directory_entry::refresh methods. The constructors and modifiers that accepted file_status arguments are now removed in v4. The cached file statuses are an implementation detail, and eventually we may want to add more cached data, as we add more observers to directory_entry. Also added a few file type observers to directory_entry. These observers allow to avoid querying the filesystem if the full file status is not cached but the file type is (i.e. when permissions are not cached). This is the case with readdir-based implementation of directory_iterator, if the underlying C library supports dirent::d_type field. recursive_directory_iterator has been updated to use the added file type observers instead of querying the full status. This may improve performance of directory iteration. Closes https://github.com/boostorg/filesystem/issues/288. --- doc/reference.html | 175 +++++++++----- doc/release_history.html | 5 + doc/v4.html | 5 +- include/boost/filesystem/directory.hpp | 301 +++++++++++++++++++++---- src/directory.cpp | 64 +++--- test/deprecated_test.cpp | 11 +- test/operations_unit_test.cpp | 45 +++- 7 files changed, 460 insertions(+), 146 deletions(-) diff --git a/doc/reference.html b/doc/reference.html index f971467..9fed19f 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -2543,23 +2543,48 @@ and permissions of a file.

// constructors and destructor directory_entry(); directory_entry(const directory_entry&); - explicit directory_entry(const path& p, file_status st=file_status(), + explicit directory_entry(const path& p); + directory_entry(const path& p, system::error_code& ec); // v4-only + directory_entry(const path& p, file_status st, // v3-only file_status symlink_st=file_status()); ~directory_entry(); // modifiers directory_entry& operator=(const directory_entry&); - void assign(const path& p, file_status st=file_status(), + void assign(const path& p); + void assign(const path& p, system::error_code& ec); // v4-only + void assign(const path& p, file_status st, // v3-only file_status symlink_st=file_status()); - void replace_filename(const path& p, file_status st=file_status(), + void replace_filename(const path& p); + void replace_filename(const path& p, system::error_code& ec); // v4-only + void replace_filename(const path& p, file_status st, // v3-only file_status symlink_st=file_status()); + void refresh(); + void refresh(system::error_code& ec); + // observers const path& path() const; + file_status status() const; file_status status(system::error_code& ec) const; file_status symlink_status() const; file_status symlink_status(system::error_code& ec) const; + file_type file_type() const; + file_type file_type(system::error_code& ec) const; + file_type symlink_file_type() const; + file_type symlink_file_type(system::error_code& ec) const; + + bool exists() const; + bool exists(system::error_code& ec) const; + bool is_regular_file() const; + bool is_regular_file(system::error_code& ec) const; + bool is_directory() const; + bool is_directory(system::error_code& ec) const; + bool is_symlink() const; + bool is_symlink(system::error_code& ec) const; + bool is_other() const; + bool is_other(system::error_code& ec) const; bool operator< (const directory_entry& rhs); bool operator==(const directory_entry& rhs); @@ -2577,10 +2602,12 @@ and permissions of a file.

} // namespace filesystem } // namespace boost -

A directory_entry object stores a path object, -a file_status object for non-symbolic link status, and a file_status object for symbolic link status. The file_status objects act as value caches.

+

A directory_entry object stores a path object, +as well as some amount of cached information about the file identified by the path. +Currently, the cached information includes a file_status object for non-symbolic +link status and a file_status object for symbolic link status.

-

[Note: Because status()on a pathname may be a relatively expensive operation, +

[Note: Because status() on a pathname may be a relatively expensive operation, some operating systems provide status information as a byproduct of directory iteration. Caching such status information can result is significant time savings. Cached and non-cached results may differ in the presence of file system races. —end note]

@@ -2589,8 +2616,14 @@ a directory with 15,047 entries was six seconds for non-cached status queries versus one second for cached status queries. Windows XP, 3.0 GHz processor, with a moderately fast hard-drive. Similar speedups are expected on Linux and BSD-derived systems that provide status as a by-product of directory iteration.

+

[Note: The exact set of cached information may vary from one Boost.Filesystem version +to another, and also between different operating systems and underlying file systems. Users' code +must not rely on whether a certain piece of information is cached or not. This means that calling +most observers and modifiers of directory_entry may or may not result in a filesystem +query that may potentially fail. Information caching is exclusively a performance feature aimed +at reducing the amount of such queries. —end note]

-

directory_entry constructors +

directory_entry constructors [directory_entry.cons]

directory_entry();
@@ -2614,9 +2647,18 @@ systems that provide status as a by-product of directory iteration.
-
explicit directory_entry(const path& p, file_status st=file_status(), file_status symlink_st=file_status());
+
explicit directory_entry(const path& p);
+directory_entry(const path& p, system::error_code& ec); // v4-only
-

Postcondition:

+

Effects:

+

v3: Initializes m_path from p and default-constructs m_status and m_symlink_status.

+

[Note: The cached file statuses will be updated when queried by the caller or by an explicit call to refresh. —end note]

+

v4: Initializes m_path from p and calls refresh() or refresh(ec), respectively.

+

Postcondition: path() == p if no error occurs, otherwise path().empty() == true.

+
+
directory_entry(const path& p, file_status st, file_status symlink_st=file_status()); // v3-only
+
+

v3: Postcondition:

@@ -2636,11 +2678,20 @@ systems that provide status as a by-product of directory iteration.
Expression
-

directory_entry modifiers +

directory_entry modifiers [directory_entry.mods]

-
void assign(const path& p, file_status st=file_status(), file_status symlink_st=file_status());
+
void assign(const path& p);
+void assign(const path& p, system::error_code& ec); // v4-only
-

Postcondition:

+

Effects:

+

v3: Assigns p to m_path and file_status() to m_status and m_symlink_status.

+

[Note: The cached file statuses will be updated when queried by the caller or by an explicit call to refresh. —end note]

+

v4: Assigns p to m_path and calls refresh() or refresh(ec), respectively. If an error + occurs, the value of the cached data is unspecified.

+
+
void assign(const path& p, file_status st, file_status symlink_st=file_status()); // v3-only
+
+

v3: Postcondition:

@@ -2660,27 +2711,25 @@ systems that provide status as a by-product of directory iteration.
Expression
-
void replace_filename(const path& p, file_status st=file_status(), file_status symlink_st=file_status());
+
void replace_filename(const path& p);
+void replace_filename(const path& p, system::error_code& ec); // v4-only
-

Postcondition:

- - - - - - - - - - - - - - - - - -
ExpressionValue
path()path().branch() / s
status()st
symlink_status()symlink_st
+

Effects:

+

v3: Calls m_path.replace_filename(p) and assigns file_status() to m_status and m_symlink_status.

+

[Note: The cached file statuses will be updated when queried by the caller or by an explicit call to refresh. —end note]

+

v4: Calls m_path.replace_filename(p) and then refresh() or refresh(ec), respectively. If an error + occurs, the value of the cached data is unspecified.

+
+
void replace_filename(const path& p, file_status st, file_status symlink_st=file_status()); // v3-only
+
+

Effects: v3: Calls m_path.replace_filename(p) and assigns st to m_status and symlink_st + to m_symlink_status.

+
+
void refresh();
+void refresh(system::error_code& ec);
+
+

Effects: Updates any cached data by querying the filesystem about the file identified by m_path. If an error occurs, + the value of the cached data is unspecified.

directory_entry observers [directory_entry.obs]

@@ -2691,35 +2740,55 @@ systems that provide status as a by-product of directory iteration.file_status status() const; file_status status(system::error_code& ec) const;
-

Effects: As if,

-
-
if ( !status_known( m_status ) )
-{
-  if ( status_known(m_symlink_status) && !is_symlink(m_symlink_status) )
-    { m_status = m_symlink_status; }
-  else { m_status = status(m_path[, ec]); }
-}
-
+

Effects: If !status_known(m_status), calls refresh() or refresh(ec), respectively.

Returns: m_status

-

Throws: As specified in Error reporting.

-
file_status  symlink_status() const;
 file_status  symlink_status(system::error_code& ec) const;
-

- Effects: As if,

-
-
if ( !status_known( m_symlink_status ) )
-{
-  m_symlink_status = symlink_status(m_path[, ec]);
-}
-
+

Effects: If !status_known(m_symlink_status), calls refresh() or refresh(ec), respectively.

Returns: m_symlink_status

-

Throws: As specified in Error reporting.

- +
+
file_type file_type() const;
+file_type file_type(system::error_code& ec) const;
+
+

Effects: Equivalent to status().type() or status(ec).type(), respectively.

+

[Note: The implementation may be more efficient than calling status, if the information + about the file type is cached, but permissions are not. —end note]

+
+
file_type symlink_file_type() const;
+file_type symlink_file_type(system::error_code& ec) const;
+
+

Effects: Equivalent to symlink_status().type() or symlink_status(ec).type(), respectively.

+

[Note: The implementation may be more efficient than calling symlink_status, if the information + about the file type is cached, but permissions are not. —end note]

+
+
bool exists() const;
+bool exists(system::error_code& ec) const;
+
+

Effects: Equivalent to exists(status()) or exists(status(ec)), respectively.

+
+
bool is_regular_file() const;
+bool is_regular_file(system::error_code& ec) const;
+
+

Effects: Equivalent to is_regular_file(status()) or is_regular_file(status(ec)), respectively.

+
+
bool is_directory() const;
+bool is_directory(system::error_code& ec) const;
+
+

Effects: Equivalent to is_directory(status()) or is_directory(status(ec)), respectively.

+
+
bool is_symlink() const;
+bool is_symlink(system::error_code& ec) const;
+
+

Effects: Equivalent to is_symlink(symlink_status()) or is_symlink(symlink_status(ec)), respectively.

+
+
bool is_other() const;
+bool is_other(system::error_code& ec) const;
+
+

Effects: Equivalent to is_other(status()) or is_other(status(ec)), respectively.

bool operator==(const directory_entry& rhs);
diff --git a/doc/release_history.html b/doc/release_history.html index d85f457..734fc8e 100644 --- a/doc/release_history.html +++ b/doc/release_history.html @@ -41,6 +41,11 @@

1.83.0


-

© Copyright Andrey Semashev, 2021-2022

+

© Copyright Andrey Semashev, 2021-2023

Distributed under the Boost Software License, Version 1.0. See www.boost.org/LICENSE_1_0.txt

diff --git a/include/boost/filesystem/directory.hpp b/include/boost/filesystem/directory.hpp index 91f6ca7..6687f2a 100644 --- a/include/boost/filesystem/directory.hpp +++ b/include/boost/filesystem/directory.hpp @@ -41,6 +41,17 @@ namespace boost { namespace filesystem { +class directory_iterator; + +namespace detail { + +struct directory_iterator_params; + +BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, directory_iterator_params* params, system::error_code* ec); +BOOST_FILESYSTEM_DECL void directory_iterator_increment(directory_iterator& it, system::error_code* ec); + +} // namespace detail + //--------------------------------------------------------------------------------------// // // // directory_entry // @@ -53,20 +64,30 @@ namespace filesystem { class directory_entry { + friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, detail::directory_iterator_params* params, system::error_code* ec); + friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_increment(directory_iterator& it, system::error_code* ec); + public: typedef boost::filesystem::path::value_type value_type; // enables class path ctor taking directory_entry directory_entry() BOOST_NOEXCEPT {} - explicit directory_entry(boost::filesystem::path const& p) : - m_path(p), m_status(file_status()), m_symlink_status(file_status()) - { - } + explicit directory_entry(boost::filesystem::path const& p); +#if BOOST_FILESYSTEM_VERSION >= 4 + directory_entry(boost::filesystem::path const& p, system::error_code& ec) : + m_path(p) + { + refresh_impl(&ec); + if (ec) + m_path.clear(); + } +#else directory_entry(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status()) : m_path(p), m_status(st), m_symlink_status(symlink_st) { } +#endif directory_entry(directory_entry const& rhs) : m_path(rhs.m_path), m_status(rhs.m_status), m_symlink_status(rhs.m_symlink_status) @@ -101,7 +122,188 @@ public: return *this; } - void assign(boost::filesystem::path&& p, file_status st = file_status(), file_status symlink_st = file_status()) + void assign(boost::filesystem::path&& p); + +#if BOOST_FILESYSTEM_VERSION >= 4 + void assign(boost::filesystem::path&& p, system::error_code& ec) + { + m_path = static_cast< boost::filesystem::path&& >(p); + refresh_impl(&ec); + } +#else + void assign(boost::filesystem::path&& p, file_status st, file_status symlink_st = file_status()) + { + assign_with_status(static_cast< boost::filesystem::path&& >(p), st, symlink_st); + } +#endif +#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + + void assign(boost::filesystem::path const& p); + +#if BOOST_FILESYSTEM_VERSION >= 4 + void assign(boost::filesystem::path const& p, system::error_code& ec) + { + m_path = p; + refresh_impl(&ec); + } +#else + void assign(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status()) + { + assign_with_status(p, st, symlink_st); + } +#endif + + void replace_filename(boost::filesystem::path const& p); + +#if BOOST_FILESYSTEM_VERSION >= 4 + void replace_filename(boost::filesystem::path const& p, system::error_code& ec) + { + m_path.replace_filename(p); + refresh_impl(&ec); + } +#else + void replace_filename(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status()) + { + replace_filename_with_status(p, st, symlink_st); + } + + BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use directory_entry::replace_filename() instead") + void replace_leaf(boost::filesystem::path const& p, file_status st, file_status symlink_st) + { + replace_filename_with_status(p, st, symlink_st); + } +#endif + + boost::filesystem::path const& path() const BOOST_NOEXCEPT { return m_path; } + operator boost::filesystem::path const&() const BOOST_NOEXCEPT { return m_path; } + + void refresh() { refresh_impl(); } + void refresh(system::error_code& ec) BOOST_NOEXCEPT { refresh_impl(&ec); } + + file_status status() const + { + if (!filesystem::status_known(m_status)) + refresh_impl(); + return m_status; + } + + file_status status(system::error_code& ec) const BOOST_NOEXCEPT + { + if (!filesystem::status_known(m_status)) + refresh_impl(&ec); + return m_status; + } + + file_status symlink_status() const + { + if (!filesystem::status_known(m_symlink_status)) + refresh_impl(); + return m_symlink_status; + } + + file_status symlink_status(system::error_code& ec) const BOOST_NOEXCEPT + { + if (!filesystem::status_known(m_symlink_status)) + refresh_impl(&ec); + return m_symlink_status; + } + + filesystem::file_type file_type() const + { + if (!filesystem::type_present(m_status)) + refresh_impl(); + return m_status.type(); + } + + filesystem::file_type file_type(system::error_code& ec) const BOOST_NOEXCEPT + { + if (!filesystem::type_present(m_status)) + refresh_impl(&ec); + return m_status.type(); + } + + filesystem::file_type symlink_file_type() const + { + if (!filesystem::type_present(m_symlink_status)) + refresh_impl(); + return m_symlink_status.type(); + } + + filesystem::file_type symlink_file_type(system::error_code& ec) const BOOST_NOEXCEPT + { + if (!filesystem::type_present(m_symlink_status)) + refresh_impl(&ec); + return m_symlink_status.type(); + } + + bool exists() const + { + filesystem::file_type ft = this->file_type(); + return ft != filesystem::status_error && ft != filesystem::file_not_found; + } + + bool exists(system::error_code& ec) const BOOST_NOEXCEPT + { + filesystem::file_type ft = this->file_type(ec); + return ft != filesystem::status_error && ft != filesystem::file_not_found; + } + + bool is_regular_file() const + { + return this->file_type() == filesystem::regular_file; + } + + bool is_regular_file(system::error_code& ec) const BOOST_NOEXCEPT + { + return this->file_type(ec) == filesystem::regular_file; + } + + bool is_directory() const + { + return this->file_type() == filesystem::directory_file; + } + + bool is_directory(system::error_code& ec) const BOOST_NOEXCEPT + { + return this->file_type(ec) == filesystem::directory_file; + } + + bool is_symlink() const + { + return this->symlink_file_type() == filesystem::symlink_file; + } + + bool is_symlink(system::error_code& ec) const BOOST_NOEXCEPT + { + return this->symlink_file_type(ec) == filesystem::symlink_file; + } + + bool is_other() const + { + filesystem::file_type ft = this->file_type(); + return ft != filesystem::status_error && ft != filesystem::file_not_found && + ft != filesystem::regular_file && ft != filesystem::directory_file; + } + + bool is_other(system::error_code& ec) const BOOST_NOEXCEPT + { + filesystem::file_type ft = this->file_type(ec); + return ft != filesystem::status_error && ft != filesystem::file_not_found && + ft != filesystem::regular_file && ft != filesystem::directory_file; + } + + bool operator==(directory_entry const& rhs) const { return m_path == rhs.m_path; } + bool operator!=(directory_entry const& rhs) const { return m_path != rhs.m_path; } + bool operator<(directory_entry const& rhs) const { return m_path < rhs.m_path; } + bool operator<=(directory_entry const& rhs) const { return m_path <= rhs.m_path; } + bool operator>(directory_entry const& rhs) const { return m_path > rhs.m_path; } + bool operator>=(directory_entry const& rhs) const { return m_path >= rhs.m_path; } + +private: + BOOST_FILESYSTEM_DECL void refresh_impl(system::error_code* ec = NULL) const; + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + void assign_with_status(boost::filesystem::path&& p, file_status st, file_status symlink_st) { m_path = static_cast< boost::filesystem::path&& >(p); m_status = static_cast< file_status&& >(st); @@ -109,7 +311,7 @@ public: } #endif - void assign(boost::filesystem::path const& p, file_status st = file_status(), file_status symlink_st = file_status()) + void assign_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st) { m_path = p; #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -121,7 +323,7 @@ public: #endif } - void replace_filename(boost::filesystem::path const& p, file_status st = file_status(), file_status symlink_st = file_status()) + void replace_filename_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st) { m_path.replace_filename(p); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -133,40 +335,58 @@ public: #endif } -#ifndef BOOST_FILESYSTEM_NO_DEPRECATED - BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use directory_entry::replace_filename() instead") - void replace_leaf(boost::filesystem::path const& p, file_status st, file_status symlink_st) - { - replace_filename(p, st, symlink_st); - } -#endif - - boost::filesystem::path const& path() const BOOST_NOEXCEPT - { - return m_path; - } - operator boost::filesystem::path const&() const BOOST_NOEXCEPT { return m_path; } - file_status status() const { return get_status(); } - file_status status(system::error_code& ec) const BOOST_NOEXCEPT { return get_status(&ec); } - file_status symlink_status() const { return get_symlink_status(); } - file_status symlink_status(system::error_code& ec) const BOOST_NOEXCEPT { return get_symlink_status(&ec); } - - bool operator==(directory_entry const& rhs) const { return m_path == rhs.m_path; } - bool operator!=(directory_entry const& rhs) const { return m_path != rhs.m_path; } - bool operator<(directory_entry const& rhs) const { return m_path < rhs.m_path; } - bool operator<=(directory_entry const& rhs) const { return m_path <= rhs.m_path; } - bool operator>(directory_entry const& rhs) const { return m_path > rhs.m_path; } - bool operator>=(directory_entry const& rhs) const { return m_path >= rhs.m_path; } - -private: - BOOST_FILESYSTEM_DECL file_status get_status(system::error_code* ec = NULL) const; - BOOST_FILESYSTEM_DECL file_status get_symlink_status(system::error_code* ec = NULL) const; - private: boost::filesystem::path m_path; mutable file_status m_status; // stat()-like mutable file_status m_symlink_status; // lstat()-like -}; // directory_entry +}; + +#if !defined(BOOST_FILESYSTEM_SOURCE) + +inline directory_entry::directory_entry(boost::filesystem::path const& p) : + m_path(p) +{ +#if BOOST_FILESYSTEM_VERSION >= 4 + refresh_impl(); +#endif +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +inline void directory_entry::assign(boost::filesystem::path&& p) +{ + m_path = static_cast< boost::filesystem::path&& >(p); +#if BOOST_FILESYSTEM_VERSION >= 4 + refresh_impl(); +#else + m_status = file_status(); + m_symlink_status = file_status(); +#endif +} +#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + +inline void directory_entry::assign(boost::filesystem::path const& p) +{ + m_path = p; +#if BOOST_FILESYSTEM_VERSION >= 4 + refresh_impl(); +#else + m_status = file_status(); + m_symlink_status = file_status(); +#endif +} + +inline void directory_entry::replace_filename(boost::filesystem::path const& p) +{ + m_path.replace_filename(p); +#if BOOST_FILESYSTEM_VERSION >= 4 + refresh_impl(); +#else + m_status = file_status(); + m_symlink_status = file_status(); +#endif +} + +#endif // !defined(BOOST_FILESYSTEM_SOURCE) namespace detail { namespace path_traits { @@ -287,8 +507,6 @@ BOOST_SCOPED_ENUM_DECLARE_END(directory_options) BOOST_BITMASK(BOOST_SCOPED_ENUM_NATIVE(directory_options)) -class directory_iterator; - namespace detail { struct dir_itr_imp : @@ -318,11 +536,6 @@ struct dir_itr_imp : BOOST_FILESYSTEM_DECL static void operator delete(void* p) BOOST_NOEXCEPT; }; -struct directory_iterator_params; - -BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, directory_iterator_params* params, system::error_code* ec); -BOOST_FILESYSTEM_DECL void directory_iterator_increment(directory_iterator& it, system::error_code* ec); - } // namespace detail //--------------------------------------------------------------------------------------// diff --git a/src/directory.cpp b/src/directory.cpp index 0f4c184..e32690d 100644 --- a/src/directory.cpp +++ b/src/directory.cpp @@ -87,42 +87,32 @@ 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)) + // 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 = m_symlink_status; - if (ec) - ec->clear(); - } - else - { - 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; } //--------------------------------------------------------------------------------------// @@ -1131,7 +1121,7 @@ void directory_iterator_construct(directory_iterator& it, path const& p, unsigne { path full_path(p); path_algorithms::append_v4(full_path, filename); - imp->dir_entry.assign + imp->dir_entry.assign_with_status ( #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) static_cast< path&& >(full_path), @@ -1200,7 +1190,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; } } @@ -1364,14 +1354,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; } @@ -1384,12 +1374,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 @@ -1399,7 +1389,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)()))) diff --git a/test/deprecated_test.cpp b/test/deprecated_test.cpp index 011fcea..7b11496 100644 --- a/test/deprecated_test.cpp +++ b/test/deprecated_test.cpp @@ -266,7 +266,16 @@ int cpp_main(int /*argc*/, char* /*argv*/[]) //path::default_name_check(fs::no_check); - fs::directory_entry de("foo/bar"); + fs::directory_entry de("foo.bar", fs::file_status(fs::regular_file, fs::owner_all), fs::file_status(fs::directory_file, fs::group_all)); + + BOOST_TEST(de.path() == "foo.bar"); + BOOST_TEST(de.status() == fs::file_status(fs::regular_file, fs::owner_all)); + BOOST_TEST(de.symlink_status() == fs::file_status(fs::directory_file, fs::group_all)); + BOOST_TEST(de < fs::directory_entry("goo.bar", fs::file_status(), fs::file_status())); + BOOST_TEST(de == fs::directory_entry("foo.bar", fs::file_status(), fs::file_status())); + BOOST_TEST(de != fs::directory_entry("goo.bar", fs::file_status(), fs::file_status())); + de.replace_filename("bar.foo", fs::file_status(), fs::file_status()); + BOOST_TEST(de.path() == "bar.foo"); de.replace_leaf("", fs::file_status(), fs::file_status()); diff --git a/test/operations_unit_test.cpp b/test/operations_unit_test.cpp index 1e94e66..e5c3c62 100644 --- a/test/operations_unit_test.cpp +++ b/test/operations_unit_test.cpp @@ -24,11 +24,15 @@ #endif #include // make sure filesystem.hpp works +#include // for BOOST_FILESYSTEM_C_STR #include #include #include #include +#include +#include +#include using namespace boost::filesystem; using namespace boost::system; @@ -41,6 +45,15 @@ using std::string; namespace { bool cleanup = true; +void create_file(const path& ph, const std::string& contents = std::string()) +{ + std::ofstream f(BOOST_FILESYSTEM_C_STR(ph)); + if (!f) + throw filesystem_error("operations_test create_file", ph, error_code(errno, system_category())); + if (!contents.empty()) + f << contents; +} + void check(bool ok, const char* file, int line) { if (ok) @@ -252,20 +265,32 @@ void operations_test() // directory_entry_test ------------------------------------------------------------// -void directory_entry_test() +void directory_entry_test(path const& temp_dir) { cout << "directory_entry test..." << endl; - directory_entry de("foo.bar", file_status(regular_file, owner_all), file_status(directory_file, group_all)); + create_file(temp_dir / "foo.bar"); + create_file(temp_dir / "goo.bar"); + create_directory(temp_dir / "bar.foo"); - CHECK(de.path() == "foo.bar"); - CHECK(de.status() == file_status(regular_file, owner_all)); - CHECK(de.symlink_status() == file_status(directory_file, group_all)); - CHECK(de < directory_entry("goo.bar")); - CHECK(de == directory_entry("foo.bar")); - CHECK(de != directory_entry("goo.bar")); + directory_entry de(temp_dir / "foo.bar"); + + CHECK(de.path() == temp_dir / "foo.bar"); + CHECK(de.status().type() == regular_file); + CHECK(de.symlink_status().type() == regular_file); + CHECK(de.is_regular_file()); + CHECK(de < directory_entry(temp_dir / "goo.bar")); + CHECK(de == directory_entry(temp_dir / "foo.bar")); + CHECK(de != directory_entry(temp_dir / "goo.bar")); de.replace_filename("bar.foo"); - CHECK(de.path() == "bar.foo"); + CHECK(de.path() == temp_dir / "bar.foo"); + CHECK(de.is_directory()); + CHECK(de.status().type() == directory_file); + CHECK(de.symlink_status().type() == directory_file); + + boost::filesystem::remove(temp_dir / "bar.foo"); + boost::filesystem::remove(temp_dir / "goo.bar"); + boost::filesystem::remove(temp_dir / "foo.bar"); } // directory_entry_overload_test ---------------------------------------------------// @@ -354,7 +379,7 @@ int cpp_main(int argc, char* argv[]) directory_iterator_test(); recursive_directory_iterator_test(); operations_test(); - directory_entry_test(); + directory_entry_test(temp_dir); directory_entry_overload_test(); error_handling_test();