Skip to content

Commit

Permalink
Mount special filesystem in chroot runners
Browse files Browse the repository at this point in the history
This mounts '/proc' and '/sys' in the input root of runners
if 'chroot' is enabled. It is done with "at"-syscall semantics in the
'Directory', to avoid side effects. A program-scope mutex is required
for unmounting because there is no easy-to-use "unmountat".
  • Loading branch information
stagnation committed Sep 12, 2023
1 parent 70efb72 commit 0172979
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
18 changes: 18 additions & 0 deletions pkg/filesystem/lazy_directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,21 @@ func (d *lazyDirectory) Apply(arg interface{}) error {
defer underlying.Close()
return underlying.Apply(arg)
}

func (d *lazyDirectory) Mount(mountpoint path.Component, source string, fstype string) error {
underlying, err := d.openUnderlying()
if err != nil {
return err
}
defer underlying.Close()
return underlying.Mount(mountpoint, source, fstype)
}

func (d *lazyDirectory) Unmount(mountpoint path.Component) error {
underlying, err := d.openUnderlying()
if err != nil {
return err
}
defer underlying.Close()
return underlying.Unmount(mountpoint)
}
6 changes: 1 addition & 5 deletions pkg/runner/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,27 @@ go_library(
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/anypb",
"@org_golang_google_protobuf//types/known/emptypb",
"@org_golang_x_sys//unix",
] + select({
"@io_bazel_rules_go//go/platform:android": [
"//pkg/proto/resourceusage",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_x_sys//unix",
],
"@io_bazel_rules_go//go/platform:darwin": [
"//pkg/proto/resourceusage",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_x_sys//unix",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"//pkg/proto/resourceusage",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_x_sys//unix",
],
"@io_bazel_rules_go//go/platform:ios": [
"//pkg/proto/resourceusage",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_x_sys//unix",
],
"@io_bazel_rules_go//go/platform:linux": [
"//pkg/proto/resourceusage",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_x_sys//unix",
],
"@io_bazel_rules_go//go/platform:windows": [
"//pkg/proto/resourceusage",
Expand Down
64 changes: 64 additions & 0 deletions pkg/runner/local_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,38 @@ package runner
import (
"context"
"errors"
"fmt"
"os/exec"
"path/filepath"
"strings"

"github.com/buildbarn/bb-remote-execution/pkg/proto/runner"
"github.com/buildbarn/bb-storage/pkg/filesystem"
"github.com/buildbarn/bb-storage/pkg/filesystem/path"
"github.com/buildbarn/bb-storage/pkg/util"
"golang.org/x/sys/unix"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/emptypb"
)

// Mount information for the special filesystems
// to mount in the input root when using 'chroot'.
var (
procMountpointComponent = path.MustNewComponent("proc")
sysMountpointComponent = path.MustNewComponent("sys")
mounts = []struct {
mountpoint path.Component
fstype string
source string
}{
{procMountpointComponent, "proc", "/proc"},
{sysMountpointComponent, "sysfs", "/sys"},
}
)

// logFileResolver is an implementation of path.ComponentWalker that is
// used by localRunner.Run() to traverse to the directory of stdout and
// stderr log files, so that they may be opened.
Expand Down Expand Up @@ -136,6 +154,44 @@ func (r *localRunner) Run(ctx context.Context, request *runner.RunRequest) (*run
}
cmd.Stdout = stdout

// Mount special filesystems.
requiresSpecialFilesystems := cmd.SysProcAttr != nil && cmd.SysProcAttr.Chroot != ""
var MountRootDirectory filesystem.Directory

if requiresSpecialFilesystems {
var dir filesystem.Directory
dir = r.buildDirectory
// TODO(nils): How to best open the input root `Directory`
// It must be a `localDirectory`, but outer directories can be `lazyDirectory`.
for _, segment := range strings.Split(request.InputRootDirectory, "/") {
component := path.MustNewComponent(segment)
dircloser, err := dir.EnterDirectory(component)
// We want to keep the input root open so we can use its file descriptor.
if segment != "root" {
defer dircloser.Close()
}

dir = dircloser.(filesystem.Directory)
if err != nil {
return nil, fmt.Errorf("Could not enter directory component '%s' in '%#v'", segment, inputRootDirectory)
}
}
MountRootDirectory = dir

for _, mount := range mounts {
err := MountRootDirectory.Mkdir(mount.mountpoint, 0o555)
if err != nil {
if err != unix.EEXIST {
return nil, util.StatusWrapf(err, "Failed to create mount point: '%#v' in the input root", mount.mountpoint)
}
}

if err := MountRootDirectory.Mount(mount.mountpoint, mount.source, mount.fstype); err != nil {
return nil, util.StatusWrapf(err, "Failed to mount '%#v' in the input root", mount)
}
}
}

stderr, err := r.openLog(request.StderrPath)
if err != nil {
stdout.Close()
Expand Down Expand Up @@ -166,6 +222,14 @@ func (r *localRunner) Run(ctx context.Context, request *runner.RunRequest) (*run
}
}

if requiresSpecialFilesystems {
for _, mount := range mounts {
if err := MountRootDirectory.Unmount(mount.mountpoint); err != nil {
return nil, util.StatusWrapf(err, "Failed to unmount '%#v' in the input root", mount)
}
}
}

// Attach rusage information to the response.
posixResourceUsage, err := anypb.New(getPOSIXResourceUsage(cmd))
if err != nil {
Expand Down

0 comments on commit 0172979

Please sign in to comment.