起因

为了在 Linux 上舒服的访问 Samba 共享,方便与 NAS 上的小姐姐们进行交流,不得已尝试了许多姿势,于是干脆做个总结好了,不过本文只讨论访问 Samba 共享的情况,如果是访问 NFS,会有其他更简单的方式。
文中主要讨论的几种方法包括:

太长懒得看?直接跳到 通过 fstab 来自动生成 systemd mount unit

用文件管理器直接访问 Samba 共享

最简单的方法,以 KDE 自带的 Dolphin 为例,直接地址栏输入 smb://,然后找到自己的 NAS 再输入用户密码即可,为了方便还可以添加到左侧的快捷栏里。
当然缺陷也非常的严重,例如播放视频时很多软件会把整个视频文件都复制到临时目录然后再进行播放,部分软件甚至会发生完全找不到文件的问题,总而言之,这着实算不上一个能够使用的方案。

命令行手动挂载

要避免上述问题,最简单的解决方案是直接把 Samba 共享挂载到一个目录,而最直接的方法可以通过一行命令实现:

sudo mount.cifs //192.168.123.123/smbshare ~/smbshare -o username=abc,password=def

或者把用户密码存到一个文件里 (记得改权限),例如 /foo/credentials.txt(注意文件权限)

username=abc
password=def

然后使用

sudo mount.cifs //192.168.123.123/smbshare ~/smbshare -o credentials=/foo/credentials.txt

首先这样很不智能,而且每次都要用到 sudo (也许需要输入密码),总之很麻烦,临时用一下还行,但总归不是长久之计。

通过修改 fstab 直接挂载

另一种方法是通过修改 fstab 来挂载,编辑 /etc/fstab 并添加:

//192.168.123.123/smbshare /home/xxx/smbshare cifs user,noauto,nofail,credentials=/foo/credentials.txt 0 0

注意其中的三个选项 user, noauto 以及 nofail
user 代表了任意用户均可使这条挂载生效,避免了每次 mount 前都要加 sudo 的情况。
noauto 代表了不会开机时自动挂载,也就是说需要开机后通过 mount -a 手动挂载。
nofail 则代表了开机时挂载失败时系统不会进入紧急维护模式,当然如果已经设置了 noauto,是否使用 nofail 就无所谓了。
修改完 fstab 后可以通过 mount -a 重新挂载或者直接重启。
这个方案也有很尴尬的地方,如果设置成开机自动挂载,那么会严重拖累系统启动速度,因为在进入登录界面之前需先要等待网络连接才行,但如果设置成 noauto 那么这种挂载方式又会变得很不方便。
而实际上这种方案比较有效的用法是只开启 nofail 选项,然后用来挂载一些非常重要的目录,但显然这并不符合我们的主要目的,我们的目标是开机时不自动挂载,等到真正需要时再自动挂载。

使用 systemd 挂载

而对于前面的问题,可以比较完美的通过 systemd mount 解决,不过这段主要内容讲的是如何自己写 mount unit,而实际上可以通过简单的办法自动生成 mount unit,所以没有兴趣建议直接跳过这段。
要想使用 systemd 挂载,其中一种办法是写一个 systemd mount unit,首先要说明的是 systemd mount unit 的文件名必须对应挂载点的绝对路径,而 systemd-escape 工具可以帮助我们,例如我们想挂载到 /home/xxx/smbshare,只需要执行:

systemd-escape -p --suffix=mount /home/xxx/smbshare

这样我们就获得了所对应的文件名 (home-xxx-smbshare.mount),然后在 /etc/systemd/system/ 下面新建文件

touch "/etc/systemd/system/home-xxx-smbshare.mount"

注意这里记得加引号,不然会被 shell 转义,然后编辑文件并添加内容:

[Unit]
Requires=network-online.target
After=network-online.target

[Mount]
Where=/home/xxx/smbshare
What=//192.168.123.123/smbshare
Type=cifs
Options=credentials=/foo/credentials.txt

[Install]
WantedBy=multi-user.target

这个文件说明了这个 unit 需要在联网后运行,会把 What=... 的目录挂载到 Where=...。虽然这时已经可以通过 systemctl enable ... 来开启这个挂载服务,但是直接这样开启就相当于一个开机时自动挂载的系统服务,也就是说在登录前仍然需要等待网络连接。为了达到按需挂载的目的,我们需要写一个 automount unit,文件名和之前一样只是后缀为 .automount(home-xxx-smbshare.automount),也同样放到 /etc/systemd/system/ 即可:

[Unit]
Description=Automount Samba Share xxx

[Automount]
Where=/home/xxx/smbshare

最后开启这个 automount unit:

sudo systemctl daemon-reload
sudo systemctl enable --now home-xxx-smbshare.automount

这样就可以在系统启动后按需挂载了。

通过 fstab 来自动生成 systemd mount unit

使用 systemd 挂载的另一种方法(更简单,并且是 man systemd.mount 里推荐的方法)就是配置 fstab 了,直接在 /etc/fstab 里添加:

//192.168.123.123/smbshare /home/xxx/smbshare cifs _netdev,nofail,noauto,x-systemd.automount,credentials=/foo/credentials.txt 0 0

没错!总共仅需三个选项,一个是 noauto 表示不需要开机时自动挂载,另一个是 x-systemd.automount 表示使用 systemd 实现按需自动挂载,而 /foo/credentials.txt 则是存储用户密码的文件,格式与使用命令行手动挂载相同。这样下次启动时就会直接自动生成 mount unit 和 automount unit,并且访问 /home/xxx/smbshare 时就会自动挂载了。另外如果需要重新挂载,只需要执行:

sudo systemctl restart home-xxx-smbshare.automount

如果希望闲置一定时间后自动卸除挂载,可以在选项里加上 x-systemd.idle-timeout=...,例如:

//192.168.123.123/smbshare /home/xxx/smbshare cifs _netdev,nofail,noauto,x-systemd.automount,x-systemd.idle-timeout=600,credentials=/foo/credentials.txt 0 0

就这么简单,我们已经达到了不拖慢开机速度并且按需挂载的目的了,可喜可贺,可喜可贺~

使用 Autofs 挂载

使用 autofs 同样可以达到我们的目的,不过配置可能显得不够直观并且需要额外安装,那为什么还要用 autofs ?首先可能对于一部分人来说更习惯 autofs,因为最早的按需挂载基本是通过 autofs 实现的。不过更重要的是它也确实提供了更多功能,例如它可以在挂载时执行脚本,这也给我们提供了很多可行性,例如:

  • 假如被挂载的目录或者挂载点不是固定的,需要通过脚本来动态解析。
  • 挂载前需要先向处于关机状态的网络设备发一个 Magic Packet 来进行网络唤醒(Wake-on-LAN)。
  • 可能 NAS 上的某个块设备是加密的,而密钥又存在本机,那就可以通过脚本来实现远程解锁了。

一些挂载时可能有用的选项

最后提一下一些挂载 Samba 共享时可能会用到的选项:

  • uid=1000forceuid 这两条一起使用可以强制 samba 客户端设置挂载点内目录和文件的所有者。同理 gid=1000forcegid 影响的是用户组。当然影响的都只是本地的挂载点。
  • dir_mode=0700file_mode=0600 则分别设定了挂载点内目录和文件的权限,同样只会影响本地。