diff --git a/pkg/filesystem/lazy_directory.go b/pkg/filesystem/lazy_directory.go index 78abceb6..c6232ce7 100644 --- a/pkg/filesystem/lazy_directory.go +++ b/pkg/filesystem/lazy_directory.go @@ -2,6 +2,7 @@ package filesystem import ( "os" + "sync" "time" "github.com/buildbarn/bb-storage/pkg/filesystem" @@ -245,3 +246,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, options int) error { + underlying, err := d.openUnderlying() + if err != nil { + return err + } + defer underlying.Close() + return underlying.Mount(mountpoint, source, fstype, options) +} + +func (d *lazyDirectory) Unmount(lock *sync.Mutex, mountpoint path.Component) error { + underlying, err := d.openUnderlying() + if err != nil { + return err + } + defer underlying.Close() + return underlying.Unmount(lock, mountpoint) +} diff --git a/pkg/runner/BUILD.bazel b/pkg/runner/BUILD.bazel index f134f66a..33d33f87 100644 --- a/pkg/runner/BUILD.bazel +++ b/pkg/runner/BUILD.bazel @@ -29,6 +29,7 @@ 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", diff --git a/pkg/runner/local_runner.go b/pkg/runner/local_runner.go index 34ac73a1..048a9944 100644 --- a/pkg/runner/local_runner.go +++ b/pkg/runner/local_runner.go @@ -3,14 +3,18 @@ package runner import ( "context" "errors" + "fmt" "os/exec" "path/filepath" + "strings" + "sync" "syscall" "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" @@ -18,6 +22,21 @@ import ( "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. @@ -66,6 +85,7 @@ type localRunner struct { buildDirectory filesystem.Directory buildDirectoryPath *path.Builder commandCreator CommandCreator + chdirLock *sync.Mutex setTmpdirEnvironmentVariable bool } @@ -106,6 +126,7 @@ func NewLocalRunner(buildDirectory filesystem.Directory, buildDirectoryPath *pat buildDirectory: buildDirectory, buildDirectoryPath: buildDirectoryPath, commandCreator: commandCreator, + chdirLock: &sync.Mutex{}, setTmpdirEnvironmentVariable: setTmpdirEnvironmentVariable, } } @@ -151,6 +172,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, 0); 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() @@ -181,6 +240,14 @@ func (r *localRunner) Run(ctx context.Context, request *runner.RunRequest) (*run } } + if requiresSpecialFilesystems { + for _, mount := range mounts { + if err := MountRootDirectory.Unmount(r.chdirLock, 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 {