go-fuse icon indicating copy to clipboard operation
go-fuse copied to clipboard

Errors from `(fs.DirStream).Next` of non-empty stream are obscured as `EIO`

Open jcharum opened this issue 2 years ago • 0 comments

If any call to (fs.DirStream).Next other than the first one returns an error, users reading the directory entries see an EIO instead of the returned error.

Here is a gist that illustrates the issue: https://gist.github.com/jcharum/c25d228e62e0338d2620dfa20f04e1d1 . The failing output looks like:

❯ go test
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
2022/08/19 21:14:13 writer: Write/Writev failed, err: 22=invalid argument. opcode: READDIRPLUS
--- FAIL: TestNextError (0.02s)
    --- FAIL: TestNextError/errAt_1 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError1269835921: input/output error"
    --- FAIL: TestNextError/errAt_2 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError1958719290: input/output error"
    --- FAIL: TestNextError/errAt_3 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError1750548731: input/output error"
    --- FAIL: TestNextError/errAt_4 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError4079832694: input/output error"
    --- FAIL: TestNextError/errAt_5 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError749431903: input/output error"
    --- FAIL: TestNextError/errAt_6 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError1728283887: input/output error"
    --- FAIL: TestNextError/errAt_7 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError4240042809: input/output error"
    --- FAIL: TestNextError/errAt_8 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError3503040526: input/output error"
    --- FAIL: TestNextError/errAt_9 (0.00s)
        streamerr_test.go:70: error is not "operation not permitted": "readdirent /tmp/streamerr-TestNextError364701475: input/output error"
FAIL
exit status 1
FAIL    streamerr       0.025s

If the first call to Next returns an error, say EPERM, that error is (wrapped and) returned to the caller of Readdir. If a subsequent call (after a successful call to Next) returns EPERM, the caller of Readdir sees a (wrapped) EIO instead.

I think this happens because the Writev to the server with the READDIRPLUS error response has unexpected data, the successful entries so far, as they remain part of the message even on error: https://github.com/hanwen/go-fuse/blob/0aaef6dde4b62dd0f21d545c5001d9e5ece0c87e/fs/bridge.go#L1001-L1013 .

If this is expected behavior, could it be documented somewhere (or maybe it is, and I didn't find it)? If not, maybe the solution is to clear the DirEntryList when we encounter an error?

jcharum avatar Aug 19 '22 21:08 jcharum