aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsqualus <squalus@squalus.net>2022-09-19 11:15:31 -0700
committersqualus <squalus@squalus.net>2022-09-19 20:13:30 -0700
commit1b595026e18afb050de3f62ded8f7180bc8b2b0e (patch)
tree6a6c3834483a46ce194cd37bafb0a8168742148c
parent9d860f34673fbf07a2cf07ff854ae6683dae2fb8 (diff)
Improve durability of schema version file writes
- call close explicitly in writeFile to prevent the close exception from being ignored - fsync after writing schema file to flush data to disk - fsync schema file parent to flush metadata to disk https://github.com/NixOS/nix/issues/7064
-rw-r--r--src/libstore/local-store.cc6
-rw-r--r--src/libutil/util.cc38
-rw-r--r--src/libutil/util.hh8
3 files changed, 45 insertions, 7 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 0b07cde34..37302d3a8 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -158,7 +158,7 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
txn.commit();
}
- writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
+ writeFile(schemaPath, fmt("%d", nixCASchemaVersion), 0666, true);
lockFile(lockFd.get(), ltRead, true);
}
}
@@ -281,7 +281,7 @@ LocalStore::LocalStore(const Params & params)
else if (curSchema == 0) { /* new store */
curSchema = nixSchemaVersion;
openDB(*state, true);
- writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
+ writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
}
else if (curSchema < nixSchemaVersion) {
@@ -329,7 +329,7 @@ LocalStore::LocalStore(const Params & params)
txn.commit();
}
- writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
+ writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str(), 0666, true);
lockFile(globalLock.get(), ltRead, true);
}
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 96ac11ea2..623b74bdd 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -353,7 +353,7 @@ void readFile(const Path & path, Sink & sink)
}
-void writeFile(const Path & path, std::string_view s, mode_t mode)
+void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
if (!fd)
@@ -364,10 +364,16 @@ void writeFile(const Path & path, std::string_view s, mode_t mode)
e.addTrace({}, "writing file '%1%'", path);
throw;
}
+ if (sync)
+ fd.fsync();
+ // Explicitly close to make sure exceptions are propagated.
+ fd.close();
+ if (sync)
+ syncParent(path);
}
-void writeFile(const Path & path, Source & source, mode_t mode)
+void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
if (!fd)
@@ -386,6 +392,20 @@ void writeFile(const Path & path, Source & source, mode_t mode)
e.addTrace({}, "writing file '%1%'", path);
throw;
}
+ if (sync)
+ fd.fsync();
+ // Explicitly close to make sure exceptions are propagated.
+ fd.close();
+ if (sync)
+ syncParent(path);
+}
+
+void syncParent(const Path & path)
+{
+ AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0);
+ if (!fd)
+ throw SysError("opening file '%1%'", path);
+ fd.fsync();
}
std::string readLine(int fd)
@@ -841,6 +861,20 @@ void AutoCloseFD::close()
}
}
+void AutoCloseFD::fsync()
+{
+ if (fd != -1) {
+ int result;
+#if __APPLE__
+ result = ::fcntl(fd, F_FULLFSYNC);
+#else
+ result = ::fsync(fd);
+#endif
+ if (result == -1)
+ throw SysError("fsync file descriptor %1%", fd);
+ }
+}
+
AutoCloseFD::operator bool() const
{
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index cd83f250f..e5c678682 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -115,9 +115,12 @@ std::string readFile(const Path & path);
void readFile(const Path & path, Sink & sink);
/* Write a string to a file. */
-void writeFile(const Path & path, std::string_view s, mode_t mode = 0666);
+void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false);
-void writeFile(const Path & path, Source & source, mode_t mode = 0666);
+void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false);
+
+/* Flush a file's parent directory to disk */
+void syncParent(const Path & path);
/* Read a line from a file descriptor. */
std::string readLine(int fd);
@@ -231,6 +234,7 @@ public:
explicit operator bool() const;
int release();
void close();
+ void fsync();
};