I am experiencing strange behavior with the semantics of the AllocationSize parameter of NtCreateFile on NTFS. According to the documentation this parameter is used when the file is created, overwritten or superseded. However it seems that the AllocationSize of the file can be changed by a simple FILE_OPEN with FILE_READ_ATTRIBUTES.
Specifically it seems that opening the file resets the AllocationSize to be just greater than the current FileSize (EndOfFile); if the FileSize is 0, then the AllocationSize becomes 0; if the FileSize is 5120, then the AllocationSize becomes 8192.
I include a test that I cobbled together to test this. Apologies for the crudeness of this test:
<<
void create_allocation_dotest(ULONG Flags, PWSTR Prefix)
{
//void *memfs = memfs_start(Flags);
HANDLE DirHandle, FileHandle;
NTSTATUS Result;
BOOLEAN Success;
WCHAR FilePath[MAX_PATH];
WCHAR UnicodePathBuf[MAX_PATH] = L"file2";
UNICODE_STRING UnicodePath;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER AllocationSize;
FILE_STANDARD_INFO StandardInfo;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\dir1",
Prefix ? L"" : L"\\?\GLOBALROOT", Prefix ? Prefix : L""/*memfs_volumename(memfs)*/);
Success = CreateDirectoryW(FilePath, 0);
ASSERT(Success);
DirHandle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != DirHandle);
AllocationSize.QuadPart = 65536;
UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR);
UnicodePath.MaximumLength = sizeof UnicodePathBuf;
UnicodePath.Buffer = UnicodePathBuf;
InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&AllocationSize, FILE_ATTRIBUTE_NORMAL, 0,
FILE_CREATE, 0, 0, 0);
ASSERT(STATUS_SUCCESS == Result);
Success = GetFileInformationByHandleEx(FileHandle, FileStandardInfo, &StandardInfo, sizeof StandardInfo);
ASSERT(Success);
ASSERT(65536 == StandardInfo.AllocationSize.QuadPart);
CloseHandle(FileHandle);
AllocationSize.QuadPart = 0;
UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR);
UnicodePath.MaximumLength = sizeof UnicodePathBuf;
UnicodePath.Buffer = UnicodePathBuf;
InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0);
Result = NtCreateFile(&FileHandle,
FILE_READ_ATTRIBUTES, &Obja, &Iosb,
&AllocationSize, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN, 0, 0, 0);
ASSERT(STATUS_SUCCESS == Result);
Success = GetFileInformationByHandleEx(FileHandle, FileStandardInfo, &StandardInfo, sizeof StandardInfo);
ASSERT(Success);
ASSERT(0 == StandardInfo.AllocationSize.QuadPart);
CloseHandle(FileHandle);
AllocationSize.QuadPart = 0;
UnicodePath.Length = (USHORT)wcslen(UnicodePathBuf) * sizeof(WCHAR);
UnicodePath.MaximumLength = sizeof UnicodePathBuf;
UnicodePath.Buffer = UnicodePathBuf;
InitializeObjectAttributes(&Obja, &UnicodePath, 0, DirHandle, 0);
Result = NtCreateFile(&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
&AllocationSize, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN, FILE_DELETE_ON_CLOSE, 0, 0);
ASSERT(STATUS_SUCCESS == Result);
Success = GetFileInformationByHandleEx(FileHandle, FileStandardInfo, &StandardInfo, sizeof StandardInfo);
ASSERT(Success);
ASSERT(0 == StandardInfo.AllocationSize.QuadPart);
CloseHandle(FileHandle);
CloseHandle(DirHandle);
//memfs_stop(memfs);
}
>
I am not certain if this should be considered NTFS specific behavior or whether all FSD’s on Windows are expected to behave like this. I tend to think the former (i.e. that this is NTFS specific behavior) and I am not currently planning to change my FSD to replicate this behavior.
I first discovered this problem when testing my FSD against IFSTEST. Specifically the CloseCleanupDelete.TruncateOnCloseTest fails with the following message on my FSD:
<<
Test :TruncateOnCloseTest
Group :CloseCleanupDelete
Status :C0000014 (IFSTEST_TEST_ALLOCATION_SIZE_ERROR)
LastGeneralValue :00010000
ExpectedGeneralValue :00001400
Description :{Msg# CloseDel!tronc!14} The allocation size queried
from the file using standard information was not
correct. The file was created with 65536 allocation
size specified. The file was then written with 5120
bytes. The handle was closed. The file was then
reopened with a NULL allocation size specified. The
standard information was queried and 65536 was returned
for the current allocation size. The allocation size
should be 5120.
>
The fact that this is checked by IFSTEST makes me wonder whether this is indeed the expected behavior of all file systems that implement allocation size. [I have not tested what happens on FastFat yet.]
As always I am seeking the wisdom of the NTFSD list.
Bill