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