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