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 int win32_chmod(const char *path, mode_t mode)
1401 DWORD attr = (DWORD)-1;
1403 Dmsg1(100, "Enter win32_chmod. path=%s\n", path);
1404 if (p_GetFileAttributesW) {
1405 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1406 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1408 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1409 if (attr != INVALID_FILE_ATTRIBUTES) {
1410 /* Use Bacula mappings define in stat() above */
1411 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1412 attr |= FILE_ATTRIBUTE_READONLY;
1414 attr &= ~FILE_ATTRIBUTE_READONLY;
1416 if (mode & S_ISVTX) {
1417 attr |= FILE_ATTRIBUTE_HIDDEN;
1419 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1421 if (mode & S_IRWXO) {
1422 attr |= FILE_ATTRIBUTE_SYSTEM;
1424 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1426 attr = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1428 free_pool_memory(pwszBuf);
1429 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1430 } else if (p_GetFileAttributesA) {
1431 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1432 attr |= FILE_ATTRIBUTE_READONLY;
1434 attr &= ~FILE_ATTRIBUTE_READONLY;
1436 if (mode & S_ISVTX) {
1437 attr |= FILE_ATTRIBUTE_HIDDEN;
1439 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1441 if (mode & S_IRWXO) {
1442 attr |= FILE_ATTRIBUTE_SYSTEM;
1444 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1446 attr = p_GetFileAttributesA(path);
1447 if (attr != INVALID_FILE_ATTRIBUTES) {
1448 attr = p_SetFileAttributesA(path, attr);
1450 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1452 Dmsg0(100, "Leave win32_chmod did nothing\n");
1456 if (attr == (DWORD)-1) {
1457 const char *err = errorString();
1458 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1459 LocalFree((void *)err);
1460 errno = b_errno_win32;
1468 win32_chdir(const char *dir)
1470 if (p_SetCurrentDirectoryW) {
1471 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1472 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1474 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1476 free_pool_memory(pwszBuf);
1479 errno = b_errno_win32;
1482 } else if (p_SetCurrentDirectoryA) {
1483 if (0 == p_SetCurrentDirectoryA(dir)) {
1484 errno = b_errno_win32;
1495 win32_mkdir(const char *dir)
1497 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1499 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1500 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1502 int n = p_wmkdir((LPCWSTR)pwszBuf);
1503 free_pool_memory(pwszBuf);
1504 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1508 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1514 win32_getcwd(char *buf, int maxlen)
1518 if (p_GetCurrentDirectoryW) {
1519 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1520 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1522 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1524 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1525 free_pool_memory(pwszBuf);
1527 } else if (p_GetCurrentDirectoryA)
1528 n = p_GetCurrentDirectoryA(maxlen, buf);
1530 if (n == 0 || n > maxlen) return NULL;
1532 if (n+1 > maxlen) return NULL;
1541 win32_fputs(const char *string, FILE *stream)
1543 /* we use WriteConsoleA / WriteConsoleA
1544 so we can be sure that unicode support works on win32.
1545 with fallback if something fails
1548 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1549 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1550 p_MultiByteToWideChar && (stream == stdout)) {
1552 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1554 DWORD dwCharsWritten;
1557 dwChars = UTF8_2_wchar(&pwszBuf, string);
1559 /* try WriteConsoleW */
1560 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1561 free_pool_memory(pwszBuf);
1562 return dwCharsWritten;
1565 /* convert to local codepage and try WriteConsoleA */
1566 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1567 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1569 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1570 free_pool_memory(pwszBuf);
1572 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1573 free_pool_memory(pszBuf);
1574 return dwCharsWritten;
1576 free_pool_memory(pszBuf);
1579 return fputs(string, stream);
1583 win32_cgets (char* buffer, int len)
1585 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1586 from the win32 console and fallback if seomething fails */
1588 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1589 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1591 wchar_t wszBuf[1024];
1594 /* nt and unicode conversion */
1595 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1597 /* null terminate at end */
1598 if (wszBuf[dwRead-1] == L'\n') {
1599 wszBuf[dwRead-1] = L'\0';
1603 if (wszBuf[dwRead-1] == L'\r') {
1604 wszBuf[dwRead-1] = L'\0';
1608 wchar_2_UTF8(buffer, wszBuf, len);
1612 /* win 9x and unicode conversion */
1613 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1615 /* null terminate at end */
1616 if (szBuf[dwRead-1] == L'\n') {
1617 szBuf[dwRead-1] = L'\0';
1621 if (szBuf[dwRead-1] == L'\r') {
1622 szBuf[dwRead-1] = L'\0';
1626 /* convert from ansii to wchar_t */
1627 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1628 /* convert from wchar_t to UTF-8 */
1629 if (wchar_2_UTF8(buffer, wszBuf, len))
1635 if (fgets(buffer, len, stdin))
1642 win32_unlink(const char *filename)
1646 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1647 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1649 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1652 * special case if file is readonly,
1653 * we retry but unset attribute before
1655 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1656 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1657 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1658 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1659 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1660 /* reset to original if it didn't help */
1662 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1666 free_pool_memory(pwszBuf);
1668 nRetCode = _unlink(filename);
1670 /* special case if file is readonly,
1671 we retry but unset attribute before */
1672 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1673 DWORD dwAttr = p_GetFileAttributesA(filename);
1674 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1675 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1676 nRetCode = _unlink(filename);
1677 /* reset to original if it didn't help */
1679 p_SetFileAttributesA(filename, dwAttr);
1688 #include "mswinver.h"
1690 char WIN_VERSION_LONG[64];
1691 char WIN_VERSION[32];
1692 char WIN_RAWVERSION[32];
1699 static winver INIT; // cause constructor to be called before main()
1702 winver::winver(void)
1704 const char *version = "";
1705 const char *platform = "";
1706 OSVERSIONINFO osvinfo;
1707 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1709 // Get the current OS version
1710 if (!GetVersionEx(&osvinfo)) {
1711 version = "Unknown";
1712 platform = "Unknown";
1714 const int ver = _mkversion(osvinfo.dwPlatformId,
1715 osvinfo.dwMajorVersion,
1716 osvinfo.dwMinorVersion);
1717 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1720 case MS_WINDOWS_95: (version = "Windows 95"); break;
1721 case MS_WINDOWS_98: (version = "Windows 98"); break;
1722 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1723 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1724 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1725 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1726 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1727 default: version = WIN_RAWVERSION; break;
1730 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1731 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1732 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1735 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1739 BPIPE *b = open_bpipe("ls -l", 10, "r");
1741 while (!feof(b->rfd)) {
1742 fgets(buf, sizeof(buf), b->rfd);
1748 BOOL CreateChildProcess(VOID);
1749 VOID WriteToPipe(VOID);
1750 VOID ReadFromPipe(VOID);
1751 VOID ErrorExit(LPCSTR);
1752 VOID ErrMsg(LPTSTR, BOOL);
1755 * Check for a quoted path, if an absolute path name is given and it contains
1756 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1757 * CreateProcess() says the best way to ensure proper results with executables
1758 * with spaces in path or filename is to quote the string.
1761 getArgv0(const char *cmdline)
1766 for (cp = cmdline; *cp; cp++)
1771 if (!inquote && isspace(*cp))
1776 int len = cp - cmdline;
1777 char *rval = (char *)malloc(len+1);
1790 * Extracts the executable or script name from the first string in
1793 * If the name contains blanks then it must be quoted with double quotes,
1794 * otherwise quotes are optional. If the name contains blanks then it
1795 * will be converted to a short name.
1797 * The optional quotes will be removed. The result is copied to a malloc'ed
1798 * buffer and returned through the pexe argument. The pargs parameter is set
1799 * to the address of the character in cmdline located after the name.
1801 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1804 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1806 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1807 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1809 const char *pBasename = NULL; /* Character after last path separator */
1810 const char *pExtension = NULL; /* Period at start of extension */
1812 const char *current = cmdline;
1814 bool bQuoted = false;
1816 /* Skip initial whitespace */
1818 while (*current == ' ' || *current == '\t')
1823 /* Calculate start of name and determine if quoted */
1825 if (*current == '"') {
1826 pExeStart = ++current;
1829 pExeStart = current;
1837 * Scan command line looking for path separators (/ and \\) and the
1838 * terminator, either a quote or a blank. The location of the
1839 * extension is also noted.
1842 for ( ; *current != '\0'; current++)
1844 if (*current == '.') {
1845 pExtension = current;
1846 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1847 pBasename = ¤t[1];
1851 /* Check for terminator, either quote or blank */
1853 if (*current != '"') {
1857 if (*current != ' ') {
1863 * Hit terminator, remember end of name (address of terminator) and
1864 * start of arguments
1868 if (bQuoted && *current == '"') {
1869 *pargs = ¤t[1];
1877 if (pBasename == NULL) {
1878 pBasename = pExeStart;
1881 if (pExeEnd == NULL) {
1890 bool bHasPathSeparators = pExeStart != pBasename;
1892 /* We have pointers to all the useful parts of the name */
1894 /* Default extensions in the order cmd.exe uses to search */
1896 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1897 DWORD dwBasePathLength = pExeEnd - pExeStart;
1899 DWORD dwAltNameLength = 0;
1900 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1901 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1903 pPathname[MAX_PATHLENGTH] = '\0';
1904 pAltPathname[MAX_PATHLENGTH] = '\0';
1906 memcpy(pPathname, pExeStart, dwBasePathLength);
1907 pPathname[dwBasePathLength] = '\0';
1909 if (pExtension == NULL) {
1910 /* Try appending extensions */
1911 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1913 if (!bHasPathSeparators) {
1914 /* There are no path separators, search in the standard locations */
1915 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1916 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1917 memcpy(pPathname, pAltPathname, dwAltNameLength);
1918 pPathname[dwAltNameLength] = '\0';
1922 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1923 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1926 pPathname[dwBasePathLength] = '\0';
1929 } else if (!bHasPathSeparators) {
1930 /* There are no path separators, search in the standard locations */
1931 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1932 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1933 memcpy(pPathname, pAltPathname, dwAltNameLength);
1934 pPathname[dwAltNameLength] = '\0';
1938 if (strchr(pPathname, ' ') != NULL) {
1939 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1941 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1942 *pexe = (char *)malloc(dwAltNameLength + 1);
1943 if (*pexe == NULL) {
1946 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1950 if (*pexe == NULL) {
1951 DWORD dwPathnameLength = strlen(pPathname);
1952 *pexe = (char *)malloc(dwPathnameLength + 1);
1953 if (*pexe == NULL) {
1956 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1963 * Create the process with WCHAR API
1966 CreateChildProcessW(const char *comspec, const char *cmdLine,
1967 PROCESS_INFORMATION *hProcInfo,
1968 HANDLE in, HANDLE out, HANDLE err)
1970 STARTUPINFOW siStartInfo;
1971 BOOL bFuncRetn = FALSE;
1973 // Set up members of the STARTUPINFO structure.
1974 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1975 siStartInfo.cb = sizeof(siStartInfo);
1976 // setup new process to use supplied handles for stdin,stdout,stderr
1978 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1979 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1981 siStartInfo.hStdInput = in;
1982 siStartInfo.hStdOutput = out;
1983 siStartInfo.hStdError = err;
1985 // Convert argument to WCHAR
1986 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1987 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1989 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1990 UTF8_2_wchar(&comspec_wchar, comspec);
1992 // Create the child process.
1993 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
1995 // try to execute program
1996 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1997 (WCHAR*)cmdLine_wchar,// command line
1998 NULL, // process security attributes
1999 NULL, // primary thread security attributes
2000 TRUE, // handles are inherited
2001 0, // creation flags
2002 NULL, // use parent's environment
2003 NULL, // use parent's current directory
2004 &siStartInfo, // STARTUPINFO pointer
2005 hProcInfo); // receives PROCESS_INFORMATION
2006 free_pool_memory(cmdLine_wchar);
2007 free_pool_memory(comspec_wchar);
2014 * Create the process with ANSI API
2017 CreateChildProcessA(const char *comspec, char *cmdLine,
2018 PROCESS_INFORMATION *hProcInfo,
2019 HANDLE in, HANDLE out, HANDLE err)
2021 STARTUPINFOA siStartInfo;
2022 BOOL bFuncRetn = FALSE;
2024 // Set up members of the STARTUPINFO structure.
2025 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2026 siStartInfo.cb = sizeof(siStartInfo);
2027 // setup new process to use supplied handles for stdin,stdout,stderr
2028 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2029 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2031 siStartInfo.hStdInput = in;
2032 siStartInfo.hStdOutput = out;
2033 siStartInfo.hStdError = err;
2035 // Create the child process.
2036 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2038 // try to execute program
2039 bFuncRetn = p_CreateProcessA(comspec,
2040 cmdLine, // command line
2041 NULL, // process security attributes
2042 NULL, // primary thread security attributes
2043 TRUE, // handles are inherited
2044 0, // creation flags
2045 NULL, // use parent's environment
2046 NULL, // use parent's current directory
2047 &siStartInfo,// STARTUPINFO pointer
2048 hProcInfo);// receives PROCESS_INFORMATION
2053 * OK, so it would seem CreateProcess only handles true executables:
2054 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2057 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2059 static const char *comspec = NULL;
2060 PROCESS_INFORMATION piProcInfo;
2061 BOOL bFuncRetn = FALSE;
2063 if (!p_CreateProcessA || !p_CreateProcessW)
2064 return INVALID_HANDLE_VALUE;
2066 if (comspec == NULL)
2067 comspec = getenv("COMSPEC");
2068 if (comspec == NULL) // should never happen
2069 return INVALID_HANDLE_VALUE;
2071 // Set up members of the PROCESS_INFORMATION structure.
2072 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2074 // if supplied handles are not used the send a copy of our STD_HANDLE
2076 if (in == INVALID_HANDLE_VALUE)
2077 in = GetStdHandle(STD_INPUT_HANDLE);
2079 if (out == INVALID_HANDLE_VALUE)
2080 out = GetStdHandle(STD_OUTPUT_HANDLE);
2082 if (err == INVALID_HANDLE_VALUE)
2083 err = GetStdHandle(STD_ERROR_HANDLE);
2086 const char *argStart;
2088 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2089 return INVALID_HANDLE_VALUE;
2092 POOL_MEM cmdLine(PM_FNAME);
2093 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2097 // New function disabled
2098 if (p_CreateProcessW && p_MultiByteToWideChar) {
2099 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2102 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2106 if (bFuncRetn == 0) {
2107 ErrorExit("CreateProcess failed\n");
2108 const char *err = errorString();
2109 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2110 LocalFree((void *)err);
2111 return INVALID_HANDLE_VALUE;
2113 // we don't need a handle on the process primary thread so we close
2115 CloseHandle(piProcInfo.hThread);
2116 return piProcInfo.hProcess;
2120 ErrorExit (LPCSTR lpszMessage)
2122 Dmsg1(0, "%s", lpszMessage);
2127 typedef struct s_bpipe {
2129 time_t worker_stime;
2138 CloseIfValid(HANDLE handle)
2140 if (handle != INVALID_HANDLE_VALUE)
2141 CloseHandle(handle);
2145 open_bpipe(char *prog, int wait, const char *mode)
2147 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2148 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2151 SECURITY_ATTRIBUTES saAttr;
2155 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2156 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2157 hInputFile = INVALID_HANDLE_VALUE;
2159 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2160 memset((void *)bpipe, 0, sizeof(BPIPE));
2162 int mode_read = (mode[0] == 'r');
2163 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2166 // Set the bInheritHandle flag so pipe handles are inherited.
2168 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2169 saAttr.bInheritHandle = TRUE;
2170 saAttr.lpSecurityDescriptor = NULL;
2174 // Create a pipe for the child process's STDOUT.
2175 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2176 ErrorExit("Stdout pipe creation failed\n");
2179 // Create noninheritable read handle and close the inheritable read
2182 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2183 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2185 DUPLICATE_SAME_ACCESS);
2187 ErrorExit("DuplicateHandle failed");
2191 CloseHandle(hChildStdoutRd);
2192 hChildStdoutRd = INVALID_HANDLE_VALUE;
2197 // Create a pipe for the child process's STDIN.
2199 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2200 ErrorExit("Stdin pipe creation failed\n");
2204 // Duplicate the write handle to the pipe so it is not inherited.
2205 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2206 GetCurrentProcess(), &hChildStdinWrDup,
2208 FALSE, // not inherited
2209 DUPLICATE_SAME_ACCESS);
2211 ErrorExit("DuplicateHandle failed");
2215 CloseHandle(hChildStdinWr);
2216 hChildStdinWr = INVALID_HANDLE_VALUE;
2218 // spawn program with redirected handles as appropriate
2219 bpipe->worker_pid = (pid_t)
2220 CreateChildProcess(prog, // commandline
2221 hChildStdinRd, // stdin HANDLE
2222 hChildStdoutWr, // stdout HANDLE
2223 hChildStdoutWr); // stderr HANDLE
2225 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2229 bpipe->worker_stime = time(NULL);
2232 CloseHandle(hChildStdoutWr); // close our write side so when
2233 // process terminates we can
2235 // ugly but convert WIN32 HANDLE to FILE*
2236 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2238 bpipe->rfd = _fdopen(rfd, "rb");
2242 CloseHandle(hChildStdinRd); // close our read side so as not
2243 // to interfre with child's copy
2244 // ugly but convert WIN32 HANDLE to FILE*
2245 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2247 bpipe->wfd = _fdopen(wfd, "wb");
2252 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2259 CloseIfValid(hChildStdoutRd);
2260 CloseIfValid(hChildStdoutRdDup);
2261 CloseIfValid(hChildStdinWr);
2262 CloseIfValid(hChildStdinWrDup);
2264 free((void *) bpipe);
2265 errno = b_errno_win32; /* do GetLastError() for error code */
2271 kill(int pid, int signal)
2274 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2276 errno = b_errno_win32;
2278 CloseHandle((HANDLE)pid);
2284 close_bpipe(BPIPE *bpipe)
2287 int32_t remaining_wait = bpipe->wait;
2299 if (remaining_wait == 0) { /* wait indefinitely */
2300 remaining_wait = INT32_MAX;
2304 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2305 const char *err = errorString();
2306 rval = b_errno_win32;
2307 Dmsg1(0, "GetExitCode error %s\n", err);
2308 LocalFree((void *)err);
2311 if (exitCode == STILL_ACTIVE) {
2312 if (remaining_wait <= 0) {
2313 rval = ETIME; /* timed out */
2316 bmicrosleep(1, 0); /* wait one second */
2318 } else if (exitCode != 0) {
2319 /* Truncate exit code as it doesn't seem to be correct */
2320 rval = (exitCode & 0xFF) | b_errno_exit;
2323 break; /* Shouldn't get here */
2327 if (bpipe->timer_id) {
2328 stop_child_timer(bpipe->timer_id);
2330 if (bpipe->rfd) fclose(bpipe->rfd);
2331 if (bpipe->wfd) fclose(bpipe->wfd);
2332 free((void *)bpipe);
2337 close_wpipe(BPIPE *bpipe)
2343 if (fclose(bpipe->wfd) != 0) {
2353 utime(const char *fname, struct utimbuf *times)
2358 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2360 cvt_utime_to_ftime(times->actime, acc);
2361 cvt_utime_to_ftime(times->modtime, mod);
2363 HANDLE h = INVALID_HANDLE_VALUE;
2365 if (p_CreateFileW) {
2366 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2367 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2369 h = p_CreateFileW((LPCWSTR)pwszBuf,
2370 FILE_WRITE_ATTRIBUTES,
2371 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2374 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2377 free_pool_memory(pwszBuf);
2378 } else if (p_CreateFileA) {
2379 h = p_CreateFileA(tmpbuf,
2380 FILE_WRITE_ATTRIBUTES,
2381 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2384 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2388 if (h == INVALID_HANDLE_VALUE) {
2389 const char *err = errorString();
2390 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2391 LocalFree((void *)err);
2392 errno = b_errno_win32;
2396 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2399 errno = b_errno_win32;
2407 file_open(const char *file, int flags, int mode)
2410 DWORD shareMode = 0;
2413 HANDLE foo = INVALID_HANDLE_VALUE;
2414 const char *remap = file;
2416 if (flags & O_WRONLY) access = GENERIC_WRITE;
2417 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2418 else access = GENERIC_READ;
2420 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2421 create = CREATE_NEW;
2422 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2423 create = CREATE_ALWAYS;
2424 else if (flags & O_CREAT)
2425 create = OPEN_ALWAYS;
2426 else if (flags & O_TRUNC)
2427 create = TRUNCATE_EXISTING;
2429 create = OPEN_EXISTING;
2433 if (flags & O_APPEND) {
2434 printf("open...APPEND not implemented yet.");
2438 if (p_CreateFileW) {
2439 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2440 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2442 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2443 free_pool_memory(pwszBuf);
2444 } else if (p_CreateFileA)
2445 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2447 if (INVALID_HANDLE_VALUE == foo) {
2448 errno = b_errno_win32;
2459 if (!CloseHandle((HANDLE)fd)) {
2460 errno = b_errno_win32;
2468 file_write(int fd, const void *data, ssize_t len)
2472 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2473 if (status) return bwrite;
2474 errno = b_errno_win32;
2480 file_read(int fd, void *data, ssize_t len)
2485 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2486 if (status) return bread;
2487 errno = b_errno_win32;
2492 file_seek(int fd, boffset_t offset, int whence)
2496 LONG offset_low = (LONG)offset;
2497 LONG offset_high = (LONG)(offset >> 32);
2501 method = FILE_BEGIN;
2504 method = FILE_CURRENT;
2515 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2516 errno = b_errno_win32;
2519 /* ***FIXME*** I doubt this works right */
2533 * Emulation of mmap and unmmap for tokyo dbm
2535 void *mmap(void *start, size_t length, int prot, int flags,
2536 int fd, off_t offset)
2538 DWORD fm_access = 0;
2539 DWORD mv_access = 0;
2550 if (flags & PROT_WRITE) {
2551 fm_access |= PAGE_READWRITE;
2552 } else if (flags & PROT_READ) {
2553 fm_access |= PAGE_READONLY;
2556 if (flags & PROT_READ) {
2557 mv_access |= FILE_MAP_READ;
2559 if (flags & PROT_WRITE) {
2560 mv_access |= FILE_MAP_WRITE;
2563 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2564 NULL /* security */,
2566 0 /* MaximumSizeHigh */,
2567 0 /* MaximumSizeLow */,
2568 NULL /* name of the file mapping object */);
2570 if (!h || h == INVALID_HANDLE_VALUE) {
2574 mv = MapViewOfFile(h, mv_access,
2580 if (!mv || mv == INVALID_HANDLE_VALUE) {
2587 int munmap(void *start, size_t length)
2592 UnmapViewOfFile(start);
2598 /* syslog function, added by Nicolas Boichat */
2599 void openlog(const char *ident, int option, int facility) {}
2602 /* Log an error message */
2603 void LogErrorMsg(const char *message)
2605 HANDLE eventHandler;
2606 const char *strings[2];
2608 /* Use the OS event logging to log the error */
2609 eventHandler = RegisterEventSource(NULL, "Bacula");
2611 strings[0] = _("\n\nBacula ERROR: ");
2612 strings[1] = message;
2615 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2619 2, /* Number of strings */
2620 0, /* raw data size */
2621 (const char **)strings, /* error strings */
2622 NULL); /* raw data */
2623 DeregisterEventSource(eventHandler);