【问题标题】:unshare mount namespace not working as expected取消共享挂载命名空间未按预期工作
【发布时间】:2017-05-24 11:43:21
【问题描述】:

调用Linux系统函数unshare(CLONE_NEWNS),返回0表示成功。但是,它似乎没有像我预期的那样工作。 具体来说,当我添加一个新的挂载(例如 tmpfs 挂载)时,它是全局可见的。因此,它实际上并不是预期的私有挂载命名空间。

这是一个演示该问题的示例程序。编译它并在一个终端中运行它。然后打开另一个终端,检查示例程序写入的路径是否可见。不应该,但确实如此。它的行为好像 unshare 调用什么也没做。我所期待的是,从那一刻起,该程序执行的任何后续挂载都不会对其他进程可见。

/* Run this program as root.  As mount and unshare requires higher privileges. */

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <stdlib.h>
#include <stdio.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
    } while (0)

int main(int argc, char *argv[])
{
    // Create a temporary directory at /tmp/unshare
    mkdir("/tmp/unshare", S_IRWXG);
    if (unshare(CLONE_NEWNS) == -1)
        errExit("unshare");

    if (mount("none", "/tmp/unshare", "tmpfs", 0, "mode=0700") == -1)
        errExit("unshare");

    FILE* fp = fopen("/tmp/unshare/test", "w");
    fprintf(fp, "This file should not be seen by other processes right?\n");
    fclose(fp);

    // Pause
    printf("Now open another shell.  As the root user, verify the file /tmp/unshare/test is not seen\n.Press enter end finish\n");
    char c = getchar();

    if (umount("/tmp/unshare") == -1)
        errExit("umount");
}

我应该指出 mount 手册页表明这应该可以工作。特别是标记为“每进程命名空间”的部分。

A process can obtain a private mount namespace if ... 
it calls unshare(2)  with  the  CLONE_NEWNS  flag,  which
causes  the  caller's  mount  namespace to obtain a private copy of the
namespace that it was previously sharing with other processes, so  that
future  mounts  and  unmounts by the caller are invisible to other pro‐
cesses (except child processes that the  caller  subsequently  creates)
and vice versa.

如果您使用 unshare 终端命令,它可以工作。但这也分叉了另一个过程。但是手册页建议在使用 unshare 系统调用时不需要分叉或克隆。我在这里做错了什么?

【问题讨论】:

  • 老兄,unshare(CLONE_NEWNS) 是一个 C 调用!这是使用 linux API。题外话是什么意思?我需要这个从 C 中工作。
  • 你是如何挂载文件系统的?在程序退出后从同一个程序还是从您的 shell?
  • 从程序中使用 mount 系统调用。没有外壳。
  • 能否附上演示问题的小程序源代码?
  • 跟踪您的 unshare 命令并查看它调用了哪些系统调用。

标签: c linux


【解决方案1】:

运行 strace 后我找到了答案。

\> strace unmount -m true
...
unshare(CLONE_NEWNS)                    = 0
mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) = 0
execve("/home/matt/.nvm/versions/node/v6.9.1/bin/true", ["true"], [/* 29 vars */]) = -1 ENOENT (No such file or directory)
...

注意取消共享后的挂载。此挂载调用似乎递归地将所有后续对挂载的更改标记为私有。看看这个沙箱代码:https://github.com/swetland/mkbox 作者也在这样做。

所以这是工作版本。

/* Run this program as root.  As mount and unshare requires higher privileges. */

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <stdlib.h>
#include <stdio.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
    } while (0)

int main(int argc, char *argv[])
{
    // Create a temporary directory at /tmp/unshare
    mkdir("/tmp/unshare", S_IRWXG);
    if (unshare(CLONE_NEWNS | CLONE_FS | CLONE_THREAD) == -1)
        errExit("unshare");

    /* ensure that changes to our mount namespace do not "leak" to
     * outside namespaces (what mount --make-rprivate / does)
     */
    if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1)
        errExit("mount1");

    if (mount("none", "/tmp/unshare", "tmpfs", 0, NULL) == -1)
        errExit("mount2");

    // if (mount("none", "/tmp/unshare", NULL, MS_PRIVATE, NULL) == -1)
    //  errExit("mount2");

    FILE* fp = fopen("/tmp/unshare/test", "w");
    fprintf(fp, "This file should not be seen\n");
    fclose(fp);

    // Pause
    printf("Now open another shell.  As the root user, verify the file /tmp/unshare/test is not seen\n.Press enter end finish\n");
    char c = getchar();

    if (umount("/tmp/unshare") == -1)
        errExit("umount");
}

【讨论】:

  • 等等!如果我们必须自己做mount --make-rprivate /,为什么我们需要unshare(CLONE_NEWNS)??? CLONE_NEWNS 应该在不同的挂载命名空间中隔离挂载/卸载操作,对吧?但是在你的例子中,它是由mount --make-rprivate /完成的,那么CLONE_NEWNS到底有什么作用呢?
  • @Eric 这可能并不明显,但据我所知,事情就是这样。 CLONE_NEWNS 确实将父 NS 克隆为一个孤立的。换句话说,您在 NS 的副本中,但更改是孤立的。因此,如果您的父 / 已使用 MS_PRIVATE 挂载,则不需要重新挂载。正如有关 pivot_root 的文档所说,它不能用于 MS_SHARED 安装。所以 MS_PRIVATE 不是唯一可以使用的标志,只要它不是 MS_SHARED。
猜你喜欢
  • 2012-02-10
  • 1970-01-01
  • 2018-08-21
  • 2011-07-20
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
  • 1970-01-01
  • 2020-12-12
相关资源
最近更新 更多