前置知识要求:Linux 基础命令、文件系统概念、bash 基础
实验环境:CentOS Stream 9 / Ubuntu 24.04 LTS,kernel 6.8+
1 文件删除的内核级原理
1.1 Linux 文件系统的删除机制
在 Linux 中,文件的删除并不等同于数据的抹除。理解这一点是恢复误删文件的前提。
当执行 rm 命令时,实际发生的事件链如下:
# 查看文件 inode 信息
ls -li /etc/passwd
# 输出示例:1310731 -rw-r--r-- 1 root root 2342 Jan 15 09:30 /etc/passwd
# ^^^^ inode 号
rm 命令的执行流程:
-
解链接:从目录树中移除文件名到 inode 的映射关系 -
链接计数递减:inode 的链接计数(link count)减 1 -
数据块标记释放:当链接计数归零且无进程持有该文件句柄时,释放数据块 -
inode 回收:inode 被标记为空闲,可被新文件复用
关键点:数据块的内容在物理层面并未立即覆写。只有当新数据写入时覆盖了这些块,原始数据才真正消失。
1.2 硬链接与软链接对删除的影响
# 创建硬链接
ln /data/important.txt /backup/important.txt
# 创建软链接
ln -s /data/important.txt /backup/important_link.txt
# 查看链接信息
ls -li /data/important.txt /backup/important.txt /backup/important_link.txt
硬链接特性:
-
共享同一个 inode -
删除任一链接,文件内容依然存在 -
不能跨文件系统 -
不能链接目录
软链接特性:
-
是独立文件,存储目标路径 -
删除原文件后,软链接成为死链接 -
可以跨文件系统 -
可以链接目录
1.3 进程持有与文件句柄
即使文件从目录树中移除,只要仍有进程持有该文件描述符,文件数据就不会被回收:
# 模拟场景:进程持有已删除文件
tail -f /var/log/nginx/access.log &
# 此时删除该日志文件
# 查看进程持有的已删除文件
lsof +L1
# 输出示例:
# tail 12345 root 4r REG 252,1 1048576 0 /var/log/nginx/access.log (deleted)
# 从 /proc 恢复数据
cat /proc/12345/fd/4 > /tmp/recovered.log
这个机制是利用 lsof 恢复误删文件的核心原理。
2 ext 系列文件系统的恢复工具
2.1 extundelete 工作原理
extundelete 是专门针对 ext3/ext4 文件系统的恢复工具,通过分析文件系统日志和inode表来恢复删除的文件。
安装步骤:
# CentOS/RHEL 系列
sudo dnf install extundelete -y
# Ubuntu/Debian 系列
sudo apt update
sudo apt install extundelete -y
# 源码编译安装(推荐,版本最新)
cd /tmp
wget https://nchc.dl.sourceforge.net/project/extundelete/extundelete/0.2.4/extundelete-0.2.4.tar.bz2
tar xjf extundelete-0.2.4.tar.bz2
cd extundelete-0.2.4
./configure
make && sudo make install
# 验证安装
extundelete --version
2.2 实战:extundelete 恢复 ext4 分区文件
实验环境准备:
# 创建测试分区(生产环境切勿在生产盘操作!)
sudo dd if=/dev/zero of=/tmp/testdisk.img bs=1M count=100
sudo mkfs.ext4 -F /tmp/testdisk.img
mkdir -p /mnt/testmount
sudo mount -o loop /tmp/testdisk.img /mnt/testmount
# 创建测试数据
sudo mkdir -p /mnt/testmount/data
echo "这是重要的配置文件" | sudo tee /mnt/testmount/data/config.yaml
echo "数据库密码: MySecretPass123" | sudo tee /mnt/testmount/data/db.conf
echo "业务数据文件内容" | sudo tee /mnt/testmount/data/business.json
ls -la /mnt/testmount/data/
模拟误删场景:
# 误删文件
sudo rm -rf /mnt/testmount/data/*
echo "文件已误删"
# 立即卸载分区(重要:阻止数据块被覆写)
sudo umount /mnt/testmount
执行恢复操作:
# 查看可恢复的 inode 信息
sudo extundelete /tmp/testdisk.img --inode 2
# 输出示例:
# Directory block 8194:
# File name | File length | Deletion time | File type
# . 4096 2024-01-15 10:30:00 2024-01-15 10:35:00 directory
# .. 4096 2024-01-15 10:30:00 2024-01-15 10:35:00 directory
# data 4096 2024-01-15 10:30:00 2024-01-15 10:35:00 directory
# 恢复整个目录
sudo extundelete /tmp/testdisk.img --restore-directory /data
# 恢复文件到当前目录的 REVERTED_FILES/ 下
# 恢复单个文件
sudo extundelete /tmp/testdisk.img --restore-file data/config.yaml
# 按时间范围恢复
sudo extundelete /tmp/testdisk.img --restore-all --after 2024-01-15 10:00:00 --before 2024-01-15 11:00:00
# 查看恢复结果
ls -la REVERTED_FILES/
2.3 extundelete 恢复原理详解
extundelete 的恢复依赖于 ext4 文件系统的以下特性:
日志机制:ext4 采用日志文件系统(journal),删除操作会记录到日志中。extundelete 通过重放日志条目来还原删除信息。
inode 缓存:文件删除时,inode 不会立即被回收。extundelete 扫描 inode table,找到标记为”已删除”但数据块尚未覆写的 inode。
# 查看文件系统的块组信息(理解 inode 分布)
dumpe2fs -h /tmp/testdisk.img | grep -E "Block size|Inode size|Inode count|Block count"
数据块寻址:ext4 采用多级索引方式管理数据块:
# ext4 数据块寻址结构(简化)
# inode 中存储:
# - 12 个直接块指针(每个指向 4KB 数据)
# - 1 个一级间接块指针
# - 1 个二级间接块指针
# - 1 个三级间接块指针
# 最大文件大小计算:
# 直接块:12 * 4KB = 48KB
# 一级间接:256 * 4KB = 1MB
# 二级间接:256^2 * 4KB = 256MB
# 三级间接:256^3 * 4KB = 64GB
# 理论最大文件:64GB + 256MB + 1MB + 48KB
3 testdisk 和 photorec 组合恢复
3.1 工具简介
testdisk 和 photorec 是 PhotoRec 的配套工具,前者用于恢复分区表和启动扇区,后者专注于文件数据恢复。
安装与获取:
# CentOS/RHEL
sudo dnf install testdisk -y
# Ubuntu/Debian
sudo apt install testdisk -y
# 验证
testdisk --version
photorec --version
3.2 testdisk 恢复分区表
当误删分区导致整个文件系统丢失时使用:
# 以只读模式启动 testdisk
sudo testdisk /dev/sdb
# 交互式操作流程:
# 1. 选择磁盘 /dev/sdb
# 2. 选择分区表类型(通常自动检测)
# 3. 选择 "Analyse" 分析分区
# 4. 选择 "Quick Search" 快速搜索
# 5. 若找到分区,选择 "Write" 写入
# 6. 退出并执行 partprobe
3.3 PhotoRec 文件签名恢复
PhotoRec 通过文件签名而非文件系统元数据来恢复文件,适用于文件系统严重损坏的场景:
# 交互式启动 PhotoRec
sudo photorec /dev/sdb1
# 操作流程:
# 1. 选择磁盘
# 2. 选择分区
# 3. 选择文件系统类型(ext4 / other)
# 4. 选择存储空间(Whole 或 Free space)
# 5. 选择输出目录
# 6. 开始恢复
# 命令行模式批量恢复
sudo photorec /d /mnt/recovered /log photorec.log /cmd /dev/sdb1 options,search
3.4 PhotoRec 支持的文件类型
PhotoRec 能识别 300+ 文件签名,常见类型:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 debugfs 恢复 ext4 文件
4.1 debugfs 基础操作
debugfs 是 e2fsprogs 工具集中的交互式文件系统调试器:
# 确认已安装
which debugfs
debugfs -V
# 打开文件系统(只读模式更安全)
sudo debugfs -w /tmp/testdisk.img
# 进入 debugfs 交互界面
4.2 debugfs 核心命令
# debugfs 交互命令
# 打开文件系统
debugfs -w /tmp/testdisk.img
# 列出已删除的 inode
debugfs: ls -d /data
# 输出示例:
# 1310731 1310732 1310733 (4096) .
# 查看 inode 详细信息
debugfs: stat <1310731>
# 输出:
# Inode: 1310731 Type: regular Mode: 0644 Flags: 0x80000
# Size: 28
# Links: 0 Blockcount: 8
# Fragment: Address: 0 Number: 0 Size: 0
# ctime: 0x65xxx: Wed Jan 15 10:35:00 2026
# atime: 0x65xxx: Wed Jan 15 10:30:00 2026
# mtime: 0x65xxx: Wed Jan 15 10:30:00 2026
# BLOCKS:
# 0: 12345
# Total: 1 blocks
# 提取文件内容
debugfs: rdump /data/config.yaml /tmp/recovered/
# 或者直接读取块内容
debugfs: cat <1310731> > /tmp/recovered/config.yaml
# 退出
debugfs: quit
4.3 批量恢复脚本
#!/bin/bash
# ext4_recover.sh - 批量恢复误删文件
DEVICE="/tmp/testdisk.img"
OUTPUT_DIR="/tmp/recovered"
MOUNT_POINT="/mnt/testmount"
# 创建输出目录
mkdir -p "$OUTPUT_DIR"
# 卸载分区(防止数据覆写)
sudo umount "$MOUNT_POINT" 2>/dev/null
# 使用 extundelete 尝试恢复
echo"=== extundelete 恢复 ==="
sudo extundelete "$DEVICE" --restore-all --output-dir "$OUTPUT_DIR"
# 使用 debugfs 深度扫描
echo"=== debugfs 深度扫描 ==="
for inode in $(sudo debugfs -R "ls -d""$DEVICE" 2>/dev/null | grep -oP '\d+' | head -20); do
echo"尝试恢复 inode: $inode"
sudo debugfs -R "rdump <$inode> $OUTPUT_DIR/inode_$inode""$DEVICE" 2>/dev/null
done
echo"恢复完成,结果保存在: $OUTPUT_DIR"
ls -la "$OUTPUT_DIR"
5 XFS 文件系统的恢复
5.1 XFS 特性与恢复挑战
XFS 是 RHEL/CentOS 默认文件系统,采用 B+树 管理,与 ext 系列差异显著:
# 查看 XFS 文件系统信息
xfs_info /dev/sda1
# 输出示例:
# meta-data=/dev/sda1 isize=512 agcount=8, agsize=262144 blks
# = sectsz=4096 attr=2, projid32bit=1
# = crc=1 finobt=1, sparse=1, rmapbt=1
# = reflink=1
# data = bsize=4096 blocks=2097152, imaxpct=25
# = sunit=0 swidth=0 blks
# naming =version 2 bsize=4096 ascii-ci=0, ftype=1
# log =internal bsize=4096 blocks=2560, version=2
# = sectsz=4096 sunit=1 blks
# realtime =none extsz=4096 blocks=0, rtextents=0
XFS 恢复难点:
-
XFS 没有 journal 可用于恢复已删除文件的元数据 -
inode 释放后立即标记为空闲 -
依赖 inode 缓存和条目链
5.2 xfsrestore 恢复 XFS 备份
# 安装 xfsdump
sudo dnf install xfsdump -y
# 执行备份
sudo xfsdump -l 0 -f /backup/xfs_backup.img /dev/sda1
# 恢复单个文件
sudo xfsrestore -f /backup/xfs_backup.img -i /mnt/restored
# 交互式恢复(类似 restore 命令)
sudo xfsrestore -f /backup/xfs_backup.img -i
5.3 XFS 文件系统急救
# 检查 XFS 元数据一致性
sudo xfs_check /dev/sda1
# 修复 XFS 文件系统(需要卸载)
sudo xfs_repair -v /dev/sda1
# 常见修复场景
# 1. 日志损坏
sudo xfs_repair -L /dev/sda1
# 2. 根文件系统修复(从 Live CD 启动)
# sudo xfs_repair -v /dev/sda1
6 高级恢复技术
6.1 磁盘镜像与数据保真
在尝试任何恢复操作前,务必创建磁盘镜像:
# 创建原始镜像(保留所有扇区)
sudo dd if=/dev/sdb of=/backup/sdb_raw.img bs=4M status=progress
# 创建镜像并压缩(节省空间)
sudo dd if=/dev/sdb bs=4M | gzip -9 > /backup/sdb.img.gz
# 镜像恢复
sudo gzip -dc /backup/sdb.img.gz | sudo dd of=/dev/sdb bs=4M status=progress
# 只镜像分区(非整盘)
sudo dd if=/dev/sdb1 of=/backup/sdb1.img bs=4M status=progress
关键参数解释:
-
bs=4M:块大小,影响 I/O 效率和进度显示 -
status=progress:显示进度信息 -
conv=noerror:遇到错误继续(慎用) -
sync:填充 I/O 错误位置
6.2 日志重放恢复原理
ext4 日志机制为恢复提供可能:
# 查看文件系统日志
sudo debugfs -R "logdump -a" /tmp/testdisk.img
# 日志条目类型
# - inode:指 inode 元数据变更
# - block:指数据块变更
# - commit:标记事务结束
# - superblock:超级块变更
6.3 inode 和块寻址手动恢复
#!/bin/bash
# manual_inode_recover.sh - 手动恢复指定 inode
DEVICE="/tmp/testdisk.img"
BLOCK_SIZE=4096
TARGET_INODE=1310731
# 计算 inode 所在块组
INODE_SIZE=256
INODES_PER_GROUP=8192
BLOCK_GROUP=$(( (TARGET_INODE - 1) / INODES_PER_GROUP ))
INODE_OFFSET=$(( (TARGET_INODE - 1) % INODES_PER_GROUP * INODE_SIZE ))
# 计算块组起始地址
# 假设 superblock 在块 0
# 块组描述符表从块 1 开始(假设)
echo"目标 inode: $TARGET_INODE"
echo"所在块组: $BLOCK_GROUP"
echo"组内偏移: $INODE_OFFSET 字节"
# 读取 inode 数据
INODE_ADDR=$(( 1024 + BLOCK_GROUP * 8192 * 4096 + 2 * 4096 + INODE_OFFSET ))
echo"计算地址: $INODE_ADDR"
# 使用 dd 提取 inode
sudo dd if=$DEVICE of=/tmp/inode_data.bin bs=1 skip=$INODE_ADDR count=$INODE_SIZE 2>/dev/null
# 解析 inode(简化版)
hexdump -C /tmp/inode_data.bin | head -20
6.4 数据碎片识别与重组
当文件被部分覆写时,需要识别碎片:
# 查看文件的块分布
sudo debugfs -R "frag /data/largefile.dat" /tmp/testdisk.img
# ext4 碎片率检查
sudo e4defrag -c /mnt/testmount
# 输出示例:
# /mnt/testmount - ext4 defragmentation status
# Device: /tmp/testdisk.img
# -mounted: yes
# -fragments before: 15
# -fragments after: 1
# -fragment ratio: [15/1]
# 手动分析数据块
sudo debugfs -R "bmap data/largefile.dat 0" /tmp/testdisk.img
7 预防策略体系
7.1 聊天级删除保护:安全提权
# 错误示范:直接删除
rm -rf /data/old/
# 正确做法:分步确认
cd /data
ls -la old/
# 确认无误后
rm -rf old/
# 更安全的别名配置(~/.bashrc)
alias rm='rm -i' # 交互式确认
alias cp='cp -i' # 覆盖前确认
alias mv='mv -i' # 移动前确认
# 危险操作需要双重确认
alias dangerous-rm='echo "WARNING: This will delete data!" && rm'
7.2 回收站机制实现
#!/bin/bash
# safe_delete.sh - 带有回收站功能的删除脚本
TRASH_DIR="$HOME/.trash"
LOG_FILE="$TRASH_DIR/.trash_log"
MAX_TRASH_SIZE=10G
RETENTION_DAYS=30
# 初始化回收站
[ ! -d "$TRASH_DIR" ] && mkdir -p "$TRASH_DIR"
[ ! -f "$LOG_FILE" ] && touch "$LOG_FILE"
safe_delete() {
local file="$1"
local timestamp=$(date +%s)
local trash_name="${timestamp}_$(basename "$file")"
if [ ! -e "$file" ]; then
echo"Error: $file does not exist"
return 1
fi
# 移动到回收站
mv "$file""$TRASH_DIR/$trash_name"
# 记录日志
echo"$timestamp|$file|$trash_name" >> "$LOG_FILE"
# 检查回收站大小
current_size=$(du -sb "$TRASH_DIR" | cut -f1)
max_size=$((MAX_TRASH_SIZE * 1024 * 1024 * 1024))
if [ $current_size -gt $max_size ]; then
echo"Warning: Trash exceeds $MAX_TRASH_SIZE, cleaning old files..."
clean_trash
fi
echo"Moved to trash: $file -> $TRASH_DIR/$trash_name"
}
clean_trash() {
local cutoff_date=$(($(date +%s) - RETENTION_DAYS * 86400))
while IFS='|'read -r timestamp original trash_name; do
if [ "$timestamp" -lt "$cutoff_date" ]; then
rm -rf "$TRASH_DIR/$trash_name"
sed -i "/^$timestamp|$original|$trash_name$/d""$LOG_FILE"
fi
done < "$LOG_FILE"
}
restore() {
local trash_name="$1"
local log_entry=$(grep "|${trash_name}$""$LOG_FILE")
if [ -z "$log_entry" ]; then
echo"Error: $trash_name not found in trash log"
return 1
fi
local original_path=$(echo"$log_entry" | cut -d'|' -f2)
mv "$TRASH_DIR/$trash_name""$original_path"
sed -i "/^.*|${trash_name}$/d""$LOG_FILE"
echo"Restored: $trash_name -> $original_path"
}
# 使用示例
# safe_delete /data/oldfile.txt
# restore 1234567890_oldfile.txt
7.3 文件系统快照( LVM / Btrfs )
LVM 快照卷:
# 创建 LVM 快照
# 假设 /data 是 LVM 卷
df -h | grep /data
# 输出:/dev/mapper/vg00-lv_data on /data type ext4 (...)
# 创建快照(需要足够空间存储变更)
sudo lvcreate -L 10G -s -n data_snap /dev/vg00/lv_data
# 快照访问(只读)
sudo mkdir -p /mnt/snapshot
sudo mount -o ro /dev/vg00/lv_datasnap /mnt/snapshot
# 从快照恢复
sudo umount /mnt/snapshot
sudo lvconvert --merge /dev/vg00/lv_datasnap
# 自动快照脚本(cron 每日执行)
cat > /usr/local/bin/auto_snapshot.sh << 'EOF'
#!/bin/bash
VG_NAME="vg00"
LV_NAME="lv_data"
SNAP_NAME="snap_$(date +%Y%m%d_%H%M%S)"
SNAP_SIZE="10G"
# 创建快照
sudo lvcreate -L ${SNAP_SIZE} -s -n ${SNAP_NAME} /dev/${VG_NAME}/${LV_NAME}
# 删除 7 天前的快照
sudo lvremove -f /dev/${VG_NAME}/snap_$(date -d '7 days ago' +%Y%m%d_000000)
echo"Snapshot created: ${SNAP_NAME}"
EOF
chmod +x /usr/local/bin/auto_snapshot.sh
# 添加到 crontab
echo"0 2 * * * /usr/local/bin/auto_snapshot.sh" | sudo tee -a /var/spool/cron/root
Btrfs 快照:
# 创建子卷快照
sudo btrfs subvolume snapshot /data /data/snap_$(date +%Y%m%d)
# 只读快照
sudo btrfs subvolume snapshot -r /data /data/ro_snap_$(date +%Y%m%d)
# 从快照恢复
sudo btrfs subvolume delete /data
sudo btrfs subvolume snapshot /data/ro_snap_20260115 /data
# 自动快照(snapper 配置)
sudo snapper create-config -f btrfs /data
sudo snapper create -c data -d "Before cleanup"
7.4 实时同步与备份策略
#!/bin/bash
# rsync_inotify_backup.sh - 实时同步备份
SOURCE_DIR="/data"
BACKUP_SERVER="192.168.1.100"
BACKUP_DIR="/backup/data"
SSH_PORT=22
INOTIFY_WAIT="/usr/bin/inotifywait"
# 安装依赖
install_dependencies() {
if ! command -v inotifywait &> /dev/null; then
echo"Installing inotify-tools..."
sudo dnf install -y inotify-tools || sudo apt install -y inotify-tools
fi
}
# 首次全量同步
initial_sync() {
echo"Performing initial full sync..."
rsync -avz -e "ssh -p $SSH_PORT" \
--delete \
--progress \
"$SOURCE_DIR/" \
"backup@${BACKUP_SERVER}:${BACKUP_DIR}/"
}
# 增量同步(监控文件变化)
incremental_sync() {
echo"Starting incremental sync monitoring..."
$INOTIFY_WAIT -m -r -e create,modify,delete,move \
--format '%w%f'"$SOURCE_DIR" | whileread file; do
echo"Change detected: $file"
rsync -avz -e "ssh -p $SSH_PORT" \
--delete \
"$SOURCE_DIR/" \
"backup@${BACKUP_SERVER}:${BACKUP_DIR}/"
done
}
# 主函数
case"$1"in
install)
install_dependencies
;;
full)
initial_sync
;;
watch)
initial_sync
incremental_sync
;;
*)
echo"Usage: $0 {install|full|watch}"
;;
esac
8 常见故障场景与排障流程
8.1 误删日志文件的恢复
场景:Nginx 日志文件被误删,但进程仍在写入
# 发现问题
ls -la /var/log/nginx/access.log
# 输出:ls: cannot access '/var/log/nginx/access.log': No such file or directory
# 检查进程状态
ps aux | grep nginx
# 输出:nginx: worker process is running (pid 12345)
# 查找已删除但仍打开的文件
lsof +L1 | grep nginx
# 输出:
# nginx 12345 www-data 4w REG 252,1 1048576000 0 /var/log/nginx/access.log (deleted)
# 方法1:从 /proc 恢复日志内容
# 先停止日志轮转,避免冲突
sudo kill -USR1 $(pgrep nginx)
# 然后重建日志文件
sudo touch /var/log/nginx/access.log
sudo chown www-data:www-data /var/log/nginx/access.log
sudo chmod 644 /var/log/nginx/access.log
# 发送 HUP 信号重载配置
sudo kill -HUP $(pgrep nginx)
# 方法2:直接复制 /proc 中的数据
sudo cp /proc/12345/fd/4 /var/log/nginx/access.log.backup
sudo mv /var/log/nginx/access.log.backup /var/log/nginx/access.log
sudo chown www-data:www-data /var/log/nginx/access.log
8.2 误删数据库文件的恢复
场景:MySQL 数据文件被误删,MySQL 服务仍在运行
# 警告:此操作有风险,可能导致数据库损坏,务必先完整备份!
# 检查 MySQL 是否仍在运行
systemctl status mysql
# 确保 MySQL 正在运行且未崩溃
# 查看 MySQL 的数据文件位置
mysql -u root -p -e "SHOW VARIABLES LIKE 'datadir';"
# 输出:/var/lib/mysql/
# 查看已删除但仍打开的文件
lsof +L1 | grep /var/lib/mysql
# 输出:
# mysqld 1234 mysql 5r REG 252,1 16777216 0 /var/lib/mysql/mysql/user.ibd (deleted)
# 立即执行数据库全量备份(重要!)
sudo mysqldump -u root -p --all-databases > /tmp/emergency_backup.sql
sudo tar czf /tmp/mysql_emergency.tar.gz -C / var/lib/mysql
# 方法1:尝试恢复表空间文件
# 获取 inode 号
lsof +L1 | grep user.ibd
# 输出:mysqld 1234 mysql 5r REG 252,1 16777216 0 /var/lib/mysql/mysql/user.ibd (deleted)
# inode 号:5(在 fd 目录下)
# 复制文件描述符内容
sudo cp /proc/1234/fd/5 /var/lib/mysql/mysql/user.ibd
sudo chown mysql:mysql /var/lib/mysql/mysql/user.ibd
# 重启 MySQL
sudo systemctl restart mysql
# 方法2:使用 MySQL Enterprise Backup
# (如有许可证)
8.3 批量误删文件的应急响应
场景:执行 rm -rf ./* 时,当前目录不是预期目录
# 立即止血
# 1. 立即终止所有可能继续写入的进程
sudo pkill -STOP -u www-data
sudo pkill -STOP -u mysql
# 2. 记录当前状态
echo"误删时间点: $(date)" > /tmp/incident.log
echo"误删目录: $(pwd)" >> /tmp/incident.log
mount | grep -E '^/dev' >> /tmp/incident.log
# 3. 查看有哪些进程仍在运行(可能有备份)
ps aux | grep -E 'rsync|backup|sync' >> /tmp/incident.log
# 4. 检查是否有最近的备份
ls -la /backup/ | tail -20
ls -la /.snapshot/ 2>/dev/null || ls -la /snapshots/ 2>/dev/null
# 5. 如果有 LVM 快照,立即挂载
sudo mount -o ro /dev/vg00/lv_data_snap /mnt/snap
ls -la /mnt/snap/
# 6. 如果有定时备份(obackup 等)
sudo rclone ls backup:data/ | head -50
# 恢复后重建权限
sudo chown -R www-data:www-data /var/www/
sudo chmod -R 755 /var/www/
9 恢复效果评估与验证
9.1 文件完整性校验
#!/bin/bash
# verify_recovery.sh - 验证恢复文件完整性
RECOVERED_DIR="/tmp/recovered"
ORIGINAL_MD5="e99a18c428cb38d5f260853678922e03"# 原始文件的 MD5
echo"=== 恢复文件完整性验证 ==="
for file in $(find "$RECOVERED_DIR" -type f); do
filename=$(basename "$file")
md5sum=$(md5sum "$file" | cut -d' ' -f1)
echo"文件: $filename"
echo"MD5: $md5sum"
# 如果有原始 MD5 进行比对
if [ -n "$ORIGINAL_MD5" ] && [ "$md5sum" = "$ORIGINAL_MD5" ]; then
echo"状态: ✓ 完整匹配"
else
echo"状态: ⚠ 无原始 MD5 或不匹配"
fi
echo"---"
done
# 对比目录结构
echo"=== 目录结构对比 ==="
echo"原始结构:"
ls -la /mnt/testmount/data/
echo""
echo"恢复结构:"
ls -la "$RECOVERED_DIR/data/" 2>/dev/null || ls -la "$RECOVERED_DIR/" | grep -v REVERTED_FILES
9.2 恢复成功率统计
# 统计恢复情况
cat > /tmp/recovery_stats.sh << 'EOF'
#!/bin/bash
ORIGINAL_COUNT=$(find /mnt/testmount/data -type f | wc -l)
RECOVERED_COUNT=$(find ./RECOVERED_FILES -type f | wc -l)
echo"原始文件数: $ORIGINAL_COUNT"
echo"恢复文件数: $RECOVERED_COUNT"
echo"恢复率: $(echo "scale=2; $RECOVERED_COUNT * 100 / $ORIGINAL_COUNT" | bc)%"
# 检查文件类型分布
echo""
echo"=== 文件类型分布 ==="
echo"原始文件类型:"
find /mnt/testmount/data -type f | sed 's/.*\.//' | sort | uniq -c
echo""
echo"恢复文件类型:"
find ./REVERTED_FILES -type f 2>/dev/null | sed 's/.*\.//' | sort | uniq -c
EOF
chmod +x /tmp/recovery_stats.sh
10 总结与最佳实践清单
核心要点
-
删除 ≠ 覆写:Linux 文件删除只是解除链接,数据块在覆写前仍可恢复 -
速度是关键:发现误删后越早行动,恢复成功率越高 -
先镜像后恢复:避免操作过程中进一步损坏数据 -
预防大于恢复:快照、备份、权限控制才是根本
恢复工具选择指南
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
日常检查清单
# 每日检查项
#!/bin/bash
echo"=== 每日文件安全检查 ==="
echo""
echo"1. 检查高危目录权限:"
ls -la /data /home /var/log 2>/dev/null | head -20
echo""
echo"2. 检查最近删除操作日志:"
sudo grep -i "rm -rf" /var/log/secure 2>/dev/null | tail -10
echo""
echo"3. 检查回收站大小:"
du -sh ~/.trash 2>/dev/null || echo"回收站未配置"
echo""
echo"4. 检查备份状态:"
sudo ls -la /backup/ 2>/dev/null | tail -5
echo""
echo"5. 检查快照:"
sudo lvs | grep snap
sudo btrfs sub list /data 2>/dev/null
应急响应流程图
误删文件发现
↓
立即停止写入(pkill -STOP)
↓
创建磁盘镜像(dd)
↓
评估恢复方案
├─→ 有快照 → 直接挂载恢复
├─→ ext4 → extundelete
├─→ 文件系统损坏 → PhotoRec
└─→ 进程持有 → lsof + /proc
↓
恢复并验证
↓
更新备份策略










暂无评论内容