C++/Win32: How to wait for a pending delete to complete

前端 未结 13 959
不思量自难忘°
不思量自难忘° 2020-11-30 03:20

Solved:

  • Workable solution: sbi\'s answer
  • Explanation for what really happens: Hans\'s answer
  • Explanation for why OpenFile do
相关标签:
13条回答
  • 2020-11-30 03:43

    This is maybe not your particular issue, but it's possible so I suggest you get out Process Monitor (Sysinternals) and see.

    I had exactly the same problem and discovered that Comodo Internet Security (cmdagent.exe) was contributing to the problem. Previously I had a dual-core machine, but when I upgraded to an Intel i7 suddenly my working software (jam.exe by Perfore software) no longer worked because it had the same pattern (a delete then create, but no check). After debugging the problem I found GetLastError() was returning access denied, but Process Monitor reveals a 'delete pending'. Here is the trace:

    10:39:10.1738151 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
    10:39:10.1738581 AM jam.exe 5032    QueryAttributeTagFile   C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS Attributes: ANCI, ReparseTag: 0x0
    10:39:10.1738830 AM jam.exe 5032    SetDispositionInformationFile   C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS Delete: True
    10:39:10.1739216 AM jam.exe 5032    CloseFile   C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1739438 AM jam.exe 5032    IRP_MJ_CLOSE    C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1744837 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   DELETE PENDING  Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
    10:39:10.1788811 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   DELETE PENDING  Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
    10:39:10.1838276 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   DELETE PENDING  Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
    10:39:10.1888407 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   DELETE PENDING  Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
    10:39:10.1936323 AM System  4   FASTIO_ACQUIRE_FOR_SECTION_SYNCHRONIZATION  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS SyncType: SyncTypeOther
    10:39:10.1936531 AM System  4   FASTIO_RELEASE_FOR_SECTION_SYNCHRONIZATION  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1936647 AM System  4   IRP_MJ_CLOSE    C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1939064 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   DELETE PENDING  Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
    10:39:10.1945733 AM cmdagent.exe    1188    CloseFile   C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1946532 AM cmdagent.exe    1188    IRP_MJ_CLOSE    C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1947020 AM cmdagent.exe    1188    IRP_MJ_CLOSE    C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS 
    10:39:10.1948945 AM cfp.exe 1832    QueryOpen   C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   FAST IO DISALLOWED  
    10:39:10.1949781 AM cfp.exe 1832    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   NAME NOT FOUND  Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
    10:39:10.1989720 AM jam.exe 5032    CreateFile  C:\Users\Dad\AppData\Local\Temp\jam5032t1.bat   SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, OpenResult: Created
    

    As you can see, there is a request to delete followed by several attempts to open the file again by jam.exe (it's an fopen in a loop). You can see cmdagent.exe presumably had the file open as it closes its handle and then suddenly jam.exe is able to now open the file.

    Of course, the suggested solution to wait and try again, and it works just fine.

    0 讨论(0)
  • 2020-11-30 03:44

    I just had this exact issue and took TWO steps to address it; I stopped using C/C++ stdlib apis and ::DeleteFile(..), and switched to:

    1. ::MoveFileEx(src,dest,MOVEFILE_WRITE_THROUGH);. See: MOVEFILE_WRITE_THROUGH

    2. h = ::CreateFile(DELETE | SYNCHRONIZE,OPEN_EXISTING,FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_OPEN_REPARSE_POINT); ::CloseHandle(h);

    The above are pseudo calls showing the relevant flags, specifically note that there is NO sharing on the CreateFile call used to achieve delete.

    Together they gave me better precision on the rename and delete semantics. They are working in my code now and have improved the precision and control from other threads/processes (watching the file-system for changes) interjecting actions on the file due to latencies [or sharing] in the other rename and/or delete APIs. Without that control, a file set to delete when its last kernel-handle was closed might actually languish open until the system was rebooted, and you might not know.

    Hopefully those feedback snippets might prove useful to others.

    Addendum: I happen to use hardlinks for a portion of the work I do. It turns out that although you can create hardlinks on a file that is OPEN, you cannot delete ANY of them until all handles to ANY of the underlying data-stream(s) to that NTFS file are closed. That is weird since:

    • the OS tracks by them using a single ID in what is effectively an INode (ntfs uid).
      • https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openfilebyid
      • https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
      • https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilenamew
      • https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_info
    • you can create hardlinks while it is open, just not delete them
      • hardlinks are actually alias-names in the attributes (XATTRs) in NTFS
    • you can rename them while it is open
      • so changing the name doesn't matter

    Which would lead you to think that only the last hardlink should be non-deletable while the kernel has one or more open-file handles referring to the hardlinked NTFS File's MFT-Entry/ATTRs. anyway, just another thing to know.

    0 讨论(0)
  • 2020-11-30 03:52

    Since you're creating a new file, processing it, then deleting it, it sounds like you don't really care about what the file name is. If that's truly the case, you should consider always creating a temporary file. That way, each time through the process, you don't have to care that the file didn't yet get deleted.

    0 讨论(0)
  • 2020-11-30 03:55

    If CreateFile returns INVALID_HANDLE_VALUE then you should determine what GetLastError returns in your particular situation (pending delete) and loop back to CreateFile based on that error code only.

    The FILE_FLAG_DELETE_ON_CLOSE flag might buy you something.

    0 讨论(0)
  • 2020-11-30 03:55

    I think this is just by poor design in the file system. I have seen the same problem when I worked with communication ports, opening/closing them.

    Unfortunately I think the simplest solution would be to just retry to create the file a number of times if you get an INVALID_HANDLE_VALUE. GetLastError() might also give you a better way of detecting this particular INVALID_HANDLE_VALUE.

    I would have preferred overlapped I/O, but their CloseHandle() and DeleteFile() don't handle overlapped operations :(

    0 讨论(0)
  • 2020-11-30 03:58

    Is there a way to detect that a file is pending deletion?

    Use the GetFileInformationByHandleEx function with the FILE_STANDARD_INFO structure.

    But the function can't solve your problem. sbi's solution neither.

    0 讨论(0)
提交回复
热议问题