通常のファイル名は 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 コマンドは武藤さんに教わりました。 最後になりましたがここでお礼を申し上げます。ありがとうございます。