Skip to content

Commit

Permalink
unix: update z/OS implementation of fcntl and mmap
Browse files Browse the repository at this point in the history
- 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: 07e32a4
GitHub-Pull-Request: #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>
  • Loading branch information
jlee3227 authored and gopherbot committed Oct 25, 2024
1 parent df4a4da commit 123459f
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 26 deletions.
14 changes: 14 additions & 0 deletions unix/mmap_zos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,17 @@ func TestMmap(t *testing.T) {
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)
}
}
41 changes: 41 additions & 0 deletions unix/syscall_zos_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,16 @@ func Munmap(b []byte) (err error) {
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)
Expand Down Expand Up @@ -3115,3 +3125,34 @@ func legacy_Mkfifoat(dirfd int, path string, mode uint32) (err error) {
//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
}
176 changes: 151 additions & 25 deletions unix/syscall_zos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package unix_test

import (
"bytes"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -202,7 +203,7 @@ func TestSignalNum(t *testing.T) {

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)
}
Expand All @@ -217,10 +218,27 @@ func TestFcntlInt(t *testing.T) {
}
}

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)
Expand All @@ -236,6 +254,23 @@ func TestFcntlFlock(t *testing.T) {
}
}

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
Expand All @@ -249,8 +284,6 @@ func TestPassFD(t *testing.T) {
return
}

tempDir := t.TempDir()

fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
if err != nil {
t.Fatalf("Socketpair: %v", err)
Expand All @@ -262,7 +295,7 @@ func TestPassFD(t *testing.T) {
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)
Expand Down Expand Up @@ -371,7 +404,7 @@ func passFDChild() {
}
}

// 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{
Expand Down Expand Up @@ -399,6 +432,23 @@ func TestUnixRightsRoundtrip(t *testing.T) {
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 {
Expand Down Expand Up @@ -474,6 +524,12 @@ func TestRlimit(t *testing.T) {
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) {
Expand All @@ -497,9 +553,9 @@ func TestSetsockoptString(t *testing.T) {
}

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())
Expand Down Expand Up @@ -654,25 +710,21 @@ func touch(t *testing.T, name string) {
}

// 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)
}
d, err := os.MkdirTemp("", "test")
if err != nil {
t.Fatalf("chtmpdir: %v", err)
t.Fatal(err)
}
if err := os.Chdir(d); err != nil {
t.Fatalf("chtmpdir: %v", err)
if err := os.Chdir(t.TempDir()); err != nil {
t.Fatal(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) {
Expand Down Expand Up @@ -2993,7 +3045,7 @@ func TestUnlinkat(t *testing.T) {
}

func TestRenameat(t *testing.T) {
defer chtmpdir(t)()
chtmpdir(t)

from, to := "renamefrom", "renameto"

Expand All @@ -3016,7 +3068,7 @@ func TestRenameat(t *testing.T) {
}

func TestRenameat2(t *testing.T) {
defer chtmpdir(t)()
chtmpdir(t)

from, to := "renamefrom", "renameto"

Expand Down Expand Up @@ -3050,7 +3102,7 @@ func TestRenameat2(t *testing.T) {
}

func TestFchmodat(t *testing.T) {
defer chtmpdir(t)()
chtmpdir(t)

touch(t, "file1")
err := os.Symlink("file1", "symlink1")
Expand Down Expand Up @@ -3148,7 +3200,7 @@ func compareStat_t(t *testing.T, otherStat string, st1, st2 *unix.Stat_t) {
}

func TestFstatat(t *testing.T) {
defer chtmpdir(t)()
chtmpdir(t)

touch(t, "file1")

Expand Down Expand Up @@ -3749,3 +3801,77 @@ func TestConsole2modify(t *testing.T) {

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

}

}
2 changes: 1 addition & 1 deletion unix/xattr_zos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

func TestXattr(t *testing.T) {
defer chtmpdir(t)()
chtmpdir(t)

f := "xattr1"
touch(t, f)
Expand Down
6 changes: 6 additions & 0 deletions unix/ztypes_zos_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@ type Flock_t struct {
Pid int32
}

type F_cnvrt struct {
Cvtcmd int32
Pccsid int16
Fccsid int16
}

type Termios struct {
Cflag uint32
Iflag uint32
Expand Down

0 comments on commit 123459f

Please sign in to comment.