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