Skip to content

Commit

Permalink
x/sys/unix: make ReadDirent available on zOS
Browse files Browse the repository at this point in the history
Currently ReadDirent is not available on zOS. This implementation modifies the current darwin implementation to work on zOS.

Also make tests available on zOS.

Fixes golang/go#54528
Fixes golang/go#54587

Change-Id: I62a09d0068ec8c5fdc849a411ce43bdf14ca3926
Reviewed-on: https://go-review.googlesource.com/c/sys/+/424778
Reviewed-by: Ian Lance Taylor <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Joedian Reid <[email protected]>
Reviewed-by: Bill O'Farrell <[email protected]>
Run-TryBot: Ian Lance Taylor <[email protected]>
Auto-Submit: Ian Lance Taylor <[email protected]>
  • Loading branch information
dustin-ward authored and gopherbot committed Oct 13, 2022
1 parent 090e330 commit 95e765b
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 8 deletions.
4 changes: 2 additions & 2 deletions unix/dirent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions unix/dirent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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_test

Expand Down
4 changes: 2 additions & 2 deletions unix/getdirentries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 darwin || dragonfly || freebsd || openbsd || netbsd
// +build darwin dragonfly freebsd openbsd netbsd
//go:build darwin || dragonfly || freebsd || openbsd || netbsd || zos
// +build darwin dragonfly freebsd openbsd netbsd zos

package unix_test

Expand Down
173 changes: 172 additions & 1 deletion unix/syscall_zos_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ package unix

import (
"bytes"
"fmt"
"runtime"
"sort"
"strings"
"sync"
"syscall"
"unsafe"
Expand Down Expand Up @@ -55,7 +57,13 @@ func (d *Dirent) NameString() string {
if d == nil {
return ""
}
return string(d.Name[:d.Namlen])
s := string(d.Name[:])
idx := strings.IndexByte(s, 0)
if idx == -1 {
return s
} else {
return s[:idx]
}
}

func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
Expand Down Expand Up @@ -1230,6 +1238,14 @@ func Readdir(dir uintptr) (*Dirent, error) {
return &ent, err
}

func readdir_r(dirp uintptr, entry *direntLE, result **direntLE) (err error) {
r0, _, e1 := syscall_syscall(SYS___READDIR_R_A, dirp, uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
if int64(r0) == -1 {
err = errnoErr(Errno(e1))
}
return
}

func Closedir(dir uintptr) error {
_, _, e := syscall_syscall(SYS_CLOSEDIR, dir, 0, 0)
if e != 0 {
Expand Down Expand Up @@ -1821,3 +1837,158 @@ func Unmount(name string, mtm int) (err error) {
}
return err
}

func fdToPath(dirfd int) (path string, err error) {
var buffer [1024]byte
// w_ctrl()
ret := runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS_W_IOCTL<<4,
[]uintptr{uintptr(dirfd), 17, 1024, uintptr(unsafe.Pointer(&buffer[0]))})
if ret == 0 {
zb := bytes.IndexByte(buffer[:], 0)
if zb == -1 {
zb = len(buffer)
}
// __e2a_l()
runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___E2A_L<<4,
[]uintptr{uintptr(unsafe.Pointer(&buffer[0])), uintptr(zb)})
return string(buffer[:zb]), nil
}
// __errno()
errno := int(*(*int32)(unsafe.Pointer(runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___ERRNO<<4,
[]uintptr{}))))
// __errno2()
errno2 := int(runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___ERRNO2<<4,
[]uintptr{}))
// strerror_r()
ret = runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS_STRERROR_R<<4,
[]uintptr{uintptr(errno), uintptr(unsafe.Pointer(&buffer[0])), 1024})
if ret == 0 {
zb := bytes.IndexByte(buffer[:], 0)
if zb == -1 {
zb = len(buffer)
}
return "", fmt.Errorf("%s (errno2=0x%x)", buffer[:zb], errno2)
} else {
return "", fmt.Errorf("fdToPath errno %d (errno2=0x%x)", errno, errno2)
}
}

func direntLeToDirentUnix(dirent *direntLE, dir uintptr, path string) (Dirent, error) {
var d Dirent

d.Ino = uint64(dirent.Ino)
offset, err := Telldir(dir)
if err != nil {
return d, err
}

d.Off = int64(offset)
s := string(bytes.Split(dirent.Name[:], []byte{0})[0])
copy(d.Name[:], s)

d.Reclen = uint16(24 + len(d.NameString()))
var st Stat_t
path = path + "/" + s
err = Lstat(path, &st)
if err != nil {
return d, err
}

d.Type = uint8(st.Mode >> 24)
return d, err
}

func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
// Simulation of Getdirentries port from the Darwin implementation.
// COMMENTS FROM DARWIN:
// It's not the full required semantics, but should handle the case
// of calling Getdirentries or ReadDirent repeatedly.
// It won't handle assigning the results of lseek to *basep, or handle
// the directory being edited underfoot.

skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
if err != nil {
return 0, err
}

// Get path from fd to avoid unavailable call (fdopendir)
path, err := fdToPath(fd)
if err != nil {
return 0, err
}
d, err := Opendir(path)
if err != nil {
return 0, err
}
defer Closedir(d)

var cnt int64
for {
var entryLE direntLE
var entrypLE *direntLE
e := readdir_r(d, &entryLE, &entrypLE)
if e != nil {
return n, e
}
if entrypLE == nil {
break
}
if skip > 0 {
skip--
cnt++
continue
}

// Dirent on zos has a different structure
entry, e := direntLeToDirentUnix(&entryLE, d, path)
if e != nil {
return n, e
}

reclen := int(entry.Reclen)
if reclen > len(buf) {
// Not enough room. Return for now.
// The counter will let us know where we should start up again.
// Note: this strategy for suspending in the middle and
// restarting is O(n^2) in the length of the directory. Oh well.
break
}

// Copy entry into return buffer.
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
copy(buf, s)

buf = buf[reclen:]
n += reclen
cnt++
}
// Set the seek offset of the input fd to record
// how many files we've already returned.
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
if err != nil {
return n, err
}

return n, nil
}

func ReadDirent(fd int, buf []byte) (n int, err error) {
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
return Getdirentries(fd, buf, base)
}

func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}

func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
reclen, ok := direntReclen(buf)
if !ok {
return 0, false
}
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}
11 changes: 10 additions & 1 deletion unix/ztypes_zos_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,23 @@ type Statfs_t struct {
Flags uint64
}

type Dirent struct {
type direntLE struct {
Reclen uint16
Namlen uint16
Ino uint32
Extra uintptr
Name [256]byte
}

type Dirent struct {
Ino uint64
Off int64
Reclen uint16
Type uint8
Name [256]uint8
_ [5]byte
}

type FdSet struct {
Bits [64]int32
}
Expand Down

0 comments on commit 95e765b

Please sign in to comment.