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