mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 08:23:05 -05:00
249 lines
6.6 KiB
C
249 lines
6.6 KiB
C
/**
|
|
* @file resilient.c
|
|
*
|
|
* @copyright 2015-2022 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;
|
|
}
|
|
}
|