github.com/tristanisham/sys@v0.0.0-20240326010300-a16cbabb7555/unix/syscall_darwin_test.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package unix_test
     6  
     7  import (
     8  	"bytes"
     9  	"net"
    10  	"os"
    11  	"path/filepath"
    12  	"testing"
    13  
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  var testData = []byte("This is a test\n")
    18  
    19  // stringsFromByteSlice converts a sequence of attributes to a []string.
    20  // On Darwin, each entry is a NULL-terminated string.
    21  func stringsFromByteSlice(buf []byte) []string {
    22  	var result []string
    23  	off := 0
    24  	for i, b := range buf {
    25  		if b == 0 {
    26  			result = append(result, string(buf[off:i]))
    27  			off = i + 1
    28  		}
    29  	}
    30  	return result
    31  }
    32  
    33  func createTestFile(t *testing.T) string {
    34  	filename := filepath.Join(t.TempDir(), t.Name())
    35  	err := os.WriteFile(filename, testData, 0600)
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  	return filename
    40  }
    41  
    42  func TestClonefile(t *testing.T) {
    43  	fileName := createTestFile(t)
    44  
    45  	clonedName := fileName + "-cloned"
    46  	err := unix.Clonefile(fileName, clonedName, 0)
    47  	if err == unix.ENOSYS || err == unix.ENOTSUP {
    48  		t.Skip("clonefile is not available or supported, skipping test")
    49  	} else if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	clonedData, err := os.ReadFile(clonedName)
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  
    58  	if !bytes.Equal(testData, clonedData) {
    59  		t.Errorf("Clonefile: got %q, expected %q", clonedData, testData)
    60  	}
    61  }
    62  
    63  func TestClonefileatWithCwd(t *testing.T) {
    64  	fileName := createTestFile(t)
    65  
    66  	clonedName := fileName + "-cloned"
    67  	err := unix.Clonefileat(unix.AT_FDCWD, fileName, unix.AT_FDCWD, clonedName, 0)
    68  	if err == unix.ENOSYS || err == unix.ENOTSUP {
    69  		t.Skip("clonefileat is not available or supported, skipping test")
    70  	} else if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  
    74  	clonedData, err := os.ReadFile(clonedName)
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  
    79  	if !bytes.Equal(testData, clonedData) {
    80  		t.Errorf("Clonefileat: got %q, expected %q", clonedData, testData)
    81  	}
    82  }
    83  
    84  func TestClonefileatWithRelativePaths(t *testing.T) {
    85  	srcFileName := createTestFile(t)
    86  	srcDir := filepath.Dir(srcFileName)
    87  	srcFd, err := unix.Open(srcDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	defer unix.Close(srcFd)
    92  
    93  	dstDir := t.TempDir()
    94  	dstFd, err := unix.Open(dstDir, unix.O_RDONLY|unix.O_DIRECTORY, 0)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	defer unix.Close(dstFd)
    99  
   100  	dstFile, err := os.Create(filepath.Join(dstDir, "TestClonefileat"))
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	err = os.Remove(dstFile.Name())
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  
   109  	src := filepath.Base(srcFileName)
   110  	dst := filepath.Base(dstFile.Name())
   111  	err = unix.Clonefileat(srcFd, src, dstFd, dst, 0)
   112  	if err == unix.ENOSYS || err == unix.ENOTSUP {
   113  		t.Skip("clonefileat is not available or supported, skipping test")
   114  	} else if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	clonedData, err := os.ReadFile(dstFile.Name())
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	if !bytes.Equal(testData, clonedData) {
   124  		t.Errorf("Clonefileat: got %q, expected %q", clonedData, testData)
   125  	}
   126  }
   127  
   128  func TestFclonefileat(t *testing.T) {
   129  	fileName := createTestFile(t)
   130  	dir := filepath.Dir(fileName)
   131  
   132  	fd, err := unix.Open(fileName, unix.O_RDONLY, 0)
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  	defer unix.Close(fd)
   137  
   138  	dstFile, err := os.Create(filepath.Join(dir, "dst"))
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	os.Remove(dstFile.Name())
   143  
   144  	err = unix.Fclonefileat(fd, unix.AT_FDCWD, dstFile.Name(), 0)
   145  	if err == unix.ENOSYS || err == unix.ENOTSUP {
   146  		t.Skip("clonefileat is not available or supported, skipping test")
   147  	} else if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	clonedData, err := os.ReadFile(dstFile.Name())
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	if !bytes.Equal(testData, clonedData) {
   157  		t.Errorf("Fclonefileat: got %q, expected %q", clonedData, testData)
   158  	}
   159  }
   160  
   161  func TestFcntlFstore(t *testing.T) {
   162  	f, err := os.CreateTemp(t.TempDir(), t.Name())
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	defer f.Close()
   167  
   168  	fstore := &unix.Fstore_t{
   169  		Flags:   unix.F_ALLOCATEALL,
   170  		Posmode: unix.F_PEOFPOSMODE,
   171  		Offset:  0,
   172  		Length:  1 << 10,
   173  	}
   174  	err = unix.FcntlFstore(f.Fd(), unix.F_PREALLOCATE, fstore)
   175  	if err == unix.EOPNOTSUPP {
   176  		t.Skipf("fcntl with F_PREALLOCATE not supported, skipping test")
   177  	} else if err != nil {
   178  		t.Fatalf("FcntlFstore: %v", err)
   179  	}
   180  
   181  	st, err := f.Stat()
   182  	if err != nil {
   183  		t.Fatal(err)
   184  	}
   185  
   186  	if st.Size() != 0 {
   187  		t.Errorf("FcntlFstore: got size = %d, want %d", st.Size(), 0)
   188  	}
   189  
   190  }
   191  
   192  func TestGetsockoptXucred(t *testing.T) {
   193  	fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
   194  	if err != nil {
   195  		t.Fatalf("Socketpair: %v", err)
   196  	}
   197  
   198  	srvFile := os.NewFile(uintptr(fds[0]), "server")
   199  	cliFile := os.NewFile(uintptr(fds[1]), "client")
   200  	defer srvFile.Close()
   201  	defer cliFile.Close()
   202  
   203  	srv, err := net.FileConn(srvFile)
   204  	if err != nil {
   205  		t.Fatalf("FileConn: %v", err)
   206  	}
   207  	defer srv.Close()
   208  
   209  	cli, err := net.FileConn(cliFile)
   210  	if err != nil {
   211  		t.Fatalf("FileConn: %v", err)
   212  	}
   213  	defer cli.Close()
   214  
   215  	cred, err := unix.GetsockoptXucred(fds[1], unix.SOL_LOCAL, unix.LOCAL_PEERCRED)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  	t.Logf("got: %+v", cred)
   220  	if got, want := cred.Uid, os.Getuid(); int(got) != int(want) {
   221  		t.Errorf("uid = %v; want %v", got, want)
   222  	}
   223  	if cred.Ngroups > 0 {
   224  		if got, want := cred.Groups[0], os.Getgid(); int(got) != int(want) {
   225  			t.Errorf("gid = %v; want %v", got, want)
   226  		}
   227  	}
   228  }
   229  
   230  func TestSysctlKinfoProc(t *testing.T) {
   231  	pid := unix.Getpid()
   232  	kp, err := unix.SysctlKinfoProc("kern.proc.pid", pid)
   233  	if err != nil {
   234  		t.Fatalf("SysctlKinfoProc: %v", err)
   235  	}
   236  	if got, want := int(kp.Proc.P_pid), pid; got != want {
   237  		t.Errorf("got pid %d, want %d", got, want)
   238  	}
   239  }
   240  
   241  func TestSysctlKinfoProcSlice(t *testing.T) {
   242  	kps, err := unix.SysctlKinfoProcSlice("kern.proc.all")
   243  	if err != nil {
   244  		t.Fatalf("SysctlKinfoProc: %v", err)
   245  	}
   246  	if len(kps) == 0 {
   247  		t.Errorf("SysctlKinfoProcSlice: expected at least one process")
   248  	}
   249  
   250  	uid := unix.Getuid()
   251  	kps, err = unix.SysctlKinfoProcSlice("kern.proc.uid", uid)
   252  	if err != nil {
   253  		t.Fatalf("SysctlKinfoProc: %v", err)
   254  	}
   255  	if len(kps) == 0 {
   256  		t.Errorf("SysctlKinfoProcSlice: expected at least one process")
   257  	}
   258  
   259  	for _, kp := range kps {
   260  		if got, want := int(kp.Eproc.Ucred.Uid), uid; got != want {
   261  			t.Errorf("process %d: got uid %d, want %d", kp.Proc.P_pid, got, want)
   262  		}
   263  	}
   264  }