mirror of
				https://github.com/winfsp/winfsp.git
				synced 2025-10-31 12:08:41 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			249 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * @file resilient.c
 | |
|  *
 | |
|  * @copyright 2015-2025 Bill Zissimopoulos
 | |
|  */
 | |
| /*
 | |
|  * This file is part of WinFsp.
 | |
|  *
 | |
|  * You can redistribute it and/or modify it under the terms of the GNU
 | |
|  * General Public License version 3 as published by the Free Software
 | |
|  * Foundation.
 | |
|  *
 | |
|  * Licensees holding a valid commercial license may use this software
 | |
|  * in accordance with the commercial license agreement provided in
 | |
|  * conjunction with the software.  The terms and conditions of any such
 | |
|  * commercial license agreement shall govern, supersede, and render
 | |
|  * ineffective any application of the GPLv3 license to this software,
 | |
|  * notwithstanding of any reference thereto in the software or
 | |
|  * associated repository.
 | |
|  */
 | |
| 
 | |
| #define WIN32_NO_STATUS
 | |
| #include <windows.h>
 | |
| #undef WIN32_NO_STATUS
 | |
| #include <winternl.h>
 | |
| #pragma warning(push)
 | |
| #pragma warning(disable:4005)           /* macro redefinition */
 | |
| #include <ntstatus.h>
 | |
| #pragma warning(pop)
 | |
| 
 | |
| #define DeleteMaxTries                  30
 | |
| #define DeleteSleepTimeout              300
 | |
| 
 | |
| static VOID WaitDeletePending(PCWSTR FileName);
 | |
| 
 | |
| NTSTATUS NTAPI ResilientNtCreateFile(
 | |
|     PHANDLE PFileHandle,
 | |
|     ACCESS_MASK DesiredAccess,
 | |
|     POBJECT_ATTRIBUTES ObjectAttributes,
 | |
|     PIO_STATUS_BLOCK IoStatusBlock,
 | |
|     PLARGE_INTEGER AllocationSize,
 | |
|     ULONG FileAttributes,
 | |
|     ULONG ShareAccess,
 | |
|     ULONG CreateDisposition,
 | |
|     ULONG CreateOptions,
 | |
|     PVOID EaBuffer,
 | |
|     ULONG EaLength)
 | |
| {
 | |
|     DWORD LastError = GetLastError(); /* preserve last error */
 | |
|     NTSTATUS Result;
 | |
| 
 | |
|     Result = NtCreateFile(
 | |
|         PFileHandle,
 | |
|         DesiredAccess,
 | |
|         ObjectAttributes,
 | |
|         IoStatusBlock,
 | |
|         AllocationSize,
 | |
|         FileAttributes,
 | |
|         ShareAccess,
 | |
|         CreateDisposition,
 | |
|         CreateOptions,
 | |
|         EaBuffer,
 | |
|         EaLength);
 | |
| 
 | |
|     if (NT_SUCCESS(Result) &&
 | |
|         (FILE_DELETE_ON_CLOSE & CreateOptions))
 | |
|     {
 | |
|         /* HACK: remember FILE_DELETE_ON_CLOSE through HANDLE_FLAG_PROTECT_FROM_CLOSE */
 | |
|         SetHandleInformation(*PFileHandle,
 | |
|             HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| NTSTATUS NTAPI ResilientNtClose(
 | |
|     HANDLE Handle)
 | |
| {
 | |
|     DWORD LastError = GetLastError(); /* preserve last error */
 | |
|     NTSTATUS Result;
 | |
|     DWORD HandleFlags = 0, FileNameLen;
 | |
|     WCHAR FileNameBuf[sizeof "\\\\?\\GLOBALROOT" - 1 + 1024] = L"\\\\?\\GLOBALROOT";
 | |
| 
 | |
|     if (GetHandleInformation(Handle, &HandleFlags) &&
 | |
|         (HANDLE_FLAG_PROTECT_FROM_CLOSE & HandleFlags))
 | |
|     {
 | |
|         SetHandleInformation(Handle,
 | |
|             HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);
 | |
|         FileNameLen = GetFinalPathNameByHandle(Handle,
 | |
|             FileNameBuf + sizeof "\\\\?\\GLOBALROOT" - 1, 1023,
 | |
|             FILE_NAME_OPENED | VOLUME_NAME_NT);
 | |
|         if (0 == FileNameLen || FileNameLen >= 1024)
 | |
|             HandleFlags = 0;
 | |
|     }
 | |
| 
 | |
|     Result = NtClose(
 | |
|         Handle);
 | |
| 
 | |
|     if (NT_SUCCESS(Result))
 | |
|     {
 | |
|         if (HANDLE_FLAG_PROTECT_FROM_CLOSE & HandleFlags)
 | |
|             WaitDeletePending(FileNameBuf);
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| HANDLE WINAPI ResilientCreateFileW(
 | |
|     LPCWSTR lpFileName,
 | |
|     DWORD dwDesiredAccess,
 | |
|     DWORD dwShareMode,
 | |
|     LPSECURITY_ATTRIBUTES lpSecurityAttributes,
 | |
|     DWORD dwCreationDisposition,
 | |
|     DWORD dwFlagsAndAttributes,
 | |
|     HANDLE hTemplateFile)
 | |
| {
 | |
|     HANDLE Handle;
 | |
|     DWORD LastError;
 | |
| 
 | |
|     Handle = CreateFileW(
 | |
|         lpFileName,
 | |
|         dwDesiredAccess,
 | |
|         dwShareMode,
 | |
|         lpSecurityAttributes,
 | |
|         dwCreationDisposition,
 | |
|         dwFlagsAndAttributes,
 | |
|         hTemplateFile);
 | |
|     LastError = GetLastError();
 | |
| 
 | |
|     if (INVALID_HANDLE_VALUE != Handle &&
 | |
|         (FILE_FLAG_DELETE_ON_CLOSE & dwFlagsAndAttributes))
 | |
|     {
 | |
|         /* HACK: remember FILE_FLAG_DELETE_ON_CLOSE through HANDLE_FLAG_PROTECT_FROM_CLOSE */
 | |
|         SetHandleInformation(Handle,
 | |
|             HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Handle;
 | |
| }
 | |
| 
 | |
| BOOL WINAPI ResilientCloseHandle(
 | |
|     HANDLE hObject)
 | |
| {
 | |
|     BOOL Success;
 | |
|     DWORD LastError;
 | |
|     DWORD HandleFlags = 0, FileNameLen;
 | |
|     WCHAR FileNameBuf[sizeof "\\\\?\\GLOBALROOT" - 1 + 1024] = L"\\\\?\\GLOBALROOT";
 | |
| 
 | |
|     if (GetHandleInformation(hObject, &HandleFlags) &&
 | |
|         (HANDLE_FLAG_PROTECT_FROM_CLOSE & HandleFlags))
 | |
|     {
 | |
|         SetHandleInformation(hObject,
 | |
|             HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);
 | |
|         FileNameLen = GetFinalPathNameByHandle(hObject,
 | |
|             FileNameBuf + sizeof "\\\\?\\GLOBALROOT" - 1, 1023,
 | |
|             FILE_NAME_OPENED | VOLUME_NAME_NT);
 | |
|         if (0 == FileNameLen || FileNameLen >= 1024)
 | |
|             HandleFlags = 0;
 | |
|     }
 | |
| 
 | |
|     Success = CloseHandle(
 | |
|         hObject);
 | |
|     LastError = GetLastError();
 | |
| 
 | |
|     if (Success)
 | |
|     {
 | |
|         if (HANDLE_FLAG_PROTECT_FROM_CLOSE & HandleFlags)
 | |
|             WaitDeletePending(FileNameBuf);
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| BOOL WINAPI ResilientDeleteFileW(
 | |
|     LPCWSTR lpFileName)
 | |
| {
 | |
|     BOOL Success;
 | |
|     DWORD LastError;
 | |
| 
 | |
|     Success = DeleteFileW(lpFileName);
 | |
|     LastError = GetLastError();
 | |
| 
 | |
|     if (Success)
 | |
|         WaitDeletePending(lpFileName);
 | |
|     else
 | |
|     {
 | |
|         for (ULONG MaxTries = DeleteMaxTries;
 | |
|             !Success && ERROR_SHARING_VIOLATION == GetLastError() && 0 != MaxTries;
 | |
|             MaxTries--)
 | |
|         {
 | |
|             Sleep(DeleteSleepTimeout);
 | |
|             Success = DeleteFileW(lpFileName);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| BOOL WINAPI ResilientRemoveDirectoryW(
 | |
|     LPCWSTR lpPathName)
 | |
| {
 | |
|     BOOL Success;
 | |
|     DWORD LastError;
 | |
| 
 | |
|     Success = RemoveDirectoryW(lpPathName);
 | |
|     LastError = GetLastError();
 | |
| 
 | |
|     if (Success)
 | |
|         WaitDeletePending(lpPathName);
 | |
|     else
 | |
|     {
 | |
|         for (ULONG MaxTries = DeleteMaxTries;
 | |
|             !Success &&
 | |
|             (ERROR_SHARING_VIOLATION == GetLastError() || ERROR_DIR_NOT_EMPTY == GetLastError()) &&
 | |
|             0 != MaxTries;
 | |
|             MaxTries--)
 | |
|         {
 | |
|             Sleep(DeleteSleepTimeout);
 | |
|             Success = RemoveDirectoryW(lpPathName);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SetLastError(LastError);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static VOID WaitDeletePending(PCWSTR FileName)
 | |
| {
 | |
|     for (ULONG MaxTries = DeleteMaxTries; 0 != MaxTries; MaxTries--)
 | |
|     {
 | |
|         HANDLE Handle = CreateFileW(FileName,
 | |
|             FILE_READ_ATTRIBUTES, 0, 0, OPEN_EXISTING,
 | |
|             FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
 | |
|         if (INVALID_HANDLE_VALUE != Handle)
 | |
|             /* should never happen! */
 | |
|             CloseHandle(Handle);
 | |
|         else if (ERROR_ACCESS_DENIED == GetLastError())
 | |
|             /* STATUS_DELETE_PENDING */
 | |
|             Sleep(DeleteSleepTimeout);
 | |
|         else
 | |
|             break;
 | |
|     }
 | |
| }
 |