Files
LVM-Recovery-09.2026/README-tree-rebuild.md
T
2026-09-29 06:46:40 -05:00

183 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.