Конференция "WinAPI" » Настоящее закрытие файла :)
 
  • Riply © (16.12.08 11:45) [0]
    Здравствуйте !
    (Не смогла придумать другое название ветки. Будем считать что и это отражает ее суть :) )

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

    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 закрыть файл "совсем" или хотябы дождаться
    > когда это сделает система

    Так ты покажи как ты делаеш это сейчас :)
  • Riply © (17.12.08 22:52) [2]
    > [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]
    Удалено модератором
    Примечание: Оффтоп в тематическом разделе...
  • Riply © (18.12.08 02:32) [4]
    > [2] Riply © (17.12.08 22:52)

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

    Если это объяснение похоже на правду, а не является "притянутым за уши",
    то и появляется мой вопрос: как из приложения "совсем" закрыть файл
    или, хотя бы подождать когда это сделает система ?
  • Rouse_ © (18.12.08 09:52) [5]
    Вообще конечно неначем проверить, но попробуй перед выставлением размера проверить MmCanFileBeTruncated() и если что-то не так то скажи MmForceSectionClosed()
  • Riply © (18.12.08 15:41) [6]
    > Вообще конечно неначем проверить, но попробуй перед выставлением размера
    > проверить 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 уже не существует...
    Вообщем совсем запуталась :(
  • Riply © (18.12.08 15:47) [7]
    Sorry. Копи\пайст подвел.
    Вот не зря у нас на форуме настойчиво советуют им не пользоваться :)
  • Rouse_ © (18.12.08 18:35) [8]
    Вот это посмотри: http://www.rsdn.ru/forum/message/624367.aspx
  • Riply © (19.12.08 06:43) [9]
    > [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

    Вообщем решила так:
    Раз с нахрапа(методом тыка) взять не удалось
    - переходим к планомерной осаде(изучению документации) :)
 
Конференция "WinAPI" » Настоящее закрытие файла :)
Есть новые Нет новых   [134435   +35][b:0][p:0.004]