linkの先には何がある (2007/02/21)


symlinks

通常のファイル名は inode へのリンクです。 リンク先のinodeは同じボリュームに存在していなければなりません。

私の記憶が確かならば、ファイル名へのリンクというのが発明されたのが BSD でした。普通のファイル名はリンク先にinodeが書いてあるわけですが、 そこにファイル名を書けるようにしたわけです。 ファイル名ですから、無いファイルでも構いません。

yuji@tern:~%ln -s /etc/nowhere hoge
yuji@tern:~%ls -l hoge
lrwxrwxrwx 1 yuji yuji 12 Feb 21 13:05 hoge -> /etc/nowhere
yuji@tern:~%cat hoge
cat: hoge: No such file or directory
  

たとえばこんな感じです。 無くてもいいくらいですから、デバイスを跨いだリンクだろうが何だろうが、 お構い無しです。 これがおなじみの symbolic link というものです。 なかなか便利です。

しかし、これができるようになると、 普通はファイル名→inode で一発終了していたデータ探索の旅が、 終らないおそれがでてきます。リンクじゃないファイルを探してどこまでも ファイルシステムをさ迷う事になりかねません。

yuji@tern:~%ls -l link*
lrwxrwxrwx 1 yuji yuji 5 Feb 21 13:13 link0 -> link1
lrwxrwxrwx 1 yuji yuji 5 Feb 21 13:13 link1 -> link0
yuji@tern:~%cat link0
cat: link0: Too many levels of symbolic links

どれくらい辿ってくれるものなんでしょうか? そのあたりはこの記事が参考になります。 要約すると、かつては5段でしたが、 symblic link を辿る処理を vfs に出したので、 8もしくはそれ以上いけるようになった。とのこと。 ただし、ファイルの開閉をOSにやってもらうのではなく、 いちいち自前でリンクを辿って行くならば、 この限りではありません。 とはいっても、symbolic link の用途を考えると、 そのようなOSの制限を超えた際限なきlinkの旅の終点にデータがある、 というような状態は避けたいところです。

さて、 ls などのツールでリンクの行き先を見ることができますが、 今や8段まで組めるようになったsymblikの行き先が、 本当はどこにあるのか?というところは誰しも興味があるところではないでしょうか?

際限なく ls -l で追いかけていくのも、私の場合、それを我慢できるのは、 2段を2回までです。 一方、サーバをいじくりまわしているとリンクのリンクのリンクのリンク ができちゃったりする場合もたまにありますし、 Debian では /etc/alternatives/ を経由して、 深くリンクを辿る場合がけっこうあります。ですから、 何かそういう便利なツールがあるはずです。

まずsymlinkの洞窟を作成しましょう。

yuji@tern:~%ls -l link*
lrwxrwxrwx 1 yuji yuji 5 Feb 21 13:47 link1 -> link0
lrwxrwxrwx 1 yuji yuji 5 Feb 21 13:47 link10 -> link9
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link100 -> link99
lrwxrwxrwx 1 yuji yuji 7 Feb 21 13:47 link101 -> link100
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link11 -> link10
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link12 -> link11
   (中略)
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link96 -> link95
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link97 -> link96
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link98 -> link97
lrwxrwxrwx 1 yuji yuji 6 Feb 21 13:47 link99 -> link98

こういう状態にしておきます。まずは、chase コマンドを試します。

chase --verbose link100
link100
-> link99
-> link98
-> link97
-> link96
-> link95
-> link94
-> link93
-> link92
-> link91
-> link90
-> link89
-> link88
-> link87
-> link86
-> link85
-> link84
-> link83
-> link82
-> link81
-> link80
-> link79
-> link78
-> link77
-> link76
-> link75
-> link74
-> link73
-> link72
-> link71
chase: /home/yuji/link71: quite many symlink hops, hope we're not looping...
-> link70
-> link69
-------------------(中略)-------------------
-> link4
-> link3
-> link2
-> link1
-> link0
/home/yuji/link0

文句言いつつも最後までたどり着きました。さすがです。

emacs lisp には file-chase-links という関数があります。 こいつを試してみましょう。

(file-chase-links "/home/yuji/link100")
"/home/yuji/link0"

こちらもさすがです。でも、ちょっとソッケないような。 ところで、link100 にアクセスしようとしても、OSのシステムコールを使う分には 当然、うまくいきません。

yuji@tern:~%cat link8
hoge
yuji@tern:~%cat link9
cat: link9: Too many levels of symbolic links

ですから、chase や file-chase-links は 自前でリンクを辿っているということが判りますね。 chase コマンドには上で見たように --verbose という、 途中停車駅を全部表示してくれるオプションがあります。 emacs lisp でも途中停車駅がリストになってたらいいのに、 と思いましたが、そういうものは、ちょっと見た範囲では無いようです。 簡単に作れるからでしょうか。 とりあえず car すると行き先が取れるものを。

(defun file-follow-links (path)
  (if (file-symlink-p path)
      (append (file-follow-links (file-symlink-p path)) (list path))
    (list path)))

実行結果はこんな感じです。

(file-follow-links "/usr/bin/xemacs")
("xemacs-21.4.19-mule-canna-wnn"
   "/usr/bin/xemacs21-mule-canna-wnn"
   "/etc/alternatives/xemacs21"
   "/usr/bin/xemacs21"
   "/etc/alternatives/xemacs"
   "/usr/bin/xemacs")

なお、文中に登場する chase コマンドは武藤さんに教わりました。 最後になりましたがここでお礼を申し上げます。ありがとうございます。


記事リストへ