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