MAP | TOP > 工房「藤車」 > 技術系執筆情報 > 『lsを読まずにプログラマを名乗るな!』 > GNU coreutils へのバグ報告 |
本ページでは、 拙著『lsを読まずにプログラマを名乗るな!』 の執筆過程で見つけた GNU coreutils のバグに関して、 バグの詳細と、 バグ報告の顛末について説明します。
GNU coreutils のトップページには、 バグ報告用メーリングリスト (bug-coreutils@gnu.org) への投函には、 ユーザ登録等が必要ない旨の記載があります。
You do not need to be subscribed in order to post messages to any GNU mailing list.
〜 from "Mailing Lists"
その一方で、 ユーザ登録無しでメーリングリストにメール投函した場合は、 人手による投稿のチェックが発生するため、 メーリングリストによる配信にはタイムラグが発生する、 という旨の記述もあります。
However non-subscribers are moderated by humans so please be patient when waiting for your email to arrive.
〜 from "Mailing Lists"
『たまたま見つけたバグ報告のためにユーザ登録』 するのが面倒であることを理解できる一方で、 『非登録ユーザからの投函を無条件で受理』する事が、 容易にスパムメールの踏み台にされててしまうという事情も理解できます。 ですので、 『人手による判定』という手順を挟むのは、 妥当な落としどころと言えるでしょう。
It has been necessary to moderate the Coreutils mailing lists to prevent the flood of spam. Postings to the lists are held for release by the list moderator.
〜 from "Mailing Lists"
しかし、 『予想に反して』なのか、 『心配した通り』なのか表現が悩ましいところですが(笑)、 未登録ユーザからのメール投函に対する『人手による判定』は、 bug-coreutils@gnu.org においては機能していないと言って良いでしょう。
私自身、 メーリングリストへのユーザ登録前に、 バグ報告をメール投函してみたのですが、 結局一か月程度待っても、 メールが受理される様子がありませんでした。
最終的には、 バグ報告用メーリングリストにユーザ登録した上で、 バグ報告メールを投函したのですが、 その場合も、 『ユーザ登録直後は投函したメールが受理されない』 ように思われました。
登録から一か月ほど経過してから、 再度投函したメールは受理されましたので、 登録から一定時間経過しないユーザからの投函は、 破棄されるか、 あるいは『人手による判定』+『判定作業の放棄』によって、 実質的に破棄されている可能性があります。
bug-coreutils@gnu.org へのバグ報告の際には、 『メーリングリストへのユーザ登録』+ 『登録後しばらくしてからの投函』をお勧めします。
以下で説明するバグの詳細は、 本書の『第2章 ディレクトリ要素の表示』および 『第4章 ディレクトリ要素の再帰的な表示』 における説明を理解していることを前提とします。
print_dir()
での
gobble_file()
呼び出し (2593行) では、
command_line_arg
引数には常に false
が指定されます。
2571| next = readdir (dirp);
〜〜〜〜
2593| total_blocks += gobble_file (next->d_name, type,
2594| RELIABLE_D_INO (next),
2595| false, name);
これは、
gobble_file()
呼び出しの対象が、
readdir(3)
によって得られた
『指定ディレクトリ直下の要素』であることから、
これらが『コマンドラインで指定されたもの』
ではないことが自明であるためです。
その一方で、
再帰的な表示 (-R
/--recursive
オプション指定時)
を行う際の、
print_dir()
での
extract_dirs_from_files()
呼び出し (2641行)
における command_line_arg
引数には、
print_dir()
呼び出しにおける
command_line_arg
引数が使用されます。
2640| if (recursive)
2641| extract_dirs_from_files (name, command_line_arg);
extract_dirs_from_files()
呼び出しにおける
command_line_arg
引数は、
queue_directory()
呼び出しによって、
単方向リスト pending_dirs
の要素に記録されます。
2640| if (recursive) 2641| extract_dirs_from_files (name, command_line_arg);→3239|static void 3240|extract_dirs_from_files (char const *dirname, | bool command_line_arg) 3241|{ 〜〜〜〜 3264| if (!dirname || f->name[0] == '/') 3265| queue_directory (f->name, f->linkname, | command_line_arg); 3266| else 3267| { 〜〜〜〜 3269| queue_directory (name, f->linkname, | command_line_arg);→2486|static void 2487|queue_directory (char const *name, char const *realname, | bool command_line_arg) 2488|{ 2489| struct pending *new = xmalloc (sizeof *new); 〜〜〜〜 2492| new->command_line_arg = command_line_arg; 2493| new->next = pending_dirs; 2494| pending_dirs = new; 2495|}ソースコード: 1-3ここで記録された
command_line_arg
引数値は、 回り回って、main()
におけるprint_dir()
呼び出しの際のcommand_line_arg
引数値として現れます。1421| while (pending_dirs) 1422| { 1423| thispend = pending_dirs; 〜〜〜〜 1444| print_dir (thispend->name, thispend->realname, 1445| thispend->command_line_arg);
ソースコード: 1-4さて、
main()
でのextract_dirs_from_files()
呼び出しでは、 対象が『コマンドラインで指定されたもの』ですから、command_line_arg
引数には常にtrue
が指定されます。1400| if (cwd_n_used) 1401| { 1402| sort_files (); 1403| if (!immediate_dirs) 1404| extract_dirs_from_files (NULL, true);
ソースコード: 1-5
main()
におけるextract_dirs_from_files()
呼び出しで、command_line_arg
引数がtrue
であることと、 これまで説明した処理の流れを組み合わせると、 以下のような呼び出しの連鎖において、 『コマンドラインで指定されたもの』 ではない要素に対しても、command_line_arg
がtrue
になってしまうことがわかります。
- 『コマンドラインで指定されたもの』に対する
extract_dirs_from_files()
呼び出し (@main()
)- 『コマンドラインで指定されたもの』に対する
queue_directory()
呼び出し (@extract_dirs_from_files()
)- 『コマンドラインで指定されたもの』に対する 単方向リスト
pending_dirs
要素の記録 (@queue_directory()
)- 単方向リスト
pending_dirs
要素を使ったprint_dir()
呼び出し (@main()
)readdir(3)
で得られた要素に対するextract_dirs_from_files()
呼び出し (@print_dir()
)readdir(3)
で得られた要素に対するqueue_directory()
呼び出し (@extract_dirs_from_files()
)readdir(3)
で得られた要素に対する 単方向リストpending_dirs
要素の記録 (@queue_directory()
)さて、 間違った
command_line_arg
引数値でのextract_dirs_from_files()
呼び出しは、 どのような影響を及ぼすのでしょうか?
print_dir()
呼び出しにおけるcommand_line_arg
引数は、opendir(3)
やreaddir(3)
、closedir(3)
等がエラー終了した場合の、file_failure()
呼び出しにおけるserious
引数としても使用されます。2502|static void 2503|print_dir (char const *name, char const *realname, | bool command_line_arg) 2504|{ 〜〜〜〜 2511| dirp = opendir (name); 2512| if (!dirp) 2513| { 2514| file_failure (command_line_arg, | _("cannot open directory %s"), name);→2469|static void 2470|file_failure (bool serious, char const *message, | char const *file) 2471|{ 2472| error (0, errno, message, quotearg_colon (file)); 2473| set_exit_status (serious);→2456|static void 2457|set_exit_status (bool serious) 2458|{ 2459| if (serious) 2460| exit_status = LS_FAILURE; 2461| else if (exit_status == EXIT_SUCCESS) 2462| exit_status = LS_MINOR_PROBLEM; 2463|}ソースコード: 1-6本来
false
であるべきcommand_line_arg
引数値がtrue
になっていることで、file_failure()
のserious
引数もtrue
になってしまいます。 その結果、 本来であれば終了コード 1 (=LS_MINOR_PROBLEM
) で終了すべきところが、 終了コード 2 (=LS_FAILURE
) で終了することになります。764|/* Exit statuses. */ 765|enum 766| { 767| /* "ls" had a minor problem. E.g., while processing a directory, 768| ls obtained the name of an entry via readdir, yet was later 769| unable to stat that name. This happens when listing a directory 770| in which entries are actively being removed or renamed. */ 771| LS_MINOR_PROBLEM = 1, 772| 773| /* "ls" had more serious trouble (e.g., memory exhausted, invalid 774| option or failure to stat a command line argument. */ 775| LS_FAILURE = 2 776| };ソースコード: 1-7
print_dir()
でのextract_dirs_from_files()
呼び出しにおけるcommand_line_arg
引数には、 明示的にfalse
を指定すべきですから、 以下の修正が妥当でしょう。diff --git a/src/ls.c b/src/ls.c index e341c67..08e86ce 100644 --- a/src/ls.c +++ b/src/ls.c @@ -2647,7 +2647,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg) contents listed rather than being mentioned here as files. */ if (recursive) - extract_dirs_from_files (name, command_line_arg); + extract_dirs_from_files (name, false); if (format == long_format || print_block_size) {修正案報告顛末
このバグは、 バグID 15249 として登録されています。
保守担当者からの、 『妥当な修正に見えるので、詳細確認後、修正内容を反映する』旨の返信の後、 私名義の修正が、 保守担当者によってコミットされた模様です。
patch コマンドのバージョン間互換性 (ID:15255)
問題の詳細/修正内容
Git 経由で入手したソースコードに対する
./bootstrap
スクリプトを実行した場合、 バージョン 2.6 よりも古い patch コマンドを使用している環境で、 以下の様なエラーが発生してしまいます。gnulib/gnulib-tool: *** patch file gl/modules/tempname.diff didn't apply cleanly gnulib/gnulib-tool: *** Stop. missing header for unified diff at line 12 of patch The text leading up to this was: -------------------------- | | Files: | lib/tempname.c -------------------------- File to patch: EOF ※ Ctrl-D による『EOF』入力 Skip this patch? [y] ※ Enter 入力 1 out of 1 hunk ignored gnulib/gnulib-tool: *** patch file gl/modules/tempname.diff didn't apply cleanly gnulib/gnulib-tool: *** Stop. ./bootstrap[348]: build-aux/prefix-gnulib-mk: not found [No such file or directory] ./bootstrap: bootstrap_post_import_hook failed./bootstrap
実行時のエラー私の確認した範囲では、 2.6 以降の patch コマンドを使用した場合、 上記のエラーは発生しません。
bootstrap
コマンドの実行可否が、patch
コマンドのバージョンに、 明らかに依存していますので、 他のユーティリティコマンドと同様に、 事前にバージョン確認を実施すべきと思われます。
bootstrap
コマンド実行時における、 各種コマンドの利用可否/バージョン確認は、 設定ファイルbootstrap.conf
で記述されていますので、 以下の様な修正が妥当と思われます。diff --git a/bootstrap.conf b/bootstrap.conf index 0863590..2535b20 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -324,7 +324,7 @@ git 1.4.4 gperf - gzip - makeinfo 4.13 -patch - +patch 2.6 perl 5.5 rsync - tar -修正案報告顛末
このバグは、 バグID 15255 として登録されています。
保守担当者によると、 『パッチファイルを修正することで、2.6 より前の
patch
コマンドにも対応可能』 とのことで、 バグ報告の際に提案した『patch
コマンドのバージョン確認の実施』ではなく、 パッチファイル自体の修正により、patch
コマンドバージョン間での非互換を回避する方向で、 対処するものと思われます。 具体的な対処方法に関しては、 ビルド時の留意点における 『patch
コマンドのバージョン依存性』を参照してください。なお、 保守担当者からの返信にも書かれているように、 実はこの障害は、 『2.6 より前の
patch
コマンドでパッチ適用が失敗』する問題ではなく、 『2.6 以降のpatch
コマンドは、適用できないハンクがあるのに正常終了してしまう』 という問題だった模様です。保守担当者による、
patch
コマンドのメーリングリストへの 『2.6 以降のpatch
コマンドの問題なのか?』 というメール投函がありましたが、 最終的には以下の様な結論に落ち着いた模様です。
patch
コマンド側での不正ハンク対応は簡単だが、 挙動変更による既存資産への影響が読めないので、修正は避けたい- そもそもの問題は、元のパッチファイルの内容の不正
- (おそらく)不正なパッチは、手動編集が施されていると思われる
- 正規の
diff
コマンド出力における問題ではないので、patch
コマンド側は修正しない
MAP TOP > 工房「藤車」 > 技術系執筆情報 > 『lsを読まずにプログラマを名乗るな!』 > GNU coreutils へのバグ報告 Copyright (C) 2013 FUJIWARA Katsunori - All Rights Reserved./ foozy@lares.dti.ne.jp