FreeRDP
Loading...
Searching...
No Matches
generic.c
1
22#include <winpr/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/wlog.h>
26#include <winpr/string.h>
27#include <winpr/path.h>
28#include <winpr/file.h>
29
30#ifdef WINPR_HAVE_UNISTD_H
31#include <unistd.h>
32#endif
33
34#ifdef WINPR_HAVE_FCNTL_H
35#include <fcntl.h>
36#endif
37
38#include "../log.h"
39#define TAG WINPR_TAG("file")
40
41#ifdef _WIN32
42#include <io.h>
43#include <sys/stat.h>
44#else
45#include <winpr/assert.h>
46#include <pthread.h>
47#include <dirent.h>
48#include <libgen.h>
49#include <errno.h>
50
51#include <sys/un.h>
52#include <sys/stat.h>
53#include <sys/socket.h>
54
55#ifdef WINPR_HAVE_AIO_H
56#undef WINPR_HAVE_AIO_H /* disable for now, incomplete */
57#endif
58
59#ifdef WINPR_HAVE_AIO_H
60#include <aio.h>
61#endif
62
63#ifdef ANDROID
64#include <sys/vfs.h>
65#else
66#include <sys/statvfs.h>
67#endif
68
69#include "../handle/handle.h"
70
71#include "../pipe/pipe.h"
72
73#include "file.h"
74
178static wArrayList* HandleCreators;
179
180static pthread_once_t HandleCreatorsInitialized = PTHREAD_ONCE_INIT;
181
182#include "../comm/comm.h"
183#include "namedPipeClient.h"
184
185static void HandleCreatorsInit(void)
186{
187 WINPR_ASSERT(HandleCreators == NULL);
188 HandleCreators = ArrayList_New(TRUE);
189
190 if (!HandleCreators)
191 return;
192
193 /*
194 * Register all file handle creators.
195 */
196 ArrayList_Append(HandleCreators, GetNamedPipeClientHandleCreator());
197 const HANDLE_CREATOR* serial = GetCommHandleCreator();
198 if (serial)
199 ArrayList_Append(HandleCreators, serial);
200 ArrayList_Append(HandleCreators, GetFileHandleCreator());
201}
202
203#ifdef WINPR_HAVE_AIO_H
204
205static BOOL g_AioSignalHandlerInstalled = FALSE;
206
207void AioSignalHandler(int signum, siginfo_t* siginfo, void* arg)
208{
209 WLog_INFO("%d", signum);
210}
211
212int InstallAioSignalHandler()
213{
214 if (!g_AioSignalHandlerInstalled)
215 {
216 struct sigaction action;
217 sigemptyset(&action.sa_mask);
218 sigaddset(&action.sa_mask, SIGIO);
219 action.sa_flags = SA_SIGINFO;
220 action.sa_sigaction = (void*)&AioSignalHandler;
221 sigaction(SIGIO, &action, NULL);
222 g_AioSignalHandlerInstalled = TRUE;
223 }
224
225 return 0;
226}
227
228#endif /* WINPR_HAVE_AIO_H */
229
230HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
231 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
232 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
233{
234 if (!lpFileName)
235 return INVALID_HANDLE_VALUE;
236
237 if (pthread_once(&HandleCreatorsInitialized, HandleCreatorsInit) != 0)
238 {
239 SetLastError(ERROR_DLL_INIT_FAILED);
240 return INVALID_HANDLE_VALUE;
241 }
242
243 if (HandleCreators == NULL)
244 {
245 SetLastError(ERROR_DLL_INIT_FAILED);
246 return INVALID_HANDLE_VALUE;
247 }
248
249 ArrayList_Lock(HandleCreators);
250
251 for (size_t i = 0; i <= ArrayList_Count(HandleCreators); i++)
252 {
253 const HANDLE_CREATOR* creator = ArrayList_GetItem(HandleCreators, i);
254
255 if (creator && creator->IsHandled(lpFileName))
256 {
257 HANDLE newHandle =
258 creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
259 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
260 ArrayList_Unlock(HandleCreators);
261 return newHandle;
262 }
263 }
264
265 ArrayList_Unlock(HandleCreators);
266 return INVALID_HANDLE_VALUE;
267}
268
269HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
270 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
271 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
272{
273 HANDLE hdl = NULL;
274 if (!lpFileName)
275 return NULL;
276 char* lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL);
277
278 if (!lpFileNameA)
279 {
280 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
281 goto fail;
282 }
283
284 hdl = CreateFileA(lpFileNameA, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
285 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
286fail:
287 free(lpFileNameA);
288 return hdl;
289}
290
291BOOL DeleteFileA(LPCSTR lpFileName)
292{
293 int status = 0;
294 status = unlink(lpFileName);
295 return (status != -1) ? TRUE : FALSE;
296}
297
298BOOL DeleteFileW(LPCWSTR lpFileName)
299{
300 if (!lpFileName)
301 return FALSE;
302 LPSTR lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL);
303 BOOL rc = FALSE;
304
305 if (!lpFileNameA)
306 goto fail;
307
308 rc = DeleteFileA(lpFileNameA);
309fail:
310 free(lpFileNameA);
311 return rc;
312}
313
314BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
315 LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
316{
317 ULONG Type = 0;
318 WINPR_HANDLE* handle = NULL;
319
320 if (hFile == INVALID_HANDLE_VALUE)
321 return FALSE;
322
323 /*
324 * from http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
325 * lpNumberOfBytesRead can be NULL only when the lpOverlapped parameter is not NULL.
326 */
327
328 if (!lpNumberOfBytesRead && !lpOverlapped)
329 return FALSE;
330
331 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
332 return FALSE;
333
334 handle = (WINPR_HANDLE*)hFile;
335
336 if (handle->ops->ReadFile)
337 return handle->ops->ReadFile(handle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
338 lpOverlapped);
339
340 WLog_ERR(TAG, "ReadFile operation not implemented");
341 return FALSE;
342}
343
344BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
345 LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
346{
347 ULONG Type = 0;
348 WINPR_HANDLE* handle = NULL;
349
350 if (hFile == INVALID_HANDLE_VALUE)
351 return FALSE;
352
353 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
354 return FALSE;
355
356 handle = (WINPR_HANDLE*)hFile;
357
358 if (handle->ops->ReadFileEx)
359 return handle->ops->ReadFileEx(handle, lpBuffer, nNumberOfBytesToRead, lpOverlapped,
360 lpCompletionRoutine);
361
362 WLog_ERR(TAG, "ReadFileEx operation not implemented");
363 return FALSE;
364}
365
366BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead,
367 LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
368{
369 ULONG Type = 0;
370 WINPR_HANDLE* handle = NULL;
371
372 if (hFile == INVALID_HANDLE_VALUE)
373 return FALSE;
374
375 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
376 return FALSE;
377
378 handle = (WINPR_HANDLE*)hFile;
379
380 if (handle->ops->ReadFileScatter)
381 return handle->ops->ReadFileScatter(handle, aSegmentArray, nNumberOfBytesToRead, lpReserved,
382 lpOverlapped);
383
384 WLog_ERR(TAG, "ReadFileScatter operation not implemented");
385 return FALSE;
386}
387
388BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
389 LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
390{
391 ULONG Type = 0;
392 WINPR_HANDLE* handle = NULL;
393
394 if (hFile == INVALID_HANDLE_VALUE)
395 return FALSE;
396
397 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
398 return FALSE;
399
400 handle = (WINPR_HANDLE*)hFile;
401
402 if (handle->ops->WriteFile)
403 return handle->ops->WriteFile(handle, lpBuffer, nNumberOfBytesToWrite,
404 lpNumberOfBytesWritten, lpOverlapped);
405
406 WLog_ERR(TAG, "WriteFile operation not implemented");
407 return FALSE;
408}
409
410BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
411 LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
412{
413 ULONG Type = 0;
414 WINPR_HANDLE* handle = NULL;
415
416 if (hFile == INVALID_HANDLE_VALUE)
417 return FALSE;
418
419 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
420 return FALSE;
421
422 handle = (WINPR_HANDLE*)hFile;
423
424 if (handle->ops->WriteFileEx)
425 return handle->ops->WriteFileEx(handle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped,
426 lpCompletionRoutine);
427
428 WLog_ERR(TAG, "WriteFileEx operation not implemented");
429 return FALSE;
430}
431
432BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[],
433 DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
434{
435 ULONG Type = 0;
436 WINPR_HANDLE* handle = NULL;
437
438 if (hFile == INVALID_HANDLE_VALUE)
439 return FALSE;
440
441 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
442 return FALSE;
443
444 handle = (WINPR_HANDLE*)hFile;
445
446 if (handle->ops->WriteFileGather)
447 return handle->ops->WriteFileGather(handle, aSegmentArray, nNumberOfBytesToWrite,
448 lpReserved, lpOverlapped);
449
450 WLog_ERR(TAG, "WriteFileGather operation not implemented");
451 return FALSE;
452}
453
454BOOL FlushFileBuffers(HANDLE hFile)
455{
456 ULONG Type = 0;
457 WINPR_HANDLE* handle = NULL;
458
459 if (hFile == INVALID_HANDLE_VALUE)
460 return FALSE;
461
462 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
463 return FALSE;
464
465 handle = (WINPR_HANDLE*)hFile;
466
467 if (handle->ops->FlushFileBuffers)
468 return handle->ops->FlushFileBuffers(handle);
469
470 WLog_ERR(TAG, "FlushFileBuffers operation not implemented");
471 return FALSE;
472}
473
474BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName,
475 WINPR_ATTR_UNUSED GET_FILEEX_INFO_LEVELS fInfoLevelId,
476 LPVOID lpFileInformation)
477{
478 LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation;
479 WIN32_FIND_DATAA findFileData = { 0 };
480
481 if (!fd)
482 return FALSE;
483
484 HANDLE hFind = FindFirstFileA(lpFileName, &findFileData);
485 if (hFind == INVALID_HANDLE_VALUE)
486 return FALSE;
487
488 FindClose(hFind);
489 fd->dwFileAttributes = findFileData.dwFileAttributes;
490 fd->ftCreationTime = findFileData.ftCreationTime;
491 fd->ftLastAccessTime = findFileData.ftLastAccessTime;
492 fd->ftLastWriteTime = findFileData.ftLastWriteTime;
493 fd->nFileSizeHigh = findFileData.nFileSizeHigh;
494 fd->nFileSizeLow = findFileData.nFileSizeLow;
495 return TRUE;
496}
497
498BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
499 LPVOID lpFileInformation)
500{
501 BOOL ret = 0;
502 if (!lpFileName)
503 return FALSE;
504 LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
505
506 if (!lpCFileName)
507 {
508 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
509 return FALSE;
510 }
511
512 ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation);
513 free(lpCFileName);
514 return ret;
515}
516
517DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName)
518{
519 WIN32_FIND_DATAA findFileData = { 0 };
520 HANDLE hFind = FindFirstFileA(lpFileName, &findFileData);
521
522 if (hFind == INVALID_HANDLE_VALUE)
523 return INVALID_FILE_ATTRIBUTES;
524
525 FindClose(hFind);
526 return findFileData.dwFileAttributes;
527}
528
529DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
530{
531 DWORD ret = 0;
532 if (!lpFileName)
533 return FALSE;
534 LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
535 if (!lpCFileName)
536 {
537 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
538 return FALSE;
539 }
540
541 ret = GetFileAttributesA(lpCFileName);
542 free(lpCFileName);
543 return ret;
544}
545
546BOOL GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
547{
548 ULONG Type = 0;
549 WINPR_HANDLE* handle = NULL;
550
551 if (hFile == INVALID_HANDLE_VALUE)
552 return FALSE;
553
554 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
555 return FALSE;
556
557 handle = (WINPR_HANDLE*)hFile;
558
559 if (handle->ops->GetFileInformationByHandle)
560 return handle->ops->GetFileInformationByHandle(handle, lpFileInformation);
561
562 WLog_ERR(TAG, "GetFileInformationByHandle operation not implemented");
563 return 0;
564}
565
566static char* append(char* buffer, size_t size, const char* append)
567{
568 winpr_str_append(append, buffer, size, "|");
569 return buffer;
570}
571
572static const char* flagsToStr(char* buffer, size_t size, DWORD flags)
573{
574 char strflags[32] = { 0 };
575 if (flags & FILE_ATTRIBUTE_READONLY)
576 append(buffer, size, "FILE_ATTRIBUTE_READONLY");
577 if (flags & FILE_ATTRIBUTE_HIDDEN)
578 append(buffer, size, "FILE_ATTRIBUTE_HIDDEN");
579 if (flags & FILE_ATTRIBUTE_SYSTEM)
580 append(buffer, size, "FILE_ATTRIBUTE_SYSTEM");
581 if (flags & FILE_ATTRIBUTE_DIRECTORY)
582 append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY");
583 if (flags & FILE_ATTRIBUTE_ARCHIVE)
584 append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE");
585 if (flags & FILE_ATTRIBUTE_DEVICE)
586 append(buffer, size, "FILE_ATTRIBUTE_DEVICE");
587 if (flags & FILE_ATTRIBUTE_NORMAL)
588 append(buffer, size, "FILE_ATTRIBUTE_NORMAL");
589 if (flags & FILE_ATTRIBUTE_TEMPORARY)
590 append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY");
591 if (flags & FILE_ATTRIBUTE_SPARSE_FILE)
592 append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE");
593 if (flags & FILE_ATTRIBUTE_REPARSE_POINT)
594 append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT");
595 if (flags & FILE_ATTRIBUTE_COMPRESSED)
596 append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED");
597 if (flags & FILE_ATTRIBUTE_OFFLINE)
598 append(buffer, size, "FILE_ATTRIBUTE_OFFLINE");
599 if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
600 append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED");
601 if (flags & FILE_ATTRIBUTE_ENCRYPTED)
602 append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED");
603 if (flags & FILE_ATTRIBUTE_VIRTUAL)
604 append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL");
605
606 (void)_snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags);
607 winpr_str_append(strflags, buffer, size, NULL);
608 return buffer;
609}
610
611BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
612{
613 BOOL rc = FALSE;
614#ifdef WINPR_HAVE_FCNTL_H
615 struct stat st = { 0 };
616 int fd = 0;
617
618 if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
619 {
620 char buffer[8192] = { 0 };
621 const char* flags =
622 flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
623 WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags);
624 }
625
626 fd = open(lpFileName, O_RDONLY);
627 if (fd < 0)
628 return FALSE;
629
630 if (fstat(fd, &st) != 0)
631 goto fail;
632
633 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
634 {
635 st.st_mode &= WINPR_ASSERTING_INT_CAST(mode_t, (mode_t)(~(S_IWUSR | S_IWGRP | S_IWOTH)));
636 }
637 else
638 {
639 st.st_mode |= S_IWUSR;
640 }
641
642 if (fchmod(fd, st.st_mode) != 0)
643 goto fail;
644
645 rc = TRUE;
646fail:
647 close(fd);
648#endif
649 return rc;
650}
651
652BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
653{
654 BOOL ret = 0;
655 LPSTR lpCFileName = NULL;
656
657 if (!lpFileName)
658 return FALSE;
659
660 if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
661 {
662 char buffer[8192] = { 0 };
663 const char* flags =
664 flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
665 WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags);
666 }
667
668 lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
669 if (!lpCFileName)
670 {
671 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
672 return FALSE;
673 }
674
675 ret = SetFileAttributesA(lpCFileName, dwFileAttributes);
676 free(lpCFileName);
677 return ret;
678}
679
680BOOL SetEndOfFile(HANDLE hFile)
681{
682 ULONG Type = 0;
683 WINPR_HANDLE* handle = NULL;
684
685 if (hFile == INVALID_HANDLE_VALUE)
686 return FALSE;
687
688 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
689 return FALSE;
690
691 handle = (WINPR_HANDLE*)hFile;
692
693 if (handle->ops->SetEndOfFile)
694 return handle->ops->SetEndOfFile(handle);
695
696 WLog_ERR(TAG, "SetEndOfFile operation not implemented");
697 return FALSE;
698}
699
700DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
701{
702 ULONG Type = 0;
703 WINPR_HANDLE* handle = NULL;
704
705 if (hFile == INVALID_HANDLE_VALUE)
706 return FALSE;
707
708 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
709 return FALSE;
710
711 handle = (WINPR_HANDLE*)hFile;
712
713 if (handle->ops->GetFileSize)
714 return handle->ops->GetFileSize(handle, lpFileSizeHigh);
715
716 WLog_ERR(TAG, "GetFileSize operation not implemented");
717 return 0;
718}
719
720DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
721 DWORD dwMoveMethod)
722{
723 ULONG Type = 0;
724 WINPR_HANDLE* handle = NULL;
725
726 if (hFile == INVALID_HANDLE_VALUE)
727 return FALSE;
728
729 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
730 return FALSE;
731
732 handle = (WINPR_HANDLE*)hFile;
733
734 if (handle->ops->SetFilePointer)
735 return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh,
736 dwMoveMethod);
737
738 WLog_ERR(TAG, "SetFilePointer operation not implemented");
739 return 0;
740}
741
742BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer,
743 DWORD dwMoveMethod)
744{
745 ULONG Type = 0;
746 WINPR_HANDLE* handle = NULL;
747
748 if (hFile == INVALID_HANDLE_VALUE)
749 return FALSE;
750
751 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
752 return FALSE;
753
754 handle = (WINPR_HANDLE*)hFile;
755
756 if (handle->ops->SetFilePointerEx)
757 return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer,
758 dwMoveMethod);
759
760 WLog_ERR(TAG, "SetFilePointerEx operation not implemented");
761 return 0;
762}
763
764BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
765 DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
766{
767 ULONG Type = 0;
768 WINPR_HANDLE* handle = NULL;
769
770 if (hFile == INVALID_HANDLE_VALUE)
771 return FALSE;
772
773 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
774 return FALSE;
775
776 handle = (WINPR_HANDLE*)hFile;
777
778 if (handle->ops->LockFile)
779 return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
780 nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
781
782 WLog_ERR(TAG, "LockFile operation not implemented");
783 return FALSE;
784}
785
786BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow,
787 DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
788{
789 ULONG Type = 0;
790 WINPR_HANDLE* handle = NULL;
791
792 if (hFile == INVALID_HANDLE_VALUE)
793 return FALSE;
794
795 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
796 return FALSE;
797
798 handle = (WINPR_HANDLE*)hFile;
799
800 if (handle->ops->LockFileEx)
801 return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow,
802 nNumberOfBytesToLockHigh, lpOverlapped);
803
804 WLog_ERR(TAG, "LockFileEx operation not implemented");
805 return FALSE;
806}
807
808BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
809 DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
810{
811 ULONG Type = 0;
812 WINPR_HANDLE* handle = NULL;
813
814 if (hFile == INVALID_HANDLE_VALUE)
815 return FALSE;
816
817 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
818 return FALSE;
819
820 handle = (WINPR_HANDLE*)hFile;
821
822 if (handle->ops->UnlockFile)
823 return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
824 nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
825
826 WLog_ERR(TAG, "UnLockFile operation not implemented");
827 return FALSE;
828}
829
830BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
831 DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
832{
833 ULONG Type = 0;
834 WINPR_HANDLE* handle = NULL;
835
836 if (hFile == INVALID_HANDLE_VALUE)
837 return FALSE;
838
839 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
840 return FALSE;
841
842 handle = (WINPR_HANDLE*)hFile;
843
844 if (handle->ops->UnlockFileEx)
845 return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow,
846 nNumberOfBytesToUnlockHigh, lpOverlapped);
847
848 WLog_ERR(TAG, "UnLockFileEx operation not implemented");
849 return FALSE;
850}
851
852BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
853 const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
854{
855 ULONG Type = 0;
856 WINPR_HANDLE* handle = NULL;
857
858 if (hFile == INVALID_HANDLE_VALUE)
859 return FALSE;
860
861 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
862 return FALSE;
863
864 handle = (WINPR_HANDLE*)hFile;
865
866 if (handle->ops->SetFileTime)
867 return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
868
869 WLog_ERR(TAG, "operation not implemented");
870 return FALSE;
871}
872
873typedef struct
874{
875 char magic[16];
876 LPSTR lpPath;
877 LPSTR lpPattern;
878 DIR* pDir;
879} WIN32_FILE_SEARCH;
880
881static const char file_search_magic[] = "file_srch_magic";
882
883WINPR_ATTR_MALLOC(FindClose, 1)
884static WIN32_FILE_SEARCH* file_search_new(const char* name, size_t namelen, const char* pattern,
885 size_t patternlen)
886{
887 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH));
888 if (!pFileSearch)
889 return NULL;
890 WINPR_ASSERT(sizeof(file_search_magic) == sizeof(pFileSearch->magic));
891 memcpy(pFileSearch->magic, file_search_magic, sizeof(pFileSearch->magic));
892
893 pFileSearch->lpPath = strndup(name, namelen);
894 pFileSearch->lpPattern = strndup(pattern, patternlen);
895 if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
896 goto fail;
897
898 pFileSearch->pDir = opendir(pFileSearch->lpPath);
899 if (!pFileSearch->pDir)
900 {
901 /* Work around for android:
902 * parent directories are not accessible, so if we have a directory without pattern
903 * try to open it directly and set pattern to '*'
904 */
905 struct stat fileStat = { 0 };
906 if (stat(name, &fileStat) == 0)
907 {
908 if (S_ISDIR(fileStat.st_mode))
909 {
910 pFileSearch->pDir = opendir(name);
911 if (pFileSearch->pDir)
912 {
913 free(pFileSearch->lpPath);
914 free(pFileSearch->lpPattern);
915 pFileSearch->lpPath = _strdup(name);
916 pFileSearch->lpPattern = _strdup("*");
917 if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
918 {
919 closedir(pFileSearch->pDir);
920 pFileSearch->pDir = NULL;
921 }
922 }
923 }
924 }
925 }
926 if (!pFileSearch->pDir)
927 goto fail;
928
929 return pFileSearch;
930fail:
931 WINPR_PRAGMA_DIAG_PUSH
932 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
933 FindClose(pFileSearch);
934 WINPR_PRAGMA_DIAG_POP
935 return NULL;
936}
937
938static BOOL is_valid_file_search_handle(HANDLE handle)
939{
940 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)handle;
941 if (!pFileSearch)
942 return FALSE;
943 if (pFileSearch == INVALID_HANDLE_VALUE)
944 return FALSE;
945 if (strncmp(file_search_magic, pFileSearch->magic, sizeof(file_search_magic)) != 0)
946 return FALSE;
947 return TRUE;
948}
949static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
950 LPWIN32_FIND_DATAA lpFindFileData)
951{
952 UINT64 ft = 0;
953 char* lastSep = NULL;
954 lpFindFileData->dwFileAttributes = 0;
955
956 if (S_ISDIR(fileStat->st_mode))
957 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
958
959 if (lpFindFileData->dwFileAttributes == 0)
960 lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
961
962 lastSep = strrchr(path, '/');
963
964 if (lastSep)
965 {
966 const char* name = lastSep + 1;
967 const size_t namelen = strlen(name);
968
969 if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
970 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
971 }
972
973 if (!(fileStat->st_mode & S_IWUSR))
974 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
975
976#ifdef _DARWIN_FEATURE_64_BIT_INODE
977 ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
978#else
979 ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
980#endif
981 lpFindFileData->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
982 lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
983 ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
984 lpFindFileData->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
985 lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
986 ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
987 lpFindFileData->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
988 lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
989 lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
990 lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF;
991 return TRUE;
992}
993
994HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
995{
996 if (!lpFindFileData || !lpFileName)
997 {
998 SetLastError(ERROR_BAD_ARGUMENTS);
999 return INVALID_HANDLE_VALUE;
1000 }
1001
1002 const WIN32_FIND_DATAA empty = { 0 };
1003 *lpFindFileData = empty;
1004
1005 WIN32_FILE_SEARCH* pFileSearch = NULL;
1006 size_t patternlen = 0;
1007 const size_t flen = strlen(lpFileName);
1008 const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE);
1009 const char* ptr = strrchr(lpFileName, sep);
1010 if (!ptr)
1011 goto fail;
1012 patternlen = strlen(ptr + 1);
1013 if (patternlen == 0)
1014 goto fail;
1015
1016 pFileSearch = file_search_new(lpFileName, flen - patternlen, ptr + 1, patternlen);
1017
1018 if (!pFileSearch)
1019 {
1020 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1021 return INVALID_HANDLE_VALUE;
1022 }
1023
1024 if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData))
1025 return (HANDLE)pFileSearch;
1026
1027fail:
1028 FindClose(pFileSearch);
1029 return INVALID_HANDLE_VALUE;
1030}
1031
1032static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,
1033 LPWIN32_FIND_DATAW lpFindFileDataW)
1034{
1035 if (!lpFindFileDataA || !lpFindFileDataW)
1036 return FALSE;
1037
1038 lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes;
1039 lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime;
1040 lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime;
1041 lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime;
1042 lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh;
1043 lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow;
1044 lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0;
1045 lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1;
1046
1047 if (ConvertUtf8NToWChar(lpFindFileDataA->cFileName, ARRAYSIZE(lpFindFileDataA->cFileName),
1048 lpFindFileDataW->cFileName, ARRAYSIZE(lpFindFileDataW->cFileName)) < 0)
1049 return FALSE;
1050
1051 return ConvertUtf8NToWChar(lpFindFileDataA->cAlternateFileName,
1052 ARRAYSIZE(lpFindFileDataA->cAlternateFileName),
1053 lpFindFileDataW->cAlternateFileName,
1054 ARRAYSIZE(lpFindFileDataW->cAlternateFileName)) >= 0;
1055}
1056
1057HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1058{
1059 LPSTR utfFileName = NULL;
1060 HANDLE h = NULL;
1061 if (!lpFileName)
1062 return INVALID_HANDLE_VALUE;
1063
1065
1066 if (!fd)
1067 {
1068 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1069 return INVALID_HANDLE_VALUE;
1070 }
1071
1072 utfFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL);
1073 if (!utfFileName)
1074 {
1075 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1076 free(fd);
1077 return INVALID_HANDLE_VALUE;
1078 }
1079
1080 h = FindFirstFileA(utfFileName, fd);
1081 free(utfFileName);
1082
1083 if (h != INVALID_HANDLE_VALUE)
1084 {
1085 if (!ConvertFindDataAToW(fd, lpFindFileData))
1086 {
1087 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1088 FindClose(h);
1089 h = INVALID_HANDLE_VALUE;
1090 goto out;
1091 }
1092 }
1093
1094out:
1095 free(fd);
1096 return h;
1097}
1098
1099HANDLE FindFirstFileExA(WINPR_ATTR_UNUSED LPCSTR lpFileName,
1100 WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1101 WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1102 WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1103 WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1104 WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1105{
1106 WLog_ERR("TODO", "TODO: Implement");
1107 return INVALID_HANDLE_VALUE;
1108}
1109
1110HANDLE FindFirstFileExW(WINPR_ATTR_UNUSED LPCWSTR lpFileName,
1111 WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1112 WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1113 WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1114 WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1115 WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1116{
1117 WLog_ERR("TODO", "TODO: Implement");
1118 return INVALID_HANDLE_VALUE;
1119}
1120
1121BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
1122{
1123 if (!lpFindFileData)
1124 return FALSE;
1125
1126 const WIN32_FIND_DATAA empty = { 0 };
1127 *lpFindFileData = empty;
1128
1129 if (!is_valid_file_search_handle(hFindFile))
1130 return FALSE;
1131
1132 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1133 struct dirent* pDirent = NULL;
1134 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1135 while ((pDirent = readdir(pFileSearch->pDir)) != NULL)
1136 {
1137 if (FilePatternMatchA(pDirent->d_name, pFileSearch->lpPattern))
1138 {
1139 BOOL success = FALSE;
1140
1141 strncpy(lpFindFileData->cFileName, pDirent->d_name, MAX_PATH);
1142 const size_t namelen = strnlen(lpFindFileData->cFileName, MAX_PATH);
1143 size_t pathlen = strlen(pFileSearch->lpPath);
1144 char* fullpath = (char*)malloc(pathlen + namelen + 2);
1145
1146 if (fullpath == NULL)
1147 {
1148 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1149 return FALSE;
1150 }
1151
1152 memcpy(fullpath, pFileSearch->lpPath, pathlen);
1153 /* Ensure path is terminated with a separator, but prevent
1154 * duplicate separators */
1155 if (fullpath[pathlen - 1] != '/')
1156 fullpath[pathlen++] = '/';
1157 memcpy(fullpath + pathlen, pDirent->d_name, namelen);
1158 fullpath[pathlen + namelen] = 0;
1159
1160 struct stat fileStat = { 0 };
1161 if (stat(fullpath, &fileStat) != 0)
1162 {
1163 free(fullpath);
1164 SetLastError(map_posix_err(errno));
1165 errno = 0;
1166 continue;
1167 }
1168
1169 /* Skip FIFO entries. */
1170 if (S_ISFIFO(fileStat.st_mode))
1171 {
1172 free(fullpath);
1173 continue;
1174 }
1175
1176 success = FindDataFromStat(fullpath, &fileStat, lpFindFileData);
1177 free(fullpath);
1178 return success;
1179 }
1180 }
1181
1182 SetLastError(ERROR_NO_MORE_FILES);
1183 return FALSE;
1184}
1185
1186BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
1187{
1189
1190 if (!fd)
1191 {
1192 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1193 return FALSE;
1194 }
1195
1196 if (FindNextFileA(hFindFile, fd))
1197 {
1198 if (!ConvertFindDataAToW(fd, lpFindFileData))
1199 {
1200 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1201 free(fd);
1202 return FALSE;
1203 }
1204
1205 free(fd);
1206 return TRUE;
1207 }
1208
1209 free(fd);
1210 return FALSE;
1211}
1212
1213BOOL FindClose(HANDLE hFindFile)
1214{
1215 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1216 if (!pFileSearch)
1217 return FALSE;
1218
1219 /* Since INVALID_HANDLE_VALUE != NULL the analyzer guesses that there
1220 * is a initialized HANDLE that is not freed properly.
1221 * Disable this return to stop confusing the analyzer. */
1222#ifndef __clang_analyzer__
1223 if (!is_valid_file_search_handle(hFindFile))
1224 return FALSE;
1225#endif
1226
1227 free(pFileSearch->lpPath);
1228 free(pFileSearch->lpPattern);
1229
1230 if (pFileSearch->pDir)
1231 closedir(pFileSearch->pDir);
1232
1233 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1234 free(pFileSearch);
1235 return TRUE;
1236}
1237
1238BOOL CreateDirectoryA(LPCSTR lpPathName,
1239 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1240{
1241 if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR))
1242 return TRUE;
1243
1244 return FALSE;
1245}
1246
1247BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1248{
1249 if (!lpPathName)
1250 return FALSE;
1251 char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL);
1252 BOOL ret = FALSE;
1253
1254 if (!utfPathName)
1255 {
1256 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1257 goto fail;
1258 }
1259
1260 ret = CreateDirectoryA(utfPathName, lpSecurityAttributes);
1261fail:
1262 free(utfPathName);
1263 return ret;
1264}
1265
1266BOOL RemoveDirectoryA(LPCSTR lpPathName)
1267{
1268 int ret = rmdir(lpPathName);
1269
1270 if (ret != 0)
1271 SetLastError(map_posix_err(errno));
1272 else
1273 SetLastError(STATUS_SUCCESS);
1274
1275 return ret == 0;
1276}
1277
1278BOOL RemoveDirectoryW(LPCWSTR lpPathName)
1279{
1280 if (!lpPathName)
1281 return FALSE;
1282 char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL);
1283 BOOL ret = FALSE;
1284
1285 if (!utfPathName)
1286 {
1287 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1288 goto fail;
1289 }
1290
1291 ret = RemoveDirectoryA(utfPathName);
1292fail:
1293 free(utfPathName);
1294 return ret;
1295}
1296
1297BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
1298{
1299 struct stat st;
1300 int ret = 0;
1301 ret = stat(lpNewFileName, &st);
1302
1303 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
1304 {
1305 if (ret == 0)
1306 {
1307 SetLastError(ERROR_ALREADY_EXISTS);
1308 return FALSE;
1309 }
1310 }
1311 else
1312 {
1313 if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
1314 {
1315 SetLastError(ERROR_ACCESS_DENIED);
1316 return FALSE;
1317 }
1318 }
1319
1320 ret = rename(lpExistingFileName, lpNewFileName);
1321
1322 if (ret != 0)
1323 SetLastError(map_posix_err(errno));
1324
1325 return ret == 0;
1326}
1327
1328BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
1329{
1330 if (!lpExistingFileName || !lpNewFileName)
1331 return FALSE;
1332
1333 LPSTR lpCExistingFileName = ConvertWCharToUtf8Alloc(lpExistingFileName, NULL);
1334 LPSTR lpCNewFileName = ConvertWCharToUtf8Alloc(lpNewFileName, NULL);
1335 BOOL ret = FALSE;
1336
1337 if (!lpCExistingFileName || !lpCNewFileName)
1338 {
1339 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1340 goto fail;
1341 }
1342
1343 ret = MoveFileExA(lpCExistingFileName, lpCNewFileName, dwFlags);
1344fail:
1345 free(lpCNewFileName);
1346 free(lpCExistingFileName);
1347 return ret;
1348}
1349
1350BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
1351{
1352 return MoveFileExA(lpExistingFileName, lpNewFileName, 0);
1353}
1354
1355BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
1356{
1357 return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
1358}
1359
1360#endif
1361
1362/* Extended API */
1363
1364int UnixChangeFileMode(const char* filename, int flags)
1365{
1366 if (!filename)
1367 return -1;
1368#ifndef _WIN32
1369 mode_t fl = 0;
1370 fl |= (flags & 0x4000) ? S_ISUID : 0;
1371 fl |= (flags & 0x2000) ? S_ISGID : 0;
1372 fl |= (flags & 0x1000) ? S_ISVTX : 0;
1373 fl |= (flags & 0x0400) ? S_IRUSR : 0;
1374 fl |= (flags & 0x0200) ? S_IWUSR : 0;
1375 fl |= (flags & 0x0100) ? S_IXUSR : 0;
1376 fl |= (flags & 0x0040) ? S_IRGRP : 0;
1377 fl |= (flags & 0x0020) ? S_IWGRP : 0;
1378 fl |= (flags & 0x0010) ? S_IXGRP : 0;
1379 fl |= (flags & 0x0004) ? S_IROTH : 0;
1380 fl |= (flags & 0x0002) ? S_IWOTH : 0;
1381 fl |= (flags & 0x0001) ? S_IXOTH : 0;
1382 return chmod(filename, fl);
1383#else
1384 int rc;
1385 WCHAR* wfl = ConvertUtf8ToWCharAlloc(filename, NULL);
1386
1387 if (!wfl)
1388 return -1;
1389
1390 /* Check for unsupported flags. */
1391 if (flags & ~(_S_IREAD | _S_IWRITE))
1392 WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags);
1393
1394 rc = _wchmod(wfl, flags);
1395 free(wfl);
1396 return rc;
1397#endif
1398}