答案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