Boost C++ Libraries: Ticket #5403: filesystem3::recursive_directory_iterator::increment(system::error_code& ec) unexpected behaviour https://svn.boost.org/trac10/ticket/5403 <p> please see this link <a class="ext-link" href="http://boost.2283326.n4.nabble.com/How-to-skip-a-sub-directory-with-filesystem3-recursive-directory-iterator-in-case-of-exception-td3417953.html"><span class="icon">​</span>http://boost.2283326.n4.nabble.com/How-to-skip-a-sub-directory-with-filesystem3-recursive-directory-iterator-in-case-of-exception-td3417953.html</a> for details. </p> <p> ideally, something like below should be working for either throwing(ec.m_val !=0) or no-throwing(ec.m_val ==0 ) case. </p> <pre class="wiki">boost::system::error_code ec; for (recursive_directory_iterator itr(root, ec), itr_end; itr!=itr_end; ) { ... ; itr.increment(ec); if(ec) itr.pop(); } </pre><p> </p> <p> Additionally, the intuitive expectation is that: recursive_directory_iterator::increment(ec) is a drop-in replacement and behaves like recursive_directory_iterator::increment() or recursive_directory_iterator::operator++() in case of no throw(ec.m_val == 0). To be more specifically, in such a case, the above code is expected to be effectively the same as: </p> <p> for (recursive_directory_iterator itr(root), itr_end; itr!=itr_end; ) { </p> <blockquote> <p> ... ; </p> <blockquote> <p> itr.increment(); <em> or itr++; </em></p> </blockquote> </blockquote> <p> } </p> <p> and </p> <p> for (recursive_directory_iterator itr(root), itr_end; itr!=itr_end; itr++) { </p> <blockquote> <p> ... ; </p> </blockquote> <p> } </p> <p> the current implementation does not allow it. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/5403 Trac 1.4.3 ttan@… Tue, 12 Apr 2011 05:28:32 GMT attachment set https://svn.boost.org/trac10/ticket/5403 https://svn.boost.org/trac10/ticket/5403 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">patch.patch</span> </li> </ul> <p> fix Ticket <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/5403" title="#5403: Bugs: filesystem3::recursive_directory_iterator::increment(system::error_code ... (closed: fixed)">#5403</a> </p> Ticket ttan@… Tue, 12 Apr 2011 05:39:03 GMT <link>https://svn.boost.org/trac10/ticket/5403#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:1</guid> <description> <p> Further debug shows in file operations.hpp </p> <pre class="wiki">void recur_dir_itr_imp::increment(system::error_code* ec) { //...skipped m_stack.push(directory_iterator(m_stack.top()-&gt;path(), *ec)); //... skipped } </pre><p> an directory_iterator object would be inserted into m_stack even when ec!=0, but m_level is <strong><em>not</em></strong> incremented in this case. This seems to be a bug beccause </p> <p> In another place in </p> <pre class="wiki"> void recur_dir_itr_imp::pop() </pre><p> m_level is eppected to be incremented, and asserts if not. </p> <p> The above attached patch increments m_level in increment() and solves this problem. </p> </description> <category>Ticket</category> </item> <item> <author>ttan@…</author> <pubDate>Tue, 12 Apr 2011 05:47:00 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5403#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:2</guid> <description> <p> The link in the original report doesn not work somehow. For completeness of the context, this is another link describing the context:http://lists.boost.org/boost-users/2011/03/67329.php </p> </description> <category>Ticket</category> </item> <item> <author>ttan@…</author> <pubDate>Tue, 12 Apr 2011 06:58:56 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/5403 https://svn.boost.org/trac10/ticket/5403 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">patch2.patch</span> </li> </ul> <p> this patch replaces the previous one </p> Ticket Benjamin Herr <ben@…> Sun, 01 Jul 2012 12:57:40 GMT <link>https://svn.boost.org/trac10/ticket/5403#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:3</guid> <description> <p> I just came across this bug trying to skip subdirectories that could not be accessed. In my case I received a segmentation fault on the ++ line: </p> <pre class="wiki">system::error_code ec; iter.increment(ec); if (ec) { iter.no_push(); ++iter; } </pre><p> I believe the problem is in the line the previous comment identified. Unlike the <code>ec == 0</code> case, if the <code>directory_iterator</code> contructor fails and <code>*ec</code> is set to an error code, the invalid <code>directory_iterator</code> object is still added to the stack. </p> <p> In subsequent calls to <code>increment()</code>, it will eventually be incremented by the <code>while (!m_stack.empty() &amp;&amp; ++m_stack.top() == directory_iterator())</code> loop, which is where the segfault happened. </p> <p> I believe a fix might be as simple as changing the aforementioned chunk to something like </p> <pre class="wiki">directory_iterator it(m_stack.top()-&gt;path(), *ec); if (*ec) return; m_stack.push(it); </pre> </description> <category>Ticket</category> </item> <item> <author>ttan@…</author> <pubDate>Mon, 02 Jul 2012 08:57:27 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5403#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:4</guid> <description> <p> The way to achieve skipping inaccessible directory in the client code without patching Boost is to follow this pattern: </p> <pre class="wiki"> for (recursive_directory_iterator itr(root, ec), itr_end; itr!=itr_end;/* itr++*/) { // do stuff // replace ++itr with a no-throw version and // pop this directory out to resume with the next one itr.increment(ec); if(ec) itr.pop(); } </pre><p> it works on my trials. Help it's helpful to others as well </p> </description> <category>Ticket</category> </item> <item> <author>Jesse Jarrell <jjarrell@…></author> <pubDate>Wed, 29 Aug 2012 13:02:06 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5403#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:5</guid> <description> <p> As long as the inaccessible directory is not in the first level, the itr.pop() works great. Otherwise you have problems with the level: </p> <pre class="wiki"> ***** Internal Program Error - assertion (m_level &gt; 0) failed in void boost::filesystem::detail::recur_dir_itr_imp::pop(): /usr/local/include/boost/filesystem/operations.hpp(818): pop() on recursive_directory_iterator with level &lt; 1 Aborted (core dumped) </pre> </description> <category>Ticket</category> </item> <item> <author>schmurtz_boost@…</author> <pubDate>Mon, 03 Sep 2012 01:18:21 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5403#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5403#comment:6</guid> <description> <p> Imagine we are listing "dir1", and we are at element "node2" in this listing. iter-&gt;path() is "dir1/node2". There're several reasons for recur_dir_itr_imp::increment() to fail: </p> <ul><li>at the beginning if "m_stack.top()-&gt;symlink_status(*ec)" or "m_stack.top()-&gt;status(*ec)" fails (stat or lstat on "dir1/node2" fails : we can't know if it is a directory). In this case, to continue, one need to call no_push() (using pop(), we would miss other files/folders after "node2" in "dir1"). </li><li>during the sublisting of "dir1/node2", in "m_stack.push(directory_iterator(m_stack.top()-&gt;path(),*ec))". At this time, one need to call pop() to continue, but, as said ttan, there's a bug is incrementing m_level. However, like Benjamin Herr, I think it's better not to push the bad directory_iterator: therefore, one just needs to call no_push() to continue listing. </li><li>during the listing of "dir1" after "node2", in "++m_stack.top()". Note that this will always throw, even if ec is not null. Imagine this is corrected (using "m_stack.top().increment(*ec)"), one need to call pop() to continue listing. As far as I know, failing in the middle of a directory listing is really uncommon. </li></ul><p> So, the first two cases are errors for going deeper in the hierarchy: to continue, one needs to no_push(). In the last case, error is when continuing listing at the same level: pop() is required to continue. As it's not possible to know what kind of error we have (without analysing exact error code, which may be difficult and implementation specific), I propose the following strategy: </p> <pre class="wiki">iter.increment(ec); while(ec) { path cur_path = iter-&gt;path(); iter.no_push(); // if it an error of the third case, no_push() won't help, but is harmless as we'll get exactly the same error. error_code saved_ec = ec; iter.increment(ec); if(!ec) { cerr &lt;&lt; "can't go inside " &lt;&lt; cur_path &lt;&lt; " " &lt;&lt; saved_ec; break; } else { cerr &lt;&lt; "can't go inside and/or can't list after " &lt;&lt; cur_path &lt;&lt; " " &lt;&lt; saved_ec; iter.pop(ec); } } </pre><p> Note that there's currently no iter.pop(error_code&amp;) function, just a iter.pop() that will throw in case of an error. </p> <p> Furthermore, in my understanding of the concept, pop() should work even at level 0 and return the end iterator. For me, pop() is just a way to pass all remaining filesystem nodes at the current level, and MUST NOT be followed by another increment (otherwise you'll miss one item, pop() being somehow an increment). </p> <p> I think also that the first item returned by recursive_directory_iterator("/test") should be "/test", as in all POSIX recursive commands. But this last point may be disputed ;). Note that in this case, /test is level 0 and then calling pop() at this level become again a non-sense. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Beman Dawes</dc:creator> <pubDate>Tue, 22 Jul 2014 15:51:29 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/5403#comment:7 https://svn.boost.org/trac10/ticket/5403#comment:7 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> <code>recursive_directory_iterator::increment</code> implementation code has been reorganized, adding an invariant that progress is always made even if an error is reported by exception or error_code. Thanks to Claudio Bley for his pull request, which was incorporated into the reorganized code. </p> <p> Tickets <a class="ext-link" href="https://svn.boost.org/trac/boost/ticket/5403"><span class="icon">​</span>5403</a> and <a class="ext-link" href="https://svn.boost.org/trac/boost/ticket/6821"><span class="icon">​</span>6821</a> have been closed. Please open a new issue and supply test cases if you continue to experience problems. </p> Ticket