FreeRDP/winpr/libwinpr/file/file.c

1486 lines
37 KiB
C
Raw Normal View History

/**
* WinPR: Windows Portable Runtime
* File Functions
*
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 Bernhard Miklautz <bernhard.miklautz@thincast.com>
2016-12-01 00:47:06 +03:00
* 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.
*/
2022-02-16 12:08:00 +03:00
#include <winpr/config.h>
#include <winpr/debug.h>
#if defined(__FreeBSD_kernel__) && defined(__GLIBC__)
#define _GNU_SOURCE
#define KFREEBSD
#endif
#include <winpr/wtypes.h>
2016-03-30 03:34:52 +03:00
#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>
2016-02-26 12:48:53 +03:00
#include <sys/stat.h>
#include <sys/time.h>
2016-12-01 00:47:06 +03:00
#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)
{
2019-11-06 17:24:51 +03:00
WINPR_FILE* file = (WINPR_FILE*)handle;
if (!FileIsHandled(handle))
return -1;
return fileno(file->fp);
}
2019-11-06 17:24:51 +03:00
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)
{
2019-11-06 17:24:51 +03:00
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
INT64 size = 0;
if (!hFile)
return FALSE;
size = _ftelli64(pFile->fp);
2017-03-18 00:05:21 +03:00
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);
2016-12-01 00:51:26 +03:00
SetLastError(map_posix_err(errno));
return FALSE;
}
return TRUE;
}
2019-11-06 17:24:51 +03:00
static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod)
{
2019-11-06 17:24:51 +03:00
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
INT64 offset = 0;
int whence = 0;
if (!hFile)
return INVALID_SET_FILE_POINTER;
2017-08-10 17:51:55 +03:00
/* If there is a high part, the sign is contained in that
* and the low integer must be interpreted as unsigned. */
if (lpDistanceToMoveHigh)
{
2017-08-10 17:51:55 +03:00
offset = (INT64)(((UINT64)*lpDistanceToMoveHigh << 32U) | (UINT64)lDistanceToMove);
}
else
2019-11-06 17:24:51 +03:00
offset = lDistanceToMove;
2019-11-06 17:24:51 +03:00
switch (dwMoveMethod)
{
2019-11-06 17:24:51 +03:00
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;
}
2021-06-17 12:25:58 +03:00
return (DWORD)_ftelli64(pFile->fp);
}
2019-11-06 17:24:51 +03:00
static BOOL FileSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove,
PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
{
2019-11-06 17:24:51 +03:00
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
int whence = 0;
if (!hFile)
return FALSE;
2019-11-06 17:24:51 +03:00
switch (dwMoveMethod)
{
2019-11-06 17:24:51 +03:00
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,
2019-11-06 17:24:51 +03:00
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;
2019-11-06 17:24:51 +03:00
file = (WINPR_FILE*)Object;
2017-03-18 00:05:21 +03:00
clearerr(file->fp);
2016-12-01 00:51:26 +03:00
io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp);
2017-03-18 00:05:21 +03:00
if (io_status == 0 && ferror(file->fp))
{
status = FALSE;
switch (errno)
{
case EWOULDBLOCK:
SetLastError(ERROR_NO_DATA);
break;
2016-12-01 00:51:26 +03:00
default:
SetLastError(map_posix_err(errno));
}
}
if (lpNumberOfBytesRead)
2021-06-16 13:58:21 +03:00
*lpNumberOfBytesRead = (DWORD)io_status;
return status;
}
static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
2019-11-06 17:24:51 +03:00
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;
2019-11-06 17:24:51 +03:00
file = (WINPR_FILE*)Object;
2017-03-18 00:05:21 +03:00
clearerr(file->fp);
2016-12-01 00:51:26 +03:00
io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp);
2017-03-18 00:05:21 +03:00
if (io_status == 0 && ferror(file->fp))
2016-12-01 00:51:26 +03:00
{
SetLastError(map_posix_err(errno));
return FALSE;
2016-12-01 00:51:26 +03:00
}
2021-06-16 13:58:21 +03:00
*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;
2019-11-06 17:24:51 +03:00
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);
}
2022-03-22 11:25:40 +03:00
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;
2022-03-22 11:25:40 +03:00
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)));
2022-03-22 11:25:40 +03:00
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,
2019-11-06 17:24:51 +03:00
DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
LPOVERLAPPED lpOverlapped)
{
2017-05-17 19:30:13 +03:00
#ifdef __sun
struct flock lock;
int lckcmd;
#else
int lock = 0;
2017-05-17 19:30:13 +03:00
#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)
{
2015-12-10 12:39:37 +03:00
WLog_ERR(TAG, "File %s already locked!", pFile->lpFileName);
return FALSE;
}
2017-05-17 19:30:13 +03:00
#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;
2019-11-06 17:24:51 +03:00
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);
2017-05-17 19:30:13 +03:00
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;
}
2017-05-17 19:30:13 +03:00
#endif
pFile->bLocked = TRUE;
return TRUE;
}
static BOOL FileUnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
2019-11-06 17:24:51 +03:00
DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
{
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
2017-05-17 19:30:13 +03:00
#ifdef __sun
struct flock lock;
#endif
if (!hFile)
return FALSE;
if (!pFile->bLocked)
{
2015-12-10 12:39:37 +03:00
WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
return FALSE;
}
2017-05-17 19:30:13 +03:00
#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);
2017-05-17 19:30:13 +03:00
return FALSE;
}
#else
if (flock(fileno(pFile->fp), LOCK_UN) < 0)
{
char ebuffer[256] = { 0 };
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
return FALSE;
}
2017-05-17 19:30:13 +03:00
#endif
return TRUE;
}
static BOOL FileUnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
2019-11-06 17:24:51 +03:00
DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
{
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
2017-05-17 19:30:13 +03:00
#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)
{
2015-12-10 12:39:37 +03:00
WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
return FALSE;
}
2017-05-17 19:30:13 +03:00
#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);
2017-05-17 19:30:13 +03:00
return FALSE;
}
#else
if (flock(fileno(pFile->fp), LOCK_UN) < 0)
{
char ebuffer[256] = { 0 };
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
return FALSE;
}
2017-05-17 19:30:13 +03:00
#endif
return TRUE;
}
static UINT64 FileTimeToUS(const FILETIME* ft)
{
2022-03-22 11:25:40 +03:00
const UINT64 EPOCH_DIFF_US = EPOCH_DIFF * 1000000ULL;
2019-11-06 17:24:51 +03:00
UINT64 tmp = ((UINT64)ft->dwHighDateTime) << 32 | ft->dwLowDateTime;
tmp /= 10; /* 100ns steps to 1us step */
2022-03-22 11:25:40 +03:00
tmp -= EPOCH_DIFF_US;
return tmp;
}
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
2016-02-26 12:48:53 +03:00
{
int rc = 0;
#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
2016-02-26 13:22:56 +03:00
struct stat buf;
2016-08-01 10:41:41 +03:00
/* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */
struct timeval timevals[2];
#else
2016-02-26 12:48:53 +03:00
struct timespec times[2]; /* last access, last modification */
#endif
2016-02-26 12:48:53 +03:00
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
if (!hFile)
return FALSE;
#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
2016-02-26 13:22:56 +03:00
rc = fstat(fileno(pFile->fp), &buf);
2016-02-26 13:22:56 +03:00
if (rc < 0)
return FALSE;
2016-02-26 13:22:56 +03:00
#endif
2016-02-26 12:48:53 +03:00
if (!lpLastAccessTime)
2016-02-26 13:22:56 +03:00
{
#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;
2016-02-26 13:22:56 +03:00
#else
times[0].tv_sec = UTIME_OMIT;
2016-02-26 12:48:53 +03:00
times[0].tv_nsec = UTIME_OMIT;
2016-02-26 13:22:56 +03:00
#endif
}
2016-02-26 12:48:53 +03:00
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
2016-02-26 12:48:53 +03:00
}
2016-02-26 12:48:53 +03:00
if (!lpLastWriteTime)
2016-02-26 13:22:56 +03:00
{
#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;
2016-02-26 13:22:56 +03:00
#else
times[1].tv_sec = UTIME_OMIT;
2016-02-26 12:48:53 +03:00
times[1].tv_nsec = UTIME_OMIT;
2016-02-26 13:22:56 +03:00
#endif
}
2016-02-26 12:48:53 +03:00
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
2016-02-26 12:48:53 +03:00
}
// TODO: Creation time can not be handled!
#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
rc = utimes(pFile->lpFileName, timevals);
2016-02-26 13:22:56 +03:00
#else
2016-02-26 12:48:53 +03:00
rc = futimens(fileno(pFile->fp), times);
2016-02-26 13:22:56 +03:00
#endif
2016-02-26 12:48:53 +03:00
if (rc != 0)
return FALSE;
return TRUE;
}
2022-03-22 11:25:40 +03:00
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,
};
2015-12-10 12:39:37 +03:00
static HANDLE_OPS shmOps = {
2022-03-22 11:25:40 +03:00
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)
{
2019-11-06 17:24:51 +03:00
BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
2019-11-06 17:24:51 +03:00
switch (dwCreationDisposition)
{
2019-11-06 17:24:51 +03:00
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 "";
}
}
2016-12-01 00:47:06 +03:00
UINT32 map_posix_err(int fs_errno)
{
NTSTATUS rc = 0;
2016-12-01 00:47:06 +03:00
/* try to return NTSTATUS version of error code */
switch (fs_errno)
{
case 0:
rc = STATUS_SUCCESS;
break;
case ENOTCONN:
2017-07-24 16:05:48 +03:00
case ENODEV:
case ENOTDIR:
case ENXIO:
rc = ERROR_FILE_NOT_FOUND;
break;
2017-07-26 12:02:55 +03:00
case EROFS:
2016-12-01 00:47:06 +03:00
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:
2019-11-06 17:24:51 +03:00
rc = ERROR_FILE_EXISTS;
2016-12-01 00:47:06 +03:00
break;
case EISDIR:
rc = STATUS_FILE_IS_A_DIRECTORY;
break;
case ENOTEMPTY:
rc = STATUS_DIRECTORY_NOT_EMPTY;
break;
2023-02-21 14:46:21 +03:00
case EMFILE:
rc = STATUS_TOO_MANY_OPENED_FILES;
break;
2016-12-01 00:47:06 +03:00
default:
{
char ebuffer[256] = { 0 };
WLog_ERR(TAG, "Missing ERRNO mapping %s [%d]",
winpr_strerror(fs_errno, ebuffer, sizeof(ebuffer)), fs_errno);
2016-12-01 00:47:06 +03:00
rc = STATUS_UNSUCCESSFUL;
}
break;
2016-12-01 00:47:06 +03:00
}
2019-11-06 17:24:51 +03:00
return (UINT32)rc;
2016-12-01 00:47:06 +03:00
}
2019-11-06 17:24:51 +03:00
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);
2017-05-17 19:30:13 +03:00
#ifdef __sun
struct flock lock;
#else
2016-01-14 18:00:31 +03:00
int lock = 0;
2017-05-17 19:30:13 +03:00
#endif
2015-12-14 11:23:42 +03:00
FILE* fp = NULL;
2016-12-01 00:53:10 +03:00
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;
}
2019-11-06 17:24:51 +03:00
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)
{
2016-12-01 00:53:10 +03:00
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)
{
2016-12-01 00:53:10 +03:00
SetLastError(map_posix_err(errno));
free(pFile->lpFileName);
free(pFile);
return INVALID_HANDLE_VALUE;
}
2015-12-14 11:23:42 +03:00
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;
}
}
2015-12-14 11:23:42 +03:00
if (NULL == fp)
fp = winpr_fopen(pFile->lpFileName, mode);
2015-12-14 11:23:42 +03:00
pFile->fp = fp;
if (!pFile->fp)
{
/* This case can occur when trying to open a
* not existing file without create flag. */
2016-12-01 00:53:10 +03:00
SetLastError(map_posix_err(errno));
free(pFile->lpFileName);
free(pFile);
return INVALID_HANDLE_VALUE;
}
setvbuf(fp, NULL, _IONBF, 0);
2017-05-17 19:30:13 +03:00
#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;
2017-05-17 19:30:13 +03:00
#endif
if (dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE))
{
2017-05-17 19:30:13 +03:00
#ifdef __sun
if (fcntl(fileno(pFile->fp), F_SETLKW, &lock) == -1)
#else
if (flock(fileno(pFile->fp), lock) < 0)
2017-05-17 19:30:13 +03:00
#endif
{
char ebuffer[256] = { 0 };
2017-05-17 19:30:13 +03:00
#ifdef __sun
WLog_ERR(TAG, "F_SETLKW failed with %s [0x%08X]",
winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
2017-05-17 19:30:13 +03:00
#else
WLog_ERR(TAG, "flock failed with %s [0x%08X]",
winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
2017-05-17 19:30:13 +03:00
#endif
2016-12-01 00:53:10 +03:00
SetLastError(map_posix_err(errno));
FileCloseHandle(pFile);
return INVALID_HANDLE_VALUE;
}
pFile->bLocked = TRUE;
}
2019-11-06 17:24:51 +03:00
if (fstat(fileno(pFile->fp), &st) == 0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
2016-12-01 00:53:10 +03:00
{
2019-11-06 17:24:51 +03:00
st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
fchmod(fileno(pFile->fp), st.st_mode);
2016-12-01 00:53:10 +03:00
}
SetLastError(STATUS_SUCCESS);
return pFile;
}
2019-11-20 13:30:14 +03:00
static BOOL IsFileDevice(LPCTSTR lpDeviceName)
{
return TRUE;
}
static HANDLE_CREATOR _FileHandleCreator = { IsFileDevice, FileCreateFileA };
2019-11-06 17:24:51 +03:00
HANDLE_CREATOR* GetFileHandleCreator(void)
{
return &_FileHandleCreator;
}
2019-11-06 17:24:51 +03:00
static WINPR_FILE* FileHandle_New(FILE* fp)
{
WINPR_FILE* pFile = NULL;
char name[MAX_PATH] = { 0 };
_snprintf(name, sizeof(name), "device_%d", fileno(fp));
2019-11-06 17:24:51 +03:00
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;
}
2019-11-06 17:24:51 +03:00
BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
2016-12-01 00:47:06 +03:00
{
2017-03-27 21:11:54 +03:00
#if defined(ANDROID)
2016-12-01 00:47:06 +03:00
#define STATVFS statfs
#else
#define STATVFS statvfs
#endif
struct STATVFS svfst = { 0 };
2016-12-01 00:47:06 +03:00
STATVFS(lpRootPathName, &svfst);
*lpSectorsPerCluster = (UINT32)MIN(svfst.f_frsize, UINT32_MAX);
2016-12-01 00:47:06 +03:00
*lpBytesPerSector = 1;
*lpNumberOfFreeClusters = (UINT32)MIN(svfst.f_bavail, UINT32_MAX);
*lpTotalNumberOfClusters = (UINT32)MIN(svfst.f_blocks, UINT32_MAX);
2016-12-01 00:47:06 +03:00
return TRUE;
}
BOOL GetDiskFreeSpaceW(LPCWSTR lpwRootPathName, LPDWORD lpSectorsPerCluster,
2019-11-06 17:24:51 +03:00
LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
LPDWORD lpTotalNumberOfClusters)
2016-12-01 00:47:06 +03:00
{
LPSTR lpRootPathName = NULL;
BOOL ret = 0;
if (!lpwRootPathName)
return FALSE;
2016-12-01 00:47:06 +03:00
lpRootPathName = ConvertWCharToUtf8Alloc(lpwRootPathName, NULL);
if (!lpRootPathName)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
2016-12-01 00:47:06 +03:00
ret = GetDiskFreeSpaceA(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector,
2019-11-06 17:24:51 +03:00
lpNumberOfFreeClusters, lpTotalNumberOfClusters);
2016-12-01 00:47:06 +03:00
free(lpRootPathName);
return ret;
}
#endif /* _WIN32 */
wClipboard: disallow Windows reserved names Another issue revealed during testing is that older Windows systems cannot handle the reserved file names well. While Windows 8 and 10 are fine (they silently abort the file transfer), using reserved names with Windows 7 can flat out crash explorer.exe or result into weird error messages like "fatal error: 0x00000000 ERROR_SUCCESS". This is not required by MS-RDPECLIP specification, but we should try to avoid this issue as not using reserved file names seems to be assumed a common sense in Windows protocols. The most convenient way to handle the issue would be on wClipboard level so that WinPR's clients do not bother with it. We should prohibit the reserved names from being used in FILEDESCRIPTOR, failing the conversion if we see such a file. POSIX subsystem (the only one at the moment) handles remote file names in two places so move the Unicode conversion and the new validation check into a separate function. The reserved file name predicate is placed into <winpr/file.h> so that it can be used in other places too. For example, other wClipboard local file subsystems will need it. (It would be really nice to enforce this check somewhere in the common code, so that the subsystems can't miss it, but other places can miss some errors thus we're doing it here, as early as possible.) The predicate acts on separate file name components rather than full file names because the backslash is a reserved character too. If we process full file names this can result in phantom directory entry in the remote file name. Not to say that handling ready-made components spares us from splitting the full file name to extract them :) The implementation is... a bit verbose, but that's fine by me. In the absence of functions for case-insensitive wide string comparison and the need to check for the [0-9] at the end of some file names this is quite readable. Thanks to FAT and NTFS for being case-insensitive and to MS-DOS for having reserved file names in the first place.
2017-04-09 02:29:52 +03:00
/**
* 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)
{
LPCWSTR c = NULL;
if (!lpFileName)
return FALSE;
wClipboard: disallow Windows reserved names Another issue revealed during testing is that older Windows systems cannot handle the reserved file names well. While Windows 8 and 10 are fine (they silently abort the file transfer), using reserved names with Windows 7 can flat out crash explorer.exe or result into weird error messages like "fatal error: 0x00000000 ERROR_SUCCESS". This is not required by MS-RDPECLIP specification, but we should try to avoid this issue as not using reserved file names seems to be assumed a common sense in Windows protocols. The most convenient way to handle the issue would be on wClipboard level so that WinPR's clients do not bother with it. We should prohibit the reserved names from being used in FILEDESCRIPTOR, failing the conversion if we see such a file. POSIX subsystem (the only one at the moment) handles remote file names in two places so move the Unicode conversion and the new validation check into a separate function. The reserved file name predicate is placed into <winpr/file.h> so that it can be used in other places too. For example, other wClipboard local file subsystems will need it. (It would be really nice to enforce this check somewhere in the common code, so that the subsystems can't miss it, but other places can miss some errors thus we're doing it here, as early as possible.) The predicate acts on separate file name components rather than full file names because the backslash is a reserved character too. If we process full file names this can result in phantom directory entry in the remote file name. Not to say that handling ready-made components spares us from splitting the full file name to extract them :) The implementation is... a bit verbose, but that's fine by me. In the absence of functions for case-insensitive wide string comparison and the need to check for the [0-9] at the end of some file names this is quite readable. Thanks to FAT and NTFS for being case-insensitive and to MS-DOS for having reserved file names in the first place.
2017-04-09 02:29:52 +03:00
/* 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 (c = lpFileName; *c; c++)
{
2019-11-06 17:24:51 +03:00
if ((*c == L'<') || (*c == L'>') || (*c == L':') || (*c == L'"') || (*c == L'/') ||
(*c == L'\\') || (*c == L'|') || (*c == L'?') || (*c == L'*'))
wClipboard: disallow Windows reserved names Another issue revealed during testing is that older Windows systems cannot handle the reserved file names well. While Windows 8 and 10 are fine (they silently abort the file transfer), using reserved names with Windows 7 can flat out crash explorer.exe or result into weird error messages like "fatal error: 0x00000000 ERROR_SUCCESS". This is not required by MS-RDPECLIP specification, but we should try to avoid this issue as not using reserved file names seems to be assumed a common sense in Windows protocols. The most convenient way to handle the issue would be on wClipboard level so that WinPR's clients do not bother with it. We should prohibit the reserved names from being used in FILEDESCRIPTOR, failing the conversion if we see such a file. POSIX subsystem (the only one at the moment) handles remote file names in two places so move the Unicode conversion and the new validation check into a separate function. The reserved file name predicate is placed into <winpr/file.h> so that it can be used in other places too. For example, other wClipboard local file subsystems will need it. (It would be really nice to enforce this check somewhere in the common code, so that the subsystems can't miss it, but other places can miss some errors thus we're doing it here, as early as possible.) The predicate acts on separate file name components rather than full file names because the backslash is a reserved character too. If we process full file names this can result in phantom directory entry in the remote file name. Not to say that handling ready-made components spares us from splitting the full file name to extract them :) The implementation is... a bit verbose, but that's fine by me. In the absence of functions for case-insensitive wide string comparison and the need to check for the [0-9] at the end of some file names this is quite readable. Thanks to FAT and NTFS for being case-insensitive and to MS-DOS for having reserved file names in the first place.
2017-04-09 02:29:52 +03:00
{
return FALSE;
}
}
return TRUE;
}
2016-03-29 23:03:15 +03:00
#ifdef _UWP
2019-11-06 17:24:51 +03:00
HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
2016-03-30 03:34:52 +03:00
{
HANDLE hFile;
CREATEFILE2_EXTENDED_PARAMETERS params = { 0 };
2016-03-30 03:34:52 +03:00
params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
2019-11-06 17:24:51 +03:00
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;
2016-03-30 03:34:52 +03:00
params.lpSecurityAttributes = lpSecurityAttributes;
params.hTemplateFile = hTemplateFile;
hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, &params);
return hFile;
}
2019-11-06 17:24:51 +03:00
HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
2016-03-30 03:34:52 +03:00
{
HANDLE hFile;
if (!lpFileName)
return NULL;
WCHAR* lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
2016-03-30 03:34:52 +03:00
if (!lpFileNameW)
return NULL;
hFile = CreateFileW(lpFileNameW, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
2019-11-06 17:24:51 +03:00
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
2016-03-30 03:34:52 +03:00
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;
}
2019-11-06 17:24:51 +03:00
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod)
2016-03-30 03:34:52 +03:00
{
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;
}
2016-03-29 23:03:15 +03:00
HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
{
2019-11-06 17:24:51 +03:00
return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
NULL, 0);
2016-03-29 23:03:15 +03:00
}
HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
{
2019-11-06 17:24:51 +03:00
return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
NULL, 0);
2016-03-29 23:03:15 +03:00
}
2016-03-30 03:34:52 +03:00
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);
2016-03-30 03:34:52 +03:00
if (!lpFileName || (nBufferLength < 1))
return 0;
lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
2016-03-30 03:34:52 +03:00
if (!lpFileNameW)
return 0;
2019-11-06 17:24:51 +03:00
lpBufferW = (WCHAR*)malloc(nBufferLengthW);
2016-03-30 03:34:52 +03:00
if (!lpBufferW)
return 0;
dwStatus = GetFullPathNameW(lpFileNameW, nBufferLengthW, lpBufferW, &lpFilePartW);
ConvertWCharNToUtf8(lpBufferW, nBufferLengthW / sizeof(WCHAR), lpBuffer, nBufferLength);
2016-03-30 03:34:52 +03:00
if (lpFilePart)
lpFilePart = lpBuffer + (lpFilePartW - lpBufferW);
free(lpFileNameW);
free(lpBufferW);
return dwStatus * 2;
}
2019-11-06 17:24:51 +03:00
BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
2016-03-30 03:34:52 +03:00
{
BOOL status;
ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
2019-11-06 17:24:51 +03:00
status = GetDiskFreeSpaceExA(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
&TotalNumberOfFreeBytes);
2016-03-30 03:34:52 +03:00
if (!status)
return FALSE;
*lpBytesPerSector = 1;
*lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
*lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
*lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
return TRUE;
}
BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
2019-11-06 17:24:51 +03:00
LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
LPDWORD lpTotalNumberOfClusters)
2016-03-30 03:34:52 +03:00
{
BOOL status;
ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
2019-11-06 17:24:51 +03:00
status = GetDiskFreeSpaceExW(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
&TotalNumberOfFreeBytes);
2016-03-30 03:34:52 +03:00
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;
}
2016-03-29 23:03:15 +03:00
#endif
/* Extended API */
2015-11-25 22:41:32 +03:00
#ifdef _WIN32
#include <io.h>
#endif
HANDLE GetFileHandleForFileDescriptor(int fd)
{
2015-11-25 22:41:32 +03:00
#ifdef _WIN32
return (HANDLE)_get_osfhandle(fd);
2019-11-06 17:24:51 +03:00
#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
}