blob: f17116a67ac368bdbe26af44fa379ca995ffac6c [file] [log] [blame]
// 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"
"net"
"os"
"path/filepath"
"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) string {
filename := filepath.Join(t.TempDir(), t.Name())
err := os.WriteFile(filename, testData, 0600)
if err != nil {
t.Fatal(err)
}
return filename
}
func TestClonefile(t *testing.T) {
fileName := createTestFile(t)
clonedName := fileName + "-cloned"
err := unix.Clonefile(fileName, 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)
}
clonedData, err := os.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) {
fileName := createTestFile(t)
clonedName := fileName + "-cloned"
err := unix.Clonefileat(unix.AT_FDCWD, fileName, 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)
}
clonedData, err := os.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) {
srcFileName := createTestFile(t)
srcDir := filepath.Dir(srcFileName)
srcFd, err := unix.Open(srcDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(srcFd)
dstDir := t.TempDir()
dstFd, err := unix.Open(dstDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(dstFd)
dstFile, err := os.Create(filepath.Join(dstDir, "TestClonefileat"))
if err != nil {
t.Fatal(err)
}
err = os.Remove(dstFile.Name())
if err != nil {
t.Fatal(err)
}
src := filepath.Base(srcFileName)
dst := filepath.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 := os.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) {
fileName := createTestFile(t)
dir := filepath.Dir(fileName)
fd, err := unix.Open(fileName, unix.O_RDONLY, 0)
if err != nil {
t.Fatal(err)
}
defer unix.Close(fd)
dstFile, err := os.Create(filepath.Join(dir, "dst"))
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 := os.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 := os.CreateTemp(t.TempDir(), t.Name())
if err != nil {
t.Fatal(err)
}
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)
}
}
}