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.
- To request Windows 7 oplocks:
- To request legacy oplocks:
Requesting an Oplock In User Mode
To request a Windows 7 oplock in user mode, call DeviceIoControl:
- Set dwIoControlCode to FSCTL_REQUEST_OPLOCK.
- Pass a pointer to a REQUEST_OPLOCK_INPUT_BUFFER structure in the lpInBuffer parameter.
- Refer to that structure's documentation for information on how to format the oplock request.
- Pass a pointer to a REQUEST_OPLOCK_OUTPUT_BUFFER structure in the lpOutBuffer parameter.
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:
- Set FsControlCode to FSCTL_REQUEST_OPLOCK.
- Pass a pointer to a REQUEST_OPLOCK_INPUT_BUFFER structure in the InputBuffer parameter and setthe InputBufferLength parameter to the size of that buffer.
- Pass a pointer to a REQUEST_OPLOCK_OUTPUT_BUFFER structure in the OutputBuffer parameter and set the OutputBufferLength parameter 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.
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:
- 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.
- 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:
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.
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.
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:
If the current oplock state is:
|
Level 2 |
Granted only if all of the following conditions are true:
If the current oplock state is:
|
Read |
Granted only if all of the following conditions are true:
If the current oplock state is:
|
Read-Handle |
Granted only if all of the following conditions are true:
If the current oplock state is:
|
Read-Write |
Granted only if all of the following conditions are true:
If the current oplock state is:
|
Read-Write-Handle |
Granted only if all of the following are true:
If the current oplock state is:
|
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.