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