如何获取文件的“有效数据长度”?

问题描述 投票:0回答:5

有一个函数可以设置“有效数据长度”值:

SetFileValidData
,但我没有找到方法获取“有效数据长度”值。

我想知道给定文件的 EOF 是否与 VDL 不同,因为在

VDL<EOF
的情况下在 VDL 之后写入将导致性能损失,如此处所述。

c windows winapi ntfs
5个回答
3
投票

对此进行了调查。无法通过任何 API 获取此信息,即使是例如NtQueryInformationFile API(FileEndOfFileInformation 仅适用于 NtSetInformationFile)。所以最后我通过手动读取NTFS记录来读取此内容。如果谁有更好的方法请告诉!显然,这也仅适用于完整的系统访问(和 NTFS),并且可能与 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) { [...] }
        [...]
    }
[...]

2
投票

我发现this页面,声称:

没有查询VDL值的机制

所以答案是“你不能”。

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


0
投票

我认为您对“有效数据长度”的实际含义感到困惑。检查这个答案 基本上,虽然

SetEndOfFile

可以让您快速增加文件的长度并分配磁盘空间,但如果您跳到(新的)文件末尾进行写入,则所有额外分配的磁盘空间都需要用零覆盖,这有点慢。


SetFileValidData

让您可以

跳过
清零。您告诉系统,“我对这些磁盘块中的任何内容都满意,继续处理”。 (这就是您需要 SE_MANAGE_VOLUME_NAME 权限的原因,因为如果您不覆盖数据,它可能会向非特权用户泄露特权数据。具有此权限的用户
 无论如何都可以访问原始驱动器数据。)
无论哪种情况,您都已设置文件的新有效大小。 (您可以

回读。)到底什么是

应该单独的“读取文件有效数据”报告? SetFileValidData 告诉

系统这些磁盘块中的内容
“有效”... 不同的解释方式:


文档

提到正在跟踪“有效数据长度”

;这样做的目的是让系统知道在必要时(例如,您关闭文件)。 不需要读回该值,因为它与实际文件大小不同的唯一方式是因为您自己确实通过上述函数更改了它...

在命令行上简单如下
SetEndOfFile
例如,使用 

fsutil file queryvaliddata

0
投票

我在 Win32 API 中找不到任何内容。您可以从 C 调用 fsutil 命令。

Fsutil file createnew C:\largefile 53687091200

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


因此,磁盘上的文件大小似乎会发生变化,而没有任何数据写入文件。

暗示,任何

SetValidData

-1
投票
GetValidData

返回“有效”文件大小。

    

© www.soinside.com 2019 - 2024. All rights reserved.