Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Настоящее закрытие файла :)


Riply ©   (2008-12-16 11:45:00) 

Здравствуйте !
(Не смогла придумать другое название ветки. Будем считать что и это отражает ее суть :) )

Пытаюсь работать, примерно, по такой схеме:

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 ?


Riply   (2008-12-19 06:43:00) [1]

> [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

Вообщем решила так:
Раз с нахрапа(методом тыка) взять не удалось
- переходим к планомерной осаде(изучению документации) :)


Rouse_   (2008-12-18 18:35:00) [2]

Вот это посмотри: http://www.rsdn.ru/forum/message/624367.aspx


Riply   (2008-12-18 15:47:00) [3]

Sorry. Копи\пайст подвел.
Вот не зря у нас на форуме настойчиво советуют им не пользоваться :)


Riply   (2008-12-18 15:41:00) [4]

> Вообще конечно неначем проверить, но попробуй перед выставлением размера
> проверить 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 уже не существует...
Вообщем совсем запуталась :(


Rouse_   (2008-12-18 09:52:00) [5]

Вообще конечно неначем проверить, но попробуй перед выставлением размера проверить MmCanFileBeTruncated() и если что-то не так то скажи MmForceSectionClosed()


Riply   (2008-12-18 02:32:00) [6]

> [2] Riply © (17.12.08 22:52)

Я себе так представляю происходящее:
В "обычном" приложении мы поработали с файлом и вызвали ZwClose(hFileObj)
"Система" получила комаду, осознала и поручила некой нити ее
выполнение тогда, когда это будет ей (системе) удобно.
Мы же, вызываем наш драйвер (ZwDeviceIoControl) и
он в своей нити пытается установить файлу нулевой размер.
И вот, когда эта попытка происходит раньше, чем первая нить
выполнила команду, мы и получаем STATUS_USER_MAPPED_FILE.

Если это объяснение похоже на правду, а не является "притянутым за уши",
то и появляется мой вопрос: как из приложения "совсем" закрыть файл
или, хотя бы подождать когда это сделает система ?


Германн   (2008-12-18 01:16:00) [7]

Удалено модератором
Примечание: Оффтоп в тематическом разделе...


Riply   (2008-12-17 22:52:00) [8]

> [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;


Ошибку очень сложно отлавливать т.к. каждый раз приходиться ждать следующего полнолуния :)
Закономерности ее появления (например, в зависимости от размера файла),
пока не нашла.


Rouse_   (2008-12-17 10:50:00) [9]


> Как бы в user-mode закрыть файл "совсем" или хотябы дождаться
> когда это сделает система

Так ты покажи как ты делаеш это сейчас :)


Наверх

Титульная страница Поиск, карта сайта Написать письмо