Compartilhar via


Solicitar e conceder oplocks

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

Oplocks são solicitados por meio de FSCTLs. Os FSCTLs a seguir são usados para os diferentes tipos de oplock, que tanto aplicativos em modo de usuário quanto drivers em modo kernel 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íncrona. A operação sobreposta não é concluída até que o oplock seja interrompido. Após a conclusão da operação, REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a interrupção 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 retornados são ERROR_OPLOCK_NOT_GRANTED e ERROR_INVALID_PARAMETER.

Solicitando um oplock no modo kernel

Para solicitar oplocks do Windows 7 no modo kernel:

Minifiltros do sistema de arquivos

Um minifiltro do sistema de arquivos deve usar FltAllocateCallbackData e preencher o FLT_CALLBACK_DATA alocado da seguinte maneira:

  • Defina o campo Iopb->MajorFunction como IRP_MJ_FILE_SYSETM_CONTROL.
  • Defina o campo Iopb->MinorFunction como IRP_MN_USER_FS_REQUEST.
  • Defina o membro Iopb->Parameters.FileSystemControl.Buffered.FsControlCode como FSCTL_REQUEST_OPLOCK.
  • Alocar um buffer cujo tamanho seja igual ao maior REQUEST_OPLOCK_INPUT_BUFFER ou REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Defina o membro alocado FLT_CALLBACK_DATA's Iopb->Parameters.FileSystemControl.Buffered.SystemBuffer para apontar para esse buffer.
    • Defina os campos alocados FLT_CALLBACK_DATA's Iopb->Parameters.FileSystemControl.Buffered.InputBufferLength e 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 do oplock.

Em seguida, o minifiltro do sistema de arquivos deve chamar FltPerformAsynchronousIo, passando o FLT_CALLBACK_DATA alocado como o 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íncrona. A operação não será concluída até a interrupção do oplock. Após a conclusão da operação, REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a interrupção 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 retornados são STATUS_OPLOCK_NOT_GRANTED e STATUS_INVALID_PARAMETER.

Outros tipos de drivers

Outros tipos de drivers podem chamar ZwFsControlFile:

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

Se o oplock solicitado puder ser concedido, a chamada ZwFsControlFile retornará STATUS_PENDING. Por esse motivo, os oplocks nunca são concedidos para E/S síncrona. A operação não será concluída até a interrupção do oplock. Após a conclusão da operação, REQUEST_OPLOCK_OUTPUT_BUFFER conterá informações sobre a interrupção 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 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 que as operações abertas evitem causar violações de modo de compartilhamento no intervalo de tempo entre abrir um arquivo e receber um oplock. Com os oplocks herdados, é necessário filtrar os oplocks e abrir dois identificadores. Com os oplocks do Windows 7, um aplicativo ou driver pode solicitar qualquer tipo de oplock usando esse procedimento e precisa abrir apenas um identificador.

Para executar o procedimento atomic create-with-oplock, você deve:

  1. Use FltCreateFileEx2 ou ZwCreateFile, conforme apropriado, para abrir o arquivo. No parâmetro CreateOptions, passe o sinalizador FILE_OPEN_REQUIRING_OPLOCK. Você pode definir conforme desejado os parâmetros DesiredAccess e ShareAccess. Por exemplo, no parâmetro DesiredAccess, defina GENERIC_READ para que você possa 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 exclusão enquanto você o tiver aberto.
  2. Use o código de controle FSCTL_REQUEST_OPLOCK para solicitar um oplock no objeto ou identificador de arquivo resultante, conforme descrito em Solicitando 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 deadlocks.

O oplock mais comum que pode ser solicitado usando-se esse procedimento é o tipo Leitura-Identificador. Isso permite conceder a outros chamadores o máximo de acesso simultâneo possível e ainda permite que você seja notificado se precisar fechar o identificador para evitar uma violação de compartilhamento em uma abertura conflitante.

Usando o oplock Filtro herdado

O oplock Filtro herdado também permite que um aplicativo "volte atrás" quando outros aplicativos/clientes tentam acessar o mesmo fluxo, mas é menos flexível do que o método atomic create-with-oplock. Esse mecanismo permite que um aplicativo acesse um fluxo sem que os outros acessadores do fluxo recebam violações de compartilhamento quando tentarem abrir o fluxo. Para evitar violações de compartilhamento, execute o seguinte procedimento de três etapas para solicitar um oplock Filtro:

  1. Abra o arquivo com o acesso necessário de FILE_READ_ATTRIBUTES e um modo de compartilhamento de FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. O identificador aberto nessa etapa não causará violações de compartilhamento em outros aplicativos porque está aberto apenas para acesso de atributo (FILE_READ_ATTRIBUTES) e não para acesso de dados (FILE_READ_DATA). Esse identificador é apropriado para solicitar o oplock Filtro, mas não para executar a E/S propriamente dita no fluxo de dados.

  2. Solicite um oplock Filtro (FSCTL_REQUEST_FILTER_OPLOCK) no identificador obtido na etapa 1. O oplock concedido nessa etapa permite que o titular 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 com o acesso de leitura. O identificador aberto nessa etapa permite que o titular do oplock execute E/S no fluxo.

O sistema de arquivos NTFS fornece uma otimização para esse procedimento por meio do sinalizador FILE_RESERVE_OPFILTER da opção de criação. Se esse sinalizador for especificado na etapa 1 do procedimento anterior, permitirá que o sistema de arquivos falhe na solicitação de criação com STATUS_OPLOCK_NOT_GRANTED se puder determinar que a etapa 2 falhará. Se a etapa 1 for bem-sucedida, isso não significa que a etapa 2 terá êxito, mesmo que haja a especificação de FILE_RESERVE_OPFILTER para a solicitação de criação.

Condições para conceder oplocks

A tabela a seguir identifica as condições necessárias para conceder um oplock.

Tipo de solicitação Condições

Nível 1

Filtro

Lote

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
    • Caso seja um diretório, STATUS_INVALID_PARAMETER será retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, será retornado STATUS_OPLOCK_NOT_GRANTED (oplocks não são concedidos para solicitações de E/S síncrona).
  • Não há transações TxF em nenhum fluxo do arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há nenhuma outra abertura no fluxo (mesmo que seja pelo mesmo thread).
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • Nível 2: a solicitação Nível 2 original é interrompida com FILE_OPLOCK_BROKEN_TO_NONE. O oplock exclusivo solicitado é concedido.

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

Nível 2

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
    • Caso seja um diretório, STATUS_INVALID_PARAMETER será retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há bloqueios atuais de intervalo de bytes no fluxo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
    • Antes do Windows 7, o sistema operacional verifica se houve um bloqueio de intervalo de bytes no fluxo desde a última abertura e, caso isso tenha ocorrido, a solicitação é negada.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • Nível 2 e/ou Leitura: a solicitação é concedida. Você pode ter vários oplocks Nível 2/Leitura concedidos no mesmo fluxo ao mesmo tempo. Várias oplocks Nível 2 (mas não Leitura) podem existir até mesmo em um único identificador.
    • Se um oplock Leitura for solicitado em um identificador que já tem um oplock Leitura concedido, o IRP do primeiro oplock Leitura será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE antes que o segundo oplock Leitura seja concedido.
  • Nível 1, Lote, Filtro, Leitura-Identificador, Leitura-Gravação, Leitura-Gravação-Identificador: é retornado STATUS_OPLOCK_NOT_GRANTED.

Ler

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há bloqueios atuais de intervalo de bytes no fluxo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há seções mapeadas pelo usuário graváveis no fluxo.
    • Do contrário, é retornado STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o sinalizador REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • Nível 2 e/ou Leitura: a solicitação é concedida. Você pode ter vários oplocks Nível 2/Leitura concedidos no mesmo fluxo ao mesmo tempo.
    • Além disso, se um oplock existente tiver a mesma chave de oplock que a nova solicitação, seu IRP será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle e o oplock existente têm uma chave de oplock diferente da nova solicitação: a solicitação é concedida. Vários oplocks Leitura e Leitura-Identificador podem coexistir no mesmo fluxo (consulte a observação após esta tabela).
    • Se as chaves de oplock são as mesmas, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Nível 1, Lote, Filtro, Leitura-Gravação, Leitura-Gravação-Identificador: é retornado STATUS_OPLOCK_NOT_GRANTED.

Leitura-Identificador

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há bloqueios atuais de intervalo de bytes no fluxo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há seções mapeadas pelo usuário graváveis no fluxo.
    • Do contrário, é retornado STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o sinalizador REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • Leitura: a solicitação é concedida.
    • Se um oplock Leitura existente tiver a mesma chave de oplock que a nova solicitação, seu IRP será concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. O resultado é que o oplock é atualizado de Leitura para Leitura-Identificador.
    • Qualquer oplock Leitura existente que não tenha a mesma chave de oplock que a nova solicitação permanecerá inalterado.
  • Nível 2, Nível 1, Lote, Filtro, Leitura-Gravação, Leitura-Gravação-Identificador: é retornado STATUS_OPLOCK_NOT_GRANTED.

Leitura/Gravação

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
    • Caso seja um diretório, STATUS_INVALID_PARAMETER será retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Se existirem outras aberturas no fluxo (mesmo que sejam pelo mesmo thread), elas deverão ter a mesma chave de oplock.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há seções mapeadas pelo usuário graváveis no fluxo.
    • Do contrário, é retornado STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o sinalizador REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • O oplock Leitura ou Leitura-Gravação e o oplock existente tem a mesma chave de oplock que a solicitação: o IRP do oplock existente é concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e a solicitação é concedida.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Nível 2, Nível 1, Lote, Filtro, Leitura-Identificador, Leitura-Gravação-Identificador: é retornado STATUS_OPLOCK_NOT_GRANTED.

Leitura-Gravação-Identificador

Concessão feita somente se todas as seguintes condições forem verdadeiras:

  • A solicitação é para um dado fluxo de um arquivo.
    • Caso seja um diretório, STATUS_INVALID_PARAMETER será retornado.
  • O fluxo é aberto para acesso assíncrono.
    • Se aberto para acesso SYNCHRONOUS, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há transações TxF no arquivo.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Se existirem outras solicitações abertas no fluxo, mesmo que sejam pelo mesmo thread, elas deverão ter a mesma chave de oplock.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Não há seções mapeadas pelo usuário graváveis no fluxo.
    • Do contrário, é retornado STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. O campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags terá o sinalizador REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT definido.

Se o estado do oplock atual é:

  • Sem oplock: a solicitação é concedida.

  • Leitura, Leitura-Identificador, Leitura-Gravação ou Leitura-Gravação-Identificador e o oplock existente tem a mesma chave de oplock que a solicitação: o IRP do oplock existente é concluído com STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e a solicitação é concedida.
    • Do contrário, é retornado STATUS_OPLOCK_NOT_GRANTED.
  • Nível 2, Nível 1, Lote, Filtro: é retornado STATUS_OPLOCK_NOT_GRANTED.

Observação

Os oplocks Leitura e Nível 2 podem coexistir no mesmo fluxo, e os oplocks Leitura e Leitura-Identificador podem coexistir, mas os oplocks Nível 2 e Leitura-Identificador não podem coexistir.