Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/file/generic.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * File Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2014 Hewlett-Packard Development Company, L.P.
7
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
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
75
/**
76
 * api-ms-win-core-file-l1-2-0.dll:
77
 *
78
 * CreateFileA
79
 * CreateFileW
80
 * CreateFile2
81
 * DeleteFileA
82
 * DeleteFileW
83
 * CreateDirectoryA
84
 * CreateDirectoryW
85
 * RemoveDirectoryA
86
 * RemoveDirectoryW
87
 * CompareFileTime
88
 * DefineDosDeviceW
89
 * DeleteVolumeMountPointW
90
 * FileTimeToLocalFileTime
91
 * LocalFileTimeToFileTime
92
 * FindClose
93
 * FindCloseChangeNotification
94
 * FindFirstChangeNotificationA
95
 * FindFirstChangeNotificationW
96
 * FindFirstFileA
97
 * FindFirstFileExA
98
 * FindFirstFileExW
99
 * FindFirstFileW
100
 * FindFirstVolumeW
101
 * FindNextChangeNotification
102
 * FindNextFileA
103
 * FindNextFileW
104
 * FindNextVolumeW
105
 * FindVolumeClose
106
 * GetDiskFreeSpaceA
107
 * GetDiskFreeSpaceExA
108
 * GetDiskFreeSpaceExW
109
 * GetDiskFreeSpaceW
110
 * GetDriveTypeA
111
 * GetDriveTypeW
112
 * GetFileAttributesA
113
 * GetFileAttributesExA
114
 * GetFileAttributesExW
115
 * GetFileAttributesW
116
 * GetFileInformationByHandle
117
 * GetFileSize
118
 * GetFileSizeEx
119
 * GetFileTime
120
 * GetFileType
121
 * GetFinalPathNameByHandleA
122
 * GetFinalPathNameByHandleW
123
 * GetFullPathNameA
124
 * GetFullPathNameW
125
 * GetLogicalDrives
126
 * GetLogicalDriveStringsW
127
 * GetLongPathNameA
128
 * GetLongPathNameW
129
 * GetShortPathNameW
130
 * GetTempFileNameW
131
 * GetTempPathW
132
 * GetVolumeInformationByHandleW
133
 * GetVolumeInformationW
134
 * GetVolumeNameForVolumeMountPointW
135
 * GetVolumePathNamesForVolumeNameW
136
 * GetVolumePathNameW
137
 * QueryDosDeviceW
138
 * SetFileAttributesA
139
 * SetFileAttributesW
140
 * SetFileTime
141
 * SetFileValidData
142
 * SetFileInformationByHandle
143
 * ReadFile
144
 * ReadFileEx
145
 * ReadFileScatter
146
 * WriteFile
147
 * WriteFileEx
148
 * WriteFileGather
149
 * FlushFileBuffers
150
 * SetEndOfFile
151
 * SetFilePointer
152
 * SetFilePointerEx
153
 * LockFile
154
 * LockFileEx
155
 * UnlockFile
156
 * UnlockFileEx
157
 */
158
159
/**
160
 * File System Behavior in the Microsoft Windows Environment:
161
 * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf
162
 */
163
164
/**
165
 * Asynchronous I/O - The GNU C Library:
166
 * http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html
167
 */
168
169
/**
170
 * aio.h - asynchronous input and output:
171
 * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
172
 */
173
174
/**
175
 * Asynchronous I/O User Guide:
176
 * http://code.google.com/p/kernel/wiki/AIOUserGuide
177
 */
178
static wArrayList* HandleCreators;
179
180
static pthread_once_t HandleCreatorsInitialized = PTHREAD_ONCE_INIT;
181
182
#include "../comm/comm.h"
183
#include "namedPipeClient.h"
184
185
static DWORD FileAttributesFromStat(const char* path, const struct stat* fileStat);
186
static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
187
                             LPWIN32_FIND_DATAA lpFindFileData);
188
189
static void HandleCreatorsInit(void)
190
0
{
191
0
  WINPR_ASSERT(HandleCreators == nullptr);
192
0
  HandleCreators = ArrayList_New(TRUE);
193
194
0
  if (!HandleCreators)
195
0
    return;
196
197
  /*
198
   * Register all file handle creators.
199
   */
200
0
  ArrayList_Append(HandleCreators, GetNamedPipeClientHandleCreator());
201
0
  const HANDLE_CREATOR* serial = GetCommHandleCreator();
202
0
  if (serial)
203
0
    ArrayList_Append(HandleCreators, serial);
204
0
  ArrayList_Append(HandleCreators, GetFileHandleCreator());
205
0
}
206
207
#ifdef WINPR_HAVE_AIO_H
208
209
static BOOL g_AioSignalHandlerInstalled = FALSE;
210
211
void AioSignalHandler(int signum, siginfo_t* siginfo, void* arg)
212
{
213
  WLog_INFO("%d", signum);
214
}
215
216
int InstallAioSignalHandler()
217
{
218
  if (!g_AioSignalHandlerInstalled)
219
  {
220
    struct sigaction action;
221
    sigemptyset(&action.sa_mask);
222
    sigaddset(&action.sa_mask, SIGIO);
223
    action.sa_flags = SA_SIGINFO;
224
    action.sa_sigaction = (void*)&AioSignalHandler;
225
    sigaction(SIGIO, &action, nullptr);
226
    g_AioSignalHandlerInstalled = TRUE;
227
  }
228
229
  return 0;
230
}
231
232
#endif /* WINPR_HAVE_AIO_H */
233
234
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
235
HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
236
                   LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
237
                   DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
238
0
{
239
0
  return winpr_CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
240
0
                          dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
241
0
}
242
#endif
243
244
HANDLE winpr_CreateFile(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
245
                        LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
246
                        DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
247
0
{
248
0
  if (!lpFileName)
249
0
    return INVALID_HANDLE_VALUE;
250
251
0
  if (pthread_once(&HandleCreatorsInitialized, HandleCreatorsInit) != 0)
252
0
  {
253
0
    SetLastError(ERROR_DLL_INIT_FAILED);
254
0
    return INVALID_HANDLE_VALUE;
255
0
  }
256
257
0
  if (HandleCreators == nullptr)
258
0
  {
259
0
    SetLastError(ERROR_DLL_INIT_FAILED);
260
0
    return INVALID_HANDLE_VALUE;
261
0
  }
262
263
0
  ArrayList_Lock(HandleCreators);
264
265
0
  for (size_t i = 0; i <= ArrayList_Count(HandleCreators); i++)
266
0
  {
267
0
    const HANDLE_CREATOR* creator = ArrayList_GetItem(HandleCreators, i);
268
269
0
    if (creator && creator->IsHandled(lpFileName))
270
0
    {
271
0
      HANDLE newHandle =
272
0
          creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
273
0
                               dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
274
0
      ArrayList_Unlock(HandleCreators);
275
0
      return newHandle;
276
0
    }
277
0
  }
278
279
0
  ArrayList_Unlock(HandleCreators);
280
0
  return INVALID_HANDLE_VALUE;
281
0
}
282
283
HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
284
                   LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
285
                   DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
286
0
{
287
0
  HANDLE hdl = nullptr;
288
0
  if (!lpFileName)
289
0
    return nullptr;
290
0
  char* lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
291
292
0
  if (!lpFileNameA)
293
0
  {
294
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
295
0
    goto fail;
296
0
  }
297
298
0
  hdl = winpr_CreateFile(lpFileNameA, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
299
0
                         dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
300
0
fail:
301
0
  free(lpFileNameA);
302
0
  return hdl;
303
0
}
304
305
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
306
BOOL DeleteFileA(LPCSTR lpFileName)
307
0
{
308
0
  return winpr_DeleteFile(lpFileName);
309
0
}
310
#endif
311
312
BOOL DeleteFileW(LPCWSTR lpFileName)
313
0
{
314
0
  if (!lpFileName)
315
0
    return FALSE;
316
0
  LPSTR lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
317
0
  BOOL rc = winpr_DeleteFile(lpFileNameA);
318
0
  free(lpFileNameA);
319
0
  return rc;
320
0
}
321
322
BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
323
              LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
324
0
{
325
0
  ULONG Type = 0;
326
0
  WINPR_HANDLE* handle = nullptr;
327
328
0
  if (hFile == INVALID_HANDLE_VALUE)
329
0
    return FALSE;
330
331
  /*
332
   * from http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
333
   * lpNumberOfBytesRead can be nullptr only when the lpOverlapped parameter is not nullptr.
334
   */
335
336
0
  if (!lpNumberOfBytesRead && !lpOverlapped)
337
0
    return FALSE;
338
339
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
340
0
    return FALSE;
341
342
0
  handle = (WINPR_HANDLE*)hFile;
343
344
0
  if (handle->ops->ReadFile)
345
0
    return handle->ops->ReadFile(handle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
346
0
                                 lpOverlapped);
347
348
0
  WLog_ERR(TAG, "ReadFile operation not implemented");
349
0
  return FALSE;
350
0
}
351
352
BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
353
                LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
354
0
{
355
0
  ULONG Type = 0;
356
0
  WINPR_HANDLE* handle = nullptr;
357
358
0
  if (hFile == INVALID_HANDLE_VALUE)
359
0
    return FALSE;
360
361
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
362
0
    return FALSE;
363
364
0
  handle = (WINPR_HANDLE*)hFile;
365
366
0
  if (handle->ops->ReadFileEx)
367
0
    return handle->ops->ReadFileEx(handle, lpBuffer, nNumberOfBytesToRead, lpOverlapped,
368
0
                                   lpCompletionRoutine);
369
370
0
  WLog_ERR(TAG, "ReadFileEx operation not implemented");
371
0
  return FALSE;
372
0
}
373
374
BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead,
375
                     LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
376
0
{
377
0
  ULONG Type = 0;
378
0
  WINPR_HANDLE* handle = nullptr;
379
380
0
  if (hFile == INVALID_HANDLE_VALUE)
381
0
    return FALSE;
382
383
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
384
0
    return FALSE;
385
386
0
  handle = (WINPR_HANDLE*)hFile;
387
388
0
  if (handle->ops->ReadFileScatter)
389
0
    return handle->ops->ReadFileScatter(handle, aSegmentArray, nNumberOfBytesToRead, lpReserved,
390
0
                                        lpOverlapped);
391
392
0
  WLog_ERR(TAG, "ReadFileScatter operation not implemented");
393
0
  return FALSE;
394
0
}
395
396
BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
397
               LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
398
0
{
399
0
  ULONG Type = 0;
400
0
  WINPR_HANDLE* handle = nullptr;
401
402
0
  if (hFile == INVALID_HANDLE_VALUE)
403
0
    return FALSE;
404
405
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
406
0
    return FALSE;
407
408
0
  handle = (WINPR_HANDLE*)hFile;
409
410
0
  if (handle->ops->WriteFile)
411
0
    return handle->ops->WriteFile(handle, lpBuffer, nNumberOfBytesToWrite,
412
0
                                  lpNumberOfBytesWritten, lpOverlapped);
413
414
0
  WLog_ERR(TAG, "WriteFile operation not implemented");
415
0
  return FALSE;
416
0
}
417
418
BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
419
                 LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
420
0
{
421
0
  ULONG Type = 0;
422
0
  WINPR_HANDLE* handle = nullptr;
423
424
0
  if (hFile == INVALID_HANDLE_VALUE)
425
0
    return FALSE;
426
427
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
428
0
    return FALSE;
429
430
0
  handle = (WINPR_HANDLE*)hFile;
431
432
0
  if (handle->ops->WriteFileEx)
433
0
    return handle->ops->WriteFileEx(handle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped,
434
0
                                    lpCompletionRoutine);
435
436
0
  WLog_ERR(TAG, "WriteFileEx operation not implemented");
437
0
  return FALSE;
438
0
}
439
440
BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[],
441
                     DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
442
0
{
443
0
  ULONG Type = 0;
444
0
  WINPR_HANDLE* handle = nullptr;
445
446
0
  if (hFile == INVALID_HANDLE_VALUE)
447
0
    return FALSE;
448
449
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
450
0
    return FALSE;
451
452
0
  handle = (WINPR_HANDLE*)hFile;
453
454
0
  if (handle->ops->WriteFileGather)
455
0
    return handle->ops->WriteFileGather(handle, aSegmentArray, nNumberOfBytesToWrite,
456
0
                                        lpReserved, lpOverlapped);
457
458
0
  WLog_ERR(TAG, "WriteFileGather operation not implemented");
459
0
  return FALSE;
460
0
}
461
462
BOOL FlushFileBuffers(HANDLE hFile)
463
0
{
464
0
  ULONG Type = 0;
465
0
  WINPR_HANDLE* handle = nullptr;
466
467
0
  if (hFile == INVALID_HANDLE_VALUE)
468
0
    return FALSE;
469
470
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
471
0
    return FALSE;
472
473
0
  handle = (WINPR_HANDLE*)hFile;
474
475
0
  if (handle->ops->FlushFileBuffers)
476
0
    return handle->ops->FlushFileBuffers(handle);
477
478
0
  WLog_ERR(TAG, "FlushFileBuffers operation not implemented");
479
0
  return FALSE;
480
0
}
481
482
BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName,
483
                                 WINPR_ATTR_UNUSED GET_FILEEX_INFO_LEVELS fInfoLevelId,
484
                                 LPVOID lpFileInformation)
485
0
{
486
0
  LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation;
487
0
  if (!fd)
488
0
    return FALSE;
489
490
0
  struct stat fileStat = WINPR_C_ARRAY_INIT;
491
0
  if (stat(lpFileName, &fileStat) != 0)
492
0
    return FALSE;
493
494
0
  WIN32_FIND_DATAA findFileData = WINPR_C_ARRAY_INIT;
495
0
  if (!FindDataFromStat(lpFileName, &fileStat, &findFileData))
496
0
    return FALSE;
497
498
0
  fd->dwFileAttributes = findFileData.dwFileAttributes;
499
0
  fd->ftCreationTime = findFileData.ftCreationTime;
500
0
  fd->ftLastAccessTime = findFileData.ftLastAccessTime;
501
0
  fd->ftLastWriteTime = findFileData.ftLastWriteTime;
502
0
  fd->nFileSizeHigh = findFileData.nFileSizeHigh;
503
0
  fd->nFileSizeLow = findFileData.nFileSizeLow;
504
0
  return TRUE;
505
0
}
506
507
BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
508
                                 LPVOID lpFileInformation)
509
0
{
510
0
  BOOL ret = 0;
511
0
  if (!lpFileName)
512
0
    return FALSE;
513
0
  LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
514
515
0
  if (!lpCFileName)
516
0
  {
517
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
518
0
    return FALSE;
519
0
  }
520
521
0
  ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation);
522
0
  free(lpCFileName);
523
0
  return ret;
524
0
}
525
526
DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName)
527
0
{
528
0
  struct stat fileStat = WINPR_C_ARRAY_INIT;
529
0
  if (stat(lpFileName, &fileStat) != 0)
530
0
    return INVALID_FILE_ATTRIBUTES;
531
532
0
  return FileAttributesFromStat(lpFileName, &fileStat);
533
0
}
534
535
DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
536
0
{
537
0
  DWORD ret = 0;
538
0
  if (!lpFileName)
539
0
    return FALSE;
540
0
  LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
541
0
  if (!lpCFileName)
542
0
  {
543
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
544
0
    return FALSE;
545
0
  }
546
547
0
  ret = GetFileAttributesA(lpCFileName);
548
0
  free(lpCFileName);
549
0
  return ret;
550
0
}
551
552
BOOL GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
553
0
{
554
0
  ULONG Type = 0;
555
0
  WINPR_HANDLE* handle = nullptr;
556
557
0
  if (hFile == INVALID_HANDLE_VALUE)
558
0
    return FALSE;
559
560
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
561
0
    return FALSE;
562
563
0
  handle = (WINPR_HANDLE*)hFile;
564
565
0
  if (handle->ops->GetFileInformationByHandle)
566
0
    return handle->ops->GetFileInformationByHandle(handle, lpFileInformation);
567
568
0
  WLog_ERR(TAG, "GetFileInformationByHandle operation not implemented");
569
0
  return 0;
570
0
}
571
572
static char* append(char* buffer, size_t size, const char* append)
573
0
{
574
0
  winpr_str_append(append, buffer, size, "|");
575
0
  return buffer;
576
0
}
577
578
static const char* flagsToStr(char* buffer, size_t size, DWORD flags)
579
0
{
580
0
  char strflags[32] = WINPR_C_ARRAY_INIT;
581
0
  if (flags & FILE_ATTRIBUTE_READONLY)
582
0
    append(buffer, size, "FILE_ATTRIBUTE_READONLY");
583
0
  if (flags & FILE_ATTRIBUTE_HIDDEN)
584
0
    append(buffer, size, "FILE_ATTRIBUTE_HIDDEN");
585
0
  if (flags & FILE_ATTRIBUTE_SYSTEM)
586
0
    append(buffer, size, "FILE_ATTRIBUTE_SYSTEM");
587
0
  if (flags & FILE_ATTRIBUTE_DIRECTORY)
588
0
    append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY");
589
0
  if (flags & FILE_ATTRIBUTE_ARCHIVE)
590
0
    append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE");
591
0
  if (flags & FILE_ATTRIBUTE_DEVICE)
592
0
    append(buffer, size, "FILE_ATTRIBUTE_DEVICE");
593
0
  if (flags & FILE_ATTRIBUTE_NORMAL)
594
0
    append(buffer, size, "FILE_ATTRIBUTE_NORMAL");
595
0
  if (flags & FILE_ATTRIBUTE_TEMPORARY)
596
0
    append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY");
597
0
  if (flags & FILE_ATTRIBUTE_SPARSE_FILE)
598
0
    append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE");
599
0
  if (flags & FILE_ATTRIBUTE_REPARSE_POINT)
600
0
    append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT");
601
0
  if (flags & FILE_ATTRIBUTE_COMPRESSED)
602
0
    append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED");
603
0
  if (flags & FILE_ATTRIBUTE_OFFLINE)
604
0
    append(buffer, size, "FILE_ATTRIBUTE_OFFLINE");
605
0
  if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
606
0
    append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED");
607
0
  if (flags & FILE_ATTRIBUTE_ENCRYPTED)
608
0
    append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED");
609
0
  if (flags & FILE_ATTRIBUTE_VIRTUAL)
610
0
    append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL");
611
612
0
  (void)_snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags);
613
0
  winpr_str_append(strflags, buffer, size, nullptr);
614
0
  return buffer;
615
0
}
616
617
BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
618
0
{
619
0
  BOOL rc = FALSE;
620
0
#ifdef WINPR_HAVE_FCNTL_H
621
0
  const uint32_t mask = ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_NORMAL);
622
0
  if (dwFileAttributes & mask)
623
0
  {
624
0
    char buffer[8192] = WINPR_C_ARRAY_INIT;
625
0
    const char* flags = flagsToStr(buffer, sizeof(buffer), dwFileAttributes & mask);
626
0
    WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags);
627
0
  }
628
629
0
  int fd = open(lpFileName, O_RDONLY);
630
0
  if (fd < 0)
631
0
    return FALSE;
632
633
0
  struct stat st = WINPR_C_ARRAY_INIT;
634
0
  if (fstat(fd, &st) != 0)
635
0
    goto fail;
636
637
0
  if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
638
0
  {
639
0
    st.st_mode &= WINPR_ASSERTING_INT_CAST(mode_t, (mode_t)(~(S_IWUSR | S_IWGRP | S_IWOTH)));
640
0
  }
641
0
  else
642
0
  {
643
0
    st.st_mode |= S_IWUSR;
644
0
  }
645
646
0
  if (fchmod(fd, st.st_mode) != 0)
647
0
    goto fail;
648
649
0
  rc = TRUE;
650
0
fail:
651
0
  close(fd);
652
0
#endif
653
0
  return rc;
654
0
}
655
656
BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
657
0
{
658
0
  BOOL ret = 0;
659
660
0
  if (!lpFileName)
661
0
    return FALSE;
662
663
0
  char* lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
664
0
  if (!lpCFileName)
665
0
  {
666
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
667
0
    return FALSE;
668
0
  }
669
670
0
  ret = SetFileAttributesA(lpCFileName, dwFileAttributes);
671
0
  free(lpCFileName);
672
0
  return ret;
673
0
}
674
675
BOOL SetEndOfFile(HANDLE hFile)
676
0
{
677
0
  ULONG Type = 0;
678
0
  WINPR_HANDLE* handle = nullptr;
679
680
0
  if (hFile == INVALID_HANDLE_VALUE)
681
0
    return FALSE;
682
683
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
684
0
    return FALSE;
685
686
0
  handle = (WINPR_HANDLE*)hFile;
687
688
0
  if (handle->ops->SetEndOfFile)
689
0
    return handle->ops->SetEndOfFile(handle);
690
691
0
  WLog_ERR(TAG, "SetEndOfFile operation not implemented");
692
0
  return FALSE;
693
0
}
694
695
DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
696
0
{
697
0
  ULONG Type = 0;
698
0
  WINPR_HANDLE* handle = nullptr;
699
700
0
  if (hFile == INVALID_HANDLE_VALUE)
701
0
    return FALSE;
702
703
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
704
0
    return FALSE;
705
706
0
  handle = (WINPR_HANDLE*)hFile;
707
708
0
  if (handle->ops->GetFileSize)
709
0
    return handle->ops->GetFileSize(handle, lpFileSizeHigh);
710
711
0
  WLog_ERR(TAG, "GetFileSize operation not implemented");
712
0
  return 0;
713
0
}
714
715
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
716
                     DWORD dwMoveMethod)
717
0
{
718
0
  ULONG Type = 0;
719
0
  WINPR_HANDLE* handle = nullptr;
720
721
0
  if (hFile == INVALID_HANDLE_VALUE)
722
0
    return FALSE;
723
724
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
725
0
    return FALSE;
726
727
0
  handle = (WINPR_HANDLE*)hFile;
728
729
0
  if (handle->ops->SetFilePointer)
730
0
    return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh,
731
0
                                       dwMoveMethod);
732
733
0
  WLog_ERR(TAG, "SetFilePointer operation not implemented");
734
0
  return 0;
735
0
}
736
737
BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer,
738
                      DWORD dwMoveMethod)
739
0
{
740
0
  ULONG Type = 0;
741
0
  WINPR_HANDLE* handle = nullptr;
742
743
0
  if (hFile == INVALID_HANDLE_VALUE)
744
0
    return FALSE;
745
746
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
747
0
    return FALSE;
748
749
0
  handle = (WINPR_HANDLE*)hFile;
750
751
0
  if (handle->ops->SetFilePointerEx)
752
0
    return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer,
753
0
                                         dwMoveMethod);
754
755
0
  WLog_ERR(TAG, "SetFilePointerEx operation not implemented");
756
0
  return 0;
757
0
}
758
759
BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
760
              DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
761
0
{
762
0
  ULONG Type = 0;
763
0
  WINPR_HANDLE* handle = nullptr;
764
765
0
  if (hFile == INVALID_HANDLE_VALUE)
766
0
    return FALSE;
767
768
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
769
0
    return FALSE;
770
771
0
  handle = (WINPR_HANDLE*)hFile;
772
773
0
  if (handle->ops->LockFile)
774
0
    return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
775
0
                                 nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
776
777
0
  WLog_ERR(TAG, "LockFile operation not implemented");
778
0
  return FALSE;
779
0
}
780
781
BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow,
782
                DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
783
0
{
784
0
  ULONG Type = 0;
785
0
  WINPR_HANDLE* handle = nullptr;
786
787
0
  if (hFile == INVALID_HANDLE_VALUE)
788
0
    return FALSE;
789
790
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
791
0
    return FALSE;
792
793
0
  handle = (WINPR_HANDLE*)hFile;
794
795
0
  if (handle->ops->LockFileEx)
796
0
    return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow,
797
0
                                   nNumberOfBytesToLockHigh, lpOverlapped);
798
799
0
  WLog_ERR(TAG, "LockFileEx operation not implemented");
800
0
  return FALSE;
801
0
}
802
803
BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
804
                DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
805
0
{
806
0
  ULONG Type = 0;
807
0
  WINPR_HANDLE* handle = nullptr;
808
809
0
  if (hFile == INVALID_HANDLE_VALUE)
810
0
    return FALSE;
811
812
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
813
0
    return FALSE;
814
815
0
  handle = (WINPR_HANDLE*)hFile;
816
817
0
  if (handle->ops->UnlockFile)
818
0
    return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
819
0
                                   nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
820
821
0
  WLog_ERR(TAG, "UnLockFile operation not implemented");
822
0
  return FALSE;
823
0
}
824
825
BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
826
                  DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
827
0
{
828
0
  ULONG Type = 0;
829
0
  WINPR_HANDLE* handle = nullptr;
830
831
0
  if (hFile == INVALID_HANDLE_VALUE)
832
0
    return FALSE;
833
834
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
835
0
    return FALSE;
836
837
0
  handle = (WINPR_HANDLE*)hFile;
838
839
0
  if (handle->ops->UnlockFileEx)
840
0
    return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow,
841
0
                                     nNumberOfBytesToUnlockHigh, lpOverlapped);
842
843
0
  WLog_ERR(TAG, "UnLockFileEx operation not implemented");
844
0
  return FALSE;
845
0
}
846
847
BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
848
                        const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
849
0
{
850
0
  ULONG Type = 0;
851
0
  WINPR_HANDLE* handle = nullptr;
852
853
0
  if (hFile == INVALID_HANDLE_VALUE)
854
0
    return FALSE;
855
856
0
  if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
857
0
    return FALSE;
858
859
0
  handle = (WINPR_HANDLE*)hFile;
860
861
0
  if (handle->ops->SetFileTime)
862
0
    return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
863
864
0
  WLog_ERR(TAG, "operation not implemented");
865
0
  return FALSE;
866
0
}
867
868
typedef struct
869
{
870
  char magic[16];
871
  LPSTR lpPath;
872
  LPSTR lpPattern;
873
  DIR* pDir;
874
} WIN32_FILE_SEARCH;
875
876
static const char file_search_magic[] = "file_srch_magic";
877
878
WINPR_ATTR_MALLOC(FindClose, 1)
879
static WIN32_FILE_SEARCH* file_search_new(const char* name, size_t namelen, const char* pattern,
880
                                          size_t patternlen)
881
28.0k
{
882
28.0k
  WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH));
883
28.0k
  if (!pFileSearch)
884
0
    return nullptr;
885
28.0k
  WINPR_ASSERT(sizeof(file_search_magic) == sizeof(pFileSearch->magic));
886
28.0k
  memcpy(pFileSearch->magic, file_search_magic, sizeof(pFileSearch->magic));
887
888
28.0k
  pFileSearch->lpPath = strndup(name, namelen);
889
28.0k
  pFileSearch->lpPattern = strndup(pattern, patternlen);
890
28.0k
  if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
891
0
    goto fail;
892
893
28.0k
  pFileSearch->pDir = opendir(pFileSearch->lpPath);
894
28.0k
  if (!pFileSearch->pDir)
895
187
  {
896
    /* Work around for android:
897
     * parent directories are not accessible, so if we have a directory without pattern
898
     * try to open it directly and set pattern to '*'
899
     */
900
187
    struct stat fileStat = WINPR_C_ARRAY_INIT;
901
187
    if (stat(name, &fileStat) == 0)
902
0
    {
903
0
      if (S_ISDIR(fileStat.st_mode))
904
0
      {
905
0
        pFileSearch->pDir = opendir(name);
906
0
        if (pFileSearch->pDir)
907
0
        {
908
0
          free(pFileSearch->lpPath);
909
0
          free(pFileSearch->lpPattern);
910
0
          pFileSearch->lpPath = _strdup(name);
911
0
          pFileSearch->lpPattern = _strdup("*");
912
0
          if (!pFileSearch->lpPath || !pFileSearch->lpPattern)
913
0
          {
914
0
            closedir(pFileSearch->pDir);
915
0
            pFileSearch->pDir = nullptr;
916
0
          }
917
0
        }
918
0
      }
919
0
    }
920
187
    else
921
187
    {
922
187
      char buffer[128] = WINPR_C_ARRAY_INIT;
923
187
      const DWORD err = map_posix_err(errno);
924
187
      WLog_DBG(TAG, "stat failed with %s [%d] -> %s",
925
187
               winpr_strerror(errno, buffer, sizeof(buffer)), errno,
926
187
               Win32ErrorCode2Tag(err & 0xFFFF));
927
187
      SetLastError(err);
928
187
    }
929
187
  }
930
28.0k
  if (!pFileSearch->pDir)
931
187
    goto fail;
932
933
27.8k
  return pFileSearch;
934
187
fail:
935
187
  WINPR_PRAGMA_DIAG_PUSH
936
187
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
937
187
  FindClose(pFileSearch);
938
187
  WINPR_PRAGMA_DIAG_POP
939
187
  return nullptr;
940
28.0k
}
941
942
static BOOL is_valid_file_search_handle(HANDLE handle)
943
88.5k
{
944
88.5k
  WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)handle;
945
88.5k
  if (!pFileSearch)
946
0
    return FALSE;
947
88.5k
  if (pFileSearch == INVALID_HANDLE_VALUE)
948
0
    return FALSE;
949
88.5k
  if (strncmp(file_search_magic, pFileSearch->magic, sizeof(file_search_magic)) != 0)
950
0
    return FALSE;
951
88.5k
  return TRUE;
952
88.5k
}
953
954
static DWORD FileAttributesFromStat(const char* path, const struct stat* fileStat)
955
54.4k
{
956
54.4k
  DWORD dwFileAttributes = 0;
957
958
54.4k
  if (S_ISDIR(fileStat->st_mode))
959
23.1k
    dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
960
961
54.4k
  if (dwFileAttributes == 0)
962
31.2k
    dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
963
964
54.4k
  const char* lastSep = strrchr(path, '/');
965
54.4k
  if (lastSep)
966
54.4k
  {
967
54.4k
    const char* name = lastSep + 1;
968
54.4k
    const size_t namelen = strlen(name);
969
970
54.4k
    if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
971
74
      dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
972
54.4k
  }
973
974
54.4k
  if (!(fileStat->st_mode & S_IWUSR))
975
2.17k
    dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
976
977
54.4k
  return dwFileAttributes;
978
54.4k
}
979
980
static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
981
                             LPWIN32_FIND_DATAA lpFindFileData)
982
54.4k
{
983
54.4k
  UINT64 ft = 0;
984
54.4k
  lpFindFileData->dwFileAttributes = FileAttributesFromStat(path, fileStat);
985
986
#ifdef _DARWIN_FEATURE_64_BIT_INODE
987
  ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
988
#else
989
54.4k
  ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
990
54.4k
#endif
991
54.4k
  lpFindFileData->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
992
54.4k
  lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
993
54.4k
  ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
994
54.4k
  lpFindFileData->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
995
54.4k
  lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
996
54.4k
  ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
997
54.4k
  lpFindFileData->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
998
54.4k
  lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
999
54.4k
  lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
1000
54.4k
  lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF;
1001
54.4k
  return TRUE;
1002
54.4k
}
1003
1004
HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
1005
28.0k
{
1006
28.0k
  if (!lpFindFileData || !lpFileName)
1007
0
  {
1008
0
    SetLastError(ERROR_BAD_ARGUMENTS);
1009
0
    return INVALID_HANDLE_VALUE;
1010
0
  }
1011
1012
28.0k
  const WIN32_FIND_DATAA empty = WINPR_C_ARRAY_INIT;
1013
28.0k
  *lpFindFileData = empty;
1014
1015
28.0k
  WIN32_FILE_SEARCH* pFileSearch = nullptr;
1016
28.0k
  size_t patternlen = 0;
1017
28.0k
  const size_t flen = strlen(lpFileName);
1018
28.0k
  const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE);
1019
28.0k
  const char* ptr = strrchr(lpFileName, sep);
1020
28.0k
  if (!ptr)
1021
0
    goto fail;
1022
28.0k
  patternlen = strlen(ptr + 1);
1023
28.0k
  if (patternlen == 0)
1024
13
    goto fail;
1025
1026
28.0k
  pFileSearch = file_search_new(lpFileName, flen - patternlen, ptr + 1, patternlen);
1027
1028
28.0k
  if (!pFileSearch)
1029
187
    return INVALID_HANDLE_VALUE;
1030
1031
27.8k
  if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData))
1032
27.7k
    return (HANDLE)pFileSearch;
1033
1034
95
fail:
1035
95
  FindClose(pFileSearch);
1036
95
  return INVALID_HANDLE_VALUE;
1037
27.8k
}
1038
1039
static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,
1040
                                LPWIN32_FIND_DATAW lpFindFileDataW)
1041
54.4k
{
1042
54.4k
  if (!lpFindFileDataA || !lpFindFileDataW)
1043
0
    return FALSE;
1044
1045
54.4k
  lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes;
1046
54.4k
  lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime;
1047
54.4k
  lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime;
1048
54.4k
  lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime;
1049
54.4k
  lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh;
1050
54.4k
  lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow;
1051
54.4k
  lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0;
1052
54.4k
  lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1;
1053
1054
54.4k
  if (ConvertUtf8NToWChar(lpFindFileDataA->cFileName, ARRAYSIZE(lpFindFileDataA->cFileName),
1055
54.4k
                          lpFindFileDataW->cFileName, ARRAYSIZE(lpFindFileDataW->cFileName)) < 0)
1056
0
    return FALSE;
1057
1058
54.4k
  return ConvertUtf8NToWChar(lpFindFileDataA->cAlternateFileName,
1059
54.4k
                             ARRAYSIZE(lpFindFileDataA->cAlternateFileName),
1060
54.4k
                             lpFindFileDataW->cAlternateFileName,
1061
54.4k
                             ARRAYSIZE(lpFindFileDataW->cAlternateFileName)) >= 0;
1062
54.4k
}
1063
1064
HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1065
28.0k
{
1066
28.0k
  LPSTR utfFileName = nullptr;
1067
28.0k
  HANDLE h = nullptr;
1068
28.0k
  if (!lpFileName)
1069
0
    return INVALID_HANDLE_VALUE;
1070
1071
28.0k
  LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
1072
1073
28.0k
  if (!fd)
1074
0
  {
1075
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1076
0
    return INVALID_HANDLE_VALUE;
1077
0
  }
1078
1079
28.0k
  utfFileName = ConvertWCharToUtf8Alloc(lpFileName, nullptr);
1080
28.0k
  if (!utfFileName)
1081
0
  {
1082
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1083
0
    free(fd);
1084
0
    return INVALID_HANDLE_VALUE;
1085
0
  }
1086
1087
28.0k
  h = FindFirstFileA(utfFileName, fd);
1088
28.0k
  free(utfFileName);
1089
1090
28.0k
  if (h != INVALID_HANDLE_VALUE)
1091
27.7k
  {
1092
27.7k
    if (!ConvertFindDataAToW(fd, lpFindFileData))
1093
0
    {
1094
0
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1095
0
      FindClose(h);
1096
0
      h = INVALID_HANDLE_VALUE;
1097
0
      goto out;
1098
0
    }
1099
27.7k
  }
1100
1101
28.0k
out:
1102
28.0k
  free(fd);
1103
28.0k
  return h;
1104
28.0k
}
1105
1106
HANDLE FindFirstFileExA(WINPR_ATTR_UNUSED LPCSTR lpFileName,
1107
                        WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1108
                        WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1109
                        WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1110
                        WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1111
                        WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1112
0
{
1113
0
  WLog_ERR("TODO", "TODO: Implement");
1114
0
  return INVALID_HANDLE_VALUE;
1115
0
}
1116
1117
HANDLE FindFirstFileExW(WINPR_ATTR_UNUSED LPCWSTR lpFileName,
1118
                        WINPR_ATTR_UNUSED FINDEX_INFO_LEVELS fInfoLevelId,
1119
                        WINPR_ATTR_UNUSED LPVOID lpFindFileData,
1120
                        WINPR_ATTR_UNUSED FINDEX_SEARCH_OPS fSearchOp,
1121
                        WINPR_ATTR_UNUSED LPVOID lpSearchFilter,
1122
                        WINPR_ATTR_UNUSED DWORD dwAdditionalFlags)
1123
0
{
1124
0
  WLog_ERR("TODO", "TODO: Implement");
1125
0
  return INVALID_HANDLE_VALUE;
1126
0
}
1127
1128
BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
1129
60.4k
{
1130
60.4k
  if (!lpFindFileData)
1131
0
    return FALSE;
1132
1133
60.4k
  const WIN32_FIND_DATAA empty = WINPR_C_ARRAY_INIT;
1134
60.4k
  *lpFindFileData = empty;
1135
1136
60.4k
  if (!is_valid_file_search_handle(hFindFile))
1137
0
    return FALSE;
1138
1139
60.4k
  WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1140
60.4k
  struct dirent* pDirent = nullptr;
1141
  // NOLINTNEXTLINE(concurrency-mt-unsafe)
1142
852k
  while ((pDirent = readdir(pFileSearch->pDir)) != nullptr)
1143
846k
  {
1144
846k
    if (FilePatternMatchA(pDirent->d_name, pFileSearch->lpPattern))
1145
54.4k
    {
1146
54.4k
      BOOL success = FALSE;
1147
1148
54.4k
      strncpy(lpFindFileData->cFileName, pDirent->d_name, MAX_PATH);
1149
54.4k
      const size_t namelen = strnlen(lpFindFileData->cFileName, MAX_PATH);
1150
54.4k
      size_t pathlen = strlen(pFileSearch->lpPath);
1151
54.4k
      char* fullpath = (char*)malloc(pathlen + namelen + 2);
1152
1153
54.4k
      if (fullpath == nullptr)
1154
0
      {
1155
0
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1156
0
        return FALSE;
1157
0
      }
1158
1159
54.4k
      memcpy(fullpath, pFileSearch->lpPath, pathlen);
1160
      /* Ensure path is terminated with a separator, but prevent
1161
       * duplicate separators */
1162
54.4k
      if (fullpath[pathlen - 1] != '/')
1163
0
        fullpath[pathlen++] = '/';
1164
54.4k
      memcpy(fullpath + pathlen, pDirent->d_name, namelen);
1165
54.4k
      fullpath[pathlen + namelen] = 0;
1166
1167
54.4k
      struct stat fileStat = WINPR_C_ARRAY_INIT;
1168
54.4k
      if (stat(fullpath, &fileStat) != 0)
1169
8
      {
1170
8
        free(fullpath);
1171
8
        SetLastError(map_posix_err(errno));
1172
8
        errno = 0;
1173
8
        continue;
1174
8
      }
1175
1176
      /* Skip FIFO entries. */
1177
54.4k
      if (S_ISFIFO(fileStat.st_mode))
1178
0
      {
1179
0
        free(fullpath);
1180
0
        continue;
1181
0
      }
1182
1183
54.4k
      success = FindDataFromStat(fullpath, &fileStat, lpFindFileData);
1184
54.4k
      free(fullpath);
1185
54.4k
      return success;
1186
54.4k
    }
1187
846k
  }
1188
1189
6.05k
  SetLastError(ERROR_NO_MORE_FILES);
1190
6.05k
  return FALSE;
1191
60.4k
}
1192
1193
BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
1194
32.6k
{
1195
32.6k
  LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
1196
1197
32.6k
  if (!fd)
1198
0
  {
1199
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1200
0
    return FALSE;
1201
0
  }
1202
1203
32.6k
  if (FindNextFileA(hFindFile, fd))
1204
26.6k
  {
1205
26.6k
    if (!ConvertFindDataAToW(fd, lpFindFileData))
1206
0
    {
1207
0
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1208
0
      free(fd);
1209
0
      return FALSE;
1210
0
    }
1211
1212
26.6k
    free(fd);
1213
26.6k
    return TRUE;
1214
26.6k
  }
1215
1216
5.97k
  free(fd);
1217
5.97k
  return FALSE;
1218
32.6k
}
1219
1220
BOOL FindClose(HANDLE hFindFile)
1221
28.0k
{
1222
28.0k
  WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1223
28.0k
  if (!pFileSearch)
1224
13
    return FALSE;
1225
1226
  /* Since INVALID_HANDLE_VALUE != nullptr the analyzer guesses that there
1227
   * is a initialized HANDLE that is not freed properly.
1228
   * Disable this return to stop confusing the analyzer. */
1229
28.0k
#ifndef __clang_analyzer__
1230
28.0k
  if (!is_valid_file_search_handle(hFindFile))
1231
0
    return FALSE;
1232
28.0k
#endif
1233
1234
28.0k
  free(pFileSearch->lpPath);
1235
28.0k
  free(pFileSearch->lpPattern);
1236
1237
28.0k
  if (pFileSearch->pDir)
1238
27.8k
    closedir(pFileSearch->pDir);
1239
1240
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1241
28.0k
  free(pFileSearch);
1242
28.0k
  return TRUE;
1243
28.0k
}
1244
1245
BOOL CreateDirectoryA(LPCSTR lpPathName,
1246
                      WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1247
0
{
1248
0
  return mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR) == 0;
1249
0
}
1250
1251
BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1252
0
{
1253
0
  if (!lpPathName)
1254
0
    return FALSE;
1255
0
  char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1256
0
  BOOL ret = FALSE;
1257
1258
0
  if (!utfPathName)
1259
0
  {
1260
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1261
0
    goto fail;
1262
0
  }
1263
1264
0
  ret = CreateDirectoryA(utfPathName, lpSecurityAttributes);
1265
0
fail:
1266
0
  free(utfPathName);
1267
0
  return ret;
1268
0
}
1269
1270
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1271
BOOL RemoveDirectoryA(LPCSTR lpPathName)
1272
0
{
1273
0
  return winpr_RemoveDirectory(lpPathName);
1274
0
}
1275
#endif
1276
1277
BOOL RemoveDirectoryW(LPCWSTR lpPathName)
1278
0
{
1279
0
  if (!lpPathName)
1280
0
    return FALSE;
1281
0
  char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, nullptr);
1282
0
  BOOL ret = FALSE;
1283
1284
0
  if (!utfPathName)
1285
0
  {
1286
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1287
0
    goto fail;
1288
0
  }
1289
1290
0
  ret = winpr_RemoveDirectory(utfPathName);
1291
0
fail:
1292
0
  free(utfPathName);
1293
0
  return ret;
1294
0
}
1295
1296
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1297
BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
1298
0
{
1299
0
  return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, dwFlags);
1300
0
}
1301
#endif
1302
1303
BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
1304
0
{
1305
0
  if (!lpExistingFileName || !lpNewFileName)
1306
0
    return FALSE;
1307
1308
0
  LPSTR lpCExistingFileName = ConvertWCharToUtf8Alloc(lpExistingFileName, nullptr);
1309
0
  LPSTR lpCNewFileName = ConvertWCharToUtf8Alloc(lpNewFileName, nullptr);
1310
0
  BOOL ret = FALSE;
1311
1312
0
  if (!lpCExistingFileName || !lpCNewFileName)
1313
0
  {
1314
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1315
0
    goto fail;
1316
0
  }
1317
1318
0
  ret = winpr_MoveFileEx(lpCExistingFileName, lpCNewFileName, dwFlags);
1319
0
fail:
1320
0
  free(lpCNewFileName);
1321
0
  free(lpCExistingFileName);
1322
0
  return ret;
1323
0
}
1324
1325
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
1326
BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
1327
0
{
1328
0
  return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
1329
0
}
1330
#endif
1331
1332
BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
1333
0
{
1334
0
  return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
1335
0
}
1336
1337
#endif
1338
1339
/* Extended API */
1340
1341
int UnixChangeFileMode(const char* filename, int flags)
1342
0
{
1343
0
  if (!filename)
1344
0
    return -1;
1345
0
#ifndef _WIN32
1346
0
  mode_t fl = 0;
1347
0
  fl |= (flags & 0x4000) ? S_ISUID : 0;
1348
0
  fl |= (flags & 0x2000) ? S_ISGID : 0;
1349
0
  fl |= (flags & 0x1000) ? S_ISVTX : 0;
1350
0
  fl |= (flags & 0x0400) ? S_IRUSR : 0;
1351
0
  fl |= (flags & 0x0200) ? S_IWUSR : 0;
1352
0
  fl |= (flags & 0x0100) ? S_IXUSR : 0;
1353
0
  fl |= (flags & 0x0040) ? S_IRGRP : 0;
1354
0
  fl |= (flags & 0x0020) ? S_IWGRP : 0;
1355
0
  fl |= (flags & 0x0010) ? S_IXGRP : 0;
1356
0
  fl |= (flags & 0x0004) ? S_IROTH : 0;
1357
0
  fl |= (flags & 0x0002) ? S_IWOTH : 0;
1358
0
  fl |= (flags & 0x0001) ? S_IXOTH : 0;
1359
0
  return chmod(filename, fl);
1360
#else
1361
  int rc;
1362
  WCHAR* wfl = ConvertUtf8ToWCharAlloc(filename, nullptr);
1363
1364
  if (!wfl)
1365
    return -1;
1366
1367
  /* Check for unsupported flags. */
1368
  if (flags & ~(_S_IREAD | _S_IWRITE))
1369
    WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags);
1370
1371
  rc = _wchmod(wfl, flags);
1372
  free(wfl);
1373
  return rc;
1374
#endif
1375
0
}
1376
1377
#if defined(_WIN32) || defined(_UWP)
1378
HANDLE winpr_CreateFile(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1379
                        LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1380
                        DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1381
{
1382
  WCHAR* filename = ConvertUtf8ToWCharAlloc(lpFileName, nullptr);
1383
  if (!filename)
1384
    return nullptr;
1385
1386
  HANDLE hdl = CreateFileW(filename, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1387
                           dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
1388
  free(filename);
1389
  return hdl;
1390
}
1391
#endif