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