unix: update z/OS implementation of fcntl and mmap

- Add a wrapper function around fcntl to handle different operation
  types and new fcntl implementation that accepts uintptr as an arg.
- Add support for calling mmap/munmap with address pointers.
- Add accompanying tests for new functions.

Change-Id: If5e77aa4cf2cccfd431de4f3bd0c5014a761e167
GitHub-Last-Rev: 07e32a4ab797c7baffdb50f8407cf679cabf1dae
GitHub-Pull-Request: golang/sys#216
Reviewed-on: https://go-review.googlesource.com/c/sys/+/610296
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Bypass: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/unix/mmap_zos_test.go b/unix/mmap_zos_test.go
index f35d9c1..15abcee 100644
--- a/unix/mmap_zos_test.go
+++ b/unix/mmap_zos_test.go
@@ -72,3 +72,17 @@
 		t.Fatalf("Munmap: %v", err)
 	}
 }
+
+func TestMmapPtr(t *testing.T) {
+	p, err := unix.MmapPtr(-1, 0, nil, uintptr(2*unix.Getpagesize()),
+		unix.PROT_READ|unix.PROT_WRITE, unix.MAP_ANON|unix.MAP_PRIVATE)
+	if err != nil {
+		t.Fatalf("MmapPtr: %v", err)
+	}
+
+	*(*byte)(p) = 42
+
+	if err := unix.MunmapPtr(p, uintptr(2*unix.Getpagesize())); err != nil {
+		t.Fatalf("MunmapPtr: %v", err)
+	}
+}
diff --git a/unix/syscall_zos_s390x.go b/unix/syscall_zos_s390x.go
index f97296d..cca8908 100644
--- a/unix/syscall_zos_s390x.go
+++ b/unix/syscall_zos_s390x.go
@@ -768,6 +768,16 @@
 	return mapper.Munmap(b)
 }
 
+func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) {
+	xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset)
+	return unsafe.Pointer(xaddr), err
+}
+
+func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) {
+	return mapper.munmap(uintptr(addr), length)
+}
+
+
 //sys   Gethostname(buf []byte) (err error) = SYS___GETHOSTNAME_A
 //sysnb	Getgid() (gid int)
 //sysnb	Getpid() (pid int)
@@ -3115,3 +3125,34 @@
 //sys	Posix_openpt(oflag int) (fd int, err error) = SYS_POSIX_OPENPT
 //sys	Grantpt(fildes int) (rc int, err error) = SYS_GRANTPT
 //sys	Unlockpt(fildes int) (rc int, err error) = SYS_UNLOCKPT
+
+func fcntlAsIs(fd uintptr, cmd int, arg uintptr) (val int, err error) {
+	runtime.EnterSyscall()
+	r0, e2, e1 := CallLeFuncWithErr(GetZosLibVec()+SYS_FCNTL<<4, uintptr(fd), uintptr(cmd), arg)
+	runtime.ExitSyscall()
+	val = int(r0)
+	if int64(r0) == -1 {
+		err = errnoErr2(e1, e2)
+	}
+	return
+}
+
+func Fcntl(fd uintptr, cmd int, op interface{}) (ret int, err error) {
+	switch op.(type) {
+	case *Flock_t:
+		err = FcntlFlock(fd, cmd, op.(*Flock_t))
+		if err != nil {
+                    ret = -1
+		}
+		return
+	case int:
+		return FcntlInt(fd, cmd, op.(int))
+	case *F_cnvrt:
+		return fcntlAsIs(fd, cmd, uintptr(unsafe.Pointer(op.(*F_cnvrt))))
+	case unsafe.Pointer:
+		return fcntlAsIs(fd, cmd, uintptr(op.(unsafe.Pointer)))
+	default:
+		return -1, EINVAL
+	}
+	return
+}
diff --git a/unix/syscall_zos_test.go b/unix/syscall_zos_test.go
index c04f5a0..e496893 100644
--- a/unix/syscall_zos_test.go
+++ b/unix/syscall_zos_test.go
@@ -7,6 +7,7 @@
 package unix_test
 
 import (
+	"bytes"
 	"errors"
 	"flag"
 	"fmt"
@@ -202,7 +203,7 @@
 
 func TestFcntlInt(t *testing.T) {
 	t.Parallel()
-	file, err := os.Create(filepath.Join(t.TempDir(), "TestFnctlInt"))
+	file, err := os.Create(filepath.Join(t.TempDir(), t.Name()))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -217,10 +218,27 @@
 	}
 }
 
+func TestFcntlInt2(t *testing.T) {
+	t.Parallel()
+	file, err := os.Create(filepath.Join(t.TempDir(), t.Name()))
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer file.Close()
+	f := file.Fd()
+	flags, err := unix.Fcntl(f, unix.F_GETFD, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if flags&unix.FD_CLOEXEC == 0 {
+		t.Errorf("flags %#x do not include FD_CLOEXEC", flags)
+	}
+}
+
 // TestFcntlFlock tests whether the file locking structure matches
 // the calling convention of each kernel.
 func TestFcntlFlock(t *testing.T) {
-	name := filepath.Join(os.TempDir(), "TestFcntlFlock")
+	name := filepath.Join(t.TempDir(), "TestFcntlFlock")
 	fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0)
 	if err != nil {
 		t.Fatalf("Open failed: %v", err)
@@ -236,6 +254,23 @@
 	}
 }
 
+func TestFcntlFlock2(t *testing.T) {
+	name := filepath.Join(t.TempDir(), "TestFcntlFlock2")
+	fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0)
+	if err != nil {
+		t.Fatalf("Open failed: %v", err)
+	}
+	defer unix.Unlink(name)
+	defer unix.Close(fd)
+	flock := unix.Flock_t{
+		Type:  unix.F_RDLCK,
+		Start: 0, Len: 0, Whence: 1,
+	}
+	if v, err := unix.Fcntl(uintptr(fd), unix.F_GETLK, &flock); err != nil {
+		t.Fatalf("FcntlFlock failed: %d %v", v, err)
+	}
+}
+
 // TestPassFD tests passing a file descriptor over a Unix socket.
 //
 // This test involved both a parent and child process. The parent
@@ -249,8 +284,6 @@
 		return
 	}
 
-	tempDir := t.TempDir()
-
 	fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
 	if err != nil {
 		t.Fatalf("Socketpair: %v", err)
@@ -262,7 +295,7 @@
 	defer writeFile.Close()
 	defer readFile.Close()
 
-	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
+	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", t.TempDir())
 	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
 	if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" {
 		cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp)
@@ -371,7 +404,7 @@
 	}
 }
 
-// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage,
+// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, ParseOneSocketControlMessage,
 // and ParseUnixRights are able to successfully round-trip lists of file descriptors.
 func TestUnixRightsRoundtrip(t *testing.T) {
 	testCases := [...][][]int{
@@ -399,6 +432,23 @@
 		if len(scms) != len(testCase) {
 			t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms)
 		}
+
+		var c int
+		for len(b) > 0 {
+			hdr, data, remainder, err := unix.ParseOneSocketControlMessage(b)
+			if err != nil {
+				t.Fatalf("ParseOneSocketControlMessage: %v", err)
+			}
+			if scms[c].Header != hdr || !bytes.Equal(scms[c].Data, data) {
+				t.Fatal("expected SocketControlMessage header and data to match")
+			}
+			b = remainder
+			c++
+		}
+		if c != len(scms) {
+			t.Fatalf("expected %d SocketControlMessages; got %d", len(scms), c)
+		}
+
 		for i, scm := range scms {
 			gotFds, err := unix.ParseUnixRights(&scm)
 			if err != nil {
@@ -474,6 +524,12 @@
 	if err != nil {
 		t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
 	}
+
+	// make sure RLIM_INFINITY can be assigned to Rlimit members
+	_ = unix.Rlimit{
+		Cur: unix.RLIM_INFINITY,
+		Max: unix.RLIM_INFINITY,
+	}
 }
 
 func TestSeekFailure(t *testing.T) {
@@ -497,9 +553,9 @@
 }
 
 func TestDup(t *testing.T) {
-	file, err := os.Create(filepath.Join(t.TempDir(), "TestDup"))
+	file, err := os.Create(filepath.Join(t.TempDir(), t.Name()))
 	if err != nil {
-		t.Fatalf("Tempfile failed: %v", err)
+		t.Fatal(err)
 	}
 	defer file.Close()
 	f := int(file.Fd())
@@ -654,25 +710,21 @@
 }
 
 // chtmpdir changes the working directory to a new temporary directory and
-// provides a cleanup function. Used when PWD is read-only.
-func chtmpdir(t *testing.T) func() {
+// sets up a cleanup function. Used when PWD is read-only.
+func chtmpdir(t *testing.T) {
+	t.Helper()
 	oldwd, err := os.Getwd()
 	if err != nil {
-		t.Fatalf("chtmpdir: %v", err)
+		t.Fatal(err)
 	}
-	d, err := os.MkdirTemp("", "test")
-	if err != nil {
-		t.Fatalf("chtmpdir: %v", err)
+	if err := os.Chdir(t.TempDir()); err != nil {
+		t.Fatal(err)
 	}
-	if err := os.Chdir(d); err != nil {
-		t.Fatalf("chtmpdir: %v", err)
-	}
-	return func() {
+	t.Cleanup(func() {
 		if err := os.Chdir(oldwd); err != nil {
-			t.Fatalf("chtmpdir: %v", err)
+			t.Fatal(err)
 		}
-		os.RemoveAll(d)
-	}
+	})
 }
 
 func TestLegacyMountUnmount(t *testing.T) {
@@ -2993,7 +3045,7 @@
 }
 
 func TestRenameat(t *testing.T) {
-	defer chtmpdir(t)()
+	chtmpdir(t)
 
 	from, to := "renamefrom", "renameto"
 
@@ -3016,7 +3068,7 @@
 }
 
 func TestRenameat2(t *testing.T) {
-	defer chtmpdir(t)()
+	chtmpdir(t)
 
 	from, to := "renamefrom", "renameto"
 
@@ -3050,7 +3102,7 @@
 }
 
 func TestFchmodat(t *testing.T) {
-	defer chtmpdir(t)()
+	chtmpdir(t)
 
 	touch(t, "file1")
 	err := os.Symlink("file1", "symlink1")
@@ -3148,7 +3200,7 @@
 }
 
 func TestFstatat(t *testing.T) {
-	defer chtmpdir(t)()
+	chtmpdir(t)
 
 	touch(t, "file1")
 
@@ -3749,3 +3801,77 @@
 
 	t.Logf("Got %s %x\n", unix.ZosEbcdicBytesToString(modstr[:], true), cmsg_cmd)
 }
+func TestTty(t *testing.T) {
+	ptmxfd, err := unix.Posix_openpt(unix.O_RDWR)
+	if err != nil {
+		t.Fatalf("Posix_openpt %+v\n", err)
+	}
+	t.Logf("ptmxfd %v\n", ptmxfd)
+
+	// convert to EBCDIC
+	cvtreq := unix.F_cnvrt{Cvtcmd: unix.SETCVTON, Pccsid: 0, Fccsid: 1047}
+	if _, err = unix.Fcntl(uintptr(ptmxfd), unix.F_CONTROL_CVT, &cvtreq); err != nil {
+		t.Fatalf("fcntl F_CONTROL_CVT %+v\n", err)
+	}
+	p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
+	if p == nil {
+		t.Fatalf("NewFile %d /dev/ptmx failed\n", ptmxfd)
+	}
+
+	// In case of error after this point, make sure we close the ptmx fd.
+	defer func() {
+		if err != nil {
+			_ = p.Close() // Best effort.
+		}
+	}()
+	sname, err := unix.Ptsname(ptmxfd)
+	if err != nil {
+		t.Fatalf("Ptsname %+v\n", err)
+	}
+	t.Logf("sname %v\n", sname)
+
+	_, err = unix.Grantpt(ptmxfd)
+	if err != nil {
+		t.Fatalf("Grantpt %+v\n", err)
+	}
+
+	if _, err = unix.Unlockpt(ptmxfd); err != nil {
+		t.Fatalf("Unlockpt %+v\n", err)
+	}
+
+	ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+	if err != nil {
+		t.Fatalf("Open %s %+v\n", sname, err)
+	}
+	if _, err = unix.Fcntl(uintptr(ptsfd), unix.F_CONTROL_CVT, &cvtreq); err != nil {
+
+		t.Fatalf("fcntl F_CONTROL_CVT ptsfd %+v\n", err)
+
+	}
+
+	tt := os.NewFile(uintptr(ptsfd), sname)
+	if err != nil {
+		t.Fatalf("NewFile %d %+v %+v\n", ptsfd, sname, err)
+	}
+	text := []byte("11111111")
+
+	n, err := tt.Write(text)
+	if err != nil {
+		t.Fatalf("ptsfd Write %+v\n", err)
+	}
+	t.Logf("bytes %d\n", n)
+
+	var buffer [1024]byte
+
+	n, err = p.Read(buffer[:n])
+	if err != nil {
+		t.Fatalf("ptmx read %+v\n", err)
+	}
+	t.Logf("Buffer %+v\n", buffer[:n])
+
+	if !bytes.Equal(text, buffer[:n]) {
+		t.Fatalf("Expected %+v, read %+v\n", text, buffer[:n])
+
+	}
+
+}
diff --git a/unix/xattr_zos_test.go b/unix/xattr_zos_test.go
index 982037f..b19a876 100644
--- a/unix/xattr_zos_test.go
+++ b/unix/xattr_zos_test.go
@@ -14,7 +14,7 @@
 )
 
 func TestXattr(t *testing.T) {
-	defer chtmpdir(t)()
+	chtmpdir(t)
 
 	f := "xattr1"
 	touch(t, f)
diff --git a/unix/ztypes_zos_s390x.go b/unix/ztypes_zos_s390x.go
index d9a13af..8c4d053 100644
--- a/unix/ztypes_zos_s390x.go
+++ b/unix/ztypes_zos_s390x.go
@@ -377,6 +377,12 @@
 	Pid    int32
 }
 
+type F_cnvrt struct {
+       Cvtcmd int32
+       Pccsid int16
+       Fccsid int16
+}
+
 type Termios struct {
 	Cflag uint32
 	Iflag uint32