aboutsummaryrefslogtreecommitdiff
path: root/src/internal/poll
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/poll')
-rw-r--r--src/internal/poll/copy_file_range_linux.go55
-rw-r--r--src/internal/poll/error_test.go3
-rw-r--r--src/internal/poll/fd_fsync_darwin.go7
-rw-r--r--src/internal/poll/fd_posix.go11
-rw-r--r--src/internal/poll/fd_unix.go18
-rw-r--r--src/internal/poll/fd_windows.go27
-rw-r--r--src/internal/poll/fd_writev_illumos.go16
-rw-r--r--src/internal/poll/hook_cloexec.go2
-rw-r--r--src/internal/poll/iovec_illumos.go16
-rw-r--r--src/internal/poll/iovec_unix.go13
-rw-r--r--src/internal/poll/read_test.go5
-rw-r--r--src/internal/poll/sock_cloexec.go2
-rw-r--r--src/internal/poll/sys_cloexec.go2
-rw-r--r--src/internal/poll/writev.go4
14 files changed, 155 insertions, 26 deletions
diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go
index 09de299ff7..fc34aef4cb 100644
--- a/src/internal/poll/copy_file_range_linux.go
+++ b/src/internal/poll/copy_file_range_linux.go
@@ -10,15 +10,61 @@ import (
"syscall"
)
-var copyFileRangeSupported int32 = 1 // accessed atomically
+var copyFileRangeSupported int32 = -1 // accessed atomically
const maxCopyFileRangeRound = 1 << 30
+func kernelVersion() (major int, minor int) {
+ var uname syscall.Utsname
+ if err := syscall.Uname(&uname); err != nil {
+ return
+ }
+
+ rl := uname.Release
+ var values [2]int
+ vi := 0
+ value := 0
+ for _, c := range rl {
+ if '0' <= c && c <= '9' {
+ value = (value * 10) + int(c-'0')
+ } else {
+ // Note that we're assuming N.N.N here. If we see anything else we are likely to
+ // mis-parse it.
+ values[vi] = value
+ vi++
+ if vi >= len(values) {
+ break
+ }
+ value = 0
+ }
+ }
+ switch vi {
+ case 0:
+ return 0, 0
+ case 1:
+ return values[0], 0
+ case 2:
+ return values[0], values[1]
+ }
+ return
+}
+
// CopyFileRange copies at most remain bytes of data from src to dst, using
// the copy_file_range system call. dst and src must refer to regular files.
func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) {
- if atomic.LoadInt32(&copyFileRangeSupported) == 0 {
+ if supported := atomic.LoadInt32(&copyFileRangeSupported); supported == 0 {
return 0, false, nil
+ } else if supported == -1 {
+ major, minor := kernelVersion()
+ if major > 5 || (major == 5 && minor >= 3) {
+ atomic.StoreInt32(&copyFileRangeSupported, 1)
+ } else {
+ // copy_file_range(2) is broken in various ways on kernels older than 5.3,
+ // see issue #42400 and
+ // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS
+ atomic.StoreInt32(&copyFileRangeSupported, 0)
+ return 0, false, nil
+ }
}
for remain > 0 {
max := remain
@@ -41,7 +87,7 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err
// use copy_file_range(2) again.
atomic.StoreInt32(&copyFileRangeSupported, 0)
return 0, false, nil
- case syscall.EXDEV, syscall.EINVAL, syscall.EOPNOTSUPP, syscall.EPERM:
+ case syscall.EXDEV, syscall.EINVAL, syscall.EIO, syscall.EOPNOTSUPP, syscall.EPERM:
// Prior to Linux 5.3, it was not possible to
// copy_file_range across file systems. Similarly to
// the ENOSYS case above, if we see EXDEV, we have
@@ -53,6 +99,9 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err
// file. This is another case where no data has been
// transfered, so we consider it unhandled.
//
+ // If src and dst are on CIFS, we can see EIO.
+ // See issue #42334.
+ //
// If the file is on NFS, we can see EOPNOTSUPP.
// See issue #40731.
//
diff --git a/src/internal/poll/error_test.go b/src/internal/poll/error_test.go
index 06b96f635a..abc8b1684f 100644
--- a/src/internal/poll/error_test.go
+++ b/src/internal/poll/error_test.go
@@ -6,6 +6,7 @@ package poll_test
import (
"fmt"
+ "io/fs"
"net"
"os"
"testing"
@@ -37,7 +38,7 @@ func parseReadError(nestedErr error, verify func(error) (string, bool)) error {
if nerr, ok := err.(*net.OpError); ok {
err = nerr.Err
}
- if nerr, ok := err.(*os.PathError); ok {
+ if nerr, ok := err.(*fs.PathError); ok {
err = nerr.Err
}
if nerr, ok := err.(*os.SyscallError); ok {
diff --git a/src/internal/poll/fd_fsync_darwin.go b/src/internal/poll/fd_fsync_darwin.go
index 91751496a4..48e7596922 100644
--- a/src/internal/poll/fd_fsync_darwin.go
+++ b/src/internal/poll/fd_fsync_darwin.go
@@ -14,7 +14,8 @@ func (fd *FD) Fsync() error {
return err
}
defer fd.decref()
-
- _, e1 := fcntl(fd.Sysfd, syscall.F_FULLFSYNC, 0)
- return e1
+ return ignoringEINTR(func() error {
+ _, err := fcntl(fd.Sysfd, syscall.F_FULLFSYNC, 0)
+ return err
+ })
}
diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go
index e5fb05c9c2..4edfa953a4 100644
--- a/src/internal/poll/fd_posix.go
+++ b/src/internal/poll/fd_posix.go
@@ -29,17 +29,6 @@ func (fd *FD) Shutdown(how int) error {
return syscall.Shutdown(fd.Sysfd, how)
}
-// Fchmod wraps syscall.Fchmod.
-func (fd *FD) Fchmod(mode uint32) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- return ignoringEINTR(func() error {
- return syscall.Fchmod(fd.Sysfd, mode)
- })
-}
-
// Fchown wraps syscall.Fchown.
func (fd *FD) Fchown(uid, gid int) error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go
index 1d5101eac3..2e77e76c87 100644
--- a/src/internal/poll/fd_unix.go
+++ b/src/internal/poll/fd_unix.go
@@ -74,7 +74,14 @@ func (fd *FD) destroy() error {
// Poller may want to unregister fd in readiness notification mechanism,
// so this must be executed before CloseFunc.
fd.pd.close()
+
+ // We don't use ignoringEINTR here because POSIX does not define
+ // whether the descriptor is closed if close returns EINTR.
+ // If the descriptor is indeed closed, using a loop would race
+ // with some other goroutine opening a new descriptor.
+ // (The Linux kernel guarantees that it is closed on an EINTR error.)
err := CloseFunc(fd.Sysfd)
+
fd.Sysfd = -1
runtime_Semrelease(&fd.csema)
return err
@@ -437,6 +444,17 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) {
}
}
+// Fchmod wraps syscall.Fchmod.
+func (fd *FD) Fchmod(mode uint32) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ return ignoringEINTR(func() error {
+ return syscall.Fchmod(fd.Sysfd, mode)
+ })
+}
+
// Fchdir wraps syscall.Fchdir.
func (fd *FD) Fchdir() error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go
index e1ef6199b3..d8c834f929 100644
--- a/src/internal/poll/fd_windows.go
+++ b/src/internal/poll/fd_windows.go
@@ -886,6 +886,33 @@ func (fd *FD) FindNextFile(data *syscall.Win32finddata) error {
return syscall.FindNextFile(fd.Sysfd, data)
}
+// Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed.
+func (fd *FD) Fchmod(mode uint32) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ var d syscall.ByHandleFileInformation
+ if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil {
+ return err
+ }
+ attrs := d.FileAttributes
+ if mode&syscall.S_IWRITE != 0 {
+ attrs &^= syscall.FILE_ATTRIBUTE_READONLY
+ } else {
+ attrs |= syscall.FILE_ATTRIBUTE_READONLY
+ }
+ if attrs == d.FileAttributes {
+ return nil
+ }
+
+ var du windows.FILE_BASIC_INFO
+ du.FileAttributes = attrs
+ l := uint32(unsafe.Sizeof(d))
+ return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, uintptr(unsafe.Pointer(&du)), l)
+}
+
// Fchdir wraps syscall.Fchdir.
func (fd *FD) Fchdir() error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/poll/fd_writev_illumos.go b/src/internal/poll/fd_writev_illumos.go
new file mode 100644
index 0000000000..1fa47ab1a3
--- /dev/null
+++ b/src/internal/poll/fd_writev_illumos.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package poll
+
+import (
+ "internal/syscall/unix"
+ "syscall"
+)
+
+func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) {
+ return unix.Writev(fd, iovecs)
+}
diff --git a/src/internal/poll/hook_cloexec.go b/src/internal/poll/hook_cloexec.go
index 5c93bdaf6c..5fd5449bb0 100644
--- a/src/internal/poll/hook_cloexec.go
+++ b/src/internal/poll/hook_cloexec.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build dragonfly freebsd linux netbsd openbsd
+// +build dragonfly freebsd illumos linux netbsd openbsd
package poll
diff --git a/src/internal/poll/iovec_illumos.go b/src/internal/poll/iovec_illumos.go
new file mode 100644
index 0000000000..057067465b
--- /dev/null
+++ b/src/internal/poll/iovec_illumos.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package poll
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func newIovecWithBase(base *byte) syscall.Iovec {
+ return syscall.Iovec{Base: (*int8)(unsafe.Pointer(base))}
+}
diff --git a/src/internal/poll/iovec_unix.go b/src/internal/poll/iovec_unix.go
new file mode 100644
index 0000000000..6f98947866
--- /dev/null
+++ b/src/internal/poll/iovec_unix.go
@@ -0,0 +1,13 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package poll
+
+import "syscall"
+
+func newIovecWithBase(base *byte) syscall.Iovec {
+ return syscall.Iovec{Base: base}
+}
diff --git a/src/internal/poll/read_test.go b/src/internal/poll/read_test.go
index b4f5236d3e..598a52ee44 100644
--- a/src/internal/poll/read_test.go
+++ b/src/internal/poll/read_test.go
@@ -5,7 +5,6 @@
package poll_test
import (
- "io/ioutil"
"os"
"runtime"
"sync"
@@ -22,7 +21,7 @@ func TestRead(t *testing.T) {
go func(p string) {
defer wg.Done()
for i := 0; i < 100; i++ {
- if _, err := ioutil.ReadFile(p); err != nil {
+ if _, err := os.ReadFile(p); err != nil {
t.Error(err)
return
}
@@ -38,7 +37,7 @@ func TestRead(t *testing.T) {
func specialFiles() []string {
var ps []string
switch runtime.GOOS {
- case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd":
+ case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd":
ps = []string{
"/dev/null",
}
diff --git a/src/internal/poll/sock_cloexec.go b/src/internal/poll/sock_cloexec.go
index 691cb8e36f..ff7982ca9e 100644
--- a/src/internal/poll/sock_cloexec.go
+++ b/src/internal/poll/sock_cloexec.go
@@ -5,7 +5,7 @@
// This file implements sysSocket and accept for platforms that
// provide a fast path for setting SetNonblock and CloseOnExec.
-// +build dragonfly freebsd linux netbsd openbsd
+// +build dragonfly freebsd illumos linux netbsd openbsd
package poll
diff --git a/src/internal/poll/sys_cloexec.go b/src/internal/poll/sys_cloexec.go
index 7b87f136df..4b3c642173 100644
--- a/src/internal/poll/sys_cloexec.go
+++ b/src/internal/poll/sys_cloexec.go
@@ -5,7 +5,7 @@
// This file implements sysSocket and accept for platforms that do not
// provide a fast path for setting SetNonblock and CloseOnExec.
-// +build aix darwin js,wasm solaris
+// +build aix darwin js,wasm solaris,!illumos
package poll
diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go
index 305e2fd209..0123fc33de 100644
--- a/src/internal/poll/writev.go
+++ b/src/internal/poll/writev.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd illumos linux netbsd openbsd
package poll
@@ -38,7 +38,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) {
if len(chunk) == 0 {
continue
}
- iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]})
+ iovecs = append(iovecs, newIovecWithBase(&chunk[0]))
if fd.IsStream && len(chunk) > 1<<30 {
iovecs[len(iovecs)-1].SetLen(1 << 30)
break // continue chunk on next writev