summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlhchavez <lhchavez@lhchavez.com>2021-09-08 06:29:58 -0700
committerlhchavez <lhchavez@lhchavez.com>2021-09-08 06:45:02 -0700
commit6571ba7b3585a390b6de164aaa5eb93036ef295c (patch)
treefd230f972c2314bc498b964abd857f5e4a1e1baf /src
parenteeceaac06c12b488ca1cb538db067b44664f4db9 (diff)
downloadlibgit2-6571ba7b3585a390b6de164aaa5eb93036ef295c.tar.gz
Only avoid `mmap(2)`/`ftruncate(2)` when in non-Windows
It turns out that if we use `mmap(2)`, non-Windows remote filesystems break due to permissions. If we don't, _Windows_ remote filesystems break due to lack of coherence between memory mapped views of the file and direct I/O operations done to the files. To break out of this impossible situation, conditionally-compile versions of Windows-specific `write_at` and `append_to_pack`.
Diffstat (limited to 'src')
-rw-r--r--src/indexer.c86
1 files changed, 84 insertions, 2 deletions
diff --git a/src/indexer.c b/src/indexer.c
index d1e354af7..d5e328880 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -601,6 +601,8 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
idx->inbuf_len += size - to_expell;
}
+#if defined(NO_MMAP) || !defined(GIT_WIN32)
+
static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
{
size_t remaining_size = size;
@@ -624,17 +626,86 @@ static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t s
static int append_to_pack(git_indexer *idx, const void *data, size_t size)
{
+ if (write_at(idx, data, idx->pack->mwf.size, size) < 0) {
+ git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+#else
+
+/*
+ * Windows may keep different views to a networked file for the mmap- and
+ * open-accessed versions of a file, so any writes done through
+ * `write(2)`/`pwrite(2)` may not be reflected on the data that `mmap(2)` is
+ * able to read.
+ */
+
+static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
+{
+ git_file fd = idx->pack->mwf.fd;
+ size_t mmap_alignment;
+ size_t page_offset;
+ off64_t page_start;
+ unsigned char *map_data;
+ git_map map;
+ int error;
+
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(size);
+
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+ return error;
+
+ /* the offset needs to be at the mmap boundary for the platform */
+ page_offset = offset % mmap_alignment;
+ page_start = offset - page_offset;
+
+ if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
+ return error;
+
+ map_data = (unsigned char *)map.data;
+ memcpy(map_data + page_offset, data, size);
+ p_munmap(&map);
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ off64_t new_size;
+ size_t mmap_alignment;
+ size_t page_offset;
+ off64_t page_start;
+ off64_t current_size = idx->pack->mwf.size;
+ int error;
+
if (!size)
return 0;
- if (write_at(idx, data, idx->pack->mwf.size, size) < 0) {
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+ return error;
+
+ /* Write a single byte to force the file system to allocate space now or
+ * report an error, since we can't report errors when writing using mmap.
+ * Round the size up to the nearest page so that we only need to perform file
+ * I/O when we add a page, instead of whenever we write even a single byte. */
+ new_size = current_size + size;
+ page_offset = new_size % mmap_alignment;
+ page_start = new_size - page_offset;
+
+ if (p_pwrite(idx->pack->mwf.fd, data, 1, page_start + mmap_alignment - 1) < 0) {
git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
return -1;
}
- return 0;
+ return write_at(idx, data, idx->pack->mwf.size, size);
}
+#endif
+
static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
{
git_packfile_stream *stream = &idx->stream;
@@ -1234,6 +1305,17 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
if (git_mwindow_free_all(&idx->pack->mwf) < 0)
goto on_error;
+#if !defined(NO_MMAP) && defined(GIT_WIN32)
+ /*
+ * Truncate file to undo rounding up to next page_size in append_to_pack only
+ * when mmap was used, to prevent failures in non-Windows remote filesystems.
+ */
+ if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
+ return -1;
+ }
+#endif
+
if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) {
git_error_set(GIT_ERROR_OS, "failed to fsync packfile");
goto on_error;