Thursday, August 29, 2013

Using opportunistic locks to get out of the way if somebody wants the file

Opportunistic locks allow you to be notified when somebody else tries to access a file you have open. This is usually done if you want to use a file provided nobody else wants it.

For example, you might be a search indexer that wants to extract information from a file, but if somebody opens the file for writing, you don't want them to get Sharing Violation. Instead, you want to stop indexing the file and let the other person get their write access.

Or you might be a file viewer application like ildasm, and you want to let the user update the file (in ildasm's case, rebuild the assembly) even though you're viewing it. (Otherwise, they will get an error from the compiler saying "Cannot open file for output.")

Or you might be Explorer, and you want to abandon generating the preview for a file if somebody tries to delete it.

(Rats I fell into the trap of trying to motivate a Little Program.)

Okay, enough motivation. Here's the program:

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

OVERLAPPED g_o;

REQUEST_OPLOCK_INPUT_BUFFER g_inputBuffer = {
  REQUEST_OPLOCK_CURRENT_VERSION,
  sizeof(g_inputBuffer),
  OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE,
  REQUEST_OPLOCK_INPUT_FLAG_REQUEST,
};

REQUEST_OPLOCK_OUTPUT_BUFFER g_outputBuffer = {
  REQUEST_OPLOCK_CURRENT_VERSION,
  sizeof(g_outputBuffer),
};

int __cdecl wmain(int argc, wchar_t **argv)
{
  g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);

  HANDLE hFile = CreateFileW(argv[1], GENERIC_READ,
    FILE_SHARE_READ, nullptr, OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED, nullptr);
  if (hFile == INVALID_HANDLE_VALUE) {
    return 0;
  }

  DeviceIoControl(hFile, FSCTL_REQUEST_OPLOCK,
      &g_inputBuffer, sizeof(g_inputBuffer),
      &g_outputBuffer, sizeof(g_outputBuffer),
      nullptr, &g_o);
  if (GetLastError() != ERROR_IO_PENDING) {
    // oplock failed
    return 0;
  }

  DWORD dwBytes;
  if (!GetOverlappedResult(hFile, &g_o, &dwBytes, TRUE)) {
    // oplock failed
    return 0;
  }

  printf("Cleaning up because somebody wants the file...\n");
  Sleep(1000); // pretend this takes some time

  printf("Closing file handle\n");
  CloseHandle(hFile);

  CloseHandle(g_o.hEvent);

  return 0;
}

QR: Inline image 1