33,311
社区成员
发帖
与我相关
我的任务
分享
Sparse File Operations
[This is preliminary documentation and subject to change.]
You can determine whether a file system supports sparse files by calling the GetVolumeInformation function and examining the FILE_SUPPORTS_SPARSE_FILES bit flag.
Most applications are not aware of sparse-files and will not create sparse files. The fact that an application is reading a sparse file is transparent to the application. An application that is aware of sparse-files should determine whether its data set is suitable to be kept in a sparse file. After that determination is made, the application must explicitly declare a file as sparse, using the FSCTL_SET_SPARSE operation.
After an application has set a file to be sparse, the application can use the FSCTL_SET_ZERO_DATA operation to set a region of the file to zero. In addition, the application can use the FSCTL_QUERY_ALLOCATED_RANGES operation to speed searches for nonzero data in the sparse file.
When you perform a write operation (with a function or operation other than FSCTL_SET_ZERO_DATA) whose data consists nothing but zeros, zeros will be written to the disk for the entire length of the write. To zero out a range of the file and maintain sparseness, use FSCTL_SET_ZERO_DATA.
A sparse aware application may also set an existing file to be sparse. If an application sets an existing file to be sparse, it should then scan the file for regions which contain zeros, and use FSCTL_SET_ZERO_DATA to reset those regions, and possibly deallocate some physical disk storage. An application upgraded to sparse file awareness should perform this conversion.
When you perform a read operation from a zeroed-out portion of a sparse file may not read from the hard drive. Instead, the system recognizes that the portion of the file to be read contains zeros, and it returns a buffer full of zeros without actually reading from the disk.
As with any other file, the system can write data to or read data from any position in a sparse file. Nonzero data being written to a previously zeroed portion of the file may result in allocation of disk space. Zeros being written over nonzero data (with FSCTL_SET_ZERO_DATA) may result in a deallocation of disk space.
Note It is up to the application to maintain sparseness by writing zeros with FSCTL_SET_ZERO_DATA.
Note Defragmenting tools which handle compressed files on NTFS file systems will correctly handle sparse files on NTFS 5.0 volumes.
楼主何不使用
Process Monitor 实时监视文件系统、注册表、进程、线程和 DLL 活动。 http://www.microsoft.com/china/technet/sysinternals/utilities/processmonitor.mspx
或
WinAPIOverridehttp://jacquelin.potier.free.fr/winapioverride32/
监视迅雷建立大文件时都调用了哪些Win API呢?
extern "C" int __cdecl _fseeki64(FILE *, __int64, int);
我在的时候,仍然很耗时
if (FILE *stream = ::fopen ("E:/Test.bin", "rb+"))
{
::_fseeki64 (stream, 0x1FFFFFFFF, SEEK_SET);
::fwrite ("!", 1, 1, stream);
::fclose (stream);
}
经历过一次耗时的更新之后,再次尝试文件结尾处修改文件内容的时候,就是瞬间完成了。
也就是说,这个方法建立的文件仍然是假的,经过耗时的更新之后,文件才变成真的。
#include <stdio.h>
#include <windows.h>
static void open_undefined( const char *file_name, __int64 file_size )
{
HMODULE module = ::GetModuleHandle ("kernel32");
typedef BOOL (WINAPI *sfvd) ( HANDLE hFile, LONGLONG ValidDataLength);
sfvd SetFileValidData = (sfvd) ::GetProcAddress (module, "SetFileValidData");
HANDLE token = 0;
::OpenProcessToken (INVALID_HANDLE_VALUE, TOKEN_ALL_ACCESS, &token);
// SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege"
TOKEN_PRIVILEGES tp = { 0 };
::LookupPrivilegeValue (0, "SeManageVolumePrivilege", &tp.Privileges->Luid);
tp.PrivilegeCount = 1;
tp.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
::AdjustTokenPrivileges (token, 0, &tp, sizeof (tp), 0, 0);
HANDLE file = ::CreateFile (file_name, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
if (file != INVALID_HANDLE_VALUE)
{
LONG size_lo = (LONG) file_size;
LONG size_hi = (LONG) (file_size >> 32);
::SetFilePointer (file, size_lo, &size_hi, FILE_BEGIN);
::SetEndOfFile (file);
SetFileValidData (file, file_size);
::CloseHandle (file);
}
::CloseHandle (token);
return;
}
extern "C" int __cdecl _fseeki64( FILE *stream, __int64 offset, int origin);
int main()
{
const char *file_name = "undefined.flat";
const __int64 file_size = 0x123456789;
::remove (file_name);
::open_undefined (file_name, file_size);
if (FILE *stream = ::fopen (file_name, "rb+"))
{
::fseek (stream, 0x1FE, SEEK_SET);
::fwrite ("\xAA\x55", 2, 1, stream);
::_fseeki64 (stream, -1, SEEK_END);
::fwrite ("\r", 1, 1, stream);
::fclose (stream);
}
return 0;
}
在Win7下NTFS分区中的运行效果,秒间完成,创建的文件,内容全是乱码,估计是之前删除过的文件的残渣。
在FAT32分区中,把文件尺寸改成小于4G,同样可以秒间完成,同样内容全是乱码。#include "stdafx.h"
#include <tchar.h>
int main()
{
HANDLE hFile;
HANDLE hMap;
DWORD dwTimer;
WCHAR szOutput[128];
dwTimer = GetTickCount();
hFile = CreateFile(L"E:\\Test.bin", GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 2, 0, NULL);
CloseHandle(hMap);
CloseHandle(hFile);
}
swprintf_s(szOutput, 128, L"Timer: %d\n", GetTickCount() - dwTimer);
OutputDebugStringW(szOutput);
return 0;
}
上面的代码, 建立一个 8GB 的文件, 用时为 0bool AdjustPrivileges() {
HANDLE hToken;
TOKEN_PRIVILEGES tp;
TOKEN_PRIVILEGES oldtp;
DWORD dwSize=sizeof(TOKEN_PRIVILEGES);
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) return true;
else return false;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
CloseHandle(hToken);
return false;
}
ZeroMemory(&tp, sizeof(tp));
tp.PrivilegeCount=1;
tp.Privileges[0].Luid=luid;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
/* Adjust Token Privileges */
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize)) {
CloseHandle(hToken);
return false;
}
// close handles
CloseHandle(hToken);
return true;
}
#include "stdafx.h"
#include <tchar.h>
int main()
{
HANDLE hFile;
HANDLE hMap;
PDWORD pView;
ULONGLONG ullOffset;
int i = 0;
DWORD dwTimer;
WCHAR szOutput[128];
dwTimer = GetTickCount();
hFile = CreateFile(L"E:\\Test.bin", GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 2, 0, NULL);
if (hMap != NULL)
{
ullOffset = (2i64 << 32) - 65536;
pView = (PDWORD)MapViewOfFile(hMap, FILE_MAP_WRITE, ullOffset >> 32, ullOffset & 0xffffffff, 65536);
for (i = 0; i < 65536 / sizeof(DWORD); i++)
*(pView + i) = i;
UnmapViewOfFile(pView);
}
CloseHandle(hMap);
CloseHandle(hFile);
}
swprintf_s(szOutput, 128, L"Timer: %d\n", GetTickCount() - dwTimer);
OutputDebugStringW(szOutput);
return 0;
}
这段代码除了创建文件映射外,还在文件末尾 65536 个字节中写入数据
用时 16 毫秒,我想不明白你所说的等待很久是有多久
hFile = CreateFile(L"E:\\Test.bin", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hMap != NULL)
{
ullOffset = (2i64 << 32) - 65536;
pView = (PDWORD)MapViewOfFile(hMap, FILE_MAP_WRITE, ullOffset >> 32, ullOffset & 0xFFFFFFFF, 65536);
UnmapViewOfFile(pView);
}
CloseHandle(hMap);
CloseHandle(hFile);
}
这段代码是用来检查数据是否成功写入的,在 UnmapViewOfFile(pView); 设断点然后查看 pView 的内存,数据也是成功写入了的