Coverage Report

Created: 2026-08-31 06:25

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