web-dev-qa-db-ja.com

UNIX / Linuxでディレクトリへのハードリンクが許可されないのはなぜですか?

Unix/Linuxではディレクトリへのハードリンクは許可されていませんが、ソフトリンクは許可されていることを教科書で読みました。それは、サイクルがあり、ハードリンクを作成し、しばらくしてから元のファイルを削除すると、ガベージバリューをポイントするからです。

サイクルがハードリンクを許可しない唯一の理由である場合、ディレクトリへのソフトリンクが許可されるのはなぜですか?

131
user3539

ハードリンクと元の名前の違いを見分ける方法がないため、これは単に悪い考えです。

ディレクトリへのハードリンクを許可すると、ファイルシステムの有向非循環グラフ構造が壊れ、ディレクトリループとダングリングディレクトリサブツリーが作成され、fsckおよびその他のファイルツリーウォーカーエラーが発生しやすくなります。

まず、これを理解するために、iノードについて話しましょう。ファイルシステムのデータはディスク上のブロックに保持され、それらのブロックはiノードによって収集されます。 iノードはTHEファイルと考えることができます。ただし、iノードにはファイル名がありません。そこでリンクが入ります。

リンクは、iノードへの単なるポインタです。ディレクトリは、リンクを保持するiノードです。ディレクトリ内の各ファイル名は、iノードへのリンクにすぎません。 Unixでファイルを開くとリンクも作成されますが、リンクのタイプは異なります(名前付きリンクではありません)。

ハードリンクは、そのiノードを指す追加のディレクトリエントリです。 ls -l、許可の後の数は名前付きリンク数です。ほとんどの通常のファイルには1つのリンクがあります。ファイルへの新しいハードリンクを作成すると、両方のファイル名が同じiノードを指すようになります。注意:

% ls -l test
ls: test: No such file or directory
% touch test
% ls -l test
-rw-r--r--  1 danny  staff  0 Oct 13 17:58 test
% ln test test2
% ls -l test*
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
% touch test3
% ls -l test*
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
-rw-r--r--  1 danny  staff  0 Oct 13 17:59 test3
            ^
            ^ this is the link count

これで、ハードリンクなどがないことがはっきりとわかります。ハードリンクは通常の名前と同じです。上記の例では、testまたはtest2、元のファイルとハードリンクのどちらですか?最後に、両方の名前が同じ内容、同じiノードを指しているため、実際には(タイムスタンプでも)わかりません。

% ls -li test*  
14445750 -rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
14445750 -rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
14445892 -rw-r--r--  1 danny  staff  0 Oct 13 17:59 test3

-iフラグをlsに指定すると、行の先頭のiノード番号が表示されます。 testtest2は同じiノード番号を持っていますが、test3は別のものを持っています。

これで、ディレクトリに対してこれを行うことが許可された場合、ファイルシステムの異なるポイントにある2つの異なるディレクトリが同じものを指す可能性があります。実際、subdirは祖父母をポイントしてループを作成する可能性があります。

なぜこのループが問題になるのですか?トラバースしているときは、ループしていることを検出する方法がないためです(トラバース中にiノード番号を追跡しないと)。あなたがduコマンドを書いていると想像してください。このコマンドは、ディスクの使用状況を調べるためにサブディレクトリを再帰的に調べる必要があります。ループにヒットしたとき、duはどのようにしてわかりますか? duがこの単純なタスクを実行するために実行する必要があるのは、エラーが発生しやすく、多くの簿記です。

シンボリックリンクは、多くのファイルファイルシステムAPIが自動的に追跡する傾向がある特別なタイプの「ファイル」であるという点で、まったく異なる野獣です。シンボリックリンクは、iノードを直接指すのではなく、名前で指すため、存在しない宛先を指す可能性があることに注意してください。 「ハードリンク」の単なる存在がファイルの存在を意味するため、その概念はハードリンクでは意味をなさない。

それでは、なぜduがハードリンクではなくシンボリックリンクを簡単に処理できるのでしょうか。上記でハードリンクが通常のディレクトリエントリと区別できないことがわかりました。ただし、シンボリックリンクは特別で、検出可能で、スキップ可能です! duは、シンボリックリンクがシンボリックリンクであることを認識し、完全にスキップします!

% ls -l 
total 4
drwxr-xr-x  3 danny  staff  102 Oct 13 18:14 test1/
lrwxr-xr-x  1 danny  staff    5 Oct 13 18:13 test2@ -> test1
% du -ah
242M    ./test1/bigfile
242M    ./test1
4.0K    ./test2
242M    .
144
Danny Dulai

マウントポイントを除いて、各ディレクトリには親が1つだけあります:_.._。

pwdを実行する1つの方法は、device:inodeの「。」を確認することです。および「..」。同じ場合は、ファイルシステムのルートに到達しています。それ以外の場合は、親で現在のディレクトリの名前を見つけ、それをスタックにプッシュして、「../。」の比較を開始します。 「../ ..」、次に「../../。」 '../../ ..'などを使用します。ルートに到達したら、スタックから名前をポップして印刷します。このアルゴリズムは、各ディレクトリに親が1つしかないという事実に依存しています。

ディレクトリへのハードリンクが許可されている場合、複数の親のどれが_.._を指す必要がありますか?これが、ディレクトリへのハードリンクが許可されない1つの説得力のある理由です。

ディレクトリへのシンボリックリンクはその問題を引き起こしません。プログラムが希望する場合は、パス名の各部分でlstat()を実行し、シンボリックリンクが検出されたときにそれを検出できます。 pwdアルゴリズムは、ターゲットディレクトリの真の絶対パス名を返します。ターゲットディレクトリを指すテキスト(シンボリックリンク)がどこかにあるという事実は、ほとんど関係ありません。そのようなシンボリックリンクが存在しても、グラフにループは作成されません。

14
Joe Inwap

Bind mountを使用してハードリンクディレクトリをシミュレートできます

Sudo mount --bind /some/existing_real_contents /else/dummy_but_existing_directory
Sudo umount /else/dummy_but_existing_directory
13
zainengineer

この質問について、もう少しポイントを追加したいと思います。 Linuxではディレクトリへのハードリンクが許可されていますが、制限された方法で行われます。

これをテストする1つの方法は、2つの特別なディレクトリ「。」を見つけたディレクトリのコンテンツをリストするときです。そして「..」。みなさんご存じのとおり "。"同じディレクトリを指し、「..」は親ディレクトリを指します。

それでは、「a」がディレクトリ「b」を子とする親ディレクトリであるディレクトリツリーを作成してみましょう。

 a
 `-- b

ディレクトリ "a"のiノードを書き留めます。そして、ls -laディレクトリ「a」から、「。」を確認できます。ディレクトリも同じiノードを指します。

797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 a

ここで、ディレクトリ「a」に3つのハードリンクがあることがわかります。これは、iノード797358に "。"という名前のハードリンクが3つあるためです。 「a」ディレクトリ内で「..」という名前。「b」ディレクトリ内で「a」という名前のディレクトリ。

$ ls -ALi a/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 .

$ ls -ALi a/b/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 ..

したがって、ここでは、ディレクトリの親および子ディレクトリにのみ接続するためのハードリンクがあることを理解できます。したがって、子のないディレクトリには2つのハードリンクしかないので、ディレクトリ "b"には2つのハードリンクしかない。

ディレクトリのハードリンクが自由に禁止された1つの理由は、ファイルシステムを横断するプログラムを混乱させる無限参照ループを回避することです。

ファイルシステムはツリーとして編成され、ツリーは循環参照を持つことができないため、これは回避されるべきでした。

7
Kannan Mohan

以下のどれも、ディレクトリへのハードリンクを許可しない本当の理由ではありません。それぞれの問題はかなり簡単に解決できます:

  • ツリー構造の循環により、走査が困難になります
  • 複数の親、それで「本当の」親はどれですか?
  • ファイルシステムガベージコレクション

本当の理由(@ThorbjørnRavn Andersenが示唆)は、ディレクトリから複数の親を持つディレクトリを削除すると..が指す:

..は何を指す必要がありますか?

ディレクトリがその親から削除されたが、リンク数が0よりも大きい場合は、どこかにまだそれが指しているはずです。 ..が何も指さないままにすることはできません。多くのプログラムは..に依存しているため、システムはファイルシステム全体をスキャンする必要があります..を更新するためだけにディレクトリを削除しました。または、ファイルシステムは、ハードリンクされたディレクトリを指すすべてのディレクトリのリストを維持する必要があります。

いずれにせよ、これはパフォーマンスオーバーヘッドであり、ファイルシステムメタデータやコードの余分な複雑化なので、デザイナーはそれを許可しないことに決めました。

6
Lqueryvg

ディレクトリでのハードリンクの作成は元に戻せません。次のように仮定します。

/dir1
├──this.txt
├──directory
│  └──subfiles
└──etc

/dir2にハードリンクします。

したがって、/dir2には、これらすべてのファイルとディレクトリも含まれるようになりました

気が変わったらどうなりますか? rmdir /dir2だけではできません(空ではないため)。

また、/dir2で再帰的に削除すると、/dir1からも削除されます。

私見これを回避するのに十分な理由です!

編集:

コメントは、ディレクトリでrmを実行してディレクトリを削除することを推奨しています。ただし、空ではないディレクトリでのrmは失敗します。ディレクトリがハードリンクされているかどうかにかかわらず、この動作は維持される必要があります。したがって、リンクを解除するためにrmすることはできません。 rmへの新しい引数が必要になります。「ディレクトリiノードの参照カウントが1より大きい場合は、ディレクトリのリンクを解除するだけです」。

これは、次に、驚きの少ない別の原則を破ります。つまり、先ほど作成したディレクトリハードリンクの削除は、通常のファイルハードリンクの削除と同じではありません...

私は自分の文章を言い換えます。それ以上の開発がなければ、ハードリンクの作成は元に戻すことができません(現在のコマンドは現在の動作と矛盾することなく削除を処理できないため)

ケース、落とし穴の数、およびデータ損失のリスクシステムがどのように機能するかについて十分に認識していない場合、そのような開発が意味するように、より多くの開発でケースを処理できるようにすると、IMHOで十分ですディレクトリのハードリンクを制限する理由。

これは良い説明です。 「複数の親のうちどれを指し示すべきか」について。 1つの解決策は、プロセスが完全なwdパスをiノードまたは文字列として維持することです。名前を変更できるため、iノードはより堅牢になります。少なくとも昔は、開いているファイルごとにコア内のiノードがあり、ファイルが開かれるたびにインクリメントされ、ファイルが閉じられるとデクリメントされていました。それがゼロに達すると、それとそれが指すストレージが解放されます。ファイルがだれによっても開かれなくなったとき、そのファイル(コア内コピー)は破棄されます。これにより、サブディレクトリが別のプロセスのパスにあるときに、他のプロセスがディレクトリを別のディレクトリに移動した場合、パスは有効なものとして維持されます。開いているファイルを削除する方法と似ていますが、ファイルはディレクトリから削除されるだけで、ファイルを開いているすべてのプロセスに対して引き続き開かれます。

Bell Labs UNIXでは、少なくともV6とV7ではハードリンクディレクトリが自由に許可されていました。Berkeley以降については不明です。フラグは不要です。ループを作ってもらえますか?はい、それをしないでください。ループを作成すると、何をしているのかが非常に明確になります。反対側の端がバルクヘッドのフックから便利に吊り下げられている場合は、ターンが飛行機からスカイダイビングに出るのを待っている間に首の結び方を練習する必要があります。

[〜#〜] i [〜#〜]今日それでやりたいことは、lhomeをhomeにハードリンクして、/ homeがカバーされているかどうかに関係なく/ home/administを使用できるようにすることでしたホームを介したautomoutで、automountはadministという名前のシンボリックリンクを/ lhome/administに持っています。これにより、プライマリホームファイルシステムの状態に関係なく機能する管理アカウントを使用できます。これは[〜#〜] is [〜#〜] Linuxの実験ですが、自動マウントはASCII文字列レベルで行われることをUCBベースのSunOSで一度に学んだと思います。それ以外の場合、任意のFSの上のレイヤーとしてどのように実行できるかを理解するのは困難です。

私はそれを他の場所で読みました。と..もディレクトリ内のファイルではありません。これには十分な理由があり、私たちが楽しむこと(NTFSをマウントできるなど)の多くはそのような理由で可能であると確信していますが、UNIXの優雅さの一部は実装にありました。このエレガンスが提供するのは、一般性や可鍛性などの利点であり、それが非常に堅牢であり、40年間も耐えることができます。エレガントな実装を失うと、最終的にはWindowsのようになります(私が間違っているといいのですが!)。その後、誰かがエレガントな原則に基づいた新しいOSを作成します。考えること。多分私は間違っている、私は(明らかに)現在の実装に精通していない。それはisすごいですが、30年前の理解はLinuxにどれほど当てはまるのでしょう...ほとんどの場合!

1
user57607

私が収集したものから、主な理由は、作業ディレクトリを使用して他のファイルを参照する実行中のプログラムを台無しにせずにディレクトリ名を変更できると便利だということです。 Wineを使用して~/.newwineprefix/drive_c/Program Files/Firefox/Firefox.exeを実行していて、代わりにプレフィックス全体を~/.wineに移動したいとします。奇妙な理由でFirefoxがdrive_c/windowsを参照して../../windowsにアクセスしていた場合、~/.newwineprefixの名前を変更すると、親ディレクトリを追跡する代わりにテキスト文字列として追跡する..の実装が壊れます。 iノード。

単一の親ディレクトリのiノードを格納することは、すべてのパスをテキスト文字列と一連のiノードの両方として追跡するよりも簡単でなければなりません。

もう1つの理由は、誤動作しているアプリケーションがループを作成できる可能性があることです。動作中のアプリケーションは、移動先のディレクトリのiノードが、移動先のネストされたディレクトリのiノードと同じであるかどうかを確認できる必要があります。これは、ディレクトリをそれ自体に移動できないのと同じですが、強制されない場合があります。ファイルシステムレベルで。

さらに別の理由として、ディレクトリをハードリンクできる場合、変更できなかったディレクトリをハードリンクしないようにする必要があります。 findは、他のユーザーが作成したファイルを一時ディレクトリからクリアするために使用されるため、セキュリティ上の考慮事項があります。これは、findが別のコマンドを呼び出している間にユーザーがシンボリックリンクの実際のディレクトリを切り替えると問題を引き起こす可能性があります。重要なディレクトリをハードリンクできると、管理者はfindにテストを追加して、それらに影響を与えないようにする必要があります。 (さて、あなたはすでにファイルに対してこれを行うことができないので、この理由は無効です。)

さらに別の理由は、親ディレクトリのiノードを保存すると、ファイルシステムが破損または損傷した場合に、余分な冗長性が提供される可能性があることです。 ..がこのディレクトリにハードリンクするすべての親ディレクトリを一覧表示するようにしたい場合、現在のディレクトリがリンク解除された場合、ハードリンクが等しいという考えに違反するだけでなく、別の任意の親を簡単に見つけることができます。ファイルシステムがiノードを格納および使用する方法を変更する必要があります。プログラムがパスを一連の(各ハードリンクに固有の)ディレクトリiノードとして扱うようにすると、これを回避できますが、ファイルシステムが損傷した場合に冗長性を得ることができません。

0
Misaki