You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ext4文件系统删除文件恢复:分区读取异常问题求助

问题分析与解决方案

问题1:os.stat获取分区大小为0的原因与解决

/dev下的块设备文件是特殊字符设备,os.stat()返回的st_size不会反映实际分区大小,而是固定为0。需要通过系统调用ioctl获取设备的真实容量。

解决方法:用fcntl获取分区实际大小

通过BLKGETSIZE64这个ioctl命令可以直接获取设备的字节数:

import fcntl
import struct

def get_partition_size(partition_path):
    with open(partition_path, 'rb') as f:
        # BLKGETSIZE64 命令码,用于获取设备总字节数
        size_bytes = struct.unpack('Q', fcntl.ioctl(f, 0x80081272, struct.pack('Q', 0)))[0]
    return size_bytes

问题2:os.walk遍历块设备无输出的原因

/dev/nvme0n1p6是块设备文件,并非挂载后的文件系统目录,os.walk只能遍历文件系统的目录结构,无法直接扫描块设备。即使挂载了分区,已删除文件的目录项已被标记为删除或覆盖,常规文件系统遍历也无法发现它们。

正确实现ext4已删除文件定位与数据访问

要恢复ext4的已删除文件,需要直接解析分区的文件系统结构,步骤如下:

1. 获取ext4实际块大小

ext4的块大小不一定是4096字节,需从文件系统超级块读取真实值:

def get_ext4_block_size(partition_path):
    SUPERBLOCK_OFFSET = 1024  # ext4超级块默认在1024字节偏移处
    with open(partition_path, 'rb') as f:
        f.seek(SUPERBLOCK_OFFSET)
        superblock = f.read(1024)
        # 读取s_log_block_size字段(偏移0x18,4字节小端),计算实际块大小
        s_log_block_size = struct.unpack('<I', superblock[0x18:0x1C])[0]
        block_size = 1024 << s_log_block_size
    return block_size

2. 查找已删除的目录项

ext4删除文件时,会将对应目录项中的inode编号设为0。我们可以遍历每个块,查找inode为0的目录项并解析文件名:

import os

def list_deleted_files(partition_path):
    deleted_files = []
    block_size = get_ext4_block_size(partition_path)
    partition_size = get_partition_size(partition_path)
    num_blocks = partition_size // block_size

    with open(partition_path, 'rb') as f:
        for block_num in range(num_blocks):
            f.seek(block_num * block_size)
            block_data = f.read(block_size)
            offset = 0
            # 遍历块内的所有目录项
            while offset + 8 <= len(block_data):  # 目录项最小长度为8字节
                inode = struct.unpack('<I', block_data[offset:offset+4])[0]
                rec_len = struct.unpack('<H', block_data[offset+4:offset+6])[0]
                name_len = block_data[offset+6]

                # 已删除文件的目录项特征:inode为0且文件名长度有效
                if inode == 0 and rec_len > 0 and name_len > 0:
                    name = block_data[offset+8:offset+8+name_len].decode('utf-8', errors='replace')
                    if name:
                        deleted_files.append(name)
                
                offset += rec_len
                # 防止无效的rec_len导致死循环
                if offset >= block_size:
                    break
    return deleted_files

# 运行需要root权限
if __name__ == '__main__':
    partition_path = '/dev/nvme0n1p6'
    deleted_files = list_deleted_files(partition_path)
    print("已找到的删除文件:")
    for name in deleted_files:
        print(name)

3. 访问已删除文件的数据(进阶)

已删除文件的数据能否恢复取决于数据块是否被覆盖:

  • 如果inode未被复用:读取inode结构(inode通常位于块组的inode表中),解析其中的直接/间接块指针,然后读取对应块的数据。
  • 如果inode已被复用:只能通过文件签名(如图片的FF D8 FF、文档的特定头)在分区中搜索原始数据。

注意:

  • 必须以root权限运行脚本,否则无法访问块设备。
  • 立即停止向目标分区写入数据,避免已删除文件的块被新数据覆盖。

内容的提问来源于stack exchange,提问作者Loopinfility

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:57:56