你正在使用的Go版本是什么( go version
)?
$ go version
go version go1.16.2 linux/amd64
这个问题在最新版本的发布中是否重现?
是的,在最新版本中。
你正在使用什么操作系统和处理器架构( go env
)?
go env
输出
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/tom/.cache/go-build"
GOENV="/home/tom/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/tom/go/pkg/mod"
GONOPROXY=...
GONOSUMDB=...
GOOS="linux"
GOPATH="/home/tom/go"
GOPRIVATE=...
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/tom/sdk/go1.16.2"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/tom/sdk/go1.16.2/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16.2"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build14584737=/tmp/go-build -gno-record-gcc-switches"
你做了什么?
我正在浏览os目录,阅读源代码,并注意到Seek
的EISDIR错误条件是损坏的。Seek
检查os特定的seek
方法是否成功处理具有dirinfo
的文件:
go/src/os/file.go
第239行到第241行:
| ife==nil&&f.dirinfo!=nil&&r!=0 { |
| e=syscall.EISDIR |
| } |
不幸的是,seek
方法总是清除dirinfo
( #35767 和 #37161 ):
go/src/os/file_unix.go
第269行到第274行:
| iff.dirinfo!=nil { |
| // 释放缓存的dirinfo,所以如果我们再次访问这个文件作为目录,我们会分配一个新的one。参见#35767和#37161。 |
| f.dirinfo.close() |
| f.dirinfo=nil |
| } |
这个脚本应该在本地运行时重现错误:
https://play.golang.org/p/brgpk5je9tP
顺便说一下:我不确定这是处理目录的最佳方式,它只会在之前调用过readdir
时触发,至少在Linux上,只有在seek实际成功后才会触发。
你期望看到什么?
返回一个syscall.EISDIR
错误。
你看到了什么?
没有错误地返回了Seek
。
7条答案
按热度按时间m0rkklqb1#
CC @ianlancetaylor
2lpgd9682#
据我所知,这正如预期的那样工作。在目录中查找是有意义的。特别是,你可以调用
Seek(1, 0)
来获取当前偏移量,然后稍后你可以回到那个位置。在(*File).Seek
中的dirinfo
的测试是为了处理 Windows 的情况,其中seek
方法不会清除dirinfo
字段。是否有特定原因使得
Seek
在 Unix 系统上的目录上应该失败?ibrsph3r3#
以下是修正后的测试程序,我遗漏了
Readdirnames
调用:https://play.golang.org/p/INPumuqPZ-V
在 Unix 系统中查找目录曾经失败过:
我不反对保持当前的行为,但我在更改中没有看到任何表明故意允许这种情况的内容。我非常确定这是一个意外,尽管之前有明确的检查,但它仍然起作用了,因此我认为这是错误的。
xxb16uws4#
同时,虽然在系统调用级别,Seek是有意义的,但我不认为它在Go级别有意义。dir_unix.go代码缓冲了每个读取的完整8KiB目录信息。如果我没记错的话,如果你试图使用Seek来记住一个位置,你最终总是会到达其中一个8KiB块的末尾,而不是在你刚刚读取的目录条目之后立即到达。
fcy6dtqo5#
好的。我之前没有意识到这个问题在之前的版本中就已经失败了。
看起来这个问题在 https://golang.org/cl/219143 版本中发生了变化。参见 #35767 和 #37161 中的测试用例。这些问题的测试用例涉及到寻求位置 0,这一直都是允许的。但是,正如你所说,那个 CL 改变了代码,使得在寻求非 0 偏移量时返回
EISDIR
的检查不再失败。有趣的是,在寻求非 0 偏移量时返回
EISDIR
可以追溯到Seek
方法首次引入的时候,即在 d94c5ab 版本中。我不太确定我们应该在未来强制执行什么样的行为。CC @randall77
uplii1fm6#
我认为我们希望
Seek
在目录上工作,用于(0, io.SeekStart)
、(n, io.SeekStart)
和n
从之前的调用返回给(0, io.SeekCur)
和(0, io.SeekCur)
。因此,whence == io.SeekStart || (whence == io.SeekCur && off == 0)
。对于其他情况,我们是否可以立即为Seek
返回错误?这可能对于 <=1.13 版本的行为是这样的。实际上,我们可以在
(*FIle).Seek
中更改测试的顺序,将dirinfo
测试放在对(*File).seek
的调用之前。引用 CL 209961 中的自己的话:
附言:我讨厌 Readdirnames API。
x759pob27#
历史上,我们只允许
Seek
的结果为0
的情况。也就是说,历史上我们没有接受(n, io.SeekStart)
作为(0, io.SeekCur)
之前调用返回的值。(尽管我上面说了)。我认为可以回到那种情况。这意味着我认为只接受(0, io.SeekStart)
是没问题的,因为虽然还有其他方法可以达到 0,但它们是不必要的复杂。