最近在鼓捣生信环境的时候,因为有时要把Win下的R脚本和fq/bam数据传到Linux虚拟机里跑bash,很自然就想到了共享文件夹。


挂载共享文件夹

两大主力虚拟化平台VirtualBox和VMware都支持这个功能,据说是VMware的I/O性能会更好一点,不过我没有实测。以VMware为例,首先需要安装增强功能(VMware Tools)。如果是新版的VMware,需要自己去下载(https://packages-prod.broadcom.com/tools/frozen/linux/linux.iso),然后选中虚拟机--设置--硬件--CD/DVD--使用ISO映像文件,选择刚刚下载的linux.iso,启动虚拟机。

# 以root用户登录
mkdir /mnt/cdrom  # 创建一个用于挂载的目录
mount /dev/cdrom /mnt/cdrom  # 挂载

# 将VMware Tools拷贝到临时目录下
mkdir /tmp
cp /mnt/cdrom/VMwareTools-*.tar.gz /tmp/
cd /tmp
tar -zxvf VMwareTools-*.tar.gz

# 运行安装程序
cd vmware-tools-distrib
./vmware-install.pl

# 通常需要重启以生效
reboot

安装好VMware Tools后,就可以在虚拟机--设置--选项--共享文件夹中,添加需要挂载给虚拟机的文件夹,可以勾选总是启用。共享文件夹一般会挂载在/mnt/hgfs/your-shared-folder-name目录下。

软链快速访问

我一般是用ln -s来做:

# 在工作目录下创建一个指向系统挂载点的符号链接
ln -s /mnt/hgfs/your-shared-folder-name ~/workspace/proj/your-short-link

这样更轻量,对我来说更方便管理一些;如果不需要这个软链了,直接rm即可,不会对挂载和内容造成影响。如果是生信分析,这样做足够了。或者,这个软链接也可以指向共享文件夹更深层的目录。

其实也可以通过:

# 在工作目录下创建一个挂载点
mkdir ~/workspace/proj/your-short-link
sudo mount --bind /mnt/hgfs/your-shared-folder-name ~/workspace/proj/your-short-link

这样做的原理是创建一个指向共享文件夹的挂载点,好处是不需要通过内核解析,所见即所得,不过这样做时,如果你挂载的共享文件夹太多,可能会出现管理混乱;而且,不需要时,要通过sudo umount来卸载。

Windows“软链”

有时,在Windows下也需要一些软链。比如:部分包读非标准路径(比如含有&、空格等特殊字符的文件夹)会有问题,这时可以用软链建一个干净的路径;当然,有时单纯只是因为路径太长,需要一个方便的软链。

在Windows下,使用CMD可以创建软链,有几种方式:

REM 在需要创建软链的目录下
mklink /D link target  && REM 需要管理员权限
mklink /J link target

两种方式的区别如下:

参数 /D参数 /J参数
实际名称 symbolic link(符号链接) junction point(联接点)
存在方式 NTFS内置机制,从Vista开始得到支持。文件类型是.SYMLINK NTFS内置机制,从Windows2000/XP开始得到支持。是 NTFS 3.0 及以上文件系统(Windows 2000 及以上系统)的特性,它是链接本地目录(可跨卷)的访问点,通过交接点的操作都会被系统映射到实际的目录上。通过建立交接点,可以在保证一个目录实例(目录的一致性)的前提下,允许用户或程序从本地文件系统中的多个位置访问此目录。
适用范围 同时适用于文件、目录。这是一种超级shortcut,文件大小为0字节、不占用空间。 只适用于目录。只能使用绝对路径。即使创建junction point时使用了相对路径,保存到NTFS中时将隐式转换成绝对路径。
使用限制 可以使用相对、绝对路径。假设创建symbolic link时使用了相对路径,保存到NTFS中的就是相对路径,不会隐式转换成绝对路径。可以跨盘符,可以跨主机,可以使用UNC路径、网络驱动器。 junction point必须与target directory位于同一local computer,可以简单理解成不能跨主机, 在local computer范围内,可以跨盘符。不能使用UNC路径;假设Z是通过网络映射生成的盘符,同样不适用于Z。
文件 符号链接(Symlink,Softlink)是对文件或目录的引用,实际上符号链接本身是一个“记录着所引用文件或目录的绝对或相对路径”的特殊文件,通过符号链接的操作都会被重定向到目标文件或目录。对符号链接和快捷方式的“读、写、遍历”等操作都会被重定向到目标文件或目录,但对它们的“复制、删除、移动、配置 ACL”等操作只针对自身。 对交接点内文件和子目录的“建立、删除、修改”等操作都被映射到对应的目录中的文件和子目录上,对交接点的“复制、粘贴、剪切、配置 ACL”,只会影响此交接点,在同一卷内移动交接点,只会影响此交接点,但在不同卷间移动交接点,会将此交接点转换为正常目录,并且交接点对应目录下的所有内容都会被移动。
关联 删除symbolic link,不影响target。删除target,symbolic link仍将存在,但失效了,变得不可用。它们可以像普通文件一样操作,但所有对符号链接的操作都实际作用于目标对象。符号链接对用户而言是透明的,符号链接看上去和普通的文件和文件夹没有区别,操作方法也一模一样(更类似于 Linux 的软链接)。 删除target,junction point仍将存在,但失效了,变得不可用。
Windows 中的硬链接、目录联接(软链接)、符号链接、快捷方式 - 漫思 - 博客园
Windows 中的硬链接、目录联接(软链接)、符号链接、快捷方式 在Linux文件系统中经常提及硬链接(Hard Link)和符号链接(Symbolic Link),Windows中也可以创建链接,但由于丰富的图形界面操作,很少提及链接。Windows 的 NTFS 文件系统支持三种链接:硬链接(

结合共享文件夹和软链接

有时,一些工具需要在Linux里运行,就要求在Linux里也有一个相对干净的路径。在实际使用中发现了一些有趣的事情。

还是以VMware为例,假设项目文件结构如下:

G:/
└──project/
   ├── analysis/
   │   ├── R-codes/
   │   └── results/
   ├── data/
   │   ├── 'A complicated name with &$*'/
   │   └── raw/
   └── README.md

在一定条件下,虚拟机是可以使用Windows中宿主机的软链的。

首先,在实际分析时,很可能会涉及要在project/analysis/R-codes里,调用./data/'A complicated name with &$*'/内的数据。这个目录实在是太不干净了,每次都需要''括起来,或者使用\&这种转义。然而,在虚拟机中,是不能创建“位于共享文件夹内、指向共享文件夹内的目录”这样的软链的,也就是说:

# 不能这样写:
cd project
ln -s ./data/A\ complicated\ name\ with\ \&\$\* ./analysis/clean-link  

但是,如果在Windows里面使用mklink /J创建过软链,这时这个软链在虚拟机中是可以直接使用的。

也就是说,如果我们有一个:

cd /d G:\project\analysis
mklink /J clean-link "G:\project\data\A complicated name with &$*"

这样的junction point,Linux里可以直接读到A complicated name with &$*/里的文件的。这确实是一个很有意思的现象。经过查阅资料,找出了可能的解释:虚拟机平台VMware的共享文件夹,在访问时并非直接把物理硬盘系统暴露给了虚拟机,而是有一个特殊组件起了桥梁作用(vmhgfs-fuse)。收到虚拟机的访问请求后,vmhgfs-fuse将请求转发给宿主机,而宿主机上的文件系统完成真正的读写,再把结果返回给虚拟机。

这样,当虚拟机试图访问clean-link时,由于junction point的特性,它已经在内核完成了解析,故而其实vmhgfs-fuse看到的就是目标目录"A complicated name with &$*\",并传给Linux。

如果事情到这里结束那可能确实是已经足够了,不过后面我又尝试发现了一些新的东西。

首先就是,根据上面的特性,我们很容易就想到的是:如果我的junction point指向共享文件夹外的目录,Linux也能访问吗?答案是肯定的,更好玩的是还可以正常读写;原因就是上面说的,虚拟机根本不觉得这是个共享文件夹外的软链,它就觉得这是个目录。

此外:

REM 管理员
cd /d G:\project\analysis
mklink /D clean-link "G:\project\data\A complicated name with &$*"

如果用mklink /D加绝对路径尝试建立这个软链,那就必须把两者的共同父目录(至少是project\挂载为共享文件夹,换句话说,共享文件夹里要同时能包裹这两个目录,否则在虚拟机里直接就不可访问,用ls -l只会展示类似clean-link -> 'G:/project/data/A complicated name with &$*'这样的dangling symlink

也可以使用相对路径:

REM 管理员
cd /d G:\project\analysis
mklink /D clean-link ..\data\"A complicated name with &$*"

这个比较好理解,简单解释一下就是,vmhgfs-fuse从Windows里拿到了symlink的元数据;Windows会告诉vmhgfs-fuse:这是一个symlink,并把语义传给它。相当于,Windows“拼”出来了一个路径告诉vmhgfs-fuse,紧接着,判断语义指向的目标是不是Linux能访问的(在不在共享文件夹目录里)。如果在,那就访问;如果不在,只能回传一个dangling symlink。一个细节是,如果可访问,vmhgfs-fuse还是会让Linux把这个软链伪装成目录。

总结一下,建议如果需要一个在Windows宿主机和Linux虚拟机的共享文件夹中同时可用的软链,推荐使用mklink /D和相对路径。使用mklink /J创建的软链如果指向共享文件夹外的文件时,可能造成预料外的修改。