Files
sys/unix/syscall_darwin_test.go
Tobias Klauser a2f17f7b99 unix: allow passing optional args to SysctlKinfoProcSlice
This allows using SysctlKinfoProcSlice to e.g. query processes by user
id using the kern.proc.uid sysctl and is still backwards compatible to
original implementation, i.e. still allows the kern.proc.all sysctl
without any additional arguments.

Change-Id: Ia2d76ce5b91a077221891e1f2dfd79a38d2be52b
Reviewed-on: https://go-review.googlesource.com/c/sys/+/359677
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2021-11-02 06:14:01 +00:00

295 lines
6.5 KiB
Go

// Copyright 2018 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.
package unix_test
import (
"bytes"
"io/ioutil"
"net"
"os"
"path"
"testing"
"golang.org/x/sys/unix"
)
var testData = []byte("This is a test\n")
// stringsFromByteSlice converts a sequence of attributes to a []string.
// On Darwin, each entry is a NULL-terminated string.
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
}
func createTestFile(t *testing.T, dir string) (f *os.File, cleanup func() error) {
file, err := ioutil.TempFile(dir, t.Name())
if err != nil {
t.Fatal(err)
}
_, err = file.Write(testData)
if err != nil {
t.Fatal(err)
}
err = file.Close()
if err != nil {
t.Fatal(err)
}
return file, func() error {
return os.Remove(file.Name())
}
}
func TestClonefile(t *testing.T) {
file, cleanup := createTestFile(t, "")
defer cleanup()
clonedName := file.Name() + "-cloned"
err := unix.Clonefile(file.Name(), clonedName, 0)
if err == unix.ENOSYS || err == unix.ENOTSUP {
t.Skip("clonefile is not available or supported, skipping test")
} else if err != nil {
t.Fatal(err)
}
defer os.Remove(clonedName)
clonedData, err := ioutil.ReadFile(clonedName)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(testData, clonedData) {
t.Errorf("Clonefile: got %q, expected %q", clonedData, testData)
}
}
func TestClonefileatWithCwd(t *testing.T) {
file, cleanup := createTestFile(t, "")
defer cleanup()
clonedName := file.Name() + "-cloned"
err := unix.Clonefileat(unix.AT_FDCWD, file.Name(), unix.AT_FDCWD, clonedName, 0)
if err == unix.ENOSYS || err == unix.ENOTSUP {
t.Skip("clonefileat is not available or supported, skipping test")
} else if err != nil {
t.Fatal(err)
}
defer os.Remove(clonedName)
clonedData, err := ioutil.ReadFile(clonedName)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(testData, clonedData) {
t.Errorf("Clonefileat: got %q, expected %q", clonedData, testData)
}
}
func TestClonefileatWithRelativePaths(t *testing.T) {
srcDir, err := ioutil.TempDir("", "src")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(srcDir)
dstDir, err := ioutil.TempDir("", "dest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dstDir)
srcFd, err := unix.Open(srcDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(srcFd)
dstFd, err := unix.Open(dstDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(dstFd)
srcFile, cleanup := createTestFile(t, srcDir)
defer cleanup()
dstFile, err := ioutil.TempFile(dstDir, "TestClonefileat")
if err != nil {
t.Fatal(err)
}
err = os.Remove(dstFile.Name())
if err != nil {
t.Fatal(err)
}
src := path.Base(srcFile.Name())
dst := path.Base(dstFile.Name())
err = unix.Clonefileat(srcFd, src, dstFd, dst, 0)
if err == unix.ENOSYS || err == unix.ENOTSUP {
t.Skip("clonefileat is not available or supported, skipping test")
} else if err != nil {
t.Fatal(err)
}
clonedData, err := ioutil.ReadFile(dstFile.Name())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(testData, clonedData) {
t.Errorf("Clonefileat: got %q, expected %q", clonedData, testData)
}
}
func TestFclonefileat(t *testing.T) {
file, cleanup := createTestFile(t, "")
defer cleanup()
fd, err := unix.Open(file.Name(), unix.O_RDONLY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(fd)
dstFile, err := ioutil.TempFile("", "TestFclonefileat")
if err != nil {
t.Fatal(err)
}
os.Remove(dstFile.Name())
err = unix.Fclonefileat(fd, unix.AT_FDCWD, dstFile.Name(), 0)
if err == unix.ENOSYS || err == unix.ENOTSUP {
t.Skip("clonefileat is not available or supported, skipping test")
} else if err != nil {
t.Fatal(err)
}
clonedData, err := ioutil.ReadFile(dstFile.Name())
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(testData, clonedData) {
t.Errorf("Fclonefileat: got %q, expected %q", clonedData, testData)
}
}
func TestFcntlFstore(t *testing.T) {
f, err := ioutil.TempFile("", t.Name())
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
defer f.Close()
fstore := &unix.Fstore_t{
Flags: unix.F_ALLOCATEALL,
Posmode: unix.F_PEOFPOSMODE,
Offset: 0,
Length: 1 << 10,
}
err = unix.FcntlFstore(f.Fd(), unix.F_PREALLOCATE, fstore)
if err == unix.EOPNOTSUPP {
t.Skipf("fcntl with F_PREALLOCATE not supported, skipping test")
} else if err != nil {
t.Fatalf("FcntlFstore: %v", err)
}
st, err := f.Stat()
if err != nil {
t.Fatal(err)
}
if st.Size() != 0 {
t.Errorf("FcntlFstore: got size = %d, want %d", st.Size(), 0)
}
}
func TestGetsockoptXucred(t *testing.T) {
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
if err != nil {
t.Fatalf("Socketpair: %v", err)
}
srvFile := os.NewFile(uintptr(fds[0]), "server")
cliFile := os.NewFile(uintptr(fds[1]), "client")
defer srvFile.Close()
defer cliFile.Close()
srv, err := net.FileConn(srvFile)
if err != nil {
t.Fatalf("FileConn: %v", err)
}
defer srv.Close()
cli, err := net.FileConn(cliFile)
if err != nil {
t.Fatalf("FileConn: %v", err)
}
defer cli.Close()
cred, err := unix.GetsockoptXucred(fds[1], unix.SOL_LOCAL, unix.LOCAL_PEERCRED)
if err != nil {
t.Fatal(err)
}
t.Logf("got: %+v", cred)
if got, want := cred.Uid, os.Getuid(); int(got) != int(want) {
t.Errorf("uid = %v; want %v", got, want)
}
if cred.Ngroups > 0 {
if got, want := cred.Groups[0], os.Getgid(); int(got) != int(want) {
t.Errorf("gid = %v; want %v", got, want)
}
}
}
func TestSysctlKinfoProc(t *testing.T) {
pid := unix.Getpid()
kp, err := unix.SysctlKinfoProc("kern.proc.pid", pid)
if err != nil {
t.Fatalf("SysctlKinfoProc: %v", err)
}
if got, want := int(kp.Proc.P_pid), pid; got != want {
t.Errorf("got pid %d, want %d", got, want)
}
}
func TestSysctlKinfoProcSlice(t *testing.T) {
kps, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
t.Fatalf("SysctlKinfoProc: %v", err)
}
if len(kps) == 0 {
t.Errorf("SysctlKinfoProcSlice: expected at least one process")
}
uid := unix.Getuid()
kps, err = unix.SysctlKinfoProcSlice("kern.proc.uid", uid)
if err != nil {
t.Fatalf("SysctlKinfoProc: %v", err)
}
if len(kps) == 0 {
t.Errorf("SysctlKinfoProcSlice: expected at least one process")
}
for _, kp := range kps {
if got, want := int(kp.Eproc.Ucred.Uid), uid; got != want {
t.Errorf("process %d: got uid %d, want %d", kp.Proc.P_pid, got, want)
}
}
}