'取消共享'在C api中没有按预期工作

时间:2016-01-19 09:00:40

标签: c linux linux-namespaces

这一系列命令有效:

unshare --fork --pid --mount 
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts

但是,相应的C程序没有按预期工作(似乎它没有卸载以前的/ proc,并且它还提供了EBUSY试图卸载devpts):

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("My pid: %i\n", getpid()); // It prints 1 as expected

umount("/proc"); // Returns 0

system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems

mount("proc", "/proc", "proc",
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL));  // Returns 0

umount("/dev/pts");  // Returns -1 errno = 0 (??)

mount("devpts", "/dev/pts", "devpts", 
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL) ); // Returns -1 errno = EBUSY

我在这里省略了错误检查的可读性

我认为unshare或unmount没有按预期工作:即使它返回零,似乎没有卸载/ proc(如果我尝试执行system("mount")之后,它会打印已挂载的文件系统)

4 个答案:

答案 0 :(得分:2)

尽管您的评论

  

"有时" umount返回0"有时" -1,但最后它并没有完全卸载/proc

,在您对pastebin代码的10000次试用中,umount() 总是对我失败,返回-1而不是卸载/proc。我不愿意相信尽管umount()未能执行请求的卸载,0仍然会返回umount(),但如果它确实如此,那将构成bash中的错误。如果你事实上可以证实这样的错误,那么社区意识的回应就是提交针对glibc的错误报告。

然后问题就变成了unshare(1)脚本行为不同的原因和方式。事实上,它似乎没有。

首先,您对unshare(2)命令的期望是错误的。与unshare函数不同,unshare命令不会影响执行它的shell。相反,它启动一个单独的进程,它有自己的指定名称空间的私有副本。通常,您将在unshare命令行上指定启动该进程的命令,实际上程序的手册页指示这样做是强制性的。

根据经验,我发现如果我没有像你那样指定这样的命令,那么unshare会启动一个新的shell作为目标进程。特别是,当我运行你的脚本(有足够的权限使用umount)时,我立即得到一个新的提示,但它是在前台运行的新shell的提示。这对我来说很明显,因为提示不同(但在这种情况下,你的提示可能没有任何不同)。此时umount没有错误消息等,因为尚未运行。如果我在(unshare d)子shell中手动尝试umount proc,则会失败并且"设备正忙碌" - 这是你的C程序尝试做的类比。

当我退出子shell时,脚本的其余部分会运行,mount/proc都失败。这是可以预料的,因为主脚本共享其mount命名空间。

/proc确实很忙,因此即使对于具有mount命名空间的私有副本的进程也无法卸载,这是完全合理的。这样的进程本身可能正在使用/dev/pts的挂载的私有副本。相反,我发现我可以在具有非共享安装命名空间的进程中成功卸载{{1}},但不能在共享该命名空间的系统副本的进程中。< / p>

答案 1 :(得分:1)

我发现问题正在检查source code of unshare command。必须使用/proc卸载MS_PRIVATE | MS_REC并在没有它们的情况下挂载,这主要是为了确保挂载仅在当前(新)命名空间中有效。第二个问题是,如果不对全局命名空间产生影响,则无法卸载/dev/pts(这是由devpts驱动程序的内部例程引起的)。拥有私有/ dev / pts唯一的方法是使用专用的-o newinstance选项安装它。最后/dev/ptmx也应该重新绑定。

因此,这是预期的C工作代码:

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("New PID after unshare is %i", getpid());

if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
    printf("Cannot umount proc! errno=%i", errno);
    exit(1);
}

if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
    printf("Cannot mount proc! errno=%i", errno);
    exit(1);
}


if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
    printf("Cannot mount pts! errno=%i", errno);
    exit(1);
}

if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
    printf("Cannot mount ptmx! errno=%i", errno);
    exit(1);
}

答案 2 :(得分:0)

我认为问题出在系统(“mount”)上,它产生一个shell并且不会带来umount。尝试在umount之后打开/ proc /中的文件,看看它是否按预期工作。

见这 -

unshare(CLONE_NEWPID | CLONE_NEWNS );
int rc = 0;
int pid = fork();
if (pid != 0) {
        int status;
        waitpid(-1, &status, 0);
        return status;
}

printf(">>> My pid: %d\n", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

答案 3 :(得分:-1)

unshare bash!= unshare c

unshare - 运行程序,其中一些名称空间与父项取消共享

所以基本上--fork你用/ bin / sh / / bin / sh(无论你执行你的脚本)都用--pid和--mount选项。 &#34;叉&#34;然后是&#34; unshare&#34;

取消共享 - 取消关联流程执行上下文的部分(当前进程) 你从初始化中取消共享,然后分叉。

CLONE_NEWPID是&#34;克隆&#34;不是#34; unshare&#34;

因此,取决于你想要实现的目标 - 我假设你正在尝试制作&#34; / proc&#34;和&#34; / dev / pts&#34;专属于儿童过程。

这是mount -bind local folders的一个小例子:

# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt

# ./unshare
My pid: 28377
Child:
point.txt
Parent:

# ls mnt

代码:

#define _GNU_SOURCE
#include <sched.h>

int main(int argc, char *argv[])
{
        /** umount global */
        system("umount mnt/");
        int pid = fork();
        if (pid != 0) {
                int status;
                waitpid(-1, &status, 0);
                printf("Parent:\n");
                /* and here we don't */
                system("ls mnt/");
                return status;
        }
        /* unshare */
        unshare(CLONE_FS | CLONE_NEWNS);
        printf("My pid: %i\n", getpid()); // It prints 1 as expected
        /* mount exclusively */
        system("mount --bind point/ mnt/");
        printf("Child:\n");
        /* here we see it */
        system("ls mnt/"); 

        return 0;
}

bash还有一个很好的例子: http://karelzak.blogspot.ru/2009/12/unshare1.html

延续:

mount取决于/ etc / mtab,它并不总是/ proc / mounts的符号链接

所以用ls -la。

检查/ etc / mtab

还要检查/ dev / pts上的umount代码:

int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
  printf("Error on umount: %s\n", strerror(errsv));
}

我很确定它已被使用 - 请使用fuser / dev / pts /

进行检查

**已编辑**

最后 - 我不确定你是否可以在命名空间中卸载procfs(我认为这是不可能的)

但您可以在命名空间中安装自己的procfs副本:

# mount -t proc proc /proc/

现在只有你的过程可以通过ps -e看到。