# 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/` 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.