| 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