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