title: Deleting a file based on disk ID
body: <p>As described <a href="https://stackoverflow.com/questions/8407472/deleting-or-renaming-a-file-using-an-open-handle" title="Deleting or Renaming a file using an open handle (Stack Overflow)">here</a>, using <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa365539%28v=VS.85%29.aspx" rel="nofollow noreferrer" title="SetInformationByHandle (MSDN)"><code>SetFileInformationByHandle</code></a> with <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364221(v=vs.85).aspx" rel="nofollow noreferrer" title="FILE_DISPOSITION_INFO (MSDN)"><code>FILE_DISPOSITION_INFO</code></a>
  allows one to set a file with an open handle to be deleted upon all handles being closed.</p>
  
  <p>However, I am trying to delete a file based on its file index (disk ID) retrieved by <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364221(v=vs.85).aspx" rel="nofollow noreferrer" title="FILE_DISPOSITION_INFO (MSDN)"><code>FILE_DISPOSITION_INFO</code></a> and
  <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a> in order to safely delete files/directories in a directory which differ only in case.
  This is safe to do in my use case, as on an NTFS system, file indexes are <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx" rel="nofollow noreferrer" title="BY_HANDLE_FILE_INFORMATION (MSDN)">persistent until deletion</a>,
  negating the use of <code>ReplaceFile</code>, which the current codebase handles.</p>
  
  <p>However, when attempting to delete the handle, I get error 87 (<code>ERROR_INVALID_PARAMETER</code>).
  If I delete using a handle created with <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx" rel="nofollow noreferrer" title="CreateFile (MSDN)"><code>CreateFileW</code></a>, I run into no problems.
  I can't do this, though, as Windows will not be able to distinguish between two file/folders of the same case, even though NTFS can.</p>
  
  <p>I am also aware that there is an ambiguity with hardlinked files opened with <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a>,
  as hardlinked files share the same disk ID.
  The issue of hardlinked files can be considered irrelevant for this scenario.
  I will only be deleting directories by ID, which cannot be hardlinked.</p>
  
  <p>Is there a parameter or setting I am missing in my <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a> call?
  Somehow, in my <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa365539%28v=VS.85%29.aspx" rel="nofollow noreferrer" title="SetInformationByHandle (MSDN)"><code>SetFileInformationByHandle</code></a> call?</p>
  
  <p>Additional methods I have tried:</p>
  
  <ul>
  <li>Calling <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251(v=vs.85).aspx" rel="nofollow noreferrer" title="DuplicateHandle (MSDN)"><code>DuplicateHandle</code></a> with the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a> handle, providing <code>DELETE</code> for <code>dwDesiredAccess</code>, and using that.
  Same <code>ERROR_INVALID_PARAMETER</code> result.</li>
  <li>Using <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365497(v=vs.85).aspx" rel="nofollow noreferrer" title="ReOpenFile (MSDN)"><code>ReOpenFile</code></a> with the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a> handle, providing <code>DELETE</code> for <code>dwDesiredAccess</code>, and using that.
  Same <code>ERROR_INVALID_PARAMETER</code> result.</li>
  <li>Using <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365497(v=vs.85).aspx" rel="nofollow noreferrer" title="ReOpenFile (MSDN)"><code>ReOpenFile</code></a> with the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspx" rel="nofollow noreferrer" title="OpenFileById (MSDN)"><code>OpenFileById</code></a> handle, providing <code>DELETE</code> for <code>dwDesiredAccess</code>, and providing the <code>FILE_FLAG_DELETE_ON_CLOSE</code> flag.
  No error is given, but the file remains after all handles are closed.</li>
  </ul>
  
  <p>Here is a minimal, yet complete, example which reproduces the problem:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;Windows.h&gt;
  
  DWORD getFileID(LPCWSTR path, LARGE_INTEGER *id)
  {
      HANDLE h = CreateFileW(path, 0, 0, 0, OPEN_EXISTING,
          FILE_FLAG_OPEN_REPARSE_POINT |
          FILE_FLAG_BACKUP_SEMANTICS |
          FILE_FLAG_POSIX_SEMANTICS,
          0);
      if (h == INVALID_HANDLE_VALUE)
          return GetLastError();
  
      BY_HANDLE_FILE_INFORMATION info;
      if (!GetFileInformationByHandle(h, &amp;info))
      {
          DWORD err = GetLastError();
          CloseHandle(h);
          return err;
      }
      id-&gt;HighPart = info.nFileIndexHigh;
      id-&gt;LowPart = info.nFileIndexLow;
      CloseHandle(h);
      return ERROR_SUCCESS;
  }
  
  DWORD deleteFileHandle(HANDLE fileHandle)
  {
      FILE_DISPOSITION_INFO info;
      info.DeleteFileW = TRUE;
      if (!SetFileInformationByHandle(
          fileHandle, FileDispositionInfo, &amp;info, sizeof(info)))
      {
          return GetLastError();
      }
      return ERROR_SUCCESS;
  }
  
  int wmain(DWORD argc, LPWSTR argv[])
  {
      if (argc != 3)
      {
          fwprintf(stderr, L"Arguments: &lt;rootpath&gt; &lt;path&gt;\n");
          return 1;
      }
  
      DWORD err;
      HANDLE rootHandle = CreateFileW(
          argv[1], 0, 0, 0, OPEN_EXISTING,
          FILE_FLAG_OPEN_REPARSE_POINT |
          FILE_FLAG_BACKUP_SEMANTICS |
          FILE_FLAG_POSIX_SEMANTICS,
          0);
      if (rootHandle == INVALID_HANDLE_VALUE)
      {
          err = GetLastError();
          fwprintf(stderr,
              L"Could not open root directory '%s', error code %d\n",
              argv[1], err);
          return err;
      }
  
      LARGE_INTEGER fileID;
      err = getFileID(argv[2], &amp;fileID);
      if (err != ERROR_SUCCESS)
      {
          fwprintf(stderr,
              L"Could not get file ID of file/directory '%s', error code %d\n",
              argv[2], err);
          CloseHandle(rootHandle);
          return err;
      }
      fwprintf(stdout,
          L"The file ID of '%s' is %lld\n",
          argv[2], fileID.QuadPart);
  
      FILE_ID_DESCRIPTOR idStruct;
      idStruct.Type = FileIdType;
      idStruct.FileId = fileID;
      HANDLE fileHandle = OpenFileById(
          rootHandle, &amp;idStruct, DELETE, FILE_SHARE_DELETE, 0,
          FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
      if (fileHandle == INVALID_HANDLE_VALUE)
      {
          err = GetLastError();
          CloseHandle(rootHandle);
          fwprintf(stderr,
              L"Could not open file by ID %lld, error code %d\n",
              fileID.QuadPart, err);
          return err;
      }
  
      err = deleteFileHandle(fileHandle);
      if (err != ERROR_SUCCESS)
      {
          fwprintf(stderr,
              L"Could not delete file by ID '%lld', error code %d\n",
              fileID.QuadPart, err);
      }
  
      CloseHandle(fileHandle);
      struct _stat _tmp;
      fwprintf(stdout,
          L"File was %ssuccessfully deleted\n",
          (_wstat(argv[2], &amp;_tmp) == 0) ? L"not " : L"");
      CloseHandle(rootHandle);
      return err;
  }
  </code></pre>
  
  <p>Any solution must work with Vista and above. Suggestions for code improvement are also welcome.</p>