我不想使用mount | grep
,而是想使用mount -l -t bind
,但这不起作用,并且-t none
显示全部坐骑。
答案1
也许这可以解决问题:
findmnt | grep "\["
例子:
$ mkdir /tmp/foo
$ sudo mount --bind /media/ /tmp/foo
$ findmnt | grep "\["
│ └─/tmp/foo /dev/sda2[/media] ext4 rw,relatime,data=ordered
答案2
绑定挂载不是文件系统类型,也不是已挂载文件系统的参数;它们是安装的参数手术。据我所知,就内核而言,以下命令序列会导致基本相同的系统状态:
mount /dev/foo /mnt/one; mount --bind /mnt/one /mnt/two
mount /dev/foo /mnt/two; mount --bind /mnt/two /mnt/one
因此,记住哪些挂载是绑定挂载的唯一方法是mount
留在/etc/mtab
.绑定挂载操作由bind
挂载指示选项(这会导致文件系统类型被忽略)。但mount
没有选项仅列出使用一组特定选项集安装的文件系统。因此您需要自己进行过滤。
mount | grep -E '[,(]bind[,)]'
</etc/mtab awk '$4 ~ /(^|,)bind(,|$)/'
请注意,/etc/mtab
仅当它是由 维护的文本文件时,这才有用mount
。有些发行版设置/etc/mtab
为符号链接/proc/mounts
;/proc/mounts
基本上等同于/etc/mtab
但确实有一些区别,其中之一是不跟踪绑定安装。
内核保留但未在 中显示的一条信息/proc/mounts
是,安装点何时仅显示已安装文件系统上的目录树的一部分。实际上,这主要发生在绑定安装上:
mount --bind /mnt/one/sub /mnt/partial
在 中,和/proc/mounts
的条目具有相同的设备、相同的文件系统类型和相同的选项。仅显示根目录的文件系统部分的信息在每个进程挂载点信息中可见/mnt/one
/mnt/partial
/mnt/partial
/sub
/proc/$pid/mountinfo
(第 4 栏)。那里的条目看起来像这样:
12 34 56:78 / /mnt/one rw,relatime - ext3 /dev/foo rw,errors=remount-ro,data=ordered
12 34 56:78 /sub /mnt/partial rw,relatime - ext3 /dev/foo rw,errors=remount-ro,data=ordered
答案3
内核不处理与以下不同的绑定安装普通的事后安装。唯一的区别在于mount
运行时发生的情况。
当您挂载文件系统(例如使用mount -t ext4 /dev/sda1 /mnt
)时,内核(稍微简化)执行三个步骤:
- 内核查找指定文件系统类型的文件系统驱动程序(如果您省略
-t
或使用-t auto
mount
猜测类型并将猜测的类型提供给内核) - 内核指示文件系统驱动程序使用源路径和任何提供的选项来访问文件系统。此时,文件系统仅由主:次数字对标识。
- 文件系统绑定到路径(挂载点)。内核在这里还使用了一些挂载选项。 (
nodev
例如,这是挂载点上的一个选项,而不是文件系统上的选项。您可以进行绑定挂载nodev
,也可以不进行绑定挂载)
如果您执行绑定安装(例如使用mount --bind /a /b
),则会发生以下情况:
- 内核解析哪个文件系统包含源路径以及从安装点到目录的相对路径。
- 使用选项和相对路径将文件系统绑定到新的安装点。
(我会跳过mount --move
,因为它与问题无关。)
这与在 Linux 上创建文件的方式非常相似:
- 内核决定哪个文件系统负责应在其中创建文件的目录。
- 文件系统中创建了一个新文件。此时文件只有一个 inode 号。
- 新文件链接到目录中的文件名。
如果您建立硬链接,则会发生以下情况:
- 内核解析源文件的inode号。
- 该文件链接到目标文件名。
正如你所看到的,创建的文件和硬链接是无法区分的:
$ touch first
$ ln first second
$ ls -li
1184243 -rw-rw-r-- 2 cg909 cg909 0 Feb 20 23:56 /tmp/first
1184243 -rw-rw-r-- 2 cg909 cg909 0 Feb 20 23:56 /tmp/second
但,由于您可以通过比较 inode 编号来识别文件的所有硬链接,因此您可以通过比较挂载的主要:次要编号来识别文件系统的所有挂载。
您可以使用findmnt -o TARGET,MAJ:MIN
或直接查看/proc/self/mountinfo
(请参阅 Linux 内核文档以获取更多信息)。
以下 Python 脚本列出了所有绑定安装。它假定与已安装文件系统根目录的相对路径最短的最旧的安装点是原始安装点。
#!/usr/bin/python3
import os.path, re
from collections import namedtuple
MountInfo = namedtuple('MountInfo', ['mountid', 'parentid', 'devid', 'root', 'mountpoint', 'mountoptions', 'extra', 'fstype', 'source', 'fsoptions'])
mounts = {}
def unescape(string):
return re.sub(r'\\([0-7]{3})', (lambda m: chr(int(m.group(1), 8))), string)
with open('/proc/self/mountinfo', 'r') as f:
for line in f:
# Parse line
mid, pid, devid, root, mp, mopt, *tail = line.rstrip().split(' ')
extra = []
for item in tail:
if item != '-':
extra.append(item)
else:
break
fstype, src, fsopt = tail[len(extra)+1:]
# Save mount info
mount = MountInfo(int(mid), int(pid), devid, unescape(root), unescape(mp), mopt, extra, fstype, unescape(src), fsopt)
mounts.setdefault(devid, []).append(mount)
for devid, mnts in mounts.items():
# Skip single mounts
if len(mnts) <= 1:
continue
# Sort list to get the first mount of the device's root dir (if still mounted)
mnts.sort(key=lambda x: x.root)
src, *binds = mnts
# Print bind mounts
for bindmount in binds:
if src.root == bindmount.root:
srcstring = src.mountpoint
else:
srcstring = src.mountpoint+':/'+os.path.relpath(bindmount.root, src.root)
print('{0} -> {1.mountpoint} ({1.mountoptions})'.format(srcstring, bindmount))
答案4
unset DONE1FSES
FSES=$(findmnt -vUPno SOURCE,FSROOT,TARGET,MAJ:MIN)
FSES=${FSES//MAJ:MIN/MAJ_MIN}
while read SEARCH1FS
do
unset DONE2FSES
eval "$SEARCH1FS"
SEARCH1SOURCE=$SOURCE
SEARCH1FSROOT=$FSROOT
SEARCH1TARGET=$TARGET
SEARCH1MAJMIN=$MAJ_MIN
FS1WASHANDLED=0
while read DONE1FS
do
if [[ $DONE1FS == $MAJ_MIN ]]
then
FS1WASHANDLED=1
break
fi
done < <(echo "$DONE1FSES")
if [[ ($SEARCH1FSROOT == /) && ($FS1WASHANDLED == 0) ]]
then
DONE1FSES+=$MAJ_MIN$'\n'
while read SEARCH2FS
do
eval "$SEARCH2FS"
SEARCH2SOURCE=$SOURCE
SEARCH2FSROOT=$FSROOT
SEARCH2TARGET=$TARGET
SEARCH2MAJMIN=$MAJ_MIN
FS2WASHANDLED=0
while read DONE2FS
do
if [[ $DONE2FS == $SEARCH2FS ]]
then
FS2WASHANDLED=1
break
fi
done < <(echo "$DONE2FSES")
if [[ ($SEARCH1MAJMIN == $SEARCH2MAJMIN) && ($SEARCH1TARGET != $SEARCH2TARGET ) && ($FS2WASHANDLED == 0 ) ]]
then
DONE2FSES+=$SEARCH2FS$'\n'
echo "$SEARCH1TARGET$SEARCH2FSROOT --> $SEARCH2TARGET"
fi
done < <(echo "$FSES")
fi
done < <(echo "$FSES")