Repository navigation
Conversation
…nign
- kernelResponse sends the first syscall.Errno in an op error's chain.
A wrapped errno, such as fmt.Errorf("lookup: %w", syscall.ENOENT) or an
*os.PathError, used to reach the kernel as EIO.
- shouldLogError judges the same errno that kernelResponse sends.
- Reply neither logs nor returns an ENOENT reply-write error. The kernel
returns ENOENT when it no longer tracks the request, for example after
the connection was aborted. libfuse also leaves it out of its error output.
- fuseutil falls back to ForgetInode when BatchForget returns a wrapped
ENOSYS.
- A nil *os.PathError no longer panics in the debug log or the wire log.
The new internal/operror package finds the errno and reports none when
unwrapping panics, as it does for a nil *os.PathError.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kernelResponsesends an op error's errno only when the error is a baresyscall.Errno. An error that wraps one, such asfmt.Errorf("lookup: %w", syscall.ENOENT)or an*os.PathError, reaches the kernel as EIO. This PR sends the firstsyscall.Errnoin the error's chain.kernelResponsesends the firstsyscall.Errnoin the op error's chain. An error with no errno still reaches the kernel as EIO.shouldLogErrorjudges the same errno. A wrapped ENOENT fromLookUpInode, or a wrapped ENOSYS, ENODATA or ERANGE from the xattr ops, no longer goes to the error log. An error that holds EIO before ENOENT is still logged, because the kernel receives EIO.Replyneither logs nor returns an ENOENT error from the reply write. The next section says when the kernel returns it. This is the case that Avoid printing the error message related to 'no such file or directory' #128 asked about.fuseutilfalls back toForgetInodewhenBatchForgetreturns a wrapped ENOSYS. The fallback moves into abatchForgetmethod, so a test can call it.*os.PathErrorno longer panics.errors.Aspanics when it unwraps one. The newinternal/operror.Errnorecovers and reports no errno, so the kernel gets EIO, as on master. On master, a nil*os.PathErroralready panics informatWireLogEntrywhen a wire logger is set, and in theReplydebug log when no error logger is set. The debug log now passes the error to%qinstead of callingError(), becausefmtrecovers from a panic inErroronly when it callsErroritself.When the reply write returns ENOENT
#128 asked when the reply write fails with ENOENT. On Linux,
fuse_dev_do_writelooks up the reply'suniqueamong the requests that wait for a reply. When it finds none, the write returns ENOENT (dev.c L1999-L2008). It also returns ENOENT when the connection is aborted while the reply is copied (L2043-L2046).A request that the file system has read stays in that list until it gets a reply, even when its caller is interrupted (
request_wait_answer, L460-L464). It leaves the list without a reply only whenfuse_abort_connends every request in flight. The kernel aborts the connection in these cases:fuse_conn_destroy, inode.c L1946-L1953).umount -fruns (fuse_umount_begin, inode.c L567-L574)./sys/fs/fuse/connections/<N>/abort(fuse_conn_abort_write, control.c L34-L41).So the error comes from an op that is still running when the mount goes away. For example, the kernel sends RELEASE in the background on a mount that is not
fuseblk(file.c L359), so an unmount right after the lastclosecan end a RELEASE that the file system still handles. The request is gone, so nothing can retry it.ServeOpsignores the error thatReplyreturns, so on master the only effect is awriteMessage: no such file or directoryline in the error log.libfuse also leaves ENOENT out of its error output (
fuse_write_msg_dev).I have not reproduced this on a mount for this PR. The steps follow from the kernel code: make
LookUpInodeinsamples/hellofssleep for a few seconds, runstaton a missing name, and write1to the connection'sabortfile while the op sleeps. The reply write then returns ENOENT.Tests
None of the new tests mounts. Run them with:
TestKernelResponseKeepsWrappedErrno: the errno the kernel receives for a bare, wrapped, joined or errno-less error, and for a nil*os.PathError.TestShouldLogErrorMatchesWrappedErrno: wrapped expected errnos are not logged. Errors whose first errno is not the expected one, and errors with no errno, are logged.TestReplyTreatsENOENTWriteAsBenign: drivesReadOpandReply. An ENOENT write error is neither logged nor returned. An EIO write error still is.TestFormatWireLogEntryStatus: the wire-logStatusfor a wrapped errno, an error with no errno, and a nil*os.PathError.TestReplyDebugLogsOpError: the exact debug-log line for an ordinary error, a nil*os.PathError, and a joined error whoseErrorpanics.TestBatchForgetFallsBackToForgetInodeOnENOSYSinfuseutil: the fallback runs for a bare or wrapped ENOSYS and for no other error.Each test fails on master (a124548) with only the
syscallWritevariable and thebatchForgetmethod added, so that the tests compile. On that tree the wrapped-errno rows fail, except inTestFormatWireLogEntryStatus, becauseformatWireLogEntryalready useserrors.As. The nil*os.PathErrorrows ofTestFormatWireLogEntryStatusandTestReplyDebugLogsOpErrorpanic. Rows that pass on both trees pin behavior this PR keeps, such as EIO for an error with no errno. The other nil*os.PathErrorrows panic ifoperror.Errnostops recovering.writeMessagenow callssyscall.Writethrough a package variable,syscallWrite. A test cannot make a write to a real file descriptor fail with ENOENT, soTestReplyTreatsENOENTWriteAsBenignreplaces it. If you prefer no test seam, I can drop the variable and that test.Not changed
fs.ErrNotExiststill reaches the kernel as EIO. It holds nosyscall.Errno, and mappingio/fserrors to errnos is a separate change.kernelResponsekeeps itshandled := falsebranch, to keep the diff small.