From cdeab19673b158a756cfcc8000b97c8e0030e111 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 26 Sep 2023 11:04:00 -0700 Subject: [PATCH] fix: always check error upon w.Close() in Write() (#18111) not checking w.Close() can prematurely make us think that the w.Write() actually succeeded, apparently Write() may or may not return an error but sometimes only during a Close() call to the fd we may see the error from Write() propagate. Fdatasync(w) on the FD would return an error requiring Close() error handling is less of a concern, however it may happen such that fdatasync() did not return an error, where as Close() would. --- cmd/xl-storage.go | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/cmd/xl-storage.go b/cmd/xl-storage.go index 6199a7cca..c42ac77ef 100644 --- a/cmd/xl-storage.go +++ b/cmd/xl-storage.go @@ -1945,7 +1945,6 @@ func (s *xlStorage) writeAllDirect(ctx context.Context, filePath string, fileSiz if err != nil { return osErrToFileErr(err) } - defer w.Close() var bufp *[]byte switch { @@ -1968,17 +1967,33 @@ func (s *xlStorage) writeAllDirect(ctx context.Context, filePath string, fileSiz written, err = io.CopyBuffer(diskHealthWriter(ctx, w), r, *bufp) } if err != nil { + w.Close() return err } if written < fileSize && fileSize >= 0 { + w.Close() return errLessData } else if written > fileSize && fileSize >= 0 { + w.Close() return errMoreData } // Only interested in flushing the size_t not mtime/atime - return Fdatasync(w) + if err = Fdatasync(w); err != nil { + w.Close() + return err + } + + // Dealing with error returns from close() - 'man 2 close' + // + // A careful programmer will check the return value of close(), since it is quite possible that + // errors on a previous write(2) operation are reported only on the final close() that releases + // the open file descriptor. + // + // Failing to check the return value when closing a file may lead to silent loss of data. + // This can especially be observed with NFS and with disk quota. + return w.Close() } func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b []byte, sync bool) (err error) { @@ -2016,18 +2031,27 @@ func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b if err != nil { return err } - defer w.Close() n, err := w.Write(b) if err != nil { + w.Close() return err } if n != len(b) { + w.Close() return io.ErrShortWrite } - return nil + // Dealing with error returns from close() - 'man 2 close' + // + // A careful programmer will check the return value of close(), since it is quite possible that + // errors on a previous write(2) operation are reported only on the final close() that releases + // the open file descriptor. + // + // Failing to check the return value when closing a file may lead to silent loss of data. + // This can especially be observed with NFS and with disk quota. + return w.Close() } func (s *xlStorage) WriteAll(ctx context.Context, volume string, path string, b []byte) (err error) {