feat: Add scripts

This commit is contained in:
keith committed 2026-09-29 06:46:40 -05:00
1 parent d15429f870
commit 3bccb79238
10 files changed
+1223

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
# Rebuilding the empty directory tree after LVM recovery
These scripts are **independent** of the file-salvage scripts. Their job is to
save the names (and, when available, numeric UID/GID/mode) of directories
**before you wipe the damaged array**, then recreate those empty directories
on the new `/storage` filesystem.
## Before rebuilding: export the surviving tree
The current damaged ext4 volume cannot mount normally. `capture-tree.py` uses
`debugfs -c` instead, just as the file-salvage scripts do. It enumerates
**directory metadata only**. It does not read or recover the file contents.
Keep the damaged LV read-only. Check:
```bash
blockdev --getro /dev/mapper/plex--new-plex--new # must print 1
findmnt /mnt/recovery # Passport must be mounted
```
Capture all *reachable* top-level directories and their descendants:
```bash
sudo python3 capture-tree.py \
--source / \
--include /Backups \
--output /mnt/recovery/plex-directory-tree.jsonl
```
`--include /Backups` adds that root manually because, in this incident, its
inode metadata is zeroed out. **It will not restore unknown children beneath
it.** Repeat `--include` for any additional known directory path you need to
exist later, including the share roots that have missing metadata.
To scan only selected trees (less time, but incomplete):
```bash
sudo python3 capture-tree.py \
--source /Media \
--source /Warez \
--include /Backups \
--output /mnt/recovery/plex-directory-tree.jsonl
```
This writes three files **to the Passport**:
- `plex-directory-tree.jsonl` — directory paths with owner/mode metadata (when
available); preferred input for the restore script.
- `plex-directory-tree.txt` — one absolute filesystem path per line, easy to
inspect or edit, and also valid restore input (without ownership/modes).
- `plex-directory-tree.warnings.txt` — unreadable directories and ambiguous
entries. **Read this** before deciding the capture is complete.
You can manually append known missing directory paths to the `.txt` file, or
re-run the capture with additional `--include` arguments. Save the resulting
three files somewhere safe besides the array you're about to rebuild.
**Important:** If a directory's inode or parent directory block was lost, no
metadata-only tool can reconstruct the unknown descendants. Keep a list of
missing Samba share roots and any especially important subdirectories to add
manually. If a directory entry's type is destroyed, `capture-tree.py` reports
it as ambiguous in the warnings file; it does *not* invent a directory.
## Alternative: use an existing full-path folder list
`restore-tree.py` also accepts a plain UTF-8 text file with **one full directory
path per line**, for example:
```text
/Media
/Media/Videos
/Media/Videos/Television
/Media/Videos/Television/Bob's Burgers
/Media/Videos/Movies
/Warez
/Warez/Books
/Backups
```
Folder paths may contain spaces and apostrophes; don't quote individual lines
in the text file. Empty directory parents are created automatically. An
indented/box-drawing `tree` diagram is **not** valid input; use `tree -dfi
--noreport` or `find ... -type d` output that prints a full path on each line.
If a working source had been mounted under `/storage`, a text export might
contain `/storage/Media/...` rather than `/Media/...`; see `--strip-prefix` below.
## After rebuilding: validate, dry-run, then apply
**Do not run `restore-tree.py --apply` until the newly rebuilt filesystem is
mounted at `/storage`.** The script refuses to apply if `/storage` is just an
unmounted directory on the OS disk. It also refuses symlinks/non-directories
along the destination path.
Verify that the *correct* filesystem is mounted:
```bash
findmnt /storage
lsblk -f
```
Dry run using the captured JSONL:
```bash
python3 restore-tree.py /mnt/recovery/plex-directory-tree.jsonl \
--target /storage
```
If it looks correct, create the empty directories:
```bash
sudo python3 restore-tree.py /mnt/recovery/plex-directory-tree.jsonl \
--target /storage --apply
```
**Optional:** Restore original numeric owners and permission modes where the
capture has valid metadata. Check that the same service accounts/UIDs exist on
the rebuilt host first. Files on the exFAT Passport do not themselves preserve
Unix owner/mode metadata; the JSONL does.
```bash
sudo python3 restore-tree.py /mnt/recovery/plex-directory-tree.jsonl \
--target /storage --apply --restore-metadata
```
Use `--expect-source /dev/mapper/<your-new-lv>` for an extra safety check if
you know the exact `findmnt` SOURCE string for the rebuilt array.
For an existing text list whose paths include the old mountpoint:
```bash
python3 restore-tree.py old-tree.txt --target /storage \
--strip-prefix /storage
sudo python3 restore-tree.py old-tree.txt --target /storage \
--strip-prefix /storage --apply
```
Re-running the restore is supported. Existing directories are retained.
`--restore-metadata` **does** change mode and owner of existing directories
listed in the snapshot, so use it deliberately.
## What this does **not** restore
- File contents, missing/unreachable directory names, symlinks, or hardlinks.
- Samba definitions (`smb.conf` or included shares), user accounts, ACLs, and
extended attributes. Those must already exist or be separately restored.
- Docker bind-mount configuration and application databases. Creating the
directory is not sufficient if a container also needs its files or metadata.
After restoration, verify the expected share root directories exist and have
appropriate permissions before starting services. For Samba, `testparm` can
validate the existing share configuration; directory creation alone cannot
recreate the share definitions.
## Recovery-incident particulars (Banshee)
- Damaged logical volume: `/dev/mapper/plex--new-plex--new`.
- Filesystem: ext4, 4,096-byte blocks.
- Previously calculated missing data-block range: `3418636288–4395389951`.
(The directory-tree scripts do **not** use this range; they simply traverse
readable directory metadata. See the main salvage README for dead-block
calculations.)
- `/Backups` root entry survives, but inode `496369665` is zeroed and cannot
be traversed. Use `--include /Backups` plus any known child directories.
- `/mnt/recovery` is the removable Passport; export onto that or another
device, **not** onto the array being rebuilt.
**Checkpoint:** Copy all three capture files off the recovery system and
review the warnings and share roots *before* formatting or recreating the LV.
Also make sure your existing Samba configuration and share definitions are
backed up independently. Useful commands before the rebuild:
```bash
testparm -s # inspect configured Samba shares and paths
cp -a /etc/samba /mnt/recovery/samba-config-pre-rebuild
```
If the new storage will be served by containers instead, independently save
their Compose files and bind-mount declarations. If the operating-system disk
isn't being rebuilt, those configurations may remain intact, but it's worth
having copies before changing the storage layout.