2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2010 Free Software Foundation Europe e.V.
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 three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
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.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
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.
29 // compat.cpp -- compatibilty layer to make bacula-fd run
30 // natively under windows
32 // Copyright transferred from Raider Solutions, Inc to
33 // Kern Sibbald and John Walker by express permission.
35 // Author : Christopher S. Hull
36 // Created On : Sat Jan 31 15:55:00 2004
42 #include "findlib/find.h"
44 #define b_errno_win32 (1<<29)
46 #define MAX_PATHLENGTH 1024
49 UTF-8 to UCS2 path conversion is expensive,
50 so we cache the conversion. During backup the
51 conversion is called 3 times (lstat, attribs, open),
52 by using the cache this is reduced to 1 time
54 static POOLMEM *g_pWin32ConvUTF8Cache = NULL;
55 static POOLMEM *g_pWin32ConvUCS2Cache = NULL;
56 static DWORD g_dwWin32ConvUTF8strlen = 0;
57 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
59 static t_pVSSPathConvert g_pVSSPathConvert;
60 static t_pVSSPathConvertW g_pVSSPathConvertW;
62 /* Forward referenced functions */
63 static const char *errorString(void);
66 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
68 g_pVSSPathConvert = pPathConvert;
69 g_pVSSPathConvertW = pPathConvertW;
72 static void Win32ConvInitCache()
74 if (g_pWin32ConvUTF8Cache) {
77 g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
78 g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
81 void Win32ConvCleanupCache()
84 if (g_pWin32ConvUTF8Cache) {
85 free_pool_memory(g_pWin32ConvUTF8Cache);
86 g_pWin32ConvUTF8Cache = NULL;
89 if (g_pWin32ConvUCS2Cache) {
90 free_pool_memory(g_pWin32ConvUCS2Cache);
91 g_pWin32ConvUCS2Cache = NULL;
94 g_dwWin32ConvUTF8strlen = 0;
99 /* to allow the usage of the original version in this file here */
103 //#define USE_WIN32_COMPAT_IO 1
104 #define USE_WIN32_32KPATHCONVERSION 1
106 extern DWORD g_platform_id;
107 extern DWORD g_MinorVersion;
109 /* From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 */
111 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
113 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
116 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
119 * Convert from UTF-8 to VSS Windows path/file
120 * Used by compatibility layer for Unix system calls
122 static void conv_unix_to_vss_win32_path(const char *name, char *win32_name, DWORD dwSize)
124 const char *fname = name;
125 char *tname = win32_name;
127 Dmsg0(100, "Enter convert_unix_to_win32_path\n");
129 if (IsPathSeparator(name[0]) &&
130 IsPathSeparator(name[1]) &&
132 IsPathSeparator(name[3])) {
134 *win32_name++ = '\\';
135 *win32_name++ = '\\';
137 *win32_name++ = '\\';
140 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
141 g_pVSSPathConvert == NULL) {
142 /* allow path to be 32767 bytes */
143 *win32_name++ = '\\';
144 *win32_name++ = '\\';
146 *win32_name++ = '\\';
150 /** Check for Unix separator and convert to Win32 */
151 if (name[0] == '/' && name[1] == '/') { /* double slash? */
152 name++; /* yes, skip first one */
155 *win32_name++ = '\\'; /* convert char */
156 /* If Win32 separator that is "quoted", remove quote */
157 } else if (*name == '\\' && name[1] == '\\') {
158 *win32_name++ = '\\';
159 name++; /* skip first \ */
161 *win32_name++ = *name; /* copy character */
165 /** Strip any trailing slash, if we stored something
166 * but leave "c:\" with backslash (root directory case
168 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
174 /** here we convert to VSS specific file name which
175 can get longer because VSS will make something like
176 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
177 from c:\bacula\uninstall.exe
179 Dmsg1(100, "path=%s\n", tname);
180 if (g_pVSSPathConvert != NULL) {
181 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
182 pszBuf = check_pool_memory_size(pszBuf, dwSize);
183 bstrncpy(pszBuf, tname, strlen(tname)+1);
184 g_pVSSPathConvert(pszBuf, tname, dwSize);
185 free_pool_memory(pszBuf);
188 Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
191 /** Conversion of a Unix filename to a Win32 filename */
192 void unix_name_to_win32(POOLMEM **win32_name, char *name)
194 /* One extra byte should suffice, but we double it */
195 /* add MAX_PATH bytes for VSS shadow copy name */
196 DWORD dwSize = 2*strlen(name)+MAX_PATH;
197 *win32_name = check_pool_memory_size(*win32_name, dwSize);
198 conv_unix_to_vss_win32_path(name, *win32_name, dwSize);
203 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
204 * will complete the input path to an absolue path of the form \\?\c:\path\file
206 * With this trick, it is possible to have 32K characters long paths.
208 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
209 * to a raw windows partition.
211 * created 02/27/2006 Thorsten Engel
214 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
217 Dmsg0(100, "Enter wchar_win32_path\n");
219 *pBIsRawPath = FALSE; /* Initialize, set later */
222 if (!p_GetCurrentDirectoryW) {
223 Dmsg0(100, "Leave wchar_win32_path no change \n");
227 wchar_t *name = (wchar_t *)pszUCSPath;
229 /* if it has already the desired form, exit without changes */
230 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
231 Dmsg0(100, "Leave wchar_win32_path no change \n");
235 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
236 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
237 DWORD dwCurDirPathSize = 0;
239 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
240 DWORD dwBufCharsNeeded = (wcslen(name)+7);
241 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
243 /* add \\?\ to support 32K long filepaths
244 it is important to make absolute paths, so we add drive and
245 current path if necessary */
247 BOOL bAddDrive = TRUE;
248 BOOL bAddCurrentPath = TRUE;
249 BOOL bAddPrefix = TRUE;
251 /* does path begin with drive? if yes, it is absolute */
252 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
254 bAddCurrentPath = FALSE;
257 /* is path absolute? */
258 if (IsPathSeparator(name[0]))
259 bAddCurrentPath = FALSE;
261 /* is path relative to itself?, if yes, skip ./ */
262 if (name[0] == '.' && IsPathSeparator(name[1])) {
266 /* is path of form '//./'? */
267 if (IsPathSeparator(name[0]) &&
268 IsPathSeparator(name[1]) &&
270 IsPathSeparator(name[3])) {
272 bAddCurrentPath = FALSE;
279 int nParseOffset = 0;
281 /* add 4 bytes header */
284 wcscpy(pwszBuf, L"\\\\?\\");
287 /* get current path if needed */
288 if (bAddDrive || bAddCurrentPath) {
289 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
290 if (dwCurDirPathSize > 0) {
291 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
292 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
293 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
295 /* we have no info for doing so */
297 bAddCurrentPath = FALSE;
302 /* add drive if needed */
303 if (bAddDrive && !bAddCurrentPath) {
306 if (IsPathSeparator(pwszCurDirBuf[0]) &&
307 IsPathSeparator(pwszCurDirBuf[1]) &&
308 pwszCurDirBuf[2] == '?' &&
309 IsPathSeparator(pwszCurDirBuf[3])) {
310 /* copy drive character */
311 szDrive[0] = pwszCurDirBuf[4];
313 /* copy drive character */
314 szDrive[0] = pwszCurDirBuf[0];
320 wcscat(pwszBuf, szDrive);
324 /* add path if needed */
325 if (bAddCurrentPath) {
326 /* the 1 add. character is for the eventually added backslash */
327 dwBufCharsNeeded += dwCurDirPathSize+1;
328 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
329 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
331 if (IsPathSeparator(pwszCurDirBuf[0]) &&
332 IsPathSeparator(pwszCurDirBuf[1]) &&
333 pwszCurDirBuf[2] == '?' &&
334 IsPathSeparator(pwszCurDirBuf[3])) {
335 /* copy complete string */
336 wcscpy(pwszBuf, pwszCurDirBuf);
339 wcscat(pwszBuf, pwszCurDirBuf);
342 nParseOffset = wcslen((LPCWSTR) pwszBuf);
344 /* check if path ends with backslash, if not, add one */
345 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
346 wcscat(pwszBuf, L"\\");
351 wchar_t *win32_name = &pwszBuf[nParseOffset];
352 wchar_t *name_start = name;
355 /* Check for Unix separator and convert to Win32, eliminating
356 * duplicate separators.
358 if (IsPathSeparator(*name)) {
359 *win32_name++ = '\\'; /* convert char */
361 /* Eliminate consecutive slashes, but not at the start so that
364 if (name_start != name && IsPathSeparator(name[1])) {
368 *win32_name++ = *name; /* copy character */
373 /* null terminate string */
376 /* here we convert to VSS specific file name which
377 * can get longer because VSS will make something like
378 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
379 * from c:\bacula\uninstall.exe
381 if (g_pVSSPathConvertW != NULL) {
382 /* is output buffer large enough? */
383 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
384 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
385 /* create temp. buffer */
386 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
387 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
388 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
393 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
394 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
395 free_pool_memory((POOLMEM *)pszBuf);
398 free_pool_memory(pszUCSPath);
399 free_pool_memory((POOLMEM *)pwszCurDirBuf);
401 Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
402 return (POOLMEM *)pwszBuf;
406 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
409 * The return value is the number of bytes written to the buffer.
410 * The number includes the byte for the null terminator.
413 if (p_WideCharToMultiByte) {
414 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
423 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
425 /* the return value is the number of wide characters written to the buffer. */
426 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
428 if (p_MultiByteToWideChar) {
429 /* strlen of UTF8 +1 is enough */
430 DWORD cchSize = (strlen(pszUTF)+1);
431 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
433 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
443 wchar_win32_path(const char *name, wchar_t *win32_name)
445 const char *fname = name;
447 /* Check for Unix separator and convert to Win32 */
449 *win32_name++ = '\\'; /* convert char */
450 /* If Win32 separated that is "quoted", remove quote */
451 } else if (*name == '\\' && name[1] == '\\') {
452 *win32_name++ = '\\';
453 name++; /* skip first \ */
455 *win32_name++ = *name; /* copy character */
459 /* Strip any trailing slash, if we stored something */
460 if (*fname != 0 && win32_name[-1] == '\\') {
468 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
471 /* if we find the utf8 string in cache, we use the cached ucs2 version.
472 we compare the stringlength first (quick check) and then compare the content.
474 if (!g_pWin32ConvUTF8Cache) {
475 Win32ConvInitCache();
476 } else if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
477 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
478 /* Return cached value */
479 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
480 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
481 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
483 return nBufSize / sizeof (WCHAR);
487 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
488 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
490 #ifdef USE_WIN32_32KPATHCONVERSION
491 /* add \\?\ to support 32K long filepaths */
492 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
495 *pBIsRawPath = FALSE;
499 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
500 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
502 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
503 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
504 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
510 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
517 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
518 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
521 void *dlopen(const char *file, int mode)
525 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
529 void *dlsym(void *handle, const char *name)
532 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
536 int dlclose(void *handle)
538 if (handle && !FreeLibrary((HMODULE)handle)) {
539 errno = b_errno_win32;
540 return 1; /* failed */
547 static char buf[200];
548 const char *err = errorString();
549 bstrncpy(buf, (char *)err, sizeof(buf));
550 LocalFree((void *)err);
554 int fcntl(int fd, int cmd)
559 int chown(const char *k, uid_t, gid_t)
564 int lchown(const char *k, uid_t, gid_t)
576 srandom(unsigned int seed)
580 // /////////////////////////////////////////////////////////////////
581 // convert from Windows concept of time to Unix concept of time
582 // /////////////////////////////////////////////////////////////////
584 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
586 uint64_t mstime = time;
587 mstime *= WIN32_FILETIME_SCALE;
588 mstime += WIN32_FILETIME_ADJUST;
590 #if defined(_MSC_VER)
591 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
593 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
595 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
599 cvt_ftime_to_utime(const FILETIME &time)
601 uint64_t mstime = time.dwHighDateTime;
603 mstime |= time.dwLowDateTime;
605 mstime -= WIN32_FILETIME_ADJUST;
606 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
608 return (time_t) (mstime & 0xffffffff);
611 static const char *errorString(void)
615 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
616 FORMAT_MESSAGE_FROM_SYSTEM |
617 FORMAT_MESSAGE_IGNORE_INSERTS,
620 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
625 /* Strip any \r or \n */
626 char *rval = (char *) lpMsgBuf;
627 char *cp = strchr(rval, '\r');
631 cp = strchr(rval, '\n');
640 statDir(const char *file, struct stat *sb)
642 WIN32_FIND_DATAW info_w; // window's file info
643 WIN32_FIND_DATAA info_a; // window's file info
645 // cache some common vars to make code more transparent
646 DWORD *pdwFileAttributes;
647 DWORD *pnFileSizeHigh;
648 DWORD *pnFileSizeLow;
650 FILETIME *pftLastAccessTime;
651 FILETIME *pftLastWriteTime;
652 FILETIME *pftCreationTime;
655 * Oh, cool, another exception: Microsoft doesn't let us do
656 * FindFile operations on a Drive, so simply fake root attibutes.
658 if (file[1] == ':' && file[2] == 0) {
659 time_t now = time(NULL);
660 Dmsg1(99, "faking ROOT attrs(%s).\n", file);
661 sb->st_mode = S_IFDIR;
662 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
670 HANDLE h = INVALID_HANDLE_VALUE;
673 if (p_FindFirstFileW) {
674 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
675 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
677 Dmsg1(100, "FindFirstFileW=%s\n", file);
678 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
679 free_pool_memory(pwszBuf);
681 pdwFileAttributes = &info_w.dwFileAttributes;
682 pdwReserved0 = &info_w.dwReserved0;
683 pnFileSizeHigh = &info_w.nFileSizeHigh;
684 pnFileSizeLow = &info_w.nFileSizeLow;
685 pftLastAccessTime = &info_w.ftLastAccessTime;
686 pftLastWriteTime = &info_w.ftLastWriteTime;
687 pftCreationTime = &info_w.ftCreationTime;
690 } else if (p_FindFirstFileA) {
691 Dmsg1(100, "FindFirstFileA=%s\n", file);
692 h = p_FindFirstFileA(file, &info_a);
694 pdwFileAttributes = &info_a.dwFileAttributes;
695 pdwReserved0 = &info_a.dwReserved0;
696 pnFileSizeHigh = &info_a.nFileSizeHigh;
697 pnFileSizeLow = &info_a.nFileSizeLow;
698 pftLastAccessTime = &info_a.ftLastAccessTime;
699 pftLastWriteTime = &info_a.ftLastWriteTime;
700 pftCreationTime = &info_a.ftCreationTime;
702 Dmsg0(100, "No findFirstFile A or W found\n");
705 if (h == INVALID_HANDLE_VALUE) {
706 const char *err = errorString();
708 * Note, in creating leading paths, it is normal that
709 * the file does not exist.
711 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
712 LocalFree((void *)err);
713 errno = b_errno_win32;
719 sb->st_mode = 0777; /* start with everything */
720 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
721 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
722 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
723 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
724 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
725 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
726 sb->st_mode |= S_IFDIR;
729 * Store reparse/mount point info in st_rdev. Note a
730 * Win32 reparse point (junction point) is like a link
731 * though it can have many properties (directory link,
732 * soft link, hard link, HSM, ...
733 * A mount point is a reparse point where another volume
734 * is mounted, so it is like a Unix mount point (change of
737 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
738 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
739 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
741 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
744 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
745 sb->st_size = *pnFileSizeHigh;
747 sb->st_size |= *pnFileSizeLow;
748 sb->st_blksize = 4096;
749 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
751 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
752 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
753 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
759 fstat(intptr_t fd, struct stat *sb)
761 BY_HANDLE_FILE_INFORMATION info;
763 if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
764 const char *err = errorString();
765 Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
766 LocalFree((void *)err);
767 errno = b_errno_win32;
771 sb->st_dev = info.dwVolumeSerialNumber;
772 sb->st_ino = info.nFileIndexHigh;
774 sb->st_ino |= info.nFileIndexLow;
775 sb->st_nlink = (short)info.nNumberOfLinks;
776 if (sb->st_nlink > 1) {
777 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
780 sb->st_mode = 0777; /* start with everything */
781 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
782 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
783 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
784 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
785 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
786 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
787 sb->st_mode |= S_IFREG;
789 /* Use st_rdev to store reparse attribute */
790 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
791 sb->st_rdev = WIN32_REPARSE_POINT;
793 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
794 (long long)sb->st_ino);
796 sb->st_size = info.nFileSizeHigh;
798 sb->st_size |= info.nFileSizeLow;
799 sb->st_blksize = 4096;
800 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
801 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
802 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
803 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
809 stat2(const char *file, struct stat *sb)
811 HANDLE h = INVALID_HANDLE_VALUE;
814 conv_unix_to_vss_win32_path(file, tmpbuf, 5000);
816 DWORD attr = (DWORD)-1;
818 if (p_GetFileAttributesW) {
819 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
820 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
822 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
824 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
825 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
827 free_pool_memory(pwszBuf);
828 } else if (p_GetFileAttributesA) {
829 attr = p_GetFileAttributesA(tmpbuf);
830 h = CreateFileA(tmpbuf, GENERIC_READ,
831 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
834 if (attr == (DWORD)-1) {
835 const char *err = errorString();
836 Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err);
837 LocalFree((void *)err);
838 if (h != INVALID_HANDLE_VALUE) {
841 errno = b_errno_win32;
845 if (h == INVALID_HANDLE_VALUE) {
846 const char *err = errorString();
847 Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
848 LocalFree((void *)err);
849 errno = b_errno_win32;
853 rval = fstat((intptr_t)h, sb);
856 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
857 file[1] == ':' && file[2] != 0) {
858 rval = statDir(file, sb);
864 stat(const char *file, struct stat *sb)
866 WIN32_FILE_ATTRIBUTE_DATA data;
869 memset(sb, 0, sizeof(*sb));
871 if (p_GetFileAttributesExW) {
872 /* dynamically allocate enough space for UCS2 filename */
873 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
874 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
876 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
877 free_pool_memory(pwszBuf);
880 return stat2(file, sb);
883 } else if (p_GetFileAttributesExA) {
884 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
885 return stat2(file, sb);
888 return stat2(file, sb);
891 sb->st_mode = 0777; /* start with everything */
892 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
893 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
895 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
896 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
898 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
899 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
901 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
902 sb->st_mode |= S_IFDIR;
904 sb->st_mode |= S_IFREG;
907 /* Use st_rdev to store reparse attribute */
908 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
911 sb->st_size = data.nFileSizeHigh;
913 sb->st_size |= data.nFileSizeLow;
914 sb->st_blksize = 4096;
915 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
916 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
917 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
918 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
921 * If we are not at the root, then to distinguish a reparse
922 * point from a mount point, we must call FindFirstFile() to
923 * get the WIN32_FIND_DATA, which has the bit that indicates
924 * that this directory is a mount point -- aren't Win32 APIs
925 * wonderful? (sarcasm). The code exists in the statDir
928 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
929 file[1] == ':' && file[2] != 0) {
932 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
933 (long long)sb->st_ino, file);
938 * We write our own ftruncate because the one in the
939 * Microsoft library mrcrt.dll does not truncate
940 * files greater than 2GB.
943 int win32_ftruncate(int fd, int64_t length)
945 /* Set point we want to truncate file */
946 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
948 if (pos != (__int64)length) {
949 errno = EACCES; /* truncation failed, get out */
954 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
955 errno = b_errno_win32;
962 int fcntl(int fd, int cmd, long arg)
985 lstat(const char *file, struct stat *sb)
987 return stat(file, sb);
1003 execvp(const char *, char *[]) {
1024 waitpid(int, int*, int)
1031 readlink(const char *, char *, int)
1040 strcasecmp(const char *s1, const char *s2)
1042 register int ch1, ch2;
1045 return 0; /* strings are equal if same object. */
1055 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1062 strncasecmp(const char *s1, const char *s2, int len)
1064 register int ch1 = 0, ch2 = 0;
1067 return 0; /* strings are equal if same object. */
1078 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1085 gettimeofday(struct timeval *tv, struct timezone *)
1090 GetSystemTime(&now);
1096 if (!SystemTimeToFileTime(&now, &tmp)) {
1097 errno = b_errno_win32;
1101 int64_t _100nsec = tmp.dwHighDateTime;
1103 _100nsec |= tmp.dwLowDateTime;
1104 _100nsec -= WIN32_FILETIME_ADJUST;
1106 tv->tv_sec = (long)(_100nsec / 10000000);
1107 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1113 * Write in Windows System log
1115 extern "C" void syslog(int type, const char *fmt, ...)
1121 msg = get_pool_memory(PM_EMSG);
1124 maxlen = sizeof_pool_memory(msg) - 1;
1125 va_start(arg_ptr, fmt);
1126 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1128 if (len < 0 || len >= (maxlen-5)) {
1129 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1134 LogErrorMsg((const char *)msg);
1155 // implement opendir/readdir/closedir on top of window's API
1159 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1160 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1161 const char *spec; // the directory we're traversing
1162 HANDLE dirh; // the search handle
1163 BOOL valid_a; // the info in data_a field is valid
1164 BOOL valid_w; // the info in data_w field is valid
1165 UINT32 offset; // pseudo offset for d_off
1169 opendir(const char *path)
1171 /* enough space for VSS !*/
1172 int max_len = strlen(path) + MAX_PATH;
1180 Dmsg1(100, "Opendir path=%s\n", path);
1181 rval = (_dir *)malloc(sizeof(_dir));
1185 memset (rval, 0, sizeof (_dir));
1187 tspec = (char *)malloc(max_len);
1192 conv_unix_to_vss_win32_path(path, tspec, max_len);
1193 Dmsg1(100, "win32 path=%s\n", tspec);
1195 // add backslash only if there is none yet (think of c:\)
1196 if (tspec[strlen(tspec)-1] != '\\')
1197 bstrncat(tspec, "\\*", max_len);
1199 bstrncat(tspec, "*", max_len);
1203 // convert to wchar_t
1204 if (p_FindFirstFileW) {
1205 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1206 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1208 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1210 free_pool_memory(pwcBuf);
1212 if (rval->dirh != INVALID_HANDLE_VALUE)
1214 } else if (p_FindFirstFileA) {
1215 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1217 if (rval->dirh != INVALID_HANDLE_VALUE)
1222 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1223 path, rval->spec, rval->dirh);
1226 if (rval->dirh == INVALID_HANDLE_VALUE)
1229 if (rval->valid_w) {
1230 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1233 if (rval->valid_a) {
1234 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1246 errno = b_errno_win32;
1253 _dir *dp = (_dir *)dirp;
1254 FindClose(dp->dirh);
1255 free((void *)dp->spec);
1261 typedef struct _WIN32_FIND_DATA {
1262 DWORD dwFileAttributes;
1263 FILETIME ftCreationTime;
1264 FILETIME ftLastAccessTime;
1265 FILETIME ftLastWriteTime;
1266 DWORD nFileSizeHigh;
1270 TCHAR cFileName[MAX_PATH];
1271 TCHAR cAlternateFileName[14];
1272 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1276 copyin(struct dirent &dp, const char *fname)
1280 char *cp = dp.d_name;
1290 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1292 _dir *dp = (_dir *)dirp;
1293 if (dp->valid_w || dp->valid_a) {
1294 entry->d_off = dp->offset;
1298 char szBuf[MAX_PATH_UTF8+1];
1299 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1300 dp->offset += copyin(*entry, szBuf);
1301 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1302 dp->offset += copyin(*entry, dp->data_a.cFileName);
1305 *result = entry; /* return entry address */
1306 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1307 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1309 // Dmsg0(99, "readdir_r !valid\n");
1310 errno = b_errno_win32;
1314 // get next file, try unicode first
1315 if (p_FindNextFileW)
1316 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1317 else if (p_FindNextFileA)
1318 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1320 dp->valid_a = FALSE;
1321 dp->valid_w = FALSE;
1328 * Dotted IP address to network address
1334 inet_aton(const char *a, struct in_addr *inp)
1337 uint32_t acc = 0, tmp = 0;
1340 if (!isdigit(*cp)) { /* first char must be digit */
1341 return 0; /* error */
1345 tmp = (tmp * 10) + (*cp -'0');
1346 } else if (*cp == '.' || *cp == 0) {
1348 return 0; /* error */
1350 acc = (acc << 8) + tmp;
1354 return 0; /* error */
1356 } while (*cp++ != 0);
1357 if (dotc != 4) { /* want 3 .'s plus EOS */
1358 return 0; /* error */
1360 inp->s_addr = htonl(acc); /* store addr in network format */
1365 nanosleep(const struct timespec *req, struct timespec *rem)
1368 rem->tv_sec = rem->tv_nsec = 0;
1369 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1374 init_signals(void terminate(int sig))
1380 init_stack_dump(void)
1387 pathconf(const char *path, int name)
1391 if (strncmp(path, "\\\\?\\", 4) == 0)
1403 WORD wVersionRequested = MAKEWORD( 1, 1);
1406 int err = WSAStartup(wVersionRequested, &wsaData);
1410 printf("Can not start Windows Sockets\n");
1418 static DWORD fill_attribute(DWORD attr, mode_t mode)
1420 Dmsg1(200, " before attr=%lld\n", (uint64_t) attr);
1421 /* Use Bacula mappings define in stat() above */
1422 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1423 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1425 attr |= FILE_ATTRIBUTE_READONLY;
1427 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1428 attr |= FILE_ATTRIBUTE_HIDDEN;
1430 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1432 if (mode & S_IRWXO) { // Other can read/write/execute ?
1433 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1435 attr |= FILE_ATTRIBUTE_SYSTEM;
1437 Dmsg1(200, " after attr=%lld\n", (uint64_t)attr);
1441 int win32_chmod(const char *path, mode_t mode)
1446 Dmsg2(100, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1447 if (p_GetFileAttributesW) {
1448 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1449 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1451 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1452 if (attr != INVALID_FILE_ATTRIBUTES) {
1453 /* Use Bacula mappings define in stat() above */
1454 attr = fill_attribute(attr, mode);
1455 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1457 free_pool_memory(pwszBuf);
1458 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1459 } else if (p_GetFileAttributesA) {
1460 attr = p_GetFileAttributesA(path);
1461 if (attr != INVALID_FILE_ATTRIBUTES) {
1462 attr = fill_attribute(attr, mode);
1463 ret = p_SetFileAttributesA(path, attr);
1465 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1467 Dmsg0(100, "Leave win32_chmod did nothing\n");
1471 const char *err = errorString();
1472 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1473 LocalFree((void *)err);
1474 errno = b_errno_win32;
1482 win32_chdir(const char *dir)
1484 if (p_SetCurrentDirectoryW) {
1485 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1486 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1488 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1490 free_pool_memory(pwszBuf);
1493 errno = b_errno_win32;
1496 } else if (p_SetCurrentDirectoryA) {
1497 if (0 == p_SetCurrentDirectoryA(dir)) {
1498 errno = b_errno_win32;
1509 win32_mkdir(const char *dir)
1511 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1513 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1514 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1516 int n = p_wmkdir((LPCWSTR)pwszBuf);
1517 free_pool_memory(pwszBuf);
1518 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1522 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1528 win32_getcwd(char *buf, int maxlen)
1532 if (p_GetCurrentDirectoryW) {
1533 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1534 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1536 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1538 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1539 free_pool_memory(pwszBuf);
1541 } else if (p_GetCurrentDirectoryA)
1542 n = p_GetCurrentDirectoryA(maxlen, buf);
1544 if (n == 0 || n > maxlen) return NULL;
1546 if (n+1 > maxlen) return NULL;
1555 win32_fputs(const char *string, FILE *stream)
1557 /* we use WriteConsoleA / WriteConsoleA
1558 so we can be sure that unicode support works on win32.
1559 with fallback if something fails
1562 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1563 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1564 p_MultiByteToWideChar && (stream == stdout)) {
1566 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1568 DWORD dwCharsWritten;
1571 dwChars = UTF8_2_wchar(&pwszBuf, string);
1573 /* try WriteConsoleW */
1574 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1575 free_pool_memory(pwszBuf);
1576 return dwCharsWritten;
1579 /* convert to local codepage and try WriteConsoleA */
1580 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1581 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1583 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1584 free_pool_memory(pwszBuf);
1586 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1587 free_pool_memory(pszBuf);
1588 return dwCharsWritten;
1590 free_pool_memory(pszBuf);
1593 return fputs(string, stream);
1597 win32_cgets (char* buffer, int len)
1599 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1600 from the win32 console and fallback if seomething fails */
1602 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1603 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1605 wchar_t wszBuf[1024];
1608 /* nt and unicode conversion */
1609 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1611 /* null terminate at end */
1612 if (wszBuf[dwRead-1] == L'\n') {
1613 wszBuf[dwRead-1] = L'\0';
1617 if (wszBuf[dwRead-1] == L'\r') {
1618 wszBuf[dwRead-1] = L'\0';
1622 wchar_2_UTF8(buffer, wszBuf, len);
1626 /* win 9x and unicode conversion */
1627 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1629 /* null terminate at end */
1630 if (szBuf[dwRead-1] == L'\n') {
1631 szBuf[dwRead-1] = L'\0';
1635 if (szBuf[dwRead-1] == L'\r') {
1636 szBuf[dwRead-1] = L'\0';
1640 /* convert from ansii to wchar_t */
1641 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1642 /* convert from wchar_t to UTF-8 */
1643 if (wchar_2_UTF8(buffer, wszBuf, len))
1649 if (fgets(buffer, len, stdin))
1656 win32_unlink(const char *filename)
1660 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1661 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1663 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1666 * special case if file is readonly,
1667 * we retry but unset attribute before
1669 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1670 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1671 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1672 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1673 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1674 /* reset to original if it didn't help */
1676 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1680 free_pool_memory(pwszBuf);
1682 nRetCode = _unlink(filename);
1684 /* special case if file is readonly,
1685 we retry but unset attribute before */
1686 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1687 DWORD dwAttr = p_GetFileAttributesA(filename);
1688 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1689 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1690 nRetCode = _unlink(filename);
1691 /* reset to original if it didn't help */
1693 p_SetFileAttributesA(filename, dwAttr);
1702 #include "mswinver.h"
1704 char WIN_VERSION_LONG[64];
1705 char WIN_VERSION[32];
1706 char WIN_RAWVERSION[32];
1713 static winver INIT; // cause constructor to be called before main()
1716 winver::winver(void)
1718 const char *version = "";
1719 const char *platform = "";
1720 OSVERSIONINFO osvinfo;
1721 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1723 // Get the current OS version
1724 if (!GetVersionEx(&osvinfo)) {
1725 version = "Unknown";
1726 platform = "Unknown";
1728 const int ver = _mkversion(osvinfo.dwPlatformId,
1729 osvinfo.dwMajorVersion,
1730 osvinfo.dwMinorVersion);
1731 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1734 case MS_WINDOWS_95: (version = "Windows 95"); break;
1735 case MS_WINDOWS_98: (version = "Windows 98"); break;
1736 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1737 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1738 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1739 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1740 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1741 default: version = WIN_RAWVERSION; break;
1744 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1745 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1746 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1749 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1753 BPIPE *b = open_bpipe("ls -l", 10, "r");
1755 while (!feof(b->rfd)) {
1756 fgets(buf, sizeof(buf), b->rfd);
1762 BOOL CreateChildProcess(VOID);
1763 VOID WriteToPipe(VOID);
1764 VOID ReadFromPipe(VOID);
1765 VOID ErrorExit(LPCSTR);
1766 VOID ErrMsg(LPTSTR, BOOL);
1769 * Check for a quoted path, if an absolute path name is given and it contains
1770 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1771 * CreateProcess() says the best way to ensure proper results with executables
1772 * with spaces in path or filename is to quote the string.
1775 getArgv0(const char *cmdline)
1780 for (cp = cmdline; *cp; cp++)
1785 if (!inquote && isspace(*cp))
1790 int len = cp - cmdline;
1791 char *rval = (char *)malloc(len+1);
1804 * Extracts the executable or script name from the first string in
1807 * If the name contains blanks then it must be quoted with double quotes,
1808 * otherwise quotes are optional. If the name contains blanks then it
1809 * will be converted to a short name.
1811 * The optional quotes will be removed. The result is copied to a malloc'ed
1812 * buffer and returned through the pexe argument. The pargs parameter is set
1813 * to the address of the character in cmdline located after the name.
1815 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1818 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1820 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1821 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1823 const char *pBasename = NULL; /* Character after last path separator */
1824 const char *pExtension = NULL; /* Period at start of extension */
1826 const char *current = cmdline;
1828 bool bQuoted = false;
1830 /* Skip initial whitespace */
1832 while (*current == ' ' || *current == '\t')
1837 /* Calculate start of name and determine if quoted */
1839 if (*current == '"') {
1840 pExeStart = ++current;
1843 pExeStart = current;
1851 * Scan command line looking for path separators (/ and \\) and the
1852 * terminator, either a quote or a blank. The location of the
1853 * extension is also noted.
1856 for ( ; *current != '\0'; current++)
1858 if (*current == '.') {
1859 pExtension = current;
1860 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1861 pBasename = ¤t[1];
1865 /* Check for terminator, either quote or blank */
1867 if (*current != '"') {
1871 if (*current != ' ') {
1877 * Hit terminator, remember end of name (address of terminator) and
1878 * start of arguments
1882 if (bQuoted && *current == '"') {
1883 *pargs = ¤t[1];
1891 if (pBasename == NULL) {
1892 pBasename = pExeStart;
1895 if (pExeEnd == NULL) {
1904 bool bHasPathSeparators = pExeStart != pBasename;
1906 /* We have pointers to all the useful parts of the name */
1908 /* Default extensions in the order cmd.exe uses to search */
1910 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1911 DWORD dwBasePathLength = pExeEnd - pExeStart;
1913 DWORD dwAltNameLength = 0;
1914 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1915 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1917 pPathname[MAX_PATHLENGTH] = '\0';
1918 pAltPathname[MAX_PATHLENGTH] = '\0';
1920 memcpy(pPathname, pExeStart, dwBasePathLength);
1921 pPathname[dwBasePathLength] = '\0';
1923 if (pExtension == NULL) {
1924 /* Try appending extensions */
1925 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1927 if (!bHasPathSeparators) {
1928 /* There are no path separators, search in the standard locations */
1929 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1930 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1931 memcpy(pPathname, pAltPathname, dwAltNameLength);
1932 pPathname[dwAltNameLength] = '\0';
1936 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1937 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1940 pPathname[dwBasePathLength] = '\0';
1943 } else if (!bHasPathSeparators) {
1944 /* There are no path separators, search in the standard locations */
1945 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1946 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1947 memcpy(pPathname, pAltPathname, dwAltNameLength);
1948 pPathname[dwAltNameLength] = '\0';
1952 if (strchr(pPathname, ' ') != NULL) {
1953 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1955 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1956 *pexe = (char *)malloc(dwAltNameLength + 1);
1957 if (*pexe == NULL) {
1960 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1964 if (*pexe == NULL) {
1965 DWORD dwPathnameLength = strlen(pPathname);
1966 *pexe = (char *)malloc(dwPathnameLength + 1);
1967 if (*pexe == NULL) {
1970 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1977 * Create the process with WCHAR API
1980 CreateChildProcessW(const char *comspec, const char *cmdLine,
1981 PROCESS_INFORMATION *hProcInfo,
1982 HANDLE in, HANDLE out, HANDLE err)
1984 STARTUPINFOW siStartInfo;
1985 BOOL bFuncRetn = FALSE;
1987 // Set up members of the STARTUPINFO structure.
1988 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1989 siStartInfo.cb = sizeof(siStartInfo);
1990 // setup new process to use supplied handles for stdin,stdout,stderr
1992 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1993 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1995 siStartInfo.hStdInput = in;
1996 siStartInfo.hStdOutput = out;
1997 siStartInfo.hStdError = err;
1999 // Convert argument to WCHAR
2000 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
2001 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2003 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2004 UTF8_2_wchar(&comspec_wchar, comspec);
2006 // Create the child process.
2007 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2009 // try to execute program
2010 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2011 (WCHAR*)cmdLine_wchar,// command line
2012 NULL, // process security attributes
2013 NULL, // primary thread security attributes
2014 TRUE, // handles are inherited
2015 0, // creation flags
2016 NULL, // use parent's environment
2017 NULL, // use parent's current directory
2018 &siStartInfo, // STARTUPINFO pointer
2019 hProcInfo); // receives PROCESS_INFORMATION
2020 free_pool_memory(cmdLine_wchar);
2021 free_pool_memory(comspec_wchar);
2028 * Create the process with ANSI API
2031 CreateChildProcessA(const char *comspec, char *cmdLine,
2032 PROCESS_INFORMATION *hProcInfo,
2033 HANDLE in, HANDLE out, HANDLE err)
2035 STARTUPINFOA siStartInfo;
2036 BOOL bFuncRetn = FALSE;
2038 // Set up members of the STARTUPINFO structure.
2039 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2040 siStartInfo.cb = sizeof(siStartInfo);
2041 // setup new process to use supplied handles for stdin,stdout,stderr
2042 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2043 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2045 siStartInfo.hStdInput = in;
2046 siStartInfo.hStdOutput = out;
2047 siStartInfo.hStdError = err;
2049 // Create the child process.
2050 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2052 // try to execute program
2053 bFuncRetn = p_CreateProcessA(comspec,
2054 cmdLine, // command line
2055 NULL, // process security attributes
2056 NULL, // primary thread security attributes
2057 TRUE, // handles are inherited
2058 0, // creation flags
2059 NULL, // use parent's environment
2060 NULL, // use parent's current directory
2061 &siStartInfo,// STARTUPINFO pointer
2062 hProcInfo);// receives PROCESS_INFORMATION
2067 * OK, so it would seem CreateProcess only handles true executables:
2068 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2071 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2073 static const char *comspec = NULL;
2074 PROCESS_INFORMATION piProcInfo;
2075 BOOL bFuncRetn = FALSE;
2077 if (!p_CreateProcessA || !p_CreateProcessW)
2078 return INVALID_HANDLE_VALUE;
2080 if (comspec == NULL)
2081 comspec = getenv("COMSPEC");
2082 if (comspec == NULL) // should never happen
2083 return INVALID_HANDLE_VALUE;
2085 // Set up members of the PROCESS_INFORMATION structure.
2086 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2088 // if supplied handles are not used the send a copy of our STD_HANDLE
2090 if (in == INVALID_HANDLE_VALUE)
2091 in = GetStdHandle(STD_INPUT_HANDLE);
2093 if (out == INVALID_HANDLE_VALUE)
2094 out = GetStdHandle(STD_OUTPUT_HANDLE);
2096 if (err == INVALID_HANDLE_VALUE)
2097 err = GetStdHandle(STD_ERROR_HANDLE);
2100 const char *argStart;
2102 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2103 return INVALID_HANDLE_VALUE;
2106 POOL_MEM cmdLine(PM_FNAME);
2107 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2111 // New function disabled
2112 if (p_CreateProcessW && p_MultiByteToWideChar) {
2113 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2116 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2120 if (bFuncRetn == 0) {
2121 ErrorExit("CreateProcess failed\n");
2122 const char *err = errorString();
2123 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2124 LocalFree((void *)err);
2125 return INVALID_HANDLE_VALUE;
2127 // we don't need a handle on the process primary thread so we close
2129 CloseHandle(piProcInfo.hThread);
2130 return piProcInfo.hProcess;
2134 ErrorExit (LPCSTR lpszMessage)
2136 Dmsg1(0, "%s", lpszMessage);
2141 typedef struct s_bpipe {
2143 time_t worker_stime;
2152 CloseIfValid(HANDLE handle)
2154 if (handle != INVALID_HANDLE_VALUE)
2155 CloseHandle(handle);
2159 open_bpipe(char *prog, int wait, const char *mode)
2161 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2162 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2165 SECURITY_ATTRIBUTES saAttr;
2169 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2170 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2171 hInputFile = INVALID_HANDLE_VALUE;
2173 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2174 memset((void *)bpipe, 0, sizeof(BPIPE));
2176 int mode_read = (mode[0] == 'r');
2177 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2180 // Set the bInheritHandle flag so pipe handles are inherited.
2182 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2183 saAttr.bInheritHandle = TRUE;
2184 saAttr.lpSecurityDescriptor = NULL;
2188 // Create a pipe for the child process's STDOUT.
2189 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2190 ErrorExit("Stdout pipe creation failed\n");
2193 // Create noninheritable read handle and close the inheritable read
2196 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2197 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2199 DUPLICATE_SAME_ACCESS);
2201 ErrorExit("DuplicateHandle failed");
2205 CloseHandle(hChildStdoutRd);
2206 hChildStdoutRd = INVALID_HANDLE_VALUE;
2211 // Create a pipe for the child process's STDIN.
2213 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2214 ErrorExit("Stdin pipe creation failed\n");
2218 // Duplicate the write handle to the pipe so it is not inherited.
2219 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2220 GetCurrentProcess(), &hChildStdinWrDup,
2222 FALSE, // not inherited
2223 DUPLICATE_SAME_ACCESS);
2225 ErrorExit("DuplicateHandle failed");
2229 CloseHandle(hChildStdinWr);
2230 hChildStdinWr = INVALID_HANDLE_VALUE;
2232 // spawn program with redirected handles as appropriate
2233 bpipe->worker_pid = (pid_t)
2234 CreateChildProcess(prog, // commandline
2235 hChildStdinRd, // stdin HANDLE
2236 hChildStdoutWr, // stdout HANDLE
2237 hChildStdoutWr); // stderr HANDLE
2239 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2243 bpipe->worker_stime = time(NULL);
2246 CloseHandle(hChildStdoutWr); // close our write side so when
2247 // process terminates we can
2249 // ugly but convert WIN32 HANDLE to FILE*
2250 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2252 bpipe->rfd = _fdopen(rfd, "rb");
2256 CloseHandle(hChildStdinRd); // close our read side so as not
2257 // to interfre with child's copy
2258 // ugly but convert WIN32 HANDLE to FILE*
2259 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2261 bpipe->wfd = _fdopen(wfd, "wb");
2266 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2273 CloseIfValid(hChildStdoutRd);
2274 CloseIfValid(hChildStdoutRdDup);
2275 CloseIfValid(hChildStdinWr);
2276 CloseIfValid(hChildStdinWrDup);
2278 free((void *) bpipe);
2279 errno = b_errno_win32; /* do GetLastError() for error code */
2285 kill(int pid, int signal)
2288 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2290 errno = b_errno_win32;
2292 CloseHandle((HANDLE)pid);
2298 close_bpipe(BPIPE *bpipe)
2301 int32_t remaining_wait = bpipe->wait;
2313 if (remaining_wait == 0) { /* wait indefinitely */
2314 remaining_wait = INT32_MAX;
2318 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2319 const char *err = errorString();
2320 rval = b_errno_win32;
2321 Dmsg1(0, "GetExitCode error %s\n", err);
2322 LocalFree((void *)err);
2325 if (exitCode == STILL_ACTIVE) {
2326 if (remaining_wait <= 0) {
2327 rval = ETIME; /* timed out */
2330 bmicrosleep(1, 0); /* wait one second */
2332 } else if (exitCode != 0) {
2333 /* Truncate exit code as it doesn't seem to be correct */
2334 rval = (exitCode & 0xFF) | b_errno_exit;
2337 break; /* Shouldn't get here */
2341 if (bpipe->timer_id) {
2342 stop_child_timer(bpipe->timer_id);
2344 if (bpipe->rfd) fclose(bpipe->rfd);
2345 if (bpipe->wfd) fclose(bpipe->wfd);
2346 free((void *)bpipe);
2351 close_wpipe(BPIPE *bpipe)
2357 if (fclose(bpipe->wfd) != 0) {
2367 utime(const char *fname, struct utimbuf *times)
2372 conv_unix_to_vss_win32_path(fname, tmpbuf, 5000);
2374 cvt_utime_to_ftime(times->actime, acc);
2375 cvt_utime_to_ftime(times->modtime, mod);
2377 HANDLE h = INVALID_HANDLE_VALUE;
2379 if (p_CreateFileW) {
2380 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2381 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2383 h = p_CreateFileW((LPCWSTR)pwszBuf,
2384 FILE_WRITE_ATTRIBUTES,
2385 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2388 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2391 free_pool_memory(pwszBuf);
2392 } else if (p_CreateFileA) {
2393 h = p_CreateFileA(tmpbuf,
2394 FILE_WRITE_ATTRIBUTES,
2395 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2398 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2402 if (h == INVALID_HANDLE_VALUE) {
2403 const char *err = errorString();
2404 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2405 LocalFree((void *)err);
2406 errno = b_errno_win32;
2410 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2413 errno = b_errno_win32;
2421 file_open(const char *file, int flags, int mode)
2424 DWORD shareMode = 0;
2427 HANDLE foo = INVALID_HANDLE_VALUE;
2428 const char *remap = file;
2430 if (flags & O_WRONLY) access = GENERIC_WRITE;
2431 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2432 else access = GENERIC_READ;
2434 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2435 create = CREATE_NEW;
2436 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2437 create = CREATE_ALWAYS;
2438 else if (flags & O_CREAT)
2439 create = OPEN_ALWAYS;
2440 else if (flags & O_TRUNC)
2441 create = TRUNCATE_EXISTING;
2443 create = OPEN_EXISTING;
2447 if (flags & O_APPEND) {
2448 printf("open...APPEND not implemented yet.");
2452 if (p_CreateFileW) {
2453 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2454 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2456 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2457 free_pool_memory(pwszBuf);
2458 } else if (p_CreateFileA)
2459 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2461 if (INVALID_HANDLE_VALUE == foo) {
2462 errno = b_errno_win32;
2473 if (!CloseHandle((HANDLE)fd)) {
2474 errno = b_errno_win32;
2482 file_write(int fd, const void *data, ssize_t len)
2486 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2487 if (status) return bwrite;
2488 errno = b_errno_win32;
2494 file_read(int fd, void *data, ssize_t len)
2499 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2500 if (status) return bread;
2501 errno = b_errno_win32;
2506 file_seek(int fd, boffset_t offset, int whence)
2510 LONG offset_low = (LONG)offset;
2511 LONG offset_high = (LONG)(offset >> 32);
2515 method = FILE_BEGIN;
2518 method = FILE_CURRENT;
2529 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2530 errno = b_errno_win32;
2533 /* ***FIXME*** I doubt this works right */
2547 * Emulation of mmap and unmmap for tokyo dbm
2549 void *mmap(void *start, size_t length, int prot, int flags,
2550 int fd, off_t offset)
2552 DWORD fm_access = 0;
2553 DWORD mv_access = 0;
2564 if (flags & PROT_WRITE) {
2565 fm_access |= PAGE_READWRITE;
2566 } else if (flags & PROT_READ) {
2567 fm_access |= PAGE_READONLY;
2570 if (flags & PROT_READ) {
2571 mv_access |= FILE_MAP_READ;
2573 if (flags & PROT_WRITE) {
2574 mv_access |= FILE_MAP_WRITE;
2577 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2578 NULL /* security */,
2580 0 /* MaximumSizeHigh */,
2581 0 /* MaximumSizeLow */,
2582 NULL /* name of the file mapping object */);
2584 if (!h || h == INVALID_HANDLE_VALUE) {
2588 mv = MapViewOfFile(h, mv_access,
2594 if (!mv || mv == INVALID_HANDLE_VALUE) {
2601 int munmap(void *start, size_t length)
2606 UnmapViewOfFile(start);
2612 /* syslog function, added by Nicolas Boichat */
2613 void openlog(const char *ident, int option, int facility) {}
2616 /* Log an error message */
2617 void LogErrorMsg(const char *message)
2619 HANDLE eventHandler;
2620 const char *strings[2];
2622 /* Use the OS event logging to log the error */
2623 eventHandler = RegisterEventSource(NULL, "Bacula");
2625 strings[0] = _("\n\nBacula ERROR: ");
2626 strings[1] = message;
2629 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2633 2, /* Number of strings */
2634 0, /* raw data size */
2635 (const char **)strings, /* error strings */
2636 NULL); /* raw data */
2637 DeregisterEventSource(eventHandler);