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

7.0 KiB
Raw Blame History

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.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:

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.