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#ifdef UF_HIDDEN
666 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
667 {
668 st.st_flags |= UF_HIDDEN;
669 }
670 else
671 {
672 st.st_flags &= ~UF_HIDDEN;
673 }
674 if (fchflags(fd, st.st_flags) != 0)
675 if (errno != ENOTSUP && errno != EOPNOTSUPP && errno != EINVAL && errno != ENOTTY)
676 goto fail;
677#endif
678
679 rc = TRUE;
680fail:
681 close(fd);
682#endif
683 return rc;
684}
685
686BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
687{
688 BOOL ret = 0;
689
690 if (!lpFileName)
691 return FALSE;
692
693 char* lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
694 if (!lpCFileName)
695 {
696 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
697 return FALSE;
698 }
699
700 ret = SetFileAttributesA(lpCFileName, dwFileAttributes);
701 free(lpCFileName);
702 return ret;
703}
704
705BOOL SetEndOfFile(HANDLE hFile)
706{
707 ULONG Type = 0;
708 WINPR_HANDLE* handle = nullptr;
709
710 if (hFile == INVALID_HANDLE_VALUE)
711 return FALSE;
712
713 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
714 return FALSE;
715
716 handle = (WINPR_HANDLE*)hFile;
717
718 if (handle->ops->SetEndOfFile)
719 return handle->ops->SetEndOfFile(handle);
720
721 WLog_ERR(TAG, "SetEndOfFile operation not implemented");
722 return FALSE;
723}
724
725DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
726{
727 ULONG Type = 0;
728 WINPR_HANDLE* handle = nullptr;
729
730 if (hFile == INVALID_HANDLE_VALUE)
731 return FALSE;
732
733 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
734 return FALSE;
735
736 handle = (WINPR_HANDLE*)hFile;
737
738 if (handle->ops->GetFileSize)
739 return handle->ops->GetFileSize(handle, lpFileSizeHigh);
740
741 WLog_ERR(TAG, "GetFileSize operation not implemented");
742 return 0;
743}
744
745DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
746 DWORD dwMoveMethod)
747{
748 ULONG Type = 0;
749 WINPR_HANDLE* handle = nullptr;
750
751 if (hFile == INVALID_HANDLE_VALUE)
752 return FALSE;
753
754 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
755 return FALSE;
756
757 handle = (WINPR_HANDLE*)hFile;
758
759 if (handle->ops->SetFilePointer)
760 return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh,
761 dwMoveMethod);
762
763 WLog_ERR(TAG, "SetFilePointer operation not implemented");
764 return 0;
765}
766
767BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer,
768 DWORD dwMoveMethod)
769{
770 ULONG Type = 0;
771 WINPR_HANDLE* handle = nullptr;
772
773 if (hFile == INVALID_HANDLE_VALUE)
774 return FALSE;
775
776 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
777 return FALSE;
778
779 handle = (WINPR_HANDLE*)hFile;
780
781 if (handle->ops->SetFilePointerEx)
782 return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer,
783 dwMoveMethod);
784
785 WLog_ERR(TAG, "SetFilePointerEx operation not implemented");
786 return 0;
787}
788
789BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
790 DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
791{
792 ULONG Type = 0;
793 WINPR_HANDLE* handle = nullptr;
794
795 if (hFile == INVALID_HANDLE_VALUE)
796 return FALSE;
797
798 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
799 return FALSE;
800
801 handle = (WINPR_HANDLE*)hFile;
802
803 if (handle->ops->LockFile)
804 return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
805 nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
806
807 WLog_ERR(TAG, "LockFile operation not implemented");
808 return FALSE;
809}
810
811BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow,
812 DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
813{
814 ULONG Type = 0;
815 WINPR_HANDLE* handle = nullptr;
816
817 if (hFile == INVALID_HANDLE_VALUE)
818 return FALSE;
819
820 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
821 return FALSE;
822
823 handle = (WINPR_HANDLE*)hFile;
824
825 if (handle->ops->LockFileEx)
826 return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow,
827 nNumberOfBytesToLockHigh, lpOverlapped);
828
829 WLog_ERR(TAG, "LockFileEx operation not implemented");
830 return FALSE;
831}
832
833BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
834 DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
835{
836 ULONG Type = 0;
837 WINPR_HANDLE* handle = nullptr;
838
839 if (hFile == INVALID_HANDLE_VALUE)
840 return FALSE;
841
842 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
843 return FALSE;
844
845 handle = (WINPR_HANDLE*)hFile;
846
847 if (handle->ops->UnlockFile)
848 return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
849 nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
850
851 WLog_ERR(TAG, "UnLockFile operation not implemented");
852 return FALSE;
853}
854
855BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
856 DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
857{
858 ULONG Type = 0;
859 WINPR_HANDLE* handle = nullptr;
860
861 if (hFile == INVALID_HANDLE_VALUE)
862 return FALSE;
863
864 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
865 return FALSE;
866
867 handle = (WINPR_HANDLE*)hFile;
868
869 if (handle->ops->UnlockFileEx)
870 return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow,
871 nNumberOfBytesToUnlockHigh, lpOverlapped);
872
873 WLog_ERR(TAG, "UnLockFileEx operation not implemented");
874 return FALSE;
875}
876
877BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
878 const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
879{
880 ULONG Type = 0;
881 WINPR_HANDLE* handle = nullptr;
882
883 if (hFile == INVALID_HANDLE_VALUE)
884 return FALSE;
885
886 if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
887 return FALSE;
888
889 handle = (WINPR_HANDLE*)hFile;
890
891 if (handle->ops->SetFileTime)
892 return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
893
894 WLog_ERR(TAG, "operation not implemented");
895 return FALSE;
896}
897
898typedef struct
899{
900 char magic[16];
901 LPSTR lpPath;
902 LPSTR lpPattern;
903 DIR* pDir;
904} WIN32_FILE_SEARCH;
905
906static const char file_search_magic[] = "file_srch_magic";
907
908WINPR_ATTR_MALLOC(FindClose, 1)
909static WIN32_FILE_SEARCH* file_search_new(const char* name, size_t namelen, const char* pattern,
910 size_t patternlen)
911{
912 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH));
913 if (!pFileSearch)
914 return nullptr;
915 WINPR_ASSERT(sizeof(file_search_magic) == sizeof(pFileSearch->magic));
916 memcpy(pFileSearch->magic, file_search_magic, sizeof(pFileSearch->magic));
917
918 pFileSearch->lpPath = strndup(name, namelen);
919 pFileSearch->lpPattern = strndup(pattern, patternlen);
920 if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
921 goto fail;
922
923 pFileSearch->pDir = opendir(pFileSearch->lpPath);
924 if (!pFileSearch->pDir)
925 {
926 /* Work around for android:
927 * parent directories are not accessible, so if we have a directory without pattern
928 * try to open it directly and set pattern to '*'
929 */
930 struct stat fileStat = WINPR_C_ARRAY_INIT;
931 if (stat(name, &fileStat) == 0)
932 {
933 if (S_ISDIR(fileStat.st_mode))
934 {
935 pFileSearch->pDir = opendir(name);
936 if (pFileSearch->pDir)
937 {
938 free(pFileSearch->lpPath);
939 free(pFileSearch->lpPattern);
940 pFileSearch->lpPath = _strdup(name);
941 pFileSearch->lpPattern = _strdup("*");
942 if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
943 {
944 closedir(pFileSearch->pDir);
945 pFileSearch->pDir = nullptr;
946 }
947 }
948 }
949 }
950 else
951 {
952 char buffer[128] = WINPR_C_ARRAY_INIT;
953 const DWORD err = map_posix_err(errno);
954 WLog_DBG(TAG, "stat failed with %s [%d] -> %s",
955 winpr_strerror(errno, buffer, sizeof(buffer)), errno,
956 Win32ErrorCode2Tag(err & 0xFFFF));
957 SetLastError(err);
958 }
959 }
960 if (!pFileSearch->pDir)
961 goto fail;
962
963 return pFileSearch;
964fail:
965 WINPR_PRAGMA_DIAG_PUSH
966 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
967 FindClose(pFileSearch);
968 WINPR_PRAGMA_DIAG_POP
969 return nullptr;
970}
971
972WINPR_ATTR_NODISCARD
973static BOOL is_valid_file_search_handle(HANDLE handle)
974{
975 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)handle;
976 if (!pFileSearch)
977 return FALSE;
978 if (pFileSearch == INVALID_HANDLE_VALUE)
979 return FALSE;
980 if (strncmp(file_search_magic, pFileSearch->magic, sizeof(file_search_magic)) != 0)
981 return FALSE;
982 return TRUE;
983}
984
985WINPR_ATTR_NODISCARD
986static DWORD GetDosAttributesFromXAttr(WINPR_ATTR_UNUSED const char* path)
987{
988#if defined(WINPR_HAVE_SYS_XATTR_H) || defined(WINPR_HAVE_LINUX_MSDOS_FS_H)
989 DWORD dwFileAttributes = 0;
990 uint32_t intAttr = 0;
991 ssize_t length = -1;
992 const DWORD supportedMask = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
993 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY |
994 FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL;
995
996#if defined(WINPR_HAVE_SYS_XATTR_H)
997 length = getxattr(path, "system.ntfs_attrib_be", &intAttr, sizeof(intAttr));
998 if (length >= 0)
999 {
1000 intAttr = ntohl(intAttr);
1001 dwFileAttributes = intAttr;
1002 }
1003#endif
1004
1005#if defined(WINPR_HAVE_SYS_XATTR_H)
1006 if ((dwFileAttributes & supportedMask) == 0)
1007 {
1008 length = getxattr(path, "user.cifs.dosattrib", &intAttr, sizeof(intAttr));
1009 if (length >= 0)
1010 dwFileAttributes = intAttr;
1011 }
1012#endif
1013
1014 if ((dwFileAttributes & supportedMask) == 0)
1015 {
1016#if defined(WINPR_HAVE_LINUX_MSDOS_FS_H)
1017 int fd = open(path, O_RDONLY | O_CLOEXEC);
1018 if (fd != -1)
1019 {
1020 const int rc = ioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &intAttr);
1021 if (rc < 0)
1022 {
1023 char buffer[64] = WINPR_C_ARRAY_INIT;
1024 WLog_DBG(TAG, "ioctl(%d, FAT_IOCTL_GET_ATTRIBUTES) failed with %s", fd,
1025 winpr_strerror(errno, buffer, sizeof(buffer)));
1026 }
1027 else
1028 dwFileAttributes = intAttr;
1029
1030 const int crc = close(fd);
1031 if (crc < 0)
1032 {
1033 char buffer[64] = WINPR_C_ARRAY_INIT;
1034 WLog_DBG(TAG, "close(%d) failed with %s", fd,
1035 winpr_strerror(errno, buffer, sizeof(buffer)));
1036 }
1037 }
1038#endif
1039 }
1040
1041#if defined(WINPR_HAVE_SYS_XATTR_H)
1042 if ((dwFileAttributes & supportedMask) == 0)
1043 {
1044 // if no xattr is set, default to archive attribute
1045 dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
1046
1047 char attrValue[11] = WINPR_C_ARRAY_INIT;
1048 length = getxattr(path, "user.DOSATTRIB", attrValue, sizeof(attrValue) - 1);
1049 if (length >= 0)
1050 {
1051 errno = 0;
1052 const unsigned long val = strtoul(attrValue, nullptr, 0);
1053 if (errno == 0)
1054 dwFileAttributes = WINPR_ASSERTING_INT_CAST(DWORD, val);
1055 }
1056 }
1057#else
1058 if ((dwFileAttributes & supportedMask) == 0)
1059 {
1060 /* fallback default when no xattr available */
1061 dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
1062 }
1063#endif
1064
1065 return dwFileAttributes;
1066#else
1067 return 0;
1068#endif
1069}
1070
1071static void SetDosAttributesToXAttr(const char* path, DWORD dwFileAttributes)
1072{
1073#if defined(WINPR_HAVE_SYS_XATTR_H) || defined(WINPR_HAVE_LINUX_MSDOS_FS_H)
1074 uint32_t intAttr =
1075 dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
1076 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL);
1077 uint32_t intAttrBE = htonl(intAttr);
1078
1079#if defined(WINPR_HAVE_SYS_XATTR_H)
1080 if (setxattr(path, "system.ntfs_attrib_be", &intAttrBE, sizeof(intAttrBE), 0) >= 0)
1081 {
1082 WLog_DBG(TAG, "Set NTFS attribute xattr for %s", path);
1083 return;
1084 }
1085
1086 /* set cifs.dosattrib xattr if it exists, otherwise set user.DOSATTRIB xattr */
1087 ssize_t length = getxattr(path, "user.cifs.dosattrib", nullptr, 0);
1088 if (length >= 0)
1089 {
1090 if (setxattr(path, "user.cifs.dosattrib", &intAttrBE, sizeof(intAttrBE), 0) >= 0)
1091 {
1092 WLog_DBG(TAG, "Set CIFS DOS attribute xattr for %s", path);
1093 return;
1094 }
1095 }
1096#endif
1097
1098 /* set FAT attributes if other methods fail */
1099#if defined(WINPR_HAVE_LINUX_MSDOS_FS_H)
1100 int fd = open(path, O_RDONLY | O_CLOEXEC);
1101 if (fd != -1)
1102 {
1103 if (ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &intAttr) != -1)
1104 {
1105 close(fd);
1106 WLog_DBG(TAG, "Set FAT attribute for %s", path);
1107 return;
1108 }
1109 close(fd);
1110 }
1111#endif
1112
1113#if defined(WINPR_HAVE_SYS_XATTR_H)
1114 /* set user.DOSATTRIB xattr */
1115
1116 /* if only ARCHIVE remove attribute */
1117 if (intAttr == FILE_ATTRIBUTE_ARCHIVE)
1118 {
1119 const int rc = removexattr(path, "user.DOSATTRIB");
1120 if (rc != 0)
1121 {
1122 char buffer[128] = WINPR_C_ARRAY_INIT;
1123 WLog_DBG(TAG, "removexattr(%s) failed with %s", path,
1124 winpr_strerror(errno, buffer, sizeof(buffer)));
1125 }
1126 return;
1127 }
1128
1129 char attrValue[11] = WINPR_C_ARRAY_INIT;
1130 const int written = snprintf(attrValue, sizeof(attrValue), "0x%x", intAttr);
1131 if (written < 0)
1132 return;
1133
1134 if (setxattr(path, "user.DOSATTRIB", attrValue, strlen(attrValue), 0) < 0)
1135 WLog_DBG(TAG, "Failed to set DOS attribute xattr for %s", path);
1136
1137 WLog_DBG(TAG, "Set DOS attribute xattr for %s", path);
1138#endif
1139#endif
1140}
1141
1142WINPR_ATTR_NODISCARD
1143static DWORD FileAttributesFromStat(const char* path, const struct stat* fileStat)
1144{
1145 DWORD dwFileAttributes = GetDosAttributesFromXAttr(path);
1146
1147 if (S_ISDIR(fileStat->st_mode))
1148 dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1149
1150 if (dwFileAttributes == 0)
1151 dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
1152
1153 const char* lastSep = strrchr(path, '/');
1154 if (lastSep)
1155 {
1156 const char* name = lastSep + 1;
1157 const size_t namelen = strlen(name);
1158
1159 if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
1160 dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1161 }
1162
1163#ifdef UF_HIDDEN
1164 if (fileStat->st_flags & UF_HIDDEN)
1165 dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1166#endif
1167
1168 if (!(fileStat->st_mode & S_IWUSR))
1169 dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1170
1171 return dwFileAttributes;
1172}
1173
1174WINPR_ATTR_NODISCARD
1175static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
1176 LPWIN32_FIND_DATAA lpFindFileData)
1177{
1178 UINT64 ft = 0;
1179 lpFindFileData->dwFileAttributes = FileAttributesFromStat(path, fileStat);
1180
1181#ifdef _DARWIN_FEATURE_64_BIT_INODE
1182 ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
1183#else
1184 ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
1185#endif
1186 lpFindFileData->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
1187 lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
1188 ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
1189 lpFindFileData->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
1190 lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
1191 ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
1192 lpFindFileData->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
1193 lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
1194 lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
1195 lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF;
1196 return TRUE;
1197}
1198
1199HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
1200{
1201 if (!lpFindFileData || !lpFileName)
1202 {
1203 SetLastError(ERROR_BAD_ARGUMENTS);
1204 return INVALID_HANDLE_VALUE;
1205 }
1206
1207 const WIN32_FIND_DATAA empty = WINPR_C_ARRAY_INIT;
1208 *lpFindFileData = empty;
1209
1210 WIN32_FILE_SEARCH* pFileSearch = nullptr;
1211 size_t patternlen = 0;
1212 const size_t flen = strlen(lpFileName);
1213 const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE);
1214 const char* ptr = strrchr(lpFileName, sep);
1215 if (!ptr)
1216 goto fail;
1217 patternlen = strlen(ptr + 1);
1218 if (patternlen == 0)
1219 goto fail;
1220
1221 pFileSearch = file_search_new(lpFileName, flen - patternlen, ptr + 1, patternlen);
1222
1223 if (!pFileSearch)
1224 return INVALID_HANDLE_VALUE;
1225
1226 if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData))
1227 return (HANDLE)pFileSearch;
1228
1229fail:
1230 FindClose(pFileSearch);
1231 return INVALID_HANDLE_VALUE;
1232}
1233
1234WINPR_ATTR_NODISCARD
1235static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,
1236 LPWIN32_FIND_DATAW lpFindFileDataW)
1237{
1238 if (!lpFindFileDataA || !lpFindFileDataW)
1239 return FALSE;
1240
1241 lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes;
1242 lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime;
1243 lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime;
1244 lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime;
1245 lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh;
1246 lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow;
1247 lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0;
1248 lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1;
1249
1250 if (ConvertUtf8NToWChar(lpFindFileDataA->cFileName, ARRAYSIZE(lpFindFileDataA->cFileName),
1251 lpFindFileDataW->cFileName, ARRAYSIZE(lpFindFileDataW->cFileName)) < 0)
1252 return FALSE;
1253
1254 return ConvertUtf8NToWChar(lpFindFileDataA->cAlternateFileName,
1255 ARRAYSIZE(lpFindFileDataA->cAlternateFileName),
1256 lpFindFileDataW->cAlternateFileName,
1257 ARRAYSIZE(lpFindFileDataW->cAlternateFileName)) >= 0;
1258}
1259
1260HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1261{
1262 LPSTR utfFileName = nullptr;
1263 HANDLE h = nullptr;
1264 if (!lpFileName)
1265 return INVALID_HANDLE_VALUE;
1266
1268
1269 if (!fd)
1270 {
1271 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1272 return INVALID_HANDLE_VALUE;
1273 }
1274
1275 utfFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
1276 if (!utfFileName)
1277 {
1278 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1279 free(fd);
1280 return INVALID_HANDLE_VALUE;
1281 }
1282
1283 h = FindFirstFileA(utfFileName, fd);
1284 free(utfFileName);
1285
1286 if (h != INVALID_HANDLE_VALUE)
1287 {
1288 if (!ConvertFindDataAToW(fd, lpFindFileData))
1289 {
1290 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1291 FindClose(h);
1292 h = INVALID_HANDLE_VALUE;
1293 goto out;
1294 }
1295 }
1296
1297out:
1298 free(fd);
1299 return h;
1300}
1301
1302HANDLE FindFirstFileExA(WINPR_ATTR_UNUSED LPCSTR lpFileName,
1303 WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1304 WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1305 WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1306 WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1307 WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1308{
1309 WLog_ERR("TODO", "TODO: Implement");
1310 return INVALID_HANDLE_VALUE;
1311}
1312
1313HANDLE FindFirstFileExW(WINPR_ATTR_UNUSED LPCWSTR lpFileName,
1314 WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1315 WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1316 WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1317 WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1318 WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1319{
1320 WLog_ERR("TODO", "TODO: Implement");
1321 return INVALID_HANDLE_VALUE;
1322}
1323
1324BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
1325{
1326 if (!lpFindFileData)
1327 return FALSE;
1328
1329 const WIN32_FIND_DATAA empty = WINPR_C_ARRAY_INIT;
1330 *lpFindFileData = empty;
1331
1332 if (!is_valid_file_search_handle(hFindFile))
1333 return FALSE;
1334
1335 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1336 struct dirent* pDirent = nullptr;
1337 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1338 while ((pDirent = readdir(pFileSearch->pDir)) != nullptr)
1339 {
1340 if (FilePatternMatchA(pDirent->d_name, pFileSearch->lpPattern))
1341 {
1342 BOOL success = FALSE;
1343
1344 strncpy(lpFindFileData->cFileName, pDirent->d_name, MAX_PATH);
1345 const size_t namelen = strnlen(lpFindFileData->cFileName, MAX_PATH);
1346 size_t pathlen = strlen(pFileSearch->lpPath);
1347 char* fullpath = (char*)malloc(pathlen + namelen + 2);
1348
1349 if (fullpath == nullptr)
1350 {
1351 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1352 return FALSE;
1353 }
1354
1355 memcpy(fullpath, pFileSearch->lpPath, pathlen);
1356 /* Ensure path is terminated with a separator, but prevent
1357 * duplicate separators */
1358 if (fullpath[pathlen - 1] != '/')
1359 fullpath[pathlen++] = '/';
1360 memcpy(fullpath + pathlen, pDirent->d_name, namelen);
1361 fullpath[pathlen + namelen] = 0;
1362
1363 char* canonical = winpr_PathCanonicalize(fullpath);
1364 free(fullpath);
1365
1366 if (!canonical)
1367 continue;
1368
1369 struct stat fileStat = WINPR_C_ARRAY_INIT;
1370 if (stat(canonical, &fileStat) != 0)
1371 {
1372 free(canonical);
1373 SetLastError(map_posix_err(errno));
1374 errno = 0;
1375 continue;
1376 }
1377
1378 /* Skip FIFO entries. */
1379 if (S_ISFIFO(fileStat.st_mode))
1380 {
1381 free(canonical);
1382 continue;
1383 }
1384
1385 success = FindDataFromStat(canonical, &fileStat, lpFindFileData);
1386 free(canonical);
1387 return success;
1388 }
1389 }
1390
1391 SetLastError(ERROR_NO_MORE_FILES);
1392 return FALSE;
1393}
1394
1395BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
1396{
1398
1399 if (!fd)
1400 {
1401 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1402 return FALSE;
1403 }
1404
1405 if (FindNextFileA(hFindFile, fd))
1406 {
1407 if (!ConvertFindDataAToW(fd, lpFindFileData))
1408 {
1409 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1410 free(fd);
1411 return FALSE;
1412 }
1413
1414 free(fd);
1415 return TRUE;
1416 }
1417
1418 free(fd);
1419 return FALSE;
1420}
1421
1422BOOL FindClose(HANDLE hFindFile)
1423{
1424 WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1425 if (!pFileSearch)
1426 return FALSE;
1427
1428 /* Since INVALID_HANDLE_VALUE != nullptr the analyzer guesses that there
1429 * is a initialized HANDLE that is not freed properly.
1430 * Disable this return to stop confusing the analyzer. */
1431#ifndef __clang_analyzer__
1432 if (!is_valid_file_search_handle(hFindFile))
1433 return FALSE;
1434#endif
1435
1436 free(pFileSearch->lpPath);
1437 free(pFileSearch->lpPattern);
1438
1439 if (pFileSearch->pDir)
1440 closedir(pFileSearch->pDir);
1441
1442 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1443 free(pFileSearch);
1444 return TRUE;
1445}
1446
1447BOOL CreateDirectoryA(LPCSTR lpPathName,
1448 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1449{
1450 return mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR) == 0;
1451}
1452
1453BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1454{
1455 if (!lpPathName)
1456 return FALSE;
1457 char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1458 BOOL ret = FALSE;
1459
1460 if (!utfPathName)
1461 {
1462 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1463 goto fail;
1464 }
1465
1466 ret = CreateDirectoryA(utfPathName, lpSecurityAttributes);
1467fail:
1468 free(utfPathName);
1469 return ret;
1470}
1471
1472#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1473BOOL RemoveDirectoryA(LPCSTR lpPathName)
1474{
1475 return winpr_RemoveDirectory(lpPathName);
1476}
1477#endif
1478
1479BOOL RemoveDirectoryW(LPCWSTR lpPathName)
1480{
1481 if (!lpPathName)
1482 return FALSE;
1483 char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1484 BOOL ret = FALSE;
1485
1486 if (!utfPathName)
1487 {
1488 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1489 goto fail;
1490 }
1491
1492 ret = winpr_RemoveDirectory(utfPathName);
1493fail:
1494 free(utfPathName);
1495 return ret;
1496}
1497
1498#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1499BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
1500{
1501 return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, dwFlags);
1502}
1503#endif
1504
1505BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
1506{
1507 if (!lpExistingFileName || !lpNewFileName)
1508 return FALSE;
1509
1510 LPSTR lpCExistingFileName = ConvertWCharToUtf8Alloc(lpExistingFileName, nullptr);
1511 LPSTR lpCNewFileName = ConvertWCharToUtf8Alloc(lpNewFileName, nullptr);
1512 BOOL ret = FALSE;
1513
1514 if (!lpCExistingFileName || !lpCNewFileName)
1515 {
1516 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1517 goto fail;
1518 }
1519
1520 ret = winpr_MoveFileEx(lpCExistingFileName, lpCNewFileName, dwFlags);
1521fail:
1522 free(lpCNewFileName);
1523 free(lpCExistingFileName);
1524 return ret;
1525}
1526
1527#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1528BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
1529{
1530 return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
1531}
1532#endif
1533
1534BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
1535{
1536 return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
1537}
1538
1539#endif
1540
1541/* Extended API */
1542
1543int UnixChangeFileMode(const char* filename, int flags)
1544{
1545 if (!filename)
1546 return -1;
1547#ifndef _WIN32
1548 mode_t fl = 0;
1549 fl |= (flags & 0x4000) ? S_ISUID : 0;
1550 fl |= (flags & 0x2000) ? S_ISGID : 0;
1551 fl |= (flags & 0x1000) ? S_ISVTX : 0;
1552 fl |= (flags & 0x0400) ? S_IRUSR : 0;
1553 fl |= (flags & 0x0200) ? S_IWUSR : 0;
1554 fl |= (flags & 0x0100) ? S_IXUSR : 0;
1555 fl |= (flags & 0x0040) ? S_IRGRP : 0;
1556 fl |= (flags & 0x0020) ? S_IWGRP : 0;
1557 fl |= (flags & 0x0010) ? S_IXGRP : 0;
1558 fl |= (flags & 0x0004) ? S_IROTH : 0;
1559 fl |= (flags & 0x0002) ? S_IWOTH : 0;
1560 fl |= (flags & 0x0001) ? S_IXOTH : 0;
1561 return chmod(filename, fl);
1562#else
1563 int rc;
1564 WCHAR* wfl = ConvertUtf8ToWCharAlloc(filename, nullptr);
1565
1566 if (!wfl)
1567 return -1;
1568
1569 /* Check for unsupported flags. */
1570 if (flags & ~(_S_IREAD | _S_IWRITE))
1571 WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags);
1572
1573 rc = _wchmod(wfl, flags);
1574 free(wfl);
1575 return rc;
1576#endif
1577}
1578
1579#if defined(_WIN32) || defined(_UWP)
1580HANDLE winpr_CreateFile(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1581 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1582 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1583{
1584 WCHAR* filename = ConvertUtf8ToWCharAlloc(lpFileName, nullptr);
1585 if (!filename)
1586 return nullptr;
1587
1588 HANDLE hdl = CreateFileW(filename, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1589 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
1590 free(filename);
1591 return hdl;
1592}
1593#endif