Skip to content

unix: update z/OS implementation of fcntl and mmap #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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