2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2009 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
43 #include "findlib/find.h"
45 #define b_errno_win32 (1<<29)
47 #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
118 void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
120 const char *fname = name;
121 char *tname = win32_name;
123 Dmsg0(100, "Enter convert_unix_to_win32_path\n");
125 if (IsPathSeparator(name[0]) &&
126 IsPathSeparator(name[1]) &&
128 IsPathSeparator(name[3])) {
130 *win32_name++ = '\\';
131 *win32_name++ = '\\';
133 *win32_name++ = '\\';
136 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
137 g_pVSSPathConvert == NULL) {
138 /* allow path to be 32767 bytes */
139 *win32_name++ = '\\';
140 *win32_name++ = '\\';
142 *win32_name++ = '\\';
146 /* Check for Unix separator and convert to Win32 */
147 if (name[0] == '/' && name[1] == '/') { /* double slash? */
148 name++; /* yes, skip first one */
151 *win32_name++ = '\\'; /* convert char */
152 /* If Win32 separator that is "quoted", remove quote */
153 } else if (*name == '\\' && name[1] == '\\') {
154 *win32_name++ = '\\';
155 name++; /* skip first \ */
157 *win32_name++ = *name; /* copy character */
161 /* Strip any trailing slash, if we stored something */
162 /* but leave "c:\" with backslash (root directory case */
163 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
169 /* here we convert to VSS specific file name which
170 can get longer because VSS will make something like
171 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
172 from c:\bacula\uninstall.exe
174 Dmsg1(100, "path=%s\n", tname);
175 if (g_pVSSPathConvert != NULL) {
176 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
177 pszBuf = check_pool_memory_size(pszBuf, dwSize);
178 bstrncpy(pszBuf, tname, strlen(tname)+1);
179 g_pVSSPathConvert(pszBuf, tname, dwSize);
180 free_pool_memory(pszBuf);
183 Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
186 /* Conversion of a Unix filename to a Win32 filename */
187 void unix_name_to_win32(POOLMEM **win32_name, char *name)
189 /* One extra byte should suffice, but we double it */
190 /* add MAX_PATH bytes for VSS shadow copy name */
191 DWORD dwSize = 2*strlen(name)+MAX_PATH;
192 *win32_name = check_pool_memory_size(*win32_name, dwSize);
193 conv_unix_to_win32_path(name, *win32_name, dwSize);
197 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
199 /* created 02/27/2006 Thorsten Engel
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.
210 Dmsg0(100, "Enter wchar_win32_path\n");
212 *pBIsRawPath = FALSE; /* Initialize, set later */
215 if (!p_GetCurrentDirectoryW) {
216 Dmsg0(100, "Leave wchar_win32_path no change \n");
220 wchar_t *name = (wchar_t *)pszUCSPath;
222 /* if it has already the desired form, exit without changes */
223 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
224 Dmsg0(100, "Leave wchar_win32_path no change \n");
228 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
229 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
230 DWORD dwCurDirPathSize = 0;
232 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
233 DWORD dwBufCharsNeeded = (wcslen(name)+7);
234 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
236 /* add \\?\ to support 32K long filepaths
237 it is important to make absolute paths, so we add drive and
238 current path if necessary */
240 BOOL bAddDrive = TRUE;
241 BOOL bAddCurrentPath = TRUE;
242 BOOL bAddPrefix = TRUE;
244 /* does path begin with drive? if yes, it is absolute */
245 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
247 bAddCurrentPath = FALSE;
250 /* is path absolute? */
251 if (IsPathSeparator(name[0]))
252 bAddCurrentPath = FALSE;
254 /* is path relative to itself?, if yes, skip ./ */
255 if (name[0] == '.' && IsPathSeparator(name[1])) {
259 /* is path of form '//./'? */
260 if (IsPathSeparator(name[0]) &&
261 IsPathSeparator(name[1]) &&
263 IsPathSeparator(name[3])) {
265 bAddCurrentPath = FALSE;
272 int nParseOffset = 0;
274 /* add 4 bytes header */
277 wcscpy(pwszBuf, L"\\\\?\\");
280 /* get current path if needed */
281 if (bAddDrive || bAddCurrentPath) {
282 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
283 if (dwCurDirPathSize > 0) {
284 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
285 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
286 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
288 /* we have no info for doing so */
290 bAddCurrentPath = FALSE;
295 /* add drive if needed */
296 if (bAddDrive && !bAddCurrentPath) {
299 if (IsPathSeparator(pwszCurDirBuf[0]) &&
300 IsPathSeparator(pwszCurDirBuf[1]) &&
301 pwszCurDirBuf[2] == '?' &&
302 IsPathSeparator(pwszCurDirBuf[3])) {
303 /* copy drive character */
304 szDrive[0] = pwszCurDirBuf[4];
306 /* copy drive character */
307 szDrive[0] = pwszCurDirBuf[0];
313 wcscat(pwszBuf, szDrive);
317 /* add path if needed */
318 if (bAddCurrentPath) {
319 /* the 1 add. character is for the eventually added backslash */
320 dwBufCharsNeeded += dwCurDirPathSize+1;
321 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
322 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
324 if (IsPathSeparator(pwszCurDirBuf[0]) &&
325 IsPathSeparator(pwszCurDirBuf[1]) &&
326 pwszCurDirBuf[2] == '?' &&
327 IsPathSeparator(pwszCurDirBuf[3])) {
328 /* copy complete string */
329 wcscpy(pwszBuf, pwszCurDirBuf);
332 wcscat(pwszBuf, pwszCurDirBuf);
335 nParseOffset = wcslen((LPCWSTR) pwszBuf);
337 /* check if path ends with backslash, if not, add one */
338 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
339 wcscat(pwszBuf, L"\\");
344 wchar_t *win32_name = &pwszBuf[nParseOffset];
345 wchar_t *name_start = name;
348 /* Check for Unix separator and convert to Win32, eliminating
349 * duplicate separators.
351 if (IsPathSeparator(*name)) {
352 *win32_name++ = '\\'; /* convert char */
354 /* Eliminate consecutive slashes, but not at the start so that
357 if (name_start != name && IsPathSeparator(name[1])) {
361 *win32_name++ = *name; /* copy character */
366 /* null terminate string */
369 /* here we convert to VSS specific file name which
370 * can get longer because VSS will make something like
371 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
372 * from c:\bacula\uninstall.exe
374 if (g_pVSSPathConvertW != NULL) {
375 /* is output buffer large enough? */
376 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
377 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
378 /* create temp. buffer */
379 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
380 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
381 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
386 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
387 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
388 free_pool_memory((POOLMEM *)pszBuf);
391 free_pool_memory(pszUCSPath);
392 free_pool_memory((POOLMEM *)pwszCurDirBuf);
394 Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
395 return (POOLMEM *)pwszBuf;
399 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
401 /* the return value is the number of bytes written to the buffer.
402 The number includes the byte for the null terminator. */
404 if (p_WideCharToMultiByte) {
405 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
414 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
416 /* the return value is the number of wide characters written to the buffer. */
417 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
419 if (p_MultiByteToWideChar) {
420 /* strlen of UTF8 +1 is enough */
421 DWORD cchSize = (strlen(pszUTF)+1);
422 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
424 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
434 wchar_win32_path(const char *name, wchar_t *win32_name)
436 const char *fname = name;
438 /* Check for Unix separator and convert to Win32 */
440 *win32_name++ = '\\'; /* convert char */
441 /* If Win32 separated that is "quoted", remove quote */
442 } else if (*name == '\\' && name[1] == '\\') {
443 *win32_name++ = '\\';
444 name++; /* skip first \ */
446 *win32_name++ = *name; /* copy character */
450 /* Strip any trailing slash, if we stored something */
451 if (*fname != 0 && win32_name[-1] == '\\') {
459 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
462 /* if we find the utf8 string in cache, we use the cached ucs2 version.
463 we compare the stringlength first (quick check) and then compare the content.
465 if (!g_pWin32ConvUTF8Cache) {
466 Win32ConvInitCache();
467 } else if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
468 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
469 /* Return cached value */
470 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
471 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
472 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
474 return nBufSize / sizeof (WCHAR);
478 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
479 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
481 #ifdef USE_WIN32_32KPATHCONVERSION
482 /* add \\?\ to support 32K long filepaths */
483 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
486 *pBIsRawPath = FALSE;
490 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
491 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
493 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
494 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
495 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
501 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
508 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
509 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
512 void *dlopen(const char *file, int mode)
516 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
520 void *dlsym(void *handle, const char *name)
523 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
527 int dlclose(void *handle)
529 if (handle && !FreeLibrary((HMODULE)handle)) {
530 errno = b_errno_win32;
531 return 1; /* failed */
538 static char buf[200];
539 const char *err = errorString();
540 bstrncpy(buf, (char *)err, sizeof(buf));
541 LocalFree((void *)err);
545 int fcntl(int fd, int cmd)
550 int chown(const char *k, uid_t, gid_t)
555 int lchown(const char *k, uid_t, gid_t)
567 srandom(unsigned int seed)
571 // /////////////////////////////////////////////////////////////////
572 // convert from Windows concept of time to Unix concept of time
573 // /////////////////////////////////////////////////////////////////
575 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
577 uint64_t mstime = time;
578 mstime *= WIN32_FILETIME_SCALE;
579 mstime += WIN32_FILETIME_ADJUST;
581 #if defined(_MSC_VER)
582 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
584 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
586 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
590 cvt_ftime_to_utime(const FILETIME &time)
592 uint64_t mstime = time.dwHighDateTime;
594 mstime |= time.dwLowDateTime;
596 mstime -= WIN32_FILETIME_ADJUST;
597 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
599 return (time_t) (mstime & 0xffffffff);
602 static const char *errorString(void)
606 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
607 FORMAT_MESSAGE_FROM_SYSTEM |
608 FORMAT_MESSAGE_IGNORE_INSERTS,
611 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
616 /* Strip any \r or \n */
617 char *rval = (char *) lpMsgBuf;
618 char *cp = strchr(rval, '\r');
622 cp = strchr(rval, '\n');
631 statDir(const char *file, struct stat *sb)
633 WIN32_FIND_DATAW info_w; // window's file info
634 WIN32_FIND_DATAA info_a; // window's file info
636 // cache some common vars to make code more transparent
637 DWORD *pdwFileAttributes;
638 DWORD *pnFileSizeHigh;
639 DWORD *pnFileSizeLow;
641 FILETIME *pftLastAccessTime;
642 FILETIME *pftLastWriteTime;
643 FILETIME *pftCreationTime;
646 * Oh, cool, another exception: Microsoft doesn't let us do
647 * FindFile operations on a Drive, so simply fake root attibutes.
649 if (file[1] == ':' && file[2] == 0) {
650 time_t now = time(NULL);
651 Dmsg1(99, "faking ROOT attrs(%s).\n", file);
652 sb->st_mode = S_IFDIR;
653 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
661 HANDLE h = INVALID_HANDLE_VALUE;
664 if (p_FindFirstFileW) {
665 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
666 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
668 Dmsg1(100, "FindFirstFileW=%s\n", file);
669 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
670 free_pool_memory(pwszBuf);
672 pdwFileAttributes = &info_w.dwFileAttributes;
673 pdwReserved0 = &info_w.dwReserved0;
674 pnFileSizeHigh = &info_w.nFileSizeHigh;
675 pnFileSizeLow = &info_w.nFileSizeLow;
676 pftLastAccessTime = &info_w.ftLastAccessTime;
677 pftLastWriteTime = &info_w.ftLastWriteTime;
678 pftCreationTime = &info_w.ftCreationTime;
681 } else if (p_FindFirstFileA) {
682 Dmsg1(100, "FindFirstFileA=%s\n", file);
683 h = p_FindFirstFileA(file, &info_a);
685 pdwFileAttributes = &info_a.dwFileAttributes;
686 pdwReserved0 = &info_a.dwReserved0;
687 pnFileSizeHigh = &info_a.nFileSizeHigh;
688 pnFileSizeLow = &info_a.nFileSizeLow;
689 pftLastAccessTime = &info_a.ftLastAccessTime;
690 pftLastWriteTime = &info_a.ftLastWriteTime;
691 pftCreationTime = &info_a.ftCreationTime;
693 Dmsg0(100, "No findFirstFile A or W found\n");
696 if (h == INVALID_HANDLE_VALUE) {
697 const char *err = errorString();
699 * Note, in creating leading paths, it is normal that
700 * the file does not exist.
702 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
703 LocalFree((void *)err);
704 errno = b_errno_win32;
710 sb->st_mode = 0777; /* start with everything */
711 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
712 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
713 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
714 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
715 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
716 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
717 sb->st_mode |= S_IFDIR;
720 * Store reparse/mount point info in st_rdev. Note a
721 * Win32 reparse point (junction point) is like a link
722 * though it can have many properties (directory link,
723 * soft link, hard link, HSM, ...
724 * A mount point is a reparse point where another volume
725 * is mounted, so it is like a Unix mount point (change of
728 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
729 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
730 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
732 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
735 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
736 sb->st_size = *pnFileSizeHigh;
738 sb->st_size |= *pnFileSizeLow;
739 sb->st_blksize = 4096;
740 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
742 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
743 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
744 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
750 fstat(intptr_t fd, struct stat *sb)
752 BY_HANDLE_FILE_INFORMATION info;
754 if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
755 const char *err = errorString();
756 Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
757 LocalFree((void *)err);
758 errno = b_errno_win32;
762 sb->st_dev = info.dwVolumeSerialNumber;
763 sb->st_ino = info.nFileIndexHigh;
765 sb->st_ino |= info.nFileIndexLow;
766 sb->st_nlink = (short)info.nNumberOfLinks;
767 if (sb->st_nlink > 1) {
768 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
771 sb->st_mode = 0777; /* start with everything */
772 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
773 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
774 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
775 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
776 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
777 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
778 sb->st_mode |= S_IFREG;
780 /* Use st_rdev to store reparse attribute */
781 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
782 sb->st_rdev = WIN32_REPARSE_POINT;
784 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
785 (long long)sb->st_ino);
787 sb->st_size = info.nFileSizeHigh;
789 sb->st_size |= info.nFileSizeLow;
790 sb->st_blksize = 4096;
791 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
792 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
793 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
794 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
800 stat2(const char *file, struct stat *sb)
802 HANDLE h = INVALID_HANDLE_VALUE;
805 conv_unix_to_win32_path(file, tmpbuf, 5000);
807 DWORD attr = (DWORD)-1;
809 if (p_GetFileAttributesW) {
810 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
811 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
813 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
815 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
816 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
818 free_pool_memory(pwszBuf);
819 } else if (p_GetFileAttributesA) {
820 attr = p_GetFileAttributesA(tmpbuf);
821 h = CreateFileA(tmpbuf, GENERIC_READ,
822 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
825 if (attr == (DWORD)-1) {
826 const char *err = errorString();
827 Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err);
828 LocalFree((void *)err);
829 if (h != INVALID_HANDLE_VALUE) {
832 errno = b_errno_win32;
836 if (h == INVALID_HANDLE_VALUE) {
837 const char *err = errorString();
838 Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
839 LocalFree((void *)err);
840 errno = b_errno_win32;
844 rval = fstat((intptr_t)h, sb);
847 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
848 file[1] == ':' && file[2] != 0) {
849 rval = statDir(file, sb);
855 stat(const char *file, struct stat *sb)
857 WIN32_FILE_ATTRIBUTE_DATA data;
860 memset(sb, 0, sizeof(*sb));
862 if (p_GetFileAttributesExW) {
863 /* dynamically allocate enough space for UCS2 filename */
864 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
865 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
867 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
868 free_pool_memory(pwszBuf);
871 return stat2(file, sb);
874 } else if (p_GetFileAttributesExA) {
875 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
876 return stat2(file, sb);
879 return stat2(file, sb);
882 sb->st_mode = 0777; /* start with everything */
883 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
884 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
886 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
887 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
889 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
890 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
892 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
893 sb->st_mode |= S_IFDIR;
895 sb->st_mode |= S_IFREG;
898 /* Use st_rdev to store reparse attribute */
899 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
902 sb->st_size = data.nFileSizeHigh;
904 sb->st_size |= data.nFileSizeLow;
905 sb->st_blksize = 4096;
906 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
907 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
908 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
909 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
912 * If we are not at the root, then to distinguish a reparse
913 * point from a mount point, we must call FindFirstFile() to
914 * get the WIN32_FIND_DATA, which has the bit that indicates
915 * that this directory is a mount point -- aren't Win32 APIs
916 * wonderful? (sarcasm). The code exists in the statDir
919 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
920 file[1] == ':' && file[2] != 0) {
923 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
924 (long long)sb->st_ino, file);
929 * We write our own ftruncate because the one in the
930 * Microsoft library mrcrt.dll does not truncate
931 * files greater than 2GB.
934 int win32_ftruncate(int fd, int64_t length)
936 /* Set point we want to truncate file */
937 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
939 if (pos != (__int64)length) {
940 errno = EACCES; /* truncation failed, get out */
945 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
946 errno = b_errno_win32;
953 int fcntl(int fd, int cmd, long arg)
976 lstat(const char *file, struct stat *sb)
978 return stat(file, sb);
994 execvp(const char *, char *[]) {
1015 waitpid(int, int*, int)
1022 readlink(const char *, char *, int)
1031 strcasecmp(const char *s1, const char *s2)
1033 register int ch1, ch2;
1036 return 0; /* strings are equal if same object. */
1046 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1053 strncasecmp(const char *s1, const char *s2, int len)
1055 register int ch1 = 0, ch2 = 0;
1058 return 0; /* strings are equal if same object. */
1069 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1076 gettimeofday(struct timeval *tv, struct timezone *)
1081 GetSystemTime(&now);
1087 if (!SystemTimeToFileTime(&now, &tmp)) {
1088 errno = b_errno_win32;
1092 int64_t _100nsec = tmp.dwHighDateTime;
1094 _100nsec |= tmp.dwLowDateTime;
1095 _100nsec -= WIN32_FILETIME_ADJUST;
1097 tv->tv_sec = (long)(_100nsec / 10000000);
1098 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1104 * Write in Windows System log
1106 extern "C" void syslog(int type, const char *fmt, ...)
1112 msg = get_pool_memory(PM_EMSG);
1115 maxlen = sizeof_pool_memory(msg) - 1;
1116 va_start(arg_ptr, fmt);
1117 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1119 if (len < 0 || len >= (maxlen-5)) {
1120 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1125 LogErrorMsg((const char *)msg);
1146 // implement opendir/readdir/closedir on top of window's API
1150 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1151 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1152 const char *spec; // the directory we're traversing
1153 HANDLE dirh; // the search handle
1154 BOOL valid_a; // the info in data_a field is valid
1155 BOOL valid_w; // the info in data_w field is valid
1156 UINT32 offset; // pseudo offset for d_off
1160 opendir(const char *path)
1162 /* enough space for VSS !*/
1163 int max_len = strlen(path) + MAX_PATH;
1170 Dmsg1(100, "Opendir path=%s\n", path);
1171 rval = (_dir *)malloc(sizeof(_dir));
1172 memset (rval, 0, sizeof (_dir));
1173 if (rval == NULL) return NULL;
1174 char *tspec = (char *)malloc(max_len);
1175 if (tspec == NULL) return NULL;
1177 conv_unix_to_win32_path(path, tspec, max_len);
1178 Dmsg1(100, "win32 path=%s\n", tspec);
1180 // add backslash only if there is none yet (think of c:\)
1181 if (tspec[strlen(tspec)-1] != '\\')
1182 bstrncat(tspec, "\\*", max_len);
1184 bstrncat(tspec, "*", max_len);
1188 // convert to wchar_t
1189 if (p_FindFirstFileW) {
1190 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1191 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1193 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1195 free_pool_memory(pwcBuf);
1197 if (rval->dirh != INVALID_HANDLE_VALUE)
1199 } else if (p_FindFirstFileA) {
1200 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1202 if (rval->dirh != INVALID_HANDLE_VALUE)
1207 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1208 path, rval->spec, rval->dirh);
1211 if (rval->dirh == INVALID_HANDLE_VALUE)
1214 if (rval->valid_w) {
1215 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1218 if (rval->valid_a) {
1219 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1225 free((void *)rval->spec);
1227 errno = b_errno_win32;
1234 _dir *dp = (_dir *)dirp;
1235 FindClose(dp->dirh);
1236 free((void *)dp->spec);
1242 typedef struct _WIN32_FIND_DATA {
1243 DWORD dwFileAttributes;
1244 FILETIME ftCreationTime;
1245 FILETIME ftLastAccessTime;
1246 FILETIME ftLastWriteTime;
1247 DWORD nFileSizeHigh;
1251 TCHAR cFileName[MAX_PATH];
1252 TCHAR cAlternateFileName[14];
1253 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1257 copyin(struct dirent &dp, const char *fname)
1261 char *cp = dp.d_name;
1271 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1273 _dir *dp = (_dir *)dirp;
1274 if (dp->valid_w || dp->valid_a) {
1275 entry->d_off = dp->offset;
1279 char szBuf[MAX_PATH_UTF8+1];
1280 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1281 dp->offset += copyin(*entry, szBuf);
1282 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1283 dp->offset += copyin(*entry, dp->data_a.cFileName);
1286 *result = entry; /* return entry address */
1287 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1288 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1290 // Dmsg0(99, "readdir_r !valid\n");
1291 errno = b_errno_win32;
1295 // get next file, try unicode first
1296 if (p_FindNextFileW)
1297 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1298 else if (p_FindNextFileA)
1299 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1301 dp->valid_a = FALSE;
1302 dp->valid_w = FALSE;
1309 * Dotted IP address to network address
1315 inet_aton(const char *a, struct in_addr *inp)
1318 uint32_t acc = 0, tmp = 0;
1321 if (!isdigit(*cp)) { /* first char must be digit */
1322 return 0; /* error */
1326 tmp = (tmp * 10) + (*cp -'0');
1327 } else if (*cp == '.' || *cp == 0) {
1329 return 0; /* error */
1331 acc = (acc << 8) + tmp;
1335 return 0; /* error */
1337 } while (*cp++ != 0);
1338 if (dotc != 4) { /* want 3 .'s plus EOS */
1339 return 0; /* error */
1341 inp->s_addr = htonl(acc); /* store addr in network format */
1346 nanosleep(const struct timespec *req, struct timespec *rem)
1349 rem->tv_sec = rem->tv_nsec = 0;
1350 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1355 init_signals(void terminate(int sig))
1361 init_stack_dump(void)
1368 pathconf(const char *path, int name)
1372 if (strncmp(path, "\\\\?\\", 4) == 0)
1384 WORD wVersionRequested = MAKEWORD( 1, 1);
1387 int err = WSAStartup(wVersionRequested, &wsaData);
1391 printf("Can not start Windows Sockets\n");
1399 static DWORD fill_attribute(DWORD attr, mode_t mode)
1401 Dmsg1(200, " before attr=%lld\n", (uint64_t) attr);
1402 /* Use Bacula mappings define in stat() above */
1403 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1404 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1406 attr |= FILE_ATTRIBUTE_READONLY;
1408 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1409 attr |= FILE_ATTRIBUTE_HIDDEN;
1411 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1413 if (mode & S_IRWXO) { // Other can read/write/execute ?
1414 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1416 attr |= FILE_ATTRIBUTE_SYSTEM;
1418 Dmsg1(200, " after attr=%lld\n", (uint64_t)attr);
1422 int win32_chmod(const char *path, mode_t mode)
1427 Dmsg2(0, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1428 if (p_GetFileAttributesW) {
1429 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1430 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1432 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1433 if (attr != INVALID_FILE_ATTRIBUTES) {
1434 /* Use Bacula mappings define in stat() above */
1435 attr = fill_attribute(attr, mode);
1436 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1438 free_pool_memory(pwszBuf);
1439 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1440 } else if (p_GetFileAttributesA) {
1441 attr = p_GetFileAttributesA(path);
1442 if (attr != INVALID_FILE_ATTRIBUTES) {
1443 attr = fill_attribute(attr, mode);
1444 ret = p_SetFileAttributesA(path, attr);
1446 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1448 Dmsg0(100, "Leave win32_chmod did nothing\n");
1452 const char *err = errorString();
1453 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1454 LocalFree((void *)err);
1455 errno = b_errno_win32;
1463 win32_chdir(const char *dir)
1465 if (p_SetCurrentDirectoryW) {
1466 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1467 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1469 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1471 free_pool_memory(pwszBuf);
1474 errno = b_errno_win32;
1477 } else if (p_SetCurrentDirectoryA) {
1478 if (0 == p_SetCurrentDirectoryA(dir)) {
1479 errno = b_errno_win32;
1490 win32_mkdir(const char *dir)
1492 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1494 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1495 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1497 int n = p_wmkdir((LPCWSTR)pwszBuf);
1498 free_pool_memory(pwszBuf);
1499 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1503 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1509 win32_getcwd(char *buf, int maxlen)
1513 if (p_GetCurrentDirectoryW) {
1514 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1515 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1517 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1519 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1520 free_pool_memory(pwszBuf);
1522 } else if (p_GetCurrentDirectoryA)
1523 n = p_GetCurrentDirectoryA(maxlen, buf);
1525 if (n == 0 || n > maxlen) return NULL;
1527 if (n+1 > maxlen) return NULL;
1536 win32_fputs(const char *string, FILE *stream)
1538 /* we use WriteConsoleA / WriteConsoleA
1539 so we can be sure that unicode support works on win32.
1540 with fallback if something fails
1543 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1544 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1545 p_MultiByteToWideChar && (stream == stdout)) {
1547 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1549 DWORD dwCharsWritten;
1552 dwChars = UTF8_2_wchar(&pwszBuf, string);
1554 /* try WriteConsoleW */
1555 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1556 free_pool_memory(pwszBuf);
1557 return dwCharsWritten;
1560 /* convert to local codepage and try WriteConsoleA */
1561 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1562 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1564 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1565 free_pool_memory(pwszBuf);
1567 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1568 free_pool_memory(pszBuf);
1569 return dwCharsWritten;
1571 free_pool_memory(pszBuf);
1574 return fputs(string, stream);
1578 win32_cgets (char* buffer, int len)
1580 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1581 from the win32 console and fallback if seomething fails */
1583 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1584 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1586 wchar_t wszBuf[1024];
1589 /* nt and unicode conversion */
1590 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1592 /* null terminate at end */
1593 if (wszBuf[dwRead-1] == L'\n') {
1594 wszBuf[dwRead-1] = L'\0';
1598 if (wszBuf[dwRead-1] == L'\r') {
1599 wszBuf[dwRead-1] = L'\0';
1603 wchar_2_UTF8(buffer, wszBuf, len);
1607 /* win 9x and unicode conversion */
1608 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1610 /* null terminate at end */
1611 if (szBuf[dwRead-1] == L'\n') {
1612 szBuf[dwRead-1] = L'\0';
1616 if (szBuf[dwRead-1] == L'\r') {
1617 szBuf[dwRead-1] = L'\0';
1621 /* convert from ansii to wchar_t */
1622 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1623 /* convert from wchar_t to UTF-8 */
1624 if (wchar_2_UTF8(buffer, wszBuf, len))
1630 if (fgets(buffer, len, stdin))
1637 win32_unlink(const char *filename)
1641 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1642 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1644 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1647 * special case if file is readonly,
1648 * we retry but unset attribute before
1650 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1651 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1652 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1653 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1654 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1655 /* reset to original if it didn't help */
1657 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1661 free_pool_memory(pwszBuf);
1663 nRetCode = _unlink(filename);
1665 /* special case if file is readonly,
1666 we retry but unset attribute before */
1667 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1668 DWORD dwAttr = p_GetFileAttributesA(filename);
1669 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1670 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1671 nRetCode = _unlink(filename);
1672 /* reset to original if it didn't help */
1674 p_SetFileAttributesA(filename, dwAttr);
1683 #include "mswinver.h"
1685 char WIN_VERSION_LONG[64];
1686 char WIN_VERSION[32];
1687 char WIN_RAWVERSION[32];
1694 static winver INIT; // cause constructor to be called before main()
1697 winver::winver(void)
1699 const char *version = "";
1700 const char *platform = "";
1701 OSVERSIONINFO osvinfo;
1702 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1704 // Get the current OS version
1705 if (!GetVersionEx(&osvinfo)) {
1706 version = "Unknown";
1707 platform = "Unknown";
1709 const int ver = _mkversion(osvinfo.dwPlatformId,
1710 osvinfo.dwMajorVersion,
1711 osvinfo.dwMinorVersion);
1712 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1715 case MS_WINDOWS_95: (version = "Windows 95"); break;
1716 case MS_WINDOWS_98: (version = "Windows 98"); break;
1717 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1718 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1719 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1720 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1721 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1722 default: version = WIN_RAWVERSION; break;
1725 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1726 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1727 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1730 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1734 BPIPE *b = open_bpipe("ls -l", 10, "r");
1736 while (!feof(b->rfd)) {
1737 fgets(buf, sizeof(buf), b->rfd);
1743 BOOL CreateChildProcess(VOID);
1744 VOID WriteToPipe(VOID);
1745 VOID ReadFromPipe(VOID);
1746 VOID ErrorExit(LPCSTR);
1747 VOID ErrMsg(LPTSTR, BOOL);
1750 * Check for a quoted path, if an absolute path name is given and it contains
1751 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1752 * CreateProcess() says the best way to ensure proper results with executables
1753 * with spaces in path or filename is to quote the string.
1756 getArgv0(const char *cmdline)
1761 for (cp = cmdline; *cp; cp++)
1766 if (!inquote && isspace(*cp))
1771 int len = cp - cmdline;
1772 char *rval = (char *)malloc(len+1);
1785 * Extracts the executable or script name from the first string in
1788 * If the name contains blanks then it must be quoted with double quotes,
1789 * otherwise quotes are optional. If the name contains blanks then it
1790 * will be converted to a short name.
1792 * The optional quotes will be removed. The result is copied to a malloc'ed
1793 * buffer and returned through the pexe argument. The pargs parameter is set
1794 * to the address of the character in cmdline located after the name.
1796 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1799 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1801 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1802 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1804 const char *pBasename = NULL; /* Character after last path separator */
1805 const char *pExtension = NULL; /* Period at start of extension */
1807 const char *current = cmdline;
1809 bool bQuoted = false;
1811 /* Skip initial whitespace */
1813 while (*current == ' ' || *current == '\t')
1818 /* Calculate start of name and determine if quoted */
1820 if (*current == '"') {
1821 pExeStart = ++current;
1824 pExeStart = current;
1832 * Scan command line looking for path separators (/ and \\) and the
1833 * terminator, either a quote or a blank. The location of the
1834 * extension is also noted.
1837 for ( ; *current != '\0'; current++)
1839 if (*current == '.') {
1840 pExtension = current;
1841 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1842 pBasename = ¤t[1];
1846 /* Check for terminator, either quote or blank */
1848 if (*current != '"') {
1852 if (*current != ' ') {
1858 * Hit terminator, remember end of name (address of terminator) and
1859 * start of arguments
1863 if (bQuoted && *current == '"') {
1864 *pargs = ¤t[1];
1872 if (pBasename == NULL) {
1873 pBasename = pExeStart;
1876 if (pExeEnd == NULL) {
1885 bool bHasPathSeparators = pExeStart != pBasename;
1887 /* We have pointers to all the useful parts of the name */
1889 /* Default extensions in the order cmd.exe uses to search */
1891 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1892 DWORD dwBasePathLength = pExeEnd - pExeStart;
1894 DWORD dwAltNameLength = 0;
1895 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1896 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1898 pPathname[MAX_PATHLENGTH] = '\0';
1899 pAltPathname[MAX_PATHLENGTH] = '\0';
1901 memcpy(pPathname, pExeStart, dwBasePathLength);
1902 pPathname[dwBasePathLength] = '\0';
1904 if (pExtension == NULL) {
1905 /* Try appending extensions */
1906 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1908 if (!bHasPathSeparators) {
1909 /* There are no path separators, search in the standard locations */
1910 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1911 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1912 memcpy(pPathname, pAltPathname, dwAltNameLength);
1913 pPathname[dwAltNameLength] = '\0';
1917 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1918 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1921 pPathname[dwBasePathLength] = '\0';
1924 } else if (!bHasPathSeparators) {
1925 /* There are no path separators, search in the standard locations */
1926 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1927 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1928 memcpy(pPathname, pAltPathname, dwAltNameLength);
1929 pPathname[dwAltNameLength] = '\0';
1933 if (strchr(pPathname, ' ') != NULL) {
1934 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1936 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1937 *pexe = (char *)malloc(dwAltNameLength + 1);
1938 if (*pexe == NULL) {
1941 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1945 if (*pexe == NULL) {
1946 DWORD dwPathnameLength = strlen(pPathname);
1947 *pexe = (char *)malloc(dwPathnameLength + 1);
1948 if (*pexe == NULL) {
1951 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1958 * Create the process with WCHAR API
1961 CreateChildProcessW(const char *comspec, const char *cmdLine,
1962 PROCESS_INFORMATION *hProcInfo,
1963 HANDLE in, HANDLE out, HANDLE err)
1965 STARTUPINFOW siStartInfo;
1966 BOOL bFuncRetn = FALSE;
1968 // Set up members of the STARTUPINFO structure.
1969 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1970 siStartInfo.cb = sizeof(siStartInfo);
1971 // setup new process to use supplied handles for stdin,stdout,stderr
1973 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1974 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1976 siStartInfo.hStdInput = in;
1977 siStartInfo.hStdOutput = out;
1978 siStartInfo.hStdError = err;
1980 // Convert argument to WCHAR
1981 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1982 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1984 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1985 UTF8_2_wchar(&comspec_wchar, comspec);
1987 // Create the child process.
1988 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
1990 // try to execute program
1991 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1992 (WCHAR*)cmdLine_wchar,// command line
1993 NULL, // process security attributes
1994 NULL, // primary thread security attributes
1995 TRUE, // handles are inherited
1996 0, // creation flags
1997 NULL, // use parent's environment
1998 NULL, // use parent's current directory
1999 &siStartInfo, // STARTUPINFO pointer
2000 hProcInfo); // receives PROCESS_INFORMATION
2001 free_pool_memory(cmdLine_wchar);
2002 free_pool_memory(comspec_wchar);
2009 * Create the process with ANSI API
2012 CreateChildProcessA(const char *comspec, char *cmdLine,
2013 PROCESS_INFORMATION *hProcInfo,
2014 HANDLE in, HANDLE out, HANDLE err)
2016 STARTUPINFOA siStartInfo;
2017 BOOL bFuncRetn = FALSE;
2019 // Set up members of the STARTUPINFO structure.
2020 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2021 siStartInfo.cb = sizeof(siStartInfo);
2022 // setup new process to use supplied handles for stdin,stdout,stderr
2023 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2024 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2026 siStartInfo.hStdInput = in;
2027 siStartInfo.hStdOutput = out;
2028 siStartInfo.hStdError = err;
2030 // Create the child process.
2031 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2033 // try to execute program
2034 bFuncRetn = p_CreateProcessA(comspec,
2035 cmdLine, // command line
2036 NULL, // process security attributes
2037 NULL, // primary thread security attributes
2038 TRUE, // handles are inherited
2039 0, // creation flags
2040 NULL, // use parent's environment
2041 NULL, // use parent's current directory
2042 &siStartInfo,// STARTUPINFO pointer
2043 hProcInfo);// receives PROCESS_INFORMATION
2048 * OK, so it would seem CreateProcess only handles true executables:
2049 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2052 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2054 static const char *comspec = NULL;
2055 PROCESS_INFORMATION piProcInfo;
2056 BOOL bFuncRetn = FALSE;
2058 if (!p_CreateProcessA || !p_CreateProcessW)
2059 return INVALID_HANDLE_VALUE;
2061 if (comspec == NULL)
2062 comspec = getenv("COMSPEC");
2063 if (comspec == NULL) // should never happen
2064 return INVALID_HANDLE_VALUE;
2066 // Set up members of the PROCESS_INFORMATION structure.
2067 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2069 // if supplied handles are not used the send a copy of our STD_HANDLE
2071 if (in == INVALID_HANDLE_VALUE)
2072 in = GetStdHandle(STD_INPUT_HANDLE);
2074 if (out == INVALID_HANDLE_VALUE)
2075 out = GetStdHandle(STD_OUTPUT_HANDLE);
2077 if (err == INVALID_HANDLE_VALUE)
2078 err = GetStdHandle(STD_ERROR_HANDLE);
2081 const char *argStart;
2083 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2084 return INVALID_HANDLE_VALUE;
2087 POOL_MEM cmdLine(PM_FNAME);
2088 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2092 // New function disabled
2093 if (p_CreateProcessW && p_MultiByteToWideChar) {
2094 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2097 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2101 if (bFuncRetn == 0) {
2102 ErrorExit("CreateProcess failed\n");
2103 const char *err = errorString();
2104 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2105 LocalFree((void *)err);
2106 return INVALID_HANDLE_VALUE;
2108 // we don't need a handle on the process primary thread so we close
2110 CloseHandle(piProcInfo.hThread);
2111 return piProcInfo.hProcess;
2115 ErrorExit (LPCSTR lpszMessage)
2117 Dmsg1(0, "%s", lpszMessage);
2122 typedef struct s_bpipe {
2124 time_t worker_stime;
2133 CloseIfValid(HANDLE handle)
2135 if (handle != INVALID_HANDLE_VALUE)
2136 CloseHandle(handle);
2140 open_bpipe(char *prog, int wait, const char *mode)
2142 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2143 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2146 SECURITY_ATTRIBUTES saAttr;
2150 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2151 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2152 hInputFile = INVALID_HANDLE_VALUE;
2154 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2155 memset((void *)bpipe, 0, sizeof(BPIPE));
2157 int mode_read = (mode[0] == 'r');
2158 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2161 // Set the bInheritHandle flag so pipe handles are inherited.
2163 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2164 saAttr.bInheritHandle = TRUE;
2165 saAttr.lpSecurityDescriptor = NULL;
2169 // Create a pipe for the child process's STDOUT.
2170 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2171 ErrorExit("Stdout pipe creation failed\n");
2174 // Create noninheritable read handle and close the inheritable read
2177 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2178 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2180 DUPLICATE_SAME_ACCESS);
2182 ErrorExit("DuplicateHandle failed");
2186 CloseHandle(hChildStdoutRd);
2187 hChildStdoutRd = INVALID_HANDLE_VALUE;
2192 // Create a pipe for the child process's STDIN.
2194 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2195 ErrorExit("Stdin pipe creation failed\n");
2199 // Duplicate the write handle to the pipe so it is not inherited.
2200 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2201 GetCurrentProcess(), &hChildStdinWrDup,
2203 FALSE, // not inherited
2204 DUPLICATE_SAME_ACCESS);
2206 ErrorExit("DuplicateHandle failed");
2210 CloseHandle(hChildStdinWr);
2211 hChildStdinWr = INVALID_HANDLE_VALUE;
2213 // spawn program with redirected handles as appropriate
2214 bpipe->worker_pid = (pid_t)
2215 CreateChildProcess(prog, // commandline
2216 hChildStdinRd, // stdin HANDLE
2217 hChildStdoutWr, // stdout HANDLE
2218 hChildStdoutWr); // stderr HANDLE
2220 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2224 bpipe->worker_stime = time(NULL);
2227 CloseHandle(hChildStdoutWr); // close our write side so when
2228 // process terminates we can
2230 // ugly but convert WIN32 HANDLE to FILE*
2231 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2233 bpipe->rfd = _fdopen(rfd, "rb");
2237 CloseHandle(hChildStdinRd); // close our read side so as not
2238 // to interfre with child's copy
2239 // ugly but convert WIN32 HANDLE to FILE*
2240 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2242 bpipe->wfd = _fdopen(wfd, "wb");
2247 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2254 CloseIfValid(hChildStdoutRd);
2255 CloseIfValid(hChildStdoutRdDup);
2256 CloseIfValid(hChildStdinWr);
2257 CloseIfValid(hChildStdinWrDup);
2259 free((void *) bpipe);
2260 errno = b_errno_win32; /* do GetLastError() for error code */
2266 kill(int pid, int signal)
2269 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2271 errno = b_errno_win32;
2273 CloseHandle((HANDLE)pid);
2279 close_bpipe(BPIPE *bpipe)
2282 int32_t remaining_wait = bpipe->wait;
2294 if (remaining_wait == 0) { /* wait indefinitely */
2295 remaining_wait = INT32_MAX;
2299 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2300 const char *err = errorString();
2301 rval = b_errno_win32;
2302 Dmsg1(0, "GetExitCode error %s\n", err);
2303 LocalFree((void *)err);
2306 if (exitCode == STILL_ACTIVE) {
2307 if (remaining_wait <= 0) {
2308 rval = ETIME; /* timed out */
2311 bmicrosleep(1, 0); /* wait one second */
2313 } else if (exitCode != 0) {
2314 /* Truncate exit code as it doesn't seem to be correct */
2315 rval = (exitCode & 0xFF) | b_errno_exit;
2318 break; /* Shouldn't get here */
2322 if (bpipe->timer_id) {
2323 stop_child_timer(bpipe->timer_id);
2325 if (bpipe->rfd) fclose(bpipe->rfd);
2326 if (bpipe->wfd) fclose(bpipe->wfd);
2327 free((void *)bpipe);
2332 close_wpipe(BPIPE *bpipe)
2338 if (fclose(bpipe->wfd) != 0) {
2348 utime(const char *fname, struct utimbuf *times)
2353 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2355 cvt_utime_to_ftime(times->actime, acc);
2356 cvt_utime_to_ftime(times->modtime, mod);
2358 HANDLE h = INVALID_HANDLE_VALUE;
2360 if (p_CreateFileW) {
2361 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2362 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2364 h = p_CreateFileW((LPCWSTR)pwszBuf,
2365 FILE_WRITE_ATTRIBUTES,
2366 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2369 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2372 free_pool_memory(pwszBuf);
2373 } else if (p_CreateFileA) {
2374 h = p_CreateFileA(tmpbuf,
2375 FILE_WRITE_ATTRIBUTES,
2376 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2379 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2383 if (h == INVALID_HANDLE_VALUE) {
2384 const char *err = errorString();
2385 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2386 LocalFree((void *)err);
2387 errno = b_errno_win32;
2391 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2394 errno = b_errno_win32;
2402 file_open(const char *file, int flags, int mode)
2405 DWORD shareMode = 0;
2408 HANDLE foo = INVALID_HANDLE_VALUE;
2409 const char *remap = file;
2411 if (flags & O_WRONLY) access = GENERIC_WRITE;
2412 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2413 else access = GENERIC_READ;
2415 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2416 create = CREATE_NEW;
2417 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2418 create = CREATE_ALWAYS;
2419 else if (flags & O_CREAT)
2420 create = OPEN_ALWAYS;
2421 else if (flags & O_TRUNC)
2422 create = TRUNCATE_EXISTING;
2424 create = OPEN_EXISTING;
2428 if (flags & O_APPEND) {
2429 printf("open...APPEND not implemented yet.");
2433 if (p_CreateFileW) {
2434 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2435 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2437 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2438 free_pool_memory(pwszBuf);
2439 } else if (p_CreateFileA)
2440 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2442 if (INVALID_HANDLE_VALUE == foo) {
2443 errno = b_errno_win32;
2454 if (!CloseHandle((HANDLE)fd)) {
2455 errno = b_errno_win32;
2463 file_write(int fd, const void *data, ssize_t len)
2467 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2468 if (status) return bwrite;
2469 errno = b_errno_win32;
2475 file_read(int fd, void *data, ssize_t len)
2480 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2481 if (status) return bread;
2482 errno = b_errno_win32;
2487 file_seek(int fd, boffset_t offset, int whence)
2491 LONG offset_low = (LONG)offset;
2492 LONG offset_high = (LONG)(offset >> 32);
2496 method = FILE_BEGIN;
2499 method = FILE_CURRENT;
2510 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2511 errno = b_errno_win32;
2514 /* ***FIXME*** I doubt this works right */
2528 * Emulation of mmap and unmmap for tokyo dbm
2530 void *mmap(void *start, size_t length, int prot, int flags,
2531 int fd, off_t offset)
2533 DWORD fm_access = 0;
2534 DWORD mv_access = 0;
2545 if (flags & PROT_WRITE) {
2546 fm_access |= PAGE_READWRITE;
2547 } else if (flags & PROT_READ) {
2548 fm_access |= PAGE_READONLY;
2551 if (flags & PROT_READ) {
2552 mv_access |= FILE_MAP_READ;
2554 if (flags & PROT_WRITE) {
2555 mv_access |= FILE_MAP_WRITE;
2558 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2559 NULL /* security */,
2561 0 /* MaximumSizeHigh */,
2562 0 /* MaximumSizeLow */,
2563 NULL /* name of the file mapping object */);
2565 if (!h || h == INVALID_HANDLE_VALUE) {
2569 mv = MapViewOfFile(h, mv_access,
2575 if (!mv || mv == INVALID_HANDLE_VALUE) {
2582 int munmap(void *start, size_t length)
2587 UnmapViewOfFile(start);
2593 /* syslog function, added by Nicolas Boichat */
2594 void openlog(const char *ident, int option, int facility) {}
2597 /* Log an error message */
2598 void LogErrorMsg(const char *message)
2600 HANDLE eventHandler;
2601 const char *strings[2];
2603 /* Use the OS event logging to log the error */
2604 eventHandler = RegisterEventSource(NULL, "Bacula");
2606 strings[0] = _("\n\nBacula ERROR: ");
2607 strings[1] = message;
2610 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2614 2, /* Number of strings */
2615 0, /* raw data size */
2616 (const char **)strings, /* error strings */
2617 NULL); /* raw data */
2618 DeregisterEventSource(eventHandler);