@@ -281,7 +281,7 @@ def unpack_many(self, ids, *, filter=None, preload=False):
281
281
item = Item (internal_dict = _item )
282
282
if "chunks" in item :
283
283
item .chunks = [ChunkListEntry (* e ) for e in item .chunks ]
284
- if "chunks_healthy" in item :
284
+ if "chunks_healthy" in item : # legacy
285
285
item .chunks_healthy = [ChunkListEntry (* e ) for e in item .chunks_healthy ]
286
286
if filter and not filter (item ):
287
287
continue
@@ -744,7 +744,6 @@ def same_item(item, st):
744
744
# if a previous extraction was interrupted between setting the mtime and setting non-default flags.
745
745
return True
746
746
747
- has_damaged_chunks = "chunks_healthy" in item
748
747
if dry_run or stdout :
749
748
with self .extract_helper (item , "" , hlm , dry_run = dry_run or stdout ) as hardlink_set :
750
749
if not hardlink_set :
@@ -771,8 +770,6 @@ def same_item(item, st):
771
770
item_size , item_chunks_size
772
771
)
773
772
)
774
- if has_damaged_chunks :
775
- raise BackupError ("File has damaged (all-zero) chunks. Try running borg check --repair." )
776
773
return
777
774
778
775
dest = self .cwd
@@ -827,8 +824,6 @@ def make_parent(path):
827
824
raise BackupError (
828
825
f"Size inconsistency detected: size { item_size } , chunks size { item_chunks_size } "
829
826
)
830
- if has_damaged_chunks :
831
- raise BackupError ("File has damaged (all-zero) chunks. Try running borg check --repair." )
832
827
return
833
828
with backup_io :
834
829
# No repository access beyond this point.
@@ -1141,10 +1136,6 @@ def chunk_processor(chunk):
1141
1136
return chunk_entry
1142
1137
1143
1138
item .chunks = []
1144
- # if we rechunkify, we'll get a fundamentally different chunks list, thus we need
1145
- # to get rid of .chunks_healthy, as it might not correspond to .chunks any more.
1146
- if self .rechunkify and "chunks_healthy" in item :
1147
- del item .chunks_healthy
1148
1139
for chunk in chunk_iter :
1149
1140
chunk_entry = chunk_processor (chunk )
1150
1141
item .chunks .append (chunk_entry )
@@ -1761,13 +1752,10 @@ def verify_data(self):
1761
1752
if defect_chunks :
1762
1753
if self .repair :
1763
1754
# if we kill the defect chunk here, subsequent actions within this "borg check"
1764
- # run will find missing chunks and replace them with all-zero replacement
1765
- # chunks and flag the files as "repaired".
1766
- # if another backup is done later and the missing chunks get backed up again,
1767
- # a "borg check" afterwards can heal all files where this chunk was missing.
1755
+ # run will find missing chunks.
1768
1756
logger .warning (
1769
- "Found defect chunks. They will be deleted now, so affected files can "
1770
- "get repaired now and maybe healed later ."
1757
+ "Found defect chunks and will delete them now. "
1758
+ "Reading files referencing these chunks will result in an I/O error ."
1771
1759
)
1772
1760
for defect_chunk in defect_chunks :
1773
1761
# remote repo (ssh): retry might help for strange network / NIC / RAM errors
@@ -1787,10 +1775,7 @@ def verify_data(self):
1787
1775
else :
1788
1776
logger .warning ("chunk %s not deleted, did not consistently fail." , bin_to_hex (defect_chunk ))
1789
1777
else :
1790
- logger .warning (
1791
- "Found defect chunks. With --repair, they would get deleted, so affected "
1792
- "files could get repaired then and maybe healed later."
1793
- )
1778
+ logger .warning ("Found defect chunks. With --repair, they would get deleted." )
1794
1779
for defect_chunk in defect_chunks :
1795
1780
logger .debug ("chunk %s is defect." , bin_to_hex (defect_chunk ))
1796
1781
log = logger .error if errors else logger .info
@@ -1901,80 +1886,18 @@ def add_reference(id_, size, cdata):
1901
1886
self .repository .put (id_ , cdata )
1902
1887
1903
1888
def verify_file_chunks (archive_name , item ):
1904
- """Verifies that all file chunks are present.
1905
-
1906
- Missing file chunks will be replaced with new chunks of the same length containing all zeros.
1907
- If a previously missing file chunk re-appears, the replacement chunk is replaced by the correct one.
1908
- """
1909
-
1910
- def replacement_chunk (size ):
1911
- chunk = Chunk (None , allocation = CH_ALLOC , size = size )
1912
- chunk_id , data = cached_hash (chunk , self .key .id_hash )
1913
- cdata = self .repo_objs .format (chunk_id , {}, data , ro_type = ROBJ_FILE_STREAM )
1914
- return chunk_id , size , cdata
1915
-
1889
+ """Verifies that all file chunks are present. Missing file chunks will be logged."""
1916
1890
offset = 0
1917
- chunk_list = []
1918
- chunks_replaced = False
1919
- has_chunks_healthy = "chunks_healthy" in item
1920
- chunks_current = item .chunks
1921
- chunks_healthy = item .chunks_healthy if has_chunks_healthy else chunks_current
1922
- if has_chunks_healthy and len (chunks_current ) != len (chunks_healthy ):
1923
- # should never happen, but there was issue #3218.
1924
- logger .warning (f"{ archive_name } : { item .path } : Invalid chunks_healthy metadata removed!" )
1925
- del item .chunks_healthy
1926
- has_chunks_healthy = False
1927
- chunks_healthy = chunks_current
1928
- for chunk_current , chunk_healthy in zip (chunks_current , chunks_healthy ):
1929
- chunk_id , size = chunk_healthy
1891
+ for chunk in item .chunks :
1892
+ chunk_id , size = chunk
1930
1893
if chunk_id not in self .chunks :
1931
- # a chunk of the healthy list is missing
1932
- if chunk_current == chunk_healthy :
1933
- logger .error (
1934
- "{}: {}: New missing file chunk detected (Byte {}-{}, Chunk {}). "
1935
- "Replacing with all-zero chunk." .format (
1936
- archive_name , item .path , offset , offset + size , bin_to_hex (chunk_id )
1937
- )
1894
+ logger .error (
1895
+ "{}: {}: Missing file chunk detected (Byte {}-{}, Chunk {})." .format (
1896
+ archive_name , item .path , offset , offset + size , bin_to_hex (chunk_id )
1938
1897
)
1939
- self .error_found = chunks_replaced = True
1940
- chunk_id , size , cdata = replacement_chunk (size )
1941
- add_reference (chunk_id , size , cdata )
1942
- else :
1943
- logger .info (
1944
- "{}: {}: Previously missing file chunk is still missing (Byte {}-{}, Chunk {}). "
1945
- "It has an all-zero replacement chunk already." .format (
1946
- archive_name , item .path , offset , offset + size , bin_to_hex (chunk_id )
1947
- )
1948
- )
1949
- chunk_id , size = chunk_current
1950
- if chunk_id not in self .chunks :
1951
- logger .warning (
1952
- "{}: {}: Missing all-zero replacement chunk detected (Byte {}-{}, Chunk {}). "
1953
- "Generating new replacement chunk." .format (
1954
- archive_name , item .path , offset , offset + size , bin_to_hex (chunk_id )
1955
- )
1956
- )
1957
- self .error_found = chunks_replaced = True
1958
- chunk_id , size , cdata = replacement_chunk (size )
1959
- add_reference (chunk_id , size , cdata )
1960
- else :
1961
- if chunk_current == chunk_healthy :
1962
- pass # normal case, all fine.
1963
- else :
1964
- logger .info (
1965
- "{}: {}: Healed previously missing file chunk! (Byte {}-{}, Chunk {})." .format (
1966
- archive_name , item .path , offset , offset + size , bin_to_hex (chunk_id )
1967
- )
1968
- )
1969
- chunk_list .append ([chunk_id , size ]) # list-typed element as chunks_healthy is list-of-lists
1898
+ )
1899
+ self .error_found = True
1970
1900
offset += size
1971
- if chunks_replaced and not has_chunks_healthy :
1972
- # if this is first repair, remember the correct chunk IDs, so we can maybe heal the file later
1973
- item .chunks_healthy = item .chunks
1974
- if has_chunks_healthy and chunk_list == chunks_healthy :
1975
- logger .info (f"{ archive_name } : { item .path } : Completely healed previously damaged file!" )
1976
- del item .chunks_healthy
1977
- item .chunks = chunk_list
1978
1901
if "size" in item :
1979
1902
item_size = item .size
1980
1903
item_chunks_size = item .get_size (from_chunks = True )
0 commit comments