From c6ba959eb343db4fd47516d1e498e596e57ea1c5 Mon Sep 17 00:00:00 2001 From: 0xferrous <0xferrous@proton.me> Date: Sat, 18 Jul 2026 07:15:39 +0000 Subject: [PATCH] mmap: use private mappings for read-only files Read-only mappings do not require shared semantics. Some filesystems, including virtiofs and GPFS, reject MAP_SHARED mappings with ENODEV while allowing MAP_PRIVATE mappings. Use GIT_MAP_PRIVATE for the read-only mapping helper and when reading pack headers. This also matches the behavior of native Git. Keep the writable Windows indexer mapping shared, since writes made through that mapping must reach the underlying file. Add a test that verifies the read-only mapping helper creates a private mapping. Refs #7136 --- src/libgit2/indexer.c | 2 +- src/util/futils.c | 2 +- tests/util/futils.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c index f1c7e437c61..6b726a694b8 100644 --- a/src/libgit2/indexer.c +++ b/src/libgit2/indexer.c @@ -97,7 +97,7 @@ static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) int error; git_map map; - if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0) + if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_PRIVATE, pack->mwf.fd, 0)) < 0) return error; memcpy(hdr, map.data, sizeof(*hdr)); diff --git a/src/util/futils.c b/src/util/futils.c index 25c3a1be180..00877f4d933 100644 --- a/src/util/futils.c +++ b/src/util/futils.c @@ -359,7 +359,7 @@ int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmod int git_futils_mmap_ro(git_map *out, git_file fd, off64_t begin, size_t len) { - return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); + return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_PRIVATE, fd, begin); } int git_futils_mmap_ro_file(git_map *out, const char *path) diff --git a/tests/util/futils.c b/tests/util/futils.c index 993b14d9b03..f15d066c24f 100644 --- a/tests/util/futils.c +++ b/tests/util/futils.c @@ -1,6 +1,10 @@ #include "clar_libgit2.h" #include "futils.h" +#if !defined(GIT_WIN32) && !defined(NO_MMAP) +#include +#endif + /* Fixture setup and teardown */ void test_futils__initialize(void) { @@ -37,6 +41,30 @@ void test_futils__writebuffer(void) git_str_dispose(&append); } +void test_futils__mmap_ro_uses_map_private(void) +{ +#if defined(GIT_WIN32) || defined(NO_MMAP) + cl_skip(); +#else + const char content[] = "mapped data"; + const char *path = "futils/mmap-file"; + git_map map = { 0 }; + git_file fd; + + cl_git_mkfile(path, content); + cl_assert((fd = p_open(path, O_RDWR)) >= 0); + cl_git_pass(git_futils_mmap_ro(&map, fd, 0, sizeof(content) - 1)); + + /* A private mapping must not propagate changes back to the file. */ + cl_must_pass(mprotect(map.data, map.len, PROT_READ | PROT_WRITE)); + ((char *)map.data)[0] = 'M'; + cl_assert_equal_file(content, sizeof(content) - 1, path); + + git_futils_mmap_free(&map); + p_close(fd); +#endif +} + void test_futils__write_hidden_file(void) { #ifndef GIT_WIN32