readdir()のエラーハンドリングを追加

rails/bootsnap

bs_rb_scan_dir()readdir()のエラーハンドリングが追加され、ディレクトリスキャン中のシステムエラーを適切に検出できるようになりました。これにより、ストリーム終端とエラーの区別が明確になり、予期しないエラーの見落としを防げます。

背景

readdir()NULLを返す場合に2つのケースがあります。ストリーム終端(正常終了)とエラー発生時です。エラー時にはerrnoが設定されますが、従来の実装ではこの区別を行っていませんでした。

POSIXの仕様では、readdir()がエラーでNULLを返す際にerrnoが設定されますが、正常なストリーム終端ではerrnoが変更されません。そのため、ループ終了時にerrnoをチェックすることで、異常終了を検出できます。ただし、readdir()呼び出し前に他の処理でerrnoが設定されている可能性があるため、呼び出し直前にerrnoをリセットする必要があります。

技術的な変更

whileループが無限ループ形式に書き換えられ、readdir()呼び出し直前にerrnoを0にリセットする処理が追加されました。

変更前:

errno = 0;
while ((entry = readdir(dirp))) {
    if (entry->d_name[0] == '.') continue;
    // ...
}

if (closedir(dirp)) {
    bs_syserr_fail_path("closedir", errno, abspath);
    return Qundef;
}

変更後:

while (1) {
    errno = 0;

    entry = readdir(dirp);
    if (entry == NULL) break;

    if (entry->d_name[0] == '.') continue;
    // ...
}

if (errno) {
  int err = errno;
  closedir(dirp);
  bs_syserr_fail_path("readdir", err, abspath);
} else if (closedir(dirp)) {
    bs_syserr_fail_path("closedir", errno, abspath);
    return Qundef;
}

entry == NULLでループを抜けた後、errnoの値を検査します。errnoが非ゼロの場合はreaddir()がエラーで終了したと判断し、bs_syserr_fail_path()でエラーを報告します。errnoがゼロの場合は正常なストリーム終端として、closedir()のエラーチェックのみを実行します。

fstatat()のエラーハンドリングも簡素化され、壊れたシンボリックリンク(ENOENT)の場合のコメントがインライン化されました。errnoの明示的なリセットは不要になり、コードの見通しが改善されています。

設計判断

while(1)breakによる無限ループ形式が採用されました。

この形式により、readdir()呼び出し直前にerrno = 0を配置できます。while ((entry = readdir(dirp)))形式では条件式内でreaddir()が評価されるため、その直前にerrnoをリセットする位置がありません。無限ループ+明示的なbreakにすることで、errnoのリセット、readdir()の呼び出し、NULLチェックを明確に分離しています。

エラー検出後はerrnoの値を一時変数errに退避してからclosedir()を呼び出す実装になっています。closedir()自体が失敗する可能性があるため、readdir()のエラー情報を失わないようにする配慮です。

本PRは、システムコールのエラーハンドリングの標準的なパターンを適用し、見落とされていたエラーケースを補完する変更です。errnoの初期化タイミングを制御できる制御構造に変更することで、POSIXのreaddir()仕様に完全に準拠した実装になりました。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)、背景、技術詳細、設計判断(各論)、まとめ(結論)という「総論→各論→結論」の構成が明確です。各セクションが適切な役割を果たしており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライトの形式(```c:ext/bootsnap/bootsnap.c)およびPR番号のリンク記法([PR #530](URL))は正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

C言語のシステムコールや`errno`に関する知識を持つエンジニアを対象としており、専門レベルが適切です。冗長な初心者向けの説明がなく、簡潔にまとめられています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクション、各パラグラフが「総論→各論」の構造で書かれ、トピックセンテンスが段落の冒頭に配置されています。1段落1トピックの原則も守られており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

変更前後のコード引用はDiffの内容を正確に反映しています。`while`ループの変更だけでなく、`fstatat`に関する簡素化にも言及しており、Diffの変更点を網羅的に捉えています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`readdir()`, `errno`, `POSIX`, `fstatat()`など、システムプログラミングに関する技術用語が正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

`readdir()`のエラーハンドリングの仕組みや、`errno`をリセットするタイミングの重要性についての説明は、技術的に正確で論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのタイトル、Description、Diffの内容によって裏付けられており、ハルシネーション(捏造)は見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#530)や関数名(`bs_rb_scan_dir`)、ファイルパスなどの固有名詞はすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「readdir()のエラーハンドリングを追加」は、PRの主題「Handle readdir errors in bs_rb_scan_dir()」を的確に要約しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事に含まれるPOSIX仕様に関する説明は、PRの変更内容を理解するための適切な文脈情報であり、PRに記載のない外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現に歪曲はなく、PRで行われた変更を事実として正確に記述しています。