diff --git a/unix/linux/types.go b/unix/linux/types.go index f3c23964..eb0fd3c9 100644 --- a/unix/linux/types.go +++ b/unix/linux/types.go @@ -111,6 +111,7 @@ struct termios2 { #include #include #include +#include #include #include #include @@ -874,6 +875,18 @@ const ( AT_EACCESS = C.AT_EACCESS ) +type OpenHow C.struct_open_how + +const SizeofOpenHow = C.sizeof_struct_open_how + +const ( + RESOLVE_BENEATH = C.RESOLVE_BENEATH + RESOLVE_IN_ROOT = C.RESOLVE_IN_ROOT + RESOLVE_NO_MAGICLINKS = C.RESOLVE_NO_MAGICLINKS + RESOLVE_NO_SYMLINKS = C.RESOLVE_NO_SYMLINKS + RESOLVE_NO_XDEV = C.RESOLVE_NO_XDEV +) + type PollFd C.struct_pollfd const ( diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go index 5b3af2e6..ec7e4c4d 100644 --- a/unix/syscall_linux.go +++ b/unix/syscall_linux.go @@ -136,6 +136,12 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) return openat(dirfd, path, flags|O_LARGEFILE, mode) } +//sys openat2(dirfd int, path string, open_how *OpenHow, size int) (fd int, err error) + +func Openat2(dirfd int, path string, how *OpenHow) (fd int, err error) { + return openat2(dirfd, path, how, SizeofOpenHow) +} + //sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go index 01f29ca4..76555d40 100644 --- a/unix/syscall_linux_test.go +++ b/unix/syscall_linux_test.go @@ -13,6 +13,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "runtime" "runtime/debug" "strconv" @@ -730,3 +731,62 @@ func TestTimerfd(t *testing.T) { count += *(*uint64)(unsafe.Pointer(&buffer)) } } + +func TestOpenat2(t *testing.T) { + how := &unix.OpenHow{ + Flags: unix.O_RDONLY, + } + fd, err := unix.Openat2(unix.AT_FDCWD, ".", how) + if err != nil { + if err == unix.ENOSYS || err == unix.EPERM { + t.Skipf("openat2: %v (old kernel? need Linux >= 5.6)", err) + } + t.Fatalf("openat2: %v", err) + } + if err := unix.Close(fd); err != nil { + t.Fatalf("close: %v", err) + } + + // prepare + tempDir, err := ioutil.TempDir("", t.Name()) + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + subdir := filepath.Join(tempDir, "dir") + if err := os.Mkdir(subdir, 0755); err != nil { + t.Fatal(err) + } + symlink := filepath.Join(subdir, "symlink") + if err := os.Symlink("../", symlink); err != nil { + t.Fatal(err) + } + + dirfd, err := unix.Open(subdir, unix.O_RDONLY, 0) + if err != nil { + t.Fatalf("open(%q): %v", subdir, err) + } + defer unix.Close(dirfd) + + // openat2 with no extra flags -- should succeed + fd, err = unix.Openat2(dirfd, "symlink", how) + if err != nil { + t.Errorf("Openat2 should succeed, got %v", err) + } + if err := unix.Close(fd); err != nil { + t.Fatalf("close: %v", err) + } + + // open with RESOLVE_BENEATH, should result in EXDEV + how.Resolve = unix.RESOLVE_BENEATH + fd, err = unix.Openat2(dirfd, "symlink", how) + if err == nil { + if err := unix.Close(fd); err != nil { + t.Fatalf("close: %v", err) + } + } + if err != unix.EXDEV { + t.Errorf("Openat2 should fail with EXDEV, got %v", err) + } +} diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go index 4eec7a79..2fbbbe5a 100644 --- a/unix/zsyscall_linux.go +++ b/unix/zsyscall_linux.go @@ -83,6 +83,22 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func openat2(dirfd int, path string, open_how *OpenHow, size int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(open_how)), uintptr(size), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go index 1879d578..68e4974a 100644 --- a/unix/ztypes_linux.go +++ b/unix/ztypes_linux.go @@ -752,6 +752,22 @@ const ( AT_EACCESS = 0x200 ) +type OpenHow struct { + Flags uint64 + Mode uint64 + Resolve uint64 +} + +const SizeofOpenHow = 0x18 + +const ( + RESOLVE_BENEATH = 0x8 + RESOLVE_IN_ROOT = 0x10 + RESOLVE_NO_MAGICLINKS = 0x2 + RESOLVE_NO_SYMLINKS = 0x4 + RESOLVE_NO_XDEV = 0x1 +) + type PollFd struct { Fd int32 Events int16