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