Coverage Report

Created: 2026-10-28 06:52

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