summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/file
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
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')
-rw-r--r--winpr/libwinpr/file/CMakeLists.txt22
-rw-r--r--winpr/libwinpr/file/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/file/file.c1483
-rw-r--r--winpr/libwinpr/file/file.h66
-rw-r--r--winpr/libwinpr/file/generic.c1378
-rw-r--r--winpr/libwinpr/file/namedPipeClient.c305
-rw-r--r--winpr/libwinpr/file/pattern.c371
-rw-r--r--winpr/libwinpr/file/test/CMakeLists.txt58
-rw-r--r--winpr/libwinpr/file/test/TestFileCreateFile.c94
-rw-r--r--winpr/libwinpr/file/test/TestFileDeleteFile.c50
-rw-r--r--winpr/libwinpr/file/test/TestFileFindFirstFile.c326
-rw-r--r--winpr/libwinpr/file/test/TestFileFindFirstFileEx.c10
-rw-r--r--winpr/libwinpr/file/test/TestFileFindNextFile.c99
-rw-r--r--winpr/libwinpr/file/test/TestFileGetStdHandle.c49
-rw-r--r--winpr/libwinpr/file/test/TestFilePatternMatch.c182
-rw-r--r--winpr/libwinpr/file/test/TestFileReadFile.c10
-rw-r--r--winpr/libwinpr/file/test/TestFileWriteFile.c10
-rw-r--r--winpr/libwinpr/file/test/TestSetFileAttributes.c152
18 files changed, 4674 insertions, 0 deletions
diff --git a/winpr/libwinpr/file/CMakeLists.txt b/winpr/libwinpr/file/CMakeLists.txt
new file mode 100644
index 0000000..986c873
--- /dev/null
+++ b/winpr/libwinpr/file/CMakeLists.txt
@@ -0,0 +1,22 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-file cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
+
+winpr_module_add(generic.c namedPipeClient.c pattern.c file.c)
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/winpr/libwinpr/file/ModuleOptions.cmake b/winpr/libwinpr/file/ModuleOptions.cmake
new file mode 100644
index 0000000..e2f0d36
--- /dev/null
+++ b/winpr/libwinpr/file/ModuleOptions.cmake
@@ -0,0 +1,9 @@
+
+set(MINWIN_LAYER "1")
+set(MINWIN_GROUP "core")
+set(MINWIN_MAJOR_VERSION "2")
+set(MINWIN_MINOR_VERSION "0")
+set(MINWIN_SHORT_NAME "file")
+set(MINWIN_LONG_NAME "File Functions")
+set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}")
+
diff --git a/winpr/libwinpr/file/file.c b/winpr/libwinpr/file/file.c
new file mode 100644
index 0000000..01328b8
--- /dev/null
+++ b/winpr/libwinpr/file/file.c
@@ -0,0 +1,1483 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 Bernhard Miklautz <bernhard.miklautz@thincast.com>
+ * 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/debug.h>
+
+#if defined(__FreeBSD_kernel__) && defined(__GLIBC__)
+#define _GNU_SOURCE
+#define KFREEBSD
+#endif
+
+#include <winpr/wtypes.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+
+#ifdef _WIN32
+
+#include <io.h>
+
+#else /* _WIN32 */
+
+#include "../log.h"
+#define TAG WINPR_TAG("file")
+
+#include <winpr/wlog.h>
+#include <winpr/string.h>
+
+#include "file.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#ifdef ANDROID
+#include <sys/vfs.h>
+#else
+#include <sys/statvfs.h>
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+static BOOL FileIsHandled(HANDLE handle)
+{
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_FILE, FALSE);
+}
+
+static int FileGetFd(HANDLE handle)
+{
+ WINPR_FILE* file = (WINPR_FILE*)handle;
+
+ if (!FileIsHandled(handle))
+ return -1;
+
+ return fileno(file->fp);
+}
+
+static BOOL FileCloseHandle(HANDLE handle)
+{
+ WINPR_FILE* file = (WINPR_FILE*)handle;
+
+ if (!FileIsHandled(handle))
+ return FALSE;
+
+ if (file->fp)
+ {
+ /* Don't close stdin/stdout/stderr */
+ if (fileno(file->fp) > 2)
+ {
+ fclose(file->fp);
+ file->fp = NULL;
+ }
+ }
+
+ free(file->lpFileName);
+ free(file);
+ return TRUE;
+}
+
+static BOOL FileSetEndOfFile(HANDLE hFile)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+ INT64 size = 0;
+
+ if (!hFile)
+ return FALSE;
+
+ size = _ftelli64(pFile->fp);
+
+ if (ftruncate(fileno(pFile->fp), size) < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "ftruncate %s failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ SetLastError(map_posix_err(errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+ INT64 offset = 0;
+ int whence = 0;
+
+ if (!hFile)
+ return INVALID_SET_FILE_POINTER;
+
+ /* If there is a high part, the sign is contained in that
+ * and the low integer must be interpreted as unsigned. */
+ if (lpDistanceToMoveHigh)
+ {
+ offset = (INT64)(((UINT64)*lpDistanceToMoveHigh << 32U) | (UINT64)lDistanceToMove);
+ }
+ else
+ offset = lDistanceToMove;
+
+ switch (dwMoveMethod)
+ {
+ case FILE_BEGIN:
+ whence = SEEK_SET;
+ break;
+ case FILE_END:
+ whence = SEEK_END;
+ break;
+ case FILE_CURRENT:
+ whence = SEEK_CUR;
+ break;
+ default:
+ return INVALID_SET_FILE_POINTER;
+ }
+
+ if (_fseeki64(pFile->fp, offset, whence))
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return INVALID_SET_FILE_POINTER;
+ }
+
+ return (DWORD)_ftelli64(pFile->fp);
+}
+
+static BOOL FileSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove,
+ PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+ int whence = 0;
+
+ if (!hFile)
+ return FALSE;
+
+ switch (dwMoveMethod)
+ {
+ case FILE_BEGIN:
+ whence = SEEK_SET;
+ break;
+ case FILE_END:
+ whence = SEEK_END;
+ break;
+ case FILE_CURRENT:
+ whence = SEEK_CUR;
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (_fseeki64(pFile->fp, liDistanceToMove.QuadPart, whence))
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+
+ if (lpNewFilePointer)
+ lpNewFilePointer->QuadPart = _ftelli64(pFile->fp);
+
+ return TRUE;
+}
+
+static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
+{
+ size_t io_status = 0;
+ WINPR_FILE* file = NULL;
+ BOOL status = TRUE;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ if (!Object)
+ return FALSE;
+
+ file = (WINPR_FILE*)Object;
+ clearerr(file->fp);
+ io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp);
+
+ if (io_status == 0 && ferror(file->fp))
+ {
+ status = FALSE;
+
+ switch (errno)
+ {
+ case EWOULDBLOCK:
+ SetLastError(ERROR_NO_DATA);
+ break;
+ default:
+ SetLastError(map_posix_err(errno));
+ }
+ }
+
+ if (lpNumberOfBytesRead)
+ *lpNumberOfBytesRead = (DWORD)io_status;
+
+ return status;
+}
+
+static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
+{
+ size_t io_status = 0;
+ WINPR_FILE* file = NULL;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ if (!Object)
+ return FALSE;
+
+ file = (WINPR_FILE*)Object;
+
+ clearerr(file->fp);
+ io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp);
+ if (io_status == 0 && ferror(file->fp))
+ {
+ SetLastError(map_posix_err(errno));
+ return FALSE;
+ }
+
+ *lpNumberOfBytesWritten = (DWORD)io_status;
+ return TRUE;
+}
+
+static DWORD FileGetFileSize(HANDLE Object, LPDWORD lpFileSizeHigh)
+{
+ WINPR_FILE* file = NULL;
+ INT64 cur = 0;
+ INT64 size = 0;
+
+ if (!Object)
+ return 0;
+
+ file = (WINPR_FILE*)Object;
+
+ cur = _ftelli64(file->fp);
+
+ if (cur < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return INVALID_FILE_SIZE;
+ }
+
+ if (_fseeki64(file->fp, 0, SEEK_END) != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", file->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return INVALID_FILE_SIZE;
+ }
+
+ size = _ftelli64(file->fp);
+
+ if (size < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return INVALID_FILE_SIZE;
+ }
+
+ if (_fseeki64(file->fp, cur, SEEK_SET) != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return INVALID_FILE_SIZE;
+ }
+
+ if (lpFileSizeHigh)
+ *lpFileSizeHigh = (UINT32)(size >> 32);
+
+ return (UINT32)(size & 0xFFFFFFFF);
+}
+
+static BOOL FileGetFileInformationByHandle(HANDLE hFile,
+ LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+ struct stat st;
+ UINT64 ft = 0;
+ const char* lastSep = NULL;
+
+ if (!pFile)
+ return FALSE;
+ if (!lpFileInformation)
+ return FALSE;
+
+ if (fstat(fileno(pFile->fp), &st) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "fstat failed with %s [%#08X]", errno,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ return FALSE;
+ }
+
+ lpFileInformation->dwFileAttributes = 0;
+
+ if (S_ISDIR(st.st_mode))
+ lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+
+ if (lpFileInformation->dwFileAttributes == 0)
+ lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
+
+ lastSep = strrchr(pFile->lpFileName, '/');
+
+ if (lastSep)
+ {
+ const char* name = lastSep + 1;
+ const size_t namelen = strlen(name);
+
+ if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
+ lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+ }
+
+ if (!(st.st_mode & S_IWUSR))
+ lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
+
+#ifdef _DARWIN_FEATURE_64_BIT_INODE
+ ft = STAT_TIME_TO_FILETIME(st.st_birthtime);
+#else
+ ft = STAT_TIME_TO_FILETIME(st.st_ctime);
+#endif
+ lpFileInformation->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFileInformation->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ ft = STAT_TIME_TO_FILETIME(st.st_mtime);
+ lpFileInformation->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFileInformation->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ ft = STAT_TIME_TO_FILETIME(st.st_atime);
+ lpFileInformation->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
+ lpFileInformation->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
+ lpFileInformation->nFileSizeHigh = ((UINT64)st.st_size) >> 32ULL;
+ lpFileInformation->nFileSizeLow = st.st_size & 0xFFFFFFFF;
+ lpFileInformation->dwVolumeSerialNumber = st.st_dev;
+ lpFileInformation->nNumberOfLinks = st.st_nlink;
+ lpFileInformation->nFileIndexHigh = (st.st_ino >> 4) & 0xFFFFFFFF;
+ lpFileInformation->nFileIndexLow = st.st_ino & 0xFFFFFFFF;
+ return TRUE;
+}
+
+static BOOL FileLockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved,
+ DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
+ LPOVERLAPPED lpOverlapped)
+{
+#ifdef __sun
+ struct flock lock;
+ int lckcmd;
+#else
+ int lock = 0;
+#endif
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ if (!hFile)
+ return FALSE;
+
+ if (pFile->bLocked)
+ {
+ WLog_ERR(TAG, "File %s already locked!", pFile->lpFileName);
+ return FALSE;
+ }
+
+#ifdef __sun
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_whence = SEEK_SET;
+
+ if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
+ lock.l_type = F_WRLCK;
+ else
+ lock.l_type = F_WRLCK;
+
+ if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
+ lckcmd = F_SETLK;
+ else
+ lckcmd = F_SETLKW;
+
+ if (fcntl(fileno(pFile->fp), lckcmd, &lock) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "F_SETLK failed with %s [0x%08X]",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+#else
+ if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
+ lock = LOCK_EX;
+ else
+ lock = LOCK_SH;
+
+ if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
+ lock |= LOCK_NB;
+
+ if (flock(fileno(pFile->fp), lock) < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "flock failed with %s [0x%08X]",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+#endif
+
+ pFile->bLocked = TRUE;
+
+ return TRUE;
+}
+
+static BOOL FileUnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+#ifdef __sun
+ struct flock lock;
+#endif
+
+ if (!hFile)
+ return FALSE;
+
+ if (!pFile->bLocked)
+ {
+ WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
+ return FALSE;
+ }
+
+#ifdef __sun
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_type = F_UNLCK;
+ if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+
+#else
+ if (flock(fileno(pFile->fp), LOCK_UN) < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+static BOOL FileUnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
+{
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+#ifdef __sun
+ struct flock lock;
+#endif
+
+ if (lpOverlapped)
+ {
+ WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+
+ if (!hFile)
+ return FALSE;
+
+ if (!pFile->bLocked)
+ {
+ WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
+ return FALSE;
+ }
+
+#ifdef __sun
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_type = F_UNLCK;
+ if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+#else
+ if (flock(fileno(pFile->fp), LOCK_UN) < 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+static UINT64 FileTimeToUS(const FILETIME* ft)
+{
+ const UINT64 EPOCH_DIFF_US = EPOCH_DIFF * 1000000ULL;
+ UINT64 tmp = ((UINT64)ft->dwHighDateTime) << 32 | ft->dwLowDateTime;
+ tmp /= 10; /* 100ns steps to 1us step */
+ tmp -= EPOCH_DIFF_US;
+ return tmp;
+}
+
+static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
+ const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
+{
+ int rc = 0;
+#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
+ struct stat buf;
+ /* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */
+ struct timeval timevals[2];
+#else
+ struct timespec times[2]; /* last access, last modification */
+#endif
+ WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+
+ if (!hFile)
+ return FALSE;
+
+#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
+ rc = fstat(fileno(pFile->fp), &buf);
+
+ if (rc < 0)
+ return FALSE;
+
+#endif
+
+ if (!lpLastAccessTime)
+ {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
+ timevals[0].tv_sec = buf.st_atime;
+#ifdef _POSIX_SOURCE
+ TIMESPEC_TO_TIMEVAL(&timevals[0], &buf.st_atim);
+#else
+ TIMESPEC_TO_TIMEVAL(&timevals[0], &buf.st_atimespec);
+#endif
+#elif defined(ANDROID)
+ timevals[0].tv_sec = buf.st_atime;
+ timevals[0].tv_usec = buf.st_atimensec / 1000UL;
+#else
+ times[0].tv_sec = UTIME_OMIT;
+ times[0].tv_nsec = UTIME_OMIT;
+#endif
+ }
+ else
+ {
+ UINT64 tmp = FileTimeToUS(lpLastAccessTime);
+#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
+ timevals[0].tv_sec = tmp / 1000000ULL;
+ timevals[0].tv_usec = tmp % 1000000ULL;
+#else
+ times[0].tv_sec = tmp / 1000000ULL;
+ times[0].tv_nsec = (tmp % 1000000ULL) * 1000ULL;
+#endif
+ }
+
+ if (!lpLastWriteTime)
+ {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
+ timevals[1].tv_sec = buf.st_mtime;
+#ifdef _POSIX_SOURCE
+ TIMESPEC_TO_TIMEVAL(&timevals[1], &buf.st_mtim);
+#else
+ TIMESPEC_TO_TIMEVAL(&timevals[1], &buf.st_mtimespec);
+#endif
+#elif defined(ANDROID)
+ timevals[1].tv_sec = buf.st_mtime;
+ timevals[1].tv_usec = buf.st_mtimensec / 1000UL;
+#else
+ times[1].tv_sec = UTIME_OMIT;
+ times[1].tv_nsec = UTIME_OMIT;
+#endif
+ }
+ else
+ {
+ UINT64 tmp = FileTimeToUS(lpLastWriteTime);
+#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
+ timevals[1].tv_sec = tmp / 1000000ULL;
+ timevals[1].tv_usec = tmp % 1000000ULL;
+#else
+ times[1].tv_sec = tmp / 1000000ULL;
+ times[1].tv_nsec = (tmp % 1000000ULL) * 1000ULL;
+#endif
+ }
+
+ // TODO: Creation time can not be handled!
+#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
+ rc = utimes(pFile->lpFileName, timevals);
+#else
+ rc = futimens(fileno(pFile->fp), times);
+#endif
+
+ if (rc != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static HANDLE_OPS fileOps = {
+ FileIsHandled,
+ FileCloseHandle,
+ FileGetFd,
+ NULL, /* CleanupHandle */
+ FileRead,
+ NULL, /* FileReadEx */
+ NULL, /* FileReadScatter */
+ FileWrite,
+ NULL, /* FileWriteEx */
+ NULL, /* FileWriteGather */
+ FileGetFileSize,
+ NULL, /* FlushFileBuffers */
+ FileSetEndOfFile,
+ FileSetFilePointer,
+ FileSetFilePointerEx,
+ NULL, /* FileLockFile */
+ FileLockFileEx,
+ FileUnlockFile,
+ FileUnlockFileEx,
+ FileSetFileTime,
+ FileGetFileInformationByHandle,
+};
+
+static HANDLE_OPS shmOps = {
+ FileIsHandled,
+ FileCloseHandle,
+ FileGetFd,
+ NULL, /* CleanupHandle */
+ FileRead,
+ NULL, /* FileReadEx */
+ NULL, /* FileReadScatter */
+ FileWrite,
+ NULL, /* FileWriteEx */
+ NULL, /* FileWriteGather */
+ NULL, /* FileGetFileSize */
+ NULL, /* FlushFileBuffers */
+ NULL, /* FileSetEndOfFile */
+ NULL, /* FileSetFilePointer */
+ NULL, /* SetFilePointerEx */
+ NULL, /* FileLockFile */
+ NULL, /* FileLockFileEx */
+ NULL, /* FileUnlockFile */
+ NULL, /* FileUnlockFileEx */
+ NULL, /* FileSetFileTime */
+ FileGetFileInformationByHandle,
+};
+
+static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDisposition, BOOL* create)
+{
+ BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
+
+ switch (dwCreationDisposition)
+ {
+ case CREATE_ALWAYS:
+ *create = TRUE;
+ return (writeable) ? "wb+" : "rwb";
+ case CREATE_NEW:
+ *create = TRUE;
+ return "wb+";
+ case OPEN_ALWAYS:
+ *create = TRUE;
+ return "rb+";
+ case OPEN_EXISTING:
+ *create = FALSE;
+ return (writeable) ? "rb+" : "rb";
+ case TRUNCATE_EXISTING:
+ *create = FALSE;
+ return "wb+";
+ default:
+ *create = FALSE;
+ return "";
+ }
+}
+
+UINT32 map_posix_err(int fs_errno)
+{
+ NTSTATUS rc = 0;
+
+ /* try to return NTSTATUS version of error code */
+
+ switch (fs_errno)
+ {
+ case 0:
+ rc = STATUS_SUCCESS;
+ break;
+
+ case ENOTCONN:
+ case ENODEV:
+ case ENOTDIR:
+ case ENXIO:
+ rc = ERROR_FILE_NOT_FOUND;
+ break;
+
+ case EROFS:
+ case EPERM:
+ case EACCES:
+ rc = ERROR_ACCESS_DENIED;
+ break;
+
+ case ENOENT:
+ rc = ERROR_FILE_NOT_FOUND;
+ break;
+
+ case EBUSY:
+ rc = ERROR_BUSY_DRIVE;
+ break;
+
+ case EEXIST:
+ rc = ERROR_FILE_EXISTS;
+ break;
+
+ case EISDIR:
+ rc = STATUS_FILE_IS_A_DIRECTORY;
+ break;
+
+ case ENOTEMPTY:
+ rc = STATUS_DIRECTORY_NOT_EMPTY;
+ break;
+
+ case EMFILE:
+ rc = STATUS_TOO_MANY_OPENED_FILES;
+ break;
+
+ default:
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "Missing ERRNO mapping %s [%d]",
+ winpr_strerror(fs_errno, ebuffer, sizeof(ebuffer)), fs_errno);
+ rc = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+
+ return (UINT32)rc;
+}
+
+static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile)
+{
+ WINPR_FILE* pFile = NULL;
+ BOOL create = 0;
+ const char* mode = FileGetMode(dwDesiredAccess, dwCreationDisposition, &create);
+#ifdef __sun
+ struct flock lock;
+#else
+ int lock = 0;
+#endif
+ FILE* fp = NULL;
+ struct stat st;
+
+ if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
+ {
+ WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
+ if (!pFile)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
+ pFile->common.ops = &fileOps;
+
+ pFile->lpFileName = _strdup(lpFileName);
+ if (!pFile->lpFileName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pFile->dwOpenMode = dwDesiredAccess;
+ pFile->dwShareMode = dwShareMode;
+ pFile->dwFlagsAndAttributes = dwFlagsAndAttributes;
+ pFile->lpSecurityAttributes = lpSecurityAttributes;
+ pFile->dwCreationDisposition = dwCreationDisposition;
+ pFile->hTemplateFile = hTemplateFile;
+
+ if (create)
+ {
+ if (dwCreationDisposition == CREATE_NEW)
+ {
+ if (stat(pFile->lpFileName, &st) == 0)
+ {
+ SetLastError(ERROR_FILE_EXISTS);
+ free(pFile->lpFileName);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+
+ fp = winpr_fopen(pFile->lpFileName, "ab");
+ if (!fp)
+ {
+ SetLastError(map_posix_err(errno));
+ free(pFile->lpFileName);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ fp = freopen(pFile->lpFileName, mode, fp);
+ }
+ else
+ {
+ if (stat(pFile->lpFileName, &st) != 0)
+ {
+ SetLastError(map_posix_err(errno));
+ free(pFile->lpFileName);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* FIFO (named pipe) would block the following fopen
+ * call if not connected. This renders the channel unusable,
+ * therefore abort early. */
+ if (S_ISFIFO(st.st_mode))
+ {
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ free(pFile->lpFileName);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+
+ if (NULL == fp)
+ fp = winpr_fopen(pFile->lpFileName, mode);
+
+ pFile->fp = fp;
+ if (!pFile->fp)
+ {
+ /* This case can occur when trying to open a
+ * not existing file without create flag. */
+ SetLastError(map_posix_err(errno));
+ free(pFile->lpFileName);
+ free(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ setvbuf(fp, NULL, _IONBF, 0);
+
+#ifdef __sun
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_whence = SEEK_SET;
+
+ if (dwShareMode & FILE_SHARE_READ)
+ lock.l_type = F_RDLCK;
+ if (dwShareMode & FILE_SHARE_WRITE)
+ lock.l_type = F_RDLCK;
+#else
+ if (dwShareMode & FILE_SHARE_READ)
+ lock = LOCK_SH;
+ if (dwShareMode & FILE_SHARE_WRITE)
+ lock = LOCK_EX;
+#endif
+
+ if (dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE))
+ {
+#ifdef __sun
+ if (fcntl(fileno(pFile->fp), F_SETLKW, &lock) == -1)
+#else
+ if (flock(fileno(pFile->fp), lock) < 0)
+#endif
+ {
+ char ebuffer[256] = { 0 };
+#ifdef __sun
+ WLog_ERR(TAG, "F_SETLKW failed with %s [0x%08X]",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+#else
+ WLog_ERR(TAG, "flock failed with %s [0x%08X]",
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
+#endif
+
+ SetLastError(map_posix_err(errno));
+ FileCloseHandle(pFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pFile->bLocked = TRUE;
+ }
+
+ if (fstat(fileno(pFile->fp), &st) == 0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
+ {
+ st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ fchmod(fileno(pFile->fp), st.st_mode);
+ }
+
+ SetLastError(STATUS_SUCCESS);
+ return pFile;
+}
+
+static BOOL IsFileDevice(LPCTSTR lpDeviceName)
+{
+ return TRUE;
+}
+
+static HANDLE_CREATOR _FileHandleCreator = { IsFileDevice, FileCreateFileA };
+
+HANDLE_CREATOR* GetFileHandleCreator(void)
+{
+ return &_FileHandleCreator;
+}
+
+static WINPR_FILE* FileHandle_New(FILE* fp)
+{
+ WINPR_FILE* pFile = NULL;
+ char name[MAX_PATH] = { 0 };
+
+ _snprintf(name, sizeof(name), "device_%d", fileno(fp));
+ pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
+ if (!pFile)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+ pFile->fp = fp;
+ pFile->common.ops = &shmOps;
+ pFile->lpFileName = _strdup(name);
+
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
+ return pFile;
+}
+
+HANDLE GetStdHandle(DWORD nStdHandle)
+{
+ FILE* fp = NULL;
+ WINPR_FILE* pFile = NULL;
+
+ switch (nStdHandle)
+ {
+ case STD_INPUT_HANDLE:
+ fp = stdin;
+ break;
+ case STD_OUTPUT_HANDLE:
+ fp = stdout;
+ break;
+ case STD_ERROR_HANDLE:
+ fp = stderr;
+ break;
+ default:
+ return INVALID_HANDLE_VALUE;
+ }
+ pFile = FileHandle_New(fp);
+ if (!pFile)
+ return INVALID_HANDLE_VALUE;
+
+ return (HANDLE)pFile;
+}
+
+BOOL SetStdHandle(DWORD nStdHandle, HANDLE hHandle)
+{
+ return FALSE;
+}
+
+BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOldHandle)
+{
+ return FALSE;
+}
+
+BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
+ LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
+{
+#if defined(ANDROID)
+#define STATVFS statfs
+#else
+#define STATVFS statvfs
+#endif
+
+ struct STATVFS svfst = { 0 };
+ STATVFS(lpRootPathName, &svfst);
+ *lpSectorsPerCluster = (UINT32)MIN(svfst.f_frsize, UINT32_MAX);
+ *lpBytesPerSector = 1;
+ *lpNumberOfFreeClusters = (UINT32)MIN(svfst.f_bavail, UINT32_MAX);
+ *lpTotalNumberOfClusters = (UINT32)MIN(svfst.f_blocks, UINT32_MAX);
+ return TRUE;
+}
+
+BOOL GetDiskFreeSpaceW(LPCWSTR lpwRootPathName, LPDWORD lpSectorsPerCluster,
+ LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
+ LPDWORD lpTotalNumberOfClusters)
+{
+ LPSTR lpRootPathName = NULL;
+ BOOL ret = 0;
+ if (!lpwRootPathName)
+ return FALSE;
+
+ lpRootPathName = ConvertWCharToUtf8Alloc(lpwRootPathName, NULL);
+ if (!lpRootPathName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ ret = GetDiskFreeSpaceA(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector,
+ lpNumberOfFreeClusters, lpTotalNumberOfClusters);
+ free(lpRootPathName);
+ return ret;
+}
+
+#endif /* _WIN32 */
+
+/**
+ * Check if a file name component is valid.
+ *
+ * Some file names are not valid on Windows. See "Naming Files, Paths, and Namespaces":
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ */
+BOOL ValidFileNameComponent(LPCWSTR lpFileName)
+{
+ if (!lpFileName)
+ return FALSE;
+
+ /* CON */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
+ (lpFileName[3] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* PRN */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'P' || lpFileName[0] == L'p')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'R' || lpFileName[1] == L'r')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
+ (lpFileName[3] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* AUX */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'A' || lpFileName[0] == L'a')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'X' || lpFileName[2] == L'x')) &&
+ (lpFileName[3] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* NUL */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'N' || lpFileName[0] == L'n')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'L' || lpFileName[2] == L'l')) &&
+ (lpFileName[3] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* LPT0-9 */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'L' || lpFileName[0] == L'l')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'P' || lpFileName[1] == L'p')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'T' || lpFileName[2] == L't')) &&
+ (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
+ (lpFileName[4] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* COM0-9 */
+ if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
+ (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
+ (lpFileName[2] != L'\0' && (lpFileName[2] == L'M' || lpFileName[2] == L'm')) &&
+ (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
+ (lpFileName[4] == L'\0'))
+ {
+ return FALSE;
+ }
+
+ /* Reserved characters */
+ for (LPCWSTR c = lpFileName; *c; c++)
+ {
+ if ((*c == L'<') || (*c == L'>') || (*c == L':') || (*c == L'"') || (*c == L'/') ||
+ (*c == L'\\') || (*c == L'|') || (*c == L'?') || (*c == L'*'))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#ifdef _UWP
+
+HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ HANDLE hFile;
+ CREATEFILE2_EXTENDED_PARAMETERS params = { 0 };
+
+ params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+
+ if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
+ params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS;
+ if (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE)
+ params.dwFileFlags |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING)
+ params.dwFileFlags |= FILE_FLAG_NO_BUFFERING;
+ if (dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL)
+ params.dwFileFlags |= FILE_FLAG_OPEN_NO_RECALL;
+ if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT)
+ params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REQUIRING_OPLOCK)
+ params.dwFileFlags |= FILE_FLAG_OPEN_REQUIRING_OPLOCK;
+ if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
+ params.dwFileFlags |= FILE_FLAG_OVERLAPPED;
+ if (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS)
+ params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS;
+ if (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS)
+ params.dwFileFlags |= FILE_FLAG_RANDOM_ACCESS;
+ if (dwFlagsAndAttributes & FILE_FLAG_SESSION_AWARE)
+ params.dwFileFlags |= FILE_FLAG_SESSION_AWARE;
+ if (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN)
+ params.dwFileFlags |= FILE_FLAG_SEQUENTIAL_SCAN;
+ if (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH)
+ params.dwFileFlags |= FILE_FLAG_WRITE_THROUGH;
+
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ARCHIVE)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_COMPRESSED)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DEVICE)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_DEVICE;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ENCRYPTED)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_HIDDEN)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_INTEGRITY_STREAM;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NORMAL)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_NO_SCRUB_DATA;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_OFFLINE)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_SPARSE_FILE;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SYSTEM)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_TEMPORARY)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
+ if (dwFlagsAndAttributes & FILE_ATTRIBUTE_VIRTUAL)
+ params.dwFileAttributes |= FILE_ATTRIBUTE_VIRTUAL;
+
+ if (dwFlagsAndAttributes & SECURITY_ANONYMOUS)
+ params.dwSecurityQosFlags |= SECURITY_ANONYMOUS;
+ if (dwFlagsAndAttributes & SECURITY_CONTEXT_TRACKING)
+ params.dwSecurityQosFlags |= SECURITY_CONTEXT_TRACKING;
+ if (dwFlagsAndAttributes & SECURITY_DELEGATION)
+ params.dwSecurityQosFlags |= SECURITY_DELEGATION;
+ if (dwFlagsAndAttributes & SECURITY_EFFECTIVE_ONLY)
+ params.dwSecurityQosFlags |= SECURITY_EFFECTIVE_ONLY;
+ if (dwFlagsAndAttributes & SECURITY_IDENTIFICATION)
+ params.dwSecurityQosFlags |= SECURITY_IDENTIFICATION;
+ if (dwFlagsAndAttributes & SECURITY_IMPERSONATION)
+ params.dwSecurityQosFlags |= SECURITY_IMPERSONATION;
+
+ params.lpSecurityAttributes = lpSecurityAttributes;
+ params.hTemplateFile = hTemplateFile;
+
+ hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, &params);
+
+ return hFile;
+}
+
+HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ HANDLE hFile;
+ if (!lpFileName)
+ return NULL;
+
+ WCHAR* lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
+
+ if (!lpFileNameW)
+ return NULL;
+
+ hFile = CreateFileW(lpFileNameW, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
+ dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+
+ free(lpFileNameW);
+
+ return hFile;
+}
+
+DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
+{
+ BOOL status;
+ LARGE_INTEGER fileSize = { 0, 0 };
+
+ if (!lpFileSizeHigh)
+ return INVALID_FILE_SIZE;
+
+ status = GetFileSizeEx(hFile, &fileSize);
+
+ if (!status)
+ return INVALID_FILE_SIZE;
+
+ *lpFileSizeHigh = fileSize.HighPart;
+
+ return fileSize.LowPart;
+}
+
+DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod)
+{
+ BOOL status;
+ LARGE_INTEGER liDistanceToMove = { 0, 0 };
+ LARGE_INTEGER liNewFilePointer = { 0, 0 };
+
+ liDistanceToMove.LowPart = lDistanceToMove;
+
+ status = SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, dwMoveMethod);
+
+ if (!status)
+ return INVALID_SET_FILE_POINTER;
+
+ if (lpDistanceToMoveHigh)
+ *lpDistanceToMoveHigh = liNewFilePointer.HighPart;
+
+ return liNewFilePointer.LowPart;
+}
+
+HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
+{
+ return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
+ NULL, 0);
+}
+
+HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
+{
+ return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
+ NULL, 0);
+}
+
+DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart)
+{
+ DWORD dwStatus;
+ WCHAR* lpFileNameW = NULL;
+ WCHAR* lpBufferW = NULL;
+ WCHAR* lpFilePartW = NULL;
+ DWORD nBufferLengthW = nBufferLength * sizeof(WCHAR);
+
+ if (!lpFileName || (nBufferLength < 1))
+ return 0;
+
+ lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
+ if (!lpFileNameW)
+ return 0;
+
+ lpBufferW = (WCHAR*)malloc(nBufferLengthW);
+
+ if (!lpBufferW)
+ return 0;
+
+ dwStatus = GetFullPathNameW(lpFileNameW, nBufferLengthW, lpBufferW, &lpFilePartW);
+
+ ConvertWCharNToUtf8(lpBufferW, nBufferLengthW / sizeof(WCHAR), lpBuffer, nBufferLength);
+
+ if (lpFilePart)
+ lpFilePart = lpBuffer + (lpFilePartW - lpBufferW);
+
+ free(lpFileNameW);
+ free(lpBufferW);
+
+ return dwStatus * 2;
+}
+
+BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
+ LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
+{
+ BOOL status;
+ ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
+ ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
+ ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
+
+ status = GetDiskFreeSpaceExA(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
+ &TotalNumberOfFreeBytes);
+
+ if (!status)
+ return FALSE;
+
+ *lpBytesPerSector = 1;
+ *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
+ *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
+ *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
+
+ return TRUE;
+}
+
+BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
+ LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
+ LPDWORD lpTotalNumberOfClusters)
+{
+ BOOL status;
+ ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
+ ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
+ ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
+
+ status = GetDiskFreeSpaceExW(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
+ &TotalNumberOfFreeBytes);
+
+ if (!status)
+ return FALSE;
+
+ *lpBytesPerSector = 1;
+ *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
+ *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
+ *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
+
+ return TRUE;
+}
+
+DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer)
+{
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return 0;
+}
+
+DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer)
+{
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return 0;
+}
+
+BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
+{
+ return FALSE;
+}
+
+UINT GetACP(void)
+{
+ return CP_UTF8;
+}
+
+#endif
+
+/* Extended API */
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+HANDLE GetFileHandleForFileDescriptor(int fd)
+{
+#ifdef _WIN32
+ return (HANDLE)_get_osfhandle(fd);
+#else /* _WIN32 */
+ WINPR_FILE* pFile = NULL;
+ FILE* fp = NULL;
+ int flags = 0;
+
+ /* Make sure it's a valid fd */
+ if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
+ return INVALID_HANDLE_VALUE;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return INVALID_HANDLE_VALUE;
+
+ if (flags & O_WRONLY)
+ fp = fdopen(fd, "wb");
+ else
+ fp = fdopen(fd, "rb");
+
+ if (!fp)
+ return INVALID_HANDLE_VALUE;
+
+ setvbuf(fp, NULL, _IONBF, 0);
+
+ pFile = FileHandle_New(fp);
+ if (!pFile)
+ return INVALID_HANDLE_VALUE;
+
+ return (HANDLE)pFile;
+#endif /* _WIN32 */
+}
+
+FILE* winpr_fopen(const char* path, const char* mode)
+{
+#ifndef _WIN32
+ return fopen(path, mode);
+#else
+ LPWSTR lpPathW = NULL;
+ LPWSTR lpModeW = NULL;
+ FILE* result = NULL;
+
+ if (!path || !mode)
+ return NULL;
+
+ lpPathW = ConvertUtf8ToWCharAlloc(path, NULL);
+ if (!lpPathW)
+ goto cleanup;
+
+ lpModeW = ConvertUtf8ToWCharAlloc(mode, NULL);
+ if (!lpModeW)
+ goto cleanup;
+
+ result = _wfopen(lpPathW, lpModeW);
+
+cleanup:
+ free(lpPathW);
+ free(lpModeW);
+ return result;
+#endif
+}
diff --git a/winpr/libwinpr/file/file.h b/winpr/libwinpr/file/file.h
new file mode 100644
index 0000000..b8be851
--- /dev/null
+++ b/winpr/libwinpr/file/file.h
@@ -0,0 +1,66 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2015 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * 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.
+ */
+
+#ifndef WINPR_FILE_PRIV_H
+#define WINPR_FILE_PRIV_H
+
+#include <winpr/winpr.h>
+#include <winpr/wtypes.h>
+
+#include <winpr/nt.h>
+#include <winpr/io.h>
+#include <winpr/error.h>
+
+#ifndef _WIN32
+
+#include <stdio.h>
+#include "../handle/handle.h"
+
+#define EPOCH_DIFF 11644473600LL
+#define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL)
+
+struct winpr_file
+{
+ WINPR_HANDLE common;
+
+ FILE* fp;
+
+ char* lpFileName;
+
+ DWORD dwOpenMode;
+ DWORD dwShareMode;
+ DWORD dwFlagsAndAttributes;
+
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes;
+ DWORD dwCreationDisposition;
+ HANDLE hTemplateFile;
+
+ BOOL bLocked;
+};
+typedef struct winpr_file WINPR_FILE;
+
+HANDLE_CREATOR* GetFileHandleCreator(void);
+
+UINT32 map_posix_err(int fs_errno);
+
+#endif /* _WIN32 */
+
+#endif /* WINPR_FILE_PRIV_H */
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
+}
diff --git a/winpr/libwinpr/file/namedPipeClient.c b/winpr/libwinpr/file/namedPipeClient.c
new file mode 100644
index 0000000..fbfeb35
--- /dev/null
+++ b/winpr/libwinpr/file/namedPipeClient.c
@@ -0,0 +1,305 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2014 Hewlett-Packard Development Company, L.P.
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 bernhard.miklautz@thincast.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/path.h>
+#include <winpr/file.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("file")
+
+#ifndef _WIN32
+
+#ifdef ANDROID
+#include <sys/vfs.h>
+#else
+#include <sys/statvfs.h>
+#endif
+
+#include "../handle/handle.h"
+
+#include "../pipe/pipe.h"
+
+static HANDLE_CREATOR _NamedPipeClientHandleCreator;
+
+static BOOL NamedPipeClientIsHandled(HANDLE handle)
+{
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE);
+}
+
+static BOOL NamedPipeClientCloseHandle(HANDLE handle)
+{
+ WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)handle;
+
+ if (!NamedPipeClientIsHandled(handle))
+ return FALSE;
+
+ if (pNamedPipe->clientfd != -1)
+ {
+ // WLOG_DBG(TAG, "closing clientfd %d", pNamedPipe->clientfd);
+ close(pNamedPipe->clientfd);
+ }
+
+ if (pNamedPipe->serverfd != -1)
+ {
+ // WLOG_DBG(TAG, "closing serverfd %d", pNamedPipe->serverfd);
+ close(pNamedPipe->serverfd);
+ }
+
+ if (pNamedPipe->pfnUnrefNamedPipe)
+ pNamedPipe->pfnUnrefNamedPipe(pNamedPipe);
+
+ free(pNamedPipe->lpFileName);
+ free(pNamedPipe->lpFilePath);
+ free(pNamedPipe->name);
+ free(pNamedPipe);
+ return TRUE;
+}
+
+static int NamedPipeClientGetFd(HANDLE handle)
+{
+ WINPR_NAMED_PIPE* file = (WINPR_NAMED_PIPE*)handle;
+
+ if (!NamedPipeClientIsHandled(handle))
+ return -1;
+
+ if (file->ServerMode)
+ return file->serverfd;
+ else
+ return file->clientfd;
+}
+
+static HANDLE_OPS ops = {
+ NamedPipeClientIsHandled,
+ NamedPipeClientCloseHandle,
+ NamedPipeClientGetFd,
+ NULL, /* CleanupHandle */
+ NamedPipeRead,
+ NULL, /* FileReadEx */
+ NULL, /* FileReadScatter */
+ NamedPipeWrite,
+ NULL, /* FileWriteEx */
+ NULL, /* FileWriteGather */
+ NULL, /* FileGetFileSize */
+ NULL, /* FlushFileBuffers */
+ NULL, /* FileSetEndOfFile */
+ NULL, /* FileSetFilePointer */
+ NULL, /* SetFilePointerEx */
+ NULL, /* FileLockFile */
+ NULL, /* FileLockFileEx */
+ NULL, /* FileUnlockFile */
+ NULL, /* FileUnlockFileEx */
+ NULL, /* SetFileTime */
+ NULL, /* FileGetFileInformationByHandle */
+};
+
+static HANDLE NamedPipeClientCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile)
+{
+ char* name = NULL;
+ int status = 0;
+ HANDLE hNamedPipe = NULL;
+ struct sockaddr_un s = { 0 };
+ WINPR_NAMED_PIPE* pNamedPipe = NULL;
+
+ if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
+ {
+ WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (!lpFileName)
+ return INVALID_HANDLE_VALUE;
+
+ if (!IsNamedPipeFileNameA(lpFileName))
+ return INVALID_HANDLE_VALUE;
+
+ name = GetNamedPipeNameWithoutPrefixA(lpFileName);
+
+ if (!name)
+ return INVALID_HANDLE_VALUE;
+
+ free(name);
+ pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE));
+
+ if (!pNamedPipe)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ hNamedPipe = (HANDLE)pNamedPipe;
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ);
+ pNamedPipe->name = _strdup(lpFileName);
+
+ if (!pNamedPipe->name)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ free(pNamedPipe);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pNamedPipe->dwOpenMode = 0;
+ pNamedPipe->dwPipeMode = 0;
+ pNamedPipe->nMaxInstances = 0;
+ pNamedPipe->nOutBufferSize = 0;
+ pNamedPipe->nInBufferSize = 0;
+ pNamedPipe->nDefaultTimeOut = 0;
+ pNamedPipe->dwFlagsAndAttributes = dwFlagsAndAttributes;
+ pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpFileName);
+
+ if (!pNamedPipe->lpFileName)
+ {
+ free((void*)pNamedPipe->name);
+ free(pNamedPipe);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpFileName);
+
+ if (!pNamedPipe->lpFilePath)
+ {
+ free((void*)pNamedPipe->lpFileName);
+ free((void*)pNamedPipe->name);
+ free(pNamedPipe);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pNamedPipe->clientfd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ pNamedPipe->serverfd = -1;
+ pNamedPipe->ServerMode = FALSE;
+ s.sun_family = AF_UNIX;
+ sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath);
+ status = connect(pNamedPipe->clientfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un));
+ pNamedPipe->common.ops = &ops;
+
+ if (status != 0)
+ {
+ close(pNamedPipe->clientfd);
+ free((char*)pNamedPipe->name);
+ free((char*)pNamedPipe->lpFileName);
+ free((char*)pNamedPipe->lpFilePath);
+ free(pNamedPipe);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
+ {
+#if 0
+ int flags = fcntl(pNamedPipe->clientfd, F_GETFL);
+
+ if (flags != -1)
+ fcntl(pNamedPipe->clientfd, F_SETFL, flags | O_NONBLOCK);
+
+#endif
+ }
+
+ return hNamedPipe;
+}
+
+extern HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void);
+HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void)
+{
+ _NamedPipeClientHandleCreator.IsHandled = IsNamedPipeFileNameA;
+ _NamedPipeClientHandleCreator.CreateFileA = NamedPipeClientCreateFileA;
+ return &_NamedPipeClientHandleCreator;
+}
+
+#endif
+
+/* Extended API */
+
+#define NAMED_PIPE_PREFIX_PATH "\\\\.\\pipe\\"
+
+BOOL IsNamedPipeFileNameA(LPCSTR lpName)
+{
+ if (strncmp(lpName, NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH) - 1) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName)
+{
+ char* lpFileName = NULL;
+
+ if (!lpName)
+ return NULL;
+
+ if (!IsNamedPipeFileNameA(lpName))
+ return NULL;
+
+ lpFileName = _strdup(&lpName[strnlen(NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH))]);
+ return lpFileName;
+}
+
+char* GetNamedPipeUnixDomainSocketBaseFilePathA(void)
+{
+ char* lpTempPath = NULL;
+ char* lpPipePath = NULL;
+ lpTempPath = GetKnownPath(KNOWN_PATH_TEMP);
+
+ if (!lpTempPath)
+ return NULL;
+
+ lpPipePath = GetCombinedPath(lpTempPath, ".pipe");
+ free(lpTempPath);
+ return lpPipePath;
+}
+
+char* GetNamedPipeUnixDomainSocketFilePathA(LPCSTR lpName)
+{
+ char* lpPipePath = NULL;
+ char* lpFileName = NULL;
+ char* lpFilePath = NULL;
+ lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA();
+ lpFileName = GetNamedPipeNameWithoutPrefixA(lpName);
+ lpFilePath = GetCombinedPath(lpPipePath, (char*)lpFileName);
+ free(lpPipePath);
+ free(lpFileName);
+ return lpFilePath;
+}
+
+int GetNamePipeFileDescriptor(HANDLE hNamedPipe)
+{
+#ifndef _WIN32
+ int fd = 0;
+ WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
+
+ if (!NamedPipeClientIsHandled(hNamedPipe))
+ return -1;
+
+ fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd;
+ return fd;
+#else
+ return -1;
+#endif
+}
diff --git a/winpr/libwinpr/file/pattern.c b/winpr/libwinpr/file/pattern.c
new file mode 100644
index 0000000..33adf9f
--- /dev/null
+++ b/winpr/libwinpr/file/pattern.c
@@ -0,0 +1,371 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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/handle.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")
+
+/**
+ * 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
+ */
+
+LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags)
+{
+ LPSTR lpWildcard = NULL;
+ *pFlags = 0;
+ lpWildcard = strpbrk(lpPattern, "*?~");
+
+ if (lpWildcard)
+ {
+ if (*lpWildcard == '*')
+ {
+ *pFlags = WILDCARD_STAR;
+ return lpWildcard;
+ }
+ else if (*lpWildcard == '?')
+ {
+ *pFlags = WILDCARD_QM;
+ return lpWildcard;
+ }
+ else if (*lpWildcard == '~')
+ {
+ if (lpWildcard[1] == '*')
+ {
+ *pFlags = WILDCARD_DOS_STAR;
+ return lpWildcard;
+ }
+ else if (lpWildcard[1] == '?')
+ {
+ *pFlags = WILDCARD_DOS_QM;
+ return lpWildcard;
+ }
+ else if (lpWildcard[1] == '.')
+ {
+ *pFlags = WILDCARD_DOS_DOT;
+ return lpWildcard;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX,
+ size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard,
+ LPCSTR* ppMatchEnd)
+{
+ LPCSTR lpMatch = NULL;
+
+ if (!lpFileName)
+ return FALSE;
+
+ if (*lpWildcard == '*')
+ {
+ /*
+ * S
+ * <-----<
+ * X | | e Y
+ * X * Y == (0)----->-(1)->-----(2)-----(3)
+ */
+
+ /*
+ * State 0: match 'X'
+ */
+ if (_strnicmp(lpFileName, lpX, cchX) != 0)
+ return FALSE;
+
+ /*
+ * State 1: match 'S' or 'e'
+ *
+ * We use 'e' to transition to state 2
+ */
+
+ /**
+ * State 2: match Y
+ */
+
+ if (cchY != 0)
+ {
+ /* TODO: case insensitive character search */
+ lpMatch = strchr(&lpFileName[cchX], *lpY);
+
+ if (!lpMatch)
+ return FALSE;
+
+ if (_strnicmp(lpMatch, lpY, cchY) != 0)
+ return FALSE;
+ }
+ else
+ {
+ lpMatch = &lpFileName[cchFileName];
+ }
+
+ /**
+ * State 3: final state
+ */
+ *ppMatchEnd = &lpMatch[cchY];
+ return TRUE;
+ }
+ else if (*lpWildcard == '?')
+ {
+ /**
+ * X S Y
+ * X ? Y == (0)---(1)---(2)---(3)
+ */
+
+ /*
+ * State 0: match 'X'
+ */
+ if (cchFileName < cchX)
+ return FALSE;
+
+ if (_strnicmp(lpFileName, lpX, cchX) != 0)
+ return FALSE;
+
+ /*
+ * State 1: match 'S'
+ */
+
+ /**
+ * State 2: match Y
+ */
+
+ if (cchY != 0)
+ {
+ /* TODO: case insensitive character search */
+ lpMatch = strchr(&lpFileName[cchX + 1], *lpY);
+
+ if (!lpMatch)
+ return FALSE;
+
+ if (_strnicmp(lpMatch, lpY, cchY) != 0)
+ return FALSE;
+ }
+ else
+ {
+ if ((cchX + 1) > cchFileName)
+ return FALSE;
+
+ lpMatch = &lpFileName[cchX + 1];
+ }
+
+ /**
+ * State 3: final state
+ */
+ *ppMatchEnd = &lpMatch[cchY];
+ return TRUE;
+ }
+ else if (*lpWildcard == '~')
+ {
+ WLog_ERR(TAG, "warning: unimplemented '~' pattern match");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern)
+{
+ BOOL match = 0;
+ LPCSTR lpTail = NULL;
+ size_t cchTail = 0;
+ size_t cchPattern = 0;
+ size_t cchFileName = 0;
+ DWORD dwFlags = 0;
+ DWORD dwNextFlags = 0;
+ LPSTR lpWildcard = NULL;
+ LPSTR lpNextWildcard = NULL;
+
+ /**
+ * Wild Card Matching
+ *
+ * '*' matches 0 or more characters
+ * '?' matches exactly one character
+ *
+ * '~*' DOS_STAR - matches 0 or more characters until encountering and matching final '.'
+ *
+ * '~?' DOS_QM - matches any single character, or upon encountering a period or end of name
+ * string, advances the expresssion to the end of the set of contiguous DOS_QMs.
+ *
+ * '~.' DOS_DOT - matches either a '.' or zero characters beyond name string.
+ */
+
+ if (!lpPattern)
+ return FALSE;
+
+ if (!lpFileName)
+ return FALSE;
+
+ cchPattern = strlen(lpPattern);
+ cchFileName = strlen(lpFileName);
+
+ /**
+ * First and foremost the file system starts off name matching with the expression “*”.
+ * If the expression contains a single wild card character ‘*’ all matches are satisfied
+ * immediately. This is the most common wild card character used in Windows and expression
+ * evaluation is optimized by looking for this character first.
+ */
+
+ if ((lpPattern[0] == '*') && (cchPattern == 1))
+ return TRUE;
+
+ /**
+ * Subsequently evaluation of the “*X” expression is performed. This is a case where
+ * the expression starts off with a wild card character and contains some non-wild card
+ * characters towards the tail end of the name. This is evaluated by making sure the
+ * expression starts off with the character ‘*’ and does not contain any wildcards in
+ * the latter part of the expression. The tail part of the expression beyond the first
+ * character ‘*’ is matched against the file name at the end uppercasing each character
+ * if necessary during the comparison.
+ */
+
+ if (lpPattern[0] == '*')
+ {
+ lpTail = &lpPattern[1];
+ cchTail = strlen(lpTail);
+
+ if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
+ {
+ /* tail contains no wildcards */
+ if (cchFileName < cchTail)
+ return FALSE;
+
+ if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
+ return TRUE;
+
+ return FALSE;
+ }
+ }
+
+ /**
+ * The remaining expressions are evaluated in a non deterministic
+ * finite order as listed below, where:
+ *
+ * 'S' is any single character
+ * 'S-.' is any single character except the final '.'
+ * 'e' is a null character transition
+ * 'EOF' is the end of the name string
+ *
+ * S
+ * <-----<
+ * X | | e Y
+ * X * Y == (0)----->-(1)->-----(2)-----(3)
+ *
+ *
+ * S-.
+ * <-----<
+ * X | | e Y
+ * X ~* Y == (0)----->-(1)->-----(2)-----(3)
+ *
+ *
+ * X S S Y
+ * X ?? Y == (0)---(1)---(2)---(3)---(4)
+ *
+ *
+ * X S-. S-. Y
+ * X ~?~? == (0)---(1)-----(2)-----(3)---(4)
+ * | |_______|
+ * | ^ |
+ * |_______________|
+ * ^EOF of .^
+ *
+ */
+ lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
+
+ if (lpWildcard)
+ {
+ LPCSTR lpX = NULL;
+ LPCSTR lpY = NULL;
+ size_t cchX = 0;
+ size_t cchY = 0;
+ LPCSTR lpMatchEnd = NULL;
+ LPCSTR lpSubPattern = NULL;
+ size_t cchSubPattern = 0;
+ LPCSTR lpSubFileName = NULL;
+ size_t cchSubFileName = 0;
+ size_t cchWildcard = 0;
+ size_t cchNextWildcard = 0;
+ cchSubPattern = cchPattern;
+ lpSubPattern = lpPattern;
+ cchSubFileName = cchFileName;
+ lpSubFileName = lpFileName;
+ cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
+ lpNextWildcard = FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
+
+ if (!lpNextWildcard)
+ {
+ lpX = lpSubPattern;
+ cchX = (lpWildcard - lpSubPattern);
+ lpY = &lpSubPattern[cchX + cchWildcard];
+ cchY = (cchSubPattern - (lpY - lpSubPattern));
+ match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
+ cchY, lpWildcard, &lpMatchEnd);
+ return match;
+ }
+ else
+ {
+ while (lpNextWildcard)
+ {
+ cchSubFileName = cchFileName - (lpSubFileName - lpFileName);
+ cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
+ lpX = lpSubPattern;
+ cchX = (lpWildcard - lpSubPattern);
+ lpY = &lpSubPattern[cchX + cchWildcard];
+ cchY = (lpNextWildcard - lpWildcard) - cchWildcard;
+ match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
+ lpY, cchY, lpWildcard, &lpMatchEnd);
+
+ if (!match)
+ return FALSE;
+
+ lpSubFileName = lpMatchEnd;
+ cchWildcard = cchNextWildcard;
+ lpWildcard = lpNextWildcard;
+ dwFlags = dwNextFlags;
+ lpNextWildcard =
+ FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
+ }
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ /* no wildcard characters */
+ if (_stricmp(lpFileName, lpPattern) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/winpr/libwinpr/file/test/CMakeLists.txt b/winpr/libwinpr/file/test/CMakeLists.txt
new file mode 100644
index 0000000..3999271
--- /dev/null
+++ b/winpr/libwinpr/file/test/CMakeLists.txt
@@ -0,0 +1,58 @@
+
+if (NOT WIN32)
+ set(MODULE_NAME "TestFile")
+ set(MODULE_PREFIX "TEST_FILE")
+
+ set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+ set(${MODULE_PREFIX}_TESTS
+ TestFileCreateFile.c
+ TestFileDeleteFile.c
+ TestFileReadFile.c
+ TestSetFileAttributes.c
+ TestFileWriteFile.c
+ TestFilePatternMatch.c
+ TestFileFindFirstFile.c
+ TestFileFindFirstFileEx.c
+ TestFileFindNextFile.c
+ TestFileGetStdHandle.c
+ )
+
+ create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+ target_link_libraries(${MODULE_NAME} winpr)
+
+ set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+ if(NOT MSVC)
+ set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area")
+ else()
+ set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area")
+ endif()
+
+ file(MAKE_DIRECTORY "${TEST_AREA}")
+ file(WRITE "${TEST_AREA}/TestFile1" "TestFile1")
+ file(WRITE "${TEST_AREA}/TestFile2" "TestFile2")
+ file(WRITE "${TEST_AREA}/TestFile3" "TestFile3")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1")
+ file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2")
+ file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1")
+ file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2")
+ file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2")
+ file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3")
+
+ foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA})
+ endforeach()
+
+ set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
+
+endif()
diff --git a/winpr/libwinpr/file/test/TestFileCreateFile.c b/winpr/libwinpr/file/test/TestFileCreateFile.c
new file mode 100644
index 0000000..a939416
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileCreateFile.c
@@ -0,0 +1,94 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/handle.h>
+#include <winpr/windows.h>
+#include <winpr/sysinfo.h>
+
+int TestFileCreateFile(int argc, char* argv[])
+{
+ HANDLE handle = NULL;
+ HRESULT hr = 0;
+ DWORD written = 0;
+ const char buffer[] = "Some random text\r\njust want it done.";
+ char cmp[sizeof(buffer)];
+ char sname[8192];
+ LPSTR name = NULL;
+ int rc = 0;
+ SYSTEMTIME systemTime;
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+ GetSystemTime(&systemTime);
+ sprintf_s(sname, sizeof(sname),
+ "CreateFile-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16
+ "%02" PRIu16 "%04" PRIu16,
+ systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
+ systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
+ name = GetKnownSubPath(KNOWN_PATH_TEMP, sname);
+
+ if (!name)
+ return -1;
+
+ /* On windows we would need '\\' or '/' as seperator.
+ * Single '\' do not work. */
+ hr = PathCchConvertStyleA(name, strlen(name), PATH_STYLE_UNIX);
+
+ if (FAILED(hr))
+ rc = -1;
+
+ handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (!handle)
+ {
+ free(name);
+ return -1;
+ }
+
+ if (!winpr_PathFileExists(name))
+ rc = -1;
+
+ if (!WriteFile(handle, buffer, sizeof(buffer), &written, NULL))
+ rc = -1;
+
+ if (written != sizeof(buffer))
+ rc = -1;
+
+ written = SetFilePointer(handle, 5, NULL, FILE_BEGIN);
+
+ if (written != 5)
+ rc = -1;
+
+ written = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
+
+ if (written != 5)
+ rc = -1;
+
+ written = SetFilePointer(handle, -5, NULL, FILE_CURRENT);
+
+ if (written != 0)
+ rc = -1;
+
+ if (!ReadFile(handle, cmp, sizeof(cmp), &written, NULL))
+ rc = -1;
+
+ if (written != sizeof(cmp))
+ rc = -1;
+
+ if (memcmp(buffer, cmp, sizeof(buffer)))
+ rc = -1;
+
+ if (!CloseHandle(handle))
+ rc = -1;
+
+ if (!winpr_DeleteFile(name))
+ rc = -1;
+
+ if (winpr_PathFileExists(name))
+ rc = -1;
+
+ free(name);
+ return rc;
+}
diff --git a/winpr/libwinpr/file/test/TestFileDeleteFile.c b/winpr/libwinpr/file/test/TestFileDeleteFile.c
new file mode 100644
index 0000000..81f0599
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileDeleteFile.c
@@ -0,0 +1,50 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/windows.h>
+
+int TestFileDeleteFile(int argc, char* argv[])
+{
+ BOOL rc = FALSE;
+ int fd = 0;
+ char validA[] = "/tmp/valid-test-file-XXXXXX";
+ char validW[] = "/tmp/valid-test-file-XXXXXX";
+ WCHAR* validWW = NULL;
+ const char invalidA[] = "/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ WCHAR invalidW[sizeof(invalidA)] = { 0 };
+
+ ConvertUtf8NToWChar(invalidA, ARRAYSIZE(invalidA), invalidW, ARRAYSIZE(invalidW));
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ rc = DeleteFileA(invalidA);
+ if (rc)
+ return -1;
+
+ rc = DeleteFileW(invalidW);
+ if (rc)
+ return -1;
+
+ fd = mkstemp(validA);
+ if (fd < 0)
+ return -1;
+
+ rc = DeleteFileA(validA);
+ if (!rc)
+ return -1;
+
+ fd = mkstemp(validW);
+ if (fd < 0)
+ return -1;
+
+ validWW = ConvertUtf8NToWCharAlloc(validW, ARRAYSIZE(validW), NULL);
+ if (validWW)
+ rc = DeleteFileW(validWW);
+ free(validWW);
+ if (!rc)
+ return -1;
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFileFindFirstFile.c b/winpr/libwinpr/file/test/TestFileFindFirstFile.c
new file mode 100644
index 0000000..04829a3
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileFindFirstFile.c
@@ -0,0 +1,326 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/handle.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/tchar.h>
+#include <winpr/collections.h>
+#include <winpr/windows.h>
+
+static const CHAR testFile1A[] = "TestFile1A";
+
+static BOOL create_layout_files(size_t level, const char* BasePath, wArrayList* files)
+{
+ for (size_t x = 0; x < 10; x++)
+ {
+ CHAR FilePath[PATHCCH_MAX_CCH] = { 0 };
+ strncpy(FilePath, BasePath, ARRAYSIZE(FilePath));
+
+ CHAR name[64] = { 0 };
+ _snprintf(name, ARRAYSIZE(name), "%zd-TestFile%zd", level, x);
+ NativePathCchAppendA(FilePath, PATHCCH_MAX_CCH, name);
+
+ HANDLE hdl =
+ CreateFileA(FilePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hdl == INVALID_HANDLE_VALUE)
+ return FALSE;
+ ArrayList_Append(files, FilePath);
+ CloseHandle(hdl);
+ }
+ return TRUE;
+}
+
+static BOOL create_layout_directories(size_t level, size_t max_level, const char* BasePath,
+ wArrayList* files)
+{
+ if (level >= max_level)
+ return TRUE;
+
+ CHAR FilePath[PATHCCH_MAX_CCH] = { 0 };
+ strncpy(FilePath, BasePath, ARRAYSIZE(FilePath));
+ PathCchConvertStyleA(FilePath, ARRAYSIZE(FilePath), PATH_STYLE_NATIVE);
+ if (!winpr_PathMakePath(FilePath, NULL))
+ return FALSE;
+ ArrayList_Append(files, FilePath);
+
+ if (!create_layout_files(level + 1, BasePath, files))
+ return FALSE;
+
+ for (size_t x = 0; x < 10; x++)
+ {
+ CHAR CurFilePath[PATHCCH_MAX_CCH] = { 0 };
+ strncpy(CurFilePath, FilePath, ARRAYSIZE(CurFilePath));
+
+ PathCchConvertStyleA(CurFilePath, ARRAYSIZE(CurFilePath), PATH_STYLE_NATIVE);
+
+ CHAR name[64] = { 0 };
+ _snprintf(name, ARRAYSIZE(name), "%zd-TestPath%zd", level, x);
+ NativePathCchAppendA(CurFilePath, PATHCCH_MAX_CCH, name);
+
+ if (!create_layout_directories(level + 1, max_level, CurFilePath, files))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL create_layout(const char* BasePath, wArrayList* files)
+{
+ CHAR BasePathNative[PATHCCH_MAX_CCH] = { 0 };
+ memcpy(BasePathNative, BasePath, sizeof(BasePathNative));
+ PathCchConvertStyleA(BasePathNative, ARRAYSIZE(BasePathNative), PATH_STYLE_NATIVE);
+
+ return create_layout_directories(0, 3, BasePathNative, files);
+}
+
+static void cleanup_layout(const char* BasePath)
+{
+ winpr_RemoveDirectory_RecursiveA(BasePath);
+}
+
+static BOOL find_first_file_success(const char* FilePath)
+{
+ BOOL rc = FALSE;
+ WIN32_FIND_DATAA FindData = { 0 };
+ HANDLE hFind = FindFirstFileA(FilePath, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ printf("FindFirstFile failure: %s (INVALID_HANDLE_VALUE -1)\n", FilePath);
+ goto fail;
+ }
+
+ printf("FindFirstFile: %s", FindData.cFileName);
+
+ if (strcmp(FindData.cFileName, testFile1A) != 0)
+ {
+ printf("FindFirstFile failure: Expected: %s, Actual: %s\n", testFile1A, FindData.cFileName);
+ goto fail;
+ }
+ rc = TRUE;
+fail:
+ FindClose(hFind);
+ return rc;
+}
+
+static BOOL list_directory_dot(const char* BasePath, wArrayList* files)
+{
+ BOOL rc = FALSE;
+ CHAR BasePathDot[PATHCCH_MAX_CCH] = { 0 };
+ memcpy(BasePathDot, BasePath, ARRAYSIZE(BasePathDot));
+ PathCchConvertStyleA(BasePathDot, ARRAYSIZE(BasePathDot), PATH_STYLE_NATIVE);
+ NativePathCchAppendA(BasePathDot, PATHCCH_MAX_CCH, ".");
+ WIN32_FIND_DATAA FindData = { 0 };
+ HANDLE hFind = FindFirstFileA(BasePathDot, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return FALSE;
+ size_t count = 0;
+ do
+ {
+ count++;
+ if (strcmp(FindData.cFileName, ".") != 0)
+ goto fail;
+ } while (FindNextFile(hFind, &FindData));
+
+ rc = TRUE;
+fail:
+ FindClose(hFind);
+
+ if (count != 1)
+ return FALSE;
+ return rc;
+}
+
+static BOOL list_directory_star(const char* BasePath, wArrayList* files)
+{
+ CHAR BasePathDot[PATHCCH_MAX_CCH] = { 0 };
+ memcpy(BasePathDot, BasePath, ARRAYSIZE(BasePathDot));
+ PathCchConvertStyleA(BasePathDot, ARRAYSIZE(BasePathDot), PATH_STYLE_NATIVE);
+ NativePathCchAppendA(BasePathDot, PATHCCH_MAX_CCH, "*");
+ WIN32_FIND_DATAA FindData = { 0 };
+ HANDLE hFind = FindFirstFileA(BasePathDot, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return FALSE;
+ size_t count = 0;
+ size_t dotcount = 0;
+ size_t dotdotcount = 0;
+ do
+ {
+ if (strcmp(FindData.cFileName, ".") == 0)
+ dotcount++;
+ else if (strcmp(FindData.cFileName, "..") == 0)
+ dotdotcount++;
+ else
+ count++;
+ } while (FindNextFile(hFind, &FindData));
+ FindClose(hFind);
+
+ const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE);
+ size_t fcount = 0;
+ const size_t baselen = strlen(BasePath);
+ const size_t total = ArrayList_Count(files);
+ for (size_t x = 0; x < total; x++)
+ {
+ const char* path = ArrayList_GetItem(files, x);
+ const size_t pathlen = strlen(path);
+ if (pathlen < baselen)
+ continue;
+ const char* skip = &path[baselen];
+ if (*skip == sep)
+ skip++;
+ const char* end = strrchr(skip, sep);
+ if (end)
+ continue;
+ fcount++;
+ }
+
+ if (fcount != count)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL find_first_file_fail(const char* FilePath)
+{
+ WIN32_FIND_DATAA FindData = { 0 };
+ HANDLE hFind = FindFirstFileA(FilePath, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return TRUE;
+
+ FindClose(hFind);
+ return FALSE;
+}
+
+static int TestFileFindFirstFileA(const char* str)
+{
+ int rc = -1;
+ if (!str)
+ return -1;
+
+ CHAR BasePath[PATHCCH_MAX_CCH] = { 0 };
+
+ strncpy(BasePath, str, ARRAYSIZE(BasePath));
+
+ const size_t length = strnlen(BasePath, PATHCCH_MAX_CCH - 1);
+
+ CHAR FilePath[PATHCCH_MAX_CCH] = { 0 };
+ CopyMemory(FilePath, BasePath, length * sizeof(CHAR));
+
+ PathCchConvertStyleA(BasePath, length, PATH_STYLE_WINDOWS);
+
+ wArrayList* files = ArrayList_New(FALSE);
+ if (!files)
+ return -3;
+ wObject* obj = ArrayList_Object(files);
+ obj->fnObjectFree = winpr_ObjectStringFree;
+ obj->fnObjectNew = winpr_ObjectStringClone;
+
+ if (!create_layout(BasePath, files))
+ return -1;
+
+ NativePathCchAppendA(FilePath, PATHCCH_MAX_CCH, testFile1A);
+
+ printf("Finding file: %s\n", FilePath);
+
+ if (!find_first_file_fail(FilePath))
+ goto fail;
+
+ HANDLE hdl =
+ CreateFileA(FilePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hdl == INVALID_HANDLE_VALUE)
+ goto fail;
+ CloseHandle(hdl);
+
+ if (!find_first_file_success(FilePath))
+ goto fail;
+
+ CHAR BasePathInvalid[PATHCCH_MAX_CCH] = { 0 };
+ memcpy(BasePathInvalid, BasePath, ARRAYSIZE(BasePathInvalid));
+ PathCchAddBackslashA(BasePathInvalid, PATHCCH_MAX_CCH);
+
+ if (!find_first_file_fail(BasePathInvalid))
+ goto fail;
+
+ if (!list_directory_dot(BasePath, files))
+ goto fail;
+
+ if (!list_directory_star(BasePath, files))
+ goto fail;
+
+ rc = 0;
+fail:
+ DeleteFileA(FilePath);
+ cleanup_layout(BasePath);
+ ArrayList_Free(files);
+ return rc;
+}
+
+static int TestFileFindFirstFileW(const char* str)
+{
+ WCHAR buffer[32] = { 0 };
+ const WCHAR* testFile1W = InitializeConstWCharFromUtf8("TestFile1W", buffer, ARRAYSIZE(buffer));
+ int rc = -1;
+ if (!str)
+ return -1;
+
+ WCHAR BasePath[PATHCCH_MAX_CCH] = { 0 };
+
+ ConvertUtf8ToWChar(str, BasePath, ARRAYSIZE(BasePath));
+
+ const size_t length = _wcsnlen(BasePath, PATHCCH_MAX_CCH - 1);
+
+ WCHAR FilePath[PATHCCH_MAX_CCH] = { 0 };
+ CopyMemory(FilePath, BasePath, length * sizeof(WCHAR));
+
+ PathCchConvertStyleW(BasePath, length, PATH_STYLE_WINDOWS);
+ NativePathCchAppendW(FilePath, PATHCCH_MAX_CCH, testFile1W);
+
+ CHAR FilePathA[PATHCCH_MAX_CCH] = { 0 };
+ ConvertWCharNToUtf8(FilePath, ARRAYSIZE(FilePath), FilePathA, ARRAYSIZE(FilePathA));
+ printf("Finding file: %s\n", FilePathA);
+
+ WIN32_FIND_DATAW FindData = { 0 };
+ HANDLE hFind = FindFirstFileW(FilePath, &FindData);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ printf("FindFirstFile failure: %s (INVALID_HANDLE_VALUE -1)\n", FilePathA);
+ goto fail;
+ }
+
+ CHAR cFileName[MAX_PATH] = { 0 };
+ ConvertWCharNToUtf8(FindData.cFileName, ARRAYSIZE(FindData.cFileName), cFileName,
+ ARRAYSIZE(cFileName));
+
+ printf("FindFirstFile: %s", cFileName);
+
+ if (_wcscmp(FindData.cFileName, testFile1W) != 0)
+ {
+ printf("FindFirstFile failure: Expected: %s, Actual: %s\n", testFile1A, cFileName);
+ goto fail;
+ }
+
+ rc = 0;
+fail:
+ DeleteFileW(FilePath);
+ FindClose(hFind);
+ return rc;
+}
+
+int TestFileFindFirstFile(int argc, char* argv[])
+{
+ char* str = GetKnownSubPath(KNOWN_PATH_TEMP, "TestFileFindFirstFile");
+ if (!str)
+ return -23;
+
+ cleanup_layout(str);
+
+ int rc1 = -1;
+ int rc2 = -1;
+ if (winpr_PathMakePath(str, NULL))
+ {
+ rc1 = TestFileFindFirstFileA(str);
+ rc2 = 0; // TestFileFindFirstFileW(str);
+ winpr_RemoveDirectory(str);
+ }
+ free(str);
+ return rc1 + rc2;
+}
diff --git a/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c b/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c
new file mode 100644
index 0000000..b8a7683
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c
@@ -0,0 +1,10 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/windows.h>
+
+int TestFileFindFirstFileEx(int argc, char* argv[])
+{
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFileFindNextFile.c b/winpr/libwinpr/file/test/TestFileFindNextFile.c
new file mode 100644
index 0000000..ebb9bec
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileFindNextFile.c
@@ -0,0 +1,99 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/tchar.h>
+#include <winpr/windows.h>
+
+static TCHAR testDirectory2File1[] = _T("TestDirectory2File1");
+static TCHAR testDirectory2File2[] = _T("TestDirectory2File2");
+
+int TestFileFindNextFile(int argc, char* argv[])
+{
+ char* str = NULL;
+ size_t length = 0;
+ BOOL status = 0;
+ HANDLE hFind = NULL;
+ LPTSTR BasePath = NULL;
+ WIN32_FIND_DATA FindData;
+ TCHAR FilePath[PATHCCH_MAX_CCH] = { 0 };
+ WINPR_UNUSED(argc);
+ str = argv[1];
+#ifdef UNICODE
+ BasePath = ConvertUtf8ToWChar(str, &length);
+
+ if (!BasePath)
+ {
+ _tprintf(_T("Unable to allocate memory"));
+ return -1;
+ }
+#else
+ BasePath = _strdup(str);
+
+ if (!BasePath)
+ {
+ printf("Unable to allocate memory");
+ return -1;
+ }
+
+ length = strlen(BasePath);
+#endif
+ /* Simple filter matching all files inside current directory */
+ CopyMemory(FilePath, BasePath, length * sizeof(TCHAR));
+ FilePath[length] = 0;
+ PathCchConvertStyle(BasePath, length, PATH_STYLE_WINDOWS);
+ NativePathCchAppend(FilePath, PATHCCH_MAX_CCH, _T("TestDirectory2"));
+ NativePathCchAppend(FilePath, PATHCCH_MAX_CCH, _T("TestDirectory2File*"));
+ free(BasePath);
+ _tprintf(_T("Finding file: %s\n"), FilePath);
+ hFind = FindFirstFile(FilePath, &FindData);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ _tprintf(_T("FindFirstFile failure: %s\n"), FilePath);
+ return -1;
+ }
+
+ _tprintf(_T("FindFirstFile: %s"), FindData.cFileName);
+
+ /**
+ * The current implementation does not enforce a particular order
+ */
+
+ if ((_tcscmp(FindData.cFileName, testDirectory2File1) != 0) &&
+ (_tcscmp(FindData.cFileName, testDirectory2File2) != 0))
+ {
+ _tprintf(_T("FindFirstFile failure: Expected: %s, Actual: %s\n"), testDirectory2File1,
+ FindData.cFileName);
+ return -1;
+ }
+
+ status = FindNextFile(hFind, &FindData);
+
+ if (!status)
+ {
+ _tprintf(_T("FindNextFile failure: Expected: TRUE, Actual: %") _T(PRId32) _T("\n"), status);
+ return -1;
+ }
+
+ if ((_tcscmp(FindData.cFileName, testDirectory2File1) != 0) &&
+ (_tcscmp(FindData.cFileName, testDirectory2File2) != 0))
+ {
+ _tprintf(_T("FindNextFile failure: Expected: %s, Actual: %s\n"), testDirectory2File2,
+ FindData.cFileName);
+ return -1;
+ }
+
+ status = FindNextFile(hFind, &FindData);
+
+ if (status)
+ {
+ _tprintf(_T("FindNextFile failure: Expected: FALSE, Actual: %") _T(PRId32) _T("\n"),
+ status);
+ return -1;
+ }
+
+ FindClose(hFind);
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFileGetStdHandle.c b/winpr/libwinpr/file/test/TestFileGetStdHandle.c
new file mode 100644
index 0000000..79ce4ae
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileGetStdHandle.c
@@ -0,0 +1,49 @@
+
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 Bernhard Miklautz <bernhard.miklautz@thincast.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/file.h>
+#include <winpr/handle.h>
+#include <string.h>
+#include <stdio.h>
+
+int TestFileGetStdHandle(int argc, char* argv[])
+{
+ HANDLE so = NULL;
+ const char buf[] = "happy happy";
+ DWORD bytesWritten = 0;
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+ so = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (so == INVALID_HANDLE_VALUE)
+ {
+ fprintf(stderr, "GetStdHandle failed ;(\n");
+ return -1;
+ }
+ WriteFile(so, buf, strnlen(buf, sizeof(buf)), &bytesWritten, FALSE);
+ if (bytesWritten != strnlen(buf, sizeof(buf)))
+ {
+ fprintf(stderr, "write failed\n");
+ return -1;
+ }
+ CloseHandle(so);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFilePatternMatch.c b/winpr/libwinpr/file/test/TestFilePatternMatch.c
new file mode 100644
index 0000000..8f7a2fb
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFilePatternMatch.c
@@ -0,0 +1,182 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/windows.h>
+
+int TestFilePatternMatch(int argc, char* argv[])
+{
+ /* '*' expression */
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+ if (!FilePatternMatchA("document.txt", "*"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "*");
+ return -1;
+ }
+
+ /* '*X' expression */
+
+ if (!FilePatternMatchA("document.txt", "*.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "*.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("document.docx", "*.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.docx", "*.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("document.txt.bak", "*.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt.bak", "*.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("bak", "*.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "bak", "*.txt");
+ return -1;
+ }
+
+ /* 'X*' expression */
+
+ if (!FilePatternMatchA("document.txt", "document.*"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "document.*");
+ return -1;
+ }
+
+ /* 'X?' expression */
+
+ if (!FilePatternMatchA("document.docx", "document.doc?"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.docx",
+ "document.doc?");
+ return -1;
+ }
+
+ if (FilePatternMatchA("document.doc", "document.doc?"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.doc",
+ "document.doc?");
+ return -1;
+ }
+
+ /* no wildcards expression */
+
+ if (!FilePatternMatchA("document.txt", "document.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt",
+ "document.txt");
+ return -1;
+ }
+
+ /* 'X * Y' expression */
+
+ if (!FilePatternMatchA("X123Y.txt", "X*Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y.txt", "X*Y.txt");
+ return -1;
+ }
+
+ if (!FilePatternMatchA("XY.txt", "X*Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XY.txt", "X*Y.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("XZ.txt", "X*Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XZ.txt", "X*Y.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("X123Z.txt", "X*Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Z.txt", "X*Y.txt");
+ return -1;
+ }
+
+ /* 'X * Y * Z' expression */
+
+ if (!FilePatternMatchA("X123Y456Z.txt", "X*Y*Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456Z.txt", "X*Y*Z.txt");
+ return -1;
+ }
+
+ if (!FilePatternMatchA("XYZ.txt", "X*Y*Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYZ.txt", "X*Y*Z.txt");
+ return -1;
+ }
+
+ if (!FilePatternMatchA("X123Y456W.txt", "X*Y*Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456W.txt", "X*Y*Z.txt");
+ return -1;
+ }
+
+ if (!FilePatternMatchA("XYW.txt", "X*Y*Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYW.txt", "X*Y*Z.txt");
+ return -1;
+ }
+
+ /* 'X ? Y' expression */
+
+ if (!FilePatternMatchA("X1Y.txt", "X?Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X1Y.txt", "X?Y.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("XY.txt", "X?Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XY.txt", "X?Y.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("XZ.txt", "X?Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XZ.txt", "X?Y.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("X123Z.txt", "X?Y.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Z.txt", "X?Y.txt");
+ return -1;
+ }
+
+ /* 'X ? Y ? Z' expression */
+
+ if (!FilePatternMatchA("X123Y456Z.txt", "X?Y?Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456Z.txt", "X?Y?Z.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("XYZ.txt", "X?Y?Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYZ.txt", "X?Y?Z.txt");
+ return -1;
+ }
+
+ if (!FilePatternMatchA("X123Y456W.txt", "X?Y?Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456W.txt", "X?Y?Z.txt");
+ return -1;
+ }
+
+ if (FilePatternMatchA("XYW.txt", "X?Y?Z.txt"))
+ {
+ printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYW.txt", "X?Y?Z.txt");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFileReadFile.c b/winpr/libwinpr/file/test/TestFileReadFile.c
new file mode 100644
index 0000000..936881a
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileReadFile.c
@@ -0,0 +1,10 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/windows.h>
+
+int TestFileReadFile(int argc, char* argv[])
+{
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestFileWriteFile.c b/winpr/libwinpr/file/test/TestFileWriteFile.c
new file mode 100644
index 0000000..a8283ee
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestFileWriteFile.c
@@ -0,0 +1,10 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/windows.h>
+
+int TestFileWriteFile(int argc, char* argv[])
+{
+ return 0;
+}
diff --git a/winpr/libwinpr/file/test/TestSetFileAttributes.c b/winpr/libwinpr/file/test/TestSetFileAttributes.c
new file mode 100644
index 0000000..f423ec2
--- /dev/null
+++ b/winpr/libwinpr/file/test/TestSetFileAttributes.c
@@ -0,0 +1,152 @@
+
+#include <stdio.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/handle.h>
+#include <winpr/windows.h>
+#include <winpr/sysinfo.h>
+
+static const DWORD allflags[] = {
+ 0,
+ FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_DIRECTORY,
+ FILE_ATTRIBUTE_ARCHIVE,
+ FILE_ATTRIBUTE_DEVICE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_TEMPORARY,
+ FILE_ATTRIBUTE_SPARSE_FILE,
+ FILE_ATTRIBUTE_REPARSE_POINT,
+ FILE_ATTRIBUTE_COMPRESSED,
+ FILE_ATTRIBUTE_OFFLINE,
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
+ FILE_ATTRIBUTE_ENCRYPTED,
+ FILE_ATTRIBUTE_VIRTUAL,
+ FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DEVICE |
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE,
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_VIRTUAL
+};
+
+static BOOL test_SetFileAttributesA(void)
+{
+ BOOL rc = FALSE;
+ HANDLE handle = NULL;
+ const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY };
+ char* name = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd");
+ if (!name)
+ goto fail;
+
+ for (size_t x = 0; x < ARRAYSIZE(allflags); x++)
+ {
+ const DWORD flag = allflags[x];
+ const BOOL brc = SetFileAttributesA(NULL, flag);
+ if (brc)
+ goto fail;
+
+ const BOOL crc = SetFileAttributesA(name, flag);
+ if (crc)
+ goto fail;
+ }
+
+ handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto fail;
+ CloseHandle(handle);
+
+ for (size_t x = 0; x < ARRAYSIZE(flags); x++)
+ {
+ DWORD attr = 0;
+ const DWORD flag = flags[x];
+ const BOOL brc = SetFileAttributesA(name, flag);
+ if (!brc)
+ goto fail;
+
+ attr = GetFileAttributesA(name);
+ if (flag != 0)
+ {
+ if ((attr & flag) == 0)
+ goto fail;
+ }
+ }
+
+ rc = TRUE;
+
+fail:
+ DeleteFileA(name);
+ free(name);
+ return rc;
+}
+
+static BOOL test_SetFileAttributesW(void)
+{
+ BOOL rc = FALSE;
+ WCHAR* name = NULL;
+ HANDLE handle = NULL;
+ const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY };
+ char* base = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd");
+ if (!base)
+ goto fail;
+
+ name = ConvertUtf8ToWCharAlloc(base, NULL);
+ if (!name)
+ goto fail;
+
+ for (size_t x = 0; x < ARRAYSIZE(allflags); x++)
+ {
+ const DWORD flag = allflags[x];
+ const BOOL brc = SetFileAttributesW(NULL, flag);
+ if (brc)
+ goto fail;
+
+ const BOOL crc = SetFileAttributesW(name, flag);
+ if (crc)
+ goto fail;
+ }
+
+ handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto fail;
+ CloseHandle(handle);
+
+ for (size_t x = 0; x < ARRAYSIZE(flags); x++)
+ {
+ DWORD attr = 0;
+ const DWORD flag = flags[x];
+ const BOOL brc = SetFileAttributesW(name, flag);
+ if (!brc)
+ goto fail;
+
+ attr = GetFileAttributesW(name);
+ if (flag != 0)
+ {
+ if ((attr & flag) == 0)
+ goto fail;
+ }
+ }
+
+ rc = TRUE;
+fail:
+ DeleteFileW(name);
+ free(name);
+ free(base);
+ return rc;
+}
+
+int TestSetFileAttributes(int argc, char* argv[])
+{
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_SetFileAttributesA())
+ return -1;
+ if (!test_SetFileAttributesW())
+ return -1;
+ return 0;
+}