#!/usr/bin/env python3 # Simple Python 3 script to create a browsable file tree from a Proxmox pmxcfs config.db. # Can be used for restore tasks or other investigation purposes. import sqlite3 import os import argparse def create_hierarchy(db_file, output_dir): # Connect to the SQLite database conn = sqlite3.connect(db_file) cursor = conn.cursor() # Fetch all entries from the table cursor.execute("SELECT inode, parent, type, name, data, mtime FROM tree") entries = cursor.fetchall() # Close the connection conn.close() # Dictionary to map inodes to paths inode_map = {} os.makedirs(output_dir, exist_ok=True) # Function to resolve parent directory path recursively def get_parent_path(parent): if parent == 0: return output_dir if parent not in inode_map: parent_entry = next((e for e in entries if e[0] == parent), None) if parent_entry: inode_map[parent] = os.path.join(get_parent_path(parent_entry[1]), parent_entry[3]) os.makedirs(inode_map[parent], exist_ok=True) return inode_map.get(parent, output_dir) # Process directories first to ensure they exist for inode, parent, obj_type, name, data, mtime in entries: if obj_type == 4: # Directory parent_path = get_parent_path(parent) dir_path = os.path.join(parent_path, name) os.makedirs(dir_path, exist_ok=True) inode_map[inode] = dir_path os.utime(dir_path, (mtime, mtime)) # Process files for inode, parent, obj_type, name, data, mtime in entries: if obj_type == 8: # File parent_path = get_parent_path(parent) file_path = os.path.join(parent_path, name) with open(file_path, "wb") as f: f.write(data if data is not None else b"") os.utime(file_path, (mtime, mtime)) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Reconstruct filesystem from SQLite database") parser.add_argument("db_file", help="Path to the SQLite database file") parser.add_argument("output_dir", help="Path to the output directory") args = parser.parse_args() create_hierarchy(args.db_file, args.output_dir)