diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/indexer.c | 86 |
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; |