Partilhar via


Solicitando e concedendo oplocks

Quando o redirecionador de rede acessa arquivos em servidores remotos, ele solicita o oplock do servidor remoto. Os aplicativos cliente solicitam oplocks diretamente somente quando o bloqueio é destinado a um arquivo no servidor local.

Oplocks são solicitados através FSCTLs. As FSCTLs a seguir são usados para os diferentes tipos de oplock, que tanto as aplicações de modo utilizador quanto os drivers de modo núcleo podem emitir.

Solicitando um oplock no modo de usuário

Para solicitar um oplock do Windows 7 no modo de usuário, chame DeviceIoControl:

Para obter mais informações, consulte FSCTL_REQUEST_OPLOCK.

Se o oplock solicitado puder ser concedido, DeviceIoControl retornará FALSE e GetLastError retornará ERROR_IO_PENDING. Por esse motivo, os oplocks nunca são concedidos para E/S síncronas. A operação sobreposta não é concluída até que o oplock seja quebrado. Após a conclusão da operação, o REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a quebra do oplock.

Se o oplock não puder ser concedido, o sistema de arquivos retornará um código de erro apropriado. Os códigos de erro mais comumente retornados são ERROR_OPLOCK_NOT_GRANTED e ERROR_INVALID_PARAMETER.

Solicitar um oplock em modo kernel

Para solicitar oplocks do Windows 7 em modo kernel:

Minifiltros do sistema de arquivos

Um minifiltro do sistema de arquivos deve usar FltAllocateCallbackData e preencher a FLT_CALLBACK_DATA alocada da seguinte forma:

  • Defina seu campo Iopb->MajorFunction como IRP_MJ_FILE_SYSETM_CONTROL.
  • Defina o campo Iopb->MinorFunction como IRP_MN_USER_FS_REQUEST.
  • Defina os parâmetros Iopb->do membro FileSystemControl.Buffered.FsControlCode para FSCTL_REQUEST_OPLOCK.
  • Aloque um buffer cujo tamanho seja igual ao maior dos REQUEST_OPLOCK_INPUT_BUFFER ou REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Defina o do FLT_CALLBACK_DATAalocado Iopb->Parameters.FileSystemControl.Buffered.SystemBuffer membro para apontar para esse buffer.
    • Defina o alocado do FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.InputBufferLength e campos Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength para o tamanho desse buffer.

Consulte a documentação da estrutura REQUEST_OPLOCK_INPUT_BUFFER para obter informações sobre como formatar a solicitação de oplock.

Em seguida, o minifiltro do sistema de arquivos deve chamar FltPerformAsynchronousIo, passando FLT_CALLBACK_DATA alocado como parâmetro CallbackData.

Se o oplock solicitado puder ser concedido, a chamada FltPerformAsynchronousIo retornará STATUS_PENDING. Por esse motivo, os oplocks nunca são concedidos para E/S síncronas. A operação não é concluída até que o "oplock" seja quebrado. Após a conclusão da operação, o REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a quebra do oplock.

Se o oplock não puder ser concedido, o sistema de arquivos retornará um código de erro apropriado. Os códigos de erro mais comumente retornados são STATUS_OPLOCK_NOT_GRANTED e STATUS_INVALID_PARAMETER.

Outros Tipos de Drivers

Outros tipos de drivers podem chamar ZwFsControlFile:

  • Defina FsControlCode como FSCTL_REQUEST_OPLOCK.
  • Passe um ponteiro para uma estrutura de REQUEST_OPLOCK_INPUT_BUFFER no parâmetro InputBuffer do e defina o parâmetro InputBufferLength para o tamanho desse buffer.
  • Passe um ponteiro para uma estrutura de REQUEST_OPLOCK_OUTPUT_BUFFER no parâmetro OutputBuffer do e defina o parâmetro OutputBufferLength para o tamanho desse buffer.

Consulte a documentação da estrutura REQUEST_OPLOCK_INPUT_BUFFER para obter informações sobre como formatar a solicitação de oplock.

Se o oplock solicitado puder ser concedido, a chamada ZwFsControlFile devolverá STATUS_PENDING. Por esse motivo, os oplocks nunca são concedidos para E/S síncronas. A operação não é concluída até que o oplock seja quebrado. Após a conclusão da operação, o REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a quebra do oplock.

Se o oplock não puder ser concedido, o sistema de arquivos retornará um código de erro apropriado. Os códigos de erro mais comumente retornados são STATUS_OPLOCK_NOT_GRANTED e STATUS_INVALID_PARAMETER.

Evitando violações de compartilhamento ao solicitar Oplocks

Usando o método Atomic Create-With-Oplock

Atomic create-with-oplock não é um tipo de oplock. Em vez disso, é um procedimento que permite operações abertas para evitar causar violações do modo de compartilhamento no intervalo de tempo entre a abertura de um arquivo e o recebimento de um oplock. Com oplocks legados, é necessário filtrar oplocks e abrir duas alças. Com os oplocks do Windows 7, um aplicativo ou driver pode solicitar qualquer tipo de oplock usando este procedimento e precisa de abrir apenas um handle.

Para executar o procedimento create-with-oplock atômico, você deve:

  1. Use FltCreateFileEx2 ou ZwCreateFile, conforme apropriado, para abrir o arquivo. No parâmetro CreateOptions, passe o indicador FILE_OPEN_REQUIRING_OPLOCK. Você pode definir os parâmetros DesiredAccess e ShareAccess conforme desejado. Por exemplo, no parâmetro DesiredAccess, defina GENERIC_READ para poder ler o arquivo, e no parâmetro ShareAccess, defina os sinalizadores FILE_SHARE_READ | FILE_SHARE_DELETE para permitir que outras pessoas leiam, renomeiem e/ou marquem o arquivo para eliminação enquanto o tiver aberto.
  2. Use o código de controlo FSCTL_REQUEST_OPLOCK para solicitar um oplock no objeto ou identificador de ficheiro resultante, conforme descrito em Solicitar um Oplock no Modo Kernel.

Não execute nenhuma operação do sistema de arquivos no arquivo entre as etapas 1 e 2. Fazer isso pode causar impasses.

O oplock mais comum a ser solicitado através deste procedimento é o tipo Read-Handle. Isso permite que você permita que outros chamadores tenham o máximo de acesso simultâneo possível, enquanto ainda permite que você seja notificado se precisar fechar seu identificador para evitar causar uma violação de compartilhamento em uma abertura conflitante.

Usando o filtro herdado Oplock

O filtro oplock legado também permite que um aplicativo retroceda quando outros aplicativos/clientes tentam acessar o mesmo fluxo, mas é menos flexível do que o método de criação-com-oplock atômico. Esse mecanismo permite que um aplicativo acesse um fluxo sem fazer com que outros acessadores do fluxo recebam violações de compartilhamento ao tentar abrir o fluxo. Para evitar violações de compartilhamento, o procedimento de três etapas a seguir deve ser usado para solicitar um bloqueio de filtro:

  1. Abra o ficheiro com um acesso requerido de FILE_READ_ATTRIBUTES e um modo de partilha de FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. O identificador aberto nesta etapa não fará com que outros aplicativos recebam violações de compartilhamento porque está aberto apenas para acesso a atributos (FILE_READ_ATTRIBUTES) e não para acesso a dados (FILE_READ_DATA). Este identificador é adequado para solicitar a operação de bloqueio de filtro, mas não para executar E/S reais no fluxo de dados.

  2. Solicite um filtro oplock (FSCTL_REQUEST_FILTER_OPLOCK) no handle do passo 1. O oplock concedido nesta etapa permite que o detentor do oplock "saia do caminho" sem causar uma violação de compartilhamento para outro aplicativo que tenta acessar o fluxo.

  3. Abra o arquivo novamente para acesso de leitura. O identificador aberto nesta etapa permite que o detentor do oplock execute operações de Entrada/Saída no fluxo.

O sistema de ficheiros NTFS fornece uma otimização para este procedimento através do sinalizador de opção de criação FILE_RESERVE_OPFILTER. Se este sinalizador for especificado na etapa 1 do procedimento anterior, isso permite que o sistema de ficheiros recuse a solicitação de criação com STATUS_OPLOCK_NOT_GRANTED se puder determinar que a etapa 2 irá falhar. Se a etapa 1 for bem-sucedida, não há garantia de que a etapa 2 será bem-sucedida, mesmo que FILE_RESERVE_OPFILTER tenha sido especificada para a solicitação de criação.

Condições para a concessão de Oplocks

A tabela a seguir identifica as condições requeridas para a concessão de um oplock.

Tipo de pedido Condições

Nível 1

Filtrar

Lote

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
    • Se for um diretório, STATUS_INVALID_PARAMETER é retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Se for aberto para acesso síncrono, STATUS_OPLOCK_NOT_GRANTED é retornado (oplocks não são concedidos para solicitações de E/S síncronas).
  • Não há transações TxF em qualquer fluxo do arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não há outras aberturas no fluxo (mesmo pelo mesmo thread).
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.

Se o estado de oplock atual for:

  • No oplock: O pedido é concedido.

  • Nível 2: A solicitação original de Nível 2 é interrompida com FILE_OPLOCK_BROKEN_TO_NONE. O bloqueio exclusivo solicitado é, então, concedido.

  • Nível 1, Batch, Filtro, Leitura, Leitura-Manuseio, Leitura-Gravação ou Leitura-Gravação-Manuseio: STATUS_OPLOCK_NOT_GRANTED é retornado.

Nível 2

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
    • Se for um diretório, STATUS_INVALID_PARAMETER é retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Caso seja aberto para acesso SÍNCRONO, será retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não existem bloqueios de intervalo de bytes atuais no fluxo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
    • Antes do Windows 7, o sistema operacional verifica se um bloqueio de intervalo de bytes já existiu no fluxo desde a última vez que foi aberto e, em caso afirmativo, falha na solicitação.

Se o estado de oplock atual for:

  • No oplock: O pedido é concedido.

  • Nível 2 e/ou Leitura: O pedido é deferido. Você pode ter vários oplocks de Nível 2/Leitura concedidos no mesmo fluxo ao mesmo tempo. Vários oplocks de Nível 2 (mas não de leitura) podem até existir na mesma alça.
    • Se um bloqueio de leitura for solicitado numa alça (handle) que já tenha um bloqueio de leitura atribuído a ela, o IRP do primeiro bloqueio de leitura será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE antes que o segundo bloqueio de leitura seja atribuído.
  • Nível 1, Lote, Filtro, Manipulação de Leitura, Leitura-Escrita, Manipulação de Leitura-Escrita: STATUS_OPLOCK_NOT_GRANTED é devolvido.

Ler

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
  • O fluxo é aberto para acesso assíncrono.
    • Caso seja aberto para acesso SÍNCRONO, será retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não existem bloqueios de intervalo de bytes atuais no fluxo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não há seções escrevíveis mapeadas pelo usuário no fluxo.
    • Caso contrário, STATUS_CANNOT_GRANT_REQUESTED_OPLOCK é devolvido. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado de oplock atual for:

  • No oplock: O pedido é concedido.

  • Nível 2 e/ou Leitura: O pedido é deferido. Você pode ter vários oplocks de Nível 2/Leitura concedidos no mesmo fluxo ao mesmo tempo.
    • Além disso, se um oplock existente tiver a mesma chave oplock que a nova solicitação, o seu IRP será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle e o oplock existente têm uma chave oplock diferente da nova solicitação: A solicitação é concedida. Vários oplocks de Leitura e de Read-Handle podem coexistir no mesmo fluxo (veja a nota a seguir a esta tabela).
    • Caso contrário, se as teclas oplock forem as mesmas, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Nível 1, Lote, Filtro, Leitura-Gravação, Leitura-Gravação-Handle: STATUS_OPLOCK_NOT_GRANTED é retornado.

Read-Handle

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
  • O fluxo é aberto para acesso assíncrono.
    • Caso seja aberto para acesso SÍNCRONO, será retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não existem bloqueios de intervalo de bytes atuais no fluxo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não há seções escrevíveis mapeadas pelo usuário no fluxo.
    • Caso contrário, STATUS_CANNOT_GRANT_REQUESTED_OPLOCK é devolvido. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado de oplock atual for:

  • Sem oplock: o pedido é deferido.

  • Leia-se: o pedido é deferido.
    • Se um oplock de leitura existente tiver a mesma chave oplock que a nova solicitação, o seu IRP será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. O resultado é que o oplock é atualizado de Read para Read-Handle.
    • Qualquer Read oplock existente que não tenha a mesma chave oplock que a nova solicitação permanece inalterada.
  • Nível 2, Nível 1, Lote, Filtro, Leitura-Gravação, Leitura-Gravação-Manipulação: STATUS_OPLOCK_NOT_GRANTED é retornado.

Read-Write

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
    • Se for um diretório, STATUS_INVALID_PARAMETER é retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Caso seja aberto para acesso SÍNCRONO, será retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Se houver outras aberturas no fluxo (mesmo pelo mesmo thread), elas devem ter a mesma tecla oplock.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não há seções escrevíveis mapeadas pelo usuário no fluxo.
    • Caso contrário, STATUS_CANNOT_GRANT_REQUESTED_OPLOCK é devolvido. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado de oplock atual for:

  • Sem oplock: o pedido é deferido.

  • Leia ou Read-Write e o oplock existente tem a mesma chave oplock que a solicitação: o IRP do oplock existente é concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e a solicitação é concedida.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Nível 2, Nível 1, Lote, Filtro, Read-Handle, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED é retornado.

Ler-Write-Handle

Concedido somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um determinado fluxo de um arquivo.
    • Se for um diretório, STATUS_INVALID_PARAMETER é retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Caso seja aberto para acesso SÍNCRONO, será retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Se houver outras solicitações abertas no fluxo, mesmo pelo mesmo thread, elas devem ter a mesma chave oplock.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Não há seções escrevíveis mapeadas pelo usuário no fluxo.
    • Caso contrário, STATUS_CANNOT_GRANT_REQUESTED_OPLOCK é devolvido. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado de oplock atual for:

  • Sem oplock: o pedido é deferido.

  • Read, Read-Handle, Read-Write, ou Read-Write-Handle e o oplock existente tem a mesma chave oplock que a solicitação: o IRP do oplock existente é concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e a solicitação é concedida.
    • Caso contrário, STATUS_OPLOCK_NOT_GRANTED é devolvido.
  • Nível 2, Nível 1, Lote, Filtro: STATUS_OPLOCK_NOT_GRANTED é retornado.

Observação

Os oplocks de leitura e de nível 2 podem coexistir no mesmo fluxo, e os oplocks de leitura e Read-Handle podem coexistir, mas os oplocks de nível 2 e Read-Handle não podem coexistir.