diff options
Diffstat (limited to '')
-rw-r--r-- | winpr/libwinpr/file/file.c | 1483 |
1 files changed, 1483 insertions, 0 deletions
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 +} |