Skip to content

Commit ea9d269

Browse files
committed
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".
1 parent 70efb72 commit ea9d269

3 files changed

Lines changed: 87 additions & 5 deletions

File tree

pkg/filesystem/lazy_directory.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package filesystem
22

33
import (
44
"os"
5+
"sync"
56
"time"
67

78
"github.com/buildbarn/bb-storage/pkg/filesystem"
@@ -245,3 +246,21 @@ func (d *lazyDirectory) Apply(arg interface{}) error {
245246
defer underlying.Close()
246247
return underlying.Apply(arg)
247248
}
249+
250+
func (d *lazyDirectory) Mount(mountpoint path.Component, source string, fstype string, options int) error {
251+
underlying, err := d.openUnderlying()
252+
if err != nil {
253+
return err
254+
}
255+
defer underlying.Close()
256+
return underlying.Mount(mountpoint, source, fstype, options)
257+
}
258+
259+
func (d *lazyDirectory) Unmount(lock *sync.Mutex, mountpoint path.Component) error {
260+
underlying, err := d.openUnderlying()
261+
if err != nil {
262+
return err
263+
}
264+
defer underlying.Close()
265+
return underlying.Unmount(lock, mountpoint)
266+
}

pkg/runner/BUILD.bazel

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,27 @@ go_library(
2929
"@org_golang_google_protobuf//proto",
3030
"@org_golang_google_protobuf//types/known/anypb",
3131
"@org_golang_google_protobuf//types/known/emptypb",
32+
"@org_golang_x_sys//unix",
3233
] + select({
3334
"@io_bazel_rules_go//go/platform:android": [
3435
"//pkg/proto/resourceusage",
3536
"@org_golang_google_protobuf//types/known/durationpb",
36-
"@org_golang_x_sys//unix",
3737
],
3838
"@io_bazel_rules_go//go/platform:darwin": [
3939
"//pkg/proto/resourceusage",
4040
"@org_golang_google_protobuf//types/known/durationpb",
41-
"@org_golang_x_sys//unix",
4241
],
4342
"@io_bazel_rules_go//go/platform:freebsd": [
4443
"//pkg/proto/resourceusage",
4544
"@org_golang_google_protobuf//types/known/durationpb",
46-
"@org_golang_x_sys//unix",
4745
],
4846
"@io_bazel_rules_go//go/platform:ios": [
4947
"//pkg/proto/resourceusage",
5048
"@org_golang_google_protobuf//types/known/durationpb",
51-
"@org_golang_x_sys//unix",
5249
],
5350
"@io_bazel_rules_go//go/platform:linux": [
5451
"//pkg/proto/resourceusage",
5552
"@org_golang_google_protobuf//types/known/durationpb",
56-
"@org_golang_x_sys//unix",
5753
],
5854
"@io_bazel_rules_go//go/platform:windows": [
5955
"//pkg/proto/resourceusage",

pkg/runner/local_runner.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,39 @@ package runner
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"os/exec"
78
"path/filepath"
9+
"strings"
10+
"sync"
811

912
"github.com/buildbarn/bb-remote-execution/pkg/proto/runner"
1013
"github.com/buildbarn/bb-storage/pkg/filesystem"
1114
"github.com/buildbarn/bb-storage/pkg/filesystem/path"
1215
"github.com/buildbarn/bb-storage/pkg/util"
16+
"golang.org/x/sys/unix"
1317

1418
"google.golang.org/grpc/codes"
1519
"google.golang.org/grpc/status"
1620
"google.golang.org/protobuf/types/known/anypb"
1721
"google.golang.org/protobuf/types/known/emptypb"
1822
)
1923

24+
// Mount information for the special filesystems
25+
// to mount in the input root when using 'chroot'.
26+
var (
27+
procMountpointComponent = path.MustNewComponent("proc")
28+
sysMountpointComponent = path.MustNewComponent("sys")
29+
mounts = []struct {
30+
mountpoint path.Component
31+
fstype string
32+
source string
33+
}{
34+
{procMountpointComponent, "proc", "/proc"},
35+
{sysMountpointComponent, "sysfs", "/sys"},
36+
}
37+
)
38+
2039
// logFileResolver is an implementation of path.ComponentWalker that is
2140
// used by localRunner.Run() to traverse to the directory of stdout and
2241
// stderr log files, so that they may be opened.
@@ -65,6 +84,7 @@ type localRunner struct {
6584
buildDirectory filesystem.Directory
6685
buildDirectoryPath *path.Builder
6786
commandCreator CommandCreator
87+
chdirLock *sync.Mutex
6888
setTmpdirEnvironmentVariable bool
6989
}
7090

@@ -95,6 +115,7 @@ func NewLocalRunner(buildDirectory filesystem.Directory, buildDirectoryPath *pat
95115
buildDirectory: buildDirectory,
96116
buildDirectoryPath: buildDirectoryPath,
97117
commandCreator: commandCreator,
118+
chdirLock: &sync.Mutex{},
98119
setTmpdirEnvironmentVariable: setTmpdirEnvironmentVariable,
99120
}
100121
}
@@ -136,6 +157,44 @@ func (r *localRunner) Run(ctx context.Context, request *runner.RunRequest) (*run
136157
}
137158
cmd.Stdout = stdout
138159

160+
// Mount special filesystems.
161+
requiresSpecialFilesystems := cmd.SysProcAttr != nil && cmd.SysProcAttr.Chroot != ""
162+
var MountRootDirectory filesystem.Directory
163+
164+
if requiresSpecialFilesystems {
165+
var dir filesystem.Directory
166+
dir = r.buildDirectory
167+
// TODO(nils): How to best open the input root `Directory`
168+
// It must be a `localDirectory`, but outer directories can be `lazyDirectory`.
169+
for _, segment := range strings.Split(request.InputRootDirectory, "/") {
170+
component := path.MustNewComponent(segment)
171+
dircloser, err := dir.EnterDirectory(component)
172+
// We want to keep the input root open so we can use its file descriptor.
173+
if segment != "root" {
174+
defer dircloser.Close()
175+
}
176+
177+
dir = dircloser.(filesystem.Directory)
178+
if err != nil {
179+
return nil, fmt.Errorf("Could not enter directory component '%s' in '%#v'", segment, inputRootDirectory)
180+
}
181+
}
182+
MountRootDirectory = dir
183+
184+
for _, mount := range mounts {
185+
err := MountRootDirectory.Mkdir(mount.mountpoint, 0o555)
186+
if err != nil {
187+
if err != unix.EEXIST {
188+
return nil, util.StatusWrapf(err, "Failed to create mount point: '%#v' in the input root", mount.mountpoint)
189+
}
190+
}
191+
192+
if err := MountRootDirectory.Mount(mount.mountpoint, mount.source, mount.fstype, 0); err != nil {
193+
return nil, util.StatusWrapf(err, "Failed to mount '%#v' in the input root", mount)
194+
}
195+
}
196+
}
197+
139198
stderr, err := r.openLog(request.StderrPath)
140199
if err != nil {
141200
stdout.Close()
@@ -166,6 +225,14 @@ func (r *localRunner) Run(ctx context.Context, request *runner.RunRequest) (*run
166225
}
167226
}
168227

228+
if requiresSpecialFilesystems {
229+
for _, mount := range mounts {
230+
if err := MountRootDirectory.Unmount(r.chdirLock, mount.mountpoint); err != nil {
231+
return nil, util.StatusWrapf(err, "Failed to unmount '%#v' in the input root", mount)
232+
}
233+
}
234+
}
235+
169236
// Attach rusage information to the response.
170237
posixResourceUsage, err := anypb.New(getPOSIXResourceUsage(cmd))
171238
if err != nil {

0 commit comments

Comments
 (0)