How do I propagate a mount created in a child namespace to the parent?
Details
I am trying to create a tool leveraging overlayfs
for allowing writes over a read only directory. The tricky bit is that I would like any user to be able to use it without root privileges. Therefore I was hoping that this could be achieved with a mount namespace, provided an admin had mounted a directory shared, any user should then be able to create an overlay under that tree that is visible from the parent namespace (so any of the users login shells can see that overlay mount).
Here is what I tried, but is not working:
# 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
Assuming a user then wants to create an overlay over /some/readonly/dir
, they should create /overlays/user/{upper,work,mnt}
. I would expect them to be able to mount an overlay under the /overlays
dir that propagates with the following code.
// 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;
}
Executing gcc user_overlay.c -o user_overlay && ./user_overlay
indeed mounts the overlay in that child process, but /overlays/user/mnt
is not propagated to the parent. However modifications to /overlays/user/upper
are visible from both parent and child.