Skip to content

Commit bc22d84

Browse files
pks-tgitster
authored andcommitted
core.fsync: new option to harden references
When writing both loose and packed references to disk we first create a lockfile, write the updated values into that lockfile, and on commit we rename the file into place. According to filesystem developers, this behaviour is broken because applications should always sync data to disk before doing the final rename to ensure data consistency [1][2][3]. If applications fail to do this correctly, a hard crash of the machine can easily result in corrupted on-disk data. This kind of corruption can in fact be easily observed with Git when the machine hard-resets shortly after writing references to disk. On machines with ext4, this will likely lead to the "empty files" problem: the file has been renamed, but its data has not been synced to disk. The result is that the reference is corrupt, and in the worst case this can lead to data loss. Implement a new option to harden references so that users and admins can avoid this scenario by syncing locked loose and packed references to disk before we rename them into place. [1]: https://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/ [2]: https://btrfs.wiki.kernel.org/index.php/FAQ (What are the crash guarantees of overwrite-by-rename) [3]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/ext4.rst (see auto_da_alloc) Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0099792 commit bc22d84

File tree

5 files changed

+10
-3
lines changed

5 files changed

+10
-3
lines changed

Documentation/config/core.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ but risks losing recent work in the event of an unclean system shutdown.
575575
* `index` hardens the index when it is modified.
576576
* `objects` is an aggregate option that is equivalent to
577577
`loose-object,pack`.
578+
* `reference` hardens references modified in the repo.
578579
* `derived-metadata` is an aggregate option that is equivalent to
579580
`pack-metadata,commit-graph`.
580581
* `committed` is an aggregate option that is currently equivalent to

cache.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,7 @@ enum fsync_component {
10051005
FSYNC_COMPONENT_PACK_METADATA = 1 << 2,
10061006
FSYNC_COMPONENT_COMMIT_GRAPH = 1 << 3,
10071007
FSYNC_COMPONENT_INDEX = 1 << 4,
1008+
FSYNC_COMPONENT_REFERENCE = 1 << 5,
10081009
};
10091010

10101011
#define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
@@ -1017,7 +1018,8 @@ enum fsync_component {
10171018
FSYNC_COMPONENTS_DERIVED_METADATA | \
10181019
~FSYNC_COMPONENT_LOOSE_OBJECT)
10191020

1020-
#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS)
1021+
#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS | \
1022+
FSYNC_COMPONENT_REFERENCE)
10211023

10221024
#define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
10231025
FSYNC_COMPONENT_INDEX)
@@ -1026,7 +1028,8 @@ enum fsync_component {
10261028
FSYNC_COMPONENT_PACK | \
10271029
FSYNC_COMPONENT_PACK_METADATA | \
10281030
FSYNC_COMPONENT_COMMIT_GRAPH | \
1029-
FSYNC_COMPONENT_INDEX)
1031+
FSYNC_COMPONENT_INDEX | \
1032+
FSYNC_COMPONENT_REFERENCE)
10301033

10311034
/*
10321035
* A bitmask indicating which components of the repo should be fsynced.

config.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,7 @@ static const struct fsync_component_name {
13331333
{ "commit-graph", FSYNC_COMPONENT_COMMIT_GRAPH },
13341334
{ "index", FSYNC_COMPONENT_INDEX },
13351335
{ "objects", FSYNC_COMPONENTS_OBJECTS },
1336+
{ "reference", FSYNC_COMPONENT_REFERENCE },
13361337
{ "derived-metadata", FSYNC_COMPONENTS_DERIVED_METADATA },
13371338
{ "committed", FSYNC_COMPONENTS_COMMITTED },
13381339
{ "added", FSYNC_COMPONENTS_ADDED },

refs/files-backend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
17871787
fd = get_lock_file_fd(&lock->lk);
17881788
if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
17891789
write_in_full(fd, &term, 1) < 0 ||
1790+
fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&lock->lk)) < 0 ||
17901791
close_ref_gently(lock) < 0) {
17911792
strbuf_addf(err,
17921793
"couldn't write '%s'", get_lock_file_path(&lock->lk));

refs/packed-backend.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,8 @@ static int write_with_updates(struct packed_ref_store *refs,
12621262
goto error;
12631263
}
12641264

1265-
if (close_tempfile_gently(refs->tempfile)) {
1265+
if (fsync_component(FSYNC_COMPONENT_REFERENCE, get_tempfile_fd(refs->tempfile)) ||
1266+
close_tempfile_gently(refs->tempfile)) {
12661267
strbuf_addf(err, "error closing file %s: %s",
12671268
get_tempfile_path(refs->tempfile),
12681269
strerror(errno));

0 commit comments

Comments
 (0)