Skip to content

Commit

Permalink
unix: extend z/OS support
Browse files Browse the repository at this point in the history
extend support for z/OS target by adding the following syscalls:
- Sendfile (library implementation)
- Fcntl
- MmapPtr
- MunmapPtr

Change-Id: I098748802c2b275c15758e9d0132fca0490e9b70
GitHub-Last-Rev: 5aafd0b
GitHub-Pull-Request: #223
Reviewed-on: https://go-review.googlesource.com/c/sys/+/620375
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bill O'Farrell <billotosyr@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
MacMalainey authored and gopherbot committed Oct 29, 2024
1 parent 18e038c commit ca04041
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 6 deletions.
56 changes: 56 additions & 0 deletions unix/syscall_zos_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -3155,3 +3155,59 @@ func Fcntl(fd uintptr, cmd int, op interface{}) (ret int, err error) {
}
return
}

func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
return sendfile(outfd, infd, offset, count)
}

func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
// TODO: use LE call instead if the call is implemented
originalOffset, err := Seek(infd, 0, SEEK_CUR)
if err != nil {
return -1, err
}
//start reading data from in_fd
if offset != nil {
_, err := Seek(infd, *offset, SEEK_SET)
if err != nil {
return -1, err
}
}

buf := make([]byte, count)
readBuf := make([]byte, 0)
var n int = 0
for i := 0; i < count; i += n {
n, err := Read(infd, buf)
if n == 0 {
if err != nil {
return -1, err
} else { // EOF
break
}
}
readBuf = append(readBuf, buf...)
buf = buf[0:0]
}

n2, err := Write(outfd, readBuf)
if err != nil {
return -1, err
}

//When sendfile() returns, this variable will be set to the
// offset of the byte following the last byte that was read.
if offset != nil {
*offset = *offset + int64(n)
// If offset is not NULL, then sendfile() does not modify the file
// offset of in_fd
_, err := Seek(infd, originalOffset, SEEK_SET)
if err != nil {
return -1, err
}
}
return n2, nil
}
157 changes: 151 additions & 6 deletions unix/syscall_zos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,11 @@ func TestPassFD(t *testing.T) {
defer writeFile.Close()
defer readFile.Close()

cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", t.TempDir())
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-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 @@ -940,7 +944,11 @@ func TestFlock(t *testing.T) {
p2status := BLOCKED
done := make(chan bool)
execP2 := func(isBlock bool) {
cmd := exec.Command(os.Args[0], "-test.run=^TestFlock$", strconv.Itoa(c.p2mode), f.Name())
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-test.run=^TestFlock$", strconv.Itoa(c.p2mode), f.Name())
cmd.Env = append(os.Environ(), "TEST_FLOCK_HELPER=1")
out, _ := cmd.CombinedOutput()
if p2status, err = strconv.Atoi(string(out)); err != nil {
Expand Down Expand Up @@ -1007,7 +1015,11 @@ func TestLegacyFlock(t *testing.T) {
if err != nil {
t.Fatalf("Flock: %s", err.Error())
}
cmd := exec.Command(os.Args[0], "-test.run=TestLegacyFlock", f.Name())
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-test.run=TestLegacyFlock", f.Name())
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
out, err := cmd.CombinedOutput()
if len(out) > 0 || err != nil {
Expand Down Expand Up @@ -2375,7 +2387,11 @@ func TestWait4(t *testing.T) {

for _, c := range testCases {
t.Run(c.name, func(t *testing.T) {
cmd := exec.Command(os.Args[0], "-test.run=^TestWait4$", fmt.Sprint(c.exitCode))
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-test.run=^TestWait4$", fmt.Sprint(c.exitCode))
cmd.Env = []string{"TEST_WAIT4_HELPER=1"}
if err := cmd.Start(); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -2676,7 +2692,11 @@ func TestMountNamespace(t *testing.T) {
defer os.Remove(f.Name())
f.Close()

cmd := exec.Command(os.Args[0], "-test.v", "-test.run=^TestMountNamespace$")
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-test.v", "-test.run=^TestMountNamespace$")
cmd.Env = append(os.Environ(), "SETNS_HELPER_PROCESS=1")
cmd.Env = append(cmd.Env, "MNT_NS_FILE="+f.Name())

Expand Down Expand Up @@ -3670,7 +3690,11 @@ func TestSetns(t *testing.T) {
}
}

cmd := exec.Command(os.Args[0], "-test.run=^TestSetns$")
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, "-test.run=^TestSetns$")
cmd.Env = append(os.Environ(), "SETNS_HELPER_PROCESS=1")
stdin, err := cmd.StdinPipe()
if err != nil {
Expand Down Expand Up @@ -3869,5 +3893,126 @@ func TestTty(t *testing.T) {

if !bytes.Equal(text, buffer[:n]) {
t.Fatalf("Expected %+v, read %+v\n", text, buffer[:n])

}

}

func TestSendfile(t *testing.T) {
srcContent := "hello, world"
srcFile, err := os.Create(filepath.Join(t.TempDir(), "source"))
if err != nil {
t.Fatal("error: ", err)
}
defer srcFile.Close()

dstFile, err := os.Create(filepath.Join(t.TempDir(), "dst"))
if err != nil {
t.Fatal("error: ", err)
}
defer dstFile.Close()

err = os.WriteFile(srcFile.Name(), []byte(srcContent), 0644)
if err != nil {
t.Fatal("error: ", err)
}

n, err := unix.Sendfile(int(dstFile.Fd()), int(srcFile.Fd()), nil, len(srcContent))
if n != len(srcContent) {
t.Fatal("error: mismatch content length want ", len(srcContent), " got ", n)
}
if err != nil {
t.Fatal("error: ", err)
}

b, err := os.ReadFile(dstFile.Name())
if err != nil {
t.Fatal("error: ", err)
}

content := string(b)
if content != srcContent {
t.Fatal("content mismatch: ", content, " vs ", srcContent)
}
}

func TestSendfileSocket(t *testing.T) {
// Set up source data file.
name := filepath.Join(t.TempDir(), "source")
const contents = "contents"
err := os.WriteFile(name, []byte(contents), 0666)
if err != nil {
t.Fatal(err)
}

done := make(chan bool)

// Start server listening on a socket.
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Skipf("listen failed: %s\n", err)
}
defer ln.Close()
go func() {
conn, err := ln.Accept()
if err != nil {
t.Errorf("failed to accept: %v", err)
return
}
defer conn.Close()
b, err := io.ReadAll(conn)
if err != nil {
t.Errorf("failed to read: %v", err)
return
}
if string(b) != contents {
t.Errorf("contents not transmitted: got %s (len=%d), want %s", string(b), len(b), contents)
}
done <- true
}()

// Open source file.
src, err := os.Open(name)
if err != nil {
t.Fatal(err)
}

// Send source file to server.
conn, err := net.Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatal(err)
}
file, err := conn.(*net.TCPConn).File()
if err != nil {
t.Fatal(err)
}
var off int64
n, err := unix.Sendfile(int(file.Fd()), int(src.Fd()), &off, len(contents))
if err != nil {
t.Errorf("Sendfile failed %s\n", err)
}
if n != len(contents) {
t.Errorf("written count wrong: want %d, got %d", len(contents), n)
}
// Note: off is updated on some systems and not others. Oh well.
// Linux: increments off by the amount sent.
// Darwin: leaves off unchanged.
// It would be nice to fix Darwin if we can.
if off != 0 && off != int64(len(contents)) {
t.Errorf("offset wrong: god %d, want %d or %d", off, 0, len(contents))
}
// The cursor position should be unchanged.
pos, err := src.Seek(0, 1)
if err != nil {
t.Errorf("can't get cursor position %s\n", err)
}
if pos != 0 {
t.Errorf("cursor position wrong: got %d, want 0", pos)
}

file.Close() // Note: required to have the close below really send EOF to the server.
conn.Close()

// Wait for server to close.
<-done
}

0 comments on commit ca04041

Please sign in to comment.