unix: add openat2 for linux

openat2 is a new syscall added to Linux 5.6. It provides a superset
of openat(2) functionality, providing a way to extend it in the future,
and (for now) adding flags telling the kernel how to resolve the path.

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

A primitive test case is added to check that Openat2 works as
it should. Tested to skip on kernel 5.5 and pass on 5.6.

Change-Id: Ib8bbd71791762f043200543cecdea16d2fd3c81d
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Reviewed-on: https://go-review.googlesource.com/c/sys/+/227280
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index 1e90b2e..10f1ea4 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -106,6 +106,7 @@
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter.h>
 #include <linux/netlink.h>
+#include <linux/openat2.h>
 #include <linux/perf_event.h>
 #include <linux/random.h>
 #include <linux/rtc.h>
@@ -833,6 +834,18 @@
 	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 bbe1abb..2b151eb 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -133,6 +133,12 @@
 	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 e572376..8adb426 100644
--- a/unix/syscall_linux_test.go
+++ b/unix/syscall_linux_test.go
@@ -663,3 +663,24 @@
 		t.Fatalf("unexpected return from prctl; got %v, expected %v", v, 1)
 	}
 }
+
+func TestOpenat2(t *testing.T) {
+	how := &unix.OpenHow{
+		Flags:   unix.O_RDONLY,
+		Resolve: unix.RESOLVE_NO_MAGICLINKS,
+	}
+	fd, err := unix.Openat2(-1, "/proc/self/fd/1", how)
+	switch err {
+	case nil:
+		unix.Close(fd)
+		t.Fatal("expected ELOOP, got nil")
+	case unix.ELOOP:
+		return
+	case unix.ENOSYS:
+		t.Skip("old kernel? (need Linux >= 5.6)")
+	case unix.ENOENT:
+		t.Skipf("openat2: %v, skipping test", err)
+	default:
+		t.Fatalf("unexpected error from openat2: %v", err)
+	}
+}
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index fd2dae8..5c5b742 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -83,6 +83,22 @@
 
 // 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 9c43c26..781a5ea 100644
--- a/unix/ztypes_linux.go
+++ b/unix/ztypes_linux.go
@@ -691,6 +691,22 @@
 	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