]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/compat/compat.cpp
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / win32 / compat / compat.cpp
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 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
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
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
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
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  */
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 */
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*
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
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
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
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
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
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+
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
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
565 void *dlsym(void *handle, const char *name)
566 {
567    void *symaddr;
568    symaddr = (void *)GetProcAddress((HMODULE)handle, name);
569    return symaddr;
570 }
571
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
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
590 int fcntl(int fd, int cmd)
591 {
592    return 0;
593 }
594
595 int chown(const char *k, uid_t, gid_t)
596 {
597    return 0;
598 }
599
600 int lchown(const char *k, uid_t, gid_t)
601 {
602    return 0;
603 }
604
605 long int
606 random(void)
607 {
608     return rand();
609 }
610
611 void
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
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
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
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
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
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
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()) */
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 */
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
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
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  */
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
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
1235 lstat(const char *file, struct stat *sb)
1236 {
1237    return stat(file, sb);
1238 }
1239
1240 void
1241 sleep(int sec)
1242 {
1243    Sleep(sec * 1000);
1244 }
1245
1246 int
1247 geteuid(void)
1248 {
1249    return 0;
1250 }
1251
1252 int
1253 execvp(const char *, char *[]) {
1254    errno = ENOSYS;
1255    return -1;
1256 }
1257
1258
1259 int
1260 fork(void)
1261 {
1262    errno = ENOSYS;
1263    return -1;
1264 }
1265
1266 int
1267 pipe(int[])
1268 {
1269    errno = ENOSYS;
1270    return -1;
1271 }
1272
1273 int
1274 waitpid(int, int*, int)
1275 {
1276    errno = ENOSYS;
1277    return -1;
1278 }
1279
1280 #ifndef HAVE_MINGW
1281 int
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
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
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  */
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
1381 closelog()
1382 {
1383 }
1384
1385 struct passwd *
1386 getpwuid(uid_t)
1387 {
1388     return NULL;
1389 }
1390
1391 struct group *
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_DATAA data_a;    // window's file info (ansii version)
1402     WIN32_FIND_DATAW data_w;    // window's file info (wchar version)
1403     const char *spec;           // the directory we're traversing
1404     HANDLE      dirh;           // the search handle
1405     BOOL        valid_a;        // the info in data_a field is valid
1406     BOOL        valid_w;        // the info in data_w field is valid
1407     UINT32      offset;         // pseudo offset for d_off
1408 } _dir;
1409
1410 DIR *
1411 opendir(const char *path)
1412 {
1413     /* enough space for VSS !*/
1414     int max_len = strlen(path) + MAX_PATH;
1415     char *tspec = NULL;
1416     _dir *rval = NULL;
1417     if (path == NULL) {
1418        errno = ENOENT;
1419        return NULL;
1420     }
1421
1422     Dmsg1(dbglvl, "Opendir path=%s\n", path);
1423     rval = (_dir *)malloc(sizeof(_dir));
1424     if (!rval) {
1425        goto err;
1426     }
1427     memset (rval, 0, sizeof (_dir));
1428
1429     tspec = (char *)malloc(max_len);
1430     if (!tspec) {
1431        goto err;
1432     }
1433
1434     conv_unix_to_vss_win32_path(path, tspec, max_len);
1435     Dmsg1(dbglvl, "win32 path=%s\n", tspec);
1436
1437     // add backslash only if there is none yet (think of c:\)
1438     if (tspec[strlen(tspec)-1] != '\\')
1439       bstrncat(tspec, "\\*", max_len);
1440     else
1441       bstrncat(tspec, "*", max_len);
1442
1443     rval->spec = tspec;
1444
1445     // convert to wchar_t
1446     if (p_FindFirstFileW) {
1447       POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1448       make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1449
1450       rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1451
1452       free_pool_memory(pwcBuf);
1453
1454       if (rval->dirh != INVALID_HANDLE_VALUE)
1455         rval->valid_w = 1;
1456     } else if (p_FindFirstFileA) {
1457       rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1458
1459       if (rval->dirh != INVALID_HANDLE_VALUE)
1460         rval->valid_a = 1;
1461     } else goto err;
1462
1463
1464     Dmsg3(dbglvl, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1465           path, rval->spec, rval->dirh);
1466
1467     rval->offset = 0;
1468     if (rval->dirh == INVALID_HANDLE_VALUE)
1469         goto err;
1470
1471     if (rval->valid_w) {
1472       Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_w.cFileName);
1473     }
1474
1475     if (rval->valid_a) {
1476       Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_a.cFileName);
1477     }
1478
1479     return (DIR *)rval;
1480
1481 err:
1482     if (rval) {
1483        free(rval);
1484     }
1485     if (tspec) {
1486        free(tspec);
1487     }
1488     errno = b_errno_win32;
1489     return NULL;
1490 }
1491
1492 int
1493 closedir(DIR *dirp)
1494 {
1495     _dir *dp = (_dir *)dirp;
1496     FindClose(dp->dirh);
1497     free((void *)dp->spec);
1498     free((void *)dp);
1499     return 0;
1500 }
1501
1502 /*
1503   typedef struct _WIN32_FIND_DATA {
1504     DWORD dwFileAttributes;
1505     FILETIME ftCreationTime;
1506     FILETIME ftLastAccessTime;
1507     FILETIME ftLastWriteTime;
1508     DWORD nFileSizeHigh;
1509     DWORD nFileSizeLow;
1510     DWORD dwReserved0;
1511     DWORD dwReserved1;
1512     TCHAR cFileName[MAX_PATH];
1513     TCHAR cAlternateFileName[14];
1514 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1515 */
1516
1517 static int
1518 copyin(struct dirent &dp, const char *fname)
1519 {
1520     dp.d_ino = 0;
1521     dp.d_reclen = 0;
1522     char *cp = dp.d_name;
1523     while (*fname) {
1524         *cp++ = *fname++;
1525         dp.d_reclen++;
1526     }
1527         *cp = 0;
1528     return dp.d_reclen;
1529 }
1530
1531 int
1532 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1533 {
1534     _dir *dp = (_dir *)dirp;
1535     if (dp->valid_w || dp->valid_a) {
1536       entry->d_off = dp->offset;
1537
1538       // copy unicode
1539       if (dp->valid_w) {
1540          POOLMEM *szBuf = get_pool_memory(PM_NAME);
1541          wchar_2_UTF8(&szBuf, dp->data_w.cFileName);
1542          dp->offset += copyin(*entry, szBuf);
1543          free_pool_memory(szBuf);
1544       } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1545          dp->offset += copyin(*entry, dp->data_a.cFileName);
1546       }
1547
1548       *result = entry;              /* return entry address */
1549       Dmsg4(dbglvl, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1550             dirp, entry->d_name, entry->d_reclen, entry->d_off);
1551     } else {
1552 //      Dmsg0(dbglvl, "readdir_r !valid\n");
1553         errno = b_errno_win32;
1554         return -1;
1555     }
1556
1557     // get next file, try unicode first
1558     if (p_FindNextFileW)
1559        dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1560     else if (p_FindNextFileA)
1561        dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1562     else {
1563        dp->valid_a = FALSE;
1564        dp->valid_w = FALSE;
1565     }
1566
1567     return 0;
1568 }
1569
1570 /*
1571  * Dotted IP address to network address
1572  *
1573  * Returns 1 if  OK
1574  *         0 on error
1575  */
1576 int
1577 inet_aton(const char *a, struct in_addr *inp)
1578 {
1579    const char *cp = a;
1580    uint32_t acc = 0, tmp = 0;
1581    int dotc = 0;
1582
1583    if (!isdigit(*cp)) {         /* first char must be digit */
1584       return 0;                 /* error */
1585    }
1586    do {
1587       if (isdigit(*cp)) {
1588          tmp = (tmp * 10) + (*cp -'0');
1589       } else if (*cp == '.' || *cp == 0) {
1590          if (tmp > 255) {
1591             return 0;           /* error */
1592          }
1593          acc = (acc << 8) + tmp;
1594          dotc++;
1595          tmp = 0;
1596       } else {
1597          return 0;              /* error */
1598       }
1599    } while (*cp++ != 0);
1600    if (dotc != 4) {              /* want 3 .'s plus EOS */
1601       return 0;                  /* error */
1602    }
1603    inp->s_addr = htonl(acc);     /* store addr in network format */
1604    return 1;
1605 }
1606
1607
1608 /*
1609  *    Convert from presentation format (which usually means ASCII printable)
1610  *     to network format (which is usually some kind of binary format).
1611  * return:
1612  *    1 if the address was valid for the specified address family
1613  *    0 if the address wasn't valid (`dst' is untouched in this case)
1614  */
1615 int
1616 binet_pton(int af, const char *src, void *dst)
1617 {
1618    switch (af) {
1619    case AF_INET:
1620    case AF_INET6:
1621       if (p_InetPton) {
1622          return p_InetPton(af, src, dst);
1623       }
1624       return 0;
1625    default:
1626       return 0;
1627    }
1628 }
1629
1630
1631 int
1632 nanosleep(const struct timespec *req, struct timespec *rem)
1633 {
1634     if (rem)
1635         rem->tv_sec = rem->tv_nsec = 0;
1636     Sleep((req->tv_sec * 1000) + (req->tv_nsec/1000000));
1637     return 0;
1638 }
1639
1640 void
1641 init_signals(void terminate(int sig))
1642 {
1643
1644 }
1645
1646 void
1647 init_stack_dump(void)
1648 {
1649
1650 }
1651
1652
1653 long
1654 pathconf(const char *path, int name)
1655 {
1656     switch(name) {
1657     case _PC_PATH_MAX :
1658         if (strncmp(path, "\\\\?\\", 4) == 0)
1659             return 32767;
1660     case _PC_NAME_MAX :
1661         return 255;
1662     }
1663     errno = ENOSYS;
1664     return -1;
1665 }
1666
1667 int
1668 WSA_Init(void)
1669 {
1670     WORD wVersionRequested = MAKEWORD(2, 2);
1671     WSADATA wsaData;
1672
1673     int err = WSAStartup(wVersionRequested, &wsaData);
1674     if (err != 0) {
1675        wVersionRequested = MAKEWORD(2, 0);
1676        err = WSAStartup(wVersionRequested, &wsaData);
1677        if (err != 0) {
1678           wVersionRequested = MAKEWORD(1, 1);
1679           err = WSAStartup(wVersionRequested, &wsaData);
1680        }
1681     }
1682
1683     if (err != 0) {
1684         printf("Can not start Windows Sockets\n");
1685         errno = ENOSYS;
1686         return -1;
1687     }
1688
1689     return 0;
1690 }
1691
1692 static DWORD fill_attribute(DWORD attr, mode_t mode)
1693 {
1694    Dmsg1(dbglvl, "  before attr=%lld\n", (uint64_t) attr);
1695    /* Use Bacula mappings define in stat() above */
1696    if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1697       attr &= ~FILE_ATTRIBUTE_READONLY;    // then this is not READONLY
1698    } else {
1699       attr |= FILE_ATTRIBUTE_READONLY;
1700    }
1701    if (mode & S_ISVTX) {                   // The sticky bit <=> HIDDEN
1702       attr |= FILE_ATTRIBUTE_HIDDEN;
1703    } else {
1704       attr &= ~FILE_ATTRIBUTE_HIDDEN;
1705    }
1706    if (mode & S_ISGID) {                   // The set group ID <=> ENCRYPTED
1707       attr |= FILE_ATTRIBUTE_ENCRYPTED;
1708    } else {
1709       attr &= ~FILE_ATTRIBUTE_ENCRYPTED;
1710    }
1711    if (mode & S_IRWXO) {              // Other can read/write/execute ?
1712       attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1713    } else {
1714       attr |= FILE_ATTRIBUTE_SYSTEM;
1715    }
1716    Dmsg1(dbglvl, "  after attr=%lld\n", (uint64_t)attr);
1717    return attr;
1718 }
1719
1720 int win32_chmod(const char *path, mode_t mode)
1721 {
1722    bool ret=false;
1723    DWORD attr;
1724
1725    Dmsg2(dbglvl, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1726    if (p_GetFileAttributesW) {
1727       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1728       make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1729
1730       attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1731       if (attr != INVALID_FILE_ATTRIBUTES) {
1732          /* Use Bacula mappings define in stat() above */
1733          attr = fill_attribute(attr, mode);
1734          ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1735       }
1736       free_pool_memory(pwszBuf);
1737       Dmsg0(dbglvl, "Leave win32_chmod. AttributesW\n");
1738    } else if (p_GetFileAttributesA) {
1739       attr = p_GetFileAttributesA(path);
1740       if (attr != INVALID_FILE_ATTRIBUTES) {
1741          attr = fill_attribute(attr, mode);
1742          ret = p_SetFileAttributesA(path, attr);
1743       }
1744       Dmsg0(dbglvl, "Leave win32_chmod did AttributesA\n");
1745    } else {
1746       Dmsg0(dbglvl, "Leave win32_chmod did nothing\n");
1747    }
1748
1749    if (!ret) {
1750       const char *err = errorString();
1751       Dmsg2(dbglvl, "Get/SetFileAttributes(%s): %s\n", path, err);
1752       LocalFree((void *)err);
1753       errno = b_errno_win32;
1754       return -1;
1755    }
1756    return 0;
1757 }
1758
1759
1760 int
1761 win32_chdir(const char *dir)
1762 {
1763    if (p_SetCurrentDirectoryW) {
1764       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1765       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1766
1767       BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1768
1769       free_pool_memory(pwszBuf);
1770
1771       if (!b) {
1772          errno = b_errno_win32;
1773          return -1;
1774       }
1775    } else if (p_SetCurrentDirectoryA) {
1776       if (0 == p_SetCurrentDirectoryA(dir)) {
1777          errno = b_errno_win32;
1778          return -1;
1779       }
1780    } else {
1781       return -1;
1782    }
1783
1784    return 0;
1785 }
1786
1787 int
1788 win32_mkdir(const char *dir)
1789 {
1790    Dmsg1(dbglvl, "enter win32_mkdir. dir=%s\n", dir);
1791    if (p_wmkdir){
1792       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1793       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1794
1795       int n = p_wmkdir((LPCWSTR)pwszBuf);
1796       free_pool_memory(pwszBuf);
1797       Dmsg0(dbglvl, "Leave win32_mkdir did wmkdir\n");
1798       return n;
1799    }
1800
1801    Dmsg0(dbglvl, "Leave win32_mkdir did _mkdir\n");
1802    return _mkdir(dir);
1803 }
1804
1805
1806 char *
1807 win32_getcwd(char *buf, int maxlen)
1808 {
1809    int n=0;
1810
1811    if (p_GetCurrentDirectoryW) {
1812       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1813       pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1814
1815       n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1816       if (n!=0)
1817          n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1818       free_pool_memory(pwszBuf);
1819
1820    } else if (p_GetCurrentDirectoryA)
1821       n = p_GetCurrentDirectoryA(maxlen, buf);
1822
1823    if (n <= 0 || n > maxlen) return NULL;
1824
1825    if (n+1 > maxlen) return NULL;
1826    if (n != 3) {
1827        buf[n] = '\\';
1828        buf[n+1] = 0;
1829    }
1830    return buf;
1831 }
1832
1833 int
1834 win32_fputs(const char *string, FILE *stream)
1835 {
1836    /* we use WriteConsoleA / WriteConsoleA
1837       so we can be sure that unicode support works on win32.
1838       with fallback if something fails
1839    */
1840
1841    HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1842    if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1843        p_MultiByteToWideChar && (stream == stdout)) {
1844
1845       POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1846
1847       DWORD dwCharsWritten;
1848       DWORD dwChars;
1849
1850       dwChars = UTF8_2_wchar(&pwszBuf, string);
1851
1852       /* try WriteConsoleW */
1853       if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1854          free_pool_memory(pwszBuf);
1855          return dwCharsWritten;
1856       }
1857
1858       /* convert to local codepage and try WriteConsoleA */
1859       POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1860       pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1861
1862       dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1863       free_pool_memory(pwszBuf);
1864
1865       if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1866          free_pool_memory(pszBuf);
1867          return dwCharsWritten;
1868       }
1869       free_pool_memory(pszBuf);
1870    }
1871    /* Fall back */
1872    return fputs(string, stream);
1873 }
1874
1875 char*
1876 win32_cgets (char* buffer, int len)
1877 {
1878    /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1879       from the win32 console and fallback if seomething fails */
1880
1881    HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1882    if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1883       DWORD dwRead;
1884       wchar_t wszBuf[1024];
1885       char  szBuf[1024];
1886
1887       /* nt and unicode conversion */
1888       if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1889
1890          /* null terminate at end */
1891          if (wszBuf[dwRead-1] == L'\n') {
1892             wszBuf[dwRead-1] = L'\0';
1893             dwRead --;
1894          }
1895
1896          if (wszBuf[dwRead-1] == L'\r') {
1897             wszBuf[dwRead-1] = L'\0';
1898             dwRead --;
1899          }
1900
1901          wchar_2_UTF8(buffer, wszBuf, len);
1902          return buffer;
1903       }
1904
1905       /* win 9x and unicode conversion */
1906       if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1907
1908          /* null terminate at end */
1909          if (szBuf[dwRead-1] == L'\n') {
1910             szBuf[dwRead-1] = L'\0';
1911             dwRead --;
1912          }
1913
1914          if (szBuf[dwRead-1] == L'\r') {
1915             szBuf[dwRead-1] = L'\0';
1916             dwRead --;
1917          }
1918
1919          /* convert from ansii to wchar_t */
1920          p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1921          /* convert from wchar_t to UTF-8 */
1922          if (wchar_2_UTF8(buffer, wszBuf, len))
1923             return buffer;
1924       }
1925    }
1926
1927    /* fallback */
1928    if (fgets(buffer, len, stdin))
1929       return buffer;
1930    else
1931       return NULL;
1932 }
1933
1934 int
1935 win32_unlink(const char *filename)
1936 {
1937    int nRetCode;
1938    if (p_wunlink) {
1939       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1940       make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1941
1942       nRetCode = _wunlink((LPCWSTR) pwszBuf);
1943
1944       /*
1945        * special case if file is readonly,
1946        * we retry but unset attribute before
1947        */
1948       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1949          DWORD dwAttr =  p_GetFileAttributesW((LPCWSTR)pwszBuf);
1950          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1951             if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1952                nRetCode = _wunlink((LPCWSTR) pwszBuf);
1953                /* reset to original if it didn't help */
1954                if (nRetCode == -1)
1955                   p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1956             }
1957          }
1958       }
1959       free_pool_memory(pwszBuf);
1960    } else {
1961       nRetCode = _unlink(filename);
1962
1963       /* special case if file is readonly,
1964       we retry but unset attribute before */
1965       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1966          DWORD dwAttr =  p_GetFileAttributesA(filename);
1967          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1968             if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1969                nRetCode = _unlink(filename);
1970                /* reset to original if it didn't help */
1971                if (nRetCode == -1)
1972                   p_SetFileAttributesA(filename, dwAttr);
1973             }
1974          }
1975       }
1976    }
1977    return nRetCode;
1978 }
1979
1980
1981 #include "mswinver.h"
1982
1983 char WIN_VERSION_LONG[64];
1984 char WIN_VERSION[32];
1985 char WIN_RAWVERSION[32];
1986
1987 class winver {
1988 public:
1989     winver(void);
1990 };
1991
1992 static winver INIT;                     // cause constructor to be called before main()
1993
1994
1995 winver::winver(void)
1996 {
1997     const char *version = "";
1998     const char *platform = "";
1999     OSVERSIONINFO osvinfo;
2000     osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
2001
2002     // Get the current OS version
2003     if (!GetVersionEx(&osvinfo)) {
2004         version = "Unknown";
2005         platform = "Unknown";
2006     }
2007         const int ver = _mkversion(osvinfo.dwPlatformId,
2008                                    osvinfo.dwMajorVersion,
2009                                    osvinfo.dwMinorVersion);
2010         snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
2011         switch (ver)
2012         {
2013         case MS_WINDOWS_95: (version =  "Windows 95"); break;
2014         case MS_WINDOWS_98: (version =  "Windows 98"); break;
2015         case MS_WINDOWS_ME: (version =  "Windows ME"); break;
2016         case MS_WINDOWS_NT4:(version =  "Windows NT 4.0"); platform = "NT"; break;
2017         case MS_WINDOWS_2K: (version =  "Windows 2000");platform = "NT"; break;
2018         case MS_WINDOWS_XP: (version =  "Windows XP");platform = "NT"; break;
2019         case MS_WINDOWS_S2003: (version =  "Windows Server 2003");platform = "NT"; break;
2020         default: version = WIN_RAWVERSION; break;
2021         }
2022
2023     bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
2024     snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
2025              platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
2026
2027 #if 0
2028     HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
2029     CloseHandle(h);
2030 #endif
2031 #if 0
2032     BPIPE *b = open_bpipe("ls -l", 10, "r");
2033     char buf[1024];
2034     while (!feof(b->rfd)) {
2035         fgets(buf, sizeof(buf), b->rfd);
2036     }
2037     close_bpipe(b);
2038 #endif
2039 }
2040
2041 BOOL CreateChildProcess(VOID);
2042 VOID WriteToPipe(VOID);
2043 VOID ReadFromPipe(VOID);
2044 VOID ErrorExit(LPCSTR);
2045 VOID ErrMsg(LPTSTR, BOOL);
2046
2047 /**
2048  * Check for a quoted path,  if an absolute path name is given and it contains
2049  * spaces it will need to be quoted.  i.e.  "c:/Program Files/foo/bar.exe"
2050  * CreateProcess() says the best way to ensure proper results with executables
2051  * with spaces in path or filename is to quote the string.
2052  */
2053 const char *
2054 getArgv0(const char *cmdline)
2055 {
2056
2057     int inquote = 0;
2058     const char *cp;
2059     for (cp = cmdline; *cp; cp++)
2060     {
2061         if (*cp == '"') {
2062             inquote = !inquote;
2063         }
2064         if (!inquote && isspace(*cp))
2065             break;
2066     }
2067
2068
2069     int len = cp - cmdline;
2070     char *rval = (char *)malloc(len+1);
2071
2072     cp = cmdline;
2073     char *rp = rval;
2074
2075     while (len--)
2076         *rp++ = *cp++;
2077
2078     *rp = 0;
2079     return rval;
2080 }
2081
2082 /*
2083  * Extracts the executable or script name from the first string in
2084  * cmdline.
2085  *
2086  * If the name contains blanks then it must be quoted with double quotes,
2087  * otherwise quotes are optional.  If the name contains blanks then it
2088  * will be converted to a short name.
2089  *
2090  * The optional quotes will be removed.  The result is copied to a malloc'ed
2091  * buffer and returned through the pexe argument.  The pargs parameter is set
2092  * to the address of the character in cmdline located after the name.
2093  *
2094  * The malloc'ed buffer returned in *pexe must be freed by the caller.
2095  */
2096 bool
2097 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
2098 {
2099    const char *pExeStart = NULL;    /* Start of executable name in cmdline */
2100    const char *pExeEnd = NULL;      /* Character after executable name (separator) */
2101
2102    const char *pBasename = NULL;    /* Character after last path separator */
2103    const char *pExtension = NULL;   /* Period at start of extension */
2104
2105    const char *current = cmdline;
2106
2107    bool bQuoted = false;
2108
2109    /* Skip initial whitespace */
2110
2111    while (*current == ' ' || *current == '\t')
2112    {
2113       current++;
2114    }
2115
2116    /* Calculate start of name and determine if quoted */
2117
2118    if (*current == '"') {
2119       pExeStart = ++current;
2120       bQuoted = true;
2121    } else {
2122       pExeStart = current;
2123       bQuoted = false;
2124    }
2125
2126    *pargs = NULL;
2127    *pexe = NULL;
2128
2129    /*
2130     * Scan command line looking for path separators (/ and \\) and the
2131     * terminator, either a quote or a blank.  The location of the
2132     * extension is also noted.
2133     */
2134
2135    for ( ; *current != '\0'; current++)
2136    {
2137       if (*current == '.') {
2138          pExtension = current;
2139       } else if (IsPathSeparator(*current) && current[1] != '\0') {
2140          pBasename = &current[1];
2141          pExtension = NULL;
2142       }
2143
2144       /* Check for terminator, either quote or blank */
2145       if (bQuoted) {
2146          if (*current != '"') {
2147             continue;
2148          }
2149       } else {
2150          if (*current != ' ') {
2151             continue;
2152          }
2153       }
2154
2155       /*
2156        * Hit terminator, remember end of name (address of terminator) and
2157        * start of arguments
2158        */
2159       pExeEnd = current;
2160
2161       if (bQuoted && *current == '"') {
2162          *pargs = &current[1];
2163       } else {
2164          *pargs = current;
2165       }
2166
2167       break;
2168    }
2169
2170    if (pBasename == NULL) {
2171       pBasename = pExeStart;
2172    }
2173
2174    if (pExeEnd == NULL) {
2175       pExeEnd = current;
2176    }
2177
2178    if (*pargs == NULL)
2179    {
2180       *pargs = current;
2181    }
2182
2183    bool bHasPathSeparators = pExeStart != pBasename;
2184
2185    /* We have pointers to all the useful parts of the name */
2186
2187    /* Default extensions in the order cmd.exe uses to search */
2188
2189    static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
2190    DWORD dwBasePathLength = pExeEnd - pExeStart;
2191
2192    DWORD dwAltNameLength = 0;
2193    char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2194    char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2195
2196    pPathname[MAX_PATHLENGTH] = '\0';
2197    pAltPathname[MAX_PATHLENGTH] = '\0';
2198
2199    memcpy(pPathname, pExeStart, dwBasePathLength);
2200    pPathname[dwBasePathLength] = '\0';
2201
2202    if (pExtension == NULL) {
2203       /* Try appending extensions */
2204       for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
2205
2206          if (!bHasPathSeparators) {
2207             /* There are no path separators, search in the standard locations */
2208             dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
2209             if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2210                memcpy(pPathname, pAltPathname, dwAltNameLength);
2211                pPathname[dwAltNameLength] = '\0';
2212                break;
2213             }
2214          } else {
2215             bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
2216             if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
2217                break;
2218             }
2219             pPathname[dwBasePathLength] = '\0';
2220          }
2221       }
2222    } else if (!bHasPathSeparators) {
2223       /* There are no path separators, search in the standard locations */
2224       dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
2225       if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
2226          memcpy(pPathname, pAltPathname, dwAltNameLength);
2227          pPathname[dwAltNameLength] = '\0';
2228       }
2229    }
2230
2231    if (strchr(pPathname, ' ') != NULL) {
2232       dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
2233
2234       if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2235          *pexe = (char *)malloc(dwAltNameLength + 1);
2236          if (*pexe == NULL) {
2237             return false;
2238          }
2239          memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
2240       }
2241    }
2242
2243    if (*pexe == NULL) {
2244       DWORD dwPathnameLength = strlen(pPathname);
2245       *pexe = (char *)malloc(dwPathnameLength + 1);
2246       if (*pexe == NULL) {
2247          return false;
2248       }
2249       memcpy(*pexe, pPathname, dwPathnameLength + 1);
2250    }
2251
2252    return true;
2253 }
2254
2255 /**
2256  * Create the process with WCHAR API
2257  */
2258 static BOOL
2259 CreateChildProcessW(const char *comspec, const char *cmdLine,
2260                     PROCESS_INFORMATION *hProcInfo,
2261                     HANDLE in, HANDLE out, HANDLE err)
2262 {
2263    STARTUPINFOW siStartInfo;
2264    BOOL bFuncRetn = FALSE;
2265
2266    // Set up members of the STARTUPINFO structure.
2267    ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2268    siStartInfo.cb = sizeof(siStartInfo);
2269    // setup new process to use supplied handles for stdin,stdout,stderr
2270
2271    siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2272    siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2273
2274    siStartInfo.hStdInput = in;
2275    siStartInfo.hStdOutput = out;
2276    siStartInfo.hStdError = err;
2277
2278    // Convert argument to WCHAR
2279    POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
2280    POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2281
2282    UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2283    UTF8_2_wchar(&comspec_wchar, comspec);
2284
2285    // Create the child process.
2286    Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2287
2288    // try to execute program
2289    bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2290                                 (WCHAR*)cmdLine_wchar,// command line
2291                                 NULL,      // process security attributes
2292                                 NULL,      // primary thread security attributes
2293                                 TRUE,      // handles are inherited
2294                                 0,         // creation flags
2295                                 NULL,      // use parent's environment
2296                                 NULL,      // use parent's current directory
2297                                 &siStartInfo,  // STARTUPINFO pointer
2298                                 hProcInfo);   // receives PROCESS_INFORMATION
2299    free_pool_memory(cmdLine_wchar);
2300    free_pool_memory(comspec_wchar);
2301
2302    return bFuncRetn;
2303 }
2304
2305
2306 /**
2307  * Create the process with ANSI API
2308  */
2309 static BOOL
2310 CreateChildProcessA(const char *comspec, char *cmdLine,
2311                     PROCESS_INFORMATION *hProcInfo,
2312                     HANDLE in, HANDLE out, HANDLE err)
2313 {
2314    STARTUPINFOA siStartInfo;
2315    BOOL bFuncRetn = FALSE;
2316
2317    // Set up members of the STARTUPINFO structure.
2318    ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2319    siStartInfo.cb = sizeof(siStartInfo);
2320    // setup new process to use supplied handles for stdin,stdout,stderr
2321    siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2322    siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2323
2324    siStartInfo.hStdInput = in;
2325    siStartInfo.hStdOutput = out;
2326    siStartInfo.hStdError = err;
2327
2328    // Create the child process.
2329    Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2330
2331    // try to execute program
2332    bFuncRetn = p_CreateProcessA(comspec,
2333                                 cmdLine,  // command line
2334                                 NULL,     // process security attributes
2335                                 NULL,     // primary thread security attributes
2336                                 TRUE,     // handles are inherited
2337                                 0,        // creation flags
2338                                 NULL,     // use parent's environment
2339                                 NULL,     // use parent's current directory
2340                                 &siStartInfo,// STARTUPINFO pointer
2341                                 hProcInfo);// receives PROCESS_INFORMATION
2342    return bFuncRetn;
2343 }
2344
2345 /**
2346  * OK, so it would seem CreateProcess only handles true executables:
2347  * .com or .exe files.  So grab $COMSPEC value and pass command line to it.
2348  */
2349 HANDLE
2350 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2351 {
2352    static const char *comspec = NULL;
2353    PROCESS_INFORMATION piProcInfo;
2354    BOOL bFuncRetn = FALSE;
2355
2356    if (!p_CreateProcessA || !p_CreateProcessW)
2357       return INVALID_HANDLE_VALUE;
2358
2359    if (comspec == NULL)
2360       comspec = getenv("COMSPEC");
2361    if (comspec == NULL) // should never happen
2362       return INVALID_HANDLE_VALUE;
2363
2364    // Set up members of the PROCESS_INFORMATION structure.
2365    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2366
2367    // if supplied handles are not used the send a copy of our STD_HANDLE
2368    // as appropriate
2369    if (in == INVALID_HANDLE_VALUE)
2370       in = GetStdHandle(STD_INPUT_HANDLE);
2371
2372    if (out == INVALID_HANDLE_VALUE)
2373       out = GetStdHandle(STD_OUTPUT_HANDLE);
2374
2375    if (err == INVALID_HANDLE_VALUE)
2376       err = GetStdHandle(STD_ERROR_HANDLE);
2377
2378    char *exeFile;
2379    const char *argStart;
2380
2381    if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2382       return INVALID_HANDLE_VALUE;
2383    }
2384
2385    POOL_MEM cmdLine(PM_FNAME);
2386    Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2387
2388    free(exeFile);
2389
2390    // New function disabled
2391    if (p_CreateProcessW && p_MultiByteToWideChar) {
2392       bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2393                                       in, out, err);
2394    } else {
2395       bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2396                                       in, out, err);
2397    }
2398
2399    if (bFuncRetn == 0) {
2400       ErrorExit("CreateProcess failed\n");
2401       Dmsg2(dbglvl, "  CreateProcess(%s, %s) failed\n",comspec,cmdLine.c_str());
2402       return INVALID_HANDLE_VALUE;
2403    }
2404    // we don't need a handle on the process primary thread so we close
2405    // this now.
2406    CloseHandle(piProcInfo.hThread);
2407    return piProcInfo.hProcess;
2408 }
2409
2410 void
2411 ErrorExit (LPCSTR lpszMessage)
2412 {
2413     const char *err = errorString();
2414     Dmsg2(dbglvl, "%s: %s", lpszMessage, err);
2415     LocalFree((void *)err);
2416     errno = b_errno_win32;
2417 }
2418
2419
2420 /*
2421 typedef struct s_bpipe {
2422    pid_t worker_pid;
2423    time_t worker_stime;
2424    int wait;
2425    btimer_t *timer_id;
2426    FILE *rfd;
2427    FILE *wfd;
2428 } BPIPE;
2429 */
2430
2431 static void
2432 CloseHandleIfValid(HANDLE handle)
2433 {
2434     if (handle != INVALID_HANDLE_VALUE) {
2435         CloseHandle(handle);
2436     }
2437 }
2438
2439 BPIPE *
2440 open_bpipe(char *prog, int wait, const char *mode, char *envp[])
2441 {
2442     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2443         hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2444         hInputFile;
2445
2446     SECURITY_ATTRIBUTES saAttr;
2447
2448     BOOL fSuccess;
2449
2450     hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2451         hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2452         hInputFile = INVALID_HANDLE_VALUE;
2453
2454     BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2455     memset((void *)bpipe, 0, sizeof(BPIPE));
2456
2457     int mode_read = (mode[0] == 'r');
2458     int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2459
2460
2461     // Set the bInheritHandle flag so pipe handles are inherited.
2462
2463     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2464     saAttr.bInheritHandle = TRUE;
2465     saAttr.lpSecurityDescriptor = NULL;
2466
2467     if (mode_read) {
2468
2469         // Create a pipe for the child process's STDOUT.
2470         if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2471             ErrorExit("Stdout pipe creation failed\n");
2472             goto cleanup;
2473         }
2474         // Create noninheritable read handle and close the inheritable read
2475         // handle.
2476
2477         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2478                                    GetCurrentProcess(), &hChildStdoutRdDup , 0,
2479                                    FALSE,
2480                                    DUPLICATE_SAME_ACCESS);
2481         if ( !fSuccess ) {
2482             ErrorExit("DuplicateHandle failed");
2483             goto cleanup;
2484         }
2485
2486         CloseHandle(hChildStdoutRd);
2487         hChildStdoutRd = INVALID_HANDLE_VALUE;
2488     }
2489
2490     if (mode_write) {
2491
2492         // Create a pipe for the child process's STDIN.
2493
2494         if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2495             ErrorExit("Stdin pipe creation failed\n");
2496             goto cleanup;
2497         }
2498
2499         // Duplicate the write handle to the pipe so it is not inherited.
2500         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2501                                    GetCurrentProcess(), &hChildStdinWrDup,
2502                                    0,
2503                                    FALSE,                  // not inherited
2504                                    DUPLICATE_SAME_ACCESS);
2505         if (!fSuccess) {
2506             ErrorExit("DuplicateHandle failed");
2507             goto cleanup;
2508         }
2509
2510         CloseHandle(hChildStdinWr);
2511         hChildStdinWr = INVALID_HANDLE_VALUE;
2512     }
2513     // spawn program with redirected handles as appropriate
2514     bpipe->worker_pid = (pid_t)
2515         CreateChildProcess(prog,             // commandline
2516                            hChildStdinRd,    // stdin HANDLE
2517                            hChildStdoutWr,   // stdout HANDLE
2518                            hChildStdoutWr);  // stderr HANDLE
2519
2520     if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE) {
2521        ErrorExit("CreateChildProcess failed");
2522        goto cleanup;
2523     }
2524
2525     bpipe->wait = wait;
2526     bpipe->worker_stime = time(NULL);
2527
2528     if (mode_read) {
2529         CloseHandle(hChildStdoutWr); // close our write side so when
2530                                      // process terminates we can
2531                                      // detect eof.
2532         // ugly but convert WIN32 HANDLE to FILE*
2533         int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2534         if (rfd >= 0) {
2535            bpipe->rfd = _fdopen(rfd, "rb");
2536         }
2537     }
2538     if (mode_write) {
2539         CloseHandle(hChildStdinRd); // close our read side so as not
2540                                     // to interfre with child's copy
2541         // ugly but convert WIN32 HANDLE to FILE*
2542         int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2543         if (wfd >= 0) {
2544            bpipe->wfd = _fdopen(wfd, "wb");
2545         }
2546     }
2547
2548     if (wait > 0) {
2549         bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2550     }
2551
2552     return bpipe;
2553
2554 cleanup:
2555
2556     CloseHandleIfValid(hChildStdoutWr);
2557     CloseHandleIfValid(hChildStdoutRd);
2558     CloseHandleIfValid(hChildStdoutRdDup);
2559     CloseHandleIfValid(hChildStdinWr);
2560     CloseHandleIfValid(hChildStdinRd);
2561     CloseHandleIfValid(hChildStdinWrDup);
2562
2563     free((void *)bpipe);
2564     errno = b_errno_win32;            /* do GetLastError() for error code */
2565     return NULL;
2566 }
2567
2568
2569 int
2570 kill(pid_t pid, int signal)
2571 {
2572    int rval = 0;
2573    if (!TerminateProcess((HANDLE)pid, (UINT)signal)) {
2574       rval = -1;
2575       errno = b_errno_win32;
2576    }
2577    CloseHandle((HANDLE)pid);
2578    return rval;
2579 }
2580
2581
2582 int
2583 close_bpipe(BPIPE *bpipe)
2584 {
2585    int rval = 0;
2586    int32_t remaining_wait = bpipe->wait;
2587
2588    /* Close pipes */
2589    if (bpipe->rfd) {
2590       fclose(bpipe->rfd);
2591       bpipe->rfd = NULL;
2592    }
2593    if (bpipe->wfd) {
2594       fclose(bpipe->wfd);
2595       bpipe->wfd = NULL;
2596    }
2597
2598    if (remaining_wait == 0) {         /* wait indefinitely */
2599       remaining_wait = INT32_MAX;
2600    }
2601    for ( ;; ) {
2602       DWORD exitCode;
2603       if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2604          const char *err = errorString();
2605          rval = b_errno_win32;
2606          Dmsg1(dbglvl, "GetExitCode error %s\n", err);
2607          LocalFree((void *)err);
2608          break;
2609       }
2610       if (exitCode == STILL_ACTIVE) {
2611          if (remaining_wait <= 0) {
2612             rval = ETIME;             /* timed out */
2613             break;
2614          }
2615          bmicrosleep(1, 0);           /* wait one second */
2616          remaining_wait--;
2617       } else if (exitCode != 0) {
2618          /* Truncate exit code as it doesn't seem to be correct */
2619          rval = (exitCode & 0xFF) | b_errno_exit;
2620          break;
2621       } else {
2622          break;                       /* Shouldn't get here */
2623       }
2624    }
2625
2626    if (bpipe->timer_id) {
2627        stop_child_timer(bpipe->timer_id);
2628    }
2629    if (bpipe->rfd) fclose(bpipe->rfd);
2630    if (bpipe->wfd) fclose(bpipe->wfd);
2631    free((void *)bpipe);
2632    return rval;
2633 }
2634
2635 int
2636 close_wpipe(BPIPE *bpipe)
2637 {
2638     int result = 1;
2639
2640     if (bpipe->wfd) {
2641         fflush(bpipe->wfd);
2642         if (fclose(bpipe->wfd) != 0) {
2643             result = 0;
2644         }
2645         bpipe->wfd = NULL;
2646     }
2647     return result;
2648 }
2649
2650 #ifndef MINGW64
2651 int
2652 utime(const char *fname, struct utimbuf *times)
2653 {
2654     FILETIME acc, mod;
2655     char tmpbuf[5000];
2656
2657     conv_unix_to_vss_win32_path(fname, tmpbuf, 5000);
2658
2659     cvt_utime_to_ftime(times->actime, acc);
2660     cvt_utime_to_ftime(times->modtime, mod);
2661
2662     HANDLE h = INVALID_HANDLE_VALUE;
2663
2664     if (p_CreateFileW) {
2665        POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2666        make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2667
2668        h = p_CreateFileW((LPCWSTR)pwszBuf,
2669                         FILE_WRITE_ATTRIBUTES,
2670                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2671                         NULL,
2672                         OPEN_EXISTING,
2673                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2674                         NULL);
2675
2676        free_pool_memory(pwszBuf);
2677     } else if (p_CreateFileA) {
2678        h = p_CreateFileA(tmpbuf,
2679                         FILE_WRITE_ATTRIBUTES,
2680                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2681                         NULL,
2682                         OPEN_EXISTING,
2683                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2684                         NULL);
2685     }
2686
2687     if (h == INVALID_HANDLE_VALUE) {
2688        const char *err = errorString();
2689        Dmsg2(dbglvl, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2690        LocalFree((void *)err);
2691        errno = b_errno_win32;
2692        return -1;
2693     }
2694
2695     int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2696     CloseHandle(h);
2697     if (rval == -1) {
2698        errno = b_errno_win32;
2699     }
2700     return rval;
2701 }
2702 #endif
2703
2704 #if 0
2705 int
2706 file_open(const char *file, int flags, int mode)
2707 {
2708    DWORD access = 0;
2709    DWORD shareMode = 0;
2710    DWORD create = 0;
2711    DWORD msflags = 0;
2712    HANDLE foo = INVALID_HANDLE_VALUE;
2713    const char *remap = file;
2714
2715    if (flags & O_WRONLY) access = GENERIC_WRITE;
2716    else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2717    else access = GENERIC_READ;
2718
2719    if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2720       create = CREATE_NEW;
2721    else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2722       create = CREATE_ALWAYS;
2723    else if (flags & O_CREAT)
2724       create = OPEN_ALWAYS;
2725    else if (flags & O_TRUNC)
2726       create = TRUNCATE_EXISTING;
2727    else
2728       create = OPEN_EXISTING;
2729
2730    shareMode = 0;
2731
2732    if (flags & O_APPEND) {
2733       printf("open...APPEND not implemented yet.");
2734       exit(-1);
2735    }
2736
2737    if (p_CreateFileW) {
2738       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2739       make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2740
2741       foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2742       free_pool_memory(pwszBuf);
2743    } else if (p_CreateFileA)
2744       foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2745
2746    if (INVALID_HANDLE_VALUE == foo) {
2747       errno = b_errno_win32;
2748       return (int)-1;
2749    }
2750    return (int)foo;
2751
2752 }
2753
2754
2755 int
2756 file_close(int fd)
2757 {
2758     if (!CloseHandle((HANDLE)fd)) {
2759         errno = b_errno_win32;
2760         return -1;
2761     }
2762
2763     return 0;
2764 }
2765
2766 ssize_t
2767 file_write(int fd, const void *data, ssize_t len)
2768 {
2769     BOOL status;
2770     DWORD bwrite;
2771     status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2772     if (status) return bwrite;
2773     errno = b_errno_win32;
2774     return -1;
2775 }
2776
2777
2778 ssize_t
2779 file_read(int fd, void *data, ssize_t len)
2780 {
2781     BOOL status;
2782     DWORD bread;
2783
2784     status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2785     if (status) return bread;
2786     errno = b_errno_win32;
2787     return -1;
2788 }
2789
2790 boffset_t
2791 file_seek(int fd, boffset_t offset, int whence)
2792 {
2793     DWORD method = 0;
2794     DWORD val;
2795     LONG  offset_low = (LONG)offset;
2796     LONG  offset_high = (LONG)(offset >> 32);
2797
2798     switch (whence) {
2799     case SEEK_SET :
2800         method = FILE_BEGIN;
2801         break;
2802     case SEEK_CUR:
2803         method = FILE_CURRENT;
2804         break;
2805     case SEEK_END:
2806         method = FILE_END;
2807         break;
2808     default:
2809         errno = EINVAL;
2810         return -1;
2811     }
2812
2813
2814     if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2815        errno = b_errno_win32;
2816        return -1;
2817     }
2818     /* ***FIXME*** I doubt this works right */
2819     return val;
2820 }
2821
2822 int
2823 file_dup2(int, int)
2824 {
2825     errno = ENOSYS;
2826     return -1;
2827 }
2828 #endif
2829
2830 #ifdef xxx
2831 /*
2832  * Emulation of mmap and unmmap for tokyo dbm
2833  */
2834 void *mmap(void *start, size_t length, int prot, int flags,
2835            int fd, off_t offset)
2836 {
2837    DWORD fm_access = 0;
2838    DWORD mv_access = 0;
2839    HANDLE h;
2840    HANDLE mv;
2841
2842    if (length == 0) {
2843       return MAP_FAILED;
2844    }
2845    if (!fd) {
2846       return MAP_FAILED;
2847    }
2848
2849    if (flags & PROT_WRITE) {
2850       fm_access |= PAGE_READWRITE;
2851    } else if (flags & PROT_READ) {
2852       fm_access |= PAGE_READONLY;
2853    }
2854
2855    if (flags & PROT_READ) {
2856       mv_access |= FILE_MAP_READ;
2857    }
2858    if (flags & PROT_WRITE) {
2859       mv_access |= FILE_MAP_WRITE;
2860    }
2861
2862    h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2863                          NULL /* security */,
2864                          fm_access,
2865                          0 /* MaximumSizeHigh */,
2866                          0 /* MaximumSizeLow */,
2867                          NULL /* name of the file mapping object */);
2868
2869    if (!h || h == INVALID_HANDLE_VALUE) {
2870       return MAP_FAILED;
2871    }
2872
2873    mv = MapViewOfFile(h, mv_access,
2874                       0 /* offset hi */,
2875                       0 /* offset lo */,
2876                       length);
2877    CloseHandle(h);
2878
2879    if (!mv || mv == INVALID_HANDLE_VALUE) {
2880       return MAP_FAILED;
2881    }
2882
2883    return (void *) mv;
2884 }
2885
2886 int munmap(void *start, size_t length)
2887 {
2888    if (!start) {
2889       return -1;
2890    }
2891    UnmapViewOfFile(start);
2892    return 0;
2893 }
2894 #endif
2895
2896 #ifdef HAVE_MINGW
2897 /* syslog function, added by Nicolas Boichat */
2898 void openlog(const char *ident, int option, int facility) {}
2899 #endif //HAVE_MINGW
2900
2901 /* Log an error message */
2902 void LogErrorMsg(const char *message)
2903 {
2904    HANDLE eventHandler;
2905    const char *strings[2];
2906
2907    /* Use the OS event logging to log the error */
2908    eventHandler = RegisterEventSource(NULL, "Bacula");
2909
2910    strings[0] = _("\n\nBacula ERROR: ");
2911    strings[1] = message;
2912
2913    if (eventHandler) {
2914       ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2915               0,                      /* category */
2916               0,                      /* ID */
2917               NULL,                   /* SID */
2918               2,                      /* Number of strings */
2919               0,                      /* raw data size */
2920               (const char **)strings, /* error strings */
2921               NULL);                  /* raw data */
2922       DeregisterEventSource(eventHandler);
2923    }
2924 }
2925
2926 /*
2927  * Don't allow OS to suspend while backup running
2928  *   Note, the OS automatically tracks these for each thread
2929  */
2930 void prevent_os_suspensions()
2931 {
2932    SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
2933 }
2934
2935 void allow_os_suspensions()
2936 {
2937    SetThreadExecutionState(ES_CONTINUOUS);
2938 }
2939
2940 int mkstemp(char *t)
2941 {
2942    char *filename = mktemp(t);
2943    if (filename == NULL) {
2944       return -1;
2945    }
2946    return open(filename, O_RDWR | O_CREAT, 0600);
2947 }
2948
2949 void malloc_trim(int)
2950 {
2951    if (p_EmptyWorkingSet) {
2952       HANDLE hProcess = GetCurrentProcess();
2953       if (!p_EmptyWorkingSet(hProcess)) {
2954          const char *err = errorString();
2955          Dmsg1(dbglvl, "EmptyWorkingSet() = %s\n", err);
2956          LocalFree((void *)err);
2957       }
2958       CloseHandle( hProcess );
2959    }
2960 }
2961
2962 uint64_t get_memory_info(char *buf, int buflen)
2963 {
2964    char ed1[50], ed2[50], ed3[50], ed4[50];
2965    uint64_t ret=0;
2966    HANDLE hProcess = GetCurrentProcess();
2967    PROCESS_MEMORY_COUNTERS pmc;
2968    buf[0] = '\0';
2969
2970    if (p_GetProcessMemoryInfo) {
2971       if (p_GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
2972          bsnprintf(buf, buflen,
2973                    "WorkingSetSize: %s QuotaPagedPoolUsage: %s QuotaNonPagedPoolUsage: %s PagefileUsage: %s",
2974                    edit_uint64_with_commas(pmc.WorkingSetSize, ed1),
2975                    edit_uint64_with_commas(pmc.QuotaPagedPoolUsage, ed2),
2976                    edit_uint64_with_commas(pmc.QuotaNonPagedPoolUsage, ed3),
2977                    edit_uint64_with_commas(pmc.PagefileUsage, ed4));
2978          ret = pmc.WorkingSetSize;
2979
2980       } else {
2981          const char *err = errorString();
2982          bsnprintf(buf, buflen, "%s", err);
2983          LocalFree((void *)err);
2984       }
2985    }
2986
2987    CloseHandle( hProcess );
2988    return ret;
2989 }