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