366 changes: 248 additions & 118 deletions src/operations.cpp

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions test/Jamfile.v2
Expand Up @@ -25,6 +25,20 @@ rule check-mklink ( properties * )
if [ MATCH (MKLINK) : $(output[1]) ]
{
result = <define>BOOST_FILESYSTEM_HAS_MKLINK ;

if ! $(.annouced-mklink)
{
ECHO " - Boost.Filesystem: mklink found" ;
.annouced-mklink = 1 ;
}
}
else
{
if ! $(.annouced-mklink)
{
ECHO " - Boost.Filesystem: mklink not found" ;
.annouced-mklink = 1 ;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/issues/99_canonical_with_junction_point.cpp
Expand Up @@ -46,7 +46,7 @@ int main()
const fs::path subDir = "sub";
fs::create_directories(real / subDir);
fs::current_path(tmp.path);
BOOST_TEST(std::system("mklink /j junction real") == 0);
BOOST_TEST(std::system("mklink /J junction real") == 0);
BOOST_TEST(fs::exists(junction));

// Due to a bug there was a dependency on the current path so try the below for all:
Expand Down
193 changes: 187 additions & 6 deletions test/operations_test.cpp
Expand Up @@ -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 <unistd.h> // sleep
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
//}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -1423,6 +1450,156 @@ 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_directory_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);
++created_count;
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()
Expand Down Expand Up @@ -2085,7 +2262,7 @@ void platform_specific_tests()
//
// Directory junctions are very similar to symlinks, but have some performance
// and other advantages over symlinks. They can be created from the command line
// with "mklink /j junction-name target-path".
// with "mklink /J junction-name target-path".

{
cout << " directory junction tests..." << endl;
Expand All @@ -2106,7 +2283,7 @@ void platform_specific_tests()
fs::path cur_path(fs::current_path());
fs::current_path(dir);
//cout << " current_path() is " << fs::current_path() << endl;
BOOST_TEST(std::system("mklink /j junc d1") == 0);
BOOST_TEST(std::system("mklink /J junc d1") == 0);
//std::system("dir");
fs::current_path(cur_path);
//cout << " current_path() is " << fs::current_path() << endl;
Expand Down Expand Up @@ -2578,8 +2755,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();
Expand All @@ -2602,4 +2783,4 @@ int cpp_main(int argc, char* argv[])

cout << "returning from main()" << endl;
return ::boost::report_errors();
} // main
}
4 changes: 1 addition & 3 deletions test/windows_attributes.cpp
Expand Up @@ -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;
Expand Down