Coverage Report

Created: 2026-08-31 06:25

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