Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTFSD
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


FILE_COMPLETE_IF_OPLOCKED not working on windows 10

Arun_PassiArun_Passi Member Posts: 12

Hi,

In my testing it looks like FILE_COMPLETE_IF_OPLOCKED flag is working fine on windows 7 but is being ignored on windows 10.

Simplified Scenario:
1. Open a file with FILE_OPEN_REQUIRING_OPLOCK (desired access aren't of much interest but I have tried with various combination of Read/Write flags)
2. Without closing the file, in the same thread open another instance of same file with FILE_COMPLETE_IF_OPLOCKED flag. This open returns STATUS_OPLOCK_BREAK_IN_PROGRESS on windows 7 but returns STATUS_SUCCESS on windows 10. On Windows 7 Ntfs!NtfsFsdCreate returns STATUS_OPLOCK_BREAK_IN_PROGRESS and IoStatus.Status is also the same. In case of windows 10, both of those return a value of STATUS_SUCCESS.
3. Try to read/write/delete on second handle, it hangs (as expected waiting on oplock to be broken, which will never happen)

Same behavior observed even when Oplock is requested using FSCTL_REQUEST_OPLOCK before attempt to open the file again and it still succeeds.

Is this change in behavior from windows 7 to windows 10 expected ?
It may not be always possible for a filter to know if any previous handles were opened with FILE_OPEN_REQUIRING_OPLOCK, so what is the best way to know if a file is already oplocked ?

Any help is much appreciated.

Thanks,
Arun

Comments

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,260

    Which build of Win10 are you testing on? Also, which file system(s)?

    -scott
    OSR

  • Arun_PassiArun_Passi Member Posts: 12

    I have tried win-10 1803, 1903 and 1909 with ntfs volume where it returns STATUS_SUCCESS instead of STATUS_OPLOCK_BREAK_IN_PROGRESS.
    win-7 with ntfs volume returns STATUS_OPLOCK_BREAK_IN_PROGRESS.

    One of my colleague tried on win 8.1 with ntfs volume, with same result as win-10.

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 303
    via Email
    FWIW, I have seen HCK's IFS Test fail oplock break test on W7 and Srv08/12
    with a vanilla system (no third party apps or drivers).

    I never trust it as a result.
  • Arun_PassiArun_Passi Member Posts: 12

    Not sure if I understand correctly. Which of the behavior is expected, one I am seeing on win7 or on win 10.
    If win 10 behavior is expected, then what is the way to detect an oplock is already acquired in order to skip any file io after opening that file again.

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 303
    via Email
    The one you observe on win7 is expected. But often it does not materialize.
    I have seen it not happen on vanilla Windows 7, Server 2008 and 2012,
    as I mentioned. I.e. without ANY third party filters or software.

    As to how to detect it - no idea. I gave up that almost a decade ago.

    Dejan.

    >
    > Not sure if I understand correctly. Which of the behavior is expected, one I
    > am seeing on win7 or on win 10.
    >
    > If win 10 behavior is expected, then what is the way to detect an oplock is
    > already acquired in order to skip any file io after opening that file
    > again.
  • Arun_PassiArun_Passi Member Posts: 12

    Thank you Dejan for clarifying.

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,260

    FILE_OPEN_REQUIRING_OPLOCK is a weird flag because it doesn't actually grant you an oplock. Instead it puts the stream into an "oplock like" state in the file system so that no one can touch the file until you send your FSCTL_REQUEST_OPLOCK. If you have an FSCTL_REQUEST_OPLOCK pending when the second handle does a read/write do you get the oplock break acknowledgment on the first handle?

    Also, can you post your code someplace? I just did the following on Win 10 1909 and a local NTFS volume:

        status = NtCreateFile(&handle, 
                              GENERIC_READ | GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              0, 
                              FILE_OPEN_IF, 
                              FILE_OPEN_REQUIRING_OPLOCK, 
                              NULL, 
                              0);
    
        if (!NT_SUCCESS(status)) {
            return 1;
        }
    
        status = NtCreateFile(&handle2, 
                              GENERIC_READ | GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              0, 
                              FILE_OPEN_IF, 
                              FILE_COMPLETE_IF_OPLOCKED, 
                              NULL, 
                              0);
    

    The second open fails with sharing violation. This is different than what you're describing so I'd be interested to see your exact combination of values.

    -scott
    OSR

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 303
    via Email
    IIRC, FILE_OPEN_IF is handled differently from FILE_OPEN in a lot of cases.

    Hence you get a sharing violation.
  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    I have tried the flags that you have used and it does fail with sharing violation. Then I removed FILE_OPEN_REQUIRING_OPLOCK and FILE_COMPLETE_IF_OPLOCKED flags from first and second create calls and it still failed with sharing violation. Most likely the reason for sharing violation is that you have provided GENERIC_READ | GENERIC_WRITE in desired access but have not provided FILE_SHARE_READ | FILE_SHARE_WRITE, so second open request cannot be allowed to succeed.

    Here is the relevant code snippet:

    `status = ZwCreateFile(&hFile1,
    FILE_WRITE_DATA | SYNCHRONIZE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN_IF,
    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_OPEN_REQUIRING_OPLOCK,
    NULL,
    0);
    if (status != 0)
    {
    return status;
    }

    status = ZwCreateFile(&hFile2,
            FILE_WRITE_DATA | SYNCHRONIZE | DELETE,
            &oa,
            &ioStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            FILE_OPEN,
            FILE_NON_DIRECTORY_FILE | FILE_COMPLETE_IF_OPLOCKED,
            NULL,
            0);
    if (status != 0)
    {
        ....
    }`
    

    Also attached the source file to this thread (since it won't let me attach a cpp file, renamed its extension to .txt)

    Thanks,
    Arun

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    My previous comment went missing, so please forgive me if this is a duplicate post.

    Your second request would have failed because it requests GENERIC_READ | GENERIC_WRITE but no sharing was allowed by the first and second open.
    I have tried the flags used in your sample and was able to reproduce the sharing violation behavior you are seeing, then added FILE_SHARE_READ and FILE_SHARE_WRITE in both open requests and it gave STATUS_SUCCESS in both requests.
    Also, if I remove FILE_OPEN_REQUIRING_OPLOCK and FILE_COMPLETE_IF_OPLOCKED it doesn't change the behavior.

    Here is relevant code snippet which I had originally used:

    status = ZwCreateFile(&hFile1,
    FILE_WRITE_DATA | SYNCHRONIZE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN_IF,
    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_OPEN_REQUIRING_OPLOCK,
    NULL,
    0);
    if (status != 0)
    {
    return status;
    }

    >

    status = ZwCreateFile(&hFile2,
    FILE_WRITE_DATA | SYNCHRONIZE | DELETE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN,
    FILE_NON_DIRECTORY_FILE | FILE_COMPLETE_IF_OPLOCKED,
    NULL,
    0);
    if (status != 0)
    {
    ...
    }

    Also attached the source file here (since it won't let me attach a .cpp file, added .txt extension to it)

    Thanks,
    Arun

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    Somehow, I am not able to post with the code/attachment.

    In your case sharing violation is returned because you have not provided FILE_SHARE_READ | FILE_SHARE_WRITE while desired access are requesting GENERIC_READ | GENERIC_WRITE. Oplock flags have no bearing on these calls. Please add the sharing flags and you will see the same result of STATUS_SUCCESS in the second call.

    Thanks,
    Arun

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,260

    Cool, presumably we're now looking at the same thing and now we're on to something...

    If I do this:

        status = NtCreateFile(&handle, 
                              GENERIC_READ, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              FILE_SHARE_READ|FILE_SHARE_WRITE, 
                              FILE_OPEN_IF, 
                              FILE_OPEN_REQUIRING_OPLOCK, 
                              NULL, 
                              0);
    
        if (!NT_SUCCESS(status)) {
            return 1;
        }
    
        status = NtCreateFile(&handle2, 
                              GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              FILE_SHARE_READ|FILE_SHARE_WRITE, 
                              FILE_OPEN_IF, 
                              FILE_COMPLETE_IF_OPLOCKED,
                              NULL, 
                              0);
    

    The second create completes immediately with STATUS_SUCCESS. This is reasonable though because the first open doesn't actually have an oplock yet (how would the file system even know what level to give you?). Like I said before it's in an "oplock like" state though so things that might require breaking an oplock will hang.

    The contract with FILE_OPEN_REQUIRING_OPLOCK is that you then need to actually request an oplock. So, if I change the code to something like this:

        status = NtCreateFile(&handle, 
                              GENERIC_READ, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              FILE_SHARE_READ|FILE_SHARE_WRITE, 
                              FILE_OPEN_IF, 
                              FILE_OPEN_REQUIRING_OPLOCK, 
                              NULL, 
                              0);
    
        if (!NT_SUCCESS(status)) {
            return 1;
        }
    
        oplockInput.StructureVersion     = REQUEST_OPLOCK_CURRENT_VERSION;
        oplockInput.StructureLength      = sizeof(REQUEST_OPLOCK_INPUT_BUFFER);
        oplockInput.RequestedOplockLevel = (OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_WRITE);
        oplockInput.Flags                = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
    
        status = NtFsControlFile(handle,
                                 event,
                                 NULL,
                                 NULL,
                                 &iosb,
                                 FSCTL_REQUEST_OPLOCK,
                                 &oplockInput,
                                 sizeof(REQUEST_OPLOCK_INPUT_BUFFER),
                                 &oplockOutput,
                                 sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER));
    
        if (!NT_SUCCESS(status)) {
            return 1;
        }
    
        status = NtCreateFile(&handle2, 
                              GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              FILE_SHARE_READ|FILE_SHARE_WRITE, 
                              FILE_OPEN_IF, 
                              FILE_COMPLETE_IF_OPLOCKED,
                              NULL, 
                              0);
    

    Then I get STATUS_OPLOCK_BREAK_IN_PROGRESS on the second open.

    I know you said you're not seeing this behavior, so I'd still like to see you code and figure out what's different. Can you put it on Dropbox or OneDrive?

    -scott
    OSR

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    Thanks for lot for your inputs,

    I was able to find the issue. Difference which mattered is the use of OPLOCK_LEVEL_CACHE_WRITE. I had that flag missing in my code in which case second open is successful. On adding that flag, I do get back STATUS_OPLOCK_BREAK_IN_PROGRESS in second open.

    So the question becomes, Is it necessary to provide OPLOCK_LEVEL_CACHE_WRITE, when the first handle only means to cache the reads ? Trying to attach my source code in .txt format, lets see if it works this time.

    Thanks,
    Arun

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 303
    via Email
    You're missing the part where you get OPLOCK_BREAK_IN_PROGRESS on Windows 7.
    If it is only your applications' interop that you are testing, the
    below may work (until a build of Windows decides to break it, or some
    filter, often Windows Defender, breaks it).

    But other applications, that did not explicitly request the oplock
    through FSCTL call, will not change most likely. And you simply won't
    get the status you are expecting.

    > Hi Scott,
    >
    > Thanks for lot for your inputs,
    >
    > I was able to find the issue. Difference which mattered is the use of
    > OPLOCK_LEVEL_CACHE_WRITE. I had that flag missing in my code in which case
    > second open is successful. On adding that flag, I do get back
    > STATUS_OPLOCK_BREAK_IN_PROGRESS in second open.
    >
    > So the question becomes, Is it necessary to provide
    > OPLOCK_LEVEL_CACHE_WRITE, when the first handle only means to cache the
    > reads ? Trying to attach my source code in .txt format, lets see if it works
    > this time.
    >
  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,260

    STATUS_OPLOCK_BREAK_IN_PROGRESS means, "your IRP_MJ_CREATE broke the oplock (based on our byzantine rules that are impossible to remember). We didn't block waiting for acknowledgement though. You must not proceed to do I/O on this handle until you an FSCTL_OPLOCK_BREAK_NOTIFY completes successfully, which means all involved parties have acknowledged the oplock break."

    However, not all oplocks break on IRP_MJ_CREATE. Imagine everyone is caching read data and you want to open for write. There's no need to break the oplock because you haven't actually written anything yet and we'll lose if you open for write, break all the oplocks, and then just close the handle. The oplock WILL need to break on write though, so that's why you get the inifinite hang on I/O if an oplock hasn't actually been requested on the file yet (the write can't proceed until the oplock breaks, but the oplock hasn't even been requested yet).

    Sooooo this brings me to what I should have asked in the first place: what are you trying to accomplish?

    -scott
    OSR

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Dejan/Scott,

    Sooooo this brings me to what I should have asked in the first place: what are you trying to accomplish?

    I started this thread because we were getting a hang with another filter driver which was opening the file but not requesting oplock through FSCTL. For some reason our filter wanted to delete that file in PreCleanup and it was opening the file again and tried to delete it where it got hung.
    I know we don't need to open the file again and file can be deleted with existing file object (for most practical cases, and we may not care about other case where we can't) . So that part has been fixed. However I was intrigued why use FILE_COMPLETE_IF_OPLOCKED didn't result in STATUS_OPLOCK_BREAK_IN_PROGRESS.

    But other applications, that did not explicitly request the oplock through FSCTL call, will not change most likely. And you simply won't get the status you are expecting.

    Atleast in that case, I can tell the third party filter driver developers that they are not playing by the rules and they must obtain the oplock if they want to use FILE_OPEN_REQUIRING_OPLOCK.

    Also, If I understand correctly, if anyone only wants to use OPLOCK_LEVEL_CACHE_READ and not OPLOCK_LEVEL_CACHE_WRITE, they must skip FILE_SHARE_WRITE and FILE_SHARE_DELETE flags.

    I think I got all the answers I was looking for. Thanks for all the help, much appreciated.

    Thanks,
    Arun

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 303
    via Email
    On a non-technical note again (and this is directed to anyone reading,
    with sincerely good intentions in mind), since interop testing took
    over half of my "development" time in the past..
    You might get the developer to change their filter to "conform" to
    Windows 10 Oplocks, but you will still need to get all the customers
    to switch to a new version (which is not doable if that requires a new
    license often).

    What I am trying to say is: consider a different approach, if at all
    possible. Unless you are Symantec, McAffee, in case of South Korea
    AHN, or some other really big name, you cannot afford to chase other
    filters :(

    I believe we had interop issues with literally every AV company on
    this list, apart from G-Data. We only got TWO of them to work with us
    on the issue.
    All the issues were their bugs or incorrect I/O handling. But the
    customer will not see it that way.

    >
    > I started this thread because we were getting a hang with another filter
    > driver which was opening the file but not requesting oplock through FSCTL.
    > For some reason our filter wanted to delete that file in PreCleanup and it
    > was opening the file again and tried to delete it where it got hung.
  • Arun_PassiArun_Passi Member Posts: 12

    Hi Dejan,

    I do agree with your comment and thats what we did, used an alternate way (which should have been used in the first place)
    This post was mostly for my own understanding.

    Thanks,
    Arun

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    My previous comment went missing, so please forgive me if this is a duplicate post.

    Your second request would have failed because it requests GENERIC_READ | GENERIC_WRITE but no sharing was allowed by the first and second open.
    I have tried the flags used in your sample and was able to reproduce the sharing violation behavior you are seeing, then added FILE_SHARE_READ and FILE_SHARE_WRITE in both open requests and it gave STATUS_SUCCESS in both requests.
    Also, if I remove FILE_OPEN_REQUIRING_OPLOCK and FILE_COMPLETE_IF_OPLOCKED it doesn't change the behavior.

    Here is relevant code snippet which I had originally used:

    status = ZwCreateFile(&hFile1,
    FILE_WRITE_DATA | SYNCHRONIZE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN_IF,
    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_OPEN_REQUIRING_OPLOCK,
    NULL,
    0);
    if (status != 0)
    {
    return status;
    }

    >

    status = ZwCreateFile(&hFile2,
    FILE_WRITE_DATA | SYNCHRONIZE | DELETE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN,
    FILE_NON_DIRECTORY_FILE | FILE_COMPLETE_IF_OPLOCKED,
    NULL,
    0);
    if (status != 0)
    {
    ...
    }

    Also attached the source file here (since it won't let me attach a .cpp file, added .txt extension to it)

    Thanks,
    Arun

  • Arun_PassiArun_Passi Member Posts: 12

    Hi Scott,

    My previous comment went missing, so please forgive me if this is a duplicate post.

    Your second request would have failed because it requests GENERIC_READ | GENERIC_WRITE but no sharing was allowed by the first and second open.
    I have tried the flags used in your sample and was able to reproduce the sharing violation behavior you are seeing, then added FILE_SHARE_READ and FILE_SHARE_WRITE in both open requests and it gave STATUS_SUCCESS in both requests.
    Also, if I remove FILE_OPEN_REQUIRING_OPLOCK and FILE_COMPLETE_IF_OPLOCKED it doesn't change the behavior.

    Here is relevant code snippet which I had originally used:

    status = ZwCreateFile(&hFile1,
    FILE_WRITE_DATA | SYNCHRONIZE,
    &oa,
    &ioStatus,
    NULL,
    FILE_ATTRIBUTE_NORMAL,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    FILE_OPEN_IF,
    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_OPEN_REQUIRING_OPLOCK,
    NULL,
    0);
    if (status != 0)
    {
    return status;
    }

    status = ZwCreateFile(&hFile2,
            FILE_WRITE_DATA | SYNCHRONIZE | DELETE,
            &oa,
            &ioStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            FILE_OPEN,
            FILE_NON_DIRECTORY_FILE | FILE_COMPLETE_IF_OPLOCKED,
            NULL,
           0);
    

    if (status != 0)
    {
    ...
    }

    Also attached the source file here (since it won't let me attach a .cpp file, added .txt extension to it)

    Thanks,
    Arun

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,260

    One of the use cases for oplocks is to avoid sharing violations. If an open is going to trigger a sharing violation and there is an oplock active of a particular type, the file system will break the oplock, wait for acknowledgement, and then retry the create. The primary use case of this is to avoid background scanning activities (A/V, search indexer, etc.) to cause sharing violations for the user.

    If someone opens with FILE_OPEN_REQUIRING_OPLOCK (and thus puts the stream in the "awaiting oplock request" state) and an open occurs that would trigger a sharing violation the file system says, "gee, I might need to break the oplock that the first open is going to eventually send". This causes the second open to hang indefinitely until the oplock is sent or the first handle is closed. Try the following exact arguments:

        status = NtCreateFile(&handle, 
                              GENERIC_READ | GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              0, 
                              FILE_OPEN_IF, 
                              FILE_OPEN_REQUIRING_OPLOCK, 
                              NULL, 
                              0);
    
        if (!NT_SUCCESS(status)) {
            return 1;
        }
    
        status = NtCreateFile(&handle2, 
                              GENERIC_READ | GENERIC_WRITE, 
                              &objAttr, 
                              &iosb, 
                              NULL, 
                              FILE_ATTRIBUTE_NORMAL, 
                              0, 
                              FILE_OPEN_IF, 
                              0, 
                              NULL, 
                              0);
    

    You should see the second create hang forever. If you add FILE_COMPLETE_IF_OPLOCKED on the second open you'll immediately get a sharing violation.

    The code you provided "just works" because it's not going to cause a sharing violation and so all of that processing is bypassed during create.

    -scott
    OSR

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 30 Mar 2020 OSR Seminar Space
Developing Minifilters 15 Jun 2020 LIVE ONLINE
Writing WDF Drivers 22 June 2020 LIVE ONLINE
Internals & Software Drivers 28 Sept 2020 Dulles, VA