diff --git a/BUILD.bazel b/BUILD.bazel index 704e23e5..d658a078 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,6 +1,6 @@ load("@bazel_gazelle//:def.bzl", "gazelle") load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier") -load("@npm//:defs.bzl", "npm_link_all_packages", "npm_link_targets") +load("@npm//:defs.bzl", "npm_link_all_packages") # gazelle:prefix github.com/buildbarn/bb-storage # gazelle:resolve proto build/bazel/remote/execution/v2/remote_execution.proto @com_github_bazelbuild_remote_apis//build/bazel/remote/execution/v2:remote_execution_proto diff --git a/go_dependencies.bzl b/go_dependencies.bzl index 770456d7..494b263b 100644 --- a/go_dependencies.bzl +++ b/go_dependencies.bzl @@ -1551,6 +1551,7 @@ def go_dependencies(): importpath = "golang.org/x/sys", sum = "h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=", version = "v0.12.0", + patches = ["//:golang-issue-59357.diff"], ) go_repository( name = "org_golang_x_term", diff --git a/patches/org_golang_x_sys/golang-issue-59357.diff b/patches/org_golang_x_sys/golang-issue-59357.diff new file mode 100644 index 00000000..2653c053 --- /dev/null +++ b/patches/org_golang_x_sys/golang-issue-59357.diff @@ -0,0 +1,181 @@ +From 9313951415ca77dcf5e84ba5fa41483b5c16d121 Mon Sep 17 00:00:00 2001 +From: Ilya Hanov +Date: Sat, 30 Sep 2023 12:02:48 +0300 +Subject: [PATCH] unix: add API for fsconfig system call + +Fixes golang/go#59537 + +Change-Id: I8d806ace3adad423c633813455d8f758706cee1d +--- + +diff --git unix/linux/types.go unix/linux/types.go +index 25b9279..b9419f3 100644 +--- unix/linux/types.go ++++ unix/linux/types.go +@@ -972,6 +972,15 @@ + FSPICK_EMPTY_PATH = C.FSPICK_EMPTY_PATH + + FSMOUNT_CLOEXEC = C.FSMOUNT_CLOEXEC ++ ++ FSCONFIG_SET_FLAG = C.FSCONFIG_SET_FLAG ++ FSCONFIG_SET_STRING = C.FSCONFIG_SET_STRING ++ FSCONFIG_SET_BINARY = C.FSCONFIG_SET_BINARY ++ FSCONFIG_SET_PATH = C.FSCONFIG_SET_PATH ++ FSCONFIG_SET_PATH_EMPTY = C.FSCONFIG_SET_PATH_EMPTY ++ FSCONFIG_SET_FD = C.FSCONFIG_SET_FD ++ FSCONFIG_CMD_CREATE = C.FSCONFIG_CMD_CREATE ++ FSCONFIG_CMD_RECONFIGURE = C.FSCONFIG_CMD_RECONFIGURE + ) + + type OpenHow C.struct_open_how +diff --git unix/syscall_linux.go unix/syscall_linux.go +index d844b16..80aadf2 100644 +--- unix/syscall_linux.go ++++ unix/syscall_linux.go +@@ -1840,6 +1840,105 @@ + //sys Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) + //sys Fsopen(fsName string, flags int) (fd int, err error) + //sys Fspick(dirfd int, pathName string, flags int) (fd int, err error) ++ ++//sys fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) ++ ++func fsconfigCommon(fd int, cmd uint, key string, value *byte, aux int) (err error) { ++ var keyp *byte ++ if keyp, err = BytePtrFromString(key); err != nil { ++ return ++ } ++ return fsconfig(fd, cmd, keyp, value, aux) ++} ++ ++// FsconfigSetFlag is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_FLAG. ++// ++// fd is the filesystem context to act upon. ++// key the parameter key to set. ++func FsconfigSetFlag(fd int, key string) (err error) { ++ return fsconfigCommon(fd, FSCONFIG_SET_FLAG, key, nil, 0) ++} ++ ++// FsconfigSetString is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_STRING. ++// ++// fd is the filesystem context to act upon. ++// key the parameter key to set. ++// value is the parameter value to set. ++func FsconfigSetString(fd int, key string, value string) (err error) { ++ var valuep *byte ++ if valuep, err = BytePtrFromString(value); err != nil { ++ return ++ } ++ return fsconfigCommon(fd, FSCONFIG_SET_STRING, key, valuep, 0) ++} ++ ++// FsconfigSetBinary is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_BINARY. ++// ++// fd is the filesystem context to act upon. ++// key the parameter key to set. ++// value is the parameter value to set. ++func FsconfigSetBinary(fd int, key string, value []byte) (err error) { ++ if len(value) == 0 { ++ return EINVAL ++ } ++ return fsconfigCommon(fd, FSCONFIG_SET_BINARY, key, &value[0], len(value)) ++} ++ ++// FsconfigSetPath is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_PATH. ++// ++// fd is the filesystem context to act upon. ++// key the parameter key to set. ++// path is a non-empty path for specified key. ++// atfd is a file descriptor at which to start lookup from or AT_FDCWD. ++func FsconfigSetPath(fd int, key string, path string, atfd int) (err error) { ++ var valuep *byte ++ if valuep, err = BytePtrFromString(path); err != nil { ++ return ++ } ++ return fsconfigCommon(fd, FSCONFIG_SET_PATH, key, valuep, atfd) ++} ++ ++// FsconfigSetPathEmpty is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_PATH_EMPTY. The same as ++// FconfigSetPath but with AT_PATH_EMPTY implied. ++func FsconfigSetPathEmpty(fd int, key string, path string, atfd int) (err error) { ++ var valuep *byte ++ if valuep, err = BytePtrFromString(path); err != nil { ++ return ++ } ++ return fsconfigCommon(fd, FSCONFIG_SET_PATH_EMPTY, key, valuep, atfd) ++} ++ ++// FsconfigSetFd is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_SET_FD. ++// ++// fd is the filesystem context to act upon. ++// key the parameter key to set. ++// value is a file descriptor to be assigned to specified key. ++func FsconfigSetFd(fd int, key string, value int) (err error) { ++ return fsconfigCommon(fd, FSCONFIG_SET_FD, key, nil, value) ++} ++ ++// FsconfigCreate is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_CMD_CREATE. ++// ++// fd is the filesystem context to act upon. ++func FsconfigCreate(fd int) (err error) { ++ return fsconfig(fd, FSCONFIG_CMD_CREATE, nil, nil, 0) ++} ++ ++// FsconfigReconfigure is equivalent to fsconfig(2) called ++// with cmd == FSCONFIG_CMD_RECONFIGURE. ++// ++// fd is the filesystem context to act upon. ++func FsconfigReconfigure(fd int) (err error) { ++ return fsconfig(fd, FSCONFIG_CMD_RECONFIGURE, nil, nil, 0) ++} ++ + //sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64 + //sysnb Getpgid(pid int) (pgid int, err error) + +diff --git unix/zsyscall_linux.go unix/zsyscall_linux.go +index 14ab34a..dd1720f 100644 +--- unix/zsyscall_linux.go ++++ unix/zsyscall_linux.go +@@ -892,6 +892,16 @@ + + // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + ++func fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) { ++ _, _, e1 := Syscall6(SYS_FSCONFIG, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(value)), uintptr(aux), 0) ++ if e1 != 0 { ++ err = errnoErr(e1) ++ } ++ return ++} ++ ++// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT ++ + func Getdents(fd int, buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { +diff --git unix/ztypes_linux.go unix/ztypes_linux.go +index 18aa70b..3c9bfa2 100644 +--- unix/ztypes_linux.go ++++ unix/ztypes_linux.go +@@ -833,6 +833,15 @@ + FSPICK_EMPTY_PATH = 0x8 + + FSMOUNT_CLOEXEC = 0x1 ++ ++ FSCONFIG_SET_FLAG = 0x0 ++ FSCONFIG_SET_STRING = 0x1 ++ FSCONFIG_SET_BINARY = 0x2 ++ FSCONFIG_SET_PATH = 0x3 ++ FSCONFIG_SET_PATH_EMPTY = 0x4 ++ FSCONFIG_SET_FD = 0x5 ++ FSCONFIG_CMD_CREATE = 0x6 ++ FSCONFIG_CMD_RECONFIGURE = 0x7 + ) + + type OpenHow struct { diff --git a/pkg/filesystem/directory.go b/pkg/filesystem/directory.go index 030861b3..65927ec7 100644 --- a/pkg/filesystem/directory.go +++ b/pkg/filesystem/directory.go @@ -112,6 +112,10 @@ type Directory interface { // Function that base types may use to implement calls that // require double dispatching, such as hardlinking and renaming. Apply(arg interface{}) error + + // Mount and Unmount. + Mount(mountpoint path.Component, source string, fstype string) error + Unmount(mountpoint path.Component) error } // DirectoryCloser is a Directory handle that can be released. diff --git a/pkg/filesystem/local_directory_darwin.go b/pkg/filesystem/local_directory_darwin.go index 6048dbd9..54e18aae 100644 --- a/pkg/filesystem/local_directory_darwin.go +++ b/pkg/filesystem/local_directory_darwin.go @@ -23,3 +23,7 @@ func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumb func clonefileImpl(oldFD int, oldName string, newFD int, newName string) error { return unix.Clonefileat(oldFD, oldName, newFD, newName, unix.CLONE_NOFOLLOW) } + +func (d *localDirectory) Mount(mountpoint path.Component, source string, fstype string) error { + return status.Error(codes.Unimplemented, "Mount is not supported") +} diff --git a/pkg/filesystem/local_directory_freebsd.go b/pkg/filesystem/local_directory_freebsd.go index c6dc4c6d..8531b242 100644 --- a/pkg/filesystem/local_directory_freebsd.go +++ b/pkg/filesystem/local_directory_freebsd.go @@ -24,3 +24,7 @@ func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumb func clonefileImpl(oldFD int, oldName string, newFD int, newName string) error { return status.Error(codes.Unimplemented, "Clonefile is only supported on Darwin") } + +func (d *localDirectory) Mount(mountpoint path.Component, source string, fstype string) error { + return status.Error(codes.Unimplemented, "Mount is not supported") +} diff --git a/pkg/filesystem/local_directory_linux.go b/pkg/filesystem/local_directory_linux.go index 1084c8a0..7149e3b8 100644 --- a/pkg/filesystem/local_directory_linux.go +++ b/pkg/filesystem/local_directory_linux.go @@ -8,6 +8,7 @@ import ( "runtime" "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" @@ -34,3 +35,37 @@ func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumb func clonefileImpl(oldFD int, oldName string, newFD int, newName string) error { return status.Error(codes.Unimplemented, "Clonefile is only supported on Darwin") } + +func (d *localDirectory) Mount(mountpoint path.Component, source string, fstype string) error { + mountname := mountpoint.String() + fd, err := unix.Fsopen(fstype, unix.FSOPEN_CLOEXEC) + if err != nil { + return util.StatusWrapf(err, "Fsopen '%s'", fstype) + } + + err = unix.FsconfigSetString(fd, "source", source) + if err != nil { + return util.StatusWrapf(err, "Fsconfig source '%s'", source) + } + + err = unix.FsconfigCreate(fd) + if err != nil { + return util.StatusWrap(err, "Fsconfig create") + } + + mfd, err := unix.Fsmount(fd, unix.FSMOUNT_CLOEXEC, unix.MS_NOEXEC) + if err != nil { + return util.StatusWrap(err, "Fsmount") + } + // NB: `Fsmount` creates a file descriptor to the mount object, that can be + // used to move it again. But we will not do so, so it is best to close it. + // Unmount will fail with `EBUSY` if it is left open. + defer unix.Close(mfd) + + err = unix.MoveMount(mfd, "", d.fd, mountname, unix.MOVE_MOUNT_F_EMPTY_PATH) + if err != nil { + return util.StatusWrapf(err, "Movemount mountname '%s' in file descriptor %d", mountname, d.fd) + } + + return nil +} diff --git a/pkg/filesystem/local_directory_unix.go b/pkg/filesystem/local_directory_unix.go index 6fb3c609..ce746437 100644 --- a/pkg/filesystem/local_directory_unix.go +++ b/pkg/filesystem/local_directory_unix.go @@ -268,7 +268,7 @@ func (d *localDirectory) Remove(name path.Component) error { var workingDirectoryLock sync.Mutex -func (d *localDirectory) unmount(name path.Component) error { +func (d *localDirectory) Unmount(name path.Component) error { defer runtime.KeepAlive(d) // POSIX systems provide no umountat() system call that permits @@ -302,7 +302,7 @@ func (d *localDirectory) removeAllChildren(parentDeviceNumber rawDeviceNumber) e // unmount until the remaining directory is on the same // file system. for parentDeviceNumber != childDeviceNumber { - if err := d.unmount(component); err != nil { + if err := d.Unmount(component); err != nil { return err } fileType, childDeviceNumber, _, err = d.lstat(component) diff --git a/pkg/filesystem/local_directory_windows.go b/pkg/filesystem/local_directory_windows.go index b7a019cb..ec880b8d 100644 --- a/pkg/filesystem/local_directory_windows.go +++ b/pkg/filesystem/local_directory_windows.go @@ -978,3 +978,11 @@ func (d *localDirectory) Apply(arg interface{}) error { return syscall.EXDEV } } + +func (d *localDirectory) Mount(mountpoint path.Component, source string, fstype string) error { + return status.Error(codes.Unimplemented, "Mount is not supported") +} + +func (d *localDirectory) Unmount(mountpoint path.Component) error { + return status.Error(codes.Unimplemented, "Unmount is not supported") +}