Поделиться через


Запрос и предоставление оплоков

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

oplocks запрашиваются через FSCTLs. Следующие FSCTLs используются для различных типов oplock, которые могут выдавать как приложения в пользовательском режиме, так и драйверы в режиме ядра.

Запрос оплока в пользовательском режиме

Чтобы запросить oplock Windows 7 в режиме пользователя, вызовите DeviceIoControl:

  • Установите для dwIoControlCode значение на FSCTL_REQUEST_OPLOCK.
  • Передайте указатель на структуру REQUEST_OPLOCK_INPUT_BUFFER в параметре lpInBuffer.
    • Для получения информации о том, как форматировать запрос oplock, см. документацию по этой структуре.
  • Передайте указатель на структуру REQUEST_OPLOCK_OUTPUT_BUFFER в параметре lpOutBuffer.

Дополнительные сведения см. в FSCTL_REQUEST_OPLOCK.

Если запрошенная блокировка может быть предоставлена, DeviceIoControl возвращает значение FALSE и GetLastError возвращает ERROR_IO_PENDING. По этой причине оплоки никогда не предоставляются при синхронных операциях ввода-вывода. Перекрываемая операция не завершается до тех пор, пока не будет снята блокировка. Как только операция завершится, REQUEST_OPLOCK_OUTPUT_BUFFER будет содержать информацию о разрыве оплока.

Если не удается предоставить оплок, файловая система возвращает соответствующий код ошибки. Чаще всего возвращаются коды ошибок ERROR_OPLOCK_NOT_GRANTED и ERROR_INVALID_PARAMETER.

Запрос блокировки в режиме ядра

Чтобы запросить оплоки Windows 7 в режиме ядра:

Мини-фильтры файловой системы

Мини-фильтр файловой системы должен использовать FltAllocateCallbackData и заполнить выделенные FLT_CALLBACK_DATA следующим образом:

  • Задайте поле Iopbна>MajorFunction значение IRP_MJ_FILE_SYSTEM_CONTROL.
  • Задайте для поля Iopb->MinorFunction значение IRP_MN_USER_FS_REQUEST.
  • Задайте параметру Iopb->элемента FileSystemControl.Buffered.FsControlCode значение FSCTL_REQUEST_OPLOCK.
  • Выделите буфер, размер которого равен большему из REQUEST_OPLOCK_INPUT_BUFFER или REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Задайте выделенный элемент FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.SystemBuffer, чтобы указать на этот буфер.
    • Задайте выделенные FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.InputBufferLength и Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength поля в размере этого буфера.

Сведения о форматировании запроса oplock см. в документации по структуре REQUEST_OPLOCK_INPUT_BUFFER.

Затем мини-фильтр файловой системы должен вызывать FltPerformAsynchronousIo, передав выделенный FLT_CALLBACK_DATA в качестве параметра CallbackData.

Если запрошенный оплок можно предоставить, вызов FltPerformAsynchronousIo возвращает STATUS_PENDING. По этой причине оплоки никогда не предоставляются при синхронных операциях ввода-вывода. Операция не завершается до тех пор, пока не будет нарушена блокировка. Как только операция завершится, REQUEST_OPLOCK_OUTPUT_BUFFER будет содержать информацию о разрыве оплока.

Если не удается предоставить оплок, файловая система возвращает соответствующий код ошибки. Чаще всего возвращаются коды ошибок STATUS_OPLOCK_NOT_GRANTED и STATUS_INVALID_PARAMETER.

Другие типы драйверов

Другие типы драйверов могут вызывать ZwFsControlFile:

  • Задайте значением FsControlCodeFSCTL_REQUEST_OPLOCK.
  • Передайте указатель на структуру REQUEST_OPLOCK_INPUT_BUFFER в параметре InputBuffer и задайте параметру InputBufferLength значение размера этого буфера.
  • Передайте указатель на структуру REQUEST_OPLOCK_OUTPUT_BUFFER в параметре OutputBuffer и задайте параметру OutputBufferLength значение размера этого буфера.

Сведения о форматировании запроса oplock см. в документации по структуре REQUEST_OPLOCK_INPUT_BUFFER.

Если запрошенный оплок может быть предоставлен, вызов ZwFsControlFile возвращает STATUS_PENDING. По этой причине оплоки никогда не предоставляются при синхронных операциях ввода-вывода. Операция не завершается до тех пор, пока не будет нарушена блокировка. Как только операция завершится, REQUEST_OPLOCK_OUTPUT_BUFFER будет содержать информацию о разрыве оплока.

Если не удается предоставить оплок, файловая система возвращает соответствующий код ошибки. Чаще всего возвращаются коды ошибок STATUS_OPLOCK_NOT_GRANTED и STATUS_INVALID_PARAMETER.

Предотвращение нарушений общего доступа при запросе оплоков

Использование метода Atomic Create-With-Oplock

Атомарный тип create-with-oplock не является типом oplock. Скорее, это процедура, которая позволяет открывать операции, чтобы избежать нарушений режима общего доступа в интервале времени между открытием файла и получением оплока. При использовании устаревших оплоков требуются фильтрационные оплоки и открытие двух ручек. При использовании oplock в Windows 7 приложение или драйвер может запрашивать любой тип oplock с помощью этой процедуры и требуется открыть только один дескриптор.

Чтобы выполнить атомарную процедуру create-with-oplock, необходимо:

  1. Для открытия файла используйте FltCreateFileEx2 или ZwCreateFile, в зависимости от необходимости. В параметре CreateOptions передайте флаг FILE_OPEN_REQUIRING_OPLOCK. Вы можете задать параметры DesiredAccess и ShareAccess как угодно. Например, в наборе параметров DesiredAccess установите GENERIC_READ, чтобы можно было прочитать файл, а в параметре ShareAccess установите флаги FILE_SHARE_READ | FILE_SHARE_DELETE, позволяющие другим пользователям читать, переименовывать и/или помечать файл для удаления, пока он открыт вами.
  2. Используйте код элемента управления FSCTL_REQUEST_OPLOCK, чтобы запросить оплок в результирующем объекте файла или дескрипторе, как описано в запросе оплока в режиме ядра.

Не выполняйте никаких операций файловой системы в файле между шагами 1 и 2. Это может привести к взаимоблокировкам.

Наиболее распространённый оплок для запроса с помощью этой процедуры — это тип Read-Handle. Это позволяет предоставить другим абонентам максимальный одновременный доступ, при этом уведомляться, если необходимо закрыть дескриптор, чтобы избежать нарушения общего доступа в случае конфликта при открытии.

Использование устаревшего фильтра Oplock

Устаревшая блокировка фильтра также позволяет приложению "вернуться", когда другие приложения или клиенты пытаются получить доступ к тому же потоку, но менее гибким, чем атомарный метод create-with-oplock. Этот механизм позволяет приложению получать доступ к потоку, не приводя к нарушениям общего доступа у других объектов доступа при попытке открыть поток. Чтобы избежать нарушений общего доступа, следует использовать следующую трехэтапную процедуру для запроса оплока фильтра:

  1. Откройте файл с доступом, необходимым для FILE_READ_ATTRIBUTES, и с режимом совместного использования FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. Дескриптор, открытый на этом шаге, не приведет к нарушениям общего доступа к другим приложениям, так как он открыт только для доступа к атрибутам (FILE_READ_ATTRIBUTES), а не доступа к данным (FILE_READ_DATA). Этот дескриптор подходит для запроса оплока фильтра, но не для выполнения фактических операций ввода-вывода в потоке данных.

  2. Запрос блокировки фильтра (FSCTL_REQUEST_FILTER_OPLOCK) на дескриптор из шага 1. Оплок, предоставленный на этом шаге, позволяет владельцу oplock "выйти из пути", не вызывая нарушение общего доступа к другому приложению, которое пытается получить доступ к потоку.

  3. Снова откройте файл для доступа на чтение. Рукоятка, открытая на этом шаге, позволяет держателю блокировки oplock выполнять ввод-вывод на потоке.

Файловая система NTFS обеспечивает оптимизацию этой процедуры с помощью флага параметра создания FILE_RESERVE_OPFILTER. Если этот флаг указан на шаге 1 предыдущей процедуры, он позволяет файловой системе завершить запрос на создание со статусом STATUS_OPLOCK_NOT_GRANTED, если файловая система сможет определить, что шаг 2 потерпит неудачу. Если шаг 1 успешно выполнен, нет гарантии успешности шага 2, даже если FILE_RESERVE_OPFILTER был указан для запроса на создание.

Условия предоставления oplocks

В следующей таблице определены необходимые условия, необходимые для предоставления оплока.

Тип запроса Условия

Уровень 1

Фильтр

Партия

Предоставляется только в том случае, если выполняются все следующие условия:

  • Запрос предназначен для заданного потока файла.
    • Если это каталог, возвращается STATUS_INVALID_PARAMETER.
  • Поток открыт для АСИНХРОННОГО доступа.
    • При открытии для синхронного доступа возвращается STATUS_OPLOCK_NOT_GRANTED (оплоки не предоставляются для синхронных запросов ввода-вывода).
  • На любом потоке файла нет ни одной транзакции TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет других открытий (даже по одному потоку).
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.

Если текущее состояние оплока:

  • Нет oplock: запрос удовлетворён.

  • Уровень 2: Исходный запрос уровня 2 прерывается на FILE_OPLOCK_BROKEN_TO_NONE. Затем запрашиваемый эксклюзивный оплок предоставляется.

  • Уровень 1, пакетная служба, фильтрация, чтение, дескриптор чтения, чтения и записи: возвращается STATUS_OPLOCK_NOT_GRANTED.

Уровень 2

Предоставляется только в том случае, если выполняются все следующие условия:

  • Запрос предназначен для заданного потока файла.
    • Если это каталог, возвращается STATUS_INVALID_PARAMETER.
  • Поток открыт для АСИНХРОННОГО доступа.
    • Если он открыт для синхронного доступа, возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В файле нет транзакций TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет текущих блокировок диапазона байтов.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
    • До Windows 7 операционная система проверяет, существовала ли когда-либо блокировка диапазона байтов в потоке с момента последнего открытия, и запрос завершается сбоем, если это так.

Если текущее состояние оплока:

  • Нет oplock: запрос удовлетворён.

  • Уровень 2 и/или чтение: запрос удовлетворен. В одном потоке одновременно могут быть предоставлены несколько оптимистических блокировок уровня 2/для чтения. Несколько оплоков уровня 2 (но не для чтения) могут существовать на одном дескрипторе.
    • Если для дескриптора запрашивается блокировка чтения, которой уже предоставлена блокировка чтения, IRP первого запроса блокировки чтения завершается с STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE до предоставления второй блокировки чтения.
  • Уровень 1, пакетная служба, фильтр, дескриптор чтения, чтение и запись, дескриптор записи: возвращается STATUS_OPLOCK_NOT_GRANTED.

Читать

Предоставляется только в том случае, если выполняются все следующие условия:

  • Запрос предназначен для заданного потока файла.
  • Поток открыт для АСИНХРОННОГО доступа.
    • Если он открыт для синхронного доступа, возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В файле нет транзакций TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет текущих блокировок диапазона байтов.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет записываемых сопоставлен ных пользователем разделов.
    • Иначе возвращается STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER. Флаги поля будут иметь набор флагов REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Если текущее состояние оплока:

  • Нет oplock: запрос удовлетворён.

  • Уровень 2 и/или чтение: запрос удовлетворен. В одном потоке одновременно могут быть предоставлены несколько оптимистических блокировок уровня 2/для чтения.
    • Кроме того, если существующий oplock имеет тот же ключ oplock, что и новый запрос, его IRP завершается с STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle и существующий oplock имеют другой ключ oplock, чем новый запрос: запрос одобряется. Несколько операций чтения и Read-Handle могут сосуществовать в одном потоке (см. примечание ниже этой таблицы).
    • Иначе (ключи oplock совпадают) STATUS_OPLOCK_NOT_GRANTED возвращается.
  • Уровень 1, пакетная служба, фильтр, чтение-запись, чтение-запись-дескриптор: возвращается STATUS_OPLOCK_NOT_GRANTED.

Read-Handle

Предоставляется только в том случае, если выполняются все следующие условия:

  • Запрос предназначен для заданного потока файла.
  • Поток открыт для АСИНХРОННОГО доступа.
    • Если он открыт для синхронного доступа, возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В файле нет транзакций TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет текущих блокировок диапазона байтов.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет записываемых сопоставлен ных пользователем разделов.
    • Иначе возвращается STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER. Флаги поля будут иметь набор флагов REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Если текущее состояние оплока:

  • Нет оплока: запрос предоставлен.

  • Прочитать: запрос удовлетворён.
    • Если существующий Read oplock имеет тот же ключ oplock, что и новый запрос, его IRP завершается со статусом STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. Результатом является обновление oplock с режима чтения до режима обработки чтения.
    • Любой существующий оплок чтения, который не имеет того же ключа oplock, что и новый запрос, остается неизменным.
  • Уровень 2, уровень 1, пакет, фильтр, чтение-запись, чтение-запись-дескриптор: возвращается STATUS_OPLOCK_NOT_GRANTED.

Read-Write

Предоставляется только в том случае, если выполняются все следующие условия:

  • Запрос предназначен для заданного потока файла.
    • Если это каталог, возвращается STATUS_INVALID_PARAMETER.
  • Поток открыт для АСИНХРОННОГО доступа.
    • Если он открыт для синхронного доступа, возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В файле нет транзакций TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • Если в потоке данных есть другие запросы на открытие (даже от того же потока), они должны использовать тот же назначенный ключ oplock.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет записываемых сопоставлен ных пользователем разделов.
    • Иначе возвращается STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER. Флаги поля будут иметь набор флагов REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Если текущее состояние оплока:

  • Нет оплока: запрос предоставлен.

  • Чтение или Read-Write, и существующий oplock имеет тот же ключ oplock, что и запрос: IRP существующего oplock завершен с STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE, и запрос будет выполнен.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • Уровень 2, уровень 1, пакет, фильтр, дескриптор для чтения, дескриптор для чтения и записи: возвращается STATUS_OPLOCK_NOT_GRANTED.

Прочтите —Write-Handle

Предоставлено только в случае, если все следующие утверждения истинны:

  • Запрос предназначен для заданного потока файла.
    • Если это каталог, возвращается STATUS_INVALID_PARAMETER.
  • Поток открыт для АСИНХРОННОГО доступа.
    • Если он открыт для синхронного доступа, возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В файле нет транзакций TxF.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • Если в потоке есть другие открытые запросы, даже если они исходят от того же потока, они должны иметь тот же ключ oplock.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • В потоке нет записываемых сопоставлен ных пользователем разделов.
    • Иначе возвращается STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER. Флаги поля будут иметь набор флагов REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Если текущее состояние оплока:

  • Нет оплока: запрос предоставлен.

  • Чтение, обработка чтения, чтение и запись илиWrite-Handle, и существующий замок имеет тот же ключ замка, что и запрос: существующий IRP замка завершен с STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE, и запрос предоставляется.
    • В противном случае возвращается STATUS_OPLOCK_NOT_GRANTED.
  • Уровень 2, уровень 1, пакет, фильтр: возвращается STATUS_OPLOCK_NOT_GRANTED.

Заметка

Операции чтения и оплоки уровня 2 могут сосуществовать в одном потоке. Операции чтения и оплоки Read-Handle могут сосуществовать. Однако оплоки уровня 2 и Read-Handle не могут сосуществовать.