[*] NT/Win32-like: Use Win32Open as opposed to CreateFileW
This commit is contained in:
parent
a8696c0b7e
commit
d0538ea4de
@ -5,6 +5,7 @@
|
||||
Date: 2023-2-16
|
||||
Author: Reece
|
||||
***/
|
||||
#define _WIN32_WINNT 0x0602
|
||||
#include <RuntimeInternal.hpp>
|
||||
#include "Source/Threading/Primitives/AuConditionMutex.NT.hpp"
|
||||
|
||||
@ -98,6 +99,8 @@ namespace Aurora
|
||||
ADD_GET_PROC_BI(Kernel32, KernelBase, VirtualAlloc2)
|
||||
ADD_GET_PROC_BI(Kernel32, KernelBase, MapViewOfFile3)
|
||||
ADD_GET_PROC_BI(Kernel32, KernelBase, UnmapViewOfFile2)
|
||||
ADD_GET_PROC_BI(Kernel32, KernelBase, CreateFileW)
|
||||
ADD_GET_PROC_BI(Kernel32, KernelBase, CreateFile2)
|
||||
|
||||
ADD_GET_PROC(Kernel32, GetSystemCpuSetInformation)
|
||||
ADD_GET_PROC(Kernel32, GetLogicalProcessorInformation)
|
||||
@ -168,6 +171,8 @@ namespace Aurora
|
||||
}
|
||||
|
||||
#else
|
||||
pCreateFile2 = CreateFile2;
|
||||
|
||||
pWaitOnAddress = WaitOnAddress;
|
||||
pWakeByAddressSingle = WakeByAddressSingle;
|
||||
pWakeByAddressAll = WakeByAddressAll;
|
||||
@ -294,4 +299,60 @@ namespace Aurora
|
||||
::TerminateProcess(::GetCurrentProcess(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE Win32Open(LPCWSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
bool bInherit,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlags,
|
||||
DWORD dwAttributes
|
||||
)
|
||||
{
|
||||
SECURITY_ATTRIBUTES attrs {};
|
||||
attrs.nLength = sizeof(attrs);
|
||||
attrs.bInheritHandle = bInherit ? TRUE : FALSE;
|
||||
|
||||
if (!dwAttributes)
|
||||
{
|
||||
dwAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
|
||||
if (pCreateFileW)
|
||||
{
|
||||
return pCreateFileW(lpFileName,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
&attrs,
|
||||
dwCreationDisposition,
|
||||
dwFlags | dwAttributes,
|
||||
NULL);
|
||||
}
|
||||
else if (pCreateFile2)
|
||||
{
|
||||
_CREATEFILE2_EXTENDED_PARAMETERS params {};
|
||||
|
||||
if (::wcscmp(lpFileName, L"CONIN$") == 0 ||
|
||||
::wcscmp(lpFileName, L"CONOUT$") == 0 ||
|
||||
::wcscmp(lpFileName, L"CONERR$") == 0)
|
||||
{
|
||||
lpFileName = L"CON";
|
||||
}
|
||||
|
||||
params.dwSize = sizeof(_CREATEFILE2_EXTENDED_PARAMETERS);
|
||||
params.dwFileFlags = dwFlags;
|
||||
params.dwFileAttributes = dwAttributes;
|
||||
params.lpSecurityAttributes = &attrs;
|
||||
|
||||
return pCreateFile2(lpFileName,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
dwCreationDisposition,
|
||||
¶ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ struct _tagADDRESS64;
|
||||
struct _MINIDUMP_CALLBACK_INFORMATION;
|
||||
struct _MIB_IPADDRTABLE;
|
||||
struct _IP_ADAPTER_INFO;
|
||||
struct _CREATEFILE2_EXTENDED_PARAMETERS;
|
||||
enum _MINIDUMP_TYPE;
|
||||
|
||||
#if defined(AURORA_COMPILER_MSVC)
|
||||
@ -163,6 +164,24 @@ namespace Aurora
|
||||
);
|
||||
#endif
|
||||
|
||||
inline HANDLE(__stdcall *pCreateFile2)(
|
||||
LPCWSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
DWORD dwCreationDisposition,
|
||||
_CREATEFILE2_EXTENDED_PARAMETERS *pCreateExParams
|
||||
);
|
||||
|
||||
inline HANDLE(__stdcall *pCreateFileW)(
|
||||
LPCWSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes,
|
||||
HANDLE hTemplateFile
|
||||
);
|
||||
|
||||
inline NTSTATUS(__stdcall *pNtNotifyChangeDirectoryFile)(
|
||||
HANDLE FileHandle,
|
||||
HANDLE Event,
|
||||
@ -510,4 +529,13 @@ namespace Aurora
|
||||
void Win32DropSchedulerResolution();
|
||||
|
||||
void Win32Terminate();
|
||||
|
||||
HANDLE Win32Open(LPCWSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
bool bInherit,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlags,
|
||||
DWORD dwAttributes
|
||||
);
|
||||
}
|
@ -771,13 +771,13 @@ namespace Aurora::Console::ConsoleStd
|
||||
#if defined(AURORA_PLATFORM_WIN32)
|
||||
GetStdHandle(STD_INPUT_HANDLE);
|
||||
#else
|
||||
CreateFileW(L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
Win32Open(L"CONIN$",
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
#endif
|
||||
|
||||
SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONIN");
|
||||
@ -788,13 +788,13 @@ namespace Aurora::Console::ConsoleStd
|
||||
#if defined(AURORA_PLATFORM_WIN32)
|
||||
GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#else
|
||||
CreateFileW(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
Win32Open(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
#endif
|
||||
|
||||
SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONOUT");
|
||||
|
@ -383,7 +383,7 @@ namespace Aurora::Debug
|
||||
|
||||
AuIOFS::CreateDirectories(utf8Path, true);
|
||||
|
||||
auto hFile = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
auto hFile = Win32Open(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, false, CREATE_ALWAYS, 0, FILE_ATTRIBUTE_NORMAL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
AuLogWarn("Couldn't open minidump file. Has a debugger locked the .dmp file?");
|
||||
@ -471,7 +471,7 @@ namespace Aurora::Debug
|
||||
|
||||
AuIOFS::CreateDirectories(utf8Path, true); // potentially unsafe / could throw inside
|
||||
|
||||
hFile = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
hFile = Win32Open(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, false, CREATE_ALWAYS, 0, FILE_ATTRIBUTE_NORMAL);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
AuLogWarn("[1] Couldn't open minidump file. Has a debugger locked the .dmp file?");
|
||||
|
@ -15,6 +15,11 @@
|
||||
namespace Aurora::IO
|
||||
{
|
||||
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess)
|
||||
{
|
||||
return DupHandle(uOSHandle, bWriteAccess, false);
|
||||
}
|
||||
|
||||
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess, bool bShareAccess)
|
||||
{
|
||||
HANDLE hTargetHandle = (HANDLE)uOSHandle;
|
||||
HANDLE hTargetProcess = ::GetCurrentProcess();
|
||||
@ -25,8 +30,8 @@ namespace Aurora::IO
|
||||
hTargetProcess,
|
||||
&hHandle,
|
||||
bWriteAccess ? GENERIC_WRITE | GENERIC_READ : GENERIC_READ,
|
||||
FALSE,
|
||||
FALSE))
|
||||
bShareAccess ? TRUE : FALSE,
|
||||
0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -50,6 +55,7 @@ namespace Aurora::IO
|
||||
HANDLE hFileHandle;
|
||||
|
||||
DWORD dwFlags {};
|
||||
DWORD dwAttrs {};
|
||||
DWORD dwShare {};
|
||||
|
||||
if (create.path.empty())
|
||||
@ -101,20 +107,20 @@ namespace Aurora::IO
|
||||
|
||||
if (!dwFlags)
|
||||
{
|
||||
dwFlags |= FILE_ATTRIBUTE_NORMAL;
|
||||
dwAttrs |= FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
|
||||
switch (create.eMode)
|
||||
{
|
||||
case FS::EFileOpenMode::eRead:
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_READ,
|
||||
dwShare,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_READ,
|
||||
dwShare,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
|
||||
if (hFileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -132,13 +138,13 @@ namespace Aurora::IO
|
||||
|
||||
if (create.bFailIfNonEmptyFile)
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
NULL,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
false,
|
||||
false,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
|
||||
if (hFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -151,23 +157,23 @@ namespace Aurora::IO
|
||||
}
|
||||
else
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
dwShare,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
dwShare,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
|
||||
if (hFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
dwShare,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ | DELETE,
|
||||
dwShare,
|
||||
false,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,13 +194,13 @@ namespace Aurora::IO
|
||||
|
||||
if (create.bFailIfNonEmptyFile)
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | DELETE,
|
||||
NULL,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | DELETE,
|
||||
NULL,
|
||||
false,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
|
||||
if (hFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -207,23 +213,23 @@ namespace Aurora::IO
|
||||
}
|
||||
else
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | FILE_READ_ATTRIBUTES | DELETE,
|
||||
dwShare,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | FILE_READ_ATTRIBUTES | DELETE,
|
||||
dwShare,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
|
||||
if (hFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
hFileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | DELETE,
|
||||
dwShare,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
NULL);
|
||||
hFileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | DELETE,
|
||||
dwShare,
|
||||
false,
|
||||
CREATE_NEW,
|
||||
dwFlags,
|
||||
dwAttrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,7 @@ namespace Aurora::IO
|
||||
// bool InitFromPath(HandleCreate create) override;
|
||||
|
||||
static AuUInt64 DupHandle(AuUInt64 uOSHandle, bool bWriteAccess);
|
||||
static AuUInt64 DupHandle(AuUInt64 uOSHandle, bool bWriteAccess, bool bShareAccess);
|
||||
static void CloseHandle(AuUInt64 uOSHandle);
|
||||
};
|
||||
}
|
@ -196,13 +196,13 @@ namespace Aurora::IO::FS
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fileHandle = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
auto fileHandle = Win32Open(win32Path.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SysPushErrorIO("Couldn't open handle: {}", path);
|
||||
|
@ -51,13 +51,13 @@ namespace Aurora::IO::FS
|
||||
return false;
|
||||
}
|
||||
|
||||
hFile = ::CreateFileW(win32Path.c_str(),
|
||||
GENERIC_WRITE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
hFile = Win32Open(win32Path.c_str(),
|
||||
GENERIC_WRITE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
@ -175,13 +175,13 @@ namespace Aurora::IO::FS
|
||||
void NTCachedPath::Warm()
|
||||
{
|
||||
HANDLE hFile;
|
||||
hFile = CreateFileW(AuLocale::ConvertFromUTF8(this->strNormalizedPath).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | (this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
||||
NULL);
|
||||
hFile = Win32Open(AuLocale::ConvertFromUTF8(this->strNormalizedPath).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0,
|
||||
0);
|
||||
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -291,13 +291,13 @@ namespace Aurora::IO::FS
|
||||
FILETIME curFileTime {};
|
||||
HANDLE hFile;
|
||||
|
||||
hFile = CreateFileW(AuLocale::ConvertFromUTF8(this->strNormalizedPath).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | (this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
||||
NULL);
|
||||
hFile = Win32Open(AuLocale::ConvertFromUTF8(this->strNormalizedPath).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0,
|
||||
0);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -352,13 +352,13 @@ namespace Aurora::IO::FS
|
||||
this->strBaseDir.pop_back();
|
||||
}
|
||||
|
||||
this->hFileHandle = CreateFileW(AuLocale::ConvertFromUTF8(this->strBaseDir.c_str()).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
this->hFileHandle = Win32Open(AuLocale::ConvertFromUTF8(this->strBaseDir.c_str()).c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
false,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
0);
|
||||
if (this->hFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return false;
|
||||
|
@ -306,7 +306,7 @@ namespace Aurora::Process
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
auto widePath = Locale::ConvertFromUTF8(pathNrml);
|
||||
auto mitigateTimeOfUse = CreateFileW(widePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
auto mitigateTimeOfUse = Win32Open(widePath.c_str(), GENERIC_READ, FILE_SHARE_READ, false, OPEN_EXISTING, 0, 0);
|
||||
|
||||
if (mitigateTimeOfUse == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ namespace Aurora::Process
|
||||
Value_t value {};
|
||||
DWORD dwBytesRead {};
|
||||
|
||||
auto hHandle = CreateFileW(Locale::ConvertFromUTF8(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
||||
auto hHandle = Win32Open(Locale::ConvertFromUTF8(path).c_str(), GENERIC_READ, FILE_SHARE_READ, false, OPEN_EXISTING, 0, 0);
|
||||
if (hHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return 0;
|
||||
|
@ -20,7 +20,9 @@
|
||||
#endif
|
||||
|
||||
// Do not change: Minimum target - Windows 7
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#if !defined(_WIN32_WINNT)
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#endif
|
||||
#include <SDKDDKVer.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
|
Loading…
Reference in New Issue
Block a user