fix pivot root, add remount for dev, sys and proc

This commit is contained in:
Antoine 2020-07-14 23:09:52 +02:00
parent 58431adc32
commit 8ab9a46d96
7 changed files with 317 additions and 40 deletions

View File

@ -43,10 +43,10 @@ release: all
# debug target
valgrind:
@valgrind --leak-check=full --show-leak-kinds=all --trace-children=yes --track-origins=yes $(BIN_DIR)/$(TARGET) ls -la
@sudo valgrind --leak-check=full --show-leak-kinds=all --trace-children=no --track-origins=yes $(BIN_DIR)/$(TARGET) /bin/ls -la /bin
run:
$(BIN_DIR)/$(TARGET) ls -la
sudo $(BIN_DIR)/$(TARGET) /bin/ls -la /
clean:
@rm -f $(OBJ_DIR)/*.o

View File

@ -1,95 +1,110 @@
#ifndef _CONTAINER
#define _CONTAINER
#include "prototype.h"
#define __USE_GNU
#define _GNU_SOURCE
#include <sched.h>
#include <stdarg.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#define STACKSIZE (1024 * 1024)
static char child_stack[STACKSIZE];
/**
* child_exec is the func that will be executed as the result of clone
* Child_exec is the func that will be executed as the result of clone
*/
int child_exec(void *stuff)
int container_create(void *stuff)
{
char rootfs_path[] = "./busybox";
container_t *c = (container_t *)stuff;
struct clone_args *cloneArgs = &c->cloneArgs;
struct clone_args *cloneArgs = &(c->cloneArgs);
if (pivot_root(rootfs_path) != 0)
{
error_handler_container(c, "failed to pivot root");
}
// mount dev, sys, proc into pivot root fs
remount();
if (sethostname(cloneArgs->hostname, strlen(cloneArgs->hostname)) != 0)
{
container_error_handler(c, "fail to set new hostname");
error_handler_container(c, "fail to set new hostname");
}
if (execvp(cloneArgs->argv[0], cloneArgs->argv) != 0)
// run container command
if (run_subprocess_async(cloneArgs->argv) != 0)
{
container_error_handler(c, "failed to execvp arguments\n");
error_handler_container(c, "failed to execvp arguments");
}
// we should never reach here!
exit(EXIT_FAILURE);
free(c); // free twice don't know why but maybe clone lost
c = NULL;
// we end container
return EXIT_SUCCESS;
}
/**
* container constructor initialize container_t object
* Container constructor initialize container_t object
*/
container_t *initialize_container(struct clone_args cloneArgs)
{
container_t *c = (container_t *)malloc(sizeof(container_t));
// in order new net ns, new mount ns, new hostname ns, new pid ns, child ns
c->_cloneFlags = CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD;
c->_cloneFlags = CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWCGROUP;
c->cloneArgs = cloneArgs;
return c;
}
/**
* launch command into container
* Launch command into container
*/
pid_t run(container_t *c)
pid_t run_container(container_t *c)
{
// the result of this call is that our child_exec will be run in another
// the result of this call is that our child_exec will be run_container in another
// process returning it's pid
pid_t pid = clone(child_exec, child_stack + STACKSIZE, c->_cloneFlags, c);
pid_t pid = clone(container_create, child_stack + STACKSIZE, c->_cloneFlags | SIGCHLD, c);
if (pid < 0)
{
container_error_handler(c, "failed to run clone");
error_handler_container(c, "failed to run_container clone");
}
// lets wait on our child process here before we, the parent, exits
if (waitpid(pid, NULL, 0) == -1)
{
container_error_handler(c, "failed to wait pid %d", pid);
exit(EXIT_FAILURE);
error_handler_container(c, "failed to wait pid %d", pid);
}
return pid;
}
/**
* handle error occured during container creation
* Handle error occured during container creation
* this methode permit to call container_destroy
* before printing error
* take at last parameter variadic argument
*/
void container_error_handler(container_t *c, const char *error_msg, ...)
void error_handler_container(container_t *c, const char *error_msg, ...)
{
va_list arg;
va_start(arg, error_msg);
destroy_container(c);
error(error_msg, arg);
va_end(arg);
last_error(error_msg, arg);
}
/**
* container destructor
* Container destructor
*/
void destroy_container(container_t *c)
{
free(c);
}
#endif

71
src/data.c Normal file
View File

@ -0,0 +1,71 @@
/**
* This file group all function which interact with data structure
*/
#ifndef _DATA
#define _DATA
#include "prototype.h"
#include <unistd.h>
/**
* Take array and lengh and fill it with character '\0'
* <b>Returned array should be</b> free
*/
char *new_array(size_t size)
{
char *arr = (char *)malloc(size * sizeof(char) + 1);
if (arr == NULL)
{
error("fatal : failed to alloc array");
}
// fill allocated array with '\0'
init_array(arr, size);
return arr;
}
void init_array(char *arr, size_t size)
{
for (size_t i = 0; i < size; i++)
{
arr[i] = '\0';
}
}
/**
* Read file descriptor without already defined size
* <b>Returned array should be</b> free
*/
char *read_infinite_fd(int fd)
{
int batch_size = 1024;
char *stdout_buffer = new_array(batch_size);
// copy read pipefd to buffer
int already_read = read(fd, stdout_buffer, batch_size);
int current_read = 0;
do
{
already_read += current_read;
char *stdout_buffer_temp = (char *)realloc(stdout_buffer, already_read + batch_size * sizeof(char));
// if realloc faile davoid memory leak free primary pointer
if (stdout_buffer_temp == NULL)
{
free(stdout_buffer);
error("fatal : failed to realloc during pipe child reading");
}
else
{
stdout_buffer = stdout_buffer_temp;
// fill new allocated part with '\0'
init_array(stdout_buffer + already_read, batch_size);
}
current_read = 0;
} while ((current_read = read(fd, stdout_buffer + already_read, batch_size)) != 0);
return stdout_buffer;
}
#endif

View File

@ -13,29 +13,43 @@
#define BREAKLINE "\n"
/**
* print error message and exit program with error
* print error message
*/
void error(const char *sys_msg, va_list arg)
{
FILE *fd = fopen("/dev/stderr", "w");
char *errorTxt = strerror(errno);
char errorNum[5];
sprintf(errorNum, " (%d)", errno);
FILE *fd = fopen("/dev/stderr", "w");
// + 1 is due to strcpy on sys_msg which add \0 at the end of the string
char *concatenated_msg = malloc(
(sizeof(*(errorTxt)) * strlen(errorTxt)) +
(sizeof(*(sys_msg)) * strlen(sys_msg) + 1) +
strlen(SEPARATOR) +
strlen(errorNum) +
strlen(BREAKLINE));
strcpy(concatenated_msg, sys_msg);
strcat(concatenated_msg, SEPARATOR);
strcat(concatenated_msg, errorTxt);
strcat(concatenated_msg, errorNum);
strcat(concatenated_msg, BREAKLINE);
vfprintf(fd, concatenated_msg, arg);
fclose(fd);
free(concatenated_msg);
va_end(arg);
}
/**
* print error message and exit program with error
*/
void last_error(const char *sys_msg, va_list arg)
{
error(sys_msg, arg);
exit(EXIT_FAILURE);
}

View File

@ -2,7 +2,7 @@
#include "prototype.h"
/**
* Main program, run container
* Main program, run_container container
*/
int main(int argc, char *const argv[])
{
@ -16,8 +16,7 @@ int main(int argc, char *const argv[])
cloneArgs.hostname = "container-hostname";
container_t *c = initialize_container(cloneArgs);
run(c);
run_container(c);
destroy_container(c);
exit(EXIT_SUCCESS);

View File

@ -8,12 +8,26 @@
#include "./type.h"
// public functions
void error(const char *sys_msg, ...);
// container.c
container_t *initialize_container(struct clone_args);
pid_t run(container_t *c);
void container_error_handler(container_t *c, const char *error_msg, ...);
pid_t run_container(container_t *c);
void error_handler_container(container_t *c, const char *error_msg, ...);
void destroy_container(container_t *c);
// error.c
void error(const char *sys_msg, ...);
void last_error(const char *sys_msg, ...);
// system.c
int run_subprocess_async(char *const *argv);
int run_subprocess(char *const *argv);
int pivot_root(char *mount_dir);
int clean_pivot_root(char *a);
void remount();
// data.c
char *new_array(size_t size);
void init_array(char *arr, size_t size);
char *read_infinite_fd(int fd);
#endif

164
src/system.c Normal file
View File

@ -0,0 +1,164 @@
/**
* This file group all function which interact with system operation
*/
#ifndef _SYSTEM
#define _SYSTEM
#include "prototype.h"
#include <errno.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#define OLDROOT "/.old"
/**
* Wrapper for pivot root syscall
*/
int pivot_root(char *mount_dir)
{
char *inner_mount_dir = malloc(
(sizeof(*(mount_dir)) * strlen(mount_dir) + 1) +
strlen(OLDROOT));
strcpy(inner_mount_dir, mount_dir);
strcat(inner_mount_dir, OLDROOT);
if (mount("/", "/", "bind", MS_REC | MS_PRIVATE, NULL))
{
error("error remount / with MS_PRIVATE");
}
if (mount(mount_dir, mount_dir, "bind", MS_BIND | MS_REC, NULL) < 0)
{
error("error mount");
}
if (mkdir(inner_mount_dir, 0755) != 0)
{
if (errno != EEXIST)
{
error("error mkdir");
}
}
int syscall_result = syscall(SYS_pivot_root, mount_dir, inner_mount_dir);
// deal with old mount directory
if (umount2(OLDROOT, MNT_DETACH) != 0)
{
error("umount old directory failed");
}
if (rmdir(inner_mount_dir) != 0)
{
error("rm old directory failed");
}
free(inner_mount_dir);
return syscall_result;
}
/**
* Umount pivot root folder
*/
int clean_pivot_root(char *a)
{
if (umount2(a, MNT_DETACH) != 0)
{
error("umount pivot_root directory failed");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/**
* Mount tmpfs into /dev and remount proc into /proc
*/
void remount()
{
// remount devtmpfs device to /dev with type devtmpfs
if (mount(NULL, "/dev", "devtmpfs", MS_NOSUID | MS_STRICTATIME, NULL))
{
error("failed mount tmpfs to /dev");
}
// remount proc device to /proc with type proc
if (mount(NULL, "/proc", "proc", MS_MGC_VAL, NULL))
{
error("failed to re mount proc into /proc");
}
// remount sys device to /sys with type sysfs
if (mount(NULL, "/sys", "sysfs", MS_MGC_VAL, NULL))
{
error("failed to re mount proc into /proc");
}
}
/**
* Run external binary
*/
int run_subprocess(char *const *argv)
{
errno = execvp(argv[0], argv);
return errno;
}
/**
* Run external binary into new sub process
*/
int run_subprocess_async(char *const *argv)
{
pid_t pid;
// pipe to permit child => parent comunication
int pipefd[2];
pipe(pipefd);
if ((pid = fork()) == -1)
{
error("fork() subprocess error");
}
else if (pid == 0)
{
close(pipefd[0]); // close reading end in the child
printf("[CHILD] : running\n");
dup2(pipefd[1], 1); // send stdout to the pipe
dup2(pipefd[1], 2); // send stderr to the pipe
close(pipefd[1]); // this descriptor is no longer needed
errno = execvp(argv[0], argv);
return errno;
}
else
{
int child_status;
close(pipefd[1]); // close the write end of the pipe in the parent
waitpid(pid, &child_status, 0);
if (WIFEXITED(child_status))
{
char *stdout_buffer = read_infinite_fd(pipefd[0]);
printf("[PARENT] child stdout : \n%s\n", stdout_buffer);
free(stdout_buffer);
close(pipefd[0]); // close the descriptor is no longer needed
int es = WEXITSTATUS(child_status);
printf("[PARENT] Exit status was %d\n", es);
return es;
}
}
// reach when wait child failed
return EXIT_FAILURE;
}
#endif