1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-2010 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Copyright transferred from Raider Solutions, Inc to
25  *   Kern Sibbald and John Walker by express permission.
26  *
27  * Author          : Christopher S. Hull
28  * Created On      : Sat Jan 31 15:55:00 2004
29  */
30 /**
31  * @file
32  * compatibilty layer to make bareos-fd run natively under windows
33  */
34 #include "include/bareos.h"
35 #include "include/jcr.h"
36 #include "dlfcn.h"
37 #include "findlib/find.h"
38 #include "lib/btimers.h"
39 #include "lib/bsignal.h"
40 #include "lib/daemon.h"
41 #include "lib/berrno.h"
42 #pragma GCC diagnostic push
43 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
44 #include "vss.h"
45 #pragma GCC diagnostic pop
46 
47 /**
48  * Sanity check to make sure FILE_ATTRIBUTE_VALID_FLAGS is always smaller
49  * than our define of FILE_ATTRIBUTE_VOLUME_MOUNT_POINT.
50  */
51 #if FILE_ATTRIBUTE_VOLUME_MOUNT_POINT < FILE_ATTRIBUTE_VALID_FLAGS
52 #error "FILE_ATTRIBUTE_VALID_FLAGS smaller than FILE_ATTRIBUTE_VALID_FLAGS"
53 #endif
54 
55 /**
56  * Note, if you want to see what Windows variables and structures
57  * are defined, include/bareos.h includes <windows.h>, which is found in:
58  *
59  *   cross-tools/mingw32/mingw32/include
60  * or
61  *   cross-tools/mingw-w64/x86_64-pc-mingw32/include
62  *
63  * depending on whether we are building the 32 bit version or
64  * the 64 bit version.
65  */
66 static const int debuglevel = 500;
67 
68 #define MAX_PATHLENGTH 1024
69 
70 static pthread_mutex_t com_security_mutex = PTHREAD_MUTEX_INITIALIZER;
71 static bool com_security_initialized = false;
72 
73 /**
74  * The CoInitializeSecurity function initializes the security layer and sets the
75  * specified values as the security default. If a process does not call
76  * CoInitializeSecurity, COM calls it automatically the first time an interface
77  * is marshaled or unmarshaled, registering the system default security. No
78  * default security packages are registered until then.
79  *
80  * This function may be called exactly once per process.
81  */
InitializeComSecurity()82 bool InitializeComSecurity()
83 {
84   HRESULT hr;
85   bool retval = false;
86 
87   P(com_security_mutex);
88   if (com_security_initialized) {
89     retval = true;
90     goto bail_out;
91   }
92 
93   hr = CoInitializeSecurity(
94       NULL, /*  Allow *all* VSS writers to communicate back! */
95       -1,   /*  Default COM authentication service */
96       NULL, /*  Default COM authorization service */
97       NULL, /*  reserved parameter */
98       RPC_C_AUTHN_LEVEL_PKT_PRIVACY, /*  Strongest COM authentication level */
99       RPC_C_IMP_LEVEL_IDENTIFY,      /*  Minimal impersonation abilities */
100       NULL,                          /*  Default COM authentication settings */
101       EOAC_NONE,                     /*  No special options */
102       NULL);                         /*  Reserved parameter */
103 
104   if (FAILED(hr)) {
105     Dmsg1(0, "InitializeComSecurity: CoInitializeSecurity returned 0x%08X\n",
106           hr);
107     errno = b_errno_win32;
108     goto bail_out;
109   }
110 
111   com_security_initialized = true;
112   retval = true;
113 
114 bail_out:
115   V(com_security_mutex);
116   return retval;
117 }
118 
119 /**
120  * UTF-8 to UCS2 path conversion is expensive,
121  * so we cache the conversion. During backup the
122  * conversion is called 3 times (lstat, attribs, open),
123  * by using the cache this is reduced to 1 time
124  */
125 struct thread_conversion_cache {
126   POOLMEM* pWin32ConvUTF8Cache;
127   POOLMEM* pWin32ConvUCS2Cache;
128   DWORD dwWin32ConvUTF8strlen;
129 };
130 
131 struct thread_vss_path_convert {
132   t_pVSSPathConvert pPathConvert;
133   t_pVSSPathConvertW pPathConvertW;
134 };
135 
136 /**
137  * We use thread specific data using pthread_set_specific and
138  * pthread_get_specific to have unique and separate data for each thread instead
139  * of global variables.
140  */
141 static pthread_mutex_t tsd_mutex = PTHREAD_MUTEX_INITIALIZER;
142 static bool pc_tsd_initialized = false;
143 static bool cc_tsd_initialized = false;
144 static pthread_key_t path_conversion_key;
145 static pthread_key_t conversion_cache_key;
146 
VSSPathConvertCleanup(void * arg)147 static void VSSPathConvertCleanup(void* arg)
148 {
149   thread_vss_path_convert* tvpc = (thread_vss_path_convert*)arg;
150 
151   Dmsg1(debuglevel,
152         "VSSPathConvertCleanup: Cleanup thread specific conversion pointers at "
153         "address %p\n",
154         tvpc);
155 
156   free(tvpc);
157 }
158 
SetVSSPathConvert(t_pVSSPathConvert pPathConvert,t_pVSSPathConvertW pPathConvertW)159 bool SetVSSPathConvert(t_pVSSPathConvert pPathConvert,
160                        t_pVSSPathConvertW pPathConvertW)
161 {
162   int status;
163   thread_vss_path_convert* tvpc = NULL;
164 
165   P(tsd_mutex);
166   if (!pc_tsd_initialized) {
167     status = pthread_key_create(&path_conversion_key, VSSPathConvertCleanup);
168     if (status != 0) {
169       V(tsd_mutex);
170       goto bail_out;
171     }
172     pc_tsd_initialized = true;
173   }
174   V(tsd_mutex);
175 
176   tvpc = (thread_vss_path_convert*)pthread_getspecific(path_conversion_key);
177   if (!tvpc) {
178     tvpc = (thread_vss_path_convert*)malloc(sizeof(thread_vss_path_convert));
179     status = pthread_setspecific(path_conversion_key, (void*)tvpc);
180     if (status != 0) { goto bail_out; }
181   }
182 
183   Dmsg1(debuglevel,
184         "SetVSSPathConvert: Setup thread specific conversion pointers at "
185         "address %p\n",
186         tvpc);
187 
188   tvpc->pPathConvert = pPathConvert;
189   tvpc->pPathConvertW = pPathConvertW;
190 
191   return true;
192 
193 bail_out:
194   if (tvpc) { free(tvpc); }
195 
196   return false;
197 }
198 
Win32GetPathConvert()199 static thread_vss_path_convert* Win32GetPathConvert()
200 {
201   thread_vss_path_convert* tvpc = NULL;
202 
203   P(tsd_mutex);
204   if (pc_tsd_initialized) {
205     tvpc = (thread_vss_path_convert*)pthread_getspecific(path_conversion_key);
206   }
207   V(tsd_mutex);
208 
209   return tvpc;
210 }
211 
212 /**
213  * UTF-8 to UCS2 path conversion caching.
214  */
Win32ConvCleanupCache(void * arg)215 static void Win32ConvCleanupCache(void* arg)
216 {
217   thread_conversion_cache* tcc = (thread_conversion_cache*)arg;
218 
219   Dmsg1(
220       debuglevel,
221       "Win32ConvCleanupCache: Cleanup of thread specific cache at address %p\n",
222       tcc);
223 
224   FreePoolMemory(tcc->pWin32ConvUCS2Cache);
225   FreePoolMemory(tcc->pWin32ConvUTF8Cache);
226   free(tcc);
227 }
228 
Win32ConvInitCache()229 static thread_conversion_cache* Win32ConvInitCache()
230 {
231   int status;
232   thread_conversion_cache* tcc = NULL;
233 
234   P(tsd_mutex);
235   if (!cc_tsd_initialized) {
236     status = pthread_key_create(&conversion_cache_key, Win32ConvCleanupCache);
237     if (status != 0) {
238       V(tsd_mutex);
239       goto bail_out;
240     }
241     cc_tsd_initialized = true;
242   }
243   V(tsd_mutex);
244 
245   /*
246    * Create a new cache.
247    */
248   tcc = (thread_conversion_cache*)malloc(sizeof(thread_conversion_cache));
249   tcc->pWin32ConvUTF8Cache = GetPoolMemory(PM_FNAME);
250   tcc->pWin32ConvUCS2Cache = GetPoolMemory(PM_FNAME);
251   tcc->dwWin32ConvUTF8strlen = 0;
252 
253   status = pthread_setspecific(conversion_cache_key, (void*)tcc);
254   if (status != 0) { goto bail_out; }
255 
256   Dmsg1(debuglevel,
257         "Win32ConvInitCache: Setup of thread specific cache at address %p\n",
258         tcc);
259 
260   return tcc;
261 
262 bail_out:
263   if (tcc) {
264     FreePoolMemory(tcc->pWin32ConvUCS2Cache);
265     FreePoolMemory(tcc->pWin32ConvUTF8Cache);
266     free(tcc);
267   }
268 
269   return NULL;
270 }
271 
Win32GetCache()272 static thread_conversion_cache* Win32GetCache()
273 {
274   thread_conversion_cache* tcc = NULL;
275 
276   P(tsd_mutex);
277   if (cc_tsd_initialized) {
278     tcc = (thread_conversion_cache*)pthread_getspecific(conversion_cache_key);
279   }
280   V(tsd_mutex);
281 
282   return tcc;
283 }
284 
Win32TSDCleanup()285 void Win32TSDCleanup()
286 {
287   P(tsd_mutex);
288   if (pc_tsd_initialized) {
289     pthread_key_delete(path_conversion_key);
290     pc_tsd_initialized = false;
291   }
292 
293   if (cc_tsd_initialized) {
294     pthread_key_delete(conversion_cache_key);
295     cc_tsd_initialized = false;
296   }
297   V(tsd_mutex);
298 }
299 
300 /**
301  * Special flag used to enable or disable Bacula compatible win32 encoding.
302  */
303 static bool win32_bacula_compatible = true;
304 
Win32ClearCompatible()305 void Win32ClearCompatible() { win32_bacula_compatible = false; }
306 
Win32SetCompatible()307 void Win32SetCompatible() { win32_bacula_compatible = true; }
308 
Win32IsCompatible()309 bool Win32IsCompatible() { return win32_bacula_compatible; }
310 
311 /**
312  * Forward referenced functions
313  */
314 const char* errorString(void);
315 
316 /**
317  * To allow the usage of the original version in this file here
318  */
319 #undef fputs
320 
321 #define USE_WIN32_32KPATHCONVERSION 1
322 
323 extern DWORD g_platform_id;
324 extern DWORD g_MinorVersion;
325 
326 /**
327  * From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970
328  */
329 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
330 
331 #define WIN32_FILETIME_SCALE 10000000  // 100ns/second
332 
333 /**
334  * Convert from UTF-8 to VSS Windows path/file
335  * Used by compatibility layer for Unix system calls
336  */
conv_unix_to_vss_win32_path(const char * name,char * win32_name,DWORD dwSize)337 static inline void conv_unix_to_vss_win32_path(const char* name,
338                                                char* win32_name,
339                                                DWORD dwSize)
340 {
341   const char* fname = name;
342   char* tname = win32_name;
343   thread_vss_path_convert* tvpc;
344 
345   Dmsg0(debuglevel, "Enter convert_unix_to_win32_path\n");
346 
347   tvpc = Win32GetPathConvert();
348   if (IsPathSeparator(name[0]) && IsPathSeparator(name[1]) && name[2] == '.' &&
349       IsPathSeparator(name[3])) {
350     *win32_name++ = '\\';
351     *win32_name++ = '\\';
352     *win32_name++ = '.';
353     *win32_name++ = '\\';
354 
355     name += 4;
356   } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS && !tvpc) {
357     /*
358      * Allow path to be 32767 bytes
359      */
360     *win32_name++ = '\\';
361     *win32_name++ = '\\';
362     *win32_name++ = '?';
363     *win32_name++ = '\\';
364   }
365 
366   while (*name) {
367     /**
368      * Check for Unix separator and convert to Win32
369      */
370     if (name[0] == '/' && name[1] == '/') { /* double slash? */
371       name++;                               /* yes, skip first one */
372     }
373     if (*name == '/') {
374       *win32_name++ = '\\'; /* convert char */
375       /*
376        * If Win32 separator that is "quoted", remove quote
377        */
378     } else if (*name == '\\' && name[1] == '\\') {
379       *win32_name++ = '\\';
380       name++; /* skip first \ */
381     } else {
382       *win32_name++ = *name; /* copy character */
383     }
384     name++;
385   }
386 
387   /**
388    * Strip any trailing slash, if we stored something
389    * but leave "c:\" with backslash (root directory case)
390    */
391   if (*fname != 0 && win32_name[-1] == '\\' && strlen(fname) != 3) {
392     win32_name[-1] = 0;
393   } else {
394     *win32_name = 0;
395   }
396 
397   /**
398    * Here we convert to VSS specific file name which
399    * can get longer because VSS will make something like
400    * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bareos\\uninstall.exe
401    * from c:\bareos\uninstall.exe
402    */
403   Dmsg1(debuglevel, "path=%s\n", tname);
404   if (tvpc) {
405     POOLMEM* pszBuf = GetPoolMemory(PM_FNAME);
406 
407     pszBuf = CheckPoolMemorySize(pszBuf, dwSize);
408     bstrncpy(pszBuf, tname, strlen(tname) + 1);
409     tvpc->pPathConvert(pszBuf, tname, dwSize);
410     FreePoolMemory(pszBuf);
411   }
412 
413   Dmsg1(debuglevel, "Leave cvt_u_to_win32_path path=%s\n", tname);
414 }
415 
416 /**
417  * Conversion of a Unix filename to a Win32 filename
418  */
unix_name_to_win32(POOLMEM * & win32_name,const char * name)419 void unix_name_to_win32(POOLMEM*& win32_name, const char* name)
420 {
421   DWORD dwSize;
422 
423   /*
424    * One extra byte should suffice, but we double it
425    * add MAX_PATH bytes for VSS shadow copy name.
426    */
427   dwSize = 2 * strlen(name) + MAX_PATH;
428   win32_name = CheckPoolMemorySize(win32_name, dwSize);
429   conv_unix_to_vss_win32_path(name, win32_name, dwSize);
430 }
431 
432 /**
433  * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
434  * will complete the input path to an absolue path of the form \\?\c:\path\file
435  *
436  * With this trick, it is possible to have 32K characters long paths.
437  *
438  * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
439  * to a raw windows partition.
440  *
441  * Created 02/27/2006 Thorsten Engel
442  */
make_wchar_win32_path(POOLMEM * pszUCSPath,BOOL * pBIsRawPath)443 static inline POOLMEM* make_wchar_win32_path(POOLMEM* pszUCSPath,
444                                              BOOL* pBIsRawPath /*= NULL*/)
445 {
446   Dmsg0(debuglevel, "Enter make_wchar_win32_path\n");
447   if (pBIsRawPath) { *pBIsRawPath = FALSE; /* Initialize, set later */ }
448 
449   if (!p_GetCurrentDirectoryW) {
450     Dmsg0(debuglevel, "Leave make_wchar_win32_path no change \n");
451     return pszUCSPath;
452   }
453 
454   wchar_t* name = (wchar_t*)pszUCSPath;
455 
456   /*
457    * If it has already the desired form, exit without changes
458    */
459   if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
460     Dmsg0(debuglevel, "Leave make_wchar_win32_path no change \n");
461     return pszUCSPath;
462   }
463 
464   wchar_t* pwszBuf = (wchar_t*)GetPoolMemory(PM_FNAME);
465   wchar_t* pwszCurDirBuf = (wchar_t*)GetPoolMemory(PM_FNAME);
466   DWORD dwCurDirPathSize = 0;
467 
468   /*
469    * Get buffer with enough size (name+max 6. wchars+1 null terminator
470    */
471   DWORD dwBufCharsNeeded = (wcslen(name) + 7);
472   pwszBuf = (wchar_t*)CheckPoolMemorySize((POOLMEM*)pwszBuf,
473                                           dwBufCharsNeeded * sizeof(wchar_t));
474 
475   /*
476    * Add \\?\ to support 32K long filepaths
477    * it is important to make absolute paths, so we add drive and
478    * current path if necessary
479    */
480   BOOL bAddDrive = TRUE;
481   BOOL bAddCurrentPath = TRUE;
482   BOOL bAddPrefix = TRUE;
483 
484   /*
485    * Does path begin with drive? if yes, it is absolute
486    */
487   if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
488     bAddDrive = FALSE;
489     bAddCurrentPath = FALSE;
490   }
491 
492   /*
493    * Is path absolute?
494    */
495   if (IsPathSeparator(name[0])) bAddCurrentPath = FALSE;
496 
497   /*
498    * Is path relative to itself?, if yes, skip ./
499    */
500   if (name[0] == '.' && IsPathSeparator(name[1])) { name += 2; }
501 
502   /*
503    * Is path of form '//./'?
504    */
505   if (IsPathSeparator(name[0]) && IsPathSeparator(name[1]) && name[2] == '.' &&
506       IsPathSeparator(name[3])) {
507     bAddDrive = FALSE;
508     bAddCurrentPath = FALSE;
509     bAddPrefix = FALSE;
510     if (pBIsRawPath) { *pBIsRawPath = TRUE; }
511   }
512 
513   int nParseOffset = 0;
514 
515   /*
516    * Add 4 bytes header
517    */
518   if (bAddPrefix) {
519     nParseOffset = 4;
520     wcscpy(pwszBuf, L"\\\\?\\");
521   }
522 
523   /*
524    * Get current path if needed
525    */
526   if (bAddDrive || bAddCurrentPath) {
527     dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
528     if (dwCurDirPathSize > 0) {
529       /*
530        * Get directory into own buffer as it may either return c:\... or
531        * \\?\C:\....
532        */
533       pwszCurDirBuf = (wchar_t*)CheckPoolMemorySize(
534           (POOLMEM*)pwszCurDirBuf, (dwCurDirPathSize + 1) * sizeof(wchar_t));
535       p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
536     } else {
537       /*
538        * We have no info for doing so
539        */
540       bAddDrive = FALSE;
541       bAddCurrentPath = FALSE;
542     }
543   }
544 
545   /*
546    * Add drive if needed
547    */
548   if (bAddDrive && !bAddCurrentPath) {
549     wchar_t szDrive[3];
550 
551     if (IsPathSeparator(pwszCurDirBuf[0]) &&
552         IsPathSeparator(pwszCurDirBuf[1]) && pwszCurDirBuf[2] == '?' &&
553         IsPathSeparator(pwszCurDirBuf[3])) {
554       /*
555        * Copy drive character
556        */
557       szDrive[0] = pwszCurDirBuf[4];
558     } else {
559       /*
560        * Copy drive character
561        */
562       szDrive[0] = pwszCurDirBuf[0];
563     }
564 
565     szDrive[1] = ':';
566     szDrive[2] = 0;
567 
568     wcscat(pwszBuf, szDrive);
569     nParseOffset += 2;
570   }
571 
572   /*
573    * Add path if needed
574    */
575   if (bAddCurrentPath) {
576     /*
577      * The 1 additional character is for the eventually added backslash
578      */
579     dwBufCharsNeeded += dwCurDirPathSize + 1;
580     pwszBuf = (wchar_t*)CheckPoolMemorySize((POOLMEM*)pwszBuf,
581                                             dwBufCharsNeeded * sizeof(wchar_t));
582 
583     /*
584      * Get directory into own buffer as it may either return c:\... or
585      * \\?\C:\....
586      */
587     if (IsPathSeparator(pwszCurDirBuf[0]) &&
588         IsPathSeparator(pwszCurDirBuf[1]) && pwszCurDirBuf[2] == '?' &&
589         IsPathSeparator(pwszCurDirBuf[3])) {
590       /*
591        * Copy complete string
592        */
593       wcscpy(pwszBuf, pwszCurDirBuf);
594     } else {
595       /*
596        * Append path
597        */
598       wcscat(pwszBuf, pwszCurDirBuf);
599     }
600 
601     nParseOffset = wcslen((LPCWSTR)pwszBuf);
602 
603     /*
604      * Check if path ends with backslash, if not, add one
605      */
606     if (!IsPathSeparator(pwszBuf[nParseOffset - 1])) {
607       wcscat(pwszBuf, L"\\");
608       nParseOffset++;
609     }
610   }
611 
612   wchar_t* win32_name = &pwszBuf[nParseOffset];
613   wchar_t* name_start = name;
614   wchar_t previous_char = 0;
615   wchar_t next_char = 0;
616 
617   while (*name) {
618     /*
619      * Check for Unix separator and convert to Win32, eliminating duplicate
620      * separators.
621      */
622     if (IsPathSeparator(*name)) {
623       /*
624        * Don't add a trailing slash.
625        */
626       next_char = *(name + 1);
627       if (previous_char != ':' && next_char == '\0') {
628         name++;
629         continue;
630       }
631       previous_char = '\\';
632       *win32_name++ = '\\'; /* convert char */
633 
634       /*
635        * Eliminate consecutive slashes, but not at the start so that \\.\ still
636        * works.
637        */
638       if (name_start != name && IsPathSeparator(name[1])) { name++; }
639     } else {
640       previous_char = *name;
641       *win32_name++ = *name; /* copy character */
642     }
643     name++;
644   }
645 
646   /*
647    * NULL Terminate string
648    */
649   *win32_name = 0;
650 
651   /*
652    * Here we convert to VSS specific file name which can get longer because VSS
653    * will make something like
654    * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bareos\\uninstall.exe
655    * from c:\bareos\uninstall.exe
656    */
657   thread_vss_path_convert* tvpc = Win32GetPathConvert();
658   if (tvpc) {
659     /*
660      * Is output buffer large enough?
661      */
662     pwszBuf = (wchar_t*)CheckPoolMemorySize(
663         (POOLMEM*)pwszBuf, (dwBufCharsNeeded + MAX_PATH) * sizeof(wchar_t));
664     /*
665      * Create temp. buffer
666      */
667     wchar_t* pszBuf = (wchar_t*)GetPoolMemory(PM_FNAME);
668     pszBuf = (wchar_t*)CheckPoolMemorySize(
669         (POOLMEM*)pszBuf, (dwBufCharsNeeded + MAX_PATH) * sizeof(wchar_t));
670     if (bAddPrefix) {
671       nParseOffset = 4;
672     } else {
673       nParseOffset = 0;
674     }
675 
676     wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf) + 1 - nParseOffset);
677     tvpc->pPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded + MAX_PATH);
678     FreePoolMemory((POOLMEM*)pszBuf);
679   }
680 
681   FreePoolMemory(pszUCSPath);
682   FreePoolMemory((POOLMEM*)pwszCurDirBuf);
683 
684   Dmsg1(debuglevel, "Leave make_wchar_win32_path=%s\n", pwszBuf);
685   return (POOLMEM*)pwszBuf;
686 }
687 
688 /**
689  * Convert from WCHAR (UCS) to UTF-8
690  */
wchar_2_UTF8(POOLMEM * & pszUTF,const WCHAR * pszUCS)691 int wchar_2_UTF8(POOLMEM*& pszUTF, const WCHAR* pszUCS)
692 {
693   /**
694    * The return value is the number of bytes written to the buffer.
695    * The number includes the byte for the null terminator.
696    */
697   if (p_WideCharToMultiByte) {
698     int nRet =
699         p_WideCharToMultiByte(CP_UTF8, 0, pszUCS, -1, NULL, 0, NULL, NULL);
700     pszUTF = CheckPoolMemorySize(pszUTF, nRet);
701     return p_WideCharToMultiByte(CP_UTF8, 0, pszUCS, -1, pszUTF, nRet, NULL,
702                                  NULL);
703   } else {
704     return 0;
705   }
706 }
707 
708 /**
709  * Convert from WCHAR (UCS) to UTF-8
710  */
wchar_2_UTF8(char * pszUTF,const WCHAR * pszUCS,int cchChar)711 int wchar_2_UTF8(char* pszUTF, const WCHAR* pszUCS, int cchChar)
712 {
713   /**
714    * The return value is the number of bytes written to the buffer.
715    * The number includes the byte for the null terminator.
716    */
717   if (p_WideCharToMultiByte) {
718     int nRet = p_WideCharToMultiByte(CP_UTF8, 0, pszUCS, -1, pszUTF, cchChar,
719                                      NULL, NULL);
720     ASSERT(nRet > 0);
721     return nRet;
722   } else {
723     return 0;
724   }
725 }
726 
UTF8_2_wchar(POOLMEM * & ppszUCS,const char * pszUTF)727 int UTF8_2_wchar(POOLMEM*& ppszUCS, const char* pszUTF)
728 {
729   /*
730    * The return value is the number of wide characters written to the buffer.
731    * convert null terminated string from utf-8 to ucs2, enlarge buffer if
732    * necessary
733    */
734   if (p_MultiByteToWideChar) {
735     /*
736      * strlen of UTF8 +1 is enough
737      */
738     DWORD cchSize = (strlen(pszUTF) + 1);
739     ppszUCS = CheckPoolMemorySize(ppszUCS, cchSize * sizeof(wchar_t));
740 
741     int nRet =
742         p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR)ppszUCS, cchSize);
743     ASSERT(nRet > 0);
744     return nRet;
745   } else {
746     return 0;
747   }
748 }
749 
750 /**
751  * Convert a C character string into an BSTR.
752  */
str_2_BSTR(const char * pSrc)753 BSTR str_2_BSTR(const char* pSrc)
754 {
755   DWORD cwch;
756   BSTR wsOut(NULL);
757 
758   if (!pSrc) { return NULL; }
759 
760   if (p_MultiByteToWideChar) {
761     if ((cwch = p_MultiByteToWideChar(CP_ACP, 0, pSrc, -1, NULL, 0))) {
762       cwch--;
763       wsOut = SysAllocStringLen(NULL, cwch);
764       if (wsOut) {
765         if (!p_MultiByteToWideChar(CP_ACP, 0, pSrc, -1, wsOut, cwch)) {
766           if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { return wsOut; }
767           SysFreeString(wsOut);
768           wsOut = NULL;
769         }
770       }
771     }
772   }
773 
774   return wsOut;
775 }
776 
777 /**
778  * Convert a BSTR into an C character string.
779  */
BSTR_2_str(const BSTR pSrc)780 char* BSTR_2_str(const BSTR pSrc)
781 {
782   DWORD cb, cwch;
783   char* szOut = NULL;
784 
785   if (!pSrc) { return NULL; }
786 
787   if (p_WideCharToMultiByte) {
788     cwch = SysStringLen(pSrc);
789     if ((cb =
790              p_WideCharToMultiByte(CP_ACP, 0, pSrc, cwch + 1, NULL, 0, 0, 0))) {
791       szOut = (char*)malloc(cb);
792       szOut[cb - 1] = '\0';
793       if (!p_WideCharToMultiByte(CP_ACP, 0, pSrc, cwch + 1, szOut, cb, 0, 0)) {
794         free(szOut);
795         szOut = NULL;
796       }
797     }
798   }
799 
800   return szOut;
801 }
802 
make_win32_path_UTF8_2_wchar(POOLMEM * & pszUCS,const char * pszUTF,BOOL * pBIsRawPath)803 int make_win32_path_UTF8_2_wchar(POOLMEM*& pszUCS,
804                                  const char* pszUTF,
805                                  BOOL* pBIsRawPath /*= NULL*/)
806 {
807   int nRet;
808   thread_conversion_cache* tcc;
809 
810   /*
811    * If we find the utf8 string in cache, we use the cached ucs2 version.
812    * we compare the stringlength first (quick check) and then compare the
813    * content.
814    */
815   tcc = Win32GetCache();
816   if (!tcc) {
817     tcc = Win32ConvInitCache();
818   } else if (tcc->dwWin32ConvUTF8strlen == strlen(pszUTF)) {
819     if (bstrcmp(pszUTF, tcc->pWin32ConvUTF8Cache)) {
820       int nBufSize;
821 
822       /*
823        * Return cached value
824        */
825       nBufSize = SizeofPoolMemory(tcc->pWin32ConvUCS2Cache);
826       pszUCS = CheckPoolMemorySize(pszUCS, nBufSize);
827       wcscpy((LPWSTR)pszUCS, (LPWSTR)tcc->pWin32ConvUCS2Cache);
828 
829       return nBufSize / sizeof(WCHAR);
830     }
831   }
832 
833   /*
834    * Helper to convert from utf-8 to UCS-2 and to complete a path for 32K path
835    * syntax
836    */
837   nRet = UTF8_2_wchar(pszUCS, pszUTF);
838 
839 #ifdef USE_WIN32_32KPATHCONVERSION
840   /*
841    * Add \\?\ to support 32K long filepaths
842    */
843   pszUCS = make_wchar_win32_path(pszUCS, pBIsRawPath);
844 #else
845   if (pBIsRawPath) { *pBIsRawPath = FALSE; }
846 #endif
847 
848   /*
849    * Populate cache
850    */
851   if (tcc) {
852     tcc->pWin32ConvUCS2Cache =
853         CheckPoolMemorySize(tcc->pWin32ConvUCS2Cache, SizeofPoolMemory(pszUCS));
854     wcscpy((LPWSTR)tcc->pWin32ConvUCS2Cache, (LPWSTR)pszUCS);
855 
856     tcc->dwWin32ConvUTF8strlen = strlen(pszUTF);
857     tcc->pWin32ConvUTF8Cache = CheckPoolMemorySize(
858         tcc->pWin32ConvUTF8Cache, tcc->dwWin32ConvUTF8strlen + 2);
859     bstrncpy(tcc->pWin32ConvUTF8Cache, pszUTF, tcc->dwWin32ConvUTF8strlen + 1);
860   }
861 
862   return nRet;
863 }
864 
865 #if !defined(_MSC_VER) || (_MSC_VER < 1400)  // VC8+
umask(int)866 int umask(int) { return 0; }
867 #endif
868 
869 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
870 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
871 #endif
872 
dlopen(const char * filename,int mode)873 void* dlopen(const char* filename, int mode)
874 {
875   void* handle;
876   DWORD dwFlags = 0;
877 
878   dwFlags |= LOAD_WITH_ALTERED_SEARCH_PATH;
879   if (mode & RTLD_NOLOAD) { dwFlags |= LOAD_LIBRARY_AS_DATAFILE; }
880 
881   handle = LoadLibraryEx(filename, NULL, dwFlags);
882 
883   return handle;
884 }
885 
dlsym(void * handle,const char * name)886 void* dlsym(void* handle, const char* name)
887 {
888   void* symaddr;
889 
890   symaddr = (void*)GetProcAddress((HMODULE)handle, name);
891 
892   return symaddr;
893 }
894 
dlclose(void * handle)895 int dlclose(void* handle)
896 {
897   if (handle && !FreeLibrary((HMODULE)handle)) {
898     errno = b_errno_win32;
899     return 1; /* failed */
900   }
901   return 0; /* OK */
902 }
903 
dlerror(void)904 char* dlerror(void)
905 {
906   static char buf[200];
907   const char* err = errorString();
908 
909   bstrncpy(buf, (char*)err, sizeof(buf));
910   LocalFree((void*)err);
911 
912   return buf;
913 }
914 
fcntl(int fd,int cmd)915 int fcntl(int fd, int cmd) { return 0; }
916 
chown(const char * k,uid_t,gid_t)917 int chown(const char* k, uid_t, gid_t) { return 0; }
918 
lchown(const char * k,uid_t,gid_t)919 int lchown(const char* k, uid_t, gid_t) { return 0; }
920 
random(void)921 long int random(void) { return rand(); }
922 
srandom(unsigned int seed)923 void srandom(unsigned int seed) { srand(seed); }
924 
CvtFtimeToUtime(const FILETIME & time)925 static time_t CvtFtimeToUtime(const FILETIME& time)
926 {
927   uint64_t mstime;
928 
929   mstime = time.dwHighDateTime;
930   mstime <<= 32;
931   mstime |= time.dwLowDateTime;
932   mstime -= WIN32_FILETIME_ADJUST;
933   mstime /= WIN32_FILETIME_SCALE; /* convert to seconds. */
934 
935   return (time_t)(mstime & 0xffffffff);
936 }
937 
CvtFtimeToUtime(const LARGE_INTEGER & time)938 static time_t CvtFtimeToUtime(const LARGE_INTEGER& time)
939 {
940   uint64_t mstime;
941 
942   mstime = time.HighPart;
943   mstime <<= 32;
944   mstime |= time.LowPart;
945   mstime -= WIN32_FILETIME_ADJUST;
946   mstime /= WIN32_FILETIME_SCALE; /* convert to seconds. */
947 
948   return (time_t)(mstime & 0xffffffff);
949 }
950 
CreateJunction(const char * szJunction,const char * szPath)951 bool CreateJunction(const char* szJunction, const char* szPath)
952 {
953   DWORD dwRet;
954   HANDLE hDir;
955   bool retval = false;
956   int buf_length, data_length;
957   int SubstituteNameLength, PrintNameLength; /* length in bytes */
958   REPARSE_DATA_BUFFER* rdb;
959   PoolMem szDestDir(PM_FNAME);
960   POOLMEM *buf, *szJunctionW, *szPrintName, *szSubstituteName;
961 
962   /*
963    * We only implement a Wide string version of CreateJunction.
964    * So if p_CreateDirectoryW is not available we refuse to do anything.
965    */
966   if (!p_CreateDirectoryW) {
967     Dmsg0(debuglevel,
968           "CreateJunction: CreateDirectoryW not found, doing nothing\n");
969     return false;
970   }
971 
972   /*
973    * Create three buffers to hold the wide char strings.
974    */
975   szJunctionW = GetPoolMemory(PM_FNAME);
976 
977   /* With \\?\\ */
978   szSubstituteName = GetPoolMemory(PM_FNAME);
979 
980   /* Simple path */
981   szPrintName = GetPoolMemory(PM_FNAME);
982 
983   /*
984    * Create a buffer big enough to hold all data.
985    */
986   buf_length = sizeof(REPARSE_DATA_BUFFER) + 2 * MAX_PATH * sizeof(WCHAR);
987   buf = GetPoolMemory(PM_NAME);
988   buf = CheckPoolMemorySize(buf, buf_length);
989   rdb = (REPARSE_DATA_BUFFER*)buf;
990 
991   /*
992    * Create directory
993    */
994   if (!UTF8_2_wchar(szJunctionW, szJunction)) { goto bail_out; }
995 
996   if (!p_CreateDirectoryW((LPCWSTR)szJunctionW, NULL)) {
997     Dmsg1(debuglevel, "CreateDirectory Failed:%s\n", errorString());
998     goto bail_out;
999   }
1000 
1001   /*
1002    * Create file/open reparse point
1003    */
1004   hDir = p_CreateFileW(
1005       (LPCWSTR)szJunctionW, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
1006       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1007 
1008   if (hDir == INVALID_HANDLE_VALUE) {
1009     Dmsg0(debuglevel, "INVALID_FILE_HANDLE");
1010     RemoveDirectoryW((LPCWSTR)szJunctionW);
1011     goto bail_out;
1012   }
1013 
1014   if (!UTF8_2_wchar(szPrintName, szPath)) { goto bail_out; }
1015 
1016   /*
1017    * Add  \??\ prefix.
1018    */
1019   Mmsg(szDestDir, "\\??\\%s", szPath);
1020   if (!UTF8_2_wchar(szSubstituteName, szDestDir.c_str())) { goto bail_out; }
1021 
1022   /*
1023    * Put data junction target into reparse buffer
1024    */
1025   memset(buf, 0, buf_length);
1026 
1027 #define MOUNTPOINTREPARSEBUFFER_FIXED_HEADER_SIZE 8
1028 
1029   SubstituteNameLength = wcslen((LPWSTR)szSubstituteName) * sizeof(WCHAR);
1030   PrintNameLength = wcslen((LPWSTR)szPrintName) * sizeof(WCHAR);
1031 
1032   wcsncpy((LPWSTR)rdb->MountPointReparseBuffer.PathBuffer,
1033           (LPWSTR)szSubstituteName, SubstituteNameLength);
1034 
1035   wcsncpy((LPWSTR)rdb->MountPointReparseBuffer.PathBuffer +
1036               wcslen((LPWSTR)szSubstituteName) + 1,
1037           (LPWSTR)szPrintName, PrintNameLength);
1038 
1039   rdb->MountPointReparseBuffer.SubstituteNameLength = SubstituteNameLength;
1040   rdb->MountPointReparseBuffer.PrintNameOffset = SubstituteNameLength + 2;
1041   rdb->MountPointReparseBuffer.PrintNameLength = PrintNameLength;
1042   rdb->ReparseDataLength = SubstituteNameLength + PrintNameLength + 12;
1043   data_length =
1044       rdb->ReparseDataLength + MOUNTPOINTREPARSEBUFFER_FIXED_HEADER_SIZE;
1045   rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1046 
1047   /*
1048    * Write reparse point
1049    * For debugging use "fsutil reparsepoint query"
1050    */
1051   if (!DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, (LPVOID)buf, data_length,
1052                        NULL, 0, &dwRet, 0)) {
1053     Dmsg1(debuglevel, "DeviceIoControl Failed:%s\n", errorString());
1054     CloseHandle(hDir);
1055     RemoveDirectoryW((LPCWSTR)szJunctionW);
1056     goto bail_out;
1057   }
1058 
1059   CloseHandle(hDir);
1060 
1061   retval = true;
1062 
1063 bail_out:
1064   FreePoolMemory(buf);
1065   FreePoolMemory(szJunctionW);
1066   FreePoolMemory(szPrintName);
1067   FreePoolMemory(szSubstituteName);
1068 
1069   return retval;
1070 }
1071 
errorString(void)1072 const char* errorString(void)
1073 {
1074   LPVOID lpMsgBuf;
1075 
1076   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
1077                     FORMAT_MESSAGE_IGNORE_INSERTS,
1078                 NULL, GetLastError(),
1079                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */
1080                 (LPTSTR)&lpMsgBuf, 0, NULL);
1081 
1082   /*
1083    * Strip any \r or \n
1084    */
1085   char* rval = (char*)lpMsgBuf;
1086   char* cp = strchr(rval, '\r');
1087   if (cp != NULL) {
1088     *cp = 0;
1089   } else {
1090     cp = strchr(rval, '\n');
1091     if (cp != NULL) *cp = 0;
1092   }
1093   return rval;
1094 }
1095 
1096 /**
1097  * Retrieve the unique devicename of a Volume MountPoint.
1098  */
GetVolumeMountPointData(const char * filename,POOLMEM * & devicename)1099 static inline bool GetVolumeMountPointData(const char* filename,
1100                                            POOLMEM*& devicename)
1101 {
1102   HANDLE h = INVALID_HANDLE_VALUE;
1103 
1104   /*
1105    * Now to find out if the directory is a mount point or a reparse point.
1106    * Explicitly open the file to read the reparse point, then call
1107    * DeviceIoControl to find out if it points to a Volume or to a directory.
1108    */
1109   if (p_GetFileAttributesW) {
1110     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
1111     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1112 
1113     if (p_CreateFileW) {
1114       h = CreateFileW(
1115           (LPCWSTR)pwszBuf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1116           FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
1117     }
1118 
1119     if (h == INVALID_HANDLE_VALUE) {
1120       Dmsg1(debuglevel, "Invalid handle from CreateFileW(%s)\n", pwszBuf);
1121       FreePoolMemory(pwszBuf);
1122       return false;
1123     }
1124 
1125     FreePoolMemory(pwszBuf);
1126   } else if (p_GetFileAttributesA) {
1127     POOLMEM* win32_fname;
1128 
1129     win32_fname = GetPoolMemory(PM_FNAME);
1130     unix_name_to_win32(win32_fname, filename);
1131     h = CreateFileA(
1132         win32_fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1133         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
1134 
1135     if (h == INVALID_HANDLE_VALUE) {
1136       Dmsg1(debuglevel, "Invalid handle from CreateFile(%s)\n", win32_fname);
1137       FreePoolMemory(win32_fname);
1138       return false;
1139     }
1140 
1141     FreePoolMemory(win32_fname);
1142   }
1143 
1144   if (h != INVALID_HANDLE_VALUE) {
1145     bool ok;
1146     POOLMEM* buf;
1147     int buf_length;
1148     REPARSE_DATA_BUFFER* rdb;
1149     DWORD bytes;
1150 
1151     /*
1152      * Create a buffer big enough to hold all data.
1153      */
1154     buf_length = sizeof(REPARSE_DATA_BUFFER) + MAX_PATH * sizeof(wchar_t);
1155     buf = GetPoolMemory(PM_NAME);
1156     buf = CheckPoolMemorySize(buf, buf_length);
1157     rdb = (REPARSE_DATA_BUFFER*)buf;
1158 
1159     memset(buf, 0, buf_length);
1160     rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1161     ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL,
1162                          0,                              /* in buffer, bytes */
1163                          (LPVOID)buf, (DWORD)buf_length, /* out buffer, btyes */
1164                          (LPDWORD)&bytes, (LPOVERLAPPED)0);
1165     if (ok) {
1166       wchar_2_UTF8(devicename,
1167                    (wchar_t*)rdb->MountPointReparseBuffer.PathBuffer);
1168     }
1169 
1170     CloseHandle(h);
1171     FreePoolMemory(buf);
1172   } else {
1173     return false;
1174   }
1175 
1176   return true;
1177 }
1178 
1179 /**
1180  * Retrieve the symlink target
1181  */
GetSymlinkData(const char * filename,POOLMEM * & symlinktarget)1182 static inline ssize_t GetSymlinkData(const char* filename,
1183                                      POOLMEM*& symlinktarget)
1184 {
1185   ssize_t nrconverted = -1;
1186   HANDLE h = INVALID_HANDLE_VALUE;
1187 
1188   if (p_GetFileAttributesW) {
1189     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
1190     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1191 
1192     if (p_CreateFileW) {
1193       h = CreateFileW(
1194           (LPCWSTR)pwszBuf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1195           FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
1196     }
1197 
1198     if (h == INVALID_HANDLE_VALUE) {
1199       Dmsg1(debuglevel, "Invalid handle from CreateFileW(%s)\n", pwszBuf);
1200       FreePoolMemory(pwszBuf);
1201       return -1;
1202     }
1203 
1204     FreePoolMemory(pwszBuf);
1205   } else if (p_GetFileAttributesA) {
1206     POOLMEM* win32_fname;
1207 
1208     win32_fname = GetPoolMemory(PM_FNAME);
1209     unix_name_to_win32(win32_fname, filename);
1210     h = CreateFileA(
1211         win32_fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1212         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
1213 
1214     if (h == INVALID_HANDLE_VALUE) {
1215       Dmsg1(debuglevel, "Invalid handle from CreateFile(%s)\n", win32_fname);
1216       FreePoolMemory(win32_fname);
1217       return -1;
1218     }
1219     FreePoolMemory(win32_fname);
1220   }
1221   if (h != INVALID_HANDLE_VALUE) {
1222     bool ok;
1223     POOLMEM* buf;
1224     int buf_length;
1225     REPARSE_DATA_BUFFER* rdb;
1226     DWORD bytes;
1227 
1228     /*
1229      * Create a buffer big enough to hold all data.
1230      */
1231     buf_length = sizeof(REPARSE_DATA_BUFFER) + MAX_PATH * sizeof(wchar_t);
1232     buf = GetPoolMemory(PM_NAME);
1233     buf = CheckPoolMemorySize(buf, buf_length);
1234     rdb = (REPARSE_DATA_BUFFER*)buf;
1235 
1236     memset(buf, 0, buf_length);
1237     rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
1238     ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL,
1239                          0,                              /* in buffer, bytes */
1240                          (LPVOID)buf, (DWORD)buf_length, /* out buffer, btyes */
1241                          (LPDWORD)&bytes, (LPOVERLAPPED)0);
1242     if (ok) {
1243       POOLMEM* path;
1244       int len, offset, ofs;
1245 
1246       len =
1247           rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
1248       offset =
1249           rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
1250 
1251       /*
1252        * null-Terminate pathbuffer
1253        */
1254       *(wchar_t*)(rdb->SymbolicLinkReparseBuffer.PathBuffer + offset + len) =
1255           L'\0';
1256 
1257       /*
1258        * convert to UTF-8
1259        */
1260       path = GetPoolMemory(PM_FNAME);
1261       nrconverted = wchar_2_UTF8(
1262           path, (wchar_t*)(rdb->SymbolicLinkReparseBuffer.PathBuffer + offset));
1263 
1264       ofs = 0;
1265       if (bstrncasecmp(path, "\\??\\", 4)) { /* skip \\??\\ if exists */
1266         ofs = 4;
1267         Dmsg0(debuglevel, "\\??\\ was in filename, skipping\n");
1268       }
1269 
1270       if (bstrncasecmp(path, "?\\",
1271                        2)) { /* skip ?\\ if exists, this is a MountPoint that we
1272                                 opened as Symlink */
1273         ofs = 2;
1274         Dmsg0(debuglevel,
1275               "?\\ was in filename, skipping, use of "
1276               "MountPointReparseDataBuffer is needed\n");
1277       }
1278 
1279       PmStrcpy(symlinktarget, path + ofs);
1280       FreePoolMemory(path);
1281     } else {
1282       Dmsg1(debuglevel, "DeviceIoControl failed:%s\n", errorString());
1283     }
1284 
1285     CloseHandle(h);
1286     FreePoolMemory(buf);
1287   } else {
1288     return -1;
1289   }
1290 
1291   return nrconverted;
1292 }
1293 
1294 /**
1295  * Encode file sttributes using the old Bacula method.
1296  */
EncodeWindowsFlagsCompatible(DWORD pdwFileAttributes,struct stat * sb)1297 static inline void EncodeWindowsFlagsCompatible(DWORD pdwFileAttributes,
1298                                                 struct stat* sb)
1299 {
1300   if (pdwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1301     sb->st_mode &= ~(S_IRUSR | S_IRGRP | S_IROTH); /* Clear all read bits */
1302   }
1303 
1304   if (pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1305     sb->st_mode &= ~S_IRWXO; /* Remove everything for other */
1306   }
1307 
1308   if (pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1309     sb->st_mode |= S_ISVTX; /* Use sticky bit -> hidden */
1310   }
1311 }
1312 
1313 /**
1314  * This is called for directories, and reparse points and is used to
1315  * get some special attributes like the type of reparse point etc..
1316  */
GetWindowsFileInfo(const char * filename,struct stat * sb,bool is_directory)1317 static int GetWindowsFileInfo(const char* filename,
1318                               struct stat* sb,
1319                               bool is_directory)
1320 {
1321   bool use_fallback_data = true;
1322   WIN32_FIND_DATAW info_w;  // window's file info
1323   WIN32_FIND_DATAA info_a;  // window's file info
1324 #if (_WIN32_WINNT >= 0x0600)
1325   FILE_BASIC_INFO binfo;  // window's basic file info
1326   HANDLE h = INVALID_HANDLE_VALUE;
1327 #endif
1328   HANDLE fh = INVALID_HANDLE_VALUE;
1329 
1330   /*
1331    * Cache some common vars to make code more transparent.
1332    */
1333   DWORD* pdwFileAttributes;
1334   DWORD* pnFileSizeHigh;
1335   DWORD* pnFileSizeLow;
1336   DWORD* pdwReserved0;
1337   FILETIME* pftLastAccessTime;
1338   FILETIME* pftLastWriteTime;
1339   FILETIME* pftCreationTime;
1340 
1341   /*
1342    * First get a findhandle and a file handle to the file.
1343    */
1344   if (p_FindFirstFileW) { /* use unicode */
1345     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
1346     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1347 
1348     Dmsg1(debuglevel, "FindFirstFileW=%s\n", pwszBuf);
1349     fh = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
1350 #if (_WIN32_WINNT >= 0x0600)
1351     if (fh != INVALID_HANDLE_VALUE) {
1352       h = p_CreateFileW(
1353           (LPCWSTR)pwszBuf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1354           FILE_FLAG_BACKUP_SEMANTICS, /* Required for directories */
1355           NULL);
1356     }
1357 #endif
1358 
1359     FreePoolMemory(pwszBuf);
1360   } else if (p_FindFirstFileA) {  // use ASCII
1361     POOLMEM* win32_fname;
1362 
1363     win32_fname = GetPoolMemory(PM_FNAME);
1364     unix_name_to_win32(win32_fname, filename);
1365 
1366     Dmsg1(debuglevel, "FindFirstFileA=%s\n", win32_fname);
1367     fh = p_FindFirstFileA(win32_fname, &info_a);
1368 #if (_WIN32_WINNT >= 0x0600)
1369     if (h != INVALID_HANDLE_VALUE) {
1370       h = CreateFileA(win32_fname, GENERIC_READ, FILE_SHARE_READ, NULL,
1371                       OPEN_EXISTING,
1372                       FILE_FLAG_BACKUP_SEMANTICS, /* Required for directories */
1373                       NULL);
1374     }
1375 #endif
1376 
1377     FreePoolMemory(win32_fname);
1378   } else {
1379     Dmsg0(debuglevel, "No findFirstFile A or W found\n");
1380   }
1381 
1382   /*
1383    * If we got a valid handle start processing the info.
1384    */
1385   if (fh != INVALID_HANDLE_VALUE) {
1386     if (p_FindFirstFileW) { /* use unicode */
1387       pdwFileAttributes = &info_w.dwFileAttributes;
1388       pdwReserved0 = &info_w.dwReserved0;
1389       pnFileSizeHigh = &info_w.nFileSizeHigh;
1390       pnFileSizeLow = &info_w.nFileSizeLow;
1391     } else {
1392       pdwFileAttributes = &info_a.dwFileAttributes;
1393       pdwReserved0 = &info_a.dwReserved0;
1394       pnFileSizeHigh = &info_a.nFileSizeHigh;
1395       pnFileSizeLow = &info_a.nFileSizeLow;
1396     }
1397 
1398 #if (_WIN32_WINNT >= 0x0600)
1399     /*
1400      * As this is retrieved by handle it has no specific A or W call.
1401      */
1402     if (h != INVALID_HANDLE_VALUE) {
1403       if (p_GetFileInformationByHandleEx) {
1404         if (p_GetFileInformationByHandleEx(h, FileBasicInfo, &binfo,
1405                                            sizeof(binfo))) {
1406           pftLastAccessTime = (FILETIME*)&binfo.LastAccessTime;
1407           pftLastWriteTime = (FILETIME*)&binfo.LastWriteTime;
1408           if (CvtFtimeToUtime(binfo.CreationTime) >
1409               CvtFtimeToUtime(binfo.ChangeTime)) {
1410             pftCreationTime = (FILETIME*)&binfo.CreationTime;
1411           } else {
1412             pftCreationTime = (FILETIME*)&binfo.ChangeTime;
1413           }
1414           use_fallback_data = false;
1415         }
1416       }
1417       CloseHandle(h);
1418     }
1419 #endif
1420 
1421     /*
1422      * See if we got anything from the GetFileInformationByHandleEx() call if
1423      * not fallback to the normal info data returned by FindFirstFileW() or
1424      * FindFirstFileA()
1425      */
1426     if (use_fallback_data) {
1427       if (p_FindFirstFileW) { /* use unicode */
1428         pftLastAccessTime = &info_w.ftLastAccessTime;
1429         pftLastWriteTime = &info_w.ftLastWriteTime;
1430         pftCreationTime = &info_w.ftCreationTime;
1431       } else {
1432         pftLastAccessTime = &info_a.ftLastAccessTime;
1433         pftLastWriteTime = &info_a.ftLastWriteTime;
1434         pftCreationTime = &info_a.ftCreationTime;
1435       }
1436     }
1437 
1438     FindClose(fh);
1439   } else {
1440     const char* err = errorString();
1441 
1442     /*
1443      * Note, in creating leading paths, it is normal that the file does not
1444      * exist.
1445      */
1446     Dmsg2(2099, "FindFirstFile(%s):%s\n", filename, err);
1447     LocalFree((void*)err);
1448     errno = b_errno_win32;
1449 
1450     return -1;
1451   }
1452 
1453   sb->st_mode = 0777;
1454 
1455   /*
1456    * See if we need to encode in the old Bacula compatible way.
1457    */
1458   if (win32_bacula_compatible) {
1459     EncodeWindowsFlagsCompatible(*pdwFileAttributes, sb);
1460   } else {
1461     /*
1462      * We store the full windows file attributes into st_rdev.
1463      */
1464     sb->st_rdev = *pdwFileAttributes;
1465   }
1466 
1467   if (is_directory) {
1468     /*
1469      * Directory
1470      */
1471     sb->st_mode |= S_IFDIR;
1472 
1473     /*
1474      * Store reparse/mount point info in st_rdev.  Note a Win32 reparse point
1475      * (junction point) is like a link though it can have many properties
1476      * (directory link, soft link, hard link, HSM, ...
1477      * The IO_REPARSE_TAG_* tag values describe what type of reparse point
1478      * it actually is.
1479      *
1480      * A mount point is a reparse point where another volume is mounted,
1481      * so it is like a Unix mount point (change of filesystem).
1482      */
1483     if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1484       switch (*pdwReserved0) {
1485         case IO_REPARSE_TAG_MOUNT_POINT: {
1486           /*
1487            * A mount point can be:
1488            * Volume Mount Point "\\??\\volume{..."
1489            * Junction Point     "\\??\\..."
1490            */
1491           POOLMEM* vmp = GetPoolMemory(PM_NAME);
1492           if (GetVolumeMountPointData(filename, vmp)) {
1493             if (bstrncasecmp(vmp, "\\??\\volume{", 11)) {
1494               Dmsg2(debuglevel, "Volume Mount Point %s points to: %s\n",
1495                     filename, vmp);
1496               sb->st_rdev |= FILE_ATTRIBUTE_VOLUME_MOUNT_POINT;
1497             } else {
1498               Dmsg2(debuglevel, "Junction Point %s points to: %s\n", filename,
1499                     vmp);
1500               sb->st_rdev |= FILE_ATTRIBUTES_JUNCTION_POINT;
1501               sb->st_mode |= S_IFLNK;
1502               sb->st_mode &= ~S_IFDIR;
1503             }
1504           }
1505           FreePoolMemory(vmp);
1506           break;
1507         }
1508         case IO_REPARSE_TAG_SYMLINK: {
1509           POOLMEM* slt;
1510 
1511           Dmsg0(debuglevel, "We have a symlinked directory!\n");
1512           sb->st_rdev |= FILE_ATTRIBUTES_SYMBOLIC_LINK;
1513           sb->st_mode |= S_IFLNK;
1514           sb->st_mode &= ~S_IFDIR;
1515 
1516           slt = GetPoolMemory(PM_NAME);
1517           slt = CheckPoolMemorySize(slt, MAX_PATH * sizeof(wchar_t));
1518 
1519           if (GetSymlinkData(filename, slt)) {
1520             Dmsg2(debuglevel, "Symlinked Directory %s points to: %s\n",
1521                   filename, slt);
1522           }
1523           FreePoolMemory(slt);
1524           break;
1525         }
1526         default:
1527           Dmsg1(debuglevel,
1528                 "IO_REPARSE_TAG_MOUNT_POINT with unhandled IO_REPARSE_TAG %d\n",
1529                 *pdwReserved0);
1530           break;
1531       }
1532     }
1533   } else {
1534     /*
1535      * No directory
1536      */
1537     if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1538       switch (*pdwReserved0) {
1539         case IO_REPARSE_TAG_SYMLINK: {
1540           POOLMEM* slt = GetPoolMemory(PM_NAME);
1541 
1542           Dmsg0(debuglevel, "We have a symlinked file!\n");
1543           sb->st_mode |= S_IFLNK;
1544 
1545           if (GetSymlinkData(filename, slt)) {
1546             Dmsg2(debuglevel, "Symlinked File %s points to: %s\n", filename,
1547                   slt);
1548           }
1549           FreePoolMemory(slt);
1550           break;
1551         }
1552         case IO_REPARSE_TAG_DEDUP:
1553           Dmsg0(debuglevel, "We have a deduplicated file!\n");
1554           sb->st_rdev |= FILE_ATTRIBUTES_DEDUPED_ITEM;
1555 
1556           /*
1557            * We treat a deduped file as a normal file.
1558            */
1559           sb->st_mode |= S_IFREG;
1560           break;
1561         default:
1562           Dmsg1(debuglevel,
1563                 "IO_REPARSE_TAG_MOUNT_POINT with unhandled IO_REPARSE_TAG %d\n",
1564                 *pdwReserved0);
1565           break;
1566       }
1567     }
1568   }
1569 
1570   Dmsg2(debuglevel, "st_rdev=%d filename=%s\n", sb->st_rdev, filename);
1571 
1572   sb->st_size = *pnFileSizeHigh;
1573   sb->st_size <<= 32;
1574   sb->st_size |= *pnFileSizeLow;
1575   sb->st_blksize = 4096;
1576   sb->st_blocks = (uint32_t)(sb->st_size + 4095) / 4096;
1577 
1578   sb->st_atime = CvtFtimeToUtime(*pftLastAccessTime);
1579   sb->st_mtime = CvtFtimeToUtime(*pftLastWriteTime);
1580   sb->st_ctime = CvtFtimeToUtime(*pftCreationTime);
1581 
1582   return 0;
1583 }
1584 
fstat(intptr_t fd,struct stat * sb)1585 int fstat(intptr_t fd, struct stat* sb)
1586 {
1587   bool use_fallback_data = true;
1588   BY_HANDLE_FILE_INFORMATION info;
1589 
1590   if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
1591     const char* err = errorString();
1592 
1593     Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
1594     LocalFree((void*)err);
1595     errno = b_errno_win32;
1596 
1597     return -1;
1598   }
1599 
1600   sb->st_dev = info.dwVolumeSerialNumber;
1601   sb->st_ino = info.nFileIndexHigh;
1602   sb->st_ino <<= 32;
1603   sb->st_ino |= info.nFileIndexLow;
1604   sb->st_nlink = (short)info.nNumberOfLinks;
1605   if (sb->st_nlink > 1) { Dmsg1(debuglevel, "st_nlink=%d\n", sb->st_nlink); }
1606 
1607   sb->st_mode = 0777;
1608   sb->st_mode |= S_IFREG;
1609 
1610   /*
1611    * See if we need to encode in the old Bacula compatible way.
1612    */
1613   if (win32_bacula_compatible) {
1614     EncodeWindowsFlagsCompatible(info.dwFileAttributes, sb);
1615   } else {
1616     /*
1617      * We store the full windows file attributes into st_rdev.
1618      */
1619     sb->st_rdev = info.dwFileAttributes;
1620   }
1621 
1622   Dmsg3(debuglevel, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev,
1623         sizeof(sb->st_ino), (long long)sb->st_ino);
1624 
1625   sb->st_size = info.nFileSizeHigh;
1626   sb->st_size <<= 32;
1627   sb->st_size |= info.nFileSizeLow;
1628   sb->st_blksize = 4096;
1629   sb->st_blocks = (uint32_t)(sb->st_size + 4095) / 4096;
1630 
1631 #if (_WIN32_WINNT >= 0x0600)
1632   if (p_GetFileInformationByHandleEx) {
1633     FILE_BASIC_INFO binfo;
1634 
1635     if (p_GetFileInformationByHandleEx((HANDLE)_get_osfhandle(fd),
1636                                        FileBasicInfo, &binfo, sizeof(binfo))) {
1637       sb->st_atime = CvtFtimeToUtime(binfo.LastAccessTime);
1638       sb->st_mtime = CvtFtimeToUtime(binfo.LastWriteTime);
1639       if (CvtFtimeToUtime(binfo.CreationTime) >
1640           CvtFtimeToUtime(binfo.ChangeTime)) {
1641         sb->st_ctime = CvtFtimeToUtime(binfo.CreationTime);
1642       } else {
1643         sb->st_ctime = CvtFtimeToUtime(binfo.ChangeTime);
1644       }
1645       use_fallback_data = false;
1646     }
1647   }
1648 #endif
1649 
1650   if (use_fallback_data) {
1651     sb->st_atime = CvtFtimeToUtime(info.ftLastAccessTime);
1652     sb->st_mtime = CvtFtimeToUtime(info.ftLastWriteTime);
1653     sb->st_ctime = CvtFtimeToUtime(info.ftCreationTime);
1654   }
1655 
1656   return 0;
1657 }
1658 
IsDriveLetterOnly(const char * filename)1659 static inline bool IsDriveLetterOnly(const char* filename)
1660 {
1661   return ((filename[1] == ':' && filename[2] == 0) ||
1662           (filename[1] == ':' && filename[2] == '/' && filename[3] == 0));
1663 }
1664 
stat2(const char * filename,struct stat * sb)1665 static int stat2(const char* filename, struct stat* sb)
1666 {
1667   HANDLE h = INVALID_HANDLE_VALUE;
1668   int rval = 0;
1669   DWORD attr = (DWORD)-1;
1670 
1671   if (p_GetFileAttributesW) {
1672     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
1673 
1674     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1675     attr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1676     if (p_CreateFileW) {
1677       h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ, FILE_SHARE_READ, NULL,
1678                       OPEN_EXISTING, 0, NULL);
1679     }
1680     FreePoolMemory(pwszBuf);
1681   } else if (p_GetFileAttributesA) {
1682     POOLMEM* win32_fname;
1683 
1684     win32_fname = GetPoolMemory(PM_FNAME);
1685     unix_name_to_win32(win32_fname, filename);
1686 
1687     attr = p_GetFileAttributesA(win32_fname);
1688     h = CreateFileA(win32_fname, GENERIC_READ, FILE_SHARE_READ, NULL,
1689                     OPEN_EXISTING, 0, NULL);
1690 
1691     FreePoolMemory(win32_fname);
1692   } else {
1693     Emsg0(M_FATAL, 0,
1694           _("p_GetFileAttributesW and p_GetFileAttributesA undefined. "
1695             "probably missing OSDependentInit() call\n"));
1696   }
1697 
1698   if (attr == (DWORD)-1) {
1699     const char* err = errorString();
1700 
1701     Dmsg2(2099, "GetFileAttributes(%s): %s\n", filename, err);
1702 
1703     LocalFree((void*)err);
1704     if (h != INVALID_HANDLE_VALUE) { CloseHandle(h); }
1705     errno = b_errno_win32;
1706 
1707     rval = -1;
1708     goto bail_out;
1709   }
1710 
1711   if (h == INVALID_HANDLE_VALUE) {
1712     const char* err = errorString();
1713 
1714     Dmsg2(2099, "Cannot open file for stat (%s):%s\n", filename, err);
1715     LocalFree((void*)err);
1716     errno = b_errno_win32;
1717 
1718     rval = -1;
1719     goto bail_out;
1720   }
1721 
1722   rval = fstat((intptr_t)h, sb);
1723   CloseHandle(h);
1724 
1725   /*
1726    * See if this is a directory or a reparse point and its not a single drive
1727    * letter. If so we call GetWindowsFileInfo() which retrieves the lowlevel
1728    * information we need.
1729    */
1730   if (((attr & FILE_ATTRIBUTE_DIRECTORY) ||
1731        (attr & FILE_ATTRIBUTE_REPARSE_POINT)) &&
1732       !IsDriveLetterOnly(filename)) {
1733     rval = GetWindowsFileInfo(filename, sb, (attr & FILE_ATTRIBUTE_DIRECTORY));
1734   }
1735 
1736 bail_out:
1737   return rval;
1738 }
1739 
stat(const char * filename,struct stat * sb)1740 int stat(const char* filename, struct stat* sb)
1741 {
1742   int rval = 0;
1743   POOLMEM* win32_fname = NULL;
1744   WIN32_FILE_ATTRIBUTE_DATA data;
1745 
1746   errno = 0;
1747   memset(sb, 0, sizeof(*sb));
1748 
1749   if (p_GetFileAttributesExW) {
1750     /*
1751      * Dynamically allocate enough space for UCS2 filename
1752      */
1753     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
1754     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1755 
1756     BOOL b =
1757         p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
1758     FreePoolMemory(pwszBuf);
1759 
1760     if (!b) { goto bail_out; }
1761   } else if (p_GetFileAttributesExA) {
1762     win32_fname = GetPoolMemory(PM_FNAME);
1763     unix_name_to_win32(win32_fname, filename);
1764 
1765     if (!p_GetFileAttributesExA(win32_fname, GetFileExInfoStandard, &data)) {
1766       goto bail_out;
1767     }
1768   } else {
1769     Emsg0(M_FATAL, 0,
1770           _("p_GetFileAttributesExW and p_GetFileAttributesExA undefined. "
1771             "probably missing OSDependentInit() call\n"));
1772     goto bail_out;
1773   }
1774 
1775   /*
1776    * See if this is a directory or a reparse point and not a single drive
1777    * letter. If so we call GetWindowsFileInfo() which retrieves the lowlevel
1778    * information we need. As GetWindowsFileInfo() fills the complete stat
1779    * structs we only need to perform the other part of the code when we don't
1780    * call the GetWindowsFileInfo() function.
1781    */
1782   if (((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1783        (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) &&
1784       !IsDriveLetterOnly(filename)) {
1785     rval = GetWindowsFileInfo(
1786         filename, sb, (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1787   } else {
1788     bool use_fallback_data = true;
1789 
1790     sb->st_mode = 0777;
1791 
1792     /*
1793      * See if we need to encode in the old Bacula compatible way.
1794      */
1795     if (win32_bacula_compatible) {
1796       EncodeWindowsFlagsCompatible(data.dwFileAttributes, sb);
1797     }
1798 
1799     /*
1800      * We store the full windows file attributes into st_rdev.
1801      */
1802     sb->st_rdev = data.dwFileAttributes;
1803 
1804     if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1805       sb->st_mode |= S_IFDIR;
1806     } else {
1807       sb->st_mode |= S_IFREG;
1808     }
1809 
1810     sb->st_nlink = 1;
1811     sb->st_size = data.nFileSizeHigh;
1812     sb->st_size <<= 32;
1813     sb->st_size |= data.nFileSizeLow;
1814     sb->st_blksize = 4096;
1815     sb->st_blocks = (uint32_t)(sb->st_size + 4095) / 4096;
1816 
1817 #if (_WIN32_WINNT >= 0x0600)
1818     /*
1819      * See if GetFileInformationByHandleEx API is available.
1820      */
1821     if (p_GetFileInformationByHandleEx) {
1822       HANDLE h = INVALID_HANDLE_VALUE;
1823 
1824       /*
1825        * The GetFileInformationByHandleEx need a file handle so we have to
1826        * open the file or directory read-only.
1827        */
1828       if (p_CreateFileW) {
1829         POOLMEM* pwszBuf;
1830 
1831         pwszBuf = GetPoolMemory(PM_FNAME);
1832         make_win32_path_UTF8_2_wchar(pwszBuf, filename);
1833 
1834         h = p_CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ, FILE_SHARE_READ, NULL,
1835                           OPEN_EXISTING, 0, NULL);
1836 
1837         FreePoolMemory(pwszBuf);
1838       } else {
1839         h = CreateFileA(win32_fname, GENERIC_READ, FILE_SHARE_READ, NULL,
1840                         OPEN_EXISTING, 0, NULL);
1841       }
1842 
1843       if (h != INVALID_HANDLE_VALUE) {
1844         FILE_BASIC_INFO binfo;
1845 
1846         if (p_GetFileInformationByHandleEx(h, FileBasicInfo, &binfo,
1847                                            sizeof(binfo))) {
1848           sb->st_atime = CvtFtimeToUtime(binfo.LastAccessTime);
1849           sb->st_mtime = CvtFtimeToUtime(binfo.LastWriteTime);
1850           if (CvtFtimeToUtime(binfo.CreationTime) >
1851               CvtFtimeToUtime(binfo.ChangeTime)) {
1852             sb->st_ctime = CvtFtimeToUtime(binfo.CreationTime);
1853           } else {
1854             sb->st_ctime = CvtFtimeToUtime(binfo.ChangeTime);
1855           }
1856           use_fallback_data = false;
1857         }
1858 
1859         CloseHandle(h);
1860       }
1861     }
1862 #endif
1863 
1864     if (use_fallback_data) {
1865       /*
1866        * Fall back to the GetFileAttributesEx data.
1867        */
1868       sb->st_atime = CvtFtimeToUtime(data.ftLastAccessTime);
1869       sb->st_mtime = CvtFtimeToUtime(data.ftLastWriteTime);
1870       sb->st_ctime = CvtFtimeToUtime(data.ftCreationTime);
1871     }
1872   }
1873   rval = 0;
1874 
1875   Dmsg3(debuglevel, "sizino=%d ino=%lld filename=%s\n", sizeof(sb->st_ino),
1876         (long long)sb->st_ino, filename);
1877 
1878   return rval;
1879 
1880 bail_out:
1881   if (win32_fname) { FreePoolMemory(win32_fname); }
1882 
1883   return stat2(filename, sb);
1884 }
1885 
1886 /**
1887  * Get the Volume MountPoint unique devicename for the given filename.
1888  * This is a wrapper around GetVolumeMountPointData() used for retrieving
1889  * the VMP data used in the VSS snapshotting.
1890  */
win32_get_vmp_devicename(const char * filename,POOLMEM * & devicename)1891 bool win32_get_vmp_devicename(const char* filename, POOLMEM*& devicename)
1892 {
1893   return GetVolumeMountPointData(filename, devicename);
1894 }
1895 
1896 /**
1897  * We write our own ftruncate because the one in the
1898  *  Microsoft library mrcrt.dll does not truncate
1899  *  files greater than 2GB.
1900  *  KES - May 2007
1901  */
win32_ftruncate(int fd,int64_t length)1902 int win32_ftruncate(int fd, int64_t length)
1903 {
1904   /* Set point we want to truncate file */
1905   __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
1906 
1907   if (pos != (__int64)length) {
1908     errno = EACCES; /* truncation failed, get out */
1909     return -1;
1910   }
1911 
1912   /* Truncate file */
1913   if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
1914     errno = b_errno_win32;
1915     return -1;
1916   }
1917   errno = 0;
1918   return 0;
1919 }
1920 
fcntl(int fd,int cmd,long arg)1921 int fcntl(int fd, int cmd, long arg)
1922 {
1923   int rval = 0;
1924 
1925   switch (cmd) {
1926     case F_GETFL:
1927       rval = O_NONBLOCK;
1928       break;
1929 
1930     case F_SETFL:
1931       rval = 0;
1932       break;
1933 
1934     default:
1935       errno = EINVAL;
1936       rval = -1;
1937       break;
1938   }
1939 
1940   return rval;
1941 }
1942 
lstat(const char * filename,struct stat * sb)1943 int lstat(const char* filename, struct stat* sb) { return stat(filename, sb); }
1944 
sleep(int sec)1945 void sleep(int sec) { Sleep(sec * 1000); }
1946 
geteuid(void)1947 int geteuid(void) { return 0; }
1948 
execvp(const char *,char * [])1949 int execvp(const char*, char*[])
1950 {
1951   errno = ENOSYS;
1952   return -1;
1953 }
1954 
fork(void)1955 int fork(void)
1956 {
1957   errno = ENOSYS;
1958   return -1;
1959 }
1960 
pipe(int[])1961 int pipe(int[])
1962 {
1963   errno = ENOSYS;
1964   return -1;
1965 }
1966 
waitpid(int,int *,int)1967 int waitpid(int, int*, int)
1968 {
1969   errno = ENOSYS;
1970   return -1;
1971 }
1972 
1973 /**
1974  * Read contents of symbolic link
1975  */
readlink(const char * path,char * buf,size_t bufsiz)1976 ssize_t readlink(const char* path, char* buf, size_t bufsiz)
1977 {
1978   POOLMEM* slt = GetPoolMemory(PM_NAME);
1979 
1980   Dmsg1(debuglevel, "readlink called for path %s\n", path);
1981   GetSymlinkData(path, slt);
1982 
1983   strncpy(buf, slt, bufsiz - 1);
1984   buf[bufsiz] = '\0';
1985   FreePoolMemory(slt);
1986 
1987   return strlen(buf);
1988 }
1989 
1990 /**
1991  * Create a directory symlink / file symlink/junction
1992  */
win32_symlink(const char * name1,const char * name2,_dev_t st_rdev)1993 int win32_symlink(const char* name1, const char* name2, _dev_t st_rdev)
1994 {
1995 #if (_WIN32_WINNT >= 0x0600)
1996   int dwFlags = 0x0;
1997 
1998   if (st_rdev & FILE_ATTRIBUTES_JUNCTION_POINT) {
1999     Dmsg0(130, "We have a Junction Point \n");
2000     if (!CreateJunction(name2, name1)) {
2001       return -1;
2002     } else {
2003       return 0;
2004     }
2005   } else if (st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT) {
2006     Dmsg0(130, "We have a Volume Mount Point \n");
2007     return 0;
2008   } else if (st_rdev & FILE_ATTRIBUTES_SYMBOLIC_LINK) {
2009     Dmsg0(130, "We have a Directory Symbolic Link\n");
2010     dwFlags = SYMBOLIC_LINK_FLAG_DIRECTORY;
2011   } else {
2012     Dmsg0(130, "We have a File Symbolic Link \n");
2013   }
2014 
2015   Dmsg2(debuglevel, "symlink called name1=%s, name2=%s\n", name1, name2);
2016   if (p_CreateSymbolicLinkW) {
2017     /*
2018      * Dynamically allocate enough space for UCS2 filename
2019      *
2020      * pwszBuf1: lpTargetFileName
2021      * pwszBuf2: lpSymlinkFileName
2022      */
2023     POOLMEM* pwszBuf1 = GetPoolMemory(PM_FNAME);
2024     POOLMEM* pwszBuf2 = GetPoolMemory(PM_FNAME);
2025 
2026     if (!UTF8_2_wchar(pwszBuf1, name1)) { goto bail_out; }
2027     make_win32_path_UTF8_2_wchar(pwszBuf2, name2);
2028 
2029     BOOL b =
2030         p_CreateSymbolicLinkW((LPCWSTR)pwszBuf2, (LPCWSTR)pwszBuf1, dwFlags);
2031 
2032     FreePoolMemory(pwszBuf1);
2033     FreePoolMemory(pwszBuf2);
2034 
2035     if (!b) {
2036       Dmsg1(debuglevel, "CreateSymbolicLinkW failed:%s\n", errorString());
2037       goto bail_out;
2038     }
2039   } else if (p_CreateSymbolicLinkA) {
2040     POOLMEM* win32_name1 = GetPoolMemory(PM_FNAME);
2041     POOLMEM* win32_name2 = GetPoolMemory(PM_FNAME);
2042     unix_name_to_win32(win32_name1, name1);
2043     unix_name_to_win32(win32_name2, name2);
2044 
2045     BOOL b = p_CreateSymbolicLinkA(win32_name2, win32_name1, dwFlags);
2046 
2047     FreePoolMemory(win32_name1);
2048     FreePoolMemory(win32_name2);
2049 
2050     if (!b) {
2051       Dmsg1(debuglevel, "CreateSymbolicLinkA failed:%s\n", errorString());
2052       goto bail_out;
2053     }
2054   } else {
2055     errno = ENOSYS;
2056     goto bail_out;
2057   }
2058 
2059   return 0;
2060 
2061 bail_out:
2062   return -1;
2063 #else
2064   errno = ENOSYS;
2065   return -1;
2066 #endif
2067 }
2068 
2069 /**
2070  * Create a hardlink
2071  */
link(const char * existing,const char * newfile)2072 int link(const char* existing, const char* newfile)
2073 {
2074   errno = ENOSYS;
2075   return -1;
2076 }
2077 
gettimeofday(struct timeval * tv,struct timezone * tz)2078 int gettimeofday(struct timeval* tv, struct timezone* tz)
2079 {
2080   return mingw_gettimeofday(tv, tz);
2081 }
2082 
2083 /**
2084  * Write in Windows System log
2085  */
syslog(int type,const char * fmt,...)2086 extern "C" void syslog(int type, const char* fmt, ...)
2087 {
2088   va_list arg_ptr;
2089   int len, maxlen;
2090   POOLMEM* msg;
2091 
2092   msg = GetPoolMemory(PM_EMSG);
2093 
2094   for (;;) {
2095     maxlen = SizeofPoolMemory(msg) - 1;
2096     va_start(arg_ptr, fmt);
2097     len = Bvsnprintf(msg, maxlen, fmt, arg_ptr);
2098     va_end(arg_ptr);
2099     if (len < 0 || len >= (maxlen - 5)) {
2100       msg = ReallocPoolMemory(msg, maxlen + maxlen / 2);
2101       continue;
2102     }
2103     break;
2104   }
2105   LogErrorMsg((const char*)msg);
2106   FreeMemory(msg);
2107 }
2108 
closelog()2109 extern "C" void closelog() {}
2110 
getpwuid(uid_t)2111 struct passwd* getpwuid(uid_t) { return NULL; }
2112 
getgrgid(uid_t)2113 struct group* getgrgid(uid_t) { return NULL; }
2114 
2115 /**
2116  * Implement opendir/readdir/closedir on top of window's API
2117  */
2118 typedef struct _dir {
2119   WIN32_FIND_DATAA data_a; /* window's file info (ansii version) */
2120   WIN32_FIND_DATAW data_w; /* window's file info (wchar version) */
2121   const char* spec;        /* the directory we're traversing */
2122   HANDLE dirh;             /* the search handle */
2123   BOOL valid_a;            /* the info in data_a field is valid */
2124   BOOL valid_w;            /* the info in data_w field is valid */
2125   UINT32 offset;           /* pseudo offset for d_off */
2126 } _dir;
2127 
opendir(const char * path)2128 DIR* opendir(const char* path)
2129 {
2130   POOLMEM* win32_path;
2131   _dir* rval = NULL;
2132 
2133   if (path == NULL) {
2134     errno = ENOENT;
2135     return NULL;
2136   }
2137 
2138   Dmsg1(debuglevel, "Opendir path=%s\n", path);
2139   rval = (_dir*)malloc(sizeof(_dir));
2140   memset(rval, 0, sizeof(_dir));
2141 
2142   win32_path = GetPoolMemory(PM_FNAME);
2143   unix_name_to_win32(win32_path, path);
2144   Dmsg1(debuglevel, "win32 path=%s\n", win32_path);
2145 
2146   /*
2147    * Add backslash only if there is none yet (think of c:\)
2148    */
2149   if (win32_path[strlen(win32_path) - 1] != '\\') {
2150     PmStrcat(win32_path, "\\*");
2151   } else {
2152     PmStrcat(win32_path, "*");
2153   }
2154 
2155   rval->spec = win32_path;
2156 
2157   /*
2158    * convert to wchar_t
2159    */
2160   if (p_FindFirstFileW) {
2161     POOLMEM* pwcBuf;
2162 
2163     pwcBuf = GetPoolMemory(PM_FNAME);
2164     make_win32_path_UTF8_2_wchar(pwcBuf, rval->spec);
2165     rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
2166     if (rval->dirh != INVALID_HANDLE_VALUE) { rval->valid_w = 1; }
2167     FreePoolMemory(pwcBuf);
2168   } else if (p_FindFirstFileA) {
2169     rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
2170     if (rval->dirh != INVALID_HANDLE_VALUE) { rval->valid_a = 1; }
2171   } else {
2172     goto bail_out;
2173   }
2174 
2175   Dmsg3(debuglevel, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
2176         path, rval->spec, rval->dirh);
2177 
2178   rval->offset = 0;
2179   if (rval->dirh == INVALID_HANDLE_VALUE) { goto bail_out; }
2180 
2181   if (rval->valid_w) {
2182     Dmsg1(debuglevel, "\tFirstFile=%s\n", rval->data_w.cFileName);
2183   }
2184 
2185   if (rval->valid_a) {
2186     Dmsg1(debuglevel, "\tFirstFile=%s\n", rval->data_a.cFileName);
2187   }
2188 
2189   return (DIR*)rval;
2190 
2191 bail_out:
2192   if (rval) { free(rval); }
2193 
2194   FreePoolMemory(win32_path);
2195 
2196   errno = b_errno_win32;
2197   return NULL;
2198 }
2199 
closedir(DIR * dirp)2200 int closedir(DIR* dirp)
2201 {
2202   _dir* dp = (_dir*)dirp;
2203   FindClose(dp->dirh);
2204   FreePoolMemory((POOLMEM*)dp->spec);
2205   free((void*)dp);
2206   return 0;
2207 }
2208 
copyin(struct dirent & dp,const char * fname)2209 static int copyin(struct dirent& dp, const char* fname)
2210 {
2211   char* cp;
2212 
2213   dp.d_ino = 0;
2214   dp.d_reclen = 0;
2215   cp = dp.d_name;
2216   while (*fname) {
2217     *cp++ = *fname++;
2218     dp.d_reclen++;
2219   }
2220   *cp = 0;
2221 
2222   return dp.d_reclen;
2223 }
2224 
2225 #ifdef USE_READDIR_R
Readdir_r(DIR * dirp,struct dirent * entry,struct dirent ** result)2226 int Readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result)
2227 {
2228   _dir* dp = (_dir*)dirp;
2229   if (dp->valid_w || dp->valid_a) {
2230     entry->d_off = dp->offset;
2231 
2232     // copy unicode
2233     if (dp->valid_w) {
2234       POOLMEM* szBuf;
2235 
2236       szBuf = GetPoolMemory(PM_NAME);
2237       wchar_2_UTF8(szBuf, dp->data_w.cFileName);
2238       dp->offset += copyin(*entry, szBuf);
2239       FreePoolMemory(szBuf);
2240     } else if (dp->valid_a) {  // copy ansi (only 1 will be valid)
2241       dp->offset += copyin(*entry, dp->data_a.cFileName);
2242     }
2243 
2244     *result = entry; /* return entry address */
2245     Dmsg4(debuglevel, "Readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
2246           dirp, entry->d_name, entry->d_reclen, entry->d_off);
2247   } else {
2248     errno = b_errno_win32;
2249     return -1;
2250   }
2251 
2252   /*
2253    * get next file, try unicode first
2254    */
2255   if (p_FindNextFileW) {
2256     dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
2257   } else if (p_FindNextFileA) {
2258     dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
2259   } else {
2260     dp->valid_a = FALSE;
2261     dp->valid_w = FALSE;
2262   }
2263 
2264   return 0;
2265 }
2266 #endif
2267 /**
2268  * Dotted IP address to network address
2269  *
2270  * Returns 1 if  OK
2271  *         0 on error
2272  */
inet_aton(const char * a,struct in_addr * inp)2273 int inet_aton(const char* a, struct in_addr* inp)
2274 {
2275   const char* cp = a;
2276   uint32_t acc = 0, tmp = 0;
2277   int dotc = 0;
2278 
2279   if (!isdigit(*cp)) { /* first char must be digit */
2280     return 0;          /* error */
2281   }
2282   do {
2283     if (isdigit(*cp)) {
2284       tmp = (tmp * 10) + (*cp - '0');
2285     } else if (*cp == '.' || *cp == 0) {
2286       if (tmp > 255) { return 0; /* error */ }
2287       acc = (acc << 8) + tmp;
2288       dotc++;
2289       tmp = 0;
2290     } else {
2291       return 0; /* error */
2292     }
2293   } while (*cp++ != 0);
2294   if (dotc != 4) { /* want 3 .'s plus EOS */
2295     return 0;      /* error */
2296   }
2297   inp->s_addr = htonl(acc); /* store addr in network format */
2298   return 1;
2299 }
2300 
InitSignals(void Terminate (int sig))2301 void InitSignals(void Terminate(int sig)) {}
2302 
InitStackDump(void)2303 void InitStackDump(void) {}
2304 
pathconf(const char * path,int name)2305 long pathconf(const char* path, int name)
2306 {
2307   switch (name) {
2308     case _PC_PATH_MAX:
2309       if (strncmp(path, "\\\\?\\", 4) == 0) return 32767;
2310     case _PC_NAME_MAX:
2311       return 255;
2312   }
2313   errno = ENOSYS;
2314   return -1;
2315 }
2316 
WSA_Init(void)2317 int WSA_Init(void)
2318 {
2319   WORD wVersionRequested = MAKEWORD(1, 1);
2320   WSADATA wsaData;
2321 
2322   int err = WSAStartup(wVersionRequested, &wsaData);
2323 
2324   if (err != 0) {
2325     printf("Can not start Windows Sockets\n");
2326     errno = ENOSYS;
2327     return -1;
2328   }
2329 
2330   return 0;
2331 }
2332 
fill_attribute(DWORD attr,mode_t mode,_dev_t rdev)2333 static DWORD fill_attribute(DWORD attr, mode_t mode, _dev_t rdev)
2334 {
2335   bool compatible = false;
2336 
2337   Dmsg1(debuglevel, "  before attr=%lld\n", (uint64_t)attr);
2338 
2339   /*
2340    * First see if there are any old encoded attributes in the mode.
2341    */
2342 
2343   /*
2344    * If file is readable then this is not READONLY
2345    */
2346   if (mode & (S_IRUSR | S_IRGRP | S_IROTH)) {
2347     attr &= ~FILE_ATTRIBUTE_READONLY;
2348   } else {
2349     attr |= FILE_ATTRIBUTE_READONLY;
2350     compatible = true;
2351   }
2352 
2353   /*
2354    * The sticky bit <=> HIDDEN
2355    */
2356   if (mode & S_ISVTX) {
2357     attr |= FILE_ATTRIBUTE_HIDDEN;
2358     compatible = true;
2359   } else {
2360     attr &= ~FILE_ATTRIBUTE_HIDDEN;
2361   }
2362 
2363   /*
2364    * Other can read/write/execute ?
2365    * => Not system
2366    */
2367   if (mode & S_IRWXO) {
2368     attr &= ~FILE_ATTRIBUTE_SYSTEM;
2369   } else {
2370     attr |= FILE_ATTRIBUTE_SYSTEM;
2371     compatible = true;
2372   }
2373 
2374   /*
2375    * See if there are any compatible mode settings in the to restore mode.
2376    * If so we don't trust the rdev setting as it is from an Bacula compatible
2377    * Filedaemon and as such doesn't encode the windows file flags in st_rdev
2378    * but uses st_rdev for marking reparse points.
2379    */
2380   if (!compatible) {
2381     /*
2382      * Based on the setting of rdev restore any special file flags.
2383      */
2384     if (rdev & FILE_ATTRIBUTE_READONLY) {
2385       attr |= FILE_ATTRIBUTE_READONLY;
2386     } else {
2387       attr &= ~FILE_ATTRIBUTE_READONLY;
2388     }
2389 
2390     if (rdev & FILE_ATTRIBUTE_HIDDEN) {
2391       attr |= FILE_ATTRIBUTE_HIDDEN;
2392     } else {
2393       attr &= ~FILE_ATTRIBUTE_HIDDEN;
2394     }
2395 
2396     if (rdev & FILE_ATTRIBUTE_SYSTEM) {
2397       attr |= FILE_ATTRIBUTE_SYSTEM;
2398     } else {
2399       attr &= ~FILE_ATTRIBUTE_SYSTEM;
2400     }
2401 
2402     if (rdev & FILE_ATTRIBUTE_ARCHIVE) {
2403       attr |= FILE_ATTRIBUTE_ARCHIVE;
2404     } else {
2405       attr &= ~FILE_ATTRIBUTE_ARCHIVE;
2406     }
2407 
2408     if (rdev & FILE_ATTRIBUTE_TEMPORARY) {
2409       attr |= FILE_ATTRIBUTE_TEMPORARY;
2410     } else {
2411       attr &= ~FILE_ATTRIBUTE_TEMPORARY;
2412     }
2413 
2414     if (rdev & FILE_ATTRIBUTE_OFFLINE) {
2415       attr |= FILE_ATTRIBUTE_OFFLINE;
2416     } else {
2417       attr &= ~FILE_ATTRIBUTE_OFFLINE;
2418     }
2419 
2420     if (rdev & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
2421       attr |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
2422     } else {
2423       attr &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
2424     }
2425   }
2426 
2427   Dmsg1(debuglevel, "  after attr=%lld\n", (uint64_t)attr);
2428 
2429   return attr;
2430 }
2431 
win32_chmod(const char * path,mode_t mode,_dev_t rdev)2432 int win32_chmod(const char* path, mode_t mode, _dev_t rdev)
2433 {
2434   bool ret = false;
2435   DWORD attr;
2436 
2437   Dmsg3(debuglevel, "win32_chmod(path=%s mode=%lld, rdev=%lld)\n", path,
2438         (uint64_t)mode, (uint64_t)rdev);
2439 
2440   if (p_GetFileAttributesW) {
2441     POOLMEM* pwszBuf;
2442 
2443     pwszBuf = GetPoolMemory(PM_FNAME);
2444     make_win32_path_UTF8_2_wchar(pwszBuf, path);
2445     attr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
2446     if (attr != INVALID_FILE_ATTRIBUTES) {
2447       /*
2448        * Use Bareos mappings define in stat() above
2449        */
2450       attr = fill_attribute(attr, mode, rdev);
2451       ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
2452     }
2453     FreePoolMemory(pwszBuf);
2454     Dmsg0(debuglevel, "Leave win32_chmod. AttributesW\n");
2455   } else if (p_GetFileAttributesA) {
2456     attr = p_GetFileAttributesA(path);
2457     if (attr != INVALID_FILE_ATTRIBUTES) {
2458       attr = fill_attribute(attr, mode, rdev);
2459       ret = p_SetFileAttributesA(path, attr);
2460     }
2461     Dmsg0(debuglevel, "Leave win32_chmod did AttributesA\n");
2462   } else {
2463     Dmsg0(debuglevel, "Leave win32_chmod did nothing\n");
2464   }
2465 
2466   if (!ret) {
2467     const char* err = errorString();
2468 
2469     Dmsg2(debuglevel, "Get/SetFileAttributes(%s): %s\n", path, err);
2470     LocalFree((void*)err);
2471     errno = b_errno_win32;
2472 
2473     return -1;
2474   }
2475 
2476   return 0;
2477 }
2478 
win32_chdir(const char * dir)2479 int win32_chdir(const char* dir)
2480 {
2481   if (p_SetCurrentDirectoryW) {
2482     POOLMEM* pwszBuf;
2483     BOOL b;
2484 
2485     pwszBuf = GetPoolMemory(PM_FNAME);
2486     make_win32_path_UTF8_2_wchar(pwszBuf, dir);
2487     b = p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
2488     FreePoolMemory(pwszBuf);
2489     if (!b) {
2490       errno = b_errno_win32;
2491       return -1;
2492     }
2493   } else if (p_SetCurrentDirectoryA) {
2494     if (0 == p_SetCurrentDirectoryA(dir)) {
2495       errno = b_errno_win32;
2496       return -1;
2497     }
2498   } else {
2499     return -1;
2500   }
2501 
2502   return 0;
2503 }
2504 
win32_mkdir(const char * dir)2505 int win32_mkdir(const char* dir)
2506 {
2507   Dmsg1(debuglevel, "enter win32_mkdir. dir=%s\n", dir);
2508   if (p_wmkdir) {
2509     int n;
2510     POOLMEM* pwszBuf;
2511 
2512     pwszBuf = GetPoolMemory(PM_FNAME);
2513     make_win32_path_UTF8_2_wchar(pwszBuf, dir);
2514     n = p_wmkdir((LPCWSTR)pwszBuf);
2515     FreePoolMemory(pwszBuf);
2516     Dmsg0(debuglevel, "Leave win32_mkdir did wmkdir\n");
2517 
2518     return n;
2519   }
2520 
2521   Dmsg0(debuglevel, "Leave win32_mkdir did _mkdir\n");
2522   return _mkdir(dir);
2523 }
2524 
win32_getcwd(char * buf,int maxlen)2525 char* win32_getcwd(char* buf, int maxlen)
2526 {
2527   int n = 0;
2528 
2529   if (p_GetCurrentDirectoryW) {
2530     POOLMEM* pwszBuf;
2531 
2532     pwszBuf = GetPoolMemory(PM_FNAME);
2533     pwszBuf = CheckPoolMemorySize(pwszBuf, maxlen * sizeof(wchar_t));
2534     n = p_GetCurrentDirectoryW(maxlen, (LPWSTR)pwszBuf);
2535     if (n != 0) { n = wchar_2_UTF8(buf, (wchar_t*)pwszBuf, maxlen) - 1; }
2536     FreePoolMemory(pwszBuf);
2537   } else if (p_GetCurrentDirectoryA) {
2538     n = p_GetCurrentDirectoryA(maxlen, buf);
2539   }
2540 
2541   if (n <= 0 || n > maxlen) { return NULL; }
2542 
2543   if (n + 1 > maxlen) { return NULL; }
2544 
2545   if (n != 3) {
2546     buf[n] = '\\';
2547     buf[n + 1] = 0;
2548   }
2549 
2550   return buf;
2551 }
2552 
win32_fputs(const char * string,FILE * stream)2553 int win32_fputs(const char* string, FILE* stream)
2554 {
2555   /*
2556    * We use WriteConsoleA / WriteConsoleA so we can be sure that unicode support
2557    * works on win32. with fallback if something fails
2558    */
2559   HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
2560   if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
2561       p_MultiByteToWideChar && (stream == stdout)) {
2562     POOLMEM* pwszBuf;
2563     POOLMEM* pszBuf;
2564     DWORD dwCharsWritten;
2565     DWORD dwChars;
2566 
2567     pwszBuf = GetPoolMemory(PM_MESSAGE);
2568     dwChars = UTF8_2_wchar(pwszBuf, string);
2569 
2570     /*
2571      * Try WriteConsoleW
2572      */
2573     if (WriteConsoleW(hOut, pwszBuf, dwChars - 1, &dwCharsWritten, NULL)) {
2574       FreePoolMemory(pwszBuf);
2575       return dwCharsWritten;
2576     }
2577 
2578     /*
2579      * Convert to local codepage and try WriteConsoleA
2580      */
2581     pszBuf = GetPoolMemory(PM_MESSAGE);
2582     pszBuf = CheckPoolMemorySize(pszBuf, dwChars + 1);
2583 
2584     dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(), 0, (LPCWSTR)pwszBuf,
2585                                     -1, pszBuf, dwChars, NULL, NULL);
2586     FreePoolMemory(pwszBuf);
2587 
2588     if (WriteConsoleA(hOut, pszBuf, dwChars - 1, &dwCharsWritten, NULL)) {
2589       FreePoolMemory(pszBuf);
2590       return dwCharsWritten;
2591     }
2592     FreePoolMemory(pszBuf);
2593   }
2594 
2595   /*
2596    * Fall back
2597    */
2598   return fputs(string, stream);
2599 }
2600 
win32_cgets(char * buffer,int len)2601 char* win32_cgets(char* buffer, int len)
2602 {
2603   /*
2604    * We use console ReadConsoleA / ReadConsoleW to be able to read unicode
2605    * from the win32 console and fallback if seomething fails
2606    */
2607   HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
2608   if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
2609       p_MultiByteToWideChar) {
2610     DWORD dwRead;
2611     wchar_t wszBuf[1024];
2612     char szBuf[1024];
2613 
2614     /*
2615      * NT and unicode conversion
2616      */
2617     if (ReadConsoleW(hIn, wszBuf, 1024, &dwRead, NULL)) {
2618       /*
2619        * NULL Terminate at end
2620        */
2621       if (wszBuf[dwRead - 1] == L'\n') {
2622         wszBuf[dwRead - 1] = L'\0';
2623         dwRead--;
2624       }
2625 
2626       if (wszBuf[dwRead - 1] == L'\r') {
2627         wszBuf[dwRead - 1] = L'\0';
2628         dwRead--;
2629       }
2630 
2631       wchar_2_UTF8(buffer, wszBuf, len);
2632       return buffer;
2633     }
2634 
2635     /*
2636      * WIN 9x and unicode conversion
2637      */
2638     if (ReadConsoleA(hIn, szBuf, 1024, &dwRead, NULL)) {
2639       /*
2640        * NULL Terminate at end
2641        */
2642       if (szBuf[dwRead - 1] == L'\n') {
2643         szBuf[dwRead - 1] = L'\0';
2644         dwRead--;
2645       }
2646 
2647       if (szBuf[dwRead - 1] == L'\r') {
2648         szBuf[dwRead - 1] = L'\0';
2649         dwRead--;
2650       }
2651 
2652       /*
2653        * Convert from ansii to wchar_t
2654        */
2655       p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf, 1024);
2656 
2657       /*
2658        * Convert from wchar_t to UTF-8
2659        */
2660       if (wchar_2_UTF8(buffer, wszBuf, len)) return buffer;
2661     }
2662   }
2663 
2664   /*
2665    * Fallback
2666    */
2667   if (fgets(buffer, len, stdin))
2668     return buffer;
2669   else
2670     return NULL;
2671 }
2672 
win32_unlink(const char * filename)2673 int win32_unlink(const char* filename)
2674 {
2675   int nRetCode;
2676   if (p_wunlink) {
2677     POOLMEM* pwszBuf;
2678 
2679     pwszBuf = GetPoolMemory(PM_FNAME);
2680     make_win32_path_UTF8_2_wchar(pwszBuf, filename);
2681     nRetCode = _wunlink((LPCWSTR)pwszBuf);
2682 
2683     /*
2684      * Special case if file is readonly, we retry but unset attribute before
2685      */
2686     if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW &&
2687         p_GetFileAttributesW) {
2688       DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
2689       if (dwAttr != INVALID_FILE_ATTRIBUTES) {
2690         if (p_SetFileAttributesW((LPCWSTR)pwszBuf,
2691                                  dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
2692           nRetCode = _wunlink((LPCWSTR)pwszBuf);
2693           /* reset to original if it didn't help */
2694           if (nRetCode == -1) p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
2695         }
2696       }
2697     }
2698 
2699     /* if deletion with _unlink failed, try to use DeleteFileW (which also can
2700      * remove file links) */
2701     if (nRetCode == -1) {
2702       Dmsg0(100, "_unlink failed, trying DeleteFileW \n");
2703       if (DeleteFileW((LPCWSTR)pwszBuf) == 0) {  // 0 = fail
2704         Dmsg0(100, "DeleteFileW failed\n");
2705         nRetCode = -1;
2706       } else {
2707         Dmsg0(100, "DeleteFileW success\n");
2708         nRetCode = 0;
2709       }
2710     }
2711 
2712 
2713     /* if deletion with DeleteFileW failed, try to use RemoveDirectoryW (which
2714      * also can remove directory links) */
2715     if (nRetCode == -1) {
2716       Dmsg0(100, "DeleteFileW failed, trying RemoveDirectoryW \n");
2717       if (RemoveDirectoryW((LPCWSTR)pwszBuf) == 0) {  // 0 = fail
2718         Dmsg0(100, "RemoveDirectoryW failed\n");
2719         nRetCode = -1;
2720       } else {
2721         Dmsg0(100, "RemoveDirectoryW success\n");
2722         nRetCode = 0;
2723       }
2724     }
2725 
2726     FreePoolMemory(pwszBuf);
2727 
2728   } else {
2729     nRetCode = _unlink(filename);
2730 
2731     /*
2732      * Special case if file is readonly, we retry but unset attribute before
2733      */
2734     if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA &&
2735         p_GetFileAttributesA) {
2736       DWORD dwAttr = p_GetFileAttributesA(filename);
2737       if (dwAttr != INVALID_FILE_ATTRIBUTES) {
2738         if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
2739           nRetCode = _unlink(filename);
2740           /*
2741            * Reset to original if it didn't help
2742            */
2743           if (nRetCode == -1) p_SetFileAttributesA(filename, dwAttr);
2744         }
2745       }
2746     }
2747   }
2748   return nRetCode;
2749 }
2750 
2751 /**
2752  * Define attributes that are legal to set with SetFileAttributes()
2753  */
2754 #define SET_ATTRS                                                           \
2755   (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL | \
2756    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_OFFLINE |            \
2757    FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_TEMPORARY)
2758 
win32_restore_file_attributes(POOLMEM * ofname,HANDLE handle,WIN32_FILE_ATTRIBUTE_DATA * atts)2759 bool win32_restore_file_attributes(POOLMEM* ofname,
2760                                    HANDLE handle,
2761                                    WIN32_FILE_ATTRIBUTE_DATA* atts)
2762 {
2763   bool retval = false;
2764 
2765   Dmsg1(100, "SetFileAtts %s\n", ofname);
2766   if (p_SetFileAttributesW) {
2767     BOOL b;
2768     POOLMEM* pwszBuf = GetPoolMemory(PM_FNAME);
2769 
2770     make_win32_path_UTF8_2_wchar(pwszBuf, ofname);
2771     b = p_SetFileAttributesW((LPCWSTR)pwszBuf,
2772                              atts->dwFileAttributes & SET_ATTRS);
2773     FreePoolMemory(pwszBuf);
2774 
2775     if (!b) { goto bail_out; }
2776   } else {
2777     BOOL b;
2778     POOLMEM* win32_ofile = GetPoolMemory(PM_FNAME);
2779 
2780     unix_name_to_win32(win32_ofile, ofname);
2781     b = p_SetFileAttributesA(win32_ofile, atts->dwFileAttributes & SET_ATTRS);
2782     FreePoolMemory(win32_ofile);
2783 
2784     if (!b) { goto bail_out; }
2785   }
2786 
2787   if (handle != INVALID_HANDLE_VALUE) {
2788     if (atts->dwFileAttributes & FILE_ATTRIBUTES_DEDUPED_ITEM) {
2789       Dmsg1(100, "File %s is a FILE_ATTRIBUTES_DEDUPED_ITEM\n", ofname);
2790     }
2791     /*
2792      * Restore the sparse file attribute on the restored file.
2793      */
2794     if (atts->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
2795       DWORD bytesreturned;
2796 
2797       Dmsg1(100, "Restore FILE_ATTRIBUTE_SPARSE_FILE on %s\n", ofname);
2798       if (!DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
2799                            &bytesreturned, NULL)) {
2800         goto bail_out;
2801       }
2802     }
2803 
2804     /*
2805      * Restore the compressed file attribute on the restored file.
2806      */
2807     if (atts->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
2808       USHORT format = COMPRESSION_FORMAT_DEFAULT;
2809       DWORD bytesreturned;
2810 
2811       Dmsg1(100, "Restore FILE_ATTRIBUTE_COMPRESSED on %s\n", ofname);
2812       if (!DeviceIoControl(handle, FSCTL_SET_COMPRESSION, &format,
2813                            sizeof(format), NULL, 0, &bytesreturned, NULL)) {
2814         goto bail_out;
2815       }
2816     }
2817 
2818     /*
2819      * Restore file times on the restored file.
2820      */
2821     Dmsg1(100, "SetFileTime %s\n", ofname);
2822     if (!SetFileTime(handle, &atts->ftCreationTime, &atts->ftLastAccessTime,
2823                      &atts->ftLastWriteTime)) {
2824       goto bail_out;
2825     }
2826   }
2827 
2828   retval = true;
2829 
2830 bail_out:
2831   return retval;
2832 }
2833 
2834 #include "mswinver.h"
2835 
2836 char WIN_VERSION_LONG[64];
2837 char WIN_VERSION[32];
2838 char WIN_RAWVERSION[32];
2839 
2840 class winver {
2841  public:
2842   winver(void);
2843 };
2844 
2845 static winver INIT; /* Cause constructor to be called before main() */
2846 
winver(void)2847 winver::winver(void)
2848 {
2849   const char* version = "";
2850   const char* platform = "";
2851   OSVERSIONINFO osvinfo;
2852   osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
2853 
2854   /*
2855    * Get the current OS version
2856    */
2857   if (!GetVersionEx(&osvinfo)) {
2858     version = "Unknown";
2859     platform = "Unknown";
2860   }
2861   const int ver = _mkversion(osvinfo.dwPlatformId, osvinfo.dwMajorVersion,
2862                              osvinfo.dwMinorVersion);
2863   snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
2864   switch (ver) {
2865     case MS_WINDOWS_95:
2866       version = "Windows 95";
2867       break;
2868     case MS_WINDOWS_98:
2869       version = "Windows 98";
2870       break;
2871     case MS_WINDOWS_ME:
2872       version = "Windows ME";
2873       break;
2874     case MS_WINDOWS_NT4:
2875       version = "Windows NT 4.0";
2876       platform = "NT";
2877       break;
2878     case MS_WINDOWS_2K:
2879       version = "Windows 2000";
2880       platform = "NT";
2881       break;
2882     case MS_WINDOWS_XP:
2883       version = "Windows XP";
2884       platform = "NT";
2885       break;
2886     case MS_WINDOWS_S2003:
2887       version = "Windows Server 2003";
2888       platform = "NT";
2889       break;
2890     default:
2891       version = WIN_RAWVERSION;
2892       break;
2893   }
2894 
2895   bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
2896   snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu", platform,
2897            osvinfo.dwMajorVersion, osvinfo.dwMinorVersion,
2898            osvinfo.dwBuildNumber);
2899 }
2900 
2901 BOOL CreateChildProcess(VOID);
2902 VOID WriteToPipe(VOID);
2903 VOID ReadFromPipe(VOID);
2904 VOID ErrorExit(LPCSTR);
2905 VOID ErrMsg(LPTSTR, BOOL);
2906 
2907 /**
2908  * Check for a quoted path,  if an absolute path name is given and it contains
2909  * spaces it will need to be quoted.  i.e.  "c:/Program Files/foo/bar.exe"
2910  * CreateProcess() says the best way to ensure proper results with executables
2911  * with spaces in path or filename is to quote the string.
2912  */
getArgv0(const char * cmdline)2913 const char* getArgv0(const char* cmdline)
2914 {
2915   int inquote = 0;
2916   const char* cp;
2917   for (cp = cmdline; *cp; cp++) {
2918     if (*cp == '"') { inquote = !inquote; }
2919     if (!inquote && isspace(*cp)) break;
2920   }
2921 
2922 
2923   int len = cp - cmdline;
2924   char* rval = (char*)malloc(len + 1);
2925 
2926   cp = cmdline;
2927   char* rp = rval;
2928 
2929   while (len--) *rp++ = *cp++;
2930 
2931   *rp = 0;
2932   return rval;
2933 }
2934 
2935 /**
2936  * Extracts the executable or script name from the first string in cmdline.
2937  *
2938  * If the name contains blanks then it must be quoted with double quotes,
2939  * otherwise quotes are optional.  If the name contains blanks then it
2940  * will be converted to a short name.
2941  *
2942  * The optional quotes will be removed.  The result is copied to a malloc'ed
2943  * buffer and returned through the pexe argument.  The pargs parameter is set
2944  * to the address of the character in cmdline located after the name.
2945  *
2946  * The malloc'ed buffer returned in *pexe must be freed by the caller.
2947  */
GetApplicationName(const char * cmdline,char ** pexe,const char ** pargs)2948 bool GetApplicationName(const char* cmdline, char** pexe, const char** pargs)
2949 {
2950   const char* pExeStart = NULL; /* Start of executable name in cmdline */
2951   const char* pExeEnd = NULL; /* Character after executable name (separator) */
2952 
2953   const char* pBasename = NULL;  /* Character after last path separator */
2954   const char* pExtension = NULL; /* Period at start of extension */
2955 
2956   const char* current = cmdline;
2957 
2958   bool bQuoted = false;
2959 
2960   /* Skip initial whitespace */
2961 
2962   while (*current == ' ' || *current == '\t') { current++; }
2963 
2964   /* Calculate start of name and determine if quoted */
2965 
2966   if (*current == '"') {
2967     pExeStart = ++current;
2968     bQuoted = true;
2969   } else {
2970     pExeStart = current;
2971     bQuoted = false;
2972   }
2973 
2974   *pargs = NULL;
2975   *pexe = NULL;
2976 
2977   /*
2978    * Scan command line looking for path separators (/ and \\) and the
2979    * terminator, either a quote or a blank.  The location of the
2980    * extension is also noted.
2981    */
2982   for (; *current != '\0'; current++) {
2983     if (*current == '.') {
2984       pExtension = current;
2985     } else if (IsPathSeparator(*current) && current[1] != '\0') {
2986       pBasename = &current[1];
2987       pExtension = NULL;
2988     }
2989 
2990     /*
2991      * Check for terminator, either quote or blank
2992      */
2993     if (bQuoted) {
2994       if (*current != '"') { continue; }
2995     } else {
2996       if (*current != ' ') { continue; }
2997     }
2998 
2999     /*
3000      * Hit terminator, remember end of name (address of terminator) and start of
3001      * arguments
3002      */
3003     pExeEnd = current;
3004 
3005     if (bQuoted && *current == '"') {
3006       *pargs = &current[1];
3007     } else {
3008       *pargs = current;
3009     }
3010 
3011     break;
3012   }
3013 
3014   if (pBasename == NULL) { pBasename = pExeStart; }
3015 
3016   if (pExeEnd == NULL) { pExeEnd = current; }
3017 
3018   if (*pargs == NULL) { *pargs = current; }
3019 
3020   bool bHasPathSeparators = pExeStart != pBasename;
3021 
3022   /*
3023    * We have pointers to all the useful parts of the name
3024    * Default extensions in the order cmd.exe uses to search
3025    */
3026   static const char ExtensionList[][5] = {".com", ".exe", ".bat", ".cmd"};
3027   DWORD dwBasePathLength = pExeEnd - pExeStart;
3028 
3029   DWORD dwAltNameLength = 0;
3030   char* pPathname = (char*)alloca(MAX_PATHLENGTH + 1);
3031   char* pAltPathname = (char*)alloca(MAX_PATHLENGTH + 1);
3032 
3033   pPathname[MAX_PATHLENGTH] = '\0';
3034   pAltPathname[MAX_PATHLENGTH] = '\0';
3035 
3036   memcpy(pPathname, pExeStart, dwBasePathLength);
3037   pPathname[dwBasePathLength] = '\0';
3038 
3039   if (pExtension == NULL) {
3040     /*
3041      * Try appending extensions
3042      */
3043     for (int index = 0;
3044          index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0]));
3045          index++) {
3046       if (!bHasPathSeparators) {
3047         /*
3048          * There are no path separators, search in the standard locations
3049          */
3050         dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index],
3051                                      MAX_PATHLENGTH, pAltPathname, NULL);
3052         if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
3053           memcpy(pPathname, pAltPathname, dwAltNameLength);
3054           pPathname[dwAltNameLength] = '\0';
3055           break;
3056         }
3057       } else {
3058         bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index],
3059                  MAX_PATHLENGTH - dwBasePathLength);
3060         if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) { break; }
3061         pPathname[dwBasePathLength] = '\0';
3062       }
3063     }
3064   } else if (!bHasPathSeparators) {
3065     /*
3066      * There are no path separators, search in the standard locations
3067      */
3068     dwAltNameLength =
3069         SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
3070     if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
3071       memcpy(pPathname, pAltPathname, dwAltNameLength);
3072       pPathname[dwAltNameLength] = '\0';
3073     }
3074   }
3075 
3076   if (strchr(pPathname, ' ') != NULL) {
3077     dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
3078 
3079     if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
3080       *pexe = (char*)malloc(dwAltNameLength + 1);
3081       if (*pexe == NULL) { return false; }
3082       memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
3083     }
3084   }
3085 
3086   if (*pexe == NULL) {
3087     DWORD dwPathnameLength = strlen(pPathname);
3088     *pexe = (char*)malloc(dwPathnameLength + 1);
3089     if (*pexe == NULL) { return false; }
3090     memcpy(*pexe, pPathname, dwPathnameLength + 1);
3091   }
3092 
3093   return true;
3094 }
3095 
3096 /**
3097  * Create the process with WCHAR API
3098  */
CreateChildProcessW(const char * comspec,const char * cmdLine,PROCESS_INFORMATION * hProcInfo,HANDLE in,HANDLE out,HANDLE err)3099 static BOOL CreateChildProcessW(const char* comspec,
3100                                 const char* cmdLine,
3101                                 PROCESS_INFORMATION* hProcInfo,
3102                                 HANDLE in,
3103                                 HANDLE out,
3104                                 HANDLE err)
3105 {
3106   STARTUPINFOW siStartInfo;
3107   BOOL bFuncRetn = FALSE;
3108   POOLMEM *cmdLine_wchar, *comspec_wchar;
3109 
3110   /*
3111    * Setup members of the STARTUPINFO structure.
3112    */
3113   ZeroMemory(&siStartInfo, sizeof(siStartInfo));
3114   siStartInfo.cb = sizeof(siStartInfo);
3115 
3116   /*
3117    * Setup new process to use supplied handles for stdin,stdout,stderr
3118    */
3119   siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
3120   siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
3121 
3122   siStartInfo.hStdInput = in;
3123   siStartInfo.hStdOutput = out;
3124   siStartInfo.hStdError = err;
3125 
3126   /*
3127    * Convert argument to WCHAR
3128    */
3129   cmdLine_wchar = GetPoolMemory(PM_FNAME);
3130   comspec_wchar = GetPoolMemory(PM_FNAME);
3131 
3132   UTF8_2_wchar(cmdLine_wchar, cmdLine);
3133   UTF8_2_wchar(comspec_wchar, comspec);
3134 
3135   /*
3136    * Create the child process.
3137    */
3138   Dmsg2(debuglevel, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar,
3139         cmdLine_wchar);
3140 
3141   /*
3142    * Try to execute program
3143    */
3144   bFuncRetn = p_CreateProcessW((wchar_t*)comspec_wchar,
3145                                (wchar_t*)cmdLine_wchar, /* Command line */
3146                                NULL, /* Process security attributes */
3147                                NULL, /* Primary thread security attributes */
3148                                TRUE, /* Handles are inherited */
3149                                0,    /* Creation flags */
3150                                NULL, /* Use parent's environment */
3151                                NULL, /* Use parent's current directory */
3152                                &siStartInfo, /* STARTUPINFO pointer */
3153                                hProcInfo);   /* Receives PROCESS_INFORMATION */
3154   FreePoolMemory(cmdLine_wchar);
3155   FreePoolMemory(comspec_wchar);
3156 
3157   return bFuncRetn;
3158 }
3159 
3160 
3161 /**
3162  * Create the process with ANSI API
3163  */
CreateChildProcessA(const char * comspec,char * cmdLine,PROCESS_INFORMATION * hProcInfo,HANDLE in,HANDLE out,HANDLE err)3164 static BOOL CreateChildProcessA(const char* comspec,
3165                                 char* cmdLine,
3166                                 PROCESS_INFORMATION* hProcInfo,
3167                                 HANDLE in,
3168                                 HANDLE out,
3169                                 HANDLE err)
3170 {
3171   STARTUPINFOA siStartInfo;
3172   BOOL bFuncRetn = FALSE;
3173 
3174   /*
3175    * Set up members of the STARTUPINFO structure.
3176    */
3177   ZeroMemory(&siStartInfo, sizeof(siStartInfo));
3178   siStartInfo.cb = sizeof(siStartInfo);
3179 
3180   /*
3181    * setup new process to use supplied handles for stdin,stdout,stderr
3182    */
3183   siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
3184   siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
3185 
3186   siStartInfo.hStdInput = in;
3187   siStartInfo.hStdOutput = out;
3188   siStartInfo.hStdError = err;
3189 
3190   /*
3191    * Create the child process.
3192    */
3193   Dmsg2(debuglevel, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
3194 
3195   /*
3196    * Try to execute program
3197    */
3198   bFuncRetn = p_CreateProcessA(comspec, cmdLine, /* Command line */
3199                                NULL, /* Process security attributes */
3200                                NULL, /* Primary thread security attributes */
3201                                TRUE, /* Handles are inherited */
3202                                0,    /* Creation flags */
3203                                NULL, /* Use parent's environment */
3204                                NULL, /* Use parent's current directory */
3205                                &siStartInfo, /* STARTUPINFO pointer */
3206                                hProcInfo);   /* Receives PROCESS_INFORMATION */
3207   return bFuncRetn;
3208 }
3209 
3210 /**
3211  * OK, so it would seem CreateProcess only handles true executables:
3212  * .com or .exe files.  So grab $COMSPEC value and pass command line to it.
3213  */
CreateChildProcess(const char * cmdline,HANDLE in,HANDLE out,HANDLE err)3214 HANDLE CreateChildProcess(const char* cmdline,
3215                           HANDLE in,
3216                           HANDLE out,
3217                           HANDLE err)
3218 {
3219   static const char* comspec = NULL;
3220   PROCESS_INFORMATION piProcInfo;
3221   BOOL bFuncRetn = FALSE;
3222 
3223   if (!p_CreateProcessA || !p_CreateProcessW) return INVALID_HANDLE_VALUE;
3224 
3225   if (comspec == NULL) comspec = getenv("COMSPEC");
3226   if (comspec == NULL)  // should never happen
3227     return INVALID_HANDLE_VALUE;
3228 
3229   /*
3230    * Set up members of the PROCESS_INFORMATION structure.
3231    */
3232   ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
3233 
3234   /*
3235    * if supplied handles are not used the send a copy of our STD_HANDLE as
3236    * appropriate.
3237    */
3238   if (in == INVALID_HANDLE_VALUE) in = GetStdHandle(STD_INPUT_HANDLE);
3239 
3240   if (out == INVALID_HANDLE_VALUE) out = GetStdHandle(STD_OUTPUT_HANDLE);
3241 
3242   if (err == INVALID_HANDLE_VALUE) err = GetStdHandle(STD_ERROR_HANDLE);
3243 
3244   char* exeFile;
3245   const char* argStart;
3246 
3247   if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
3248     return INVALID_HANDLE_VALUE;
3249   }
3250 
3251   PoolMem cmdLine(PM_FNAME);
3252   Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
3253 
3254   free(exeFile);
3255 
3256   /*
3257    * New function disabled
3258    */
3259   if (p_CreateProcessW && p_MultiByteToWideChar) {
3260     bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo, in,
3261                                     out, err);
3262   } else {
3263     bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo, in,
3264                                     out, err);
3265   }
3266 
3267   if (bFuncRetn == 0) {
3268     ErrorExit("CreateProcess failed\n");
3269     const char* err = errorString();
3270 
3271     Dmsg3(debuglevel, "CreateProcess(%s, %s, ...)=%s\n", comspec,
3272           cmdLine.c_str(), err);
3273     LocalFree((void*)err);
3274 
3275     return INVALID_HANDLE_VALUE;
3276   }
3277 
3278   /*
3279    * we don't need a handle on the process primary thread so we close this now.
3280    */
3281   CloseHandle(piProcInfo.hThread);
3282 
3283   return piProcInfo.hProcess;
3284 }
3285 
ErrorExit(LPCSTR lpszMessage)3286 void ErrorExit(LPCSTR lpszMessage) { Dmsg1(0, "%s", lpszMessage); }
3287 
CloseHandleIfValid(HANDLE handle)3288 static void CloseHandleIfValid(HANDLE handle)
3289 {
3290   if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); }
3291 }
3292 
OpenBpipe(char * prog,int wait,const char * mode,bool dup_stderr)3293 Bpipe* OpenBpipe(char* prog, int wait, const char* mode, bool dup_stderr)
3294 {
3295   int mode_read, mode_write;
3296   SECURITY_ATTRIBUTES saAttr;
3297   BOOL fSuccess;
3298   Bpipe* bpipe;
3299   HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup, hChildStdoutRd,
3300       hChildStdoutWr, hChildStdoutRdDup;
3301 
3302   hChildStdinRd = INVALID_HANDLE_VALUE;
3303   hChildStdinWr = INVALID_HANDLE_VALUE;
3304   hChildStdinWrDup = INVALID_HANDLE_VALUE;
3305   hChildStdoutRd = INVALID_HANDLE_VALUE;
3306   hChildStdoutWr = INVALID_HANDLE_VALUE;
3307   hChildStdoutRdDup = INVALID_HANDLE_VALUE;
3308 
3309   bpipe = (Bpipe*)malloc(sizeof(Bpipe));
3310   memset((void*)bpipe, 0, sizeof(Bpipe));
3311   mode_read = (mode[0] == 'r');
3312   mode_write = (mode[0] == 'w' || mode[1] == 'w');
3313 
3314   /*
3315    * Set the bInheritHandle flag so pipe handles are inherited.
3316    */
3317   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3318   saAttr.bInheritHandle = TRUE;
3319   saAttr.lpSecurityDescriptor = NULL;
3320 
3321   if (mode_read) {
3322     /*
3323      * Create a pipe for the child process's STDOUT.
3324      */
3325     if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
3326       ErrorExit("Stdout pipe creation failed\n");
3327       goto cleanup;
3328     }
3329 
3330     /*
3331      * Create noninheritable read handle and close the inheritable read handle.
3332      */
3333     fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
3334                                GetCurrentProcess(), &hChildStdoutRdDup, 0,
3335                                FALSE, DUPLICATE_SAME_ACCESS);
3336     if (!fSuccess) {
3337       ErrorExit("DuplicateHandle failed");
3338       goto cleanup;
3339     }
3340 
3341     CloseHandle(hChildStdoutRd);
3342     hChildStdoutRd = INVALID_HANDLE_VALUE;
3343   }
3344 
3345   if (mode_write) {
3346     /*
3347      * Create a pipe for the child process's STDIN.
3348      */
3349     if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
3350       ErrorExit("Stdin pipe creation failed\n");
3351       goto cleanup;
3352     }
3353 
3354     /*
3355      * Duplicate the write handle to the pipe so it is not inherited.
3356      */
3357     fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
3358                                GetCurrentProcess(), &hChildStdinWrDup, 0,
3359                                FALSE,  // not inherited
3360                                DUPLICATE_SAME_ACCESS);
3361     if (!fSuccess) {
3362       ErrorExit("DuplicateHandle failed");
3363       goto cleanup;
3364     }
3365 
3366     CloseHandle(hChildStdinWr);
3367     hChildStdinWr = INVALID_HANDLE_VALUE;
3368   }
3369 
3370   /*
3371    * Spawn program with redirected handles as appropriate
3372    */
3373   bpipe->worker_pid =
3374       (pid_t)CreateChildProcess(prog,            /* Commandline */
3375                                 hChildStdinRd,   /* stdin HANDLE */
3376                                 hChildStdoutWr,  /* stdout HANDLE */
3377                                 hChildStdoutWr); /* stderr HANDLE */
3378 
3379   if ((HANDLE)bpipe->worker_pid == INVALID_HANDLE_VALUE) goto cleanup;
3380 
3381   bpipe->wait = wait;
3382   bpipe->worker_stime = time(NULL);
3383 
3384   if (mode_read) {
3385     /*
3386      * Close our write side so when process terminates we can detect eof.
3387      */
3388     CloseHandle(hChildStdoutWr);
3389     hChildStdoutWr = INVALID_HANDLE_VALUE;
3390 
3391     int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
3392     if (rfd >= 0) { bpipe->rfd = _fdopen(rfd, "rb"); }
3393   }
3394 
3395   if (mode_write) {
3396     /*
3397      * Close our read side so to not interfere with child's copy.
3398      */
3399     CloseHandle(hChildStdinRd);
3400     hChildStdinRd = INVALID_HANDLE_VALUE;
3401 
3402     int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
3403     if (wfd >= 0) { bpipe->wfd = _fdopen(wfd, "wb"); }
3404   }
3405 
3406   if (wait > 0) {
3407     bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
3408   }
3409 
3410   return bpipe;
3411 
3412 cleanup:
3413 
3414   CloseHandleIfValid(hChildStdoutRd);
3415   CloseHandleIfValid(hChildStdoutWr);
3416   CloseHandleIfValid(hChildStdoutRdDup);
3417   CloseHandleIfValid(hChildStdinRd);
3418   CloseHandleIfValid(hChildStdinWr);
3419   CloseHandleIfValid(hChildStdinWrDup);
3420 
3421   free((void*)bpipe);
3422   errno = b_errno_win32; /* Do GetLastError() for error code */
3423   return NULL;
3424 }
3425 
kill(int pid,int signal)3426 int kill(int pid, int signal)
3427 {
3428   int rval = 0;
3429   if (!TerminateProcess((HANDLE)(UINT_PTR)pid, (UINT)signal)) {
3430     rval = -1;
3431     errno = b_errno_win32;
3432   }
3433   CloseHandle((HANDLE)(UINT_PTR)pid);
3434   return rval;
3435 }
3436 
CloseBpipe(Bpipe * bpipe)3437 int CloseBpipe(Bpipe* bpipe)
3438 {
3439   int rval = 0;
3440   int32_t remaining_wait = bpipe->wait;
3441 
3442   /*
3443    * Close pipes
3444    */
3445   if (bpipe->rfd) {
3446     fclose(bpipe->rfd);
3447     bpipe->rfd = NULL;
3448   }
3449   if (bpipe->wfd) {
3450     fclose(bpipe->wfd);
3451     bpipe->wfd = NULL;
3452   }
3453 
3454   if (remaining_wait == 0) { /* Wait indefinitely */
3455     remaining_wait = INT32_MAX;
3456   }
3457   for (;;) {
3458     DWORD exitCode;
3459     if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
3460       const char* err = errorString();
3461 
3462       rval = b_errno_win32;
3463       Dmsg1(debuglevel, "GetExitCode error %s\n", err);
3464       LocalFree((void*)err);
3465       break;
3466     }
3467     if (exitCode == STILL_ACTIVE) {
3468       if (remaining_wait <= 0) {
3469         rval = ETIME; /* Timed out */
3470         break;
3471       }
3472       Bmicrosleep(1, 0); /* Wait one second */
3473       remaining_wait--;
3474     } else if (exitCode != 0) {
3475       /*
3476        * Truncate exit code as it doesn't seem to be correct
3477        */
3478       rval = (exitCode & 0xFF) | b_errno_exit;
3479       break;
3480     } else {
3481       break; /* Shouldn't get here */
3482     }
3483   }
3484 
3485   if (bpipe->timer_id) { StopChildTimer(bpipe->timer_id); }
3486 
3487   if (bpipe->rfd) { fclose(bpipe->rfd); }
3488 
3489   if (bpipe->wfd) { fclose(bpipe->wfd); }
3490 
3491   free((void*)bpipe);
3492   return rval;
3493 }
3494 
CloseWpipe(Bpipe * bpipe)3495 int CloseWpipe(Bpipe* bpipe)
3496 {
3497   int result = 1;
3498 
3499   if (bpipe->wfd) {
3500     fflush(bpipe->wfd);
3501     if (fclose(bpipe->wfd) != 0) { result = 0; }
3502     bpipe->wfd = NULL;
3503   }
3504   return result;
3505 }
3506 
3507 /**
3508  * Syslog function, added by Nicolas Boichat
3509  */
openlog(const char * ident,int option,int facility)3510 extern "C" void openlog(const char* ident, int option, int facility) {}
3511 
3512 /**
3513  * Log an error message
3514  */
LogErrorMsg(const char * message)3515 void LogErrorMsg(const char* message)
3516 {
3517   HANDLE eventHandler;
3518   const char* strings[2];
3519 
3520   /*
3521    * Use the OS event logging to log the error
3522    */
3523   strings[0] = _("\n\nBareos ERROR: ");
3524   strings[1] = message;
3525 
3526   eventHandler = RegisterEventSource(NULL, "Bareos");
3527   if (eventHandler) {
3528     ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE, 0, /* Category */
3529                 0,                                    /* ID */
3530                 NULL,                                 /* SID */
3531                 2,                                    /* Number of strings */
3532                 0,                                    /* Raw data size */
3533                 (const char**)strings,                /* Error strings */
3534                 NULL);                                /* Raw data */
3535     DeregisterEventSource(eventHandler);
3536   }
3537 }
3538 
3539 /**
3540  * Don't allow OS to suspend while backup running
3541  * Note, the OS automatically tracks these for each thread
3542  */
PreventOsSuspensions()3543 void PreventOsSuspensions()
3544 {
3545   SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
3546 }
3547 
AllowOsSuspensions()3548 void AllowOsSuspensions() { SetThreadExecutionState(ES_CONTINUOUS); }
3549