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