Created
November 10, 2025 15:59
-
-
Save stone/e4d0611068c1a6adff2db6fa16743748 to your computer and use it in GitHub Desktop.
Did you delete your proxmox /etc/pve/nodes directory? Script to recreate pmxcfs database from a file backup (like etckeeper/restic..)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # 2025-11-09 - Initial version | |
| # Restore pmxcfs Proxmox config.db | |
| # (C) Frdrik Steen <fredrik@tty.se> | |
| set -euo pipefail | |
| # Configuration | |
| DB="${DB:-config.db}" | |
| # Place your node files in the BACKUP_DIR to restore | |
| BACKUP_DIR="${1:?Usage: $0 <backup_dir> [node_name]}" | |
| NODE="${2:-$(basename "$BACKUP_DIR")}" | |
| WRITER=0 | |
| VERSION=1 | |
| [[ -d "$BACKUP_DIR" ]] || { echo "Error: Directory $BACKUP_DIR not found"; exit 1; } | |
| echo "Restoring from: $BACKUP_DIR" | |
| echo "Node name: $NODE" | |
| echo "Database: $DB" | |
| echo | |
| # Get next available inode | |
| get_next_inode() { | |
| sqlite3 "$DB" "SELECT COALESCE(MAX(inode), 0) + 1 FROM tree;" | |
| } | |
| # Create directory entry in database | |
| create_dir() { | |
| local parent_inode="$1" | |
| local basename="$2" | |
| local inode | |
| local now | |
| now=$(date +%s) | |
| inode=$(get_next_inode) | |
| >&2 echo "Creating directory: $basename (inode=$inode, parent=$parent_inode)" | |
| sqlite3 "$DB" <<EOF | |
| INSERT INTO tree (inode, parent, version, writer, mtime, type, name, data) | |
| VALUES ($inode, $parent_inode, $VERSION, $WRITER, $now, 4, '$basename', NULL); | |
| EOF | |
| echo "$inode" | |
| } | |
| # Insert file into database | |
| insert_file() { | |
| local inode | |
| local now | |
| local parent_inode="$1" | |
| local basename="$2" | |
| local file_path="$3" | |
| if [[ ! -f "$file_path" ]]; then | |
| echo "Warning: $file_path not found, skipping" | |
| return | |
| fi | |
| inode=$(get_next_inode) | |
| now=$(date +%s) | |
| echo "Inserting file: $basename (inode=$inode, parent=$parent_inode)" | |
| sqlite3 "$DB" <<EOF | |
| INSERT INTO tree (inode, parent, version, writer, mtime, type, name, data) | |
| VALUES ($inode, $parent_inode, $VERSION, $WRITER, $now, 8, '$basename', readfile('$file_path')); | |
| EOF | |
| } | |
| # Recursively process directory | |
| process_directory() { | |
| local basename | |
| local subdir_inode | |
| local parent_inode="$1" | |
| local dir_path="$2" | |
| # Process files in current directory | |
| while IFS= read -r -d '' file; do | |
| basename=$(basename "$file") | |
| insert_file "$parent_inode" "$basename" "$file" | |
| done < <(find "$dir_path" -maxdepth 1 -type f -print0 | sort -z) | |
| # Process subdirectories | |
| while IFS= read -r -d '' subdir; do | |
| basename=$(basename "$subdir") | |
| subdir_inode=$(create_dir "$parent_inode" "$basename") | |
| process_directory "$subdir_inode" "$subdir" | |
| done < <(find "$dir_path" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) | |
| } | |
| echo "Creating nodes/$NODE directory structure..." | |
| # Create nodes/ directory if it doesn't exist | |
| NODES_INODE=$(sqlite3 "$DB" "SELECT inode FROM tree WHERE parent=0 AND name='nodes' LIMIT 1;") | |
| if [[ -z "$NODES_INODE" ]]; then | |
| NODES_INODE=$(create_dir 0 "nodes") | |
| fi | |
| # Create node directory under nodes/ | |
| NODE_INODE=$(create_dir "$NODES_INODE" "$NODE") | |
| echo "" | |
| echo "Processing files and directories..." | |
| # Process all content from backup directory | |
| process_directory "$NODE_INODE" "$BACKUP_DIR" | |
| echo "" | |
| echo "Restore complete!" | |
| echo "" | |
| echo "Database verification:" | |
| sqlite3 "$DB" "SELECT inode, parent, type, | |
| CASE type WHEN 4 THEN 'DIR' WHEN 8 THEN 'FILE' ELSE type END as type_name, | |
| name | |
| FROM tree | |
| WHERE inode >= $NODES_INODE | |
| ORDER BY inode;" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment