fs.FS内のファイルを他のプロセスに渡したい
Go 1.16 で追加された io/fs
パッケージで色々なファイルシステムに共通のインターフェイスでアクセスできて便利になりました.このfs.FS
の中身にシェルやエクスプローラからアクセスして操作できると便利そうです.
例えば,ネットワークストレージのクライアントを Go で実装する場合 fs.FS
インターフェイスを実装すると思いますが,そのクライアントをそのままマウントできれば他のアプリケーションからデータにアクセスできます.
GoでFUSEを使う
大抵の環境では,ユーザプログラムがファイルシステムを提供するためのFUSEという仕組みが使えます.
Linuxの場合は,go-fuse というbindingsがあります.
専用のインターフェイスを提供する必要があってfs.FSを使うのは少し面倒ですが,高機能かつ活発に開発が続いています.
Windowsの場合は,DokanというWindows上でFUSEのようなものを提供するライブラリがあって,dokan-goというbindingsもありました.
しかし,ずっとメンテナンスが止まっていて,最近の環境だとコンパイルするために少しだけ修正が必要です.
(8/28追記: DokanではなくWinFspのGoのbindingsは最近までメンテナンスされてそうでした.cgofuse )
dkango
以前から,go-fuse と dokan-go のラッパーを書いて使っていたのですが,dokan-goの本家のリポジトリはずっとメンテナンスが止まっていて,自分で修正しながら使うのも面倒になってきました.特にcgoを使っているので久しぶりに使おうとするとWindows上のgccのセットアップではまったりします.
仕方ないので,dkangoというライブラリを書きました.Goだけで実装したのでcgoは使わないです.
unsafeとsyscallの塊でそんなにテストもしてないので,クラッシュしたりするバグが残ってるかもしれません. 自分でしばらく使っていますが大丈夫そうでした.
fsmount
上記のdkangoとgo-fuseを環境に合わせて使い分けるラッパーです.
fs.FSとマウントポイントを渡すだけで簡単にマウントできます.Windows以外はあまりテストしていませんが,Windows/Linux/MacOSで似たような動作をすると思います.
package main
import (
"os"
"github.com/binzume/fsmount"
)
func main() {
mount, _ := fsmount.MountFS("X:", os.DirFS("."), nil)
defer mount.Close()
// Block forever
select {}
}
この例ではカレントディレクトリからFSを作って X: ドライブとして追加します.(Linuxの場合はマウントポイントとして適当なディレクトリを指定してください)
いくつかメソッドを実装すれば書き込み可能にもなりますが,そのあたりはサンプルを参照してください.
最後に
カジュアルにファイルシステムを実装できると色々遊べると思います.
Windowsのエクスプローラでリバーシをプレイしている.たぶん10の30乗個近いフォルダを含むのでコピーできないです pic.twitter.com/IKaN1XFJxU
— Kousuke Kawahira (@binzume) August 27, 2022
非実用的なサンプルとしてリバーシの盤面をマウントしてみた例です.フォルダを開くごとにゲームが進みます.無駄にAIとも対戦できます.このファイルシステムはリバーシのすべての可能な状態を含んでいる上にパフォーマンスも良くないので,ディレクトリをすべてスキャンするようなソフトウェアは止めておいたほうが良さそうです.