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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hbkit
3
- Version: 0.4.0
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 # probe an unknown archive, prove it's readable
96
- hbk <archive> info # task name, codec, shares, encryption
97
- hbk <archive> list [pattern] # search the file index
98
- hbk <archive> get <glob> <dest> [-j N] # extract, preserving tree and mtimes
99
- hbk <archive> verify <glob> [-j N] # integrity-check, write nothing
100
- hbk <archive> tui # same as hbk-tui
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`, rebuilt automatically when the archive
142
- changes. Browsing 1.1M files is instant after the first open.
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.** The reference archive is DSM 7,
260
- Hyper Backup 4.1.2, unencrypted, LZ4, single version, single pool. Older record layouts
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 # probe an unknown archive, prove it's readable
67
- hbk <archive> info # task name, codec, shares, encryption
68
- hbk <archive> list [pattern] # search the file index
69
- hbk <archive> get <glob> <dest> [-j N] # extract, preserving tree and mtimes
70
- hbk <archive> verify <glob> [-j N] # integrity-check, write nothing
71
- hbk <archive> tui # same as hbk-tui
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`, rebuilt automatically when the archive
113
- changes. Browsing 1.1M files is instant after the first open.
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.** The reference archive is DSM 7,
231
- Hyper Backup 4.1.2, unencrypted, LZ4, single version, single pool. Older record layouts
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,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hbkit"
7
- version = "0.4.0"
7
+ version = "0.4.2"
8
8
  description = "Recover files from Synology Hyper Backup (.hbk) archives without Synology software"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  """hbkit - recover files from Synology Hyper Backup (.hbk) archives without Synology software."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.4.2"
4
4
 
5
5
  from .archive import (Archive, NeedPassword, UnsupportedArchive, # noqa: F401
6
6
  find_archive_root, is_archive)
@@ -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] extract (keeps tree + mtimes)
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 dir listings
13
- hbk unmount <dir> unmount it
14
- hbk remotes list configured rclone 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/hbk-recovery/ and rebuilt automatically
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
- CACHE = os.path.expanduser(os.environ.get("HBK_CACHE", "~/.cache/hbk-recovery"))
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