win7下清除带有稀疏块的文件的稀疏属性时导致文件损坏

众所周之,使用下面的代码可以设置文件为稀疏文件。
::DeviceIoControl(file, FSCTL_SET_SPARSE, 0, 0, 0, 0, &temp, 0);

使用这段代码清除稀疏文件标志(vista及以后才有效)
DWORD temp;
FILE_SET_SPARSE_BUFFER b;
b.SetSparse = FALSE;
::DeviceIoControl(file, FSCTL_SET_SPARSE, &b, sizeof(b), 0, 0, &temp, 0));

但,在使用libtorrent 1.6.0的代码时遇到问题,对于一个较大文件的下载时,可能有一块内存数据未写入
到物理文件中时就清除了SPARSE属性,然后下载完成的文件被损坏。以写模式访问会返回错误码5,
拷贝该文件到一定进度会失败,删除也不能删除。

解决办法是,将该文件重新设置为稀疏文件。

所以,在清除稀疏标志之前,必须判断是否还有hole(稀疏的部分)。

bool HasSparseRegion(HANDLE file)
{
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(file, &fileSize))
{
return true;
}

FILE_ALLOCATED_RANGE_BUFFER in;
in.FileOffset.QuadPart = 0;
in.Length.QuadPart = fileSize.QuadPart;

FILE_ALLOCATED_RANGE_BUFFER out[2];

DWORD processedSize = 0;
BOOL ret = DeviceIoControl(file,
                           FSCTL_QUERY_ALLOCATED_RANGES,
                           (void*)&in,
                           sizeof(in),
                           out,
                           sizeof(out),
                           &processedSize,
                           NULL);
if (ret == FALSE)
{
    return true;
}

// if we only have a single range in the file, we're not sparse
return processedSize != sizeof(FILE_ALLOCATED_RANGE_BUFFER);

}