1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 // -*- Mode: C++ -*-
20 // compat.cpp -- compatibilty layer to make bacula-fd run
21 // natively under windows
22 //
23 // Copyright transferred from Raider Solutions, Inc to
24 // Kern Sibbald and John Walker by express permission.
25 //
26 // Author : Christopher S. Hull
27 // Created On : Sat Jan 31 15:55:00 2004
28
29 #include "bacula.h"
30 #include "compat.h"
31 #include "jcr.h"
32 #include "findlib/find.h"
33
34 /* Note, if you want to see what Windows variables and structures
35 * are defined, bacula.h includes <windows.h>, which is found in:
36 *
37 * cross-tools/mingw32/mingw32/include
38 * or
39 * cross-tools/mingw-w64/x86_64-pc-mingw32/include
40 *
41 * depending on whether we are building the 32 bit version or
42 * the 64 bit version.
43 */
44
45 static const int dbglvl = 500;
46
47 #define b_errno_win32 (1<<29)
48
49 #define MAX_PATHLENGTH 1024
50
51 /**
52 UTF-8 to UCS2 path conversion is expensive,
53 so we cache the conversion. During backup the
54 conversion is called 3 times (lstat, attribs, open),
55 by using the cache this is reduced to 1 time
56 */
57 static POOLMEM *g_pWin32ConvUTF8Cache = NULL;
58 static POOLMEM *g_pWin32ConvUCS2Cache = NULL;
59 static DWORD g_dwWin32ConvUTF8strlen = 0;
60 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
61
62 /* Forward referenced functions */
63 static const char *errorString(void);
64
65 /* The following functions are available only in the FileDaemon with VSS
66 * These functions uses the VSSObject to resolve a Path to a Snapshot Path,
67 * the VSSObject is available "per job", and some jobs such as Restore or Verify
68 * may not have a VSSObject.
69 */
70
default_VSSPathConverter()71 static BOOL default_VSSPathConverter()
72 {
73 return false;
74 }
75
76 static t_pVSSPathConvert g_pVSSPathConvert = NULL;
77 static t_pVSSPathConvertW g_pVSSPathConvertW = NULL;
78 static t_pVSSPathConverter g_pVSSPathConverter = default_VSSPathConverter; /* To know if we can use the VSSPath functions */
79
80
SetVSSPathConvert(t_pVSSPathConverter pPathConverter,t_pVSSPathConvert pPathConvert,t_pVSSPathConvertW pPathConvertW)81 void SetVSSPathConvert(t_pVSSPathConverter pPathConverter, t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
82 {
83 g_pVSSPathConvert = pPathConvert;
84 g_pVSSPathConvertW = pPathConvertW;
85 g_pVSSPathConverter = pPathConverter;
86 }
87
Win32ConvInitCache()88 static void Win32ConvInitCache()
89 {
90 if (g_pWin32ConvUTF8Cache) {
91 return;
92 }
93 g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
94 g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
95 }
96
Win32ConvCleanupCache()97 void Win32ConvCleanupCache()
98 {
99 P(Win32Convmutex);
100 if (g_pWin32ConvUTF8Cache) {
101 free_pool_memory(g_pWin32ConvUTF8Cache);
102 g_pWin32ConvUTF8Cache = NULL;
103 }
104
105 if (g_pWin32ConvUCS2Cache) {
106 free_pool_memory(g_pWin32ConvUCS2Cache);
107 g_pWin32ConvUCS2Cache = NULL;
108 }
109
110 g_dwWin32ConvUTF8strlen = 0;
111 V(Win32Convmutex);
112 }
113
114
115 /* to allow the usage of the original version in this file here */
116 #undef fputs
117
118
119 //#define USE_WIN32_COMPAT_IO 1
120 #define USE_WIN32_32KPATHCONVERSION 1
121
122 extern DWORD g_platform_id;
123 extern DWORD g_MinorVersion;
124
125 /* From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 */
126 #ifdef HAVE_MINGW
127 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
128 #else
129 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
130 #endif
131
132 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
133
134 /**
135 * Convert POSIX like UTF-8 path into Windows UTF-8 path (replace '/' with '\\')
136 * and add the \\?\ prefix or a "VSS" prefix if VSS snapshot exists
137 * don't convert to UTF-16-LE
138 */
conv_unix_to_vss_win32_path(const char * name,char * win32_name,DWORD dwSize)139 static void conv_unix_to_vss_win32_path(const char *name, char *win32_name, DWORD dwSize)
140 {
141 const char *fname = name;
142 char *tname = win32_name;
143
144 Dmsg0(dbglvl, "Enter convert_unix_to_win32_path\n");
145
146 if (IsPathSeparator(name[0]) &&
147 IsPathSeparator(name[1]) &&
148 name[2] == '.' &&
149 IsPathSeparator(name[3])) {
150
151 *win32_name++ = '\\';
152 *win32_name++ = '\\';
153 *win32_name++ = '.';
154 *win32_name++ = '\\';
155
156 name += 4;
157 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS && !g_pVSSPathConverter()) {
158 /* allow path to be 32767 bytes */
159 *win32_name++ = '\\';
160 *win32_name++ = '\\';
161 *win32_name++ = '?';
162 *win32_name++ = '\\';
163 }
164
165 while (*name) {
166 /** Check for Unix separator and convert to Win32 */
167 if (name[0] == '/' && name[1] == '/') { /* double slash? */
168 name++; /* yes, skip first one */
169 }
170 if (*name == '/') {
171 *win32_name++ = '\\'; /* convert char */
172 /* If Win32 separator that is "quoted", remove quote */
173 } else if (*name == '\\' && name[1] == '\\') {
174 *win32_name++ = '\\';
175 name++; /* skip first \ */
176 } else {
177 *win32_name++ = *name; /* copy character */
178 }
179 name++;
180 }
181 /** Strip any trailing slash, if we stored something
182 * but leave "c:\" with backslash (root directory case
183 */
184 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
185 win32_name[-1] = 0;
186 } else {
187 *win32_name = 0;
188 }
189
190 Dmsg1(dbglvl, "Leave cvt_u_to_win32_path path=%s\n", tname);
191 }
192
193 /** Conversion of a Unix filename to a Win32 filename */
unix_name_to_win32(POOLMEM ** win32_name,const char * name)194 void unix_name_to_win32(POOLMEM **win32_name, const char *name)
195 {
196 /* One extra byte should suffice, but we double it */
197 /* add MAX_PATH bytes for VSS shadow copy name */
198 DWORD dwSize = 2*strlen(name)+MAX_PATH;
199 *win32_name = check_pool_memory_size(*win32_name, dwSize);
200 conv_unix_to_vss_win32_path(name, *win32_name, dwSize);
201 }
202
203
204 /**
205 * This function normalize a wchar Windows path:
206 * - make the path absolute if relative
207 * - prefix the path with r"\\?\" but keep r"\\.\" prefix untouched
208 * - convert any '/' into '\\'
209 * A device path starting with \\.\ is left untouched and pBIsRawPath is set
210 * Prefixing the path with \\?\ allow to use 32K characters long paths.
211 *
212 * samples:
213 * c:\path\file -> \\?\c:\path\file
214 * path\file -> \\?\c:\path\file
215 *
216 * created 02/27/2006 Thorsten Engel
217 *
218 * This is a "universal" function that does more than what Bacula need,
219 * like handling relative path using the CWD.
220 */
221 static void
norm_wchar_win32_path(POOLMEM ** pszUCSPath,BOOL * pBIsRawPath)222 norm_wchar_win32_path(POOLMEM **pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
223 {
224
225 Dmsg1(dbglvl, "Enter norm_wchar_win32_path %ls\n", *pszUCSPath);
226 if (pBIsRawPath) {
227 *pBIsRawPath = FALSE; /* Initialize, set later */
228 }
229
230 wchar_t *name = (wchar_t *)*pszUCSPath;
231 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
232 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
233 DWORD dwCurDirPathSize = 0;
234
235 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
236 DWORD dwBufCharsNeeded = (wcslen(name)+7);
237 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
238
239 /* it is important to make absolute paths, so we add drive and
240 * current path if necessary
241 */
242
243 BOOL bAddDrive = TRUE;
244 BOOL bAddCurrentPath = TRUE;
245 BOOL bAddPrefix = TRUE;
246
247 if (IsWPathSeparator(name[0]) && IsWPathSeparator(name[1])
248 && (name[2] == L'?' || name[2] == L'.') && IsWPathSeparator(name[3])) {
249 /* already starting with \\?\ or \\.\ */
250 bAddDrive = FALSE;
251 bAddCurrentPath = FALSE;
252 bAddPrefix = FALSE;
253 if (pBIsRawPath && name[2] == L'.') {
254 /* the Win32 Device NameSpace like '\\.\PhysicalDrive0' */
255 *pBIsRawPath = TRUE;
256 }
257 } else if (iswalpha(name[0]) && name[1] == L':' && IsWPathSeparator(name[2])) {
258 /* path begins with a drive letter, it is absolute */
259 bAddDrive = FALSE;
260 bAddCurrentPath = FALSE;
261 } else if (IsWPathSeparator(name[0])) {
262 /* path is absolute */
263 bAddCurrentPath = FALSE;
264 }
265
266 /* is path relative to itself?, if yes, skip ./ */
267 if (name[0] == L'.' && IsWPathSeparator(name[1])) {
268 name += 2;
269 }
270
271 /* get current path if needed */
272 if (bAddDrive || bAddCurrentPath) {
273 if (p_GetCurrentDirectoryW &&
274 (dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL)) > 0) {
275 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
276 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
277 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
278 } else {
279 /* we have no info for doing so */
280 bAddDrive = FALSE;
281 bAddCurrentPath = FALSE;
282 bAddPrefix = FALSE;
283 }
284 }
285
286 int nParseOffset = 0;
287
288 /* add 4 bytes header */
289 if (bAddPrefix) {
290 nParseOffset = 4;
291 wcscpy(pwszBuf, L"\\\\?\\");
292 }
293
294 /* add drive if needed */
295 if (bAddDrive && !bAddCurrentPath) {
296 wchar_t szDrive[3];
297
298 if (IsWPathSeparator(pwszCurDirBuf[0]) &&
299 IsWPathSeparator(pwszCurDirBuf[1]) &&
300 pwszCurDirBuf[2] == L'?' &&
301 IsWPathSeparator(pwszCurDirBuf[3])) {
302 /* copy drive character */
303 szDrive[0] = pwszCurDirBuf[4];
304 } else {
305 /* copy drive character */
306 szDrive[0] = pwszCurDirBuf[0];
307 }
308
309 szDrive[1] = L':';
310 szDrive[2] = L'\0';
311
312 wcscat(pwszBuf, szDrive);
313 nParseOffset +=2;
314 }
315
316 /* add path if needed */
317 if (bAddCurrentPath) {
318 /* the 1 add. character is for the eventually added backslash */
319 dwBufCharsNeeded += dwCurDirPathSize+1;
320 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
321 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
322
323 if (IsWPathSeparator(pwszCurDirBuf[0]) &&
324 IsWPathSeparator(pwszCurDirBuf[1]) &&
325 pwszCurDirBuf[2] == L'?' &&
326 IsWPathSeparator(pwszCurDirBuf[3])) {
327 /* copy complete string */
328 wcscpy(pwszBuf, pwszCurDirBuf);
329 } else {
330 /* append path */
331 wcscat(pwszBuf, pwszCurDirBuf);
332 }
333
334 nParseOffset = wcslen((LPCWSTR) pwszBuf);
335
336 /* check if path ends with backslash, if not, add one */
337 if (!IsWPathSeparator(pwszBuf[nParseOffset-1])) {
338 wcscat(pwszBuf, L"\\");
339 nParseOffset++;
340 }
341 }
342
343 wchar_t *win32_name = &pwszBuf[nParseOffset];
344 wchar_t *name_start = name;
345
346 while (*name) {
347 /* Check for Unix separator and convert to Win32, eliminating
348 * duplicate separators.
349 */
350 if (IsWPathSeparator(*name)) {
351 *win32_name++ = L'\\'; /* convert char */
352
353 /* Eliminate consecutive slashes, but not at the start so that
354 * \\.\ still works.
355 */
356 if (name_start != name && IsWPathSeparator(name[1])) {
357 name++;
358 }
359 } else {
360 *win32_name++ = *name; /* copy character */
361 }
362 name++;
363 }
364
365 /* null terminate string */
366 *win32_name = L'\0';
367
368 free_pool_memory(*pszUCSPath);
369 free_pool_memory((POOLMEM *)pwszCurDirBuf);
370
371 Dmsg1(dbglvl, "Leave norm_wchar_win32_path=%ls\n", pwszBuf);
372 *pszUCSPath = (POOLMEM *)pwszBuf;
373 }
374
375 /*
376 * Both functions wchar_path_2_wutf8() and wutf8_path_2_wchar() convert file path
377 * between Windows UTF16-LE and Bacula UTF8.
378 * Both function use WideCharToMultiByte() and WideCharToMultiByte() but
379 * support invalid UTF16 input. Invalid UTF16 char are encoded into valid
380 * UTF8 string using the WUTF8_ESCAPE char ('*') that is not allowed into
381 * Windows path. The valid UTF8 path is decoded back into the same invalid
382 * UTF16 path. WUTF8_ESCAPE are also escaped just in case.
383 * The encoded path is valid UTF8 and it can be manipulate by Bacula it as any
384 * other path.
385 * This trick allows to restore these files on most POSIX and Windows systems.
386 * The main improvement is that the FileDaemon was not able to backup these
387 * files with invalid char because the code that navigate inside the directory
388 * tree use UTF8 and the conversion back to UTF16 (to open the file) was
389 * generating a path different of the original one that was not existing.
390 *
391 * Use these functions only for windows path. Don't use them for other strings
392 * that could hold WUTF8_ESCAPE chars.
393 *
394 * return the length of the output string including the null terminator
395 * or 0 for any error
396 */
397 static int WUTF8_ESCAPE='*';
398 static wchar_t WUTF8_WCHAR_ESCAPE=L'*';
399
400 #ifndef WC_ERR_INVALID_CHARS
401 #define WC_ERR_INVALID_CHARS 0x00000080 // Windows constant
402 #endif
403
wchar_path_2_wutf8(POOLMEM ** pszUTF,const wchar_t * pszUCS)404 int wchar_path_2_wutf8(POOLMEM **pszUTF, const wchar_t *pszUCS)
405 {
406 if (!p_WideCharToMultiByte) {
407 return 0;
408 }
409 // WC_ERR_INVALID_CHARS=0x00000080
410 // ERROR_NO_UNICODE_TRANSLATION=1113
411 int buflen = p_WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,pszUCS,-1,NULL,0,NULL,NULL);
412 if (buflen > 0) {
413 /* no invalid char: buflen is always > 0 because it counts the final '\0' */
414 *pszUTF = check_pool_memory_size(*pszUTF, buflen);
415 buflen = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,*pszUTF,buflen,NULL,NULL);
416 if (strchr(*pszUTF, WUTF8_ESCAPE)==NULL) {
417 // No WUTF8_ESCAPE char to escape
418 return buflen;
419 }
420 // IF WE GO BELOW THIS LINE, this is because a Windows file name holds an invalid
421 // char or an unexpected char WUTF8_ESCAPE
422 } else {
423 buflen = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,NULL,0,NULL,NULL);
424 *pszUTF = check_pool_memory_size(*pszUTF, buflen);
425 buflen = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,*pszUTF,buflen,NULL,NULL);
426 }
427 // pszUCS is an invalid UTF-16-LE string,
428 // let WideCharToMultiByte() replace all invalid char with U+FFFD
429 // then count the number of U+FFFD (aka \xef\xbf\xbd in utf8)
430 for (char *p=*pszUTF; p!=NULL && *p!='\0'; p++) {
431 p=strstr(p, "\xef\xbf\xbd");
432 if (p == NULL) {
433 break;
434 }
435 buflen += 2; // enlarge the buffer to replace "\xef\xbf\xbd" with "*XXXXX"
436 }
437 // count the number of WUTF8_ESCAPE to escape them
438 for (char *p=*pszUTF; p!=NULL && *p!='\0'; p++) {
439 p=strchr(p, WUTF8_ESCAPE);
440 if (p == NULL) {
441 break;
442 }
443 buflen += 1; // enlarge the buffer to escape WUTF8_ESCAPE
444 }
445
446 // enlarge the buffer to hold the escape sequence
447 *pszUTF = check_pool_memory_size(*pszUTF, buflen);
448 // Decode the UTF16 char by char using section
449 // "2.2 Decoding UTF-16" at http://www.ietf.org/rfc/rfc2781.txt
450 const wchar_t *s = pszUCS;
451 char *d = *pszUTF;
452 while (*s != 0) {
453 if (*s == WUTF8_WCHAR_ESCAPE) {
454 *d++ = WUTF8_ESCAPE;
455 *d++ = WUTF8_ESCAPE;
456 buflen -= 2;
457 s++;
458 continue;
459 } else if (*s<0xd800 || *s>0xdfff) {
460 // single Unicode scalar value
461 int r = p_WideCharToMultiByte(CP_UTF8,0,s,1,d,buflen,NULL,NULL);
462 if (r == 0) {
463 Dmsg1(0, "Unexpected error for single unicode char wchar_off=%ld\n", s-pszUCS);
464 return 0; // Unexpected error
465 }
466 buflen -= r;
467 d += r;
468 s++;
469 continue;
470 } else if (0xd800<=*s && *s<=0xdbff && 0xdc00<=*(s+1) && *(s+1)<=0xdfff) {
471 // we have a surrogate of 2 valid 16bits chars
472 int r = p_WideCharToMultiByte(CP_UTF8,0,s,2,d,buflen,NULL,NULL);
473 if (r == 0) {
474 Dmsg1(0, "Unexpected error for surrogate of 2 valid 16bits char wchar_off=%ld\n", s-pszUCS);
475 }
476 buflen -= r;
477 d += r;
478 s += 2;
479 continue;
480 }
481 // Use WUTF8_ESCAPE char to escape the invalid char
482 int r = bsnprintf(d, buflen, "%c%04x", (int)WUTF8_ESCAPE, (int)*s);
483 if (r != 5) {
484 Dmsg2(0, "Unexpected error buffer too small 1 %d %s\n", r, d);
485 return 0; // Unexpected error, buffer too small !
486 }
487 buflen -= r;
488 d += r;
489 s++;
490 }
491 if (buflen <= 0) {
492 Dmsg0(0, "Unexpected error buffer too small 2\n");
493 return 0; // Unexpected error, buffer too small !
494 }
495 *d = '\0';
496 d++;
497 buflen--;
498 return d-*pszUTF;
499 }
500
501 /* Do the inverse of function wchar_path_2_wutf8()
502 * return the number of wchar_t written including the final '\0'
503 * or O if any error
504 */
wutf8_path_2_wchar(POOLMEM ** ppszUCS,const char * pszUTF)505 int wutf8_path_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
506 {
507 if (!p_MultiByteToWideChar) {
508 return 0;
509 }
510 DWORD cchSize = (strlen(pszUTF)+1);
511 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof(wchar_t));
512 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
513 ASSERT (nRet > 0);
514 if (strchr(pszUTF, WUTF8_WCHAR_ESCAPE) == NULL) {
515 /* their is no WUTF8_ESCAPE char in the string, process the all string at once */
516 /* strlen of UTF8+1 is enough */
517 return nRet;
518 }
519 // IF WE GO BELOW THIS LINE, this is because the original Windows file name
520 // was holding an invalid char or an unexpected char WUTF8_ESCAPE
521 wchar_t *s=(wchar_t *)*ppszUCS; // source
522 wchar_t *d=(wchar_t *)*ppszUCS; // destination is smaller than source
523 int n = 0;
524 while (*s != L'\0') {
525 if (*s == WUTF8_WCHAR_ESCAPE && *(s+1) == WUTF8_WCHAR_ESCAPE) {
526 // two WUTF8_WCHAR_ESCAPE in a row, copy one and drop the other
527 *d++ = *s++;
528 s++;
529 n++;
530 } else if (*s == WUTF8_WCHAR_ESCAPE) {
531 // replace the the encoded '*WXYZ' char into its code point '\0xWXYZ'
532 wchar_t w = 0;
533 int val;
534 s++;
535 for (int i = 0; i < 4; i++) {
536 if (*s == L'\0') {
537 Dmsg0(0, "unexpected WUTF8_WCHAR_ESCAPE at end of the string!\n");
538 return 0;
539 }
540 if (L'0' <= *s && *s <= L'9') {
541 val=*s-L'0';
542 } else if (L'a' <= *s && *s <= L'f') {
543 val=*s-L'a'+10;
544 } else if (L'A' <= *s && *s <= L'F') {
545 val=*s-L'A'+10;
546 } else {
547 Dmsg0(0, "a WUTF8_WCHAR_ESCAPE should be followed by 4 hexa digits!\n");
548 return 0;
549 }
550 w = (w << 4) | val;
551 s++;
552 }
553 *d++=w;
554 n++;
555 } else {
556 *d++ = *s++;
557 n++;
558 }
559 }
560 *d = L'\0';
561 n++;
562 return n;
563 }
564
565 /*
566 * Convert from WCHAR (UTF-16-LE) to UTF-8
567 * Don't use this function for path, use wchar_path_2_wutf8() instead
568 */
569 int
wchar_2_UTF8(POOLMEM ** pszUTF,const wchar_t * pszUCS)570 wchar_2_UTF8(POOLMEM **pszUTF, const wchar_t *pszUCS)
571 {
572 /**
573 * The return value is the number of bytes written to the buffer.
574 * The number includes the byte for the null terminator.
575 */
576
577 if (p_WideCharToMultiByte) {
578 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,NULL,0,NULL,NULL);
579 *pszUTF = check_pool_memory_size(*pszUTF, nRet);
580 return p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,*pszUTF,nRet,NULL,NULL);
581
582 }
583 return 0;
584 }
585
586 /*
587 * Convert from WCHAR (UTF-16-LE) to UTF-8
588 * Don't use this function for path, use wchar_path_2_wutf8() instead
589 */
590 int
wchar_2_UTF8(char * pszUTF,const wchar_t * pszUCS,int cchChar)591 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
592 {
593 /**
594 * The return value is the number of bytes written to the buffer.
595 * The number includes the byte for the null terminator.
596 */
597
598 if (p_WideCharToMultiByte) {
599 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
600 ASSERT (nRet > 0);
601 return nRet;
602 }
603 return 0;
604 }
605
606 /*
607 * Don't use this function for path, use wchar_path_2_wutf8() instead
608 */
609 int
UTF8_2_wchar(POOLMEM ** ppszUCS,const char * pszUTF)610 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
611 {
612 /* the return value is the number of wide characters written to the buffer. */
613 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
614
615 if (p_MultiByteToWideChar) {
616 /* strlen of UTF8 +1 is enough */
617 DWORD cchSize = (strlen(pszUTF)+1);
618 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
619
620 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
621 ASSERT (nRet > 0);
622 return nRet;
623 }
624 return 0;
625 }
626
627 #if 0
628 // not used anywhere
629 void
630 wchar_win32_path(const char *name, wchar_t *win32_name)
631 {
632 const char *fname = name;
633 while (*name) {
634 /* Check for Unix separator and convert to Win32 */
635 if (*name == '/') {
636 *win32_name++ = '\\'; /* convert char */
637 /* If Win32 separated that is "quoted", remove quote */
638 } else if (*name == '\\' && name[1] == '\\') {
639 *win32_name++ = '\\';
640 name++; /* skip first \ */
641 } else {
642 *win32_name++ = *name; /* copy character */
643 }
644 name++;
645 }
646 /* Strip any trailing slash, if we stored something */
647 if (*fname != 0 && win32_name[-1] == '\\') {
648 win32_name[-1] = 0;
649 } else {
650 *win32_name = 0;
651 }
652 }
653 #endif
654
655 /*
656 * Convert a WUTF8 path into a normalized wchar windows path
657 * Get the result from cache
658 * or
659 * call wutf8_path_2_wchar() and norm_wchar_win32_path()
660 * and save the result in cache
661 */
662 int
make_win32_path_UTF8_2_wchar(POOLMEM ** pszUCS,const char * pszUTF,BOOL * pBIsRawPath)663 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
664 {
665 P(Win32Convmutex);
666 /* if we find the utf8 string in cache, we use the cached wchar version. */
667 if (!g_pWin32ConvUTF8Cache) {
668 Win32ConvInitCache();
669 } else if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
670 /* Return cached value */
671 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
672 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
673 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
674 V(Win32Convmutex);
675 return nBufSize / sizeof (WCHAR);
676 }
677
678 /* convert from utf-8 to wchar */
679 int nRet = wutf8_path_2_wchar(pszUCS, pszUTF);
680
681 #ifdef USE_WIN32_32KPATHCONVERSION
682 /* add \\?\ to support 32K long filepaths */
683 norm_wchar_win32_path(pszUCS, pBIsRawPath);
684 #else
685 if (pBIsRawPath)
686 *pBIsRawPath = FALSE;
687 #endif
688
689 /* populate cache */
690 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
691 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
692
693 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
694 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
695 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
696 V(Win32Convmutex);
697
698 return nRet;
699 }
700
701 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
umask(int)702 int umask(int)
703 {
704 return 0;
705 }
706 #endif
707
708 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
709 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
710 #endif
711
dlopen(const char * file,int mode)712 void *dlopen(const char *file, int mode)
713 {
714 void *handle;
715
716 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
717 return handle;
718 }
719
dlsym(void * handle,const char * name)720 void *dlsym(void *handle, const char *name)
721 {
722 void *symaddr;
723 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
724 return symaddr;
725 }
726
dlclose(void * handle)727 int dlclose(void *handle)
728 {
729 if (handle && !FreeLibrary((HMODULE)handle)) {
730 errno = b_errno_win32;
731 return 1; /* failed */
732 }
733 return 0; /* OK */
734 }
735
dlerror(void)736 char *dlerror(void)
737 {
738 static char buf[200];
739 const char *err = errorString();
740 bstrncpy(buf, (char *)err, sizeof(buf));
741 LocalFree((void *)err);
742 return buf;
743 }
744
fcntl(int fd,int cmd)745 int fcntl(int fd, int cmd)
746 {
747 return 0;
748 }
749
chown(const char * k,uid_t,gid_t)750 int chown(const char *k, uid_t, gid_t)
751 {
752 return 0;
753 }
754
lchown(const char * k,uid_t,gid_t)755 int lchown(const char *k, uid_t, gid_t)
756 {
757 return 0;
758 }
759
760 long int
random(void)761 random(void)
762 {
763 return rand();
764 }
765
766 void
srandom(unsigned int seed)767 srandom(unsigned int seed)
768 {
769 srand(seed);
770 }
771 // /////////////////////////////////////////////////////////////////
772 // convert from Windows concept of time to Unix concept of time
773 // /////////////////////////////////////////////////////////////////
774 void
cvt_utime_to_ftime(const time_t & time,FILETIME & wintime)775 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
776 {
777 uint64_t mstime = time;
778 mstime *= WIN32_FILETIME_SCALE;
779 mstime += WIN32_FILETIME_ADJUST;
780
781 #if defined(_MSC_VER)
782 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
783 #else
784 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
785 #endif
786 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
787 }
788
789 time_t
cvt_ftime_to_utime(const FILETIME & time)790 cvt_ftime_to_utime(const FILETIME &time)
791 {
792 uint64_t mstime = time.dwHighDateTime;
793 mstime <<= 32;
794 mstime |= time.dwLowDateTime;
795
796 mstime -= WIN32_FILETIME_ADJUST;
797 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
798
799 return (time_t) (mstime & 0xffffffff);
800 }
801
errorString(void)802 static const char *errorString(void)
803 {
804 LPVOID lpMsgBuf;
805
806 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
807 FORMAT_MESSAGE_FROM_SYSTEM |
808 FORMAT_MESSAGE_IGNORE_INSERTS,
809 NULL,
810 GetLastError(),
811 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
812 (LPTSTR) &lpMsgBuf,
813 0,
814 NULL);
815
816 /* Strip any \r or \n */
817 char *rval = (char *) lpMsgBuf;
818 char *cp = strchr(rval, '\r');
819 if (cp != NULL) {
820 *cp = 0;
821 } else {
822 cp = strchr(rval, '\n');
823 if (cp != NULL)
824 *cp = 0;
825 }
826 return rval;
827 }
828
829 /* retrieve information about reparse point
830 * the HANDLE must have been open with flag FILE_FLAG_OPEN_REPARSE_POINT
831 * return value can be any of
832 * -1 for error
833 * 0 this is not a M$ reparse point, BUT it can be reparse point !!!!
834 * WIN32_REPARSE_POINT Can be any reparse point, but not one of the following
835 * WIN32_MOUNT_POINT A Volume mounted in a directory
836 * WIN32_JUNCTION_POINT A special type of symlink to a directory
837 * WIN32_SYMLINK_POINT A symlink to a file or a directory (look for FILE_ATTRIBUTE_DIRECTORY )
838 */
win_get_reparse_point(HANDLE h,DWORD * reparse_tag=NULL,POOLMEM ** reparse=NULL)839 static int win_get_reparse_point(HANDLE h, DWORD *reparse_tag=NULL, POOLMEM **reparse=NULL)
840 {
841 int ret = WIN32_REPARSE_NONE;
842 char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
843 REPARSE_DATA_BUFFER *rdata = (REPARSE_DATA_BUFFER *)buffer;
844
845 // Query the reparse data
846 DWORD dwRetLen;
847 BOOL r = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, rdata, sizeof(buffer), &dwRetLen, NULL);
848 if (r == FALSE)
849 {
850 Dmsg1(0, "DeviceIoControl error=%ld\n", GetLastError());
851 return -1;
852 }
853
854 if (reparse_tag != NULL) {
855 *reparse_tag = rdata->ReparseTag;
856 }
857 if (IsReparseTagMicrosoft(rdata->ReparseTag))
858 {
859 if (rdata->ReparseTag == IO_REPARSE_TAG_SYMLINK)
860 {
861 if (reparse != NULL) {
862 wchar_path_2_wutf8(reparse,
863 (wchar_t *)&rdata->SymbolicLinkReparseBuffer.PathBuffer[rdata->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)]);
864 }
865 ret = WIN32_SYMLINK_POINT;
866 goto bailout;
867 } else if (rdata->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
868 POOLMEM *path = NULL;
869 POOLMEM **rpath = reparse;
870 if (rpath == NULL) {
871 path = get_pool_memory(PM_FNAME);
872 rpath = &path;
873 }
874 wchar_path_2_wutf8(rpath,
875 (wchar_t *)&rdata->MountPointReparseBuffer.PathBuffer[rdata->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)]);
876 ret = (strncasecmp(*rpath, "\\??\\Volume{", 11) == 0)?WIN32_MOUNT_POINT:WIN32_JUNCTION_POINT;
877 if (path != NULL) {
878 free_pool_memory(path);
879 }
880 goto bailout;
881 }
882 ret = WIN32_REPARSE_POINT;
883 goto bailout;
884 }
885 else
886 {
887 // Not a Microsoft-reparse point
888 ret = WIN32_REPARSE_NONE;
889 }
890 bailout:
891 return ret;
892 }
893
win_get_reparse_point(const wchar_t * path,DWORD * reparse_tag=NULL,POOLMEM ** reparse=NULL)894 static int win_get_reparse_point(const wchar_t *path, DWORD *reparse_tag=NULL, POOLMEM **reparse=NULL)
895 {
896 HANDLE h = CreateFileW(path, FILE_READ_EA,
897 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
898 NULL, OPEN_EXISTING,
899 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
900 if (h == INVALID_HANDLE_VALUE) {
901 return -1;
902 }
903 int type = win_get_reparse_point(h, reparse_tag, reparse);
904 CloseHandle(h);
905 return type;
906 }
907
win_get_reparse_point(const char * path,DWORD * reparse_tag=NULL,POOLMEM ** reparse=NULL)908 static int win_get_reparse_point(const char *path, DWORD *reparse_tag=NULL, POOLMEM **reparse=NULL)
909 {
910 POOL_MEM wpath(PM_FNAME);
911 make_win32_path_UTF8_2_wchar(&wpath.addr(), path);
912
913 return win_get_reparse_point((wchar_t*)wpath.c_str(), reparse_tag, reparse);
914 }
915
916 /* Convert Windows file attributes into a "unix" st_mode */
attribute_to_mode(DWORD FileAttributes)917 static uint16_t attribute_to_mode(DWORD FileAttributes)
918 {
919 uint16_t st_mode = 0777; /* start with everything */
920 if (FileAttributes & FILE_ATTRIBUTE_READONLY)
921 st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
922 if (FileAttributes & FILE_ATTRIBUTE_SYSTEM)
923 st_mode &= ~S_IRWXO; /* remove everything for other */
924 if (FileAttributes & FILE_ATTRIBUTE_HIDDEN)
925 st_mode |= S_ISVTX; /* use sticky bit -> hidden */
926 if (FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
927 st_mode |= S_ISGID; /* use set group ID -> encrypted */
928 if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
929 st_mode |= S_IFDIR;
930 } else {
931 st_mode |= S_IFREG;
932 }
933 return st_mode;
934 }
935
936 /* fill in sb with Windows attributes */
attributes_to_stat(struct stat * sb,DWORD FileAttributes,DWORD FileSizeHigh,DWORD FileSizeLow,const FILETIME & LastAccessTime,const FILETIME & LastWriteTime)937 static void attributes_to_stat(struct stat *sb, DWORD FileAttributes,
938 DWORD FileSizeHigh, DWORD FileSizeLow,
939 const FILETIME &LastAccessTime, const FILETIME &LastWriteTime)
940 {
941 sb->st_mode = attribute_to_mode(FileAttributes);
942 sb->st_fattrs = FileAttributes;
943
944 sb->st_size = FileSizeHigh;
945 sb->st_size <<= 32;
946 sb->st_size |= FileSizeLow;
947
948 sb->st_blksize = 4096;
949
950 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
951
952 sb->st_atime = cvt_ftime_to_utime(LastAccessTime);
953 sb->st_mtime = cvt_ftime_to_utime(LastWriteTime);
954 sb->st_ctime = MAX(sb->st_mtime, sb->st_ctime);
955 }
956 #if 0
957 /*
958 * This is only called for directories, and is used to get the directory
959 * attributes and find out if we have a junction point or a mount point
960 * or other kind of "funny" directory.
961 * Notice: Python use CreateFileW() and GetFileInformationByHandle()
962 * to get this information instead and fallback to FindFirstFile()
963 */
964 static int
965 statDir(const char *file, struct stat *sb, POOLMEM **readlnk=NULL)
966 {
967 WIN32_FIND_DATAW info_w; // window's file info
968 HANDLE h = INVALID_HANDLE_VALUE;
969
970 /*
971 * Oh, cool, another exception: Microsoft doesn't let us do
972 * FindFile operations on a Drive, so simply fake root attributes.
973 * We could try CreateFileW() and GetFileInformationByHandle()
974 */
975 if (file[1] == ':' && file[2] == 0) {
976 time_t now = time(NULL);
977 Dmsg1(dbglvl, "faking ROOT attrs(%s).\n", file);
978 sb->st_mode = S_IFDIR;
979 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
980 sb->st_ctime = now; /* File change time (inode change...) */
981 sb->st_mtime = now; /* File modify time */
982 sb->st_atime = now; /* File access time */
983 sb->st_rdev = 0;
984 return 0;
985 }
986
987 POOL_MEM pwszBuf(PM_FNAME);
988 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), file);
989
990 Dmsg1(dbglvl, "FindFirstFileW=%s\n", file);
991 h = p_FindFirstFileW((LPCWSTR)pwszBuf.c_str(), &info_w);
992
993 if (h == INVALID_HANDLE_VALUE) {
994 const char *err = errorString();
995 /*
996 * Note, in creating leading paths, it is normal that
997 * the file does not exist.
998 */
999 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
1000 LocalFree((void *)err);
1001 errno = b_errno_win32;
1002 return -1;
1003 }
1004
1005 FindClose(h);
1006
1007 attributes_to_stat(sb, info_w.dwFileAttributes,
1008 info_w.nFileSizeHigh, info_w.nFileSizeLow,
1009 info_w.ftLastAccessTime, info_w.ftLastWriteTime);
1010
1011 Dmsg2(200, "Fattrs=0x%x st_mode=0x%x\n", sb->st_fattrs, sb->st_mode);
1012 /*
1013 * Store reparse/mount point info in st_rdev. Note a
1014 * Win32 reparse point (junction point) is like a link
1015 * though it can have many properties (directory link,
1016 * soft link, hard link, HSM, ...
1017 * A mount point is a reparse point where another volume
1018 * is mounted, so it is like a Unix mount point (change of
1019 * filesystem).
1020 */
1021 if (info_w.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1022 sb->st_rdev = WIN32_MOUNT_POINT;
1023 } else {
1024 sb->st_rdev = 0;
1025 }
1026 /* This is a lot of work just to know that it is deduped */
1027 if (info_w.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
1028 (info_w.dwReserved0 & IO_REPARSE_TAG_DEDUP)) {
1029 sb->st_fattrs |= FILE_ATTRIBUTE_DEDUP; /* add our own bit */
1030 }
1031 if ((info_w.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
1032 (info_w.dwReserved0 & IO_REPARSE_TAG_MOUNT_POINT)) {
1033 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
1034 /*
1035 * Now to find out if the directory is a mount point or
1036 * a reparse point, we must do a song and a dance.
1037 * Explicitly open the file to read the reparse point, then
1038 * call DeviceIoControl to find out if it points to a Volume
1039 * or to a directory.
1040 */
1041 h = CreateFileW((LPCWSTR)pwszBuf.c_str(), GENERIC_READ,
1042 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1043 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1044 NULL);
1045 if (h != INVALID_HANDLE_VALUE) {
1046 char dummy[1000];
1047 REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)dummy;
1048 rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1049 DWORD bytes;
1050 bool ok;
1051 ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
1052 NULL, 0, /* in buffer, bytes */
1053 (LPVOID)rdb, (DWORD)sizeof(dummy), /* out buffer, btyes */
1054 (LPDWORD)&bytes, (LPOVERLAPPED)0);
1055 if (ok) {
1056 POOLMEM *utf8 = get_pool_memory(PM_NAME);
1057 wchar_path_2_wutf8(&utf8, (wchar_t *)rdb->SymbolicLinkReparseBuffer.PathBuffer);
1058 Dmsg2(dbglvl, "Junction %s points to: %s\n", file, utf8);
1059 if (strncasecmp(utf8, "\\??\\volume{", 11) == 0) {
1060 Dmsg2(dbglvl, "FIRST MOUNT POINT %s points to: %s remove code for SECOND\n", file, utf8);
1061 sb->st_rdev = WIN32_MOUNT_POINT;
1062 } else if (strncasecmp(utf8, "\\\\?\\volume{", 11) == 0) {
1063 /* Alain: I think this one is the right one !!!! */
1064 Dmsg2(dbglvl, "SECOND MOUNT POINT %s points to: %s remove code for FIRST\n", file, utf8);
1065 sb->st_rdev = WIN32_MOUNT_POINT;
1066 } else {
1067 /* It points to a directory so we ignore it. */
1068 sb->st_rdev = WIN32_JUNCTION_POINT;
1069 }
1070 /* If requested, store the link for future use */
1071 if (readlnk) {
1072 pm_strcpy(readlnk, utf8);
1073 }
1074 free_pool_memory(utf8);
1075 }
1076 CloseHandle(h);
1077 } else {
1078 Dmsg1(dbglvl, "Invalid handle from CreateFile(%s)\n", file);
1079 }
1080 }
1081 Dmsg2(dbglvl, "st_rdev=%d file=%s\n", sb->st_rdev, file);
1082 sb->st_ctime = MAX(sb->st_mtime, sb->st_ctime);
1083 /* Note ctime is last change time -- not creation time */
1084 Dmsg2(200, "Fattrs=0x%x st_mode=0x%x\n", sb->st_fattrs, sb->st_mode);
1085
1086 return 0;
1087 }
1088 #endif
1089
1090 /* On success, readlink() returns the number of bytes placed in buf. On
1091 * error, -1 is returned and errno is set to indicate the error.
1092 *
1093 * TODO: Still need to activate the readlink() call in find_one.c
1094 * by returning a S_ISLNK(st_mode) compatible flag.
1095 */
1096 int
readlink(const char * path,char * buf,int bufsiz)1097 readlink(const char *path, char *buf, int bufsiz)
1098 {
1099 int ret=-1;
1100 POOLMEM *lnk = get_pool_memory(PM_FNAME);
1101 *lnk = 0;
1102 int type = win_get_reparse_point(path, NULL, &lnk);
1103 if (type == WIN32_SYMLINK_POINT) {
1104 ret = bstrncpy(buf, lnk, bufsiz) - buf - 1; // Don't count the last \0
1105 }
1106 free_pool_memory(lnk);
1107 return ret;
1108 }
1109
1110 /* symlink() shall return 0; otherwise, it shall return -1 and set errno to
1111 * indicate the error.
1112 */
1113 int
symlink(const char * path1,const char * path2)1114 symlink(const char *path1, const char *path2)
1115 {
1116 int ret = 0;
1117 struct stat st;
1118 DWORD isdir = 0;
1119
1120 if (stat(path1, &st) == 0) {
1121 if (st.st_mode & S_IFDIR) {
1122 isdir=1;
1123 }
1124 } else {
1125 Dmsg1(200, "Cannot find the source directory %s\n", path1);
1126 return -1;
1127 }
1128
1129 if (!p_CreateSymbolicLinkW) {
1130 Dmsg0(200, "No implementation of CreateSymbolicLink available\n");
1131 return -1;
1132 }
1133
1134 POOL_MEM pwszBuf1(PM_FNAME);
1135 POOL_MEM pwszBuf2(PM_FNAME);
1136
1137 make_win32_path_UTF8_2_wchar(&pwszBuf1.addr(), path1);
1138 make_win32_path_UTF8_2_wchar(&pwszBuf2.addr(), path2);
1139
1140 Dmsg2(dbglvl, "Trying to symlink (%s -> %s)\n", path1, path2);
1141
1142 if (!p_CreateSymbolicLinkW((LPCWSTR)pwszBuf2.c_str(), (LPCWSTR)pwszBuf1.c_str(), isdir)) {
1143 const char *err = errorString();
1144 Dmsg3(200, "Cannot create symlink (%s -> %s):%s\n", path1, path2, err);
1145 LocalFree((void *)err);
1146 errno = b_errno_win32;
1147 ret = -1;
1148 }
1149
1150 return ret;
1151 }
1152
1153 /* Do a stat() on a valid HANDLE (opened with CreateFile()) */
hstat(HANDLE h,struct stat * sb)1154 int hstat(HANDLE h, struct stat *sb)
1155 {
1156 BY_HANDLE_FILE_INFORMATION info;
1157
1158 if (!GetFileInformationByHandle(h, &info)) {
1159 const char *err = errorString();
1160 Dmsg1(dbglvl, "GetfileInformationByHandle: %s\n", err);
1161 LocalFree((void *)err);
1162 errno = b_errno_win32;
1163 return -1;
1164 }
1165
1166 /* We should modify only variables that are modified in stat()
1167 * everything else should be carefully tested.
1168 */
1169
1170 /* When turned on, we see a lot of messages such as
1171 * C:/PerfLogs is a different filesystem. Will not descend from C:/ into it.
1172 */
1173 //sb->st_dev = info.dwVolumeSerialNumber;
1174
1175 /* The st_ino is not used in stat() */
1176 sb->st_ino = info.nFileIndexHigh;
1177 sb->st_ino <<= 32;
1178 sb->st_ino |= info.nFileIndexLow;
1179
1180 sb->st_nlink = 1;
1181 #if 0 // We don't have the link() call right now
1182 // TODO: something with CreateHardLinkFunc()
1183 sb->st_nlink = (short)info.nNumberOfLinks;
1184 if (sb->st_nlink > 1) {
1185 Dmsg1(dbglvl, "st_nlink=%d\n", sb->st_nlink);
1186 }
1187 #endif
1188 attributes_to_stat(sb, info.dwFileAttributes,
1189 info.nFileSizeHigh, info.nFileSizeLow,
1190 info.ftLastAccessTime, info.ftLastWriteTime);
1191
1192 /* Get the ChangeTime information with an other API, when attributes are modified
1193 * the ChangeTime is modified while CreationTime and WriteTime are not
1194 */
1195 FILE_BASIC_INFO file_basic_info;
1196 if (p_GetFileInformationByHandleEx &&
1197 p_GetFileInformationByHandleEx(h, FileBasicInfo, &file_basic_info, sizeof(file_basic_info))) {
1198 FILETIME *pftChangeTime = (FILETIME *)&file_basic_info.ChangeTime;
1199 sb->st_ctime = cvt_ftime_to_utime(*pftChangeTime);
1200 sb->st_ctime = MAX(sb->st_mtime, sb->st_ctime);
1201 }
1202
1203 /* Use st_rdev to store reparse attribute */
1204 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1205 DWORD reparse_tag;
1206 sb->st_rdev = WIN32_REPARSE_POINT;
1207 int type = win_get_reparse_point(h, &reparse_tag);
1208 if (type == WIN32_MOUNT_POINT || type == WIN32_JUNCTION_POINT) {
1209 sb->st_rdev = type;
1210 if (!(sb->st_mode & S_IFDIR)) {
1211 Pmsg1(0, "A reparse point of type %d is expected to be a directory\n", type);
1212 ASSERTD(FALSE, "A mount point or a junction should be a directory\n");
1213 }
1214 } else if (type == WIN32_SYMLINK_POINT) {
1215 sb->st_rdev = type;
1216 }
1217 }
1218 Dmsg3(dbglvl, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
1219 (long long)sb->st_ino);
1220
1221 Dmsg2(200, "Fattrs=0x%x st_mode=0x%x\n", sb->st_fattrs, sb->st_mode);
1222 return 0;
1223 }
1224
1225 /* add a trailing "\\" to a "naked" drive letter like "X:"
1226 * expect a wchar_t path */
sanitize_drive_root(POOLMEM * & wpath)1227 void sanitize_drive_root(POOLMEM *&wpath)
1228 {
1229 wchar_t *p=(wchar_t *)wpath;
1230 if (p[1] == L':' && p[2] == L'\0') {
1231 wpath = check_pool_memory_size(wpath, (wcslen((wchar_t*)wpath)+1)*sizeof(wchar_t));
1232 wcscat((wchar_t *)wpath, L"\\");
1233 }
1234 }
1235
1236 /*
1237 * stat() don't like path like:
1238 * - c:
1239 * - \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy5
1240 * You must add a trailing '\'
1241 */
stat(const char * file,struct stat * sb)1242 int stat(const char *file, struct stat *sb)
1243 {
1244 WIN32_FILE_ATTRIBUTE_DATA data;
1245
1246 errno = 0;
1247 /* We do the first try with a file HANDLER, because we want to use the
1248 * ChangeTime that is only available with GetFileInformationByHandleEx
1249 */
1250 POOL_MEM pwszBuf(PM_FNAME);
1251 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), file);
1252 sanitize_drive_root(pwszBuf.addr());
1253
1254 #if 0
1255 ret = stat2((wchar_t *)pwszBuf.c_str(), sb);
1256
1257 if (!ret) {
1258 return ret;
1259 }
1260
1261 if (!p_GetFileAttributesExW) {
1262 return -1;
1263 }
1264 /* We were not able to open a filehandler on the file to get attributes,
1265 * so we try with the name. It may happen for example with encrypted files.
1266 */
1267 #endif
1268 memset(sb, 0, sizeof(*sb));
1269 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf.c_str(), GetFileExInfoStandard, &data);
1270
1271 if (!b) {
1272 const char *err = errorString();
1273 Dmsg3(dbglvl, "GetFileAttributesExW(%s):%s %ls\n", file, err, pwszBuf.c_str());
1274 LocalFree((void *)err);
1275 return -1;
1276 }
1277
1278 attributes_to_stat(sb, data.dwFileAttributes,
1279 data.nFileSizeHigh, data.nFileSizeLow,
1280 data.ftLastAccessTime, data.ftLastWriteTime);
1281
1282 /* Use st_rdev to store reparse attribute */
1283 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? WIN32_REPARSE_POINT : 0;
1284
1285 sb->st_nlink = 1;
1286 sb->st_ctime = sb->st_mtime;
1287
1288 if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1289 #if 0
1290 WIN32_FIND_DATAW info_w;
1291 HANDLE h = p_FindFirstFileW((LPCWSTR)pwszBuf.c_str(), &info_w);
1292
1293 sb->st_rdev = WIN32_REPARSE_POINT;
1294 if (h == INVALID_HANDLE_VALUE) {
1295 const char *err = errorString();
1296 /*
1297 * Note, in creating leading paths, it is normal that
1298 * the file does not exist.
1299 */
1300 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
1301 LocalFree((void *)err);
1302 errno = b_errno_win32;
1303 return -1;
1304 }
1305 FindClose(h);
1306 #endif
1307
1308 DWORD reparse_tag;
1309 int type = win_get_reparse_point((wchar_t*)pwszBuf.c_str(), &reparse_tag);
1310 #ifndef DO_WE_NEED_THAT // ?????
1311 if (reparse_tag & IO_REPARSE_TAG_DEDUP) {
1312 sb->st_fattrs |= FILE_ATTRIBUTE_DEDUP; /* add our own bit */
1313 }
1314 #endif
1315 Dmsg1(dbglvl, "reparse_point type=%d\n", type);
1316 if (type == WIN32_MOUNT_POINT || type == WIN32_JUNCTION_POINT) {
1317 sb->st_rdev = type;
1318 if (!(sb->st_mode & S_IFDIR)) {
1319 Pmsg1(0, "A reparse point of type %d is expected to be a directory\n", type);
1320 ASSERTD(FALSE, "A mount point or a junction should be a directory\n");
1321 }
1322 } else if (type == WIN32_SYMLINK_POINT) {
1323 sb->st_rdev = type;
1324 }
1325 }
1326 Dmsg4(dbglvl, "sizino=%d ino=%lld file=%s rdev=%d\n", sizeof(sb->st_ino),
1327 (long long)sb->st_ino, file, sb->st_rdev);
1328 Dmsg2(200, "Fattrs=0x%x st_mode=0x%x\n", sb->st_fattrs, sb->st_mode);
1329 return 0;
1330 }
1331
1332 int
fstat(intptr_t fd,struct stat * sb)1333 fstat(intptr_t fd, struct stat *sb)
1334 {
1335 return hstat((HANDLE)_get_osfhandle(fd), sb);
1336 }
1337
1338 /*
1339 * We write our own ftruncate because the one in the
1340 * Microsoft library mrcrt.dll does not truncate
1341 * files greater than 2GB.
1342 * KES - May 2007
1343 */
win32_ftruncate(int fd,int64_t length)1344 int win32_ftruncate(int fd, int64_t length)
1345 {
1346 /* Set point we want to truncate file */
1347 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
1348
1349 if (pos != (__int64)length) {
1350 errno = EACCES; /* truncation failed, get out */
1351 return -1;
1352 }
1353
1354 /* Truncate file */
1355 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
1356 errno = b_errno_win32;
1357 return -1;
1358 }
1359 errno = 0;
1360 return 0;
1361 }
1362
win_flock_hold(HANDLE fhandle,int non_blocking,int exclusive)1363 int win_flock_hold(HANDLE fhandle, int non_blocking, int exclusive) {
1364 DWORD file_lower, file_upper;
1365 file_lower = GetFileSize(fhandle, &file_upper);
1366 OVERLAPPED ov;
1367 memset(&ov, 0, sizeof ov);
1368 int flags = 0;
1369
1370 if (non_blocking) {
1371 flags |= LOCKFILE_FAIL_IMMEDIATELY;
1372 }
1373
1374 if (exclusive) {
1375 flags |= LOCKFILE_EXCLUSIVE_LOCK;
1376 }
1377
1378 /* Lock the whole file */
1379 return LockFileEx(fhandle, flags, 0, file_lower, file_upper, &ov);
1380 }
1381
win_flock_release(HANDLE fhandle)1382 int win_flock_release(HANDLE fhandle) {
1383 DWORD size_lower, size_upper;
1384 size_lower = GetFileSize(fhandle, &size_upper);
1385 return UnlockFile(fhandle, 0, 0, size_lower, size_upper);
1386 }
1387
flock(int fd,int operation)1388 int flock(int fd, int operation)
1389 {
1390 DWORD success;
1391 HANDLE fhandle = (HANDLE) _get_osfhandle(fd);
1392
1393 if (fhandle == INVALID_HANDLE_VALUE) {
1394 errno = EBADF;
1395 return -1;
1396 }
1397
1398 int nb_flag = operation & LOCK_NB;
1399 operation &= ~LOCK_NB;
1400
1401 switch (operation) {
1402 case LOCK_SH:
1403 success = win_flock_hold(fhandle, nb_flag, 0);
1404 break;
1405 case LOCK_EX:
1406 success = win_flock_hold(fhandle, nb_flag, 1);
1407 break;
1408 case LOCK_UN:
1409 success = win_flock_release(fhandle);
1410 break;
1411 default:
1412 errno = EINVAL;
1413 return -1;
1414 }
1415
1416 if (!success) {
1417 DWORD err = GetLastError();
1418 switch (err) {
1419 case ERROR_LOCK_VIOLATION:
1420 errno = EAGAIN;
1421 break;
1422 case ERROR_NOT_ENOUGH_MEMORY:
1423 errno = ENOMEM;
1424 break;
1425 case ERROR_BAD_COMMAND:
1426 errno = EINVAL;
1427 break;
1428 default:
1429 errno = err;
1430 }
1431
1432 return -1;
1433 }
1434
1435 return 0;
1436 }
1437
fcntl(int fd,int cmd,long arg)1438 int fcntl(int fd, int cmd, long arg)
1439 {
1440 int rval = 0;
1441
1442 switch (cmd) {
1443 case F_GETFL:
1444 rval = O_NONBLOCK;
1445 break;
1446
1447 case F_SETFL:
1448 rval = 0;
1449 break;
1450
1451 default:
1452 errno = EINVAL;
1453 rval = -1;
1454 break;
1455 }
1456
1457 return rval;
1458 }
1459
1460 int
lstat(const char * file,struct stat * sb)1461 lstat(const char *file, struct stat *sb)
1462 {
1463 return stat(file, sb);
1464 }
1465
1466 void
sleep(int sec)1467 sleep(int sec)
1468 {
1469 Sleep(sec * 1000);
1470 }
1471
1472 int
geteuid(void)1473 geteuid(void)
1474 {
1475 return 0;
1476 }
1477
1478 int
execvp(const char *,char * [])1479 execvp(const char *, char *[]) {
1480 errno = ENOSYS;
1481 return -1;
1482 }
1483
1484
1485 int
fork(void)1486 fork(void)
1487 {
1488 errno = ENOSYS;
1489 return -1;
1490 }
1491
1492 int
pipe(int[])1493 pipe(int[])
1494 {
1495 errno = ENOSYS;
1496 return -1;
1497 }
1498
1499 int
waitpid(int,int *,int)1500 waitpid(int, int*, int)
1501 {
1502 errno = ENOSYS;
1503 return -1;
1504 }
1505
1506 #ifndef HAVE_MINGW
1507 int
strcasecmp(const char * s1,const char * s2)1508 strcasecmp(const char *s1, const char *s2)
1509 {
1510 register int ch1, ch2;
1511
1512 if (s1==s2)
1513 return 0; /* strings are equal if same object. */
1514 else if (!s1)
1515 return -1;
1516 else if (!s2)
1517 return 1;
1518 do {
1519 ch1 = *s1;
1520 ch2 = *s2;
1521 s1++;
1522 s2++;
1523 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1524
1525 return(ch1 - ch2);
1526 }
1527 #endif //HAVE_MINGW
1528
1529 int
strncasecmp(const char * s1,const char * s2,int len)1530 strncasecmp(const char *s1, const char *s2, int len)
1531 {
1532 register int ch1 = 0, ch2 = 0;
1533
1534 if (s1==s2)
1535 return 0; /* strings are equal if same object. */
1536 else if (!s1)
1537 return -1;
1538 else if (!s2)
1539 return 1;
1540
1541 while (len--) {
1542 ch1 = *s1;
1543 ch2 = *s2;
1544 s1++;
1545 s2++;
1546 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1547 }
1548
1549 return (ch1 - ch2);
1550 }
1551
1552 int
gettimeofday(struct timeval * tv,struct timezone *)1553 gettimeofday(struct timeval *tv, struct timezone *)
1554 {
1555 SYSTEMTIME now;
1556 FILETIME tmp;
1557
1558 GetSystemTime(&now);
1559
1560 if (tv == NULL) {
1561 errno = EINVAL;
1562 return -1;
1563 }
1564 if (!SystemTimeToFileTime(&now, &tmp)) {
1565 errno = b_errno_win32;
1566 return -1;
1567 }
1568
1569 int64_t _100nsec = tmp.dwHighDateTime;
1570 _100nsec <<= 32;
1571 _100nsec |= tmp.dwLowDateTime;
1572 _100nsec -= WIN32_FILETIME_ADJUST;
1573
1574 tv->tv_sec = (long)(_100nsec / 10000000);
1575 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1576 return 0;
1577
1578 }
1579
1580 /*
1581 * Write in Windows System log
1582 */
syslog(int type,const char * fmt,...)1583 void syslog(int type, const char *fmt, ...)
1584 {
1585 va_list arg_ptr;
1586 int len, maxlen;
1587 POOLMEM *msg;
1588
1589 msg = get_pool_memory(PM_EMSG);
1590
1591 for (;;) {
1592 maxlen = sizeof_pool_memory(msg) - 1;
1593 va_start(arg_ptr, fmt);
1594 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1595 va_end(arg_ptr);
1596 if (len < 0 || len >= (maxlen-5)) {
1597 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1598 continue;
1599 }
1600 break;
1601 }
1602 LogErrorMsg((const char *)msg);
1603 free_memory(msg);
1604 }
1605
1606 void
closelog()1607 closelog()
1608 {
1609 }
1610
1611 struct passwd *
getpwuid(uid_t)1612 getpwuid(uid_t)
1613 {
1614 return NULL;
1615 }
1616
1617 struct group *
getgrgid(uid_t)1618 getgrgid(uid_t)
1619 {
1620 return NULL;
1621 }
1622
1623 // implement opendir/readdir/closedir on top of window's API
1624
1625 typedef struct _dir
1626 {
1627 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1628 POOLMEM *spec; // the directory we're traversing
1629 HANDLE dirh; // the search handle
1630 bool call_findnextfile; // use FindFirstFile data first
1631 } _dir;
1632
1633 DIR *
opendir(const char * path)1634 opendir(const char *path)
1635 {
1636 /* enough space for VSS !*/
1637 _dir *rval = NULL;
1638 POOL_MEM pwcBuf(PM_FNAME);
1639
1640 if (path == NULL) {
1641 errno = ENOENT;
1642 return NULL;
1643 }
1644 if (!p_FindFirstFileW || !p_FindNextFileW) {
1645 errno = ENOMEM;
1646 return NULL;
1647 }
1648
1649 Dmsg1(10, "Opendir path=%s\n", path);
1650 rval = (_dir *)get_pool_memory(PM_FNAME);
1651 rval = (_dir *)check_pool_memory_size((POOLMEM *)rval, sizeof(_dir));
1652 memset (rval, 0, sizeof (_dir));
1653
1654 rval->spec = get_pool_memory(PM_FNAME);
1655 pm_strcpy(rval->spec, path);
1656
1657 // convert to wchar_t
1658 make_win32_path_UTF8_2_wchar(&pwcBuf.addr(), path);
1659
1660 // Add a "*" or "\\*" to the path, see documentation of FindFirstFile()
1661 // Do it after the call to make_win32_path_UTF8_2_wchar() that escape '*'
1662 int len = wcslen((wchar_t*)pwcBuf.c_str());
1663 pwcBuf.check_size((len+2)*sizeof(wchar_t));
1664 wchar_t *p = (wchar_t*)pwcBuf.c_str();
1665 if (p[len-1] != L'\\') {
1666 p[len++] = L'\\';
1667 }
1668 p[len++] = L'*';
1669 p[len] = L'\0';
1670 Dmsg2(10, "opendir XXX=%ls %s\n", p, path);
1671
1672 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf.c_str(), &rval->data_w);
1673 rval->call_findnextfile = false;
1674
1675 if (rval->dirh == INVALID_HANDLE_VALUE) {
1676 if (GetLastError() == ERROR_FILE_NOT_FOUND) {
1677 /* the directory is empty, continue with an INVALID_HANDLE_VALUE handle */
1678 rval->data_w.cFileName[0]='\0';
1679 } else {
1680 goto err;
1681 }
1682 }
1683 Dmsg4(10, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %p cFileName=%ls\n",
1684 path, rval->spec, rval->dirh, rval->data_w.cFileName);
1685
1686 return (DIR *)rval;
1687
1688 err:
1689 if (rval) {
1690 if (rval->spec) {
1691 free_pool_memory(rval->spec);
1692 }
1693 free_pool_memory((POOLMEM *)rval);
1694 }
1695 errno = b_errno_win32;
1696 return NULL;
1697 }
1698
1699 int
closedir(DIR * dirp)1700 closedir(DIR *dirp)
1701 {
1702 _dir *dp = (_dir *)dirp;
1703 if (dp->dirh != INVALID_HANDLE_VALUE) {
1704 FindClose(dp->dirh);
1705 }
1706 free_pool_memory(dp->spec);
1707 free_pool_memory((POOLMEM *)dp);
1708 return 0;
1709 }
1710
1711 /*
1712 typedef struct _WIN32_FIND_DATA {
1713 DWORD dwFileAttributes;
1714 FILETIME ftCreationTime;
1715 FILETIME ftLastAccessTime;
1716 FILETIME ftLastWriteTime;
1717 DWORD nFileSizeHigh;
1718 DWORD nFileSizeLow;
1719 DWORD dwReserved0;
1720 DWORD dwReserved1;
1721 TCHAR cFileName[MAX_PATH];
1722 TCHAR cAlternateFileName[14];
1723 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1724 */
1725
breaddir(DIR * dirp,POOLMEM * & dname)1726 int breaddir(DIR *dirp, POOLMEM *&dname)
1727 {
1728 _dir *dp = (_dir *)dirp;
1729
1730 if (dirp == NULL) {
1731 errno = EBADF;
1732 return EBADF;
1733 }
1734
1735 if (dp->call_findnextfile) {
1736 if (p_FindNextFileW(dp->dirh, &dp->data_w)) {
1737 } else {
1738 if (GetLastError() == ERROR_NO_MORE_FILES) {
1739 Dmsg1(dbglvl, "breaddir(%p) ERROR_NO_MORE_FILES\n", dirp);
1740 return -1; // end of directory reached
1741 } else {
1742 errno = b_errno_win32;
1743 return b_errno_win32;
1744 }
1745 }
1746 } else {
1747 // use data from FindFirstFile first then next time call FindNextFileW
1748 if (dp->dirh == INVALID_HANDLE_VALUE) {
1749 return -1; // the directory is empty, no "." nor ".." (special case)
1750 }
1751 dp->call_findnextfile = true;
1752 }
1753 wchar_path_2_wutf8(&dname, dp->data_w.cFileName);
1754 Dmsg2(10, "breaddir %ls => %s\n", dp->data_w.cFileName, dname);
1755 return 0;
1756 }
1757
1758 /*
1759 * Dotted IP address to network address
1760 *
1761 * Returns 1 if OK
1762 * 0 on error
1763 */
1764 int
inet_aton(const char * a,struct in_addr * inp)1765 inet_aton(const char *a, struct in_addr *inp)
1766 {
1767 const char *cp = a;
1768 uint32_t acc = 0, tmp = 0;
1769 int dotc = 0;
1770
1771 if (!isdigit(*cp)) { /* first char must be digit */
1772 return 0; /* error */
1773 }
1774 do {
1775 if (isdigit(*cp)) {
1776 tmp = (tmp * 10) + (*cp -'0');
1777 } else if (*cp == '.' || *cp == 0) {
1778 if (tmp > 255) {
1779 return 0; /* error */
1780 }
1781 acc = (acc << 8) + tmp;
1782 dotc++;
1783 tmp = 0;
1784 } else {
1785 return 0; /* error */
1786 }
1787 } while (*cp++ != 0);
1788 if (dotc != 4) { /* want 3 .'s plus EOS */
1789 return 0; /* error */
1790 }
1791 inp->s_addr = htonl(acc); /* store addr in network format */
1792 return 1;
1793 }
1794
1795
1796 /*
1797 * Convert from presentation format (which usually means ASCII printable)
1798 * to network format (which is usually some kind of binary format).
1799 * return:
1800 * 1 if the address was valid for the specified address family
1801 * 0 if the address wasn't valid (`dst' is untouched in this case)
1802 */
1803 int
binet_pton(int af,const char * src,void * dst)1804 binet_pton(int af, const char *src, void *dst)
1805 {
1806 switch (af) {
1807 case AF_INET:
1808 case AF_INET6:
1809 if (p_InetPton) {
1810 return p_InetPton(af, src, dst);
1811 }
1812 return 0;
1813 default:
1814 return 0;
1815 }
1816 }
1817
1818
1819 int
nanosleep(const struct timespec * req,struct timespec * rem)1820 nanosleep(const struct timespec *req, struct timespec *rem)
1821 {
1822 if (rem)
1823 rem->tv_sec = rem->tv_nsec = 0;
1824 Sleep((req->tv_sec * 1000) + (req->tv_nsec/1000000));
1825 return 0;
1826 }
1827
1828 void
init_signals(void terminate (int sig))1829 init_signals(void terminate(int sig))
1830 {
1831
1832 }
1833
1834 void
init_stack_dump(void)1835 init_stack_dump(void)
1836 {
1837
1838 }
1839
1840
1841 long
pathconf(const char * path,int name)1842 pathconf(const char *path, int name)
1843 {
1844 switch(name) {
1845 case _PC_PATH_MAX :
1846 if (strncmp(path, "\\\\?\\", 4) == 0)
1847 return 32767;
1848 case _PC_NAME_MAX :
1849 return 255;
1850 }
1851 errno = ENOSYS;
1852 return -1;
1853 }
1854
1855 int
WSA_Init(void)1856 WSA_Init(void)
1857 {
1858 WORD wVersionRequested = MAKEWORD(2, 2);
1859 WSADATA wsaData;
1860
1861 int err = WSAStartup(wVersionRequested, &wsaData);
1862 if (err != 0) {
1863 wVersionRequested = MAKEWORD(2, 0);
1864 err = WSAStartup(wVersionRequested, &wsaData);
1865 if (err != 0) {
1866 wVersionRequested = MAKEWORD(1, 1);
1867 err = WSAStartup(wVersionRequested, &wsaData);
1868 }
1869 }
1870
1871 if (err != 0) {
1872 printf("Can not start Windows Sockets\n");
1873 errno = ENOSYS;
1874 return -1;
1875 }
1876
1877 return 0;
1878 }
1879
fill_attribute(DWORD attr,mode_t mode)1880 static DWORD fill_attribute(DWORD attr, mode_t mode)
1881 {
1882 Dmsg1(dbglvl, " before attr=%lld\n", (uint64_t) attr);
1883 /* Use Bacula mappings define in stat() above */
1884 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1885 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1886 } else {
1887 attr |= FILE_ATTRIBUTE_READONLY;
1888 }
1889 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1890 attr |= FILE_ATTRIBUTE_HIDDEN;
1891 } else {
1892 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1893 }
1894 if (mode & S_ISGID) { // The set group ID <=> ENCRYPTED
1895 attr |= FILE_ATTRIBUTE_ENCRYPTED;
1896 } else {
1897 attr &= ~FILE_ATTRIBUTE_ENCRYPTED;
1898 }
1899 if (mode & S_IRWXO) { // Other can read/write/execute ?
1900 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1901 } else {
1902 attr |= FILE_ATTRIBUTE_SYSTEM;
1903 }
1904 Dmsg1(dbglvl, " after attr=%lld\n", (uint64_t)attr);
1905 return attr;
1906 }
1907
win32_chmod(const char * path,mode_t mode)1908 int win32_chmod(const char *path, mode_t mode)
1909 {
1910 bool ret=false;
1911 DWORD attr;
1912
1913 Dmsg2(dbglvl, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1914 POOL_MEM pwszBuf(PM_FNAME);
1915 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), path);
1916
1917 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf.c_str());
1918 if (attr != INVALID_FILE_ATTRIBUTES) {
1919 /* Use Bacula mappings define in stat() above */
1920 attr = fill_attribute(attr, mode);
1921 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf.c_str(), attr);
1922 }
1923 Dmsg0(dbglvl, "Leave win32_chmod. AttributesW\n");
1924
1925 if (!ret) {
1926 const char *err = errorString();
1927 Dmsg2(dbglvl, "Get/SetFileAttributes(%s): %s\n", path, err);
1928 LocalFree((void *)err);
1929 errno = b_errno_win32;
1930 return -1;
1931 }
1932 return 0;
1933 }
1934
1935
1936 int
win32_chdir(const char * dir)1937 win32_chdir(const char *dir)
1938 {
1939 POOL_MEM pwszBuf(PM_FNAME);
1940 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), dir);
1941
1942 BOOL b=p_SetCurrentDirectoryW && p_SetCurrentDirectoryW((LPCWSTR)pwszBuf.c_str());
1943
1944 if (!b) {
1945 errno = b_errno_win32;
1946 return -1;
1947 }
1948
1949 return 0;
1950 }
1951
1952 int
win32_mkdir(const char * dir)1953 win32_mkdir(const char *dir)
1954 {
1955 Dmsg1(dbglvl, "enter win32_mkdir. dir=%s\n", dir);
1956 if (p_wmkdir){
1957 POOL_MEM pwszBuf(PM_FNAME);
1958 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), dir);
1959
1960 int n = p_wmkdir((LPCWSTR)pwszBuf.c_str());
1961 Dmsg0(dbglvl, "Leave win32_mkdir did wmkdir\n");
1962 return n;
1963 }
1964
1965 Dmsg0(dbglvl, "Leave win32_mkdir did _mkdir\n");
1966 return _mkdir(dir);
1967 }
1968
1969
1970 char *
win32_getcwd(char * buf,int maxlen)1971 win32_getcwd(char *buf, int maxlen)
1972 {
1973 if (!p_GetCurrentDirectoryW) {
1974 return NULL;
1975 }
1976 POOL_MEM pwszBuf(PM_FNAME);
1977 pwszBuf.check_size(maxlen*sizeof(wchar_t));
1978
1979 if (0 == p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf.c_str())) {
1980 return NULL;
1981 }
1982
1983 POOL_MEM wtf8(PM_FNAME);
1984 int n = wchar_path_2_wutf8(&wtf8.addr(), (wchar_t *)pwszBuf.c_str());
1985 memcpy(buf, wtf8.c_str(), n<maxlen?n:maxlen);
1986 if (buf[n-2] != '\\') {
1987 // Add a trailing '\'
1988 if (n+1 > maxlen) {
1989 return NULL;
1990 }
1991 buf[n-1] = '\\';
1992 buf[n] = 0;
1993 }
1994 return buf;
1995 }
1996
1997 int
win32_fputs(const char * string,FILE * stream)1998 win32_fputs(const char *string, FILE *stream)
1999 {
2000 /* we use WriteConsoleA / WriteConsoleA
2001 so we can be sure that unicode support works on win32.
2002 with fallback if something fails
2003 */
2004
2005 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
2006 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
2007 p_MultiByteToWideChar && (stream == stdout)) {
2008
2009 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
2010
2011 DWORD dwCharsWritten;
2012 DWORD dwChars;
2013
2014 dwChars = UTF8_2_wchar(&pwszBuf, string);
2015
2016 /* try WriteConsoleW */
2017 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
2018 free_pool_memory(pwszBuf);
2019 return dwCharsWritten;
2020 }
2021
2022 /* convert to local codepage and try WriteConsoleA */
2023 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
2024 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
2025
2026 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
2027 free_pool_memory(pwszBuf);
2028
2029 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
2030 free_pool_memory(pszBuf);
2031 return dwCharsWritten;
2032 }
2033 free_pool_memory(pszBuf);
2034 }
2035 /* Fall back */
2036 return fputs(string, stream);
2037 }
2038
2039 char*
win32_cgets(char * buffer,int len)2040 win32_cgets (char* buffer, int len)
2041 {
2042 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
2043 from the win32 console and fallback if something fails */
2044
2045 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
2046 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
2047 DWORD dwRead;
2048 wchar_t wszBuf[1024];
2049 char szBuf[1024];
2050
2051 /* nt and unicode conversion */
2052 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
2053
2054 /* null terminate at end */
2055 if (wszBuf[dwRead-1] == L'\n') {
2056 wszBuf[dwRead-1] = L'\0';
2057 dwRead --;
2058 }
2059
2060 if (wszBuf[dwRead-1] == L'\r') {
2061 wszBuf[dwRead-1] = L'\0';
2062 dwRead --;
2063 }
2064
2065 wchar_2_UTF8(buffer, wszBuf, len);
2066 return buffer;
2067 }
2068
2069 /* win 9x and unicode conversion */
2070 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
2071
2072 /* null terminate at end */
2073 if (szBuf[dwRead-1] == L'\n') {
2074 szBuf[dwRead-1] = L'\0';
2075 dwRead --;
2076 }
2077
2078 if (szBuf[dwRead-1] == L'\r') {
2079 szBuf[dwRead-1] = L'\0';
2080 dwRead --;
2081 }
2082
2083 /* convert from ansii to wchar_t */
2084 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
2085 /* convert from wchar_t to UTF-8 */
2086 if (wchar_2_UTF8(buffer, wszBuf, len))
2087 return buffer;
2088 }
2089 }
2090
2091 /* fallback */
2092 if (fgets(buffer, len, stdin))
2093 return buffer;
2094 else
2095 return NULL;
2096 }
2097
2098 int
win32_unlink(const char * filename)2099 win32_unlink(const char *filename)
2100 {
2101 int nRetCode;
2102 if (p_wunlink) {
2103 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2104 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
2105
2106 nRetCode = _wunlink((LPCWSTR) pwszBuf);
2107
2108 /*
2109 * special case if file is readonly,
2110 * we retry but unset attribute before
2111 */
2112 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
2113 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
2114 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
2115 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
2116 nRetCode = _wunlink((LPCWSTR) pwszBuf);
2117 /* reset to original if it didn't help */
2118 if (nRetCode == -1)
2119 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
2120 }
2121 }
2122 }
2123 free_pool_memory(pwszBuf);
2124 } else {
2125 nRetCode = _unlink(filename);
2126
2127 /* special case if file is readonly,
2128 we retry but unset attribute before */
2129 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
2130 DWORD dwAttr = p_GetFileAttributesA(filename);
2131 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
2132 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
2133 nRetCode = _unlink(filename);
2134 /* reset to original if it didn't help */
2135 if (nRetCode == -1)
2136 p_SetFileAttributesA(filename, dwAttr);
2137 }
2138 }
2139 }
2140 }
2141 return nRetCode;
2142 }
2143
2144
2145 #include "mswinver.h"
2146
2147 char WIN_VERSION_LONG[64];
2148 char WIN_VERSION[32];
2149 char WIN_RAWVERSION[32];
2150
2151 class winver {
2152 public:
2153 winver(void);
2154 };
2155
2156 static winver INIT; // cause constructor to be called before main()
2157
2158
winver(void)2159 winver::winver(void)
2160 {
2161 const char *version = "";
2162 const char *platform = "";
2163 OSVERSIONINFO osvinfo;
2164 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
2165
2166 // Get the current OS version
2167 if (!GetVersionEx(&osvinfo)) {
2168 version = "Unknown";
2169 platform = "Unknown";
2170 }
2171 const int ver = _mkversion(osvinfo.dwPlatformId,
2172 osvinfo.dwMajorVersion,
2173 osvinfo.dwMinorVersion);
2174 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
2175 switch (ver)
2176 {
2177 case MS_WINDOWS_95: (version = "Windows 95"); break;
2178 case MS_WINDOWS_98: (version = "Windows 98"); break;
2179 case MS_WINDOWS_ME: (version = "Windows ME"); break;
2180 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
2181 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
2182 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
2183 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
2184 default: version = WIN_RAWVERSION; break;
2185 }
2186
2187 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
2188 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
2189 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
2190
2191 #if 0
2192 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
2193 CloseHandle(h);
2194 #endif
2195 #if 0
2196 BPIPE *b = open_bpipe("ls -l", 10, "r");
2197 char buf[1024];
2198 while (!feof(b->rfd)) {
2199 fgets(buf, sizeof(buf), b->rfd);
2200 }
2201 close_bpipe(b);
2202 #endif
2203 }
2204
2205 BOOL CreateChildProcess(VOID);
2206 VOID WriteToPipe(VOID);
2207 VOID ReadFromPipe(VOID);
2208 VOID ErrorExit(LPCSTR);
2209 VOID ErrMsg(LPTSTR, BOOL);
2210
2211 /**
2212 * Check for a quoted path, if an absolute path name is given and it contains
2213 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
2214 * CreateProcess() says the best way to ensure proper results with executables
2215 * with spaces in path or filename is to quote the string.
2216 */
2217 const char *
getArgv0(const char * cmdline)2218 getArgv0(const char *cmdline)
2219 {
2220
2221 int inquote = 0;
2222 const char *cp;
2223 for (cp = cmdline; *cp; cp++)
2224 {
2225 if (*cp == '"') {
2226 inquote = !inquote;
2227 }
2228 if (!inquote && isspace(*cp))
2229 break;
2230 }
2231
2232
2233 int len = cp - cmdline;
2234 char *rval = (char *)malloc(len+1);
2235
2236 cp = cmdline;
2237 char *rp = rval;
2238
2239 while (len--)
2240 *rp++ = *cp++;
2241
2242 *rp = 0;
2243 return rval;
2244 }
2245
2246 /*
2247 * Extracts the executable or script name from the first string in
2248 * cmdline.
2249 *
2250 * If the name contains blanks then it must be quoted with double quotes,
2251 * otherwise quotes are optional. If the name contains blanks then it
2252 * will be converted to a short name.
2253 *
2254 * The optional quotes will be removed. The result is copied to a malloc'ed
2255 * buffer and returned through the pexe argument. The pargs parameter is set
2256 * to the address of the character in cmdline located after the name.
2257 *
2258 * The malloc'ed buffer returned in *pexe must be freed by the caller.
2259 */
2260 bool
GetApplicationName(const char * cmdline,char ** pexe,const char ** pargs)2261 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
2262 {
2263 const char *pExeStart = NULL; /* Start of executable name in cmdline */
2264 const char *pExeEnd = NULL; /* Character after executable name (separator) */
2265
2266 const char *pBasename = NULL; /* Character after last path separator */
2267 const char *pExtension = NULL; /* Period at start of extension */
2268
2269 const char *current = cmdline;
2270
2271 bool bQuoted = false;
2272
2273 /* Skip initial whitespace */
2274
2275 while (*current == ' ' || *current == '\t')
2276 {
2277 current++;
2278 }
2279
2280 /* Calculate start of name and determine if quoted */
2281
2282 if (*current == '"') {
2283 pExeStart = ++current;
2284 bQuoted = true;
2285 } else {
2286 pExeStart = current;
2287 bQuoted = false;
2288 }
2289
2290 *pargs = NULL;
2291 *pexe = NULL;
2292
2293 /*
2294 * Scan command line looking for path separators (/ and \\) and the
2295 * terminator, either a quote or a blank. The location of the
2296 * extension is also noted.
2297 */
2298
2299 for ( ; *current != '\0'; current++)
2300 {
2301 if (*current == '.') {
2302 pExtension = current;
2303 } else if (IsPathSeparator(*current) && current[1] != '\0') {
2304 pBasename = ¤t[1];
2305 pExtension = NULL;
2306 }
2307
2308 /* Check for terminator, either quote or blank */
2309 if (bQuoted) {
2310 if (*current != '"') {
2311 continue;
2312 }
2313 } else {
2314 if (*current != ' ') {
2315 continue;
2316 }
2317 }
2318
2319 /*
2320 * Hit terminator, remember end of name (address of terminator) and
2321 * start of arguments
2322 */
2323 pExeEnd = current;
2324
2325 if (bQuoted && *current == '"') {
2326 *pargs = ¤t[1];
2327 } else {
2328 *pargs = current;
2329 }
2330
2331 break;
2332 }
2333
2334 if (pBasename == NULL) {
2335 pBasename = pExeStart;
2336 }
2337
2338 if (pExeEnd == NULL) {
2339 pExeEnd = current;
2340 }
2341
2342 if (*pargs == NULL)
2343 {
2344 *pargs = current;
2345 }
2346
2347 bool bHasPathSeparators = pExeStart != pBasename;
2348
2349 /* We have pointers to all the useful parts of the name */
2350
2351 /* Default extensions in the order cmd.exe uses to search */
2352
2353 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
2354 DWORD dwBasePathLength = pExeEnd - pExeStart;
2355
2356 DWORD dwAltNameLength = 0;
2357 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2358 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2359
2360 pPathname[MAX_PATHLENGTH] = '\0';
2361 pAltPathname[MAX_PATHLENGTH] = '\0';
2362
2363 memcpy(pPathname, pExeStart, dwBasePathLength);
2364 pPathname[dwBasePathLength] = '\0';
2365
2366 if (pExtension == NULL) {
2367 /* Try appending extensions */
2368 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
2369
2370 if (!bHasPathSeparators) {
2371 /* There are no path separators, search in the standard locations */
2372 dwAltNameLength = SearchPathA(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
2373 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2374 memcpy(pPathname, pAltPathname, dwAltNameLength);
2375 pPathname[dwAltNameLength] = '\0';
2376 break;
2377 }
2378 } else {
2379 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
2380 if (GetFileAttributesA(pPathname) != INVALID_FILE_ATTRIBUTES) {
2381 break;
2382 }
2383 pPathname[dwBasePathLength] = '\0';
2384 }
2385 }
2386 } else if (!bHasPathSeparators) {
2387 /* There are no path separators, search in the standard locations */
2388 dwAltNameLength = SearchPathA(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
2389 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
2390 memcpy(pPathname, pAltPathname, dwAltNameLength);
2391 pPathname[dwAltNameLength] = '\0';
2392 }
2393 }
2394
2395 if (strchr(pPathname, ' ') != NULL) {
2396 dwAltNameLength = GetShortPathNameA(pPathname, pAltPathname, MAX_PATHLENGTH);
2397
2398 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2399 *pexe = (char *)malloc(dwAltNameLength + 1);
2400 if (*pexe == NULL) {
2401 return false;
2402 }
2403 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
2404 }
2405 }
2406
2407 if (*pexe == NULL) {
2408 DWORD dwPathnameLength = strlen(pPathname);
2409 *pexe = (char *)malloc(dwPathnameLength + 1);
2410 if (*pexe == NULL) {
2411 return false;
2412 }
2413 memcpy(*pexe, pPathname, dwPathnameLength + 1);
2414 }
2415
2416 return true;
2417 }
2418
2419 /**
2420 * Create the process with WCHAR API
2421 */
2422 static BOOL
CreateChildProcessW(const char * comspec,const char * cmdLine,PROCESS_INFORMATION * hProcInfo,HANDLE in,HANDLE out,HANDLE err)2423 CreateChildProcessW(const char *comspec, const char *cmdLine,
2424 PROCESS_INFORMATION *hProcInfo,
2425 HANDLE in, HANDLE out, HANDLE err)
2426 {
2427 STARTUPINFOW siStartInfo;
2428 BOOL bFuncRetn = FALSE;
2429
2430 // Set up members of the STARTUPINFO structure.
2431 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2432 siStartInfo.cb = sizeof(siStartInfo);
2433 // setup new process to use supplied handles for stdin,stdout,stderr
2434
2435 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2436 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2437
2438 siStartInfo.hStdInput = in;
2439 siStartInfo.hStdOutput = out;
2440 siStartInfo.hStdError = err;
2441
2442 // Convert argument to WCHAR
2443 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
2444 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2445
2446 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2447 UTF8_2_wchar(&comspec_wchar, comspec);
2448
2449 // Create the child process.
2450 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2451
2452 // try to execute program
2453 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2454 (WCHAR*)cmdLine_wchar,// command line
2455 NULL, // process security attributes
2456 NULL, // primary thread security attributes
2457 TRUE, // handles are inherited
2458 0, // creation flags
2459 NULL, // use parent's environment
2460 NULL, // use parent's current directory
2461 &siStartInfo, // STARTUPINFO pointer
2462 hProcInfo); // receives PROCESS_INFORMATION
2463 free_pool_memory(cmdLine_wchar);
2464 free_pool_memory(comspec_wchar);
2465
2466 return bFuncRetn;
2467 }
2468
2469
2470 /**
2471 * Create the process with ANSI API
2472 */
2473 static BOOL
CreateChildProcessA(const char * comspec,char * cmdLine,PROCESS_INFORMATION * hProcInfo,HANDLE in,HANDLE out,HANDLE err)2474 CreateChildProcessA(const char *comspec, char *cmdLine,
2475 PROCESS_INFORMATION *hProcInfo,
2476 HANDLE in, HANDLE out, HANDLE err)
2477 {
2478 STARTUPINFOA siStartInfo;
2479 BOOL bFuncRetn = FALSE;
2480
2481 // Set up members of the STARTUPINFO structure.
2482 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2483 siStartInfo.cb = sizeof(siStartInfo);
2484 // setup new process to use supplied handles for stdin,stdout,stderr
2485 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2486 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2487
2488 siStartInfo.hStdInput = in;
2489 siStartInfo.hStdOutput = out;
2490 siStartInfo.hStdError = err;
2491
2492 // Create the child process.
2493 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2494
2495 // try to execute program
2496 bFuncRetn = p_CreateProcessA(comspec,
2497 cmdLine, // command line
2498 NULL, // process security attributes
2499 NULL, // primary thread security attributes
2500 TRUE, // handles are inherited
2501 0, // creation flags
2502 NULL, // use parent's environment
2503 NULL, // use parent's current directory
2504 &siStartInfo,// STARTUPINFO pointer
2505 hProcInfo);// receives PROCESS_INFORMATION
2506 return bFuncRetn;
2507 }
2508
2509 /**
2510 * OK, so it would seem CreateProcess only handles true executables:
2511 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2512 */
2513 HANDLE
CreateChildProcess(const char * cmdline,HANDLE in,HANDLE out,HANDLE err)2514 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2515 {
2516 static const char *comspec = NULL;
2517 PROCESS_INFORMATION piProcInfo;
2518 BOOL bFuncRetn = FALSE;
2519
2520 if (!p_CreateProcessA || !p_CreateProcessW)
2521 return INVALID_HANDLE_VALUE;
2522
2523 if (comspec == NULL)
2524 comspec = getenv("COMSPEC");
2525 if (comspec == NULL) // should never happen
2526 return INVALID_HANDLE_VALUE;
2527
2528 // Set up members of the PROCESS_INFORMATION structure.
2529 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2530
2531 // if supplied handles are not used the send a copy of our STD_HANDLE
2532 // as appropriate
2533 if (in == INVALID_HANDLE_VALUE)
2534 in = GetStdHandle(STD_INPUT_HANDLE);
2535
2536 if (out == INVALID_HANDLE_VALUE)
2537 out = GetStdHandle(STD_OUTPUT_HANDLE);
2538
2539 if (err == INVALID_HANDLE_VALUE)
2540 err = GetStdHandle(STD_ERROR_HANDLE);
2541
2542 char *exeFile;
2543 const char *argStart;
2544
2545 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2546 return INVALID_HANDLE_VALUE;
2547 }
2548
2549 POOL_MEM cmdLine(PM_FNAME);
2550 Mmsg(cmdLine, "%s /c \"%s\"%s", comspec, exeFile, argStart);
2551
2552 free(exeFile);
2553
2554 // New function disabled
2555 if (p_CreateProcessW && p_MultiByteToWideChar) {
2556 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2557 in, out, err);
2558 } else {
2559 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2560 in, out, err);
2561 }
2562
2563 if (bFuncRetn == 0) {
2564 ErrorExit("CreateProcess failed\n");
2565 Dmsg2(dbglvl, " CreateProcess(%s, %s) failed\n",comspec,cmdLine.c_str());
2566 return INVALID_HANDLE_VALUE;
2567 }
2568 // we don't need a handle on the process primary thread so we close
2569 // this now.
2570 CloseHandle(piProcInfo.hThread);
2571 return piProcInfo.hProcess;
2572 }
2573
2574 void
ErrorExit(LPCSTR lpszMessage)2575 ErrorExit (LPCSTR lpszMessage)
2576 {
2577 const char *err = errorString();
2578 Dmsg2(dbglvl, "%s: %s", lpszMessage, err);
2579 LocalFree((void *)err);
2580 errno = b_errno_win32;
2581 }
2582
2583
2584 /*
2585 typedef struct s_bpipe {
2586 pid_t worker_pid;
2587 time_t worker_stime;
2588 int wait;
2589 btimer_t *timer_id;
2590 FILE *rfd;
2591 FILE *wfd;
2592 } BPIPE;
2593 */
2594
2595 static void
CloseHandleIfValid(HANDLE handle)2596 CloseHandleIfValid(HANDLE handle)
2597 {
2598 if (handle != INVALID_HANDLE_VALUE) {
2599 CloseHandle(handle);
2600 }
2601 }
2602
2603 #define MODE_READ 1
2604 #define MODE_WRITE 2
2605 #define MODE_SHELL 4
2606 #define MODE_STDERR 8
2607
2608 BPIPE *
open_bpipe(char * prog,int wait,const char * mode,char * envp[])2609 open_bpipe(char *prog, int wait, const char *mode, char *envp[])
2610 {
2611 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2612 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2613 hChildStderrRd, hChildStderrWr, hChildStderrRdDup,
2614 hInputFile;
2615
2616 SECURITY_ATTRIBUTES saAttr;
2617
2618 BOOL fSuccess;
2619
2620 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2621 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2622 hChildStderrRd = hChildStderrWr = hChildStderrRdDup =
2623 hInputFile = INVALID_HANDLE_VALUE;
2624
2625 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2626 memset((void *)bpipe, 0, sizeof(BPIPE));
2627
2628 int mode_map(0);
2629 if (strchr(mode,'r')) mode_map|=MODE_READ;
2630 if (strchr(mode,'w')) mode_map|=MODE_WRITE;
2631 if (strchr(mode,'s')) mode_map|=MODE_SHELL;
2632 if (strchr(mode,'e')) mode_map|=MODE_STDERR;
2633
2634 // Set the bInheritHandle flag so pipe handles are inherited.
2635 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2636 saAttr.bInheritHandle = TRUE;
2637 saAttr.lpSecurityDescriptor = NULL;
2638
2639 if (mode_map & MODE_READ) {
2640
2641 // Create a pipe for the child process's STDOUT.
2642 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2643 ErrorExit("Stdout pipe creation failed\n");
2644 goto cleanup;
2645 }
2646 // Create noninheritable read handle and close the inheritable read
2647 // handle.
2648
2649 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2650 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2651 FALSE,
2652 DUPLICATE_SAME_ACCESS);
2653 if ( !fSuccess ) {
2654 ErrorExit("DuplicateHandle failed");
2655 goto cleanup;
2656 }
2657
2658 CloseHandle(hChildStdoutRd);
2659 hChildStdoutRd = INVALID_HANDLE_VALUE;
2660 }
2661
2662 if (mode_map & MODE_STDERR) {
2663
2664 // Create a pipe for the child process's STDOUT.
2665 if (! CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) {
2666 ErrorExit("Stderr pipe creation failed\n");
2667 goto cleanup;
2668 }
2669 // Create noninheritable read handle and close the inheritable read
2670 // handle.
2671
2672 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStderrRd,
2673 GetCurrentProcess(), &hChildStderrRdDup , 0,
2674 FALSE,
2675 DUPLICATE_SAME_ACCESS);
2676 if ( !fSuccess ) {
2677 ErrorExit("DuplicateHandle failed");
2678 goto cleanup;
2679 }
2680
2681 CloseHandle(hChildStderrRd);
2682 hChildStderrRd = INVALID_HANDLE_VALUE;
2683 }
2684
2685 if (mode_map & MODE_WRITE) {
2686
2687 // Create a pipe for the child process's STDIN.
2688
2689 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2690 ErrorExit("Stdin pipe creation failed\n");
2691 goto cleanup;
2692 }
2693
2694 // Duplicate the write handle to the pipe so it is not inherited.
2695 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2696 GetCurrentProcess(), &hChildStdinWrDup,
2697 0,
2698 FALSE, // not inherited
2699 DUPLICATE_SAME_ACCESS);
2700 if (!fSuccess) {
2701 ErrorExit("DuplicateHandle failed");
2702 goto cleanup;
2703 }
2704
2705 CloseHandle(hChildStdinWr);
2706 hChildStdinWr = INVALID_HANDLE_VALUE;
2707 }
2708 // spawn program with redirected handles as appropriate
2709 bpipe->worker_pid = (pid_t)
2710 CreateChildProcess(prog, // commandline
2711 hChildStdinRd, // stdin HANDLE
2712 hChildStdoutWr, // stdout HANDLE
2713 (mode_map & MODE_STDERR) ? hChildStderrWr:hChildStdoutWr); // stderr HANDLE
2714
2715 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE) {
2716 ErrorExit("CreateChildProcess failed");
2717 goto cleanup;
2718 }
2719
2720 bpipe->wait = wait;
2721 bpipe->worker_stime = time(NULL);
2722
2723 if (mode_map & MODE_READ) {
2724 CloseHandle(hChildStdoutWr); // close our write side so when
2725 // process terminates we can
2726 // detect eof.
2727 // ugly but convert WIN32 HANDLE to FILE*
2728 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2729 if (rfd >= 0) {
2730 bpipe->rfd = _fdopen(rfd, "rb");
2731 }
2732 }
2733 if (mode_map & MODE_STDERR) {
2734 CloseHandle(hChildStderrWr); // close our write side so when
2735 // process terminates we can
2736 // detect eof.
2737 // ugly but convert WIN32 HANDLE to FILE*
2738 int rfd = _open_osfhandle((intptr_t)hChildStderrRdDup, O_RDONLY | O_BINARY);
2739 if (rfd >= 0) {
2740 bpipe->efd = _fdopen(rfd, "rb");
2741 }
2742 }
2743 if (mode_map & MODE_WRITE) {
2744 CloseHandle(hChildStdinRd); // close our read side so as not
2745 // to interfre with child's copy
2746 // ugly but convert WIN32 HANDLE to FILE*
2747 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2748 if (wfd >= 0) {
2749 bpipe->wfd = _fdopen(wfd, "wb");
2750 }
2751 }
2752
2753 if (wait > 0) {
2754 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2755 }
2756
2757 return bpipe;
2758
2759 cleanup:
2760
2761 CloseHandleIfValid(hChildStdoutWr);
2762 CloseHandleIfValid(hChildStdoutRd);
2763 CloseHandleIfValid(hChildStdoutRdDup);
2764 CloseHandleIfValid(hChildStderrWr);
2765 CloseHandleIfValid(hChildStderrRd);
2766 CloseHandleIfValid(hChildStderrRdDup);
2767 CloseHandleIfValid(hChildStdinWr);
2768 CloseHandleIfValid(hChildStdinRd);
2769 CloseHandleIfValid(hChildStdinWrDup);
2770
2771 free((void *)bpipe);
2772 errno = b_errno_win32; /* do GetLastError() for error code */
2773 return NULL;
2774 }
2775
2776
2777 int
kill(pid_t pid,int signal)2778 kill(pid_t pid, int signal)
2779 {
2780 int rval = 0;
2781 if (!TerminateProcess((HANDLE)pid, (UINT)signal)) {
2782 rval = -1;
2783 errno = b_errno_win32;
2784 }
2785 CloseHandle((HANDLE)pid);
2786 return rval;
2787 }
2788
2789
2790 int
close_bpipe(BPIPE * bpipe)2791 close_bpipe(BPIPE *bpipe)
2792 {
2793 int rval = 0;
2794 int32_t remaining_wait = bpipe->wait;
2795
2796 /* Close pipes */
2797 if (bpipe->rfd) {
2798 fclose(bpipe->rfd);
2799 bpipe->rfd = NULL;
2800 }
2801 if (bpipe->efd) {
2802 fclose(bpipe->efd);
2803 bpipe->efd = NULL;
2804 }
2805 if (bpipe->wfd) {
2806 fclose(bpipe->wfd);
2807 bpipe->wfd = NULL;
2808 }
2809
2810 if (remaining_wait == 0) { /* wait indefinitely */
2811 remaining_wait = INT32_MAX;
2812 }
2813 for ( ;; ) {
2814 DWORD exitCode;
2815 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2816 const char *err = errorString();
2817 rval = b_errno_win32;
2818 Dmsg1(dbglvl, "GetExitCode error %s\n", err);
2819 LocalFree((void *)err);
2820 break;
2821 }
2822 if (exitCode == STILL_ACTIVE) {
2823 if (remaining_wait <= 0) {
2824 rval = ETIME; /* timed out */
2825 break;
2826 }
2827 bmicrosleep(1, 0); /* wait one second */
2828 remaining_wait--;
2829 } else if (exitCode != 0) {
2830 /* Truncate exit code as it doesn't seem to be correct */
2831 rval = (exitCode & 0xFF) | b_errno_exit;
2832 break;
2833 } else {
2834 break; /* Shouldn't get here */
2835 }
2836 }
2837
2838 if (bpipe->timer_id) {
2839 stop_child_timer(bpipe->timer_id);
2840 }
2841 if (bpipe->rfd) fclose(bpipe->rfd);
2842 if (bpipe->efd) fclose(bpipe->efd);
2843 if (bpipe->wfd) fclose(bpipe->wfd);
2844 free((void *)bpipe);
2845 return rval;
2846 }
2847
2848 int
close_wpipe(BPIPE * bpipe)2849 close_wpipe(BPIPE *bpipe)
2850 {
2851 int result = 1;
2852
2853 if (bpipe->wfd) {
2854 fflush(bpipe->wfd);
2855 if (fclose(bpipe->wfd) != 0) {
2856 result = 0;
2857 }
2858 bpipe->wfd = NULL;
2859 }
2860 return result;
2861 }
2862
2863 /* Close the stderror pipe only */
close_epipe(BPIPE * bpipe)2864 int close_epipe(BPIPE *bpipe)
2865 {
2866 int stat = 1;
2867
2868 if (bpipe->efd) {
2869 if (fclose(bpipe->efd) != 0) {
2870 stat = 0;
2871 }
2872 bpipe->efd = NULL;
2873 }
2874 return stat;
2875 }
2876
2877 #ifndef MINGW64
2878 int
utime(const char * fname,struct utimbuf * times)2879 utime(const char *fname, struct utimbuf *times)
2880 {
2881 FILETIME acc, mod;
2882 char tmpbuf[5000];
2883 POOL_MEM pwszBuf(PM_FNAME);
2884
2885 cvt_utime_to_ftime(times->actime, acc);
2886 cvt_utime_to_ftime(times->modtime, mod);
2887
2888 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), fname);
2889
2890 HANDLE h = p_CreateFileW((LPCWSTR)pwszBuf.c_str(),
2891 FILE_WRITE_ATTRIBUTES,
2892 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2893 NULL,
2894 OPEN_EXISTING,
2895 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2896 NULL);
2897
2898 if (h == INVALID_HANDLE_VALUE) {
2899 const char *err = errorString();
2900 Dmsg2(dbglvl, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2901 LocalFree((void *)err);
2902 errno = b_errno_win32;
2903 return -1;
2904 }
2905
2906 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2907 CloseHandle(h);
2908 if (rval == -1) {
2909 errno = b_errno_win32;
2910 }
2911 return rval;
2912 }
2913 #endif
2914
2915 #if 0
2916 int
2917 file_open(const char *file, int flags, int mode)
2918 {
2919 DWORD access = 0;
2920 DWORD shareMode = 0;
2921 DWORD create = 0;
2922 DWORD msflags = 0;
2923 HANDLE foo = INVALID_HANDLE_VALUE;
2924 const char *remap = file;
2925
2926 if (flags & O_WRONLY) access = GENERIC_WRITE;
2927 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2928 else access = GENERIC_READ;
2929
2930 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2931 create = CREATE_NEW;
2932 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2933 create = CREATE_ALWAYS;
2934 else if (flags & O_CREAT)
2935 create = OPEN_ALWAYS;
2936 else if (flags & O_TRUNC)
2937 create = TRUNCATE_EXISTING;
2938 else
2939 create = OPEN_EXISTING;
2940
2941 shareMode = 0;
2942
2943 if (flags & O_APPEND) {
2944 printf("open...APPEND not implemented yet.");
2945 exit(-1);
2946 }
2947
2948 POOL_MEM pwszBuf(PM_FNAME);
2949 make_win32_path_UTF8_2_wchar(&pwszBuf.addr(), file);
2950 foo = p_CreateFileW((LPCWSTR) pwszBuf.c_str(), access, shareMode, NULL, create, msflags, NULL);
2951
2952 if (INVALID_HANDLE_VALUE == foo) {
2953 errno = b_errno_win32;
2954 return (int)-1;
2955 }
2956 return (int)foo;
2957
2958 }
2959
2960
2961 int
2962 file_close(int fd)
2963 {
2964 if (!CloseHandle((HANDLE)fd)) {
2965 errno = b_errno_win32;
2966 return -1;
2967 }
2968
2969 return 0;
2970 }
2971
2972 ssize_t
2973 file_write(int fd, const void *data, ssize_t len)
2974 {
2975 BOOL status;
2976 DWORD bwrite;
2977 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2978 if (status) return bwrite;
2979 errno = b_errno_win32;
2980 return -1;
2981 }
2982
2983
2984 ssize_t
2985 file_read(int fd, void *data, ssize_t len)
2986 {
2987 BOOL status;
2988 DWORD bread;
2989
2990 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2991 if (status) return bread;
2992 errno = b_errno_win32;
2993 return -1;
2994 }
2995
2996 boffset_t
2997 file_seek(int fd, boffset_t offset, int whence)
2998 {
2999 DWORD method = 0;
3000 DWORD val;
3001 LONG offset_low = (LONG)offset;
3002 LONG offset_high = (LONG)(offset >> 32);
3003
3004 switch (whence) {
3005 case SEEK_SET :
3006 method = FILE_BEGIN;
3007 break;
3008 case SEEK_CUR:
3009 method = FILE_CURRENT;
3010 break;
3011 case SEEK_END:
3012 method = FILE_END;
3013 break;
3014 default:
3015 errno = EINVAL;
3016 return -1;
3017 }
3018
3019
3020 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
3021 errno = b_errno_win32;
3022 return -1;
3023 }
3024 /* ***FIXME*** I doubt this works right */
3025 return val;
3026 }
3027
3028 int
3029 file_dup2(int, int)
3030 {
3031 errno = ENOSYS;
3032 return -1;
3033 }
3034 #endif
3035
3036 #ifdef xxx
3037 /*
3038 * Emulation of mmap and unmmap for tokyo dbm
3039 */
mmap(void * start,size_t length,int prot,int flags,int fd,off_t offset)3040 void *mmap(void *start, size_t length, int prot, int flags,
3041 int fd, off_t offset)
3042 {
3043 DWORD fm_access = 0;
3044 DWORD mv_access = 0;
3045 HANDLE h;
3046 HANDLE mv;
3047
3048 if (length == 0) {
3049 return MAP_FAILED;
3050 }
3051 if (!fd) {
3052 return MAP_FAILED;
3053 }
3054
3055 if (flags & PROT_WRITE) {
3056 fm_access |= PAGE_READWRITE;
3057 } else if (flags & PROT_READ) {
3058 fm_access |= PAGE_READONLY;
3059 }
3060
3061 if (flags & PROT_READ) {
3062 mv_access |= FILE_MAP_READ;
3063 }
3064 if (flags & PROT_WRITE) {
3065 mv_access |= FILE_MAP_WRITE;
3066 }
3067
3068 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
3069 NULL /* security */,
3070 fm_access,
3071 0 /* MaximumSizeHigh */,
3072 0 /* MaximumSizeLow */,
3073 NULL /* name of the file mapping object */);
3074
3075 if (!h || h == INVALID_HANDLE_VALUE) {
3076 return MAP_FAILED;
3077 }
3078
3079 mv = MapViewOfFile(h, mv_access,
3080 0 /* offset hi */,
3081 0 /* offset lo */,
3082 length);
3083 CloseHandle(h);
3084
3085 if (!mv || mv == INVALID_HANDLE_VALUE) {
3086 return MAP_FAILED;
3087 }
3088
3089 return (void *) mv;
3090 }
3091
munmap(void * start,size_t length)3092 int munmap(void *start, size_t length)
3093 {
3094 if (!start) {
3095 return -1;
3096 }
3097 UnmapViewOfFile(start);
3098 return 0;
3099 }
3100 #endif
3101
3102 #ifdef HAVE_MINGW
3103 /* syslog function, added by Nicolas Boichat */
openlog(const char * ident,int option,int facility)3104 void openlog(const char *ident, int option, int facility) {}
3105 #endif //HAVE_MINGW
3106
3107 /* Log an error message */
LogErrorMsg(const char * message)3108 void LogErrorMsg(const char *message)
3109 {
3110 HANDLE eventHandler;
3111 const char *strings[2];
3112
3113 /* Use the OS event logging to log the error */
3114 eventHandler = RegisterEventSource(NULL, "Bacula");
3115
3116 strings[0] = _("\n\nBacula ERROR: ");
3117 strings[1] = message;
3118
3119 if (eventHandler) {
3120 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
3121 0, /* category */
3122 0, /* ID */
3123 NULL, /* SID */
3124 2, /* Number of strings */
3125 0, /* raw data size */
3126 (const char **)strings, /* error strings */
3127 NULL); /* raw data */
3128 DeregisterEventSource(eventHandler);
3129 }
3130 }
3131
mkstemp(char * t)3132 int mkstemp(char *t)
3133 {
3134 char *filename = mktemp(t);
3135 if (filename == NULL) {
3136 return -1;
3137 }
3138 return open(filename, O_RDWR | O_CREAT, 0600);
3139 }
3140
malloc_trim(int)3141 void malloc_trim(int)
3142 {
3143 if (p_EmptyWorkingSet) {
3144 HANDLE hProcess = GetCurrentProcess();
3145 if (!p_EmptyWorkingSet(hProcess)) {
3146 const char *err = errorString();
3147 Dmsg1(dbglvl, "EmptyWorkingSet() = %s\n", err);
3148 LocalFree((void *)err);
3149 }
3150 CloseHandle( hProcess );
3151 }
3152 }
3153
3154 /*
3155 * get win32 memory information.
3156 * when buf is NULL then it return the process WorkingSetSize (used as Unix heap) only.
3157 */
get_memory_info(char * buf,int buflen)3158 uint64_t get_memory_info(char *buf, int buflen)
3159 {
3160 char ed1[50], ed2[50], ed3[50], ed4[50];
3161 uint64_t ret=0;
3162 HANDLE hProcess = GetCurrentProcess();
3163 PROCESS_MEMORY_COUNTERS pmc;
3164
3165 if (!buf) {
3166 return ret;
3167 }
3168 if (buf){
3169 buf[0] = '\0';
3170 }
3171 if (p_GetProcessMemoryInfo) {
3172 if (p_GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
3173 if (buf){
3174 bsnprintf(buf, buflen,
3175 "WorkingSetSize: %s QuotaPagedPoolUsage: %s QuotaNonPagedPoolUsage: %s PagefileUsage: %s",
3176 edit_uint64_with_commas(pmc.WorkingSetSize, ed1),
3177 edit_uint64_with_commas(pmc.QuotaPagedPoolUsage, ed2),
3178 edit_uint64_with_commas(pmc.QuotaNonPagedPoolUsage, ed3),
3179 edit_uint64_with_commas(pmc.PagefileUsage, ed4));
3180 }
3181 ret = pmc.WorkingSetSize;
3182
3183 } else {
3184 const char *err = errorString();
3185 bsnprintf(buf, buflen, "%s", err);
3186 LocalFree((void *)err);
3187 }
3188 }
3189
3190 CloseHandle( hProcess );
3191 return ret;
3192 }
3193
3194 /* normalize path provided by the fileset File directive
3195 * "f:" -> "f:/" add a ':' to "relative" to drive path
3196 */
win32_normalize_fileset_path(POOLMEM * & fname)3197 void win32_normalize_fileset_path(POOLMEM *&fname)
3198 {
3199 if (isalpha(fname[0]) && fname[1] == ':' && fname[2] == '\0') {
3200 fname = check_pool_memory_size(fname, 3);
3201 fname[2]='/';
3202 fname[3]='\0';
3203 }
3204 }
3205
3206 /*
3207 int test_wchar(const wchar_t *st, const char *comment)
3208 {
3209 int err=0;
3210 char ascii[4096];
3211 char hexa[4096];
3212 POOL_MEM name(PM_FNAME);
3213 POOL_MEM name2(PM_FNAME);
3214
3215 name.strcpy("");
3216 name2.strcpy("");
3217 int owsl=wcslen(st);
3218 printf(" ----- %s -- %s ----- %s\n", asciidump((char*)st, 2*owsl, ascii, sizeof(ascii)), hexdump((char*)st, 2*owsl, hexa, sizeof(hexa)), comment);
3219 int l=wchar_path_2_wutf8(&name.addr(), st);
3220 int sl=strlen(name.c_str());
3221 printf(" \t=> len=%d(%d+1) -- %s -- %s -- \n", l, sl, asciidump(name.c_str(), l, ascii, sizeof(ascii)), hexdump(name.c_str(), l, hexa, sizeof(hexa)));
3222 int wl=wutf8_path_2_wchar(&name2.addr(), name.c_str());
3223 int wsl=wcslen((wchar_t *)name2.c_str());
3224 printf(" \t<= len=%d(%d+1) -- %s -- %s -- \n", wl, wsl, asciidump(name2.c_str(), wl, ascii, sizeof(ascii)), hexdump(name2.c_str(), wl, hexa, sizeof(hexa)));
3225 if (memcmp((wchar_t *)name2.c_str(), st, 2*(wcslen(st)+1))!=0 ||
3226 owsl != wsl || l!=sl+1) {
3227 err=1;
3228 printf(" \tERROR !!!!!!!!!!\n");
3229 } else {
3230 printf(" \tOK\n");
3231 }
3232 return err;
3233 }
3234
3235 int test_all_wchar()
3236 {
3237 printf("test_all_wchar\n");
3238 test_wchar(L"Hello world", "Hello world (valid)");
3239 test_wchar(L"Hello*world", "must escape * in the middle");
3240 test_wchar(L"*Hello**world*", "must escape * in the middle");
3241 test_wchar(L"Hello\x63dfworld", "Hello world with a strange char in the middle");
3242 test_wchar(L"Hello\xdf63world", "Hello world with invalid char in the middle");
3243 test_wchar((const wchar_t*)"\x41\x00\x63\xd9\x63\xdd\x42\x00\x00\x00", "A<valide surrogate>B");
3244 test_wchar((const wchar_t*)"\x41\x00\x63\xdf\x42\x00\x00\x00", "A<invalid single char>B");
3245 test_wchar((const wchar_t*)"\x41\x00\x63\xdc\x63\xdd\x42\x00\x00\x00", "A<w1 is out of 0xD800 and 0xDBFF>B");
3246 test_wchar((const wchar_t*)"\x41\x00\x63\xd8\x63\x63\x42\x00\x00\x00", "A<w2 is out of 0xDC00 and 0xDBFF but only w1 will be escaped>B");
3247 return 0;
3248 }
3249 */
3250