循环设备在卸载时不会自动分离

循环设备在卸载时不会自动分离

我有一个备份脚本,其中我卸载了一个 squashfs 文件,然后备份,然后重新挂载它。该脚本的简化版本如下:

umount /home/backup/auto/mnt/os
mksquashfs /src-dir /home/backup/auto/os.sqfs.img
mount -t squashfs -o loop /home/backup/auto/os.sqfs.img /home/backup/auto/mnt/os

这样做的目的是可以随时访问备份数据。

这种方法持续了很长时间。但是我得到了一个错误,提示没有足够的回环设备,并且我发现 umount 不再分离使用 mount 安装的回环设备。

最近我有两个重大的变化:

  1. 我将内核更新到了 2.6.32-754.11.1.el6.i686
  2. 我将操作系统移到了 SSD 上。

我刚刚注意到有新的内核更新 2.6.32-754.12.1.el6,所以我进行了更新,但问题并没有得到解决。

操作系统:CentOS 6.10 i686

更新: 这是在故障系统上:

# strace -e trace=ioctl,mount mount -t squashfs -o loop os.sqfs.img mnt/os
ioctl(3, LOOP_GET_STATUS, {number=0, offset=0, flags=0, name="/home/squash/web.img", ...}) = 0
ioctl(3, LOOP_GET_STATUS, {number=0, offset=0, flags=0, name="/home/squash/web.img", ...}) = -1 ENXIO (No such device or address)
ioctl(4, LOOP_SET_FD, 0x3)              = 0
ioctl(4, LOOP_SET_STATUS64, {offset=0, number=0, flags=0, file_name="/home/backup/auto/os.sqfs.img", ...}) = 0
mount("/dev/loop1", "mnt/os", "squashfs", MS_MGC_VAL, NULL) = 0
+++ exited with 0 +++

# cat /etc/mtab|grep 'mnt/os'
/dev/loop1 /home/backup/auto/mnt/os squashfs ro,relatime 0 0

# ls -la /etc/mtab
lrwxrwxrwx. 1 root root 12 Apr 17 02:53 /etc/mtab -> /proc/mounts

# notice that here there is no even the umount call. Am I missing something?
# strace -e trace=ioctl,umount,mount umount mnt/os
+++ exited with 0 +++

这是在另一个系统上,所有内容都具有相同的版本,但它是 64 位的:

# strace -e trace=ioctl,mount mount -t squashfs -o loop os.sqfs.img mnt/os
ioctl(3, LOOP_GET_STATUS, {number=0, offset=0, flags=0, name="/storage/backup/auto/home.sqfs.img", ...}) = 0
ioctl(3, LOOP_GET_STATUS, {number=1, offset=0, flags=0, name="/storage/backup/auto/mail.sqfs.img", ...}) = 0
ioctl(3, LOOP_GET_STATUS, {number=1, offset=0, flags=0, name="/storage/backup/auto/mail.sqfs.img", ...}) = -1 ENXIO (No such device or address)
ioctl(4, LOOP_SET_FD, 0x3)              = 0
ioctl(4, LOOP_SET_STATUS64, {offset=0, number=0, flags=0, file_name="/storage/backup/auto/os.sqfs.img", ...}) = 0
mount("/dev/loop2", "mnt/os", "squashfs", MS_MGC_VAL, NULL) = 0
+++ exited with 0 +++

# strace -e trace=ioctl,umount,mount umount mnt/os
umount("/storage/backup/auto/mnt/os", 0) = 0
ioctl(3, LOOP_CLR_FD)                   = 0
+++ exited with 0 +++

# losetup -a
/dev/loop0: [0904]:35656625 (/storage/backup/auto/home.sqfs.img)
/dev/loop1: [0904]:35656626 (/storage/backup/auto/mail.sqfs.img)

我也没有在良好的系统上看到 LO_FLAGS_AUTOCLEAR。但我确实看到了 umount 中的差异。

我重新安装了util-linux-ng- 但没用。这个包没有更新,也从未更新过。也许与内核有关。我不知道为什么它在 64 位操作系统上运行良好。我可能会安装一个 32 位 VM 来测试它是否会做同样的事情。

- 使固定 -

问题是,在使用 CentOS 7 的 Live CD 进行系统复制时,我创建了 /etc/mtab 到 /proc/mounts 的链接,但我忘记了。我不得不重新安装 grub,因此我 chroot 到操作系统并创建了链接,因为 grub-install 不起作用。我忘记恢复它了。我通过以下方式修复了这个问题:

rm -f /etc/mtab && cat /proc/mounts |grep -v rootfs > /etc/mtab

现在它起作用了:

root@home auto# smount os.sqfs.img mnt/os
root@home auto# losetup -a
/dev/loop0: [0816]:7090072 (/home/squash/web.img)
/dev/loop1: [0816]:7864675 (/home/backup/auto/os.sqfs.img)
/dev/loop4: [0816]:7864569 (/home/backup/auto/web.sqfs.img)
root@home auto# umount mnt/os
root@home auto# losetup -a
/dev/loop0: [0816]:7090072 (/home/squash/web.img)
/dev/loop4: [0816]:7864569 (/home/backup/auto/web.sqfs.img)

答案1

这是我通过研究 CentOS 6 中包含的 util-linux-ng 2.17.2 的源代码所收集到的信息:

在内核 < 2.6.37 上,Linux 要求将条目写入/etc/mtab循环挂载文件系统,以便稍后自动清除循环设备。如果/etc/mtab在挂载时不可写或条目已从中删除,/etc/mtab则不会自动清除循环设备。在内核 >= 2.6.37 上,内核会记住循环设备后备存储路径,并且不需要将其写入/etc/mtab

我能够在 CentOS 6.10 上通过将其设置/etc/mtab为不可变来重现此chattr +i /etc/mtab问题。仅仅删除它是不够的;它将至少使用部分挂载表重新创建。这在正常操作中不应该发生,因此如果您发现此文件已设置为不可变,则应该查找管理员错误或安全漏洞。

如果/etc/mtab在旧版 Linux 系统上不存在或不完整,则可以从 复制一份/proc/mounts。不过,不要在现代 Linux 系统上这样做,而是将 保留/etc/mtab为 的符号链接,/proc/self/mounts以便向后兼容需要在该位置有挂载表的古老代码。

答案2

TL;DR:这似乎不是内核问题,而是命令mount未按预期运行,未设置循环设备的LO_FLAGS_AUTOCLEAR标志。使用较新的发行版外挂载命令应该可以解决这个问题。


跟踪(最近系统的)命令挂载,让我循环(4)的旗帜LO_FLAGS_AUTOCLEAR

LO_FLAGS_AUTOCLEAR (since Linux 2.6.25)
    The loopback device will autodestruct on last close.

因此,我的“现代” mount 命令成功发出了这个 ioctl,如下所示:

# strace -e trace=ioctl,mount mount -o loop /tmp/block.img /mnt/
ioctl(3, LOOP_CTL_GET_FREE)             = 0
ioctl(4, LOOP_SET_FD, 3)                = 0
ioctl(4, LOOP_SET_STATUS64, {lo_offset=0, lo_number=0, lo_flags=LO_FLAGS_AUTOCLEAR, lo_file_name="/tmp/block.img", ...}) = 0
ioctl(3, BLKGETSIZE64, [1073741824])    = 0
ioctl(3, CDROM_GET_CAPABILITY, 0)       = -1 EINVAL (Invalid argument)
ioctl(3, BLKSSZGET, [512])              = 0
mount("/dev/loop0", "/mnt", "ext4", MS_MGC_VAL, NULL) = 0
+++ exited with 0 +++
# umount /mnt
# losetup -l
#

(这里的losetup语法与CentOS6不同)。

我认为如果问题不在内核中(因为 OP 的 2.6.32 > 2.6.25),那么可能来自 mount 命令。为了检查,我安装了一个 centos6-i686 LXC 容器,安装了 strace,创建了丢失的 /dev/loop* 文件,并在其中运行了一个完全特权的 shell(使用lxc-attach's-e选项),允许执行挂载操作:

# lxc-attach -e -n centos6-i686
[root@centos6-i686 ~]# cat /etc/centos-release 
CentOS release 6.10 (Final)
# dd if=/dev/zero of=/tmp/block.img bs=1 count=1 seek=$((2**30-1))
[...]
# ls -lh /tmp/block.img 
-rw-r--r--. 1 root root 1.0G Apr 22 19:06 /tmp/block.img
# mkfs.ext4 /tmp/block.img 
[...]
# strace -e trace=ioctl,mount mount -o loop /tmp/block.img /mnt/
ioctl(3, LOOP_GET_STATUS, {number=0, offset=0, flags=0, name="", ...}) = -1 ENXIO (No such device or address)
ioctl(4, LOOP_SET_FD, 0x3)              = 0
ioctl(4, LOOP_SET_STATUS64, {offset=0, number=0, flags=0, file_name="/tmp/block.img", ...}) = 0
ioctl(3, BLKGETSIZE64, 1073741824)      = 0
ioctl(3, CDROM_GET_CAPABILITY or SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, 0) = -1 EINVAL (Invalid argument)
mount("/dev/loop0", "/mnt/", "ext4", MS_MGC_VAL, NULL) = 0
+++ exited with 0 +++
# umount /mnt
# losetup --show /dev/loop0
/dev/loop0: [003f]:1756539 (/tmp/block.img)
# losetup -d /dev/loop0
#

显然,ioctl 没有使用 clear 标志发出,这导致了循环设备泄漏。查看源代码,似乎此功能已经存在很长时间了,并且存在于 util-linux 2.17.2(CentOS6 版本)中。CentOS6man mount甚至告诉:

由于 Linux 2.6.25 支持循环设备的自动销毁,因此任何由 mount 分配的循环设备都将通过 /etc/mtab 上的 umount 独立释放。

所以我不知道为什么它不起作用,而且它似乎是一个错误(或者这可能与我的环境有关:64 位内核 5.0.x 等)。

相关内容