Add logging and error handling for file syncing

Add logging and error handling inside, and outside of FileCommit.
Functions such as fsync, fdatasync will return error in case of hardware
I/O errors, and ignoring this means it can silently continue through
data corruption.  (c.f.
https://lwn.net/SubscriberLink/752063/12b232ab5039efbe/)
This commit is contained in:
Wladimir J. van der Laan 2018-04-20 11:21:08 +02:00
parent 8b262eb2d8
commit cf0277928f
4 changed files with 36 additions and 13 deletions

View file

@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// Serialize // Serialize
if (!SerializeDB(fileout, data)) return false; if (!SerializeDB(fileout, data)) return false;
FileCommit(fileout.Get()); if (!FileCommit(fileout.Get()))
return error("%s: Failed to flush file %s", __func__, pathTmp.string());
fileout.fclose(); fileout.fclose();
// replace existing file, if any, with new file // replace existing file, if any, with new file

View file

@ -788,21 +788,37 @@ bool TryCreateDirectories(const fs::path& p)
return false; return false;
} }
void FileCommit(FILE *file) bool FileCommit(FILE *file)
{ {
fflush(file); // harmless if redundantly called if (fflush(file) != 0) { // harmless if redundantly called
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
return false;
}
#ifdef WIN32 #ifdef WIN32
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
FlushFileBuffers(hFile); if (FlushFileBuffers(hFile) == 0) {
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
return false;
}
#else #else
#if defined(__linux__) || defined(__NetBSD__) #if defined(__linux__) || defined(__NetBSD__)
fdatasync(fileno(file)); if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
}
#elif defined(__APPLE__) && defined(F_FULLFSYNC) #elif defined(__APPLE__) && defined(F_FULLFSYNC)
fcntl(fileno(file), F_FULLFSYNC, 0); if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
return false;
}
#else #else
fsync(fileno(file)); if (fsync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
return false;
}
#endif #endif
#endif #endif
return true;
} }
bool TruncateFile(FILE *file, unsigned int length) { bool TruncateFile(FILE *file, unsigned int length) {

View file

@ -71,7 +71,7 @@ bool error(const char* fmt, const Args&... args)
} }
void PrintExceptionContinue(const std::exception *pex, const char* pszThread); void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
void FileCommit(FILE *file); bool FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length); bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD); int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);

View file

@ -1615,22 +1615,27 @@ void static FlushBlockFile(bool fFinalize = false)
LOCK(cs_LastBlockFile); LOCK(cs_LastBlockFile);
CDiskBlockPos posOld(nLastBlockFile, 0); CDiskBlockPos posOld(nLastBlockFile, 0);
bool status = true;
FILE *fileOld = OpenBlockFile(posOld); FILE *fileOld = OpenBlockFile(posOld);
if (fileOld) { if (fileOld) {
if (fFinalize) if (fFinalize)
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
FileCommit(fileOld); status &= FileCommit(fileOld);
fclose(fileOld); fclose(fileOld);
} }
fileOld = OpenUndoFile(posOld); fileOld = OpenUndoFile(posOld);
if (fileOld) { if (fileOld) {
if (fFinalize) if (fFinalize)
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
FileCommit(fileOld); status &= FileCommit(fileOld);
fclose(fileOld); fclose(fileOld);
} }
if (!status) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
} }
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
@ -4760,7 +4765,8 @@ bool DumpMempool(void)
} }
file << mapDeltas; file << mapDeltas;
FileCommit(file.Get()); if (!FileCommit(file.Get()))
throw std::runtime_error("FileCommit failed");
file.fclose(); file.fclose();
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
int64_t last = GetTimeMicros(); int64_t last = GetTimeMicros();