1// Copyright 2016 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 linux 6// +build linux 7 8package unix_test 9 10import ( 11 "bufio" 12 "bytes" 13 "errors" 14 "fmt" 15 "io/ioutil" 16 "net" 17 "os" 18 "path/filepath" 19 "runtime" 20 "runtime/debug" 21 "strconv" 22 "strings" 23 "testing" 24 "time" 25 "unsafe" 26 27 "golang.org/x/sys/unix" 28) 29 30func TestIoctlGetEthtoolDrvinfo(t *testing.T) { 31 if runtime.GOOS == "android" { 32 t.Skip("ethtool driver info is not available on android, skipping test") 33 } 34 35 s, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) 36 if err != nil { 37 t.Fatalf("failed to open socket: %v", err) 38 } 39 defer unix.Close(s) 40 41 ifis, err := net.Interfaces() 42 if err != nil { 43 t.Fatalf("failed to get network interfaces: %v", err) 44 } 45 46 // Print the interface name and associated driver information for each 47 // network interface supported by ethtool. 48 for _, ifi := range ifis { 49 drv, err := unix.IoctlGetEthtoolDrvinfo(s, ifi.Name) 50 if err != nil { 51 if err == unix.EOPNOTSUPP { 52 continue 53 } 54 55 t.Fatalf("failed to get ethtool driver info for %q: %v", ifi.Name, err) 56 } 57 58 // Trim trailing NULLs. 59 t.Logf("%s: %q", ifi.Name, string(bytes.TrimRight(drv.Driver[:], "\x00"))) 60 } 61} 62 63func TestIoctlGetInt(t *testing.T) { 64 f, err := os.Open("/dev/random") 65 if err != nil { 66 t.Fatalf("failed to open device: %v", err) 67 } 68 defer f.Close() 69 70 v, err := unix.IoctlGetInt(int(f.Fd()), unix.RNDGETENTCNT) 71 if err != nil { 72 t.Fatalf("failed to perform ioctl: %v", err) 73 } 74 75 t.Logf("%d bits of entropy available", v) 76} 77 78func TestIoctlRetInt(t *testing.T) { 79 f, err := os.Open("/proc/self/ns/mnt") 80 if err != nil { 81 t.Skipf("skipping test, %v", err) 82 } 83 defer f.Close() 84 85 v, err := unix.IoctlRetInt(int(f.Fd()), unix.NS_GET_NSTYPE) 86 if err != nil { 87 if err == unix.ENOTTY { 88 t.Skipf("old kernel? (need Linux >= 4.11)") 89 } 90 t.Fatalf("failed to perform ioctl: %v", err) 91 } 92 if v != unix.CLONE_NEWNS { 93 t.Fatalf("unexpected return from ioctl; expected %v, got %v", v, unix.CLONE_NEWNS) 94 } 95} 96 97func TestIoctlGetRTCTime(t *testing.T) { 98 f, err := os.Open("/dev/rtc0") 99 if err != nil { 100 t.Skipf("skipping test, %v", err) 101 } 102 defer f.Close() 103 104 v, err := unix.IoctlGetRTCTime(int(f.Fd())) 105 if err != nil { 106 t.Fatalf("failed to perform ioctl: %v", err) 107 } 108 109 t.Logf("RTC time: %04d-%02d-%02d %02d:%02d:%02d", v.Year+1900, v.Mon+1, v.Mday, v.Hour, v.Min, v.Sec) 110} 111 112func TestIoctlGetRTCWkAlrm(t *testing.T) { 113 f, err := os.Open("/dev/rtc0") 114 if err != nil { 115 t.Skipf("skipping test, %v", err) 116 } 117 defer f.Close() 118 119 v, err := unix.IoctlGetRTCWkAlrm(int(f.Fd())) 120 if err != nil { 121 t.Fatalf("failed to perform ioctl: %v", err) 122 } 123 124 t.Logf("RTC wake alarm enabled '%d'; time: %04d-%02d-%02d %02d:%02d:%02d", 125 v.Enabled, v.Time.Year+1900, v.Time.Mon+1, v.Time.Mday, v.Time.Hour, v.Time.Min, v.Time.Sec) 126} 127 128func TestIoctlIfreq(t *testing.T) { 129 s, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) 130 if err != nil { 131 t.Fatalf("failed to open socket: %v", err) 132 } 133 defer unix.Close(s) 134 135 ifis, err := net.Interfaces() 136 if err != nil { 137 t.Fatalf("failed to get network interfaces: %v", err) 138 } 139 140 // Compare the network interface fetched from rtnetlink with the data from 141 // the equivalent ioctl API. 142 for _, ifi := range ifis { 143 ifr, err := unix.NewIfreq(ifi.Name) 144 if err != nil { 145 t.Fatalf("failed to create ifreq for %q: %v", ifi.Name, err) 146 } 147 148 if err := unix.IoctlIfreq(s, unix.SIOCGIFINDEX, ifr); err != nil { 149 t.Fatalf("failed to get interface index for %q: %v", ifi.Name, err) 150 } 151 152 if want, got := ifi.Index, int(ifr.Uint32()); want != got { 153 t.Fatalf("unexpected interface index for %q: got: %d, want: %d", 154 ifi.Name, got, want) 155 } 156 157 if want, got := ifi.Name, ifr.Name(); want != got { 158 t.Fatalf("unexpected interface name for index %d: got: %q, want: %q", 159 ifi.Index, got, want) 160 } 161 162 wantIP, ok := firstIPv4(t, &ifi) 163 if err := unix.IoctlIfreq(s, unix.SIOCGIFADDR, ifr); err != nil { 164 // Interface may have no assigned IPv4 address. 165 if err != unix.EADDRNOTAVAIL { 166 t.Fatalf("failed to get IPv4 address for %q: %v", ifi.Name, err) 167 } 168 169 // But if we found an address via rtnetlink, we should expect the 170 // ioctl to return one. 171 if ok { 172 t.Fatalf("found IPv4 address %q for %q but ioctl returned none", wantIP, ifi.Name) 173 } 174 175 continue 176 } 177 178 // Found an address, compare it directly. 179 addr, err := ifr.Inet4Addr() 180 if err != nil { 181 t.Fatalf("failed to get ifreq IPv4 address: %v", err) 182 } 183 184 if want, got := wantIP, addr; !want.Equal(got) { 185 t.Fatalf("unexpected first IPv4 address for %q: got: %q, want: %q", 186 ifi.Name, got, want) 187 } 188 } 189} 190 191// firstIPv4 reports whether the interface has an IPv4 address assigned, 192// returning the first discovered address. 193func firstIPv4(t *testing.T, ifi *net.Interface) (net.IP, bool) { 194 t.Helper() 195 196 addrs, err := ifi.Addrs() 197 if err != nil { 198 t.Fatalf("failed to get interface %q addresses: %v", ifi.Name, err) 199 } 200 201 for _, a := range addrs { 202 // Only want valid IPv4 addresses. 203 ipn, ok := a.(*net.IPNet) 204 if !ok || ipn.IP.To4() == nil { 205 continue 206 } 207 208 return ipn.IP, true 209 } 210 211 return nil, false 212} 213 214func TestPpoll(t *testing.T) { 215 if runtime.GOOS == "android" { 216 t.Skip("mkfifo syscall is not available on android, skipping test") 217 } 218 219 defer chtmpdir(t)() 220 f, cleanup := mktmpfifo(t) 221 defer cleanup() 222 223 const timeout = 100 * time.Millisecond 224 225 ok := make(chan bool, 1) 226 go func() { 227 select { 228 case <-time.After(10 * timeout): 229 t.Errorf("Ppoll: failed to timeout after %d", 10*timeout) 230 case <-ok: 231 } 232 }() 233 234 fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}} 235 timeoutTs := unix.NsecToTimespec(int64(timeout)) 236 n, err := unix.Ppoll(fds, &timeoutTs, nil) 237 ok <- true 238 if err != nil { 239 t.Errorf("Ppoll: unexpected error: %v", err) 240 return 241 } 242 if n != 0 { 243 t.Errorf("Ppoll: wrong number of events: got %v, expected %v", n, 0) 244 return 245 } 246} 247 248func TestTime(t *testing.T) { 249 var ut unix.Time_t 250 ut2, err := unix.Time(&ut) 251 if err != nil { 252 t.Fatalf("Time: %v", err) 253 } 254 if ut != ut2 { 255 t.Errorf("Time: return value %v should be equal to argument %v", ut2, ut) 256 } 257 258 var now time.Time 259 260 for i := 0; i < 10; i++ { 261 ut, err = unix.Time(nil) 262 if err != nil { 263 t.Fatalf("Time: %v", err) 264 } 265 266 now = time.Now() 267 diff := int64(ut) - now.Unix() 268 if -1 <= diff && diff <= 1 { 269 return 270 } 271 } 272 273 t.Errorf("Time: return value %v should be nearly equal to time.Now().Unix() %v±1", ut, now.Unix()) 274} 275 276func TestUtime(t *testing.T) { 277 defer chtmpdir(t)() 278 279 touch(t, "file1") 280 281 buf := &unix.Utimbuf{ 282 Modtime: 12345, 283 } 284 285 err := unix.Utime("file1", buf) 286 if err != nil { 287 t.Fatalf("Utime: %v", err) 288 } 289 290 fi, err := os.Stat("file1") 291 if err != nil { 292 t.Fatal(err) 293 } 294 295 if fi.ModTime().Unix() != 12345 { 296 t.Errorf("Utime: failed to change modtime: expected %v, got %v", 12345, fi.ModTime().Unix()) 297 } 298} 299 300func TestRlimitAs(t *testing.T) { 301 // disable GC during to avoid flaky test 302 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 303 304 var rlim unix.Rlimit 305 err := unix.Getrlimit(unix.RLIMIT_AS, &rlim) 306 if err != nil { 307 t.Fatalf("Getrlimit: %v", err) 308 } 309 var zero unix.Rlimit 310 if zero == rlim { 311 t.Fatalf("Getrlimit: got zero value %#v", rlim) 312 } 313 set := rlim 314 set.Cur = uint64(unix.Getpagesize()) 315 err = unix.Setrlimit(unix.RLIMIT_AS, &set) 316 if err != nil { 317 t.Fatalf("Setrlimit: set failed: %#v %v", set, err) 318 } 319 320 // RLIMIT_AS was set to the page size, so mmap()'ing twice the page size 321 // should fail. See 'man 2 getrlimit'. 322 _, err = unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE) 323 if err == nil { 324 t.Fatal("Mmap: unexpectedly succeeded after setting RLIMIT_AS") 325 } 326 327 err = unix.Setrlimit(unix.RLIMIT_AS, &rlim) 328 if err != nil { 329 t.Fatalf("Setrlimit: restore failed: %#v %v", rlim, err) 330 } 331 332 b, err := unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE) 333 if err != nil { 334 t.Fatalf("Mmap: %v", err) 335 } 336 err = unix.Munmap(b) 337 if err != nil { 338 t.Fatalf("Munmap: %v", err) 339 } 340} 341 342func TestPselect(t *testing.T) { 343 for { 344 n, err := unix.Pselect(0, nil, nil, nil, &unix.Timespec{Sec: 0, Nsec: 0}, nil) 345 if err == unix.EINTR { 346 t.Logf("Pselect interrupted") 347 continue 348 } else if err != nil { 349 t.Fatalf("Pselect: %v", err) 350 } 351 if n != 0 { 352 t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n) 353 } 354 break 355 } 356 357 dur := 2500 * time.Microsecond 358 var took time.Duration 359 for { 360 // On some platforms (e.g. Linux), the passed-in timespec is 361 // updated by pselect(2). Make sure to reset to the full 362 // duration in case of an EINTR. 363 ts := unix.NsecToTimespec(int64(dur)) 364 start := time.Now() 365 n, err := unix.Pselect(0, nil, nil, nil, &ts, nil) 366 took = time.Since(start) 367 if err == unix.EINTR { 368 t.Logf("Pselect interrupted after %v", took) 369 continue 370 } else if err != nil { 371 t.Fatalf("Pselect: %v", err) 372 } 373 if n != 0 { 374 t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n) 375 } 376 break 377 } 378 379 // On some builder the actual timeout might also be slightly less than the requested. 380 // Add an acceptable margin to avoid flaky tests. 381 if took < dur*2/3 { 382 t.Errorf("Pselect: got %v timeout, expected at least %v", took, dur) 383 } 384} 385 386func TestSchedSetaffinity(t *testing.T) { 387 var newMask unix.CPUSet 388 newMask.Zero() 389 if newMask.Count() != 0 { 390 t.Errorf("CpuZero: didn't zero CPU set: %v", newMask) 391 } 392 cpu := 1 393 newMask.Set(cpu) 394 if newMask.Count() != 1 || !newMask.IsSet(cpu) { 395 t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask) 396 } 397 cpu = 5 398 newMask.Set(cpu) 399 if newMask.Count() != 2 || !newMask.IsSet(cpu) { 400 t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask) 401 } 402 newMask.Clear(cpu) 403 if newMask.Count() != 1 || newMask.IsSet(cpu) { 404 t.Errorf("CpuClr: didn't clear CPU %d in set: %v", cpu, newMask) 405 } 406 407 runtime.LockOSThread() 408 defer runtime.UnlockOSThread() 409 410 var oldMask unix.CPUSet 411 err := unix.SchedGetaffinity(0, &oldMask) 412 if err != nil { 413 t.Fatalf("SchedGetaffinity: %v", err) 414 } 415 416 if runtime.NumCPU() < 2 { 417 t.Skip("skipping setaffinity tests on single CPU system") 418 } 419 if runtime.GOOS == "android" { 420 t.Skip("skipping setaffinity tests on android") 421 } 422 423 // On a system like ppc64x where some cores can be disabled using ppc64_cpu, 424 // setaffinity should only be called with enabled cores. The valid cores 425 // are found from the oldMask, but if none are found then the setaffinity 426 // tests are skipped. Issue #27875. 427 cpu = 1 428 if !oldMask.IsSet(cpu) { 429 newMask.Zero() 430 for i := 0; i < len(oldMask); i++ { 431 if oldMask.IsSet(i) { 432 newMask.Set(i) 433 break 434 } 435 } 436 if newMask.Count() == 0 { 437 t.Skip("skipping setaffinity tests if CPU not available") 438 } 439 } 440 441 err = unix.SchedSetaffinity(0, &newMask) 442 if err != nil { 443 t.Fatalf("SchedSetaffinity: %v", err) 444 } 445 446 var gotMask unix.CPUSet 447 err = unix.SchedGetaffinity(0, &gotMask) 448 if err != nil { 449 t.Fatalf("SchedGetaffinity: %v", err) 450 } 451 452 if gotMask != newMask { 453 t.Errorf("SchedSetaffinity: returned affinity mask does not match set affinity mask") 454 } 455 456 // Restore old mask so it doesn't affect successive tests 457 err = unix.SchedSetaffinity(0, &oldMask) 458 if err != nil { 459 t.Fatalf("SchedSetaffinity: %v", err) 460 } 461} 462 463func TestStatx(t *testing.T) { 464 var stx unix.Statx_t 465 err := unix.Statx(unix.AT_FDCWD, ".", 0, 0, &stx) 466 if err == unix.ENOSYS || err == unix.EPERM { 467 t.Skip("statx syscall is not available, skipping test") 468 } else if err != nil { 469 t.Fatalf("Statx: %v", err) 470 } 471 472 defer chtmpdir(t)() 473 touch(t, "file1") 474 475 var st unix.Stat_t 476 err = unix.Stat("file1", &st) 477 if err != nil { 478 t.Fatalf("Stat: %v", err) 479 } 480 481 flags := unix.AT_STATX_SYNC_AS_STAT 482 err = unix.Statx(unix.AT_FDCWD, "file1", flags, unix.STATX_ALL, &stx) 483 if err != nil { 484 t.Fatalf("Statx: %v", err) 485 } 486 487 if uint32(stx.Mode) != st.Mode { 488 t.Errorf("Statx: returned stat mode does not match Stat") 489 } 490 491 ctime := unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)} 492 mtime := unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)} 493 494 if stx.Ctime != ctime { 495 t.Errorf("Statx: returned stat ctime does not match Stat") 496 } 497 if stx.Mtime != mtime { 498 t.Errorf("Statx: returned stat mtime does not match Stat") 499 } 500 501 err = os.Symlink("file1", "symlink1") 502 if err != nil { 503 t.Fatal(err) 504 } 505 506 err = unix.Lstat("symlink1", &st) 507 if err != nil { 508 t.Fatalf("Lstat: %v", err) 509 } 510 511 err = unix.Statx(unix.AT_FDCWD, "symlink1", flags, unix.STATX_BASIC_STATS, &stx) 512 if err != nil { 513 t.Fatalf("Statx: %v", err) 514 } 515 516 // follow symlink, expect a regulat file 517 if stx.Mode&unix.S_IFREG == 0 { 518 t.Errorf("Statx: didn't follow symlink") 519 } 520 521 err = unix.Statx(unix.AT_FDCWD, "symlink1", flags|unix.AT_SYMLINK_NOFOLLOW, unix.STATX_ALL, &stx) 522 if err != nil { 523 t.Fatalf("Statx: %v", err) 524 } 525 526 // follow symlink, expect a symlink 527 if stx.Mode&unix.S_IFLNK == 0 { 528 t.Errorf("Statx: unexpectedly followed symlink") 529 } 530 if uint32(stx.Mode) != st.Mode { 531 t.Errorf("Statx: returned stat mode does not match Lstat") 532 } 533 534 ctime = unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)} 535 mtime = unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)} 536 537 if stx.Ctime != ctime { 538 t.Errorf("Statx: returned stat ctime does not match Lstat") 539 } 540 if stx.Mtime != mtime { 541 t.Errorf("Statx: returned stat mtime does not match Lstat") 542 } 543} 544 545// stringsFromByteSlice converts a sequence of attributes to a []string. 546// On Linux, each entry is a NULL-terminated string. 547func stringsFromByteSlice(buf []byte) []string { 548 var result []string 549 off := 0 550 for i, b := range buf { 551 if b == 0 { 552 result = append(result, string(buf[off:i])) 553 off = i + 1 554 } 555 } 556 return result 557} 558 559func TestFaccessat(t *testing.T) { 560 defer chtmpdir(t)() 561 touch(t, "file1") 562 563 err := unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, 0) 564 if err != nil { 565 t.Errorf("Faccessat: unexpected error: %v", err) 566 } 567 568 err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, 2) 569 if err != unix.EINVAL { 570 t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err) 571 } 572 573 err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, unix.AT_EACCESS) 574 if err != nil { 575 t.Errorf("Faccessat: unexpected error: %v", err) 576 } 577 578 err = os.Symlink("file1", "symlink1") 579 if err != nil { 580 t.Fatal(err) 581 } 582 583 err = unix.Faccessat(unix.AT_FDCWD, "symlink1", unix.R_OK, unix.AT_SYMLINK_NOFOLLOW) 584 if err != nil { 585 t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err) 586 } 587 588 // We can't really test AT_SYMLINK_NOFOLLOW, because there 589 // doesn't seem to be any way to change the mode of a symlink. 590 // We don't test AT_EACCESS because such tests are only 591 // meaningful if run as root. 592 593 err = unix.Fchmodat(unix.AT_FDCWD, "file1", 0, 0) 594 if err != nil { 595 t.Errorf("Fchmodat: unexpected error %v", err) 596 } 597 598 err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.F_OK, unix.AT_SYMLINK_NOFOLLOW) 599 if err != nil { 600 t.Errorf("Faccessat: unexpected error: %v", err) 601 } 602 603 err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, unix.AT_SYMLINK_NOFOLLOW) 604 if err != unix.EACCES { 605 if unix.Getuid() != 0 { 606 t.Errorf("Faccessat: unexpected error: %v, want EACCES", err) 607 } 608 } 609} 610 611func TestSyncFileRange(t *testing.T) { 612 file, err := ioutil.TempFile("", "TestSyncFileRange") 613 if err != nil { 614 t.Fatal(err) 615 } 616 defer os.Remove(file.Name()) 617 defer file.Close() 618 619 err = unix.SyncFileRange(int(file.Fd()), 0, 0, 0) 620 if err == unix.ENOSYS || err == unix.EPERM { 621 t.Skip("sync_file_range syscall is not available, skipping test") 622 } else if err != nil { 623 t.Fatalf("SyncFileRange: %v", err) 624 } 625 626 // invalid flags 627 flags := 0xf00 628 err = unix.SyncFileRange(int(file.Fd()), 0, 0, flags) 629 if err != unix.EINVAL { 630 t.Fatalf("SyncFileRange: unexpected error: %v, want EINVAL", err) 631 } 632} 633 634func TestClockNanosleep(t *testing.T) { 635 delay := 50 * time.Millisecond 636 637 // Relative timespec. 638 start := time.Now() 639 rel := unix.NsecToTimespec(delay.Nanoseconds()) 640 remain := unix.Timespec{} 641 for { 642 err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain) 643 if err == unix.ENOSYS || err == unix.EPERM { 644 t.Skip("clock_nanosleep syscall is not available, skipping test") 645 } else if err == unix.EINTR { 646 t.Logf("ClockNanosleep interrupted after %v", time.Since(start)) 647 rel = remain 648 continue 649 } else if err != nil { 650 t.Errorf("ClockNanosleep(CLOCK_MONOTONIC, 0, %#v, nil) = %v", &rel, err) 651 } else if slept := time.Since(start); slept < delay { 652 t.Errorf("ClockNanosleep(CLOCK_MONOTONIC, 0, %#v, nil) slept only %v", &rel, slept) 653 } 654 break 655 } 656 657 // Absolute timespec. 658 for { 659 start = time.Now() 660 until := start.Add(delay) 661 abs := unix.NsecToTimespec(until.UnixNano()) 662 err := unix.ClockNanosleep(unix.CLOCK_REALTIME, unix.TIMER_ABSTIME, &abs, nil) 663 if err == unix.EINTR { 664 t.Logf("ClockNanosleep interrupted after %v", time.Since(start)) 665 continue 666 } else if err != nil { 667 t.Errorf("ClockNanosleep(CLOCK_REALTIME, TIMER_ABSTIME, %#v (=%v), nil) = %v", &abs, until, err) 668 } else if slept := time.Since(start); slept < delay { 669 t.Errorf("ClockNanosleep(CLOCK_REALTIME, TIMER_ABSTIME, %#v (=%v), nil) slept only %v", &abs, until, slept) 670 } 671 break 672 } 673 674 // Invalid clock. clock_nanosleep(2) says EINVAL, but it’s actually EOPNOTSUPP. 675 err := unix.ClockNanosleep(unix.CLOCK_THREAD_CPUTIME_ID, 0, &rel, nil) 676 if err != unix.EINVAL && err != unix.EOPNOTSUPP { 677 t.Errorf("ClockNanosleep(CLOCK_THREAD_CPUTIME_ID, 0, %#v, nil) = %v, want EINVAL or EOPNOTSUPP", &rel, err) 678 } 679} 680 681func TestOpenByHandleAt(t *testing.T) { 682 skipIfNotSupported := func(t *testing.T, name string, err error) { 683 if err == unix.EPERM { 684 t.Skipf("skipping %s test without CAP_DAC_READ_SEARCH", name) 685 } 686 if err == unix.ENOSYS { 687 t.Skipf("%s system call not available", name) 688 } 689 if err == unix.EOPNOTSUPP { 690 t.Skipf("%s not supported on this filesystem", name) 691 } 692 } 693 694 h, mountID, err := unix.NameToHandleAt(unix.AT_FDCWD, "syscall_linux_test.go", 0) 695 if err != nil { 696 skipIfNotSupported(t, "name_to_handle_at", err) 697 t.Fatalf("NameToHandleAt: %v", err) 698 } 699 t.Logf("mountID: %v, handle: size=%d, type=%d, bytes=%q", mountID, 700 h.Size(), h.Type(), h.Bytes()) 701 mount, err := openMountByID(mountID) 702 if err != nil { 703 t.Fatalf("openMountByID: %v", err) 704 } 705 defer mount.Close() 706 707 for _, clone := range []bool{false, true} { 708 t.Run("clone="+strconv.FormatBool(clone), func(t *testing.T) { 709 if clone { 710 h = unix.NewFileHandle(h.Type(), h.Bytes()) 711 } 712 fd, err := unix.OpenByHandleAt(int(mount.Fd()), h, unix.O_RDONLY) 713 skipIfNotSupported(t, "open_by_handle_at", err) 714 if err != nil { 715 t.Fatalf("OpenByHandleAt: %v", err) 716 } 717 defer unix.Close(fd) 718 719 t.Logf("opened fd %v", fd) 720 f := os.NewFile(uintptr(fd), "") 721 slurp, err := ioutil.ReadAll(f) 722 if err != nil { 723 t.Fatal(err) 724 } 725 const substr = "Some substring for a test." 726 if !strings.Contains(string(slurp), substr) { 727 t.Errorf("didn't find substring %q in opened file; read %d bytes", substr, len(slurp)) 728 } 729 }) 730 } 731} 732 733func openMountByID(mountID int) (f *os.File, err error) { 734 mi, err := os.Open("/proc/self/mountinfo") 735 if err != nil { 736 return nil, err 737 } 738 defer mi.Close() 739 bs := bufio.NewScanner(mi) 740 wantPrefix := []byte(fmt.Sprintf("%v ", mountID)) 741 for bs.Scan() { 742 if !bytes.HasPrefix(bs.Bytes(), wantPrefix) { 743 continue 744 } 745 fields := strings.Fields(bs.Text()) 746 dev := fields[4] 747 return os.Open(dev) 748 } 749 if err := bs.Err(); err != nil { 750 return nil, err 751 } 752 return nil, errors.New("mountID not found") 753} 754 755func TestEpoll(t *testing.T) { 756 efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) 757 if err != nil { 758 t.Fatalf("EpollCreate1: %v", err) 759 } 760 defer unix.Close(efd) 761 762 r, w, err := os.Pipe() 763 if err != nil { 764 t.Fatal(err) 765 } 766 defer r.Close() 767 defer w.Close() 768 769 fd := int(r.Fd()) 770 ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)} 771 772 err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev) 773 if err != nil { 774 t.Fatalf("EpollCtl: %v", err) 775 } 776 777 if _, err := w.Write([]byte("HELLO GOPHER")); err != nil { 778 t.Fatal(err) 779 } 780 781 events := make([]unix.EpollEvent, 128) 782 n, err := unix.EpollWait(efd, events, 1) 783 if err != nil { 784 t.Fatalf("EpollWait: %v", err) 785 } 786 787 if n != 1 { 788 t.Errorf("EpollWait: wrong number of events: got %v, expected 1", n) 789 } 790 791 got := int(events[0].Fd) 792 if got != fd { 793 t.Errorf("EpollWait: wrong Fd in event: got %v, expected %v", got, fd) 794 } 795} 796 797func TestPrctlRetInt(t *testing.T) { 798 err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) 799 if err != nil { 800 t.Skipf("Prctl: %v, skipping test", err) 801 } 802 v, err := unix.PrctlRetInt(unix.PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) 803 if err != nil { 804 t.Fatalf("failed to perform prctl: %v", err) 805 } 806 if v != 1 { 807 t.Fatalf("unexpected return from prctl; got %v, expected %v", v, 1) 808 } 809} 810 811func TestTimerfd(t *testing.T) { 812 var now unix.Timespec 813 if err := unix.ClockGettime(unix.CLOCK_REALTIME, &now); err != nil { 814 t.Fatalf("ClockGettime: %v", err) 815 } 816 817 tfd, err := unix.TimerfdCreate(unix.CLOCK_REALTIME, 0) 818 if err == unix.ENOSYS { 819 t.Skip("timerfd_create system call not implemented") 820 } else if err != nil { 821 t.Fatalf("TimerfdCreate: %v", err) 822 } 823 defer unix.Close(tfd) 824 825 var timeSpec unix.ItimerSpec 826 if err := unix.TimerfdGettime(tfd, &timeSpec); err != nil { 827 t.Fatalf("TimerfdGettime: %v", err) 828 } 829 830 if timeSpec.Value.Nsec != 0 || timeSpec.Value.Sec != 0 { 831 t.Fatalf("TimerfdGettime: timer is already set, but shouldn't be") 832 } 833 834 timeSpec = unix.ItimerSpec{ 835 Interval: unix.NsecToTimespec(int64(time.Millisecond)), 836 Value: now, 837 } 838 839 if err := unix.TimerfdSettime(tfd, unix.TFD_TIMER_ABSTIME, &timeSpec, nil); err != nil { 840 t.Fatalf("TimerfdSettime: %v", err) 841 } 842 843 const totalTicks = 10 844 const bufferLength = 8 845 846 buffer := make([]byte, bufferLength) 847 848 var count uint64 = 0 849 for count < totalTicks { 850 n, err := unix.Read(tfd, buffer) 851 if err != nil { 852 t.Fatalf("Timerfd: %v", err) 853 } else if n != bufferLength { 854 t.Fatalf("Timerfd: got %d bytes from timerfd, expected %d bytes", n, bufferLength) 855 } 856 857 count += *(*uint64)(unsafe.Pointer(&buffer)) 858 } 859} 860 861func TestOpenat2(t *testing.T) { 862 how := &unix.OpenHow{ 863 Flags: unix.O_RDONLY, 864 } 865 fd, err := unix.Openat2(unix.AT_FDCWD, ".", how) 866 if err != nil { 867 if err == unix.ENOSYS || err == unix.EPERM { 868 t.Skipf("openat2: %v (old kernel? need Linux >= 5.6)", err) 869 } 870 t.Fatalf("openat2: %v", err) 871 } 872 if err := unix.Close(fd); err != nil { 873 t.Fatalf("close: %v", err) 874 } 875 876 // prepare 877 tempDir, err := ioutil.TempDir("", t.Name()) 878 if err != nil { 879 t.Fatal(err) 880 } 881 defer os.RemoveAll(tempDir) 882 883 subdir := filepath.Join(tempDir, "dir") 884 if err := os.Mkdir(subdir, 0755); err != nil { 885 t.Fatal(err) 886 } 887 symlink := filepath.Join(subdir, "symlink") 888 if err := os.Symlink("../", symlink); err != nil { 889 t.Fatal(err) 890 } 891 892 dirfd, err := unix.Open(subdir, unix.O_RDONLY, 0) 893 if err != nil { 894 t.Fatalf("open(%q): %v", subdir, err) 895 } 896 defer unix.Close(dirfd) 897 898 // openat2 with no extra flags -- should succeed 899 fd, err = unix.Openat2(dirfd, "symlink", how) 900 if err != nil { 901 t.Errorf("Openat2 should succeed, got %v", err) 902 } 903 if err := unix.Close(fd); err != nil { 904 t.Fatalf("close: %v", err) 905 } 906 907 // open with RESOLVE_BENEATH, should result in EXDEV 908 how.Resolve = unix.RESOLVE_BENEATH 909 fd, err = unix.Openat2(dirfd, "symlink", how) 910 if err == nil { 911 if err := unix.Close(fd); err != nil { 912 t.Fatalf("close: %v", err) 913 } 914 } 915 if err != unix.EXDEV { 916 t.Errorf("Openat2 should fail with EXDEV, got %v", err) 917 } 918} 919 920func TestIoctlFileDedupeRange(t *testing.T) { 921 f1, err := ioutil.TempFile("", t.Name()) 922 if err != nil { 923 t.Fatal(err) 924 } 925 defer f1.Close() 926 defer os.Remove(f1.Name()) 927 928 // Test deduplication with two blocks of zeros 929 data := make([]byte, 4096) 930 931 for i := 0; i < 2; i += 1 { 932 _, err = f1.Write(data) 933 if err != nil { 934 t.Fatal(err) 935 } 936 } 937 938 f2, err := ioutil.TempFile("", t.Name()) 939 if err != nil { 940 t.Fatal(err) 941 } 942 defer f2.Close() 943 defer os.Remove(f2.Name()) 944 945 for i := 0; i < 2; i += 1 { 946 // Make the 2nd block different 947 if i == 1 { 948 data[1] = 1 949 } 950 951 _, err = f2.Write(data) 952 if err != nil { 953 t.Fatal(err) 954 } 955 } 956 957 dedupe := unix.FileDedupeRange{ 958 Src_offset: uint64(0), 959 Src_length: uint64(4096), 960 Info: []unix.FileDedupeRangeInfo{ 961 unix.FileDedupeRangeInfo{ 962 Dest_fd: int64(f2.Fd()), 963 Dest_offset: uint64(0), 964 }, 965 unix.FileDedupeRangeInfo{ 966 Dest_fd: int64(f2.Fd()), 967 Dest_offset: uint64(4096), 968 }, 969 }} 970 971 err = unix.IoctlFileDedupeRange(int(f1.Fd()), &dedupe) 972 if err == unix.EOPNOTSUPP || err == unix.EINVAL || err == unix.ENOTTY { 973 t.Skip("deduplication not supported on this filesystem") 974 } else if err != nil { 975 t.Fatal(err) 976 } 977 978 // The first Info should be equal 979 if dedupe.Info[0].Status < 0 { 980 errno := unix.Errno(-dedupe.Info[0].Status) 981 if errno == unix.EINVAL { 982 t.Skip("deduplication not supported on this filesystem") 983 } 984 t.Errorf("Unexpected error in FileDedupeRange: %s", unix.ErrnoName(errno)) 985 } else if dedupe.Info[0].Status == unix.FILE_DEDUPE_RANGE_DIFFERS { 986 t.Errorf("Unexpected different bytes in FileDedupeRange") 987 } 988 if dedupe.Info[0].Bytes_deduped != 4096 { 989 t.Errorf("Unexpected amount of bytes deduped %v != %v", 990 dedupe.Info[0].Bytes_deduped, 4096) 991 } 992 993 // The second Info should be different 994 if dedupe.Info[1].Status < 0 { 995 errno := unix.Errno(-dedupe.Info[1].Status) 996 if errno == unix.EINVAL { 997 t.Skip("deduplication not supported on this filesystem") 998 } 999 t.Errorf("Unexpected error in FileDedupeRange: %s", unix.ErrnoName(errno)) 1000 } else if dedupe.Info[1].Status == unix.FILE_DEDUPE_RANGE_SAME { 1001 t.Errorf("Unexpected equal bytes in FileDedupeRange") 1002 } 1003 if dedupe.Info[1].Bytes_deduped != 0 { 1004 t.Errorf("Unexpected amount of bytes deduped %v != %v", 1005 dedupe.Info[1].Bytes_deduped, 0) 1006 } 1007} 1008