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 two of the GNU 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 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
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 */
53 static POOLMEM *g_pWin32ConvUTF8Cache = NULL;
54 static POOLMEM *g_pWin32ConvUCS2Cache = NULL;
55 static DWORD g_dwWin32ConvUTF8strlen = 0;
56 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
58 static t_pVSSPathConvert g_pVSSPathConvert;
59 static t_pVSSPathConvertW g_pVSSPathConvertW;
61 /* Forward referenced functions */
62 static const char *errorString(void);
65 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
67 g_pVSSPathConvert = pPathConvert;
68 g_pVSSPathConvertW = pPathConvertW;
71 static void Win32ConvInitCache()
73 if (g_pWin32ConvUTF8Cache) {
76 g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
77 g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
80 void Win32ConvCleanupCache()
83 if (g_pWin32ConvUTF8Cache) {
84 free_pool_memory(g_pWin32ConvUTF8Cache);
85 g_pWin32ConvUTF8Cache = NULL;
88 if (g_pWin32ConvUCS2Cache) {
89 free_pool_memory(g_pWin32ConvUCS2Cache);
90 g_pWin32ConvUCS2Cache = NULL;
93 g_dwWin32ConvUTF8strlen = 0;
98 /* to allow the usage of the original version in this file here */
102 //#define USE_WIN32_COMPAT_IO 1
103 #define USE_WIN32_32KPATHCONVERSION 1
105 extern DWORD g_platform_id;
106 extern DWORD g_MinorVersion;
108 /* From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 */
110 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
112 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
115 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
118 * Convert from UTF-8 to VSS Windows path/file
119 * Used by compatibility layer for Unix system calls
121 static void conv_unix_to_vss_win32_path(const char *name, char *win32_name, DWORD dwSize)
123 const char *fname = name;
124 char *tname = win32_name;
126 Dmsg0(100, "Enter convert_unix_to_win32_path\n");
128 if (IsPathSeparator(name[0]) &&
129 IsPathSeparator(name[1]) &&
131 IsPathSeparator(name[3])) {
133 *win32_name++ = '\\';
134 *win32_name++ = '\\';
136 *win32_name++ = '\\';
139 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
140 g_pVSSPathConvert == NULL) {
141 /* allow path to be 32767 bytes */
142 *win32_name++ = '\\';
143 *win32_name++ = '\\';
145 *win32_name++ = '\\';
149 /** Check for Unix separator and convert to Win32 */
150 if (name[0] == '/' && name[1] == '/') { /* double slash? */
151 name++; /* yes, skip first one */
154 *win32_name++ = '\\'; /* convert char */
155 /* If Win32 separator that is "quoted", remove quote */
156 } else if (*name == '\\' && name[1] == '\\') {
157 *win32_name++ = '\\';
158 name++; /* skip first \ */
160 *win32_name++ = *name; /* copy character */
164 /** Strip any trailing slash, if we stored something */
165 /** but leave "c:\" with backslash (root directory case */
166 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
172 /** here we convert to VSS specific file name which
173 can get longer because VSS will make something like
174 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
175 from c:\bacula\uninstall.exe
177 Dmsg1(100, "path=%s\n", tname);
178 if (g_pVSSPathConvert != NULL) {
179 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
180 pszBuf = check_pool_memory_size(pszBuf, dwSize);
181 bstrncpy(pszBuf, tname, strlen(tname)+1);
182 g_pVSSPathConvert(pszBuf, tname, dwSize);
183 free_pool_memory(pszBuf);
186 Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
189 /** Conversion of a Unix filename to a Win32 filename */
190 void unix_name_to_win32(POOLMEM **win32_name, char *name)
192 /* One extra byte should suffice, but we double it */
193 /* add MAX_PATH bytes for VSS shadow copy name */
194 DWORD dwSize = 2*strlen(name)+MAX_PATH;
195 *win32_name = check_pool_memory_size(*win32_name, dwSize);
196 conv_unix_to_vss_win32_path(name, *win32_name, dwSize);
201 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
202 * will complete the input path to an absolue path of the form \\?\c:\path\file
204 * With this trick, it is possible to have 32K characters long paths.
206 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
207 * to a raw windows partition.
209 * created 02/27/2006 Thorsten Engel
212 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
215 Dmsg0(100, "Enter wchar_win32_path\n");
217 *pBIsRawPath = FALSE; /* Initialize, set later */
220 if (!p_GetCurrentDirectoryW) {
221 Dmsg0(100, "Leave wchar_win32_path no change \n");
225 wchar_t *name = (wchar_t *)pszUCSPath;
227 /* if it has already the desired form, exit without changes */
228 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
229 Dmsg0(100, "Leave wchar_win32_path no change \n");
233 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
234 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
235 DWORD dwCurDirPathSize = 0;
237 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
238 DWORD dwBufCharsNeeded = (wcslen(name)+7);
239 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
241 /* add \\?\ to support 32K long filepaths
242 it is important to make absolute paths, so we add drive and
243 current path if necessary */
245 BOOL bAddDrive = TRUE;
246 BOOL bAddCurrentPath = TRUE;
247 BOOL bAddPrefix = TRUE;
249 /* does path begin with drive? if yes, it is absolute */
250 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
252 bAddCurrentPath = FALSE;
255 /* is path absolute? */
256 if (IsPathSeparator(name[0]))
257 bAddCurrentPath = FALSE;
259 /* is path relative to itself?, if yes, skip ./ */
260 if (name[0] == '.' && IsPathSeparator(name[1])) {
264 /* is path of form '//./'? */
265 if (IsPathSeparator(name[0]) &&
266 IsPathSeparator(name[1]) &&
268 IsPathSeparator(name[3])) {
270 bAddCurrentPath = FALSE;
277 int nParseOffset = 0;
279 /* add 4 bytes header */
282 wcscpy(pwszBuf, L"\\\\?\\");
285 /* get current path if needed */
286 if (bAddDrive || bAddCurrentPath) {
287 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
288 if (dwCurDirPathSize > 0) {
289 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
290 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
291 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
293 /* we have no info for doing so */
295 bAddCurrentPath = FALSE;
300 /* add drive if needed */
301 if (bAddDrive && !bAddCurrentPath) {
304 if (IsPathSeparator(pwszCurDirBuf[0]) &&
305 IsPathSeparator(pwszCurDirBuf[1]) &&
306 pwszCurDirBuf[2] == '?' &&
307 IsPathSeparator(pwszCurDirBuf[3])) {
308 /* copy drive character */
309 szDrive[0] = pwszCurDirBuf[4];
311 /* copy drive character */
312 szDrive[0] = pwszCurDirBuf[0];
318 wcscat(pwszBuf, szDrive);
322 /* add path if needed */
323 if (bAddCurrentPath) {
324 /* the 1 add. character is for the eventually added backslash */
325 dwBufCharsNeeded += dwCurDirPathSize+1;
326 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
327 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
329 if (IsPathSeparator(pwszCurDirBuf[0]) &&
330 IsPathSeparator(pwszCurDirBuf[1]) &&
331 pwszCurDirBuf[2] == '?' &&
332 IsPathSeparator(pwszCurDirBuf[3])) {
333 /* copy complete string */
334 wcscpy(pwszBuf, pwszCurDirBuf);
337 wcscat(pwszBuf, pwszCurDirBuf);
340 nParseOffset = wcslen((LPCWSTR) pwszBuf);
342 /* check if path ends with backslash, if not, add one */
343 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
344 wcscat(pwszBuf, L"\\");
349 wchar_t *win32_name = &pwszBuf[nParseOffset];
350 wchar_t *name_start = name;
353 /* Check for Unix separator and convert to Win32, eliminating
354 * duplicate separators.
356 if (IsPathSeparator(*name)) {
357 *win32_name++ = '\\'; /* convert char */
359 /* Eliminate consecutive slashes, but not at the start so that
362 if (name_start != name && IsPathSeparator(name[1])) {
366 *win32_name++ = *name; /* copy character */
371 /* null terminate string */
374 /* here we convert to VSS specific file name which
375 * can get longer because VSS will make something like
376 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
377 * from c:\bacula\uninstall.exe
379 if (g_pVSSPathConvertW != NULL) {
380 /* is output buffer large enough? */
381 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
382 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
383 /* create temp. buffer */
384 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
385 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
386 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
391 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
392 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
393 free_pool_memory((POOLMEM *)pszBuf);
396 free_pool_memory(pszUCSPath);
397 free_pool_memory((POOLMEM *)pwszCurDirBuf);
399 Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
400 return (POOLMEM *)pwszBuf;
404 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
407 * The return value is the number of bytes written to the buffer.
408 * The number includes the byte for the null terminator.
411 if (p_WideCharToMultiByte) {
412 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
421 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
423 /* the return value is the number of wide characters written to the buffer. */
424 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
426 if (p_MultiByteToWideChar) {
427 /* strlen of UTF8 +1 is enough */
428 DWORD cchSize = (strlen(pszUTF)+1);
429 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
431 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
441 wchar_win32_path(const char *name, wchar_t *win32_name)
443 const char *fname = name;
445 /* Check for Unix separator and convert to Win32 */
447 *win32_name++ = '\\'; /* convert char */
448 /* If Win32 separated that is "quoted", remove quote */
449 } else if (*name == '\\' && name[1] == '\\') {
450 *win32_name++ = '\\';
451 name++; /* skip first \ */
453 *win32_name++ = *name; /* copy character */
457 /* Strip any trailing slash, if we stored something */
458 if (*fname != 0 && win32_name[-1] == '\\') {
466 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
469 /* if we find the utf8 string in cache, we use the cached ucs2 version.
470 we compare the stringlength first (quick check) and then compare the content.
472 if (!g_pWin32ConvUTF8Cache) {
473 Win32ConvInitCache();
474 } else if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
475 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
476 /* Return cached value */
477 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
478 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
479 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
481 return nBufSize / sizeof (WCHAR);
485 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
486 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
488 #ifdef USE_WIN32_32KPATHCONVERSION
489 /* add \\?\ to support 32K long filepaths */
490 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
493 *pBIsRawPath = FALSE;
497 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
498 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
500 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
501 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
502 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
508 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
515 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
516 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
519 void *dlopen(const char *file, int mode)
523 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
527 void *dlsym(void *handle, const char *name)
530 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
534 int dlclose(void *handle)
536 if (handle && !FreeLibrary((HMODULE)handle)) {
537 errno = b_errno_win32;
538 return 1; /* failed */
545 static char buf[200];
546 const char *err = errorString();
547 bstrncpy(buf, (char *)err, sizeof(buf));
548 LocalFree((void *)err);
552 int fcntl(int fd, int cmd)
557 int chown(const char *k, uid_t, gid_t)
562 int lchown(const char *k, uid_t, gid_t)
574 srandom(unsigned int seed)
578 // /////////////////////////////////////////////////////////////////
579 // convert from Windows concept of time to Unix concept of time
580 // /////////////////////////////////////////////////////////////////
582 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
584 uint64_t mstime = time;
585 mstime *= WIN32_FILETIME_SCALE;
586 mstime += WIN32_FILETIME_ADJUST;
588 #if defined(_MSC_VER)
589 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
591 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
593 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
597 cvt_ftime_to_utime(const FILETIME &time)
599 uint64_t mstime = time.dwHighDateTime;
601 mstime |= time.dwLowDateTime;
603 mstime -= WIN32_FILETIME_ADJUST;
604 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
606 return (time_t) (mstime & 0xffffffff);
609 static const char *errorString(void)
613 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
614 FORMAT_MESSAGE_FROM_SYSTEM |
615 FORMAT_MESSAGE_IGNORE_INSERTS,
618 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
623 /* Strip any \r or \n */
624 char *rval = (char *) lpMsgBuf;
625 char *cp = strchr(rval, '\r');
629 cp = strchr(rval, '\n');
638 statDir(const char *file, struct stat *sb)
640 WIN32_FIND_DATAW info_w; // window's file info
641 WIN32_FIND_DATAA info_a; // window's file info
643 // cache some common vars to make code more transparent
644 DWORD *pdwFileAttributes;
645 DWORD *pnFileSizeHigh;
646 DWORD *pnFileSizeLow;
648 FILETIME *pftLastAccessTime;
649 FILETIME *pftLastWriteTime;
650 FILETIME *pftCreationTime;
653 * Oh, cool, another exception: Microsoft doesn't let us do
654 * FindFile operations on a Drive, so simply fake root attibutes.
656 if (file[1] == ':' && file[2] == 0) {
657 time_t now = time(NULL);
658 Dmsg1(99, "faking ROOT attrs(%s).\n", file);
659 sb->st_mode = S_IFDIR;
660 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
668 HANDLE h = INVALID_HANDLE_VALUE;
671 if (p_FindFirstFileW) {
672 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
673 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
675 Dmsg1(100, "FindFirstFileW=%s\n", file);
676 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
677 free_pool_memory(pwszBuf);
679 pdwFileAttributes = &info_w.dwFileAttributes;
680 pdwReserved0 = &info_w.dwReserved0;
681 pnFileSizeHigh = &info_w.nFileSizeHigh;
682 pnFileSizeLow = &info_w.nFileSizeLow;
683 pftLastAccessTime = &info_w.ftLastAccessTime;
684 pftLastWriteTime = &info_w.ftLastWriteTime;
685 pftCreationTime = &info_w.ftCreationTime;
688 } else if (p_FindFirstFileA) {
689 Dmsg1(100, "FindFirstFileA=%s\n", file);
690 h = p_FindFirstFileA(file, &info_a);
692 pdwFileAttributes = &info_a.dwFileAttributes;
693 pdwReserved0 = &info_a.dwReserved0;
694 pnFileSizeHigh = &info_a.nFileSizeHigh;
695 pnFileSizeLow = &info_a.nFileSizeLow;
696 pftLastAccessTime = &info_a.ftLastAccessTime;
697 pftLastWriteTime = &info_a.ftLastWriteTime;
698 pftCreationTime = &info_a.ftCreationTime;
700 Dmsg0(100, "No findFirstFile A or W found\n");
703 if (h == INVALID_HANDLE_VALUE) {
704 const char *err = errorString();
706 * Note, in creating leading paths, it is normal that
707 * the file does not exist.
709 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
710 LocalFree((void *)err);
711 errno = b_errno_win32;
717 sb->st_mode = 0777; /* start with everything */
718 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
719 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
720 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
721 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
722 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
723 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
724 sb->st_mode |= S_IFDIR;
727 * Store reparse/mount point info in st_rdev. Note a
728 * Win32 reparse point (junction point) is like a link
729 * though it can have many properties (directory link,
730 * soft link, hard link, HSM, ...
731 * A mount point is a reparse point where another volume
732 * is mounted, so it is like a Unix mount point (change of
735 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
736 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
737 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
739 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
742 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
743 sb->st_size = *pnFileSizeHigh;
745 sb->st_size |= *pnFileSizeLow;
746 sb->st_blksize = 4096;
747 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
749 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
750 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
751 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
757 fstat(intptr_t fd, struct stat *sb)
759 BY_HANDLE_FILE_INFORMATION info;
761 if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
762 const char *err = errorString();
763 Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
764 LocalFree((void *)err);
765 errno = b_errno_win32;
769 sb->st_dev = info.dwVolumeSerialNumber;
770 sb->st_ino = info.nFileIndexHigh;
772 sb->st_ino |= info.nFileIndexLow;
773 sb->st_nlink = (short)info.nNumberOfLinks;
774 if (sb->st_nlink > 1) {
775 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
778 sb->st_mode = 0777; /* start with everything */
779 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
780 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
781 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
782 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
783 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
784 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
785 sb->st_mode |= S_IFREG;
787 /* Use st_rdev to store reparse attribute */
788 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
789 sb->st_rdev = WIN32_REPARSE_POINT;
791 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
792 (long long)sb->st_ino);
794 sb->st_size = info.nFileSizeHigh;
796 sb->st_size |= info.nFileSizeLow;
797 sb->st_blksize = 4096;
798 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
799 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
800 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
801 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
807 stat2(const char *file, struct stat *sb)
809 HANDLE h = INVALID_HANDLE_VALUE;
812 conv_unix_to_vss_win32_path(file, tmpbuf, 5000);
814 DWORD attr = (DWORD)-1;
816 if (p_GetFileAttributesW) {
817 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
818 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
820 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
822 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
823 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
825 free_pool_memory(pwszBuf);
826 } else if (p_GetFileAttributesA) {
827 attr = p_GetFileAttributesA(tmpbuf);
828 h = CreateFileA(tmpbuf, GENERIC_READ,
829 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
832 if (attr == (DWORD)-1) {
833 const char *err = errorString();
834 Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err);
835 LocalFree((void *)err);
836 if (h != INVALID_HANDLE_VALUE) {
839 errno = b_errno_win32;
843 if (h == INVALID_HANDLE_VALUE) {
844 const char *err = errorString();
845 Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
846 LocalFree((void *)err);
847 errno = b_errno_win32;
851 rval = fstat((intptr_t)h, sb);
854 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
855 file[1] == ':' && file[2] != 0) {
856 rval = statDir(file, sb);
862 stat(const char *file, struct stat *sb)
864 WIN32_FILE_ATTRIBUTE_DATA data;
867 memset(sb, 0, sizeof(*sb));
869 if (p_GetFileAttributesExW) {
870 /* dynamically allocate enough space for UCS2 filename */
871 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
872 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
874 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
875 free_pool_memory(pwszBuf);
878 return stat2(file, sb);
881 } else if (p_GetFileAttributesExA) {
882 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
883 return stat2(file, sb);
886 return stat2(file, sb);
889 sb->st_mode = 0777; /* start with everything */
890 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
891 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
893 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
894 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
896 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
897 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
899 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
900 sb->st_mode |= S_IFDIR;
902 sb->st_mode |= S_IFREG;
905 /* Use st_rdev to store reparse attribute */
906 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
909 sb->st_size = data.nFileSizeHigh;
911 sb->st_size |= data.nFileSizeLow;
912 sb->st_blksize = 4096;
913 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
914 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
915 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
916 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
919 * If we are not at the root, then to distinguish a reparse
920 * point from a mount point, we must call FindFirstFile() to
921 * get the WIN32_FIND_DATA, which has the bit that indicates
922 * that this directory is a mount point -- aren't Win32 APIs
923 * wonderful? (sarcasm). The code exists in the statDir
926 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
927 file[1] == ':' && file[2] != 0) {
930 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
931 (long long)sb->st_ino, file);
936 * We write our own ftruncate because the one in the
937 * Microsoft library mrcrt.dll does not truncate
938 * files greater than 2GB.
941 int win32_ftruncate(int fd, int64_t length)
943 /* Set point we want to truncate file */
944 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
946 if (pos != (__int64)length) {
947 errno = EACCES; /* truncation failed, get out */
952 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
953 errno = b_errno_win32;
960 int fcntl(int fd, int cmd, long arg)
983 lstat(const char *file, struct stat *sb)
985 return stat(file, sb);
1001 execvp(const char *, char *[]) {
1022 waitpid(int, int*, int)
1029 readlink(const char *, char *, int)
1038 strcasecmp(const char *s1, const char *s2)
1040 register int ch1, ch2;
1043 return 0; /* strings are equal if same object. */
1053 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1060 strncasecmp(const char *s1, const char *s2, int len)
1062 register int ch1 = 0, ch2 = 0;
1065 return 0; /* strings are equal if same object. */
1076 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1083 gettimeofday(struct timeval *tv, struct timezone *)
1088 GetSystemTime(&now);
1094 if (!SystemTimeToFileTime(&now, &tmp)) {
1095 errno = b_errno_win32;
1099 int64_t _100nsec = tmp.dwHighDateTime;
1101 _100nsec |= tmp.dwLowDateTime;
1102 _100nsec -= WIN32_FILETIME_ADJUST;
1104 tv->tv_sec = (long)(_100nsec / 10000000);
1105 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1111 * Write in Windows System log
1113 extern "C" void syslog(int type, const char *fmt, ...)
1119 msg = get_pool_memory(PM_EMSG);
1122 maxlen = sizeof_pool_memory(msg) - 1;
1123 va_start(arg_ptr, fmt);
1124 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1126 if (len < 0 || len >= (maxlen-5)) {
1127 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1132 LogErrorMsg((const char *)msg);
1153 // implement opendir/readdir/closedir on top of window's API
1157 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1158 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1159 const char *spec; // the directory we're traversing
1160 HANDLE dirh; // the search handle
1161 BOOL valid_a; // the info in data_a field is valid
1162 BOOL valid_w; // the info in data_w field is valid
1163 UINT32 offset; // pseudo offset for d_off
1167 opendir(const char *path)
1169 /* enough space for VSS !*/
1170 int max_len = strlen(path) + MAX_PATH;
1178 Dmsg1(100, "Opendir path=%s\n", path);
1179 rval = (_dir *)malloc(sizeof(_dir));
1183 memset (rval, 0, sizeof (_dir));
1185 tspec = (char *)malloc(max_len);
1190 conv_unix_to_vss_win32_path(path, tspec, max_len);
1191 Dmsg1(100, "win32 path=%s\n", tspec);
1193 // add backslash only if there is none yet (think of c:\)
1194 if (tspec[strlen(tspec)-1] != '\\')
1195 bstrncat(tspec, "\\*", max_len);
1197 bstrncat(tspec, "*", max_len);
1201 // convert to wchar_t
1202 if (p_FindFirstFileW) {
1203 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1204 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1206 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1208 free_pool_memory(pwcBuf);
1210 if (rval->dirh != INVALID_HANDLE_VALUE)
1212 } else if (p_FindFirstFileA) {
1213 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1215 if (rval->dirh != INVALID_HANDLE_VALUE)
1220 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1221 path, rval->spec, rval->dirh);
1224 if (rval->dirh == INVALID_HANDLE_VALUE)
1227 if (rval->valid_w) {
1228 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1231 if (rval->valid_a) {
1232 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1244 errno = b_errno_win32;
1251 _dir *dp = (_dir *)dirp;
1252 FindClose(dp->dirh);
1253 free((void *)dp->spec);
1259 typedef struct _WIN32_FIND_DATA {
1260 DWORD dwFileAttributes;
1261 FILETIME ftCreationTime;
1262 FILETIME ftLastAccessTime;
1263 FILETIME ftLastWriteTime;
1264 DWORD nFileSizeHigh;
1268 TCHAR cFileName[MAX_PATH];
1269 TCHAR cAlternateFileName[14];
1270 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1274 copyin(struct dirent &dp, const char *fname)
1278 char *cp = dp.d_name;
1288 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1290 _dir *dp = (_dir *)dirp;
1291 if (dp->valid_w || dp->valid_a) {
1292 entry->d_off = dp->offset;
1296 char szBuf[MAX_PATH_UTF8+1];
1297 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1298 dp->offset += copyin(*entry, szBuf);
1299 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1300 dp->offset += copyin(*entry, dp->data_a.cFileName);
1303 *result = entry; /* return entry address */
1304 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1305 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1307 // Dmsg0(99, "readdir_r !valid\n");
1308 errno = b_errno_win32;
1312 // get next file, try unicode first
1313 if (p_FindNextFileW)
1314 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1315 else if (p_FindNextFileA)
1316 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1318 dp->valid_a = FALSE;
1319 dp->valid_w = FALSE;
1326 * Dotted IP address to network address
1332 inet_aton(const char *a, struct in_addr *inp)
1335 uint32_t acc = 0, tmp = 0;
1338 if (!isdigit(*cp)) { /* first char must be digit */
1339 return 0; /* error */
1343 tmp = (tmp * 10) + (*cp -'0');
1344 } else if (*cp == '.' || *cp == 0) {
1346 return 0; /* error */
1348 acc = (acc << 8) + tmp;
1352 return 0; /* error */
1354 } while (*cp++ != 0);
1355 if (dotc != 4) { /* want 3 .'s plus EOS */
1356 return 0; /* error */
1358 inp->s_addr = htonl(acc); /* store addr in network format */
1363 nanosleep(const struct timespec *req, struct timespec *rem)
1366 rem->tv_sec = rem->tv_nsec = 0;
1367 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1372 init_signals(void terminate(int sig))
1378 init_stack_dump(void)
1385 pathconf(const char *path, int name)
1389 if (strncmp(path, "\\\\?\\", 4) == 0)
1401 WORD wVersionRequested = MAKEWORD( 1, 1);
1404 int err = WSAStartup(wVersionRequested, &wsaData);
1408 printf("Can not start Windows Sockets\n");
1416 static DWORD fill_attribute(DWORD attr, mode_t mode)
1418 Dmsg1(200, " before attr=%lld\n", (uint64_t) attr);
1419 /* Use Bacula mappings define in stat() above */
1420 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1421 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1423 attr |= FILE_ATTRIBUTE_READONLY;
1425 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1426 attr |= FILE_ATTRIBUTE_HIDDEN;
1428 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1430 if (mode & S_IRWXO) { // Other can read/write/execute ?
1431 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1433 attr |= FILE_ATTRIBUTE_SYSTEM;
1435 Dmsg1(200, " after attr=%lld\n", (uint64_t)attr);
1439 int win32_chmod(const char *path, mode_t mode)
1444 Dmsg2(100, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1445 if (p_GetFileAttributesW) {
1446 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1447 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1449 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1450 if (attr != INVALID_FILE_ATTRIBUTES) {
1451 /* Use Bacula mappings define in stat() above */
1452 attr = fill_attribute(attr, mode);
1453 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1455 free_pool_memory(pwszBuf);
1456 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1457 } else if (p_GetFileAttributesA) {
1458 attr = p_GetFileAttributesA(path);
1459 if (attr != INVALID_FILE_ATTRIBUTES) {
1460 attr = fill_attribute(attr, mode);
1461 ret = p_SetFileAttributesA(path, attr);
1463 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1465 Dmsg0(100, "Leave win32_chmod did nothing\n");
1469 const char *err = errorString();
1470 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1471 LocalFree((void *)err);
1472 errno = b_errno_win32;
1480 win32_chdir(const char *dir)
1482 if (p_SetCurrentDirectoryW) {
1483 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1484 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1486 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1488 free_pool_memory(pwszBuf);
1491 errno = b_errno_win32;
1494 } else if (p_SetCurrentDirectoryA) {
1495 if (0 == p_SetCurrentDirectoryA(dir)) {
1496 errno = b_errno_win32;
1507 win32_mkdir(const char *dir)
1509 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1511 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1512 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1514 int n = p_wmkdir((LPCWSTR)pwszBuf);
1515 free_pool_memory(pwszBuf);
1516 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1520 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1526 win32_getcwd(char *buf, int maxlen)
1530 if (p_GetCurrentDirectoryW) {
1531 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1532 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1534 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1536 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1537 free_pool_memory(pwszBuf);
1539 } else if (p_GetCurrentDirectoryA)
1540 n = p_GetCurrentDirectoryA(maxlen, buf);
1542 if (n == 0 || n > maxlen) return NULL;
1544 if (n+1 > maxlen) return NULL;
1553 win32_fputs(const char *string, FILE *stream)
1555 /* we use WriteConsoleA / WriteConsoleA
1556 so we can be sure that unicode support works on win32.
1557 with fallback if something fails
1560 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1561 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1562 p_MultiByteToWideChar && (stream == stdout)) {
1564 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1566 DWORD dwCharsWritten;
1569 dwChars = UTF8_2_wchar(&pwszBuf, string);
1571 /* try WriteConsoleW */
1572 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1573 free_pool_memory(pwszBuf);
1574 return dwCharsWritten;
1577 /* convert to local codepage and try WriteConsoleA */
1578 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1579 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1581 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1582 free_pool_memory(pwszBuf);
1584 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1585 free_pool_memory(pszBuf);
1586 return dwCharsWritten;
1588 free_pool_memory(pszBuf);
1591 return fputs(string, stream);
1595 win32_cgets (char* buffer, int len)
1597 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1598 from the win32 console and fallback if seomething fails */
1600 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1601 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1603 wchar_t wszBuf[1024];
1606 /* nt and unicode conversion */
1607 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1609 /* null terminate at end */
1610 if (wszBuf[dwRead-1] == L'\n') {
1611 wszBuf[dwRead-1] = L'\0';
1615 if (wszBuf[dwRead-1] == L'\r') {
1616 wszBuf[dwRead-1] = L'\0';
1620 wchar_2_UTF8(buffer, wszBuf, len);
1624 /* win 9x and unicode conversion */
1625 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1627 /* null terminate at end */
1628 if (szBuf[dwRead-1] == L'\n') {
1629 szBuf[dwRead-1] = L'\0';
1633 if (szBuf[dwRead-1] == L'\r') {
1634 szBuf[dwRead-1] = L'\0';
1638 /* convert from ansii to wchar_t */
1639 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1640 /* convert from wchar_t to UTF-8 */
1641 if (wchar_2_UTF8(buffer, wszBuf, len))
1647 if (fgets(buffer, len, stdin))
1654 win32_unlink(const char *filename)
1658 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1659 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1661 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1664 * special case if file is readonly,
1665 * we retry but unset attribute before
1667 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1668 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1669 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1670 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1671 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1672 /* reset to original if it didn't help */
1674 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1678 free_pool_memory(pwszBuf);
1680 nRetCode = _unlink(filename);
1682 /* special case if file is readonly,
1683 we retry but unset attribute before */
1684 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1685 DWORD dwAttr = p_GetFileAttributesA(filename);
1686 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1687 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1688 nRetCode = _unlink(filename);
1689 /* reset to original if it didn't help */
1691 p_SetFileAttributesA(filename, dwAttr);
1700 #include "mswinver.h"
1702 char WIN_VERSION_LONG[64];
1703 char WIN_VERSION[32];
1704 char WIN_RAWVERSION[32];
1711 static winver INIT; // cause constructor to be called before main()
1714 winver::winver(void)
1716 const char *version = "";
1717 const char *platform = "";
1718 OSVERSIONINFO osvinfo;
1719 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1721 // Get the current OS version
1722 if (!GetVersionEx(&osvinfo)) {
1723 version = "Unknown";
1724 platform = "Unknown";
1726 const int ver = _mkversion(osvinfo.dwPlatformId,
1727 osvinfo.dwMajorVersion,
1728 osvinfo.dwMinorVersion);
1729 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1732 case MS_WINDOWS_95: (version = "Windows 95"); break;
1733 case MS_WINDOWS_98: (version = "Windows 98"); break;
1734 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1735 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1736 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1737 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1738 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1739 default: version = WIN_RAWVERSION; break;
1742 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1743 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1744 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1747 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1751 BPIPE *b = open_bpipe("ls -l", 10, "r");
1753 while (!feof(b->rfd)) {
1754 fgets(buf, sizeof(buf), b->rfd);
1760 BOOL CreateChildProcess(VOID);
1761 VOID WriteToPipe(VOID);
1762 VOID ReadFromPipe(VOID);
1763 VOID ErrorExit(LPCSTR);
1764 VOID ErrMsg(LPTSTR, BOOL);
1767 * Check for a quoted path, if an absolute path name is given and it contains
1768 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1769 * CreateProcess() says the best way to ensure proper results with executables
1770 * with spaces in path or filename is to quote the string.
1773 getArgv0(const char *cmdline)
1778 for (cp = cmdline; *cp; cp++)
1783 if (!inquote && isspace(*cp))
1788 int len = cp - cmdline;
1789 char *rval = (char *)malloc(len+1);
1802 * Extracts the executable or script name from the first string in
1805 * If the name contains blanks then it must be quoted with double quotes,
1806 * otherwise quotes are optional. If the name contains blanks then it
1807 * will be converted to a short name.
1809 * The optional quotes will be removed. The result is copied to a malloc'ed
1810 * buffer and returned through the pexe argument. The pargs parameter is set
1811 * to the address of the character in cmdline located after the name.
1813 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1816 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1818 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1819 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1821 const char *pBasename = NULL; /* Character after last path separator */
1822 const char *pExtension = NULL; /* Period at start of extension */
1824 const char *current = cmdline;
1826 bool bQuoted = false;
1828 /* Skip initial whitespace */
1830 while (*current == ' ' || *current == '\t')
1835 /* Calculate start of name and determine if quoted */
1837 if (*current == '"') {
1838 pExeStart = ++current;
1841 pExeStart = current;
1849 * Scan command line looking for path separators (/ and \\) and the
1850 * terminator, either a quote or a blank. The location of the
1851 * extension is also noted.
1854 for ( ; *current != '\0'; current++)
1856 if (*current == '.') {
1857 pExtension = current;
1858 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1859 pBasename = ¤t[1];
1863 /* Check for terminator, either quote or blank */
1865 if (*current != '"') {
1869 if (*current != ' ') {
1875 * Hit terminator, remember end of name (address of terminator) and
1876 * start of arguments
1880 if (bQuoted && *current == '"') {
1881 *pargs = ¤t[1];
1889 if (pBasename == NULL) {
1890 pBasename = pExeStart;
1893 if (pExeEnd == NULL) {
1902 bool bHasPathSeparators = pExeStart != pBasename;
1904 /* We have pointers to all the useful parts of the name */
1906 /* Default extensions in the order cmd.exe uses to search */
1908 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1909 DWORD dwBasePathLength = pExeEnd - pExeStart;
1911 DWORD dwAltNameLength = 0;
1912 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1913 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1915 pPathname[MAX_PATHLENGTH] = '\0';
1916 pAltPathname[MAX_PATHLENGTH] = '\0';
1918 memcpy(pPathname, pExeStart, dwBasePathLength);
1919 pPathname[dwBasePathLength] = '\0';
1921 if (pExtension == NULL) {
1922 /* Try appending extensions */
1923 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1925 if (!bHasPathSeparators) {
1926 /* There are no path separators, search in the standard locations */
1927 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1928 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1929 memcpy(pPathname, pAltPathname, dwAltNameLength);
1930 pPathname[dwAltNameLength] = '\0';
1934 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1935 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1938 pPathname[dwBasePathLength] = '\0';
1941 } else if (!bHasPathSeparators) {
1942 /* There are no path separators, search in the standard locations */
1943 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1944 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1945 memcpy(pPathname, pAltPathname, dwAltNameLength);
1946 pPathname[dwAltNameLength] = '\0';
1950 if (strchr(pPathname, ' ') != NULL) {
1951 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1953 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1954 *pexe = (char *)malloc(dwAltNameLength + 1);
1955 if (*pexe == NULL) {
1958 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1962 if (*pexe == NULL) {
1963 DWORD dwPathnameLength = strlen(pPathname);
1964 *pexe = (char *)malloc(dwPathnameLength + 1);
1965 if (*pexe == NULL) {
1968 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1975 * Create the process with WCHAR API
1978 CreateChildProcessW(const char *comspec, const char *cmdLine,
1979 PROCESS_INFORMATION *hProcInfo,
1980 HANDLE in, HANDLE out, HANDLE err)
1982 STARTUPINFOW siStartInfo;
1983 BOOL bFuncRetn = FALSE;
1985 // Set up members of the STARTUPINFO structure.
1986 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1987 siStartInfo.cb = sizeof(siStartInfo);
1988 // setup new process to use supplied handles for stdin,stdout,stderr
1990 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1991 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1993 siStartInfo.hStdInput = in;
1994 siStartInfo.hStdOutput = out;
1995 siStartInfo.hStdError = err;
1997 // Convert argument to WCHAR
1998 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1999 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2001 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2002 UTF8_2_wchar(&comspec_wchar, comspec);
2004 // Create the child process.
2005 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2007 // try to execute program
2008 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2009 (WCHAR*)cmdLine_wchar,// command line
2010 NULL, // process security attributes
2011 NULL, // primary thread security attributes
2012 TRUE, // handles are inherited
2013 0, // creation flags
2014 NULL, // use parent's environment
2015 NULL, // use parent's current directory
2016 &siStartInfo, // STARTUPINFO pointer
2017 hProcInfo); // receives PROCESS_INFORMATION
2018 free_pool_memory(cmdLine_wchar);
2019 free_pool_memory(comspec_wchar);
2026 * Create the process with ANSI API
2029 CreateChildProcessA(const char *comspec, char *cmdLine,
2030 PROCESS_INFORMATION *hProcInfo,
2031 HANDLE in, HANDLE out, HANDLE err)
2033 STARTUPINFOA siStartInfo;
2034 BOOL bFuncRetn = FALSE;
2036 // Set up members of the STARTUPINFO structure.
2037 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2038 siStartInfo.cb = sizeof(siStartInfo);
2039 // setup new process to use supplied handles for stdin,stdout,stderr
2040 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2041 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2043 siStartInfo.hStdInput = in;
2044 siStartInfo.hStdOutput = out;
2045 siStartInfo.hStdError = err;
2047 // Create the child process.
2048 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2050 // try to execute program
2051 bFuncRetn = p_CreateProcessA(comspec,
2052 cmdLine, // command line
2053 NULL, // process security attributes
2054 NULL, // primary thread security attributes
2055 TRUE, // handles are inherited
2056 0, // creation flags
2057 NULL, // use parent's environment
2058 NULL, // use parent's current directory
2059 &siStartInfo,// STARTUPINFO pointer
2060 hProcInfo);// receives PROCESS_INFORMATION
2065 * OK, so it would seem CreateProcess only handles true executables:
2066 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2069 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2071 static const char *comspec = NULL;
2072 PROCESS_INFORMATION piProcInfo;
2073 BOOL bFuncRetn = FALSE;
2075 if (!p_CreateProcessA || !p_CreateProcessW)
2076 return INVALID_HANDLE_VALUE;
2078 if (comspec == NULL)
2079 comspec = getenv("COMSPEC");
2080 if (comspec == NULL) // should never happen
2081 return INVALID_HANDLE_VALUE;
2083 // Set up members of the PROCESS_INFORMATION structure.
2084 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2086 // if supplied handles are not used the send a copy of our STD_HANDLE
2088 if (in == INVALID_HANDLE_VALUE)
2089 in = GetStdHandle(STD_INPUT_HANDLE);
2091 if (out == INVALID_HANDLE_VALUE)
2092 out = GetStdHandle(STD_OUTPUT_HANDLE);
2094 if (err == INVALID_HANDLE_VALUE)
2095 err = GetStdHandle(STD_ERROR_HANDLE);
2098 const char *argStart;
2100 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2101 return INVALID_HANDLE_VALUE;
2104 POOL_MEM cmdLine(PM_FNAME);
2105 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2109 // New function disabled
2110 if (p_CreateProcessW && p_MultiByteToWideChar) {
2111 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2114 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2118 if (bFuncRetn == 0) {
2119 ErrorExit("CreateProcess failed\n");
2120 const char *err = errorString();
2121 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2122 LocalFree((void *)err);
2123 return INVALID_HANDLE_VALUE;
2125 // we don't need a handle on the process primary thread so we close
2127 CloseHandle(piProcInfo.hThread);
2128 return piProcInfo.hProcess;
2132 ErrorExit (LPCSTR lpszMessage)
2134 Dmsg1(0, "%s", lpszMessage);
2139 typedef struct s_bpipe {
2141 time_t worker_stime;
2150 CloseIfValid(HANDLE handle)
2152 if (handle != INVALID_HANDLE_VALUE)
2153 CloseHandle(handle);
2157 open_bpipe(char *prog, int wait, const char *mode)
2159 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2160 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2163 SECURITY_ATTRIBUTES saAttr;
2167 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2168 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2169 hInputFile = INVALID_HANDLE_VALUE;
2171 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2172 memset((void *)bpipe, 0, sizeof(BPIPE));
2174 int mode_read = (mode[0] == 'r');
2175 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2178 // Set the bInheritHandle flag so pipe handles are inherited.
2180 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2181 saAttr.bInheritHandle = TRUE;
2182 saAttr.lpSecurityDescriptor = NULL;
2186 // Create a pipe for the child process's STDOUT.
2187 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2188 ErrorExit("Stdout pipe creation failed\n");
2191 // Create noninheritable read handle and close the inheritable read
2194 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2195 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2197 DUPLICATE_SAME_ACCESS);
2199 ErrorExit("DuplicateHandle failed");
2203 CloseHandle(hChildStdoutRd);
2204 hChildStdoutRd = INVALID_HANDLE_VALUE;
2209 // Create a pipe for the child process's STDIN.
2211 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2212 ErrorExit("Stdin pipe creation failed\n");
2216 // Duplicate the write handle to the pipe so it is not inherited.
2217 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2218 GetCurrentProcess(), &hChildStdinWrDup,
2220 FALSE, // not inherited
2221 DUPLICATE_SAME_ACCESS);
2223 ErrorExit("DuplicateHandle failed");
2227 CloseHandle(hChildStdinWr);
2228 hChildStdinWr = INVALID_HANDLE_VALUE;
2230 // spawn program with redirected handles as appropriate
2231 bpipe->worker_pid = (pid_t)
2232 CreateChildProcess(prog, // commandline
2233 hChildStdinRd, // stdin HANDLE
2234 hChildStdoutWr, // stdout HANDLE
2235 hChildStdoutWr); // stderr HANDLE
2237 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2241 bpipe->worker_stime = time(NULL);
2244 CloseHandle(hChildStdoutWr); // close our write side so when
2245 // process terminates we can
2247 // ugly but convert WIN32 HANDLE to FILE*
2248 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2250 bpipe->rfd = _fdopen(rfd, "rb");
2254 CloseHandle(hChildStdinRd); // close our read side so as not
2255 // to interfre with child's copy
2256 // ugly but convert WIN32 HANDLE to FILE*
2257 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2259 bpipe->wfd = _fdopen(wfd, "wb");
2264 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2271 CloseIfValid(hChildStdoutRd);
2272 CloseIfValid(hChildStdoutRdDup);
2273 CloseIfValid(hChildStdinWr);
2274 CloseIfValid(hChildStdinWrDup);
2276 free((void *) bpipe);
2277 errno = b_errno_win32; /* do GetLastError() for error code */
2283 kill(int pid, int signal)
2286 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2288 errno = b_errno_win32;
2290 CloseHandle((HANDLE)pid);
2296 close_bpipe(BPIPE *bpipe)
2299 int32_t remaining_wait = bpipe->wait;
2311 if (remaining_wait == 0) { /* wait indefinitely */
2312 remaining_wait = INT32_MAX;
2316 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2317 const char *err = errorString();
2318 rval = b_errno_win32;
2319 Dmsg1(0, "GetExitCode error %s\n", err);
2320 LocalFree((void *)err);
2323 if (exitCode == STILL_ACTIVE) {
2324 if (remaining_wait <= 0) {
2325 rval = ETIME; /* timed out */
2328 bmicrosleep(1, 0); /* wait one second */
2330 } else if (exitCode != 0) {
2331 /* Truncate exit code as it doesn't seem to be correct */
2332 rval = (exitCode & 0xFF) | b_errno_exit;
2335 break; /* Shouldn't get here */
2339 if (bpipe->timer_id) {
2340 stop_child_timer(bpipe->timer_id);
2342 if (bpipe->rfd) fclose(bpipe->rfd);
2343 if (bpipe->wfd) fclose(bpipe->wfd);
2344 free((void *)bpipe);
2349 close_wpipe(BPIPE *bpipe)
2355 if (fclose(bpipe->wfd) != 0) {
2365 utime(const char *fname, struct utimbuf *times)
2370 conv_unix_to_vss_win32_path(fname, tmpbuf, 5000);
2372 cvt_utime_to_ftime(times->actime, acc);
2373 cvt_utime_to_ftime(times->modtime, mod);
2375 HANDLE h = INVALID_HANDLE_VALUE;
2377 if (p_CreateFileW) {
2378 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2379 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2381 h = p_CreateFileW((LPCWSTR)pwszBuf,
2382 FILE_WRITE_ATTRIBUTES,
2383 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2386 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2389 free_pool_memory(pwszBuf);
2390 } else if (p_CreateFileA) {
2391 h = p_CreateFileA(tmpbuf,
2392 FILE_WRITE_ATTRIBUTES,
2393 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2396 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2400 if (h == INVALID_HANDLE_VALUE) {
2401 const char *err = errorString();
2402 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2403 LocalFree((void *)err);
2404 errno = b_errno_win32;
2408 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2411 errno = b_errno_win32;
2419 file_open(const char *file, int flags, int mode)
2422 DWORD shareMode = 0;
2425 HANDLE foo = INVALID_HANDLE_VALUE;
2426 const char *remap = file;
2428 if (flags & O_WRONLY) access = GENERIC_WRITE;
2429 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2430 else access = GENERIC_READ;
2432 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2433 create = CREATE_NEW;
2434 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2435 create = CREATE_ALWAYS;
2436 else if (flags & O_CREAT)
2437 create = OPEN_ALWAYS;
2438 else if (flags & O_TRUNC)
2439 create = TRUNCATE_EXISTING;
2441 create = OPEN_EXISTING;
2445 if (flags & O_APPEND) {
2446 printf("open...APPEND not implemented yet.");
2450 if (p_CreateFileW) {
2451 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2452 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2454 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2455 free_pool_memory(pwszBuf);
2456 } else if (p_CreateFileA)
2457 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2459 if (INVALID_HANDLE_VALUE == foo) {
2460 errno = b_errno_win32;
2471 if (!CloseHandle((HANDLE)fd)) {
2472 errno = b_errno_win32;
2480 file_write(int fd, const void *data, ssize_t len)
2484 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2485 if (status) return bwrite;
2486 errno = b_errno_win32;
2492 file_read(int fd, void *data, ssize_t len)
2497 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2498 if (status) return bread;
2499 errno = b_errno_win32;
2504 file_seek(int fd, boffset_t offset, int whence)
2508 LONG offset_low = (LONG)offset;
2509 LONG offset_high = (LONG)(offset >> 32);
2513 method = FILE_BEGIN;
2516 method = FILE_CURRENT;
2527 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2528 errno = b_errno_win32;
2531 /* ***FIXME*** I doubt this works right */
2545 * Emulation of mmap and unmmap for tokyo dbm
2547 void *mmap(void *start, size_t length, int prot, int flags,
2548 int fd, off_t offset)
2550 DWORD fm_access = 0;
2551 DWORD mv_access = 0;
2562 if (flags & PROT_WRITE) {
2563 fm_access |= PAGE_READWRITE;
2564 } else if (flags & PROT_READ) {
2565 fm_access |= PAGE_READONLY;
2568 if (flags & PROT_READ) {
2569 mv_access |= FILE_MAP_READ;
2571 if (flags & PROT_WRITE) {
2572 mv_access |= FILE_MAP_WRITE;
2575 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2576 NULL /* security */,
2578 0 /* MaximumSizeHigh */,
2579 0 /* MaximumSizeLow */,
2580 NULL /* name of the file mapping object */);
2582 if (!h || h == INVALID_HANDLE_VALUE) {
2586 mv = MapViewOfFile(h, mv_access,
2592 if (!mv || mv == INVALID_HANDLE_VALUE) {
2599 int munmap(void *start, size_t length)
2604 UnmapViewOfFile(start);
2610 /* syslog function, added by Nicolas Boichat */
2611 void openlog(const char *ident, int option, int facility) {}
2614 /* Log an error message */
2615 void LogErrorMsg(const char *message)
2617 HANDLE eventHandler;
2618 const char *strings[2];
2620 /* Use the OS event logging to log the error */
2621 eventHandler = RegisterEventSource(NULL, "Bacula");
2623 strings[0] = _("\n\nBacula ERROR: ");
2624 strings[1] = message;
2627 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2631 2, /* Number of strings */
2632 0, /* raw data size */
2633 (const char **)strings, /* error strings */
2634 NULL); /* raw data */
2635 DeregisterEventSource(eventHandler);