-
Здравствуйте !
(Не смогла придумать другое название ветки. Будем считать что и это отражает ее суть :) )
Пытаюсь работать, примерно, по такой схеме:Result := Nt_FileReadToBMem(pImageFileName, pImageInfo, False, nil, SIZE_RESTRICT_MAX);
if NT_SUCCESS(Result) then
begin
FilePos.QuadPart := pImageInfo.Length;
if FilePos.QuadPart > 0
then Result := ZwDeviceIoControl(..., IOCTL_MY_SUPER_PUPER_CODE, ...);
Где Nt_FileReadToBMem открывает файл на чтение, работает с ним и закрывает Handle.
В ней мы обращаемся к файлу из user-mode.
Ф-ия ZwDeviceIoControl(..., IOCTL_MY_SUPER_PUPER_CODE
Сообщает драйверу, что работа закончена и столько-то байт обработано (BytesTransfer).
Драйвер сравнивает три значения: размер данного файла (pImageFileName) в user-mode,
его же размер в kernel-mode и BytesTransfer (обработанные байты).
В зависимости от результата предпринимает различные дейтвия в т.ч.
если все три значения совпали то устанавливает файлу нулевой размер
при помощи ZwSetInformationFile(..., FileEndOfFileInformation).
Все работает и не пискает, кроме как в полнолуние :)
А в полнолуние ZwSetInformationFile выдает нам следующую ошибку:
STATUS_USER_MAPPED_FILE (The requested operation cannot be performed on a file with a user-mapped section open)
Теперь попробую сформулировать вопрос (если правильно понимаю суть происходящего)
Как бы в user-mode закрыть файл "совсем" или хотябы дождаться когда это сделает система,
чтобы смело вызывать ZwDeviceIoControl ? -
Rouse_ © (17.12.08 10:50) [1]
> Как бы в user-mode закрыть файл "совсем" или хотябы дождаться
> когда это сделает система
Так ты покажи как ты делаеш это сейчас :) -
> [1] Rouse_ © (17.12.08 10:50)
> Так ты покажи как ты делаеш это сейчас :)
Саш, в общем-то все как у всех. Ничего выходящего за рамки приличий себе не позволяю :)
Ф-ия Nt_FileReadToBMem выглядит, примерно так:function Nt_FileReadToBMem(const pFileName: PUNICODE_STRING; ...; const RestrictSize: ULONG = SIZE_RESTRICT_MAX): NTSTATUS;
//...
begin
Result := ZwOpenFile(..., FILE_READ_DATA or SYNCHRONIZE, FILE_SHARE_SH_ALL,
FILE_SEQUENTIAL_ONLY or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, ...);
if NT_SUCCESS(Result) then
try
Result := Zw_FileGetSize(hFileObj, @Offset);
if NT_SUCCESS(Result) then
if Offset <= RestrictSize then
begin
Result := ZwReadFile(hFileObj, ..., Offset, ...);
if NT_SUCCESS(Result) then
begin
//...
end;
end
else Result := STATUS_INSUFFICIENT_RESOURCES;
finally
NT_STATUS_SET_WORST(Result, ZwClose(hFileObj));
end;
end;
Т.к. в ней мы открываем файл только FILE_READ_DATA доступом (иначе не откроется),
то мы здесь ограничены в попытках что-то предпринять типа ZwFlushFileBuffer :(
Схема происходящего в драйвере (в DeviceIOControlDispatch)case FunctionCode of
IOCTL_MY_SUPER_PUPER_CODE:
begin
//...
Result := Zw_SetFilePointer(pBuffFile.hFileHandle, nil, pNewFilePointer);
if NT_SUCCESS(Result) then
begin
Result := Zw_SetEndOfFileByOffset(pBuffFile.hFileHandle, nil);
{$IFDEF SH_DRV_DEBUG}DrvLog_WriteMessW('BFILE_CompactFileDirect', 'Zw_SetEndOfFileByOffset', Result, pNewFilePointer.LowPart, pNewFilePointer.HighPart);{$ENDIF}
end
else {$IFDEF SH_DRV_DEBUG}DrvLog_WriteMessW('BFILE_CompactFileDirect', 'Zw_SetFilePointer', Result, pNewFilePointer.LowPart, pNewFilePointer.HighPart);{$ENDIF}
//...
end;
где ф-ия Zw_SetEndOfFileByOffset, выглядит, примерно так:function Zw_SetEndOfFileByOffset(const hFileObj: THANDLE; const pOffset: PLARGE_INTEGER): NTSTATUS;{$IFDEF SH_KRNL_DRV}stdcall;{$ENDIF}
var
IoStatusBlock: IO_STATUS_BLOCK;
EndOfFileInfo: FILE_END_OF_FILE_INFORMATION;
{$IFNDEF SH_KRNL_DRV}
FileAllocationInfo: FILE_ALLOCATION_INFORMATION;
{$ENDIF !SH_KRNL_DRV}
begin
if pOffset <> nil
then EndOfFileInfo.EndOfFile := pOffset^
else EndOfFileInfo.EndOfFile := LARGE_INTEGER(Make_Int64(0, 0));
// This call is not supposed to free up any space after the eof marker
// if the file gets truncated. We have to deallocate the space explicitly afterwards.
// But...most file systems dispatch both FileEndOfFileInformation
// and FileAllocationInformation as they were the same command.
Result := ZwSetInformationFile(hFileObj, @IoStatusBlock, @EndOfFileInfo,
SizeOf(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation);
{$IFNDEF SH_KRNL_DRV}
if NT_SUCCESS(Result) then
begin { TODO -oSashka -cCore functions : Is AllcationInfo need in the user mode ? }
if pOffset <> nil
//....
end;
{$ENDIF !SH_KRNL_DRV}
end;
Ошибку очень сложно отлавливать т.к. каждый раз приходиться ждать следующего полнолуния :)
Закономерности ее появления (например, в зависимости от размера файла),
пока не нашла. -
Германн © (18.12.08 01:16) [3]Удалено модератором
Примечание: Оффтоп в тематическом разделе... -
> [2] Riply © (17.12.08 22:52)
Я себе так представляю происходящее:
В "обычном" приложении мы поработали с файлом и вызвали ZwClose(hFileObj)
"Система" получила комаду, осознала и поручила некой нити ее
выполнение тогда, когда это будет ей (системе) удобно.
Мы же, вызываем наш драйвер (ZwDeviceIoControl) и
он в своей нити пытается установить файлу нулевой размер.
И вот, когда эта попытка происходит раньше, чем первая нить
выполнила команду, мы и получаем STATUS_USER_MAPPED_FILE.
Если это объяснение похоже на правду, а не является "притянутым за уши",
то и появляется мой вопрос: как из приложения "совсем" закрыть файл
или, хотя бы подождать когда это сделает система ? -
Rouse_ © (18.12.08 09:52) [5]Вообще конечно неначем проверить, но попробуй перед выставлением размера проверить MmCanFileBeTruncated() и если что-то не так то скажи MmForceSectionClosed()
-
> Вообще конечно неначем проверить, но попробуй перед выставлением размера
> проверить MmCanFileBeTruncated()
> и если что-то не так то скажи MmForceSectionClosed()
Попробовала потестить при помощи такой ф-ии:function Section_ForceCloseTest(const hFileObj: THANDLE): NTSTATUS; stdcall;
var
pFileObj: PFILE_OBJECT;
pSectionPointers: PSECTION_OBJECT_POINTERS;
begin
if OBJECT_TYPES_TABLE.IoFileObjectType <> nil then // глобальная таблица типов объектов
begin
Result := ObReferenceObjectByHandle(hFileObj, FILE_WRITE_DATA, OBJECT_TYPES_TABLE.IoFileObjectType,
KernelMode, PVOID(pFileObj), nil);
if NT_SUCCESS(Result) then
try
pSectionPointers := pFileObj.SectionObjectPointer;
if pSectionPointers <> nil then
if not MmCanFileBeTruncated(pSectionPointers, @LARGE_NULL) then
if MmFlushImageSection(pSectionPointers, MmFlushForWrite) then
if MmForceSectionClosed(pSectionPointers, False) then
begin
Result := STATUS_SH_SUCCESS;
end
else DrvLog_WriteMessW('Section_ForceCloseTest', 'MmForceSectionClosed', Result, 0, 0)
else DrvLog_WriteMessW('Section_ForceCloseTest', 'MmFlushImageSection', Result, 0, 0)
else DrvLog_WriteMessW('Section_ForceCloseTest', 'MmCanFileBeTruncated', Result, 0, 0)
else
begin
Result := STATUS_ACCESS_DENIED;
DrvLog_WriteMessW('Section_ForceCloseTest', 'pSectionPointers <> nil', Result, 0, 0);
end;
finally
ObDereferenceObject(pFileObj);
end
else DrvLog_WriteMessW('Section_ForceCloseTest', 'ObReferenceObjectByHandle', Result, 0, 0);
end
else
begin
Result := STATUS_ACCESS_DENIED;
DrvLog_WriteMessW('Section_ForceCloseTest', 'IoFileObjectType <> nil', Result, 0, 0);
end;
end;
И, после ее вызова, сразу пытаемся обнулить размер файла.
MmFlushImageSection - можно коментировать.
Первичные (ни в коем случае не окончательные) результаты такие:
Если MmCanFileBeTruncated успешна, то и Zw_SetEndOfFileByOffset отрабатывает без ругани
и наоборот: Zw_SetEndOfFileByOffset ни разу не проскочила при not MmCanFileBeTruncated.
Дальше хуже.
Удалось поймать такие моменты:
1. MmForceSectionClosed - False, Zw_SetEndOfFileByOffset - False;
2. MmForceSectionClosed - True, Zw_SetEndOfFileByOffset - False;
Иными словами Zw_SetEndOfFileByOffset глубоко плевать на результат MmForceSectionClosed
при именно таком тесте (не пробовала варьировать параметры)
Мне кажется, что такого результата и следовало ожидать.
У нас было два Handle`а этого файла - "ядерный" и "юзерный".
Мы устроили танцы с бубном... тьфу ! с секцией :)
которая относится к "ядерному" варианту, а не к "юзерному".
Или я неправильно понимаю ?
Если правильно, то может попробовать как-то добраться до "юзерной" секции ?
Где-то она же сидит...
От чего бы оттолкнутся, ведь "юзерный" Handle уже не существует...
Вообщем совсем запуталась :(
Мне кажется, что такого результата и следовало ожидать.
У нас было два Handle`а этого файла - "ядерный" и "юзерный".
Мы устроили танцы с бубном... тьфу ! с секцией :)
которая относится к "ядерному" варианту, а не к "юзерному".
Или я неправильно понимаю ?
Если правильно, то может попробовать как-то добраться до "юзерной" секции ?
Где-то она же сидит...
От чего бы оттолкнутся, ведь "юзерный" Handle уже не существует...
Вообщем совсем запуталась :( -
Sorry. Копи\пайст подвел.
Вот не зря у нас на форуме настойчиво советуют им не пользоваться :) -
Rouse_ © (18.12.08 18:35) [8]Вот это посмотри: http://www.rsdn.ru/forum/message/624367.aspx
-
> [8] Rouse_ © (18.12.08 18:35)
> Вот это посмотри: http://www.rsdn.ru/forum/message/624367.aspx
Посмотрела. Спасибо.
Правда, не поняла с чем (и зачем) он работает в этих строчках:if (pFileObject->SectionObjectPointer->ImageSectionObject != NULL){
ExAcquireResourceExclusiveLite( pFCB->Header.PagingIoResource, TRUE);
ExReleaseResourceLite( pFCB->Header.PagingIoResource);
Ну да ладно. Будем считать, что его (xBlackCat`а) сугубо личное дело :)
Попрогоняла такой вариант теста:if not MmCanFileBeTruncated(pSectionPointers, @LARGE_NULL) then
if MmFlushImageSection(pSectionPointers, MmFlushForWrite) then
begin
CcFlushCache(pSectionPointers, nil, 0, nil);
if CcPurgeCacheSection(pSectionPointers, nil, 0, True) then
begin
Result := STATUS_SUCCESS;
DrvLog_WriteMessW('Section_ForceCloseTest', 'Flush all possible !', Result, 0, 0)
end
Огорчению не было предела, когда на черт знает какой попытке получила,
что зафлешено и пропургено все что можно :), а эта зараза (Zw_SetEndOfFileByOffset)
совершенно невозмутимо заявляет: STATUS_USER_MAPPED_FILE
Вообщем решила так:
Раз с нахрапа(методом тыка) взять не удалось
- переходим к планомерной осаде(изучению документации) :)