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);
1103 /* For apcupsd this is in src/lib/wincompat.c */
1104 extern "C" void syslog(int type, const char *fmt, ...)
1106 /*#ifndef HAVE_CONSOLE
1107 MessageBox(NULL, msg, "Bacula", MB_OK);
1128 // implement opendir/readdir/closedir on top of window's API
1132 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1133 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1134 const char *spec; // the directory we're traversing
1135 HANDLE dirh; // the search handle
1136 BOOL valid_a; // the info in data_a field is valid
1137 BOOL valid_w; // the info in data_w field is valid
1138 UINT32 offset; // pseudo offset for d_off
1142 opendir(const char *path)
1144 /* enough space for VSS !*/
1145 int max_len = strlen(path) + MAX_PATH;
1152 Dmsg1(100, "Opendir path=%s\n", path);
1153 rval = (_dir *)malloc(sizeof(_dir));
1154 memset (rval, 0, sizeof (_dir));
1155 if (rval == NULL) return NULL;
1156 char *tspec = (char *)malloc(max_len);
1157 if (tspec == NULL) return NULL;
1159 conv_unix_to_win32_path(path, tspec, max_len);
1160 Dmsg1(100, "win32 path=%s\n", tspec);
1162 // add backslash only if there is none yet (think of c:\)
1163 if (tspec[strlen(tspec)-1] != '\\')
1164 bstrncat(tspec, "\\*", max_len);
1166 bstrncat(tspec, "*", max_len);
1170 // convert to wchar_t
1171 if (p_FindFirstFileW) {
1172 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1173 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1175 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1177 free_pool_memory(pwcBuf);
1179 if (rval->dirh != INVALID_HANDLE_VALUE)
1181 } else if (p_FindFirstFileA) {
1182 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1184 if (rval->dirh != INVALID_HANDLE_VALUE)
1189 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1190 path, rval->spec, rval->dirh);
1193 if (rval->dirh == INVALID_HANDLE_VALUE)
1196 if (rval->valid_w) {
1197 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1200 if (rval->valid_a) {
1201 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1207 free((void *)rval->spec);
1209 errno = b_errno_win32;
1216 _dir *dp = (_dir *)dirp;
1217 FindClose(dp->dirh);
1218 free((void *)dp->spec);
1224 typedef struct _WIN32_FIND_DATA {
1225 DWORD dwFileAttributes;
1226 FILETIME ftCreationTime;
1227 FILETIME ftLastAccessTime;
1228 FILETIME ftLastWriteTime;
1229 DWORD nFileSizeHigh;
1233 TCHAR cFileName[MAX_PATH];
1234 TCHAR cAlternateFileName[14];
1235 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1239 copyin(struct dirent &dp, const char *fname)
1243 char *cp = dp.d_name;
1253 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1255 _dir *dp = (_dir *)dirp;
1256 if (dp->valid_w || dp->valid_a) {
1257 entry->d_off = dp->offset;
1261 char szBuf[MAX_PATH_UTF8+1];
1262 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1263 dp->offset += copyin(*entry, szBuf);
1264 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1265 dp->offset += copyin(*entry, dp->data_a.cFileName);
1268 *result = entry; /* return entry address */
1269 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1270 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1272 // Dmsg0(99, "readdir_r !valid\n");
1273 errno = b_errno_win32;
1277 // get next file, try unicode first
1278 if (p_FindNextFileW)
1279 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1280 else if (p_FindNextFileA)
1281 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1283 dp->valid_a = FALSE;
1284 dp->valid_w = FALSE;
1291 * Dotted IP address to network address
1297 inet_aton(const char *a, struct in_addr *inp)
1300 uint32_t acc = 0, tmp = 0;
1303 if (!isdigit(*cp)) { /* first char must be digit */
1304 return 0; /* error */
1308 tmp = (tmp * 10) + (*cp -'0');
1309 } else if (*cp == '.' || *cp == 0) {
1311 return 0; /* error */
1313 acc = (acc << 8) + tmp;
1317 return 0; /* error */
1319 } while (*cp++ != 0);
1320 if (dotc != 4) { /* want 3 .'s plus EOS */
1321 return 0; /* error */
1323 inp->s_addr = htonl(acc); /* store addr in network format */
1328 nanosleep(const struct timespec *req, struct timespec *rem)
1331 rem->tv_sec = rem->tv_nsec = 0;
1332 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1337 init_signals(void terminate(int sig))
1343 init_stack_dump(void)
1350 pathconf(const char *path, int name)
1354 if (strncmp(path, "\\\\?\\", 4) == 0)
1366 WORD wVersionRequested = MAKEWORD( 1, 1);
1369 int err = WSAStartup(wVersionRequested, &wsaData);
1373 printf("Can not start Windows Sockets\n");
1381 int win32_chmod(const char *path, mode_t mode)
1383 DWORD attr = (DWORD)-1;
1385 Dmsg1(100, "Enter win32_chmod. path=%s\n", path);
1386 if (p_GetFileAttributesW) {
1387 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1388 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1390 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1391 if (attr != INVALID_FILE_ATTRIBUTES) {
1392 /* Use Bacula mappings define in stat() above */
1393 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1394 attr |= FILE_ATTRIBUTE_READONLY;
1396 attr &= ~FILE_ATTRIBUTE_READONLY;
1398 if (mode & S_ISVTX) {
1399 attr |= FILE_ATTRIBUTE_HIDDEN;
1401 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1403 if (mode & S_IRWXO) {
1404 attr |= FILE_ATTRIBUTE_SYSTEM;
1406 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1408 attr = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1410 free_pool_memory(pwszBuf);
1411 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1412 } else if (p_GetFileAttributesA) {
1413 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1414 attr |= FILE_ATTRIBUTE_READONLY;
1416 attr &= ~FILE_ATTRIBUTE_READONLY;
1418 if (mode & S_ISVTX) {
1419 attr |= FILE_ATTRIBUTE_HIDDEN;
1421 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1423 if (mode & S_IRWXO) {
1424 attr |= FILE_ATTRIBUTE_SYSTEM;
1426 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1428 attr = p_GetFileAttributesA(path);
1429 if (attr != INVALID_FILE_ATTRIBUTES) {
1430 attr = p_SetFileAttributesA(path, attr);
1432 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1434 Dmsg0(100, "Leave win32_chmod did nothing\n");
1438 if (attr == (DWORD)-1) {
1439 const char *err = errorString();
1440 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1441 LocalFree((void *)err);
1442 errno = b_errno_win32;
1450 win32_chdir(const char *dir)
1452 if (p_SetCurrentDirectoryW) {
1453 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1454 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1456 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1458 free_pool_memory(pwszBuf);
1461 errno = b_errno_win32;
1464 } else if (p_SetCurrentDirectoryA) {
1465 if (0 == p_SetCurrentDirectoryA(dir)) {
1466 errno = b_errno_win32;
1477 win32_mkdir(const char *dir)
1479 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1481 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1482 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1484 int n = p_wmkdir((LPCWSTR)pwszBuf);
1485 free_pool_memory(pwszBuf);
1486 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1490 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1496 win32_getcwd(char *buf, int maxlen)
1500 if (p_GetCurrentDirectoryW) {
1501 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1502 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1504 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1506 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1507 free_pool_memory(pwszBuf);
1509 } else if (p_GetCurrentDirectoryA)
1510 n = p_GetCurrentDirectoryA(maxlen, buf);
1512 if (n == 0 || n > maxlen) return NULL;
1514 if (n+1 > maxlen) return NULL;
1523 win32_fputs(const char *string, FILE *stream)
1525 /* we use WriteConsoleA / WriteConsoleA
1526 so we can be sure that unicode support works on win32.
1527 with fallback if something fails
1530 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1531 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1532 p_MultiByteToWideChar && (stream == stdout)) {
1534 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1536 DWORD dwCharsWritten;
1539 dwChars = UTF8_2_wchar(&pwszBuf, string);
1541 /* try WriteConsoleW */
1542 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1543 free_pool_memory(pwszBuf);
1544 return dwCharsWritten;
1547 /* convert to local codepage and try WriteConsoleA */
1548 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1549 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1551 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1552 free_pool_memory(pwszBuf);
1554 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1555 free_pool_memory(pszBuf);
1556 return dwCharsWritten;
1558 free_pool_memory(pszBuf);
1561 return fputs(string, stream);
1565 win32_cgets (char* buffer, int len)
1567 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1568 from the win32 console and fallback if seomething fails */
1570 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1571 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1573 wchar_t wszBuf[1024];
1576 /* nt and unicode conversion */
1577 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1579 /* null terminate at end */
1580 if (wszBuf[dwRead-1] == L'\n') {
1581 wszBuf[dwRead-1] = L'\0';
1585 if (wszBuf[dwRead-1] == L'\r') {
1586 wszBuf[dwRead-1] = L'\0';
1590 wchar_2_UTF8(buffer, wszBuf, len);
1594 /* win 9x and unicode conversion */
1595 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1597 /* null terminate at end */
1598 if (szBuf[dwRead-1] == L'\n') {
1599 szBuf[dwRead-1] = L'\0';
1603 if (szBuf[dwRead-1] == L'\r') {
1604 szBuf[dwRead-1] = L'\0';
1608 /* convert from ansii to wchar_t */
1609 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1610 /* convert from wchar_t to UTF-8 */
1611 if (wchar_2_UTF8(buffer, wszBuf, len))
1617 if (fgets(buffer, len, stdin))
1624 win32_unlink(const char *filename)
1628 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1629 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1631 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1634 * special case if file is readonly,
1635 * we retry but unset attribute before
1637 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1638 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1639 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1640 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1641 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1642 /* reset to original if it didn't help */
1644 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1648 free_pool_memory(pwszBuf);
1650 nRetCode = _unlink(filename);
1652 /* special case if file is readonly,
1653 we retry but unset attribute before */
1654 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1655 DWORD dwAttr = p_GetFileAttributesA(filename);
1656 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1657 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1658 nRetCode = _unlink(filename);
1659 /* reset to original if it didn't help */
1661 p_SetFileAttributesA(filename, dwAttr);
1670 #include "mswinver.h"
1672 char WIN_VERSION_LONG[64];
1673 char WIN_VERSION[32];
1674 char WIN_RAWVERSION[32];
1681 static winver INIT; // cause constructor to be called before main()
1684 winver::winver(void)
1686 const char *version = "";
1687 const char *platform = "";
1688 OSVERSIONINFO osvinfo;
1689 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1691 // Get the current OS version
1692 if (!GetVersionEx(&osvinfo)) {
1693 version = "Unknown";
1694 platform = "Unknown";
1696 const int ver = _mkversion(osvinfo.dwPlatformId,
1697 osvinfo.dwMajorVersion,
1698 osvinfo.dwMinorVersion);
1699 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1702 case MS_WINDOWS_95: (version = "Windows 95"); break;
1703 case MS_WINDOWS_98: (version = "Windows 98"); break;
1704 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1705 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1706 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1707 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1708 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1709 default: version = WIN_RAWVERSION; break;
1712 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1713 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1714 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1717 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1721 BPIPE *b = open_bpipe("ls -l", 10, "r");
1723 while (!feof(b->rfd)) {
1724 fgets(buf, sizeof(buf), b->rfd);
1730 BOOL CreateChildProcess(VOID);
1731 VOID WriteToPipe(VOID);
1732 VOID ReadFromPipe(VOID);
1733 VOID ErrorExit(LPCSTR);
1734 VOID ErrMsg(LPTSTR, BOOL);
1737 * Check for a quoted path, if an absolute path name is given and it contains
1738 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1739 * CreateProcess() says the best way to ensure proper results with executables
1740 * with spaces in path or filename is to quote the string.
1743 getArgv0(const char *cmdline)
1748 for (cp = cmdline; *cp; cp++)
1753 if (!inquote && isspace(*cp))
1758 int len = cp - cmdline;
1759 char *rval = (char *)malloc(len+1);
1772 * Extracts the executable or script name from the first string in
1775 * If the name contains blanks then it must be quoted with double quotes,
1776 * otherwise quotes are optional. If the name contains blanks then it
1777 * will be converted to a short name.
1779 * The optional quotes will be removed. The result is copied to a malloc'ed
1780 * buffer and returned through the pexe argument. The pargs parameter is set
1781 * to the address of the character in cmdline located after the name.
1783 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1786 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1788 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1789 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1791 const char *pBasename = NULL; /* Character after last path separator */
1792 const char *pExtension = NULL; /* Period at start of extension */
1794 const char *current = cmdline;
1796 bool bQuoted = false;
1798 /* Skip initial whitespace */
1800 while (*current == ' ' || *current == '\t')
1805 /* Calculate start of name and determine if quoted */
1807 if (*current == '"') {
1808 pExeStart = ++current;
1811 pExeStart = current;
1819 * Scan command line looking for path separators (/ and \\) and the
1820 * terminator, either a quote or a blank. The location of the
1821 * extension is also noted.
1824 for ( ; *current != '\0'; current++)
1826 if (*current == '.') {
1827 pExtension = current;
1828 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1829 pBasename = ¤t[1];
1833 /* Check for terminator, either quote or blank */
1835 if (*current != '"') {
1839 if (*current != ' ') {
1845 * Hit terminator, remember end of name (address of terminator) and
1846 * start of arguments
1850 if (bQuoted && *current == '"') {
1851 *pargs = ¤t[1];
1859 if (pBasename == NULL) {
1860 pBasename = pExeStart;
1863 if (pExeEnd == NULL) {
1872 bool bHasPathSeparators = pExeStart != pBasename;
1874 /* We have pointers to all the useful parts of the name */
1876 /* Default extensions in the order cmd.exe uses to search */
1878 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1879 DWORD dwBasePathLength = pExeEnd - pExeStart;
1881 DWORD dwAltNameLength = 0;
1882 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1883 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1885 pPathname[MAX_PATHLENGTH] = '\0';
1886 pAltPathname[MAX_PATHLENGTH] = '\0';
1888 memcpy(pPathname, pExeStart, dwBasePathLength);
1889 pPathname[dwBasePathLength] = '\0';
1891 if (pExtension == NULL) {
1892 /* Try appending extensions */
1893 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1895 if (!bHasPathSeparators) {
1896 /* There are no path separators, search in the standard locations */
1897 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1898 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1899 memcpy(pPathname, pAltPathname, dwAltNameLength);
1900 pPathname[dwAltNameLength] = '\0';
1904 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1905 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1908 pPathname[dwBasePathLength] = '\0';
1911 } else if (!bHasPathSeparators) {
1912 /* There are no path separators, search in the standard locations */
1913 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1914 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1915 memcpy(pPathname, pAltPathname, dwAltNameLength);
1916 pPathname[dwAltNameLength] = '\0';
1920 if (strchr(pPathname, ' ') != NULL) {
1921 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1923 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1924 *pexe = (char *)malloc(dwAltNameLength + 1);
1925 if (*pexe == NULL) {
1928 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1932 if (*pexe == NULL) {
1933 DWORD dwPathnameLength = strlen(pPathname);
1934 *pexe = (char *)malloc(dwPathnameLength + 1);
1935 if (*pexe == NULL) {
1938 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1945 * Create the process with WCHAR API
1948 CreateChildProcessW(const char *comspec, const char *cmdLine,
1949 PROCESS_INFORMATION *hProcInfo,
1950 HANDLE in, HANDLE out, HANDLE err)
1952 STARTUPINFOW siStartInfo;
1953 BOOL bFuncRetn = FALSE;
1955 // Set up members of the STARTUPINFO structure.
1956 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1957 siStartInfo.cb = sizeof(siStartInfo);
1958 // setup new process to use supplied handles for stdin,stdout,stderr
1960 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1961 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1963 siStartInfo.hStdInput = in;
1964 siStartInfo.hStdOutput = out;
1965 siStartInfo.hStdError = err;
1967 // Convert argument to WCHAR
1968 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1969 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1971 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1972 UTF8_2_wchar(&comspec_wchar, comspec);
1974 // Create the child process.
1975 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
1977 // try to execute program
1978 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1979 (WCHAR*)cmdLine_wchar,// command line
1980 NULL, // process security attributes
1981 NULL, // primary thread security attributes
1982 TRUE, // handles are inherited
1983 0, // creation flags
1984 NULL, // use parent's environment
1985 NULL, // use parent's current directory
1986 &siStartInfo, // STARTUPINFO pointer
1987 hProcInfo); // receives PROCESS_INFORMATION
1988 free_pool_memory(cmdLine_wchar);
1989 free_pool_memory(comspec_wchar);
1996 * Create the process with ANSI API
1999 CreateChildProcessA(const char *comspec, char *cmdLine,
2000 PROCESS_INFORMATION *hProcInfo,
2001 HANDLE in, HANDLE out, HANDLE err)
2003 STARTUPINFOA siStartInfo;
2004 BOOL bFuncRetn = FALSE;
2006 // Set up members of the STARTUPINFO structure.
2007 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2008 siStartInfo.cb = sizeof(siStartInfo);
2009 // setup new process to use supplied handles for stdin,stdout,stderr
2010 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2011 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2013 siStartInfo.hStdInput = in;
2014 siStartInfo.hStdOutput = out;
2015 siStartInfo.hStdError = err;
2017 // Create the child process.
2018 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2020 // try to execute program
2021 bFuncRetn = p_CreateProcessA(comspec,
2022 cmdLine, // command line
2023 NULL, // process security attributes
2024 NULL, // primary thread security attributes
2025 TRUE, // handles are inherited
2026 0, // creation flags
2027 NULL, // use parent's environment
2028 NULL, // use parent's current directory
2029 &siStartInfo,// STARTUPINFO pointer
2030 hProcInfo);// receives PROCESS_INFORMATION
2035 * OK, so it would seem CreateProcess only handles true executables:
2036 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2039 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2041 static const char *comspec = NULL;
2042 PROCESS_INFORMATION piProcInfo;
2043 BOOL bFuncRetn = FALSE;
2045 if (!p_CreateProcessA || !p_CreateProcessW)
2046 return INVALID_HANDLE_VALUE;
2048 if (comspec == NULL)
2049 comspec = getenv("COMSPEC");
2050 if (comspec == NULL) // should never happen
2051 return INVALID_HANDLE_VALUE;
2053 // Set up members of the PROCESS_INFORMATION structure.
2054 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2056 // if supplied handles are not used the send a copy of our STD_HANDLE
2058 if (in == INVALID_HANDLE_VALUE)
2059 in = GetStdHandle(STD_INPUT_HANDLE);
2061 if (out == INVALID_HANDLE_VALUE)
2062 out = GetStdHandle(STD_OUTPUT_HANDLE);
2064 if (err == INVALID_HANDLE_VALUE)
2065 err = GetStdHandle(STD_ERROR_HANDLE);
2068 const char *argStart;
2070 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2071 return INVALID_HANDLE_VALUE;
2074 POOL_MEM cmdLine(PM_FNAME);
2075 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2079 // New function disabled
2080 if (p_CreateProcessW && p_MultiByteToWideChar) {
2081 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2084 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2088 if (bFuncRetn == 0) {
2089 ErrorExit("CreateProcess failed\n");
2090 const char *err = errorString();
2091 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2092 LocalFree((void *)err);
2093 return INVALID_HANDLE_VALUE;
2095 // we don't need a handle on the process primary thread so we close
2097 CloseHandle(piProcInfo.hThread);
2098 return piProcInfo.hProcess;
2102 ErrorExit (LPCSTR lpszMessage)
2104 Dmsg1(0, "%s", lpszMessage);
2109 typedef struct s_bpipe {
2111 time_t worker_stime;
2120 CloseIfValid(HANDLE handle)
2122 if (handle != INVALID_HANDLE_VALUE)
2123 CloseHandle(handle);
2127 open_bpipe(char *prog, int wait, const char *mode)
2129 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2130 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2133 SECURITY_ATTRIBUTES saAttr;
2137 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2138 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2139 hInputFile = INVALID_HANDLE_VALUE;
2141 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2142 memset((void *)bpipe, 0, sizeof(BPIPE));
2144 int mode_read = (mode[0] == 'r');
2145 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2148 // Set the bInheritHandle flag so pipe handles are inherited.
2150 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2151 saAttr.bInheritHandle = TRUE;
2152 saAttr.lpSecurityDescriptor = NULL;
2156 // Create a pipe for the child process's STDOUT.
2157 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2158 ErrorExit("Stdout pipe creation failed\n");
2161 // Create noninheritable read handle and close the inheritable read
2164 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2165 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2167 DUPLICATE_SAME_ACCESS);
2169 ErrorExit("DuplicateHandle failed");
2173 CloseHandle(hChildStdoutRd);
2174 hChildStdoutRd = INVALID_HANDLE_VALUE;
2179 // Create a pipe for the child process's STDIN.
2181 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2182 ErrorExit("Stdin pipe creation failed\n");
2186 // Duplicate the write handle to the pipe so it is not inherited.
2187 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2188 GetCurrentProcess(), &hChildStdinWrDup,
2190 FALSE, // not inherited
2191 DUPLICATE_SAME_ACCESS);
2193 ErrorExit("DuplicateHandle failed");
2197 CloseHandle(hChildStdinWr);
2198 hChildStdinWr = INVALID_HANDLE_VALUE;
2200 // spawn program with redirected handles as appropriate
2201 bpipe->worker_pid = (pid_t)
2202 CreateChildProcess(prog, // commandline
2203 hChildStdinRd, // stdin HANDLE
2204 hChildStdoutWr, // stdout HANDLE
2205 hChildStdoutWr); // stderr HANDLE
2207 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2211 bpipe->worker_stime = time(NULL);
2214 CloseHandle(hChildStdoutWr); // close our write side so when
2215 // process terminates we can
2217 // ugly but convert WIN32 HANDLE to FILE*
2218 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2220 bpipe->rfd = _fdopen(rfd, "rb");
2224 CloseHandle(hChildStdinRd); // close our read side so as not
2225 // to interfre with child's copy
2226 // ugly but convert WIN32 HANDLE to FILE*
2227 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2229 bpipe->wfd = _fdopen(wfd, "wb");
2234 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2241 CloseIfValid(hChildStdoutRd);
2242 CloseIfValid(hChildStdoutRdDup);
2243 CloseIfValid(hChildStdinWr);
2244 CloseIfValid(hChildStdinWrDup);
2246 free((void *) bpipe);
2247 errno = b_errno_win32; /* do GetLastError() for error code */
2253 kill(int pid, int signal)
2256 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2258 errno = b_errno_win32;
2260 CloseHandle((HANDLE)pid);
2266 close_bpipe(BPIPE *bpipe)
2269 int32_t remaining_wait = bpipe->wait;
2281 if (remaining_wait == 0) { /* wait indefinitely */
2282 remaining_wait = INT32_MAX;
2286 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2287 const char *err = errorString();
2288 rval = b_errno_win32;
2289 Dmsg1(0, "GetExitCode error %s\n", err);
2290 LocalFree((void *)err);
2293 if (exitCode == STILL_ACTIVE) {
2294 if (remaining_wait <= 0) {
2295 rval = ETIME; /* timed out */
2298 bmicrosleep(1, 0); /* wait one second */
2300 } else if (exitCode != 0) {
2301 /* Truncate exit code as it doesn't seem to be correct */
2302 rval = (exitCode & 0xFF) | b_errno_exit;
2305 break; /* Shouldn't get here */
2309 if (bpipe->timer_id) {
2310 stop_child_timer(bpipe->timer_id);
2312 if (bpipe->rfd) fclose(bpipe->rfd);
2313 if (bpipe->wfd) fclose(bpipe->wfd);
2314 free((void *)bpipe);
2319 close_wpipe(BPIPE *bpipe)
2325 if (fclose(bpipe->wfd) != 0) {
2335 utime(const char *fname, struct utimbuf *times)
2340 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2342 cvt_utime_to_ftime(times->actime, acc);
2343 cvt_utime_to_ftime(times->modtime, mod);
2345 HANDLE h = INVALID_HANDLE_VALUE;
2347 if (p_CreateFileW) {
2348 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2349 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2351 h = p_CreateFileW((LPCWSTR)pwszBuf,
2352 FILE_WRITE_ATTRIBUTES,
2353 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2356 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2359 free_pool_memory(pwszBuf);
2360 } else if (p_CreateFileA) {
2361 h = p_CreateFileA(tmpbuf,
2362 FILE_WRITE_ATTRIBUTES,
2363 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2366 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2370 if (h == INVALID_HANDLE_VALUE) {
2371 const char *err = errorString();
2372 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2373 LocalFree((void *)err);
2374 errno = b_errno_win32;
2378 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2381 errno = b_errno_win32;
2389 file_open(const char *file, int flags, int mode)
2392 DWORD shareMode = 0;
2395 HANDLE foo = INVALID_HANDLE_VALUE;
2396 const char *remap = file;
2398 if (flags & O_WRONLY) access = GENERIC_WRITE;
2399 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2400 else access = GENERIC_READ;
2402 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2403 create = CREATE_NEW;
2404 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2405 create = CREATE_ALWAYS;
2406 else if (flags & O_CREAT)
2407 create = OPEN_ALWAYS;
2408 else if (flags & O_TRUNC)
2409 create = TRUNCATE_EXISTING;
2411 create = OPEN_EXISTING;
2415 if (flags & O_APPEND) {
2416 printf("open...APPEND not implemented yet.");
2420 if (p_CreateFileW) {
2421 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2422 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2424 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2425 free_pool_memory(pwszBuf);
2426 } else if (p_CreateFileA)
2427 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2429 if (INVALID_HANDLE_VALUE == foo) {
2430 errno = b_errno_win32;
2441 if (!CloseHandle((HANDLE)fd)) {
2442 errno = b_errno_win32;
2450 file_write(int fd, const void *data, ssize_t len)
2454 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2455 if (status) return bwrite;
2456 errno = b_errno_win32;
2462 file_read(int fd, void *data, ssize_t len)
2467 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2468 if (status) return bread;
2469 errno = b_errno_win32;
2474 file_seek(int fd, boffset_t offset, int whence)
2478 LONG offset_low = (LONG)offset;
2479 LONG offset_high = (LONG)(offset >> 32);
2483 method = FILE_BEGIN;
2486 method = FILE_CURRENT;
2497 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2498 errno = b_errno_win32;
2501 /* ***FIXME*** I doubt this works right */
2515 * Emulation of mmap and unmmap for tokyo dbm
2517 void *mmap(void *start, size_t length, int prot, int flags,
2518 int fd, off_t offset)
2520 DWORD fm_access = 0;
2521 DWORD mv_access = 0;
2532 if (flags & PROT_WRITE) {
2533 fm_access |= PAGE_READWRITE;
2534 } else if (flags & PROT_READ) {
2535 fm_access |= PAGE_READONLY;
2538 if (flags & PROT_READ) {
2539 mv_access |= FILE_MAP_READ;
2541 if (flags & PROT_WRITE) {
2542 mv_access |= FILE_MAP_WRITE;
2545 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2546 NULL /* security */,
2548 0 /* MaximumSizeHigh */,
2549 0 /* MaximumSizeLow */,
2550 NULL /* name of the file mapping object */);
2552 if (!h || h == INVALID_HANDLE_VALUE) {
2556 mv = MapViewOfFile(h, mv_access,
2562 if (!mv || mv == INVALID_HANDLE_VALUE) {
2569 int munmap(void *start, size_t length)
2574 UnmapViewOfFile(start);
2580 /* syslog function, added by Nicolas Boichat */
2581 void openlog(const char *ident, int option, int facility) {}