-
Notifications
You must be signed in to change notification settings - Fork 167
Description
I'm using the latest develop branch (will be 1.71)
From operations.cpp:
// Invariant: On return, the top of the iterator stack is the next valid (possibly
// end) iterator, regardless of whether or not an error is reported, and regardless of
// whether any error is reported by exception or error code. In other words, progress
// is always made so a loop on the iterator will always eventually terminate
// regardless of errors.
BOOST_FILESYSTEM_DECL
void recur_dir_itr_imp::increment(system::error_code* ec)
This is not (always?) true as the iterator does not advance when an error is reported during push_directory.
Steps to reproduce
Consider this program (slightly modified simple_ls.cpp from examples)
recursive_ls.cpp
#define BOOST_FILESYSTEM_VERSION 3
// As an example program, we don't want to use any deprecated features
#ifndef BOOST_FILESYSTEM_NO_DEPRECATED
# define BOOST_FILESYSTEM_NO_DEPRECATED
#endif
#ifndef BOOST_SYSTEM_NO_DEPRECATED
# define BOOST_SYSTEM_NO_DEPRECATED
#endif
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/progress.hpp"
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc, char* argv[])
{
fs::path p(fs::current_path());
if (argc > 1)
p = fs::system_complete(argv[1]);
else
std::cout << "\nusage: recursive_ls [path]" << std::endl;
unsigned long file_count = 0;
unsigned long dir_count = 0;
unsigned long other_count = 0;
unsigned long err_count = 0;
if (!fs::exists(p))
{
std::cout << "\nNot found: " << p << std::endl;
return 1;
}
if (fs::is_directory(p))
{
std::cout << "\nIn directory: " << p << "\n\n";
fs::recursive_directory_iterator end_iter;
for (fs::recursive_directory_iterator dir_itr(p);
dir_itr != end_iter;)
{
try
{
if (fs::is_directory(dir_itr->status()))
{
++dir_count;
std::cout << dir_itr->path() << " [directory]\n";
}
else if (fs::is_regular_file(dir_itr->status()))
{
++file_count;
std::cout << dir_itr->path() << "\n";
}
else
{
++other_count;
std::cout << dir_itr->path() << " [other]\n";
}
boost::system::error_code ec;
dir_itr.increment(ec);
if (ec)
{
++err_count;
std::cout << "*" << dir_itr->path() << " *" << ec.message() << std::endl;
}
}
catch (const std::exception & ex)
{
++err_count;
std::cout << dir_itr->path() << " " << ex.what() << std::endl;
}
}
std::cout << "\n" << file_count << " files\n"
<< dir_count << " directories\n"
<< other_count << " others\n"
<< err_count << " errors\n";
}
else // must be a file
{
std::cout << "\nFound: " << p << "\n";
}
return 0;
}
Now run the following commands (tested on linux):
mkdir testdir
chmod 000 testdir
mkdir otherdir
./recursive_ls
Actual result
usage: recursive_ls [path]
In directory: "/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test"
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" *Permission denied
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" *Permission denied
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" *Permission denied
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" *Permission denied
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" *Permission denied
... infinite loop
Expected result
One of:
- Fullfill the invariant and make the iterator always advance. I can see that other tests expect that the for loop finishes without special handling or errors
- Drop the invariant and write into documentation that on errors,
it.no_push()
must be called in order to continue.
I'm not sure what is better.
The first would have the disadvantage that because the iterator has already advances, the current path would not correspond to the error code, like this:
"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/testdir" [directory]
*"/home/rodney/Projects/boost/boost/libs/filesystem/test/recursive_test/otherdir" *Permission denied
This is because testdir record is read and reported just fine, the error only happens once we try to recurse into that directory. So the contents are skipped and iterator now points to the next record in the parent directory.
The second variant, on the other hand, only works for errors connected to directory opens. What about other errors? Should another api be added to "acknowledge" the current error?
Many thanks for a wonderful library.