clinuxlinux-namespaces

How to provide file isolation using linux namespace


I am trying to run the same program in two linux namespace.

The program needs to read and write the file /tmp/server.log.

So I want to make sure that program A read/write server.log, but actually it reads and writes /tmp/server-A.log. And for program B read/write server.log, it is actually reading and writing /tmp/server-B.log.

I try to use mount but not succeeded ... Can anyone help me? Or is there another way for me to provide file isolation so that the two programs will not actually read/write the same file?

#define _GNU_SOURCE
#include<sched.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>

static int child_func(void* arg) {
  system("mount --bind /tmp ./a");
  FILE* file;
  file = fopen("/tmp/server.log","rw");
  // write some log ...
  return 0;
}

static int child2_func(void* arg) {
  system("mount --bind /tmp ./b");
  file = fopen("/tmp/server.log","rw");
  // write some log.... 
  return 0;
}


int main(int argc, char** argv) {
  // Allocate stack for child task.
  const int STACK_SIZE = 1 * 1024 * 1024;
  char* stack = malloc(STACK_SIZE);
  char* stack2 = malloc(STACK_SIZE);
  if (!stack || !stack2) {
    perror("malloc");
    exit(1);
  }
  pid_t pid,pid2;


  if ((pid = clone(child_func, stack + STACK_SIZE, CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWNET | SIGCHLD, NULL)) == -1) {
    perror("clone");
    exit(1);
  }

  if ((pid2 = clone(child2_func, stack2 + STACK_SIZE, CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWNET | SIGCHLD, NULL)) == -1) {
    perror("clone");
    exit(1);
  }


  waitpid(pid,NULL,0);
  waitpid(pid2,NULL,0);


  return 0;
}

Update: I solve the problem based on the solutions answered below! Their solutions really help me!


Solution

  • You want something like this:

    #define _GNU_SOURCE
    
    #include <sched.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/mount.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    int doChild(const char *source) {
        if(unshare(CLONE_NEWNS)) {
            perror("unshare");
            return 1;
        }
        if(mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
            perror("mount");
            return 1;
        }
        if(mount(source, "/tmp/server.log", NULL, MS_BIND, NULL)) {
            perror("mount");
            return 1;
        }
        execlp("myunmodifiablepythonscript", "myunmodifiablepythonscript", (char*)NULL);
        perror("execlp");
        return 1;
    }
    
    int main(void) {
        pid_t pidA, pidB;
        pidA = fork();
        if(pidA < 0) {
            perror("fork");
            return 1;
        } else if(pidA == 0) {
            return doChild("/tmp/server-A.log");
        }
        pidB = fork();
        if(pidB < 0) {
            perror("fork");
            /* n.b.: pidA will still be running as an orphan. */
            return 1;
        } else if(pidB == 0) {
            return doChild("/tmp/server-B.log");
        }
        waitpid(pidA, NULL, 0);
        /* n.b.: if pidB finishes first, it will be a zombie until pidA finishes. */
        waitpid(pidB, NULL, 0);
        return 0;
    }
    

    A few notes: