【问题标题】:Propagate a mount from child namespace to the parent namespace?将挂载从子命名空间传播到父命名空间?
【发布时间】:2018-06-17 19:23:09
【问题描述】:

如何将在子命名空间中创建的挂载传播到父命名空间?

详情

我正在尝试创建一个利用 overlayfs 的工具,以允许对只读目录进行写入。棘手的一点是,我希望任何用户都能够在没有 root 权限的情况下使用它。因此,我希望这可以通过挂载命名空间来实现,前提是管理员已经挂载了一个共享目录,那么任何用户都应该能够在该树下创建从父命名空间可见的覆盖(因此任何用户登录shells 可以看到覆盖安装)。

这是我尝试过的,但不起作用:

# admin creates a shared tree for users to mount under
sudo mkdir /overlays
# bind mount over itself with MS_REC | MS_SHARED
sudo mount --bind --rshared /overlays /overlays

假设用户想要在/some/readonly/dir 上创建覆盖,他们应该创建/overlays/user/{upper,work,mnt}。我希望他们能够在/overlays 目录下安装一个覆盖,并使用以下代码进行传播。

// user_overlay.c
#define _GNU_SOURCE                                                                                                                                                                                          
#include <sched.h>                                                                                                                                                                                           

#include <stdio.h>                                                                                                                                                                                           
#include <stdlib.h>                                                                                                                                                                                          
#include <signal.h>                                                                                                                                                                                          
#include <linux/capability.h>                                                                                                                                                                                
#include <sys/mount.h>                                                                                                                                                                                       
#include <sys/types.h>                                                                                                                                                                                       
#include <sys/wait.h>                                                                                                                                                                                        
#include <unistd.h>                                                                                                                                                                                          

int child(void *args)                                                                                                                                                                                        
{                                                                                                                                                                                                            
    pid_t p;                                                                                                                               
    p = mount("overlay", "/overlays/user/mnt", "overlay", 0, "lowerdir=/some/readonly/dir,upperdir=/overlays/user/upper,workdir=/overlays/user/work");                                                                           
    if (p == -1){                                                                                                                                                                                            
        perror("Failed to mount overlay");                                                                                                                                                                                     
        exit(1);                                                                                                                                                                                             
    }                                                                                                                                                                                                        

    // Expose the mount to the parent namespace                                                                                                                                                              
    p = mount("none", "/overlays/user/mnt", NULL, MS_SHARED, NULL);                                                                                                                                                 
    if (p == -1){                                                                                                                                                                                            
        perror("Failed to mark mount as shared");                                                                                                                                                                                     
        exit(1);                                                                                                                                                                                             
    }                                                                                                                                                                                                        

    // Exec bash so I can ensure that the mnt was created
    // though in practice I would just daemonize this proc
    // such that the mount is visible in the parent 
    // until this proc is killed
    char *newargv[] = { "/bin/bash", NULL };                                                                                                                                                                 

    execv("/bin/bash", newargv);                                                                                                                                                                             
    perror("exec");                                                                                                                                                                                          
    exit(EXIT_FAILURE);                                                                                                                                                                                      

    return 0;                                                                                                                                                                                                
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
int main()                                                                                                                                                                                                   
{                                                                                                                                                                                                            
    pid_t p = clone(child, malloc(4096) + 4096, CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);                                                                                                                
    if (p == -1) {                                                                                                                                                                                           
        perror("clone");                                                                                                                                                                                     
        exit(1);                                                                                                                                                                                             
    }                                                                                                                                                                                                        

    // Wait until the bash proc in the child finishes
    waitpid(p, NULL, 0);                                                                                                                                                                                     
    return 0;                          
}

执行gcc user_overlay.c -o user_overlay &amp;&amp; ./user_overlay 确实会在该子进程中安装覆盖,但/overlays/user/mnt 不会传播到父进程。然而,对/overlays/user/upper 的修改对父子节点都是可见的。

【问题讨论】:

    标签: c linux filesystems linux-containers linux-namespaces


    【解决方案1】:

    您试图实现的目标似乎是不可能的,至少不使用上述方法。您想通过CLONE_NEWUSER 创建一个新的用户命名空间,向非特权用户授予挂载权限。但是,引用mount_namespaces(7)(强调我的):

    对挂载命名空间的限制
    关于挂载命名空间,请注意以下几点:
    * 挂载命名空间有一个所有者用户命名空间。 所有者用户命名空间与其父挂载命名空间的所有者用户命名空间不同的挂载命名空间被视为特权较低的挂载命名空间。

    * 当创建一个权限较低的挂载命名空间时,共享挂载会减少为从属挂载。(共享和从属挂载将在下面讨论。)这可以确保在权限较低的挂载命名空间中执行的映射不会传播到更多特权挂载命名空间。

    这意味着您正在创建的挂载实际上具有slave 传播类型,而不是您所期望的shared。这会导致挂载事件不会传播到父挂载命名空间。

    【讨论】:

      猜你喜欢
      • 2016-01-06
      • 2010-10-20
      • 2013-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多