diff --git a/src/borg/archive.py b/src/borg/archive.py index ef5de3f0c..f9fdfc129 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -834,6 +834,7 @@ Utilization of max. archive size: {csize_max:.0%} with backup_io('open'): fd = open(path, 'wb') with fd: + trailing_hole = False ids = [c.id for c in item.chunks] for data in self.pipeline.fetch_many(ids, is_preloaded=True): if pi: @@ -842,10 +843,18 @@ Utilization of max. archive size: {csize_max:.0%} if sparse and zeros.startswith(data): # all-zero chunk: create a hole in a sparse file fd.seek(len(data), 1) + trailing_hole = True else: fd.write(data) + trailing_hole = False with backup_io('truncate_and_attrs'): pos = item_chunks_size = fd.tell() + if is_win32 and trailing_hole and pos > 0: + # Windows: truncate() does not zero-fill properly (no VDL update). + # Writing a single zero at the end forces NTFS to zero-fill the hole + # and update valid data length. + fd.seek(pos - 1) + fd.write(b"\0") fd.truncate(pos) fd.flush() self.restore_attrs(path, item, fd=fd.fileno())