私は継承したいくつかのコードを持っています。これは反復するためのクラスの一部であり、ディレクトリの内容にアクセスし、boost::filesystem::path を使用します。コードの一部は次のとおりです。
struct directory_iterator_impl
{
private:
void skip_dots(const path& p, const char* who)
{
static const std::string d(".");
static const std::string dd("..");
ASSERT( m_current_entry != 0 );
while ( m_current_entry != 0 &&
(m_current_entry->d_name == d ||
m_current_entry->d_name == dd) ) {
read(p, who);
}
}
void handle_read_error(const path& p, const char* who)
{
ASSERT( errno != EBADF );
ASSERT( errno != ENOENT );
switch ( errno ) {
case EOVERFLOW:
case ESTALE:
throw_error(who,
error_code_impl::expected_unix_error(errno), p);
break;
default:
base::utilities::abort_with_unknown_error();
throw_error(who,
error_code_impl::unexpected_unix_error(errno), p);
}
}
void read(const path& p, const char* who)
{
errno = 0;
m_current_entry = readdir(m_directory_stream);
if ( errno != 0 ) {
handle_read_error(p, who);
}
}
void sync_current()
{
ASSERT( m_current_entry != 0 );
m_current_name = m_current_entry->d_name;
}
void handle_open_error(const path& p, const char* who)
{
ASSERT( errno != EFAULT );
switch ( errno ) {
case EACCES:
case ELOOP:
case ENAMETOOLONG:
case ENOENT:
case ENOTDIR:
case EMFILE:
case ENFILE:
case ENOMEM:
case ESTALE:
throw_error(who,
error_code_impl::expected_unix_error(errno), p);
break;
default:
base::utilities::abort_with_unknown_error();
throw_error(who,
error_code_impl::unexpected_unix_error(errno), p);
}
}
public:
directory_iterator_impl(const path& p, const char* w)
{
m_directory_stream = opendir(p.as_string().c_str());
if (m_directory_stream == 0) {
handle_open_error(p, w);
}
unsafe_increment(p, w);
}
~directory_iterator_impl()
{
do {
// coverity[double_free]
if ( (m_directory_stream != 0) &&
(closedir(m_directory_stream)==0) ) {
return;
}
} while (errno == EINTR);
ASSERT( errno != EBADF );
base::utilities::abort_with_unknown_error();
}
void unsafe_increment(const path& p, const char* who)
{
read(p, who);
if ( !no_entries() ) {
skip_dots(p, who);
}
if ( !no_entries() ) {
sync_current();
}
}
void increment(const char* w)
{
ASSERT( !no_entries() );
unsafe_increment("", w);
}
const std::string& dereference() const
{
return m_current_name;
}
bool no_entries() const
{
return m_current_entry == 0;
}
private:
DIR* m_directory_stream;
struct dirent* m_current_entry;
std::string m_current_name;
};
私が遭遇した問題は、handle_read_error() で errno == EBADF の再現不可能な ASSERT です。コードを調べると、コンストラクターで m_directory_stream が設定されており、他に何も触れていないように見えます。NULL ではないか、コンストラクターが handle_open_error() を呼び出し、このケースは発生しませんでした。したがって、構築時点では、m_directory_stream は有効であり、開いたときにエラーは発生しませんでした。ただし、しばらくして unsafe_increment() が呼び出され、おそらくコンストラクターの直後に呼び出され、この時点で DIR オブジェクトは EBADF になり、アサーションの失敗がスタックに記録されます。
障害が検出されたマシンに関する情報は次のとおりです (アプリケーションはシングル スレッドであることに注意してください)。
Linux 2.6.9-67.ELsmp x86_64
OS: RedHat Enterprise Linux 4.0 U6
CPU: 8 x 3000 MHz, Intel(R)Xeon(R) (2 socket, quad core, No HT)
Memory: 16 GB RAM, 33 GB Swap
ファイル記述子を保持している間に、どのようにしてファイル記述子が不良になる可能性がありますか? これは、dirent.h インターフェイスの既知の問題ですか?
このコードを改善し、この再現不可能な問題を回避する方法を提案してください。