FreeRDP/winpr/libwinpr/file/file.c

1495 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>
#include <winpr/assert.h>
#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)
{
(void)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;
if (!hFile)
return FALSE;
const INT64 size = _ftelli64(pFile->fp);
if (size < 0)
return FALSE;
2017-03-18 00:05:21 +03:00
if (ftruncate(fileno(pFile->fp), (off_t)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;
}
static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove,
const 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
2024-08-29 12:11:11 +03:00
lpFileInformation->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
2022-03-22 11:25:40 +03:00
lpFileInformation->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
ft = STAT_TIME_TO_FILETIME(st.st_mtime);
2024-08-29 12:11:11 +03:00
lpFileInformation->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
2022-03-22 11:25:40 +03:00
lpFileInformation->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
ft = STAT_TIME_TO_FILETIME(st.st_atime);
2024-08-29 12:11:11 +03:00
lpFileInformation->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
2022-03-22 11:25:40 +03:00
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;
}
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
static struct timespec filetimeToTimespec(const FILETIME* ftime)
{
WINPR_ASSERT(ftime);
UINT64 tmp = FileTimeToUS(ftime);
struct timespec ts = { 0 };
ts.tv_sec = tmp / 1000000ULL;
ts.tv_nsec = (tmp % 1000000ULL) * 1000ULL;
return ts;
}
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
2016-02-26 12:48:53 +03:00
{
struct timespec times[2] = { { UTIME_OMIT, UTIME_OMIT },
{ UTIME_OMIT, UTIME_OMIT } }; /* last access, last modification */
2016-02-26 12:48:53 +03:00
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
if (!hFile)
return FALSE;
if (lpLastAccessTime)
times[0] = filetimeToTimespec(lpLastAccessTime);
if (lpLastWriteTime)
times[1] = filetimeToTimespec(lpLastWriteTime);
// TODO: Creation time can not be handled!
const int rc = futimens(fileno(pFile->fp), times);
if (rc != 0)
2016-02-26 13:22:56 +03:00
return FALSE;
return TRUE;
}
#elif defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
static struct timeval filetimeToTimeval(const FILETIME* ftime)
{
WINPR_ASSERT(ftime);
UINT64 tmp = FileTimeToUS(ftime);
struct timeval tv = { 0 };
tv.tv_sec = tmp / 1000000ULL;
tv.tv_usec = tmp % 1000000ULL;
return tv;
}
static struct timeval statToTimeval(const struct stat* sval)
{
WINPR_ASSERT(sval);
struct timeval tv = { 0 };
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
tv.tv_sec = sval->st_atime;
#ifdef _POSIX_SOURCE
TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atim);
#else
TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atimespec);
#endif
#elif defined(ANDROID)
tv.tv_sec = sval->st_atime;
tv.tv_usec = sval->st_atimensec / 1000UL;
#endif
return tv;
}
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
{
struct stat buf = { 0 };
/* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
if (!hFile)
return FALSE;
const int rc = fstat(fileno(pFile->fp), &buf);
if (rc < 0)
return FALSE;
struct timeval timevals[2] = { statToTimeval(&buf), statToTimeval(&buf) };
if (lpLastAccessTime)
timevals[0] = filetimeToTimeval(lpLastAccessTime);
if (lpLastWriteTime)
timevals[1] = filetimeToTimeval(lpLastWriteTime);
// TODO: Creation time can not be handled!
2016-02-26 12:48:53 +03:00
{
const int rc = utimes(pFile->lpFileName, timevals);
if (rc != 0)
return FALSE;
2016-02-26 12:48:53 +03:00
}
return TRUE;
}
2016-02-26 13:22:56 +03:00
#else
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
{
WINPR_FILE* pFile = (WINPR_FILE*)hFile;
if (!hFile)
2016-02-26 12:48:53 +03:00
return FALSE;
WLog_WARN(TAG, "TODO: Creation, Access and Write time can not be handled!");
WLog_WARN(TAG,
"TODO: Define _POSIX_C_SOURCE >= 200809L or implement a platform specific handler!");
2016-02-26 12:48:53 +03:00
return TRUE;
}
#endif
2016-02-26 12:48:53 +03:00
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;
}
(void)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 };
(void)_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)
{
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 (LPCWSTR c = lpFileName; *c; c++)
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
{
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;
(void)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
}