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