| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build zos && s390x |
| |
| package unix_test |
| |
| import ( |
| "bytes" |
| "errors" |
| "flag" |
| "fmt" |
| "io" |
| "log" |
| "net" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "reflect" |
| "regexp" |
| "runtime" |
| "strconv" |
| "strings" |
| "syscall" |
| "testing" |
| "time" |
| "unsafe" |
| |
| "golang.org/x/sys/unix" |
| ) |
| |
| func TestLibVec(t *testing.T) { |
| ret := unix.GetZosLibVec() |
| if ret == 0 { |
| t.Fatalf("initLibVec failed %v\n", ret) |
| } |
| } |
| |
| func TestFprintf(t *testing.T) { |
| const expected = 61 |
| ret, _, _ := unix.CallLeFuncWithErr(unix.GetZosLibVec()+unix.SYS___FPRINTF_A<<4, |
| unix.ZosStdioFilep(2), uintptr(unsafe.Pointer( |
| reflect.ValueOf([]byte("TEST DATA please ignore, fprintf stderr data %d %d %d %d\x0a\x00")).Pointer())), |
| 111, 222, 333, 444) |
| if ret != expected { |
| t.Fatalf("expected bytes written %d , receive %d\n", expected, int(ret)) |
| } |
| } |
| |
| func TestErrnos(t *testing.T) { |
| ret, err1, err2 := unix.CallLeFuncWithErr(unix.GetZosLibVec()+unix.SYS___OPEN_A<<4, |
| uintptr(unsafe.Pointer(&(([]byte("/dev/nothing" + "\x00"))[0]))), 0, 0) |
| if ret != 0xffffffffffffffff || err2 != 0x79 || err1 != 0x562003f { |
| t.Fatalf("Expect ret ffffffffffffffff err2 79 err1 562003f, received ret %x err2 %x err1 %x\n", ret, err2, err1) |
| } |
| } |
| |
| func TestErrnoString(t *testing.T) { |
| var ( |
| ws unix.WaitStatus |
| rus unix.Rusage |
| ) |
| _, err := unix.Wait4(-1, &ws, unix.WNOHANG, &rus) |
| if syscall.Errno(int32(err.(syscall.Errno))) != unix.ECHILD { |
| t.Fatalf("err != unix.ECHILD") |
| } |
| } |
| |
| func BypassTestOnUntil(sys string, date string) bool { |
| t0, er0 := time.Parse(time.RFC3339, date) |
| if er0 != nil { |
| fmt.Printf("Bad date-time spec %s\n", date) |
| return false |
| } |
| if time.Now().After(t0) { |
| return false |
| } |
| host1, er1 := os.Hostname() |
| hostname := strings.Split(host1, ".")[0] |
| |
| if er1 == nil && strings.EqualFold(hostname, sys) { |
| pc, file, line, ok := runtime.Caller(1) |
| if ok { |
| name := runtime.FuncForPC(pc).Name() |
| fmt.Fprintf(os.Stderr, "TODO: Test bypassed on %s %v:%v %v\n", hostname, file, line, name) |
| return true |
| } |
| } |
| return false |
| |
| } |
| |
| var euid = unix.Geteuid() |
| |
| // Tests that below functions, structures and constants are consistent |
| // on all Unix-like systems. |
| func _() { |
| // program scheduling priority functions and constants |
| var ( |
| _ func(int, int, int) error = unix.Setpriority |
| _ func(int, int) (int, error) = unix.Getpriority |
| ) |
| const ( |
| _ int = unix.PRIO_USER |
| _ int = unix.PRIO_PROCESS |
| _ int = unix.PRIO_PGRP |
| ) |
| |
| // termios constants |
| const ( |
| _ int = unix.TCIFLUSH |
| _ int = unix.TCIOFLUSH |
| _ int = unix.TCOFLUSH |
| ) |
| |
| // fcntl file locking structure and constants |
| var ( |
| _ = unix.Flock_t{ |
| Type: int16(0), |
| Whence: int16(0), |
| Start: int64(0), |
| Len: int64(0), |
| Pid: int32(0), |
| } |
| ) |
| const ( |
| _ = unix.F_GETLK |
| _ = unix.F_SETLK |
| _ = unix.F_SETLKW |
| ) |
| } |
| |
| func zosLeVersion() (version, release uint32) { |
| p1 := (*(*uintptr)(unsafe.Pointer(uintptr(1208)))) >> 32 |
| p1 = *(*uintptr)(unsafe.Pointer(uintptr(p1 + 88))) |
| p1 = *(*uintptr)(unsafe.Pointer(uintptr(p1 + 8))) |
| p1 = *(*uintptr)(unsafe.Pointer(uintptr(p1 + 984))) |
| vrm := *(*uint32)(unsafe.Pointer(p1 + 80)) |
| version = (vrm & 0x00ff0000) >> 16 |
| release = (vrm & 0x0000ff00) >> 8 |
| return |
| } |
| |
| func TestErrnoSignalName(t *testing.T) { |
| testErrors := []struct { |
| num syscall.Errno |
| name string |
| }{ |
| {syscall.EPERM, "EDC5139I"}, |
| {syscall.EINVAL, "EDC5121I"}, |
| {syscall.ENOENT, "EDC5129I"}, |
| } |
| |
| for _, te := range testErrors { |
| t.Run(fmt.Sprintf("%d/%s", te.num, te.name), func(t *testing.T) { |
| e := unix.ErrnoName(te.num) |
| if e != te.name { |
| t.Errorf("ErrnoName(%d) returned %s, want %s", te.num, e, te.name) |
| } |
| }) |
| } |
| |
| testSignals := []struct { |
| num syscall.Signal |
| name string |
| }{ |
| {syscall.SIGHUP, "SIGHUP"}, |
| {syscall.SIGPIPE, "SIGPIPE"}, |
| {syscall.SIGSEGV, "SIGSEGV"}, |
| } |
| |
| for _, ts := range testSignals { |
| t.Run(fmt.Sprintf("%d/%s", ts.num, ts.name), func(t *testing.T) { |
| s := unix.SignalName(ts.num) |
| if s != ts.name { |
| t.Errorf("SignalName(%d) returned %s, want %s", ts.num, s, ts.name) |
| } |
| }) |
| } |
| } |
| |
| func TestSignalNum(t *testing.T) { |
| testSignals := []struct { |
| name string |
| want syscall.Signal |
| }{ |
| {"SIGHUP", syscall.SIGHUP}, |
| {"SIGPIPE", syscall.SIGPIPE}, |
| {"SIGSEGV", syscall.SIGSEGV}, |
| {"NONEXISTS", 0}, |
| } |
| for _, ts := range testSignals { |
| t.Run(fmt.Sprintf("%s/%d", ts.name, ts.want), func(t *testing.T) { |
| got := unix.SignalNum(ts.name) |
| if got != ts.want { |
| t.Errorf("SignalNum(%s) returned %d, want %d", ts.name, got, ts.want) |
| } |
| }) |
| |
| } |
| } |
| |
| func TestFcntlInt(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.FcntlInt(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) |
| } |
| } |
| |
| 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(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) |
| } |
| defer unix.Unlink(name) |
| defer unix.Close(fd) |
| flock := unix.Flock_t{ |
| Type: unix.F_RDLCK, |
| Start: 0, Len: 0, Whence: 1, |
| } |
| if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { |
| t.Fatalf("FcntlFlock failed: %v", err) |
| } |
| } |
| |
| 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 |
| // process is invoked as a normal test, with "go test", which then |
| // runs the child process by running the current test binary with args |
| // "-test.run=^TestPassFD$" and an environment variable used to signal |
| // that the test should become the child process instead. |
| func TestPassFD(t *testing.T) { |
| if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { |
| passFDChild() |
| return |
| } |
| |
| fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) |
| if err != nil { |
| t.Fatalf("Socketpair: %v", err) |
| } |
| defer unix.Close(fds[0]) |
| defer unix.Close(fds[1]) |
| writeFile := os.NewFile(uintptr(fds[0]), "child-writes") |
| readFile := os.NewFile(uintptr(fds[1]), "parent-reads") |
| defer writeFile.Close() |
| defer readFile.Close() |
| |
| 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) |
| } |
| cmd.ExtraFiles = []*os.File{writeFile} |
| |
| out, err := cmd.CombinedOutput() |
| if len(out) > 0 || err != nil { |
| t.Fatalf("child process: %q, %v", out, err) |
| } |
| |
| c, err := net.FileConn(readFile) |
| if err != nil { |
| t.Fatalf("FileConn: %v", err) |
| } |
| defer c.Close() |
| |
| uc, ok := c.(*net.UnixConn) |
| if !ok { |
| t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) |
| } |
| |
| buf := make([]byte, 32) // expect 1 byte |
| oob := make([]byte, 32) // expect 24 bytes |
| closeUnix := time.AfterFunc(5*time.Second, func() { |
| t.Logf("timeout reading from unix socket") |
| uc.Close() |
| }) |
| _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) |
| if err != nil { |
| t.Fatalf("ReadMsgUnix: %v", err) |
| } |
| closeUnix.Stop() |
| |
| scms, err := unix.ParseSocketControlMessage(oob[:oobn]) |
| if err != nil { |
| t.Fatalf("ParseSocketControlMessage: %v", err) |
| } |
| if len(scms) != 1 { |
| t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) |
| } |
| scm := scms[0] |
| gotFds, err := unix.ParseUnixRights(&scm) |
| if err != nil { |
| t.Fatalf("unix.ParseUnixRights: %v", err) |
| } |
| if len(gotFds) != 1 { |
| t.Fatalf("wanted 1 fd; got %#v", gotFds) |
| } |
| |
| f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") |
| defer f.Close() |
| |
| got, err := io.ReadAll(f) |
| want := "Hello from child process!\n" |
| if string(got) != want { |
| t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) |
| } |
| } |
| |
| // passFDChild is the child process used by TestPassFD. |
| func passFDChild() { |
| defer os.Exit(0) |
| |
| // Look for our fd. It should be fd 3, but we work around an fd leak |
| // bug here (http://golang.org/issue/2603) to let it be elsewhere. |
| var uc *net.UnixConn |
| for fd := uintptr(3); fd <= 10; fd++ { |
| f := os.NewFile(fd, "unix-conn") |
| var ok bool |
| netc, _ := net.FileConn(f) |
| uc, ok = netc.(*net.UnixConn) |
| if ok { |
| break |
| } |
| } |
| if uc == nil { |
| fmt.Println("failed to find unix fd") |
| return |
| } |
| |
| // Make a file f to send to our parent process on uc. |
| // We make it in tempDir, which our parent will clean up. |
| flag.Parse() |
| tempDir := flag.Arg(0) |
| f, err := os.CreateTemp(tempDir, "") |
| if err != nil { |
| fmt.Printf("TempFile: %v", err) |
| return |
| } |
| defer f.Close() |
| |
| f.Write([]byte("Hello from child process!\n")) |
| f.Seek(0, 0) |
| |
| rights := unix.UnixRights(int(f.Fd())) |
| dummyByte := []byte("x") |
| n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) |
| if err != nil { |
| fmt.Printf("WriteMsgUnix: %v", err) |
| return |
| } |
| if n != 1 || oobn != len(rights) { |
| fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) |
| return |
| } |
| } |
| |
| // 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{ |
| {{42}}, |
| {{1, 2}}, |
| {{3, 4, 5}}, |
| {{}}, |
| {{1, 2}, {3, 4, 5}, {}, {7}}, |
| } |
| for _, testCase := range testCases { |
| b := []byte{} |
| var n int |
| for _, fds := range testCase { |
| // Last assignment to n wins |
| n = len(b) + unix.CmsgLen(4*len(fds)) |
| b = append(b, unix.UnixRights(fds...)...) |
| } |
| // Truncate b |
| b = b[:n] |
| |
| scms, err := unix.ParseSocketControlMessage(b) |
| if err != nil { |
| t.Fatalf("ParseSocketControlMessage: %v", err) |
| } |
| 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 { |
| t.Fatalf("ParseUnixRights: %v", err) |
| } |
| wantFds := testCase[i] |
| if len(gotFds) != len(wantFds) { |
| t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) |
| } |
| for j, fd := range gotFds { |
| if fd != wantFds[j] { |
| t.Fatalf("expected fd %v, got %v", wantFds[j], fd) |
| } |
| } |
| } |
| } |
| } |
| |
| func TestPrlimit(t *testing.T) { |
| var rlimit, get, set, zero unix.Rlimit |
| // Save initial settings |
| err := unix.Prlimit(0, unix.RLIMIT_NOFILE, nil, &rlimit) |
| if err != nil { |
| t.Fatalf("Prlimit: save failed: %v", err) |
| } |
| if zero == rlimit { |
| t.Fatalf("Prlimit: save failed: got zero value %#v", rlimit) |
| } |
| set = rlimit |
| set.Cur = set.Max - 1 |
| // Set to one below max |
| err = unix.Prlimit(0, unix.RLIMIT_NOFILE, &set, nil) |
| if err != nil { |
| t.Fatalf("Prlimit: set failed: %#v %v", set, err) |
| } |
| // Get and restore to original |
| err = unix.Prlimit(0, unix.RLIMIT_NOFILE, &rlimit, &get) |
| if err != nil { |
| t.Fatalf("Prlimit: get and restore failed: %v", err) |
| } |
| if set != get { |
| t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) |
| } |
| } |
| |
| func TestRlimit(t *testing.T) { |
| var rlimit, zero unix.Rlimit |
| err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit) |
| if err != nil { |
| t.Fatalf("Getrlimit: save failed: %v", err) |
| } |
| if zero == rlimit { |
| t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) |
| } |
| set := rlimit |
| set.Cur = set.Max - 1 |
| |
| err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set) |
| if err != nil { |
| t.Fatalf("Setrlimit: set failed: %#v %v", set, err) |
| } |
| var get unix.Rlimit |
| err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get) |
| if err != nil { |
| t.Fatalf("Getrlimit: get failed: %v", err) |
| } |
| set = rlimit |
| set.Cur = set.Max - 1 |
| if set != get { |
| t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) |
| } |
| err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) |
| 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) { |
| _, err := unix.Seek(-1, 0, 0) |
| if err == nil { |
| t.Fatalf("Seek(-1, 0, 0) did not fail") |
| } |
| str := err.Error() // used to crash on Linux |
| t.Logf("Seek: %v", str) |
| if str == "" { |
| t.Fatalf("Seek(-1, 0, 0) return error with empty message") |
| } |
| } |
| |
| func TestSetsockoptString(t *testing.T) { |
| // should not panic on empty string, see issue #31277 |
| err := unix.SetsockoptString(-1, 0, 0, "") |
| if err == nil { |
| t.Fatalf("SetsockoptString: did not fail") |
| } |
| } |
| |
| func TestDup(t *testing.T) { |
| file, err := os.Create(filepath.Join(t.TempDir(), t.Name())) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer file.Close() |
| f := int(file.Fd()) |
| |
| newFd, err := unix.Dup(f) |
| if err != nil { |
| t.Fatalf("Dup: %v", err) |
| } |
| |
| // Create and reserve a file descriptor. |
| // Dup2 automatically closes it before reusing it. |
| nullFile, err := os.Open("/dev/null") |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer nullFile.Close() |
| |
| dupFd := int(file.Fd()) |
| err = unix.Dup2(newFd, dupFd) |
| if err != nil { |
| t.Fatalf("Dup2: %v", err) |
| } |
| // Keep the dummy file open long enough to not be closed in |
| // its finalizer. |
| runtime.KeepAlive(nullFile) |
| |
| b1 := []byte("Test123") |
| b2 := make([]byte, 7) |
| _, err = unix.Write(dupFd, b1) |
| if err != nil { |
| t.Fatalf("Write to dup2 fd failed: %v", err) |
| } |
| _, err = unix.Seek(f, 0, 0) |
| if err != nil { |
| t.Fatalf("Seek failed: %v", err) |
| } |
| _, err = unix.Read(f, b2) |
| if err != nil { |
| t.Fatalf("Read back failed: %v", err) |
| } |
| if string(b1) != string(b2) { |
| t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2)) |
| } |
| } |
| |
| func TestGetwd(t *testing.T) { |
| fd, err := os.Open(".") |
| if err != nil { |
| t.Fatalf("Open .: %s", err) |
| } |
| defer fd.Close() |
| // Directory list for test. Do not worry if any are symlinks or do not |
| // exist on some common unix desktop environments. That will be checked. |
| dirs := []string{"/", "/usr/bin", "/etc", "/var", "/opt"} |
| oldwd := os.Getenv("PWD") |
| for _, d := range dirs { |
| // Check whether d exists, is a dir and that d's path does not contain a symlink |
| fi, err := os.Stat(d) |
| if err != nil || !fi.IsDir() { |
| t.Logf("Test dir %s stat error (%v) or not a directory, skipping", d, err) |
| continue |
| } |
| check, err := filepath.EvalSymlinks(d) |
| if err != nil || check != d { |
| t.Logf("Test dir %s (%s) is symlink or other error (%v), skipping", d, check, err) |
| continue |
| } |
| err = os.Chdir(d) |
| if err != nil { |
| t.Fatalf("Chdir: %v", err) |
| } |
| pwd, err := unix.Getwd() |
| if err != nil { |
| t.Fatalf("Getwd in %s: %s", d, err) |
| } |
| os.Setenv("PWD", oldwd) |
| err = fd.Chdir() |
| if err != nil { |
| // We changed the current directory and cannot go back. |
| // Don't let the tests continue; they'll scribble |
| // all over some other directory. |
| fmt.Fprintf(os.Stderr, "fchdir back to dot failed: %s\n", err) |
| os.Exit(1) |
| } |
| if pwd != d { |
| t.Fatalf("Getwd returned %q want %q", pwd, d) |
| } |
| } |
| } |
| |
| func TestMkdev(t *testing.T) { |
| major := uint32(42) |
| minor := uint32(7) |
| dev := unix.Mkdev(major, minor) |
| |
| if unix.Major(dev) != major { |
| t.Errorf("Major(%#x) == %d, want %d", dev, unix.Major(dev), major) |
| } |
| if unix.Minor(dev) != minor { |
| t.Errorf("Minor(%#x) == %d, want %d", dev, unix.Minor(dev), minor) |
| } |
| } |
| func TestZosFdToPath(t *testing.T) { |
| f, err := os.OpenFile("/tmp", os.O_RDONLY, 0755) |
| if err != nil { |
| t.Fatalf("Openfile %v", err) |
| } |
| defer f.Close() |
| fd := f.Fd() |
| |
| var res string |
| res, err = unix.ZosFdToPath(int(fd)) |
| if err != nil { |
| t.Fatalf("ZosFdToPath %v", err) |
| } |
| chk := regexp.MustCompile(`^.*/([^\/]+)`).FindStringSubmatch(res) |
| lastpath := chk[len(chk)-1] |
| if lastpath != "tmp" { |
| t.Fatalf("original %s last part of path \"%s\" received, expected \"tmp\" \n", res, lastpath) |
| } |
| } |
| |
| // mktmpfifo creates a temporary FIFO and provides a cleanup function. |
| func mktmpfifo(t *testing.T) (*os.File, func()) { |
| err := unix.Mkfifo("fifo", 0666) |
| if err != nil { |
| t.Fatalf("mktmpfifo: failed to create FIFO: %v", err) |
| } |
| |
| f, err := os.OpenFile("fifo", os.O_RDWR, 0666) |
| if err != nil { |
| os.Remove("fifo") |
| t.Fatalf("mktmpfifo: failed to open FIFO: %v", err) |
| } |
| |
| return f, func() { |
| f.Close() |
| os.Remove("fifo") |
| } |
| } |
| |
| // utilities taken from os/os_test.go |
| |
| func touch(t *testing.T, name string) { |
| f, err := os.Create(name) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if err := f.Close(); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| // chtmpdir changes the working directory to a new temporary directory and |
| // 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.Fatal(err) |
| } |
| if err := os.Chdir(t.TempDir()); err != nil { |
| t.Fatal(err) |
| } |
| t.Cleanup(func() { |
| if err := os.Chdir(oldwd); err != nil { |
| t.Fatal(err) |
| } |
| }) |
| } |
| |
| func TestLegacyMountUnmount(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| b2s := func(arr []byte) string { |
| var str string |
| for i := 0; i < len(arr); i++ { |
| if arr[i] == 0 { |
| str = string(arr[:i]) |
| break |
| } |
| } |
| return str |
| } |
| // use an available fs |
| var buffer struct { |
| header unix.W_Mnth |
| fsinfo [64]unix.W_Mntent |
| } |
| fs_count, err := unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer))) |
| if err != nil { |
| t.Fatalf("W_Getmntent_A returns with error: %s", err.Error()) |
| } else if fs_count == 0 { |
| t.Fatalf("W_Getmntent_A returns no entries") |
| } |
| var fs string |
| var fstype string |
| var mountpoint string |
| var available bool = false |
| for i := 0; i < fs_count; i++ { |
| err = unix.Unmount(b2s(buffer.fsinfo[i].Mountpoint[:]), unix.MTM_RDWR) |
| if err != nil { |
| // Unmount and Mount require elevated privilege |
| // If test is run without such permission, skip test |
| if err == unix.EPERM { |
| t.Logf("Permission denied for Unmount. Skipping test (Errno2: %X)", unix.Errno2()) |
| return |
| } else if err == unix.EBUSY { |
| continue |
| } else { |
| t.Fatalf("Unmount returns with error: %s", err.Error()) |
| } |
| } else { |
| available = true |
| fs = b2s(buffer.fsinfo[i].Fsname[:]) |
| fstype = b2s(buffer.fsinfo[i].Fstname[:]) |
| mountpoint = b2s(buffer.fsinfo[i].Mountpoint[:]) |
| t.Logf("using file system = %s; fstype = %s and mountpoint = %s\n", fs, fstype, mountpoint) |
| break |
| } |
| } |
| if !available { |
| t.Fatalf("No filesystem available") |
| } |
| // test unmount |
| buffer.header = unix.W_Mnth{} |
| fs_count, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer))) |
| if err != nil { |
| t.Fatalf("W_Getmntent_A returns with error: %s", err.Error()) |
| } |
| for i := 0; i < fs_count; i++ { |
| if b2s(buffer.fsinfo[i].Fsname[:]) == fs { |
| t.Fatalf("File system found after unmount") |
| } |
| } |
| // test mount |
| err = unix.Mount(fs, mountpoint, fstype, unix.MTM_RDWR, "") |
| if err != nil { |
| t.Fatalf("Mount returns with error: %s", err.Error()) |
| } |
| buffer.header = unix.W_Mnth{} |
| fs_count, err = unix.W_Getmntent_A((*byte)(unsafe.Pointer(&buffer)), int(unsafe.Sizeof(buffer))) |
| if err != nil { |
| t.Fatalf("W_Getmntent_A returns with error: %s", err.Error()) |
| } |
| fs_mounted := false |
| for i := 0; i < fs_count; i++ { |
| if b2s(buffer.fsinfo[i].Fsname[:]) == fs && b2s(buffer.fsinfo[i].Mountpoint[:]) == mountpoint { |
| fs_mounted = true |
| } |
| } |
| if !fs_mounted { |
| t.Fatalf("%s not mounted after Mount()", fs) |
| } |
| } |
| |
| func TestChroot(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| // create temp dir and tempfile 1 |
| tempDir := t.TempDir() |
| f, err := os.CreateTemp(tempDir, "chroot_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %s", err.Error()) |
| } |
| defer f.Close() |
| |
| // chroot temp dir |
| err = unix.Chroot(tempDir) |
| // Chroot requires elevated privilege |
| // If test is run without such permission, skip test |
| if err == unix.EPERM { |
| t.Logf("Denied permission for Chroot. Skipping test (Errno2: %X)", unix.Errno2()) |
| return |
| } else if err != nil { |
| t.Fatalf("Chroot: %s", err.Error()) |
| } |
| // check if tempDir contains test file |
| files, err := os.ReadDir("/") |
| if err != nil { |
| t.Fatalf("ReadDir: %s", err.Error()) |
| } |
| found := false |
| for _, file := range files { |
| if file.Name() == filepath.Base(f.Name()) { |
| found = true |
| break |
| } |
| } |
| if !found { |
| t.Fatalf("Temp file not found in temp dir") |
| } |
| } |
| |
| func TestFlock(t *testing.T) { |
| if v, r := zosLeVersion(); v <= 2 && r <= 4 { |
| t.Skipf("New flock can't be used in %d.%d < 2.5. Run TestLegacyFlock", v, r) |
| } |
| |
| const ( |
| SUCCESS = iota |
| BLOCKED |
| ) |
| |
| if os.Getenv("TEST_FLOCK_HELPER") == "1" { |
| defer os.Exit(0) |
| if len(os.Args) != 4 { |
| log.Fatal("bad argument") |
| } |
| mode, err := strconv.Atoi(os.Args[2]) |
| if err != nil { |
| log.Fatalf("%s is invalid: %s", os.Args[2], err) |
| } |
| filename := os.Args[3] |
| f, err := os.OpenFile(filename, os.O_RDWR, 0755) |
| if err != nil { |
| log.Fatalf("%s", err.Error()) |
| } |
| defer f.Close() |
| |
| go func() { |
| // timeout |
| time.Sleep(5 * time.Second) |
| unix.Flock(int(f.Fd()), unix.LOCK_UN) |
| fmt.Print(BLOCKED) |
| os.Exit(1) |
| }() |
| |
| err = unix.Flock(int(f.Fd()), mode) |
| if err == unix.EWOULDBLOCK { |
| fmt.Print(int(unix.EWOULDBLOCK)) |
| os.Exit(1) |
| } |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer unix.Flock(int(f.Fd()), unix.LOCK_UN) |
| fmt.Print(0) |
| return |
| } |
| |
| f, err := os.Create(filepath.Join(t.TempDir(), "flock_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %s\n", err) |
| } |
| defer f.Close() |
| fd := int(f.Fd()) |
| |
| testCases := []struct { |
| name string |
| fd int |
| p1modes []int |
| p2mode int |
| expected syscall.Errno |
| }{ |
| {"Invalid fd", -1, []int{unix.LOCK_SH}, unix.LOCK_SH, unix.EBADF}, |
| {"Invalid mode", fd, []int{unix.LOCK_EX | unix.LOCK_SH}, unix.LOCK_SH, unix.EINVAL}, |
| {"EX-EX", fd, []int{unix.LOCK_SH, unix.LOCK_EX}, unix.LOCK_EX, BLOCKED}, |
| {"EX-SH", fd, []int{unix.LOCK_SH, unix.LOCK_EX}, unix.LOCK_SH, BLOCKED}, |
| {"SH-EX", fd, []int{unix.LOCK_EX, unix.LOCK_SH}, unix.LOCK_EX, BLOCKED}, |
| {"SH-SH", fd, []int{unix.LOCK_EX, unix.LOCK_SH}, unix.LOCK_SH, SUCCESS}, |
| {"EX-EXNB", fd, []int{unix.LOCK_SH, unix.LOCK_EX}, unix.LOCK_EX | unix.LOCK_NB, unix.EWOULDBLOCK}, |
| {"EX-SHNB", fd, []int{unix.LOCK_SH, unix.LOCK_EX}, unix.LOCK_SH | unix.LOCK_NB, unix.EWOULDBLOCK}, |
| {"SH-SHNB", fd, []int{unix.LOCK_EX, unix.LOCK_SH}, unix.LOCK_EX | unix.LOCK_NB, unix.EWOULDBLOCK}, |
| {"SH-SHNB", fd, []int{unix.LOCK_EX, unix.LOCK_SH}, unix.LOCK_SH | unix.LOCK_NB, SUCCESS}, |
| } |
| // testcase: |
| for _, c := range testCases { |
| t.Run(c.name, func(t *testing.T) { |
| for _, mode := range c.p1modes { |
| err = unix.Flock(c.fd, mode) |
| if err == c.expected { |
| return |
| } |
| if err != nil { |
| t.Fatalf("failed to acquire Flock with mode(%d): %s\n", mode, err) |
| } |
| } |
| |
| p2status := BLOCKED |
| done := make(chan bool) |
| execP2 := func(isBlock bool) { |
| 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 { |
| log.Fatalf("p2status is not valid: %s\n", err) |
| } |
| if isBlock { |
| done <- true |
| } |
| } |
| |
| if c.expected == BLOCKED { |
| go execP2(true) |
| <-done |
| } else { |
| execP2(false) |
| } |
| |
| if p2status != int(c.expected) { |
| unix.Flock(c.fd, unix.LOCK_UN) |
| t.Fatalf("expected %d, actual %d\n", c.expected, p2status) |
| } |
| unix.Flock(c.fd, unix.LOCK_UN) |
| }) |
| |
| } |
| } |
| |
| func TestLegacyFlock(t *testing.T) { |
| if v, r := zosLeVersion(); v > 2 || (v == 2 && r > 4) { |
| t.Skipf("Legacy flock can't be used in %d.%d > 2.4. Run TestFlock", v, r) |
| } |
| if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { |
| defer os.Exit(0) |
| if len(os.Args) != 3 { |
| fmt.Printf("bad argument") |
| return |
| } |
| fn := os.Args[2] |
| f, err := os.OpenFile(fn, os.O_RDWR, 0755) |
| if err != nil { |
| fmt.Printf("%s", err.Error()) |
| return |
| } |
| err = unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB) |
| // if the lock we are trying should be locked, ignore EAGAIN error |
| // otherwise, report all errors |
| if err != nil && err != unix.EAGAIN { |
| fmt.Printf("%s", err.Error()) |
| } |
| } else { |
| // create tempfile 1 |
| f, err := os.Create(filepath.Join(t.TempDir(), "flock_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %s", err.Error()) |
| } |
| defer f.Close() |
| |
| fd := int(f.Fd()) |
| |
| /* Test Case 1 |
| * Try acquiring an occupied lock from another process |
| */ |
| err = unix.Flock(fd, unix.LOCK_EX) |
| if err != nil { |
| t.Fatalf("Flock: %s", err.Error()) |
| } |
| 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 { |
| t.Fatalf("child process: %q, %v", out, err) |
| } |
| err = unix.Flock(fd, unix.LOCK_UN) |
| if err != nil { |
| t.Fatalf("Flock: %s", err.Error()) |
| } |
| |
| /* Test Case 2 |
| * Try locking with Flock and FcntlFlock for same file |
| */ |
| err = unix.Flock(fd, unix.LOCK_EX) |
| if err != nil { |
| t.Fatalf("Flock: %s", err.Error()) |
| } |
| flock := unix.Flock_t{ |
| Type: int16(unix.F_WRLCK), |
| Whence: int16(0), |
| Start: int64(0), |
| Len: int64(0), |
| Pid: int32(unix.Getppid()), |
| } |
| err = unix.FcntlFlock(f.Fd(), unix.F_SETLK, &flock) |
| if err != nil { |
| t.Fatalf("FcntlFlock: %s", err.Error()) |
| } |
| } |
| } |
| |
| func TestSelect(t *testing.T) { |
| for { |
| n, err := unix.Select(0, nil, nil, nil, &unix.Timeval{Sec: 0, Usec: 0}) |
| if err == unix.EINTR { |
| t.Logf("Select interrupted") |
| continue |
| } else if err != nil { |
| t.Fatalf("Select: %v", err) |
| } |
| if n != 0 { |
| t.Fatalf("Select: got %v ready file descriptors, expected 0", n) |
| } |
| break |
| } |
| |
| dur := 250 * time.Millisecond |
| var took time.Duration |
| for { |
| // On some platforms (e.g. Linux), the passed-in timeval is |
| // updated by select(2). Make sure to reset to the full duration |
| // in case of an EINTR. |
| tv := unix.NsecToTimeval(int64(dur)) |
| start := time.Now() |
| n, err := unix.Select(0, nil, nil, nil, &tv) |
| took = time.Since(start) |
| if err == unix.EINTR { |
| t.Logf("Select interrupted after %v", took) |
| continue |
| } else if err != nil { |
| t.Fatalf("Select: %v", err) |
| } |
| if n != 0 { |
| t.Fatalf("Select: got %v ready file descriptors, expected 0", n) |
| } |
| break |
| } |
| |
| // On some BSDs the actual timeout might also be slightly less than the requested. |
| // Add an acceptable margin to avoid flaky tests. |
| if took < dur*2/3 { |
| t.Errorf("Select: got %v timeout, expected at least %v", took, dur) |
| } |
| |
| rr, ww, err := os.Pipe() |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer rr.Close() |
| defer ww.Close() |
| |
| if _, err := ww.Write([]byte("HELLO GOPHER")); err != nil { |
| t.Fatal(err) |
| } |
| |
| rFdSet := &unix.FdSet{} |
| fd := int(rr.Fd()) |
| rFdSet.Set(fd) |
| |
| for { |
| n, err := unix.Select(fd+1, rFdSet, nil, nil, nil) |
| if err == unix.EINTR { |
| t.Log("Select interrupted") |
| continue |
| } else if err != nil { |
| t.Fatalf("Select: %v", err) |
| } |
| if n != 1 { |
| t.Fatalf("Select: got %v ready file descriptors, expected 1", n) |
| } |
| break |
| } |
| } |
| |
| func TestUnixCredentials(t *testing.T) { |
| var ucred syscall.Ucred |
| if os.Getuid() != 0 { |
| ucred.Pid = int32(os.Getpid()) |
| ucred.Uid = 0 |
| ucred.Gid = 0 |
| } |
| |
| ucred.Pid = int32(os.Getpid()) |
| ucred.Uid = uint32(os.Getuid()) |
| ucred.Gid = uint32(os.Getgid()) |
| oob := syscall.UnixCredentials(&ucred) |
| |
| // On SOCK_STREAM, this is internally going to send a dummy byte |
| scm, err := syscall.ParseSocketControlMessage(oob) |
| if err != nil { |
| t.Fatalf("ParseSocketControlMessage: %v", err) |
| } |
| newUcred, err := syscall.ParseUnixCredentials(&scm[0]) |
| if err != nil { |
| t.Fatalf("ParseUnixCredentials: %v", err) |
| } |
| if *newUcred != ucred { |
| t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred) |
| } |
| } |
| |
| func TestFutimes(t *testing.T) { |
| // Create temp dir and file |
| f, err := os.Create(filepath.Join(t.TempDir(), "futimes_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %s", err.Error()) |
| } |
| defer f.Close() |
| |
| fd := int(f.Fd()) |
| |
| // Set mod time to newTime |
| newTime := time.Date(2001, time.Month(2), 15, 7, 7, 7, 0, time.UTC) |
| err = unix.Futimes( |
| fd, |
| []unix.Timeval{ |
| unix.Timeval{newTime.Unix(), 0}, |
| unix.Timeval{newTime.Unix(), 0}, |
| }) |
| if err != nil { |
| t.Fatalf("TestFutimes: %v", err) |
| } |
| |
| // Compare mod time |
| stats, err := f.Stat() |
| if err != nil { |
| t.Fatalf("Stat: %v", err) |
| } |
| |
| modTime := stats.ModTime() |
| if modTime.UTC() != newTime { |
| t.Fatalf("TestFutimes: modTime = %v, want %v", modTime.UTC(), newTime) |
| } |
| } |
| |
| func TestLutimes(t *testing.T) { |
| // Create temp dir and file |
| tempDir := t.TempDir() |
| f, err := os.CreateTemp(tempDir, "lutimes_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %s", err.Error()) |
| } |
| defer f.Close() |
| |
| symlinkPath := tempDir + "/test_symlink" |
| err = os.Symlink(f.Name(), symlinkPath) |
| if err != nil { |
| t.Fatalf("Symlink: %v", err) |
| } |
| |
| // Set mod time to newTime |
| newTime := time.Date(2001, time.Month(2), 15, 7, 7, 7, 0, time.UTC) |
| err = unix.Lutimes( |
| symlinkPath, |
| []unix.Timeval{ |
| unix.Timeval{newTime.Unix(), 0}, |
| unix.Timeval{newTime.Unix(), 0}, |
| }) |
| if err != nil { |
| t.Fatalf("TestLutimes: %v", err) |
| } |
| |
| // Compare mod time |
| stats, err := os.Lstat(symlinkPath) |
| if err != nil { |
| t.Fatalf("Lstat: %v", err) |
| } |
| |
| modTime := stats.ModTime() |
| if modTime.UTC() != newTime { |
| t.Fatalf("TestLutimes: modTime = %v, want %v", modTime.UTC(), newTime) |
| } |
| |
| } |
| |
| func TestDirfd(t *testing.T) { |
| // Create temporary directory |
| tempDir := t.TempDir() |
| |
| f, err := os.CreateTemp(tempDir, "dirfd_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer f.Close() |
| |
| // Open temp dir and get stream |
| dirStream, err := unix.Opendir(tempDir) |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| // Get fd from stream |
| dirFd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirFd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirFd) |
| } |
| |
| oldwd, err := os.Getwd() |
| if err != nil { |
| t.Fatalf("Getwd: %v", err) |
| } |
| defer os.Chdir(oldwd) |
| |
| // Change dir to fd and get path |
| err = unix.Fchdir(dirFd) |
| if err != nil { |
| t.Fatalf("Fchdir: %v", err) |
| } |
| path, err := os.Getwd() |
| if err != nil { |
| t.Fatalf("Getwd: %v", err) |
| } |
| |
| pathInfo, err := os.Lstat(path) |
| if err != nil { |
| t.Fatalf("os.Stat: %v", err) |
| } |
| tempDirInfo2, err := os.Lstat(tempDir) |
| if err != nil { |
| t.Fatalf("os.Stat: %v", err) |
| } |
| // Perform Test |
| if !os.SameFile(pathInfo, tempDirInfo2) { |
| t.Fatalf("Dirfd: expected working directory to be %v, actually: %v", tempDir, path) |
| } |
| } |
| |
| func TestEpollCreate(t *testing.T) { |
| if BypassTestOnUntil("zoscan59", "2024-04-01T12:45:21.123Z") { |
| t.Skip("skipping on zoscan59 until 2024-04-01") |
| } |
| efd, err := unix.EpollCreate(1) |
| if err != nil { |
| t.Fatalf("EpollCreate: %v", err) |
| } |
| defer unix.Close(efd) |
| } |
| |
| func TestEpollCreate1(t *testing.T) { |
| if BypassTestOnUntil("zoscan59", "2024-04-01T12:45:21.123Z") { |
| t.Skip("skipping on zoscan59 until 2024-04-01") |
| } |
| efd, err := unix.EpollCreate1(0) |
| if err != nil { |
| t.Fatalf("EpollCreate1: %v", err) |
| } |
| unix.Close(efd) |
| } |
| |
| func TestEpoll(t *testing.T) { |
| if BypassTestOnUntil("zoscan59", "2024-04-01T12:45:21.123Z") { |
| t.Skip("skipping on zoscan59 until 2024-04-01") |
| } |
| efd, err := unix.EpollCreate1(0) // no CLOEXEC equivalent on z/OS |
| if err != nil { |
| t.Fatalf("EpollCreate1: %v", err) |
| } |
| // no need to defer a close on efd, as it's not a real file descriptor on zos |
| |
| r, w, err := os.Pipe() |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer r.Close() |
| defer w.Close() |
| |
| fd := int(r.Fd()) |
| ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)} |
| |
| err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev) |
| if err != nil { |
| t.Fatalf("EpollCtl: %v", err) |
| } |
| |
| if _, err := w.Write([]byte("HELLO GOPHER")); err != nil { |
| t.Fatal(err) |
| } |
| |
| events := make([]unix.EpollEvent, 128) |
| n, err := unix.EpollWait(efd, events, 1) |
| if err != nil { |
| t.Fatalf("EpollWait: %v", err) |
| } |
| |
| if n != 1 { |
| t.Errorf("EpollWait: wrong number of events: got %v, expected 1", n) |
| } |
| |
| got := int(events[0].Fd) |
| if got != fd { |
| t.Errorf("EpollWait: wrong Fd in event: got %v, expected %v", got, fd) |
| } |
| |
| if events[0].Events&unix.EPOLLIN == 0 { |
| t.Errorf("Expected EPOLLIN flag to be set, got %b", events[0].Events) |
| } |
| |
| x := 0 |
| n, err = unix.EpollPwait(efd, events, 1, &x) |
| if err != nil { |
| t.Fatalf("EpollPwait: %v", err) |
| } |
| } |
| |
| func TestEventfd(t *testing.T) { |
| fd, err := unix.Eventfd(0, 0) |
| if err != nil { |
| t.Fatalf("Eventfd: %v", err) |
| } |
| if fd <= 2 { |
| t.Fatalf("Eventfd: fd <= 2, got: %d", fd) |
| } |
| } |
| |
| func TestEventfdSemaphore(t *testing.T) { |
| efd, err := unix.Eventfd(1, unix.EFD_SEMAPHORE|unix.EFD_NONBLOCK|unix.EFD_CLOEXEC) |
| if err != nil { |
| t.Fatalf("Eventfd: %v", err) |
| } |
| |
| writeBytes := make([]byte, 8) |
| writeBytes[7] = 0x4 |
| n, err := unix.Write(efd, writeBytes) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| if n != 8 { |
| t.Fatalf("Write: only wrote %d bytes, wanted 8", n) |
| } |
| |
| for i := 0; i < 5; i++ { |
| readBytes := make([]byte, 8) |
| n, err := unix.Read(efd, readBytes) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n != 8 { |
| t.Fatalf("Read: only read %d bytes, wanted 8", n) |
| } |
| } |
| readBytes := make([]byte, 8) |
| n, err = unix.Read(efd, readBytes) |
| if err == nil || err.Error() != "EDC5112I Resource temporarily unavailable." { |
| t.Fatalf("Read: expected error \"EDC5112I Resource temporarily unavailable.\", got %v", err) |
| } |
| if n != -1 { |
| t.Fatalf("Read: expected error code -1, got %d", n) |
| } |
| if readBytes[7] != 0 { |
| t.Fatalf("Read: expected return of 0, got %d", readBytes[7]) |
| } |
| } |
| |
| func TestStatfs(t *testing.T) { |
| // Create temporary directory |
| tempDir := t.TempDir() |
| |
| var stat unix.Statfs_t |
| if err := unix.Statfs(tempDir, &stat); err != nil { |
| t.Fatalf("Stafs: %v", err) |
| } |
| |
| if stat.Files == 0 { |
| t.Fatalf("Statfs: expected files > 0") |
| } |
| } |
| |
| func TestStatfsProc(t *testing.T) { |
| // Create temporary directory |
| |
| if _, err := os.Stat("/proc/self"); errors.Is(err, os.ErrNotExist) { |
| t.Skip("/proc/self is not exist skipping the test") |
| } |
| |
| var stat unix.Statfs_t |
| if err := unix.Statfs("/proc/self/ns", &stat); err != nil { |
| t.Fatalf("Stafs: %v", err) |
| } |
| |
| if stat.Type != unix.PROC_SUPER_MAGIC { |
| t.Fatalf("Statfs: expected files > 0") |
| } |
| } |
| |
| func TestFstatfs(t *testing.T) { |
| // Create temporary directory |
| file, err := os.Create(filepath.Join(t.TempDir(), "fstatfs_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer file.Close() |
| |
| fd := int(file.Fd()) |
| |
| var stat unix.Statfs_t |
| if err = unix.Fstatfs(fd, &stat); err != nil { |
| t.Fatalf("Stafs: %v", err) |
| } |
| |
| if stat.Files == 0 { |
| t.Fatalf("Statfs: expected files > 0") |
| } |
| } |
| |
| func TestFdatasync(t *testing.T) { |
| t.Skip("FAIL: Known failure, would hang if not skipped") |
| // Create temporary directory |
| file, err := os.Create(filepath.Join(t.TempDir(), "fdatasync_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| file.Close() |
| |
| fd := int(file.Fd()) |
| |
| var stat1 unix.Stat_t |
| if err = unix.Fstat(fd, &stat1); err != nil { |
| t.Fatalf("Fstat: %v", err) |
| } |
| |
| time.Sleep(1 * time.Second) |
| if _, err := unix.Write(fd, []byte("Test string")); err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| |
| var stat2 unix.Stat_t |
| if err = unix.Fstat(fd, &stat2); err != nil { |
| t.Fatalf("Fstat: %v", err) |
| } |
| |
| time.Sleep(1 * time.Second) |
| if err = unix.Fdatasync(fd); err != nil { |
| t.Fatalf("Fdatasync: %v", err) |
| } |
| |
| var stat3 unix.Stat_t |
| if err = unix.Fstat(fd, &stat3); err != nil { |
| t.Fatalf("Fstat: %v", err) |
| } |
| |
| if stat2.Mtim != stat3.Mtim { |
| t.Fatalf("Fdatasync: Modify times do not match. Wanted %v, got %v", stat2.Mtim, stat3.Mtim) |
| } |
| } |
| |
| func TestReadDirent(t *testing.T) { |
| // Create temporary directory and files |
| tempDir := t.TempDir() |
| |
| f1, err := os.CreateTemp(tempDir, "ReadDirent_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer f1.Close() |
| f2, err := os.CreateTemp(tempDir, "ReadDirent_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer f2.Close() |
| |
| tempSubDir, err := os.MkdirTemp(tempDir, "ReadDirent_SubDir") |
| if err != nil { |
| t.Fatalf("TempDir: %v", err) |
| } |
| f3, err := os.CreateTemp(tempSubDir, "ReadDirent_subDir_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer f3.Close() |
| |
| // Get fd of tempDir |
| dir, err := os.Open(tempDir) |
| if err != nil { |
| t.Fatalf("Open: %v", err) |
| } |
| defer dir.Close() |
| |
| fd := int(dir.Fd()) |
| |
| // Run Getdirentries |
| buf := make([]byte, 2048) |
| n, err := unix.ReadDirent(fd, buf) |
| if err != nil { |
| t.Fatalf("ReadDirent: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("ReadDirent: 0 bytes read") |
| } |
| |
| names := make([]string, 0) |
| consumed, count, _ := unix.ParseDirent(buf, 100, names) |
| if consumed == 0 { |
| t.Fatalf("ParseDirent: consumed 0 bytes") |
| } |
| if count != 3 { |
| t.Fatalf("ParseDirent: only recorded %d entries, expected 3", count) |
| } |
| } |
| |
| func TestPipe2(t *testing.T) { |
| var p [2]int |
| err := unix.Pipe2(p[:], unix.O_CLOEXEC) |
| if err != nil { |
| t.Fatalf("Pipe2: %v", err) |
| } |
| |
| r, w := int(p[0]), int(p[1]) |
| |
| n1, err := unix.Write(w, []byte("Testing pipe2!")) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| |
| buf := make([]byte, 256) |
| n2, err := unix.Read(r, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| |
| if n1 != n2 { |
| t.Fatalf("Pipe2: bytes read != bytes written. Wrote %d, read %d", n1, n2) |
| } |
| } |
| |
| func TestInotify(t *testing.T) { |
| // Driver func to try and capture all possible events |
| t.Run("IN_ACCESS", inotify_access) |
| t.Run("IN_ATTRIB", inotify_attrib) |
| t.Run("IN_CLOSE_WRITE", inotify_close_write) |
| t.Run("IN_CLOSE_NOWRITE", inotify_close_nowrite) |
| t.Run("IN_CREATE", inotify_create) |
| t.Run("IN_DELETE", inotify_delete) |
| t.Run("IN_DELETE_SELF", inotify_delete_self) |
| t.Run("IN_MODIFY", inotify_modify) |
| t.Run("IN_MOVE_SELF", inotify_move_self) |
| t.Run("IN_MOVED_FROM", inotify_moved_from) |
| t.Run("IN_MOVED_TO", inotify_moved_to) |
| t.Run("IN_OPEN", inotify_open) |
| } |
| |
| func inotify_access(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.Create(filepath.Join(tempDir, "inotify_access_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_ACCESS) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| n, err := tempFile.Write([]byte("Writing before reading")) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| if n <= 0 { |
| t.Fatalf("Did not write any data") |
| } |
| tempFile.Seek(0, 0) |
| |
| buf := make([]byte, 64) |
| n, err = tempFile.Read(buf) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n <= 0 { |
| t.Fatalf("Did not read any data") |
| } |
| |
| // Expect event |
| buf = make([]byte, unix.SizeofInotifyEvent) |
| n, err = unix.Read(infd, buf[:]) |
| if n == -1 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_attrib(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.Create(filepath.Join(tempDir, "inotify_attrib_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| defer unix.Close(infd) |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_ATTRIB) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| if err = tempFile.Chmod(0777); err != nil { |
| t.Fatalf("Chmod: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if n == -1 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_close_write(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.Create(filepath.Join(tempDir, "inotify_close_write_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| // File closed in test later |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_CLOSE_WRITE) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| _, err = tempFile.Write([]byte("Writing before closing")) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| tempFile.Close() |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if n == -1 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_close_nowrite(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_close_nowrite_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| // File closed later in test |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempDir, unix.IN_CLOSE_NOWRITE) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| d, err := os.Open(tempDir) |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| tempFile.Close() |
| d.Close() |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent*4) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_create(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempDir, unix.IN_CREATE) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| f, err := os.CreateTemp(tempDir, "inotify_create_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| f.Close() |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent*4) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_delete(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_delete_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| // File closed later in test |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempDir, unix.IN_DELETE) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| name := tempFile.Name() |
| tempFile.Close() |
| if err = os.Remove(name); err != nil { |
| t.Fatalf("Remove: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent*4) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_delete_self(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_delete_self_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| // File closed later in test |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| _, err = unix.InotifyAddWatch(infd, tempDir, unix.IN_DELETE_SELF) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| tempFile.Close() |
| if err = os.RemoveAll(tempDir); err != nil { |
| t.Fatalf("RemoveAll: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| } |
| |
| func inotify_modify(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_modify_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_MODIFY) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| _, err = tempFile.Write([]byte("Writing before closing")) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_move_self(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_move_self_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_MOVE_SELF) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| err = os.Rename(tempFile.Name(), tempFile.Name()+"2") |
| if err != nil { |
| t.Fatalf("Rename: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_moved_from(t *testing.T) { |
| // Create temporary files |
| tempDir1 := t.TempDir() |
| tempDir2 := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir1, "inotify_moved_from_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempDir1, unix.IN_MOVED_FROM) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| filename := strings.TrimPrefix(tempFile.Name(), tempDir1) |
| err = os.Rename(tempDir1+filename, |
| tempDir2+filename) |
| if err != nil { |
| t.Fatalf("Rename: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent*4) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_moved_to(t *testing.T) { |
| // Create temporary files |
| tempDir1 := t.TempDir() |
| tempDir2 := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir1, "inotify_moved_to_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempDir2, unix.IN_MOVED_TO) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| filename := strings.TrimPrefix(tempFile.Name(), tempDir1) |
| err = os.Rename(tempDir1+filename, |
| tempDir2+filename) |
| if err != nil { |
| t.Fatalf("Rename: %v", err) |
| } |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent*4) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func inotify_open(t *testing.T) { |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.CreateTemp(tempDir, "inotify_open_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| // File closed later in test |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_OPEN) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| name := tempFile.Name() |
| tempFile.Close() |
| f, err := os.Open(name) |
| if err != nil { |
| t.Fatalf("Open: %v", err) |
| } |
| defer f.Close() |
| |
| // Expect event |
| buf := make([]byte, unix.SizeofInotifyEvent) |
| n, err := unix.Read(infd, buf[:]) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n == 0 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func TestSockNonblock(t *testing.T) { |
| ch1 := make(chan int) |
| go func() { |
| select { |
| case <-ch1: |
| } |
| |
| client, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) |
| if err != nil { |
| t.Fatalf("Socket: %v", err) |
| } |
| defer unix.Close(client) |
| |
| clientSA := unix.SockaddrInet4{Port: 33333, Addr: [4]byte{127, 0, 0, 1}} |
| err = unix.Connect(client, &clientSA) |
| if err != nil { |
| t.Fatalf("Connect: %v", err) |
| } |
| |
| select { |
| case <-ch1: |
| } |
| }() |
| |
| server, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) |
| if err != nil { |
| t.Fatalf("Socket: %v", err) |
| } |
| defer unix.Close(server) |
| |
| serverSA := unix.SockaddrInet4{Port: 33333, Addr: [4]byte{}} |
| |
| err = unix.SetsockoptInt(server, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) |
| if err != nil { |
| t.Fatalf("SetsockoptInt: %v", err) |
| } |
| |
| err = unix.Bind(server, &serverSA) |
| if err != nil { |
| t.Fatalf("Bind: %v", err) |
| } |
| |
| err = unix.Listen(server, 3) |
| if err != nil { |
| t.Fatalf("Listen: %v", err) |
| } |
| |
| ch1 <- 1 |
| |
| accept, _, err := unix.Accept4(server, unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC) |
| if err != nil { |
| t.Fatalf("Accept: %v", err) |
| } |
| |
| buf := make([]byte, 16) |
| _, err = unix.Read(accept, buf) |
| if err.Error() != "EDC8102I Operation would block." { |
| t.Fatalf("Read: Expected error \"EDC8102I Operation would block.\", but got \"%v\"", err) |
| } |
| |
| ch1 <- 1 |
| } |
| |
| func TestSockIPTTL(t *testing.T) { |
| server, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) |
| if err != nil { |
| t.Fatalf("Socket: %v", err) |
| } |
| |
| ttl, err := unix.GetsockoptInt(server, unix.IPPROTO_IP, unix.IP_TTL) |
| if err != nil { |
| t.Fatalf("GetsockoptInt: %v", err) |
| } |
| |
| if ttl != 64 { |
| t.Fatalf("Expected TTL value of 64, got %v", ttl) |
| } |
| |
| err = unix.SetsockoptInt(server, unix.IPPROTO_IP, unix.IP_TTL, 65) |
| if err != nil { |
| t.Fatalf("SetsockoptInt: %v", err) |
| } |
| |
| ttl, err = unix.GetsockoptInt(server, unix.IPPROTO_IP, unix.IP_TTL) |
| if err != nil { |
| t.Fatalf("GetsockoptInt: %v", err) |
| } |
| |
| if ttl != 65 { |
| t.Fatalf("Expected TTL value of 65, got %v", ttl) |
| } |
| } |
| |
| func TestSethostname(t *testing.T) { |
| name, err := os.Hostname() |
| if err != nil { |
| t.Fatalf("Failed to get hostname: %v", err) |
| } |
| |
| err = unix.Sethostname([]byte(name)) |
| if !strings.Contains(err.Error(), unix.ENOTSUP.Error()) { |
| t.Fatalf("Sethostname: Expected error \"EDC5247I Operation not supported.\", but got \"%v\"", err) |
| } |
| } |
| |
| func TestGetrandom(t *testing.T) { |
| buf := make([]byte, 16) |
| n, err := unix.Getrandom(buf, unix.GRND_NONBLOCK|unix.GRND_RANDOM) |
| if err != nil { |
| t.Fatalf("Getrandom: %v", err) |
| } |
| if n != 16 { |
| t.Fatalf("Expected to read %d bytes. Actually read %d", 16, n) |
| } |
| |
| sum := 0 |
| for _, v := range buf { |
| sum += int(v) |
| } |
| if sum == 0 { |
| t.Fatalf("Getrandom: no random values retrieved") |
| } |
| } |
| |
| func TestTermios(t *testing.T) { |
| const ioctlReadTermios = unix.TCGETS |
| const ioctlWriteTermios = unix.TCSETS |
| |
| // Get address of controlling terminal |
| tty, err := unix.Ctermid() |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| t.Skip("No terminal") |
| } |
| |
| // Open controlling terminal |
| f, err := unix.Open(tty, 3, 0755) |
| if err != nil { |
| t.Skipf("Skipping because opening /dev/tty failed: %v", err) |
| } |
| defer unix.Close(f) |
| |
| // Test IoctlGetTermios |
| termios, err := unix.IoctlGetTermios(f, ioctlReadTermios) |
| |
| // Save old terminal settings to restore |
| oldTermios := *termios |
| if err != nil { |
| t.Fatalf("IoctlGetTermios: %v", err) |
| } |
| |
| // This attempts to replicate the behaviour documented for cfmakeraw in |
| // the termios(3) manpage. |
| termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON |
| termios.Oflag &^= unix.OPOST |
| termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN |
| termios.Cflag &^= unix.CSIZE | unix.PARENB |
| termios.Cflag |= unix.CS8 |
| termios.Cc[unix.VMIN] = 1 |
| termios.Cc[unix.VTIME] = 0 |
| |
| // Test IoctlSetTermios |
| if err := unix.IoctlSetTermios(f, ioctlWriteTermios, termios); err != nil { |
| t.Fatalf("IoctlSetTermios: %v", err) |
| } |
| |
| // Restore |
| if err := unix.IoctlSetTermios(f, ioctlWriteTermios, &oldTermios); err != nil { |
| t.Fatalf("IoctlSetTermios: %v", err) |
| } |
| } |
| |
| func TestDup3(t *testing.T) { |
| data := []byte("Test String") |
| |
| // Create temporary files |
| tempDir := t.TempDir() |
| tempFile, err := os.Create(filepath.Join(tempDir, "dup3_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Duplicate fd |
| fd1, err := unix.Open(tempFile.Name(), unix.O_RDWR|unix.O_CLOEXEC, 777) |
| if err != nil { |
| t.Fatalf("Open: %v", err) |
| } |
| defer unix.Close(fd1) |
| |
| fd2 := 11 |
| if err := unix.Dup3(fd1, fd2, unix.O_CLOEXEC); err != nil { |
| t.Fatalf("Dup3: %v", err) |
| } |
| defer unix.Close(fd2) |
| |
| // Write |
| n, err := unix.Write(fd2, data) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| if n != len(data) { |
| t.Fatalf("Write: only wrote %d bytes, expected %d", n, len(data)) |
| } |
| |
| // Read |
| buf := make([]byte, 16) |
| n, err = tempFile.Read(buf) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n != len(data) { |
| t.Fatalf("Read: only read %d bytes, expected %d", n, len(data)) |
| } |
| |
| // Compare |
| for i := 0; i < len(data); i++ { |
| if buf[i] != data[i] { |
| t.Fatalf("Dup3: data read did not match data written") |
| } |
| } |
| } |
| |
| func TestWait4(t *testing.T) { |
| if v, r := zosLeVersion(); v <= 2 && r <= 4 { |
| t.Skipf("New wait4 can't be used in %d.%d < 2.5", v, r) |
| } |
| if os.Getenv("TEST_WAIT4_HELPER") == "1" { |
| exitCode, err := strconv.Atoi(os.Args[2]) |
| if err != nil { |
| fmt.Printf("exit code cannot be parsed: %s\n", err) |
| } |
| defer os.Exit(exitCode) |
| for i := 0; i < 50000000; i++ { |
| } |
| return |
| } |
| |
| const ( |
| childPid = -2 |
| core = 0x80 |
| exited = 0x00 |
| stopped = 0x7F |
| shift = 8 |
| ) |
| pgid, err := unix.Getpgid(os.Getpid()) |
| if err != nil { |
| t.Fatal(err) |
| } |
| testCases := []struct { |
| name string |
| exitCode int |
| pid int |
| options int |
| signals []syscall.Signal |
| wpid int |
| err error |
| ws unix.WaitStatus |
| }{ |
| {"Child's pgid", 0, -pgid, 0, []syscall.Signal{}, childPid, nil, exited}, |
| {"Any", 0, -1, 0, []syscall.Signal{}, childPid, nil, exited}, |
| {"Pid zero", 0, 0, 0, []syscall.Signal{}, childPid, nil, exited}, |
| {"Child's pid", 0, childPid, 0, []syscall.Signal{}, childPid, nil, exited}, |
| {"Exited with 2", 2, childPid, 0, []syscall.Signal{}, childPid, nil, unix.WaitStatus((2 << shift) | exited)}, |
| {"No hang", 0, childPid, unix.WNOHANG, []syscall.Signal{}, 0, nil, exited}, |
| {"No child", 0, os.Getpid(), 0, []syscall.Signal{}, -1, unix.ECHILD, exited}, |
| {"Inval", 0, -1, -1, []syscall.Signal{}, -1, unix.EINVAL, exited}, |
| {"Killed", 0, childPid, 0, []syscall.Signal{unix.SIGKILL}, childPid, nil, unix.WaitStatus(unix.SIGKILL)}, |
| {"Interrupted", 0, childPid, 0, []syscall.Signal{unix.SIGINT}, childPid, nil, unix.WaitStatus(unix.SIGINT)}, |
| {"Stopped", 0, childPid, unix.WUNTRACED, []syscall.Signal{unix.SIGSTOP}, childPid, nil, unix.WaitStatus((unix.SIGSTOP << shift) | stopped)}, |
| {"Core dump", 0, childPid, unix.WUNTRACED, []syscall.Signal{unix.SIGTRAP}, childPid, nil, unix.WaitStatus(core | unix.SIGTRAP)}, |
| // TODO(paulc): Skipping these two until wait4 behaves the same as in Linux |
| // {"Continued", 0, cpid, unix.WCONTINUED, []syscall.Signal{unix.SIGSTOP, unix.SIGCONT}, cpid, nil, 0xffff}, |
| // {"Intmin", 0, -2147483648, 0, []syscall.Signal{}, -1, unix.ESRCH, exited}, |
| } |
| |
| for _, c := range testCases { |
| t.Run(c.name, func(t *testing.T) { |
| 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) |
| } |
| |
| for i, sig := range c.signals { |
| if err := cmd.Process.Signal(sig); err != nil { |
| cmd.Process.Kill() |
| t.Fatal(err) |
| } |
| if i != len(c.signals)-1 { |
| time.Sleep(1000 * time.Millisecond) |
| } |
| } |
| |
| pid, wpid := c.pid, c.wpid |
| if c.pid == childPid { |
| pid = cmd.Process.Pid |
| } |
| if c.wpid == childPid { |
| wpid = cmd.Process.Pid |
| } |
| ws := unix.WaitStatus(0) |
| ru := unix.Rusage{} |
| ret, err := unix.Wait4(pid, &ws, c.options, &ru) |
| if err != c.err { |
| t.Fatalf("expected %s error but got %s error\n", c.err, err) |
| } |
| if ret != wpid { |
| t.Fatalf("expected return value of %d but got %d\n", wpid, ret) |
| } |
| if ws != c.ws { |
| t.Fatalf("expected wait status %x but got %x\n", c.ws, ws) |
| } |
| if err == nil && len(c.signals) == 0 && c.options&unix.WNOHANG != unix.WNOHANG { |
| if emptyRU := new(unix.Rusage); ru == *emptyRU { |
| t.Fatalf("expected non-empty rusage but got %+v", ru) |
| } |
| } |
| cmd.Process.Kill() |
| }) |
| } |
| } |
| |
| func TestNanosleep(t *testing.T) { |
| waitTime := int64(10000000) |
| |
| var ts, tsLeftover unix.Timespec |
| ts = unix.Timespec{ |
| Sec: 0, |
| Nsec: waitTime, |
| } |
| tsLeftover = unix.Timespec{ |
| Sec: 0, |
| Nsec: 0, |
| } |
| |
| t1 := time.Now().UnixNano() |
| if err := unix.Nanosleep(&ts, &tsLeftover); err != nil { |
| t.Fatalf("Nanosleep: %v", err) |
| } |
| t2 := time.Now().UnixNano() |
| |
| if t2-t1 < waitTime { |
| t.Fatalf("Nanosleep: did not wait long enough. Expected: %d, got: %d", waitTime, t2-t1) |
| } |
| } |
| |
| func TestOpenat2(t *testing.T) { |
| how := &unix.OpenHow{ |
| Flags: unix.O_RDONLY, |
| } |
| fd, err := unix.Openat2(unix.AT_FDCWD, ".", how) |
| if err != nil { |
| t.Fatalf("openat2: %v", err) |
| } |
| if err := unix.Close(fd); err != nil { |
| t.Fatalf("close: %v", err) |
| } |
| |
| // prepare |
| tempDir := t.TempDir() |
| |
| subdir := filepath.Join(tempDir, "dir") |
| if err := os.Mkdir(subdir, 0755); err != nil { |
| t.Fatal(err) |
| } |
| symlink := filepath.Join(subdir, "symlink") |
| if err := os.Symlink("../", symlink); err != nil { |
| t.Fatal(err) |
| } |
| |
| dirfd, err := unix.Open(subdir, unix.O_RDONLY, 0) |
| if err != nil { |
| t.Fatalf("open(%q): %v", subdir, err) |
| } |
| defer unix.Close(dirfd) |
| |
| // openat2 with no extra flags -- should succeed |
| fd, err = unix.Openat2(dirfd, "symlink", how) |
| if err != nil { |
| t.Errorf("Openat2 should succeed, got %v", err) |
| } |
| if err := unix.Close(fd); err != nil { |
| t.Fatalf("close: %v", err) |
| } |
| |
| // open with RESOLVE_BENEATH, should result in EXDEV |
| how.Resolve = unix.RESOLVE_BENEATH |
| fd, err = unix.Openat2(dirfd, "symlink", how) |
| if err == nil { |
| if err := unix.Close(fd); err != nil { |
| t.Fatalf("close: %v", err) |
| } |
| } |
| if err != unix.EXDEV { |
| t.Errorf("Openat2 should fail with EXDEV, got %v", err) |
| } |
| } |
| |
| func TestUtimesNanoAt(t *testing.T) { |
| // z/OS currently does not support setting milli/micro/nanoseconds for files |
| // The Nsec field will be 0 when trying to get atime/mtime |
| |
| // Create temp dir and file |
| f, err := os.Create(filepath.Join(t.TempDir(), "utimesNanoAt_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer f.Close() |
| |
| fd := int(f.Fd()) |
| |
| // Set atime and mtime |
| ts := []unix.Timespec{ |
| unix.Timespec{123456789, 123456789}, |
| unix.Timespec{123456789, 123456789}, |
| } |
| atimeTS := time.Unix(ts[0].Sec, ts[0].Nsec) |
| mtimeTS := time.Unix(ts[1].Sec, ts[1].Nsec) |
| |
| err = unix.UtimesNanoAt(fd, f.Name(), ts, 0) |
| if err != nil { |
| t.Fatalf("TestUtimesNanoAt: %v", err) |
| } |
| |
| // Compare atime and mtime |
| var statAfter unix.Stat_t |
| if err = unix.Fstat(fd, &statAfter); err != nil { |
| t.Fatalf("Fstat: %v", err) |
| } |
| atimeAfter := time.Unix(statAfter.Atim.Sec, statAfter.Atim.Nsec) |
| mtimeAfter := time.Unix(statAfter.Mtim.Sec, statAfter.Mtim.Nsec) |
| |
| // TODO (joon): check using time.Equal() once z/OS supports finer timestamps for files |
| if atimeAfter.Unix() != atimeTS.Unix() { |
| t.Fatalf("Expected atime to be %v. Got %v", atimeAfter.Unix(), atimeTS.Unix()) |
| } |
| if mtimeAfter.Unix() != mtimeTS.Unix() { |
| t.Fatalf("Expected mtime to be %v. Got %v", atimeAfter.Unix(), atimeTS.Unix()) |
| } |
| } |
| |
| func TestPivotRoot(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| err := unix.Unshare(unix.CLONE_NEWNS) |
| if err != nil { |
| t.Fatalf("Unshare: %v", err) |
| } |
| |
| // Create our 'new_root' and bind mount it to satisfy one of the conditions of pivot_root |
| newRoot := t.TempDir() |
| |
| err = unix.Mount(newRoot, newRoot, "", unix.MS_BIND|unix.MS_REC, "") |
| if err != nil { |
| t.Fatalf("Mount: %v", err) |
| } |
| |
| // Create our 'old_root' |
| oldRoot, err := os.MkdirTemp(newRoot, "oldRoot") |
| if err != nil { |
| t.Fatalf("TempDir: %v", err) |
| } |
| |
| // Perform the pivot and check that our old root is now a subfolder in our new root |
| if err = unix.PivotRoot(newRoot, oldRoot); err != nil { |
| t.Fatalf("PivotRoot: %v", err) |
| } |
| |
| err = unix.Chdir("/") |
| if err != nil { |
| t.Fatalf("Chdir: %v", err) |
| } |
| |
| if _, err := os.Stat("/" + filepath.Base(oldRoot)); os.IsNotExist(err) { |
| t.Fatalf("Expected to see old root as a subdirectory.") |
| } |
| } |
| |
| func TestMountUnmount(t *testing.T) { |
| if v, r := zosLeVersion(); v < 2 || (v == 2 && r < 4) { |
| t.Skipf("New mount can't be used in %d.%d < 2.5. Run TestLegacyMountUnmount", v, r) |
| } |
| |
| if _, err := os.Stat("/proc/self"); errors.Is(err, os.ErrNotExist) { |
| t.Skip("/proc/self is not exist skipping the test") |
| } |
| |
| // Check that TFS is installed on the system, otherwise the test cannot be performed. |
| b, err := os.ReadFile("/proc/filesystems") |
| if err != nil { |
| t.Fatalf("ReadFile: %v", err) |
| } |
| filesystems := string(b) |
| if !strings.Contains(filesystems, "TFS") { |
| t.Skip("Missing TFS filesystem") |
| } |
| |
| // Create a temp directory for the TFS |
| tempSrc := t.TempDir() |
| |
| // Mount the TFS |
| tfs := "testTFS" |
| err = exec.Command("/usr/sbin/mount", "-t", "TFS", "-f", tfs, tempSrc).Run() |
| if err != nil { |
| t.Skip("Could not create TFS") |
| } |
| |
| // Create a temp dir and test Mount() |
| tempTgt := t.TempDir() |
| |
| err = unix.Mount(tempSrc, tempTgt, "TFS", 0, "") |
| if err != nil { |
| t.Fatalf("Mount: %v", err) |
| } |
| |
| // Unmount and cleanup |
| err = unix.Unmount(tempTgt, 0) |
| if err != nil { |
| t.Fatalf("Unmount: %v", err) |
| } |
| err = exec.Command("/usr/sbin/unmount", "-f", tfs).Run() |
| if err != nil { |
| t.Fatalf("Could not remove TFS") |
| } |
| } |
| |
| func TestMountNamespace(t *testing.T) { |
| if v, r := zosLeVersion(); v <= 2 && r <= 4 { |
| t.Skipf("Namespaces not available on z/OS %v.%v", v, r) |
| } |
| |
| // Check that TFS is installed on the system, otherwise the test cannot be performed. |
| b, err := os.ReadFile("/proc/filesystems") |
| if err != nil { |
| t.Skipf("Problem with reading /proc/filesystems: %v", err) |
| } |
| filesystems := string(b) |
| if !strings.Contains(filesystems, "TFS") { |
| t.Skipf("Missing TFS filesystem") |
| } |
| |
| if os.Getenv("SETNS_HELPER_PROCESS") == "1" { |
| err := unix.Unshare(unix.CLONE_NEWNS) |
| if err != nil { |
| t.Skipf("Unshare: %v", err) |
| } |
| |
| // Create a temp directory for the TFS |
| tempSrc := t.TempDir() |
| |
| // Mount the TFS |
| err = exec.Command("/usr/sbin/mount", "-t", "TFS", "-f", "testTFS", tempSrc).Run() |
| if err != nil { |
| t.Skipf("Could not create TFS") |
| } |
| |
| data, err := os.ReadFile("/proc/mounts") |
| if err != nil { |
| t.Fatalf("ReadFile: %v", err) |
| } |
| |
| err = os.WriteFile(os.Getenv("MNT_NS_FILE"), data, 644) |
| if err != nil { |
| t.Fatalf("WriteFile: %v", err) |
| } |
| return |
| } |
| |
| // Make a file to copy the child process' mount information |
| f, err := os.CreateTemp("", "mntNsTestFile") |
| if err != nil { |
| t.Fatalf("Could not create temp file") |
| } |
| defer os.Remove(f.Name()) |
| f.Close() |
| |
| 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()) |
| |
| // Create the child process and get the path of the TFS mount to be cleaned up |
| out, err := cmd.CombinedOutput() |
| if err != nil { |
| t.Fatalf("helper process failed: %v\n%v", err, string(out)) |
| } |
| if strings.Contains(string(out), "SKIP") { |
| t.Skipf("helper process: %v", string(out)) |
| } |
| tfsDir := strings.Split(string(out), "\n")[1] |
| defer os.RemoveAll(tfsDir) |
| |
| d1, err := os.ReadFile(f.Name()) |
| if err != nil { |
| t.Fatalf("ReadFile: %v", err) |
| } |
| |
| d2, err := os.ReadFile("/proc/mounts") |
| if err != nil { |
| t.Fatalf("ReadFile: %v", err) |
| } |
| |
| // Check that the TFS created in the child process was not made in the parent |
| if !strings.Contains(string(d1), tfsDir) { |
| t.Errorf("Expected to see %v in child process' /proc/mounts", tfsDir) |
| } |
| |
| if strings.Contains(string(d2), tfsDir) { |
| t.Errorf("Expected not to see %v in parent process' /proc/mounts", tfsDir) |
| } |
| } |
| func TestMkfifoat(t *testing.T) { |
| name := fmt.Sprintf("fifo%d", os.Getpid()) |
| pname := fmt.Sprintf("/tmp/fifo%d", os.Getpid()) |
| dirStream, err := unix.Opendir("/tmp") |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirFd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirFd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirFd) |
| } |
| err = unix.Mkfifoat(dirFd, name, 0666) |
| if err != nil { |
| t.Fatalf("Mkfifoat: failed to create FIFO: %v at %d", err, dirFd) |
| } |
| st, err := os.Stat(pname) |
| if err != nil { |
| t.Fatalf("Mkfifoat: failed to stat FIFO: %s %v", pname, err) |
| } |
| if st.Mode()&os.ModeNamedPipe != os.ModeNamedPipe { |
| t.Fatalf("Mkfifoat: %s is not a FIFO", pname) |
| } |
| |
| os.Remove(pname) |
| } |
| |
| func TestMkdirat(t *testing.T) { |
| name := fmt.Sprintf("dir%d", os.Getpid()) |
| pname := fmt.Sprintf("/tmp/dir%d", os.Getpid()) |
| dirStream, err := unix.Opendir("/tmp") |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirFd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirFd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirFd) |
| } |
| err = unix.Mkdirat(dirFd, name, 0777) |
| if err != nil { |
| t.Fatalf("Mkdirat: failed to create directory: %v at %d", err, dirFd) |
| } |
| st, err := os.Stat(pname) |
| if err != nil { |
| t.Fatalf("Mkdirat: failed to stat directory: %s %v", pname, err) |
| } |
| if !st.Mode().IsDir() { |
| t.Fatalf("Mkdirat: %s is not a directory", pname) |
| } |
| os.Remove(pname) |
| } |
| |
| func TestLinkat(t *testing.T) { |
| lName := fmt.Sprintf("testLinkatLink%d", os.Getpid()) |
| |
| dirStream, err := unix.Opendir("/tmp") |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirFd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirFd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirFd) |
| } |
| |
| f, err := os.CreateTemp("/tmp", "tesLinkatFile") |
| if err != nil { |
| t.Fatalf("CreateTemp: %v", err) |
| } |
| defer os.Remove(f.Name()) |
| defer f.Close() |
| |
| err = unix.Linkat(dirFd, f.Name(), dirFd, lName, 0) |
| if err != nil { |
| t.Fatalf("Linkat: Failed to create link: %v", err) |
| } |
| defer os.Remove("/tmp/" + lName) |
| |
| fInfo, err := os.Lstat(f.Name()) |
| if err != nil { |
| t.Fatalf("Lstat: %v", err) |
| } |
| lInfo, err := os.Lstat(filepath.Join("/tmp/", lName)) |
| if err != nil { |
| t.Fatalf("Lstat: %v", err) |
| } |
| |
| if !os.SameFile(fInfo, lInfo) { |
| t.Errorf("Expected FileInfo for %s to match %s", lName, f.Name()) |
| } |
| } |
| |
| func TestSymlinkat(t *testing.T) { |
| f, err := os.Create(filepath.Join(t.TempDir(), "symlinkatTestFile")) |
| if err != nil { |
| t.Fatal("CreateTemp:", err) |
| } |
| f.Close() |
| |
| dir, err := os.Open(filepath.Dir(f.Name())) |
| if err != nil { |
| t.Fatal("Open:", err) |
| } |
| defer dir.Close() |
| |
| linkName := fmt.Sprintf("testSymlink%d", os.Getpid()) |
| err = unix.Symlinkat(f.Name(), int(dir.Fd()), linkName) |
| if err != nil { |
| t.Fatal("Symlinkat:", err) |
| } |
| |
| buf := make([]byte, 256) |
| _, err = unix.Readlinkat(int(dir.Fd()), linkName, buf) |
| if err != nil { |
| t.Fatal("Readlink:", err) |
| } |
| |
| if string(f.Name()) != string(buf[:len(f.Name())]) { |
| t.Errorf("Expected buffer contents to be: %s. Got: %s.", f.Name(), string(buf[:])) |
| } |
| } |
| |
| func TestMknodat(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| dirStream, err := unix.Opendir("/tmp") |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirFd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirFd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirFd) |
| } |
| |
| name := fmt.Sprintf("mknodatTest%d", os.Getpid()) |
| fifoName := fmt.Sprintf("mknodatTestFIFO%d", os.Getpid()) |
| scfName := fmt.Sprintf("mknodatTestSCF%d", os.Getpid()) |
| |
| err = unix.Mknodat(dirFd, name, unix.S_IFREG, 0) |
| if err != nil { |
| t.Fatalf("Mknodat - regular: %v", err) |
| } |
| defer os.Remove("/tmp/" + name) |
| |
| err = unix.Mknodat(dirFd, fifoName, unix.S_IFIFO, 0) |
| if err != nil { |
| t.Fatalf("Mknodat - directory: %v", err) |
| } |
| defer os.Remove("/tmp/" + fifoName) |
| |
| err = unix.Mknodat(dirFd, scfName, unix.S_IFCHR|unix.S_IRUSR|unix.S_IWUSR, 0x00010000|0x0001) |
| if err != nil { |
| t.Fatalf("Mknodat - character special file: %v", err) |
| } |
| defer os.Remove("/tmp/" + scfName) |
| } |
| |
| func TestFchownat(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| f, err := os.Create(filepath.Join(t.TempDir(), "fchownatTestFile")) |
| if err != nil { |
| t.Fatalf("CreateTemp: %v", err) |
| } |
| defer f.Close() |
| |
| dir, err := os.Open(filepath.Dir(f.Name())) |
| if err != nil { |
| t.Fatalf("Open: %v", err) |
| } |
| defer dir.Close() |
| |
| dirfd := int(dir.Fd()) |
| |
| err = unix.Fchownat(0, f.Name(), os.Getuid(), os.Getgid(), 0) |
| if err != nil { |
| t.Errorf("Fchownat: %v", err) |
| } |
| |
| unix.Fchownat(dirfd, filepath.Base(f.Name()), os.Getuid(), os.Getgid(), 0) |
| if err != nil { |
| t.Errorf("Fchownat: %v", err) |
| } |
| err = unix.Fchownat(dirfd, "blah", os.Getuid(), os.Getgid(), 0) |
| if err != nil { |
| if !strings.Contains(err.Error(), "EDC5129I No such file or directory.") { |
| t.Errorf("Expected: EDC5129I No such file or directory. Got: %v", err) |
| } |
| } else { |
| t.Error("Fchownat: Expected to get error \"EDC5129I No such file or directory.\"") |
| } |
| } |
| |
| func TestFaccessat(t *testing.T) { |
| f, err := os.CreateTemp("/tmp", "faccessatTestFile") |
| if err != nil { |
| t.Fatalf("CreateTemp: %v", err) |
| } |
| defer os.Remove(f.Name()) |
| defer f.Close() |
| |
| dirStream, err := unix.Opendir("/tmp") |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirfd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirfd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirfd) |
| } |
| |
| err = unix.Faccessat(dirfd, filepath.Base(f.Name()), unix.R_OK|unix.W_OK, unix.AT_EACCESS) |
| if err != nil { |
| t.Errorf("Faccessat - relative file path: %v", err) |
| } |
| err = unix.Faccessat2(dirfd, filepath.Base(f.Name()), unix.R_OK|unix.W_OK, unix.AT_EACCESS) |
| if err != nil { |
| t.Errorf("Faccessat - relative file path: %v", err) |
| } |
| |
| err = unix.Faccessat(dirfd, f.Name(), unix.R_OK|unix.W_OK, unix.AT_EACCESS) |
| if err != nil { |
| t.Errorf("Faccessat - absolute file path: %v", err) |
| } |
| |
| err = unix.Faccessat(0, filepath.Base(f.Name()), unix.R_OK, unix.AT_EACCESS) |
| if err != nil { |
| if !strings.Contains(err.Error(), "EDC5135I Not a directory.") { |
| t.Errorf("Expected: EDC5135I Not a directory. Got: %v", err) |
| } |
| } else { |
| t.Error("Faccessat: Expected to get error \"EDC5135I Not a directory.\"") |
| } |
| |
| err = unix.Faccessat(0, "/", unix.R_OK, unix.AT_EACCESS) |
| if err != nil { |
| t.Errorf("Faccessat - read root directory: %v", err) |
| } |
| |
| err = unix.Faccessat(0, "/", unix.W_OK, unix.AT_EACCESS) |
| if err != nil { |
| if !strings.Contains(err.Error(), "EDC5141I Read-only file system.") { |
| t.Errorf("Expected: EDC5141I Read-only file system. Got: %v", err) |
| } |
| } else { |
| if BypassTestOnUntil("zoscan56", "2024-04-01T12:45:21.123Z") { |
| fmt.Fprintf(os.Stderr, "Faccessat: Expected to get error \"EDC5141I Read-only file system.\"") |
| } else { |
| t.Error("Faccessat: Expected to get error \"EDC5141I Read-only file system.\"") |
| } |
| } |
| } |
| |
| func TestUnlinkat(t *testing.T) { |
| tmpdir := t.TempDir() |
| f, err := os.CreateTemp(tmpdir, "unlinkatTestFile") |
| if err != nil { |
| log.Fatal("CreateTemp:", err) |
| } |
| // file close later |
| |
| dirStream, err := unix.Opendir(tmpdir) |
| if err != nil { |
| t.Fatalf("Opendir: %v", err) |
| } |
| defer unix.Closedir(dirStream) |
| |
| dirfd, err := unix.Dirfd(dirStream) |
| if err != nil { |
| t.Fatalf("Dirfd: %v", err) |
| } |
| if dirfd < 0 { |
| t.Fatalf("Dirfd: fd < 0, (fd = %v)", dirfd) |
| } |
| |
| if err := f.Close(); err != nil { |
| t.Fatalf("Close: %v", err) |
| } |
| |
| err = unix.Unlinkat(dirfd, filepath.Base(f.Name()), 0) |
| if err != nil { |
| t.Fatalf("Unlinkat: %v", err) |
| } |
| |
| _, err = os.Open(f.Name()) |
| if err != nil { |
| if !os.IsNotExist(err) { |
| t.Errorf("Expected to get error \"EDC5129I No such file or directory\". Got: %v", err) |
| } |
| } else { |
| t.Error("Unlinkat: Expected to get error \"EDC5129I No such file or directory\"") |
| } |
| |
| err = unix.Unlinkat(dirfd, tmpdir, unix.AT_REMOVEDIR) |
| if err != nil { |
| t.Fatalf("Unlinkat: %v", err) |
| } |
| |
| _, err = os.Open(tmpdir) |
| if err != nil { |
| if !os.IsNotExist(err) { |
| t.Errorf("Expected to get error \"EDC5129I No such file or directory\". Got: %v", err) |
| } |
| } else { |
| t.Error("Unlinkat: Expected to get error \"EDC5129I No such file or directory\"") |
| } |
| } |
| |
| func TestRenameat(t *testing.T) { |
| chtmpdir(t) |
| |
| from, to := "renamefrom", "renameto" |
| |
| touch(t, from) |
| |
| err := unix.Renameat(unix.AT_FDCWD, from, unix.AT_FDCWD, to) |
| if err != nil { |
| t.Fatalf("Renameat: unexpected error: %v", err) |
| } |
| |
| _, err = os.Stat(to) |
| if err != nil { |
| t.Error(err) |
| } |
| |
| _, err = os.Stat(from) |
| if err == nil { |
| t.Errorf("Renameat: stat of renamed file %q unexpectedly succeeded", from) |
| } |
| } |
| |
| func TestRenameat2(t *testing.T) { |
| chtmpdir(t) |
| |
| from, to := "renamefrom", "renameto" |
| |
| touch(t, from) |
| |
| err := unix.Renameat2(unix.AT_FDCWD, from, unix.AT_FDCWD, to, 0) |
| if err != nil { |
| t.Fatalf("Renameat2: unexpected error: %v", err) |
| } |
| |
| _, err = os.Stat(to) |
| if err != nil { |
| t.Error(err) |
| } |
| |
| _, err = os.Stat(from) |
| if err == nil { |
| t.Errorf("Renameat2: stat of renamed file %q unexpectedly succeeded", from) |
| } |
| |
| touch(t, from) |
| |
| err = unix.Renameat2(unix.AT_FDCWD, from, unix.AT_FDCWD, to, unix.RENAME_NOREPLACE) |
| if err != nil { |
| if err.Error() != "EDC5117I File exists." { |
| t.Errorf("Renameat2: expected to get error \"EDC5117I File exists.\" Got: %v", err) |
| } |
| } else { |
| t.Errorf("Renameat2: Unexpected error: %v", err) |
| } |
| } |
| |
| func TestFchmodat(t *testing.T) { |
| chtmpdir(t) |
| |
| touch(t, "file1") |
| err := os.Symlink("file1", "symlink1") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| mode := os.FileMode(0444) |
| err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0) |
| if err != nil { |
| t.Fatalf("Fchmodat: unexpected error: %v", err) |
| } |
| |
| fi, err := os.Stat("file1") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if fi.Mode() != mode { |
| t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode()) |
| } |
| |
| mode = os.FileMode(0644) |
| didChmodSymlink := true |
| err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW) |
| if err != nil { |
| if err == unix.EOPNOTSUPP { |
| didChmodSymlink = false |
| } else { |
| t.Fatalf("Fchmodat: unexpected error: %v", err) |
| } |
| } |
| |
| if !didChmodSymlink { |
| // Didn't change mode of the symlink. On Linux, the permissions |
| // of a symbolic link are always 0777 according to symlink(7) |
| mode = os.FileMode(0777) |
| } |
| |
| var st unix.Stat_t |
| err = unix.Lstat("symlink1", &st) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| got := os.FileMode(st.Mode & 0777) |
| if got != mode { |
| t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got) |
| } |
| } |
| func TestPosix_openpt(t *testing.T) { |
| masterfd, err := unix.Posix_openpt(unix.O_RDWR) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer unix.Close(masterfd) |
| _, err = unix.Grantpt(masterfd) |
| if err != nil { |
| t.Fatal(err) |
| } |
| _, err = unix.Unlockpt(masterfd) |
| if err != nil { |
| t.Fatal(err) |
| } |
| slavename, err := unix.Ptsname(masterfd) |
| if err != nil { |
| t.Fatal(err) |
| } |
| fd, err := unix.Open(slavename, unix.O_RDWR, 0) |
| if err != nil { |
| t.Fatal(err) |
| } |
| unix.Close(fd) |
| } |
| |
| func compareStat_t(t *testing.T, otherStat string, st1, st2 *unix.Stat_t) { |
| if st2.Dev != st1.Dev { |
| t.Errorf("%s/Fstatat: got dev %v, expected %v", otherStat, st2.Dev, st1.Dev) |
| } |
| if st2.Ino != st1.Ino { |
| t.Errorf("%s/Fstatat: got ino %v, expected %v", otherStat, st2.Ino, st1.Ino) |
| } |
| if st2.Mode != st1.Mode { |
| t.Errorf("%s/Fstatat: got mode %v, expected %v", otherStat, st2.Mode, st1.Mode) |
| } |
| if st2.Uid != st1.Uid { |
| t.Errorf("%s/Fstatat: got uid %v, expected %v", otherStat, st2.Uid, st1.Uid) |
| } |
| if st2.Gid != st1.Gid { |
| t.Errorf("%s/Fstatat: got gid %v, expected %v", otherStat, st2.Gid, st1.Gid) |
| } |
| if st2.Size != st1.Size { |
| t.Errorf("%s/Fstatat: got size %v, expected %v", otherStat, st2.Size, st1.Size) |
| } |
| } |
| |
| func TestFstatat(t *testing.T) { |
| chtmpdir(t) |
| |
| touch(t, "file1") |
| |
| var st1 unix.Stat_t |
| err := unix.Stat("file1", &st1) |
| if err != nil { |
| t.Fatalf("Stat: %v", err) |
| } |
| |
| var st2 unix.Stat_t |
| err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0) |
| if err != nil { |
| t.Fatalf("Fstatat: %v", err) |
| } |
| |
| compareStat_t(t, "Stat", &st1, &st2) |
| |
| err = os.Symlink("file1", "symlink1") |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| err = unix.Lstat("symlink1", &st1) |
| if err != nil { |
| t.Fatalf("Lstat: %v", err) |
| } |
| |
| err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW) |
| if err != nil { |
| t.Fatalf("Fstatat: %v", err) |
| } |
| |
| compareStat_t(t, "Lstat", &st1, &st2) |
| } |
| |
| func TestFreezeUnfreeze(t *testing.T) { |
| rv, rc, rn := unix.Bpx4ptq(unix.QUIESCE_FREEZE, "FREEZE") |
| if rc != 0 { |
| t.Fatalf(fmt.Sprintf("Bpx4ptq FREEZE %v %v %v\n", rv, rc, rn)) |
| } |
| rv, rc, rn = unix.Bpx4ptq(unix.QUIESCE_UNFREEZE, "UNFREEZE") |
| if rc != 0 { |
| t.Fatalf(fmt.Sprintf("Bpx4ptq UNFREEZE %v %v %v\n", rv, rc, rn)) |
| } |
| } |
| func TestPtrace(t *testing.T) { |
| cmd := exec.Command("/bin/sleep", "1000") |
| cmd.Stdout = os.Stdout |
| err := cmd.Start() |
| if err != nil { |
| log.Fatal(err) |
| } |
| rv, rc, rn := unix.Bpx4ptr(unix.PT_ATTACH, int32(cmd.Process.Pid), unsafe.Pointer(uintptr(0)), unsafe.Pointer(uintptr(0)), unsafe.Pointer(uintptr(0))) |
| if rc != 0 { |
| t.Fatalf("ptrace: Bpx4ptr rv %d, rc %d, rn %d\n", rv, rc, rn) |
| } |
| cmd.Process.Kill() |
| } |
| |
| func TestFutimesat(t *testing.T) { |
| // Create temp dir and file |
| tempDir := t.TempDir() |
| |
| dir, err := os.Open(tempDir) |
| if err != nil { |
| t.Fatal("Can not open tempDir: ", tempDir) |
| } |
| defer dir.Close() |
| |
| tempFile, err := os.CreateTemp(tempDir, "futimesat_test_file") |
| if err != nil { |
| t.Fatalf("TempFile: %s", err.Error()) |
| } |
| defer tempFile.Close() |
| |
| // Set mod time to newTime |
| newTime := time.Date(2001, time.Month(2), 15, 7, 7, 7, 0, time.UTC) |
| err = unix.Futimesat( |
| int(dir.Fd()), |
| filepath.Base(tempFile.Name()), |
| []unix.Timeval{ |
| unix.Timeval{Sec: newTime.Unix(), Usec: 0}, |
| unix.Timeval{Sec: newTime.Unix(), Usec: 0}, |
| }) |
| if err != nil { |
| t.Fatalf("TestFutimes: %v", err) |
| } |
| |
| // Compare mod time |
| stats, err := tempFile.Stat() |
| if err != nil { |
| t.Fatalf("Stat: %v", err) |
| } |
| |
| modTime := stats.ModTime() |
| if modTime.UTC() != newTime { |
| t.Fatalf("TestFutimes: modTime = %v, want %v", modTime.UTC(), newTime) |
| } |
| } |
| |
| func TestInotifyAccess(t *testing.T) { |
| // Create temporary files |
| tempFile, err := os.Create(filepath.Join(t.TempDir(), "inotify_access_test_file")) |
| if err != nil { |
| t.Fatalf("TempFile: %v", err) |
| } |
| defer tempFile.Close() |
| |
| // Setup iNotify |
| infd, err := unix.InotifyInit() |
| if err != nil { |
| t.Fatalf("InotifyInit1: %v", err) |
| } |
| |
| wd, err := unix.InotifyAddWatch(infd, tempFile.Name(), unix.IN_ACCESS) |
| if err != nil { |
| t.Fatalf("InotifyAddWatch: %v", err) |
| } |
| |
| // Trigger Event |
| n, err := tempFile.Write([]byte("Writing before reading")) |
| if err != nil { |
| t.Fatalf("Write: %v", err) |
| } |
| if n <= 0 { |
| t.Fatalf("Did not write any data") |
| } |
| tempFile.Seek(0, 0) |
| |
| buf := make([]byte, 64) |
| n, err = tempFile.Read(buf) |
| if err != nil { |
| t.Fatalf("Read: %v", err) |
| } |
| if n <= 0 { |
| t.Fatalf("Did not read any data") |
| } |
| |
| // Expect event |
| buf = make([]byte, unix.SizeofInotifyEvent) |
| n, err = unix.Read(infd, buf[:]) |
| if n == -1 { |
| t.Fatalf("No event was read from the iNotify fd") |
| } |
| |
| // Remove Watch |
| if _, err = unix.InotifyRmWatch(infd, uint32(wd)); err != nil { |
| t.Fatalf("InotifyRmWatch: %v", err) |
| } |
| } |
| |
| func TestAccess(t *testing.T) { |
| tempFile, err := os.Create(filepath.Join(t.TempDir(), "test_access")) |
| if err != nil { |
| t.Fatal("fail to create temp file ", tempFile) |
| } |
| defer tempFile.Close() |
| err = unix.Access(tempFile.Name(), unix.R_OK|unix.W_OK) |
| if err != nil { |
| t.Fatalf("error when access %s: %v", tempFile.Name(), err) |
| } |
| err = unix.Access("not_exist_file", unix.F_OK) |
| if err == nil { |
| t.Fatalf("error when access not exist file: %v", err) |
| } |
| } |
| |
| func TestCreat(t *testing.T) { |
| tempFile, err := os.Create(filepath.Join(t.TempDir(), "test_create")) |
| if err != nil { |
| t.Fatal("fail to create temp file ", tempFile) |
| } |
| defer tempFile.Close() |
| |
| tempFile.Write([]byte("random1")) |
| if err != nil { |
| t.Fatal("error write to file: ", err) |
| } |
| // creat |
| fd, err := unix.Creat(tempFile.Name(), 0o777) |
| if err != nil { |
| t.Fatal("Creat error: ", err) |
| } |
| writeContent := []byte("random2") |
| n, err := unix.Write(fd, writeContent) |
| if err != nil { |
| t.Fatal("Write error: ", err) |
| } else if n <= 0 { |
| t.Fatal("Write error: 0 is written") |
| } |
| // Using creat is the equivalent of using the open callable service |
| // with the create, truncate, and write-only options: |
| // so we can not use the same file descriptor |
| b, err := os.ReadFile(tempFile.Name()) |
| if err != nil { |
| t.Fatal("Read error: ", err) |
| } |
| if n <= 0 { |
| t.Fatal("Creat error: Cannot truncate file") |
| } |
| if string(b) != string(writeContent) { |
| t.Fatal("data mismatch: expect ", string(writeContent), " actual: ", string(b)) |
| } |
| |
| // testing file create function |
| newFile := tempFile.Name() + "2" |
| fd2, err := unix.Creat(newFile, 0o777) |
| if err != nil { |
| t.Fatal("Creat error: ", err) |
| } |
| writeContent = []byte("random3") |
| n, err = unix.Write(fd2, writeContent) |
| if err != nil { |
| t.Fatal("Write error: ", err) |
| } else if n <= 0 { |
| t.Fatal("Write error: 0 is written") |
| } |
| |
| b, err = os.ReadFile(newFile) |
| if err != nil { |
| t.Fatal("Read error: ", err) |
| } |
| if n <= 0 { |
| t.Fatal("Creat error: Cannot truncate file") |
| } |
| if string(b) != string(writeContent) { |
| t.Fatal("data mismatch: expect ", string(writeContent), " actual: ", string(b)) |
| } |
| |
| } |
| |
| func TestGetPageSize(t *testing.T) { |
| size := unix.Getpagesize() |
| if size <= 0 { |
| t.Fatal("get page size return: ", size) |
| } |
| } |
| |
| func TestSyscallSetegid(t *testing.T) { |
| err := unix.Setegid(unix.Getgid()) |
| if err != nil { |
| t.Fatal("error setting euid: ", err) |
| } |
| id := unix.Getegid() |
| if id != unix.Getgid() { |
| t.Fatal("euid mismatch: expect ", unix.Getgid(), ", actual ", id) |
| } |
| } |
| |
| func TestSyscallSeteuid(t *testing.T) { |
| err := unix.Seteuid(unix.Getuid()) |
| if err != nil { |
| t.Fatal("error setting euid: ", err) |
| } |
| id := unix.Geteuid() |
| if id != unix.Getuid() { |
| t.Fatal("euid mismatch: expect ", unix.Getuid(), ", actual ", id) |
| } |
| } |
| |
| func TestSyscallSetgid(t *testing.T) { |
| err := unix.Setgid(unix.Getegid()) |
| if err != nil { |
| t.Fatal("error setting gid: ", err) |
| } |
| id := unix.Getgid() |
| if id != unix.Getegid() { |
| t.Fatal("guid mismatch: expect 0, actual ", id) |
| } |
| } |
| |
| func TestSyscallSetpgid(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| pid := unix.Getpid() |
| pgid, _ := unix.Getpgid(pid) |
| err := unix.Setpgid(pid, pgid) |
| if err != nil { |
| t.Fatal("error setting pgid: ", err) |
| } |
| id, err := unix.Getpgid(pid) |
| if err != nil { |
| t.Fatal("Getpgid error: ", err) |
| } |
| if gid, _ := unix.Getpgid(pid); gid != id { |
| t.Fatal("pgid mismatch: expect ", gid, ", actual ", id) |
| } |
| } |
| |
| func TestSyscallSetregid(t *testing.T) { |
| gid := unix.Getgid() |
| err := unix.Setregid(gid, gid) |
| if err != nil { |
| t.Fatal("error setting regid: ", err) |
| } |
| // currently missing Getresgid can not validate |
| // The get function also not provided in syscall package as well as other platform |
| } |
| |
| func TestSyscallSetreuid(t *testing.T) { |
| uid := unix.Getuid() |
| err := unix.Setreuid(uid, uid) |
| if err != nil { |
| t.Fatal("error setting reuid: ", err) |
| } |
| // currently missing Getresgid can not validate |
| // The get function also not provided in syscall package as well as other platform |
| |
| } |
| |
| func TestWriteAndSync(t *testing.T) { |
| // this test cannot really test sync function |
| // since unix.write does not require a sync function to actual write to the file |
| |
| tempFile, err := os.Create(filepath.Join(t.TempDir(), "test_write_and_sync")) |
| if err != nil { |
| t.Fatal("error: ", err) |
| } |
| defer tempFile.Close() |
| fileContent := "hello world" |
| n, err := unix.Write(int(tempFile.Fd()), []byte(fileContent)) |
| if err != nil { |
| t.Fatal("write error: ", err) |
| } |
| if n != len(fileContent) { |
| t.Fatal("error: write length mismatch") |
| } |
| unix.Sync() |
| |
| b := make([]byte, len(fileContent), 256) |
| unix.Seek(int(tempFile.Fd()), 0, 0) |
| _, err = unix.Read(int(tempFile.Fd()), b) |
| if err != nil { |
| t.Fatal("read error: ", err) |
| } |
| if string(b) != fileContent { |
| t.Fatal("file data mismatch: expect ", fileContent, " actual", string(b)) |
| } |
| } |
| |
| func TestTimes(t *testing.T) { |
| var startTimes, endTimes unix.Tms |
| |
| // Get the start time |
| _, err := unix.Times(&startTimes) |
| if err != nil { |
| t.Fatal("times error: ", err) |
| } |
| |
| sum := 0 |
| // Perform some operations |
| for i := 0; i < 1000000000; i++ { |
| sum += i % 100 |
| } |
| |
| // Get the end time |
| _, err = unix.Times(&endTimes) |
| if err != nil { |
| t.Fatal("times error: ", err) |
| } |
| |
| if int64(endTimes.Utime)-int64(startTimes.Utime) <= 0 || int64(endTimes.Stime)-int64(startTimes.Stime) <= 0 { |
| t.Fatal("times error: endtime - starttime <= 0") |
| } |
| } |
| |
| func TestMlock(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| twoM := 2 * 1024 * 1024 |
| b := make([]byte, twoM, twoM) |
| for i := 0; i < twoM; i++ { |
| b[i] = byte(i % 127) |
| } |
| err := unix.Mlock(b) |
| if err != nil { |
| t.Fatal("mlock error: ", err) |
| } |
| for i := 0; i < twoM; i++ { |
| if b[i] != byte(i%127) { |
| t.Fatal("error: memory not correct: expect ", i%127, " actual ", b[i]) |
| } |
| } |
| |
| err = unix.Munlock(b) |
| if err != nil { |
| t.Fatal("munlock error: ", err) |
| } |
| for i := 0; i < twoM; i++ { |
| if b[i] != byte(i%127) { |
| t.Fatal("error: memory not correct: expect ", i%127, " actual ", b[i]) |
| } |
| } |
| |
| } |
| |
| func TestMlockAll(t *testing.T) { |
| if euid != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| twoM := 2 * 1024 * 1024 |
| b := make([]byte, twoM, twoM) |
| for i := 0; i < twoM; i++ { |
| b[i] = byte(i % 127) |
| } |
| // Mlockall flag do not have zos semantics, so passing 0 |
| err := unix.Mlockall(0) |
| if err != nil { |
| t.Fatal("mlock error: ", err) |
| } |
| for i := 0; i < twoM; i++ { |
| if b[i] != byte(i%127) { |
| t.Fatal("error: memory not correct: expect ", i%127, " actual ", b[i]) |
| } |
| } |
| |
| err = unix.Munlockall() |
| if err != nil { |
| t.Fatal("munlock error: ", err) |
| } |
| for i := 0; i < twoM; i++ { |
| if b[i] != byte(i%127) { |
| t.Fatal("error: memory not correct: expect ", i%127, " actual ", b[i]) |
| } |
| } |
| } |
| |
| func TestGettid(t *testing.T) { |
| tid := unix.Gettid() |
| if tid < 0 { |
| t.Fatal("error: tid less than 0: tid = ", tid) |
| } |
| } |
| |
| func TestSetns(t *testing.T) { |
| // TODO (joon): for some reason changing ipc on zos fails |
| namespaces := map[string]int{ |
| // "ipc": unix.CLONE_NEWIPC, |
| "uts": unix.CLONE_NEWUTS, |
| "net": unix.CLONE_NEWNET, |
| // "pid": unix.CLONE_NEWPID, |
| } |
| |
| if unix.Geteuid() != 0 { |
| t.Skip("euid != 0") |
| } |
| |
| if os.Getenv("SETNS_HELPER_PROCESS") == "1" { |
| pid := unix.Getppid() |
| |
| fmt.Scanln() |
| |
| for k, v := range namespaces { |
| path := fmt.Sprintf("/proc/%d/ns/%s", pid, k) |
| fd, err := unix.Open(path, unix.O_RDONLY, 0) |
| err = unix.Setns(fd, v) |
| if err != nil { |
| t.Fatalf("Setns failed: %v", err) |
| } |
| } |
| for { |
| } |
| } |
| |
| 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 { |
| t.Fatalf("Failed to get stdin for helper process: %v\n", err) |
| } |
| |
| if err := cmd.Start(); err != nil { |
| t.Fatalf("failed to create helper process: %v\n", err) |
| } |
| defer cmd.Process.Kill() |
| |
| ppid := unix.Getpid() |
| pid := cmd.Process.Pid |
| |
| for k, _ := range namespaces { |
| hPath := fmt.Sprintf("/proc/%d/ns/%s", pid, k) |
| pPath := fmt.Sprintf("/proc/%d/ns/%s", ppid, k) |
| |
| hFI, _ := os.Stat(hPath) |
| pFI, _ := os.Stat(pPath) |
| |
| if !os.SameFile(hFI, pFI) { |
| t.Fatalf("namespace links for %s did not match before calling Unshare in parent\n", k) |
| } |
| } |
| |
| unix.Unshare(unix.CLONE_NEWUTS | unix.CLONE_NEWNET) |
| |
| for k, _ := range namespaces { |
| hPath := fmt.Sprintf("/proc/%d/ns/%s", pid, k) |
| pPath := fmt.Sprintf("/proc/%d/ns/%s", ppid, k) |
| |
| hFI, _ := os.Stat(hPath) |
| pFI, _ := os.Stat(pPath) |
| |
| if os.SameFile(hFI, pFI) { |
| t.Fatalf("Setns: namespace link for %s matched after calling Unshare but before Setns\n", k) |
| } |
| } |
| |
| stdin.Write([]byte("\n")) |
| stdin.Close() |
| time.Sleep(1000 * time.Millisecond) |
| |
| for k, _ := range namespaces { |
| hPath := fmt.Sprintf("/proc/%d/ns/%s", pid, k) |
| pPath := fmt.Sprintf("/proc/%d/ns/%s", ppid, k) |
| |
| hFI, _ := os.Stat(hPath) |
| pFI, _ := os.Stat(pPath) |
| |
| if !os.SameFile(hFI, pFI) { |
| t.Errorf("Setns: namespace link for %s did not match after calling Setns\n", k) |
| } |
| } |
| |
| } |
| |
| func stringsFromByteSlice(buf []byte) []string { |
| var result []string |
| off := 0 |
| for i, b := range buf { |
| if b == 0 { |
| result = append(result, string(buf[off:i])) |
| off = i + 1 |
| } |
| } |
| return result |
| } |
| |
| // This test is based on mmap_unix_test, but tweaked for z/OS, which does not support memadvise |
| // or anonymous mmapping. |
| func TestConsole2(t *testing.T) { |
| var cmsg unix.ConsMsg2 |
| var nullptr *byte |
| var cmsg_cmd uint32 |
| |
| cmsg_rout := [2]uint32{1, 0} |
| cmsg_desc := [2]uint32{12, 0} |
| cmsg.Cm2Format = unix.CONSOLE_FORMAT_2 |
| msg := "__console2 test" |
| cmsg.Cm2Msg = &unix.ZosStringToEbcdicBytes(msg, true)[0] |
| cmsg.Cm2Msglength = uint32(len(msg)) |
| cmsg.Cm2Routcde = &cmsg_rout[0] |
| cmsg.Cm2Descr = &cmsg_desc[0] |
| cmsg.Cm2Token = 0 |
| |
| err := unix.Console2(&cmsg, nullptr, &cmsg_cmd) |
| if err != nil { |
| t.Fatalf("__console2: %v", err) |
| } |
| } |
| |
| func TestConsole2modify(t *testing.T) { |
| if os.Getenv("ZOS_MANUAL_TEST") != "1" { |
| t.Skip("This test is not run unless env-var ZOS_MANUAL_TEST=1 is set") |
| } |
| |
| job, err := unix.ZosJobname() |
| if err != nil { |
| t.Fatalf("Failed to get jobname %v", err) |
| } |
| |
| var cmsg unix.ConsMsg2 |
| var cmsg_cmd uint32 |
| cmsg_rout := [2]uint32{1, 0} |
| cmsg_desc := [2]uint32{12, 0} |
| cmsg.Cm2Format = unix.CONSOLE_FORMAT_2 |
| msg := "Issue console command 'F " + job + ",APPL=123' to continue" |
| cmsg.Cm2Msg = &unix.ZosStringToEbcdicBytes(msg, true)[0] |
| cmsg.Cm2Msglength = uint32(len(msg)) |
| cmsg.Cm2Routcde = &cmsg_rout[0] |
| cmsg.Cm2Descr = &cmsg_desc[0] |
| cmsg.Cm2Token = 0 |
| |
| var modstr [128]byte |
| t.Logf("Issue console command 'F %s,APPL=123' to continue\n", job) |
| |
| err = unix.Console2(&cmsg, &modstr[0], &cmsg_cmd) |
| if err != nil { |
| t.Fatalf("__console2: %v", err) |
| } |
| |
| recv := unix.ZosEbcdicBytesToString(modstr[:], true) |
| if recv != "123" || cmsg_cmd != 1 { |
| t.Fatalf("__console2 modify: Got %s %x, Expect 123 1\n", unix.ZosEbcdicBytesToString(modstr[:], true), cmsg_cmd) |
| } |
| |
| 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]) |
| |
| } |
| |
| } |
| |
| 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 |
| } |