Windowsのハードリンクとジャンクションとシンボリックリンクについて

WindowsのNTFSファイルシステムで、サポートされているファイルリンクとして、ハードリンクとジャンクションとシンボリックリンクがあります。

それぞれについての理解をまとめます。

## ドキュメント

マイクロソフトのドキュメント

- [MSDocs File and Directory Linking
](https://docs.microsoft.com/en-us/windows/win32/fileio/file-and-directory-linking)
- [MSDocs Hard Links and Junctions](https://docs.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions)
- [MSDocs Symbolic Links
](https://docs.microsoft.com/en-us/windows/win32/fileio/symbolic-links)

## ハードリンク

ハードリンクは同一ボリューム内の1つの**ファイル**に対して、複数のパスを作ることができます。
ファイルに対して作成するものとなり、ディレクトリに対してハードリンクを作成する事はできません。

複数パスを作ることにより、作成したすべてのファイルパスが削除されるまで削除されず、ハードリンクが一つでも残されていれば引き続きアクセスする事ができます。

例としては`c:/temp/file/dir-origin/file-origin.txt` というオリジナルのファイルに対して、ハードリンク`C:\temp\file\hardlink.txt`を作成して確認してみます。

```PowerShell:Hardlinkサンプル
# c:\temp\file\dir-origin を作成
# file-origin.txtを作成
New-Item -ItemType File -Path C:\temp\file\dir-origin\file-origin.txt -Value HelloWorld -Force

# hardlinkを作成
New-Item -ItemType HardLink -Path C:\temp\file\hardlink.txt -Target C:\temp\file\dir-origin/file-origin.txt
```

作成した`hardlink.txt`側をテキストエディタで開いて変更後、`origin.txt`側を開くと`hardlink.txt`実施した変更が反映される事を確認できます。

`fsutil file queryfileid <<ファイルパス>>`で実際に格納されている場所`File ID`が取得できるようなので確認してみます。

```PowerShell:fsutilでハードリンクのFile IDを確認
fsutil file queryfileid C:\temp\file\dir-origin\file-origin.txt
fsutil file queryfileid C:\temp\file\hardlink.txt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/cb328314-dc4f-fc87-3d74-2602e408ce7e.png)

`file-origin.txt`と`hardlink.txt`の`File ID`が同じ値である事を確認できます。

## ジャンクション

ジャンクション（ソフトリンク）は同一のコンピュータ上のディレクトリに対して、リンクを作成する機能となり。
ハードリンクとは異なり、ディレクトリのみを対象として作成する事ができます。

あくまでリンクとなるため参照先となるファイルが削除される、ディレクトリ名が変更されると作成したジャンクションからリンク先へアクセスする事はできなくなります。

また参照先の指定については絶対パスのみ利用する事ができます。

```PowerShell
# ジャンクションの作成
New-Item -ItemType Junction -Path C:\temp\file\junction -Target C:\temp\file\dir-origin
```

`C:\temp\file\junction`にアクセスすると、`file-origin.txt`が存在して先程作成した、`c:\temp\file\dir-origin`にリンクされている事がわかるかと思います。

```PowerShell:fsutilでジャンクションのFileIDを確認
fsutil file queryfileid C:\temp\file\dir-origin
fsutil file queryfileid C:\temp\file\junction
```

下記画像のようにすべてのFile IDが別物となっており、ハードリンクとは違う事がわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/708e6dc8-04cc-c057-b108-4d063f2f3f92.png)

## シンボリックリンク

シンボリックリンクは、ハードリンク、ジャンクションとは異なりファイル、ディレクトリ問わずにリンクを作成する事ができます。
基本的には、作成に管理者権限が必要となります。

またジャンクションとは異なり、参照先を絶対パスだけではなく相対パスでも記述する事ができます。

```PowerShell:【管理者権限が必要】
# 絶対パスでシンボリックリンクを作成
New-Item -ItemType SymbolicLink -Path C:\temp\file\symbolic-absolute-dir -Target C:\temp\file\dir-origin
New-Item -ItemType SymbolicLink -Path C:\temp\file\symbolic-absolute-file.txt -Target C:\temp\file\dir-origin\file-origin.txt

# 相対パスでシンボリックリンクを作成
New-Item -ItemType SymbolicLink -Path C:\temp\file\symbolic-relative-dir -Target .\dir-origin
New-Item -ItemType SymbolicLink -Path C:\temp\file\symbolic-relative-file.txt -Target ./dir-origin/file-origin.txt
```

```PowerShell:fsutilでシンボリックリンクのFileIDを確認
fsutil file queryfileid C:\temp\file\dir-origin
fsutil file queryfileid C:\temp\file\dir-origin\file-origin.txt

fsutil file queryfileid C:\temp\file\symbolic-absolute-dir
fsutil file queryfileid C:\temp\file\symbolic-absolute-file.txt

fsutil file queryfileid C:\temp\file\symbolic-relative-dir
fsutil file queryfileid C:\temp\file\symbolic-relative-file.txt
```

下記画像のようにすべてのFile IDが別物となっており、ハードリンクとは違う事がわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/d912417b-40b7-ca00-cd9f-351fd2a94558.png)

### おまけ fsutil usnを確認してみる

```PowerShell:fsutil-usn-readdata
fsutil usn readdata c:\temp\file\dir-origin | findstr ^FileRef#
fsutil usn readdata c:\temp\file\dir-origin\file-origin.txt | findstr ^FileRef#

fsutil usn readdata c:\temp\file\junction | findstr ^FileRef#
fsutil usn readdata c:\temp\file\hardlink.txt | findstr ^FileRef#

fsutil usn readdata c:\temp\file\symbolic-absolute-dir | findstr ^FileRef#
fsutil usn readdata c:\temp\file\symbolic-absolute-file.txt | findstr ^FileRef#

fsutil usn readdata c:\temp\file\symbolic-relative-dir | findstr ^FileRef#
fsutil usn readdata c:\temp\file\symbolic-relative-file.txt | findstr ^FileRef#
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/68aa6d84-78ea-73cd-09f6-5c6bcf757f78.png)

| Name | FileRef |
| --- | --- |
| dir-origin | FileRef#         : 0x00000000000000000029000000036159 | 
| dir-origin\file-origin.txt | FileRef#         : 0x0000000000000000001a000000037837 |
| junction | FileRef#         : 0x00000000000000000029000000036159 |
| hardlink.txt | FileRef#         : 0x0000000000000000001a000000037837 |
| symbolic-absolute-dir | FileRef#         : 0x00000000000000000029000000036159 |
| symbolic-absolute-file.txt | FileRef#         : 0x0000000000000000001a000000037837 |
| symbolic-relative-dir | FileRef#         : 0x00000000000000000029000000036159 |
| symbolic-relative-file.txt | FileRef#         : 0x0000000000000000001a000000037837 |

## 各ファイルについて確認してみる

Get-ChildItemで取得できる構造の`LinkType`を参照すると、ハードリンクなのかジャンクションなのかシンボリックリンクなのか判別する事ができます。

```PowerShell
Get-ChildItem -Recurse  | Select-Object fullname , LinkType | sort FullName
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b040b742-a09d-1649-0756-f4a6f18936a7.png)


## 総評

ジャンクションとハードリンク。　シンボリックリンク　については実際に作成してみて確認するのがわかりやすいのでおすすめ。