使用惰性卸载的文件系统还能做什么?

使用惰性卸载的文件系统还能做什么?

umount --lazyumount(2)设置标志的调用MNT_DETACHumount(2)说这将:

“执行延迟卸载:使挂载点不可用于新的访问,立即断开文件系统以及挂载在其下的所有文件系统之间的连接以及与挂载表的连接,并在挂载点不再繁忙时实际执行卸载。

umount(8)说文件系统正忙...

例如,当其上有打开的文件时,或者当某个进程在那里有其工作目录时,或者当其上的交换文件正在使用时。

但“无法进行新访问”到底是什么意思呢?我见过一些应用程序chdir(2)进入一个随后被卸载的目录,并且它们表现得很好。

答案1

汤姆·黑尔的回答继续澄清:

...看起来文件系统已被卸载,但实际上它只是从文件命名空间/层次结构中隐藏。

  • 进程仍然可以通过打开的文件描述符进行写入
  • 新的或现有的文件可以通过相对路径名由挂载点内具有工作目录的进程打开以进行写入

汤姆的回答确实一针见血,但重申一下:

  • “不可用于新访问”只是意味着您无法解析包含安装点的路径名。

  • 你可以做任何事物使用挂载点,除了打开新文件/目录通过绝对路径

  • 你唯一能成为的肯定调用后发生的情况umount(MNT_DETACH)是,无法通过名称访问挂载点下方的内容。

选项名称MNT_DETACH甚至解释了这种行为:挂载点与目录层次结构分离,但实际安装的文件系统不会发生任何事情。


当您考虑时,这有点明显,但当前工作目录本质上是对该目录的打开文件引用,但由内核维护。因此:

chdir("/foo");
open("./bar.txt", O_RDONLY);
相当于
chdir("/foo");
openat(AT_FDCWD, "bar.txt", O_RDONLY);
这相当于
int dirfd = open("/foo", O_RDONLY | O_DIRECTORY);
openat(dirfd, "bar.txt", O_RDONLY);

我做了一些关于延迟解除绑定和打开目录的测试:

  • 如果您有一个引用挂载点上目录的打开文件描述符:
    • 您仍然可以调用getdents(2)来读取目录内容
    • 您仍然可以openat(2)使用相对于该目录的路径来打开该目录下的文件!

该程序演示了:

#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>

static void 
show_dir_listing(DIR *dir)
{
    printf("Listing directory (by handle):\n");
    rewinddir(dir);

    for (;;) {
        struct dirent *d;

        errno = 0;
        d = readdir(dir);
        if (d == NULL) {
            if (errno)
                error(2, errno, "readdir failed");
            break;
        }

        printf("    %s%s\n",
                d->d_name,
                (d->d_type == DT_DIR) ? "/" : "");
    }
}

int main(int argc, char **argv)
{
    const char *dirpath;
    const char *filename;
    DIR *dir;
    int fd, rc;

    if (argc < 3) {
        fprintf(stderr, "Usage: %s DIR FILE\n",
                program_invocation_short_name);
        return 1;
    }
    dirpath = argv[1];
    filename = argv[2];

    printf("PID: %u\n", (unsigned int)getpid());

    printf("Opening handle to %s\n", dirpath);
    dir = opendir(dirpath);
    if (dir == NULL)
        error(2, errno, "opendir failed: %s", dirpath);

    show_dir_listing(dir);

    printf("\nLazy-unmounting %s\n\n", dirpath);
    rc = umount2(dirpath, MNT_DETACH);
    if (rc < 0)
        error(2, errno, "umount2 failed");

    show_dir_listing(dir);


    /* Try to open by full path name */
    {
        char path[PATH_MAX];
        path[0] = '\0';
        strcat(path, dirpath);
        strcat(path, "/");
        strcat(path, filename);

        printf("Trying to open(\"%s\")... ", path);
        fd = open(path, O_RDONLY);
        if (fd < 0) {
            printf("Failed!\n");
        }
        else {
            printf("Success: fd=%d\n", fd);
            close(fd);
        }
    }

    /* Try to openat relative to dir */
    {
        int dfd = dirfd(dir);
        printf("Trying to openat(%d, \"%s\")... ", dfd, filename);
        fd = openat(dfd, filename, O_RDONLY);
        if (fd < 0) {
            printf("Failed!\n");
        }
        else {
            printf("Success: fd=%d\n", fd);
            close(fd);
        }
    }

    return 0;
}

测试:

$ ls /tmp/to-be-bound/
bar.txt  crackle.txt  foo.txt  pop.txt  snap.txt
$ mkdir /tmp/readonly-bind
$ mount -o bind,ro /tmp/to-be-bound /tmp/readonly-bind
$ ls /tmp/readonly-bind/
bar.txt  crackle.txt  foo.txt  pop.txt  snap.txt
$ echo 'should fail' >> /tmp/readonly-bind/foo.txt 
-bash: /tmp/readonly-bind/foo.txt: Read-only file system

$ sudo ./lazytest /tmp/readonly-bind foo.txt
PID: 21160
Opening handle to /tmp/readonly-bind
Listing directory (by handle):
    ./
    ../
    pop.txt
    crackle.txt
    snap.txt
    bar.txt
    foo.txt

Lazy-unmounting /tmp/readonly-bind

Listing directory (by handle):
    ./
    ../
    pop.txt
    crackle.txt
    snap.txt
    bar.txt
    foo.txt
Trying to open("/tmp/readonly-bind/foo.txt")... Failed!
Trying to openat(3, "foo.txt")... Success: fd=4

相关内容