ソースコード・リテラシーのススメ

第22回 ソースコード・リテラシー【実践編】

この記事を読むのに必要な時間:およそ 6.5 分

リスト4 src/policy.cのfind_sysfs_device関数

 229  snprintf( devfilename, sizeof( devfilename ), "%s/device", devdirname );
 230 
 231  /* read out the link */
 232  if( !sysfs_get_link( devfilename, linkfilename, 1024 ) )
 233    sysdev = sysfs_open_device_path( linkfilename );
 234 

このsysfs_get_link()sysfs_open_device_path()という関数はpmountの中にはなく,sysfsutilsパッケージが提供するlibsysfsの中にあるようです。

% nm /usr/lib/libsysfs.so.2.0.0 | grep sysfs_get_link
00001f70 T sysfs_get_link
% nm /usr/lib/libsysfs.so.2.0.0 | grep sysfs_open_device_path
00005450 T sysfs_open_device_path

必要なライブラリが正しくリンクされていないのかな,とlddコマンドでリンクされるライブラリを調べても,libsysfs.so.2はきちんとリンクされています。

% ldd work/usr/bin/pmount
  linux-gate.so.1 =>  (0xffffe000)
  libsysfs.so.2 => /usr/lib/libsysfs.so.2 (0xb7f58000)
  libblkid.so.1 => /lib/libblkid.so.1 (0xb7f4f000)
  libc.so.6 => /lib/libc.so.6 (0xb7e21000)
  libuuid.so.1 => /lib/libuuid.so.1 (0xb7e1d000)
  /lib/ld-linux.so.2 (0xb7f89000)

ライブラリの呼び出し方でも変更されたのかなぁ,とsysfsutilsを調べてみると,2.1.0というバージョンがリリースされていましたが,libsysfs を新しいバージョンに入れ替えてもpmountの動作には変化なしでした。

ソースコードを調べるのも手詰りになったので,さてどうしたものか,pmountlibsysfsをキーワードに Google で検索してみると,gentooのbugzillaで同じ問題が議論されていることを見つけました。

このページによると,libsysfs はバージョンごとに変るカーネルの sysfs 機能に対応できていないので,使うべきではないこと(カーネルソースに含まれるDocumentation/sysfs-rules.txtにも "Do not use libsysfs" という記述がありました),カーネルのビルド時にCONFIG_SYSFS_DEPRECATEDを定義すればlibsysfsが想定している旧来の形式と互換性を保つようになること,Debian方面でlibsysfsへのパッチが公開されていることなどがわかりました。

こうなると,選択肢としては,⁠libsysfsとpmountは使わない⁠⁠,⁠カーネルのビルド時にCONFIG_SYSFS_DEPRECATEDオプションを指定した上でオリジナルのlibsysfsとpmountを使う⁠⁠,⁠libsysfsにパッチをあててpmountを使う」という3つがありそうです。

いずれの方法もメリット,デメリットがありますが,今回は「libsysfsにパッチをあててpmountを使う」方法を取ることにし,パッチをあてた libsysfs を作成,その元では pmount が正しく動作することを確認しました。

% ./work/usr/bin/pmount /dev/sdb1                    
Error: device /dev/sdb1 is not removable                                    
% su                                                 
パスワード: XXXXX
# updatepkg sysfsutils-2.1.0_1-i586-P1.tgz 
removing sysfsutils-2.l.0                          

Removing package sysfsutils...
Removing files:               
  --> Deleting symlink usr/lib/libsysfs.so
...
sysfsutils-2.1.0_1-i586-P1 のインストール中
PACKAGE DESCRIPTION:
sysfsutils-2.1.0_1-i586-P1 のインストールスクリプトを実行中

# exit
% ./work/usr/bin/pmount /dev/sdb1
% df
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/hda3             19236340  16605608   1653580  91% /
none                   1037060       168   1036892   1% /dev
/media                 1037060         0   1037060   0% /media
...
/dev/sdb1               942864    885448     57416  94% /media/sdb1

このデバイスはpumountで正しくアンマウントされ,pmountが生成したマウントポイント(/media/sdb1)も消去されました。

冗長になるので問題の背景は省きましたが,Plamo-4.51で採用しようとしている新しい(2.6.27系)カーネルでは,リムーバブルメディアではない通常のHDDもudevがリムーバブルメディアのように処理しようとしてしまう問題が報告され,その対応のためにリムーバブルメディアの操作にはpmountを使うつもりでpmountをパッケージ化しました。その際,udevの新しいバージョンも調べましたが,udevのドキュメントには「CONFIG_SYSFS_DEPRECATED は指定すべきではない」とありました。それらを勘案して,libsysfsにパッチをあてる方法を採用したのですが,あとから見直してみると,pmountを使わなくてもudevルールを改良すればリムーバブルメディアを見分けることができそうなので,libsysfsとpmountを捨てるのも手だったかな……と思っているところです。

今回は比較的規模の小さいソフトウェアを例に,Cのソースコードを辿りながら,問題点を調べていく例を紹介しました。今回取りあげたpmountは,ソースコードのコメントも豊富で,デバッグメッセージも詳細だったので,比較的スムーズに原因の特定や対応方法を見い出すことができました。このあたりが不十分なソフトウェアでは,ソースコードの重要そうなポイントにprintf文を追加して進行状況を確認したり,デバッガでステップトレースしながら変数の値を確認するような作業が必要になるでしょう。

Cで書かれたソースコードの場合,構成しているファイルが膨大な数に昇り,それら全てを調べることは不可能なことも多いでしょう。そのような大規模なソフトウェアでは,出力メッセージを手がかりに問題となっているファイルや行を調べ(出力メッセージが不十分ならばソースコードレベルでデバッグメッセージを追加して⁠⁠,そこから処理の流れを遡っていくのが,一見遠回りながら,最も効率的な方法だと思っています。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたのが,いつの間にかミイラ取りがミイラになってOSSを仕事にするようになってしまいました。最近はスペシャリスト養成を目的とした専門職大学院で教壇に立ったりもしています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html