Files
duplicati/Tools/Verification/DuplicatiVerify.py
T
Jean-François Paris 22e43a5ed4 Added command line argument for the buffer sie with default value to 8kb
This leaves the current program behaviour as close as possible to the original behaviour
(with the exception of the byte by byte feeding to the hash algorithm which was probably
a bug)
2021-02-26 10:30:53 +02:00

104 lines
3.5 KiB
Python

#!/usr/bin/env python
"""
This file is a standalone python script that is tested with python 2.7 &
python 3.8
If Duplicati is producing a backup with the option --upload-verification-file,
it will produce a *-verification.json file on the backend, which can be verified
by this script. Simply run this script with the path to the backup
folder as input, and it will verify all *-verification.json files in the folder.
"""
from __future__ import print_function
import sys
import os
import string
import json
import base64
import codecs
from hashlib import sha256
import argparse
""" Utility function to return byte chunks from a binary file """
def bytes_from_file(filename, buffer_size=8192):
with open(filename, "rb") as f:
while True:
chunk = f.read(buffer_size)
if chunk:
yield chunk
else:
break
""" Verifies a single -verification.json file """
def verifyHashes(filename, quiet = True, buffer_size=8192):
errorCount = 0
checked = 0
if (not os.path.exists(filename)):
print("Specified file does not exist:", filename)
return -1
folder = os.path.dirname(filename)
with codecs.open(filename, "r", "utf-8-sig") as f:
doc = json.load(f)
for file in doc:
filename = file["Name"]
hash = file["Hash"]
size = file["Size"]
fullpath = os.path.join(folder, filename)
if not os.path.exists(fullpath):
print("File missing:", fullpath)
errorCount += 1
else:
checked += 1
if not quiet:
print("Verifying file", filename)
hashalg = sha256()
for b in bytes_from_file(fullpath, buffer_size):
hashalg.update(b)
hashval = base64.b64encode(hashalg.digest())
hashval = hashval.decode('utf-8')
if hashval != hash:
print("*** Hash check failed for file:", fullpath)
errorCount += 1
if errorCount > 0:
print("Errors were found")
else:
print("No errors found")
return errorCount
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Verify hashes of backup files.')
parser.add_argument("--quiet", action='store_true', help="Be noisy about each file being verified")
parser.add_argument("--buffer_size", type=int, default=8, help="Buffer size for file IO (in kb). \
Default value is 8. \
Increasing to size of blockfile can increase verification speed")
parser.add_argument("path", type=str, nargs='?',
help="""path to the verification file or folder containing the verification file.\
Defaulf is curent path""")
args = parser.parse_args()
if args.path is None:
args.path = os.getcwd()
if not os.path.exists(args.path):
print("No such file or directory: ", args.path)
else:
if os.path.isfile(args.path):
verifyHashes(args.path, args.quiet, args.buffer_size * 1024)
else:
files = 0
for f in os.listdir(args.path):
if (f.endswith("-verification.json")):
print("Verifying file:", f)
files += 1
verifyHashes(os.path.join(args.path, f), args.quiet, args.buffer_size *1024)
if files == 0:
print("No verification files in folder:", args.path)