コード例 #1
0
def _setup_container_filesystem(temp_dir, dir_modes, container_system_config):
    # We put all temp files on a RAM disk
    libc.mount(None, temp_dir, b"tmpfs", 0, b"size=100%")

    mount_base = os.path.join(temp_dir,
                              b"mount")  # base dir for container mounts
    temp_base = os.path.join(temp_dir, b"temp")  # upper layer for overlayfs
    work_base = os.path.join(temp_dir, b"overlayfs")  # work dir for overlayfs
    os.mkdir(mount_base)
    os.mkdir(temp_base)
    os.mkdir(work_base)
    container.duplicate_mount_hierarchy(mount_base, temp_base, work_base,
                                        dir_modes)

    def make_tmpfs_dir(path):
        """Ensure that a tmpfs is mounted on path, if the path exists"""
        if path in dir_modes:
            return  # explicitly configured by user
        mount_tmpfs = mount_base + path
        if os.path.isdir(mount_tmpfs):
            temp_tmpfs = temp_base + path
            util.makedirs(temp_tmpfs, exist_ok=True)
            container.make_bind_mount(temp_tmpfs, mount_tmpfs)

    make_tmpfs_dir(b"/dev/shm")
    make_tmpfs_dir(b"/run/shm")

    if container_system_config:
        container.setup_container_system_config(temp_base, mount_base,
                                                dir_modes)

    cwd = os.getcwd()
    container.chroot(mount_base)
    os.chdir(cwd)
コード例 #2
0
    def _setup_container_filesystem(self, temp_dir, output_dir, memlimit,
                                    memory_nodes):
        """Setup the filesystem layout in the container.
        As first step, we create a copy of all existing mountpoints in mount_base,
        recursively, and as "private" mounts
        (i.e., changes to existing mountpoints afterwards won't propagate to our copy).
        Then we iterate over all mountpoints and change them according to the mode
        the user has specified (hidden, read-only, overlay, or full-access).
        This has do be done for each mountpoint because overlays are not recursive.
        Then we chroot into the new mount hierarchy.

        The new filesystem layout still has a view of the host's /proc. We do not mount
        a fresh /proc here because the grandchild still needs the old /proc.

        We do simply iterate over all existing mount points and set them to
        read-only/overlay them, because it is easier to create a new hierarchy and
        chroot into it. First, we still have access to the original mountpoints while
        doing so, and second, we avoid race conditions if someone else changes the
        existing mountpoints.

        @param temp_dir:
            The base directory under which all our directories should be created.
        """
        # All strings here are bytes to avoid issues
        # if existing mountpoints are invalid UTF-8.

        # directory with files created by tool
        temp_base = self._get_result_files_base(temp_dir).encode()
        temp_dir = temp_dir.encode()

        tmpfs_opts = ["size=" + str(memlimit or "100%")]
        if memory_nodes:
            tmpfs_opts.append("mpol=bind:" + ",".join(map(str, memory_nodes)))
        tmpfs_opts = (",".join(tmpfs_opts)).encode()
        if self._container_tmpfs:
            libc.mount(None, temp_dir, b"tmpfs", 0, tmpfs_opts)

        mount_base = os.path.join(temp_dir,
                                  b"mount")  # base dir for container mounts
        os.mkdir(mount_base)
        os.mkdir(temp_base)

        # Overlayfs needs its own additional temporary directory ("work" directory).
        # temp_base will be the "upper" layer, the host FS the "lower" layer,
        # and mount_base the mount target.
        work_base = os.path.join(temp_dir, b"overlayfs")
        os.mkdir(work_base)

        # Copy all mounts to mount_base and apply directory modes
        container.duplicate_mount_hierarchy(mount_base, temp_base, work_base,
                                            self._dir_modes)

        # Now configure some special hard-coded cases

        def make_tmpfs_dir(path):
            """Ensure that a tmpfs is mounted on path, if the path exists"""
            if path in self._dir_modes:
                return  # explicitly configured by user
            mount_tmpfs = mount_base + path
            temp_tmpfs = temp_base + path
            os.makedirs(temp_tmpfs, exist_ok=True)
            if os.path.isdir(mount_tmpfs):
                # If we already have a tmpfs, we can just bind mount it,
                # otherwise we need one
                if self._container_tmpfs:
                    container.make_bind_mount(temp_tmpfs, mount_tmpfs)
                else:
                    libc.mount(None, mount_tmpfs, b"tmpfs", 0, tmpfs_opts)

        # The following directories should be writable RAM disks
        # for Posix shared memory. For example, the Python multiprocessing module
        # explicitly checks for a tmpfs instance.
        make_tmpfs_dir(b"/dev/shm")
        make_tmpfs_dir(b"/run/shm")

        if self._container_system_config:
            container.setup_container_system_config(temp_base, mount_base,
                                                    self._dir_modes)

        if output_dir:
            # We need a way to see temp_base in the container in order to be able to
            # copy result files out of it, so we need a directory that is guaranteed to
            # exist in order to use it as mountpoint for a bind mount to temp_base.
            # Of course, the tool inside the container should not have access to
            # temp_base, so we will add another bind mount with an empty directory on
            # top (equivalent to --hidden-dir). After the tool terminates we can unmount
            # the top-level bind mount and then access temp_base. However, this works
            # only if there is no other mount point below that directory, and the user
            # can force us to create mount points at arbitrary directory if a directory
            # mode is specified. So we need an existing directory with no mount points
            # below, and luckily temp_dir fulfills all requirements (because we have
            # just created it as fresh drectory ourselves).
            # So we mount temp_base outside of the container to temp_dir inside.
            os.makedirs(mount_base + temp_dir, exist_ok=True)
            container.make_bind_mount(temp_base,
                                      mount_base + temp_dir,
                                      read_only=True)
            # And the following if branch will automatically hide the bind
            # mount below an empty directory.

        # If necessary, (i.e., if /tmp is not already hidden),
        # hide the directory where we store our files from processes in the container
        # by mounting an empty directory over it.
        if os.path.exists(mount_base + temp_dir):
            os.makedirs(temp_base + temp_dir, exist_ok=True)
            container.make_bind_mount(temp_base + temp_dir,
                                      mount_base + temp_dir)

        # Now we make mount_base the new root directory.
        container.chroot(mount_base)