In ec983eb5d2 (core.fsyncobjectfiles: batched disk flushes, 2021-10-04), we have introduced batched syncing of object files. This mode works by only requesting a writeback of the page cache backing the file on written files, followed by a single hardware-flush via a temporary file created in the directory we want to flush. Given modern journaling file systems, this pattern is expected to be durable. While it's possible to reuse the `git_fsync()` helper to synchronize the page cache only, there is no helper which would allow for doing a hardware flush of a directory by creating a temporary file. Other callers which want to follow the same pattern would thus have to repeat this logic. Extract a new helper `git_fsync_dir()` from the object files code which neatly encapsulates this logic such that it can be reused. Signed-off-by: Patrick Steinhardt --- bulk-checkin.c | 13 +++---------- git-compat-util.h | 7 +++++++ wrapper.c | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/bulk-checkin.c b/bulk-checkin.c index 4deee1af46..e6ebdd1db5 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -98,16 +98,9 @@ static void do_batch_fsync(void) * hardware. */ - if (needs_batch_fsync) { - struct strbuf temp_path = STRBUF_INIT; - struct tempfile *temp; - - strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", get_object_directory()); - temp = xmks_tempfile(temp_path.buf); - fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp)); - delete_tempfile(&temp); - strbuf_release(&temp_path); - } + if (needs_batch_fsync && + git_fsync_dir(get_object_directory()) < 0) + die_errno("fsyncing object directory"); if (bulk_fsync_objdir) tmp_objdir_migrate(bulk_fsync_objdir); diff --git a/git-compat-util.h b/git-compat-util.h index 97f97178e7..f890bd07fd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1221,6 +1221,13 @@ enum fsync_action { int git_fsync(int fd, enum fsync_action action); +/* + * Issue a full hardware flush against a temporary file in the given directory + * to ensure that all files inside that directory are durable before any renames + * occur. + */ +int git_fsync_dir(const char *path); + /* * Preserves errno, prints a message, but gives no warning for ENOENT. * Returns 0 on success, which includes trying to unlink an object that does diff --git a/wrapper.c b/wrapper.c index e20df4f3a6..6c6cc8b74f 100644 --- a/wrapper.c +++ b/wrapper.c @@ -3,6 +3,7 @@ */ #include "cache.h" #include "config.h" +#include "tempfile.h" static int memory_limit_check(size_t size, int gentle) { @@ -601,6 +602,26 @@ int git_fsync(int fd, enum fsync_action action) return 0; } +int git_fsync_dir(const char *path) +{ + struct strbuf temp_path = STRBUF_INIT; + struct tempfile *temp; + + strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", path); + + temp = mks_tempfile(temp_path.buf); + if (!temp) + return -1; + + if (git_fsync(get_tempfile_fd(temp), FSYNC_HARDWARE_FLUSH) < 0) + return -1; + + delete_tempfile(&temp); + strbuf_release(&temp_path); + + return 0; +} + static int warn_if_unremovable(const char *op, const char *file, int rc) { int err; -- 2.33.1