如何用纯Win32 API查找稀疏文件空洞?求ISO C代码示例
Win32下查找稀疏文件的所有空洞
在Win32平台上,没有直接对应POSIX系统lseek(..., SEEK_HOLE, ...)/lseek(..., SEEK_DATA, ...)的API,但可以通过**DeviceIoControl函数配合FSCTL_GET_RETRIEVAL_POINTERS**控制码实现相同功能。这个控制码能获取文件的簇分配信息,通过分析返回的检索指针数组,就能区分已分配的数据段和未分配的空洞区域。
实现思路
- 以只读方式打开目标文件,获取文件句柄。
- 调用
DeviceIoControl发送FSCTL_GET_RETRIEVAL_POINTERS请求,获取文件的检索指针列表。 - 遍历检索指针数组,对比相邻指针的位置,识别数据段和空洞:
- 连续的分配指针覆盖的区域是数据段;
- 指针间隙对应的未分配区域就是空洞。
- 处理文件末尾到实际文件大小之间的未分配区域(如果存在)。
ISO C代码示例
#include <windows.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #define BUFFER_SIZE 4096 void PrintFileRanges(HANDLE hFile, LPCWSTR fileName) { LARGE_INTEGER fileSize; if (!GetFileSizeEx(hFile, &fileSize)) { wprintf(L"Failed to get file size. Error: %u\n", GetLastError()); return; } // 获取文件所在卷的簇大小 WCHAR drivePath[MAX_PATH]; if (!GetVolumePathNameW(fileName, drivePath, MAX_PATH)) { wprintf(L"Failed to get volume path. Error: %u\n", GetLastError()); return; } DWORD sectorsPerCluster, bytesPerSector; if (!GetDiskFreeSpaceW(drivePath, §orsPerCluster, &bytesPerSector, NULL, NULL)) { wprintf(L"Failed to get disk cluster info. Error: %u\n", GetLastError()); return; } LONGLONG bytesPerCluster = (LONGLONG)sectorsPerCluster * bytesPerSector; wprintf(L"file %s\n", fileName); STARTING_VCN_INPUT_BUFFER inputBuffer = {0}; inputBuffer.StartingVcn.QuadPart = 0; RETRIEVAL_POINTERS_BUFFER* outputBuffer = malloc(BUFFER_SIZE); if (!outputBuffer) { wprintf(L"Memory allocation failed.\n"); return; } DWORD bytesReturned; while (TRUE) { if (!DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &inputBuffer, sizeof(inputBuffer), outputBuffer, BUFFER_SIZE, &bytesReturned, NULL)) { if (GetLastError() == ERROR_MORE_DATA) { free(outputBuffer); outputBuffer = malloc(bytesReturned); if (!outputBuffer) { wprintf(L"Memory allocation failed.\n"); return; } continue; } else { wprintf(L"DeviceIoControl failed. Error: %u\n", GetLastError()); free(outputBuffer); return; } } break; } LONGLONG currentPos = 0; DWORD i; for (i = 0; i < outputBuffer->ExtentCount; i++) { LONGLONG nextVcn = outputBuffer->Extents[i].NextVcn.QuadPart; LONGLONG lcn = outputBuffer->Extents[i].Lcn.QuadPart; LONGLONG extentStart = currentPos; LONGLONG extentEnd = nextVcn * bytesPerCluster; // 识别当前位置到数据段起始点之间的空洞 if (currentPos < extentStart) { wprintf(L"hole from pos %lld...pos %lld\n", currentPos, extentStart - 1); } // LCN为-1表示该区域未分配,否则为数据段 if (lcn != -1) { wprintf(L"data from pos %lld...pos %lld\n", extentStart, extentEnd - 1); } currentPos = extentEnd; } // 处理文件末尾到实际文件大小的剩余空洞 if (currentPos < fileSize.QuadPart) { wprintf(L"hole from pos %lld...pos %lld\n", currentPos, fileSize.QuadPart - 1); } free(outputBuffer); } int wmain(int argc, wchar_t* argv[]) { if (argc != 2) { wprintf(L"Usage: %s <file path>\n", argv[0]); return 1; } HANDLE hFile = CreateFileW(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile == INVALID_HANDLE_VALUE) { wprintf(L"Failed to open file. Error: %u\n", GetLastError()); return 1; } PrintFileRanges(hFile, argv[1]); CloseHandle(hFile); return 0; }
示例输出
file xyz.data data from pos 0...pos 16383 hole from pos 16384...pos 65535 data from pos 65546...pos 100020
内容的提问来源于stack exchange,提问作者Cedric Blancher
相关产品推荐
相关产品推荐

