7.0 KiB
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:
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:
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):
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:
/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:
findmnt /storage
lsblk -f
Dry run using the captured JSONL:
python3 restore-tree.py /mnt/recovery/plex-directory-tree.jsonl \
--target /storage
If it looks correct, create the empty directories:
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.
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:
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.confor 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.) /Backupsroot entry survives, but inode496369665is zeroed and cannot be traversed. Use--include /Backupsplus any known child directories./mnt/recoveryis 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:
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.