Coverage Report

Created: 2026-10-15 06:26

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