unix: add openat2 for linux

openat2 is a new syscall added to Linux 5.6. It provides a superset of
openat(2) functionality, extending it with flags telling the kernel how
to resolve the paths.

For more info, see https://lwn.net/Articles/803237/

NOTE that this is a second attempt to add the call; the previous one
(https://golang.org/cl/227280) was reverted
(https://golang.org/cl/227846) due to the test case failure on ARM
(https://golang.org/issue/38357).

This CL has the test case reworked to be less assumptive to the testing
environment. In particular, it first tries if the most simplistic
openat2() call succeeds, and skips the test otherwise. It is done that
way because CI can be under under different kernels and in various
envrionments -- in particular, Docker+seccomp can result in EPERM from a
system call (which is not expected otherwise).

For previous discussions about the test case, see
https://golang.org/cl/227865.

Change-Id: I4276cf13dc29ecbdbdc9c58da0f76270f585a67f
Reviewed-on: https://go-review.googlesource.com/c/sys/+/253057
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
This commit is contained in:
Kir Kolyshkin
2020-09-03 13:53:33 -07:00
committed by Tobias Klauser
parent d2e65c121b
commit eff7692f90
5 changed files with 111 additions and 0 deletions

View File

@@ -111,6 +111,7 @@ struct termios2 {
#include <linux/netfilter.h>
#include <linux/netlink.h>
#include <linux/nexthop.h>
#include <linux/openat2.h>
#include <linux/perf_event.h>
#include <linux/random.h>
#include <linux/rtc.h>
@@ -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 (

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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