如何获取文件的"valid data length"?

How to get "valid data length" of a file?

有一个函数可以 设置 "valid data length" 值:SetFileValidData,但我没有找到 get 的方法 "valid data length" 值。

如果 EOF 与 VDL 不同,我想知道给定的文件,因为在 VDL<EOF 的情况下在 VDL 之后写入会导致性能损失,如 here 所述。

我认为您对 "valid data length" 实际上 的意思 感到困惑。检查 this answer.

基本上,虽然 SetEndOfFile 让您可以快速增加文件的长度,并分配磁盘 space,但如果您跳到(新的)文件末尾在那里写入,所有额外分配的磁盘 space 都需要用零覆盖,这有点慢。

SetFileValidData 让您 跳过 那个归零。你在告诉系统,"I am OK with whatever is in those disk blocks, get on with it"。 (这就是为什么您需要 SE_MANAGE_VOLUME_NAME 特权,因为如果您不覆盖数据,它可能会向非特权用户泄露特权数据。具有 特权的用户 可以访问原始数据无论如何驱动数据。)

无论哪种情况,您都设置了文件的新有效大小。 (您 可以 回读。)确切地说,应该 单独的 "read file valid data" 报告什么? SetFileValidData 告诉系统那些磁盘块 "valid"...


不同的解释方式:

documentation 提到 "valid data length" 被跟踪;这样做的目的是让系统知道它仍然需要零出的范围(从有效数据结束到文件结束),在SetEndOfFile,必要时(例如关闭文件)。 不需要读回这个值,因为唯一的方法就是它可能与实际文件不同大小 是因为您自己通过上述功能更改了它...

SetValidData(根据 MSDN)可用于创建大型文件,而无需写入文件。对于数据库,这将分配一个(连续的)存储区域。

因此,似乎磁盘上的文件大小发生了变化,但并未向文件写入任何数据。

暗示,任何 GetValidData(不存在)只是 returns 文件的大小,因此您可以使用 GetFileSize 其中 returns "valid" 文件大小。

我找到了 this 页面,声称:

there is no mechanism to query the value of the VDL

所以答案是"you can't"。

如果您关心性能,您可以将 VDL 设置为 EOF,但请注意,您可能允许访问磁盘上的旧垃圾 - 这两个指针之间的部分,如果您要访问它,则应该为零没有将 VDL 设置为指向 EOF 的文件。

对此进行了调查。无法通过任何 API 获取此信息,即使是例如NtQueryInformationFile API(FileEndOfFileInformation 仅适用于 NtSetInformationFile)。所以最后我通过手动读取 NTFS 记录来阅读这个。如果谁有更好的方法,请告诉!这也显然只适用于完全系统访问(和 NTFS),并且可能与 in-memory 信息不同步 Windows 使用。

#pragma pack(push)
#pragma pack(1)
struct NTFSFileRecord
{
    char magic[4];
    unsigned short sequence_offset;
    unsigned short sequence_size;
    uint64 lsn;
    unsigned short squence_number;
    unsigned short hardlink_count;
    unsigned short attribute_offset;
    unsigned short flags;
    unsigned int real_size;
    unsigned int allocated_size;
    uint64 base_record;
    unsigned short next_id;
    //char padding[470];
};

struct MFTAttribute
{
    unsigned int type;
    unsigned int length;
    unsigned char nonresident;
    unsigned char name_lenght;
    unsigned short name_offset;
    unsigned short flags;
    unsigned short attribute_id;
    unsigned int attribute_length;
    unsigned short attribute_offset;
    unsigned char indexed_flag;
    unsigned char padding1;
    //char padding2[488];
};

struct MFTAttributeNonResident
{
    unsigned int type;
    unsigned int lenght;
    unsigned char nonresident;
    unsigned char name_length;
    unsigned short name_offset;
    unsigned short flags;
    unsigned short attribute_id;
    uint64 starting_vnc;
    uint64 last_vnc;
    unsigned short run_offset;
    unsigned short compression_size;
    unsigned int padding;
    uint64 allocated_size;
    uint64 real_size;
    uint64 initial_size;
};
#pragma pack(pop)

HANDLE GetVolumeData(const std::wstring& volfn, NTFS_VOLUME_DATA_BUFFER& vol_data)
{
    HANDLE vol = CreateFileW(volfn.c_str(), GENERIC_WRITE | GENERIC_READ, 
        FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (vol == INVALID_HANDLE_VALUE)
        return vol;

    DWORD ret_bytes;
    BOOL b = DeviceIoControl(vol, FSCTL_GET_NTFS_VOLUME_DATA,
        NULL, 0, &vol_data, sizeof(vol_data), &ret_bytes, NULL);

    if (!b)
    {
        CloseHandle(vol);
        return INVALID_HANDLE_VALUE;
    }

    return vol;
}


int64 GetFileValidData(HANDLE file, HANDLE vol, const NTFS_VOLUME_DATA_BUFFER& vol_data)
{
    BY_HANDLE_FILE_INFORMATION hfi;
    BOOL b = GetFileInformationByHandle(file, &hfi);
    if (!b)
        return -1;

    NTFS_FILE_RECORD_INPUT_BUFFER record_in;
    record_in.FileReferenceNumber.HighPart = hfi.nFileIndexHigh;
    record_in.FileReferenceNumber.LowPart = hfi.nFileIndexLow;
    std::vector<BYTE> buf;
    buf.resize(sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + vol_data.BytesPerFileRecordSegment - 1);
    NTFS_FILE_RECORD_OUTPUT_BUFFER* record_out = reinterpret_cast<NTFS_FILE_RECORD_OUTPUT_BUFFER*>(buf.data());
    DWORD bout;
    b = DeviceIoControl(vol, FSCTL_GET_NTFS_FILE_RECORD, &record_in,
        sizeof(record_in), record_out, 4096, &bout, NULL);

    if (!b)
        return -1;

    NTFSFileRecord* record = reinterpret_cast<NTFSFileRecord*>(record_out->FileRecordBuffer);

    unsigned int currpos = record->attribute_offset;
    MFTAttribute* attr = nullptr;
    while ( (attr==nullptr ||
        attr->type != 0xFFFFFFFF  )
        && record_out->FileRecordBuffer + currpos +sizeof(MFTAttribute)<buf.data() + bout)
    {
        attr = reinterpret_cast<MFTAttribute*>(record_out->FileRecordBuffer + currpos);
        if (attr->type == 0x80
            && record_out->FileRecordBuffer + currpos + attr->attribute_offset+sizeof(MFTAttributeNonResident)
                < buf.data()+ bout)
        {
            if (attr->nonresident == 0)
                return -1;

            MFTAttributeNonResident* dataattr = reinterpret_cast<MFTAttributeNonResident*>(record_out->FileRecordBuffer
                + currpos + attr->attribute_offset);
            return dataattr->initial_size;
        }
        currpos += attr->length;
    } 

    return -1;
}

[...]
    NTFS_VOLUME_DATA_BUFFER vol_data;
    HANDLE vol = GetVolumeData(L"\??\D:", vol_data);
    if (vol != INVALID_HANDLE_VALUE)
    {
        int64 vdl = GetFileValidData(alloc_test->getOsHandle(), vol, vol_data);
        if(vdl>=0) { [...] }
        [...]
    }
[...]