Coverage Report

Created: 2026-10-28 06:49

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