Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/file/file.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * File Functions
4
 *
5
 * Copyright 2015 Thincast Technologies GmbH
6
 * Copyright 2015 Bernhard Miklautz <bernhard.miklautz@thincast.com>
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
#include <winpr/debug.h>
24
#include <winpr/assert.h>
25
26
#include <winpr/wtypes.h>
27
#include <winpr/crt.h>
28
#include <winpr/file.h>
29
30
#ifdef _WIN32
31
32
#include <io.h>
33
34
#else /* _WIN32 */
35
36
#include "../log.h"
37
#define TAG WINPR_TAG("file")
38
39
#include <winpr/wlog.h>
40
#include <winpr/string.h>
41
42
#include "file.h"
43
#include <errno.h>
44
#include <fcntl.h>
45
#include <sys/file.h>
46
#include <sys/stat.h>
47
#include <sys/time.h>
48
49
#ifdef ANDROID
50
#include <sys/vfs.h>
51
#else
52
#include <sys/statvfs.h>
53
#endif
54
55
#ifndef MIN
56
0
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
57
#endif
58
59
static WINPR_FILE* pStdHandleFile = NULL;
60
61
static void GetStdHandle_Uninit(void) __attribute__((destructor));
62
63
static BOOL FileIsHandled(HANDLE handle)
64
0
{
65
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_FILE, FALSE);
66
0
}
67
68
static int FileGetFd(HANDLE handle)
69
0
{
70
0
  WINPR_FILE* file = (WINPR_FILE*)handle;
71
72
0
  if (!FileIsHandled(handle))
73
0
    return -1;
74
75
0
  return fileno(file->fp);
76
0
}
77
78
static BOOL FileCloseHandleInt(HANDLE handle, BOOL force)
79
0
{
80
0
  WINPR_FILE* file = (WINPR_FILE*)handle;
81
82
0
  if (!FileIsHandled(handle))
83
0
    return FALSE;
84
85
0
  if (!force)
86
0
  {
87
0
    if (handle == pStdHandleFile)
88
0
    {
89
0
      return FALSE;
90
0
    }
91
0
  }
92
93
0
  if (file->fp)
94
0
  {
95
    /* Don't close stdin/stdout/stderr */
96
0
    if (fileno(file->fp) > 2)
97
0
    {
98
0
      (void)fclose(file->fp);
99
0
      file->fp = NULL;
100
0
    }
101
0
  }
102
103
0
  free(file->lpFileName);
104
0
  free(file);
105
0
  return TRUE;
106
0
}
107
108
static BOOL FileCloseHandle(HANDLE handle)
109
0
{
110
0
  return FileCloseHandleInt(handle, FALSE);
111
0
}
112
113
static BOOL FileSetEndOfFile(HANDLE hFile)
114
0
{
115
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
116
117
0
  if (!hFile)
118
0
    return FALSE;
119
120
0
  const INT64 size = _ftelli64(pFile->fp);
121
0
  if (size < 0)
122
0
    return FALSE;
123
124
0
  if (ftruncate(fileno(pFile->fp), (off_t)size) < 0)
125
0
  {
126
0
    char ebuffer[256] = { 0 };
127
0
    WLog_ERR(TAG, "ftruncate %s failed with %s [0x%08X]", pFile->lpFileName,
128
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
129
0
    SetLastError(map_posix_err(errno));
130
0
    return FALSE;
131
0
  }
132
133
0
  return TRUE;
134
0
}
135
136
// NOLINTBEGIN(readability-non-const-parameter)
137
static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove,
138
                                const PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
139
// NOLINTEND(readability-non-const-parameter)
140
0
{
141
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
142
0
  INT64 offset = 0;
143
0
  int whence = 0;
144
145
0
  if (!hFile)
146
0
    return INVALID_SET_FILE_POINTER;
147
148
  /* If there is a high part, the sign is contained in that
149
   * and the low integer must be interpreted as unsigned. */
150
0
  if (lpDistanceToMoveHigh)
151
0
  {
152
0
    offset = (INT64)(((UINT64)*lpDistanceToMoveHigh << 32U) | (UINT64)lDistanceToMove);
153
0
  }
154
0
  else
155
0
    offset = lDistanceToMove;
156
157
0
  switch (dwMoveMethod)
158
0
  {
159
0
    case FILE_BEGIN:
160
0
      whence = SEEK_SET;
161
0
      break;
162
0
    case FILE_END:
163
0
      whence = SEEK_END;
164
0
      break;
165
0
    case FILE_CURRENT:
166
0
      whence = SEEK_CUR;
167
0
      break;
168
0
    default:
169
0
      return INVALID_SET_FILE_POINTER;
170
0
  }
171
172
0
  if (_fseeki64(pFile->fp, offset, whence))
173
0
  {
174
0
    char ebuffer[256] = { 0 };
175
0
    WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName,
176
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
177
0
    SetLastError(map_posix_err(errno));
178
0
    return INVALID_SET_FILE_POINTER;
179
0
  }
180
181
0
  return (DWORD)_ftelli64(pFile->fp);
182
0
}
183
184
static BOOL FileSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove,
185
                                 PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
186
0
{
187
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
188
0
  int whence = 0;
189
190
0
  if (!hFile)
191
0
    return FALSE;
192
193
0
  switch (dwMoveMethod)
194
0
  {
195
0
    case FILE_BEGIN:
196
0
      whence = SEEK_SET;
197
0
      break;
198
0
    case FILE_END:
199
0
      whence = SEEK_END;
200
0
      break;
201
0
    case FILE_CURRENT:
202
0
      whence = SEEK_CUR;
203
0
      break;
204
0
    default:
205
0
      return FALSE;
206
0
  }
207
208
0
  if (_fseeki64(pFile->fp, liDistanceToMove.QuadPart, whence))
209
0
  {
210
0
    char ebuffer[256] = { 0 };
211
0
    WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName,
212
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
213
0
    SetLastError(map_posix_err(errno));
214
0
    return FALSE;
215
0
  }
216
217
0
  if (lpNewFilePointer)
218
0
    lpNewFilePointer->QuadPart = _ftelli64(pFile->fp);
219
220
0
  return TRUE;
221
0
}
222
223
static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
224
                     LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
225
0
{
226
0
  size_t io_status = 0;
227
0
  WINPR_FILE* file = NULL;
228
0
  BOOL status = TRUE;
229
230
0
  if (lpOverlapped)
231
0
  {
232
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
233
0
    SetLastError(ERROR_NOT_SUPPORTED);
234
0
    return FALSE;
235
0
  }
236
237
0
  if (!Object)
238
0
    return FALSE;
239
240
0
  file = (WINPR_FILE*)Object;
241
0
  clearerr(file->fp);
242
0
  io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp);
243
244
0
  if (io_status == 0 && ferror(file->fp))
245
0
  {
246
0
    status = FALSE;
247
248
0
    switch (errno)
249
0
    {
250
0
      case EWOULDBLOCK:
251
0
        SetLastError(ERROR_NO_DATA);
252
0
        break;
253
0
      default:
254
0
        SetLastError(map_posix_err(errno));
255
0
    }
256
0
  }
257
258
0
  if (lpNumberOfBytesRead)
259
0
    *lpNumberOfBytesRead = (DWORD)io_status;
260
261
0
  return status;
262
0
}
263
264
static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
265
                      LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
266
0
{
267
0
  size_t io_status = 0;
268
0
  WINPR_FILE* file = NULL;
269
270
0
  if (lpOverlapped)
271
0
  {
272
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
273
0
    SetLastError(ERROR_NOT_SUPPORTED);
274
0
    return FALSE;
275
0
  }
276
277
0
  if (!Object)
278
0
    return FALSE;
279
280
0
  file = (WINPR_FILE*)Object;
281
282
0
  clearerr(file->fp);
283
0
  io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp);
284
0
  if (io_status == 0 && ferror(file->fp))
285
0
  {
286
0
    SetLastError(map_posix_err(errno));
287
0
    return FALSE;
288
0
  }
289
290
0
  *lpNumberOfBytesWritten = (DWORD)io_status;
291
0
  return TRUE;
292
0
}
293
294
static DWORD FileGetFileSize(HANDLE Object, LPDWORD lpFileSizeHigh)
295
0
{
296
0
  WINPR_FILE* file = NULL;
297
0
  INT64 cur = 0;
298
0
  INT64 size = 0;
299
300
0
  if (!Object)
301
0
    return 0;
302
303
0
  file = (WINPR_FILE*)Object;
304
305
0
  cur = _ftelli64(file->fp);
306
307
0
  if (cur < 0)
308
0
  {
309
0
    char ebuffer[256] = { 0 };
310
0
    WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
311
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
312
0
    SetLastError(map_posix_err(errno));
313
0
    return INVALID_FILE_SIZE;
314
0
  }
315
316
0
  if (_fseeki64(file->fp, 0, SEEK_END) != 0)
317
0
  {
318
0
    char ebuffer[256] = { 0 };
319
0
    WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", file->lpFileName,
320
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
321
0
    SetLastError(map_posix_err(errno));
322
0
    return INVALID_FILE_SIZE;
323
0
  }
324
325
0
  size = _ftelli64(file->fp);
326
327
0
  if (size < 0)
328
0
  {
329
0
    char ebuffer[256] = { 0 };
330
0
    WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
331
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
332
0
    SetLastError(map_posix_err(errno));
333
0
    return INVALID_FILE_SIZE;
334
0
  }
335
336
0
  if (_fseeki64(file->fp, cur, SEEK_SET) != 0)
337
0
  {
338
0
    char ebuffer[256] = { 0 };
339
0
    WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName,
340
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
341
0
    SetLastError(map_posix_err(errno));
342
0
    return INVALID_FILE_SIZE;
343
0
  }
344
345
0
  if (lpFileSizeHigh)
346
0
    *lpFileSizeHigh = (UINT32)(size >> 32);
347
348
0
  return (UINT32)(size & 0xFFFFFFFF);
349
0
}
350
351
static BOOL FileFlushFileBuffers(HANDLE hFile)
352
0
{
353
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
354
355
0
  if (!pFile->fp)
356
0
  {
357
0
    SetLastError(ERROR_INVALID_HANDLE);
358
0
    return FALSE;
359
0
  }
360
361
  // See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
362
0
  if ((pFile->dwOpenMode & GENERIC_WRITE) == 0)
363
0
  {
364
0
    SetLastError(ERROR_ACCESS_DENIED);
365
0
    return FALSE;
366
0
  }
367
368
0
  if (fflush(pFile->fp) != 0)
369
0
  {
370
0
    SetLastError(map_posix_err(errno));
371
0
    return FALSE;
372
0
  }
373
374
0
  return TRUE;
375
0
}
376
377
static BOOL FileGetFileInformationByHandle(HANDLE hFile,
378
                                           LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
379
0
{
380
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
381
0
  struct stat st;
382
0
  UINT64 ft = 0;
383
0
  const char* lastSep = NULL;
384
385
0
  if (!pFile)
386
0
    return FALSE;
387
0
  if (!lpFileInformation)
388
0
    return FALSE;
389
390
0
  if (fstat(fileno(pFile->fp), &st) == -1)
391
0
  {
392
0
    char ebuffer[256] = { 0 };
393
0
    WLog_ERR(TAG, "fstat failed with %s [%#08X]", errno,
394
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
395
0
    SetLastError(map_posix_err(errno));
396
0
    return FALSE;
397
0
  }
398
399
0
  lpFileInformation->dwFileAttributes = 0;
400
401
0
  if (S_ISDIR(st.st_mode))
402
0
    lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
403
404
0
  if (lpFileInformation->dwFileAttributes == 0)
405
0
    lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
406
407
0
  lastSep = strrchr(pFile->lpFileName, '/');
408
409
0
  if (lastSep)
410
0
  {
411
0
    const char* name = lastSep + 1;
412
0
    const size_t namelen = strlen(name);
413
414
0
    if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
415
0
      lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
416
0
  }
417
418
0
  if (!(st.st_mode & S_IWUSR))
419
0
    lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
420
421
#ifdef _DARWIN_FEATURE_64_BIT_INODE
422
  ft = STAT_TIME_TO_FILETIME(st.st_birthtime);
423
#else
424
0
  ft = STAT_TIME_TO_FILETIME(st.st_ctime);
425
0
#endif
426
0
  lpFileInformation->ftCreationTime.dwHighDateTime = (ft) >> 32ULL;
427
0
  lpFileInformation->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
428
0
  ft = STAT_TIME_TO_FILETIME(st.st_mtime);
429
0
  lpFileInformation->ftLastWriteTime.dwHighDateTime = (ft) >> 32ULL;
430
0
  lpFileInformation->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
431
0
  ft = STAT_TIME_TO_FILETIME(st.st_atime);
432
0
  lpFileInformation->ftLastAccessTime.dwHighDateTime = (ft) >> 32ULL;
433
0
  lpFileInformation->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
434
0
  lpFileInformation->nFileSizeHigh = ((UINT64)st.st_size) >> 32ULL;
435
0
  lpFileInformation->nFileSizeLow = st.st_size & 0xFFFFFFFF;
436
0
  lpFileInformation->dwVolumeSerialNumber = (UINT32)st.st_dev;
437
0
  lpFileInformation->nNumberOfLinks = (UINT32)st.st_nlink;
438
0
  lpFileInformation->nFileIndexHigh = (st.st_ino >> 4) & 0xFFFFFFFF;
439
0
  lpFileInformation->nFileIndexLow = st.st_ino & 0xFFFFFFFF;
440
0
  return TRUE;
441
0
}
442
443
static BOOL FileLockFileEx(HANDLE hFile, DWORD dwFlags, WINPR_ATTR_UNUSED DWORD dwReserved,
444
                           WINPR_ATTR_UNUSED DWORD nNumberOfBytesToLockLow,
445
                           WINPR_ATTR_UNUSED DWORD nNumberOfBytesToLockHigh,
446
                           LPOVERLAPPED lpOverlapped)
447
0
{
448
#ifdef __sun
449
  struct flock lock;
450
  int lckcmd;
451
#else
452
0
  int lock = 0;
453
0
#endif
454
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
455
456
0
  if (lpOverlapped)
457
0
  {
458
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
459
0
    SetLastError(ERROR_NOT_SUPPORTED);
460
0
    return FALSE;
461
0
  }
462
463
0
  if (!hFile)
464
0
    return FALSE;
465
466
0
  if (pFile->bLocked)
467
0
  {
468
0
    WLog_ERR(TAG, "File %s already locked!", pFile->lpFileName);
469
0
    return FALSE;
470
0
  }
471
472
#ifdef __sun
473
  lock.l_start = 0;
474
  lock.l_len = 0;
475
  lock.l_whence = SEEK_SET;
476
477
  if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
478
    lock.l_type = F_WRLCK;
479
  else
480
    lock.l_type = F_WRLCK;
481
482
  if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
483
    lckcmd = F_SETLK;
484
  else
485
    lckcmd = F_SETLKW;
486
487
  if (fcntl(fileno(pFile->fp), lckcmd, &lock) == -1)
488
  {
489
    char ebuffer[256] = { 0 };
490
    WLog_ERR(TAG, "F_SETLK failed with %s [0x%08X]",
491
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
492
    SetLastError(map_posix_err(errno));
493
    return FALSE;
494
  }
495
#else
496
0
  if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK)
497
0
    lock = LOCK_EX;
498
0
  else
499
0
    lock = LOCK_SH;
500
501
0
  if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY)
502
0
    lock |= LOCK_NB;
503
504
0
  if (flock(fileno(pFile->fp), lock) < 0)
505
0
  {
506
0
    char ebuffer[256] = { 0 };
507
0
    WLog_ERR(TAG, "flock failed with %s [0x%08X]",
508
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
509
0
    SetLastError(map_posix_err(errno));
510
0
    return FALSE;
511
0
  }
512
0
#endif
513
514
0
  pFile->bLocked = TRUE;
515
516
0
  return TRUE;
517
0
}
518
519
static BOOL FileUnlockFile(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFileOffsetLow,
520
                           WINPR_ATTR_UNUSED DWORD dwFileOffsetHigh,
521
                           WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockLow,
522
                           WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockHigh)
523
0
{
524
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
525
#ifdef __sun
526
  struct flock lock;
527
#endif
528
529
0
  if (!hFile)
530
0
    return FALSE;
531
532
0
  if (!pFile->bLocked)
533
0
  {
534
0
    WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
535
0
    return FALSE;
536
0
  }
537
538
#ifdef __sun
539
  lock.l_start = 0;
540
  lock.l_len = 0;
541
  lock.l_whence = SEEK_SET;
542
  lock.l_type = F_UNLCK;
543
  if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
544
  {
545
    char ebuffer[256] = { 0 };
546
    WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName,
547
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
548
    SetLastError(map_posix_err(errno));
549
    return FALSE;
550
  }
551
552
#else
553
0
  if (flock(fileno(pFile->fp), LOCK_UN) < 0)
554
0
  {
555
0
    char ebuffer[256] = { 0 };
556
0
    WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
557
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
558
0
    SetLastError(map_posix_err(errno));
559
0
    return FALSE;
560
0
  }
561
0
#endif
562
563
0
  return TRUE;
564
0
}
565
566
static BOOL FileUnlockFileEx(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwReserved,
567
                             WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockLow,
568
                             WINPR_ATTR_UNUSED DWORD nNumberOfBytesToUnlockHigh,
569
                             LPOVERLAPPED lpOverlapped)
570
0
{
571
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
572
#ifdef __sun
573
  struct flock lock;
574
#endif
575
576
0
  if (lpOverlapped)
577
0
  {
578
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
579
0
    SetLastError(ERROR_NOT_SUPPORTED);
580
0
    return FALSE;
581
0
  }
582
583
0
  if (!hFile)
584
0
    return FALSE;
585
586
0
  if (!pFile->bLocked)
587
0
  {
588
0
    WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName);
589
0
    return FALSE;
590
0
  }
591
592
#ifdef __sun
593
  lock.l_start = 0;
594
  lock.l_len = 0;
595
  lock.l_whence = SEEK_SET;
596
  lock.l_type = F_UNLCK;
597
  if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1)
598
  {
599
    char ebuffer[256] = { 0 };
600
    WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName,
601
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
602
    SetLastError(map_posix_err(errno));
603
    return FALSE;
604
  }
605
#else
606
0
  if (flock(fileno(pFile->fp), LOCK_UN) < 0)
607
0
  {
608
0
    char ebuffer[256] = { 0 };
609
0
    WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName,
610
0
             winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
611
0
    SetLastError(map_posix_err(errno));
612
0
    return FALSE;
613
0
  }
614
0
#endif
615
616
0
  return TRUE;
617
0
}
618
619
static INT64 FileTimeToUS(const FILETIME* ft)
620
0
{
621
0
  const INT64 EPOCH_DIFF_US = EPOCH_DIFF * 1000000LL;
622
0
  INT64 tmp = ((INT64)ft->dwHighDateTime) << 32 | ft->dwLowDateTime;
623
0
  tmp /= 10; /* 100ns steps to 1us step */
624
0
  tmp -= EPOCH_DIFF_US;
625
0
  return tmp;
626
0
}
627
628
#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
629
static struct timespec filetimeToTimespec(const FILETIME* ftime)
630
0
{
631
0
  WINPR_ASSERT(ftime);
632
0
  INT64 tmp = FileTimeToUS(ftime);
633
0
  struct timespec ts = { 0 };
634
0
  ts.tv_sec = tmp / 1000000LL;
635
0
  ts.tv_nsec = (tmp % 1000000LL) * 1000LL;
636
0
  return ts;
637
0
}
638
639
static BOOL FileSetFileTime(HANDLE hFile, WINPR_ATTR_UNUSED const FILETIME* lpCreationTime,
640
                            const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
641
0
{
642
0
  struct timespec times[2] = { { UTIME_OMIT, UTIME_OMIT },
643
0
                             { UTIME_OMIT, UTIME_OMIT } }; /* last access, last modification */
644
0
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
645
646
0
  if (!hFile)
647
0
    return FALSE;
648
649
0
  if (lpLastAccessTime)
650
0
    times[0] = filetimeToTimespec(lpLastAccessTime);
651
652
0
  if (lpLastWriteTime)
653
0
    times[1] = filetimeToTimespec(lpLastWriteTime);
654
655
  // TODO: Creation time can not be handled!
656
0
  const int rc = futimens(fileno(pFile->fp), times);
657
0
  if (rc != 0)
658
0
  {
659
0
    char ebuffer[256] = { 0 };
660
0
    WLog_ERR(TAG, "futimens failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
661
0
             errno);
662
0
    SetLastError(map_posix_err(errno));
663
0
    return FALSE;
664
0
  }
665
666
0
  return TRUE;
667
0
}
668
#elif defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD)
669
static struct timeval filetimeToTimeval(const FILETIME* ftime)
670
{
671
  WINPR_ASSERT(ftime);
672
  UINT64 tmp = FileTimeToUS(ftime);
673
  struct timeval tv = { 0 };
674
  tv.tv_sec = tmp / 1000000ULL;
675
  tv.tv_usec = tmp % 1000000ULL;
676
  return tv;
677
}
678
679
static struct timeval statToTimeval(const struct stat* sval)
680
{
681
  WINPR_ASSERT(sval);
682
  struct timeval tv = { 0 };
683
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD)
684
  tv.tv_sec = sval->st_atime;
685
#ifdef _POSIX_SOURCE
686
  TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atim);
687
#else
688
  TIMESPEC_TO_TIMEVAL(&tv, &sval->st_atimespec);
689
#endif
690
#elif defined(ANDROID)
691
  tv.tv_sec = sval->st_atime;
692
  tv.tv_usec = sval->st_atimensec / 1000UL;
693
#endif
694
  return tv;
695
}
696
697
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
698
                            const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
699
{
700
  struct stat buf = { 0 };
701
  /* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */
702
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
703
704
  if (!hFile)
705
    return FALSE;
706
707
  const int rc = fstat(fileno(pFile->fp), &buf);
708
  if (rc < 0)
709
  {
710
    char ebuffer[256] = { 0 };
711
    WLog_ERR(TAG, "fstat failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
712
             errno);
713
    SetLastError(map_posix_err(errno));
714
    return FALSE;
715
  }
716
717
  struct timeval timevals[2] = { statToTimeval(&buf), statToTimeval(&buf) };
718
  if (lpLastAccessTime)
719
    timevals[0] = filetimeToTimeval(lpLastAccessTime);
720
721
  if (lpLastWriteTime)
722
    timevals[1] = filetimeToTimeval(lpLastWriteTime);
723
724
  // TODO: Creation time can not be handled!
725
  {
726
    const int res = utimes(pFile->lpFileName, timevals);
727
    if (res != 0)
728
    {
729
      char ebuffer[256] = { 0 };
730
      WLog_ERR(TAG, "utimes failed: %s [%d]", winpr_strerror(errno, ebuffer, sizeof(ebuffer)),
731
               errno);
732
      SetLastError(map_posix_err(errno));
733
      return FALSE;
734
    }
735
  }
736
737
  return TRUE;
738
}
739
#else
740
static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
741
                            const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
742
{
743
  WINPR_FILE* pFile = (WINPR_FILE*)hFile;
744
745
  if (!hFile)
746
    return FALSE;
747
748
  WLog_WARN(TAG, "TODO: Creation, Access and Write time can not be handled!");
749
  WLog_WARN(TAG,
750
            "TODO: Define _POSIX_C_SOURCE >= 200809L or implement a platform specific handler!");
751
  return TRUE;
752
}
753
#endif
754
755
static HANDLE_OPS fileOps = {
756
  FileIsHandled,
757
  FileCloseHandle,
758
  FileGetFd,
759
  NULL, /* CleanupHandle */
760
  FileRead,
761
  NULL, /* FileReadEx */
762
  NULL, /* FileReadScatter */
763
  FileWrite,
764
  NULL, /* FileWriteEx */
765
  NULL, /* FileWriteGather */
766
  FileGetFileSize,
767
  FileFlushFileBuffers,
768
  FileSetEndOfFile,
769
  FileSetFilePointer,
770
  FileSetFilePointerEx,
771
  NULL, /* FileLockFile */
772
  FileLockFileEx,
773
  FileUnlockFile,
774
  FileUnlockFileEx,
775
  FileSetFileTime,
776
  FileGetFileInformationByHandle,
777
};
778
779
static HANDLE_OPS shmOps = {
780
  FileIsHandled,
781
  FileCloseHandle,
782
  FileGetFd,
783
  NULL, /* CleanupHandle */
784
  FileRead,
785
  NULL, /* FileReadEx */
786
  NULL, /* FileReadScatter */
787
  FileWrite,
788
  NULL, /* FileWriteEx */
789
  NULL, /* FileWriteGather */
790
  NULL, /* FileGetFileSize */
791
  NULL, /*  FlushFileBuffers */
792
  NULL, /* FileSetEndOfFile */
793
  NULL, /* FileSetFilePointer */
794
  NULL, /* SetFilePointerEx */
795
  NULL, /* FileLockFile */
796
  NULL, /* FileLockFileEx */
797
  NULL, /* FileUnlockFile */
798
  NULL, /* FileUnlockFileEx */
799
  NULL, /* FileSetFileTime */
800
  FileGetFileInformationByHandle,
801
};
802
803
static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDisposition, BOOL* create)
804
0
{
805
0
  BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
806
807
0
  switch (dwCreationDisposition)
808
0
  {
809
0
    case CREATE_ALWAYS:
810
0
      *create = TRUE;
811
0
      return (writeable) ? "wb+" : "rwb";
812
0
    case CREATE_NEW:
813
0
      *create = TRUE;
814
0
      return "wb+";
815
0
    case OPEN_ALWAYS:
816
0
      *create = TRUE;
817
0
      return "rb+";
818
0
    case OPEN_EXISTING:
819
0
      *create = FALSE;
820
0
      return (writeable) ? "rb+" : "rb";
821
0
    case TRUNCATE_EXISTING:
822
0
      *create = FALSE;
823
0
      return "wb+";
824
0
    default:
825
0
      *create = FALSE;
826
0
      return "";
827
0
  }
828
0
}
829
830
UINT32 map_posix_err(int fs_errno)
831
0
{
832
0
  NTSTATUS rc = 0;
833
834
  /* try to return NTSTATUS version of error code */
835
836
0
  switch (fs_errno)
837
0
  {
838
0
    case 0:
839
0
      rc = STATUS_SUCCESS;
840
0
      break;
841
842
0
    case ENOTCONN:
843
0
    case ENODEV:
844
0
    case ENOTDIR:
845
0
    case ENXIO:
846
0
      rc = ERROR_FILE_NOT_FOUND;
847
0
      break;
848
849
0
    case EROFS:
850
0
    case EPERM:
851
0
    case EACCES:
852
0
      rc = ERROR_ACCESS_DENIED;
853
0
      break;
854
855
0
    case ENOENT:
856
0
      rc = ERROR_FILE_NOT_FOUND;
857
0
      break;
858
859
0
    case EBUSY:
860
0
      rc = ERROR_BUSY_DRIVE;
861
0
      break;
862
863
0
    case EEXIST:
864
0
      rc = ERROR_FILE_EXISTS;
865
0
      break;
866
867
0
    case EISDIR:
868
0
      rc = STATUS_FILE_IS_A_DIRECTORY;
869
0
      break;
870
871
0
    case ENOTEMPTY:
872
0
      rc = STATUS_DIRECTORY_NOT_EMPTY;
873
0
      break;
874
875
0
    case EMFILE:
876
0
      rc = STATUS_TOO_MANY_OPENED_FILES;
877
0
      break;
878
879
0
    default:
880
0
    {
881
0
      char ebuffer[256] = { 0 };
882
0
      WLog_ERR(TAG, "Missing ERRNO mapping %s [%d]",
883
0
               winpr_strerror(fs_errno, ebuffer, sizeof(ebuffer)), fs_errno);
884
0
      rc = STATUS_UNSUCCESSFUL;
885
0
    }
886
0
    break;
887
0
  }
888
889
0
  return (UINT32)rc;
890
0
}
891
892
static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
893
                              LPSECURITY_ATTRIBUTES lpSecurityAttributes,
894
                              DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
895
                              HANDLE hTemplateFile)
896
0
{
897
0
  WINPR_FILE* pFile = NULL;
898
0
  BOOL create = 0;
899
0
  const char* mode = FileGetMode(dwDesiredAccess, dwCreationDisposition, &create);
900
#ifdef __sun
901
  struct flock lock;
902
#else
903
0
  int lock = 0;
904
0
#endif
905
0
  FILE* fp = NULL;
906
0
  struct stat st;
907
908
0
  if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
909
0
  {
910
0
    WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
911
0
    SetLastError(ERROR_NOT_SUPPORTED);
912
0
    return INVALID_HANDLE_VALUE;
913
0
  }
914
915
0
  pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
916
0
  if (!pFile)
917
0
  {
918
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
919
0
    return INVALID_HANDLE_VALUE;
920
0
  }
921
922
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
923
0
  pFile->common.ops = &fileOps;
924
925
0
  pFile->lpFileName = _strdup(lpFileName);
926
0
  if (!pFile->lpFileName)
927
0
  {
928
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
929
0
    free(pFile);
930
0
    return INVALID_HANDLE_VALUE;
931
0
  }
932
933
0
  pFile->dwOpenMode = dwDesiredAccess;
934
0
  pFile->dwShareMode = dwShareMode;
935
0
  pFile->dwFlagsAndAttributes = dwFlagsAndAttributes;
936
0
  pFile->lpSecurityAttributes = lpSecurityAttributes;
937
0
  pFile->dwCreationDisposition = dwCreationDisposition;
938
0
  pFile->hTemplateFile = hTemplateFile;
939
940
0
  if (create)
941
0
  {
942
0
    if (dwCreationDisposition == CREATE_NEW)
943
0
    {
944
0
      if (stat(pFile->lpFileName, &st) == 0)
945
0
      {
946
0
        SetLastError(ERROR_FILE_EXISTS);
947
0
        free(pFile->lpFileName);
948
0
        free(pFile);
949
0
        return INVALID_HANDLE_VALUE;
950
0
      }
951
0
    }
952
953
0
    fp = winpr_fopen(pFile->lpFileName, "ab");
954
0
    if (!fp)
955
0
    {
956
0
      SetLastError(map_posix_err(errno));
957
0
      free(pFile->lpFileName);
958
0
      free(pFile);
959
0
      return INVALID_HANDLE_VALUE;
960
0
    }
961
962
0
    fp = freopen(pFile->lpFileName, mode, fp);
963
0
  }
964
0
  else
965
0
  {
966
0
    if (stat(pFile->lpFileName, &st) != 0)
967
0
    {
968
0
      SetLastError(map_posix_err(errno));
969
0
      free(pFile->lpFileName);
970
0
      free(pFile);
971
0
      return INVALID_HANDLE_VALUE;
972
0
    }
973
974
    /* FIFO (named pipe) would block the following fopen
975
     * call if not connected. This renders the channel unusable,
976
     * therefore abort early. */
977
0
    if (S_ISFIFO(st.st_mode))
978
0
    {
979
0
      SetLastError(ERROR_FILE_NOT_FOUND);
980
0
      free(pFile->lpFileName);
981
0
      free(pFile);
982
0
      return INVALID_HANDLE_VALUE;
983
0
    }
984
0
  }
985
986
0
  if (NULL == fp)
987
0
    fp = winpr_fopen(pFile->lpFileName, mode);
988
989
0
  pFile->fp = fp;
990
0
  if (!pFile->fp)
991
0
  {
992
    /* This case can occur when trying to open a
993
     * not existing file without create flag. */
994
0
    SetLastError(map_posix_err(errno));
995
0
    free(pFile->lpFileName);
996
0
    free(pFile);
997
0
    return INVALID_HANDLE_VALUE;
998
0
  }
999
1000
0
  (void)setvbuf(fp, NULL, _IONBF, 0);
1001
1002
#ifdef __sun
1003
  lock.l_start = 0;
1004
  lock.l_len = 0;
1005
  lock.l_whence = SEEK_SET;
1006
1007
  if (dwShareMode & FILE_SHARE_READ)
1008
    lock.l_type = F_RDLCK;
1009
  if (dwShareMode & FILE_SHARE_WRITE)
1010
    lock.l_type = F_RDLCK;
1011
#else
1012
0
  if (dwShareMode & FILE_SHARE_READ)
1013
0
    lock = LOCK_SH;
1014
0
  if (dwShareMode & FILE_SHARE_WRITE)
1015
0
    lock = LOCK_EX;
1016
0
#endif
1017
1018
0
  if (dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE))
1019
0
  {
1020
#ifdef __sun
1021
    if (fcntl(fileno(pFile->fp), F_SETLKW, &lock) == -1)
1022
#else
1023
0
    if (flock(fileno(pFile->fp), lock) < 0)
1024
0
#endif
1025
0
    {
1026
0
      char ebuffer[256] = { 0 };
1027
#ifdef __sun
1028
      WLog_ERR(TAG, "F_SETLKW failed with %s [0x%08X]",
1029
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1030
#else
1031
0
      WLog_ERR(TAG, "flock failed with %s [0x%08X]",
1032
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1033
0
#endif
1034
1035
0
      SetLastError(map_posix_err(errno));
1036
0
      FileCloseHandle(pFile);
1037
0
      return INVALID_HANDLE_VALUE;
1038
0
    }
1039
1040
0
    pFile->bLocked = TRUE;
1041
0
  }
1042
1043
0
  if (fstat(fileno(pFile->fp), &st) == 0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
1044
0
  {
1045
0
    st.st_mode &= WINPR_ASSERTING_INT_CAST(mode_t, (mode_t)(~(S_IWUSR | S_IWGRP | S_IWOTH)));
1046
0
    if (fchmod(fileno(pFile->fp), st.st_mode) != 0)
1047
0
    {
1048
0
      SetLastError(map_posix_err(errno));
1049
0
      FileCloseHandle(pFile);
1050
0
      return INVALID_HANDLE_VALUE;
1051
0
    }
1052
0
  }
1053
1054
0
  SetLastError(STATUS_SUCCESS);
1055
0
  return pFile;
1056
0
}
1057
1058
static BOOL IsFileDevice(WINPR_ATTR_UNUSED LPCTSTR lpDeviceName)
1059
0
{
1060
0
  return TRUE;
1061
0
}
1062
1063
static const HANDLE_CREATOR FileHandleCreator = { IsFileDevice, FileCreateFileA };
1064
1065
const HANDLE_CREATOR* GetFileHandleCreator(void)
1066
0
{
1067
0
  return &FileHandleCreator;
1068
0
}
1069
1070
static WINPR_FILE* FileHandle_New(FILE* fp)
1071
0
{
1072
0
  WINPR_FILE* pFile = NULL;
1073
0
  char name[MAX_PATH] = { 0 };
1074
1075
0
  (void)_snprintf(name, sizeof(name), "device_%d", fileno(fp));
1076
0
  pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE));
1077
0
  if (!pFile)
1078
0
  {
1079
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1080
0
    return NULL;
1081
0
  }
1082
0
  pFile->fp = fp;
1083
0
  pFile->common.ops = &shmOps;
1084
0
  pFile->lpFileName = _strdup(name);
1085
1086
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ);
1087
0
  return pFile;
1088
0
}
1089
1090
void GetStdHandle_Uninit(void)
1091
0
{
1092
0
  FileCloseHandleInt(pStdHandleFile, TRUE);
1093
0
}
1094
1095
HANDLE GetStdHandle(DWORD nStdHandle)
1096
0
{
1097
0
  FILE* fp = NULL;
1098
1099
0
  switch (nStdHandle)
1100
0
  {
1101
0
    case STD_INPUT_HANDLE:
1102
0
      fp = stdin;
1103
0
      break;
1104
0
    case STD_OUTPUT_HANDLE:
1105
0
      fp = stdout;
1106
0
      break;
1107
0
    case STD_ERROR_HANDLE:
1108
0
      fp = stderr;
1109
0
      break;
1110
0
    default:
1111
0
      return INVALID_HANDLE_VALUE;
1112
0
  }
1113
0
  if (!pStdHandleFile)
1114
0
    pStdHandleFile = FileHandle_New(fp);
1115
1116
0
  if (!pStdHandleFile)
1117
0
    return INVALID_HANDLE_VALUE;
1118
1119
0
  return (HANDLE)pStdHandleFile;
1120
0
}
1121
1122
BOOL SetStdHandle(WINPR_ATTR_UNUSED DWORD nStdHandle, WINPR_ATTR_UNUSED HANDLE hHandle)
1123
0
{
1124
0
  return FALSE;
1125
0
}
1126
1127
BOOL SetStdHandleEx(WINPR_ATTR_UNUSED DWORD dwStdHandle, WINPR_ATTR_UNUSED HANDLE hNewHandle,
1128
                    WINPR_ATTR_UNUSED HANDLE* phOldHandle)
1129
0
{
1130
0
  return FALSE;
1131
0
}
1132
1133
BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
1134
                       LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
1135
0
{
1136
#if defined(ANDROID)
1137
#define STATVFS statfs
1138
#else
1139
0
#define STATVFS statvfs
1140
0
#endif
1141
1142
0
  struct STATVFS svfst = { 0 };
1143
0
  STATVFS(lpRootPathName, &svfst);
1144
0
  *lpSectorsPerCluster = (UINT32)MIN(svfst.f_frsize, UINT32_MAX);
1145
0
  *lpBytesPerSector = 1;
1146
0
  *lpNumberOfFreeClusters = (UINT32)MIN(svfst.f_bavail, UINT32_MAX);
1147
0
  *lpTotalNumberOfClusters = (UINT32)MIN(svfst.f_blocks, UINT32_MAX);
1148
0
  return TRUE;
1149
0
}
1150
1151
BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
1152
                       LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
1153
                       LPDWORD lpTotalNumberOfClusters)
1154
0
{
1155
0
  BOOL ret = 0;
1156
0
  if (!lpRootPathName)
1157
0
    return FALSE;
1158
1159
0
  char* rootPathName = ConvertWCharToUtf8Alloc(lpRootPathName, NULL);
1160
0
  if (!rootPathName)
1161
0
  {
1162
0
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1163
0
    return FALSE;
1164
0
  }
1165
0
  ret = GetDiskFreeSpaceA(rootPathName, lpSectorsPerCluster, lpBytesPerSector,
1166
0
                          lpNumberOfFreeClusters, lpTotalNumberOfClusters);
1167
0
  free(rootPathName);
1168
0
  return ret;
1169
0
}
1170
1171
#endif /* _WIN32 */
1172
1173
/**
1174
 * Check if a file name component is valid.
1175
 *
1176
 * Some file names are not valid on Windows. See "Naming Files, Paths, and Namespaces":
1177
 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
1178
 */
1179
BOOL ValidFileNameComponent(LPCWSTR lpFileName)
1180
0
{
1181
0
  if (!lpFileName)
1182
0
    return FALSE;
1183
1184
  /* CON */
1185
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
1186
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
1187
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
1188
0
      (lpFileName[3] == L'\0'))
1189
0
  {
1190
0
    return FALSE;
1191
0
  }
1192
1193
  /* PRN */
1194
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'P' || lpFileName[0] == L'p')) &&
1195
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'R' || lpFileName[1] == L'r')) &&
1196
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) &&
1197
0
      (lpFileName[3] == L'\0'))
1198
0
  {
1199
0
    return FALSE;
1200
0
  }
1201
1202
  /* AUX */
1203
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'A' || lpFileName[0] == L'a')) &&
1204
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
1205
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'X' || lpFileName[2] == L'x')) &&
1206
0
      (lpFileName[3] == L'\0'))
1207
0
  {
1208
0
    return FALSE;
1209
0
  }
1210
1211
  /* NUL */
1212
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'N' || lpFileName[0] == L'n')) &&
1213
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) &&
1214
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'L' || lpFileName[2] == L'l')) &&
1215
0
      (lpFileName[3] == L'\0'))
1216
0
  {
1217
0
    return FALSE;
1218
0
  }
1219
1220
  /* LPT0-9 */
1221
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'L' || lpFileName[0] == L'l')) &&
1222
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'P' || lpFileName[1] == L'p')) &&
1223
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'T' || lpFileName[2] == L't')) &&
1224
0
      (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
1225
0
      (lpFileName[4] == L'\0'))
1226
0
  {
1227
0
    return FALSE;
1228
0
  }
1229
1230
  /* COM0-9 */
1231
0
  if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) &&
1232
0
      (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) &&
1233
0
      (lpFileName[2] != L'\0' && (lpFileName[2] == L'M' || lpFileName[2] == L'm')) &&
1234
0
      (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) &&
1235
0
      (lpFileName[4] == L'\0'))
1236
0
  {
1237
0
    return FALSE;
1238
0
  }
1239
1240
  /* Reserved characters */
1241
0
  for (LPCWSTR c = lpFileName; *c; c++)
1242
0
  {
1243
0
    if ((*c == L'<') || (*c == L'>') || (*c == L':') || (*c == L'"') || (*c == L'/') ||
1244
0
        (*c == L'\\') || (*c == L'|') || (*c == L'?') || (*c == L'*'))
1245
0
    {
1246
0
      return FALSE;
1247
0
    }
1248
0
  }
1249
1250
0
  return TRUE;
1251
0
}
1252
1253
#ifdef _UWP
1254
1255
HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1256
                   LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1257
                   DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1258
{
1259
  HANDLE hFile;
1260
  CREATEFILE2_EXTENDED_PARAMETERS params = { 0 };
1261
1262
  params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
1263
1264
  if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
1265
    params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS;
1266
  if (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE)
1267
    params.dwFileFlags |= FILE_FLAG_DELETE_ON_CLOSE;
1268
  if (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING)
1269
    params.dwFileFlags |= FILE_FLAG_NO_BUFFERING;
1270
  if (dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL)
1271
    params.dwFileFlags |= FILE_FLAG_OPEN_NO_RECALL;
1272
  if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT)
1273
    params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
1274
  if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REQUIRING_OPLOCK)
1275
    params.dwFileFlags |= FILE_FLAG_OPEN_REQUIRING_OPLOCK;
1276
  if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)
1277
    params.dwFileFlags |= FILE_FLAG_OVERLAPPED;
1278
  if (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS)
1279
    params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS;
1280
  if (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS)
1281
    params.dwFileFlags |= FILE_FLAG_RANDOM_ACCESS;
1282
  if (dwFlagsAndAttributes & FILE_FLAG_SESSION_AWARE)
1283
    params.dwFileFlags |= FILE_FLAG_SESSION_AWARE;
1284
  if (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN)
1285
    params.dwFileFlags |= FILE_FLAG_SEQUENTIAL_SCAN;
1286
  if (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH)
1287
    params.dwFileFlags |= FILE_FLAG_WRITE_THROUGH;
1288
1289
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ARCHIVE)
1290
    params.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
1291
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_COMPRESSED)
1292
    params.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
1293
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DEVICE)
1294
    params.dwFileAttributes |= FILE_ATTRIBUTE_DEVICE;
1295
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DIRECTORY)
1296
    params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1297
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ENCRYPTED)
1298
    params.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED;
1299
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_HIDDEN)
1300
    params.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1301
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM)
1302
    params.dwFileAttributes |= FILE_ATTRIBUTE_INTEGRITY_STREAM;
1303
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NORMAL)
1304
    params.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
1305
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
1306
    params.dwFileAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
1307
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA)
1308
    params.dwFileAttributes |= FILE_ATTRIBUTE_NO_SCRUB_DATA;
1309
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_OFFLINE)
1310
    params.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
1311
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)
1312
    params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1313
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1314
    params.dwFileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
1315
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
1316
    params.dwFileAttributes |= FILE_ATTRIBUTE_SPARSE_FILE;
1317
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SYSTEM)
1318
    params.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM;
1319
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_TEMPORARY)
1320
    params.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
1321
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_VIRTUAL)
1322
    params.dwFileAttributes |= FILE_ATTRIBUTE_VIRTUAL;
1323
1324
  if (dwFlagsAndAttributes & SECURITY_ANONYMOUS)
1325
    params.dwSecurityQosFlags |= SECURITY_ANONYMOUS;
1326
  if (dwFlagsAndAttributes & SECURITY_CONTEXT_TRACKING)
1327
    params.dwSecurityQosFlags |= SECURITY_CONTEXT_TRACKING;
1328
  if (dwFlagsAndAttributes & SECURITY_DELEGATION)
1329
    params.dwSecurityQosFlags |= SECURITY_DELEGATION;
1330
  if (dwFlagsAndAttributes & SECURITY_EFFECTIVE_ONLY)
1331
    params.dwSecurityQosFlags |= SECURITY_EFFECTIVE_ONLY;
1332
  if (dwFlagsAndAttributes & SECURITY_IDENTIFICATION)
1333
    params.dwSecurityQosFlags |= SECURITY_IDENTIFICATION;
1334
  if (dwFlagsAndAttributes & SECURITY_IMPERSONATION)
1335
    params.dwSecurityQosFlags |= SECURITY_IMPERSONATION;
1336
1337
  params.lpSecurityAttributes = lpSecurityAttributes;
1338
  params.hTemplateFile = hTemplateFile;
1339
1340
  hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, &params);
1341
1342
  return hFile;
1343
}
1344
1345
HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
1346
                   LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1347
                   DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1348
{
1349
  HANDLE hFile;
1350
  if (!lpFileName)
1351
    return NULL;
1352
1353
  WCHAR* lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
1354
1355
  if (!lpFileNameW)
1356
    return NULL;
1357
1358
  hFile = CreateFileW(lpFileNameW, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
1359
                      dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
1360
1361
  free(lpFileNameW);
1362
1363
  return hFile;
1364
}
1365
1366
DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
1367
{
1368
  BOOL status;
1369
  LARGE_INTEGER fileSize = { 0, 0 };
1370
1371
  if (!lpFileSizeHigh)
1372
    return INVALID_FILE_SIZE;
1373
1374
  status = GetFileSizeEx(hFile, &fileSize);
1375
1376
  if (!status)
1377
    return INVALID_FILE_SIZE;
1378
1379
  *lpFileSizeHigh = fileSize.HighPart;
1380
1381
  return fileSize.LowPart;
1382
}
1383
1384
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
1385
                     DWORD dwMoveMethod)
1386
{
1387
  BOOL status;
1388
  LARGE_INTEGER liDistanceToMove = { 0, 0 };
1389
  LARGE_INTEGER liNewFilePointer = { 0, 0 };
1390
1391
  liDistanceToMove.LowPart = lDistanceToMove;
1392
1393
  status = SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, dwMoveMethod);
1394
1395
  if (!status)
1396
    return INVALID_SET_FILE_POINTER;
1397
1398
  if (lpDistanceToMoveHigh)
1399
    *lpDistanceToMoveHigh = liNewFilePointer.HighPart;
1400
1401
  return liNewFilePointer.LowPart;
1402
}
1403
1404
HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
1405
{
1406
  return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
1407
                          NULL, 0);
1408
}
1409
1410
HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1411
{
1412
  return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch,
1413
                          NULL, 0);
1414
}
1415
1416
DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart)
1417
{
1418
  DWORD dwStatus;
1419
  WCHAR* lpFileNameW = NULL;
1420
  WCHAR* lpBufferW = NULL;
1421
  WCHAR* lpFilePartW = NULL;
1422
  DWORD nBufferLengthW = nBufferLength * sizeof(WCHAR);
1423
1424
  if (!lpFileName || (nBufferLength < 1))
1425
    return 0;
1426
1427
  lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
1428
  if (!lpFileNameW)
1429
    return 0;
1430
1431
  lpBufferW = (WCHAR*)malloc(nBufferLengthW);
1432
1433
  if (!lpBufferW)
1434
    return 0;
1435
1436
  dwStatus = GetFullPathNameW(lpFileNameW, nBufferLengthW, lpBufferW, &lpFilePartW);
1437
1438
  (void)ConvertWCharNToUtf8(lpBufferW, nBufferLengthW / sizeof(WCHAR), lpBuffer, nBufferLength);
1439
1440
  if (lpFilePart)
1441
    lpFilePart = lpBuffer + (lpFilePartW - lpBufferW);
1442
1443
  free(lpFileNameW);
1444
  free(lpBufferW);
1445
1446
  return dwStatus * 2;
1447
}
1448
1449
BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector,
1450
                       LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters)
1451
{
1452
  BOOL status;
1453
  ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
1454
  ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
1455
  ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
1456
1457
  status = GetDiskFreeSpaceExA(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
1458
                               &TotalNumberOfFreeBytes);
1459
1460
  if (!status)
1461
    return FALSE;
1462
1463
  *lpBytesPerSector = 1;
1464
  *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
1465
  *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
1466
  *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
1467
1468
  return TRUE;
1469
}
1470
1471
BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster,
1472
                       LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters,
1473
                       LPDWORD lpTotalNumberOfClusters)
1474
{
1475
  BOOL status;
1476
  ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 };
1477
  ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 };
1478
  ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 };
1479
1480
  status = GetDiskFreeSpaceExW(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes,
1481
                               &TotalNumberOfFreeBytes);
1482
1483
  if (!status)
1484
    return FALSE;
1485
1486
  *lpBytesPerSector = 1;
1487
  *lpSectorsPerCluster = TotalNumberOfBytes.LowPart;
1488
  *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart;
1489
  *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart;
1490
1491
  return TRUE;
1492
}
1493
1494
DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer)
1495
{
1496
  SetLastError(ERROR_INVALID_FUNCTION);
1497
  return 0;
1498
}
1499
1500
DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer)
1501
{
1502
  SetLastError(ERROR_INVALID_FUNCTION);
1503
  return 0;
1504
}
1505
1506
BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
1507
{
1508
  return FALSE;
1509
}
1510
1511
UINT GetACP(void)
1512
{
1513
  return CP_UTF8;
1514
}
1515
1516
#endif
1517
1518
/* Extended API */
1519
1520
#ifdef _WIN32
1521
#include <io.h>
1522
#endif
1523
1524
HANDLE GetFileHandleForFileDescriptor(int fd)
1525
0
{
1526
#ifdef _WIN32
1527
  return (HANDLE)_get_osfhandle(fd);
1528
#else  /* _WIN32 */
1529
0
  WINPR_FILE* pFile = NULL;
1530
0
  FILE* fp = NULL;
1531
0
  int flags = 0;
1532
1533
  /* Make sure it's a valid fd */
1534
0
  if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
1535
0
    return INVALID_HANDLE_VALUE;
1536
1537
0
  flags = fcntl(fd, F_GETFL);
1538
0
  if (flags == -1)
1539
0
    return INVALID_HANDLE_VALUE;
1540
1541
0
  if (flags & O_WRONLY)
1542
0
    fp = fdopen(fd, "wb");
1543
0
  else
1544
0
    fp = fdopen(fd, "rb");
1545
1546
0
  if (!fp)
1547
0
    return INVALID_HANDLE_VALUE;
1548
1549
0
  (void)setvbuf(fp, NULL, _IONBF, 0);
1550
1551
  // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
1552
0
  pFile = FileHandle_New(fp);
1553
0
  if (!pFile)
1554
0
    return INVALID_HANDLE_VALUE;
1555
1556
0
  return (HANDLE)pFile;
1557
0
#endif /* _WIN32 */
1558
0
}
1559
1560
FILE* winpr_fopen(const char* path, const char* mode)
1561
0
{
1562
0
#ifndef _WIN32
1563
0
  return fopen(path, mode);
1564
#else
1565
  LPWSTR lpPathW = NULL;
1566
  LPWSTR lpModeW = NULL;
1567
  FILE* result = NULL;
1568
1569
  if (!path || !mode)
1570
    return NULL;
1571
1572
  lpPathW = ConvertUtf8ToWCharAlloc(path, NULL);
1573
  if (!lpPathW)
1574
    goto cleanup;
1575
1576
  lpModeW = ConvertUtf8ToWCharAlloc(mode, NULL);
1577
  if (!lpModeW)
1578
    goto cleanup;
1579
1580
  result = _wfopen(lpPathW, lpModeW);
1581
1582
cleanup:
1583
  free(lpPathW);
1584
  free(lpModeW);
1585
  return result;
1586
#endif
1587
0
}
1588
1589
#if !defined(_UWP) && !defined(_WIN32)
1590
DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer)
1591
0
{
1592
0
  char* buffer = NULL;
1593
0
  if (nBufferLength > 0)
1594
0
  {
1595
0
    buffer = calloc(nBufferLength, sizeof(char));
1596
0
    if (!buffer)
1597
0
      return 0;
1598
0
  }
1599
1600
0
  const DWORD rc = GetLogicalDriveStringsA(nBufferLength, buffer);
1601
0
  if (buffer)
1602
0
    ConvertMszUtf8NToWChar(buffer, rc, lpBuffer, nBufferLength);
1603
0
  free(buffer);
1604
0
  return rc;
1605
0
}
1606
1607
DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer)
1608
0
{
1609
  /* format is '<name1>\0<name2>\0...<nameX>\0\0'
1610
   * for details see
1611
   * https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getlogicaldrivestringsa
1612
   */
1613
0
  const char path[] = "/\0";
1614
0
  const size_t len = sizeof(path);
1615
0
  if (nBufferLength < len)
1616
0
    return WINPR_ASSERTING_INT_CAST(DWORD, len);
1617
1618
0
  memcpy(lpBuffer, path, len);
1619
0
  return len - 1;
1620
0
}
1621
#endif