SIer だけど技術やりたいブログ

mount コマンドはもう古い? findmnt を使おう

linux

「このディレクトリって何のファイルシステム?」とか「マウントオプションは?」を確認するときに、手癖で mount コマンドを実行してるけど、

$ mount
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=4096k,nr_inodes=118922,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,size=194516k,nr_inodes=819200,mode=755)
...

このコマンドはもう古いらしい。もうすこし正確にいうと、mount(8) の man によれば、「リスト表示機能は後方互換性のために残してる。よりロバストで出力をカスタマイズ可能な findmnt を利用するのがおススメ。」されていた。そうなんだ…めちゃくちゃ mount 使ってるな。

   Listing the mounts
       The listing mode is maintained for backward compatibility only.

       For more robust and customizable output use findmnt(8), especially in your scripts. Note that control characters in the
       mountpoint name are replaced with '?'.

       The following command lists all mounted filesystems (of type type):

          mount [-l] [-t type]

       The option -l adds labels to this listing. See below.

findmnt の特徴

findmnt なら、階層構造で見やすく表示できるし、

$ findmnt
TARGET                       SOURCE     FSTYPE     OPTIONS
/                            /dev/xvda1 xfs        rw,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota
├─/proc                      proc       proc       rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc systemd-1  autofs     rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=12945
├─/sys                       sysfs      sysfs      rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/security     securityfs securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup           cgroup2    cgroup2    rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memory_recursiveprot
...

指定したファイルやディレクトリの所属するマウントポイントを表示できるし、

$ findmnt --target /tmp/.font-unix/
TARGET SOURCE FSTYPE OPTIONS
/tmp   tmpfs  tmpfs  rw,nosuid,nodev,seclabel,nr_inodes=1048576

出力をリアルなストレージデバイスに絞ることもできる。

$ findmnt --real
TARGET SOURCE     FSTYPE OPTIONS
/      /dev/xvda1 xfs    rw,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota

もちろん mount コマンドにあった、ファイルシステムによる絞り込みもある。

# findmnt -t tmpfs
TARGET           SOURCE        FSTYPE OPTIONS
/dev/shm         tmpfs         tmpfs  rw,nosuid,nodev,seclabel
/run             tmpfs         tmpfs  rw,nosuid,nodev,seclabel,size=194516k,nr_inodes=819200,mode=755
├─/run/user/1000 tmpfs         tmpfs  rw,nosuid,nodev,relatime,seclabel,size=97256k,nr_inodes=24314,mode=700,uid=1000,gid=1000
└─/run/netns     tmpfs[/netns] tmpfs  rw,nosuid,nodev,seclabel,size=194516k,nr_inodes=819200,mode=755
/tmp             tmpfs         tmpfs  rw,nosuid,nodev,seclabel,nr_inodes=1048576

また出力を json フォーマットにもできる。

$ findmnt -J | jq
{
  "filesystems": [
    {
      "target": "/",
      "source": "/dev/xvda1",
      "fstype": "xfs",
      "options": "rw,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota",
      "children": [
        {
          "target": "/proc",
          "source": "proc",
          "fstype": "proc",
          "options": "rw,nosuid,nodev,noexec,relatime",
          "children": [
            {
                ...

そしてなぜか df コマンドっぽい出力を模倣するオプションもある(これは df でいいんじゃないかという気がするが)。

# findmnt --df
SOURCE                FSTYPE     SIZE  USED  AVAIL USE% TARGET
devtmpfs              devtmpfs     4M     0     4M   0% /dev
tmpfs                 tmpfs    474.9M     0 474.9M   0% /dev/shm
tmpfs                 tmpfs      190M  2.8M 187.2M   1% /run
/dev/xvda1            xfs        7.9G  2.3G   5.7G  29% /
tmpfs                 tmpfs    474.9M     0 474.9M   0% /tmp
tmpfs                 tmpfs       95M     0    95M   0% /run/user/1000

追加パッケージは不要

「でも追加パッケージが必要なんでしょ…いいよそういうのは… mount で足りてるんだ。」と思ったそこのアナタ(自分含む)。Amazon Linux 2023 の場合、findmnt コマンドは mount コマンドと同じ util-linux-core パッケージに入っていますので追加のインストールは不要です(アップストリームでも util-linux で開発されているので、たいていのディストリビューションでは同梱されているはずです)。

mount との差

じゃあ今後は findmnt 使おうかな…と思える人は素直で素晴らしい人ですが、自分はなるべく手癖で生きていたいし、なんならデータの加工は grep や awk 使うからべつにいいよと思ってしまう派なので、具体的に出力がどう違うのかを調べてみようかなと。(ついでに最近 bind マウントの確認は findmnt 使わないとダメだよ、と教えてもらったんですが、それがなぜなのか確認しようかなと)

コマンドで出力された情報はどちらも、デバイス名・マウントポイント・ファイルシステム種別・マウントオプションの 4 種類の情報から構成されており、階層化されている事以外は、パッと見では情報量は同じに見えます。

$ mount
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=4096k,nr_inodes=118922,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)

$ findmnt
TARGET                       SOURCE     FSTYPE     OPTIONS
/                            /dev/xvda1 xfs        rw,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota
├─/proc                      proc       proc       rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc systemd-1  autofs     rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=12945
├─/sys                       sysfs      sysfs      rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/security     securityfs securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup           cgroup2    cgroup2    rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memory_recursiveprot

そもそもこれらのマウント情報は、どちらのコマンドも /proc/self/mountinfo から取得しているようです。

$ strace -ttf -e openat mount
...
00:13:10.322574 openat(AT_FDCWD, "/proc/self/mountinfo", O_RDONLY|O_CLOEXEC) = 3
<コマンドの出力>

$ strace -ttf -e openat findmnt
...
00:32:46.673625 openat(AT_FDCWD, "/proc/self/mountinfo", O_RDONLY|O_CLOEXEC) = 3
<コマンドの出力>

では /proc/self/mountinfo を見てみると、出力の元になっている情報が色々あります。

# cat /proc/self/mountinfo  | head
19 59 0:18 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
20 59 0:19 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel
21 59 0:5 / /dev rw,nosuid shared:8 - devtmpfs devtmpfs rw,seclabel,size=4096k,nr_inodes=118922,mode=755
22 20 0:6 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:3 - securityfs securityfs rw
23 21 0:20 / /dev/shm rw,nosuid,nodev shared:9 - tmpfs tmpfs rw,seclabel

proc(5) の man に細かい定義があるので詳細は控えますが、以下の情報などが含まれているようです。

  • マウント ID
  • 親のマウント ID
  • デバイスのメジャー番号:マイナー番号
  • ファイルシステムから見たときのマウントするパス(バインドマウントなどで、ファイルシステムの一部パスをマウントすることがありえるので)
  • マウントポイント
  • マウントオプション
  • オプションフィールド(sharedとか。よく分からん。)
  • ファイルシステム種別
  • マウントのソース(デバイス名など)

で findmnt コマンドと mount コマンドの違いとしては、やっぱり会社でアドバイス貰った通り、bind マウント時の挙動が違いそうです。

バックエンドがストレージのファイルシステムを適当にバインドマウントしてみると、

# mkdir -p /mnt/bind
# mount -o bind,ro /root/tmp /mnt/bind

# mount  | grep -E "bind|xvda1 "
/dev/xvda1 on / type xfs (rw,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota)
/dev/xvda1 on /mnt/bind type xfs (ro,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota)

# findmnt | grep bind
└─/mnt/bind                                            /dev/xvda1[/root/tmp]  xfs        ro,noatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,sunit=1024,swidth=1024,noquota

findmnt のほうは、バインドマウントされていることがわかります(/dev/xvda1[/root/tmp])が、mount は分かりません。まあこの例であれば、mount コマンドであっても、マウントのソース(/dev/xvda1)が識別可能なので、同じストレージをマウントしてる->バインドマウントかもと気づけるかもしれませんが、バインド元のファイルパス(/root/tmp)はわかりません。

しかしもっと困るのは、バックエンドがストレージのファイルシステムじゃない場合(tmpfs とか)です。この場合、マウントのソースが tmpfs になってしまい判別不可能なので、mount コマンドでは、バインドマウントされたことが全くわかりません。

# mount --bind /tmp/work /mnt/bind

# mount | grep bind
tmpfs on /mnt/bind type tmpfs (rw,nosuid,nodev,seclabel,nr_inodes=1048576)

# mount | grep bind
# findmnt | grep bind
└─/mnt/bind                                             tmpfs[/work]           tmpfs      rw,nosuid,nodev,seclabel,nr_inodes=1048576

ということで、いまはコンテナ全盛時代であり、バインドマウント使いまくってる現状を考えると、日頃から mount じゃなくて findmnt 使っといたほうがいいんじゃないかと思ったという話でした。