@vimanuelt @ericbsd
EXPERIMENT WITH at Devuan(OpenZFS 2.1) and import pool from GhostBSD(OpenZFS 2.2)

root@devuan:/home/freeartist-devuan/Downloads/zfs# zfs version
zfs-2.1.11-1+deb12u1
zfs-kmod-2.1.11-1+deb12u1
root@devuan:/home/freeartist-devuan/Downloads/zfs# 
root@devuan:/home/freeartist-devuan/Downloads/zfs# zpool import
   pool: zroot
     id: 16114756010544144606
  state: UNAVAIL
status: The pool uses the following feature(s) not supported on this system:
	com.klarasystems:vdev_zaps_v2
action: The pool cannot be imported. Access the pool on a system that supports
	the required feature(s), or recreate the pool from backup.
 config:

	zroot                       UNAVAIL  unsupported feature(s)
	  ata-ST3160310CS_9TS11TEW  ONLINE
root@devuan:/home/freeartist-devuan/Downloads/zfs# 

So i can presume that previous my fault with wiped zfs not related with version of zfs?
UPD
Here history of commands that i did before, what command from this list can did harm of my previous installed zfs disk?

root@devuan:/home/freeartist-devuan/Downloads/zfs# history | grep zroot
  138  zpool export zroot
  144  zpool import zroot -o readonly=on
  151  sudo zpool create zroot sdb2
  152  sudo zpool create zroot sdd2
  154  sudo zpool create zroot sdb2
  155  sudo zpool create zroot sdb2 -f
  159  zpool import -R /mnt zroot
  162  mount -t zfs pool/zroot/dataset /mnt
  163  mount -t zfs pool/zroot /mnt
  164  mount -t zfs zroot /mnt
  168  zpool import -R /mnt zroot
  173  zpool import -R /mnt -f zroot
  175  zpool export -af zroot
  176  zpool export zroot
  178  zpool import -R /mnt -f zroot
  183  ls zroot
  184  ls /zroot
  187  ls /zroot
  189  zfs mount zroot
  191  zfs mount zroot
  192  zfs remount zroot
  193  zfs umount zroot
  194  zfs remount zroot
  195  zfs mount zroot
  197  ls /zroot
  200  zfs  zroot
  203  zpool export zroot
  206  zpool import -R /mnt zroot
  211  zpool export zroot
  212  zfs set mountpoint=none zroot/ROOT
  216  zfs set mountpoint=none zroot/ROOT
  217  zfs set mountpoint=none zroot/ROOT
  220  zpool export zroot
  221  sudo zfs set mountpoint=/mnt/zfs/ROOT zroot/ROOT/default
  223  zfs get mountpoint,mounted zroot -t filesystem -r
  225  sudo zfs get mountpoint,mounted zroot -t filesystem -r
  226  zfs inherit mountpoint zroot/usr/home
  227  zfs inherit -r mountpoint zroot
  229  zpool export zroot
  231  sudo zfs get mountpoint,mounted zroot -t filesystem -r
  233  sudo zfs get mountpoint,mounted zroot -t filesystem -r
  235  ls /mnt/zroot
  237  zpool export zroot
  263  zpool set bootfs=zroot/root/zroot
  264  zpool set bootfs=zroot/root zroot
  266  zpool export zroot
  267  zpool import -R /mnt -o cachefile=/boot/zfs/zpool.cache zroot
  270  zpool import -N -f zroot
  271  zpool export zroot
  272  zpool import -N -f zroot
  281  zpool export zroot
  288  sudo zpool import -o readonly=on zroot
  289  sudo zpool status zroot
  291  sudo zfs list -t snapshot -r zroot/ROOT
  294  sudo zpool export zroot
  307  zpool import -af zroot
  308  zpool -af import zroot
  309  zpool import zroot
  310  zpool -f import zroot
  311  zpool import -f zroot
  313  sudo zfs mount zroot/ROOT/default
  314  zpool import -o altroot=/mnt -Nf zroot 
  318  zpool export zroot
  319  zpool import -R /mnt zroot
  321  zpool import -R /mnt zroot
  322  zpool import -R /mnt zroot_tmp
  323  zpool export zroot
  324  zpool import -f zroot
  326  zfs get all zroot |grep mountpoint
  328  zfs set mountpoint=legacy zroot
  329  mount -t zfs zroot /mnt
  331  zpool export zroot
  351  zpool import zroot
  352  zpool import -f zroot /mnt
  353  zpool import -f /mnt zroot
  354  zpool import -f zroot
  360  zdb -e zroot
  362  zdb -e zroot
  363  zdb -e zroot > zoot.txt
  365  zpool export zroot
  366  zpool -f export zroot
  367  zpool umount zroot
  368  zpool umount zroot
  369  zpool export zroot
  371  zpool export -ad zroot
  372  zpool export -af zroot
  373  zpool export -f zroot
  376  zpool export -f zroot
  377  zpool -f export zroot
  378  zpool export zroot
  379  zpool export zroot
  383  zpool export zroot
  385  zfs umount zroot
  386  umount zroot
  388  umount -r zfs zroot
  395  umount -l zroot
  397  zfs umount zroot/ROOT/default
  400  zfs umount zroot/tmp
  401  zfs umount zroot/var/log
  402  zfs umount -f zroot/var/log
  406  zpool export zroot
  411  zfs umount -f zroot/var/log
  416  zpool export -f zroot
  417  zpool export -F zroot
  418  lsof | grep zroot
  421  zpool export zroot
  423  sudo zpool status zroot
  425  sudo zfs mount zroot/ROOT/default
  427  sudo zfs mount zroot/ROOT/default /mnt
  431  zfs -fa umount zfs zroot
  432  zfs -fa umount zroot
  433  zfs -fa umount zroot
  451  mount | grep zroot
  452  zfs -fa umount zroot/gome
  453  zfs -fa umount zroot/home
  455  zfs -fa umount zroot/home
  456  zfs -f umount zroot/home
  457  zfs -f umount zroot
  458  mount | grep zroot
  459  zfs -f umount zroot/ROOT/default
  466  zfs -f umount zroot/ROOT/default
  467  zfs -f umount zroot/ROOT/
  468  zfs -f umount zroot
  469  zfs -f umount zroot/home
  473  zfs set canmount=noauto zroot
  474  zfs set canmount=noauto zroot
  476  zpool export zroot
  478  zfs set canmount=noauto zroot
  480  zpool export zroot
  500  zpool import -o readonly=on zroot
  501  zpool import -o -f readonly=on zroot
  503  zpool import -o -f readonly=on zroot
  504  zpool import -f zroot
  505  history | grep zroot
root@devuan:/home/freeartist-devuan/Downloads/zfs# 

    12sunflowers
    Ah, I see. Your ZFS pool was likely destroyed by this command: 155 sudo zpool create zroot sdb2 -f

    The -f flag forcibly overwrites existing ZFS metadata. Any prior pool on sdb2 was wiped at that point.

    The zpool import error about com.klarasystems:vdev_zaps_v2 confirms GhostBSD upgraded the pool to OpenZFS 2.2, which Devuan (OpenZFS 2.1) cannot read — but this mismatch did not cause the data loss.

    The data loss was caused by using zpool create on a disk that still held a valid pool.

      Here's a script that performs all recovery-safe steps using zdb and zpool import. It captures output, avoids any writes by default, and tells you what to do next.

      #!/bin/bash
      # zfs-recover-check.sh – Safely analyze and check recoverability of a damaged ZFS pool
      
      set -euo pipefail
      
      POOL="zroot"
      DISK="/dev/sdb2"  # Adjust if your overwritten device is different
      OUTDIR="./zfs_recovery_$(date +%Y%m%d_%H%M%S)"
      mkdir -p "$OUTDIR"
      
      echo "[*] Step 1: Running zdb to scan pool metadata (non-destructive)..."
      zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2>&1 || echo "[!] zdb failed (check $OUTDIR/zdb-output.txt)"
      
      echo "[*] Step 2: Attempting dry-run rollback import (read-only)..."
      zpool import -F -n -o readonly=on "$POOL" > "$OUTDIR/zpool-dryrun.txt" 2>&1 || echo "[!] zpool import dry-run failed (check $OUTDIR/zpool-dryrun.txt)"
      
      echo
      echo "=== SUMMARY ==="
      echo "Output saved to: $OUTDIR"
      echo
      echo "• Review zdb output:        less $OUTDIR/zdb-output.txt"
      echo "• Review rollback preview:  less $OUTDIR/zpool-dryrun.txt"
      echo
      echo "If rollback looks safe, you can try:"
      echo "  sudo zpool import -F -o readonly=on $POOL"
      echo
      echo "Then list datasets with:"
      echo "  sudo zfs list -r $POOL"

        vimanuelt Here's a script that performs all recovery-safe steps using zdb and zpool import. It captures output, avoids any writes by default, and tells you what to do next.

        Hi. I will try new script today here with Openzfs 2.3 and report how it work

        Here is an updated version of the script.

        #!/bin/bash
        # zfs-recover-check.sh – Safely analyze and check recoverability of a damaged ZFS pool
        
        set -euo pipefail
        
        # === USER CONFIGURABLE ===
        POOL="zroot"
        DEVICE_HINT="/dev"  # Change to /dev/disk/by-id or other path if needed
        # ==========================
        
        TIMESTAMP=$(date +%Y%m%d_%H%M%S)
        OUTDIR="./zfs_recovery_$TIMESTAMP"
        mkdir -p "$OUTDIR"
        
        log()     { echo "[*] $1"; }
        warn()    { echo "[!] $1" >&2; }
        fail_exit() { warn "$1"; exit 1; }
        
        command -v zdb >/dev/null 2>&1 || fail_exit "zdb not found. Please install OpenZFS utilities."
        command -v zpool >/dev/null 2>&1 || fail_exit "zpool not found. Please install OpenZFS utilities."
        
        log "Step 1: Attempting to locate pool '$POOL'..."
        if ! zpool import | grep -q "$POOL"; then
          warn "Pool '$POOL' not found in system import list."
          log "Attempting to scan pool by device path: $DEVICE_HINT..."
          if ! zpool import -d "$DEVICE_HINT" | grep -q "$POOL"; then
            warn "Pool still not found via 'zpool import -d $DEVICE_HINT'. Trying anyway..."
          else
            log "Pool '$POOL' found via device scan."
          fi
        else
          log "Pool '$POOL' is visible to zpool import."
        fi
        
        log "Step 2: Running zdb to analyze pool metadata (non-destructive)..."
        if ! zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2>&1; then
          warn "zdb failed. Metadata may be corrupt. See: $OUTDIR/zdb-output.txt"
        else
          log "zdb scan complete. Output: $OUTDIR/zdb-output.txt"
        fi
        
        log "Step 3: Attempting dry-run rollback import (read-only)..."
        if ! zpool import -F -n -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-dryrun.txt" 2>&1; then
          warn "Dry-run rollback failed. Review: $OUTDIR/zpool-dryrun.txt"
        else
          log "Dry-run rollback complete. Output: $OUTDIR/zpool-dryrun.txt"
        fi
        
        echo
        echo "=== SUMMARY ==="
        echo "Output directory: $OUTDIR"
        echo
        echo "• zdb output:       less $OUTDIR/zdb-output.txt"
        echo "• rollback preview: less $OUTDIR/zpool-dryrun.txt"
        echo
        echo "To attempt actual import:"
        echo "  sudo zpool import -F -o readonly=on -d $DEVICE_HINT $POOL"
        echo
        echo "Then list datasets:"
        echo "  sudo zfs list -r $POOL"

          Here is another recovery script.

          #!/bin/bash
          # zfs-recovery-toolkit.sh – ZFS recovery script with detailed logs and structured report
          
          set -euo pipefail
          
          # === USER CONFIGURABLE ===
          POOL="zroot"
          DEVICE_HINT="/dev"
          AUTO_IMPORT="yes"
          AUTO_EXPORT="yes"
          # ==========================
          
          TIMESTAMP=$(date +%Y%m%d_%H%M%S)
          HOST=$(hostname)
          OUTDIR="./zfs_recovery_$TIMESTAMP"
          REPORT="$OUTDIR/recovery-report.txt"
          mkdir -p "$OUTDIR"
          
          log()     { echo "[*] $1"; echo "[*] $1" >> "$REPORT"; }
          warn()    { echo "[!] $1" >&2; echo "[!] $1" >> "$REPORT"; }
          error()   { echo "[ERROR] $1" >&2; echo "[ERROR] $1" >> "$REPORT"; }
          fail_exit() { error "$1"; echo "[ABORTED]" >> "$REPORT"; exit 1; }
          
          # === BEGIN REPORT ===
          echo "ZFS Recovery Report" > "$REPORT"
          echo "===================" >> "$REPORT"
          echo "Host: $HOST" >> "$REPORT"
          echo "Time: $(date)" >> "$REPORT"
          echo "Pool: $POOL" >> "$REPORT"
          echo "Device Hint: $DEVICE_HINT" >> "$REPORT"
          echo "Output Dir: $OUTDIR" >> "$REPORT"
          echo >> "$REPORT"
          
          for cmd in zdb zpool zfs; do
            if ! command -v "$cmd" >/dev/null 2>&1; then
              fail_exit "Required command '$cmd' not found."
            fi
          done
          
          log "Step 1: Checking pool visibility..."
          if ! zpool import 2> "$OUTDIR/import-visible-error.txt" | grep -q "$POOL"; then
            warn "Pool not found in import list. Attempting device scan..."
            if ! zpool import -d "$DEVICE_HINT" > "$OUTDIR/import-scan.txt" 2> "$OUTDIR/import-scan-error.txt"; then
              warn "zpool import scan failed. See $OUTDIR/import-scan-error.txt"
            fi
            if grep -q "$POOL" "$OUTDIR/import-scan.txt"; then
              log "Pool found via device hint."
            else
              warn "Pool not found even with device scan. Proceeding cautiously."
            fi
          else
            log "Pool is visible via normal import."
          fi
          
          log "Step 2: zdb metadata analysis..."
          if ! zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2> "$OUTDIR/zdb-error.txt"; then
            warn "zdb failed. Output: $OUTDIR/zdb-error.txt"
          else
            log "zdb completed. Output: $OUTDIR/zdb-output.txt"
          fi
          
          log "Step 3: Dry-run import with rollback..."
          if ! zpool import -F -n -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-dryrun.txt" 2> "$OUTDIR/zpool-dryrun-error.txt"; then
            warn "Dry-run import failed. See $OUTDIR/zpool-dryrun-error.txt"
          else
            log "Dry-run succeeded. Output: $OUTDIR/zpool-dryrun.txt"
          fi
          
          if [[ "$AUTO_IMPORT" == "yes" ]]; then
            if zpool list | grep -q "^$POOL"; then
              log "Pool already imported. Skipping real import."
            else
              log "Step 4: Performing read-only import..."
              if ! zpool import -F -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-import.txt" 2> "$OUTDIR/zpool-import-error.txt"; then
                warn "Import failed. Output: $OUTDIR/zpool-import-error.txt"
              else
                log "Import completed. Output: $OUTDIR/zpool-import.txt"
              fi
            fi
          
            log "Step 5: Listing datasets..."
            if ! zfs list -r "$POOL" > "$OUTDIR/zfs-list.txt" 2> "$OUTDIR/zfs-list-error.txt"; then
              warn "Dataset listing failed. Output: $OUTDIR/zfs-list-error.txt"
            else
              log "Datasets listed. Output: $OUTDIR/zfs-list.txt"
            fi
          
            if [[ "$AUTO_EXPORT" == "yes" ]]; then
              log "Step 6: Exporting pool..."
              if ! zpool export "$POOL" > "$OUTDIR/zpool-export.txt" 2> "$OUTDIR/zpool-export-error.txt"; then
                warn "Export failed. Output: $OUTDIR/zpool-export-error.txt"
              else
                log "Export successful. Output: $OUTDIR/zpool-export.txt"
              fi
            else
              log "AUTO_EXPORT disabled. Pool remains imported (read-only)."
            fi
          else
            log "AUTO_IMPORT disabled. No changes made to system."
          fi
          
          # === FINAL SUMMARY ===
          echo >> "$REPORT"
          echo "Final Notes:" >> "$REPORT"
          echo "  - All operations performed in non-destructive or read-only mode." >> "$REPORT"
          echo "  - No datasets were mounted." >> "$REPORT"
          echo "  - All logs saved under: $OUTDIR" >> "$REPORT"
          echo >> "$REPORT"
          echo "[COMPLETED]" >> "$REPORT"
          
          log "Recovery completed. Review $REPORT for summary."

          vimanuelt Ah, I see. Your ZFS pool was likely destroyed by this command: 155 sudo zpool create zroot sdb2 -f

          Thanks a lot that you find my mistake.
          Beacause i have old version of zfs at linux i sew that message
          UNAVAIL unsupported feature(s)
          i am starting google and by bruteforce tried mount pool, all my mistakes did damage zfs. That all because i have no any idea that problem in different versions of zfs.

          5 days later

          vimanuelt Here is an updated version of the script.

          Hi. Sorry for late report.
          Just only now can starting test yours script ( i am tired from fighting with *nix 🙂 )
          http://0x0.st/8Jqa.zip
          seem all worked?
          why process take about 30min here?

            12sunflowers
            Here is an updated script.

            #!/bin/bash
            # zfs-recovery-toolkit.sh – ZFS recovery script with detailed logs and structured report
            
            set -euo pipefail
            
            # === USER CONFIGURABLE ===
            POOL="${1:-zroot}"
            DEVICE_HINT="${2:-/dev}"
            AUTO_IMPORT="yes"
            AUTO_EXPORT="yes"
            # ==========================
            
            TIMESTAMP=$(date +%Y%m%d_%H%M%S)
            HOST=$(hostname)
            OUTDIR="./zfs_recovery_${POOL}_${TIMESTAMP}"
            REPORT="$OUTDIR/recovery-report.txt"
            mkdir -p "$OUTDIR"
            
            log()     { echo "[*] $(date +%H:%M:%S) $1"; echo "[*] $1" >> "$REPORT"; }
            warn()    { echo "[!] $(date +%H:%M:%S) $1" >&2; echo "[!] $1" >> "$REPORT"; }
            error()   { echo "[✘] $(date +%H:%M:%S) $1" >&2; echo "[ERROR] $1" >> "$REPORT"; }
            fail_exit() { error "$1"; echo "[ABORTED]" >> "$REPORT"; exit 1; }
            
            # === BEGIN REPORT ===
            {
            echo "ZFS Recovery Report"
            echo "==================="
            echo "Host: $HOST"
            echo "Time: $(date)"
            echo "Pool: $POOL"
            echo "Device Hint: $DEVICE_HINT"
            echo "Output Dir: $OUTDIR"
            echo
            } > "$REPORT"
            
            # --- Check for required commands ---
            for cmd in zdb zpool zfs; do
              if ! command -v "$cmd" >/dev/null 2>&1; then
                fail_exit "Required command '$cmd' not found in PATH."
              fi
            done
            
            log "Step 1: Checking pool visibility..."
            if ! zpool import 2> "$OUTDIR/import-visible-error.txt" | grep -q "$POOL"; then
              warn "Pool not found in normal import list. Trying device hint scan..."
              if ! zpool import -d "$DEVICE_HINT" > "$OUTDIR/import-scan.txt" 2> "$OUTDIR/import-scan-error.txt"; then
                warn "Device scan failed. See $OUTDIR/import-scan-error.txt"
              fi
              if grep -q "$POOL" "$OUTDIR/import-scan.txt"; then
                log "Pool '$POOL' found via device hint."
              else
                warn "Pool '$POOL' still not found. Continuing with caution."
              fi
            else
              log "Pool '$POOL' is visible via normal import."
            fi
            
            log "Step 2: Analyzing pool metadata with zdb..."
            if ! zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2> "$OUTDIR/zdb-error.txt"; then
              warn "zdb failed. Output saved to $OUTDIR/zdb-error.txt"
            else
              log "zdb completed successfully. Output: $OUTDIR/zdb-output.txt"
            fi
            
            log "Step 3: Attempting dry-run import with rollback enabled..."
            if ! zpool import -F -n -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-dryrun.txt" 2> "$OUTDIR/zpool-dryrun-error.txt"; then
              warn "Dry-run import failed. See $OUTDIR/zpool-dryrun-error.txt"
            else
              log "Dry-run import succeeded. See $OUTDIR/zpool-dryrun.txt"
            fi
            
            if [[ "$AUTO_IMPORT" == "yes" ]]; then
              if zpool list | grep -q "^$POOL"; then
                log "Pool '$POOL' already imported. Skipping read-only import."
              else
                log "Step 4: Performing safe read-only import..."
                if ! zpool import -F -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-import.txt" 2> "$OUTDIR/zpool-import-error.txt"; then
                  warn "Read-only import failed. See $OUTDIR/zpool-import-error.txt"
                else
                  log "Read-only import succeeded. See $OUTDIR/zpool-import.txt"
                fi
              fi
            
              log "Step 5: Listing datasets..."
              if ! zfs list -r "$POOL" > "$OUTDIR/zfs-list.txt" 2> "$OUTDIR/zfs-list-error.txt"; then
                warn "Dataset listing failed. See $OUTDIR/zfs-list-error.txt"
              else
                log "Datasets listed successfully. See $OUTDIR/zfs-list.txt"
              fi
            
              if [[ "$AUTO_EXPORT" == "yes" ]]; then
                log "Step 6: Exporting the pool..."
                if ! zpool export "$POOL" > "$OUTDIR/zpool-export.txt" 2> "$OUTDIR/zpool-export-error.txt"; then
                  warn "Export failed. See $OUTDIR/zpool-export-error.txt"
                else
                  log "Export completed successfully. See $OUTDIR/zpool-export.txt"
                fi
              else
                log "AUTO_EXPORT disabled. Pool remains imported (read-only)."
              fi
            else
              log "AUTO_IMPORT disabled. No pool was imported."
            fi
            
            # --- Final Summary ---
            {
            echo
            echo "Final Notes:"
            echo "  - All operations were performed in non-destructive read-only mode."
            echo "  - No datasets were mounted."
            echo "  - Log and output directory: $OUTDIR"
            echo "[COMPLETED]"
            } >> "$REPORT"
            
            log "Recovery script completed. Review $REPORT for details."

              Thank you for the feedback. Here is an updated script.

              #!/bin/bash
              # zfs-recovery-toolkit.sh – ZFS recovery script with detailed logs and structured report
              
              set -euo pipefail
              
              # === USER CONFIGURABLE ===
              POOL="${1:-zroot}"
              DEVICE_HINT="${2:-/dev}"
              AUTO_IMPORT="yes"
              AUTO_EXPORT="yes"
              # ==========================
              
              TIMESTAMP=$(date +%Y%m%d_%H%M%S)
              HOST=$(hostname)
              OUTDIR="./zfs_recovery_${POOL}_${TIMESTAMP}"
              REPORT="$OUTDIR/recovery-report.txt"
              mkdir -p "$OUTDIR"
              
              log()     { echo "[*] $(date +%H:%M:%S) $1"; echo "[*] $1" >> "$REPORT"; }
              warn()    { echo "[!] $(date +%H:%M:%S) $1" >&2; echo "[!] $1" >> "$REPORT"; }
              error()   { echo "[✘] $(date +%H:%M:%S) $1" >&2; echo "[ERROR] $1" >> "$REPORT"; }
              fail_exit() { error "$1"; echo "[ABORTED]" >> "$REPORT"; exit 1; }
              
              # === BEGIN REPORT ===
              {
              echo "ZFS Recovery Report"
              echo "==================="
              echo "Host: $HOST"
              echo "Time: $(date)"
              echo "Pool: $POOL"
              echo "Device Hint: $DEVICE_HINT"
              echo "Output Dir: $OUTDIR"
              echo
              } > "$REPORT"
              
              # --- Check for required commands ---
              for cmd in zdb zpool zfs; do
                if ! command -v "$cmd" >/dev/null 2>&1; then
                  fail_exit "Required command '$cmd' not found in PATH."
                fi
              done
              
              log "Step 1: Checking pool visibility..."
              if ! zpool import 2> "$OUTDIR/import-visible-error.txt" | grep -q "$POOL"; then
                warn "Pool not found in normal import list. Trying device hint scan..."
                if ! zpool import -d "$DEVICE_HINT" > "$OUTDIR/import-scan.txt" 2> "$OUTDIR/import-scan-error.txt"; then
                  warn "Device scan failed. See $OUTDIR/import-scan-error.txt"
                fi
                if grep -q "$POOL" "$OUTDIR/import-scan.txt"; then
                  log "Pool '$POOL' found via device hint."
                else
                  warn "Pool '$POOL' still not found. Continuing with caution."
                fi
              else
                log "Pool '$POOL' is visible via normal import."
              fi
              
              log "Step 2: Analyzing pool metadata with zdb..."
              if ! zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2> "$OUTDIR/zdb-error.txt"; then
                warn "zdb failed. Output saved to $OUTDIR/zdb-error.txt"
              else
                log "zdb completed successfully. Output: $OUTDIR/zdb-output.txt"
              fi
              
              log "Step 3: Attempting dry-run import with rollback enabled..."
              if ! zpool import -F -n -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-dryrun.txt" 2> "$OUTDIR/zpool-dryrun-error.txt"; then
                warn "Dry-run import failed. See $OUTDIR/zpool-dryrun-error.txt"
              else
                log "Dry-run import succeeded. See $OUTDIR/zpool-dryrun.txt"
              fi
              
              if [[ "$AUTO_IMPORT" == "yes" ]]; then
                if zpool list | grep -q "^$POOL"; then
                  log "Pool '$POOL' already imported. Skipping read-only import."
                else
                  log "Step 4: Performing safe read-only import..."
                  if ! zpool import -F -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-import.txt" 2> "$OUTDIR/zpool-import-error.txt"; then
                    warn "Read-only import failed. See $OUTDIR/zpool-import-error.txt"
                  else
                    log "Read-only import succeeded. See $OUTDIR/zpool-import.txt"
                  fi
                fi
              
                log "Step 5: Listing datasets..."
                if ! zfs list -r "$POOL" > "$OUTDIR/zfs-list.txt" 2> "$OUTDIR/zfs-list-error.txt"; then
                  warn "Dataset listing failed. See $OUTDIR/zfs-list-error.txt"
                else
                  log "Datasets listed successfully. See $OUTDIR/zfs-list.txt"
                fi
              
                if [[ "$AUTO_EXPORT" == "yes" ]]; then
                  log "Step 6: Exporting the pool..."
                  if ! zpool export "$POOL" > "$OUTDIR/zpool-export.txt" 2> "$OUTDIR/zpool-export-error.txt"; then
                    warn "Export failed. See $OUTDIR/zpool-export-error.txt"
                  else
                    log "Export completed successfully. See $OUTDIR/zpool-export.txt"
                  fi
                else
                  log "AUTO_EXPORT disabled. Pool remains imported (read-only)."
                fi
              else
                log "AUTO_IMPORT disabled. No pool was imported."
              fi
              
              # --- Final Summary ---
              {
              echo
              echo "Final Notes:"
              echo "  - All operations were performed in non-destructive read-only mode."
              echo "  - No datasets were mounted."
              echo "  - Log and output directory: $OUTDIR"
              echo "[COMPLETED]"
              } >> "$REPORT"
              
              log "Recovery script completed. Review $REPORT for details."

                12sunflowers
                Here is an updated script that safely inspects and optionally imports a ZFS pool in a read-only, non-destructive manner. It is designed to assist in data recovery when a pool appears damaged, was not cleanly exported, or is being accessed from a different system.

                #!/bin/bash
                # zfs-recovery-toolkit.sh – ZFS recovery script with detailed logs and structured report
                
                set -euo pipefail
                
                # === USER CONFIGURABLE ===
                POOL="${1:-zroot}"
                DEVICE_HINT="${2:-/dev}"
                DEBUG="${3:-no}"
                AUTO_IMPORT="yes"
                AUTO_EXPORT="yes"
                # ==========================
                
                # --- Check for required commands early ---
                for cmd in zdb zpool zfs; do
                  command -v "$cmd" >/dev/null 2>&1 || {
                    echo "[✘] Required command '$cmd' not found in PATH." >&2; exit 1;
                  }
                done
                
                [[ -z "$POOL" ]] && { echo "[✘] POOL name must not be empty." >&2; exit 1; }
                [[ ! -d "$DEVICE_HINT" ]] && { echo "[✘] DEVICE_HINT '$DEVICE_HINT' is not a valid directory." >&2; exit 1; }
                
                [[ "$DEBUG" == "yes" ]] && set -x
                
                TIMESTAMP=$(date +%Y%m%d_%H%M%S)
                HOST=$(hostname)
                OUTDIR="./zfs_recovery_${POOL}_${TIMESTAMP}"
                REPORT="$OUTDIR/recovery-report.txt"
                mkdir -p "$OUTDIR"
                
                log()     { echo "[*] $(date +%H:%M:%S) $1"; echo "[*] $1" >> "$REPORT"; }
                warn()    { echo "[!] $(date +%H:%M:%S) $1" >&2; echo "[!] $1" >> "$REPORT"; }
                error()   { echo "[✘] $(date +%H:%M:%S) $1" >&2; echo "[ERROR] $1" >> "$REPORT"; }
                fail_exit() { error "$1"; echo "[ABORTED]" >> "$REPORT"; exit 1; }
                
                trap 'echo "[!] Script interrupted or failed unexpectedly." >> "$REPORT"' EXIT
                
                # === DISCLAIMER ===
                echo "WARNING: This tool performs low-level read-only inspection of ZFS pools." >&2
                echo "It attempts recovery in a non-destructive manner, but improper use may risk data loss." >&2
                echo "Ensure you have backups. Continue at your own risk." >&2
                read -rp "Do you wish to continue? (yes/no): " CONFIRM
                if [[ "$CONFIRM" != "yes" ]]; then
                  fail_exit "User aborted script."
                fi
                
                # === BEGIN REPORT ===
                {
                echo "ZFS Recovery Report"
                echo "==================="
                echo "Host: $HOST"
                echo "Time: $(date)"
                echo "Pool: $POOL"
                echo "Device Hint: $DEVICE_HINT"
                echo "Output Dir: $OUTDIR"
                echo
                echo "Disclaimer acknowledged: user chose to proceed."
                } > "$REPORT"
                
                log "Step 1: Checking pool visibility..."
                if ! zpool import 2> "$OUTDIR/import-visible-error.txt" | grep -q "$POOL"; then
                  warn "Pool not found in normal import list. Trying device hint scan..."
                  if ! zpool import -d "$DEVICE_HINT" > "$OUTDIR/import-scan.txt" 2> "$OUTDIR/import-scan-error.txt"; then
                    warn "Device scan failed. See $OUTDIR/import-scan-error.txt"
                  fi
                  if grep -q "$POOL" "$OUTDIR/import-scan.txt"; then
                    log "Pool '$POOL' found via device hint."
                  else
                    warn "Pool '$POOL' still not found. Continuing with caution."
                  fi
                else
                  log "Pool '$POOL' is visible via normal import."
                fi
                
                log "Step 2: Analyzing pool metadata with zdb..."
                if ! zdb -e -bcsvL "$POOL" > "$OUTDIR/zdb-output.txt" 2> "$OUTDIR/zdb-error.txt"; then
                  warn "zdb failed. Output saved to $OUTDIR/zdb-error.txt"
                else
                  log "zdb completed successfully. Output: $OUTDIR/zdb-output.txt"
                fi
                
                log "Step 3: Checking for dirty log state (ZIL)..."
                ZPOOL_LOGSTATE=$(zpool import -l -d "$DEVICE_HINT" "$POOL" 2>&1 || true)
                echo "$ZPOOL_LOGSTATE" > "$OUTDIR/zpool-logstate.txt"
                if echo "$ZPOOL_LOGSTATE" | grep -q "logs with pending data"; then
                  warn "Pool has uncommitted ZIL. Consider import with -FX."
                  read -rp "Attempt destructive rollback using -FX (last resort)? (yes/no): " DO_FX
                  if [[ "$DO_FX" == "yes" ]]; then
                    log "Attempting forced rollback with -FX..."
                    if ! zpool import -FX -o readonly=on -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-import-FX.txt" 2>&1; then
                      warn "-FX import failed. See $OUTDIR/zpool-import-FX.txt"
                    else
                      log "-FX import succeeded."
                    fi
                  fi
                fi
                
                log "Step 4: Attempting dry-run import with rollback enabled..."
                DRYRUN_OUTPUT=$(zpool import -F -n -o readonly=on -d "$DEVICE_HINT" "$POOL" 2>&1 || true)
                echo "$DRYRUN_OUTPUT" > "$OUTDIR/zpool-dryrun-error.txt"
                if echo "$DRYRUN_OUTPUT" | grep -q "was previously in use from another system"; then
                  warn "Pool was used on another system. Attempting forced read-only import."
                elif echo "$DRYRUN_OUTPUT" | grep -q "cannot import"; then
                  warn "Dry-run import failed. See $OUTDIR/zpool-dryrun-error.txt"
                else
                  log "Dry-run import succeeded."
                fi
                
                if [[ "$AUTO_IMPORT" == "yes" ]]; then
                  if zpool list | grep -q "^$POOL"; then
                    log "Pool '$POOL' already imported. Skipping read-only import."
                  else
                    log "Step 5: Performing safe read-only import..."
                    if ! zpool import -F -o readonly=on -f -d "$DEVICE_HINT" "$POOL" > "$OUTDIR/zpool-import.txt" 2> "$OUTDIR/zpool-import-error.txt"; then
                      warn "Read-only import failed. See $OUTDIR/zpool-import-error.txt"
                    else
                      log "Read-only import succeeded. See $OUTDIR/zpool-import.txt"
                    fi
                  fi
                
                  log "Step 6: Listing datasets..."
                  if ! zfs list -r "$POOL" > "$OUTDIR/zfs-list.txt" 2> "$OUTDIR/zfs-list-error.txt"; then
                    warn "Dataset listing failed. See $OUTDIR/zfs-list-error.txt"
                  else
                    log "Datasets listed successfully. See $OUTDIR/zfs-list.txt"
                  fi
                
                  if [[ "$AUTO_EXPORT" == "yes" ]]; then
                    log "Step 7: Exporting the pool..."
                    if ! zpool export "$POOL" > "$OUTDIR/zpool-export.txt" 2> "$OUTDIR/zpool-export-error.txt"; then
                      warn "Export failed. See $OUTDIR/zpool-export-error.txt"
                    else
                      log "Export completed successfully. See $OUTDIR/zpool-export.txt"
                    fi
                  else
                    log "AUTO_EXPORT disabled. Pool remains imported (read-only)."
                  fi
                else
                  log "AUTO_IMPORT disabled. No pool was imported."
                fi
                
                # --- Final Summary ---
                {
                echo
                echo "Final Notes:"
                echo "  - All operations were performed in non-destructive read-only mode."
                echo "  - No datasets were mounted."
                echo "  - Log and output directory: $OUTDIR"
                echo "[COMPLETED]"
                } >> "$REPORT"
                
                log "Recovery script completed. Review $REPORT for details."
                echo "{\"status\":\"complete\",\"pool\":\"$POOL\",\"output\":\"$OUTDIR\"}" > "$OUTDIR/summary.json"

                  12sunflowers
                  The pool is depicted as being:

                  • Structurally sound.
                  • Readable.
                  • The datasets and metadata show no red flags.