hbkit 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hbkit might be problematic. Click here for more details.
- {hbkit-0.4.0 → hbkit-0.4.2}/PKG-INFO +24 -12
- {hbkit-0.4.0 → hbkit-0.4.2}/README.md +23 -11
- {hbkit-0.4.0 → hbkit-0.4.2}/pyproject.toml +1 -1
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/__init__.py +1 -1
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/cli.py +9 -4
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/index.py +12 -2
- {hbkit-0.4.0 → hbkit-0.4.2}/tests/test_hbkit.py +14 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/.gitignore +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/FORMAT.md +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/LICENSE +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/archive.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/crypto.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/doctor.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/manifest.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/mount.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/runner.py +0 -0
- {hbkit-0.4.0 → hbkit-0.4.2}/src/hbkit/tui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hbkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Recover files from Synology Hyper Backup (.hbk) archives without Synology software
|
|
5
5
|
Project-URL: Homepage, https://github.com/YordiLorenzo/hbkit
|
|
6
6
|
Project-URL: Source, https://github.com/YordiLorenzo/hbkit
|
|
@@ -83,7 +83,11 @@ case is a loud failure naming the file.
|
|
|
83
83
|
a All n Clear d Destination r Recover / Search q Quit │
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
`space` tick · `a` all · `n` clear · `/` search · `d` destination · `r` recover
|
|
86
|
+
`space` tick · `a` all · `n` clear · `/` search · `d` destination · `r` recover ·
|
|
87
|
+
`ctrl+q` quit (plain `q` types into whichever field has focus, so quit is ctrl+q).
|
|
88
|
+
|
|
89
|
+
During a restore it shows live throughput, ETA, a rolling list of recently completed files,
|
|
90
|
+
and a log of any failures.
|
|
87
91
|
|
|
88
92
|
Folders show subtree size and file count. Ticking a folder takes its whole subtree; the
|
|
89
93
|
destination panel warns before you start if the selection will not fit. Recovery shows a
|
|
@@ -92,12 +96,18 @@ live progress bar, throughput, ETA and a failure log.
|
|
|
92
96
|
## Commands
|
|
93
97
|
|
|
94
98
|
```sh
|
|
95
|
-
hbk <archive> doctor
|
|
96
|
-
hbk <archive> info
|
|
97
|
-
hbk <archive> list [pattern]
|
|
98
|
-
hbk <archive> get <glob> <dest> [-j N]
|
|
99
|
-
hbk <archive>
|
|
100
|
-
hbk <archive>
|
|
99
|
+
hbk <archive> doctor # probe an archive, prove it's readable
|
|
100
|
+
hbk <archive> info # task name, codec, shares, encryption
|
|
101
|
+
hbk <archive> list [pattern] # search the file index
|
|
102
|
+
hbk <archive> get <glob> <dest> [-j N] # extract, preserving tree and mtimes
|
|
103
|
+
hbk <archive> get <glob> <dest> --strict # ...re-verifying checksums on resume
|
|
104
|
+
hbk <archive> verify <glob> [-j N] # integrity-check, write nothing
|
|
105
|
+
hbk <archive> tui # same as hbk-tui
|
|
106
|
+
|
|
107
|
+
hbk mount <remote:bucket> <dir> [--for browse|restore] # mount object storage
|
|
108
|
+
hbk warm <dir> # pre-cache directory listings
|
|
109
|
+
hbk unmount <dir>
|
|
110
|
+
hbk remotes # list configured rclone remotes
|
|
101
111
|
```
|
|
102
112
|
|
|
103
113
|
For encrypted archives pass `-p/--password`, set `HBK_PASSWORD`, or let it prompt. The TUI
|
|
@@ -138,8 +148,9 @@ readable by rebuilding a random sample of real files with full checksum verifica
|
|
|
138
148
|
- **Sidecars skipped.** `@eaDir`, `@SynoEAStream` and `@SynoResource` are Synology
|
|
139
149
|
metadata — thumbnails and xattr streams, not your data. In one real archive they were
|
|
140
150
|
half of all entries but under 1% of the bytes.
|
|
141
|
-
- **Index cached** per archive in `~/.cache/hbkit
|
|
142
|
-
changes. Browsing 1.1M files is instant after the first
|
|
151
|
+
- **Index cached** per archive in `~/.cache/hbkit` (override with `HBK_CACHE`), rebuilt
|
|
152
|
+
automatically when the archive changes. Browsing 1.1M files is instant after the first
|
|
153
|
+
open; building it takes ~10s locally, ~48s over a network mount.
|
|
143
154
|
|
|
144
155
|
## Network mounts (rclone / S3 / R2)
|
|
145
156
|
|
|
@@ -256,8 +267,9 @@ Read this before trusting it with the only copy of anything.
|
|
|
256
267
|
- **The index cache stores decrypted filenames.** Browsing an encrypted archive requires
|
|
257
268
|
the password because the directory tree itself is ciphertext, so `~/.cache/hbkit` will
|
|
258
269
|
contain plaintext names (not file contents). Delete it if that matters to you.
|
|
259
|
-
- **Proven against a limited set of archives.**
|
|
260
|
-
|
|
270
|
+
- **Proven against a limited set of archives.** A 3 TB DSM 7 / Hyper Backup 4.1.2
|
|
271
|
+
archive (unencrypted, LZ4, single version, single pool) and a small encrypted one from
|
|
272
|
+
the same DSM version, both verified byte-exact. Older record layouts
|
|
261
273
|
(16-byte `chunk_index`, 28-byte bucket records), zlib chunks and multi-version archives
|
|
262
274
|
are implemented from disassembly but have not met a real archive of that kind. `doctor`
|
|
263
275
|
exists precisely so you can find out in seconds rather than mid-restore.
|
|
@@ -54,7 +54,11 @@ case is a loud failure naming the file.
|
|
|
54
54
|
a All n Clear d Destination r Recover / Search q Quit │
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
`space` tick · `a` all · `n` clear · `/` search · `d` destination · `r` recover
|
|
57
|
+
`space` tick · `a` all · `n` clear · `/` search · `d` destination · `r` recover ·
|
|
58
|
+
`ctrl+q` quit (plain `q` types into whichever field has focus, so quit is ctrl+q).
|
|
59
|
+
|
|
60
|
+
During a restore it shows live throughput, ETA, a rolling list of recently completed files,
|
|
61
|
+
and a log of any failures.
|
|
58
62
|
|
|
59
63
|
Folders show subtree size and file count. Ticking a folder takes its whole subtree; the
|
|
60
64
|
destination panel warns before you start if the selection will not fit. Recovery shows a
|
|
@@ -63,12 +67,18 @@ live progress bar, throughput, ETA and a failure log.
|
|
|
63
67
|
## Commands
|
|
64
68
|
|
|
65
69
|
```sh
|
|
66
|
-
hbk <archive> doctor
|
|
67
|
-
hbk <archive> info
|
|
68
|
-
hbk <archive> list [pattern]
|
|
69
|
-
hbk <archive> get <glob> <dest> [-j N]
|
|
70
|
-
hbk <archive>
|
|
71
|
-
hbk <archive>
|
|
70
|
+
hbk <archive> doctor # probe an archive, prove it's readable
|
|
71
|
+
hbk <archive> info # task name, codec, shares, encryption
|
|
72
|
+
hbk <archive> list [pattern] # search the file index
|
|
73
|
+
hbk <archive> get <glob> <dest> [-j N] # extract, preserving tree and mtimes
|
|
74
|
+
hbk <archive> get <glob> <dest> --strict # ...re-verifying checksums on resume
|
|
75
|
+
hbk <archive> verify <glob> [-j N] # integrity-check, write nothing
|
|
76
|
+
hbk <archive> tui # same as hbk-tui
|
|
77
|
+
|
|
78
|
+
hbk mount <remote:bucket> <dir> [--for browse|restore] # mount object storage
|
|
79
|
+
hbk warm <dir> # pre-cache directory listings
|
|
80
|
+
hbk unmount <dir>
|
|
81
|
+
hbk remotes # list configured rclone remotes
|
|
72
82
|
```
|
|
73
83
|
|
|
74
84
|
For encrypted archives pass `-p/--password`, set `HBK_PASSWORD`, or let it prompt. The TUI
|
|
@@ -109,8 +119,9 @@ readable by rebuilding a random sample of real files with full checksum verifica
|
|
|
109
119
|
- **Sidecars skipped.** `@eaDir`, `@SynoEAStream` and `@SynoResource` are Synology
|
|
110
120
|
metadata — thumbnails and xattr streams, not your data. In one real archive they were
|
|
111
121
|
half of all entries but under 1% of the bytes.
|
|
112
|
-
- **Index cached** per archive in `~/.cache/hbkit
|
|
113
|
-
changes. Browsing 1.1M files is instant after the first
|
|
122
|
+
- **Index cached** per archive in `~/.cache/hbkit` (override with `HBK_CACHE`), rebuilt
|
|
123
|
+
automatically when the archive changes. Browsing 1.1M files is instant after the first
|
|
124
|
+
open; building it takes ~10s locally, ~48s over a network mount.
|
|
114
125
|
|
|
115
126
|
## Network mounts (rclone / S3 / R2)
|
|
116
127
|
|
|
@@ -227,8 +238,9 @@ Read this before trusting it with the only copy of anything.
|
|
|
227
238
|
- **The index cache stores decrypted filenames.** Browsing an encrypted archive requires
|
|
228
239
|
the password because the directory tree itself is ciphertext, so `~/.cache/hbkit` will
|
|
229
240
|
contain plaintext names (not file contents). Delete it if that matters to you.
|
|
230
|
-
- **Proven against a limited set of archives.**
|
|
231
|
-
|
|
241
|
+
- **Proven against a limited set of archives.** A 3 TB DSM 7 / Hyper Backup 4.1.2
|
|
242
|
+
archive (unencrypted, LZ4, single version, single pool) and a small encrypted one from
|
|
243
|
+
the same DSM version, both verified byte-exact. Older record layouts
|
|
232
244
|
(16-byte `chunk_index`, 28-byte bucket records), zlib chunks and multi-version archives
|
|
233
245
|
are implemented from disassembly but have not met a real archive of that kind. `doctor`
|
|
234
246
|
exists precisely so you can find out in seconds rather than mid-restore.
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
hbk <archive> doctor check whether it can be recovered
|
|
5
5
|
hbk <archive> info show task, shares, codec
|
|
6
6
|
hbk <archive> list [pattern] search the file index
|
|
7
|
-
hbk <archive> get <glob> <dest> [-j N] [--strict]
|
|
7
|
+
hbk <archive> get <glob> <dest> [-j N] [--strict] extract (keeps tree + mtimes)
|
|
8
8
|
hbk <archive> verify <glob> [-j N] integrity-check, write nothing
|
|
9
9
|
hbk <archive> tui browse in the full-screen UI
|
|
10
10
|
|
|
11
11
|
hbk mount <remote:bucket> <dir> [--for browse|restore] [--no-warm]
|
|
12
|
-
hbk warm <dir> pre-cache
|
|
13
|
-
hbk unmount <dir>
|
|
14
|
-
hbk remotes
|
|
12
|
+
hbk warm <dir> pre-cache directory listings
|
|
13
|
+
hbk unmount <dir> unmount it
|
|
14
|
+
hbk remotes list configured rclone remotes
|
|
15
|
+
hbk --version print the installed version
|
|
15
16
|
|
|
16
17
|
Encrypted archives: add -p/--password, or set HBK_PASSWORD, or you'll be prompted.
|
|
17
18
|
|
|
@@ -162,6 +163,10 @@ def run(arc, sel, dest, jobs, check, password=None, strict=False):
|
|
|
162
163
|
|
|
163
164
|
def main() -> int:
|
|
164
165
|
a = sys.argv[1:]
|
|
166
|
+
if a and a[0] in ("--version", "-V"):
|
|
167
|
+
from . import __version__
|
|
168
|
+
print(f"hbkit {__version__}")
|
|
169
|
+
return 0
|
|
165
170
|
jobs = 8
|
|
166
171
|
password = None
|
|
167
172
|
if "-j" in a:
|
|
@@ -7,7 +7,7 @@ where a directory's `size`/`nfiles` are SUBTREE totals, so a UI can show folder
|
|
|
7
7
|
without walking children. Synology sidecar metadata (@eaDir, @SynoEAStream,
|
|
8
8
|
@SynoResource) is pruned - it is not user data.
|
|
9
9
|
|
|
10
|
-
Indexes are cached per archive under ~/.cache/
|
|
10
|
+
Indexes are cached per archive under ~/.cache/hbkit/ and rebuilt automatically
|
|
11
11
|
when the archive's share databases change.
|
|
12
12
|
"""
|
|
13
13
|
from __future__ import annotations
|
|
@@ -21,7 +21,17 @@ import time
|
|
|
21
21
|
|
|
22
22
|
from . import archive as hbk
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
def _default_cache() -> str:
|
|
25
|
+
"""~/.cache/hbkit, falling back to the pre-0.4.1 name if that is what exists locally
|
|
26
|
+
so an upgrade does not silently orphan an index someone waited minutes to build."""
|
|
27
|
+
new = os.path.expanduser("~/.cache/hbkit")
|
|
28
|
+
old = os.path.expanduser("~/.cache/hbk-recovery")
|
|
29
|
+
if not os.path.isdir(new) and os.path.isdir(old):
|
|
30
|
+
return old
|
|
31
|
+
return new
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
CACHE = os.path.expanduser(os.environ.get("HBK_CACHE", "")) or _default_cache()
|
|
25
35
|
SCHEMA = 2
|
|
26
36
|
|
|
27
37
|
|
|
@@ -401,3 +401,17 @@ def test_sweep_parts_removes_only_part_files(tmp_path):
|
|
|
401
401
|
def test_missing_file_is_never_complete(tmp_path):
|
|
402
402
|
from hbkit import manifest as mf
|
|
403
403
|
assert not mf.is_complete(str(tmp_path), "nope.bin", 10, {})
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def test_cache_dir_prefers_new_name_but_honours_the_old_one(tmp_path, monkeypatch):
|
|
407
|
+
"""Renaming the cache must not orphan an index someone waited minutes to build."""
|
|
408
|
+
from hbkit import index as hbki
|
|
409
|
+
home = tmp_path
|
|
410
|
+
monkeypatch.setenv("HOME", str(home))
|
|
411
|
+
monkeypatch.setattr(os.path, "expanduser",
|
|
412
|
+
lambda p: p.replace("~", str(home)) if p.startswith("~") else p)
|
|
413
|
+
assert hbki._default_cache().endswith("/.cache/hbkit") # nothing exists yet
|
|
414
|
+
(home / ".cache" / "hbk-recovery").mkdir(parents=True)
|
|
415
|
+
assert hbki._default_cache().endswith("/.cache/hbk-recovery") # legacy honoured
|
|
416
|
+
(home / ".cache" / "hbkit").mkdir(parents=True)
|
|
417
|
+
assert hbki._default_cache().endswith("/.cache/hbkit") # new wins once present
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|