1// Copyright 2013 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// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
6
7package unix_test
8
9import (
10	"flag"
11	"fmt"
12	"io/ioutil"
13	"net"
14	"os"
15	"os/exec"
16	"path/filepath"
17	"runtime"
18	"strconv"
19	"syscall"
20	"testing"
21	"time"
22
23	"golang.org/x/sys/unix"
24)
25
26// Tests that below functions, structures and constants are consistent
27// on all Unix-like systems.
28func _() {
29	// program scheduling priority functions and constants
30	var (
31		_ func(int, int, int) error   = unix.Setpriority
32		_ func(int, int) (int, error) = unix.Getpriority
33	)
34	const (
35		_ int = unix.PRIO_USER
36		_ int = unix.PRIO_PROCESS
37		_ int = unix.PRIO_PGRP
38	)
39
40	// termios constants
41	const (
42		_ int = unix.TCIFLUSH
43		_ int = unix.TCIOFLUSH
44		_ int = unix.TCOFLUSH
45	)
46
47	// fcntl file locking structure and constants
48	var (
49		_ = unix.Flock_t{
50			Type:   int16(0),
51			Whence: int16(0),
52			Start:  int64(0),
53			Len:    int64(0),
54			Pid:    int32(0),
55		}
56	)
57	const (
58		_ = unix.F_GETLK
59		_ = unix.F_SETLK
60		_ = unix.F_SETLKW
61	)
62}
63
64func TestErrnoSignalName(t *testing.T) {
65	testErrors := []struct {
66		num  syscall.Errno
67		name string
68	}{
69		{syscall.EPERM, "EPERM"},
70		{syscall.EINVAL, "EINVAL"},
71		{syscall.ENOENT, "ENOENT"},
72	}
73
74	for _, te := range testErrors {
75		t.Run(fmt.Sprintf("%d/%s", te.num, te.name), func(t *testing.T) {
76			e := unix.ErrnoName(te.num)
77			if e != te.name {
78				t.Errorf("ErrnoName(%d) returned %s, want %s", te.num, e, te.name)
79			}
80		})
81	}
82
83	testSignals := []struct {
84		num  syscall.Signal
85		name string
86	}{
87		{syscall.SIGHUP, "SIGHUP"},
88		{syscall.SIGPIPE, "SIGPIPE"},
89		{syscall.SIGSEGV, "SIGSEGV"},
90	}
91
92	for _, ts := range testSignals {
93		t.Run(fmt.Sprintf("%d/%s", ts.num, ts.name), func(t *testing.T) {
94			s := unix.SignalName(ts.num)
95			if s != ts.name {
96				t.Errorf("SignalName(%d) returned %s, want %s", ts.num, s, ts.name)
97			}
98		})
99	}
100}
101
102func TestSignalNum(t *testing.T) {
103	testSignals := []struct {
104		name string
105		want syscall.Signal
106	}{
107		{"SIGHUP", syscall.SIGHUP},
108		{"SIGPIPE", syscall.SIGPIPE},
109		{"SIGSEGV", syscall.SIGSEGV},
110		{"NONEXISTS", 0},
111	}
112	for _, ts := range testSignals {
113		t.Run(fmt.Sprintf("%s/%d", ts.name, ts.want), func(t *testing.T) {
114			got := unix.SignalNum(ts.name)
115			if got != ts.want {
116				t.Errorf("SignalNum(%s) returned %d, want %d", ts.name, got, ts.want)
117			}
118		})
119
120	}
121}
122
123func TestFcntlInt(t *testing.T) {
124	t.Parallel()
125	file, err := ioutil.TempFile("", "TestFnctlInt")
126	if err != nil {
127		t.Fatal(err)
128	}
129	defer os.Remove(file.Name())
130	defer file.Close()
131	f := file.Fd()
132	flags, err := unix.FcntlInt(f, unix.F_GETFD, 0)
133	if err != nil {
134		t.Fatal(err)
135	}
136	if flags&unix.FD_CLOEXEC == 0 {
137		t.Errorf("flags %#x do not include FD_CLOEXEC", flags)
138	}
139}
140
141// TestFcntlFlock tests whether the file locking structure matches
142// the calling convention of each kernel.
143func TestFcntlFlock(t *testing.T) {
144	name := filepath.Join(os.TempDir(), "TestFcntlFlock")
145	fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0)
146	if err != nil {
147		t.Fatalf("Open failed: %v", err)
148	}
149	defer unix.Unlink(name)
150	defer unix.Close(fd)
151	flock := unix.Flock_t{
152		Type:  unix.F_RDLCK,
153		Start: 0, Len: 0, Whence: 1,
154	}
155	if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil {
156		t.Fatalf("FcntlFlock failed: %v", err)
157	}
158}
159
160// TestPassFD tests passing a file descriptor over a Unix socket.
161//
162// This test involved both a parent and child process. The parent
163// process is invoked as a normal test, with "go test", which then
164// runs the child process by running the current test binary with args
165// "-test.run=^TestPassFD$" and an environment variable used to signal
166// that the test should become the child process instead.
167func TestPassFD(t *testing.T) {
168	if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
169		t.Skip("cannot exec subprocess on iOS, skipping test")
170	}
171
172	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
173		passFDChild()
174		return
175	}
176
177	if runtime.GOOS == "aix" {
178		// Unix network isn't properly working on AIX
179		// 7.2 with Technical Level < 2
180		out, err := exec.Command("oslevel", "-s").Output()
181		if err != nil {
182			t.Skipf("skipping on AIX because oslevel -s failed: %v", err)
183		}
184
185		if len(out) < len("7200-XX-ZZ-YYMM") { // AIX 7.2, Tech Level XX, Service Pack ZZ, date YYMM
186			t.Skip("skipping on AIX because oslevel -s hasn't the right length")
187		}
188		aixVer := string(out[:4])
189		tl, err := strconv.Atoi(string(out[5:7]))
190		if err != nil {
191			t.Skipf("skipping on AIX because oslevel -s output cannot be parsed: %v", err)
192		}
193		if aixVer < "7200" || (aixVer == "7200" && tl < 2) {
194			t.Skip("skipped on AIX versions previous to 7.2 TL 2")
195		}
196	}
197
198	tempDir, err := ioutil.TempDir("", "TestPassFD")
199	if err != nil {
200		t.Fatal(err)
201	}
202	defer os.RemoveAll(tempDir)
203
204	fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
205	if err != nil {
206		t.Fatalf("Socketpair: %v", err)
207	}
208	defer unix.Close(fds[0])
209	defer unix.Close(fds[1])
210	writeFile := os.NewFile(uintptr(fds[0]), "child-writes")
211	readFile := os.NewFile(uintptr(fds[1]), "parent-reads")
212	defer writeFile.Close()
213	defer readFile.Close()
214
215	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
216	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
217	if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" {
218		cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp)
219	}
220	cmd.ExtraFiles = []*os.File{writeFile}
221
222	out, err := cmd.CombinedOutput()
223	if len(out) > 0 || err != nil {
224		t.Fatalf("child process: %q, %v", out, err)
225	}
226
227	c, err := net.FileConn(readFile)
228	if err != nil {
229		t.Fatalf("FileConn: %v", err)
230	}
231	defer c.Close()
232
233	uc, ok := c.(*net.UnixConn)
234	if !ok {
235		t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c)
236	}
237
238	buf := make([]byte, 32) // expect 1 byte
239	oob := make([]byte, 32) // expect 24 bytes
240	closeUnix := time.AfterFunc(5*time.Second, func() {
241		t.Logf("timeout reading from unix socket")
242		uc.Close()
243	})
244	_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
245	if err != nil {
246		t.Fatalf("ReadMsgUnix: %v", err)
247	}
248	closeUnix.Stop()
249
250	scms, err := unix.ParseSocketControlMessage(oob[:oobn])
251	if err != nil {
252		t.Fatalf("ParseSocketControlMessage: %v", err)
253	}
254	if len(scms) != 1 {
255		t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms)
256	}
257	scm := scms[0]
258	gotFds, err := unix.ParseUnixRights(&scm)
259	if err != nil {
260		t.Fatalf("unix.ParseUnixRights: %v", err)
261	}
262	if len(gotFds) != 1 {
263		t.Fatalf("wanted 1 fd; got %#v", gotFds)
264	}
265
266	f := os.NewFile(uintptr(gotFds[0]), "fd-from-child")
267	defer f.Close()
268
269	got, err := ioutil.ReadAll(f)
270	want := "Hello from child process!\n"
271	if string(got) != want {
272		t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want)
273	}
274}
275
276// passFDChild is the child process used by TestPassFD.
277func passFDChild() {
278	defer os.Exit(0)
279
280	// Look for our fd. It should be fd 3, but we work around an fd leak
281	// bug here (http://golang.org/issue/2603) to let it be elsewhere.
282	var uc *net.UnixConn
283	for fd := uintptr(3); fd <= 10; fd++ {
284		f := os.NewFile(fd, "unix-conn")
285		var ok bool
286		netc, _ := net.FileConn(f)
287		uc, ok = netc.(*net.UnixConn)
288		if ok {
289			break
290		}
291	}
292	if uc == nil {
293		fmt.Println("failed to find unix fd")
294		return
295	}
296
297	// Make a file f to send to our parent process on uc.
298	// We make it in tempDir, which our parent will clean up.
299	flag.Parse()
300	tempDir := flag.Arg(0)
301	f, err := ioutil.TempFile(tempDir, "")
302	if err != nil {
303		fmt.Printf("TempFile: %v", err)
304		return
305	}
306
307	f.Write([]byte("Hello from child process!\n"))
308	f.Seek(0, 0)
309
310	rights := unix.UnixRights(int(f.Fd()))
311	dummyByte := []byte("x")
312	n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil)
313	if err != nil {
314		fmt.Printf("WriteMsgUnix: %v", err)
315		return
316	}
317	if n != 1 || oobn != len(rights) {
318		fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights))
319		return
320	}
321}
322
323// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage,
324// and ParseUnixRights are able to successfully round-trip lists of file descriptors.
325func TestUnixRightsRoundtrip(t *testing.T) {
326	testCases := [...][][]int{
327		{{42}},
328		{{1, 2}},
329		{{3, 4, 5}},
330		{{}},
331		{{1, 2}, {3, 4, 5}, {}, {7}},
332	}
333	for _, testCase := range testCases {
334		b := []byte{}
335		var n int
336		for _, fds := range testCase {
337			// Last assignment to n wins
338			n = len(b) + unix.CmsgLen(4*len(fds))
339			b = append(b, unix.UnixRights(fds...)...)
340		}
341		// Truncate b
342		b = b[:n]
343
344		scms, err := unix.ParseSocketControlMessage(b)
345		if err != nil {
346			t.Fatalf("ParseSocketControlMessage: %v", err)
347		}
348		if len(scms) != len(testCase) {
349			t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms)
350		}
351		for i, scm := range scms {
352			gotFds, err := unix.ParseUnixRights(&scm)
353			if err != nil {
354				t.Fatalf("ParseUnixRights: %v", err)
355			}
356			wantFds := testCase[i]
357			if len(gotFds) != len(wantFds) {
358				t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds)
359			}
360			for j, fd := range gotFds {
361				if fd != wantFds[j] {
362					t.Fatalf("expected fd %v, got %v", wantFds[j], fd)
363				}
364			}
365		}
366	}
367}
368
369func TestRlimit(t *testing.T) {
370	var rlimit, zero unix.Rlimit
371	err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit)
372	if err != nil {
373		t.Fatalf("Getrlimit: save failed: %v", err)
374	}
375	if zero == rlimit {
376		t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
377	}
378	set := rlimit
379	set.Cur = set.Max - 1
380	if runtime.GOOS == "darwin" && set.Cur > 10240 {
381		// The max file limit is 10240, even though
382		// the max returned by Getrlimit is 1<<63-1.
383		// This is OPEN_MAX in sys/syslimits.h.
384		set.Cur = 10240
385	}
386	err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set)
387	if err != nil {
388		t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
389	}
390	var get unix.Rlimit
391	err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get)
392	if err != nil {
393		t.Fatalf("Getrlimit: get failed: %v", err)
394	}
395	set = rlimit
396	set.Cur = set.Max - 1
397	if set != get {
398		// Seems like Darwin requires some privilege to
399		// increase the soft limit of rlimit sandbox, though
400		// Setrlimit never reports an error.
401		switch runtime.GOOS {
402		case "darwin":
403		default:
404			t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
405		}
406	}
407	err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit)
408	if err != nil {
409		t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
410	}
411}
412
413func TestSeekFailure(t *testing.T) {
414	_, err := unix.Seek(-1, 0, 0)
415	if err == nil {
416		t.Fatalf("Seek(-1, 0, 0) did not fail")
417	}
418	str := err.Error() // used to crash on Linux
419	t.Logf("Seek: %v", str)
420	if str == "" {
421		t.Fatalf("Seek(-1, 0, 0) return error with empty message")
422	}
423}
424
425func TestSetsockoptString(t *testing.T) {
426	// should not panic on empty string, see issue #31277
427	err := unix.SetsockoptString(-1, 0, 0, "")
428	if err == nil {
429		t.Fatalf("SetsockoptString: did not fail")
430	}
431}
432
433func TestDup(t *testing.T) {
434	file, err := ioutil.TempFile("", "TestDup")
435	if err != nil {
436		t.Fatalf("Tempfile failed: %v", err)
437	}
438	defer os.Remove(file.Name())
439	defer file.Close()
440	f := int(file.Fd())
441
442	newFd, err := unix.Dup(f)
443	if err != nil {
444		t.Fatalf("Dup: %v", err)
445	}
446
447	// Create and reserve a file descriptor.
448	// Dup2 automatically closes it before reusing it.
449	nullFile, err := os.Open("/dev/null")
450	if err != nil {
451		t.Fatal(err)
452	}
453	dupFd := int(file.Fd())
454	err = unix.Dup2(newFd, dupFd)
455	if err != nil {
456		t.Fatalf("Dup2: %v", err)
457	}
458	// Keep the dummy file open long enough to not be closed in
459	// its finalizer.
460	runtime.KeepAlive(nullFile)
461
462	b1 := []byte("Test123")
463	b2 := make([]byte, 7)
464	_, err = unix.Write(dupFd, b1)
465	if err != nil {
466		t.Fatalf("Write to dup2 fd failed: %v", err)
467	}
468	_, err = unix.Seek(f, 0, 0)
469	if err != nil {
470		t.Fatalf("Seek failed: %v", err)
471	}
472	_, err = unix.Read(f, b2)
473	if err != nil {
474		t.Fatalf("Read back failed: %v", err)
475	}
476	if string(b1) != string(b2) {
477		t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2))
478	}
479}
480
481func TestPoll(t *testing.T) {
482	if runtime.GOOS == "android" ||
483		(runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")) {
484		t.Skip("mkfifo syscall is not available on android and iOS, skipping test")
485	}
486
487	defer chtmpdir(t)()
488	f, cleanup := mktmpfifo(t)
489	defer cleanup()
490
491	const timeout = 100
492
493	ok := make(chan bool, 1)
494	go func() {
495		select {
496		case <-time.After(10 * timeout * time.Millisecond):
497			t.Errorf("Poll: failed to timeout after %d milliseconds", 10*timeout)
498		case <-ok:
499		}
500	}()
501
502	fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}}
503	n, err := unix.Poll(fds, timeout)
504	ok <- true
505	if err != nil {
506		t.Errorf("Poll: unexpected error: %v", err)
507		return
508	}
509	if n != 0 {
510		t.Errorf("Poll: wrong number of events: got %v, expected %v", n, 0)
511		return
512	}
513}
514
515func TestSelect(t *testing.T) {
516	for {
517		n, err := unix.Select(0, nil, nil, nil, &unix.Timeval{Sec: 0, Usec: 0})
518		if err == unix.EINTR {
519			t.Logf("Select interrupted")
520			continue
521		} else if err != nil {
522			t.Fatalf("Select: %v", err)
523		}
524		if n != 0 {
525			t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
526		}
527		break
528	}
529
530	dur := 250 * time.Millisecond
531	tv := unix.NsecToTimeval(int64(dur))
532	var took time.Duration
533	for {
534		start := time.Now()
535		n, err := unix.Select(0, nil, nil, nil, &tv)
536		took = time.Since(start)
537		if err == unix.EINTR {
538			t.Logf("Select interrupted after %v", took)
539			continue
540		} else if err != nil {
541			t.Fatalf("Select: %v", err)
542		}
543		if n != 0 {
544			t.Fatalf("Select: got %v ready file descriptors, expected 0", n)
545		}
546		break
547	}
548
549	// On some BSDs the actual timeout might also be slightly less than the requested.
550	// Add an acceptable margin to avoid flaky tests.
551	if took < dur*2/3 {
552		t.Errorf("Select: got %v timeout, expected at least %v", took, dur)
553	}
554
555	rr, ww, err := os.Pipe()
556	if err != nil {
557		t.Fatal(err)
558	}
559	defer rr.Close()
560	defer ww.Close()
561
562	if _, err := ww.Write([]byte("HELLO GOPHER")); err != nil {
563		t.Fatal(err)
564	}
565
566	rFdSet := &unix.FdSet{}
567	fd := int(rr.Fd())
568	rFdSet.Set(fd)
569
570	for {
571		n, err := unix.Select(fd+1, rFdSet, nil, nil, nil)
572		if err == unix.EINTR {
573			t.Log("Select interrupted")
574			continue
575		} else if err != nil {
576			t.Fatalf("Select: %v", err)
577		}
578		if n != 1 {
579			t.Fatalf("Select: got %v ready file descriptors, expected 1", n)
580		}
581		break
582	}
583}
584
585func TestGetwd(t *testing.T) {
586	fd, err := os.Open(".")
587	if err != nil {
588		t.Fatalf("Open .: %s", err)
589	}
590	defer fd.Close()
591	// Directory list for test. Do not worry if any are symlinks or do not
592	// exist on some common unix desktop environments. That will be checked.
593	dirs := []string{"/", "/usr/bin", "/etc", "/var", "/opt"}
594	switch runtime.GOOS {
595	case "android":
596		dirs = []string{"/", "/system/bin"}
597	case "darwin":
598		switch runtime.GOARCH {
599		case "arm", "arm64":
600			d1, err := ioutil.TempDir("", "d1")
601			if err != nil {
602				t.Fatalf("TempDir: %v", err)
603			}
604			d2, err := ioutil.TempDir("", "d2")
605			if err != nil {
606				t.Fatalf("TempDir: %v", err)
607			}
608			dirs = []string{d1, d2}
609		}
610	}
611	oldwd := os.Getenv("PWD")
612	for _, d := range dirs {
613		// Check whether d exists, is a dir and that d's path does not contain a symlink
614		fi, err := os.Stat(d)
615		if err != nil || !fi.IsDir() {
616			t.Logf("Test dir %s stat error (%v) or not a directory, skipping", d, err)
617			continue
618		}
619		check, err := filepath.EvalSymlinks(d)
620		if err != nil || check != d {
621			t.Logf("Test dir %s (%s) is symlink or other error (%v), skipping", d, check, err)
622			continue
623		}
624		err = os.Chdir(d)
625		if err != nil {
626			t.Fatalf("Chdir: %v", err)
627		}
628		pwd, err := unix.Getwd()
629		if err != nil {
630			t.Fatalf("Getwd in %s: %s", d, err)
631		}
632		os.Setenv("PWD", oldwd)
633		err = fd.Chdir()
634		if err != nil {
635			// We changed the current directory and cannot go back.
636			// Don't let the tests continue; they'll scribble
637			// all over some other directory.
638			fmt.Fprintf(os.Stderr, "fchdir back to dot failed: %s\n", err)
639			os.Exit(1)
640		}
641		if pwd != d {
642			t.Fatalf("Getwd returned %q want %q", pwd, d)
643		}
644	}
645}
646
647func TestFstatat(t *testing.T) {
648	defer chtmpdir(t)()
649
650	touch(t, "file1")
651
652	var st1 unix.Stat_t
653	err := unix.Stat("file1", &st1)
654	if err != nil {
655		t.Fatalf("Stat: %v", err)
656	}
657
658	var st2 unix.Stat_t
659	err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0)
660	if err != nil {
661		t.Fatalf("Fstatat: %v", err)
662	}
663
664	if st1 != st2 {
665		t.Errorf("Fstatat: returned stat does not match Stat")
666	}
667
668	err = os.Symlink("file1", "symlink1")
669	if err != nil {
670		t.Fatal(err)
671	}
672
673	err = unix.Lstat("symlink1", &st1)
674	if err != nil {
675		t.Fatalf("Lstat: %v", err)
676	}
677
678	err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW)
679	if err != nil {
680		t.Fatalf("Fstatat: %v", err)
681	}
682
683	if st2.Dev != st1.Dev {
684		t.Errorf("Fstatat: got dev %v, expected %v", st2.Dev, st1.Dev)
685	}
686	if st2.Ino != st1.Ino {
687		t.Errorf("Fstatat: got ino %v, expected %v", st2.Ino, st1.Ino)
688	}
689	if st2.Mode != st1.Mode {
690		t.Errorf("Fstatat: got mode %v, expected %v", st2.Mode, st1.Mode)
691	}
692	if st2.Uid != st1.Uid {
693		t.Errorf("Fstatat: got uid %v, expected %v", st2.Uid, st1.Uid)
694	}
695	if st2.Gid != st1.Gid {
696		t.Errorf("Fstatat: got gid %v, expected %v", st2.Gid, st1.Gid)
697	}
698	if st2.Size != st1.Size {
699		t.Errorf("Fstatat: got size %v, expected %v", st2.Size, st1.Size)
700	}
701}
702
703func TestFchmodat(t *testing.T) {
704	defer chtmpdir(t)()
705
706	touch(t, "file1")
707	err := os.Symlink("file1", "symlink1")
708	if err != nil {
709		t.Fatal(err)
710	}
711
712	mode := os.FileMode(0444)
713	err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0)
714	if err != nil {
715		t.Fatalf("Fchmodat: unexpected error: %v", err)
716	}
717
718	fi, err := os.Stat("file1")
719	if err != nil {
720		t.Fatal(err)
721	}
722
723	if fi.Mode() != mode {
724		t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode())
725	}
726
727	mode = os.FileMode(0644)
728	didChmodSymlink := true
729	err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW)
730	if err != nil {
731		if (runtime.GOOS == "android" || runtime.GOOS == "linux" ||
732			runtime.GOOS == "solaris" || runtime.GOOS == "illumos") && err == unix.EOPNOTSUPP {
733			// Linux and Illumos don't support flags != 0
734			didChmodSymlink = false
735		} else {
736			t.Fatalf("Fchmodat: unexpected error: %v", err)
737		}
738	}
739
740	if !didChmodSymlink {
741		// Didn't change mode of the symlink. On Linux, the permissions
742		// of a symbolic link are always 0777 according to symlink(7)
743		mode = os.FileMode(0777)
744	}
745
746	var st unix.Stat_t
747	err = unix.Lstat("symlink1", &st)
748	if err != nil {
749		t.Fatal(err)
750	}
751
752	got := os.FileMode(st.Mode & 0777)
753	if got != mode {
754		t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got)
755	}
756}
757
758func TestMkdev(t *testing.T) {
759	major := uint32(42)
760	minor := uint32(7)
761	dev := unix.Mkdev(major, minor)
762
763	if unix.Major(dev) != major {
764		t.Errorf("Major(%#x) == %d, want %d", dev, unix.Major(dev), major)
765	}
766	if unix.Minor(dev) != minor {
767		t.Errorf("Minor(%#x) == %d, want %d", dev, unix.Minor(dev), minor)
768	}
769}
770
771func TestRenameat(t *testing.T) {
772	defer chtmpdir(t)()
773
774	from, to := "renamefrom", "renameto"
775
776	touch(t, from)
777
778	err := unix.Renameat(unix.AT_FDCWD, from, unix.AT_FDCWD, to)
779	if err != nil {
780		t.Fatalf("Renameat: unexpected error: %v", err)
781	}
782
783	_, err = os.Stat(to)
784	if err != nil {
785		t.Error(err)
786	}
787
788	_, err = os.Stat(from)
789	if err == nil {
790		t.Errorf("Renameat: stat of renamed file %q unexpectedly succeeded", from)
791	}
792}
793
794func TestUtimesNanoAt(t *testing.T) {
795	defer chtmpdir(t)()
796
797	symlink := "symlink1"
798	os.Remove(symlink)
799	err := os.Symlink("nonexisting", symlink)
800	if err != nil {
801		t.Fatal(err)
802	}
803
804	// Some filesystems only support microsecond resolution. Account for
805	// that in Nsec.
806	ts := []unix.Timespec{
807		{Sec: 1111, Nsec: 2000},
808		{Sec: 3333, Nsec: 4000},
809	}
810	err = unix.UtimesNanoAt(unix.AT_FDCWD, symlink, ts, unix.AT_SYMLINK_NOFOLLOW)
811	if err != nil {
812		t.Fatalf("UtimesNanoAt: %v", err)
813	}
814
815	var st unix.Stat_t
816	err = unix.Lstat(symlink, &st)
817	if err != nil {
818		t.Fatalf("Lstat: %v", err)
819	}
820
821	// Only check Mtim, Atim might not be supported by the underlying filesystem
822	expected := ts[1]
823	if st.Mtim.Nsec == 0 {
824		// Some filesystems only support 1-second time stamp resolution
825		// and will always set Nsec to 0.
826		expected.Nsec = 0
827	}
828	if st.Mtim != expected {
829		t.Errorf("UtimesNanoAt: wrong mtime: got %v, expected %v", st.Mtim, expected)
830	}
831}
832
833// mktmpfifo creates a temporary FIFO and provides a cleanup function.
834func mktmpfifo(t *testing.T) (*os.File, func()) {
835	err := unix.Mkfifo("fifo", 0666)
836	if err != nil {
837		t.Fatalf("mktmpfifo: failed to create FIFO: %v", err)
838	}
839
840	f, err := os.OpenFile("fifo", os.O_RDWR, 0666)
841	if err != nil {
842		os.Remove("fifo")
843		t.Fatalf("mktmpfifo: failed to open FIFO: %v", err)
844	}
845
846	return f, func() {
847		f.Close()
848		os.Remove("fifo")
849	}
850}
851
852// utilities taken from os/os_test.go
853
854func touch(t *testing.T, name string) {
855	f, err := os.Create(name)
856	if err != nil {
857		t.Fatal(err)
858	}
859	if err := f.Close(); err != nil {
860		t.Fatal(err)
861	}
862}
863
864// chtmpdir changes the working directory to a new temporary directory and
865// provides a cleanup function. Used when PWD is read-only.
866func chtmpdir(t *testing.T) func() {
867	oldwd, err := os.Getwd()
868	if err != nil {
869		t.Fatalf("chtmpdir: %v", err)
870	}
871	d, err := ioutil.TempDir("", "test")
872	if err != nil {
873		t.Fatalf("chtmpdir: %v", err)
874	}
875	if err := os.Chdir(d); err != nil {
876		t.Fatalf("chtmpdir: %v", err)
877	}
878	return func() {
879		if err := os.Chdir(oldwd); err != nil {
880			t.Fatalf("chtmpdir: %v", err)
881		}
882		os.RemoveAll(d)
883	}
884}
885