First commit

This commit is contained in:
bursa-pastoris 2022-05-07 17:06:10 +02:00
commit 757eed3bb8
3 changed files with 105 additions and 0 deletions

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright 2022 bursa-pastoris
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

42
README.md Normal file
View File

@ -0,0 +1,42 @@
## How-to
This is a script to restore data from corrupted duplicity backups without the
`manifest` file, maintaining the original directory tree.
The script requires unencrypted archives. If you encrypted them, you must
decrypt them massively with
gpg --multifile --decrypt path/to/encrypted/archives/duplicity-full*.difftar.gpg
This will decrypt all the archives in the same path. Depending on their size
it can require some time and may require you to type your passphrase multiple
times.
The decrypted archives must be extracted with `tar`:
tar xvf path/to/decrypted/archives/*.difftar --directory=restore/path
After that, in `restore/path` there will be two path trees:
- `snapshot` contains files large at most 64KB
- `multivol-snapshot` contains all the larger files: each of them is split in
fragments large up to 64KB each, gathered in a directory with the same name
of the original file
For example, an original file `Documents/bank-account-data.md` large 100B will
be available right after the extraction as a single file stored in
`restore/path/snapshot/Documents/`.
`Download/how-to-crack-assassins-creed.pdf` large 200KB will be split in
200/64=3.125 -> 4 files, stored in
`restore/path/multivol-snapshot/Downloads/how-to-crack-assassins-creed.pdf/`.
This script takes as input the *full* paths to `snapshot`, `multivol_snapshot`
and `restore/path` and restores all the files from the first two to the third.
The three input parameters must be written directly into the script editing the
values of `FROM_SNAPSHOT`, `FROM_MULTIVOL` and `TO` variables respectively.
`TO` path must *not* exist when the script is launched.
## License
The script is released under the [Expat license](./LICENSE) (also known as the
potentially misleading name of "MIT license").

44
main.py Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/python3
import shutil
import os
# FROM_SNAPSHOT
# Path containing monovol files
#
# FROM_MULTIVOL
# Path containing multivol files
#
# TO
# Path to restore the files to
FROM_SNAPSHOT = "/home/user/duplicity/snapshot"
FROM_MULTIVOL = "/home/user/duplicity/multivol_snapshot"
TO = "/home/user/restore"
def main():
# PART 1: restore monovol (`snapshot`) tree and files
shutil.copytree(FROM_SNAPSHOT,TO)
# PART 2: restore multivol (`multivol_snapshot`)
for (path, dirs, files) in os.walk(FROM_MULTIVOL):
if len(files) == 0:
# If path is a directory, create the directory in the restore path
if not os.path.exists(path.replace(FROM_MULTIVOL,TO,1)):
os.mkdir(path.replace(FROM_MULTIVOL,TO,1))
# The triple for the directory is generated before the triples
# for any of its subdirectories, so parents of current path
# will always exist
if len(dirs) == 0:
# If path is a file, recreate it the destination path
os.system('find "{path_to_find}" -type f | sort -V | xargs cat - > "{path_to_cat_to}"'
.format(path_to_find=path,
path_to_cat_to=path.replace(FROM_MULTIVOL,TO,1)
)
)
if __name__ == '__main__':
main()