FreeRDP
Loading...
Searching...
No Matches
winpr/libwinpr/file/file.c
1
22#include <winpr/config.h>
23#include <winpr/debug.h>
24#include <winpr/assert.h>
25
26#include <winpr/wtypes.h>
27#include <winpr/crt.h>
28#include <winpr/file.h>
29
30#ifdef _WIN32
31
32#include <io.h>
33
34#else /* _WIN32 */
35
36#include "../log.h"
37#define TAG WINPR_TAG("file")
38
39#include <winpr/wlog.h>
40#include <winpr/string.h>
41
42#include "file.h"
43#include <errno.h>
44#include <fcntl.h>
45#include <sys/file.h>
46#include <sys/stat.h>
47#include <sys/time.h>
48
49#ifdef ANDROID
50#include <sys/vfs.h>
51#else
52#include <sys/statvfs.h>
53#endif
54
55#ifndef MIN
56#define MIN(x, y) (((x) < (y)) ? (x) : (y))
57#endif
58
59static WINPR_FILE* pStdHandleFile = NULL;
60
61static void GetStdHandle_Uninit(void) __attribute__((destructor));
62
63static BOOL FileIsHandled(HANDLE handle)
64{
65 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_FILE, FALSE);
66}
67
68static int FileGetFd(HANDLE handle)
69{
70 WINPR_FILE* file = (WINPR_FILE*)handle;
71
72 if (!FileIsHandled(handle))
73 return -1;
74
75 return fileno(file->fp);
76}
77
78static BOOL FileCloseHandleInt(HANDLE handle, BOOL force)
79{
80 WINPR_FILE* file = (WINPR_FILE*)handle;
81
82 if (!FileIsHandled(handle))
83 return FALSE;
84
85 if (!force)
86 {
87 if (handle == pStdHandleFile)
88 {
89 return FALSE;
90 }
91 }
92
93 if (file->fp)
94 {
95 /* Don't close stdin/stdout/stderr */
96 if (fileno(file->fp) > 2)
97 {
98 (void)fclose(file->fp);
99 file->fp = NULL;
100 }
101 }
102
103 free(file->lpFileName);
104 free(file);
105 return TRUE;
106}
107
108static BOOL FileCloseHandle(HANDLE handle)
109{
110 return FileCloseHandleInt(handle, FALSE);
111}
112
113#define log_error(fkt, file) log_error_((fkt), (file), __FILE__, __func__, __LINE__)
114static BOOL log_error_(const char* name, const WINPR_FILE* pFile, const char* file, const char* fkt,
115 size_t line)
116{
117 char ebuffer[256] = { 0 };
118
119 WINPR_ASSERT(name);
120 WINPR_ASSERT(pFile);
121 WINPR_ASSERT(file);
122 WINPR_ASSERT(fkt);
123
124 const DWORD level = WLOG_ERROR;
125 static wLog* log = NULL;
126 if (!log)
127 log = WLog_Get(TAG);
128
129 if (WLog_IsLevelActive(log, level))
130 {
131 WLog_PrintTextMessage(log, level, line, file, fkt, "%s %s failed with %s [0x08%x]", name,
132 pFile->lpFileName, winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
133 WINPR_CXX_COMPAT_CAST(unsigned, errno));
134 }
135 SetLastError(map_posix_err(errno));
136 return FALSE;
137}
138
139static BOOL FileSetEndOfFile(HANDLE hFile)
140{
141 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
142
143 if (!hFile)
144 return FALSE;
145
146 const INT64 size = _ftelli64(pFile->fp);
147 if (size < 0)
148 return FALSE;
149
150 if (ftruncate(fileno(pFile->fp), (off_t)size) < 0)
151 return log_error("ftruncate", pFile);
152
153 return TRUE;
154}
155
156// NOLINTBEGIN(readability-non-const-parameter)
157static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove,
158 const PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
159// NOLINTEND(readability-non-const-parameter)
160{
161 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
162 INT64 offset = 0;
163 int whence = 0;
164
165 if (!hFile)
166 return INVALID_SET_FILE_POINTER;
167
168 /* If there is a high part, the sign is contained in that
169 * and the low integer must be interpreted as unsigned. */
170 if (lpDistanceToMoveHigh)
171 {
172 offset = (INT64)(((UINT64)*lpDistanceToMoveHigh << 32U) | (UINT64)lDistanceToMove);
173 }
174 else
175 offset = lDistanceToMove;
176
177 switch (dwMoveMethod)
178 {
179 case FILE_BEGIN:
180 whence = SEEK_SET;
181 break;
182 case FILE_END:
183 whence = SEEK_END;
184 break;
185 case FILE_CURRENT:
186 whence = SEEK_CUR;
187 break;
188 default:
189 return INVALID_SET_FILE_POINTER;
190 }
191
192 if (_fseeki64(pFile->fp, offset, whence))
193 {
194 (void)log_error("_fseeki64", pFile);
195 return INVALID_SET_FILE_POINTER;
196 }
197
198 return (DWORD)_ftelli64(pFile->fp);
199}
200
201static BOOL FileSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove,
202 PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
203{
204 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
205 int whence = 0;
206
207 if (!hFile)
208 return FALSE;
209
210 switch (dwMoveMethod)
211 {
212 case FILE_BEGIN:
213 whence = SEEK_SET;
214 break;
215 case FILE_END:
216 whence = SEEK_END;
217 break;
218 case FILE_CURRENT:
219 whence = SEEK_CUR;
220 break;
221 default:
222 return FALSE;
223 }
224
225 if (_fseeki64(pFile->fp, liDistanceToMove.QuadPart, whence))
226 return log_error("_fseeki64", pFile);
227
228 if (lpNewFilePointer)
229 lpNewFilePointer->QuadPart = _ftelli64(pFile->fp);
230
231 return TRUE;
232}
233
234static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
235 LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
236{
237 size_t io_status = 0;
238 WINPR_FILE* file = NULL;
239 BOOL status = TRUE;
240
241 if (lpOverlapped)
242 {
243 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
244 SetLastError(ERROR_NOT_SUPPORTED);
245 return FALSE;
246 }
247
248 if (!Object)
249 return FALSE;
250
251 file = (WINPR_FILE*)Object;
252 clearerr(file->fp);
253 io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp);
254
255 if (io_status == 0 && ferror(file->fp))
256 {
257 status = FALSE;
258
259 switch (errno)
260 {
261 case EWOULDBLOCK:
262 SetLastError(ERROR_NO_DATA);
263 break;
264 default:
265 SetLastError(map_posix_err(errno));
266 }
267 }
268
269 if (lpNumberOfBytesRead)
270 *lpNumberOfBytesRead = (DWORD)io_status;
271
272 return status;
273}
274
275static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
276 LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
277{
278 size_t io_status = 0;
279 WINPR_FILE* file = NULL;
280
281 if (lpOverlapped)
282 {
283 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
284 SetLastError(ERROR_NOT_SUPPORTED);
285 return FALSE;
286 }
287
288 if (!Object)
289 return FALSE;
290
291 file = (WINPR_FILE*)Object;
292
293 clearerr(file->fp);
294 io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp);
295 if (io_status == 0 && ferror(file->fp))
296 {
297 SetLastError(map_posix_err(errno));
298 return FALSE;
299 }
300
301 *lpNumberOfBytesWritten = (DWORD)io_status;
302 return TRUE;
303}
304
305static DWORD FileGetFileSize(HANDLE Object, LPDWORD lpFileSizeHigh)
306{
307 WINPR_FILE* file = NULL;
308 INT64 cur = 0;
309 INT64 size = 0;
310
311 if (!Object)
312 return 0;
313
314 file = (WINPR_FILE*)Object;
315
316 cur = _ftelli64(file->fp);
317
318 if (cur < 0)
319 {
320 (void)log_error("_ftelli64", file);
321 return INVALID_FILE_SIZE;
322 }
323
324 if (_fseeki64(file->fp, 0, SEEK_END) != 0)
325 {
326 (void)log_error("_fseeki64", file);
327 return INVALID_FILE_SIZE;
328 }
329
330 size = _ftelli64(file->fp);
331
332 if (size < 0)
333 {
334 (void)log_error("_ftelli64", file);
335 return INVALID_FILE_SIZE;
336 }
337
338 if (_fseeki64(file->fp, cur, SEEK_SET) != 0)
339 {
340 (void)log_error("_fseeki64", file);
341 return INVALID_FILE_SIZE;
342 }
343
344 if (lpFileSizeHigh)
345 *lpFileSizeHigh = (UINT32)(size >> 32);
346
347 return (UINT32)(size & 0xFFFFFFFF);
348}
349
350static BOOL FileFlushFileBuffers(HANDLE hFile)
351{
352 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
353
354 if (!pFile->fp)
355 {
356 SetLastError(ERROR_INVALID_HANDLE);
357 return FALSE;
358 }
359
360 // See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
361 if ((pFile->dwOpenMode & GENERIC_WRITE) == 0)
362 {
363 SetLastError(ERROR_ACCESS_DENIED);
364 return FALSE;
365 }
366
367 if (fflush(pFile->fp) != 0)
368 {
369 SetLastError(map_posix_err(errno));
370 return FALSE;
371 }
372
373 return TRUE;
374}
375
376static BOOL FileGetFileInformationByHandle(HANDLE hFile,
377 LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
378{
379 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
380 struct stat st;
381 UINT64 ft = 0;
382 const char* lastSep = NULL;
383
384 if (!pFile)
385 return FALSE;
386 if (!lpFileInformation)
387 return FALSE;
388
389 if (fstat(fileno(pFile->fp), &st) == -1)
390 return log_error("fstat", pFile);
391
392 lpFileInformation->dwFileAttributes = 0;
393
394 if (S_ISDIR(st.st_mode))
395 lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
396
397 if (lpFileInformation->dwFileAttributes == 0)
398 lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
399
400 lastSep = strrchr(pFile->lpFileName, '/');
401
402 if (lastSep)
403 {
404 const char* name = lastSep + 1;
405 const size_t namelen = strlen(name);
406
407 if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
408 lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
409 }
410
411 if (!(st.st_mode & S_IWUSR))
412 lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
413
414#ifdef _DARWIN_FEATURE_64_BIT_INODE
415 ft = STAT_TIME_TO_FILETIME(st.st_birthtime);
416#else
417 ft = STAT_TIME_TO_FILETIME(st.st_ctime);
418#endif
419 lpFileInformation->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
420 lpFileInformation->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
421 ft = STAT_TIME_TO_FILETIME(st.st_mtime);
422 lpFileInformation->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
423 lpFileInformation->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
424 ft = STAT_TIME_TO_FILETIME(st.st_atime);
425 lpFileInformation->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
426 lpFileInformation->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
427 lpFileInformation->nFileSizeHigh = ((UINT64)st.st_size) >> 32ULL;
428 lpFileInformation->nFileSizeLow = st.st_size & 0xFFFFFFFF;
429 lpFileInformation->dwVolumeSerialNumber = (UINT32)st.st_dev;
430 lpFileInformation->nNumberOfLinks = (UINT32)st.st_nlink;
431 lpFileInformation->nFileIndexHigh = (st.st_ino >> 4) & 0xFFFFFFFF;
432 lpFileInformation->nFileIndexLow = st.st_ino & 0xFFFFFFFF;
433 return TRUE;
434}
435
436static BOOL FileLockFileEx(HANDLE hFile, DWORD dwFlags, WINPR_ATTR_UNUSED DWORD dwReserved,
437 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToLockLow,
438 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToLockHigh,
439 LPOVERLAPPED lpOverlapped)
440{
441#ifdef __sun
442 struct flock lock;
443 int lckcmd;
444#else
445 int lock = 0;
446#endif
447 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
448
449 if (lpOverlapped)
450 {
451 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
452 SetLastError(ERROR_NOT_SUPPORTED);
453 return FALSE;
454 }
455
456 if (!hFile)
457 return FALSE;
458
459 if (pFile->bLocked)
460 {
461 WLog_ERR(TAG, "File %s already locked!", pFile->lpFileName);
462 return FALSE;
463 }
464
465#ifdef __sun
466 lock.l_start = 0;
467 lock.l_len = 0;
468 lock.l_whence = SEEK_SET;
469
470 if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
471 lock.l_type = F_WRLCK;
472 else
473 lock.l_type = F_WRLCK;
474
475 if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
476 lckcmd = F_SETLK;
477 else
478 lckcmd = F_SETLKW;
479
480 if (fcntl(fileno(pFile->fp), lckcmd, &lock) == -1)
481 {
482 char ebuffer[256] = { 0 };
483 WLog_ERR(TAG, "F_SETLK failed with %s [0x%08X]",
484 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
485 SetLastError(map_posix_err(errno));
486 return FALSE;
487 }
488#else
489 if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
490 lock = LOCK_EX;
491 else
492 lock = LOCK_SH;
493
494 if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
495 lock |= LOCK_NB;
496
497 if (flock(fileno(pFile->fp), lock) < 0)
498 return log_error("flock", pFile);
499#endif
500
501 pFile->bLocked = TRUE;
502
503 return TRUE;
504}
505
506static BOOL FileUnlockFile(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFileOffsetLow,
507 WINPR_ATTR_UNUSED DWORD dwFileOffsetHigh,
508 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockLow,
509 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockHigh)
510{
511 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
512#ifdef __sun
513 struct flock lock;
514#endif
515
516 if (!hFile)
517 return FALSE;
518
519 if (!pFile->bLocked)
520 {
521 WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
522 return FALSE;
523 }
524
525#ifdef __sun
526 lock.l_start = 0;
527 lock.l_len = 0;
528 lock.l_whence = SEEK_SET;
529 lock.l_type = F_UNLCK;
530 if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
531 return log_error("F_GETLK", pFile);
532
533#else
534 if (flock(fileno(pFile->fp), LOCK_UN) < 0)
535 return log_error("flock", pFile);
536#endif
537
538 return TRUE;
539}
540
541static BOOL FileUnlockFileEx(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwReserved,
542 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockLow,
543 WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockHigh,
544 LPOVERLAPPED lpOverlapped)
545{
546 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
547#ifdef __sun
548 struct flock lock;
549#endif
550
551 if (lpOverlapped)
552 {
553 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
554 SetLastError(ERROR_NOT_SUPPORTED);
555 return FALSE;
556 }
557
558 if (!hFile)
559 return FALSE;
560
561 if (!pFile->bLocked)
562 {
563 WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
564 return FALSE;
565 }
566
567#ifdef __sun
568 lock.l_start = 0;
569 lock.l_len = 0;
570 lock.l_whence = SEEK_SET;
571 lock.l_type = F_UNLCK;
572 if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
573 return log_error("F_GETLK", pFile);
574#else
575 if (flock(fileno(pFile->fp), LOCK_UN) < 0)
576 return log_error("flock", pFile);
577#endif
578
579 return TRUE;
580}
581
582static INT64 FileTimeToUS(const FILETIME* ft)
583{
584 const INT64 EPOCH_DIFF_US = EPOCH_DIFF * 1000000LL;
585 INT64 tmp = ((INT64)ft->dwHighDateTime) << 32 | ft->dwLowDateTime;
586 tmp /= 10; /* 100ns steps to 1us step */
587 tmp -= EPOCH_DIFF_US;
588 return tmp;
589}
590
591#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
592static struct timespec filetimeToTimespec(const FILETIME* ftime)
593{
594 WINPR_ASSERT(ftime);
595 INT64 tmp = FileTimeToUS(ftime);
596 struct timespec ts = { 0 };
597 ts.tv_sec = tmp / 1000000LL;
598 ts.tv_nsec = (tmp % 1000000LL) * 1000LL;
599 return ts;
600}
601
602static BOOL FileSetFileTime(HANDLE hFile, WINPR_ATTR_UNUSED const FILETIME* lpCreationTime,
603 const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
604{
605 struct timespec times[2] = { { UTIME_OMIT, UTIME_OMIT },
606 { UTIME_OMIT, UTIME_OMIT } }; /* last access, last modification */
607 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
608
609 if (!hFile)
610 return FALSE;
611
612 if (lpLastAccessTime)
613 times[0] = filetimeToTimespec(lpLastAccessTime);
614
615 if (lpLastWriteTime)
616 times[1] = filetimeToTimespec(lpLastWriteTime);
617
618 // TODO: Creation time can not be handled!
619 const int rc = futimens(fileno(pFile->fp), times);
620 if (rc != 0)
621 {
622 char ebuffer[256] = { 0 };
623 WLog_ERR(TAG, "futimens failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
624 errno);
625 SetLastError(map_posix_err(errno));
626 return FALSE;
627 }
628
629 return TRUE;
630}
631#elif defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
632static struct timeval filetimeToTimeval(const FILETIME* ftime)
633{
634 WINPR_ASSERT(ftime);
635 UINT64 tmp = FileTimeToUS(ftime);
636 struct timeval tv = { 0 };
637 tv.tv_sec = tmp / 1000000ULL;
638 tv.tv_usec = tmp % 1000000ULL;
639 return tv;
640}
641
642static struct timeval statToTimeval(const struct stat* sval)
643{
644 WINPR_ASSERT(sval);
645 struct timeval tv = { 0 };
646#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
647 tv.tv_sec = sval->st_atime;
648#ifdef _POSIX_SOURCE
649 TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atim);
650#else
651 TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atimespec);
652#endif
653#elif defined(ANDROID)
654 tv.tv_sec = sval->st_atime;
655 tv.tv_usec = sval->st_atimensec / 1000UL;
656#endif
657 return tv;
658}
659
660static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
661 const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
662{
663 struct stat buf = { 0 };
664 /* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */
665 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
666
667 if (!hFile)
668 return FALSE;
669
670 const int rc = fstat(fileno(pFile->fp), &buf);
671 if (rc < 0)
672 {
673 char ebuffer[256] = { 0 };
674 WLog_ERR(TAG, "fstat failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
675 errno);
676 SetLastError(map_posix_err(errno));
677 return FALSE;
678 }
679
680 struct timeval timevals[2] = { statToTimeval(&buf), statToTimeval(&buf) };
681 if (lpLastAccessTime)
682 timevals[0] = filetimeToTimeval(lpLastAccessTime);
683
684 if (lpLastWriteTime)
685 timevals[1] = filetimeToTimeval(lpLastWriteTime);
686
687 // TODO: Creation time can not be handled!
688 {
689 const int res = utimes(pFile->lpFileName, timevals);
690 if (res != 0)
691 {
692 char ebuffer[256] = { 0 };
693 WLog_ERR(TAG, "utimes failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
694 errno);
695 SetLastError(map_posix_err(errno));
696 return FALSE;
697 }
698 }
699
700 return TRUE;
701}
702#else
703static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
704 const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
705{
706 WINPR_FILE* pFile = (WINPR_FILE*)hFile;
707
708 if (!hFile)
709 return FALSE;
710
711 WLog_WARN(TAG, "TODO: Creation, Access and Write time can not be handled!");
712 WLog_WARN(TAG,
713 "TODO: Define _POSIX_C_SOURCE >= 200809L or implement a platform specific handler!");
714 return TRUE;
715}
716#endif
717
718static HANDLE_OPS fileOps = {
719 FileIsHandled,
720 FileCloseHandle,
721 FileGetFd,
722 NULL, /* CleanupHandle */
723 FileRead,
724 NULL, /* FileReadEx */
725 NULL, /* FileReadScatter */
726 FileWrite,
727 NULL, /* FileWriteEx */
728 NULL, /* FileWriteGather */
729 FileGetFileSize,
730 FileFlushFileBuffers,
731 FileSetEndOfFile,
732 FileSetFilePointer,
733 FileSetFilePointerEx,
734 NULL, /* FileLockFile */
735 FileLockFileEx,
736 FileUnlockFile,
737 FileUnlockFileEx,
738 FileSetFileTime,
739 FileGetFileInformationByHandle,
740};
741
742static HANDLE_OPS shmOps = {
743 FileIsHandled,
744 FileCloseHandle,
745 FileGetFd,
746 NULL, /* CleanupHandle */
747 FileRead,
748 NULL, /* FileReadEx */
749 NULL, /* FileReadScatter */
750 FileWrite,
751 NULL, /* FileWriteEx */
752 NULL, /* FileWriteGather */
753 NULL, /* FileGetFileSize */
754 NULL, /* FlushFileBuffers */
755 NULL, /* FileSetEndOfFile */
756 NULL, /* FileSetFilePointer */
757 NULL, /* SetFilePointerEx */
758 NULL, /* FileLockFile */
759 NULL, /* FileLockFileEx */
760 NULL, /* FileUnlockFile */
761 NULL, /* FileUnlockFileEx */
762 NULL, /* FileSetFileTime */
763 FileGetFileInformationByHandle,
764};
765
766static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDisposition, BOOL* create)
767{
768 BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
769
770 switch (dwCreationDisposition)
771 {
772 case CREATE_ALWAYS:
773 *create = TRUE;
774 return (writeable) ? "wb+" : "rwb";
775 case CREATE_NEW:
776 *create = TRUE;
777 return "wb+";
778 case OPEN_ALWAYS:
779 *create = TRUE;
780 return "rb+";
781 case OPEN_EXISTING:
782 *create = FALSE;
783 return (writeable) ? "rb+" : "rb";
784 case TRUNCATE_EXISTING:
785 *create = FALSE;
786 return "wb+";
787 default:
788 *create = FALSE;
789 return "";
790 }
791}
792
793UINT32 map_posix_err(int fs_errno)
794{
795 NTSTATUS rc = 0;
796
797 /* try to return NTSTATUS version of error code */
798
799 switch (fs_errno)
800 {
801 case 0:
802 rc = STATUS_SUCCESS;
803 break;
804
805 case ENOTCONN:
806 case ENODEV:
807 case ENOTDIR:
808 case ENXIO:
809 rc = ERROR_FILE_NOT_FOUND;
810 break;
811
812 case EROFS:
813 case EPERM:
814 case EACCES:
815 rc = ERROR_ACCESS_DENIED;
816 break;
817
818 case ENOENT:
819 rc = ERROR_FILE_NOT_FOUND;
820 break;
821
822 case EBUSY:
823 rc = ERROR_BUSY_DRIVE;
824 break;
825
826 case EEXIST:
827 rc = ERROR_FILE_EXISTS;
828 break;
829
830 case EISDIR:
831 rc = STATUS_FILE_IS_A_DIRECTORY;
832 break;
833
834 case ENOTEMPTY:
835 rc = STATUS_DIRECTORY_NOT_EMPTY;
836 break;
837
838 case EMFILE:
839 rc = STATUS_TOO_MANY_OPENED_FILES;
840 break;
841
842 default:
843 {
844 char ebuffer[256] = { 0 };
845 WLog_ERR(TAG, "Missing ERRNO mapping %s [%d]",
846 winpr_strerror(fs_errno, ebuffer, sizeof(ebuffer)), fs_errno);
847 rc = STATUS_UNSUCCESSFUL;
848 }
849 break;
850 }
851
852 return (UINT32)rc;
853}
854
855static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
856 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
857 DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
858 HANDLE hTemplateFile)
859{
860 WINPR_FILE* pFile = NULL;
861 BOOL create = 0;
862 const char* mode = FileGetMode(dwDesiredAccess, dwCreationDisposition, &create);
863#ifdef __sun
864 struct flock lock;
865#else
866 int lock = 0;
867#endif
868 FILE* fp = NULL;
869 struct stat st;
870
871 if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
872 {
873 WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
874 SetLastError(ERROR_NOT_SUPPORTED);
875 return INVALID_HANDLE_VALUE;
876 }
877
878 pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
879 if (!pFile)
880 {
881 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
882 return INVALID_HANDLE_VALUE;
883 }
884
885 WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
886 pFile->common.ops = &fileOps;
887
888 pFile->lpFileName = _strdup(lpFileName);
889 if (!pFile->lpFileName)
890 {
891 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
892 free(pFile);
893 return INVALID_HANDLE_VALUE;
894 }
895
896 pFile->dwOpenMode = dwDesiredAccess;
897 pFile->dwShareMode = dwShareMode;
898 pFile->dwFlagsAndAttributes = dwFlagsAndAttributes;
899 pFile->lpSecurityAttributes = lpSecurityAttributes;
900 pFile->dwCreationDisposition = dwCreationDisposition;
901 pFile->hTemplateFile = hTemplateFile;
902
903 if (create)
904 {
905 if (dwCreationDisposition == CREATE_NEW)
906 {
907 if (stat(pFile->lpFileName, &st) == 0)
908 {
909 SetLastError(ERROR_FILE_EXISTS);
910 free(pFile->lpFileName);
911 free(pFile);
912 return INVALID_HANDLE_VALUE;
913 }
914 }
915
916 fp = winpr_fopen(pFile->lpFileName, "ab");
917 if (!fp)
918 {
919 SetLastError(map_posix_err(errno));
920 free(pFile->lpFileName);
921 free(pFile);
922 return INVALID_HANDLE_VALUE;
923 }
924
925 fp = freopen(pFile->lpFileName, mode, fp);
926 }
927 else
928 {
929 if (stat(pFile->lpFileName, &st) != 0)
930 {
931 SetLastError(map_posix_err(errno));
932 free(pFile->lpFileName);
933 free(pFile);
934 return INVALID_HANDLE_VALUE;
935 }
936
937 /* FIFO (named pipe) would block the following fopen
938 * call if not connected. This renders the channel unusable,
939 * therefore abort early. */
940 if (S_ISFIFO(st.st_mode))
941 {
942 SetLastError(ERROR_FILE_NOT_FOUND);
943 free(pFile->lpFileName);
944 free(pFile);
945 return INVALID_HANDLE_VALUE;
946 }
947 }
948
949 if (NULL == fp)
950 fp = winpr_fopen(pFile->lpFileName, mode);
951
952 pFile->fp = fp;
953 if (!pFile->fp)
954 {
955 /* This case can occur when trying to open a
956 * not existing file without create flag. */
957 SetLastError(map_posix_err(errno));
958 free(pFile->lpFileName);
959 free(pFile);
960 return INVALID_HANDLE_VALUE;
961 }
962
963 (void)setvbuf(fp, NULL, _IONBF, 0);
964
965#ifdef __sun
966 lock.l_start = 0;
967 lock.l_len = 0;
968 lock.l_whence = SEEK_SET;
969
970 if (dwShareMode & FILE_SHARE_READ)
971 lock.l_type = F_RDLCK;
972 if (dwShareMode & FILE_SHARE_WRITE)
973 lock.l_type = F_RDLCK;
974#else
975 if (dwShareMode & FILE_SHARE_READ)
976 lock = LOCK_SH;
977 if (dwShareMode & FILE_SHARE_WRITE)
978 lock = LOCK_EX;
979#endif
980
981 if (dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE))
982 {
983#ifdef __sun
984 if (fcntl(fileno(pFile->fp), F_SETLKW, &lock) == -1)
985#else
986 if (flock(fileno(pFile->fp), lock) < 0)
987#endif
988 {
989#ifdef __sun
990 (void)log_error("F_SETLKW", pFile);
991#else
992 (void)log_error("flock", pFile);
993#endif
994
995 SetLastError(map_posix_err(errno));
996 FileCloseHandle(pFile);
997 return INVALID_HANDLE_VALUE;
998 }
999
1000 pFile->bLocked = TRUE;
1001 }
1002
1003 if (fstat(fileno(pFile->fp), &st) == 0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
1004 {
1005 st.st_mode &= WINPR_ASSERTING_INT_CAST(mode_t, (mode_t)(~(S_IWUSR | S_IWGRP | S_IWOTH)));
1006 if (fchmod(fileno(pFile->fp), st.st_mode) != 0)
1007 {
1008 SetLastError(map_posix_err(errno));
1009 FileCloseHandle(pFile);
1010 return INVALID_HANDLE_VALUE;
1011 }
1012 }
1013
1014 SetLastError(STATUS_SUCCESS);
1015 return pFile;
1016}
1017
1018static BOOL IsFileDevice(WINPR_ATTR_UNUSED LPCTSTR lpDeviceName)
1019{
1020 return TRUE;
1021}
1022
1023static const HANDLE_CREATOR FileHandleCreator = { IsFileDevice, FileCreateFileA };
1024
1025const HANDLE_CREATOR* GetFileHandleCreator(void)
1026{
1027 return &FileHandleCreator;
1028}
1029
1030static WINPR_FILE* FileHandle_New(FILE* fp)
1031{
1032 WINPR_FILE* pFile = NULL;
1033 char name[MAX_PATH] = { 0 };
1034
1035 (void)_snprintf(name, sizeof(name), "device_%d", fileno(fp));
1036 pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
1037 if (!pFile)
1038 {
1039 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1040 return NULL;
1041 }
1042 pFile->fp = fp;
1043 pFile->common.ops = &shmOps;
1044 pFile->lpFileName = _strdup(name);
1045
1046 WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
1047 return pFile;
1048}
1049
1050void GetStdHandle_Uninit(void)
1051{
1052 FileCloseHandleInt(pStdHandleFile, TRUE);
1053}
1054
1055HANDLE GetStdHandle(DWORD nStdHandle)
1056{
1057 FILE* fp = NULL;
1058
1059 switch (nStdHandle)
1060 {
1061 case STD_INPUT_HANDLE:
1062 fp = stdin;
1063 break;
1064 case STD_OUTPUT_HANDLE:
1065 fp = stdout;
1066 break;
1067 case STD_ERROR_HANDLE:
1068 fp = stderr;
1069 break;
1070 default:
1071 return INVALID_HANDLE_VALUE;
1072 }
1073 if (!pStdHandleFile)
1074 pStdHandleFile = FileHandle_New(fp);
1075
1076 if (!pStdHandleFile)
1077 return INVALID_HANDLE_VALUE;
1078
1079 return (HANDLE)pStdHandleFile;
1080}
1081
1082BOOL SetStdHandle(WINPR_ATTR_UNUSED DWORD nStdHandle, WINPR_ATTR_UNUSED HANDLE hHandle)
1083{
1084 return FALSE;
1085}
1086
1087BOOL SetStdHandleEx(WINPR_ATTR_UNUSED DWORD dwStdHandle, WINPR_ATTR_UNUSED HANDLE hNewHandle,
1088 WINPR_ATTR_UNUSED HANDLE* phOldHandle)
1089{
1090 return FALSE;
1091}
1092
1093BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
1094 LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
1095{
1096#if defined(ANDROID)
1097#define STATVFS statfs
1098#else
1099#define STATVFS statvfs
1100#endif
1101
1102 struct STATVFS svfst = { 0 };
1103 STATVFS(lpRootPathName, &svfst);
1104 *lpSectorsPerCluster = (UINT32)MIN(svfst.f_frsize, UINT32_MAX);
1105 *lpBytesPerSector = 1;
1106 *lpNumberOfFreeClusters = (UINT32)MIN(svfst.f_bavail, UINT32_MAX);
1107 *lpTotalNumberOfClusters = (UINT32)MIN(svfst.f_blocks, UINT32_MAX);
1108 return TRUE;
1109}
1110
1111BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
1112 LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
1113 LPDWORD lpTotalNumberOfClusters)
1114{
1115 BOOL ret = 0;
1116 if (!lpRootPathName)
1117 return FALSE;
1118
1119 char* rootPathName = ConvertWCharToUtf8Alloc(lpRootPathName, NULL);
1120 if (!rootPathName)
1121 {
1122 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1123 return FALSE;
1124 }
1125 ret = GetDiskFreeSpaceA(rootPathName, lpSectorsPerCluster, lpBytesPerSector,
1126 lpNumberOfFreeClusters, lpTotalNumberOfClusters);
1127 free(rootPathName);
1128 return ret;
1129}
1130
1131#endif /* _WIN32 */
1132
1139BOOL ValidFileNameComponent(LPCWSTR lpFileName)
1140{
1141 if (!lpFileName)
1142 return FALSE;
1143
1144 /* CON */
1145 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
1146 (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
1147 (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
1148 (lpFileName[3] == L'\0'))
1149 {
1150 return FALSE;
1151 }
1152
1153 /* PRN */
1154 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'P' || lpFileName[0] == L'p')) &&
1155 (lpFileName[1] != L'\0' && (lpFileName[1] == L'R' || lpFileName[1] == L'r')) &&
1156 (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
1157 (lpFileName[3] == L'\0'))
1158 {
1159 return FALSE;
1160 }
1161
1162 /* AUX */
1163 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'A' || lpFileName[0] == L'a')) &&
1164 (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
1165 (lpFileName[2] != L'\0' && (lpFileName[2] == L'X' || lpFileName[2] == L'x')) &&
1166 (lpFileName[3] == L'\0'))
1167 {
1168 return FALSE;
1169 }
1170
1171 /* NUL */
1172 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'N' || lpFileName[0] == L'n')) &&
1173 (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
1174 (lpFileName[2] != L'\0' && (lpFileName[2] == L'L' || lpFileName[2] == L'l')) &&
1175 (lpFileName[3] == L'\0'))
1176 {
1177 return FALSE;
1178 }
1179
1180 /* LPT0-9 */
1181 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'L' || lpFileName[0] == L'l')) &&
1182 (lpFileName[1] != L'\0' && (lpFileName[1] == L'P' || lpFileName[1] == L'p')) &&
1183 (lpFileName[2] != L'\0' && (lpFileName[2] == L'T' || lpFileName[2] == L't')) &&
1184 (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
1185 (lpFileName[4] == L'\0'))
1186 {
1187 return FALSE;
1188 }
1189
1190 /* COM0-9 */
1191 if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
1192 (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
1193 (lpFileName[2] != L'\0' && (lpFileName[2] == L'M' || lpFileName[2] == L'm')) &&
1194 (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
1195 (lpFileName[4] == L'\0'))
1196 {
1197 return FALSE;
1198 }
1199
1200 /* Reserved characters */
1201 for (LPCWSTR c = lpFileName; *c; c++)
1202 {
1203 if ((*c == L'<') || (*c == L'>') || (*c == L':') || (*c == L'"') || (*c == L'/') ||
1204 (*c == L'\\') || (*c == L'|') || (*c == L'?') || (*c == L'*'))
1205 {
1206 return FALSE;
1207 }
1208 }
1209
1210 return TRUE;
1211}
1212
1213#ifdef _UWP
1214
1215HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1216 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1217 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1218{
1219 HANDLE hFile;
1220 CREATEFILE2_EXTENDED_PARAMETERS params = { 0 };
1221
1222 params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
1223
1224 if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
1225 params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS;
1226 if (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE)
1227 params.dwFileFlags |= FILE_FLAG_DELETE_ON_CLOSE;
1228 if (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING)
1229 params.dwFileFlags |= FILE_FLAG_NO_BUFFERING;
1230 if (dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL)
1231 params.dwFileFlags |= FILE_FLAG_OPEN_NO_RECALL;
1232 if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT)
1233 params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
1234 if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REQUIRING_OPLOCK)
1235 params.dwFileFlags |= FILE_FLAG_OPEN_REQUIRING_OPLOCK;
1236 if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
1237 params.dwFileFlags |= FILE_FLAG_OVERLAPPED;
1238 if (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS)
1239 params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS;
1240 if (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS)
1241 params.dwFileFlags |= FILE_FLAG_RANDOM_ACCESS;
1242 if (dwFlagsAndAttributes & FILE_FLAG_SESSION_AWARE)
1243 params.dwFileFlags |= FILE_FLAG_SESSION_AWARE;
1244 if (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN)
1245 params.dwFileFlags |= FILE_FLAG_SEQUENTIAL_SCAN;
1246 if (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH)
1247 params.dwFileFlags |= FILE_FLAG_WRITE_THROUGH;
1248
1249 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ARCHIVE)
1250 params.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
1251 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_COMPRESSED)
1252 params.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
1253 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DEVICE)
1254 params.dwFileAttributes |= FILE_ATTRIBUTE_DEVICE;
1255 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DIRECTORY)
1256 params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1257 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ENCRYPTED)
1258 params.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED;
1259 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_HIDDEN)
1260 params.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1261 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM)
1262 params.dwFileAttributes |= FILE_ATTRIBUTE_INTEGRITY_STREAM;
1263 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NORMAL)
1264 params.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
1265 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
1266 params.dwFileAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
1267 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA)
1268 params.dwFileAttributes |= FILE_ATTRIBUTE_NO_SCRUB_DATA;
1269 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_OFFLINE)
1270 params.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
1271 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
1272 params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1273 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1274 params.dwFileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
1275 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
1276 params.dwFileAttributes |= FILE_ATTRIBUTE_SPARSE_FILE;
1277 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SYSTEM)
1278 params.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM;
1279 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_TEMPORARY)
1280 params.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
1281 if (dwFlagsAndAttributes & FILE_ATTRIBUTE_VIRTUAL)
1282 params.dwFileAttributes |= FILE_ATTRIBUTE_VIRTUAL;
1283
1284 if (dwFlagsAndAttributes & SECURITY_ANONYMOUS)
1285 params.dwSecurityQosFlags |= SECURITY_ANONYMOUS;
1286 if (dwFlagsAndAttributes & SECURITY_CONTEXT_TRACKING)
1287 params.dwSecurityQosFlags |= SECURITY_CONTEXT_TRACKING;
1288 if (dwFlagsAndAttributes & SECURITY_DELEGATION)
1289 params.dwSecurityQosFlags |= SECURITY_DELEGATION;
1290 if (dwFlagsAndAttributes & SECURITY_EFFECTIVE_ONLY)
1291 params.dwSecurityQosFlags |= SECURITY_EFFECTIVE_ONLY;
1292 if (dwFlagsAndAttributes & SECURITY_IDENTIFICATION)
1293 params.dwSecurityQosFlags |= SECURITY_IDENTIFICATION;
1294 if (dwFlagsAndAttributes & SECURITY_IMPERSONATION)
1295 params.dwSecurityQosFlags |= SECURITY_IMPERSONATION;
1296
1297 params.lpSecurityAttributes = lpSecurityAttributes;
1298 params.hTemplateFile = hTemplateFile;
1299
1300 hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, &params);
1301
1302 return hFile;
1303}
1304
1305HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1306 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1307 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1308{
1309 HANDLE hFile;
1310 if (!lpFileName)
1311 return NULL;
1312
1313 WCHAR* lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
1314
1315 if (!lpFileNameW)
1316 return NULL;
1317
1318 hFile = CreateFileW(lpFileNameW, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1319 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
1320
1321 free(lpFileNameW);
1322
1323 return hFile;
1324}
1325
1326DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
1327{
1328 BOOL status;
1329 LARGE_INTEGER fileSize = { 0, 0 };
1330
1331 if (!lpFileSizeHigh)
1332 return INVALID_FILE_SIZE;
1333
1334 status = GetFileSizeEx(hFile, &fileSize);
1335
1336 if (!status)
1337 return INVALID_FILE_SIZE;
1338
1339 *lpFileSizeHigh = fileSize.HighPart;
1340
1341 return fileSize.LowPart;
1342}
1343
1344DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
1345 DWORD dwMoveMethod)
1346{
1347 BOOL status;
1348 LARGE_INTEGER liDistanceToMove = { 0, 0 };
1349 LARGE_INTEGER liNewFilePointer = { 0, 0 };
1350
1351 liDistanceToMove.LowPart = lDistanceToMove;
1352
1353 status = SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, dwMoveMethod);
1354
1355 if (!status)
1356 return INVALID_SET_FILE_POINTER;
1357
1358 if (lpDistanceToMoveHigh)
1359 *lpDistanceToMoveHigh = liNewFilePointer.HighPart;
1360
1361 return liNewFilePointer.LowPart;
1362}
1363
1364HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
1365{
1366 return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
1367 NULL, 0);
1368}
1369
1370HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1371{
1372 return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
1373 NULL, 0);
1374}
1375
1376DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart)
1377{
1378 DWORD dwStatus;
1379 WCHAR* lpFileNameW = NULL;
1380 WCHAR* lpBufferW = NULL;
1381 WCHAR* lpFilePartW = NULL;
1382 DWORD nBufferLengthW = nBufferLength * sizeof(WCHAR);
1383
1384 if (!lpFileName || (nBufferLength < 1))
1385 return 0;
1386
1387 lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
1388 if (!lpFileNameW)
1389 return 0;
1390
1391 lpBufferW = (WCHAR*)malloc(nBufferLengthW);
1392
1393 if (!lpBufferW)
1394 return 0;
1395
1396 dwStatus = GetFullPathNameW(lpFileNameW, nBufferLengthW, lpBufferW, &lpFilePartW);
1397
1398 (void)ConvertWCharNToUtf8(lpBufferW, nBufferLengthW / sizeof(WCHAR), lpBuffer, nBufferLength);
1399
1400 if (lpFilePart)
1401 lpFilePart = lpBuffer + (lpFilePartW - lpBufferW);
1402
1403 free(lpFileNameW);
1404 free(lpBufferW);
1405
1406 return dwStatus * 2;
1407}
1408
1409BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
1410 LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
1411{
1412 BOOL status;
1413 ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
1414 ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
1415 ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
1416
1417 status = GetDiskFreeSpaceExA(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
1418 &TotalNumberOfFreeBytes);
1419
1420 if (!status)
1421 return FALSE;
1422
1423 *lpBytesPerSector = 1;
1424 *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
1425 *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
1426 *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
1427
1428 return TRUE;
1429}
1430
1431BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
1432 LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
1433 LPDWORD lpTotalNumberOfClusters)
1434{
1435 BOOL status;
1436 ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
1437 ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
1438 ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
1439
1440 status = GetDiskFreeSpaceExW(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
1441 &TotalNumberOfFreeBytes);
1442
1443 if (!status)
1444 return FALSE;
1445
1446 *lpBytesPerSector = 1;
1447 *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
1448 *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
1449 *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
1450
1451 return TRUE;
1452}
1453
1454DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer)
1455{
1456 SetLastError(ERROR_INVALID_FUNCTION);
1457 return 0;
1458}
1459
1460DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer)
1461{
1462 SetLastError(ERROR_INVALID_FUNCTION);
1463 return 0;
1464}
1465
1466BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
1467{
1468 return FALSE;
1469}
1470
1471UINT GetACP(void)
1472{
1473 return CP_UTF8;
1474}
1475
1476#endif
1477
1478/* Extended API */
1479
1480#ifdef _WIN32
1481#include <io.h>
1482#endif
1483
1484HANDLE GetFileHandleForFileDescriptor(int fd)
1485{
1486#ifdef _WIN32
1487 return (HANDLE)_get_osfhandle(fd);
1488#else /* _WIN32 */
1489 WINPR_FILE* pFile = NULL;
1490 FILE* fp = NULL;
1491 int flags = 0;
1492
1493 /* Make sure it's a valid fd */
1494 if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
1495 return INVALID_HANDLE_VALUE;
1496
1497 flags = fcntl(fd, F_GETFL);
1498 if (flags == -1)
1499 return INVALID_HANDLE_VALUE;
1500
1501 if (flags & O_WRONLY)
1502 fp = fdopen(fd, "wb");
1503 else
1504 fp = fdopen(fd, "rb");
1505
1506 if (!fp)
1507 return INVALID_HANDLE_VALUE;
1508
1509 (void)setvbuf(fp, NULL, _IONBF, 0);
1510
1511 // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
1512 pFile = FileHandle_New(fp);
1513 if (!pFile)
1514 return INVALID_HANDLE_VALUE;
1515
1516 return (HANDLE)pFile;
1517#endif /* _WIN32 */
1518}
1519
1520FILE* winpr_fopen(const char* path, const char* mode)
1521{
1522#ifndef _WIN32
1523 return fopen(path, mode);
1524#else
1525 LPWSTR lpPathW = NULL;
1526 LPWSTR lpModeW = NULL;
1527 FILE* result = NULL;
1528
1529 if (!path || !mode)
1530 return NULL;
1531
1532 lpPathW = ConvertUtf8ToWCharAlloc(path, NULL);
1533 if (!lpPathW)
1534 goto cleanup;
1535
1536 lpModeW = ConvertUtf8ToWCharAlloc(mode, NULL);
1537 if (!lpModeW)
1538 goto cleanup;
1539
1540 result = _wfopen(lpPathW, lpModeW);
1541
1542cleanup:
1543 free(lpPathW);
1544 free(lpModeW);
1545 return result;
1546#endif
1547}
1548
1549#if !defined(_UWP) && !defined(_WIN32)
1550DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer)
1551{
1552 char* buffer = NULL;
1553 if (nBufferLength > 0)
1554 {
1555 buffer = calloc(nBufferLength, sizeof(char));
1556 if (!buffer)
1557 return 0;
1558 }
1559
1560 const DWORD rc = GetLogicalDriveStringsA(nBufferLength, buffer);
1561 if (buffer)
1562 ConvertMszUtf8NToWChar(buffer, rc, lpBuffer, nBufferLength);
1563 free(buffer);
1564 return rc;
1565}
1566
1567DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer)
1568{
1569 /* format is '<name1>\0<name2>\0...<nameX>\0\0'
1570 * for details see
1571 * https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getlogicaldrivestringsa
1572 */
1573 const char path[] = "/\0";
1574 const size_t len = sizeof(path);
1575 if (nBufferLength < len)
1576 return WINPR_ASSERTING_INT_CAST(DWORD, len);
1577
1578 memcpy(lpBuffer, path, len);
1579 return len - 1;
1580}
1581#endif