【问题标题】:How to provide file isolation using linux namespace如何使用 linux 命名空间提供文件隔离
【发布时间】:2020-04-18 00:08:55
【问题描述】:

我试图在两个 linux 命名空间中运行相同的程序。

程序需要读写文件/tmp/server.log。

所以我想确保程序A读/写server.log,但实际上它读和写/tmp/server-A.log。而对于程序B读写server.log,其实就是读写/tmp/server-B.log。

我尝试使用 mount 但没有成功...有人可以帮助我吗?或者我有没有另一种方法来提供文件隔离,这样两个程序就不会真正读/写同一个文件?

#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;
}

更新:我根据下面回答的解决方案解决了问题!他们的解决方案对我很有帮助!

【问题讨论】:

  • 这里有这么多问题,但我先从大的开始:为什么要使用Linux命名空间来解决这样一个简单的问题,而不是仅仅传递文件名作为参数写入?
  • 我有点困惑。如果你想让一个子进程写入/tmp/server-A.log,另一个写入/tmp/server-B.log,为什么不简单地设置一个变量,比如char suffix = 'A'; fork第一个进程,然后设置suffix = 'B';并fork第二个,然后在每个进程使用suffix创建文件名?有很多方法可以做到这一点。如果你想要更多,那么#define SUFFIX "ABCDEFGHIJKLMNOPQRSTUVWXYZ",然后保持一个计数器和循环分叉SUFFIX[n++],或类似的东西。
  • 我相信也有 mount 系统调用,不需要用 system() 调用 shell:man7.org/linux/man-pages/man2/mount.2.html
  • child_func其实是在调用python脚本,我无法修改python脚本来指定写入路径,我这里只是用一个简单的函数来演示一下@JosephSible-ReinstateMonica的案例跨度>
  • @David C. Rankin 我做不到,我只是写了这个愚蠢的函数来显示我需要做什么

标签: c linux linux-namespaces


【解决方案1】:

你想要这样的东西:

#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;
}

几点说明:

  • 正确使用clone(你没有使用)是一种痛苦。使用fork 然后unshare 会更容易。
  • systemd 愚蠢地makes mounts shared by default,这基本上使挂载命名空间什么都不做(即,更改将传播回其他命名空间,从而破坏了私有命名空间的目的)。 mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) 将其撤消以使它们真正起作用。
  • 我不确定您尝试绑定什么,但这是错误的。正确的做法是将单个日志挂载到共享名称。

【讨论】:

  • 非常感谢!!!这就是我需要的,我使用你的想法并成功实现我想要的。在我添加 unshare 函数调用并正确使用 mount 后,我​​得到了我想要的!
  • 我是 StackOverflow 新手,谢谢提醒!
【解决方案2】:
  1. 您需要将 root 重新挂载到私有。检查这个link

  2. 当你做--bind时,你需要反过来做。

static int child_func(void* arg) {
  mount("/", "/", NULL, MS_PRIVATE, NULL);
  mount("./a", "/tmp", NULL, MS_BIND, NULL);

  FILE* file;
  file = fopen("/tmp/server.log","w");

  return 0;
}
static int child_func(void* arg) {
  mount("/", "/", NULL, MS_PRIVATE, NULL);
  mount("./b", "/tmp", NULL, MS_BIND, NULL);

  FILE* file;
  file = fopen("/tmp/server.log","w");

  return 0;
}

【讨论】:

  • 是的,你是对的,我应该将 root 重新挂载到私有。但我认为我们也应该在挂载之前调用 unshare(CLONE_NEWNS) 。我首先尝试了您的想法,但它仍然无法正常工作,在我尝试将@Joseph Sible-Reinstate Monica 的想法与您结合后,我得到了我想要的。谢谢!!!
  • 如果您使用cloneCLONE_NEWNS,应该没问题。我在 Centos7.5 中测试。
  • 可能是因为我用的是Ubuntu,还是谢谢你!
  • 我再次测试,发现使用clone with CLONE_NEWS不需要在函数中再次调用unshare。可能背后的原因是我们需要在使用 MS_PRIVATE 挂载“/”时添加 MS_REC
猜你喜欢
  • 2017-09-23
  • 2015-03-09
  • 2012-04-05
  • 2015-07-11
  • 1970-01-01
  • 2012-05-01
  • 2021-07-16
  • 2016-02-12
  • 1970-01-01
相关资源
最近更新 更多