os: recognize EFTYPE, EINVAL as a refusal to open a symlink

NetBSD returns EFTYPE when opening a symlink with O_NOFOLLOW.

Dragonfly seems to return EINVAL. Only check for EINVAL on Dragonfly,
since that seems like a bit of a broad net.

Change-Id: I031357816f1fe4c370373001207e65996087597f
Reviewed-on: https://go-review.googlesource.com/c/go/+/630396
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Damien Neil 2024-11-20 17:00:57 -08:00
parent 0a0a7a5642
commit 57147256e6
3 changed files with 49 additions and 3 deletions

19
src/os/eloop_netbsd.go Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2024 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.
//go:build netbsd
package os
import "syscall"
// isNoFollowErr reports whether err may result from O_NOFOLLOW blocking an open operation.
func isNoFollowErr(err error) bool {
// NetBSD returns EFTYPE, but check the other possibilities as well.
switch err {
case syscall.ELOOP, syscall.EMLINK, syscall.EFTYPE:
return true
}
return false
}

27
src/os/eloop_other.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2024 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.
//go:build aix || darwin || dragonfly || freebsd || linux || openbsd || solaris || wasip1
package os
import (
"runtime"
"syscall"
)
// isNoFollowErr reports whether err may result from O_NOFOLLOW blocking an open operation.
func isNoFollowErr(err error) bool {
switch err {
case syscall.ELOOP, syscall.EMLINK:
return true
}
if runtime.GOOS == "dragonfly" {
// Dragonfly appears to return EINVAL from openat in this case.
if err == syscall.EINVAL {
return true
}
}
return false
}

View File

@ -61,7 +61,7 @@ func openRootInRoot(r *Root, name string) (*Root, error) {
fd, err := doInRoot(r, name, func(parent int, name string) (fd int, err error) {
ignoringEINTR(func() error {
fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
if err == syscall.ELOOP || err == syscall.EMLINK {
if isNoFollowErr(err) {
err = checkSymlink(parent, name, err)
}
return err
@ -79,7 +79,7 @@ func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File,
fd, err := doInRoot(root, name, func(parent int, name string) (fd int, err error) {
ignoringEINTR(func() error {
fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|flag, uint32(perm))
if err == syscall.ELOOP || err == syscall.ENOTDIR || err == syscall.EMLINK {
if isNoFollowErr(err) || err == syscall.ENOTDIR {
err = checkSymlink(parent, name, err)
}
return err
@ -100,7 +100,7 @@ func rootOpenDir(parent int, name string) (int, error) {
)
ignoringEINTR(func() error {
fd, err = unix.Openat(parent, name, syscall.O_NOFOLLOW|syscall.O_CLOEXEC|syscall.O_DIRECTORY, 0)
if err == syscall.ELOOP || err == syscall.ENOTDIR || err == syscall.EMLINK {
if isNoFollowErr(err) || err == syscall.ENOTDIR {
err = checkSymlink(parent, name, err)
} else if err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP {
// ENOTSUP and EOPNOTSUPP are often, but not always, the same errno.