Quebra de Oplocks
Depois que um oplock é solicitado e concedido, o proprietário desse oplock tem acesso ao fluxo com base no tipo de oplock que foi solicitado. Se a operação recebida não for compatível com o oplock atual, o sistema interromperá o oplock.
Quando um oplock é concedido, o sistema aguarda o IRP solicitante. Quando um oplock é quebrado, o IRP de solicitação do oplock pendente é concluído com STATUS_SUCCESS. Para oplocks de Nível 1, Lote e Filtro, o membro IoStatus.Information do IRP está definido para indicar o nível para o qual o oplock está sendo quebrado. Esses níveis são:
FILE_OPLOCK_BROKEN_TO_NONE - O oplock foi quebrado e não há nenhum oplock atual no fluxo. Diz-se que o oplock foi "quebrado para Nenhum".
FILE_OPLOCK_BROKEN_TO_LEVEL_2 – o oplock atual (Nível 1 ou Lote) foi convertido em um oplock de Nível 2. Filtrar oplocks nunca quebram para o Nível 2, eles sempre quebram para Nenhum.
Para oplocks read-handle, read-write e read-write-handle, o nível para o qual o oplock está quebrando é descrito como uma combinação de zero ou mais dos sinalizadores OPLOCK_LEVEL_CACHE_READ, OPLOCK_LEVEL_CACHE_HANDLE ou OPLOCK_LEVEL_CACHE_WRITE no membro NewOplockLevel da estrutura REQUEST_OPLOCK_OUTPUT_BUFFER passada como o parâmetro lpOutBuffer de DeviceIoControl. De maneira semelhante, FltFsControlFile e ZwFsControlFile podem ser usados para solicitar oplocks do Windows 7 do modo kernel. Para obter mais informações, consulte FSCTL_REQUEST_OPLOCK.
Quando o pacote oplock do sistema quebra um Nível 1, Lote, Filtro, Leitura-Gravação, Identificador de Leitura/Gravação ou, em determinadas circunstâncias, um Read-Handle oplock:
- O pacote oplock conclui o IRP de solicitação oplock pendente.
- A operação que causou a interrupção do oplock está pendente.
Se a operação for emitida em um identificador síncrono ou se for uma IRP_MJ_CREATE, que é sempre síncrona, o gerenciador de E/S fará com que a operação seja bloqueada, em vez de retornar STATUS_PENDING, aguardando uma confirmação do proprietário do oplock para informar ao pacote oplock que ele terminou o processamento e é seguro para a operação pendente continuar. A finalidade desse atraso é permitir que o proprietário do oplock coloque o fluxo de volta em um estado consistente antes que a operação atual continue. O sistema aguarda para sempre para receber a confirmação, pois não há tempo limite. Portanto, cabe ao proprietário do oplock reconhecer a quebra em tempo hábil. O IRP da operação pendente é definido em um estado cancelável. Se o aplicativo ou driver que executa a espera terminar, o pacote oplock concluirá imediatamente o IRP com STATUS_CANCELLED.
Um IRP_MJ_CREATE IRP pode especificar a opção FILE_COMPLETE_IF_OPLOCKED criar para evitar ser bloqueado como parte da confirmação de interrupção do oplock. Essa opção informa ao pacote oplock para não bloquear a criação de IRP até que a confirmação de interrupção do oplock seja recebida. Em vez disso, a criação tem permissão para continuar. Se uma criação bem-sucedida resultar em uma quebra de oplock, o código de retorno será STATUS_OPLOCK_BREAK_IN_PROGRESS, em vez de STATUS_SUCCESS. O sinalizador FILE_COMPLETE_IF_OPLOCKED normalmente é usado para evitar deadlocks. Por exemplo, se um cliente tiver um oplock em um fluxo e o mesmo cliente mais tarde abrir o mesmo fluxo, o cliente bloqueará a espera por si mesmo para reconhecer a interrupção do oplock. Nesse cenário, o uso do sinalizador FILE_COMPLETE_IF_OPLOCKED evita o deadlock.
Como o sistema de arquivos NTFS inicia quebras de oplock para oplocks de Lote e Filtro antes de verificar se há violações de compartilhamento, é possível que uma criação que especificou FILE_COMPLETE_IF_OPLOCKED falhe com STATUS_SHARING_VIOLATION, mas ainda faça com que um oplock de Lote ou Filtro seja interrompido. Nesse caso, o membro de informações da estrutura IO_STATUS_BLOCK é definido como FILE_OPBATCH_BREAK_UNDERWAY para permitir que o chamador detecte esse caso.
Para Read-Handle e oplocks de Identificador de Leitura/Gravação, a interrupção do oplock é iniciada após o NTFS verificar e detectar uma violação de compartilhamento. Isso dá aos titulares desses oplocks a oportunidade de fechar seus identificadores e sair do caminho, permitindo assim a possibilidade de não retornar a violação de compartilhamento ao usuário. Ele também evita interromper incondicionalmente o oplock nos casos em que o identificador que o oplock armazena em cache não entra em conflito com a nova criação.
Quando o Nível 2, a Leitura e, em determinadas circunstâncias Read-Handle os oplocks são interrompidos, o sistema não aguarda uma confirmação. Isso ocorre porque não deve haver nenhum estado armazenado em cache no fluxo que precise ser restaurado para o arquivo antes de permitir que outros clientes o acessem.
Há determinadas operações do sistema de arquivos que marcar o estado atual do oplock para determinar se o oplock precisa ser interrompido. Os artigos a seguir listam cada operação e descrevem o que dispara uma quebra de oplock, o que determina o nível para o qual o oplock é interrompido e se uma confirmação da quebra é necessária:
Uma quebra de um oplock do Windows 7 exigirá uma confirmação se o sinalizador REQUEST_OPLOCK_OUTPUT_FLAG_ACK_REQUIRED for definido no membro Flags da estrutura REQUEST_OPLOCK_OUTPUT_BUFFER passada como o parâmetro de saída de DeviceIoControl(lpOutBuffer), FltFsControlFile(OutBuffer) ou ZwFsControlFile(OutBuffer). Para obter mais informações, consulte FSCTL_REQUEST_OPLOCK.
Os artigos por operação listados descrevem os detalhes de quando uma interrupção de um Read-Handle oplock resulta no pendente da operação que quebrou o oplock. Por exemplo, o artigo IRP_MJ_CREATE contém os detalhes de Read-Handle associados.