Requesting and granting oplocks

When the network redirector accesses files on remote servers, it requests the oplock from the remote server. Client applications directly request oplocks only when the lock is intended for a file on the local server.

Oplocks are requested through FSCTLs. The following FSCTLs are used for the different oplock types, which both user-mode applications and kernel-mode drivers can issue.

Requesting an Oplock In User Mode

To request a Windows 7 oplock in user mode, call DeviceIoControl:

For more information, see FSCTL_REQUEST_OPLOCK.

If the requested oplock can be granted, DeviceIoControl returns FALSE and GetLastError returns ERROR_IO_PENDING. For this reason, oplocks are never granted for synchronous I/O. The overlapped operation doesn't complete until the oplock is broken. After the operation completes, the REQUEST_OPLOCK_OUTPUT_BUFFER will contain information about the oplock break.

If the oplock can't be granted, the file system returns an appropriate error code. The most commonly returned error codes are ERROR_OPLOCK_NOT_GRANTED and ERROR_INVALID_PARAMETER.

Requesting an Oplock In Kernel Mode

To request Windows 7 oplocks in kernel mode:

File System Minifilters

A file system minifilter must use FltAllocateCallbackData and fill in the allocated FLT_CALLBACK_DATA like so:

  • Set its Iopb->MajorFunction field to IRP_MJ_FILE_SYSETM_CONTROL.
  • Set its Iopb->MinorFunction field to IRP_MN_USER_FS_REQUEST.
  • Set its Iopb->Parameters.FileSystemControl.Buffered.FsControlCode member to FSCTL_REQUEST_OPLOCK.
  • Allocate a buffer whose size is equal to the larger of REQUEST_OPLOCK_INPUT_BUFFER or REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Set the allocated FLT_CALLBACK_DATA's Iopb->Parameters.FileSystemControl.Buffered.SystemBuffer member to point to that buffer.
    • Set the allocated FLT_CALLBACK_DATA's Iopb->Parameters.FileSystemControl.Buffered.InputBufferLength and Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength fields to the size of that buffer.

Refer to the documentation of the REQUEST_OPLOCK_INPUT_BUFFER structure for information on how to format the oplock request.

Then the file system minifilter must call FltPerformAsynchronousIo, passing the allocated FLT_CALLBACK_DATA as the CallbackData parameter.

If the requested oplock can be granted, the FltPerformAsynchronousIo call returns STATUS_PENDING. For this reason, oplocks are never granted for synchronous I/O. The operation does not complete until the oplock is broken. After the operation completes, the REQUEST_OPLOCK_OUTPUT_BUFFER will contain information about the oplock break.

If the oplock can't be granted, the file system returns an appropriate error code. The most commonly returned error codes are STATUS_OPLOCK_NOT_GRANTED and STATUS_INVALID_PARAMETER.

Other Kinds of Drivers

Other kinds of drivers may call ZwFsControlFile:

Refer to the documentation of the REQUEST_OPLOCK_INPUT_BUFFER structure for information on how to format the oplock request.

If the requested oplock can be granted, the ZwFsControlFile call returns STATUS_PENDING. For this reason, oplocks are never granted for synchronous I/O. The operation does not complete until the oplock is broken. After the operation completes, the REQUEST_OPLOCK_OUTPUT_BUFFER will contain information about the oplock break.

If the oplock can't be granted, the file system returns an appropriate error code. The most commonly returned error codes are STATUS_OPLOCK_NOT_GRANTED and STATUS_INVALID_PARAMETER.

Avoiding Sharing Violations When Requesting Oplocks

Using the Atomic Create-With-Oplock Method

Atomic create-with-oplock is not an oplock type, it is a procedure that allows open operations to avoid causing sharing-mode violations in the time span between opening a file and receiving an oplock. With legacy oplocks this is only possible with Filter oplocks and requires opening two handles. With Windows 7 oplocks an application or driver may request any type of oplock using this procedure and need open only one handle.

To perform the atomic create-with-oplock procedure you should do the following:

  1. Use FltCreateFileEx2 or ZwCreateFile, as appropriate, to open the file. In the CreateOptions parameter pass the flag FILE_OPEN_REQUIRING_OPLOCK. You may set the DesiredAccess and ShareAccess parameters as desired. For example, in the DesiredAccess parameter set GENERIC_READ so you can read the file, and in the ShareAccess parameter set the FILE_SHARE_READ | FILE_SHARE_DELETE flags to allow others to read, rename, and/or mark the file for deletion while you have it open.
  2. Use the FSCTL_REQUEST_OPLOCK control code to request an oplock on the resulting file object or handle, as described above under Requesting an Oplock In Kernel Mode.

Note

You should not perform any file system operations on the file between steps 1 and 2. Doing so may cause deadlocks.

The most common oplock to request using this procedure is the Read-Handle type. This allows you to allow other callers as much concurrent access as possible, while still allowing you to be notified if you needs to close your handle to avoid causing a sharing violation to a conflicting open.

Using the Legacy Filter Oplock

The legacy Filter oplock also allows an application to "back out" when other applications/clients try to access the same stream, but is less flexible than the atomic create-with-oplock method. This mechanism allows an application to access a stream without causing other accessors of the stream to receive sharing violations when attempting to open the stream. To avoid sharing violations, the following three-step procedure should be used to request a Filter oplock:

  1. Open the file with a required access of FILE_READ_ATTRIBUTES and a share mode of FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. The handle opened in this step won't cause other applications to receive sharing violations because it's open only for attribute access (FILE_READ_ATTRIBUTES) and not data access (FILE_READ_DATA). This handle is suitable for requesting the Filter oplock, but not for performing actual I/O on the data stream.

  2. Request a Filter oplock (FSCTL_REQUEST_FILTER_OPLOCK) on the handle from step 1. The oplock granted in this step allows the oplock holder to "get out of the way" without causing a sharing violation to another application that attempts to access the stream.

  3. Open the file again for read access. The handle opened in this step allows the oplock holder to perform I/O on the stream.

The NTFS file system provides an optimization for this procedure through the FILE_RESERVE_OPFILTER create option flag. If this flag is specified in step 1 of the previous procedure, it allows the file system to fail the create request with STATUS_OPLOCK_NOT_GRANTED if the file system can determine that step 2 will fail. If step 1 succeeds, there's no guarantee that step 2 will succeed, even if FILE_RESERVE_OPFILTER was specified for the create request.

Conditions For Granting Oplocks

The following table identifies the required conditions necessary to grant an oplock.

Request type Conditions

Level 1

Filter

Batch

Granted only if all of the following conditions are true:

  • The request is for a given stream of a file.
    • If a directory, STATUS_INVALID_PARAMETER is returned.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned (oplocks aren't granted for synchronous I/O requests).
  • There are no TxF transactions on any stream of the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no other opens on the stream (even by the same thread).
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.

If the current oplock state is:

  • No oplock: The request is granted.

  • Level 2: The original Level 2 request is broken with FILE_OPLOCK_BROKEN_TO_NONE. The requested exclusive oplock is then granted.

  • Level 1, Batch, Filter, Read, Read-Handle, Read-Write, or Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED is returned.

Level 2

Granted only if all of the following conditions are true:

  • The request is for a given stream of a file.
    • If a directory, STATUS_INVALID_PARAMETER is returned.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no TxF transactions on the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no current Byte Range Locks on the stream.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
    • Before Windows 7, the operating system verifies if a byte range lock ever existed on the stream since the last time it was opened, and fails the request if so.

If the current oplock state is:

  • No oplock: The request is granted.

  • Level 2 and/or Read: The request is granted. You can have multiple Level 2/Read oplocks granted on the same stream at the same time. Multiple Level 2 (but not Read) oplocks can even exist on the same handle.
    • If a Read oplock is requested on a handle that already has a Read oplock granted to it, the first Read oplock's IRP is completed with STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE before the second Read oplock is granted.
  • Level 1, Batch, Filter, Read-Handle, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED is returned.

Read

Granted only if all of the following conditions are true:

  • The request is for a given stream of a file.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no TxF transactions on the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no current Byte Range Locks on the stream.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no writable user-mapped sections on the stream.
    • Else STATUS_CANNOT_GRANT_REQUESTED_OPLOCK is returned. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags field will have the REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT flag set.

If the current oplock state is:

  • No oplock: The request is granted.

  • Level 2 and/or Read: The request is granted. You can have multiple Level 2/Read oplocks granted on the same stream at the same time.
    • Additionally, if an existing oplock has the same oplock key as the new request, its IRP is completed with STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle and the existing oplock have a different oplock key from the new request: The request is granted. Multiple Read and Read-Handle oplocks can coexist on the same stream (see the note following this table).
    • Else (oplock keys are the same) STATUS_OPLOCK_NOT_GRANTED is returned.
  • Level 1, Batch, Filter, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED is returned.

Read-Handle

Granted only if all of the following conditions are true:

  • The request is for a given stream of a file.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no TxF transactions on the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no current Byte Range Locks on the stream.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no writable user-mapped sections on the stream.
    • Else STATUS_CANNOT_GRANT_REQUESTED_OPLOCK is returned. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags field will have the REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT flag set.

If the current oplock state is:

  • No oplock: the request is granted.

  • Read: the request is granted.
    • If an existing Read oplock has the same oplock key as the new request, its IRP is completed with STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. The result is that the oplock is upgraded from Read to Read-Handle.
    • Any existing Read oplock that doesn't have the same oplock key as the new request remains unchanged.
  • Level 2, Level 1, Batch, Filter, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED is returned.

Read-Write

Granted only if all of the following conditions are true:

  • The request is for a given stream of a file.
    • If a directory, STATUS_INVALID_PARAMETER is returned.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no TxF transactions on the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • If there are other opens on the stream (even by the same thread), they must have the same oplock key.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no writable user-mapped sections on the stream.
    • Else STATUS_CANNOT_GRANT_REQUESTED_OPLOCK is returned. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags field will have the REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT flag set.

If the current oplock state is:

  • No oplock: the request is granted.

  • Read or Read-Write and the existing oplock has the same oplock key as the request: the existing oplock's IRP is completed with STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE and the request is granted.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • Level 2, Level 1, Batch, Filter, Read-Handle, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED is returned.

Read-Write-Handle

Granted only if all of the following are true:

  • The request is for a given stream of a file.
    • If a directory, STATUS_INVALID_PARAMETER is returned.
  • The stream is opened for ASYNCHRONOUS access.
    • If opened for SYNCHRONOUS access, STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no TxF transactions on the file.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • If there are other open requests on the stream, even by the same thread, they must have the same oplock key.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • There are no writable user-mapped sections on the stream.
    • Else STATUS_CANNOT_GRANT_REQUESTED_OPLOCK is returned. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags field will have the REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT flag set.

If the current oplock state is:

  • No oplock: the request is granted.

  • Read, Read-Handle, Read-Write, or Read-Write-Handle and the existing oplock has the same oplock key as the request: the existing oplock's IRP is completed with STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE and the request is granted.
    • Else STATUS_OPLOCK_NOT_GRANTED is returned.
  • Level 2, Level 1, Batch, Filter: STATUS_OPLOCK_NOT_GRANTED is returned.

Note

Read and Level 2 oplocks can coexist on the same stream, and Read and Read-Handle oplocks can coexist, but Level 2 and Read-Handle oplocks cannot coexist.