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;
1171 Dmsg1(100, "Opendir path=%s\n", path);
1172 rval = (_dir *)malloc(sizeof(_dir));
1176 memset (rval, 0, sizeof (_dir));
1178 tspec = (char *)malloc(max_len);
1183 conv_unix_to_win32_path(path, tspec, max_len);
1184 Dmsg1(100, "win32 path=%s\n", tspec);
1186 // add backslash only if there is none yet (think of c:\)
1187 if (tspec[strlen(tspec)-1] != '\\')
1188 bstrncat(tspec, "\\*", max_len);
1190 bstrncat(tspec, "*", max_len);
1194 // convert to wchar_t
1195 if (p_FindFirstFileW) {
1196 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1197 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1199 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1201 free_pool_memory(pwcBuf);
1203 if (rval->dirh != INVALID_HANDLE_VALUE)
1205 } else if (p_FindFirstFileA) {
1206 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1208 if (rval->dirh != INVALID_HANDLE_VALUE)
1213 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1214 path, rval->spec, rval->dirh);
1217 if (rval->dirh == INVALID_HANDLE_VALUE)
1220 if (rval->valid_w) {
1221 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1224 if (rval->valid_a) {
1225 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1237 errno = b_errno_win32;
1244 _dir *dp = (_dir *)dirp;
1245 FindClose(dp->dirh);
1246 free((void *)dp->spec);
1252 typedef struct _WIN32_FIND_DATA {
1253 DWORD dwFileAttributes;
1254 FILETIME ftCreationTime;
1255 FILETIME ftLastAccessTime;
1256 FILETIME ftLastWriteTime;
1257 DWORD nFileSizeHigh;
1261 TCHAR cFileName[MAX_PATH];
1262 TCHAR cAlternateFileName[14];
1263 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1267 copyin(struct dirent &dp, const char *fname)
1271 char *cp = dp.d_name;
1281 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1283 _dir *dp = (_dir *)dirp;
1284 if (dp->valid_w || dp->valid_a) {
1285 entry->d_off = dp->offset;
1289 char szBuf[MAX_PATH_UTF8+1];
1290 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1291 dp->offset += copyin(*entry, szBuf);
1292 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1293 dp->offset += copyin(*entry, dp->data_a.cFileName);
1296 *result = entry; /* return entry address */
1297 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1298 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1300 // Dmsg0(99, "readdir_r !valid\n");
1301 errno = b_errno_win32;
1305 // get next file, try unicode first
1306 if (p_FindNextFileW)
1307 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1308 else if (p_FindNextFileA)
1309 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1311 dp->valid_a = FALSE;
1312 dp->valid_w = FALSE;
1319 * Dotted IP address to network address
1325 inet_aton(const char *a, struct in_addr *inp)
1328 uint32_t acc = 0, tmp = 0;
1331 if (!isdigit(*cp)) { /* first char must be digit */
1332 return 0; /* error */
1336 tmp = (tmp * 10) + (*cp -'0');
1337 } else if (*cp == '.' || *cp == 0) {
1339 return 0; /* error */
1341 acc = (acc << 8) + tmp;
1345 return 0; /* error */
1347 } while (*cp++ != 0);
1348 if (dotc != 4) { /* want 3 .'s plus EOS */
1349 return 0; /* error */
1351 inp->s_addr = htonl(acc); /* store addr in network format */
1356 nanosleep(const struct timespec *req, struct timespec *rem)
1359 rem->tv_sec = rem->tv_nsec = 0;
1360 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1365 init_signals(void terminate(int sig))
1371 init_stack_dump(void)
1378 pathconf(const char *path, int name)
1382 if (strncmp(path, "\\\\?\\", 4) == 0)
1394 WORD wVersionRequested = MAKEWORD( 1, 1);
1397 int err = WSAStartup(wVersionRequested, &wsaData);
1401 printf("Can not start Windows Sockets\n");
1409 static DWORD fill_attribute(DWORD attr, mode_t mode)
1411 Dmsg1(200, " before attr=%lld\n", (uint64_t) attr);
1412 /* Use Bacula mappings define in stat() above */
1413 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1414 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1416 attr |= FILE_ATTRIBUTE_READONLY;
1418 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1419 attr |= FILE_ATTRIBUTE_HIDDEN;
1421 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1423 if (mode & S_IRWXO) { // Other can read/write/execute ?
1424 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1426 attr |= FILE_ATTRIBUTE_SYSTEM;
1428 Dmsg1(200, " after attr=%lld\n", (uint64_t)attr);
1432 int win32_chmod(const char *path, mode_t mode)
1437 Dmsg2(100, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1438 if (p_GetFileAttributesW) {
1439 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1440 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1442 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1443 if (attr != INVALID_FILE_ATTRIBUTES) {
1444 /* Use Bacula mappings define in stat() above */
1445 attr = fill_attribute(attr, mode);
1446 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1448 free_pool_memory(pwszBuf);
1449 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1450 } else if (p_GetFileAttributesA) {
1451 attr = p_GetFileAttributesA(path);
1452 if (attr != INVALID_FILE_ATTRIBUTES) {
1453 attr = fill_attribute(attr, mode);
1454 ret = p_SetFileAttributesA(path, attr);
1456 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1458 Dmsg0(100, "Leave win32_chmod did nothing\n");
1462 const char *err = errorString();
1463 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1464 LocalFree((void *)err);
1465 errno = b_errno_win32;
1473 win32_chdir(const char *dir)
1475 if (p_SetCurrentDirectoryW) {
1476 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1477 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1479 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1481 free_pool_memory(pwszBuf);
1484 errno = b_errno_win32;
1487 } else if (p_SetCurrentDirectoryA) {
1488 if (0 == p_SetCurrentDirectoryA(dir)) {
1489 errno = b_errno_win32;
1500 win32_mkdir(const char *dir)
1502 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1504 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1505 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1507 int n = p_wmkdir((LPCWSTR)pwszBuf);
1508 free_pool_memory(pwszBuf);
1509 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1513 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1519 win32_getcwd(char *buf, int maxlen)
1523 if (p_GetCurrentDirectoryW) {
1524 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1525 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1527 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1529 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1530 free_pool_memory(pwszBuf);
1532 } else if (p_GetCurrentDirectoryA)
1533 n = p_GetCurrentDirectoryA(maxlen, buf);
1535 if (n == 0 || n > maxlen) return NULL;
1537 if (n+1 > maxlen) return NULL;
1546 win32_fputs(const char *string, FILE *stream)
1548 /* we use WriteConsoleA / WriteConsoleA
1549 so we can be sure that unicode support works on win32.
1550 with fallback if something fails
1553 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1554 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1555 p_MultiByteToWideChar && (stream == stdout)) {
1557 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1559 DWORD dwCharsWritten;
1562 dwChars = UTF8_2_wchar(&pwszBuf, string);
1564 /* try WriteConsoleW */
1565 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1566 free_pool_memory(pwszBuf);
1567 return dwCharsWritten;
1570 /* convert to local codepage and try WriteConsoleA */
1571 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1572 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1574 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1575 free_pool_memory(pwszBuf);
1577 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1578 free_pool_memory(pszBuf);
1579 return dwCharsWritten;
1581 free_pool_memory(pszBuf);
1584 return fputs(string, stream);
1588 win32_cgets (char* buffer, int len)
1590 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1591 from the win32 console and fallback if seomething fails */
1593 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1594 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1596 wchar_t wszBuf[1024];
1599 /* nt and unicode conversion */
1600 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1602 /* null terminate at end */
1603 if (wszBuf[dwRead-1] == L'\n') {
1604 wszBuf[dwRead-1] = L'\0';
1608 if (wszBuf[dwRead-1] == L'\r') {
1609 wszBuf[dwRead-1] = L'\0';
1613 wchar_2_UTF8(buffer, wszBuf, len);
1617 /* win 9x and unicode conversion */
1618 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1620 /* null terminate at end */
1621 if (szBuf[dwRead-1] == L'\n') {
1622 szBuf[dwRead-1] = L'\0';
1626 if (szBuf[dwRead-1] == L'\r') {
1627 szBuf[dwRead-1] = L'\0';
1631 /* convert from ansii to wchar_t */
1632 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1633 /* convert from wchar_t to UTF-8 */
1634 if (wchar_2_UTF8(buffer, wszBuf, len))
1640 if (fgets(buffer, len, stdin))
1647 win32_unlink(const char *filename)
1651 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1652 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1654 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1657 * special case if file is readonly,
1658 * we retry but unset attribute before
1660 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1661 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1662 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1663 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1664 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1665 /* reset to original if it didn't help */
1667 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1671 free_pool_memory(pwszBuf);
1673 nRetCode = _unlink(filename);
1675 /* special case if file is readonly,
1676 we retry but unset attribute before */
1677 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1678 DWORD dwAttr = p_GetFileAttributesA(filename);
1679 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1680 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1681 nRetCode = _unlink(filename);
1682 /* reset to original if it didn't help */
1684 p_SetFileAttributesA(filename, dwAttr);
1693 #include "mswinver.h"
1695 char WIN_VERSION_LONG[64];
1696 char WIN_VERSION[32];
1697 char WIN_RAWVERSION[32];
1704 static winver INIT; // cause constructor to be called before main()
1707 winver::winver(void)
1709 const char *version = "";
1710 const char *platform = "";
1711 OSVERSIONINFO osvinfo;
1712 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1714 // Get the current OS version
1715 if (!GetVersionEx(&osvinfo)) {
1716 version = "Unknown";
1717 platform = "Unknown";
1719 const int ver = _mkversion(osvinfo.dwPlatformId,
1720 osvinfo.dwMajorVersion,
1721 osvinfo.dwMinorVersion);
1722 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1725 case MS_WINDOWS_95: (version = "Windows 95"); break;
1726 case MS_WINDOWS_98: (version = "Windows 98"); break;
1727 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1728 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1729 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1730 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1731 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1732 default: version = WIN_RAWVERSION; break;
1735 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1736 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1737 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1740 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1744 BPIPE *b = open_bpipe("ls -l", 10, "r");
1746 while (!feof(b->rfd)) {
1747 fgets(buf, sizeof(buf), b->rfd);
1753 BOOL CreateChildProcess(VOID);
1754 VOID WriteToPipe(VOID);
1755 VOID ReadFromPipe(VOID);
1756 VOID ErrorExit(LPCSTR);
1757 VOID ErrMsg(LPTSTR, BOOL);
1760 * Check for a quoted path, if an absolute path name is given and it contains
1761 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1762 * CreateProcess() says the best way to ensure proper results with executables
1763 * with spaces in path or filename is to quote the string.
1766 getArgv0(const char *cmdline)
1771 for (cp = cmdline; *cp; cp++)
1776 if (!inquote && isspace(*cp))
1781 int len = cp - cmdline;
1782 char *rval = (char *)malloc(len+1);
1795 * Extracts the executable or script name from the first string in
1798 * If the name contains blanks then it must be quoted with double quotes,
1799 * otherwise quotes are optional. If the name contains blanks then it
1800 * will be converted to a short name.
1802 * The optional quotes will be removed. The result is copied to a malloc'ed
1803 * buffer and returned through the pexe argument. The pargs parameter is set
1804 * to the address of the character in cmdline located after the name.
1806 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1809 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1811 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1812 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1814 const char *pBasename = NULL; /* Character after last path separator */
1815 const char *pExtension = NULL; /* Period at start of extension */
1817 const char *current = cmdline;
1819 bool bQuoted = false;
1821 /* Skip initial whitespace */
1823 while (*current == ' ' || *current == '\t')
1828 /* Calculate start of name and determine if quoted */
1830 if (*current == '"') {
1831 pExeStart = ++current;
1834 pExeStart = current;
1842 * Scan command line looking for path separators (/ and \\) and the
1843 * terminator, either a quote or a blank. The location of the
1844 * extension is also noted.
1847 for ( ; *current != '\0'; current++)
1849 if (*current == '.') {
1850 pExtension = current;
1851 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1852 pBasename = ¤t[1];
1856 /* Check for terminator, either quote or blank */
1858 if (*current != '"') {
1862 if (*current != ' ') {
1868 * Hit terminator, remember end of name (address of terminator) and
1869 * start of arguments
1873 if (bQuoted && *current == '"') {
1874 *pargs = ¤t[1];
1882 if (pBasename == NULL) {
1883 pBasename = pExeStart;
1886 if (pExeEnd == NULL) {
1895 bool bHasPathSeparators = pExeStart != pBasename;
1897 /* We have pointers to all the useful parts of the name */
1899 /* Default extensions in the order cmd.exe uses to search */
1901 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1902 DWORD dwBasePathLength = pExeEnd - pExeStart;
1904 DWORD dwAltNameLength = 0;
1905 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1906 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1908 pPathname[MAX_PATHLENGTH] = '\0';
1909 pAltPathname[MAX_PATHLENGTH] = '\0';
1911 memcpy(pPathname, pExeStart, dwBasePathLength);
1912 pPathname[dwBasePathLength] = '\0';
1914 if (pExtension == NULL) {
1915 /* Try appending extensions */
1916 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1918 if (!bHasPathSeparators) {
1919 /* There are no path separators, search in the standard locations */
1920 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1921 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1922 memcpy(pPathname, pAltPathname, dwAltNameLength);
1923 pPathname[dwAltNameLength] = '\0';
1927 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1928 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1931 pPathname[dwBasePathLength] = '\0';
1934 } else if (!bHasPathSeparators) {
1935 /* There are no path separators, search in the standard locations */
1936 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1937 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1938 memcpy(pPathname, pAltPathname, dwAltNameLength);
1939 pPathname[dwAltNameLength] = '\0';
1943 if (strchr(pPathname, ' ') != NULL) {
1944 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1946 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1947 *pexe = (char *)malloc(dwAltNameLength + 1);
1948 if (*pexe == NULL) {
1951 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1955 if (*pexe == NULL) {
1956 DWORD dwPathnameLength = strlen(pPathname);
1957 *pexe = (char *)malloc(dwPathnameLength + 1);
1958 if (*pexe == NULL) {
1961 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1968 * Create the process with WCHAR API
1971 CreateChildProcessW(const char *comspec, const char *cmdLine,
1972 PROCESS_INFORMATION *hProcInfo,
1973 HANDLE in, HANDLE out, HANDLE err)
1975 STARTUPINFOW siStartInfo;
1976 BOOL bFuncRetn = FALSE;
1978 // Set up members of the STARTUPINFO structure.
1979 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1980 siStartInfo.cb = sizeof(siStartInfo);
1981 // setup new process to use supplied handles for stdin,stdout,stderr
1983 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1984 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1986 siStartInfo.hStdInput = in;
1987 siStartInfo.hStdOutput = out;
1988 siStartInfo.hStdError = err;
1990 // Convert argument to WCHAR
1991 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1992 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1994 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1995 UTF8_2_wchar(&comspec_wchar, comspec);
1997 // Create the child process.
1998 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2000 // try to execute program
2001 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2002 (WCHAR*)cmdLine_wchar,// command line
2003 NULL, // process security attributes
2004 NULL, // primary thread security attributes
2005 TRUE, // handles are inherited
2006 0, // creation flags
2007 NULL, // use parent's environment
2008 NULL, // use parent's current directory
2009 &siStartInfo, // STARTUPINFO pointer
2010 hProcInfo); // receives PROCESS_INFORMATION
2011 free_pool_memory(cmdLine_wchar);
2012 free_pool_memory(comspec_wchar);
2019 * Create the process with ANSI API
2022 CreateChildProcessA(const char *comspec, char *cmdLine,
2023 PROCESS_INFORMATION *hProcInfo,
2024 HANDLE in, HANDLE out, HANDLE err)
2026 STARTUPINFOA siStartInfo;
2027 BOOL bFuncRetn = FALSE;
2029 // Set up members of the STARTUPINFO structure.
2030 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2031 siStartInfo.cb = sizeof(siStartInfo);
2032 // setup new process to use supplied handles for stdin,stdout,stderr
2033 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2034 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2036 siStartInfo.hStdInput = in;
2037 siStartInfo.hStdOutput = out;
2038 siStartInfo.hStdError = err;
2040 // Create the child process.
2041 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2043 // try to execute program
2044 bFuncRetn = p_CreateProcessA(comspec,
2045 cmdLine, // command line
2046 NULL, // process security attributes
2047 NULL, // primary thread security attributes
2048 TRUE, // handles are inherited
2049 0, // creation flags
2050 NULL, // use parent's environment
2051 NULL, // use parent's current directory
2052 &siStartInfo,// STARTUPINFO pointer
2053 hProcInfo);// receives PROCESS_INFORMATION
2058 * OK, so it would seem CreateProcess only handles true executables:
2059 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2062 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2064 static const char *comspec = NULL;
2065 PROCESS_INFORMATION piProcInfo;
2066 BOOL bFuncRetn = FALSE;
2068 if (!p_CreateProcessA || !p_CreateProcessW)
2069 return INVALID_HANDLE_VALUE;
2071 if (comspec == NULL)
2072 comspec = getenv("COMSPEC");
2073 if (comspec == NULL) // should never happen
2074 return INVALID_HANDLE_VALUE;
2076 // Set up members of the PROCESS_INFORMATION structure.
2077 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2079 // if supplied handles are not used the send a copy of our STD_HANDLE
2081 if (in == INVALID_HANDLE_VALUE)
2082 in = GetStdHandle(STD_INPUT_HANDLE);
2084 if (out == INVALID_HANDLE_VALUE)
2085 out = GetStdHandle(STD_OUTPUT_HANDLE);
2087 if (err == INVALID_HANDLE_VALUE)
2088 err = GetStdHandle(STD_ERROR_HANDLE);
2091 const char *argStart;
2093 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2094 return INVALID_HANDLE_VALUE;
2097 POOL_MEM cmdLine(PM_FNAME);
2098 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2102 // New function disabled
2103 if (p_CreateProcessW && p_MultiByteToWideChar) {
2104 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2107 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2111 if (bFuncRetn == 0) {
2112 ErrorExit("CreateProcess failed\n");
2113 const char *err = errorString();
2114 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2115 LocalFree((void *)err);
2116 return INVALID_HANDLE_VALUE;
2118 // we don't need a handle on the process primary thread so we close
2120 CloseHandle(piProcInfo.hThread);
2121 return piProcInfo.hProcess;
2125 ErrorExit (LPCSTR lpszMessage)
2127 Dmsg1(0, "%s", lpszMessage);
2132 typedef struct s_bpipe {
2134 time_t worker_stime;
2143 CloseIfValid(HANDLE handle)
2145 if (handle != INVALID_HANDLE_VALUE)
2146 CloseHandle(handle);
2150 open_bpipe(char *prog, int wait, const char *mode)
2152 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2153 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2156 SECURITY_ATTRIBUTES saAttr;
2160 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2161 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2162 hInputFile = INVALID_HANDLE_VALUE;
2164 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2165 memset((void *)bpipe, 0, sizeof(BPIPE));
2167 int mode_read = (mode[0] == 'r');
2168 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2171 // Set the bInheritHandle flag so pipe handles are inherited.
2173 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2174 saAttr.bInheritHandle = TRUE;
2175 saAttr.lpSecurityDescriptor = NULL;
2179 // Create a pipe for the child process's STDOUT.
2180 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2181 ErrorExit("Stdout pipe creation failed\n");
2184 // Create noninheritable read handle and close the inheritable read
2187 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2188 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2190 DUPLICATE_SAME_ACCESS);
2192 ErrorExit("DuplicateHandle failed");
2196 CloseHandle(hChildStdoutRd);
2197 hChildStdoutRd = INVALID_HANDLE_VALUE;
2202 // Create a pipe for the child process's STDIN.
2204 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2205 ErrorExit("Stdin pipe creation failed\n");
2209 // Duplicate the write handle to the pipe so it is not inherited.
2210 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2211 GetCurrentProcess(), &hChildStdinWrDup,
2213 FALSE, // not inherited
2214 DUPLICATE_SAME_ACCESS);
2216 ErrorExit("DuplicateHandle failed");
2220 CloseHandle(hChildStdinWr);
2221 hChildStdinWr = INVALID_HANDLE_VALUE;
2223 // spawn program with redirected handles as appropriate
2224 bpipe->worker_pid = (pid_t)
2225 CreateChildProcess(prog, // commandline
2226 hChildStdinRd, // stdin HANDLE
2227 hChildStdoutWr, // stdout HANDLE
2228 hChildStdoutWr); // stderr HANDLE
2230 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2234 bpipe->worker_stime = time(NULL);
2237 CloseHandle(hChildStdoutWr); // close our write side so when
2238 // process terminates we can
2240 // ugly but convert WIN32 HANDLE to FILE*
2241 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2243 bpipe->rfd = _fdopen(rfd, "rb");
2247 CloseHandle(hChildStdinRd); // close our read side so as not
2248 // to interfre with child's copy
2249 // ugly but convert WIN32 HANDLE to FILE*
2250 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2252 bpipe->wfd = _fdopen(wfd, "wb");
2257 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2264 CloseIfValid(hChildStdoutRd);
2265 CloseIfValid(hChildStdoutRdDup);
2266 CloseIfValid(hChildStdinWr);
2267 CloseIfValid(hChildStdinWrDup);
2269 free((void *) bpipe);
2270 errno = b_errno_win32; /* do GetLastError() for error code */
2276 kill(int pid, int signal)
2279 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2281 errno = b_errno_win32;
2283 CloseHandle((HANDLE)pid);
2289 close_bpipe(BPIPE *bpipe)
2292 int32_t remaining_wait = bpipe->wait;
2304 if (remaining_wait == 0) { /* wait indefinitely */
2305 remaining_wait = INT32_MAX;
2309 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2310 const char *err = errorString();
2311 rval = b_errno_win32;
2312 Dmsg1(0, "GetExitCode error %s\n", err);
2313 LocalFree((void *)err);
2316 if (exitCode == STILL_ACTIVE) {
2317 if (remaining_wait <= 0) {
2318 rval = ETIME; /* timed out */
2321 bmicrosleep(1, 0); /* wait one second */
2323 } else if (exitCode != 0) {
2324 /* Truncate exit code as it doesn't seem to be correct */
2325 rval = (exitCode & 0xFF) | b_errno_exit;
2328 break; /* Shouldn't get here */
2332 if (bpipe->timer_id) {
2333 stop_child_timer(bpipe->timer_id);
2335 if (bpipe->rfd) fclose(bpipe->rfd);
2336 if (bpipe->wfd) fclose(bpipe->wfd);
2337 free((void *)bpipe);
2342 close_wpipe(BPIPE *bpipe)
2348 if (fclose(bpipe->wfd) != 0) {
2358 utime(const char *fname, struct utimbuf *times)
2363 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2365 cvt_utime_to_ftime(times->actime, acc);
2366 cvt_utime_to_ftime(times->modtime, mod);
2368 HANDLE h = INVALID_HANDLE_VALUE;
2370 if (p_CreateFileW) {
2371 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2372 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2374 h = p_CreateFileW((LPCWSTR)pwszBuf,
2375 FILE_WRITE_ATTRIBUTES,
2376 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2379 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2382 free_pool_memory(pwszBuf);
2383 } else if (p_CreateFileA) {
2384 h = p_CreateFileA(tmpbuf,
2385 FILE_WRITE_ATTRIBUTES,
2386 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2389 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2393 if (h == INVALID_HANDLE_VALUE) {
2394 const char *err = errorString();
2395 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2396 LocalFree((void *)err);
2397 errno = b_errno_win32;
2401 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2404 errno = b_errno_win32;
2412 file_open(const char *file, int flags, int mode)
2415 DWORD shareMode = 0;
2418 HANDLE foo = INVALID_HANDLE_VALUE;
2419 const char *remap = file;
2421 if (flags & O_WRONLY) access = GENERIC_WRITE;
2422 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2423 else access = GENERIC_READ;
2425 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2426 create = CREATE_NEW;
2427 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2428 create = CREATE_ALWAYS;
2429 else if (flags & O_CREAT)
2430 create = OPEN_ALWAYS;
2431 else if (flags & O_TRUNC)
2432 create = TRUNCATE_EXISTING;
2434 create = OPEN_EXISTING;
2438 if (flags & O_APPEND) {
2439 printf("open...APPEND not implemented yet.");
2443 if (p_CreateFileW) {
2444 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2445 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2447 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2448 free_pool_memory(pwszBuf);
2449 } else if (p_CreateFileA)
2450 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2452 if (INVALID_HANDLE_VALUE == foo) {
2453 errno = b_errno_win32;
2464 if (!CloseHandle((HANDLE)fd)) {
2465 errno = b_errno_win32;
2473 file_write(int fd, const void *data, ssize_t len)
2477 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2478 if (status) return bwrite;
2479 errno = b_errno_win32;
2485 file_read(int fd, void *data, ssize_t len)
2490 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2491 if (status) return bread;
2492 errno = b_errno_win32;
2497 file_seek(int fd, boffset_t offset, int whence)
2501 LONG offset_low = (LONG)offset;
2502 LONG offset_high = (LONG)(offset >> 32);
2506 method = FILE_BEGIN;
2509 method = FILE_CURRENT;
2520 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2521 errno = b_errno_win32;
2524 /* ***FIXME*** I doubt this works right */
2538 * Emulation of mmap and unmmap for tokyo dbm
2540 void *mmap(void *start, size_t length, int prot, int flags,
2541 int fd, off_t offset)
2543 DWORD fm_access = 0;
2544 DWORD mv_access = 0;
2555 if (flags & PROT_WRITE) {
2556 fm_access |= PAGE_READWRITE;
2557 } else if (flags & PROT_READ) {
2558 fm_access |= PAGE_READONLY;
2561 if (flags & PROT_READ) {
2562 mv_access |= FILE_MAP_READ;
2564 if (flags & PROT_WRITE) {
2565 mv_access |= FILE_MAP_WRITE;
2568 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2569 NULL /* security */,
2571 0 /* MaximumSizeHigh */,
2572 0 /* MaximumSizeLow */,
2573 NULL /* name of the file mapping object */);
2575 if (!h || h == INVALID_HANDLE_VALUE) {
2579 mv = MapViewOfFile(h, mv_access,
2585 if (!mv || mv == INVALID_HANDLE_VALUE) {
2592 int munmap(void *start, size_t length)
2597 UnmapViewOfFile(start);
2603 /* syslog function, added by Nicolas Boichat */
2604 void openlog(const char *ident, int option, int facility) {}
2607 /* Log an error message */
2608 void LogErrorMsg(const char *message)
2610 HANDLE eventHandler;
2611 const char *strings[2];
2613 /* Use the OS event logging to log the error */
2614 eventHandler = RegisterEventSource(NULL, "Bacula");
2616 strings[0] = _("\n\nBacula ERROR: ");
2617 strings[1] = message;
2620 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2624 2, /* Number of strings */
2625 0, /* raw data size */
2626 (const char **)strings, /* error strings */
2627 NULL); /* raw data */
2628 DeregisterEventSource(eventHandler);