Windowsのジャンクションとシンボリックリンクの違い

Windows 7をインストールしていてホームディレクトリをC:\Users\Adsariaから別の場所へ移したいと思って色々とやってみたのだが、やっぱりActive Directoryが無い環境だとプロファイルやホームディレクトリを簡単には変更できないようだ。ユーザのプロパティでホームディレクトリを別のパスに指定しても殆ど意味がない。

また、Windowsの場合、絶対パスに依存したり、環境変数を見ていないアプリケーションもチラホラあるので安易にパスを変更したくない。Windowsでは、アプリケーションのインストールなどもできる限りデフォルトのフォルダを使うのが安定して動かす心得の一つと思っている。

そこで色々と調べていくと“シンボリック リンク”というものがあるのが分かった。恥ずかしながら今まで知らなかった。これまで“あぁ、ショートカットのことを偉そうに言っているのだろう”と思ってロクに調べていなかった。WindowsはNTの頃から“ショートカット”を“(シンボリック)リンク”と称していたので。ショートカットは昔からあるUNIXのハードリンクやシンボリックリンクに比べるとオモチャみたいな機能だった。

ところがWindowsにはショートカットはと別に“シンボリック リンク”も2000のころからあったようだ。これを使えばボリュームをまたいで任意の場所にフォルダを移動できる。ただし、昔は特殊なコマンドをインストールしないと使えなかったらしい。そのため一般ユーザには余り知られていなかったのかも知れない。しかし、Vistaのころからシンボリック リンクを作成する“mklink”というコマンドが標準で用意されている。

さて、ここまでは良かったのだが、シンボリックリンクに似た機能で(シンボリックリンクよりも以前からあった)ジャンクションという機能もあることが分かった。両方ともNTFSの“リパース・ポイント”という機能を利用して実装しているらしい。
http://www.atmarkit.co.jp/fwin2k/win2ktips/991fjunction/fjunction.html
ここでジャンクションとシンボリックリンクのどちらを使えばいいのか?という疑問が出てくる。つまり、どう違うのか?ということ。機能的には殆ど同じなので、後から登場したシンボリックリンクを使う方がいいのかなぁ、などと漫然と思っていたが、調べていくとネットワーク環境で機能に違いが出てくることがわかった。
http://technet.microsoft.com/ja-jp/magazine/2007.02.vistakernel.aspx
ところが、このマイクロソフトのドキュメントが物凄く分かりにくい。(しかもシンボリックリンクの説明のある行の途中から唐突に“MMCSS”の説明に変わっているので???って感じだ。)

ドキュメントを読んで何となく分かったのは、ジャンクションはジャンクションが存在する側のマシンのWindowsが解釈する。一方、シンボリックリンクはアプリケーション側のマシンのOSが解釈する、ということ。

そこで実験をしてみた。まず、2台のWindows 7マシンを用意する。1台はファイルサーバ役で、ここにジャンクションやシンボリックリンクを作っておく。もう1台はサーバを参照するクライアント側となる。

ファイルサーバ側の設定はこんな感じにしておいた:

  • “C:\Shared”と“C:\Local”というフォルダを作成する。
  • “C:\Local”の下に“C:\Local\Folder”というフォルダを作成する。
  • “C:\Local\Folder”フォルダにFile.txtとうファイルを作っておく。
  • “C:\Shared”フォルダには“C:\Local\Folder”フォルダを指す次の3種類のリンクファイルを作成する。
  • “C:\Shared”は共有設定で他のマシンから参照できるようにする

まず“C:\Shared”と“C:\Local”というフォルダを作成する。

C:\>mkdir Shared

C:\>mkdir Local

次に“C:\Local”の下に“C:\Local\Folder”というフォルダを作成し、その下にFile.txtとうファイルを作っておく。

C:\>cd Local

C:\Local>mkdir Local_Folder

C:\Local>cd Local_Folder

C:\Local\Local_Folder>echo Hello! > File.txt

C:\Local\Local_Folder>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 1234-ABCD です

 C:\Local\Local_Folder のディレクトリ

2010/07/11  14:41    <DIR>          .
2010/07/11  14:41    <DIR>          ..
2010/07/11  14:41                 9 File.txt
               1 個のファイル                   9 バイト
               2 個のディレクトリ  20,057,595,904 バイトの空き領域

C:\Local\Local_Folder>cd \

C:\>

そして“C:\Shared”フォルダには“C:\Local\Folder”フォルダを指す次の3種類のリンクファイルを作成する。ジャンクション、と相対パス指定のシンボリック リンク、そして絶対パス指定のシンボリック リンクを作る。

C:\>cd Shared

C:\Shared>mklink /J Folder_J1 ..\Local\Local_Folder
Folder_J1 <<===>> ..\Local\Local_Folder のジャンクションが作成されました

C:\Shared>mklink /D Folder_S1 ..\Local\Local_Folder
Folder_S1 <<===>> ..\Local\Local_Folder のシンボリック リンクが作成されました

C:\Shared>mklink /D Folder_S2 C:\Local\Local_Folder
Folder_S2 <<===>> C:\Local\Local_Folder のシンボリック リンクが作成されました

C:\Shared>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 1234-ABCD です

 C:\Shared のディレクトリ

2010/07/11  14:44    <DIR>          .
2010/07/11  14:44    <DIR>          ..
2010/07/11  14:44    <JUNCTION>     Folder_J1 [C:\Local\Local_Folder]
2010/07/11  14:44    <SYMLINKD>     Folder_S1 [..\Local\Local_Folder]
2010/07/11  14:44    <SYMLINKD>     Folder_S2 [C:\Local\Local_Folder]
               0 個のファイル                   0 バイト
               5 個のディレクトリ  20,057,595,904 バイトの空き領域

C:\Shared>cd \

C:\>

この例で分かるようにジャンクションはmklinkコマンドで相対パス指定しても作成されるジャンクションは絶対パスのリンクとなる。シンボリック リンクは相対パス絶対パスの両方に対応している。(Windowsもやっと形だけのサポートではなく相対パスを扱えるようになってきたようだ。ジャンクションは寂しい限りだが。)

最後に、“C:\Shared”を共有設定で他のマシンから参照できるようにして実験の準備は完了。

C:\>net share Shared=C:\Shared
Shared が共有されました。

C:\>

さて、先ずはサーバ上でリンクファイルが正常に機能することを確認する。

C:\>type Shared\Folder_J1\File.txt
Hello!

C:\>type Shared\Folder_S1\File.txt
Hello!

C:\>type Shared\Folder_S2\File.txt
Hello!

C:\>

これは、当たり前と言えば当たり前。この限りにおいてジャンクションもシンボリック リンクにも違いは見られない。

では、別のマシンからサーバの“C:\Shared”を参照するとどうなるか。まず、共有フォルダ内にリンクファイルがあるのを確認する。

C:\Users\Administrator>dir \\Server\Shared
 ドライブ \\Server\Shared のボリューム ラベルがありません。
 ボリューム シリアル番号は 1234-ABCD です

 \\Server\Shared のディレクトリ

2010/07/11  14:44    <DIR>          .
2010/07/11  14:44    <DIR>          ..
2010/07/11  14:44    <JUNCTION>     Folder_J1 [C:\Local\Local_Folder]
2010/07/11  14:44    <SYMLINKD>     Folder_S1 [..\Local\Local_Folder]
2010/07/11  14:44    <SYMLINKD>     Folder_S2 [C:\Local\Local_Folder]
               0 個のファイル                   0 バイト
               5 個のディレクトリ  20,057,595,904 バイトの空き領域

C:\Users\Administrator>

さて、先ほどと同様にリンクファイルを使って“File.txt”の内容を出力してみる。

C:\Users\Administrator>type \\Server\Shared\Folder_J1\File.txt
Hello!

C:\Users\Administrator>type \\Server\Shared\Folder_S1\File.txt
シンボリック リンクの種類が無効なため、リンク先に接続できません。

C:\Users\Administrator>type \\Server\Shared\Folder_S2\File.txt
シンボリック リンクの種類が無効なため、リンク先に接続できません。

C:\Users\Administrator>

なんと、シンボリックリンクはクライアント側では解釈できない。細かい実装がどうなっているのか分からないが、“これは別のマシンのシンボリックファイルなので解釈しない”という感じだ。それに対してジャンクションはちゃんと目的のフォルダとして扱える。これはジャンクションはサーバ側のWindowsが解釈して、解釈した結果をクライアント側に返すためだろう。

ここで、さらに驚くことに、ジャンクションを使うと、本来、クライアントから見えない場所にあるフォルダやディレクトリも見えてしまう。上記の例のようにサーバの“C:\Local”には共有設定をしていない。従って本来はネットワーク上の他のマシンからは見えてはいけないフォルダである。しかし、ジャンクションが指す先であれば共有設定がされてなくても他のマシンからも参照できる。

これは、良し悪しである。安易にジャンクションでリンクを張ってしまうと、共有していないフォルダやファイルまでもが参照されてしまうことになる。一方で、たとえば“C:\Temp”といった作業用ディレクトリを共有する際、大きなファイルが置かれても問題ない様に、“C:”ドライブ以外の容量の大きなボリュームのフォルダにリンクしておくことができる。(上の例は同じC:ドライブ内のリンクだが、別のドライブにあるフォルダをジャンクションでリンクしてもちゃんと機能する。)

さて、一方でシンボリック リンクの方は、他のマシンから参照するとエラーになる。ここで、上の例ではシンボリック リンクの指している先が共有ホルダ以外だからでは?という疑問が出てくる。それでは確認してみよう。共有ホルダShared内を指すシンボリック リンクを作成してみる。サーバ側でSharedの下に“Folder_X”というフォルダを作り、先ほど同様にその下にテキストファイルを置いておく。

C:\>cd Shared

C:\Shared>mkdir Folder_X

C:\Shared>echo Hello!!! > Folder_X\File_X.txt

C:\Shared>echo C:\Shared\Folder_X\File_X.txt
C:\Shared\Folder_X\File_X.txt

C:\Shared>type C:\Shared\Folder_X\File_X.txt
Hello!!!

そして、Sharedの下にFolder_Xを指すリンクファイルを作成する。

C:\Shared>mklink /J Folder_X_J1 Folder_X
Folder_X_J1 <<===>> Folder_X のジャンクションが作成されました

C:\Shared>mklink /D Folder_X_S1 .\Folder_X
Folder_X_S1 <<===>> .\Folder_X のシンボリック リンクが作成されました

C:\Shared>mklink /D Folder_X_S2 C:\Shared\Folder_X
Folder_X_S2 <<===>> C:\Shared\Folder_X のシンボリック リンクが作成されました

C:\Shared>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 1234-ABCD です

 C:\Shared のディレクトリ

2010/07/11  15:12    <DIR>          .
2010/07/11  15:12    <DIR>          ..
2010/07/11  14:55    <JUNCTION>     Folder_J1 [C:\Local\Local_Folder]
2010/07/11  14:56    <SYMLINKD>     Folder_S1 [..\Local\Local_Folder]
2010/07/11  14:56    <SYMLINKD>     Folder_S2 [C:\Local\Local_Folder]
2010/07/11  15:10    <DIR>          Folder_X
2010/07/11  15:11    <JUNCTION>     Folder_X_J1 [C:\Shared\Folder_X]
2010/07/11  15:11    <SYMLINKD>     Folder_X_S1 [.\Folder_X]
2010/07/11  15:12    <SYMLINKD>     Folder_X_S2 [C:\Shared\Folder_X]
               0 個のファイル                   0 バイト
               9 個のディレクトリ  20,056,051,712 バイトの空き領域

C:\Shared>

さて、結果はどうなるか? クライアント側からアクセスしてみる。

C:\Users\Administrator>dir \\Server\Shared
 ドライブ \\Server\Shared のボリューム ラベルがありません。
 ボリューム シリアル番号は 1234-ABCD です

 \\Server\Shared のディレクトリ

2010/07/11  15:13    <DIR>          .
2010/07/11  15:13    <DIR>          ..
2010/07/11  14:55    <JUNCTION>     Folder_J1 [C:\Local\Local_Folder]
2010/07/11  14:56    <SYMLINKD>     Folder_S1 [..\Local\Local_Folder]
2010/07/11  14:56    <SYMLINKD>     Folder_S2 [C:\Local\Local_Folder]
2010/07/11  15:14    <DIR>          Folder_X
2010/07/11  15:11    <JUNCTION>     Folder_X_J1 [C:\Shared\Folder_X]
2010/07/11  15:11    <SYMLINKD>     Folder_X_S1 [.\Folder_X]
2010/07/11  15:12    <SYMLINKD>     Folder_X_S2 [C:\Shared\Folder_X]
               0 個のファイル                   0 バイト
               9 個のディレクトリ  20,056,076,288 バイトの空き領域

C:\Users\Administrator>type \\Server\Shared\Folder_X\File_X.txt
Hello!!!

C:\Users\Administrator>type \\Server\Shared\Folder_X_J1\File_X.txt
Hello!!!

C:\Users\Administrator>type \\Server\Shared\Folder_X_S1\File_X.txt
シンボリック リンクの種類が無効なため、リンク先に接続できません。

C:\Users\Administrator>type \\Server\Shared\Folder_X_S2\File_X.txt
シンボリック リンクの種類が無効なため、リンク先に接続できません。

C:\Users\Administrator>

やはり、ジャンクションは機能するが、シンボリック リンクは機能しない。ここで次の疑問が湧いてくる。クライアント側のWindowsがシンボリック リンクを読みだして“C:\Shared”というクライアント側には存在しないフォルダをアクセスしようとするからエラーになるのでは? しかし、それであれば上の例の“Folder_X_S1”は相対バス指定なので機能しなければならない。でも、念には念を入れてクライアント側に“C:\Shared\Folder_X\File.txt”を作って試してみよう。

C:\Users\Administrator>mkdir C:\Shared

C:\Users\Administrator>mkdir C:\Shared\Folder_X

C:\Users\Administrator>echo Hellooooo! > C:\Shared\Folder_X\File_X.txt

C:\Users\Administrator>type C:\Shared\Folder_X\File_X.txt
Hellooooo!

C:\Users\Administrator>type \\Server\Shared\Folder_X_S2\File_X.txt
シンボリック リンクの種類が無効なため、リンク先に接続できません。

C:\Users\Administrator>

どうも、シンボリック リンクはネットワーク越しには使えないようだ。(先のマイクロソフトの記事ではジャンクションとシンボリック リンクは解釈する側が違うだけで両方使えるような記述になっていたので、なんか釈然としない。シンボリック リンクがネットワーク越しに使えないというのはチョットおかしい様な気もする。セキュリティ(ACL)の設定とか色々と変えてやっては見たが、やっぱり上のような結果になってしまう。シンボリック リンクをネットワーク越しに使えるようにする何か設定があるのかも知れないが。シンボリック リンクのネットワークによる共有はSMB 2.0からでないと利用できない、という記事もいくつかあったが、今回の実験に使ってたのはサーバ側もクライアント側もWindows 7なのでSMB 2.0の筈なのだが。)

あと、もう一つ、面白い現象があった。シンボリック リンク・ファイル C:\Shared\Folder_X_S2 自身を共有設定すると、リンク先のファイルへアクセスできるようになる。う〜ん、よくわからない。


まぁ、ジャンクションとシンボリックリンクにはこの様な違いあることを認識して使えば随分と利用勝手のいい機能だと思う。