Verify changing WebDAV hash metadata without rejecting unchanged files
This commit is contained in:
1 parent
7e94915442
commit
8245571f15
4 files changed
+43
-5
No files matched your search
@@ -145,6 +145,28 @@ class Runner:
|
||||
raise BackupError(f'Independent staging hash mismatch: {name}')
|
||||
log('Independent local size/hash verification passed')
|
||||
|
||||
def validate_final_inventory(self, before, after):
|
||||
if before == after:
|
||||
return
|
||||
atomic_json(self.state / 'changed-inventory.json', after)
|
||||
# WebDAV checksum properties can appear/disappear without a file changing.
|
||||
# Changes to actual paths, sizes or modification times still require a new run.
|
||||
metadata = lambda values: {name: (item['size'], item['mtime']) for name, item in values.items()}
|
||||
if metadata(before) != metadata(after):
|
||||
raise BackupError('Cloud changed during sync/verification (paths, sizes or modification times); retry with a stable inventory')
|
||||
changed = {name: after[name] for name in before if before[name]['hashes'] != after[name]['hashes']}
|
||||
self.verify_local_hashes(changed)
|
||||
missing = [name for name, item in changed.items() if item['size'] and not any(
|
||||
key.lower().replace('-', '') in ('sha1', 'md5') for key in item['hashes'])]
|
||||
if missing:
|
||||
flags = []
|
||||
if not any('\n' in name or '\r' in name for name in missing):
|
||||
file_list = self.state / 'changed-hash-files.txt'
|
||||
file_list.write_text('\n'.join(sorted(missing)) + '\n')
|
||||
flags = ['--files-from-raw', str(file_list)]
|
||||
self.cloud('check', self.remote, str(self.stage), '--download', *flags)
|
||||
log(f'Checksum metadata changed on {len(changed)} files; current contents independently verified')
|
||||
|
||||
@staticmethod
|
||||
def totals(manifest):
|
||||
return len(manifest), sum(x['size'] for x in manifest.values())
|
||||
@@ -273,6 +295,7 @@ class Runner:
|
||||
baseline = self.baseline()
|
||||
self.phase = 'cloud inventory'
|
||||
before = self.manifest()
|
||||
atomic_json(self.state / 'source-inventory.json', before)
|
||||
count, size = self.guard_source(before, baseline)
|
||||
self.staging_preflight(before)
|
||||
self.phase = 'cloud sync'
|
||||
@@ -298,8 +321,7 @@ class Runner:
|
||||
'--files-from-raw', str(file_list))
|
||||
self.verify_local_hashes(before)
|
||||
after = self.manifest()
|
||||
if before != after:
|
||||
raise BackupError('Cloud changed during sync/verification; retry with a stable inventory')
|
||||
self.validate_final_inventory(before, after)
|
||||
atomic_json(self.state / 'verified-inventory.json', after)
|
||||
self.nas()
|
||||
self.phase = 'borg create'
|
||||
|
||||
Reference in new issue
Block a user