diff --git a/doc/release_history.html b/doc/release_history.html
index 6710cd73c..4a1050228 100644
--- a/doc/release_history.html
+++ b/doc/release_history.html
@@ -49,6 +49,7 @@
1.78.0
Optimized overloads of path::assign
, path::append
, path::concat
and the corresponding operators to avoid unnecessary path copying and reduce the amount of code redundancy.
On POSIX systems, fixed absolute(p, base)
returning a path with root name base.root_name()
if p
starts with a root directory. In such a case p
is already an absolute path and should be returned as is.
create_directories
no longer reports an error if the input path consists entirely of dot (".") and dot-dot ("..") elements. The implementation is no longer using recursion internally and therefore is better protected from stack overflow on extremely long paths.
+ On Windows, remove
now supports deleting read-only files. The operation will attempt to reset the read-only attribute prior to removal. Note that this introduces a possibility of the read-only attribute being left unset, if the operation fails and the original value of the attribute fails to be restored. This also affects remove_all
. (#216)
Fixed a linking error about unresolved references to Boost.ContainerHash functions when user's code includes boost/filesystem/path.hpp
but not boost/container_hash/hash.hpp
and the compiler is set to preserve unused inline functions. (#215)
diff --git a/include/boost/filesystem/file_status.hpp b/include/boost/filesystem/file_status.hpp
index cedee8562..484efdaa2 100644
--- a/include/boost/filesystem/file_status.hpp
+++ b/include/boost/filesystem/file_status.hpp
@@ -46,10 +46,8 @@ enum file_type
fifo_file,
socket_file,
reparse_file, // Windows: FILE_ATTRIBUTE_REPARSE_POINT that is not a symlink
- type_unknown, // file does exist, but isn't one of the above types or
+ type_unknown // file does exist, but isn't one of the above types or
// we don't have strong enough permission to find its type
-
- _detail_directory_symlink // internal use only; never exposed to users
};
//--------------------------------------------------------------------------------------//
diff --git a/src/operations.cpp b/src/operations.cpp
index 66c13601f..e8b8490b2 100644
--- a/src/operations.cpp
+++ b/src/operations.cpp
@@ -295,8 +295,6 @@ union reparse_data_buffer
#define BOOST_SET_CURRENT_DIRECTORY(P) (::chdir(P) == 0)
#define BOOST_CREATE_HARD_LINK(F, T) (::link(T, F) == 0)
-#define BOOST_REMOVE_DIRECTORY(P) (::rmdir(P) == 0)
-#define BOOST_DELETE_FILE(P) (::unlink(P) == 0)
#define BOOST_MOVE_FILE(OLD, NEW) (::rename(OLD, NEW) == 0)
#define BOOST_RESIZE_FILE(P, SZ) (::truncate(P, SZ) == 0)
@@ -304,11 +302,8 @@ union reparse_data_buffer
#define BOOST_SET_CURRENT_DIRECTORY(P) (::SetCurrentDirectoryW(P) != 0)
#define BOOST_CREATE_HARD_LINK(F, T) (create_hard_link_api(F, T, 0) != 0)
-#define BOOST_REMOVE_DIRECTORY(P) (::RemoveDirectoryW(P) != 0)
-#define BOOST_DELETE_FILE(P) (::DeleteFileW(P) != 0)
#define BOOST_MOVE_FILE(OLD, NEW) (::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0)
#define BOOST_RESIZE_FILE(P, SZ) (resize_file_api(P, SZ) != 0)
-#define BOOST_READ_SYMLINK(P, T)
#endif
@@ -355,8 +350,6 @@ BOOST_CONSTEXPR_OR_CONST unsigned int symloop_max =
#endif
;
-fs::file_type query_file_type(path const& p, error_code* ec);
-
// general helpers -----------------------------------------------------------------//
bool is_empty_directory(path const& p, error_code* ec)
@@ -368,81 +361,6 @@ bool is_empty_directory(path const& p, error_code* ec)
bool not_found_error(int errval) BOOST_NOEXCEPT; // forward declaration
-// only called if directory exists
-inline bool remove_directory(path const& p) // true if succeeds or not found
-{
- return BOOST_REMOVE_DIRECTORY(p.c_str()) || not_found_error(BOOST_ERRNO); // mitigate possible file system race. See #11166
-}
-
-// only called if file exists
-inline bool remove_file(path const& p) // true if succeeds or not found
-{
- return BOOST_DELETE_FILE(p.c_str()) || not_found_error(BOOST_ERRNO); // mitigate possible file system race. See #11166
-}
-
-// called by remove and remove_all_aux
-// return true if file removed, false if not removed
-bool remove_file_or_directory(path const& p, fs::file_type type, error_code* ec)
-{
- if (type == fs::file_not_found)
- {
- if (ec)
- ec->clear();
- return false;
- }
-
- if (type == fs::directory_file
-#ifdef BOOST_WINDOWS_API
- || type == fs::_detail_directory_symlink
-#endif
- )
- {
- if (error(!remove_directory(p) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::remove"))
- return false;
- }
- else
- {
- if (error(!remove_file(p) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::remove"))
- return false;
- }
- return true;
-}
-
-uintmax_t remove_all_aux(path const& p, fs::file_type type, error_code* ec)
-{
- uintmax_t count = 0u;
-
- 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::none), ec);
- if (ec && *ec)
- return count;
-
- const fs::directory_iterator end_dit;
- while (itr != end_dit)
- {
- fs::file_type tmp_type = query_file_type(itr->path(), ec);
- if (ec && *ec)
- return count;
-
- count += remove_all_aux(itr->path(), tmp_type, ec);
- if (ec && *ec)
- return count;
-
- fs::detail::directory_iterator_increment(itr, ec);
- if (ec && *ec)
- return count;
- }
- }
-
- remove_file_or_directory(p, type, ec);
- if (ec && *ec)
- return count;
-
- return ++count;
-}
-
#ifdef BOOST_POSIX_API
//--------------------------------------------------------------------------------------//
@@ -977,9 +895,101 @@ const syscall_initializer syscall_init;
#endif // defined(linux) || defined(__linux) || defined(__linux__)
-inline fs::file_type query_file_type(path const& p, error_code* ec)
+//! remove() implementation
+inline bool remove_impl(path const& p, fs::file_type type, error_code* ec)
+{
+ if (type == fs::file_not_found)
+ return false;
+
+ int res;
+ if (type == fs::directory_file)
+ res = ::rmdir(p.c_str());
+ else
+ res = ::unlink(p.c_str());
+
+ if (res != 0)
+ {
+ int err = errno;
+ if (BOOST_UNLIKELY(!not_found_error(err)))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+
+ return true;
+}
+
+//! remove() implementation
+inline bool remove_impl(path const& p, error_code* ec)
+{
+ // Since POSIX remove() is specified to work with either files or directories, in a
+ // perfect world it could just be called. But some important real-world operating
+ // systems (Windows, Mac OS, for example) don't implement the POSIX spec. So
+ // we have to distinguish between files and directories and call corresponding APIs
+ // to remove them.
+
+ error_code local_ec;
+ fs::file_type type = fs::detail::symlink_status(p, &local_ec).type();
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove", p, local_ec));
+
+ *ec = local_ec;
+ return false;
+ }
+
+ return fs::detail::remove_impl(p, type, ec);
+}
+
+//! remove_all() implementation
+uintmax_t remove_all_impl(path const& p, error_code* ec)
{
- return fs::detail::symlink_status(p, ec).type();
+ fs::file_type type;
+ {
+ error_code local_ec;
+ type = fs::detail::symlink_status(p, &local_ec).type();
+
+ if (type == fs::file_not_found)
+ return 0u;
+
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return 0u;
+ }
+ }
+
+ uintmax_t count = 0u;
+
+ 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::none), ec);
+ if (ec && *ec)
+ return count;
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ count += fs::detail::remove_all_impl(itr->path(), ec);
+ if (ec && *ec)
+ return count;
+
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return count;
+ }
+ }
+
+ count += fs::detail::remove_impl(p, type, ec);
+ if (ec && *ec)
+ return count;
+
+ return count;
}
#else // defined(BOOST_POSIX_API)
@@ -1085,16 +1095,14 @@ bool is_reparse_point_a_symlink(path const& p)
|| buf->rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; // aka "directory junction" or "junction"
}
-inline std::size_t get_full_path_name(
- path const& src, std::size_t len, wchar_t* buf, wchar_t** p)
+inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* buf, wchar_t** p)
{
- return static_cast< std::size_t >(
- ::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p));
+ return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p));
}
-fs::file_status process_status_failure(path const& p, error_code* ec)
+inline fs::file_status process_status_failure(path const& p, error_code* ec)
{
- int errval(::GetLastError());
+ int errval = ::GetLastError();
if (ec) // always report errval, even though some
ec->assign(errval, system_category()); // errval values are not status_errors
@@ -1106,32 +1114,163 @@ fs::file_status process_status_failure(path const& p, error_code* ec)
{
return fs::file_status(fs::type_unknown);
}
+
if (!ec)
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, error_code(errval, system_category())));
+
return fs::file_status(fs::status_error);
}
-// differs from symlink_status() in that directory symlinks are reported as
-// _detail_directory_symlink, as required on Windows by remove() and its helpers.
-fs::file_type query_file_type(path const& p, error_code* ec)
+//! remove() implementation
+inline bool remove_impl(path const& p, DWORD attrs, error_code* ec)
{
- DWORD attr(::GetFileAttributesW(p.c_str()));
- if (attr == 0xFFFFFFFF)
+ // The following is similar to symlink_status(), except that it distinguishes between symlinks
+ // to directories and to files, and also preserves the full file attributes, which we'll need below.
+ bool is_directory;
+ if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
{
- return process_status_failure(p, ec).type();
+ error_code local_ec;
+ file_type type = process_status_failure(p, &local_ec).type();
+
+ if (type == fs::file_not_found)
+ return false;
+
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove", p, local_ec));
+
+ *ec = local_ec;
+ return false;
+ }
+
+ is_directory = type == fs::directory_file;
+ }
+ else if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ is_directory = (attrs & FILE_ATTRIBUTE_DIRECTORY) && is_reparse_point_a_symlink(p);
+ }
+ else
+ {
+ is_directory = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
- if (ec)
- ec->clear();
+ if (is_directory)
+ {
+ BOOL res = ::RemoveDirectoryW(p.c_str());
+ if (BOOST_UNLIKELY(!res))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ emit_error(err, p, ec, "boost::filesystem::remove");
- if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
+ return false;
+ }
+ }
+ else
{
- if (is_reparse_point_a_symlink(p))
- return (attr & FILE_ATTRIBUTE_DIRECTORY) ? fs::_detail_directory_symlink : fs::symlink_file;
- return fs::reparse_file;
+ const bool is_read_only = (attrs & FILE_ATTRIBUTE_READONLY) != 0;
+ if (is_read_only)
+ {
+ // DeleteFileW does not allow to remove a read-only file, so we have to drop the attribute
+ DWORD new_attrs = attrs & ~FILE_ATTRIBUTE_READONLY;
+ BOOL res = ::SetFileAttributesW(p.c_str(), new_attrs);
+ if (BOOST_UNLIKELY(!res))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+ }
+
+ BOOL res = ::DeleteFileW(p.c_str());
+ if (BOOST_UNLIKELY(!res))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ {
+ if (is_read_only)
+ {
+ // Try to restore the read-only attribute
+ ::SetFileAttributesW(p.c_str(), attrs);
+ }
+
+ emit_error(err, p, ec, "boost::filesystem::remove");
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//! remove() implementation
+inline bool remove_impl(path const& p, error_code* ec)
+{
+ return remove_impl(p, ::GetFileAttributesW(p.c_str()), ec);
+}
+
+//! remove_all() implementation
+uintmax_t remove_all_impl(path const& p, error_code* ec)
+{
+ const DWORD attrs = ::GetFileAttributesW(p.c_str());
+ bool recurse;
+ if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
+ {
+ error_code local_ec;
+ file_type type = process_status_failure(p, &local_ec).type();
+
+ if (type == fs::file_not_found)
+ return 0u;
+
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return 0u;
+ }
+
+ // Some unknown file type
+ recurse = false;
+ }
+ else
+ {
+ // Recurse into directories, but not into junctions or directory symlinks
+ recurse = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
+ }
+
+ uintmax_t count = 0u;
+
+ if (recurse)
+ {
+ fs::directory_iterator itr;
+ fs::detail::directory_iterator_construct(itr, p, static_cast< unsigned int >(directory_options::none), ec);
+ if (ec && *ec)
+ return count;
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ count += remove_all_impl(itr->path(), ec);
+ if (ec && *ec)
+ return count;
+
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return count;
+ }
}
- return (attr & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file;
+ count += remove_impl(p, attrs, ec);
+ if (ec && *ec)
+ return count;
+
+ return count;
}
inline BOOL resize_file_api(const wchar_t* p, uintmax_t size)
@@ -3074,28 +3213,19 @@ path relative(path const& p, path const& base, error_code* ec)
BOOST_FILESYSTEM_DECL
bool remove(path const& p, error_code* ec)
{
- error_code tmp_ec;
- file_type type = query_file_type(p, &tmp_ec);
- if (error(type == status_error ? tmp_ec.value() : 0, p, ec, "boost::filesystem::remove"))
- return false;
+ if (ec)
+ ec->clear();
- // Since POSIX remove() is specified to work with either files or directories, in a
- // perfect world it could just be called. But some important real-world operating
- // systems (Windows, Mac OS X, for example) don't implement the POSIX spec. So
- // remove_file_or_directory() is always called to keep it simple.
- return remove_file_or_directory(p, type, ec);
+ return detail::remove_impl(p, ec);
}
BOOST_FILESYSTEM_DECL
uintmax_t remove_all(path const& p, error_code* ec)
{
- error_code tmp_ec;
- file_type type = query_file_type(p, &tmp_ec);
- if (error(type == status_error ? tmp_ec.value() : 0, p, ec, "boost::filesystem::remove_all"))
- return 0;
+ if (ec)
+ ec->clear();
- return (type != status_error && type != file_not_found) // exists
- ? remove_all_aux(p, type, ec) : 0;
+ return detail::remove_all_impl(p, ec);
}
BOOST_FILESYSTEM_DECL
diff --git a/test/operations_test.cpp b/test/operations_test.cpp
index 612f9bebb..2b5d92b7e 100644
--- a/test/operations_test.cpp
+++ b/test/operations_test.cpp
@@ -82,6 +82,24 @@ inline void unsetenv_(const char* name)
SetEnvironmentVariableW(convert(name).c_str(), 0);
}
+//! Sets read-only attribute on a file
+inline void set_read_only(fs::path const& p)
+{
+ DWORD attrs = GetFileAttributesW(p.c_str());
+ if (attrs == INVALID_FILE_ATTRIBUTES)
+ {
+ DWORD err = GetLastError();
+ throw fs::filesystem_error("operations_test set_read_only: failed to get file attributes", p, error_code(err, system_category()));
+ }
+
+ attrs |= FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(p.c_str(), attrs))
+ {
+ DWORD err = GetLastError();
+ throw fs::filesystem_error("operations_test set_read_only: failed to set file attributes", p, error_code(err, system_category()));
+ }
+}
+
#else
#include // sleep
@@ -149,7 +167,6 @@ bool throws_fs_error(F func, errno_t en, int line)
{
func();
}
-
catch (const fs::filesystem_error& ex)
{
if (report_throws)
@@ -266,8 +283,7 @@ class renamer
// else if (s.type() == fs::fifo_file) { os << "fifo_file"; }
// else if (s.type() == fs::socket_file) { os << "socket_file"; }
// else if (s.type() == fs::reparse_file) { os << "reparse_file"; }
-// else if (s.type() == fs::type_unknown) { os << "type_unknown"; }
-// else { os << "_detail_directory_symlink"; }
+// else { os << "type_unknown"; }
// return os;
//}
@@ -1346,6 +1362,17 @@ void remove_tests(const fs::path& dirx)
BOOST_TEST(!fs::remove("no-such-file"));
BOOST_TEST(!fs::remove("no-such-directory/no-such-file"));
+#if defined(BOOST_WINDOWS_API)
+ // remove() read-only file
+ BOOST_TEST(!fs::exists(f1x));
+ create_file(f1x, "");
+ BOOST_TEST(fs::exists(f1x));
+ BOOST_TEST(!fs::is_directory(f1x));
+ set_read_only(f1x);
+ BOOST_TEST(fs::remove(f1x));
+ BOOST_TEST(!fs::exists(f1x));
+#endif // defined(BOOST_WINDOWS_API)
+
// remove() directory
fs::path d1x = dirx / "shortlife_dir";
BOOST_TEST(!fs::exists(d1x));
@@ -1423,6 +1450,155 @@ void remove_symlink_tests()
BOOST_TEST(!fs::exists(f1x));
}
+// remove_all_tests ----------------------------------------------------------------//
+
+void remove_all_tests(const fs::path& dirx)
+{
+ cout << "remove_all_tests..." << endl;
+
+ // remove_all() file
+ {
+ fs::path f1x = dirx / "shortlife";
+ BOOST_TEST(!fs::exists(f1x));
+ create_file(f1x, "");
+ BOOST_TEST(fs::exists(f1x));
+ BOOST_TEST(!fs::is_directory(f1x));
+ BOOST_TEST_EQ(fs::remove_all(f1x), 1u);
+ BOOST_TEST(!fs::exists(f1x));
+ BOOST_TEST_EQ(fs::remove_all("no-such-file"), 0u);
+ BOOST_TEST_EQ(fs::remove_all("no-such-directory/no-such-file"), 0u);
+ }
+
+ // remove_all() directory tree
+ {
+ unsigned int created_count = 0u;
+ fs::path d1x = dirx / "shortlife_dir";
+ BOOST_TEST(!fs::exists(d1x));
+ fs::create_directory(d1x);
+ ++created_count;
+ BOOST_TEST(fs::exists(d1x));
+ BOOST_TEST(fs::is_directory(d1x));
+
+ fs::path d2x = d1x / "nested_dir";
+ BOOST_TEST(!fs::exists(d2x));
+ fs::create_directory(d2x);
+ ++created_count;
+ BOOST_TEST(fs::exists(d2x));
+ BOOST_TEST(fs::is_directory(d2x));
+
+ fs::path f1x = d1x / "shortlife";
+ BOOST_TEST(!fs::exists(f1x));
+ create_file(f1x, "");
+ ++created_count;
+ BOOST_TEST(fs::exists(f1x));
+ BOOST_TEST(!fs::is_directory(f1x));
+
+#if defined(BOOST_WINDOWS_API)
+ // read-only file
+ fs::path f2x = d1x / "shortlife_ro";
+ BOOST_TEST(!fs::exists(f2x));
+ create_file(f2x, "");
+ ++created_count;
+ BOOST_TEST(fs::exists(f2x));
+ BOOST_TEST(!fs::is_directory(f2x));
+ set_read_only(f2x);
+#endif // defined(BOOST_WINDOWS_API)
+
+ boost::uintmax_t removed_count = fs::remove_all(d1x);
+ BOOST_TEST_EQ(removed_count, created_count);
+
+ BOOST_TEST(!fs::exists(d1x));
+ }
+}
+
+// remove_all_symlink_tests --------------------------------------------------------//
+
+void remove_all_symlink_tests(const fs::path& dirx)
+{
+ cout << "remove_all_symlink_tests..." << endl;
+
+ // External directory tree
+ fs::path d1x = dirx / "shortlife_dir1";
+ BOOST_TEST(!fs::exists(d1x));
+ fs::create_directory(d1x);
+ BOOST_TEST(fs::exists(d1x));
+ BOOST_TEST(fs::is_directory(d1x));
+
+ fs::path f1x = d1x / "shortlife1";
+ BOOST_TEST(!fs::exists(f1x));
+ create_file(f1x, "");
+ BOOST_TEST(fs::exists(f1x));
+ BOOST_TEST(!fs::is_directory(f1x));
+
+ fs::path f2x = d1x / "shortlife2";
+ BOOST_TEST(!fs::exists(f2x));
+ create_file(f2x, "");
+ BOOST_TEST(fs::exists(f2x));
+ BOOST_TEST(!fs::is_directory(f2x));
+
+ // remove_all() directory tree that has symlinks to external directories
+ unsigned int created_count = 0u;
+ fs::path d2x = dirx / "shortlife_dir2";
+ BOOST_TEST(!fs::exists(d2x));
+ fs::create_directory(d2x);
+ ++created_count;
+ BOOST_TEST(fs::exists(d2x));
+ BOOST_TEST(fs::is_directory(d2x));
+
+ fs::path f3x = d2x / "shortlife";
+ BOOST_TEST(!fs::exists(f3x));
+ create_file(f3x, "");
+ ++created_count;
+ BOOST_TEST(fs::exists(f3x));
+ BOOST_TEST(!fs::is_directory(f3x));
+
+ fs::path d3x = d2x / "symlink_dir";
+ BOOST_TEST(!fs::exists(d3x));
+ fs::create_symlink(d1x, d3x);
+ ++created_count;
+ BOOST_TEST(fs::exists(d3x));
+ BOOST_TEST(fs::is_symlink(d3x));
+
+#if defined(BOOST_FILESYSTEM_HAS_MKLINK)
+ fs::path junc = d2x / "junc";
+ fs::path cur_path(fs::current_path());
+ fs::current_path(d2x);
+ BOOST_TEST(std::system("mklink /j junc ..\\shortlife_dir1") == 0);
+ fs::current_path(cur_path);
+ BOOST_TEST(fs::exists(junc));
+#endif
+
+ fs::path f4x = d2x / "symlink";
+ BOOST_TEST(!fs::exists(f4x));
+ fs::create_symlink(f1x, f4x);
+ ++created_count;
+ BOOST_TEST(fs::exists(f4x));
+ BOOST_TEST(fs::is_symlink(f4x));
+
+ fs::path f5x = d2x / "hardlink";
+ BOOST_TEST(!fs::exists(f5x));
+ fs::create_hard_link(f2x, f5x);
+ ++created_count;
+ BOOST_TEST(fs::exists(f5x));
+ BOOST_TEST(!fs::is_directory(f5x));
+
+ boost::uintmax_t removed_count = fs::remove_all(d2x);
+ BOOST_TEST_EQ(removed_count, created_count);
+
+ BOOST_TEST(!fs::exists(d2x));
+
+ // Check that external directory and file are intact
+ BOOST_TEST(fs::exists(d1x));
+ BOOST_TEST(fs::is_directory(d1x));
+ BOOST_TEST(fs::exists(f1x));
+ BOOST_TEST(!fs::is_directory(f1x));
+ BOOST_TEST(fs::exists(f2x));
+ BOOST_TEST(!fs::is_directory(f2x));
+
+ // Cleanup
+ fs::remove_all(d1x);
+}
+
// absolute_tests -----------------------------------------------------------------//
void absolute_tests()
@@ -2578,8 +2754,12 @@ int cpp_main(int argc, char* argv[])
recursive_iterator_status_tests(); // lots of cases by now, so a good time to test
rename_tests();
remove_tests(dir);
+ remove_all_tests(dir);
if (create_symlink_ok) // only if symlinks supported
+ {
remove_symlink_tests();
+ remove_all_symlink_tests(dir);
+ }
creation_time_tests(dir);
write_time_tests(dir);
temp_directory_path_tests();
@@ -2602,4 +2782,4 @@ int cpp_main(int argc, char* argv[])
cout << "returning from main()" << endl;
return ::boost::report_errors();
-} // main
+}
diff --git a/test/windows_attributes.cpp b/test/windows_attributes.cpp
index 2e80a96fa..cdca5355e 100644
--- a/test/windows_attributes.cpp
+++ b/test/windows_attributes.cpp
@@ -96,10 +96,8 @@ int cpp_main(int argc, char* argv[])
"fifo_file",
"socket_file",
"reparse_file", // Windows: FILE_ATTRIBUTE_REPARSE_POINT that is not a symlink
- "type_unknown", // file does exist", but isn't one of the above types or
+ "type_unknown" // file does exist", but isn't one of the above types or
// we don't have strong enough permission to find its type
-
- "_detail_directory_symlink" // internal use only; never exposed to users
};
std::cout << "boost::filesystem::status().type() is " << types[stat.type()] << std::endl;