]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/compat/compat.cpp
088bc43ad65df2aaf7febfa0bd35913d23ec9aa6
[bacula/bacula] / bacula / src / win32 / compat / compat.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 //                              -*- Mode: C++ -*-
29 // compat.cpp -- compatibilty layer to make bacula-fd run
30 //               natively under windows
31 //
32 // Copyright transferred from Raider Solutions, Inc to
33 //   Kern Sibbald and John Walker by express permission.
34 //
35 // Author          : Christopher S. Hull
36 // Created On      : Sat Jan 31 15:55:00 2004
37 // $Id$
38
39
40 #include "bacula.h"
41 #include "compat.h"
42 #include "jcr.h"
43
44 #define b_errno_win32 (1<<29)
45
46 #define MAX_PATHLENGTH  1024
47
48 /* UTF-8 to UCS2 path conversion is expensive,
49    so we cache the conversion. During backup the
50    conversion is called 3 times (lstat, attribs, open),
51    by using the cache this is reduced to 1 time */
52
53 static POOLMEM *g_pWin32ConvUTF8Cache = get_pool_memory (PM_FNAME);
54 static POOLMEM *g_pWin32ConvUCS2Cache = get_pool_memory (PM_FNAME);
55 static DWORD g_dwWin32ConvUTF8strlen = 0;
56 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
57
58 static t_pVSSPathConvert   g_pVSSPathConvert;
59 static t_pVSSPathConvertW  g_pVSSPathConvertW;
60
61 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
62 {
63    g_pVSSPathConvert = pPathConvert;
64    g_pVSSPathConvertW = pPathConvertW;
65 }
66
67 void Win32ConvCleanupCache()
68 {
69    if (g_pWin32ConvUTF8Cache) {
70       free_pool_memory(g_pWin32ConvUTF8Cache);
71       g_pWin32ConvUTF8Cache = NULL;
72    }
73
74    if (g_pWin32ConvUCS2Cache) {
75       free_pool_memory(g_pWin32ConvUCS2Cache);   
76       g_pWin32ConvUCS2Cache = NULL;
77    }
78
79    g_dwWin32ConvUTF8strlen = 0;
80 }
81
82
83 /* to allow the usage of the original version in this file here */
84 #undef fputs
85
86
87 //#define USE_WIN32_COMPAT_IO 1
88 #define USE_WIN32_32KPATHCONVERSION 1
89
90 extern DWORD   g_platform_id;
91 extern DWORD   g_MinorVersion;
92
93 // from MicroSoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970
94 #ifdef HAVE_MINGW
95 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL 
96 #else
97 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
98 #endif
99
100 #define WIN32_FILETIME_SCALE  10000000             // 100ns/second
101
102 void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
103 {
104     const char *fname = name;
105     char *tname = win32_name;
106
107     Dmsg0(100, "Enter convert_unix_to_win32_path\n");
108
109     if (IsPathSeparator(name[0]) &&
110         IsPathSeparator(name[1]) &&
111         name[2] == '.' &&
112         IsPathSeparator(name[3])) {
113
114         *win32_name++ = '\\';
115         *win32_name++ = '\\';
116         *win32_name++ = '.';
117         *win32_name++ = '\\';
118
119         name += 4;
120     } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
121                g_pVSSPathConvert == NULL) {
122         /* allow path to be 32767 bytes */
123         *win32_name++ = '\\';
124         *win32_name++ = '\\';
125         *win32_name++ = '?';
126         *win32_name++ = '\\';
127     }
128
129     while (*name) {
130         /* Check for Unix separator and convert to Win32 */
131         if (name[0] == '/' && name[1] == '/') {  /* double slash? */
132            name++;                               /* yes, skip first one */
133         }
134         if (*name == '/') {
135             *win32_name++ = '\\';     /* convert char */
136         /* If Win32 separator that is "quoted", remove quote */
137         } else if (*name == '\\' && name[1] == '\\') {
138             *win32_name++ = '\\';
139             name++;                   /* skip first \ */
140         } else {
141             *win32_name++ = *name;    /* copy character */
142         }
143         name++;
144     }
145     /* Strip any trailing slash, if we stored something */
146     /* but leave "c:\" with backslash (root directory case */
147     if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
148         win32_name[-1] = 0;
149     } else {
150         *win32_name = 0;
151     }
152
153     /* here we convert to VSS specific file name which
154        can get longer because VSS will make something like
155        \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
156        from c:\bacula\uninstall.exe
157     */
158     Dmsg1(100, "path=%s\n", tname);
159     if (g_pVSSPathConvert != NULL) {
160        POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
161        pszBuf = check_pool_memory_size(pszBuf, dwSize);
162        bstrncpy(pszBuf, tname, strlen(tname)+1);
163        g_pVSSPathConvert(pszBuf, tname, dwSize);
164        free_pool_memory(pszBuf);
165     }
166
167     Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
168 }
169
170 /* Conversion of a Unix filename to a Win32 filename */
171 void unix_name_to_win32(POOLMEM **win32_name, char *name)
172 {
173    /* One extra byte should suffice, but we double it */
174    /* add MAX_PATH bytes for VSS shadow copy name */
175    DWORD dwSize = 2*strlen(name)+MAX_PATH;
176    *win32_name = check_pool_memory_size(*win32_name, dwSize);
177    conv_unix_to_win32_path(name, *win32_name, dwSize);
178 }
179
180 POOLMEM* 
181 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
182 {
183    /* created 02/27/2006 Thorsten Engel
184     * 
185     * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
186     * will complete the input path to an absolue path of the form \\?\c:\path\file
187     * 
188     * With this trick, it is possible to have 32K characters long paths.
189     *
190     * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
191     * to a raw windows partition.  
192     */
193
194    Dmsg0(100, "Enter wchar_win32_path\n");
195    if (pBIsRawPath) {
196       *pBIsRawPath = FALSE;              /* Initialize, set later */
197    }
198
199    if (!p_GetCurrentDirectoryW) {
200       Dmsg0(100, "Leave wchar_win32_path no change \n");
201       return pszUCSPath;
202    }
203    
204    wchar_t *name = (wchar_t *)pszUCSPath;
205
206    /* if it has already the desired form, exit without changes */
207    if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
208       Dmsg0(100, "Leave wchar_win32_path no change \n");
209       return pszUCSPath;
210    }
211
212    wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
213    wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
214    DWORD dwCurDirPathSize = 0;
215
216    /* get buffer with enough size (name+max 6. wchars+1 null terminator */
217    DWORD dwBufCharsNeeded = (wcslen(name)+7);
218    pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
219       
220    /* add \\?\ to support 32K long filepaths 
221       it is important to make absolute paths, so we add drive and
222       current path if necessary */
223
224    BOOL bAddDrive = TRUE;
225    BOOL bAddCurrentPath = TRUE;
226    BOOL bAddPrefix = TRUE;
227
228    /* does path begin with drive? if yes, it is absolute */
229    if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
230       bAddDrive = FALSE;
231       bAddCurrentPath = FALSE;
232    }
233
234    /* is path absolute? */
235    if (IsPathSeparator(name[0]))
236       bAddCurrentPath = FALSE; 
237
238    /* is path relative to itself?, if yes, skip ./ */
239    if (name[0] == '.' && IsPathSeparator(name[1])) {
240       name += 2;
241    }
242
243    /* is path of form '//./'? */
244    if (IsPathSeparator(name[0]) && 
245        IsPathSeparator(name[1]) && 
246        name[2] == '.' && 
247        IsPathSeparator(name[3])) {
248       bAddDrive = FALSE;
249       bAddCurrentPath = FALSE;
250       bAddPrefix = FALSE;
251       if (pBIsRawPath) {
252          *pBIsRawPath = TRUE;
253       }
254    }
255
256    int nParseOffset = 0;
257    
258    /* add 4 bytes header */
259    if (bAddPrefix) {
260       nParseOffset = 4;
261       wcscpy(pwszBuf, L"\\\\?\\");
262    }
263
264    /* get current path if needed */
265    if (bAddDrive || bAddCurrentPath) {
266       dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
267       if (dwCurDirPathSize > 0) {
268          /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */         
269          pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
270          p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
271       } else {
272          /* we have no info for doing so */
273          bAddDrive = FALSE;
274          bAddCurrentPath = FALSE;
275       }
276    }
277       
278
279    /* add drive if needed */
280    if (bAddDrive && !bAddCurrentPath) {
281       wchar_t szDrive[3];
282
283       if (IsPathSeparator(pwszCurDirBuf[0]) && 
284           IsPathSeparator(pwszCurDirBuf[1]) && 
285           pwszCurDirBuf[2] == '?' && 
286           IsPathSeparator(pwszCurDirBuf[3])) {
287          /* copy drive character */
288          szDrive[0] = pwszCurDirBuf[4];
289       } else {
290          /* copy drive character */
291          szDrive[0] = pwszCurDirBuf[0];
292       }
293
294       szDrive[1] = ':';
295       szDrive[2] = 0;
296
297       wcscat(pwszBuf, szDrive);
298       nParseOffset +=2;
299    }
300
301    /* add path if needed */
302    if (bAddCurrentPath) {
303       /* the 1 add. character is for the eventually added backslash */
304       dwBufCharsNeeded += dwCurDirPathSize+1; 
305       pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
306       /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
307       
308       if (IsPathSeparator(pwszCurDirBuf[0]) && 
309           IsPathSeparator(pwszCurDirBuf[1]) && 
310           pwszCurDirBuf[2] == '?' && 
311           IsPathSeparator(pwszCurDirBuf[3])) {
312          /* copy complete string */
313          wcscpy(pwszBuf, pwszCurDirBuf);
314       } else {
315          /* append path  */
316          wcscat(pwszBuf, pwszCurDirBuf);
317       }
318
319       nParseOffset = wcslen((LPCWSTR) pwszBuf);
320
321       /* check if path ends with backslash, if not, add one */
322       if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
323          wcscat(pwszBuf, L"\\");
324          nParseOffset++;
325       }
326    }
327
328    wchar_t *win32_name = &pwszBuf[nParseOffset];
329    wchar_t *name_start = name;
330
331    while (*name) {
332       /* Check for Unix separator and convert to Win32, eliminating 
333        * duplicate separators.
334        */
335       if (IsPathSeparator(*name)) {
336          *win32_name++ = '\\';     /* convert char */
337
338          /* Eliminate consecutive slashes, but not at the start so that 
339           * \\.\ still works.
340           */
341          if (name_start != name && IsPathSeparator(name[1])) {
342             name++;
343          }
344       } else {
345          *win32_name++ = *name;    /* copy character */
346       }
347       name++;
348    }
349
350    /* null terminate string */
351    *win32_name = 0;
352
353    /* here we convert to VSS specific file name which
354     * can get longer because VSS will make something like
355     * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
356     * from c:\bacula\uninstall.exe
357    */ 
358    if (g_pVSSPathConvertW != NULL) {
359       /* is output buffer large enough? */
360       pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, 
361                                                   (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
362       /* create temp. buffer */
363       wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
364       pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf, 
365                                                  (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
366       if (bAddPrefix)
367          nParseOffset = 4;
368       else
369          nParseOffset = 0; 
370       wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
371       g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
372       free_pool_memory((POOLMEM *)pszBuf);
373    }   
374
375    free_pool_memory(pszUCSPath);
376    free_pool_memory((POOLMEM *)pwszCurDirBuf);
377
378    Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
379    return (POOLMEM *)pwszBuf;
380 }
381
382 int
383 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
384 {
385    /* the return value is the number of bytes written to the buffer.
386       The number includes the byte for the null terminator. */
387
388    if (p_WideCharToMultiByte) {
389          int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
390          ASSERT (nRet > 0);
391          return nRet;
392       }
393    else
394       return 0;
395 }
396
397 int
398 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
399 {
400    /* the return value is the number of wide characters written to the buffer. */
401    /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
402
403    if (p_MultiByteToWideChar) {
404       /* strlen of UTF8 +1 is enough */
405       DWORD cchSize = (strlen(pszUTF)+1);
406       *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
407
408       int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
409       ASSERT (nRet > 0);
410       return nRet;
411    }
412    else
413       return 0;
414 }
415
416
417 void
418 wchar_win32_path(const char *name, wchar_t *win32_name)
419 {
420     const char *fname = name;
421     while (*name) {
422         /* Check for Unix separator and convert to Win32 */
423         if (*name == '/') {
424             *win32_name++ = '\\';     /* convert char */
425         /* If Win32 separated that is "quoted", remove quote */
426         } else if (*name == '\\' && name[1] == '\\') {
427             *win32_name++ = '\\';
428             name++;                   /* skip first \ */
429         } else {
430             *win32_name++ = *name;    /* copy character */
431         }
432         name++;
433     }
434     /* Strip any trailing slash, if we stored something */
435     if (*fname != 0 && win32_name[-1] == '\\') {
436         win32_name[-1] = 0;
437     } else {
438         *win32_name = 0;
439     }
440 }
441
442 int 
443 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
444 {
445    P(Win32Convmutex);
446    /* if we find the utf8 string in cache, we use the cached ucs2 version.
447       we compare the stringlength first (quick check) and then compare the content.            
448    */
449    if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
450       if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
451          int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
452          *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);      
453          wcscpy((LPWSTR) *pszUCS, (LPWSTR) g_pWin32ConvUCS2Cache);
454          V(Win32Convmutex);
455          return nBufSize / sizeof (WCHAR);
456       }
457    }
458
459    /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
460    int nRet = UTF8_2_wchar(pszUCS, pszUTF);
461
462 #ifdef USE_WIN32_32KPATHCONVERSION
463    /* add \\?\ to support 32K long filepaths */
464    *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
465 #else
466    if (pBIsRawPath)
467       *pBIsRawPath = FALSE;
468 #endif
469
470    /* populate cache */      
471    g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
472    wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
473    
474    g_dwWin32ConvUTF8strlen = strlen(pszUTF);
475    g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+1);
476    bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
477    V(Win32Convmutex);
478
479    return nRet;
480 }
481
482 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
483 int umask(int)
484 {
485    return 0;
486 }
487 #endif
488
489 int fcntl(int fd, int cmd)
490 {
491    return 0;
492 }
493
494 int chmod(const char *, mode_t)
495 {
496    return 0;
497 }
498
499 int chown(const char *k, uid_t, gid_t)
500 {
501    return 0;
502 }
503
504 int lchown(const char *k, uid_t, gid_t)
505 {
506    return 0;
507 }
508
509 long int
510 random(void)
511 {
512     return rand();
513 }
514
515 void
516 srandom(unsigned int seed)
517 {
518    srand(seed);
519 }
520 // /////////////////////////////////////////////////////////////////
521 // convert from Windows concept of time to Unix concept of time
522 // /////////////////////////////////////////////////////////////////
523 void
524 cvt_utime_to_ftime(const time_t  &time, FILETIME &wintime)
525 {
526    uint64_t mstime = time;
527    mstime *= WIN32_FILETIME_SCALE;
528    mstime += WIN32_FILETIME_ADJUST;
529
530 #if defined(_MSC_VER)
531    wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
532 #else
533    wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
534 #endif
535    wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
536 }
537
538 time_t
539 cvt_ftime_to_utime(const FILETIME &time)
540 {
541     uint64_t mstime = time.dwHighDateTime;
542     mstime <<= 32;
543     mstime |= time.dwLowDateTime;
544
545     mstime -= WIN32_FILETIME_ADJUST;
546     mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
547
548     return (time_t) (mstime & 0xffffffff);
549 }
550
551 static const char *
552 errorString(void)
553 {
554    LPVOID lpMsgBuf;
555
556    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
557                  FORMAT_MESSAGE_FROM_SYSTEM |
558                  FORMAT_MESSAGE_IGNORE_INSERTS,
559                  NULL,
560                  GetLastError(),
561                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
562                  (LPTSTR) &lpMsgBuf,
563                  0,
564                  NULL);
565
566    /* Strip any \r or \n */
567    char *rval = (char *) lpMsgBuf;
568    char *cp = strchr(rval, '\r');
569    if (cp != NULL) {
570       *cp = 0;
571    } else {
572       cp = strchr(rval, '\n');
573       if (cp != NULL)
574          *cp = 0;
575    }
576    return rval;
577 }
578
579
580 static int
581 statDir(const char *file, struct stat *sb)
582 {
583    WIN32_FIND_DATAW info_w;       // window's file info
584    WIN32_FIND_DATAA info_a;       // window's file info
585
586    // cache some common vars to make code more transparent
587    DWORD* pdwFileAttributes;
588    DWORD* pnFileSizeHigh;
589    DWORD* pnFileSizeLow;
590    FILETIME* pftLastAccessTime;
591    FILETIME* pftLastWriteTime;
592    FILETIME* pftCreationTime;
593
594    if (file[1] == ':' && file[2] == 0) {
595         Dmsg1(99, "faking ROOT attrs(%s).\n", file);
596         sb->st_mode = S_IFDIR;
597         sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
598         time(&sb->st_ctime);
599         time(&sb->st_mtime);
600         time(&sb->st_atime);
601         return 0;
602     }
603
604    HANDLE h = INVALID_HANDLE_VALUE;
605
606    // use unicode or ascii
607    if (p_FindFirstFileW) {
608       POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
609       make_win32_path_UTF8_2_wchar(&pwszBuf, file);
610
611       h = p_FindFirstFileW((LPCWSTR) pwszBuf, &info_w);
612       free_pool_memory(pwszBuf);
613
614       pdwFileAttributes = &info_w.dwFileAttributes;
615       pnFileSizeHigh    = &info_w.nFileSizeHigh;
616       pnFileSizeLow     = &info_w.nFileSizeLow;
617       pftLastAccessTime = &info_w.ftLastAccessTime;
618       pftLastWriteTime  = &info_w.ftLastWriteTime;
619       pftCreationTime   = &info_w.ftCreationTime;
620    }
621    else if (p_FindFirstFileA) {
622       h = p_FindFirstFileA(file, &info_a);
623
624       pdwFileAttributes = &info_a.dwFileAttributes;
625       pnFileSizeHigh    = &info_a.nFileSizeHigh;
626       pnFileSizeLow     = &info_a.nFileSizeLow;
627       pftLastAccessTime = &info_a.ftLastAccessTime;
628       pftLastWriteTime  = &info_a.ftLastWriteTime;
629       pftCreationTime   = &info_a.ftCreationTime;
630    }
631
632     if (h == INVALID_HANDLE_VALUE) {
633         const char *err = errorString();
634         Dmsg2(99, "FindFirstFile(%s):%s\n", file, err);
635         LocalFree((void *)err);
636         errno = b_errno_win32;
637         return -1;
638     }
639
640     sb->st_mode = 0777;               /* start with everything */
641     if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
642         sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
643     if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
644         sb->st_mode &= ~S_IRWXO; /* remove everything for other */
645     if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
646         sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
647     sb->st_mode |= S_IFDIR;
648
649     sb->st_size = *pnFileSizeHigh;
650     sb->st_size <<= 32;
651     sb->st_size |= *pnFileSizeLow;
652     sb->st_blksize = 4096;
653     sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
654
655     sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
656     sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
657     sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
658     FindClose(h);
659
660     return 0;
661 }
662
663 int
664 fstat(int fd, struct stat *sb)
665 {
666     BY_HANDLE_FILE_INFORMATION info;
667     char tmpbuf[1024];
668
669     if (!GetFileInformationByHandle((HANDLE)fd, &info)) {
670         const char *err = errorString();
671         Dmsg2(99, "GetfileInformationByHandle(%s): %s\n", tmpbuf, err);
672         LocalFree((void *)err);
673         errno = b_errno_win32;
674         return -1;
675     }
676
677     sb->st_dev = info.dwVolumeSerialNumber;
678     sb->st_ino = info.nFileIndexHigh;
679     sb->st_ino <<= 32;
680     sb->st_ino |= info.nFileIndexLow;
681     sb->st_nlink = (short)info.nNumberOfLinks;
682     if (sb->st_nlink > 1) {
683        Dmsg1(99,  "st_nlink=%d\n", sb->st_nlink);
684     }
685
686     sb->st_mode = 0777;               /* start with everything */
687     if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
688         sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
689     if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
690         sb->st_mode &= ~S_IRWXO; /* remove everything for other */
691     if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
692         sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
693     sb->st_mode |= S_IFREG;
694
695     sb->st_size = info.nFileSizeHigh;
696     sb->st_size <<= 32;
697     sb->st_size |= info.nFileSizeLow;
698     sb->st_blksize = 4096;
699     sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
700     sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
701     sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
702     sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
703
704     return 0;
705 }
706
707 static int
708 stat2(const char *file, struct stat *sb)
709 {
710     HANDLE h;
711     int rval = 0;
712     char tmpbuf[1024];
713     conv_unix_to_win32_path(file, tmpbuf, 1024);
714
715     DWORD attr = (DWORD)-1;
716
717     if (p_GetFileAttributesW) {
718       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
719       make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
720
721       attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
722       free_pool_memory(pwszBuf);
723     } else if (p_GetFileAttributesA) {
724        attr = p_GetFileAttributesA(tmpbuf);
725     }
726
727     if (attr == (DWORD)-1) {
728         const char *err = errorString();
729         Dmsg2(99, "GetFileAttributes(%s): %s\n", tmpbuf, err);
730         LocalFree((void *)err);
731         errno = b_errno_win32;
732         return -1;
733     }
734
735     if (attr & FILE_ATTRIBUTE_DIRECTORY)
736         return statDir(tmpbuf, sb);
737
738     h = CreateFileA(tmpbuf, GENERIC_READ,
739                    FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
740
741     if (h == INVALID_HANDLE_VALUE) {
742         const char *err = errorString();
743         Dmsg2(99, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
744         LocalFree((void *)err);
745         errno = b_errno_win32;
746         return -1;
747     }
748
749     rval = fstat((int)h, sb);
750     CloseHandle(h);
751  
752     return rval;
753 }
754
755 int
756 stat(const char *file, struct stat *sb)
757 {
758    WIN32_FILE_ATTRIBUTE_DATA data;
759    errno = 0;
760
761
762    memset(sb, 0, sizeof(*sb));
763
764    /* why not allow win 95 to use p_GetFileAttributesExA ? 
765     * this function allows _some_ open files to be stat'ed 
766     * if (g_platform_id == VER_PLATFORM_WIN32_WINDOWS) {
767     *    return stat2(file, sb);
768     * }
769     */
770
771    if (p_GetFileAttributesExW) {
772       /* dynamically allocate enough space for UCS2 filename */
773       POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
774       make_win32_path_UTF8_2_wchar(&pwszBuf, file);
775
776       BOOL b = p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, &data);
777       free_pool_memory(pwszBuf);
778
779       if (!b) {
780          return stat2(file, sb);
781       }
782    } else if (p_GetFileAttributesExA) {
783       if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
784          return stat2(file, sb);
785        }
786    } else {
787       return stat2(file, sb);
788    }
789
790    sb->st_mode = 0777;               /* start with everything */
791    if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
792       sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
793    }
794    if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
795       sb->st_mode &= ~S_IRWXO; /* remove everything for other */
796    }
797    if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
798       sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
799    }
800    if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
801       sb->st_mode |= S_IFDIR;
802    } else {
803       sb->st_mode |= S_IFREG;
804    }
805
806    sb->st_nlink = 1;
807    sb->st_size = data.nFileSizeHigh;
808    sb->st_size <<= 32;
809    sb->st_size |= data.nFileSizeLow;
810    sb->st_blksize = 4096;
811    sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
812    sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
813    sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
814    sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
815    return 0;
816 }
817
818 int win32_ftruncate(int fd, int64_t length) 
819 {
820    /* Set point we want to truncate file */
821    __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
822
823    if (pos != (__int64)length) {
824       errno = EACCES;         /* truncation failed, get out */
825       return -1;
826    }
827
828    /* Truncate file */
829    if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
830       errno = b_errno_win32;
831       return -1;
832    }
833    errno = 0;
834    return 0;
835 }
836
837 int fcntl(int fd, int cmd, long arg)
838 {
839    int rval = 0;
840
841    switch (cmd) {
842    case F_GETFL:
843       rval = O_NONBLOCK;
844       break;
845
846    case F_SETFL:
847       rval = 0;
848       break;
849
850    default:
851       errno = EINVAL;
852       rval = -1;
853       break;
854    }
855
856    return rval;
857 }
858
859 int
860 lstat(const char *file, struct stat *sb)
861 {
862    return stat(file, sb);
863 }
864
865 void
866 sleep(int sec)
867 {
868    Sleep(sec * 1000);
869 }
870
871 int
872 geteuid(void)
873 {
874    return 0;
875 }
876
877 int
878 execvp(const char *, char *[]) {
879    errno = ENOSYS;
880    return -1;
881 }
882
883
884 int
885 fork(void)
886 {
887    errno = ENOSYS;
888    return -1;
889 }
890
891 int
892 pipe(int[])
893 {
894    errno = ENOSYS;
895    return -1;
896 }
897
898 int
899 waitpid(int, int*, int)
900 {
901    errno = ENOSYS;
902    return -1;
903 }
904
905 int
906 readlink(const char *, char *, int)
907 {
908    errno = ENOSYS;
909    return -1;
910 }
911
912
913 #ifndef HAVE_MINGW
914 int
915 strcasecmp(const char *s1, const char *s2)
916 {
917    register int ch1, ch2;
918
919    if (s1==s2)
920       return 0;       /* strings are equal if same object. */
921    else if (!s1)
922       return -1;
923    else if (!s2)
924       return 1;
925    do {
926       ch1 = *s1;
927       ch2 = *s2;
928       s1++;
929       s2++;
930    } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
931
932    return(ch1 - ch2);
933 }
934 #endif //HAVE_MINGW
935
936 int
937 strncasecmp(const char *s1, const char *s2, int len)
938 {
939    register int ch1 = 0, ch2 = 0;
940
941    if (s1==s2)
942       return 0;       /* strings are equal if same object. */
943    else if (!s1)
944       return -1;
945    else if (!s2)
946       return 1;
947
948    while (len--) {
949       ch1 = *s1;
950       ch2 = *s2;
951       s1++;
952       s2++;
953       if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
954    }
955
956    return (ch1 - ch2);
957 }
958
959 int
960 gettimeofday(struct timeval *tv, struct timezone *)
961 {
962     SYSTEMTIME now;
963     FILETIME tmp;
964     GetSystemTime(&now);
965
966     if (tv == NULL) {
967        errno = EINVAL;
968        return -1;
969     }
970     if (!SystemTimeToFileTime(&now, &tmp)) {
971        errno = b_errno_win32;
972        return -1;
973     }
974
975     int64_t _100nsec = tmp.dwHighDateTime;
976     _100nsec <<= 32;
977     _100nsec |= tmp.dwLowDateTime;
978     _100nsec -= WIN32_FILETIME_ADJUST;
979
980     tv->tv_sec =(long) (_100nsec / 10000000);
981     tv->tv_usec = (long) ((_100nsec % 10000000)/10);
982     return 0;
983
984 }
985
986 /* For apcupsd this is in src/lib/wincompat.c */
987 extern "C" void syslog(int type, const char *fmt, ...) 
988 {
989 /*#ifndef HAVE_CONSOLE
990     MessageBox(NULL, msg, "Bacula", MB_OK);
991 #endif*/
992 }
993
994 void
995 closelog()
996 {
997 }
998
999 struct passwd *
1000 getpwuid(uid_t)
1001 {
1002     return NULL;
1003 }
1004
1005 struct group *
1006 getgrgid(uid_t)
1007 {
1008     return NULL;
1009 }
1010
1011 // implement opendir/readdir/closedir on top of window's API
1012
1013 typedef struct _dir
1014 {
1015     WIN32_FIND_DATAA data_a;    // window's file info (ansii version)
1016     WIN32_FIND_DATAW data_w;    // window's file info (wchar version)
1017     const char *spec;           // the directory we're traversing
1018     HANDLE      dirh;           // the search handle
1019     BOOL        valid_a;        // the info in data_a field is valid
1020     BOOL        valid_w;        // the info in data_w field is valid
1021     UINT32      offset;         // pseudo offset for d_off
1022 } _dir;
1023
1024 DIR *
1025 opendir(const char *path)
1026 {
1027     /* enough space for VSS !*/
1028     int max_len = strlen(path) + MAX_PATH;
1029     _dir *rval = NULL;
1030     if (path == NULL) {
1031        errno = ENOENT;
1032        return NULL;
1033     }
1034
1035     Dmsg1(100, "Opendir path=%s\n", path);
1036     rval = (_dir *)malloc(sizeof(_dir));
1037     memset (rval, 0, sizeof (_dir));
1038     if (rval == NULL) return NULL;
1039     char *tspec = (char *)malloc(max_len);
1040     if (tspec == NULL) return NULL;
1041
1042     conv_unix_to_win32_path(path, tspec, max_len);
1043     Dmsg1(100, "win32 path=%s\n", tspec);
1044
1045     // add backslash only if there is none yet (think of c:\)
1046     if (tspec[strlen(tspec)-1] != '\\')
1047       bstrncat(tspec, "\\*", max_len);
1048     else
1049       bstrncat(tspec, "*", max_len);
1050
1051     rval->spec = tspec;
1052
1053     // convert to wchar_t
1054     if (p_FindFirstFileW) {
1055       POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1056       make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1057
1058       rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1059
1060       free_pool_memory(pwcBuf);
1061
1062       if (rval->dirh != INVALID_HANDLE_VALUE)
1063         rval->valid_w = 1;
1064     } else if (p_FindFirstFileA) {
1065       rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1066
1067       if (rval->dirh != INVALID_HANDLE_VALUE)
1068         rval->valid_a = 1;
1069     } else goto err;
1070
1071
1072     Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1073           path, rval->spec, rval->dirh);
1074
1075     rval->offset = 0;
1076     if (rval->dirh == INVALID_HANDLE_VALUE)
1077         goto err;
1078
1079     if (rval->valid_w) {
1080       Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1081     }
1082
1083     if (rval->valid_a) {
1084       Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1085     }
1086
1087     return (DIR *)rval;
1088
1089 err:
1090     free((void *)rval->spec);
1091     free(rval);
1092     errno = b_errno_win32;
1093     return NULL;
1094 }
1095
1096 int
1097 closedir(DIR *dirp)
1098 {
1099     _dir *dp = (_dir *)dirp;
1100     FindClose(dp->dirh);
1101     free((void *)dp->spec);
1102     free((void *)dp);
1103     return 0;
1104 }
1105
1106 /*
1107   typedef struct _WIN32_FIND_DATA {
1108     DWORD dwFileAttributes;
1109     FILETIME ftCreationTime;
1110     FILETIME ftLastAccessTime;
1111     FILETIME ftLastWriteTime;
1112     DWORD nFileSizeHigh;
1113     DWORD nFileSizeLow;
1114     DWORD dwReserved0;
1115     DWORD dwReserved1;
1116     TCHAR cFileName[MAX_PATH];
1117     TCHAR cAlternateFileName[14];
1118 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1119 */
1120
1121 static int
1122 copyin(struct dirent &dp, const char *fname)
1123 {
1124     dp.d_ino = 0;
1125     dp.d_reclen = 0;
1126     char *cp = dp.d_name;
1127     while (*fname) {
1128         *cp++ = *fname++;
1129         dp.d_reclen++;
1130     }
1131         *cp = 0;
1132     return dp.d_reclen;
1133 }
1134
1135 int
1136 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1137 {
1138     _dir *dp = (_dir *)dirp;
1139     if (dp->valid_w || dp->valid_a) {
1140       entry->d_off = dp->offset;
1141
1142       // copy unicode
1143       if (dp->valid_w) {
1144          char szBuf[MAX_PATH_UTF8+1];
1145          wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1146          dp->offset += copyin(*entry, szBuf);
1147       } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1148          dp->offset += copyin(*entry, dp->data_a.cFileName);
1149       }
1150
1151       *result = entry;              /* return entry address */
1152       Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1153             dirp, entry->d_name, entry->d_reclen, entry->d_off);
1154     } else {
1155 //      Dmsg0(99, "readdir_r !valid\n");
1156         errno = b_errno_win32;
1157         return -1;
1158     }
1159
1160     // get next file, try unicode first
1161     if (p_FindNextFileW)
1162        dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1163     else if (p_FindNextFileA)
1164        dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1165     else {
1166        dp->valid_a = FALSE;
1167        dp->valid_w = FALSE;
1168     }
1169
1170     return 0;
1171 }
1172
1173 /*
1174  * Dotted IP address to network address
1175  *
1176  * Returns 1 if  OK
1177  *         0 on error
1178  */
1179 int
1180 inet_aton(const char *a, struct in_addr *inp)
1181 {
1182    const char *cp = a;
1183    uint32_t acc = 0, tmp = 0;
1184    int dotc = 0;
1185
1186    if (!isdigit(*cp)) {         /* first char must be digit */
1187       return 0;                 /* error */
1188    }
1189    do {
1190       if (isdigit(*cp)) {
1191          tmp = (tmp * 10) + (*cp -'0');
1192       } else if (*cp == '.' || *cp == 0) {
1193          if (tmp > 255) {
1194             return 0;           /* error */
1195          }
1196          acc = (acc << 8) + tmp;
1197          dotc++;
1198          tmp = 0;
1199       } else {
1200          return 0;              /* error */
1201       }
1202    } while (*cp++ != 0);
1203    if (dotc != 4) {              /* want 3 .'s plus EOS */
1204       return 0;                  /* error */
1205    }
1206    inp->s_addr = htonl(acc);     /* store addr in network format */
1207    return 1;
1208 }
1209
1210 int
1211 nanosleep(const struct timespec *req, struct timespec *rem)
1212 {
1213     if (rem)
1214         rem->tv_sec = rem->tv_nsec = 0;
1215     Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1216     return 0;
1217 }
1218
1219 void
1220 init_signals(void terminate(int sig))
1221 {
1222
1223 }
1224
1225 void
1226 init_stack_dump(void)
1227 {
1228
1229 }
1230
1231
1232 long
1233 pathconf(const char *path, int name)
1234 {
1235     switch(name) {
1236     case _PC_PATH_MAX :
1237         if (strncmp(path, "\\\\?\\", 4) == 0)
1238             return 32767;
1239     case _PC_NAME_MAX :
1240         return 255;
1241     }
1242     errno = ENOSYS;
1243     return -1;
1244 }
1245
1246 int
1247 WSA_Init(void)
1248 {
1249     WORD wVersionRequested = MAKEWORD( 1, 1);
1250     WSADATA wsaData;
1251
1252     int err = WSAStartup(wVersionRequested, &wsaData);
1253
1254
1255     if (err != 0) {
1256         printf("Can not start Windows Sockets\n");
1257         errno = ENOSYS;
1258         return -1;
1259     }
1260
1261     return 0;
1262 }
1263
1264
1265 int
1266 win32_chdir(const char *dir)
1267 {
1268    if (p_SetCurrentDirectoryW) {
1269       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1270       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1271
1272       BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1273       
1274       free_pool_memory(pwszBuf);
1275
1276       if (!b) {
1277          errno = b_errno_win32;
1278          return -1;
1279       }
1280    }
1281    else if (p_SetCurrentDirectoryA) {
1282       if (0 == p_SetCurrentDirectoryA(dir)) {
1283          errno = b_errno_win32;
1284          return -1;
1285       }
1286    }
1287    else return -1;
1288
1289    return 0;
1290 }
1291
1292 int
1293 win32_mkdir(const char *dir)
1294 {
1295    if (p_wmkdir){
1296       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1297       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1298
1299       int n = p_wmkdir((LPCWSTR)pwszBuf);
1300       free_pool_memory(pwszBuf);
1301       return n;
1302    }
1303
1304    return _mkdir(dir);
1305 }
1306
1307
1308 char *
1309 win32_getcwd(char *buf, int maxlen)
1310 {
1311    int n=0;
1312
1313    if (p_GetCurrentDirectoryW) {
1314       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1315       pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1316
1317       n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1318       if (n!=0)
1319          n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1320       free_pool_memory(pwszBuf);
1321
1322    } else if (p_GetCurrentDirectoryA)
1323       n = p_GetCurrentDirectoryA(maxlen, buf);
1324
1325    if (n == 0 || n > maxlen) return NULL;
1326
1327    if (n+1 > maxlen) return NULL;
1328    if (n != 3) {
1329        buf[n] = '\\';
1330        buf[n+1] = 0;
1331    }
1332    return buf;
1333 }
1334
1335 int
1336 win32_fputs(const char *string, FILE *stream)
1337 {
1338    /* we use WriteConsoleA / WriteConsoleA
1339       so we can be sure that unicode support works on win32.
1340       with fallback if something fails
1341    */
1342
1343    HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1344    if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1345        p_MultiByteToWideChar && (stream == stdout)) {
1346
1347       POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1348
1349       DWORD dwCharsWritten;
1350       DWORD dwChars;
1351
1352       dwChars = UTF8_2_wchar(&pwszBuf, string);
1353
1354       /* try WriteConsoleW */
1355       if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1356          free_pool_memory(pwszBuf);
1357          return dwCharsWritten;
1358       }
1359
1360       /* convert to local codepage and try WriteConsoleA */
1361       POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1362       pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1363
1364       dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1365       free_pool_memory(pwszBuf);
1366
1367       if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1368          free_pool_memory(pszBuf);
1369          return dwCharsWritten;
1370       }
1371       free_pool_memory(pszBuf);
1372    }
1373    /* Fall back */
1374    return fputs(string, stream);
1375 }
1376
1377 char*
1378 win32_cgets (char* buffer, int len)
1379 {
1380    /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1381       from the win32 console and fallback if seomething fails */
1382
1383    HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1384    if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1385       DWORD dwRead;
1386       wchar_t wszBuf[1024];
1387       char  szBuf[1024];
1388
1389       /* nt and unicode conversion */
1390       if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1391
1392          /* null terminate at end */
1393          if (wszBuf[dwRead-1] == L'\n') {
1394             wszBuf[dwRead-1] = L'\0';
1395             dwRead --;
1396          }
1397
1398          if (wszBuf[dwRead-1] == L'\r') {
1399             wszBuf[dwRead-1] = L'\0';
1400             dwRead --;
1401          }
1402
1403          wchar_2_UTF8(buffer, wszBuf, len);
1404          return buffer;
1405       }
1406
1407       /* win 9x and unicode conversion */
1408       if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1409
1410          /* null terminate at end */
1411          if (szBuf[dwRead-1] == L'\n') {
1412             szBuf[dwRead-1] = L'\0';
1413             dwRead --;
1414          }
1415
1416          if (szBuf[dwRead-1] == L'\r') {
1417             szBuf[dwRead-1] = L'\0';
1418             dwRead --;
1419          }
1420
1421          /* convert from ansii to wchar_t */
1422          p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1423          /* convert from wchar_t to UTF-8 */
1424          if (wchar_2_UTF8(buffer, wszBuf, len))
1425             return buffer;
1426       }
1427    }
1428
1429    /* fallback */
1430    if (fgets(buffer, len, stdin))
1431       return buffer;
1432    else
1433       return NULL;
1434 }
1435
1436 int
1437 win32_unlink(const char *filename)
1438 {
1439    int nRetCode;
1440    if (p_wunlink) {
1441       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1442       make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1443
1444       nRetCode = _wunlink((LPCWSTR) pwszBuf);
1445
1446       /* special case if file is readonly,
1447       we retry but unset attribute before */
1448       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1449          DWORD dwAttr =  p_GetFileAttributesW((LPCWSTR)pwszBuf);
1450          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1451             if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1452                nRetCode = _wunlink((LPCWSTR) pwszBuf);
1453                /* reset to original if it didn't help */
1454                if (nRetCode == -1)
1455                   p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1456             }
1457          }
1458       }
1459       free_pool_memory(pwszBuf);
1460    } else {
1461       nRetCode = _unlink(filename);
1462
1463       /* special case if file is readonly,
1464       we retry but unset attribute before */
1465       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1466          DWORD dwAttr =  p_GetFileAttributesA(filename);
1467          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1468             if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1469                nRetCode = _unlink(filename);
1470                /* reset to original if it didn't help */
1471                if (nRetCode == -1)
1472                   p_SetFileAttributesA(filename, dwAttr);
1473             }
1474          }
1475       }
1476    }
1477    return nRetCode;
1478 }
1479
1480
1481 #include "mswinver.h"
1482
1483 char WIN_VERSION_LONG[64];
1484 char WIN_VERSION[32];
1485 char WIN_RAWVERSION[32];
1486
1487 class winver {
1488 public:
1489     winver(void);
1490 };
1491
1492 static winver INIT;                     // cause constructor to be called before main()
1493
1494
1495 winver::winver(void)
1496 {
1497     const char *version = "";
1498     const char *platform = "";
1499     OSVERSIONINFO osvinfo;
1500     osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1501
1502     // Get the current OS version
1503     if (!GetVersionEx(&osvinfo)) {
1504         version = "Unknown";
1505         platform = "Unknown";
1506     }
1507         const int ver = _mkversion(osvinfo.dwPlatformId,
1508                                    osvinfo.dwMajorVersion,
1509                                    osvinfo.dwMinorVersion);
1510         snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1511          switch (ver)
1512         {
1513         case MS_WINDOWS_95: (version =  "Windows 95"); break;
1514         case MS_WINDOWS_98: (version =  "Windows 98"); break;
1515         case MS_WINDOWS_ME: (version =  "Windows ME"); break;
1516         case MS_WINDOWS_NT4:(version =  "Windows NT 4.0"); platform = "NT"; break;
1517         case MS_WINDOWS_2K: (version =  "Windows 2000");platform = "NT"; break;
1518         case MS_WINDOWS_XP: (version =  "Windows XP");platform = "NT"; break;
1519         case MS_WINDOWS_S2003: (version =  "Windows Server 2003");platform = "NT"; break;
1520         default: version = WIN_RAWVERSION; break;
1521         }
1522
1523     bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1524     snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1525              platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1526
1527 #if 0
1528     HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1529     CloseHandle(h);
1530 #endif
1531 #if 0
1532     BPIPE *b = open_bpipe("ls -l", 10, "r");
1533     char buf[1024];
1534     while (!feof(b->rfd)) {
1535         fgets(buf, sizeof(buf), b->rfd);
1536     }
1537     close_bpipe(b);
1538 #endif
1539 }
1540
1541 BOOL CreateChildProcess(VOID);
1542 VOID WriteToPipe(VOID);
1543 VOID ReadFromPipe(VOID);
1544 VOID ErrorExit(LPCSTR);
1545 VOID ErrMsg(LPTSTR, BOOL);
1546
1547 /**
1548  * Check for a quoted path,  if an absolute path name is given and it contains
1549  * spaces it will need to be quoted.  i.e.  "c:/Program Files/foo/bar.exe"
1550  * CreateProcess() says the best way to ensure proper results with executables
1551  * with spaces in path or filename is to quote the string.
1552  */
1553 const char *
1554 getArgv0(const char *cmdline)
1555 {
1556
1557     int inquote = 0;
1558     const char *cp;
1559     for (cp = cmdline; *cp; cp++)
1560     {
1561         if (*cp == '"') {
1562             inquote = !inquote;
1563         }
1564         if (!inquote && isspace(*cp))
1565             break;
1566     }
1567
1568
1569     int len = cp - cmdline;
1570     char *rval = (char *)malloc(len+1);
1571
1572     cp = cmdline;
1573     char *rp = rval;
1574
1575     while (len--)
1576         *rp++ = *cp++;
1577
1578     *rp = 0;
1579     return rval;
1580 }
1581
1582 /*
1583  * Extracts the executable or script name from the first string in 
1584  * cmdline.
1585  *
1586  * If the name contains blanks then it must be quoted with double quotes,
1587  * otherwise quotes are optional.  If the name contains blanks then it 
1588  * will be converted to a short name.
1589  *
1590  * The optional quotes will be removed.  The result is copied to a malloc'ed
1591  * buffer and returned through the pexe argument.  The pargs parameter is set
1592  * to the address of the character in cmdline located after the name.
1593  *
1594  * The malloc'ed buffer returned in *pexe must be freed by the caller.
1595  */
1596 bool
1597 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1598 {
1599    const char *pExeStart = NULL;    /* Start of executable name in cmdline */
1600    const char *pExeEnd = NULL;      /* Character after executable name (separator) */
1601
1602    const char *pBasename = NULL;    /* Character after last path separator */
1603    const char *pExtension = NULL;   /* Period at start of extension */
1604
1605    const char *current = cmdline;
1606
1607    bool bQuoted = false;
1608
1609    /* Skip initial whitespace */
1610
1611    while (*current == ' ' || *current == '\t')
1612    {
1613       current++;
1614    }
1615
1616    /* Calculate start of name and determine if quoted */
1617
1618    if (*current == '"') {
1619       pExeStart = ++current;
1620       bQuoted = true;
1621    } else {
1622       pExeStart = current;
1623       bQuoted = false;
1624    }
1625
1626    *pargs = NULL;
1627    *pexe = NULL;
1628
1629    /* 
1630     * Scan command line looking for path separators (/ and \\) and the 
1631     * terminator, either a quote or a blank.  The location of the 
1632     * extension is also noted.
1633     */
1634
1635    for ( ; *current != '\0'; current++)
1636    {
1637       if (*current == '.') {
1638          pExtension = current;
1639       } else if (IsPathSeparator(*current) && current[1] != '\0') {
1640          pBasename = &current[1];
1641          pExtension = NULL;
1642       }
1643
1644       /* Check for terminator, either quote or blank */
1645       if (bQuoted) {
1646          if (*current != '"') {
1647             continue;
1648          }
1649       } else {
1650          if (*current != ' ') {
1651             continue;
1652          }
1653       }
1654
1655       /*
1656        * Hit terminator, remember end of name (address of terminator) and 
1657        * start of arguments 
1658        */
1659       pExeEnd = current;
1660
1661       if (bQuoted && *current == '"') {
1662          *pargs = &current[1];
1663       } else {
1664          *pargs = current;
1665       }
1666
1667       break;
1668    }
1669
1670    if (pBasename == NULL) {
1671       pBasename = pExeStart;
1672    }
1673
1674    if (pExeEnd == NULL) {
1675       pExeEnd = current;
1676    }
1677
1678    if (*pargs == NULL)
1679    {
1680       *pargs = current;
1681    }
1682
1683    bool bHasPathSeparators = pExeStart != pBasename;
1684
1685    /* We have pointers to all the useful parts of the name */
1686
1687    /* Default extensions in the order cmd.exe uses to search */
1688
1689    static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1690    DWORD dwBasePathLength = pExeEnd - pExeStart;
1691
1692    DWORD dwAltNameLength = 0;
1693    char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1694    char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1695
1696    pPathname[MAX_PATHLENGTH] = '\0';
1697    pAltPathname[MAX_PATHLENGTH] = '\0';
1698
1699    memcpy(pPathname, pExeStart, dwBasePathLength);
1700    pPathname[dwBasePathLength] = '\0';
1701
1702    if (pExtension == NULL) {
1703       /* Try appending extensions */
1704       for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1705
1706          if (!bHasPathSeparators) {
1707             /* There are no path separators, search in the standard locations */
1708             dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1709             if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1710                memcpy(pPathname, pAltPathname, dwAltNameLength);
1711                pPathname[dwAltNameLength] = '\0';
1712                break;
1713             }
1714          } else {
1715             bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1716             if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1717                break;
1718             }
1719             pPathname[dwBasePathLength] = '\0';
1720          }
1721       }
1722    } else if (!bHasPathSeparators) {
1723       /* There are no path separators, search in the standard locations */
1724       dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1725       if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1726          memcpy(pPathname, pAltPathname, dwAltNameLength);
1727          pPathname[dwAltNameLength] = '\0';
1728       }
1729    }
1730
1731    if (strchr(pPathname, ' ') != NULL) {
1732       dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1733
1734       if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1735          *pexe = (char *)malloc(dwAltNameLength + 1);
1736          if (*pexe == NULL) {
1737             return false;
1738          }
1739          memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1740       }
1741    }
1742
1743    if (*pexe == NULL) {
1744       DWORD dwPathnameLength = strlen(pPathname);
1745       *pexe = (char *)malloc(dwPathnameLength + 1);
1746       if (*pexe == NULL) {
1747          return false;
1748       }
1749       memcpy(*pexe, pPathname, dwPathnameLength + 1);
1750    }
1751
1752    return true;
1753 }
1754
1755 /**
1756  * OK, so it would seem CreateProcess only handles true executables:
1757  * .com or .exe files.  So grab $COMSPEC value and pass command line to it.
1758  */
1759 HANDLE
1760 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
1761 {
1762    static const char *comspec = NULL;
1763    PROCESS_INFORMATION piProcInfo;
1764    STARTUPINFOA siStartInfo;
1765    BOOL bFuncRetn = FALSE;
1766
1767    if (comspec == NULL) {
1768       comspec = getenv("COMSPEC");
1769    }
1770    if (comspec == NULL) // should never happen
1771       return INVALID_HANDLE_VALUE;
1772
1773    // Set up members of the PROCESS_INFORMATION structure.
1774    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
1775
1776    // Set up members of the STARTUPINFO structure.
1777
1778    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
1779    siStartInfo.cb = sizeof(STARTUPINFO);
1780    // setup new process to use supplied handles for stdin,stdout,stderr
1781    // if supplied handles are not used the send a copy of our STD_HANDLE
1782    // as appropriate
1783    siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1784    siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1785
1786    if (in != INVALID_HANDLE_VALUE)
1787       siStartInfo.hStdInput = in;
1788    else
1789       siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1790
1791    if (out != INVALID_HANDLE_VALUE)
1792       siStartInfo.hStdOutput = out;
1793    else
1794       siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1795    if (err != INVALID_HANDLE_VALUE)
1796       siStartInfo.hStdError = err;
1797    else
1798       siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1799
1800    // Create the child process.
1801
1802    char *exeFile;
1803    const char *argStart;
1804
1805    if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
1806       return INVALID_HANDLE_VALUE;
1807    }
1808
1809    int cmdLen = strlen(comspec) + 4 + strlen(exeFile) + strlen(argStart) + 1;
1810
1811    char *cmdLine = (char *)alloca(cmdLen);
1812
1813    snprintf(cmdLine, cmdLen, "%s /c %s%s", comspec, exeFile, argStart);
1814
1815    free(exeFile);
1816
1817    Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
1818
1819    // try to execute program
1820    bFuncRetn = CreateProcessA(comspec,
1821                               cmdLine,       // command line
1822                               NULL,          // process security attributes
1823                               NULL,          // primary thread security attributes
1824                               TRUE,          // handles are inherited
1825                               0,             // creation flags
1826                               NULL,          // use parent's environment
1827                               NULL,          // use parent's current directory
1828                               &siStartInfo,  // STARTUPINFO pointer
1829                               &piProcInfo);  // receives PROCESS_INFORMATION
1830
1831    if (bFuncRetn == 0) {
1832       ErrorExit("CreateProcess failed\n");
1833       const char *err = errorString();
1834       Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n", comspec, cmdLine, err);
1835       LocalFree((void *)err);
1836       return INVALID_HANDLE_VALUE;
1837    }
1838    // we don't need a handle on the process primary thread so we close
1839    // this now.
1840    CloseHandle(piProcInfo.hThread);
1841
1842    return piProcInfo.hProcess;
1843 }
1844
1845
1846 void
1847 ErrorExit (LPCSTR lpszMessage)
1848 {
1849     Dmsg1(0, "%s", lpszMessage);
1850 }
1851
1852
1853 /*
1854 typedef struct s_bpipe {
1855    pid_t worker_pid;
1856    time_t worker_stime;
1857    int wait;
1858    btimer_t *timer_id;
1859    FILE *rfd;
1860    FILE *wfd;
1861 } BPIPE;
1862 */
1863
1864 static void
1865 CloseIfValid(HANDLE handle)
1866 {
1867     if (handle != INVALID_HANDLE_VALUE)
1868         CloseHandle(handle);
1869 }
1870
1871 BPIPE *
1872 open_bpipe(char *prog, int wait, const char *mode)
1873 {
1874     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
1875         hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
1876         hInputFile;
1877
1878     SECURITY_ATTRIBUTES saAttr;
1879
1880     BOOL fSuccess;
1881
1882     hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
1883         hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
1884         hInputFile = INVALID_HANDLE_VALUE;
1885
1886     BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
1887     memset((void *)bpipe, 0, sizeof(BPIPE));
1888
1889     int mode_read = (mode[0] == 'r');
1890     int mode_write = (mode[0] == 'w' || mode[1] == 'w');
1891
1892
1893     // Set the bInheritHandle flag so pipe handles are inherited.
1894
1895     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1896     saAttr.bInheritHandle = TRUE;
1897     saAttr.lpSecurityDescriptor = NULL;
1898
1899     if (mode_read) {
1900
1901         // Create a pipe for the child process's STDOUT.
1902         if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
1903             ErrorExit("Stdout pipe creation failed\n");
1904             goto cleanup;
1905         }
1906         // Create noninheritable read handle and close the inheritable read
1907         // handle.
1908
1909         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
1910                                    GetCurrentProcess(), &hChildStdoutRdDup , 0,
1911                                    FALSE,
1912                                    DUPLICATE_SAME_ACCESS);
1913         if ( !fSuccess ) {
1914             ErrorExit("DuplicateHandle failed");
1915             goto cleanup;
1916         }
1917
1918         CloseHandle(hChildStdoutRd);
1919         hChildStdoutRd = INVALID_HANDLE_VALUE;
1920     }
1921
1922     if (mode_write) {
1923
1924         // Create a pipe for the child process's STDIN.
1925
1926         if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
1927             ErrorExit("Stdin pipe creation failed\n");
1928             goto cleanup;
1929         }
1930
1931         // Duplicate the write handle to the pipe so it is not inherited.
1932         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
1933                                    GetCurrentProcess(), &hChildStdinWrDup,
1934                                    0,
1935                                    FALSE,                  // not inherited
1936                                    DUPLICATE_SAME_ACCESS);
1937         if (!fSuccess) {
1938             ErrorExit("DuplicateHandle failed");
1939             goto cleanup;
1940         }
1941
1942         CloseHandle(hChildStdinWr);
1943         hChildStdinWr = INVALID_HANDLE_VALUE;
1944     }
1945     // spawn program with redirected handles as appropriate
1946     bpipe->worker_pid = (pid_t)
1947         CreateChildProcess(prog,             // commandline
1948                            hChildStdinRd,    // stdin HANDLE
1949                            hChildStdoutWr,   // stdout HANDLE
1950                            hChildStdoutWr);  // stderr HANDLE
1951
1952     if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
1953         goto cleanup;
1954
1955     bpipe->wait = wait;
1956     bpipe->worker_stime = time(NULL);
1957
1958     if (mode_read) {
1959         CloseHandle(hChildStdoutWr); // close our write side so when
1960                                      // process terminates we can
1961                                      // detect eof.
1962         // ugly but convert WIN32 HANDLE to FILE*
1963         int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY | O_BINARY);
1964         if (rfd >= 0) {
1965            bpipe->rfd = _fdopen(rfd, "rb");
1966         }
1967     }
1968     if (mode_write) {
1969         CloseHandle(hChildStdinRd); // close our read side so as not
1970                                     // to interfre with child's copy
1971         // ugly but convert WIN32 HANDLE to FILE*
1972         int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY | O_BINARY);
1973         if (wfd >= 0) {
1974            bpipe->wfd = _fdopen(wfd, "wb");
1975         }
1976     }
1977
1978     if (wait > 0) {
1979         bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
1980     }
1981
1982     return bpipe;
1983
1984 cleanup:
1985
1986     CloseIfValid(hChildStdoutRd);
1987     CloseIfValid(hChildStdoutRdDup);
1988     CloseIfValid(hChildStdinWr);
1989     CloseIfValid(hChildStdinWrDup);
1990
1991     free((void *) bpipe);
1992     errno = b_errno_win32;            /* do GetLastError() for error code */
1993     return NULL;
1994 }
1995
1996
1997 int
1998 kill(int pid, int signal)
1999 {
2000    int rval = 0;
2001    if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2002       rval = -1;
2003       errno = b_errno_win32;
2004    }
2005    CloseHandle((HANDLE)pid);
2006    return rval;
2007 }
2008
2009
2010 int
2011 close_bpipe(BPIPE *bpipe)
2012 {
2013    int rval = 0;
2014    int32_t remaining_wait = bpipe->wait;
2015
2016    /* Close pipes */
2017    if (bpipe->rfd) {
2018       fclose(bpipe->rfd);
2019       bpipe->rfd = NULL;
2020    }
2021    if (bpipe->wfd) {
2022       fclose(bpipe->wfd);
2023       bpipe->wfd = NULL;
2024    }
2025
2026    if (remaining_wait == 0) {         /* wait indefinitely */
2027       remaining_wait = INT32_MAX;
2028    }
2029    for ( ;; ) {
2030       DWORD exitCode;
2031       if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2032          const char *err = errorString();
2033          rval = b_errno_win32;
2034          Dmsg1(0, "GetExitCode error %s\n", err);
2035          LocalFree((void *)err);
2036          break;
2037       }
2038       if (exitCode == STILL_ACTIVE) {
2039          if (remaining_wait <= 0) {
2040             rval = ETIME;             /* timed out */
2041             break;
2042          }
2043          bmicrosleep(1, 0);           /* wait one second */
2044          remaining_wait--;
2045       } else if (exitCode != 0) {
2046          /* Truncate exit code as it doesn't seem to be correct */
2047          rval = (exitCode & 0xFF) | b_errno_exit;
2048          break;
2049       } else {
2050          break;                       /* Shouldn't get here */
2051       }
2052    }
2053
2054    if (bpipe->timer_id) {
2055        stop_child_timer(bpipe->timer_id);
2056    }
2057    if (bpipe->rfd) fclose(bpipe->rfd);
2058    if (bpipe->wfd) fclose(bpipe->wfd);
2059    free((void *)bpipe);
2060    return rval;
2061 }
2062
2063 int
2064 close_wpipe(BPIPE *bpipe)
2065 {
2066     int result = 1;
2067
2068     if (bpipe->wfd) {
2069         fflush(bpipe->wfd);
2070         if (fclose(bpipe->wfd) != 0) {
2071             result = 0;
2072         }
2073         bpipe->wfd = NULL;
2074     }
2075     return result;
2076 }
2077
2078 #include "findlib/find.h"
2079
2080 int
2081 utime(const char *fname, struct utimbuf *times)
2082 {
2083     FILETIME acc, mod;
2084     char tmpbuf[5000];
2085
2086     conv_unix_to_win32_path(fname, tmpbuf, 5000);
2087
2088     cvt_utime_to_ftime(times->actime, acc);
2089     cvt_utime_to_ftime(times->modtime, mod);
2090
2091     HANDLE h = INVALID_HANDLE_VALUE;
2092
2093     if (p_CreateFileW) {
2094       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2095       make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2096
2097       h = p_CreateFileW((LPCWSTR)pwszBuf,
2098                         FILE_WRITE_ATTRIBUTES,
2099                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2100                         NULL,
2101                         OPEN_EXISTING,
2102                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2103                         NULL);
2104
2105       free_pool_memory(pwszBuf);
2106     } else if (p_CreateFileA) {
2107       h = p_CreateFileA(tmpbuf,
2108                         FILE_WRITE_ATTRIBUTES,
2109                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2110                         NULL,
2111                         OPEN_EXISTING,
2112                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2113                         NULL);
2114     }
2115
2116     if (h == INVALID_HANDLE_VALUE) {
2117         const char *err = errorString();
2118         Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2119         LocalFree((void *)err);
2120         errno = b_errno_win32;
2121         return -1;
2122     }
2123
2124     int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2125     CloseHandle(h);
2126     if (rval == -1) {
2127        errno = b_errno_win32;
2128     }
2129     return rval;
2130 }
2131
2132 #if 0
2133 int
2134 file_open(const char *file, int flags, int mode)
2135 {
2136     DWORD access = 0;
2137     DWORD shareMode = 0;
2138     DWORD create = 0;
2139     DWORD msflags = 0;
2140     HANDLE foo = INVALID_HANDLE_VALUE;
2141     const char *remap = file;
2142
2143     if (flags & O_WRONLY) access = GENERIC_WRITE;
2144     else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2145     else access = GENERIC_READ;
2146
2147     if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2148        create = CREATE_NEW;
2149     else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2150        create = CREATE_ALWAYS;
2151     else if (flags & O_CREAT)
2152        create = OPEN_ALWAYS;
2153     else if (flags & O_TRUNC)
2154        create = TRUNCATE_EXISTING;
2155     else 
2156        create = OPEN_EXISTING;
2157
2158     shareMode = 0;
2159
2160     if (flags & O_APPEND) {
2161         printf("open...APPEND not implemented yet.");
2162         exit(-1);
2163     }
2164
2165     if (p_CreateFileW) {
2166        POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2167        make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2168
2169        foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2170        free_pool_memory(pwszBuf);
2171     } else if (p_CreateFileA)
2172        foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2173
2174     if (INVALID_HANDLE_VALUE == foo) {
2175         errno = b_errno_win32;
2176         return(int) -1;
2177     }
2178     return (int)foo;
2179
2180 }
2181
2182
2183 int
2184 file_close(int fd)
2185 {
2186     if (!CloseHandle((HANDLE)fd)) {
2187         errno = b_errno_win32;
2188         return -1;
2189     }
2190
2191     return 0;
2192 }
2193
2194 ssize_t
2195 file_write(int fd, const void *data, ssize_t len)
2196 {
2197     BOOL status;
2198     DWORD bwrite;
2199     status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2200     if (status) return bwrite;
2201     errno = b_errno_win32;
2202     return -1;
2203 }
2204
2205
2206 ssize_t
2207 file_read(int fd, void *data, ssize_t len)
2208 {
2209     BOOL status;
2210     DWORD bread;
2211
2212     status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2213     if (status) return bread;
2214     errno = b_errno_win32;
2215     return -1;
2216 }
2217
2218 boffset_t
2219 file_seek(int fd, boffset_t offset, int whence)
2220 {
2221     DWORD method = 0;
2222     DWORD val;
2223     LONG  offset_low = (LONG)offset;
2224     LONG  offset_high = (LONG)(offset >> 32);
2225
2226     switch (whence) {
2227     case SEEK_SET :
2228         method = FILE_BEGIN;
2229         break;
2230     case SEEK_CUR:
2231         method = FILE_CURRENT;
2232         break;
2233     case SEEK_END:
2234         method = FILE_END;
2235         break;
2236     default:
2237         errno = EINVAL;
2238         return -1;
2239     }
2240
2241
2242     if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2243        errno = b_errno_win32;
2244        return -1;
2245     }
2246     /* ***FIXME*** I doubt this works right */
2247     return val;
2248 }
2249
2250 int
2251 file_dup2(int, int)
2252 {
2253     errno = ENOSYS;
2254     return -1;
2255 }
2256 #endif
2257
2258 #ifdef HAVE_MINGW
2259 /* syslog function, added by Nicolas Boichat */
2260 void openlog(const char *ident, int option, int facility) {}  
2261 #endif //HAVE_MINGW