Skip to content

Commit 71e4cd6

Browse files
wgoianlancetaylor
authored andcommitted
unix: augment support for zos/s390x
This augments sys/unix support for zos/s390x by adding a small number of syscalls: Errno2 Err2ad W_Getmntent_A (pure ascii version of W_Getmntent) Select It also makes Mount and Unmount more Linux-like. A few necessary constants and types are added, and some tests. These changes do not affect other platforms in any way. Fixes golang/go#45838 Change-Id: I5783784a79b6c80a47cca74f3352bc07ea4ca682 Reviewed-on: https://go-review.googlesource.com/c/sys/+/314950 Run-TryBot: Tobias Klauser <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Trust: Emmanuel Odeke <[email protected]>
1 parent d19ff85 commit 71e4cd6

7 files changed

+384
-7
lines changed

unix/fdset.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
6-
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
5+
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
6+
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
77

88
package unix
99

unix/mmap_zos_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestMmap(t *testing.T) {
3333
fmt.Fprintf(destination, "%s\n", "0 <- Flipped between 0 and 1 when test runs successfully")
3434
fmt.Fprintf(destination, "%s\n", "//Do not change contents - mmap test relies on this")
3535
destination.Close()
36-
fd, err := unix.Open(filename, unix.O_RDWR, 0o777)
36+
fd, err := unix.Open(filename, unix.O_RDWR, 0777)
3737
if err != nil {
3838
t.Fatalf("Open: %v", err)
3939
}

unix/syscall_zos_s390x.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ func (cmsg *Cmsghdr) SetLen(length int) {
222222
//sys Creat(path string, mode uint32) (fd int, err error) = SYS___CREAT_A
223223
//sys Dup(oldfd int) (fd int, err error)
224224
//sys Dup2(oldfd int, newfd int) (err error)
225+
//sys Errno2() (er2 int) = SYS___ERRNO2
226+
//sys Err2ad() (eadd *int) = SYS___ERR2AD
225227
//sys Exit(code int)
226228
//sys Fchdir(fd int) (err error)
227229
//sys Fchmod(fd int, mode uint32) (err error)
@@ -245,10 +247,12 @@ func Fstat(fd int, stat *Stat_t) (err error) {
245247
//sys Poll(fds []PollFd, timeout int) (n int, err error) = SYS_POLL
246248
//sys Times(tms *Tms) (ticks uintptr, err error) = SYS_TIMES
247249
//sys W_Getmntent(buff *byte, size int) (lastsys int, err error) = SYS_W_GETMNTENT
250+
//sys W_Getmntent_A(buff *byte, size int) (lastsys int, err error) = SYS___W_GETMNTENT_A
248251

249-
//sys Mount(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) = SYS___MOUNT_A
250-
//sys Unmount(filesystem string, mtm int) (err error) = SYS___UMOUNT_A
252+
//sys mount_LE(path string, filesystem string, fstype string, mtm uint32, parmlen int32, parm string) (err error) = SYS___MOUNT_A
253+
//sys unmount(filesystem string, mtm int) (err error) = SYS___UMOUNT_A
251254
//sys Chroot(path string) (err error) = SYS___CHROOT_A
255+
//sys Select(nmsgsfds int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (ret int, err error) = SYS_SELECT
252256
//sysnb Uname(buf *Utsname) (err error) = SYS___UNAME_A
253257

254258
func Ptsname(fd int) (name string, err error) {
@@ -1779,3 +1783,47 @@ func SetNonblock(fd int, nonblocking bool) (err error) {
17791783
func Exec(argv0 string, argv []string, envv []string) error {
17801784
return syscall.Exec(argv0, argv, envv)
17811785
}
1786+
1787+
func Mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
1788+
if needspace := 8 - len(fstype); needspace <= 0 {
1789+
fstype = fstype[:8]
1790+
} else {
1791+
fstype += " "[:needspace]
1792+
}
1793+
return mount_LE(target, source, fstype, uint32(flags), int32(len(data)), data)
1794+
}
1795+
1796+
func Unmount(name string, mtm int) (err error) {
1797+
// mountpoint is always a full path and starts with a '/'
1798+
// check if input string is not a mountpoint but a filesystem name
1799+
if name[0] != '/' {
1800+
return unmount(name, mtm)
1801+
}
1802+
// treat name as mountpoint
1803+
b2s := func(arr []byte) string {
1804+
nulli := bytes.IndexByte(arr, 0)
1805+
if nulli == -1 {
1806+
return string(arr)
1807+
} else {
1808+
return string(arr[:nulli])
1809+
}
1810+
}
1811+
var buffer struct {
1812+
header W_Mnth
1813+
fsinfo [64]W_Mntent
1814+
}
1815+
fsCount, err := W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
1816+
if err != nil {
1817+
return err
1818+
}
1819+
if fsCount == 0 {
1820+
return EINVAL
1821+
}
1822+
for i := 0; i < fsCount; i++ {
1823+
if b2s(buffer.fsinfo[i].Mountpoint[:]) == name {
1824+
err = unmount(b2s(buffer.fsinfo[i].Fsname[:]), mtm)
1825+
break
1826+
}
1827+
}
1828+
return err
1829+
}

unix/syscall_zos_test.go

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package unix_test
99

1010
import (
11+
"bytes"
1112
"flag"
1213
"fmt"
1314
"io/ioutil"
@@ -20,6 +21,7 @@ import (
2021
"syscall"
2122
"testing"
2223
"time"
24+
"unsafe"
2325

2426
"golang.org/x/sys/unix"
2527
)
@@ -604,3 +606,266 @@ func chtmpdir(t *testing.T) func() {
604606
os.RemoveAll(d)
605607
}
606608
}
609+
610+
func TestMountUnmount(t *testing.T) {
611+
b2s := func(arr []byte) string {
612+
nulli := bytes.IndexByte(arr, 0)
613+
if nulli == -1 {
614+
return string(arr)
615+
} else {
616+
return string(arr[:nulli])
617+
}
618+
}
619+
// use an available fs
620+
var buffer struct {
621+
header unix.W_Mnth
622+
fsinfo [64]unix.W_Mntent
623+
}
624+
fsCount, err := unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
625+
if err != nil {
626+
t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
627+
} else if fsCount == 0 {
628+
t.Fatalf("W_Getmntent_A returns no entries")
629+
}
630+
var fs string
631+
var fstype string
632+
var mountpoint string
633+
var available bool = false
634+
for i := 0; i < fsCount; i++ {
635+
err = unix.Unmount(b2s(buffer.fsinfo[i].Mountpoint[:]), unix.MTM_RDWR)
636+
if err != nil {
637+
// Unmount and Mount require elevated privilege
638+
// If test is run without such permission, skip test
639+
if err == unix.EPERM {
640+
t.Logf("Permission denied for Unmount. Skipping test (Errno2: %X)", unix.Errno2())
641+
return
642+
} else if err == unix.EBUSY {
643+
continue
644+
} else {
645+
t.Fatalf("Unmount returns with error: %s", err.Error())
646+
}
647+
} else {
648+
available = true
649+
fs = b2s(buffer.fsinfo[i].Fsname[:])
650+
fstype = b2s(buffer.fsinfo[i].Fstname[:])
651+
mountpoint = b2s(buffer.fsinfo[i].Mountpoint[:])
652+
t.Logf("using file system = %s; fstype = %s and mountpoint = %s\n", fs, fstype, mountpoint)
653+
break
654+
}
655+
}
656+
if !available {
657+
t.Fatalf("No filesystem available")
658+
}
659+
// test unmount
660+
buffer.header = unix.W_Mnth{}
661+
fsCount, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
662+
if err != nil {
663+
t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
664+
}
665+
for i := 0; i < fsCount; i++ {
666+
if b2s(buffer.fsinfo[i].Fsname[:]) == fs {
667+
t.Fatalf("File system found after unmount")
668+
}
669+
}
670+
// test mount
671+
err = unix.Mount(fs, mountpoint, fstype, unix.MTM_RDWR, "")
672+
if err != nil {
673+
t.Fatalf("Mount returns with error: %s", err.Error())
674+
}
675+
buffer.header = unix.W_Mnth{}
676+
fsCount, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer)))
677+
if err != nil {
678+
t.Fatalf("W_Getmntent_A returns with error: %s", err.Error())
679+
}
680+
fsMounted := false
681+
for i := 0; i < fsCount; i++ {
682+
if b2s(buffer.fsinfo[i].Fsname[:]) == fs && b2s(buffer.fsinfo[i].Mountpoint[:]) == mountpoint {
683+
fsMounted = true
684+
}
685+
}
686+
if !fsMounted {
687+
t.Fatalf("%s not mounted after Mount()", fs)
688+
}
689+
}
690+
691+
func TestChroot(t *testing.T) {
692+
// create temp dir and tempfile 1
693+
tempDir, err := ioutil.TempDir("", "TestChroot")
694+
if err != nil {
695+
t.Fatalf("TempDir: %s", err.Error())
696+
}
697+
defer os.RemoveAll(tempDir)
698+
f, err := ioutil.TempFile(tempDir, "chroot_test_file")
699+
if err != nil {
700+
t.Fatalf("TempFile: %s", err.Error())
701+
}
702+
// chroot temp dir
703+
err = unix.Chroot(tempDir)
704+
// Chroot requires elevated privilege
705+
// If test is run without such permission, skip test
706+
if err == unix.EPERM {
707+
t.Logf("Denied permission for Chroot. Skipping test (Errno2: %X)", unix.Errno2())
708+
return
709+
} else if err != nil {
710+
t.Fatalf("Chroot: %s", err.Error())
711+
}
712+
// check if tempDir contains test file
713+
files, err := ioutil.ReadDir("/")
714+
if err != nil {
715+
t.Fatalf("ReadDir: %s", err.Error())
716+
}
717+
found := false
718+
for _, file := range files {
719+
if file.Name() == filepath.Base(f.Name()) {
720+
found = true
721+
break
722+
}
723+
}
724+
if !found {
725+
t.Fatalf("Temp file not found in temp dir")
726+
}
727+
}
728+
729+
func TestFlock(t *testing.T) {
730+
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
731+
defer os.Exit(0)
732+
if len(os.Args) != 3 {
733+
fmt.Printf("bad argument")
734+
return
735+
}
736+
fn := os.Args[2]
737+
f, err := os.OpenFile(fn, os.O_RDWR, 0755)
738+
if err != nil {
739+
fmt.Printf("%s", err.Error())
740+
return
741+
}
742+
err = unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
743+
// if the lock we are trying should be locked, ignore EAGAIN error
744+
// otherwise, report all errors
745+
if err != nil && err != unix.EAGAIN {
746+
fmt.Printf("%s", err.Error())
747+
}
748+
} else {
749+
// create temp dir and tempfile 1
750+
tempDir, err := ioutil.TempDir("", "TestFlock")
751+
if err != nil {
752+
t.Fatalf("TempDir: %s", err.Error())
753+
}
754+
defer os.RemoveAll(tempDir)
755+
f, err := ioutil.TempFile(tempDir, "flock_test_file")
756+
if err != nil {
757+
t.Fatalf("TempFile: %s", err.Error())
758+
}
759+
fd := int(f.Fd())
760+
761+
/* Test Case 1
762+
* Try acquiring an occupied lock from another process
763+
*/
764+
err = unix.Flock(fd, unix.LOCK_EX)
765+
if err != nil {
766+
t.Fatalf("Flock: %s", err.Error())
767+
}
768+
cmd := exec.Command(os.Args[0], "-test.run=TestFlock", f.Name())
769+
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
770+
out, err := cmd.CombinedOutput()
771+
if len(out) > 0 || err != nil {
772+
t.Fatalf("child process: %q, %v", out, err)
773+
}
774+
err = unix.Flock(fd, unix.LOCK_UN)
775+
if err != nil {
776+
t.Fatalf("Flock: %s", err.Error())
777+
}
778+
779+
/* Test Case 2
780+
* Try locking with Flock and FcntlFlock for same file
781+
*/
782+
err = unix.Flock(fd, unix.LOCK_EX)
783+
if err != nil {
784+
t.Fatalf("Flock: %s", err.Error())
785+
}
786+
flock := unix.Flock_t{
787+
Type: int16(unix.F_WRLCK),
788+
Whence: int16(0),
789+
Start: int64(0),
790+
Len: int64(0),
791+
Pid: int32(unix.Getppid()),
792+
}
793+
err = unix.FcntlFlock(f.Fd(), unix.F_SETLK, &flock)
794+
if err != nil {
795+
t.Fatalf("FcntlFlock: %s", err.Error())
796+
}
797+
}
798+
}
799+
800+
func TestSelect(t *testing.T) {
801+
for {
802+
n, err := unix.Select(0, nil, nil, nil, &unix.Timeval{Sec: 0, Usec: 0})
803+
if err == unix.EINTR {
804+
t.Logf("Select interrupted")
805+
continue
806+
} else if err != nil {
807+
t.Fatalf("Select: %v", err)
808+
}
809+
if n != 0 {
810+
t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
811+
}
812+
break
813+
}
814+
815+
dur := 250 * time.Millisecond
816+
var took time.Duration
817+
for {
818+
// On some platforms (e.g. Linux), the passed-in timeval is
819+
// updated by select(2). Make sure to reset to the full duration
820+
// in case of an EINTR.
821+
tv := unix.NsecToTimeval(int64(dur))
822+
start := time.Now()
823+
n, err := unix.Select(0, nil, nil, nil, &tv)
824+
took = time.Since(start)
825+
if err == unix.EINTR {
826+
t.Logf("Select interrupted after %v", took)
827+
continue
828+
} else if err != nil {
829+
t.Fatalf("Select: %v", err)
830+
}
831+
if n != 0 {
832+
t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
833+
}
834+
break
835+
}
836+
837+
// On some BSDs the actual timeout might also be slightly less than the requested.
838+
// Add an acceptable margin to avoid flaky tests.
839+
if took < dur*2/3 {
840+
t.Errorf("Select: got %v timeout, expected at least %v", took, dur)
841+
}
842+
843+
rr, ww, err := os.Pipe()
844+
if err != nil {
845+
t.Fatal(err)
846+
}
847+
defer rr.Close()
848+
defer ww.Close()
849+
850+
if _, err := ww.Write([]byte("HELLO GOPHER")); err != nil {
851+
t.Fatal(err)
852+
}
853+
854+
rFdSet := &unix.FdSet{}
855+
fd := int(rr.Fd())
856+
rFdSet.Set(fd)
857+
858+
for {
859+
n, err := unix.Select(fd+1, rFdSet, nil, nil, nil)
860+
if err == unix.EINTR {
861+
t.Log("Select interrupted")
862+
continue
863+
} else if err != nil {
864+
t.Fatalf("Select: %v", err)
865+
}
866+
if n != 1 {
867+
t.Fatalf("Select: got %v ready file descriptors, expected 1", n)
868+
}
869+
break
870+
}
871+
}

0 commit comments

Comments
 (0)