unix: augment support for zos/s390x
This augments sys/unix support for zos/s390x by
adding a small number of syscalls:
Errno2
Err2ad
W_Getmntent_A (pure ascii version of W_Getmntent)
Select
It also makes Mount and Unmount more Linux-like.
A few necessary constants and types are added,
and some tests.
These changes do not affect other platforms in any way.
Fixes golang/go#45838
Change-Id: I5783784a79b6c80a47cca74f3352bc07ea4ca682
Reviewed-on: https://go-review.googlesource.com/c/sys/+/314950
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
diff --git a/unix/fdset.go b/unix/fdset.go
index b1e07b2..a8068f9 100644
--- a/unix/fdset.go
+++ b/unix/fdset.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package unix
diff --git a/unix/mmap_zos_test.go b/unix/mmap_zos_test.go
index 4c17182..8d9303b 100644
--- a/unix/mmap_zos_test.go
+++ b/unix/mmap_zos_test.go
@@ -33,7 +33,7 @@
fmt.Fprintf(destination, "%s\n", "0 <- Flipped between 0 and 1 when test runs successfully")
fmt.Fprintf(destination, "%s\n", "//Do not change contents - mmap test relies on this")
destination.Close()
- fd, err := unix.Open(filename, unix.O_RDWR, 0o777)
+ fd, err := unix.Open(filename, unix.O_RDWR, 0777)
if err != nil {
t.Fatalf("Open: %v", err)
}
diff --git a/unix/syscall_zos_s390x.go b/unix/syscall_zos_s390x.go
index 13f58d2..1ffd8bf 100644
--- a/unix/syscall_zos_s390x.go
+++ b/unix/syscall_zos_s390x.go
@@ -222,6 +222,8 @@
//sys Creat(path string, mode uint32) (fd int, err error) = SYS___CREAT_A
//sys Dup(oldfd int) (fd int, err error)
//sys Dup2(oldfd int, newfd int) (err error)
+//sys Errno2() (er2 int) = SYS___ERRNO2
+//sys Err2ad() (eadd *int) = SYS___ERR2AD
//sys Exit(code int)
//sys Fchdir(fd int) (err error)
//sys Fchmod(fd int, mode uint32) (err error)
@@ -245,10 +247,12 @@
//sys Poll(fds []PollFd, timeout int) (n int, err error) = SYS_POLL
//sys Times(tms *Tms) (ticks uintptr, err error) = SYS_TIMES
//sys W_Getmntent(buff *byte, size int) (lastsys int, err error) = SYS_W_GETMNTENT
+//sys W_Getmntent_A(buff *byte, size int) (lastsys int, err error) = SYS___W_GETMNTENT_A
-//sys Mount(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) = SYS___MOUNT_A
-//sys Unmount(filesystem string, mtm int) (err error) = SYS___UMOUNT_A
+//sys mount_LE(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) = SYS___MOUNT_A
+//sys unmount(filesystem string, mtm int) (err error) = SYS___UMOUNT_A
//sys Chroot(path string) (err error) = SYS___CHROOT_A
+//sys Select(nmsgsfds int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (ret int, err error) = SYS_SELECT
//sysnb Uname(buf *Utsname) (err error) = SYS___UNAME_A
func Ptsname(fd int) (name string, err error) {
@@ -1779,3 +1783,47 @@
func Exec(argv0 string, argv []string, envv []string) error {
return syscall.Exec(argv0, argv, envv)
}
+
+func Mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
+ if needspace := 8 - len(fstype); needspace <= 0 {
+ fstype = fstype[:8]
+ } else {
+ fstype += " "[:needspace]
+ }
+ return mount_LE(target, source, fstype, uint32(flags), int32(len(data)), data)
+}
+
+func Unmount(name string, mtm int) (err error) {
+ // mountpoint is always a full path and starts with a '/'
+ // check if input string is not a mountpoint but a filesystem name
+ if name[0] != '/' {
+ return unmount(name, mtm)
+ }
+ // treat name as mountpoint
+ b2s := func(arr []byte) string {
+ nulli := bytes.IndexByte(arr, 0)
+ if nulli == -1 {
+ return string(arr)
+ } else {
+ return string(arr[:nulli])
+ }
+ }
+ var buffer struct {
+ header W_Mnth
+ fsinfo [64]W_Mntent
+ }
+ fsCount, err := W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
+ if err != nil {
+ return err
+ }
+ if fsCount == 0 {
+ return EINVAL
+ }
+ for i := 0; i < fsCount; i++ {
+ if b2s(buffer.fsinfo[i].Mountpoint[:]) == name {
+ err = unmount(b2s(buffer.fsinfo[i].Fsname[:]), mtm)
+ break
+ }
+ }
+ return err
+}
diff --git a/unix/syscall_zos_test.go b/unix/syscall_zos_test.go
index ef5e391..640d264 100644
--- a/unix/syscall_zos_test.go
+++ b/unix/syscall_zos_test.go
@@ -8,6 +8,7 @@
package unix_test
import (
+ "bytes"
"flag"
"fmt"
"io/ioutil"
@@ -20,6 +21,7 @@
"syscall"
"testing"
"time"
+ "unsafe"
"golang.org/x/sys/unix"
)
@@ -604,3 +606,266 @@
os.RemoveAll(d)
}
}
+
+func TestMountUnmount(t *testing.T) {
+ b2s := func(arr []byte) string {
+ nulli := bytes.IndexByte(arr, 0)
+ if nulli == -1 {
+ return string(arr)
+ } else {
+ return string(arr[:nulli])
+ }
+ }
+ // use an available fs
+ var buffer struct {
+ header unix.W_Mnth
+ fsinfo [64]unix.W_Mntent
+ }
+ fsCount, err := unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
+ if err != nil {
+ t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
+ } else if fsCount == 0 {
+ t.Fatalf("W_Getmntent_A returns no entries")
+ }
+ var fs string
+ var fstype string
+ var mountpoint string
+ var available bool = false
+ for i := 0; i < fsCount; i++ {
+ err = unix.Unmount(b2s(buffer.fsinfo[i].Mountpoint[:]), unix.MTM_RDWR)
+ if err != nil {
+ // Unmount and Mount require elevated privilege
+ // If test is run without such permission, skip test
+ if err == unix.EPERM {
+ t.Logf("Permission denied for Unmount. Skipping test (Errno2: %X)", unix.Errno2())
+ return
+ } else if err == unix.EBUSY {
+ continue
+ } else {
+ t.Fatalf("Unmount returns with error: %s", err.Error())
+ }
+ } else {
+ available = true
+ fs = b2s(buffer.fsinfo[i].Fsname[:])
+ fstype = b2s(buffer.fsinfo[i].Fstname[:])
+ mountpoint = b2s(buffer.fsinfo[i].Mountpoint[:])
+ t.Logf("using file system = %s; fstype = %s and mountpoint = %s\n", fs, fstype, mountpoint)
+ break
+ }
+ }
+ if !available {
+ t.Fatalf("No filesystem available")
+ }
+ // test unmount
+ buffer.header = unix.W_Mnth{}
+ fsCount, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
+ if err != nil {
+ t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
+ }
+ for i := 0; i < fsCount; i++ {
+ if b2s(buffer.fsinfo[i].Fsname[:]) == fs {
+ t.Fatalf("File system found after unmount")
+ }
+ }
+ // test mount
+ err = unix.Mount(fs, mountpoint, fstype, unix.MTM_RDWR, "")
+ if err != nil {
+ t.Fatalf("Mount returns with error: %s", err.Error())
+ }
+ buffer.header = unix.W_Mnth{}
+ fsCount, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
+ if err != nil {
+ t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
+ }
+ fsMounted := false
+ for i := 0; i < fsCount; i++ {
+ if b2s(buffer.fsinfo[i].Fsname[:]) == fs && b2s(buffer.fsinfo[i].Mountpoint[:]) == mountpoint {
+ fsMounted = true
+ }
+ }
+ if !fsMounted {
+ t.Fatalf("%s not mounted after Mount()", fs)
+ }
+}
+
+func TestChroot(t *testing.T) {
+ // create temp dir and tempfile 1
+ tempDir, err := ioutil.TempDir("", "TestChroot")
+ if err != nil {
+ t.Fatalf("TempDir: %s", err.Error())
+ }
+ defer os.RemoveAll(tempDir)
+ f, err := ioutil.TempFile(tempDir, "chroot_test_file")
+ if err != nil {
+ t.Fatalf("TempFile: %s", err.Error())
+ }
+ // chroot temp dir
+ err = unix.Chroot(tempDir)
+ // Chroot requires elevated privilege
+ // If test is run without such permission, skip test
+ if err == unix.EPERM {
+ t.Logf("Denied permission for Chroot. Skipping test (Errno2: %X)", unix.Errno2())
+ return
+ } else if err != nil {
+ t.Fatalf("Chroot: %s", err.Error())
+ }
+ // check if tempDir contains test file
+ files, err := ioutil.ReadDir("/")
+ if err != nil {
+ t.Fatalf("ReadDir: %s", err.Error())
+ }
+ found := false
+ for _, file := range files {
+ if file.Name() == filepath.Base(f.Name()) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Fatalf("Temp file not found in temp dir")
+ }
+}
+
+func TestFlock(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ defer os.Exit(0)
+ if len(os.Args) != 3 {
+ fmt.Printf("bad argument")
+ return
+ }
+ fn := os.Args[2]
+ f, err := os.OpenFile(fn, os.O_RDWR, 0755)
+ if err != nil {
+ fmt.Printf("%s", err.Error())
+ return
+ }
+ err = unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
+ // if the lock we are trying should be locked, ignore EAGAIN error
+ // otherwise, report all errors
+ if err != nil && err != unix.EAGAIN {
+ fmt.Printf("%s", err.Error())
+ }
+ } else {
+ // create temp dir and tempfile 1
+ tempDir, err := ioutil.TempDir("", "TestFlock")
+ if err != nil {
+ t.Fatalf("TempDir: %s", err.Error())
+ }
+ defer os.RemoveAll(tempDir)
+ f, err := ioutil.TempFile(tempDir, "flock_test_file")
+ if err != nil {
+ t.Fatalf("TempFile: %s", err.Error())
+ }
+ fd := int(f.Fd())
+
+ /* Test Case 1
+ * Try acquiring an occupied lock from another process
+ */
+ err = unix.Flock(fd, unix.LOCK_EX)
+ if err != nil {
+ t.Fatalf("Flock: %s", err.Error())
+ }
+ cmd := exec.Command(os.Args[0], "-test.run=TestFlock", f.Name())
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 || err != nil {
+ t.Fatalf("child process: %q, %v", out, err)
+ }
+ err = unix.Flock(fd, unix.LOCK_UN)
+ if err != nil {
+ t.Fatalf("Flock: %s", err.Error())
+ }
+
+ /* Test Case 2
+ * Try locking with Flock and FcntlFlock for same file
+ */
+ err = unix.Flock(fd, unix.LOCK_EX)
+ if err != nil {
+ t.Fatalf("Flock: %s", err.Error())
+ }
+ flock := unix.Flock_t{
+ Type: int16(unix.F_WRLCK),
+ Whence: int16(0),
+ Start: int64(0),
+ Len: int64(0),
+ Pid: int32(unix.Getppid()),
+ }
+ err = unix.FcntlFlock(f.Fd(), unix.F_SETLK, &flock)
+ if err != nil {
+ t.Fatalf("FcntlFlock: %s", err.Error())
+ }
+ }
+}
+
+func TestSelect(t *testing.T) {
+ for {
+ n, err := unix.Select(0, nil, nil, nil, &unix.Timeval{Sec: 0, Usec: 0})
+ if err == unix.EINTR {
+ t.Logf("Select interrupted")
+ continue
+ } else if err != nil {
+ t.Fatalf("Select: %v", err)
+ }
+ if n != 0 {
+ t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
+ }
+ break
+ }
+
+ dur := 250 * time.Millisecond
+ var took time.Duration
+ for {
+ // On some platforms (e.g. Linux), the passed-in timeval is
+ // updated by select(2). Make sure to reset to the full duration
+ // in case of an EINTR.
+ tv := unix.NsecToTimeval(int64(dur))
+ start := time.Now()
+ n, err := unix.Select(0, nil, nil, nil, &tv)
+ took = time.Since(start)
+ if err == unix.EINTR {
+ t.Logf("Select interrupted after %v", took)
+ continue
+ } else if err != nil {
+ t.Fatalf("Select: %v", err)
+ }
+ if n != 0 {
+ t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
+ }
+ break
+ }
+
+ // On some BSDs the actual timeout might also be slightly less than the requested.
+ // Add an acceptable margin to avoid flaky tests.
+ if took < dur*2/3 {
+ t.Errorf("Select: got %v timeout, expected at least %v", took, dur)
+ }
+
+ rr, ww, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer rr.Close()
+ defer ww.Close()
+
+ if _, err := ww.Write([]byte("HELLO GOPHER")); err != nil {
+ t.Fatal(err)
+ }
+
+ rFdSet := &unix.FdSet{}
+ fd := int(rr.Fd())
+ rFdSet.Set(fd)
+
+ for {
+ n, err := unix.Select(fd+1, rFdSet, nil, nil, nil)
+ if err == unix.EINTR {
+ t.Log("Select interrupted")
+ continue
+ } else if err != nil {
+ t.Fatalf("Select: %v", err)
+ }
+ if n != 1 {
+ t.Fatalf("Select: got %v ready file descriptors, expected 1", n)
+ }
+ break
+ }
+}
diff --git a/unix/zerrors_zos_s390x.go b/unix/zerrors_zos_s390x.go
index 4e87b4b..fc7d050 100644
--- a/unix/zerrors_zos_s390x.go
+++ b/unix/zerrors_zos_s390x.go
@@ -67,24 +67,43 @@
IPPORT_RESERVED = 1024
IPPORT_USERRESERVED = 5000
IPPROTO_AH = 51
+ SOL_AH = 51
IPPROTO_DSTOPTS = 60
+ SOL_DSTOPTS = 60
IPPROTO_EGP = 8
+ SOL_EGP = 8
IPPROTO_ESP = 50
+ SOL_ESP = 50
IPPROTO_FRAGMENT = 44
+ SOL_FRAGMENT = 44
IPPROTO_GGP = 2
+ SOL_GGP = 2
IPPROTO_HOPOPTS = 0
+ SOL_HOPOPTS = 0
IPPROTO_ICMP = 1
+ SOL_ICMP = 1
IPPROTO_ICMPV6 = 58
+ SOL_ICMPV6 = 58
IPPROTO_IDP = 22
+ SOL_IDP = 22
IPPROTO_IP = 0
+ SOL_IP = 0
IPPROTO_IPV6 = 41
+ SOL_IPV6 = 41
IPPROTO_MAX = 256
+ SOL_MAX = 256
IPPROTO_NONE = 59
+ SOL_NONE = 59
IPPROTO_PUP = 12
+ SOL_PUP = 12
IPPROTO_RAW = 255
+ SOL_RAW = 255
IPPROTO_ROUTING = 43
+ SOL_ROUTING = 43
IPPROTO_TCP = 6
+ SOL_TCP = 6
IPPROTO_UDP = 17
+ SOL_UDP = 17
IPV6_ADDR_PREFERENCES = 32
IPV6_CHECKSUM = 19
IPV6_DONTFRAG = 29
@@ -186,6 +205,7 @@
MTM_SYNCHONLY = 0x00000200
MTM_REMOUNT = 0x00000100
MTM_NOSECURITY = 0x00000080
+ NFDBITS = 0x20
O_ACCMODE = 0x03
O_APPEND = 0x08
O_ASYNCSIG = 0x0200
@@ -359,6 +379,8 @@
S_IFMST = 0x00FF0000
TCP_KEEPALIVE = 0x8
TCP_NODELAY = 0x1
+ TCP_INFO = 0xb
+ TCP_USER_TIMEOUT = 0x1
TIOCGWINSZ = 0x4008a368
TIOCSWINSZ = 0x8008a367
TIOCSBRK = 0x2000a77b
diff --git a/unix/zsyscall_zos_s390x.go b/unix/zsyscall_zos_s390x.go
index 8285ab8..f207945 100644
--- a/unix/zsyscall_zos_s390x.go
+++ b/unix/zsyscall_zos_s390x.go
@@ -364,6 +364,22 @@
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Errno2() (er2 int) {
+ uer2, _, _ := syscall_syscall(SYS___ERRNO2, 0, 0, 0)
+ er2 = int(uer2)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Err2ad() (eadd *int) {
+ ueadd, _, _ := syscall_syscall(SYS___ERR2AD, 0, 0, 0)
+ eadd = (*int)(unsafe.Pointer(ueadd))
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Exit(code int) {
syscall_syscall(SYS_EXIT, uintptr(code), 0, 0)
return
@@ -531,7 +547,18 @@
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Mount(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) {
+func W_Getmntent_A(buff *byte, size int) (lastsys int, err error) {
+ r0, _, e1 := syscall_syscall(SYS___W_GETMNTENT_A, uintptr(unsafe.Pointer(buff)), uintptr(size), 0)
+ lastsys = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount_LE(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
@@ -561,7 +588,7 @@
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Unmount(filesystem string, mtm int) (err error) {
+func unmount(filesystem string, mtm int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(filesystem)
if err != nil {
@@ -1215,3 +1242,14 @@
}
return
}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(nmsgsfds int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (ret int, err error) {
+ r0, _, e1 := syscall_syscall6(SYS_SELECT, uintptr(nmsgsfds), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+ ret = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
diff --git a/unix/ztypes_zos_s390x.go b/unix/ztypes_zos_s390x.go
index 8bffde7..4ab638c 100644
--- a/unix/ztypes_zos_s390x.go
+++ b/unix/ztypes_zos_s390x.go
@@ -347,6 +347,10 @@
Name [256]byte
}
+type FdSet struct {
+ Bits [64]int32
+}
+
// This struct is packed on z/OS so it can't be used directly.
type Flock_t struct {
Type int16