summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/file/generic.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/file/generic.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/file/generic.c')
-rw-r--r--winpr/libwinpr/file/generic.c1378
1 files changed, 1378 insertions, 0 deletions
diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c
new file mode 100644
index 0000000..e1437ec
--- /dev/null
+++ b/winpr/libwinpr/file/generic.c
@@ -0,0 +1,1378 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2014 Hewlett-Packard Development Company, L.P.
+ * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/string.h>
+#include <winpr/path.h>
+#include <winpr/file.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef WINPR_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("file")
+
+#ifdef _WIN32
+#include <io.h>
+#include <sys/stat.h>
+#else
+#include <winpr/assert.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#ifdef WINPR_HAVE_AIO_H
+#undef WINPR_HAVE_AIO_H /* disable for now, incomplete */
+#endif
+
+#ifdef WINPR_HAVE_AIO_H
+#include <aio.h>
+#endif
+
+#ifdef ANDROID
+#include <sys/vfs.h>
+#else
+#include <sys/statvfs.h>
+#endif
+
+#include "../handle/handle.h"
+
+#include "../pipe/pipe.h"
+
+#include "file.h"
+
+/**
+ * api-ms-win-core-file-l1-2-0.dll:
+ *
+ * CreateFileA
+ * CreateFileW
+ * CreateFile2
+ * DeleteFileA
+ * DeleteFileW
+ * CreateDirectoryA
+ * CreateDirectoryW
+ * RemoveDirectoryA
+ * RemoveDirectoryW
+ * CompareFileTime
+ * DefineDosDeviceW
+ * DeleteVolumeMountPointW
+ * FileTimeToLocalFileTime
+ * LocalFileTimeToFileTime
+ * FindClose
+ * FindCloseChangeNotification
+ * FindFirstChangeNotificationA
+ * FindFirstChangeNotificationW
+ * FindFirstFileA
+ * FindFirstFileExA
+ * FindFirstFileExW
+ * FindFirstFileW
+ * FindFirstVolumeW
+ * FindNextChangeNotification
+ * FindNextFileA
+ * FindNextFileW
+ * FindNextVolumeW
+ * FindVolumeClose
+ * GetDiskFreeSpaceA
+ * GetDiskFreeSpaceExA
+ * GetDiskFreeSpaceExW
+ * GetDiskFreeSpaceW
+ * GetDriveTypeA
+ * GetDriveTypeW
+ * GetFileAttributesA
+ * GetFileAttributesExA
+ * GetFileAttributesExW
+ * GetFileAttributesW
+ * GetFileInformationByHandle
+ * GetFileSize
+ * GetFileSizeEx
+ * GetFileTime
+ * GetFileType
+ * GetFinalPathNameByHandleA
+ * GetFinalPathNameByHandleW
+ * GetFullPathNameA
+ * GetFullPathNameW
+ * GetLogicalDrives
+ * GetLogicalDriveStringsW
+ * GetLongPathNameA
+ * GetLongPathNameW
+ * GetShortPathNameW
+ * GetTempFileNameW
+ * GetTempPathW
+ * GetVolumeInformationByHandleW
+ * GetVolumeInformationW
+ * GetVolumeNameForVolumeMountPointW
+ * GetVolumePathNamesForVolumeNameW
+ * GetVolumePathNameW
+ * QueryDosDeviceW
+ * SetFileAttributesA
+ * SetFileAttributesW
+ * SetFileTime
+ * SetFileValidData
+ * SetFileInformationByHandle
+ * ReadFile
+ * ReadFileEx
+ * ReadFileScatter
+ * WriteFile
+ * WriteFileEx
+ * WriteFileGather
+ * FlushFileBuffers
+ * SetEndOfFile
+ * SetFilePointer
+ * SetFilePointerEx
+ * LockFile
+ * LockFileEx
+ * UnlockFile
+ * UnlockFileEx
+ */
+
+/**
+ * File System Behavior in the Microsoft Windows Environment:
+ * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf
+ */
+
+/**
+ * Asynchronous I/O - The GNU C Library:
+ * http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html
+ */
+
+/**
+ * aio.h - asynchronous input and output:
+ * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
+ */
+
+/**
+ * Asynchronous I/O User Guide:
+ * http://code.google.com/p/kernel/wiki/AIOUserGuide
+ */
+static wArrayList* _HandleCreators;
+
+static pthread_once_t _HandleCreatorsInitialized = PTHREAD_ONCE_INIT;
+
+extern HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void);
+
+#if defined __linux__ && !defined ANDROID
+#include "../comm/comm.h"
+#endif /* __linux__ && !defined ANDROID */
+
+static void _HandleCreatorsInit(void)
+{
+ WINPR_ASSERT(_HandleCreators == NULL);
+ _HandleCreators = ArrayList_New(TRUE);
+
+ if (!_HandleCreators)
+ return;
+
+ /*
+ * Register all file handle creators.
+ */
+ ArrayList_Append(_HandleCreators, GetNamedPipeClientHandleCreator());
+#if defined __linux__ && !defined ANDROID
+ ArrayList_Append(_HandleCreators, GetCommHandleCreator());
+#endif /* __linux__ && !defined ANDROID */
+ ArrayList_Append(_HandleCreators, GetFileHandleCreator());
+}
+
+#ifdef WINPR_HAVE_AIO_H
+
+static BOOL g_AioSignalHandlerInstalled = FALSE;
+
+void AioSignalHandler(int signum, siginfo_t* siginfo, void* arg)
+{
+ WLog_INFO("%d", signum);
+}
+
+int InstallAioSignalHandler()
+{
+ if (!g_AioSignalHandlerInstalled)
+ {
+ struct sigaction action;
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGIO);
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = (void*)&AioSignalHandler;
+ sigaction(SIGIO, &action, NULL);
+ g_AioSignalHandlerInstalled = TRUE;
+ }
+
+ return 0;
+}
+
+#endif /* WINPR_HAVE_AIO_H */
+
+HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ if (!lpFileName)
+ return INVALID_HANDLE_VALUE;
+
+ if (pthread_once(&_HandleCreatorsInitialized, _HandleCreatorsInit) != 0)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (_HandleCreators == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ ArrayList_Lock(_HandleCreators);
+
+ for (size_t i = 0; i <= ArrayList_Count(_HandleCreators); i++)
+ {
+ HANDLE_CREATOR* creator = ArrayList_GetItem(_HandleCreators, i);
+
+ if (creator && creator->IsHandled(lpFileName))
+ {
+ HANDLE newHandle =
+ creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
+ dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ ArrayList_Unlock(_HandleCreators);
+ return newHandle;
+ }
+ }
+
+ ArrayList_Unlock(_HandleCreators);
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ HANDLE hdl = NULL;
+ if (!lpFileName)
+ return NULL;
+ char* lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+
+ if (!lpFileNameA)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto fail;
+ }
+
+ hdl = CreateFileA(lpFileNameA, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
+ dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+fail:
+ free(lpFileNameA);
+ return hdl;
+}
+
+BOOL DeleteFileA(LPCSTR lpFileName)
+{
+ int status = 0;
+ status = unlink(lpFileName);
+ return (status != -1) ? TRUE : FALSE;
+}
+
+BOOL DeleteFileW(LPCWSTR lpFileName)
+{
+ if (!lpFileName)
+ return FALSE;
+ LPSTR lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+ BOOL rc = FALSE;
+
+ if (!lpFileNameA)
+ goto fail;
+
+ rc = DeleteFileA(lpFileNameA);
+fail:
+ free(lpFileNameA);
+ return rc;
+}
+
+BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ /*
+ * from http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
+ * lpNumberOfBytesRead can be NULL only when the lpOverlapped parameter is not NULL.
+ */
+
+ if (!lpNumberOfBytesRead && !lpOverlapped)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->ReadFile)
+ return handle->ops->ReadFile(handle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
+ lpOverlapped);
+
+ WLog_ERR(TAG, "ReadFile operation not implemented");
+ return FALSE;
+}
+
+BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->ReadFileEx)
+ return handle->ops->ReadFileEx(handle, lpBuffer, nNumberOfBytesToRead, lpOverlapped,
+ lpCompletionRoutine);
+
+ WLog_ERR(TAG, "ReadFileEx operation not implemented");
+ return FALSE;
+}
+
+BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead,
+ LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->ReadFileScatter)
+ return handle->ops->ReadFileScatter(handle, aSegmentArray, nNumberOfBytesToRead, lpReserved,
+ lpOverlapped);
+
+ WLog_ERR(TAG, "ReadFileScatter operation not implemented");
+ return FALSE;
+}
+
+BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->WriteFile)
+ return handle->ops->WriteFile(handle, lpBuffer, nNumberOfBytesToWrite,
+ lpNumberOfBytesWritten, lpOverlapped);
+
+ WLog_ERR(TAG, "WriteFile operation not implemented");
+ return FALSE;
+}
+
+BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->WriteFileEx)
+ return handle->ops->WriteFileEx(handle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped,
+ lpCompletionRoutine);
+
+ WLog_ERR(TAG, "WriteFileEx operation not implemented");
+ return FALSE;
+}
+
+BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[],
+ DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->WriteFileGather)
+ return handle->ops->WriteFileGather(handle, aSegmentArray, nNumberOfBytesToWrite,
+ lpReserved, lpOverlapped);
+
+ WLog_ERR(TAG, "WriteFileGather operation not implemented");
+ return FALSE;
+}
+
+BOOL FlushFileBuffers(HANDLE hFile)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->FlushFileBuffers)
+ return handle->ops->FlushFileBuffers(handle);
+
+ WLog_ERR(TAG, "FlushFileBuffers operation not implemented");
+ return FALSE;
+}
+
+BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ LPVOID lpFileInformation)
+{
+ LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation;
+ WIN32_FIND_DATAA findFileData;
+ HANDLE hFind = NULL;
+
+ if (!fd)
+ return FALSE;
+
+ if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ FindClose(hFind);
+ fd->dwFileAttributes = findFileData.dwFileAttributes;
+ fd->ftCreationTime = findFileData.ftCreationTime;
+ fd->ftLastAccessTime = findFileData.ftLastAccessTime;
+ fd->ftLastWriteTime = findFileData.ftLastWriteTime;
+ fd->nFileSizeHigh = findFileData.nFileSizeHigh;
+ fd->nFileSizeLow = findFileData.nFileSizeLow;
+ return TRUE;
+}
+
+BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ LPVOID lpFileInformation)
+{
+ BOOL ret = 0;
+ if (!lpFileName)
+ return FALSE;
+ LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+
+ if (!lpCFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation);
+ free(lpCFileName);
+ return ret;
+}
+
+DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName)
+{
+ WIN32_FIND_DATAA findFileData;
+ HANDLE hFind = NULL;
+
+ if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE)
+ return INVALID_FILE_ATTRIBUTES;
+
+ FindClose(hFind);
+ return findFileData.dwFileAttributes;
+}
+
+DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
+{
+ DWORD ret = 0;
+ if (!lpFileName)
+ return FALSE;
+ LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+ if (!lpCFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ ret = GetFileAttributesA(lpCFileName);
+ free(lpCFileName);
+ return ret;
+}
+
+BOOL GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->GetFileInformationByHandle)
+ return handle->ops->GetFileInformationByHandle(handle, lpFileInformation);
+
+ WLog_ERR(TAG, "GetFileInformationByHandle operation not implemented");
+ return 0;
+}
+
+static char* append(char* buffer, size_t size, const char* append)
+{
+ winpr_str_append(append, buffer, size, "|");
+ return buffer;
+}
+
+static const char* flagsToStr(char* buffer, size_t size, DWORD flags)
+{
+ char strflags[32] = { 0 };
+ if (flags & FILE_ATTRIBUTE_READONLY)
+ append(buffer, size, "FILE_ATTRIBUTE_READONLY");
+ if (flags & FILE_ATTRIBUTE_HIDDEN)
+ append(buffer, size, "FILE_ATTRIBUTE_HIDDEN");
+ if (flags & FILE_ATTRIBUTE_SYSTEM)
+ append(buffer, size, "FILE_ATTRIBUTE_SYSTEM");
+ if (flags & FILE_ATTRIBUTE_DIRECTORY)
+ append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY");
+ if (flags & FILE_ATTRIBUTE_ARCHIVE)
+ append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE");
+ if (flags & FILE_ATTRIBUTE_DEVICE)
+ append(buffer, size, "FILE_ATTRIBUTE_DEVICE");
+ if (flags & FILE_ATTRIBUTE_NORMAL)
+ append(buffer, size, "FILE_ATTRIBUTE_NORMAL");
+ if (flags & FILE_ATTRIBUTE_TEMPORARY)
+ append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY");
+ if (flags & FILE_ATTRIBUTE_SPARSE_FILE)
+ append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE");
+ if (flags & FILE_ATTRIBUTE_REPARSE_POINT)
+ append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT");
+ if (flags & FILE_ATTRIBUTE_COMPRESSED)
+ append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED");
+ if (flags & FILE_ATTRIBUTE_OFFLINE)
+ append(buffer, size, "FILE_ATTRIBUTE_OFFLINE");
+ if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED");
+ if (flags & FILE_ATTRIBUTE_ENCRYPTED)
+ append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED");
+ if (flags & FILE_ATTRIBUTE_VIRTUAL)
+ append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL");
+
+ _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags);
+ winpr_str_append(strflags, buffer, size, NULL);
+ return buffer;
+}
+
+BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
+{
+ struct stat st;
+ int fd = 0;
+ BOOL rc = FALSE;
+
+ if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
+ {
+ char buffer[8192] = { 0 };
+ const char* flags =
+ flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+ WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags);
+ }
+
+ fd = open(lpFileName, O_RDONLY);
+ if (fd < 0)
+ return FALSE;
+
+ if (fstat(fd, &st) != 0)
+ goto fail;
+
+ if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ {
+ st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+ else
+ {
+ st.st_mode |= S_IWUSR;
+ }
+
+ if (fchmod(fd, st.st_mode) != 0)
+ goto fail;
+
+ rc = TRUE;
+fail:
+ close(fd);
+ return rc;
+}
+
+BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
+{
+ BOOL ret = 0;
+ LPSTR lpCFileName = NULL;
+
+ if (!lpFileName)
+ return FALSE;
+
+ if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
+ {
+ char buffer[8192] = { 0 };
+ const char* flags =
+ flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+ WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags);
+ }
+
+ lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+ if (!lpCFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ ret = SetFileAttributesA(lpCFileName, dwFileAttributes);
+ free(lpCFileName);
+ return ret;
+}
+
+BOOL SetEndOfFile(HANDLE hFile)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->SetEndOfFile)
+ return handle->ops->SetEndOfFile(handle);
+
+ WLog_ERR(TAG, "SetEndOfFile operation not implemented");
+ return FALSE;
+}
+
+DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->GetFileSize)
+ return handle->ops->GetFileSize(handle, lpFileSizeHigh);
+
+ WLog_ERR(TAG, "GetFileSize operation not implemented");
+ return 0;
+}
+
+DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->SetFilePointer)
+ return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh,
+ dwMoveMethod);
+
+ WLog_ERR(TAG, "SetFilePointer operation not implemented");
+ return 0;
+}
+
+BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer,
+ DWORD dwMoveMethod)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->SetFilePointerEx)
+ return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer,
+ dwMoveMethod);
+
+ WLog_ERR(TAG, "SetFilePointerEx operation not implemented");
+ return 0;
+}
+
+BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->LockFile)
+ return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
+ nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
+
+ WLog_ERR(TAG, "LockFile operation not implemented");
+ return FALSE;
+}
+
+BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->LockFileEx)
+ return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow,
+ nNumberOfBytesToLockHigh, lpOverlapped);
+
+ WLog_ERR(TAG, "LockFileEx operation not implemented");
+ return FALSE;
+}
+
+BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->UnlockFile)
+ return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
+ nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
+
+ WLog_ERR(TAG, "UnLockFile operation not implemented");
+ return FALSE;
+}
+
+BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->UnlockFileEx)
+ return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow,
+ nNumberOfBytesToUnlockHigh, lpOverlapped);
+
+ WLog_ERR(TAG, "UnLockFileEx operation not implemented");
+ return FALSE;
+}
+
+BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
+ const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* handle = NULL;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
+ return FALSE;
+
+ handle = (WINPR_HANDLE*)hFile;
+
+ if (handle->ops->SetFileTime)
+ return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
+
+ WLog_ERR(TAG, "operation not implemented");
+ return FALSE;
+}
+
+typedef struct
+{
+ char magic[16];
+ LPSTR lpPath;
+ LPSTR lpPattern;
+ DIR* pDir;
+} WIN32_FILE_SEARCH;
+
+static const char file_search_magic[] = "file_srch_magic";
+
+static WIN32_FILE_SEARCH* file_search_new(const char* name, size_t namelen, const char* pattern,
+ size_t patternlen)
+{
+ WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH));
+ if (!pFileSearch)
+ return NULL;
+ strncpy(pFileSearch->magic, file_search_magic, sizeof(pFileSearch->magic));
+
+ pFileSearch->lpPath = strndup(name, namelen);
+ pFileSearch->lpPattern = strndup(pattern, patternlen);
+ if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
+ goto fail;
+
+ pFileSearch->pDir = opendir(pFileSearch->lpPath);
+ if (!pFileSearch->pDir)
+ {
+ /* Work around for android:
+ * parent directories are not accessible, so if we have a directory without pattern
+ * try to open it directly and set pattern to '*'
+ */
+ struct stat fileStat = { 0 };
+ if (stat(name, &fileStat) == 0)
+ {
+ if (S_ISDIR(fileStat.st_mode))
+ {
+ pFileSearch->pDir = opendir(name);
+ if (pFileSearch->pDir)
+ {
+ free(pFileSearch->lpPath);
+ free(pFileSearch->lpPattern);
+ pFileSearch->lpPath = _strdup(name);
+ pFileSearch->lpPattern = _strdup("*");
+ if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
+ {
+ closedir(pFileSearch->pDir);
+ pFileSearch->pDir = NULL;
+ }
+ }
+ }
+ }
+ }
+ if (!pFileSearch->pDir)
+ goto fail;
+
+ return pFileSearch;
+fail:
+ FindClose(pFileSearch);
+ return NULL;
+}
+
+static BOOL is_valid_file_search_handle(HANDLE handle)
+{
+ WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)handle;
+ if (!pFileSearch)
+ return FALSE;
+ if (pFileSearch == INVALID_HANDLE_VALUE)
+ return FALSE;
+ if (strcmp(file_search_magic, pFileSearch->magic) != 0)
+ return FALSE;
+ return TRUE;
+}
+static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
+ LPWIN32_FIND_DATAA lpFindFileData)
+{
+ UINT64 ft = 0;
+ char* lastSep = NULL;
+ lpFindFileData->dwFileAttributes = 0;
+
+ if (S_ISDIR(fileStat->st_mode))
+ lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+
+ if (lpFindFileData->dwFileAttributes == 0)
+ lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
+
+ lastSep = strrchr(path, '/');
+
+ if (lastSep)
+ {
+ const char* name = lastSep + 1;
+ const size_t namelen = strlen(name);
+
+ if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
+ lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+ }
+
+ if (!(fileStat->st_mode & S_IWUSR))
+ lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
+
+#ifdef _DARWIN_FEATURE_64_BIT_INODE
+ ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
+#else
+ ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
+#endif
+ lpFindFileData->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
+ lpFindFileData->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
+ lpFindFileData->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
+ lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF;
+ return TRUE;
+}
+
+HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
+{
+ if (!lpFindFileData || !lpFileName)
+ {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ const WIN32_FIND_DATAA empty = { 0 };
+ *lpFindFileData = empty;
+
+ WIN32_FILE_SEARCH* pFileSearch = NULL;
+ size_t patternlen = 0;
+ const size_t flen = strlen(lpFileName);
+ const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE);
+ const char* ptr = strrchr(lpFileName, sep);
+ if (!ptr)
+ goto fail;
+ patternlen = strlen(ptr + 1);
+ if (patternlen == 0)
+ goto fail;
+
+ pFileSearch = file_search_new(lpFileName, flen - patternlen, ptr + 1, patternlen);
+
+ if (!pFileSearch)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData))
+ return (HANDLE)pFileSearch;
+
+fail:
+ FindClose(pFileSearch);
+ return INVALID_HANDLE_VALUE;
+}
+
+static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,
+ LPWIN32_FIND_DATAW lpFindFileDataW)
+{
+ if (!lpFindFileDataA || !lpFindFileDataW)
+ return FALSE;
+
+ lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes;
+ lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime;
+ lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime;
+ lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime;
+ lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh;
+ lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow;
+ lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0;
+ lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1;
+
+ if (ConvertUtf8NToWChar(lpFindFileDataA->cFileName, ARRAYSIZE(lpFindFileDataA->cFileName),
+ lpFindFileDataW->cFileName, ARRAYSIZE(lpFindFileDataW->cFileName)) < 0)
+ return FALSE;
+
+ return ConvertUtf8NToWChar(lpFindFileDataA->cAlternateFileName,
+ ARRAYSIZE(lpFindFileDataA->cAlternateFileName),
+ lpFindFileDataW->cAlternateFileName,
+ ARRAYSIZE(lpFindFileDataW->cAlternateFileName)) >= 0;
+}
+
+HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
+{
+ LPSTR utfFileName = NULL;
+ HANDLE h = NULL;
+ if (!lpFileName)
+ return FALSE;
+ LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
+
+ if (!fd)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ utfFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
+ if (!utfFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ free(fd);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ h = FindFirstFileA(utfFileName, fd);
+ free(utfFileName);
+
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (!ConvertFindDataAToW(fd, lpFindFileData))
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ FindClose(h);
+ h = INVALID_HANDLE_VALUE;
+ goto out;
+ }
+ }
+
+out:
+ free(fd);
+ return h;
+}
+
+HANDLE FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
+ FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
+{
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
+ FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
+{
+ return INVALID_HANDLE_VALUE;
+}
+
+BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
+{
+ if (!lpFindFileData)
+ return FALSE;
+
+ const WIN32_FIND_DATAA empty = { 0 };
+ *lpFindFileData = empty;
+
+ if (!is_valid_file_search_handle(hFindFile))
+ return FALSE;
+
+ WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
+ struct dirent* pDirent = NULL;
+ while ((pDirent = readdir(pFileSearch->pDir)) != NULL)
+ {
+ if (FilePatternMatchA(pDirent->d_name, pFileSearch->lpPattern))
+ {
+ BOOL success = FALSE;
+
+ strncpy(lpFindFileData->cFileName, pDirent->d_name, MAX_PATH);
+ const size_t namelen = strnlen(lpFindFileData->cFileName, MAX_PATH);
+ size_t pathlen = strlen(pFileSearch->lpPath);
+ char* fullpath = (char*)malloc(pathlen + namelen + 2);
+
+ if (fullpath == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ memcpy(fullpath, pFileSearch->lpPath, pathlen);
+ /* Ensure path is terminated with a separator, but prevent
+ * duplicate separators */
+ if (fullpath[pathlen - 1] != '/')
+ fullpath[pathlen++] = '/';
+ memcpy(fullpath + pathlen, pDirent->d_name, namelen);
+ fullpath[pathlen + namelen] = 0;
+
+ struct stat fileStat = { 0 };
+ if (stat(fullpath, &fileStat) != 0)
+ {
+ free(fullpath);
+ SetLastError(map_posix_err(errno));
+ errno = 0;
+ continue;
+ }
+
+ /* Skip FIFO entries. */
+ if (S_ISFIFO(fileStat.st_mode))
+ {
+ free(fullpath);
+ continue;
+ }
+
+ success = FindDataFromStat(fullpath, &fileStat, lpFindFileData);
+ free(fullpath);
+ return success;
+ }
+ }
+
+ SetLastError(ERROR_NO_MORE_FILES);
+ return FALSE;
+}
+
+BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
+{
+ LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
+
+ if (!fd)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ if (FindNextFileA(hFindFile, fd))
+ {
+ if (!ConvertFindDataAToW(fd, lpFindFileData))
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ free(fd);
+ return FALSE;
+ }
+
+ free(fd);
+ return TRUE;
+ }
+
+ free(fd);
+ return FALSE;
+}
+
+BOOL FindClose(HANDLE hFindFile)
+{
+ WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
+ if (!pFileSearch)
+ return FALSE;
+
+ /* Since INVALID_HANDLE_VALUE != NULL the analyzer guesses that there
+ * is a initialized HANDLE that is not freed properly.
+ * Disable this return to stop confusing the analyzer. */
+#ifndef __clang_analyzer__
+ if (!is_valid_file_search_handle(hFindFile))
+ return FALSE;
+#endif
+
+ free(pFileSearch->lpPath);
+ free(pFileSearch->lpPattern);
+
+ if (pFileSearch->pDir)
+ closedir(pFileSearch->pDir);
+
+ free(pFileSearch);
+ return TRUE;
+}
+
+BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR))
+ return TRUE;
+
+ return FALSE;
+}
+
+BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ if (!lpPathName)
+ return FALSE;
+ char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL);
+ BOOL ret = FALSE;
+
+ if (!utfPathName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto fail;
+ }
+
+ ret = CreateDirectoryA(utfPathName, lpSecurityAttributes);
+fail:
+ free(utfPathName);
+ return ret;
+}
+
+BOOL RemoveDirectoryA(LPCSTR lpPathName)
+{
+ int ret = rmdir(lpPathName);
+
+ if (ret != 0)
+ SetLastError(map_posix_err(errno));
+ else
+ SetLastError(STATUS_SUCCESS);
+
+ return ret == 0;
+}
+
+BOOL RemoveDirectoryW(LPCWSTR lpPathName)
+{
+ if (!lpPathName)
+ return FALSE;
+ char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL);
+ BOOL ret = FALSE;
+
+ if (!utfPathName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto fail;
+ }
+
+ ret = RemoveDirectoryA(utfPathName);
+fail:
+ free(utfPathName);
+ return ret;
+}
+
+BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
+{
+ struct stat st;
+ int ret = 0;
+ ret = stat(lpNewFileName, &st);
+
+ if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
+ {
+ if (ret == 0)
+ {
+ SetLastError(ERROR_ALREADY_EXISTS);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
+ {
+ SetLastError(ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+ }
+
+ ret = rename(lpExistingFileName, lpNewFileName);
+
+ if (ret != 0)
+ SetLastError(map_posix_err(errno));
+
+ return ret == 0;
+}
+
+BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
+{
+ if (!lpExistingFileName || !lpNewFileName)
+ return FALSE;
+
+ LPSTR lpCExistingFileName = ConvertWCharToUtf8Alloc(lpExistingFileName, NULL);
+ LPSTR lpCNewFileName = ConvertWCharToUtf8Alloc(lpNewFileName, NULL);
+ BOOL ret = FALSE;
+
+ if (!lpCExistingFileName || !lpCNewFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto fail;
+ }
+
+ ret = MoveFileExA(lpCExistingFileName, lpCNewFileName, dwFlags);
+fail:
+ free(lpCNewFileName);
+ free(lpCExistingFileName);
+ return ret;
+}
+
+BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
+{
+ return MoveFileExA(lpExistingFileName, lpNewFileName, 0);
+}
+
+BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
+{
+ return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
+}
+
+#endif
+
+/* Extended API */
+
+int UnixChangeFileMode(const char* filename, int flags)
+{
+ if (!filename)
+ return -1;
+#ifndef _WIN32
+ mode_t fl = 0;
+ fl |= (flags & 0x4000) ? S_ISUID : 0;
+ fl |= (flags & 0x2000) ? S_ISGID : 0;
+ fl |= (flags & 0x1000) ? S_ISVTX : 0;
+ fl |= (flags & 0x0400) ? S_IRUSR : 0;
+ fl |= (flags & 0x0200) ? S_IWUSR : 0;
+ fl |= (flags & 0x0100) ? S_IXUSR : 0;
+ fl |= (flags & 0x0040) ? S_IRGRP : 0;
+ fl |= (flags & 0x0020) ? S_IWGRP : 0;
+ fl |= (flags & 0x0010) ? S_IXGRP : 0;
+ fl |= (flags & 0x0004) ? S_IROTH : 0;
+ fl |= (flags & 0x0002) ? S_IWOTH : 0;
+ fl |= (flags & 0x0001) ? S_IXOTH : 0;
+ return chmod(filename, fl);
+#else
+ int rc;
+ WCHAR* wfl = ConvertUtf8ToWCharAlloc(filename, NULL);
+
+ if (!wfl)
+ return -1;
+
+ /* Check for unsupported flags. */
+ if (flags & ~(_S_IREAD | _S_IWRITE))
+ WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags);
+
+ rc = _wchmod(wfl, flags);
+ free(wfl);
+ return rc;
+#endif
+}