File size: 4,657 Bytes
bdfb884
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""
neural-vault CLI. Dependency-free except `mount` (needs WinFsp + winfspy).

  store  <vault> <src_folder> [--k K --m M]   ingest a folder (dedup + RS)
  ls     <vault>                              list stored files
  stat   <vault>                              dedup / redundancy stats
  export <vault> <dst_folder>                 reconstruct all files (RS-healed)
  heal   <vault>                              regenerate any missing shards
  mount  <vault> <mountpoint>                 expose as a drive (WinFsp)

  image         <device> <out.pt> [--k --m --part-mb]   scan a whole drive/partition
                (e.g. \\\\.\\C: or an .img, may need admin) into a self-healing .pt
  image-verify  <out.pt>                      report archive health
  image-heal    <out.pt>                      repair detected bit-rot
  image-restore <out.pt> <out.img>            reconstruct the image bit-exact
"""
import argparse
from storage.vault import Vault
from storage.vaultfs import winfsp_available
from storage import diskimage


def main():
    ap = argparse.ArgumentParser(prog="neural-vault")
    sub = ap.add_subparsers(dest="cmd", required=True)
    p = sub.add_parser("store"); p.add_argument("vault"); p.add_argument("src")
    p.add_argument("--k", type=int, default=4); p.add_argument("--m", type=int, default=2)
    for name in ("ls", "stat", "heal"):
        q = sub.add_parser(name); q.add_argument("vault")
    e = sub.add_parser("export"); e.add_argument("vault"); e.add_argument("dst")
    mo = sub.add_parser("mount"); mo.add_argument("vault"); mo.add_argument("mountpoint")
    im = sub.add_parser("image"); im.add_argument("device"); im.add_argument("out")
    im.add_argument("--k", type=int, default=4); im.add_argument("--m", type=int, default=2)
    im.add_argument("--part-mb", type=int, default=64); im.add_argument("--label")
    iv = sub.add_parser("image-verify"); iv.add_argument("out")
    ih = sub.add_parser("image-heal"); ih.add_argument("out")
    ir = sub.add_parser("image-restore"); ir.add_argument("out"); ir.add_argument("dst")
    args = ap.parse_args()

    if args.cmd == "store":
        v = Vault(args.vault, args.k, args.m)
        v.store_folder(args.src)
        s = v.stats()
        print(f"stored {len(v.list_files())} files, {s['unique_chunks']} unique chunks, "
              f"{s['stored']//1024} KB on disk ({s['overhead']:.2f}x, RS {args.k}+{args.m})")
    elif args.cmd == "ls":
        for rel, sz in Vault(args.vault).list_files():
            print(f"{sz:>10}  {rel}")
    elif args.cmd == "stat":
        s = Vault(args.vault).stats()
        print(f"logical {s['logical']//1024} KB | stored {s['stored']//1024} KB | "
              f"{s['unique_chunks']} chunks | overhead {s['overhead']:.2f}x")
    elif args.cmd == "heal":
        print(f"regenerated {Vault(args.vault).heal()} missing shard(s)")
    elif args.cmd == "export":
        print(f"exported {Vault(args.vault).export(args.dst)} files")
    elif args.cmd == "mount":
        if not winfsp_available():
            print("WinFsp/winfspy not installed. Install WinFsp (https://winfsp.dev) "
                  "and `pip install winfspy`, or use `export` for now.")
            return
        from storage.vaultfs import mount
        fs = mount(Vault(args.vault), args.mountpoint)
        print(f"mounted vault at {args.mountpoint} (Ctrl+C to unmount)")
        try:
            import time
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            fs.stop()
            print("unmounted")
    elif args.cmd == "image":
        r = diskimage.create(args.device, args.out, k=args.k, m=args.m,
                             part_mb=args.part_mb, label=args.label)
        red = (args.k + args.m) / args.k
        print(f"imaged {r['bytes']//1024} KB -> {r['out']} ({r['mode']}, {r['parts']} part(s), "
              f"RS {args.k}+{args.m} ~{red:.2f}x). bad sectors: {r['bad_sectors']}")
        if r["bad_sectors"]:
            print(f"  WARNING: {r['bad_sectors']} sectors unreadable at scan (zero-filled).")
    elif args.cmd == "image-verify":
        h = diskimage.verify(args.out)
        print(f"health: {'HEALTHY' if h['healthy'] else 'DAMAGED'}  "
              f"chunks={h['chunks']} corrupted_shards={h['corrupted_shards']} "
              f"repairable={h['repairable_chunks']} lost={h['lost_chunks']}")
    elif args.cmd == "image-heal":
        print(f"repaired {diskimage.heal(args.out)} shard(s)")
    elif args.cmd == "image-restore":
        n = diskimage.restore(args.out, args.dst)
        print(f"restored {n//1024} KB -> {args.dst}")


if __name__ == "__main__":
    main()