UML で hostfs と overlayfs を使う

User mode linux で hostfs でホストのファイルシステムを参照しつつ、書き込みは別の場所にしたかったので overlayfs を使ってみました。

User mode linux (UML) は linux kernel をいろいろ頑張って普通のプロセスとして実行するものです。 ptrace とかで頑張っているらしいけど、ちゃんと調べていないです。

hostfs は UML からホストのファイルシステムを透過的に見せる薄いファイルシステムで kernel の fs/hostfs 以下で実装されています。

overlayfs は unionfs とか言われるやつで二つの FS を組み合わせて ro なベースイメージに差分だけ別の箇所に書くみたいなことに使うようです。 組込デバイスで使ったり、Docker では storage backend として aufs を使っています。 aufs は out-of-tree ですけど overlayfs は kernel tree に含まれています。

先ずは UML をビルドします。今回は githublinusリポジトリから HEAD をもってきました。 menuconfig するときに File systems から Overlay filesystem support を有効にしておきます。 あと、いらなそうな機能は削っておくとビルドが早いです。

git clone https://github.com/torvalds/linux.git
cd linux
make defconfig ARCH=um
make manuconfig ARCH=um
make ARCH=um

ビルドが終わると vmlinux という実行ファイルができるので、次のように実行します。

./vmlinux rootfstype=hostfs init=/bin/sh mem=128M

あとは UML の中で、overlayfs を使ってみます。

mount -t tmpfs none /tmp
cd tmp
mkdir l u w m
mount -t hostfs -o/,ro none /tmp/l
mount -t overlay -olowerdir=/tmp/l,upperdir=/tmp/u,workdir=/tmp/w none /tmp/m

適当なファイルを読んでみましょう。

cat /tmp/m/etc/hosts

すると以下のように失敗すると思います。

cat: /tmp/m/etc/hosts: No such file or directory

ホスト側と UML から strace してみます。

strace /bin/cat /tmp/m/etc/hosts
  • ホスト
strace -p $UML_PID

すると、UML 側では以下のように open に失敗しています。

open("/tmp/m/etc/hosts", O_RDONLY)      = -1 ENOENT (No such file or directory)

ホスト側では以下のようになっています。

open(" \205Ui/etc/hosts", O_RDONLY)     = -1 ENOENT (No such file or directory)

なんかすごく怪しいですね。

カーネルを見てみると、open は fs/hostfs/hostfs_kern.c の hostfs_open でやっています。

 name = dentry_name(file_dentry(file));
    if (name == NULL)
        return -ENOMEM;

    fd = open_file(name, r, w, append);

dentry_name は __dentry_name を呼び出していて、以下のようになっています。

 char *p = dentry_path_raw(dentry, name, PATH_MAX);
    char *root;
    size_t len;

    root = dentry->d_sb->s_fs_info;
...
        strcpy(name + len, p);

    return name;
}

fs_info には mount 時の起点のパスが入るようになっています。 今は -o/,ro としたので / が入るはずです。 上では p には /etc/hosts が入っており、root には / が入るはずです。

しかし実際には fs_info に謎の \205Ui というデータが入ってしまっています。

cat は呼べているため overlayfs 側の問題のように見えます。 git log してみると、怪しいコミットがありました。

overlayfs: Make f_path always point to the overlay and f_inode to the underlay

どうやら file->f_path が hostfs のものでなく overlayfs のものになっているため、superblock に入っている fs_info が別のものを指すようになったようです。

これに対して、fs: add file_dentry() で正しい dentry を参照するための 関数が導入されています。 また、続くコミットで、nfs, ext4 が修正されています。 が、何故かそれだけで、btrfs や f2fs は独自に改修しています。

原因は分かったので、適当にパッチ を当てたツリーを使ってビルドしたら正しく動きました。

UMLメーリングリストにパッチを投稿したら以下の返信がありましたが、なんでそうしなかったのかよくわからないです。

User-mode Linux kernel port / Mailing Lists

Is there a reason why not all instances of file->f_path.dentry have been replaced by file_dentry()? I smell more fallout...

現状では overlayfs は ext4 や btrfs で動かすのが安全そうです。