From 01729791c366b6d713bf4f5e6c706cb274292539 Mon Sep 17 00:00:00 2001 From: Nils Wireklint Date: Mon, 4 Sep 2023 14:05:14 +0200 Subject: [PATCH] Mount special filesystem in chroot runners 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". --- pkg/filesystem/lazy_directory.go | 18 +++++++++ pkg/runner/BUILD.bazel | 6 +-- pkg/runner/local_runner.go | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/pkg/filesystem/lazy_directory.go b/pkg/filesystem/lazy_directory.go index 78abceb6..96de17e3 100644 --- a/pkg/filesystem/lazy_directory.go +++ b/pkg/filesystem/lazy_directory.go @@ -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) +} diff --git a/pkg/runner/BUILD.bazel b/pkg/runner/BUILD.bazel index de0c7fe9..e10780e6 100644 --- a/pkg/runner/BUILD.bazel +++ b/pkg/runner/BUILD.bazel @@ -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", diff --git a/pkg/runner/local_runner.go b/pkg/runner/local_runner.go index 266450ab..d70d3285 100644 --- a/pkg/runner/local_runner.go +++ b/pkg/runner/local_runner.go @@ -3,13 +3,16 @@ 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" @@ -17,6 +20,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. @@ -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() @@ -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 {