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//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 7 8package unix_test 9 10import ( 11 "bytes" 12 "flag" 13 "fmt" 14 "io/ioutil" 15 "net" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "runtime" 20 "strconv" 21 "syscall" 22 "testing" 23 "time" 24 25 "golang.org/x/sys/unix" 26) 27 28// Tests that below functions, structures and constants are consistent 29// on all Unix-like systems. 30func _() { 31 // program scheduling priority functions and constants 32 var ( 33 _ func(int, int, int) error = unix.Setpriority 34 _ func(int, int) (int, error) = unix.Getpriority 35 ) 36 const ( 37 _ int = unix.PRIO_USER 38 _ int = unix.PRIO_PROCESS 39 _ int = unix.PRIO_PGRP 40 ) 41 42 // termios constants 43 const ( 44 _ int = unix.TCIFLUSH 45 _ int = unix.TCIOFLUSH 46 _ int = unix.TCOFLUSH 47 ) 48 49 // fcntl file locking structure and constants 50 var ( 51 _ = unix.Flock_t{ 52 Type: int16(0), 53 Whence: int16(0), 54 Start: int64(0), 55 Len: int64(0), 56 Pid: int32(0), 57 } 58 ) 59 const ( 60 _ = unix.F_GETLK 61 _ = unix.F_SETLK 62 _ = unix.F_SETLKW 63 ) 64} 65 66func TestErrnoSignalName(t *testing.T) { 67 testErrors := []struct { 68 num syscall.Errno 69 name string 70 }{ 71 {syscall.EPERM, "EPERM"}, 72 {syscall.EINVAL, "EINVAL"}, 73 {syscall.ENOENT, "ENOENT"}, 74 } 75 76 for _, te := range testErrors { 77 t.Run(fmt.Sprintf("%d/%s", te.num, te.name), func(t *testing.T) { 78 e := unix.ErrnoName(te.num) 79 if e != te.name { 80 t.Errorf("ErrnoName(%d) returned %s, want %s", te.num, e, te.name) 81 } 82 }) 83 } 84 85 testSignals := []struct { 86 num syscall.Signal 87 name string 88 }{ 89 {syscall.SIGHUP, "SIGHUP"}, 90 {syscall.SIGPIPE, "SIGPIPE"}, 91 {syscall.SIGSEGV, "SIGSEGV"}, 92 } 93 94 for _, ts := range testSignals { 95 t.Run(fmt.Sprintf("%d/%s", ts.num, ts.name), func(t *testing.T) { 96 s := unix.SignalName(ts.num) 97 if s != ts.name { 98 t.Errorf("SignalName(%d) returned %s, want %s", ts.num, s, ts.name) 99 } 100 }) 101 } 102} 103 104func TestSignalNum(t *testing.T) { 105 testSignals := []struct { 106 name string 107 want syscall.Signal 108 }{ 109 {"SIGHUP", syscall.SIGHUP}, 110 {"SIGPIPE", syscall.SIGPIPE}, 111 {"SIGSEGV", syscall.SIGSEGV}, 112 {"NONEXISTS", 0}, 113 } 114 for _, ts := range testSignals { 115 t.Run(fmt.Sprintf("%s/%d", ts.name, ts.want), func(t *testing.T) { 116 got := unix.SignalNum(ts.name) 117 if got != ts.want { 118 t.Errorf("SignalNum(%s) returned %d, want %d", ts.name, got, ts.want) 119 } 120 }) 121 122 } 123} 124 125func TestFcntlInt(t *testing.T) { 126 t.Parallel() 127 file, err := ioutil.TempFile("", "TestFcntlInt") 128 if err != nil { 129 t.Fatal(err) 130 } 131 defer os.Remove(file.Name()) 132 defer file.Close() 133 f := file.Fd() 134 flags, err := unix.FcntlInt(f, unix.F_GETFD, 0) 135 if err != nil { 136 t.Fatal(err) 137 } 138 if flags&unix.FD_CLOEXEC == 0 { 139 t.Errorf("flags %#x do not include FD_CLOEXEC", flags) 140 } 141} 142 143// TestFcntlFlock tests whether the file locking structure matches 144// the calling convention of each kernel. 145func TestFcntlFlock(t *testing.T) { 146 name := filepath.Join(os.TempDir(), "TestFcntlFlock") 147 fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0) 148 if err != nil { 149 t.Fatalf("Open failed: %v", err) 150 } 151 defer unix.Unlink(name) 152 defer unix.Close(fd) 153 flock := unix.Flock_t{ 154 Type: unix.F_RDLCK, 155 Start: 0, Len: 0, Whence: 1, 156 } 157 if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { 158 t.Fatalf("FcntlFlock failed: %v", err) 159 } 160} 161 162// TestPassFD tests passing a file descriptor over a Unix socket. 163// 164// This test involved both a parent and child process. The parent 165// process is invoked as a normal test, with "go test", which then 166// runs the child process by running the current test binary with args 167// "-test.run=^TestPassFD$" and an environment variable used to signal 168// that the test should become the child process instead. 169func TestPassFD(t *testing.T) { 170 if runtime.GOOS == "ios" { 171 t.Skip("cannot exec subprocess on iOS, skipping test") 172 } 173 174 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 175 passFDChild() 176 return 177 } 178 179 if runtime.GOOS == "aix" { 180 // Unix network isn't properly working on AIX 181 // 7.2 with Technical Level < 2 182 out, err := exec.Command("oslevel", "-s").Output() 183 if err != nil { 184 t.Skipf("skipping on AIX because oslevel -s failed: %v", err) 185 } 186 187 if len(out) < len("7200-XX-ZZ-YYMM") { // AIX 7.2, Tech Level XX, Service Pack ZZ, date YYMM 188 t.Skip("skipping on AIX because oslevel -s hasn't the right length") 189 } 190 aixVer := string(out[:4]) 191 tl, err := strconv.Atoi(string(out[5:7])) 192 if err != nil { 193 t.Skipf("skipping on AIX because oslevel -s output cannot be parsed: %v", err) 194 } 195 if aixVer < "7200" || (aixVer == "7200" && tl < 2) { 196 t.Skip("skipped on AIX versions previous to 7.2 TL 2") 197 } 198 } 199 200 tempDir, err := ioutil.TempDir("", "TestPassFD") 201 if err != nil { 202 t.Fatal(err) 203 } 204 defer os.RemoveAll(tempDir) 205 206 fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) 207 if err != nil { 208 t.Fatalf("Socketpair: %v", err) 209 } 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" || runtime.GOOS == "ios") && set.Cur > 4096 { 381 // rlim_min for RLIMIT_NOFILE should be equal to 382 // or lower than kern.maxfilesperproc, which on 383 // some machines are 4096. See #40564. 384 set.Cur = 4096 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 (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && set.Cur > 4096 { 398 set.Cur = 4096 399 } 400 if set != get { 401 // Seems like Darwin requires some privilege to 402 // increase the soft limit of rlimit sandbox, though 403 // Setrlimit never reports an error. 404 switch runtime.GOOS { 405 case "darwin", "ios": 406 default: 407 t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) 408 } 409 } 410 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) 411 if err != nil { 412 t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) 413 } 414 415 // make sure RLIM_INFINITY can be assigned to Rlimit members 416 _ = unix.Rlimit{ 417 Cur: unix.RLIM_INFINITY, 418 Max: unix.RLIM_INFINITY, 419 } 420} 421 422func TestSeekFailure(t *testing.T) { 423 _, err := unix.Seek(-1, 0, 0) 424 if err == nil { 425 t.Fatalf("Seek(-1, 0, 0) did not fail") 426 } 427 str := err.Error() // used to crash on Linux 428 t.Logf("Seek: %v", str) 429 if str == "" { 430 t.Fatalf("Seek(-1, 0, 0) return error with empty message") 431 } 432} 433 434func TestSetsockoptString(t *testing.T) { 435 // should not panic on empty string, see issue #31277 436 err := unix.SetsockoptString(-1, 0, 0, "") 437 if err == nil { 438 t.Fatalf("SetsockoptString: did not fail") 439 } 440} 441 442func TestDup(t *testing.T) { 443 file, err := ioutil.TempFile("", "TestDup") 444 if err != nil { 445 t.Fatalf("Tempfile failed: %v", err) 446 } 447 defer os.Remove(file.Name()) 448 defer file.Close() 449 f := int(file.Fd()) 450 451 newFd, err := unix.Dup(f) 452 if err != nil { 453 t.Fatalf("Dup: %v", err) 454 } 455 456 // Create and reserve a file descriptor. 457 // Dup2 automatically closes it before reusing it. 458 nullFile, err := os.Open("/dev/null") 459 if err != nil { 460 t.Fatal(err) 461 } 462 dupFd := int(file.Fd()) 463 err = unix.Dup2(newFd, dupFd) 464 if err != nil { 465 t.Fatalf("Dup2: %v", err) 466 } 467 // Keep the dummy file open long enough to not be closed in 468 // its finalizer. 469 runtime.KeepAlive(nullFile) 470 471 b1 := []byte("Test123") 472 b2 := make([]byte, 7) 473 _, err = unix.Write(dupFd, b1) 474 if err != nil { 475 t.Fatalf("Write to dup2 fd failed: %v", err) 476 } 477 _, err = unix.Seek(f, 0, 0) 478 if err != nil { 479 t.Fatalf("Seek failed: %v", err) 480 } 481 _, err = unix.Read(f, b2) 482 if err != nil { 483 t.Fatalf("Read back failed: %v", err) 484 } 485 if string(b1) != string(b2) { 486 t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2)) 487 } 488} 489 490func TestPoll(t *testing.T) { 491 if runtime.GOOS == "android" || runtime.GOOS == "ios" { 492 t.Skip("mkfifo syscall is not available on android and iOS, skipping test") 493 } 494 495 defer chtmpdir(t)() 496 f, cleanup := mktmpfifo(t) 497 defer cleanup() 498 499 const timeout = 100 500 501 ok := make(chan bool, 1) 502 go func() { 503 select { 504 case <-time.After(10 * timeout * time.Millisecond): 505 t.Errorf("Poll: failed to timeout after %d milliseconds", 10*timeout) 506 case <-ok: 507 } 508 }() 509 510 for { 511 fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}} 512 n, err := unix.Poll(fds, timeout) 513 ok <- true 514 if err == unix.EINTR { 515 t.Logf("Poll interrupted") 516 continue 517 } else if err != nil { 518 t.Errorf("Poll: unexpected error: %v", err) 519 return 520 } 521 if n != 0 { 522 t.Errorf("Poll: wrong number of events: got %v, expected %v", n, 0) 523 } 524 break 525 } 526} 527 528func TestSelect(t *testing.T) { 529 for { 530 n, err := unix.Select(0, nil, nil, nil, &unix.Timeval{Sec: 0, Usec: 0}) 531 if err == unix.EINTR { 532 t.Logf("Select interrupted") 533 continue 534 } else if err != nil { 535 t.Fatalf("Select: %v", err) 536 } 537 if n != 0 { 538 t.Fatalf("Select: got %v ready file descriptors, expected 0", n) 539 } 540 break 541 } 542 543 dur := 250 * time.Millisecond 544 var took time.Duration 545 for { 546 // On some platforms (e.g. Linux), the passed-in timeval is 547 // updated by select(2). Make sure to reset to the full duration 548 // in case of an EINTR. 549 tv := unix.NsecToTimeval(int64(dur)) 550 start := time.Now() 551 n, err := unix.Select(0, nil, nil, nil, &tv) 552 took = time.Since(start) 553 if err == unix.EINTR { 554 t.Logf("Select interrupted after %v", took) 555 continue 556 } else if err != nil { 557 t.Fatalf("Select: %v", err) 558 } 559 if n != 0 { 560 t.Fatalf("Select: got %v ready file descriptors, expected 0", n) 561 } 562 break 563 } 564 565 // On some BSDs the actual timeout might also be slightly less than the requested. 566 // Add an acceptable margin to avoid flaky tests. 567 if took < dur*2/3 { 568 t.Errorf("Select: got %v timeout, expected at least %v", took, dur) 569 } 570 571 rr, ww, err := os.Pipe() 572 if err != nil { 573 t.Fatal(err) 574 } 575 defer rr.Close() 576 defer ww.Close() 577 578 if _, err := ww.Write([]byte("HELLO GOPHER")); err != nil { 579 t.Fatal(err) 580 } 581 582 rFdSet := &unix.FdSet{} 583 fd := int(rr.Fd()) 584 rFdSet.Set(fd) 585 586 for { 587 n, err := unix.Select(fd+1, rFdSet, nil, nil, nil) 588 if err == unix.EINTR { 589 t.Log("Select interrupted") 590 continue 591 } else if err != nil { 592 t.Fatalf("Select: %v", err) 593 } 594 if n != 1 { 595 t.Fatalf("Select: got %v ready file descriptors, expected 1", n) 596 } 597 break 598 } 599} 600 601func TestGetwd(t *testing.T) { 602 fd, err := os.Open(".") 603 if err != nil { 604 t.Fatalf("Open .: %s", err) 605 } 606 defer fd.Close() 607 // Directory list for test. Do not worry if any are symlinks or do not 608 // exist on some common unix desktop environments. That will be checked. 609 dirs := []string{"/", "/usr/bin", "/etc", "/var", "/opt"} 610 switch runtime.GOOS { 611 case "android": 612 dirs = []string{"/", "/system/bin"} 613 case "ios": 614 d1, err := ioutil.TempDir("", "d1") 615 if err != nil { 616 t.Fatalf("TempDir: %v", err) 617 } 618 d2, err := ioutil.TempDir("", "d2") 619 if err != nil { 620 t.Fatalf("TempDir: %v", err) 621 } 622 dirs = []string{d1, d2} 623 } 624 oldwd := os.Getenv("PWD") 625 for _, d := range dirs { 626 // Check whether d exists, is a dir and that d's path does not contain a symlink 627 fi, err := os.Stat(d) 628 if err != nil || !fi.IsDir() { 629 t.Logf("Test dir %s stat error (%v) or not a directory, skipping", d, err) 630 continue 631 } 632 check, err := filepath.EvalSymlinks(d) 633 if err != nil || check != d { 634 t.Logf("Test dir %s (%s) is symlink or other error (%v), skipping", d, check, err) 635 continue 636 } 637 err = os.Chdir(d) 638 if err != nil { 639 t.Fatalf("Chdir: %v", err) 640 } 641 pwd, err := unix.Getwd() 642 if err != nil { 643 t.Fatalf("Getwd in %s: %s", d, err) 644 } 645 os.Setenv("PWD", oldwd) 646 err = fd.Chdir() 647 if err != nil { 648 // We changed the current directory and cannot go back. 649 // Don't let the tests continue; they'll scribble 650 // all over some other directory. 651 fmt.Fprintf(os.Stderr, "fchdir back to dot failed: %s\n", err) 652 os.Exit(1) 653 } 654 if pwd != d { 655 t.Fatalf("Getwd returned %q want %q", pwd, d) 656 } 657 } 658} 659 660func compareStat_t(t *testing.T, otherStat string, st1, st2 *unix.Stat_t) { 661 if st2.Dev != st1.Dev { 662 t.Errorf("%s/Fstatat: got dev %v, expected %v", otherStat, st2.Dev, st1.Dev) 663 } 664 if st2.Ino != st1.Ino { 665 t.Errorf("%s/Fstatat: got ino %v, expected %v", otherStat, st2.Ino, st1.Ino) 666 } 667 if st2.Mode != st1.Mode { 668 t.Errorf("%s/Fstatat: got mode %v, expected %v", otherStat, st2.Mode, st1.Mode) 669 } 670 if st2.Uid != st1.Uid { 671 t.Errorf("%s/Fstatat: got uid %v, expected %v", otherStat, st2.Uid, st1.Uid) 672 } 673 if st2.Gid != st1.Gid { 674 t.Errorf("%s/Fstatat: got gid %v, expected %v", otherStat, st2.Gid, st1.Gid) 675 } 676 if st2.Size != st1.Size { 677 t.Errorf("%s/Fstatat: got size %v, expected %v", otherStat, st2.Size, st1.Size) 678 } 679} 680 681func TestFstatat(t *testing.T) { 682 defer chtmpdir(t)() 683 684 touch(t, "file1") 685 686 var st1 unix.Stat_t 687 err := unix.Stat("file1", &st1) 688 if err != nil { 689 t.Fatalf("Stat: %v", err) 690 } 691 692 var st2 unix.Stat_t 693 err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0) 694 if err != nil { 695 t.Fatalf("Fstatat: %v", err) 696 } 697 698 compareStat_t(t, "Stat", &st1, &st2) 699 700 err = os.Symlink("file1", "symlink1") 701 if err != nil { 702 t.Fatal(err) 703 } 704 705 err = unix.Lstat("symlink1", &st1) 706 if err != nil { 707 t.Fatalf("Lstat: %v", err) 708 } 709 710 err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW) 711 if err != nil { 712 t.Fatalf("Fstatat: %v", err) 713 } 714 715 compareStat_t(t, "Lstat", &st1, &st2) 716} 717 718func TestFchmodat(t *testing.T) { 719 defer chtmpdir(t)() 720 721 touch(t, "file1") 722 err := os.Symlink("file1", "symlink1") 723 if err != nil { 724 t.Fatal(err) 725 } 726 727 mode := os.FileMode(0444) 728 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0) 729 if err != nil { 730 t.Fatalf("Fchmodat: unexpected error: %v", err) 731 } 732 733 fi, err := os.Stat("file1") 734 if err != nil { 735 t.Fatal(err) 736 } 737 738 if fi.Mode() != mode { 739 t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode()) 740 } 741 742 mode = os.FileMode(0644) 743 didChmodSymlink := true 744 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW) 745 if err != nil { 746 if (runtime.GOOS == "android" || runtime.GOOS == "linux" || 747 runtime.GOOS == "solaris" || runtime.GOOS == "illumos") && err == unix.EOPNOTSUPP { 748 // Linux and Illumos don't support flags != 0 749 didChmodSymlink = false 750 } else { 751 t.Fatalf("Fchmodat: unexpected error: %v", err) 752 } 753 } 754 755 if !didChmodSymlink { 756 // Didn't change mode of the symlink. On Linux, the permissions 757 // of a symbolic link are always 0777 according to symlink(7) 758 mode = os.FileMode(0777) 759 } 760 761 var st unix.Stat_t 762 err = unix.Lstat("symlink1", &st) 763 if err != nil { 764 t.Fatal(err) 765 } 766 767 got := os.FileMode(st.Mode & 0777) 768 if got != mode { 769 t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got) 770 } 771} 772 773func TestMkdev(t *testing.T) { 774 major := uint32(42) 775 minor := uint32(7) 776 dev := unix.Mkdev(major, minor) 777 778 if unix.Major(dev) != major { 779 t.Errorf("Major(%#x) == %d, want %d", dev, unix.Major(dev), major) 780 } 781 if unix.Minor(dev) != minor { 782 t.Errorf("Minor(%#x) == %d, want %d", dev, unix.Minor(dev), minor) 783 } 784} 785 786func TestPipe(t *testing.T) { 787 const s = "hello" 788 var pipes [2]int 789 err := unix.Pipe(pipes[:]) 790 if err != nil { 791 t.Fatalf("pipe: %v", err) 792 } 793 r := pipes[0] 794 w := pipes[1] 795 go func() { 796 n, err := unix.Write(w, []byte(s)) 797 if err != nil { 798 t.Errorf("bad write: %v", err) 799 return 800 } 801 if n != len(s) { 802 t.Errorf("bad write count: %d", n) 803 return 804 } 805 err = unix.Close(w) 806 if err != nil { 807 t.Errorf("bad close: %v", err) 808 return 809 } 810 }() 811 var buf [10 + len(s)]byte 812 n, err := unix.Read(r, buf[:]) 813 if err != nil { 814 t.Fatalf("bad read: %v", err) 815 } 816 if n != len(s) { 817 t.Fatalf("bad read count: %d", n) 818 } 819 if string(buf[:n]) != s { 820 t.Fatalf("bad contents: %s", string(buf[:n])) 821 } 822 err = unix.Close(r) 823 if err != nil { 824 t.Fatalf("bad close: %v", err) 825 } 826} 827 828func TestRenameat(t *testing.T) { 829 defer chtmpdir(t)() 830 831 from, to := "renamefrom", "renameto" 832 833 touch(t, from) 834 835 err := unix.Renameat(unix.AT_FDCWD, from, unix.AT_FDCWD, to) 836 if err != nil { 837 t.Fatalf("Renameat: unexpected error: %v", err) 838 } 839 840 _, err = os.Stat(to) 841 if err != nil { 842 t.Error(err) 843 } 844 845 _, err = os.Stat(from) 846 if err == nil { 847 t.Errorf("Renameat: stat of renamed file %q unexpectedly succeeded", from) 848 } 849} 850 851func TestUtimesNanoAt(t *testing.T) { 852 defer chtmpdir(t)() 853 854 symlink := "symlink1" 855 os.Remove(symlink) 856 err := os.Symlink("nonexisting", symlink) 857 if err != nil { 858 t.Fatal(err) 859 } 860 861 // Some filesystems only support microsecond resolution. Account for 862 // that in Nsec. 863 ts := []unix.Timespec{ 864 {Sec: 1111, Nsec: 2000}, 865 {Sec: 3333, Nsec: 4000}, 866 } 867 err = unix.UtimesNanoAt(unix.AT_FDCWD, symlink, ts, unix.AT_SYMLINK_NOFOLLOW) 868 if err != nil { 869 t.Fatalf("UtimesNanoAt: %v", err) 870 } 871 872 var st unix.Stat_t 873 err = unix.Lstat(symlink, &st) 874 if err != nil { 875 t.Fatalf("Lstat: %v", err) 876 } 877 878 // Only check Mtim, Atim might not be supported by the underlying filesystem 879 expected := ts[1] 880 if st.Mtim.Nsec == 0 { 881 // Some filesystems only support 1-second time stamp resolution 882 // and will always set Nsec to 0. 883 expected.Nsec = 0 884 } 885 if st.Mtim != expected { 886 t.Errorf("UtimesNanoAt: wrong mtime: got %v, expected %v", st.Mtim, expected) 887 } 888} 889 890func TestSend(t *testing.T) { 891 ec := make(chan error, 2) 892 ts := []byte("HELLO GOPHER") 893 894 fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) 895 if err != nil { 896 t.Fatalf("Socketpair: %v", err) 897 } 898 defer unix.Close(fds[0]) 899 defer unix.Close(fds[1]) 900 901 go func() { 902 data := make([]byte, len(ts)) 903 904 _, _, err := unix.Recvfrom(fds[1], data, 0) 905 if err != nil { 906 ec <- err 907 } 908 if !bytes.Equal(ts, data) { 909 ec <- fmt.Errorf("data sent != data received. Received %q", data) 910 } 911 ec <- nil 912 }() 913 err = unix.Send(fds[0], ts, 0) 914 if err != nil { 915 ec <- err 916 } 917 918 select { 919 case err = <-ec: 920 if err != nil { 921 t.Fatalf("Send: %v", err) 922 } 923 case <-time.After(2 * time.Second): 924 t.Fatal("Send: nothing received after 2 seconds") 925 } 926} 927 928// mktmpfifo creates a temporary FIFO and provides a cleanup function. 929func mktmpfifo(t *testing.T) (*os.File, func()) { 930 err := unix.Mkfifo("fifo", 0666) 931 if err != nil { 932 t.Fatalf("mktmpfifo: failed to create FIFO: %v", err) 933 } 934 935 f, err := os.OpenFile("fifo", os.O_RDWR, 0666) 936 if err != nil { 937 os.Remove("fifo") 938 t.Fatalf("mktmpfifo: failed to open FIFO: %v", err) 939 } 940 941 return f, func() { 942 f.Close() 943 os.Remove("fifo") 944 } 945} 946 947// utilities taken from os/os_test.go 948 949func touch(t *testing.T, name string) { 950 f, err := os.Create(name) 951 if err != nil { 952 t.Fatal(err) 953 } 954 if err := f.Close(); err != nil { 955 t.Fatal(err) 956 } 957} 958 959// chtmpdir changes the working directory to a new temporary directory and 960// provides a cleanup function. Used when PWD is read-only. 961func chtmpdir(t *testing.T) func() { 962 oldwd, err := os.Getwd() 963 if err != nil { 964 t.Fatalf("chtmpdir: %v", err) 965 } 966 d, err := ioutil.TempDir("", "test") 967 if err != nil { 968 t.Fatalf("chtmpdir: %v", err) 969 } 970 if err := os.Chdir(d); err != nil { 971 t.Fatalf("chtmpdir: %v", err) 972 } 973 return func() { 974 if err := os.Chdir(oldwd); err != nil { 975 t.Fatalf("chtmpdir: %v", err) 976 } 977 os.RemoveAll(d) 978 } 979} 980