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