Skip to content

Instantly share code, notes, and snippets.

@ig0rmin
Created January 30, 2024 20:49
Show Gist options
  • Save ig0rmin/e29694c640def5aef97ae26c83c4e7f3 to your computer and use it in GitHub Desktop.
Save ig0rmin/e29694c640def5aef97ae26c83c4e7f3 to your computer and use it in GitHub Desktop.
Clone a process into a new mount namespace and pivot root to a given directory
// This example demonstrates creating mount namespace and using pivot_root()
// It doesn't create a user namespace, so to run it we need sudo
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/wait.h>
static void waitChild(pid_t pid) {
int status = 0;
pid_t res = waitpid(pid, &status, 0);
if (res == -1) {
printf("Waitpid returned error, errno: %d\n", errno);
return;
}
if (WIFEXITED(status)) {
printf("Child %d exited with exit status %d\n", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("Child %d was killed by a signal %d (core dumped: %d)\n",
pid, WTERMSIG(status), __WCOREDUMP(status));
} else {
printf("error: unexpected waitpid result\n");
}
}
static int _clone(int flags) {
return syscall(SYS_clone, flags, NULL, NULL, NULL, NULL);
}
static int pivot_root(const char *new_root, const char *put_old) {
return syscall(SYS_pivot_root, new_root, put_old);
}
static void doPivotRoot(const char* newRoot) {
const char *oldRoot = "/old_root";
// Recursively change mount propagation to "private"
mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL);
// Make sure that new new root is a mount point
mount(newRoot, newRoot, NULL, MS_BIND, NULL);
// Create directory to which old root will be pivoted
char path[PATH_MAX] = {0};
snprintf(path, sizeof(path), "%s/%s", newRoot, oldRoot);
mkdir(path, 0777);
pivot_root(newRoot, path);
chdir("/");
umount2(oldRoot, MNT_DETACH);
rmdir(oldRoot);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <new-root>\n", argv[0]);
return 1;
}
const char* newRoot = argv[1];
fprintf(stdout, "New root: %s\n", newRoot);
int pid = _clone(SIGCHLD | CLONE_NEWNS);
if (pid == -1) {
// Error
fprintf(stderr, "clone returned -1\n");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child
printf("Child, PID: %d\n", getpid());
doPivotRoot(newRoot);
// Execute a shell inside a container (we assume a new FS has it)
char* newExe = "/bin/sh";
char* newArgs[] = {newExe, NULL};
execv(newExe, newArgs);
fprintf(stderr, "execv failed\n");
exit(EXIT_FAILURE);
} else {
printf("Parent, Child PID: %d\n", pid);
waitChild(pid);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment