Skip to content

Commit a041f42

Browse files
authored
Open directories using O_SEARCH (#228)
Right now we open directories using O_RDONLY. This is problematic, because it means we can't perform operations on directories that only have execute permissions. The solution for this is to use O_SEARCH, which was added in POSIX 2008. Linux doesn't offer O_SEARCH, but we can emulate it using O_PATH. macOS has supported this feature since Ventura (October 2022).
1 parent f5a181e commit a041f42

6 files changed

+66
-3
lines changed

MODULE.bazel

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ go_deps_dev.module_override(
9999
patches = ["//:patches/org_golang_x_oauth2/injectable-clock.diff"],
100100
path = "golang.org/x/oauth2",
101101
)
102+
go_deps_dev.module_override(
103+
patches = ["//:patches/org_golang_x_sys/o-search.diff"],
104+
path = "golang.org/x/sys",
105+
)
102106
go_deps_dev.module_override(
103107
patches = ["//:patches/org_uber_go_mock/mocks-for-funcs.diff"],
104108
path = "go.uber.org/mock",
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
diff --git unix/zerrors_darwin_amd64.go unix/zerrors_darwin_amd64.go
2+
index d73c465..d406964 100644
3+
--- unix/zerrors_darwin_amd64.go
4+
+++ unix/zerrors_darwin_amd64.go
5+
@@ -1128,6 +1128,7 @@ const (
6+
O_DSYNC = 0x400000
7+
O_EVTONLY = 0x8000
8+
O_EXCL = 0x800
9+
+ O_EXEC = 0x40000000
10+
O_EXLOCK = 0x20
11+
O_FSYNC = 0x80
12+
O_NDELAY = 0x4
13+
@@ -1138,6 +1139,7 @@ const (
14+
O_POPUP = 0x80000000
15+
O_RDONLY = 0x0
16+
O_RDWR = 0x2
17+
+ O_SEARCH = 0x40100000
18+
O_SHLOCK = 0x10
19+
O_SYMLINK = 0x200000
20+
O_SYNC = 0x80
21+
diff --git unix/zerrors_darwin_arm64.go unix/zerrors_darwin_arm64.go
22+
index 4a55a40..c47c6e9 100644
23+
--- unix/zerrors_darwin_arm64.go
24+
+++ unix/zerrors_darwin_arm64.go
25+
@@ -1128,6 +1128,7 @@ const (
26+
O_DSYNC = 0x400000
27+
O_EVTONLY = 0x8000
28+
O_EXCL = 0x800
29+
+ O_EXEC = 0x40000000
30+
O_EXLOCK = 0x20
31+
O_FSYNC = 0x80
32+
O_NDELAY = 0x4
33+
@@ -1138,6 +1139,7 @@ const (
34+
O_POPUP = 0x80000000
35+
O_RDONLY = 0x0
36+
O_RDWR = 0x2
37+
+ O_SEARCH = 0x40100000
38+
O_SHLOCK = 0x10
39+
O_SYMLINK = 0x200000
40+
O_SYNC = 0x80

pkg/filesystem/local_directory_darwin.go

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
// rawDeviceNumber is the equivalent of POSIX dev_t.
1717
type rawDeviceNumber = int32
1818

19+
const oflagSearch = unix.O_SEARCH
20+
1921
func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumber DeviceNumber) error {
2022
return status.Error(codes.Unimplemented, "Creation of device nodes is not supported on Darwin")
2123
}

pkg/filesystem/local_directory_freebsd.go

+3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/buildbarn/bb-storage/pkg/filesystem/path"
1010

11+
"golang.org/x/sys/unix"
1112
"google.golang.org/grpc/codes"
1213
"google.golang.org/grpc/status"
1314
)
1415

1516
// rawDeviceNumber is the equivalent of POSIX dev_t.
1617
type rawDeviceNumber = uint64
1718

19+
const oflagSearch = unix.O_SEARCH
20+
1821
func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumber DeviceNumber) error {
1922
// Though mknodat() exists on FreeBSD, device nodes created
2023
// outside of devfs are non-functional.

pkg/filesystem/local_directory_linux.go

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
// rawDeviceNumber is the equivalent of POSIX dev_t.
1919
type rawDeviceNumber = uint64
2020

21+
const oflagSearch = unix.O_PATH
22+
2123
func (d *localDirectory) Mknod(name path.Component, perm os.FileMode, deviceNumber DeviceNumber) error {
2224
defer runtime.KeepAlive(d)
2325

pkg/filesystem/local_directory_unix.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func NewLocalDirectory(directoryParser path.Parser) (DirectoryCloser, error) {
4343
return nil, util.StatusWrap(err, "Failed to create local representation of directory")
4444
}
4545

46-
fd, err := unix.Openat(unix.AT_FDCWD, pathString, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_RDONLY, 0)
46+
fd, err := unix.Openat(unix.AT_FDCWD, pathString, unix.O_DIRECTORY|unix.O_NOFOLLOW|oflagSearch, 0)
4747
if err != nil {
4848
return nil, err
4949
}
@@ -53,7 +53,7 @@ func NewLocalDirectory(directoryParser path.Parser) (DirectoryCloser, error) {
5353
func (d *localDirectory) enter(name path.Component) (*localDirectory, error) {
5454
defer runtime.KeepAlive(d)
5555

56-
fd, err := unix.Openat(d.fd, name.String(), unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_RDONLY, 0)
56+
fd, err := unix.Openat(d.fd, name.String(), unix.O_DIRECTORY|unix.O_NOFOLLOW|oflagSearch, 0)
5757
if err != nil {
5858
if runtime.GOOS == "freebsd" && err == syscall.EMLINK {
5959
// FreeBSD erroneously returns EMLINK.
@@ -406,7 +406,19 @@ func (d *localDirectory) Symlink(oldName path.Parser, newName path.Component) er
406406
func (d *localDirectory) Sync() error {
407407
defer runtime.KeepAlive(d)
408408

409-
return unix.Fsync(d.fd)
409+
// Linux doesn't permit calling fsync() on directories opened
410+
// with O_PATH. Reopen the directory with O_RDONLY.
411+
dfd := d.fd
412+
if runtime.GOOS == "linux" {
413+
var err error
414+
dfd, err = unix.Openat(d.fd, ".", unix.O_DIRECTORY|unix.O_RDONLY, 0)
415+
if err != nil {
416+
return err
417+
}
418+
defer unix.Close(dfd)
419+
}
420+
421+
return unix.Fsync(dfd)
410422
}
411423

412424
func (d *localDirectory) Chtimes(name path.Component, atime, mtime time.Time) error {

0 commit comments

Comments
 (0)