diff --git a/CMakeLists.txt b/CMakeLists.txt index 57a0c74..f6a7c0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ include(CheckCXXSymbolExists) # versions of do not expose fdatasync() in in standard C mode # (-std=c11), but do expose the function in standard C++ mode (-std=c++11). check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) +check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC) include(CheckCXXSourceCompiles) diff --git a/port/port_config.h.in b/port/port_config.h.in index 645c78c..d6a6d01 100644 --- a/port/port_config.h.in +++ b/port/port_config.h.in @@ -10,6 +10,11 @@ #cmakedefine01 HAVE_FDATASYNC #endif // !defined(HAVE_FDATASYNC) +// Define to 1 if you have a definition for F_FULLFSYNC in . +#if !defined(HAVE_FULLFSYNC) +#cmakedefine01 HAVE_FULLFSYNC +#endif // !defined(HAVE_FULLFSYNC) + // Define to 1 if you have Google CRC32C. #if !defined(HAVE_CRC32C) #cmakedefine01 HAVE_CRC32C diff --git a/util/env_posix.cc b/util/env_posix.cc index 76bb648..362adb3 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -35,12 +35,6 @@ #include "util/posix_logger.h" #include "util/env_posix_test_helper.h" -// HAVE_FDATASYNC is defined in the auto-generated port_config.h, which is -// included by port_stdcxx.h. -#if !HAVE_FDATASYNC -#define fdatasync fsync -#endif // !HAVE_FDATASYNC - namespace leveldb { namespace { @@ -314,10 +308,11 @@ class PosixWritableFile final : public WritableFile { } status = FlushBuffer(); - if (status.ok() && ::fdatasync(fd_) != 0) { - status = PosixError(filename_, errno); + if (!status.ok()) { + return status; } - return status; + + return SyncFd(fd_, filename_); } private: @@ -352,14 +347,41 @@ class PosixWritableFile final : public WritableFile { if (fd < 0) { status = PosixError(dirname_, errno); } else { - if (::fsync(fd) < 0) { - status = PosixError(dirname_, errno); - } + status = SyncFd(fd, dirname_); ::close(fd); } return status; } + // Ensures that all the caches associated with the given file descriptor's + // data are flushed all the way to durable media, and can withstand power + // failures. + // + // The path argument is only used to populate the description string in the + // returned Status if an error occurs. + static Status SyncFd(int fd, const std::string& fd_path) { +#if HAVE_FULLFSYNC + // On macOS and iOS, fsync() doesn't guarantee durability past power + // failures. fcntl(F_FULLFSYNC) is required for that purpose. Some + // filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to + // fsync(). + if (::fcntl(fd, F_FULLFSYNC) == 0) { + return Status::OK(); + } +#endif // HAVE_FULLFSYNC + +#if HAVE_FDATASYNC + bool sync_success = ::fdatasync(fd) == 0; +#else + bool sync_success = ::fsync(fd) == 0; +#endif // HAVE_FDATASYNC + + if (sync_success) { + return Status::OK(); + } + return PosixError(fd_path, errno); + } + // Returns the directory name in a path pointing to a file. // // Returns "." if the path does not contain any directory separator.