diff options
Diffstat (limited to 'winpr/libwinpr/file')
-rw-r--r-- | winpr/libwinpr/file/CMakeLists.txt | 22 | ||||
-rw-r--r-- | winpr/libwinpr/file/ModuleOptions.cmake | 9 | ||||
-rw-r--r-- | winpr/libwinpr/file/file.c | 1483 | ||||
-rw-r--r-- | winpr/libwinpr/file/file.h | 66 | ||||
-rw-r--r-- | winpr/libwinpr/file/generic.c | 1378 | ||||
-rw-r--r-- | winpr/libwinpr/file/namedPipeClient.c | 305 | ||||
-rw-r--r-- | winpr/libwinpr/file/pattern.c | 371 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/CMakeLists.txt | 58 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileCreateFile.c | 94 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileDeleteFile.c | 50 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileFindFirstFile.c | 326 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileFindFirstFileEx.c | 10 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileFindNextFile.c | 99 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileGetStdHandle.c | 49 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFilePatternMatch.c | 182 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileReadFile.c | 10 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestFileWriteFile.c | 10 | ||||
-rw-r--r-- | winpr/libwinpr/file/test/TestSetFileAttributes.c | 152 |
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, ¶ms); + + 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; +} |