@@ -601,6 +601,8 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
601601 idx -> inbuf_len += size - to_expell ;
602602}
603603
604+ #if defined(NO_MMAP ) || !defined(GIT_WIN32 )
605+
604606static int write_at (git_indexer * idx , const void * data , off64_t offset , size_t size )
605607{
606608 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
624626
625627static int append_to_pack (git_indexer * idx , const void * data , size_t size )
626628{
629+ if (write_at (idx , data , idx -> pack -> mwf .size , size ) < 0 ) {
630+ git_error_set (GIT_ERROR_OS , "cannot extend packfile '%s'" , idx -> pack -> pack_name );
631+ return -1 ;
632+ }
633+
634+ return 0 ;
635+ }
636+
637+ #else
638+
639+ /*
640+ * Windows may keep different views to a networked file for the mmap- and
641+ * open-accessed versions of a file, so any writes done through
642+ * `write(2)`/`pwrite(2)` may not be reflected on the data that `mmap(2)` is
643+ * able to read.
644+ */
645+
646+ static int write_at (git_indexer * idx , const void * data , off64_t offset , size_t size )
647+ {
648+ git_file fd = idx -> pack -> mwf .fd ;
649+ size_t mmap_alignment ;
650+ size_t page_offset ;
651+ off64_t page_start ;
652+ unsigned char * map_data ;
653+ git_map map ;
654+ int error ;
655+
656+ GIT_ASSERT_ARG (data );
657+ GIT_ASSERT_ARG (size );
658+
659+ if ((error = git__mmap_alignment (& mmap_alignment )) < 0 )
660+ return error ;
661+
662+ /* the offset needs to be at the mmap boundary for the platform */
663+ page_offset = offset % mmap_alignment ;
664+ page_start = offset - page_offset ;
665+
666+ if ((error = p_mmap (& map , page_offset + size , GIT_PROT_WRITE , GIT_MAP_SHARED , fd , page_start )) < 0 )
667+ return error ;
668+
669+ map_data = (unsigned char * )map .data ;
670+ memcpy (map_data + page_offset , data , size );
671+ p_munmap (& map );
672+
673+ return 0 ;
674+ }
675+
676+ static int append_to_pack (git_indexer * idx , const void * data , size_t size )
677+ {
678+ off64_t new_size ;
679+ size_t mmap_alignment ;
680+ size_t page_offset ;
681+ off64_t page_start ;
682+ off64_t current_size = idx -> pack -> mwf .size ;
683+ int error ;
684+
627685 if (!size )
628686 return 0 ;
629687
630- if (write_at (idx , data , idx -> pack -> mwf .size , size ) < 0 ) {
688+ if ((error = git__mmap_alignment (& mmap_alignment )) < 0 )
689+ return error ;
690+
691+ /* Write a single byte to force the file system to allocate space now or
692+ * report an error, since we can't report errors when writing using mmap.
693+ * Round the size up to the nearest page so that we only need to perform file
694+ * I/O when we add a page, instead of whenever we write even a single byte. */
695+ new_size = current_size + size ;
696+ page_offset = new_size % mmap_alignment ;
697+ page_start = new_size - page_offset ;
698+
699+ if (p_pwrite (idx -> pack -> mwf .fd , data , 1 , page_start + mmap_alignment - 1 ) < 0 ) {
631700 git_error_set (GIT_ERROR_OS , "cannot extend packfile '%s'" , idx -> pack -> pack_name );
632701 return -1 ;
633702 }
634703
635- return 0 ;
704+ return write_at ( idx , data , idx -> pack -> mwf . size , size ) ;
636705}
637706
707+ #endif
708+
638709static int read_stream_object (git_indexer * idx , git_indexer_progress * stats )
639710{
640711 git_packfile_stream * stream = & idx -> stream ;
@@ -1234,6 +1305,17 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
12341305 if (git_mwindow_free_all (& idx -> pack -> mwf ) < 0 )
12351306 goto on_error ;
12361307
1308+ #if !defined(NO_MMAP ) && defined(GIT_WIN32 )
1309+ /*
1310+ * Truncate file to undo rounding up to next page_size in append_to_pack only
1311+ * when mmap was used, to prevent failures in non-Windows remote filesystems.
1312+ */
1313+ if (p_ftruncate (idx -> pack -> mwf .fd , idx -> pack -> mwf .size ) < 0 ) {
1314+ git_error_set (GIT_ERROR_OS , "failed to truncate pack file '%s'" , idx -> pack -> pack_name );
1315+ return -1 ;
1316+ }
1317+ #endif
1318+
12371319 if (idx -> do_fsync && p_fsync (idx -> pack -> mwf .fd ) < 0 ) {
12381320 git_error_set (GIT_ERROR_OS , "failed to fsync packfile" );
12391321 goto on_error ;
0 commit comments