WSLでGoogle Drive File Stream （GDFS) にアクセスする

# Postscript 2021/October/1
`Google Drive for Desktop`現在最新の`Version: Version: 51.0.14.0`にて確認したところ，`WSL1`及び`WSL2`の**両バージョン**で
これまで問題だった`mv`もちゃんと動くようになっていたことを確認できました．
これからは特に難しいことを意識せずにWSLからGoogle Driveを利用できます．ﾖｶﾀﾖｶﾀ

そのため，ほとんどの場合，この記事で紹介しているSSHを用いてGoogle Drive for Desktopにアクセスする手法は必要なくなりました．
Windows側に何か処理をsshで頼みたいときなどの参考にはなるかもしれません．


# Postscript 2021/July/8
新たにリリースされたGoogle Drive for Desktop (旧Google Drive File Stream)`Version: 49.0.8.0`で
少なくとも`ls`, `cp`, `rm`ができるようになっているのを確認しました！
しかもマルチバイト文字の表示もちゃんと対応しているようです！素晴らしい．

注意点としては

- なぜかいまだに`mv`コマンドが使えないのでちゃんとディレクトリ操作をしたい人にはまだ本記事の手法は有効かもしれない
- WSL2のみで動作確認．WSL1ではダメだった．のでシリアル通信とかもしたい人はまだちょっと苦労するかも．これはWSL側の問題？

# Postscript
Google Drive for Desktop (旧Google Drive File Stream) のv.47のおそらくβ版で少なくとも`ls`と`cp`ができるようになったらしい．
https://github.com/Microsoft/WSL/issues/2999#issuecomment-818452537
（本記事のSSHでマウントする方法が投稿されたスレッド）
今後に期待が高まる！

# Abstract
1. Windows側でSSHサーバを立てる
2. WSL **2** 側でSSHFSでSSHプロトコルを介してGDFSをマウントする

注意として，現状`ls`などのファイル操作に対して依然マルチバイトに対応しておらず，
マウントするディレクトリ以下にはマルチバイト文字を入れない方が無難．

# Background
大学の共同研究でファイルのやり取りをGoogle Drive File Stream (GDFS) というGDriveのstreamバージョンを使うことになった．

ただ，このGDFSはほかのストリーム型クラウドアカウントとはかなり異なって独特で，
Windowsにはデフォルトで`G:`としてマウントされる．

そして，WSLからはこの`G:`に`cd /mnt/g/`などでアクセス自体はできるものの，
例として以下のような操作に対応していなかった：

- `ls`に対してのファイルリストの出力
- VSCodeのRemote-WSLでディレクトリツリーの表示


このため，WSL上でPythonなりなんなりやってた身としてはもどかしさがパなかったので解決策を求めた [^dokan-inf]．

[^dokan-inf]: こういったクラウドファイルストリームアプリが使っている`Dokan`というライブラリを更新すればうまく言う話もあるが，GDFSはその辺も独自にForkしててややこしいらしい．

軽く調べた感じ，以下のSSHを介してWSL上にWinディレクトリをマウントする方法でとりあえず行けたので記述する．


# Procedure
この記事を読んでる人はすでに`WSL`を導入していることを想定しています．
もしWSLの導入からという人はぜひこちらからどぞ: [VScode・WSL・Git導入][]
PowershellとWSLを使うのでWindows Terminalを使うと便利かもしれません．

以下，凡その操作は以下のスレッド上の投稿に拠っています：
https://github.com/Microsoft/WSL/issues/2999#issuecomment-655833213
ただ，SSHの設定などは知ってる前提なので，今回はそこもちょっとだけ詳しく書きます．


実行環境は`Win10 20H2`, `Ubuntu20.04`(WSL ver.2)でした．

## 1. WSL2への切り替え
WSL **1** とWSL **2** はLinuxを動かす仕組みが根本から異なっています [^WSL1vs2]．
本手法は現状WSL **2** でのみ動作を確認しています [^wsl1sshfs]．


したがってWSL **1** の人は以下の手順でWSL **2** に更新してください．

管理者権限でpowershellを開き以下のコマンドを実行

```powershell
# 現在のWSLのバージョンを確認．(Windows ビルド 18362 以上でのみ使用可能 == WSL2がインストール可能な場合のみ実行可能)
wsl --list --verbose
# WSLのバージョンを2に変更．オプションの引数を1にして実行することでWSL1に戻すことが可能．
wsl --set-version 2

# versinが2になっていることを確認する．
wsl --list --verbose
```
また今後別のディストリビューションを導入することを考えて以下も実行しておく．

```powershell
# WSL2を既定のアーキテクチャに設定
wsl --set-default-version 2
```


ついでにWSL側の各アプリケーションをアップデートしておきましょう．
WSLのシェルを立ち上げて

```bash
# aptじゃない人は適宜良しなにオナシャス
sudo apt update
sudo apt upgrade
```

参考：
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10#set-your-distribution-version-to-wsl-1-or-wsl-2
https://docs.microsoft.com/ja-jp/windows/wsl/reference

[^WSL1vs2]: WSL2はWSL1とは違いLinuxに最適化された仮想環境上でモノホンのLinuxカーネルを動かしているようです [^wsl-compare-versions] [^forest]
[wsl-compare-versions]: https://docs.microsoft.com/ja-jp/windows/wsl/compare-versions
[^wsl-compare-versions]: [WSL 1 と WSL 2 の比較, 2020/09/28, Microsoft][wsl-compare-versions]
[forest]: https://forest.watch.impress.co.jp/docs/shseri/win10may2020/1250493.html
[^forest]: [「WSL 2」が正式リリース！ ～「WSL 1」とのメリットは？ 「Windows Terminal」にも注目, 樽井 秀人2020年5月5日 06:55, 窓の杜][forest]

[^wsl1sshfs]: WSL **1** では現状実行できません．というのもWSL **1** では[FUSE][]という`SSHFS`の大元の部分が現状実装されていないためです [^wsl-sshfs_ja]
[^wsl-sshfs_ja]:  [20200622: WSL2 - sshfs, kou1okada 2020年06月23日(火) , PIB][wsl-sshfs_ja]．
[wsl-sshfs_ja]:https://seesaawiki.jp/w/kou1okada/d/20200622%3A%20WSL2%20-%20sshfs


[VScode・WSL・Git導入]:https://qiita.com/Philosophistoria/items/48c4779739e6fafc63e0

## 2. OpenSSH Server をWin10にインストール

### OpenSSH サーバのインストール
`設定`→`アプリと機能`→`オプション機能`→`機能の追加`
から`Open SSH サーバー`にチェックを入れるだけでインストールできます．

また，インストールしたらOpenSSHサーバをたちあげて初期設定をしてください．

### OpenSSH サーバの初期設定
PowerShellを立ち上げて以下のコマンドを実行

```PowerShell
# SSHサーバ（sshd)を立ち上げる
Start-Service sshd
# OPTIONAL but recommended: 
# Windows起動時に毎回サーバも自動的に立ち上げるようにする
Set-Service -Name sshd -StartupType 'Automatic'
```

また以下でファイヤーウォール設定の確認

```Powershell
# Confirm the Firewall rule is configured. It should be created automatically by setup.
Get-NetFirewallRule -Name *ssh*
```

以上のコマンドの出力で`Enabled : True`となっていればOK．もしそうでないなら，以下を実行

```PowerShell
# There should be a firewall rule named "OpenSSH-Server-In-TCP", which should be enabled
# If the firewall does not exist, create one
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 2
```

### SSHサーバの起動確認
ここまででとりあえずSSHサーバは起動した．このSSHサーバが正常に動いていることを確認するために，
Powershellで以下のコマンドを実行すればパスワードを確認され，Winログイン時のパスワードを入力すれば入れる [^win10-ssh-client]．
`exit`で接続を切れる．

```Powershell
ssh localhost
```

参考：
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
https://askme4tech.com/how-install-and-configure-open-ssh-server-windows-10
https://qiita.com/iShinkai/items/a12c9d26f8f4264897f9

[^win10-ssh-client]: ちなみにこの`ssh`コマンドは`ssh client`プログラムで，元から入ってるはずだが，もし入っていなければ，サーバのインストールとほぼ同じ手順でインストールできる．


## 3. WSLのクライアント側の設定

[sshの基礎の基礎][ssh-qiita]

### SSH Keyを作る
`ssh`で公開鍵でログインするための準備．
パスワードログインでいいという人，すでにsshキーを作ってあるという人なら飛ばしてもok．

まず[秘密鍵と公開鍵（錠）を作る][ssh-keygen-qiita]．鍵ファイルの名前や保存場所やパスフレーズ，暗号化方法などの詳細はは各個人お任せ．

```bash
ssh-keygen
```

多分一般的な設定だと`~/.ssh/`に`id-rsa`と`id-rsa.pub`みたいな2つのファイルができると思われる．
`id-rsa`が秘密鍵，みんなに秘密な奴．
`id-rsa.pub`が公開鍵．みんなに配っていい奴．

で，この`id-rsa.pub`をWin10のSSHサーバに登録しなければならない．

[ssh-keygen-qiita]:https://qiita.com/soma_sekimoto/items/35845495bc565c38ae9d
[ssh-qiita]:https://qiita.com/syou007/items/9bbc1b50b9608651c390

### SSH 公開鍵の登録
SSHの公開鍵は大抵，ホストなるSSHサーバを立ち上げてるユーザのホームディレクトリ直下の`.ssh/`にある`authorized_keys`というファイルに追加する．
しかしここではまりポイントが一つある

#### はまりポイントその1: 公開鍵の登録場所．
今この操作を行っている人は大体Win10で管理者アカウントでやってる人も多いと思うが，sshサーバの初期設定で
管理者グループに属するユーザからのアクセスの場合：`C:\ProgramData\ssh\administrators_authorized_keys`を使用
その他:`%USERPROFILE%\.ssh\authorized_keys`を使用
ということになっている．

そのため，管理者アカウントで実行使用している人は`C:\ProgramData\ssh\administrators_authorized_keys`（なければ作成）に
その他のアカウントで実行している人は`%USERPROFILE%\.ssh\authorized_keys`に
`id-rsa.pub`の内容を新しい行として追加．

WSLでは以下のようにしてもよい：

```bash
# 管理者アカウントの人
cat ~/.ssh/id-rsa.pub >> /mnt/c/ProgramData/ssh/administrators_authorized_keys
# 一般アカウントの人
cat ~/.ssh/id-rsa.pub >> /mnt/c/User/<username>/.ssh/authorized_keys
```

または管理者権限でエディタを開き，`/mnt/c/ProgramData/ssh/sshd_conf`を編集し以下の2行をコメントアウトする．
これにより，ほかのユーザと同様に`%USERPROFILE%\.ssh\authorized_keys`を使用できる．

```
#Match Group administrators
#       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
```

なお，この`sshd_conf`ではほかにもいろいろ設定できる[^sshd_conf_comment] ([参考][sshd_conf_matome])．例えばパスワードログインをゆるさない様にもできる．

[^sshd_conf_comment]: なお，この`sshd_conf`でコメントアウトになっているのはもともとのシステムのデフォ値なのでコメントアウトを外さなくても有効になっている．もし設定を変更したい場合は`sshd_conf`を丸ごとバックアップしておいてから各コメントアウトを外して，設定変更するか，各行のコピーを取っておくことがおすすめされる．

[sshd_conf_matome]:nina.jp/server/slackware/openssh/sshd_config.html

参考：
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration
https://dev.classmethod.jp/articles/configure-windows-server-2019-sshd/
https://github.com/PowerShell/Win32-OpenSSH/wiki/sshd_config
https://operationslab.wordpress.com/2013/04/11/ssh-%E3%81%A7-root-%E3%81%AE%E3%81%BF%E5%85%AC%E9%96%8B%E9%8D%B5%E8%AA%8D%E8%A8%BC%E3%82%92%E5%BC%B7%E5%88%B6%E3%81%99%E3%82%8B/

---

そしてこの後`.ssh`と`authorized_keys`のアクセスを設定しないと，公開鍵ログインを拒否されるため，設定を行う．

#### はまりポイントその2：ファイルのアクセス制限
そしてLinuxの場合，`.ssh`のアクセスを`700`，`authorized_keys`のアクセスを`600`にそれぞれchmodで変更するが，
ご存じのようにLinuxとWindowsではアクセス制限の方法がまるっきり違うため以下のような操作が必要．
Windowsの場合，どうも，`authorized_keys`のアクセスだけいじればいいっぽい．(もしかしたら何かやったのに忘れただけかも．)

以下Win10のエクスプローラで操作する方法：

1. アクセス設定の継承の無効化
  1. `%USERPROFILE%\.ssh\authorized_keys`を開く．
  2. `authorized_keys`を右クリックからのプロパティ
  3. `セキュリティ`タブを開く
  4. `詳細設定`ボタンをクリック
  5. `継承の無効化`ボタンをクリック
  6. `継承されたアクセス許可をこのアブジェクトの明示的なアクセス許可に変換します`をクリック
2. アクセス許可を`SYSTEM`と`Administrators`を残し，それ以外を削除．一般アカウントの場合`User`は消さないかもしれない？

powershelで操作する方法：
https://askme4tech.com/how-install-and-configure-open-ssh-server-windows-10
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement#deploying-the-public-key

他参考：
https://qiita.com/ShortArrow/items/5a2e408580414e745bc8

---

ここまででとりあえず公開鍵ログインができるようになった．
のでとりまWSLからWin10のsshサーバにログインしてみる

#### はまりポイントその3: WSL2のlocalhost問題
Win10もWSLも同じマシン上で動いているはずで，ネットワークアダプタは1個しかないが，
WSL2は前述のとおり仮想マシン上で動いているため，TCP/IPスタックがWin10とWSLとでそれぞれ独立しており，
**localhostでWSLからWin10へアクセスできない**．[^wintowsllocalhost] [^wsltowinlocalhost]

[^wintowsllocalhost]: Win10からWSLへはなぜかlocalhostでアクセスできるように機能追加された ([参考][win10towsl-localhost]) が，その逆は未実装？　これから機能追加されると信じたい
[^wsltowinlocalhost]: WSL **1** ではWin10と同様のプロトロルスタックを利用していたのでlocalhostで互いにアクセスできた．

しかしWSLを起動した際にWin10とWSLそれぞれ仮想ネットワークアダプタを介し，仮想スイッチでつながっている．
そしてこの仮想ネットワークアダプタにプライベートIPアドレスがふってあり，これらを指定することでアクセス可能である．
（このIPアドレスはWSLの起動のたびにランダムらしい）

このWSLからはWin10の仮想ネットワークアダプタを指定すればよく，以下のコマンド確認できる．

```bash
cat /etc/resolv.conf
```
結果例：

```bash
## This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.28.144.1
```

したがって上記の例の場合WSL2からWin10にsshログインするには

```bash
ssh <username>@172.28.144.1
```
とすればよい，もしsshキーが設定されていなければパスワードが求められる．
またもしsshキーがデフォルトの箇所に保存されていない場合，`-i <locate ssh private key>`オプションを付け加える．

これで入れたらおk．もしなにか躓く箇所があれば，sshの基礎知識を調べるといいかもしれません．

追記：
`Ubuntu 20.04.1`だとログイン時に以下のような表示が出てきて，`eth0`という名前の仮想ネットワークアダプタが見えるが，
この`eth0`がWSL自身のもので，windows側 **から** WSLへのアクセスに使えるが，
今回知りたいアドレスとは違うので注意．

```bash
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 4.19.128-microsoft-standard x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Dec  4 13:14:33 JST 2020

  System load:  0.0                Processes:             8
  Usage of /:   1.3% of 250.98GB   Users logged in:       0
  Memory usage: 0%                 IPv4 address for eth0: 172.28.154.190
  Swap usage:   0%

0 updates can be installed immediately.
0 of these updates are security updates.
```

参考：
WSL2のlocalhost事情
https://www.atmarkit.co.jp/ait/articles/1909/09/news020.html
WLS2でWin10の仮想ネットワークアダプタのIPアドレスを確認する方法
https://www.yokoweb.net/2020/06/12/windows10-wsl2-xserver-x410-emacs/
`/etc/resolv.conf`について
https://www.itmedia.co.jp/help/tips/linux/l0356.html
[win10towsl-localhost]:https://www.yokoweb.net/2020/06/12/windows10-wsl2-xserver-x410-emacs/


## 4.SSHFSをWSLに導入する．
Linuxにはローカルに自由にファイルシステムを実装できる`fuse`([wiki][FUSE]) という機能があり，
WSL1ではこれが実装されてなかったが，ほぼ完ぺきなWSL2ではこれが使える．

[FUSE]:https://ja.wikipedia.org/wiki/Filesystem_in_Userspace

fuseについてはにわか知識で説明に試みると……
`SSHFS`はfuseを用いてssh経由でファイルシステムをマウントするというものである．すなわちこれを用いてGDFSにアクセスしようとすると，

- WSLで`ls <GDFS mounted path>`
- fuseがssh経由で得たデータをWSLが読める形で提供
- sshの先ではWin10アプリであるsshサーバがWindows用のファイルのデータを送る．

GDFSはWSLでは扱えないが，Win10では自由に扱えるので，sshをインターフェースとすることで，GDFSの変なWin10依存なところが吸収される

### SSHFSのインストール

``` bash
sudo apt-get install sshfs
```

### fuseの設定
/etc/fuse.conf の以下の`user_allow_other`をアンコメントして有効にする
これによりシステム(ssh)がfuseを使えるようにする？

```
sudo sed --in-place '/user_allow_other/s/^#//g' /etc/fuse.conf
```

### sshfsでマウントするパスを作っておく (例 `/mnt/drive`):

```
sudo mkdir /mnt/drive && sudo chown `id --user`:`id --group` /mnt/drive 
```

## 5. SSHFSでマウントするスクリプトを作成・実行
手打ちでもいいが，今後WSLを立ち上げるたびに行わなければならない（bashrcなどに追加してスタートアップ設定する）ので
スクリプトに書いておく．

何でもよいが`sshfs_drive.sh`という名前のファイルを作って以下を書き込む
大元：https://github.com/Microsoft/WSL/issues/2999#issuecomment-655833213

```
#!/bin/sh

# 日本語でGDFSを導入している人は'G:\マイドライブ'や'G:\共有ドライブ\hogehoge\ふがふが\'のようになっても大丈夫です．
DRIVE_PATH='G:\My Drive'
# 前述した，Win10の仮想ネットワークアダプタのIPアドレスをゲットしています．
HOST_IP=`tail --lines=1 /etc/resolv.conf | awk '{print $2}'`
# ssh の秘密鍵のパス
IDENTITY_FILE='<identity_file>'
# Win10でsshサーバを実行しているアカウント名
USERNAME='<username>'
# 以上の情報をオプションとして，GFDSを/mnt/driveにマウントする．
sshfs -o "IdentityFile=$IDENTITY_FILE" $USERNAME@$HOST_IP:"$DRIVE_PATH" -o allow_other,gid=`id --group`,idmap=user,uid=`id --user` /mnt/drive
```
ここで，`<username>`と`<identity_file>`は良しなに．`<identity_file>`は大抵: `~/.ssh/id-rsa`

作ったスクリプトを実行可能にしておく

```
`chmod +x sshfs_drive.sh`
``` 

試しに実行するには

```bash
source sshfs_drive.sh 
```
など．これで`ls /mnt/drive/`としてファイルが一覧されれば成功！！！

この状態でVSCodeのRemote-WSLでGDFS以下のフォルダを開いても見えるはず．

注意：
ただし，理由は特に検証していないが，このようにマウント時にはマルチバイト文字をディレクトリ名に含んでもいいが，
マウントした後はもマルチバイト文字を含むディレクトリ，ファイルは，表示されないどころか，アクセスできない．
そのため，マウントするディレクトリ以下はAsciiコード内でファイル名を付けるのをお勧め

# Conclusion
~~Googleは早く修正して~~修正されました