#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
#include <Windows.h>
#include <winioctl.h>
#include <vector>
#include <map>
#pragma pack(push, 1)
#define FILE_RECORD_MAGIC 0x454C4946 // 'FILE'
typedef struct {
DWORD magic;
USHORT usa_offset;
USHORT usa_count;
ULONGLONG lsn;
USHORT sequence_number;
USHORT link_count;
USHORT attrs_offset;
USHORT flags;
DWORD bytes_in_use;
DWORD bytes_allocated;
ULONGLONG base_file_record;
USHORT next_attr_id;
DWORD mft_record_number;
} FILE_RECORD_HEADER;
typedef struct {
DWORD type;
DWORD length;
BYTE non_resident_flag;
BYTE ;
USHORT name_offset;
USHORT flags;
USHORT instance;
union {
struct {
DWORD value_length;
USHORT value_offset;
BYTE reserved[2];
} resident_data;
struct {
ULONGLONG lowest_vcn;
ULONGLONG highest_vcn;
USHORT data_run_offset;
USHORT compression_unit;
DWORD reserved;
ULONGLONG allocated_size;
ULONGLONG data_size;
ULONGLONG initialized_size;
ULONGLONG compressed_size;
} non_resident_data;
};
} ATTRIBUTE_HEADER;
typedef struct {
FILETIME creation_time;
FILETIME modification_time;
FILETIME mft_change_time;
FILETIME last_access_time;
DWORD file_attributes;
DWORD maximum_versions;
DWORD version_number;
DWORD class_id;
DWORD owner_id;
DWORD security_id;
ULONGLONG quota_charged;
ULONGLONG usn;
} STANDARD_INFORMATION_ATTRIBUTE;
typedef struct {
ULONGLONG parent_directory;
FILETIME creation_time;
FILETIME modification_time;
FILETIME mft_change_time;
FILETIME last_access_time;
ULONGLONG allocated_size;
ULONGLONG data_size;
DWORD file_attributes;
DWORD packed_ea_size;
BYTE name_length;
BYTE name_type;
WCHAR name[1];
} FILE_NAME_ATTRIBUTE;
// 文件信息结构体
typedef struct {
ULONGLONG mft_reference;
QString filename;
ULONGLONG parent_mft_reference;
ULONGLONG file_size;
ULONGLONG allocated_size;
FILETIME creation_time;
FILETIME modification_time;
FILETIME last_access_time;
DWORD file_attributes;
bool is_directory;
} FILE_INFO;
#pragma pack(pop)
// 检查卷是否为NTFS文件系统
bool isNTFSVolume(const QString& drivePath) {
wchar_t volumeName[MAX_PATH];
wchar_t fileSystemName[MAX_PATH];
DWORD serialNumber, maxComponentLen, fileSystemFlags;
if (GetVolumeInformationW(
(LPCWSTR)drivePath.utf16(),
volumeName,
MAX_PATH,
&serialNumber,
&maxComponentLen,
&fileSystemFlags,
fileSystemName,
MAX_PATH)) {
return QString::fromWCharArray(fileSystemName).toUpper() == "NTFS";
}
return false;
}
// 将FILETIME转换为可读字符串
QString fileTimeToString(const FILETIME& ft) {
if (ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0) {
return "N/A";
}
SYSTEMTIME st;
FileTimeToSystemTime(&ft, &st);
return QString("%1-%2-%3 %4:%5:%6")
.arg(st.wYear, 4)
.arg(st.wMonth, 2, 10, QLatin1Char('0'))
.arg(st.wDay, 2, 10, QLatin1Char('0'))
.arg(st.wHour, 2, 10, QLatin1Char('0'))
.arg(st.wMinute, 2, 10, QLatin1Char('0'))
.arg(st.wSecond, 2, 10, QLatin1Char('0'));
}
// 从MFT引用号中提取记录号(低48位)
ULONGLONG extractMftRecordNumber(ULONGLONG mft_reference) {
return mft_reference & 0x0000FFFFFFFFFFFF;
}
// 构建完整路径
QString buildFullPath(ULONGLONG mft_reference,
const std::map<ULONGLONG, FILE_INFO>& mft_map,
const QString& driveLetter) {
std::vector<QString> pathComponents;
ULONGLONG current_ref = extractMftRecordNumber(mft_reference);
// 最大递归深度,防止循环引用
int max_depth = 100;
while (current_ref != 0 && max_depth-- > 0) {
auto it = mft_map.find(current_ref);
if (it == mft_map.end()) {
// 如果找不到记录,可能是根目录或系统保留记录
if (current_ref == 5) { // 根目录的记录号通常是5
break;
}
qDebug() << "找不到MFT记录:" << current_ref;
break;
}
const FILE_INFO& info = it->second;
// 跳过根目录的文件名(它通常是空字符串)
if (current_ref != 5) {
pathComponents.push_back(info.filename);
}
// 根目录的父引用是自身或0
ULONGLONG parent_ref = extractMftRecordNumber(info.parent_mft_reference);
if (parent_ref == current_ref || parent_ref == 0 || current_ref == 5) {
break;
}
current_ref = parent_ref;
}
// 构建路径
QString fullPath = driveLetter;
for (auto it = pathComponents.rbegin(); it != pathComponents.rend(); ++it) {
fullPath += "\\" + *it;
}
return fullPath;
}
bool parseMFTAndGetFileInfo(const QString& drivePath,
std::vector<FILE_INFO>& fileInfoList,
std::map<ULONGLONG, FILE_INFO>& mftMap) {
QString volumePath = QString("\\\\.\\%1").arg(drivePath.left(2));
HANDLE hVolume = CreateFileW((LPCWSTR)volumePath.utf16(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
qWarning() << "无法打开卷" << drivePath << "错误代码:" << GetLastError();
return false;
}
// 获取NTFS卷数据
NTFS_VOLUME_DATA_BUFFER ntfsInfo;
DWORD bytesReturned;
if (!DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0,
&ntfsInfo, sizeof(ntfsInfo), &bytesReturned, NULL)) {
qWarning() << "无法获取NTFS卷数据. 错误代码:" << GetLastError();
CloseHandle(hVolume);
return false;
}
// 计算MFT的起始字节偏移
ULONGLONG mftStartOffset = ntfsInfo.MftStartLcn.QuadPart * ntfsInfo.BytesPerCluster;
LARGE_INTEGER li;
li.QuadPart = mftStartOffset;
if (!SetFilePointerEx(hVolume, li, NULL, FILE_BEGIN)) {
qWarning() << "无法设置文件指针到MFT起始位置. 错误代码:" << GetLastError();
CloseHandle(hVolume);
return false;
}
const DWORD recordSize = ntfsInfo.BytesPerFileRecordSegment;
BYTE* recordBuffer = new BYTE[recordSize];
ULONGLONG totalRecords = ntfsInfo.MftValidDataLength.QuadPart / recordSize;
qDebug() << "开始解析MFT,共" << totalRecords << "条记录...";
for (ULONGLONG i = 0; i < totalRecords; i++) {
DWORD bytesRead;
if (!ReadFile(hVolume, recordBuffer, recordSize, &bytesRead, NULL)) {
qWarning() << "读取MFT记录失败. 错误代码:" << GetLastError();
break;
}
if (bytesRead != recordSize) {
qWarning() << "读取的字节数不正确. 期望:" << recordSize << "实际:" << bytesRead;
continue;
}
FILE_RECORD_HEADER* recordHeader = (FILE_RECORD_HEADER*)recordBuffer;
if (recordHeader->magic != FILE_RECORD_MAGIC || !(recordHeader->flags & 0x01)) {
continue;
}
FILE_INFO fileInfo = {};
fileInfo.mft_reference = i;
// 解析属性
BYTE* attrOffset = recordBuffer + recordHeader->attrs_offset;
while (attrOffset < recordBuffer + recordSize) {
ATTRIBUTE_HEADER* attrHeader = (ATTRIBUTE_HEADER*)attrOffset;
if (attrHeader->type == 0xFFFFFFFF) break;
// 处理$FILE_NAME属性
if (attrHeader->type == 0x30 && !attrHeader->non_resident_flag) {
BYTE* attrData = attrOffset + attrHeader->resident_data.value_offset;
if (attrData + sizeof(FILE_NAME_ATTRIBUTE) > recordBuffer + recordSize) {
break; // 属性数据越界
}
FILE_NAME_ATTRIBUTE* fileNameAttr = (FILE_NAME_ATTRIBUTE*)attrData;
if ((fileNameAttr->file_attributes & 0x10) != 0
|| (fileNameAttr->file_attributes & 0x4) != 0
|| (fileNameAttr->file_attributes & 0x6) != 0
|| (fileNameAttr->file_attributes & 0x2020) != 0
|| (fileNameAttr->file_attributes & 0x20000006) != 0
|| (fileNameAttr->file_attributes & 0x10000000) != 0) {
break; // 属性数据越界
}
// 确保文件名长度不会导致越界
int name_len = fileNameAttr->name_length;
qDebug() << "name_length" << name_len;
if (attrData + sizeof(FILE_NAME_ATTRIBUTE) + name_len * sizeof(WCHAR) > recordBuffer + recordSize) {
name_len = (recordBuffer + recordSize - attrData - sizeof(FILE_NAME_ATTRIBUTE)) / sizeof(WCHAR);
}
fileInfo.filename = QString::fromWCharArray(fileNameAttr->name, name_len);
fileInfo.parent_mft_reference = fileNameAttr->parent_directory;
fileInfo.allocated_size = fileNameAttr->allocated_size;
fileInfo.file_size = fileNameAttr->data_size;
fileInfo.file_attributes = fileNameAttr->file_attributes;
fileInfo.creation_time = fileNameAttr->creation_time;
fileInfo.modification_time = fileNameAttr->modification_time;
fileInfo.last_access_time = fileNameAttr->last_access_time;
// 检查是否是目录
fileInfo.is_directory = (fileNameAttr->file_attributes & 0x10) != 0;
break;
}
// 移动到下一个属性
if (attrHeader->length == 0) break;
attrOffset += attrHeader->length;
if (attrOffset >= recordBuffer + recordSize) break;
}
if (!fileInfo.filename.isEmpty()) {
fileInfoList.push_back(fileInfo);
// 将文件信息添加到映射表中,键是MFT记录号
ULONGLONG record_num = extractMftRecordNumber(i);
mftMap[record_num] = fileInfo;
}
if (fileInfoList.size() % 10000 == 0) {
qDebug() << "已找到" << fileInfoList.size() << "个文件...";
}
}
delete[] recordBuffer;
CloseHandle(hVolume);
return true;
}
int main(int argc, char* argv[]) {
QCoreApplication a(argc, argv);
std::vector<FILE_INFO> fileInfoList;
std::map<ULONGLONG, FILE_INFO> mftMap; // MFT记录号到文件信息的映射
QFileInfoList drives = QDir::drives();
for (const QFileInfo& drive : drives) {
QString drivePath = drive.absoluteFilePath();
if (isNTFSVolume(drivePath)) {
qDebug() << "解析NTFS卷:" << drivePath;
if (parseMFTAndGetFileInfo(drivePath, fileInfoList, mftMap)) {
qDebug() << "找到" << fileInfoList.size() << "个文件";
// 显示前几个文件的信息,包括完整路径
for (int i = 0; i < 1000 && i < fileInfoList.size(); i++) {
const FILE_INFO& info = fileInfoList[i];
QString fullPath = buildFullPath(info.mft_reference, mftMap, drivePath.left(2));
qDebug() << "\n文件:" << info.filename;
qDebug() << " 完整路径:" << fullPath;
qDebug() << " MFT记录号:" << info.mft_reference;
qDebug() << " 父目录MFT:" << info.parent_mft_reference;
qDebug() << " 提取的父目录记录号:" << extractMftRecordNumber(info.parent_mft_reference);
qDebug() << " 大小:" << info.file_size << "字节";
qDebug() << " 分配大小:" << info.allocated_size << "字节";
qDebug() << " 创建时间:" << fileTimeToString(info.creation_time);
qDebug() << " 修改时间:" << fileTimeToString(info.modification_time);
qDebug() << " 属性: 0x" << QString::number(info.file_attributes, 16);
qDebug() << " 是否是目录:" << (info.is_directory ? "是" : "否");
}
}
break;
}
}
return a.exec();
}
我想输出文件名与路径是完整的,而不短文件名,名字中间带~
最新发布