Coverage Report

Created: 2026-02-26 06:50

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