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