WSL 上の Python 仮想環境 と Windows エラー 0x80070780

### 問題

`C:`ドライブ上のPython開発用ディレクトリを、外付けドライブにバックアップしようとしたところで、こんなエラーに遭遇した。

![250111-80070780.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712175/1dadb76a-b985-3a42-58a4-cce5f84de79b.png)

「エラー 0x80070780: ファイルにアクセスできません」とはなんだろう、どこから来たのだろう、と調べてみると、[Windows Subsystem for Linux 2 (WSL2)](https://learn.microsoft.com/ja-jp/windows/wsl/about) で運用していた Python の仮想環境（[`virtualenv`](https://virtualenv.pypa.io/en/latest/)）の仕業ということが判明した。

### Python 仮想環境

この頃 流行りな ~~女の子~~ Python 仮想環境は、サードパーティパッケージの導入をプロジェクト単位で分けることのできる、だってなんだか だってだってなものだ（使い方は「[venv: Python 仮想環境管理](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)」を参照してほしい）。Node 使いならおなじみな手法である。

わたしはこれを WSL2 で運用している。プログラム絡みの操作は Ubuntu ターミナルでやっているが、編集やバックアップなどのファイル操作は Windows 側からやっているので、Python プロジェクトは `C:`ドライブから見えるように`C:\home\<project>`のようなところに置いてある。Unix 側では`/mnt/c/home/<project>`である。

で、Unix側で `python3 -m venv venv` のように仮想環境を作ると、`venv`の下にこんなサブディレクトリが作成される。

```bash
$ /mnt/c/home/project1/venv$ ls -l
total 0
drwxrwxrwx 1 user group 4096 Dec 11 10:02 bin/
drwxrwxrwx 1 user group 4096 Dec 11 10:02 include/
drwxrwxrwx 1 user group 4096 Dec 11 10:02 lib/
lrwxrwxrwx 1 user group    3 Dec 11 10:02 lib64 -> lib/
-rwxrwxrwx 1 user group  182 Dec 11 10:02 pyvenv.cfg*
```

見てのとおり、`lib64`ディレクトリは`lib`ディレクトリのシンボリックリンクである。これが悪さのもとである。

### Windows ジャンクション

この[シンボリックリンク](https://learn.microsoft.com/ja-jp/windows/win32/fileio/hard-links-and-junctions)<sup>†</sup> を Windows のファイルシステムから見ると、次のようにジャンクションと映る。

```
C:\home\project1\venv>dir
︙
2024/12/11  10:02    <DIR>          .
2024/12/11  10:02    <DIR>          ..
2024/12/11  10:02    <DIR>          bin
2024/12/11  10:02    <DIR>          include
2024/12/11  10:02    <DIR>          lib
2024/12/11  10:02    <JUNCTION>     lib64 [...]
2024/12/11  10:02               182 pyvenv.cfg
               1 個のファイル                 182 バイト
               6 個のディレクトリ  283,616,174,080 バイトの空き領域
```

普通、ジャンクションの `[...]` の中にはリンク元（この場合 `lib`）が示されるが、てんてんになっている。リンク先をロストしているからだ。

ためしに、消去などの操作をしてみる。

```
C:\home\project1\venv>del lib64
ファイルにアクセスできません。
```

このように、`venv` で作成したディレクトリへのシンボリックリンクに対するファイル操作が行われると、冒頭と同じ「アクセスできません」エラーになるようだ。

> <sup>†</sup> この Microsoft Learn では、「ジャンクション」を「接合」と訳している。機械翻訳だろう。

### 解決策

Unix 側のシンボリックリンクを削除し、Windows 側で Windows が好きなジャンクションを作成すればよい（はずだ）。

#### ① Unix 側でシンボリックリンクを消す

Windows 側では操作ができないので、Unix 側から行う。

```bash
$ cd /mnt/home/project1/venv

$ ls -AF
bin/  include/  lib/  lib64@  pyvenv.cfg*

$ rm -i lib64
rm: remove symbolic link 'lib64'? y
```

#### ② Windows 側でジャンクションを作成する

コマンドは [`mklink`](https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/mklink) だ。作成するのはジャンクションなので、スイッチには `/j` を指定する。

```
C:\home\project1\venv> mklink /J lib64 \home\project1\venv\lib
lib64 <<===>> \home\project1\venv\lib のジャンクションが作成されました
```

#### ③ 確認する

Windows 側。`[]` の中にアクセス可能なフルパスが入っているところがポイント。

```
C:\home\project1\venv>dir
︙
2025/01/11  15:50    <DIR>          .
2024/10/30  17:44    <DIR>          ..
2024/10/30  17:44    <DIR>          bin
2024/10/30  17:44    <DIR>          include
2024/10/30  17:44    <DIR>          lib
2025/01/11  15:50    <JUNCTION>     lib64 [C:\home\project1\lib]
2024/10/30  17:44               165 pyvenv.cfg
               1 個のファイル                 165 バイト
               6 個のディレクトリ  283,482,308,608 バイトの空き領域
```

Unix 側

```bash
$ ls -l /mnt/c/home/project1/venv/
total 0
drwxrwxrwx 1 user group 4096 Dec 11 10:02 bin/
drwxrwxrwx 1 user group 4096 Dec 11 10:02 include/
drwxrwxrwx 1 user group 4096 Dec 11 10:02 lib/
lrwxrwxrwx 1 user group    3 Dec 11 10:02 lib64 -> /mnt/c/home/project1/venv/lib
```

これで、WSL 上でいつも通りに Python 仮想環境が使える。


### おわりに

Windows Subsystem for Linux 2 は Unix の完全な仮想環境だと思って油断をしていたが、ファイルシステム絡みには危険がいっぱいである。

前にも痛い目にあっている。Windows 側のフラッシュメモリ（`D:`ドライブなど）を Unix 側でマウントして使っていたら、致命的なファイルシステムエラーでメモリをまるごとおしゃかにしてしまった（復旧に3万円かかった）。

くわばらくわばらである。