代码改变世界

lseek函数与文件空洞

2013-10-16 21:14  DVwei  阅读(1074)  评论(0编辑  收藏  举报

  在UNIX/LINUX系统中,文件位移量可以大于文件的当前长度,这种情况下向文件中写入数据就会产生文件空洞(hole),这些没写入数据的文件空洞部分默认会被0填满。虽然这些文件空洞并没有实际的数据,但是它们仍然占据硬盘空间。

  在Windows下同样支持这种文件空洞,以下简单的代码产生一个6KB的空洞文件:

#include <afx.h>
#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    CFile testFile(_T("D:\\test"), CFile::modeCreate | CFile::modeWrite);
    CHAR buff[1024];
    memset(buff, 12, 1024);
    testFile.Write(buff, 1024);
    testFile.Seek(1024 * 3, CFile::begin);
    memset(buff, 22, 1024);
    testFile.Write(buff, 1024);
    testFile.Seek(1024 * 5, CFile::begin);
    memset(buff, 20, 1024);
    testFile.Write(buff, 1024);
    testFile.Close();
    return 0;
}

  用Sublime Text2打开,可以发现中间两部分是NULL(0),这就是文件空洞

 

  从代码地图上可以看到此文件有3KB是空的:

  

 

  Windows下的NTFS文件系统还支持文件空洞的压缩,那些0都是无用的数据,却又占据了空间资源,NTFS文件空洞的压缩算法可以释放这些0字节的空间。这种文件被称为稀疏文件,通过指定DeviceIoControl函数

BOOL WINAPI DeviceIoControl(
  _In_         HANDLE hDevice,
  _In_         DWORD dwIoControlCode,
  _In_opt_     LPVOID lpInBuffer,
  _In_         DWORD nInBufferSize,
  _Out_opt_    LPVOID lpOutBuffer,
  _In_         DWORD nOutBufferSize,
  _Out_opt_    LPDWORD lpBytesReturned,
  _Inout_opt_  LPOVERLAPPED lpOverlapped
);

设置FSCTL_SET_SPARSE标记可以产生稀疏文件,以下代码是一个例子:

    hFile = CreateFile("tmp_file", GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL, CREATE_ALWAYS,0,NULL);
    DWORD dwTemp;
    DeviceIoControl(hFile,FSCTL_SET_SPARSE, NULL,0,NULL,0,&dwTemp,NULL);

    SetFilePointer(hFile, 0x100000, NULL, FILE_BEGIN);
    WriteFile(hFile,"123", 3, &nWritten, NULL);
    SetEndOfFile(hFile);
    CloseHandle(hFile);

通过GetFileInformationByHandle可以查看文件是否为稀疏文件:

BOOL WINAPI GetFileInformationByHandle(
  _In_   HANDLE hFile,
  _Out_  LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);

 

FSCTL_SET_SPARSE