VMware Fusion 上の Linux 仮想マシンから Mac の共有フォルダが見えなくなることが、たびたび (Linux カーネルをアップデートするたび?) あります。
$ ls /mnt/hgfs/
(見えない...。)
どうも vmhgfs (VMware Host-Guest File System) というカーネルモジュールがロードされていないことが原因らしい。
$ lsmod | grep vmhgfs
(ロードされてない...。)
いままでこうなったときは VMware Tools を再インストールして修復していました。しかし、VMware Tools は Linux 3.x に対応していないのか、モジュールのビルドに失敗するなど、色々とおかしくなっており、今回は VMware Tools を再インストールしても問題は解消せず。仕方なく手で直しました。
とりえあず共有フォルダが使えるようになるところまでです。この記事は参考程度に、修復の実施は自己責任でお願いします。
環境
- VMware Fusion 5.03
- ホスト: OS X 10.8.5 (Mountain Lion)
- ゲスト: Linux 3.10.11 (Debian GNU/Linux jessie amd-64)
そもそも
試していませんが open-vm-tools を使うとすぐに解消できるかもしれません。
はじめに
まずは VMware Tools を再インストールしましょう。それで直れば解決です。
ぼくは VMware Tools 9.2.3 を使いました。
VMware Fusion のメニューから [仮想マシン] -> [VMware Tools の再インストール] で Linux 側の /dev/cdrom に CD-ROM が入るので、それを適当にマウント、中にある tarball を展開、vmware-install.pl を実行すると再インストールできます。
再インストールしても修復できませんでしたので、手で直しました。
まず initscript の動作がおかしい
Debian 特有の問題かもしれませんが、vmware-tools のサービスを立ち上げると以下のような出力があります。
# /etc/init.d/vmware-tools start
Checking acpi hot plug done
Starting VMware Tools services in the virtual machine:
Switching to guest configuration: done
/etc/init.d/vmware-tools: 1090: local: ': bad variable name
/etc/init.d/vmware-tools: 1090: local: ': bad variable name
Blocking file system: failed
/etc/init.d/vmware-tools: 1187: local: ': bad variable name
Guest operating system daemon: done
原因は initscript 内の get_version_integer() 関数にあります。この関数で Linux カーネルのバージョンを取得するとき、uname -r した結果を使いますが、2.6.34 という風な 2 つ以上のピリオドで区切られた出力を期待しているため、動かないようです。実際に uname -r してみると、こんな感じ。
$ uname -r
3.10-3-amd64
ぼくは区切り文字に - (ハイフン) を追加して適当に直しました。実際は Linux 3.10.11 を使っていますが、この修正方法だと 3.10.3 と誤認識されます。
--- /etc/init.d/vmware-tools.orig
+++ /etc/init.d/vmware-tools
@@ -847,7 +847,7 @@
# There is no double quote around the back-quoted expression on purpose
# There is no double quote around $version_uts on purpose
- set `IFS='.'; echo $version_uts`
+ set `IFS='.-'; echo $version_uts`
v1="$1"
v2="$2"
v3="$3"
ちゃんとやるのであれば、uname の代わりに LINUX_VERSION_CODE から取得するのが正しそうです。(コマンドでサクッと取得できる方法があるとよいのですが、わからなかったです。)
#include <iostream>
#include <linux/version.h>
int main(void) {
std::cout << ((LINUX_VERSION_CODE & 0xFF0000) >> 16)
<< "."
<< ((LINUX_VERSION_CODE & 0x00FF00) >> 8)
<< "."
<< (LINUX_VERSION_CODE & 0x0000FF)
<< std::endl;
return 0;
}
修正すると、initscript の問題は解消されました。
vmhgfs モジュールをビルドする
VMware Tools の tarball の中にソースコードがあります。
$ tar xzf VMwareTools-9.2.3-1031360.tar.gz
$ tar xf vmware-tools-distrib/lib/modules/source/vmhgfs.tar
$ cd vmhgfs-only
make すると以下のような出力があり、失敗します。
$ make
Using 2.6.x kernel build system.
make -C /lib/modules/3.10-3-amd64/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
MODULEBUILDDIR= modules
make[1]: ディレクトリ `/usr/src/linux-headers-3.10-3-amd64' に入ります
CC [M] /home/yu/work/vmware/vmhgfs-only/backdoor.o
CC [M] /home/yu/work/vmware/vmhgfs-only/backdoorGcc64.o
CC [M] /home/yu/work/vmware/vmhgfs-only/bdhandler.o
CC [M] /home/yu/work/vmware/vmhgfs-only/cpName.o
CC [M] /home/yu/work/vmware/vmhgfs-only/cpNameLinux.o
CC [M] /home/yu/work/vmware/vmhgfs-only/cpNameLite.o
CC [M] /home/yu/work/vmware/vmhgfs-only/dentry.o
CC [M] /home/yu/work/vmware/vmhgfs-only/dir.o
CC [M] /home/yu/work/vmware/vmhgfs-only/file.o
/home/yu/work/vmware/vmhgfs-only/file.c: In function ‘HgfsAioRead’:
/home/yu/work/vmware/vmhgfs-only/file.c:754:32: error: dereferencing pointer to incomplete type
/home/yu/work/vmware/vmhgfs-only/file.c: In function ‘HgfsAioWrite’:
/home/yu/work/vmware/vmhgfs-only/file.c:803:32: error: dereferencing pointer to incomplete type
make[4]: *** [/home/yu/work/vmware/vmhgfs-only/file.o] エラー 1
make[3]: *** [_module_/home/yu/work/vmware/vmhgfs-only] エラー 2
make[2]: *** [sub-make] エラー 2
make[1]: *** [all] エラー 2
make[1]: ディレクトリ `/usr/src/linux-headers-3.10-3-amd64' から出ます
make: *** [vmhgfs.ko] エラー 2
初っ端に Using 2.6.x kernel build system. と出力されているあたりから、3.x に対応していないことが伺えます。
結局ぼくの環境では vmhgfs モジュールをビルドして動かすまでに file.c, inode.c 周り, vmci.c 周り を修正する必要がありました。
file.c の修正
make すると以下のようなエラーが発生しました。
/home/yu/work/vmware/vmhgfs-only/file.c: In function ‘HgfsAioRead’: /home/yu/work/vmware/vmhgfs-only/file.c:754:32: error: dereferencing pointer to incomplete type /home/yu/work/vmware/vmhgfs-only/file.c: In function ‘HgfsAioWrite’: /home/yu/work/vmware/vmhgfs-only/file.c:803:32: error: dereferencing pointer to incomplete type
これは kiocb 構造体の宣言が見つからないことが原因のようです。宣言は linux/aio.h にありますので、それをインクルードすると解消されました。
--- a/file.c.orig +++ b/file.c @@ -25,6 +25,7 @@ /* Must come before any kernel header file. */ #include "driver-config.h" +#include <linux/aio.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/signal.h>
inode.c 周りの修正
make すると以下のようなエラーが発生しました。
/home/yu/work/vmware/vmhgfs-only/inode.c: In function ‘HgfsTruncatePages’: /home/yu/work/vmware/vmhgfs-only/inode.c:888:4: error: implicit declaration of function ‘vmtruncate’ [-Werror=implicit-function-declaration] /home/yu/work/vmware/vmhgfs-only/inode.c: In function ‘HgfsPermission’: /home/yu/work/vmware/vmhgfs-only/inode.c:1821:64: error: macro "hlist_for_each_entry" passed 4 arguments, but takes just 3 /home/yu/work/vmware/vmhgfs-only/inode.c:1821:7: error: ‘hlist_for_each_entry’ undeclared (first use in this function) /home/yu/work/vmware/vmhgfs-only/inode.c:1821:7: note: each undeclared identifier is reported only once for each function it appears in /home/yu/work/vmware/vmhgfs-only/inode.c:1821:66: error: expected ‘;’ before ‘{’ token /home/yu/work/vmware/vmhgfs-only/inode.c:1815:11: warning: unused variable ‘dcount’ [-Wunused-variable] /home/yu/work/vmware/vmhgfs-only/inode.c:1814:26: warning: unused variable ‘p’ [-Wunused-variable]
エラーの発生原因は 2 つあります。
ひとつは Linux 3.9 から hlist_for_each_entry マクロのインターフェースが変わったこと、もうひとつは Linux 3.8 から vmtruncate() 関数が廃止されたことです。
hlist_for_each_entry マクロはいまのインターフェースを参考に使い方を変更し、vmtruncate() 関数は元の実装を参考にマクロ定義しました。inode_operations 構造体が truncate を持たなくなったようなので、その辺りも考慮します。
vmware-tools-9.2.3-inode.patch
--- a/inode.c.orig +++ b/inode.c @@ -1811,14 +1811,13 @@ */ if (mask & MAY_ACCESS) { /* For sys_access. */ struct dentry *dentry; - struct hlist_node *p; int dcount = 0; if (mask & MAY_NOT_BLOCK) return -ECHILD; /* Find a dentry with valid d_count. Refer bug 587879. */ - hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) { + hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { dcount = dentry->d_count; if (dcount) { LOG(4, ("Found %s %d \n", dentry->d_name.name, dcount)); --- a/shared/compat_mm.h.orig +++ b/shared/compat_mm.h @@ -90,9 +90,9 @@ #endif /* - * In 2.4.10, vmtruncate was changed from returning void to returning int. + * In 3.8.0, vmtruncate was changed from returning void to returning int. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 10) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) #define compat_vmtruncate(inode, size) \ ({ \ int result = 0; \ @@ -100,5 +100,12 @@ result; \ }) #else -#define compat_vmtruncate(inode, size) vmtruncate(inode, size) +#define compat_vmtruncate(inode, size) \ +({ \ + int result = inode_newsize_ok(inode, size); \ + if (!result) { \ + truncate_setsize(inode, size); \ + } \ + result; \ +}) #endif
vmci.c 周りを修正する
file.c と inode.c 周りを修正することでひとまずビルドは通るようになりますが、いざ動かしてみるとシンボルが見つからない旨のエラーが発生してしまいました。
$ sudo insmod vmhgfs.ko Error: could not insert module vmhgfs.ko: Unknown symbol in module
dmesg すると以下のようなエラーが出力されていました。
$ dmesg | tail [42830.843944] vmhgfs: Unknown symbol VMCIDatagram_Send (err 0) [42830.844015] vmhgfs: Unknown symbol VMCIDatagram_DestroyHnd (err 0) [42830.844024] vmhgfs: Unknown symbol VMCIDatagram_CreateHnd (err 0)
いつからかは調べていませんが、どうも VMCI の関数名が変わったようです。ぼくは open-vm-tools を参考に直してしまいました。(最初から open-vm-tools を使えばよかったのかもしれない...。)
(diff は長いので割愛。)
やっと
これでようやく共有フォルダが見えるようになりました。
$ sudo insmod vmhgfs.ko
$ lsmod | grep vmhgfs
vmhgfs 52573 0
vmw_vmci 48221 1 vmhgfs
$ sudo mount -t vmhgfs .host:/ /mnt/hgfs/
$ ls /mnt/hgfs/
shared