Coverage Report

Created: 2026-03-04 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/path/shell.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Path Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/config.h>
22
#include <winpr/build-config.h>
23
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <sys/stat.h>
28
29
#include <winpr/crt.h>
30
#include <winpr/platform.h>
31
#include <winpr/file.h>
32
#include <winpr/tchar.h>
33
#include <winpr/environment.h>
34
35
#include <winpr/path.h>
36
#include <winpr/wlog.h>
37
38
#include "../file/file.h"
39
40
#include "../log.h"
41
#define TAG WINPR_TAG("path.shell")
42
43
#if defined(__IOS__)
44
#include "shell_ios.h"
45
#endif
46
47
#if defined(WIN32)
48
#include <windows.h>
49
#include <shlobj.h>
50
#else
51
#include <errno.h>
52
#include <dirent.h>
53
#endif
54
55
static char* GetPath_XDG_CONFIG_HOME(void);
56
static char* GetPath_XDG_RUNTIME_DIR(void);
57
58
/**
59
 * SHGetKnownFolderPath function:
60
 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
61
 */
62
63
#if defined(WIN32) && !defined(_UWP)
64
65
static char* win_get_known_folder(REFKNOWNFOLDERID id, BOOL currentUser)
66
{
67
  WCHAR* wpath = nullptr;
68
  HANDLE handle = currentUser ? nullptr : (HANDLE)-1;
69
  if (FAILED(SHGetKnownFolderPath(id, 0, handle, &wpath)))
70
    return nullptr;
71
72
  if (!wpath)
73
    return nullptr;
74
75
  char* path = ConvertWCharToUtf8Alloc(wpath, nullptr);
76
  CoTaskMemFree(wpath);
77
  return path;
78
}
79
#endif
80
81
/**
82
 * XDG Base Directory Specification:
83
 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
84
 */
85
86
char* GetEnvAlloc(LPCSTR lpName)
87
0
{
88
0
  DWORD nSize = 0;
89
0
  DWORD nStatus = 0;
90
0
  char* env = nullptr;
91
92
0
  nSize = GetEnvironmentVariableX(lpName, nullptr, 0);
93
94
0
  if (nSize > 0)
95
0
  {
96
0
    env = malloc(nSize);
97
98
0
    if (!env)
99
0
      return nullptr;
100
101
0
    nStatus = GetEnvironmentVariableX(lpName, env, nSize);
102
103
0
    if (nStatus != (nSize - 1))
104
0
    {
105
0
      free(env);
106
0
      return nullptr;
107
0
    }
108
0
  }
109
110
0
  return env;
111
0
}
112
113
static char* GetPath_HOME(void)
114
0
{
115
0
  char* path = nullptr;
116
#ifdef _WIN32
117
  path = GetEnvAlloc("UserProfile");
118
#elif defined(__IOS__)
119
  path = ios_get_home();
120
#else
121
0
  path = GetEnvAlloc("HOME");
122
0
#endif
123
0
  return path;
124
0
}
125
126
static char* GetPath_TEMP(void)
127
0
{
128
0
  char* path = nullptr;
129
#ifdef _WIN32
130
  path = GetEnvAlloc("TEMP");
131
#elif defined(__IOS__)
132
  path = ios_get_temp();
133
#else
134
0
  path = GetEnvAlloc("TMPDIR");
135
136
0
  if (!path)
137
0
    path = _strdup("/tmp");
138
139
0
#endif
140
0
  return path;
141
0
}
142
143
static char* GetPath_XDG_DATA_HOME(void)
144
0
{
145
0
  char* path = nullptr;
146
#if defined(WIN32) || defined(__IOS__)
147
  path = GetPath_XDG_CONFIG_HOME();
148
#else
149
0
  size_t size = 0;
150
0
  char* home = nullptr;
151
  /**
152
   * There is a single base directory relative to which user-specific data files should be
153
   * written. This directory is defined by the environment variable $XDG_DATA_HOME.
154
   *
155
   * $XDG_DATA_HOME defines the base directory relative to which user specific data files should
156
   * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
157
   * $HOME/.local/share should be used.
158
   */
159
0
  path = GetEnvAlloc("XDG_DATA_HOME");
160
161
0
  if (path)
162
0
    return path;
163
164
0
  home = GetPath_HOME();
165
166
0
  if (!home)
167
0
    return nullptr;
168
169
0
  size = strlen(home) + strlen("/.local/share") + 1;
170
0
  path = (char*)malloc(size);
171
172
0
  if (!path)
173
0
  {
174
0
    free(home);
175
0
    return nullptr;
176
0
  }
177
178
0
  (void)sprintf_s(path, size, "%s%s", home, "/.local/share");
179
0
  free(home);
180
0
#endif
181
0
  return path;
182
0
}
183
184
static char* GetPath_XDG_CONFIG_HOME(void)
185
0
{
186
0
  char* path = nullptr;
187
#if defined(WIN32) && !defined(_UWP)
188
189
  path = win_get_known_folder(&FOLDERID_RoamingAppData, TRUE);
190
191
#elif defined(__IOS__)
192
  path = ios_get_data();
193
#else
194
0
  size_t size = 0;
195
0
  char* home = nullptr;
196
  /**
197
   * There is a single base directory relative to which user-specific configuration files should
198
   * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
199
   *
200
   * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
201
   * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
202
   * $HOME/.config should be used.
203
   */
204
0
  path = GetEnvAlloc("XDG_CONFIG_HOME");
205
206
0
  if (path)
207
0
    return path;
208
209
0
  home = GetPath_HOME();
210
211
0
  if (!home)
212
0
    home = GetPath_TEMP();
213
214
0
  if (!home)
215
0
    return nullptr;
216
217
0
  size = strlen(home) + strlen("/.config") + 1;
218
0
  path = (char*)malloc(size);
219
220
0
  if (!path)
221
0
  {
222
0
    free(home);
223
0
    return nullptr;
224
0
  }
225
226
0
  (void)sprintf_s(path, size, "%s%s", home, "/.config");
227
0
  free(home);
228
0
#endif
229
0
  return path;
230
0
}
231
232
static char* GetPath_SYSTEM_CONFIG_HOME(void)
233
11.3k
{
234
11.3k
  char* path = nullptr;
235
#if defined(WIN32) && !defined(_UWP)
236
237
  path = win_get_known_folder(&FOLDERID_ProgramData, FALSE);
238
239
#elif defined(__IOS__)
240
  path = ios_get_data();
241
#else
242
11.3k
  path = _strdup(WINPR_INSTALL_SYSCONFDIR);
243
11.3k
#endif
244
11.3k
  return path;
245
11.3k
}
246
247
static char* GetPath_XDG_CACHE_HOME(void)
248
0
{
249
0
  char* path = nullptr;
250
#if defined(WIN32)
251
  {
252
    char* home = GetPath_XDG_RUNTIME_DIR();
253
254
    if (home)
255
    {
256
      path = GetCombinedPath(home, "cache");
257
258
      if (!winpr_PathFileExists(path))
259
        if (!winpr_PathMakePath(path, nullptr))
260
          path = nullptr;
261
    }
262
263
    free(home);
264
  }
265
#elif defined(__IOS__)
266
  path = ios_get_cache();
267
#else
268
0
  size_t size = 0;
269
  /**
270
   * There is a single base directory relative to which user-specific non-essential (cached) data
271
   * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
272
   *
273
   * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data
274
   * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to
275
   * $HOME/.cache should be used.
276
   */
277
0
  path = GetEnvAlloc("XDG_CACHE_HOME");
278
279
0
  if (path)
280
0
    return path;
281
282
0
  char* home = GetPath_HOME();
283
284
0
  if (!home)
285
0
    return nullptr;
286
287
0
  size = strlen(home) + strlen("/.cache") + 1;
288
0
  path = (char*)malloc(size);
289
290
0
  if (!path)
291
0
  {
292
0
    free(home);
293
0
    return nullptr;
294
0
  }
295
296
0
  (void)sprintf_s(path, size, "%s%s", home, "/.cache");
297
0
  free(home);
298
0
#endif
299
0
  return path;
300
0
}
301
302
char* GetPath_XDG_RUNTIME_DIR(void)
303
0
{
304
0
  char* path = nullptr;
305
#if defined(WIN32) && !defined(_UWP)
306
307
  path = win_get_known_folder(&FOLDERID_LocalAppData, TRUE);
308
309
#else
310
  /**
311
   * There is a single base directory relative to which user-specific runtime files and other file
312
   * objects should be placed. This directory is defined by the environment variable
313
   * $XDG_RUNTIME_DIR.
314
   *
315
   * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
316
   * runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
317
   * The directory MUST be owned by the user, and he MUST be the only one having read and write
318
   * access to it. Its Unix access mode MUST be 0700.
319
   *
320
   * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created
321
   * when the user first logs in and if the user fully logs out the directory MUST be removed. If
322
   * the user logs in more than once he should get pointed to the same directory, and it is
323
   * mandatory that the directory continues to exist from his first login to his last logout on
324
   * the system, and not removed in between. Files in the directory MUST not survive reboot or a
325
   * full logout/login cycle.
326
   *
327
   * The directory MUST be on a local file system and not shared with any other system. The
328
   * directory MUST by fully-featured by the standards of the operating system. More specifically,
329
   * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper
330
   * permissions, file locking, sparse files, memory mapping, file change notifications, a
331
   * reliable hard link count must be supported, and no restrictions on the file name character
332
   * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To
333
   * ensure that your files are not removed, they should have their access time timestamp modified
334
   * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
335
   *
336
   * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with
337
   * similar capabilities and print a warning message. Applications should use this directory for
338
   * communication and synchronization purposes and should not place larger files in it, since it
339
   * might reside in runtime memory and cannot necessarily be swapped out to disk.
340
   */
341
0
  path = GetEnvAlloc("XDG_RUNTIME_DIR");
342
0
#endif
343
344
0
  if (path)
345
0
    return path;
346
347
0
  path = GetPath_TEMP();
348
0
  return path;
349
0
}
350
351
char* GetKnownPath(eKnownPathTypes id)
352
11.3k
{
353
11.3k
  char* path = nullptr;
354
355
11.3k
  switch (id)
356
11.3k
  {
357
0
    case KNOWN_PATH_HOME:
358
0
      path = GetPath_HOME();
359
0
      break;
360
361
0
    case KNOWN_PATH_TEMP:
362
0
      path = GetPath_TEMP();
363
0
      break;
364
365
0
    case KNOWN_PATH_XDG_DATA_HOME:
366
0
      path = GetPath_XDG_DATA_HOME();
367
0
      break;
368
369
0
    case KNOWN_PATH_XDG_CONFIG_HOME:
370
0
      path = GetPath_XDG_CONFIG_HOME();
371
0
      break;
372
373
0
    case KNOWN_PATH_XDG_CACHE_HOME:
374
0
      path = GetPath_XDG_CACHE_HOME();
375
0
      break;
376
377
0
    case KNOWN_PATH_XDG_RUNTIME_DIR:
378
0
      path = GetPath_XDG_RUNTIME_DIR();
379
0
      break;
380
381
11.3k
    case KNOWN_PATH_SYSTEM_CONFIG_HOME:
382
11.3k
      path = GetPath_SYSTEM_CONFIG_HOME();
383
11.3k
      break;
384
385
0
    default:
386
0
      path = nullptr;
387
0
      break;
388
11.3k
  }
389
390
11.3k
  if (!path)
391
0
    WLog_WARN(TAG, "Path %s is nullptr",
392
11.3k
              GetKnownPathIdString(WINPR_ASSERTING_INT_CAST(int, id)));
393
11.3k
  return path;
394
11.3k
}
395
396
char* GetKnownSubPath(eKnownPathTypes id, const char* path)
397
0
{
398
0
  if (!path)
399
0
    return GetKnownSubPathV(id, "%s", "");
400
0
  return GetKnownSubPathV(id, "%s", path);
401
0
}
402
403
char* GetKnownSubPathV(eKnownPathTypes id, const char* path, ...)
404
11.3k
{
405
11.3k
  va_list ap = WINPR_C_ARRAY_INIT;
406
407
11.3k
  va_start(ap, path);
408
11.3k
  char* str = GetKnownSubPathVA(id, path, ap);
409
11.3k
  va_end(ap);
410
11.3k
  return str;
411
11.3k
}
412
413
char* GetKnownSubPathVA(eKnownPathTypes id, const char* path, va_list ap)
414
11.3k
{
415
11.3k
  char* knownPath = GetKnownPath(id);
416
11.3k
  if (!knownPath)
417
0
    return nullptr;
418
419
11.3k
  char* subPath = GetCombinedPathVA(knownPath, path, ap);
420
11.3k
  free(knownPath);
421
11.3k
  return subPath;
422
11.3k
}
423
424
char* GetEnvironmentPath(char* name)
425
0
{
426
0
  char* env = nullptr;
427
0
  DWORD nSize = 0;
428
0
  DWORD nStatus = 0;
429
0
  nSize = GetEnvironmentVariableX(name, nullptr, 0);
430
431
0
  if (nSize)
432
0
  {
433
0
    env = (LPSTR)malloc(nSize);
434
435
0
    if (!env)
436
0
      return nullptr;
437
438
0
    nStatus = GetEnvironmentVariableX(name, env, nSize);
439
440
0
    if (nStatus != (nSize - 1))
441
0
    {
442
0
      free(env);
443
0
      return nullptr;
444
0
    }
445
0
  }
446
447
0
  return env;
448
0
}
449
450
char* GetEnvironmentSubPath(char* name, const char* path)
451
0
{
452
0
  if (!path)
453
0
    return GetEnvironmentSubPathV(name, "%s", "");
454
0
  return GetEnvironmentSubPathV(name, "%s", path);
455
0
}
456
457
char* GetEnvironmentSubPathV(char* name, const char* path, ...)
458
0
{
459
0
  va_list ap = WINPR_C_ARRAY_INIT;
460
0
  va_start(ap, path);
461
0
  char* str = GetEnvironmentSubPathVA(name, path, ap);
462
0
  va_end(ap);
463
0
  return str;
464
0
}
465
466
char* GetEnvironmentSubPathVA(char* name, WINPR_FORMAT_ARG const char* path, va_list ap)
467
0
{
468
0
  char* env = GetEnvironmentPath(name);
469
470
0
  if (!env)
471
0
    return nullptr;
472
473
0
  char* subpath = GetCombinedPathVA(env, path, ap);
474
0
  free(env);
475
0
  return subpath;
476
0
}
477
478
char* GetCombinedPath(const char* basePath, const char* subPathFmt)
479
0
{
480
0
  if (!subPathFmt)
481
0
    return GetCombinedPathV(basePath, "%s", "");
482
0
  return GetCombinedPathV(basePath, "%s", subPathFmt);
483
0
}
484
485
char* GetCombinedPathV(const char* basePath, const char* subPathFmt, ...)
486
11.3k
{
487
11.3k
  va_list ap = WINPR_C_ARRAY_INIT;
488
489
11.3k
  va_start(ap, subPathFmt);
490
11.3k
  char* str = GetCombinedPathVA(basePath, subPathFmt, ap);
491
11.3k
  va_end(ap);
492
11.3k
  return str;
493
11.3k
}
494
495
char* GetCombinedPathVA(const char* basePath, WINPR_FORMAT_ARG const char* subPathFmt, va_list ap)
496
34.1k
{
497
34.1k
  HRESULT status = 0;
498
34.1k
  char* subPathCpy = nullptr;
499
34.1k
  size_t basePathLength = 0;
500
34.1k
  size_t subPathLength = 0;
501
502
34.1k
  if (basePath)
503
34.1k
    basePathLength = strlen(basePath);
504
505
34.1k
  bool haveSubPath = subPathFmt && (*subPathFmt != '\0');
506
34.1k
  if (haveSubPath)
507
34.1k
  {
508
34.1k
    const int rc = winpr_vasprintf(&subPathCpy, &subPathLength, subPathFmt, ap);
509
34.1k
    if (rc < 0)
510
0
      return nullptr;
511
34.1k
    if (rc == 0)
512
0
    {
513
0
      free(subPathCpy);
514
0
      subPathCpy = nullptr;
515
0
      subPathLength = 0;
516
0
      haveSubPath = false;
517
0
    }
518
34.1k
  }
519
520
34.1k
  const size_t length = basePathLength + subPathLength + 1;
521
34.1k
  char* path = (char*)calloc(1, length + 1);
522
523
34.1k
  if (!path)
524
0
    goto fail;
525
526
34.1k
  if (basePath)
527
34.1k
    CopyMemory(path, basePath, basePathLength);
528
529
34.1k
  if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
530
0
    goto fail;
531
532
34.1k
  if (!haveSubPath)
533
0
    return path;
534
535
34.1k
  if (!subPathCpy)
536
0
    goto fail;
537
538
34.1k
  if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
539
0
    goto fail;
540
541
34.1k
  status = NativePathCchAppendA(path, length + 1, subPathCpy);
542
34.1k
  if (FAILED(status))
543
0
    goto fail;
544
545
34.1k
  free(subPathCpy);
546
34.1k
  return path;
547
548
0
fail:
549
0
  free(path);
550
0
  free(subPathCpy);
551
0
  return nullptr;
552
34.1k
}
553
554
BOOL PathMakePathA(LPCSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
555
0
{
556
#if defined(_UWP)
557
  return FALSE;
558
#elif defined(_WIN32)
559
  return (SHCreateDirectoryExA(nullptr, path, lpAttributes) == ERROR_SUCCESS);
560
#else
561
0
  const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
562
0
  char* dup = nullptr;
563
0
  BOOL result = TRUE;
564
  /* we only operate on a non-null, absolute path */
565
#if defined(__OS2__)
566
567
  if (!path)
568
    return FALSE;
569
570
#else
571
572
0
  if (!path || *path != delim)
573
0
    return FALSE;
574
575
0
#endif
576
577
0
  if (!(dup = _strdup(path)))
578
0
    return FALSE;
579
580
#ifdef __OS2__
581
  p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
582
583
  while (p)
584
#else
585
0
  for (char* p = dup; p;)
586
0
#endif
587
0
  {
588
0
    if ((p = strchr(p + 1, delim)))
589
0
      *p = '\0';
590
591
0
    if (mkdir(dup, 0777) != 0)
592
0
      if (errno != EEXIST)
593
0
      {
594
0
        result = FALSE;
595
0
        break;
596
0
      }
597
598
0
    if (p)
599
0
      *p = delim;
600
0
  }
601
602
0
  free(dup);
603
0
  return (result);
604
0
#endif
605
0
}
606
607
BOOL PathMakePathW(LPCWSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
608
0
{
609
#if defined(_UWP)
610
  return FALSE;
611
#elif defined(_WIN32)
612
  return (SHCreateDirectoryExW(nullptr, path, lpAttributes) == ERROR_SUCCESS);
613
#else
614
0
  const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
615
0
  const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
616
0
  char* dup = nullptr;
617
0
  BOOL result = TRUE;
618
  /* we only operate on a non-null, absolute path */
619
#if defined(__OS2__)
620
621
  if (!path)
622
    return FALSE;
623
624
#else
625
626
0
  if (!path || *path != wdelim)
627
0
    return FALSE;
628
629
0
#endif
630
631
0
  dup = ConvertWCharToUtf8Alloc(path, nullptr);
632
0
  if (!dup)
633
0
    return FALSE;
634
635
#ifdef __OS2__
636
  p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
637
638
  while (p)
639
#else
640
0
  for (char* p = dup; p;)
641
0
#endif
642
0
  {
643
0
    if ((p = strchr(p + 1, delim)))
644
0
      *p = '\0';
645
646
0
    if (mkdir(dup, 0777) != 0)
647
0
      if (errno != EEXIST)
648
0
      {
649
0
        result = FALSE;
650
0
        break;
651
0
      }
652
653
0
    if (p)
654
0
      *p = delim;
655
0
  }
656
657
0
  free(dup);
658
0
  return (result);
659
0
#endif
660
0
}
661
662
#if !defined(_WIN32) || defined(_UWP)
663
664
BOOL PathIsRelativeA(LPCSTR pszPath)
665
0
{
666
0
  if (!pszPath)
667
0
    return FALSE;
668
669
0
  return pszPath[0] != '/';
670
0
}
671
672
BOOL PathIsRelativeW(LPCWSTR pszPath)
673
0
{
674
0
  LPSTR lpFileNameA = nullptr;
675
0
  BOOL ret = FALSE;
676
677
0
  if (!pszPath)
678
0
    goto fail;
679
680
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
681
0
  if (!lpFileNameA)
682
0
    goto fail;
683
0
  ret = PathIsRelativeA(lpFileNameA);
684
0
fail:
685
0
  free(lpFileNameA);
686
0
  return ret;
687
0
}
688
689
BOOL PathFileExistsA(LPCSTR pszPath)
690
0
{
691
0
  struct stat stat_info;
692
693
0
  return (stat(pszPath, &stat_info) == 0);
694
0
}
695
696
BOOL PathFileExistsW(LPCWSTR pszPath)
697
0
{
698
0
  LPSTR lpFileNameA = nullptr;
699
0
  BOOL ret = FALSE;
700
701
0
  if (!pszPath)
702
0
    goto fail;
703
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
704
0
  if (!lpFileNameA)
705
0
    goto fail;
706
707
0
  ret = winpr_PathFileExists(lpFileNameA);
708
0
fail:
709
0
  free(lpFileNameA);
710
0
  return ret;
711
0
}
712
713
BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
714
0
{
715
0
  struct dirent* dp = nullptr;
716
0
  int empty = 1;
717
0
  DIR* dir = opendir(pszPath);
718
719
0
  if (dir == nullptr) /* Not a directory or doesn't exist */
720
0
    return 1;
721
722
  // NOLINTNEXTLINE(concurrency-mt-unsafe)
723
0
  while ((dp = readdir(dir)) != nullptr)
724
0
  {
725
0
    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
726
0
      continue; /* Skip . and .. */
727
728
0
    empty = 0;
729
0
    break;
730
0
  }
731
732
0
  closedir(dir);
733
0
  return empty;
734
0
}
735
736
BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
737
0
{
738
0
  LPSTR lpFileNameA = nullptr;
739
0
  BOOL ret = FALSE;
740
0
  if (!pszPath)
741
0
    goto fail;
742
0
  lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, nullptr);
743
0
  if (!lpFileNameA)
744
0
    goto fail;
745
0
  ret = PathIsDirectoryEmptyA(lpFileNameA);
746
0
fail:
747
0
  free(lpFileNameA);
748
0
  return ret;
749
0
}
750
751
#endif
752
753
BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
754
0
{
755
0
  return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
756
0
}
757
758
BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
759
0
{
760
0
#ifndef _WIN32
761
0
  struct stat st;
762
0
  int ret = 0;
763
0
  ret = stat(lpNewFileName, &st);
764
765
0
  if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
766
0
  {
767
0
    if (ret == 0)
768
0
    {
769
0
      SetLastError(ERROR_ALREADY_EXISTS);
770
0
      return FALSE;
771
0
    }
772
0
  }
773
0
  else
774
0
  {
775
0
    if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
776
0
    {
777
0
      SetLastError(ERROR_ACCESS_DENIED);
778
0
      return FALSE;
779
0
    }
780
0
  }
781
782
0
  ret = rename(lpExistingFileName, lpNewFileName);
783
784
0
  if (ret != 0)
785
0
    SetLastError(map_posix_err(errno));
786
787
0
  return ret == 0;
788
#else
789
  BOOL result = FALSE;
790
  LPWSTR lpExistingFileNameW = nullptr;
791
  LPWSTR lpNewFileNameW = nullptr;
792
793
  if (!lpExistingFileName || !lpNewFileName)
794
    return FALSE;
795
796
  lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, nullptr);
797
  if (!lpExistingFileNameW)
798
    goto cleanup;
799
  lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, nullptr);
800
  if (!lpNewFileNameW)
801
    goto cleanup;
802
803
  result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
804
805
cleanup:
806
  free(lpExistingFileNameW);
807
  free(lpNewFileNameW);
808
  return result;
809
#endif
810
0
}
811
812
BOOL winpr_DeleteFile(const char* lpFileName)
813
0
{
814
0
#ifndef _WIN32
815
0
  if (!lpFileName)
816
0
    return FALSE;
817
818
0
  const int status = unlink(lpFileName);
819
0
  return (status != -1);
820
#else
821
  LPWSTR lpFileNameW = nullptr;
822
  BOOL result = FALSE;
823
824
  if (lpFileName)
825
    lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, nullptr);
826
827
  if (!lpFileNameW)
828
    goto cleanup;
829
830
  result = DeleteFileW(lpFileNameW);
831
832
cleanup:
833
  free(lpFileNameW);
834
  return result;
835
#endif
836
0
}
837
838
BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
839
0
{
840
0
#ifndef _WIN32
841
0
  int ret = rmdir(lpPathName);
842
843
0
  if (ret != 0)
844
0
    SetLastError(map_posix_err(errno));
845
0
  else
846
0
    SetLastError(STATUS_SUCCESS);
847
848
0
  return ret == 0;
849
#else
850
  LPWSTR lpPathNameW = nullptr;
851
  BOOL result = FALSE;
852
853
  if (lpPathName)
854
    lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, nullptr);
855
856
  if (!lpPathNameW)
857
    goto cleanup;
858
859
  result = RemoveDirectoryW(lpPathNameW);
860
861
cleanup:
862
  free(lpPathNameW);
863
  return result;
864
#endif
865
0
}
866
867
BOOL winpr_PathFileExists(const char* pszPath)
868
0
{
869
0
  if (!pszPath)
870
0
    return FALSE;
871
0
#ifndef _WIN32
872
0
  return PathFileExistsA(pszPath);
873
#else
874
  WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, nullptr);
875
  BOOL result = FALSE;
876
877
  if (!pathW)
878
    return FALSE;
879
880
  result = PathFileExistsW(pathW);
881
  free(pathW);
882
883
  return result;
884
#endif
885
0
}
886
887
BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
888
0
{
889
0
  if (!path)
890
0
    return FALSE;
891
0
#ifndef _WIN32
892
0
  return PathMakePathA(path, lpAttributes);
893
#else
894
  WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, nullptr);
895
  BOOL result = FALSE;
896
897
  if (!pathW)
898
    return FALSE;
899
900
  result = SHCreateDirectoryExW(nullptr, pathW, lpAttributes) == ERROR_SUCCESS;
901
  free(pathW);
902
903
  return result;
904
#endif
905
0
}