Opened 11 years ago

Closed 11 years ago

#5652 closed Bugs (fixed)

recursive_directory_iterator fails on cyclic symbolic links

Reported by: Geurt Vos <geurt.vos@…> Owned by: Beman Dawes
Milestone: Boost 1.49.0 Component: filesystem
Version: Boost 1.46.1 Severity: Problem
Keywords: Cc: macbishop@…

Description

recursive_directory_iterator throws 'Too many levels of symbolic links' on bad, yet correct symbolic links. To reproduce on Linux:

$ ln -s mybadlink mybadlink

iterating through directory containing 'mybadlink' (with symlink_option::none) results in exception:

boost::filesystem::status: Too many levels of symbolic links: "./mybadlink"

Also tested on boost 1.47.0-beta1

Attachments (2)

bug5652.diff (815 bytes ) - added by Daniel Aarno <macbishop@…> 11 years ago.
Fix for bug 5652: Check if file is symbolic link before stat:ing it during recursion (an not the other way around)
bug5652-perf-opt.diff (833 bytes ) - added by Daniel Aarno <macbishop@…> 11 years ago.
Performance optimized patch for bug 5652: Check if file is symbolic link before stat:ing it during recursion (an not the other way around) but first check if we are following symlinks

Download all attachments as: .zip

Change History (8)

by Daniel Aarno <macbishop@…>, 11 years ago

Attachment: bug5652.diff added

Fix for bug 5652: Check if file is symbolic link before stat:ing it during recursion (an not the other way around)

comment:1 by Daniel Aarno <macbishop@…>, 11 years ago

I can confirm this issue and there is an easy fix. To reproduce and test the issue the following simple program that counts the files (recursively) in a directory can be used on a directory containing the "symlink to self" as described by Geurt.

#include <string> #include <iostream>

#include <boost/filesystem.hpp>

int ftw(const std::string &root) {

using namespace boost::filesystem; typedef recursive_directory_iterator walker; int count = 0; for (walker w(root); w != walker(); ++w)

++count;

return count;

}

int main (int argc, const char argv) {

int count = ftw(argv[1]); std::cout << count << std::endl;

}

The problem is in the increment method in recur_dir_itr_imp. It checks the file with is_directory, which will end up in a "stat" call on the file. Of course if the file is a symlink to itself stat will try to follow the link indefinitely (since it will try to deference the symlink until a real file is found). This causes stat to generate an ELOOP error and the exception is thrown.

The solution is to not check if the file is a directory if it is a symlink - unless of course we are following symlinks during recursion, in which case the exception should occur. We can test this by adding symlink_option::recurse as an option to the walker constructor in the above example.

comment:2 by Daniel Aarno <macbishop@…>, 11 years ago

Cc: macbishop@… added

For efficiency reasons it would also be a good idea to first check for symlink_option::recurse and *then* for is_symlink, since if we are following symlinks we will not have to do the (expensive) call to lstat and we will only do an integer comparision.

by Daniel Aarno <macbishop@…>, 11 years ago

Attachment: bug5652-perf-opt.diff added

Performance optimized patch for bug 5652: Check if file is symbolic link before stat:ing it during recursion (an not the other way around) but first check if we are following symlinks

comment:3 by benjamin.kircher@…, 11 years ago

Just walked into this with 1.48. +1

in reply to:  1 ; comment:4 by Beman Dawes, 11 years ago

Status: newassigned

Replying to Daniel Aarno <macbishop@…>:

I can confirm this issue and there is an easy fix. To reproduce and test the issue the following simple program that counts the files (recursively) in a directory can be used on a directory containing the "symlink to self" as described by Geurt.

#include <string> #include <iostream>

#include <boost/filesystem.hpp>

int ftw(const std::string &root) {

using namespace boost::filesystem; typedef recursive_directory_iterator walker; int count = 0; for (walker w(root); w != walker(); ++w)

++count;

return count;

}

int main (int argc, const char argv) {

int count = ftw(argv[1]); std::cout << count << std::endl;

}

Thanks for the test program! Providing a test program is a very effective way to communicative exactly what problem you are having, and prevents me from chasing my tail around in circles.

On Windows the program throws with a message "The name of the file cannot be resolved by the system". I haven't looked at your patch yet, but wanted to thank you for the test case first,

--Beman

in reply to:  4 comment:5 by macbishop@…, 11 years ago

Replying to bemandawes:

On Windows the program throws with a message "The name of the file cannot be resolved by the system". I haven't looked at your patch yet, but wanted to thank you for the test case first

Thanks for looking into this. To clarify, the test case takes as its first argument a path to a directory containing the recursive symlink, so you need to:

1) Setup a directory with a symlink that points to itself (not sure how to do it on windows) 2) Then run the test case, with the path to the directory given as the only argument, e.g. ./a.out thedir

Let me know if you have any questions or suggestions on the test-case or patches.

comment:6 by Beman Dawes, 11 years ago

Milestone: To Be DeterminedBoost 1.49.0
Resolution: fixed
Status: assignedclosed

Fixed by changeset 76556.

Thanks, all!

--Beman

Note: See TracTickets for help on using tickets.