Condividi tramite


Richiesta e concessione di oplock

Quando il redirector di rete accede ai file nei server remoti, richiede l'oplock dal server remoto. Le applicazioni client richiedono direttamente oplock solo quando il blocco è destinato a un file nel server locale.

i oplock vengono richiesti tramite . I seguenti FSCTL sono utilizzati per i diversi tipi di oplock e, che possono essere emessi sia dalle applicazioni in modalità utente sia dai driver in modalità kernel.

Richiesta di un oplock in modalità utente

Per richiedere un oplock di Windows 7 in modalità utente, chiamare DeviceIoControl:

  • Impostare dwIoControlCode su FSCTL_REQUEST_OPLOCK.
  • Passare un puntatore a una struttura REQUEST_OPLOCK_INPUT_BUFFER nel parametro lpInBuffer.
    • Per informazioni su come formattare la richiesta di oplock, vedere la documentazione di tale struttura.
  • Passa un puntatore a una struttura REQUEST_OPLOCK_OUTPUT_BUFFER nel parametro lpOutBuffer .

Per altre informazioni, vedere FSCTL_REQUEST_OPLOCK.

Se è possibile concedere l'oplock richiesto, DeviceIoControl restituisce FALSE e GetLastError restituisce ERROR_IO_PENDING. Per questo motivo, gli oplock non vengono mai concessi per le operazioni di I/O sincrone. L'operazione sovrapposta non viene completata fino a quando l'oplock non si interrompe. Al termine dell'operazione, il REQUEST_OPLOCK_OUTPUT_BUFFER conterrà informazioni sull'interruzione oplock.

Se non è possibile concedere l'oplock, il file system restituisce un codice di errore appropriato. I codici di errore restituiti più comunemente sono ERROR_OPLOCK_NOT_GRANTED e ERROR_INVALID_PARAMETER.

Richiesta di un oplock in modalità kernel

Per richiedere oplock di Windows 7 in modalità kernel:

Minifiltri del file system

Un minifiltro del file system deve usare FltAllocateCallbackData e compilare il FLT_CALLBACK_DATA allocato in questo modo:

  • Imposta il campo MajorFunction di>-su IRP_MJ_FILE_SYSTEM_CONTROL.
  • Imposta il campo Iopb->MinorFunction su IRP_MN_USER_FS_REQUEST.
  • Impostare il membro dei parametri Iopb->.FileSystemControl.Buffered.FsControlCode su FSCTL_REQUEST_OPLOCK.
  • Allocare un buffer le cui dimensioni sono uguali a quelle di REQUEST_OPLOCK_INPUT_BUFFER o di REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Impostare il membro > in modo che punti al buffer allocato FLT_CALLBACK_DATA.
    • Impostare i campi del FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.InputBufferLength e Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength alle dimensioni del buffer.

Per informazioni su come formattare la richiesta di oplock, vedere la documentazione della struttura REQUEST_OPLOCK_INPUT_BUFFER.

Il minifiltro del file system deve quindi chiamare FltPerformAsynchronousIo, passando il FLT_CALLBACK_DATA allocato come parametro CallbackData.

Se è possibile concedere l'oplock richiesto, la chiamata FltPerformAsynchronousIo restituisce STATUS_PENDING. Per questo motivo, gli oplock non vengono mai concessi per le operazioni di I/O sincrone. L'operazione non viene completata finché l'oplock non viene revocato. Al termine dell'operazione, il REQUEST_OPLOCK_OUTPUT_BUFFER conterrà informazioni sull'interruzione oplock.

Se non è possibile concedere l'oplock, il file system restituisce un codice di errore appropriato. I codici di errore restituiti più comunemente sono STATUS_OPLOCK_NOT_GRANTED e STATUS_INVALID_PARAMETER.

Altri tipi di driver

Altri tipi di driver possono chiamare ZwFsControlFile:

  • Impostare FsControlCode su FSCTL_REQUEST_OPLOCK.
  • Passare un puntatore a una struttura REQUEST_OPLOCK_INPUT_BUFFER nel parametro InputBuffer e impostare il parametro InputBufferLength sulla dimensione di quel buffer.
  • Passare un puntatore a una struttura REQUEST_OPLOCK_OUTPUT_BUFFER nel parametro OutputBuffer e impostare il parametro OutputBufferLength sulle dimensioni del buffer.

Per informazioni su come formattare la richiesta di oplock, vedere la documentazione della struttura REQUEST_OPLOCK_INPUT_BUFFER.

Se l'oplock richiesto può essere concesso, la chiamata ZwFsControlFile restituisce STATUS_PENDING. Per questo motivo, gli oplock non vengono mai concessi per le operazioni di I/O sincrone. L'operazione non viene completata finché l'oplock non viene revocato. Al termine dell'operazione, il REQUEST_OPLOCK_OUTPUT_BUFFER conterrà informazioni sull'interruzione oplock.

Se non è possibile concedere l'oplock, il file system restituisce un codice di errore appropriato. I codici di errore restituiti più comunemente sono STATUS_OPLOCK_NOT_GRANTED e STATUS_INVALID_PARAMETER.

Evitare violazioni di condivisione durante la richiesta di Oplock

Uso del metodo Atomic Create-With-Oplock

La creazione atomica con oplock non è un tipo di oplock. Si tratta invece di una procedura che consente alle operazioni aperte di evitare di causare violazioni della modalità di condivisione nell'intervallo di tempo tra l'apertura di un file e la ricezione di un oplock. Con gli oplock legacy, è necessario filtrare gli oplock e aprire due handle. Con gli oplock di Windows 7, un'applicazione o un driver può richiedere qualsiasi tipo di oplock usando questa procedura ed è necessario aprire un solo handle.

Per eseguire la procedura atomica di creazione con oplock, è necessario:

  1. Usare FltCreateFileEx2 o ZwCreateFile, in base alle esigenze, per aprire il file. Nel parametro CreateOptions, passare il flag FILE_OPEN_REQUIRING_OPLOCK. È possibile impostare i parametri DesiredAccess e ShareAccess in base alle esigenze. Ad esempio, nel set di parametri DesiredAccessGENERIC_READ in modo da poter leggere il file e nel parametro ShareAccess impostare il FILE_SHARE_READ | FILE_SHARE_DELETE flag per consentire ad altri utenti di leggere, rinominare e/o contrassegnare il file per l'eliminazione mentre è aperto.
  2. Usare il codice di controllo FSCTL_REQUEST_OPLOCK per richiedere un oplock sull'oggetto file o sull'handle risultante, come descritto in Richiesta di oplock in modalità kernel.

Non eseguire alcuna operazione di file system sul file tra i passaggi 1 e 2. In questo modo possono verificarsi deadlock.

L'oplock più comune da richiedere tramite questa procedura è il tipo di Read-Handle. In questo modo è possibile consentire ad altri chiamanti il massimo accesso simultaneo possibile, ricevendo comunque una notifica se è necessario chiudere l'handle per evitare di causare una violazione di condivisione con un'apertura in conflitto.

Utilizzo dell'oplock del filtro legacy

L'oplock filtro legacy consente anche a un'applicazione di fare un passo indietro quando altre applicazioni/clienti tentano di accedere allo stesso flusso, ma è meno flessibile rispetto al metodo "atomic create-with-oplock". Questo meccanismo consente a un'applicazione di accedere a un flusso senza causare la ricezione di violazioni di condivisione da parte di altre funzioni di accesso del flusso durante il tentativo di aprire il flusso. Per evitare violazioni di condivisione, è necessario utilizzare la seguente procedura in tre passaggi per richiedere un Filter oplock:

  1. Aprire il file con l'accesso necessario di FILE_READ_ATTRIBUTES e una modalità di condivisione di FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. L'handle aperto in questo passaggio non causerà la ricezione di violazioni di condivisione da parte di altre applicazioni perché è aperta solo per l'accesso agli attributi (FILE_READ_ATTRIBUTES) e non per l'accesso ai dati (FILE_READ_DATA). Questo handle è adatto per richiedere il filtro oplock, ma non per eseguire operazioni di I/O effettive sul flusso di dati.

  2. Richiedere un oplock di filtro (FSCTL_REQUEST_FILTER_OPLOCK) nell'handle del passaggio 1. L'oplock concesso in questo passaggio consente al titolare di oplock di "farsi da parte" senza causare una violazione di condivisione a un'altra applicazione che tenta di accedere al flusso di dati.

  3. Aprire di nuovo il file per l'accesso in lettura. L'handle aperto in questo passaggio consente al titolare dell'oplock di effettuare operazioni di I/O nel flusso.

Il file system NTFS fornisce un'ottimizzazione per questa procedura tramite il flag di creazione dell'opzione FILE_RESERVE_OPFILTER. Se questo flag viene specificato nel passaggio 1 della procedura precedente, consente al file system di far fallire la richiesta di creazione con STATUS_OPLOCK_NOT_GRANTED se il file system può determinare che il passaggio 2 non andrà a buon fine. Se il passaggio 1 ha esito positivo, non è garantito che il passaggio 2 abbia esito positivo, anche se FILE_RESERVE_OPFILTER è stato specificato per la richiesta di creazione.

Condizioni per la concessione di oplocks

Nella tabella seguente vengono identificate le condizioni necessarie per concedere un oplock.

Tipo di richiesta Condizioni

Livello 1

Filtro

Lotto

Concesso solo se tutte le condizioni seguenti sono vere:

  • La richiesta è per un determinato flusso di un file.
    • Se il parametro è una directory, viene restituito STATUS_INVALID_PARAMETER.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED (gli oplock non vengono concessi per le richieste di I/O sincrone).
  • Non ci sono transazioni TxF in nessun flusso del file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non ci sono altre aperture attive nello stream (anche dallo stesso thread).
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta è stata accettata.

  • Livello 2: la richiesta di Livello 2 originale viene disattivata con FILE_OPLOCK_BROKEN_TO_NONE. Viene quindi concesso l'oplock esclusivo richiesto.

  • Livello 1, Batch, Filtro, Lettura, Handle di lettura, Lettura/Scrittura o Handle di lettura/scrittura: viene restituito STATUS_OPLOCK_NOT_GRANTED.

Livello 2

Concesso solo se tutte le condizioni seguenti sono vere:

  • La richiesta è per un determinato flusso di un file.
    • Se il parametro è una directory, viene restituito STATUS_INVALID_PARAMETER.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Non sono presenti transazioni TxF nel file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti blocchi di intervallo di byte correnti nel flusso.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
    • Prima di Windows 7, il sistema operativo verificava se un blocco di intervallo di byte fosse mai esistito nel flusso dalla sua ultima apertura e, in tal caso, la richiesta falliva.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta è stata accettata.

  • Livello 2 e/o Lettura: la richiesta viene concessa. È possibile concedere al contempo più oplock di livello 2/Read sullo stesso flusso. Più oplock di livello 2 (ma non di lettura) possono esistere anche nello stesso handle.
    • Se viene richiesto un oplock di lettura su un handle che dispone già di un oplock di lettura assegnato, l'IRP del primo oplock viene completato con STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE prima che venga assegnato il secondo oplock di lettura.
  • Livello 1, Batch, Filtro, Read-Handle, Read-Write, Read-Write-Handle: viene restituito STATUS_OPLOCK_NOT_GRANTED.

Leggere

Concesso solo se tutte le condizioni seguenti sono vere:

  • La richiesta è per un determinato flusso di un file.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Non sono presenti transazioni TxF nel file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti blocchi di intervallo di byte correnti nel flusso.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti sezioni scrivibili mappate dall'utente nel flusso.
    • In caso contrario, viene restituito STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. Il campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags avrà il flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT impostato.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta è stata accettata.

  • Livello 2 e/o Lettura: la richiesta viene concessa. È possibile concedere al contempo più oplock di livello 2/Read sullo stesso flusso.
    • Inoltre, se un oplock esistente ha la stessa chiave di oplock della nuova richiesta, il relativo IRP viene completato con STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle e il blocco operativo esistente hanno una chiave di oplock diversa dalla nuova richiesta: la richiesta è accolta. Più operazioni di lettura e Read-Handle possono coesistere nello stesso flusso (vedere la nota seguente questa tabella).
    • In caso contrario (le chiavi oplock sono le stesse) viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Livello 1, Batch, Filtro, Lettura/Scrittura, Read-Write-Handle: viene restituito STATUS_OPLOCK_NOT_GRANTED.

Read-Handle

Concesso solo se tutte le condizioni seguenti sono vere:

  • La richiesta è per un determinato flusso di un file.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Non sono presenti transazioni TxF nel file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti blocchi di intervallo di byte correnti nel flusso.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti sezioni scrivibili mappate dall'utente nel flusso.
    • In caso contrario, viene restituito STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. Il campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags avrà il flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT impostato.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta viene concessa.

  • Lettura: viene concessa la richiesta.
    • Se un Read oplock esistente ha la stessa oplock key della nuova richiesta, il relativo IRP viene completato con STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. Il risultato è che l'oplock viene aggiornato da Read a Read-Handle.
    • Qualsiasi oplock di lettura esistente che non ha la stessa chiave oplock della nuova richiesta rimane invariata.
  • Livello 2, Livello 1, Batch, Filtro, Lettura/Scrittura, Lettura-Scrittura-Gestione: viene restituito STATUS_OPLOCK_NOT_GRANTED.

Read-Write

Concesso solo se tutte le condizioni seguenti sono vere:

  • La richiesta è per un determinato flusso di un file.
    • Se il parametro è una directory, viene restituito STATUS_INVALID_PARAMETER.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Non sono presenti transazioni TxF nel file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Se sono presenti altre aperture nel flusso (anche dallo stesso thread), devono avere la stessa chiave di oplock.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti sezioni scrivibili mappate dall'utente nel flusso.
    • In caso contrario, viene restituito STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. Il campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags avrà il flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT impostato.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta viene concessa.

  • Lettura o Read-Write e l'oplock esistente ha la stessa chiave di oplock della richiesta: l'IRP dell'oplock esistente viene completato con STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e viene concessa la richiesta.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Livello 2, livello 1, batch, filtro, handle di lettura, handle di lettura/scrittura: viene restituito STATUS_OPLOCK_NOT_GRANTED.

Lettura -Write-Handle

Concesso solo se sono soddisfatte tutte le condizioni seguenti:

  • La richiesta è per un determinato flusso di un file.
    • Se il parametro è una directory, viene restituito STATUS_INVALID_PARAMETER.
  • Il flusso è aperto per l'accesso asincrono.
    • Se aperto per l'accesso SINCRONO, viene restituito STATUS_OPLOCK_NOT_GRANTED.
  • Non sono presenti transazioni TxF nel file.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Se sono presenti altre richieste attive nel flusso, anche se sono generate dallo stesso thread, devono avere la stessa chiave di oplock.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Non sono presenti sezioni scrivibili mappate dall'utente nel flusso.
    • In caso contrario, viene restituito STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. Il campo REQUEST_OPLOCK_OUTPUT_BUFFER.Flags avrà il flag REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT impostato.

Se lo stato di oplock corrente è:

  • Nessun oplock: la richiesta viene concessa.

  • Read, Read-Handle, Read-Write o Read-Write-Handle e il oplock esistente ha la stessa chiave oplock della richiesta: l'IRP dell'oplock esistente viene completato con STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE e viene concessa la richiesta.
    • In caso contrario, STATUS_OPLOCK_NOT_GRANTED viene restituito.
  • Livello 2, Livello 1, Batch, Filtro: STATUS_OPLOCK_NOT_GRANTED viene restituito.

Nota

Gli oplock di lettura e di livello 2 possono coesistere nello stesso flusso e gli oplock di lettura e Read-Handle possono coesistere, ma gli oplock di livello 2 e Read-Handle non possono coesistere.