NetBSD Advent Calendar 2018 14日目の記事です。
今日はNetBSDのユーザランド上でファイルシステムを実現する、PUFFSを試してみようと思います。
puffsって何?
puffs(3)のmanページを見ると、ユーザ空間でファイルシステムを開発するためのフレームワークのようです。LinuxでいうところのFuseと考えて良さそうです。
また、 libpuffs
というライブラリの形で機能が提供されているようです。
NAME
puffs - Pass-to-Userspace Framework File System development interface
LIBRARY
puffs Convenience Library (libpuffs, -lpuffs)
puffsのサンプルを試してみる
まずはpuffsの使い方や動作を把握するため、サンプルを探してみます。
カーネルソースツリーの /usr/src/share/examples/puffs
にサンプルがあるようです。
$ sudo bash
# cd /usr/src/share/examples/puffs
# ls -F
CVS/ Makefile dtfs/ icfs/ pgfs/ pnullfs/ rot13fs/
pnullfsサンプルを試してみる
pnullfsディレクトリにある pnullfs.c
を見ると、"puffs nullfs example"というコメントがあります。どうやらpuffsを利用した何もしないファイルシステムのサンプルのようです。
これを動かしてみれば、puffsの使い方を把握できそうです。
実際に試してみると分かるのですが、ここで言う「何もしない」はマウント先で書き込まれたデータはpuffs側で加工することなく、そのままファイルに書き出すという意味でのpnullfsになっています。
(てっきり /dev/null
的な意味での pnullfs
だと勘違いしていました...)
# cd pnullfs
# cat pnullfs.c
...
/*
* pnullfs: puffs nullfs example
*/
...
さっそくビルドしてみます。 pnullfs
という実行ファイルが生成されました。
# make
# ls -F
.gdbinit CVS/ Makefile pnullfs* pnullfs.c pnullfs.o
nullpath
と mountpath
を指定して起動すれば良さそうです。
# ./pnullfs
pnullfs: usage: pnullfs [-s] [-o mntopts] nullpath mountpath
適当なディレクトリを作成し、 nullpath
に指定してみます。pnullfs
コマンドを実行すると、 /mnt
にファイルタイプが"puffs|pnullfs|としてマウントされています。
# mkdir _null_dir
# ./pnullfs ./_null_dir /mnt
#
# mount
...
./_null_dir on /mnt type puffs|pnullfs
マウント先に書き込んだファイルが、そのまま nullpath
で指定した場所に書き込まれるという動作になっています。
これで大まかな使い方のイメージが把握できました。
# echo 'hello,world.' > /mnt/hello.txt
# cat ./_null_dir/hello.txt
hello,world.
rot13fsを試してみる
rot13fs
というサンプルもあるので、こちらも試してみます。シーザー暗号のひとつであるROT13の名前が付いているので、おそらくマウント先に書き込まれたデータを暗号化してファイルに書き込むというサンプルのように思えます。
さっそくビルドして動かしてみます。
# make
# ls -F
.gdbinit CVS/ Makefile _tmp/ rot13fs* rot13fs.c rot13fs.o
コマンドの実行時引数は先ほどの pnullfs
と同じですね。
# ./rot13fs
rot13fs: usage: rot13fs [-s] [-o mntopts] rot13path mountpath
#
# mkdir _rot13_dir
# ./rot13fs ./_rot13_dir /mnt
# mount
...
./_rot13_dir on /mnt type puffs|rot13
# echo 'hello,world.' > /mnt/hello.txt
# ls /mnt
hello.txt
# ls _rot13_dir
uryyb.gkg
# cat ./_rot13_dir/uryyb.gkg
uryyb,jbeyq.
ちょっとだけサンプルソースを眺めてみる
サンプルからpuffsの使い方と振る舞いがざっくり把握できました。サンプルソースの pnullfs.c
から実装の大まかな流れも見てみましょう。
puffsの初期化は PUFFSOF_INIT()
と puffs_init()
で行うようです。
/usr/src/share/examples/puffs/pnullfs/pnullfs.c:
48 int
49 main(int argc, char *argv[])
50 {
51 struct puffs_usermount *pu;
52 struct puffs_ops *pops;
53 struct puffs_pathobj *po_root;
54 struct puffs_node *pn_root;
...
96 PUFFSOP_INIT(pops);
97 puffs_null_setops(pops);
98
99 if ((pu = puffs_init(pops, argv[0], "pnullfs", NULL, pflags)) == NULL)
100 err(1, "init");
PUFFSOF_INIT()
の中ではpuffsに必要なメモリの確保を行っています。
/usr/src/lib/libpuffs/puffs.h:
428 #define PUFFSOP_INIT(ops) \
429 ops = malloc(sizeof(struct puffs_ops)); \
430 memset(ops, 0, sizeof(struct puffs_ops))
puffs_init()
で取得した struct puffs_usermount
を指定して、 puffs_pn_new()
を呼び出しだします。
puffsの /
を設定しているようです。
102 pn_root = puffs_pn_new(pu, NULL);
103 if (pn_root == NULL)
104 err(1, "puffs_pn_new");
105 puffs_setroot(pu, pn_root);
106 puffs_setfhsize(pu, 0, PUFFS_FHFLAG_PASSTHROUGH);
107
108 po_root = puffs_getrootpathobj(pu);
109 if (po_root == NULL)
110 err(1, "getrootpathobj");
111 po_root->po_path = argv[0];
112 po_root->po_len = strlen(argv[0]);
113 puffs_stat2vattr(&pn_root->pn_va, &sb);
最後に puffs_mount()
でpuffs的なマウント処理を行うという流れになっています。
119 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1)
120 err(1, "puffs_mount");
121 if (puffs_mainloop(pu) == -1)
122 err(1, "mainloop");
123
124 return 0;
まとめ
NetBSDのpuffsのサンプルを試してみました。OS的な設定等は不要で、 libpuffs
を利用したユーザランドのアプリケーションプログラムという位置付けで考えると良さそうです。
明日の記事では、このpuffsを使ってサンプルアプリを作る手順を紹介できればと思います。