2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2008 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 = get_pool_memory(PM_FNAME);
55 static POOLMEM *g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
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 void Win32ConvCleanupCache()
74 if (g_pWin32ConvUTF8Cache) {
75 free_pool_memory(g_pWin32ConvUTF8Cache);
76 g_pWin32ConvUTF8Cache = NULL;
79 if (g_pWin32ConvUCS2Cache) {
80 free_pool_memory(g_pWin32ConvUCS2Cache);
81 g_pWin32ConvUCS2Cache = NULL;
84 g_dwWin32ConvUTF8strlen = 0;
88 /* to allow the usage of the original version in this file here */
92 //#define USE_WIN32_COMPAT_IO 1
93 #define USE_WIN32_32KPATHCONVERSION 1
95 extern DWORD g_platform_id;
96 extern DWORD g_MinorVersion;
98 // from MicroSoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970
100 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
102 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
105 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
107 void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
109 const char *fname = name;
110 char *tname = win32_name;
112 Dmsg0(100, "Enter convert_unix_to_win32_path\n");
114 if (IsPathSeparator(name[0]) &&
115 IsPathSeparator(name[1]) &&
117 IsPathSeparator(name[3])) {
119 *win32_name++ = '\\';
120 *win32_name++ = '\\';
122 *win32_name++ = '\\';
125 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
126 g_pVSSPathConvert == NULL) {
127 /* allow path to be 32767 bytes */
128 *win32_name++ = '\\';
129 *win32_name++ = '\\';
131 *win32_name++ = '\\';
135 /* Check for Unix separator and convert to Win32 */
136 if (name[0] == '/' && name[1] == '/') { /* double slash? */
137 name++; /* yes, skip first one */
140 *win32_name++ = '\\'; /* convert char */
141 /* If Win32 separator that is "quoted", remove quote */
142 } else if (*name == '\\' && name[1] == '\\') {
143 *win32_name++ = '\\';
144 name++; /* skip first \ */
146 *win32_name++ = *name; /* copy character */
150 /* Strip any trailing slash, if we stored something */
151 /* but leave "c:\" with backslash (root directory case */
152 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
158 /* here we convert to VSS specific file name which
159 can get longer because VSS will make something like
160 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
161 from c:\bacula\uninstall.exe
163 Dmsg1(100, "path=%s\n", tname);
164 if (g_pVSSPathConvert != NULL) {
165 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
166 pszBuf = check_pool_memory_size(pszBuf, dwSize);
167 bstrncpy(pszBuf, tname, strlen(tname)+1);
168 g_pVSSPathConvert(pszBuf, tname, dwSize);
169 free_pool_memory(pszBuf);
172 Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
175 /* Conversion of a Unix filename to a Win32 filename */
176 void unix_name_to_win32(POOLMEM **win32_name, char *name)
178 /* One extra byte should suffice, but we double it */
179 /* add MAX_PATH bytes for VSS shadow copy name */
180 DWORD dwSize = 2*strlen(name)+MAX_PATH;
181 *win32_name = check_pool_memory_size(*win32_name, dwSize);
182 conv_unix_to_win32_path(name, *win32_name, dwSize);
186 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
188 /* created 02/27/2006 Thorsten Engel
190 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
191 * will complete the input path to an absolue path of the form \\?\c:\path\file
193 * With this trick, it is possible to have 32K characters long paths.
195 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
196 * to a raw windows partition.
199 Dmsg0(100, "Enter wchar_win32_path\n");
201 *pBIsRawPath = FALSE; /* Initialize, set later */
204 if (!p_GetCurrentDirectoryW) {
205 Dmsg0(100, "Leave wchar_win32_path no change \n");
209 wchar_t *name = (wchar_t *)pszUCSPath;
211 /* if it has already the desired form, exit without changes */
212 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
213 Dmsg0(100, "Leave wchar_win32_path no change \n");
217 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
218 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
219 DWORD dwCurDirPathSize = 0;
221 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
222 DWORD dwBufCharsNeeded = (wcslen(name)+7);
223 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
225 /* add \\?\ to support 32K long filepaths
226 it is important to make absolute paths, so we add drive and
227 current path if necessary */
229 BOOL bAddDrive = TRUE;
230 BOOL bAddCurrentPath = TRUE;
231 BOOL bAddPrefix = TRUE;
233 /* does path begin with drive? if yes, it is absolute */
234 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
236 bAddCurrentPath = FALSE;
239 /* is path absolute? */
240 if (IsPathSeparator(name[0]))
241 bAddCurrentPath = FALSE;
243 /* is path relative to itself?, if yes, skip ./ */
244 if (name[0] == '.' && IsPathSeparator(name[1])) {
248 /* is path of form '//./'? */
249 if (IsPathSeparator(name[0]) &&
250 IsPathSeparator(name[1]) &&
252 IsPathSeparator(name[3])) {
254 bAddCurrentPath = FALSE;
261 int nParseOffset = 0;
263 /* add 4 bytes header */
266 wcscpy(pwszBuf, L"\\\\?\\");
269 /* get current path if needed */
270 if (bAddDrive || bAddCurrentPath) {
271 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
272 if (dwCurDirPathSize > 0) {
273 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
274 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
275 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
277 /* we have no info for doing so */
279 bAddCurrentPath = FALSE;
284 /* add drive if needed */
285 if (bAddDrive && !bAddCurrentPath) {
288 if (IsPathSeparator(pwszCurDirBuf[0]) &&
289 IsPathSeparator(pwszCurDirBuf[1]) &&
290 pwszCurDirBuf[2] == '?' &&
291 IsPathSeparator(pwszCurDirBuf[3])) {
292 /* copy drive character */
293 szDrive[0] = pwszCurDirBuf[4];
295 /* copy drive character */
296 szDrive[0] = pwszCurDirBuf[0];
302 wcscat(pwszBuf, szDrive);
306 /* add path if needed */
307 if (bAddCurrentPath) {
308 /* the 1 add. character is for the eventually added backslash */
309 dwBufCharsNeeded += dwCurDirPathSize+1;
310 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
311 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
313 if (IsPathSeparator(pwszCurDirBuf[0]) &&
314 IsPathSeparator(pwszCurDirBuf[1]) &&
315 pwszCurDirBuf[2] == '?' &&
316 IsPathSeparator(pwszCurDirBuf[3])) {
317 /* copy complete string */
318 wcscpy(pwszBuf, pwszCurDirBuf);
321 wcscat(pwszBuf, pwszCurDirBuf);
324 nParseOffset = wcslen((LPCWSTR) pwszBuf);
326 /* check if path ends with backslash, if not, add one */
327 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
328 wcscat(pwszBuf, L"\\");
333 wchar_t *win32_name = &pwszBuf[nParseOffset];
334 wchar_t *name_start = name;
337 /* Check for Unix separator and convert to Win32, eliminating
338 * duplicate separators.
340 if (IsPathSeparator(*name)) {
341 *win32_name++ = '\\'; /* convert char */
343 /* Eliminate consecutive slashes, but not at the start so that
346 if (name_start != name && IsPathSeparator(name[1])) {
350 *win32_name++ = *name; /* copy character */
355 /* null terminate string */
358 /* here we convert to VSS specific file name which
359 * can get longer because VSS will make something like
360 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
361 * from c:\bacula\uninstall.exe
363 if (g_pVSSPathConvertW != NULL) {
364 /* is output buffer large enough? */
365 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
366 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
367 /* create temp. buffer */
368 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
369 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
370 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
375 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
376 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
377 free_pool_memory((POOLMEM *)pszBuf);
380 free_pool_memory(pszUCSPath);
381 free_pool_memory((POOLMEM *)pwszCurDirBuf);
383 Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
384 return (POOLMEM *)pwszBuf;
388 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
390 /* the return value is the number of bytes written to the buffer.
391 The number includes the byte for the null terminator. */
393 if (p_WideCharToMultiByte) {
394 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
403 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
405 /* the return value is the number of wide characters written to the buffer. */
406 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
408 if (p_MultiByteToWideChar) {
409 /* strlen of UTF8 +1 is enough */
410 DWORD cchSize = (strlen(pszUTF)+1);
411 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
413 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
423 wchar_win32_path(const char *name, wchar_t *win32_name)
425 const char *fname = name;
427 /* Check for Unix separator and convert to Win32 */
429 *win32_name++ = '\\'; /* convert char */
430 /* If Win32 separated that is "quoted", remove quote */
431 } else if (*name == '\\' && name[1] == '\\') {
432 *win32_name++ = '\\';
433 name++; /* skip first \ */
435 *win32_name++ = *name; /* copy character */
439 /* Strip any trailing slash, if we stored something */
440 if (*fname != 0 && win32_name[-1] == '\\') {
448 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
451 /* if we find the utf8 string in cache, we use the cached ucs2 version.
452 we compare the stringlength first (quick check) and then compare the content.
454 if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
455 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
456 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
457 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
458 wcscpy((LPWSTR) *pszUCS, (LPWSTR) g_pWin32ConvUCS2Cache);
460 return nBufSize / sizeof (WCHAR);
464 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
465 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
467 #ifdef USE_WIN32_32KPATHCONVERSION
468 /* add \\?\ to support 32K long filepaths */
469 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
472 *pBIsRawPath = FALSE;
476 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
477 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
479 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
480 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+1);
481 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
487 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
494 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
495 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
498 void *dlopen(const char *file, int mode)
502 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
506 void *dlsym(void *handle, const char *name)
509 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
513 int dlclose(void *handle)
515 if (handle && !FreeLibrary((HMODULE)handle)) {
516 errno = b_errno_win32;
517 return 1; /* failed */
524 static char buf[200];
525 const char *err = errorString();
526 bstrncpy(buf, (char *)err, sizeof(buf));
527 LocalFree((void *)err);
531 int fcntl(int fd, int cmd)
536 int chown(const char *k, uid_t, gid_t)
541 int lchown(const char *k, uid_t, gid_t)
553 srandom(unsigned int seed)
557 // /////////////////////////////////////////////////////////////////
558 // convert from Windows concept of time to Unix concept of time
559 // /////////////////////////////////////////////////////////////////
561 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
563 uint64_t mstime = time;
564 mstime *= WIN32_FILETIME_SCALE;
565 mstime += WIN32_FILETIME_ADJUST;
567 #if defined(_MSC_VER)
568 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
570 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
572 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
576 cvt_ftime_to_utime(const FILETIME &time)
578 uint64_t mstime = time.dwHighDateTime;
580 mstime |= time.dwLowDateTime;
582 mstime -= WIN32_FILETIME_ADJUST;
583 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
585 return (time_t) (mstime & 0xffffffff);
588 static const char *errorString(void)
592 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
593 FORMAT_MESSAGE_FROM_SYSTEM |
594 FORMAT_MESSAGE_IGNORE_INSERTS,
597 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
602 /* Strip any \r or \n */
603 char *rval = (char *) lpMsgBuf;
604 char *cp = strchr(rval, '\r');
608 cp = strchr(rval, '\n');
617 statDir(const char *file, struct stat *sb)
619 WIN32_FIND_DATAW info_w; // window's file info
620 WIN32_FIND_DATAA info_a; // window's file info
622 // cache some common vars to make code more transparent
623 DWORD *pdwFileAttributes;
624 DWORD *pnFileSizeHigh;
625 DWORD *pnFileSizeLow;
627 FILETIME *pftLastAccessTime;
628 FILETIME *pftLastWriteTime;
629 FILETIME *pftCreationTime;
632 * Oh, cool, another exception: Microsoft doesn't let us do
633 * FindFile operations on a Drive, so simply fake root attibutes.
635 if (file[1] == ':' && file[2] == 0) {
636 time_t now = time(NULL);
637 Dmsg1(99, "faking ROOT attrs(%s).\n", file);
638 sb->st_mode = S_IFDIR;
639 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
647 HANDLE h = INVALID_HANDLE_VALUE;
650 if (p_FindFirstFileW) {
651 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
652 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
654 Dmsg1(100, "FindFirstFileW=%s\n", file);
655 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
656 free_pool_memory(pwszBuf);
658 pdwFileAttributes = &info_w.dwFileAttributes;
659 pdwReserved0 = &info_w.dwReserved0;
660 pnFileSizeHigh = &info_w.nFileSizeHigh;
661 pnFileSizeLow = &info_w.nFileSizeLow;
662 pftLastAccessTime = &info_w.ftLastAccessTime;
663 pftLastWriteTime = &info_w.ftLastWriteTime;
664 pftCreationTime = &info_w.ftCreationTime;
667 } else if (p_FindFirstFileA) {
668 Dmsg1(100, "FindFirstFileA=%s\n", file);
669 h = p_FindFirstFileA(file, &info_a);
671 pdwFileAttributes = &info_a.dwFileAttributes;
672 pdwReserved0 = &info_a.dwReserved0;
673 pnFileSizeHigh = &info_a.nFileSizeHigh;
674 pnFileSizeLow = &info_a.nFileSizeLow;
675 pftLastAccessTime = &info_a.ftLastAccessTime;
676 pftLastWriteTime = &info_a.ftLastWriteTime;
677 pftCreationTime = &info_a.ftCreationTime;
679 Dmsg0(100, "No findFirstFile A or W found\n");
682 if (h == INVALID_HANDLE_VALUE) {
683 const char *err = errorString();
685 * Note, in creating leading paths, it is normal that
686 * the file does not exist.
688 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
689 LocalFree((void *)err);
690 errno = b_errno_win32;
696 sb->st_mode = 0777; /* start with everything */
697 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
698 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
699 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
700 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
701 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
702 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
703 sb->st_mode |= S_IFDIR;
706 * Store reparse/mount point info in st_rdev. Note a
707 * Win32 reparse point (junction point) is like a link
708 * though it can have many properties (directory link,
709 * soft link, hard link, HSM, ...
710 * A mount point is a reparse point where another volume
711 * is mounted, so it is like a Unix mount point (change of
714 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
715 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
716 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
718 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
721 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
722 sb->st_size = *pnFileSizeHigh;
724 sb->st_size |= *pnFileSizeLow;
725 sb->st_blksize = 4096;
726 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
728 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
729 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
730 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
736 fstat(intptr_t fd, struct stat *sb)
738 BY_HANDLE_FILE_INFORMATION info;
740 if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) {
741 const char *err = errorString();
742 Dmsg1(2099, "GetfileInformationByHandle: %s\n", err);
743 LocalFree((void *)err);
744 errno = b_errno_win32;
748 sb->st_dev = info.dwVolumeSerialNumber;
749 sb->st_ino = info.nFileIndexHigh;
751 sb->st_ino |= info.nFileIndexLow;
752 sb->st_nlink = (short)info.nNumberOfLinks;
753 if (sb->st_nlink > 1) {
754 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
757 sb->st_mode = 0777; /* start with everything */
758 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
759 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
760 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
761 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
762 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
763 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
764 sb->st_mode |= S_IFREG;
766 /* Use st_rdev to store reparse attribute */
767 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
768 sb->st_rdev = WIN32_REPARSE_POINT;
770 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
771 (long long)sb->st_ino);
773 sb->st_size = info.nFileSizeHigh;
775 sb->st_size |= info.nFileSizeLow;
776 sb->st_blksize = 4096;
777 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
778 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
779 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
780 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
786 stat2(const char *file, struct stat *sb)
788 HANDLE h = INVALID_HANDLE_VALUE;
791 conv_unix_to_win32_path(file, tmpbuf, 5000);
793 DWORD attr = (DWORD)-1;
795 if (p_GetFileAttributesW) {
796 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
797 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
799 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
801 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
802 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
804 free_pool_memory(pwszBuf);
805 } else if (p_GetFileAttributesA) {
806 attr = p_GetFileAttributesA(tmpbuf);
807 h = CreateFileA(tmpbuf, GENERIC_READ,
808 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
811 if (attr == (DWORD)-1) {
812 const char *err = errorString();
813 Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err);
814 LocalFree((void *)err);
815 if (h != INVALID_HANDLE_VALUE) {
818 errno = b_errno_win32;
822 if (h == INVALID_HANDLE_VALUE) {
823 const char *err = errorString();
824 Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
825 LocalFree((void *)err);
826 errno = b_errno_win32;
830 rval = fstat((intptr_t)h, sb);
833 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
834 file[1] == ':' && file[2] != 0) {
835 rval = statDir(file, sb);
841 stat(const char *file, struct stat *sb)
843 WIN32_FILE_ATTRIBUTE_DATA data;
846 memset(sb, 0, sizeof(*sb));
848 if (p_GetFileAttributesExW) {
849 /* dynamically allocate enough space for UCS2 filename */
850 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
851 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
853 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
854 free_pool_memory(pwszBuf);
857 return stat2(file, sb);
860 } else if (p_GetFileAttributesExA) {
861 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
862 return stat2(file, sb);
865 return stat2(file, sb);
868 sb->st_mode = 0777; /* start with everything */
869 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
870 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
872 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
873 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
875 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
876 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
878 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
879 sb->st_mode |= S_IFDIR;
881 sb->st_mode |= S_IFREG;
884 /* Use st_rdev to store reparse attribute */
885 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
888 sb->st_size = data.nFileSizeHigh;
890 sb->st_size |= data.nFileSizeLow;
891 sb->st_blksize = 4096;
892 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
893 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
894 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
895 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
898 * If we are not at the root, then to distinguish a reparse
899 * point from a mount point, we must call FindFirstFile() to
900 * get the WIN32_FIND_DATA, which has the bit that indicates
901 * that this directory is a mount point -- aren't Win32 APIs
902 * wonderful? (sarcasm). The code exists in the statDir
905 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
906 file[1] == ':' && file[2] != 0) {
909 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
910 (long long)sb->st_ino, file);
915 * We write our own ftruncate because the one in the
916 * Microsoft library mrcrt.dll does not truncate
917 * files greater than 2GB.
920 int win32_ftruncate(int fd, int64_t length)
922 /* Set point we want to truncate file */
923 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
925 if (pos != (__int64)length) {
926 errno = EACCES; /* truncation failed, get out */
931 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
932 errno = b_errno_win32;
939 int fcntl(int fd, int cmd, long arg)
962 lstat(const char *file, struct stat *sb)
964 return stat(file, sb);
980 execvp(const char *, char *[]) {
1001 waitpid(int, int*, int)
1008 readlink(const char *, char *, int)
1017 strcasecmp(const char *s1, const char *s2)
1019 register int ch1, ch2;
1022 return 0; /* strings are equal if same object. */
1032 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1039 strncasecmp(const char *s1, const char *s2, int len)
1041 register int ch1 = 0, ch2 = 0;
1044 return 0; /* strings are equal if same object. */
1055 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1062 gettimeofday(struct timeval *tv, struct timezone *)
1067 GetSystemTime(&now);
1073 if (!SystemTimeToFileTime(&now, &tmp)) {
1074 errno = b_errno_win32;
1078 int64_t _100nsec = tmp.dwHighDateTime;
1080 _100nsec |= tmp.dwLowDateTime;
1081 _100nsec -= WIN32_FILETIME_ADJUST;
1083 tv->tv_sec = (long)(_100nsec / 10000000);
1084 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1089 /* For apcupsd this is in src/lib/wincompat.c */
1090 extern "C" void syslog(int type, const char *fmt, ...)
1092 /*#ifndef HAVE_CONSOLE
1093 MessageBox(NULL, msg, "Bacula", MB_OK);
1114 // implement opendir/readdir/closedir on top of window's API
1118 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1119 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1120 const char *spec; // the directory we're traversing
1121 HANDLE dirh; // the search handle
1122 BOOL valid_a; // the info in data_a field is valid
1123 BOOL valid_w; // the info in data_w field is valid
1124 UINT32 offset; // pseudo offset for d_off
1128 opendir(const char *path)
1130 /* enough space for VSS !*/
1131 int max_len = strlen(path) + MAX_PATH;
1138 Dmsg1(100, "Opendir path=%s\n", path);
1139 rval = (_dir *)malloc(sizeof(_dir));
1140 memset (rval, 0, sizeof (_dir));
1141 if (rval == NULL) return NULL;
1142 char *tspec = (char *)malloc(max_len);
1143 if (tspec == NULL) return NULL;
1145 conv_unix_to_win32_path(path, tspec, max_len);
1146 Dmsg1(100, "win32 path=%s\n", tspec);
1148 // add backslash only if there is none yet (think of c:\)
1149 if (tspec[strlen(tspec)-1] != '\\')
1150 bstrncat(tspec, "\\*", max_len);
1152 bstrncat(tspec, "*", max_len);
1156 // convert to wchar_t
1157 if (p_FindFirstFileW) {
1158 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1159 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1161 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1163 free_pool_memory(pwcBuf);
1165 if (rval->dirh != INVALID_HANDLE_VALUE)
1167 } else if (p_FindFirstFileA) {
1168 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1170 if (rval->dirh != INVALID_HANDLE_VALUE)
1175 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1176 path, rval->spec, rval->dirh);
1179 if (rval->dirh == INVALID_HANDLE_VALUE)
1182 if (rval->valid_w) {
1183 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1186 if (rval->valid_a) {
1187 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1193 free((void *)rval->spec);
1195 errno = b_errno_win32;
1202 _dir *dp = (_dir *)dirp;
1203 FindClose(dp->dirh);
1204 free((void *)dp->spec);
1210 typedef struct _WIN32_FIND_DATA {
1211 DWORD dwFileAttributes;
1212 FILETIME ftCreationTime;
1213 FILETIME ftLastAccessTime;
1214 FILETIME ftLastWriteTime;
1215 DWORD nFileSizeHigh;
1219 TCHAR cFileName[MAX_PATH];
1220 TCHAR cAlternateFileName[14];
1221 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1225 copyin(struct dirent &dp, const char *fname)
1229 char *cp = dp.d_name;
1239 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1241 _dir *dp = (_dir *)dirp;
1242 if (dp->valid_w || dp->valid_a) {
1243 entry->d_off = dp->offset;
1247 char szBuf[MAX_PATH_UTF8+1];
1248 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1249 dp->offset += copyin(*entry, szBuf);
1250 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1251 dp->offset += copyin(*entry, dp->data_a.cFileName);
1254 *result = entry; /* return entry address */
1255 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1256 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1258 // Dmsg0(99, "readdir_r !valid\n");
1259 errno = b_errno_win32;
1263 // get next file, try unicode first
1264 if (p_FindNextFileW)
1265 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1266 else if (p_FindNextFileA)
1267 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1269 dp->valid_a = FALSE;
1270 dp->valid_w = FALSE;
1277 * Dotted IP address to network address
1283 inet_aton(const char *a, struct in_addr *inp)
1286 uint32_t acc = 0, tmp = 0;
1289 if (!isdigit(*cp)) { /* first char must be digit */
1290 return 0; /* error */
1294 tmp = (tmp * 10) + (*cp -'0');
1295 } else if (*cp == '.' || *cp == 0) {
1297 return 0; /* error */
1299 acc = (acc << 8) + tmp;
1303 return 0; /* error */
1305 } while (*cp++ != 0);
1306 if (dotc != 4) { /* want 3 .'s plus EOS */
1307 return 0; /* error */
1309 inp->s_addr = htonl(acc); /* store addr in network format */
1314 nanosleep(const struct timespec *req, struct timespec *rem)
1317 rem->tv_sec = rem->tv_nsec = 0;
1318 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1323 init_signals(void terminate(int sig))
1329 init_stack_dump(void)
1336 pathconf(const char *path, int name)
1340 if (strncmp(path, "\\\\?\\", 4) == 0)
1352 WORD wVersionRequested = MAKEWORD( 1, 1);
1355 int err = WSAStartup(wVersionRequested, &wsaData);
1359 printf("Can not start Windows Sockets\n");
1367 int win32_chmod(const char *path, mode_t mode)
1369 DWORD attr = (DWORD)-1;
1371 Dmsg1(100, "Enter win32_chmod. path=%s\n", path);
1372 if (p_GetFileAttributesW) {
1373 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1374 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1376 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1377 if (attr != INVALID_FILE_ATTRIBUTES) {
1378 /* Use Bacula mappings define in stat() above */
1379 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1380 attr |= FILE_ATTRIBUTE_READONLY;
1382 attr &= ~FILE_ATTRIBUTE_READONLY;
1384 if (mode & S_ISVTX) {
1385 attr |= FILE_ATTRIBUTE_HIDDEN;
1387 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1389 if (mode & S_IRWXO) {
1390 attr |= FILE_ATTRIBUTE_SYSTEM;
1392 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1394 attr = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1396 free_pool_memory(pwszBuf);
1397 Dmsg0(100, "Leave win32_chmod. AttributesW\n");
1398 } else if (p_GetFileAttributesA) {
1399 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1400 attr |= FILE_ATTRIBUTE_READONLY;
1402 attr &= ~FILE_ATTRIBUTE_READONLY;
1404 if (mode & S_ISVTX) {
1405 attr |= FILE_ATTRIBUTE_HIDDEN;
1407 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1409 if (mode & S_IRWXO) {
1410 attr |= FILE_ATTRIBUTE_SYSTEM;
1412 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1414 attr = p_GetFileAttributesA(path);
1415 if (attr != INVALID_FILE_ATTRIBUTES) {
1416 attr = p_SetFileAttributesA(path, attr);
1418 Dmsg0(100, "Leave win32_chmod did AttributesA\n");
1420 Dmsg0(100, "Leave win32_chmod did nothing\n");
1424 if (attr == (DWORD)-1) {
1425 const char *err = errorString();
1426 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1427 LocalFree((void *)err);
1428 errno = b_errno_win32;
1436 win32_chdir(const char *dir)
1438 if (p_SetCurrentDirectoryW) {
1439 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1440 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1442 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1444 free_pool_memory(pwszBuf);
1447 errno = b_errno_win32;
1450 } else if (p_SetCurrentDirectoryA) {
1451 if (0 == p_SetCurrentDirectoryA(dir)) {
1452 errno = b_errno_win32;
1463 win32_mkdir(const char *dir)
1465 Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir);
1467 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1468 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1470 int n = p_wmkdir((LPCWSTR)pwszBuf);
1471 free_pool_memory(pwszBuf);
1472 Dmsg0(100, "Leave win32_mkdir did wmkdir\n");
1476 Dmsg0(100, "Leave win32_mkdir did _mkdir\n");
1482 win32_getcwd(char *buf, int maxlen)
1486 if (p_GetCurrentDirectoryW) {
1487 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1488 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1490 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1492 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1493 free_pool_memory(pwszBuf);
1495 } else if (p_GetCurrentDirectoryA)
1496 n = p_GetCurrentDirectoryA(maxlen, buf);
1498 if (n == 0 || n > maxlen) return NULL;
1500 if (n+1 > maxlen) return NULL;
1509 win32_fputs(const char *string, FILE *stream)
1511 /* we use WriteConsoleA / WriteConsoleA
1512 so we can be sure that unicode support works on win32.
1513 with fallback if something fails
1516 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1517 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1518 p_MultiByteToWideChar && (stream == stdout)) {
1520 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1522 DWORD dwCharsWritten;
1525 dwChars = UTF8_2_wchar(&pwszBuf, string);
1527 /* try WriteConsoleW */
1528 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1529 free_pool_memory(pwszBuf);
1530 return dwCharsWritten;
1533 /* convert to local codepage and try WriteConsoleA */
1534 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1535 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1537 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1538 free_pool_memory(pwszBuf);
1540 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1541 free_pool_memory(pszBuf);
1542 return dwCharsWritten;
1544 free_pool_memory(pszBuf);
1547 return fputs(string, stream);
1551 win32_cgets (char* buffer, int len)
1553 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1554 from the win32 console and fallback if seomething fails */
1556 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1557 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1559 wchar_t wszBuf[1024];
1562 /* nt and unicode conversion */
1563 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1565 /* null terminate at end */
1566 if (wszBuf[dwRead-1] == L'\n') {
1567 wszBuf[dwRead-1] = L'\0';
1571 if (wszBuf[dwRead-1] == L'\r') {
1572 wszBuf[dwRead-1] = L'\0';
1576 wchar_2_UTF8(buffer, wszBuf, len);
1580 /* win 9x and unicode conversion */
1581 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1583 /* null terminate at end */
1584 if (szBuf[dwRead-1] == L'\n') {
1585 szBuf[dwRead-1] = L'\0';
1589 if (szBuf[dwRead-1] == L'\r') {
1590 szBuf[dwRead-1] = L'\0';
1594 /* convert from ansii to wchar_t */
1595 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1596 /* convert from wchar_t to UTF-8 */
1597 if (wchar_2_UTF8(buffer, wszBuf, len))
1603 if (fgets(buffer, len, stdin))
1610 win32_unlink(const char *filename)
1614 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1615 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1617 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1620 * special case if file is readonly,
1621 * we retry but unset attribute before
1623 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1624 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1625 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1626 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1627 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1628 /* reset to original if it didn't help */
1630 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1634 free_pool_memory(pwszBuf);
1636 nRetCode = _unlink(filename);
1638 /* special case if file is readonly,
1639 we retry but unset attribute before */
1640 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1641 DWORD dwAttr = p_GetFileAttributesA(filename);
1642 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1643 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1644 nRetCode = _unlink(filename);
1645 /* reset to original if it didn't help */
1647 p_SetFileAttributesA(filename, dwAttr);
1656 #include "mswinver.h"
1658 char WIN_VERSION_LONG[64];
1659 char WIN_VERSION[32];
1660 char WIN_RAWVERSION[32];
1667 static winver INIT; // cause constructor to be called before main()
1670 winver::winver(void)
1672 const char *version = "";
1673 const char *platform = "";
1674 OSVERSIONINFO osvinfo;
1675 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1677 // Get the current OS version
1678 if (!GetVersionEx(&osvinfo)) {
1679 version = "Unknown";
1680 platform = "Unknown";
1682 const int ver = _mkversion(osvinfo.dwPlatformId,
1683 osvinfo.dwMajorVersion,
1684 osvinfo.dwMinorVersion);
1685 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1688 case MS_WINDOWS_95: (version = "Windows 95"); break;
1689 case MS_WINDOWS_98: (version = "Windows 98"); break;
1690 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1691 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1692 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1693 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1694 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1695 default: version = WIN_RAWVERSION; break;
1698 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1699 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1700 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1703 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1707 BPIPE *b = open_bpipe("ls -l", 10, "r");
1709 while (!feof(b->rfd)) {
1710 fgets(buf, sizeof(buf), b->rfd);
1716 BOOL CreateChildProcess(VOID);
1717 VOID WriteToPipe(VOID);
1718 VOID ReadFromPipe(VOID);
1719 VOID ErrorExit(LPCSTR);
1720 VOID ErrMsg(LPTSTR, BOOL);
1723 * Check for a quoted path, if an absolute path name is given and it contains
1724 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1725 * CreateProcess() says the best way to ensure proper results with executables
1726 * with spaces in path or filename is to quote the string.
1729 getArgv0(const char *cmdline)
1734 for (cp = cmdline; *cp; cp++)
1739 if (!inquote && isspace(*cp))
1744 int len = cp - cmdline;
1745 char *rval = (char *)malloc(len+1);
1758 * Extracts the executable or script name from the first string in
1761 * If the name contains blanks then it must be quoted with double quotes,
1762 * otherwise quotes are optional. If the name contains blanks then it
1763 * will be converted to a short name.
1765 * The optional quotes will be removed. The result is copied to a malloc'ed
1766 * buffer and returned through the pexe argument. The pargs parameter is set
1767 * to the address of the character in cmdline located after the name.
1769 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1772 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1774 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1775 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1777 const char *pBasename = NULL; /* Character after last path separator */
1778 const char *pExtension = NULL; /* Period at start of extension */
1780 const char *current = cmdline;
1782 bool bQuoted = false;
1784 /* Skip initial whitespace */
1786 while (*current == ' ' || *current == '\t')
1791 /* Calculate start of name and determine if quoted */
1793 if (*current == '"') {
1794 pExeStart = ++current;
1797 pExeStart = current;
1805 * Scan command line looking for path separators (/ and \\) and the
1806 * terminator, either a quote or a blank. The location of the
1807 * extension is also noted.
1810 for ( ; *current != '\0'; current++)
1812 if (*current == '.') {
1813 pExtension = current;
1814 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1815 pBasename = ¤t[1];
1819 /* Check for terminator, either quote or blank */
1821 if (*current != '"') {
1825 if (*current != ' ') {
1831 * Hit terminator, remember end of name (address of terminator) and
1832 * start of arguments
1836 if (bQuoted && *current == '"') {
1837 *pargs = ¤t[1];
1845 if (pBasename == NULL) {
1846 pBasename = pExeStart;
1849 if (pExeEnd == NULL) {
1858 bool bHasPathSeparators = pExeStart != pBasename;
1860 /* We have pointers to all the useful parts of the name */
1862 /* Default extensions in the order cmd.exe uses to search */
1864 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1865 DWORD dwBasePathLength = pExeEnd - pExeStart;
1867 DWORD dwAltNameLength = 0;
1868 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1869 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1871 pPathname[MAX_PATHLENGTH] = '\0';
1872 pAltPathname[MAX_PATHLENGTH] = '\0';
1874 memcpy(pPathname, pExeStart, dwBasePathLength);
1875 pPathname[dwBasePathLength] = '\0';
1877 if (pExtension == NULL) {
1878 /* Try appending extensions */
1879 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1881 if (!bHasPathSeparators) {
1882 /* There are no path separators, search in the standard locations */
1883 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1884 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1885 memcpy(pPathname, pAltPathname, dwAltNameLength);
1886 pPathname[dwAltNameLength] = '\0';
1890 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1891 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1894 pPathname[dwBasePathLength] = '\0';
1897 } else if (!bHasPathSeparators) {
1898 /* There are no path separators, search in the standard locations */
1899 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1900 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1901 memcpy(pPathname, pAltPathname, dwAltNameLength);
1902 pPathname[dwAltNameLength] = '\0';
1906 if (strchr(pPathname, ' ') != NULL) {
1907 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1909 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1910 *pexe = (char *)malloc(dwAltNameLength + 1);
1911 if (*pexe == NULL) {
1914 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1918 if (*pexe == NULL) {
1919 DWORD dwPathnameLength = strlen(pPathname);
1920 *pexe = (char *)malloc(dwPathnameLength + 1);
1921 if (*pexe == NULL) {
1924 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1931 * Create the process with WCHAR API
1934 CreateChildProcessW(const char *comspec, const char *cmdLine,
1935 PROCESS_INFORMATION *hProcInfo,
1936 HANDLE in, HANDLE out, HANDLE err)
1938 STARTUPINFOW siStartInfo;
1939 BOOL bFuncRetn = FALSE;
1941 // Set up members of the STARTUPINFO structure.
1942 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1943 siStartInfo.cb = sizeof(siStartInfo);
1944 // setup new process to use supplied handles for stdin,stdout,stderr
1946 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1947 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1949 siStartInfo.hStdInput = in;
1950 siStartInfo.hStdOutput = out;
1951 siStartInfo.hStdError = err;
1953 // Convert argument to WCHAR
1954 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1955 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1957 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1958 UTF8_2_wchar(&comspec_wchar, comspec);
1960 // Create the child process.
1961 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
1963 // try to execute program
1964 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1965 (WCHAR*)cmdLine_wchar,// command line
1966 NULL, // process security attributes
1967 NULL, // primary thread security attributes
1968 TRUE, // handles are inherited
1969 0, // creation flags
1970 NULL, // use parent's environment
1971 NULL, // use parent's current directory
1972 &siStartInfo, // STARTUPINFO pointer
1973 hProcInfo); // receives PROCESS_INFORMATION
1974 free_pool_memory(cmdLine_wchar);
1975 free_pool_memory(comspec_wchar);
1982 * Create the process with ANSI API
1985 CreateChildProcessA(const char *comspec, char *cmdLine,
1986 PROCESS_INFORMATION *hProcInfo,
1987 HANDLE in, HANDLE out, HANDLE err)
1989 STARTUPINFOA siStartInfo;
1990 BOOL bFuncRetn = FALSE;
1992 // Set up members of the STARTUPINFO structure.
1993 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1994 siStartInfo.cb = sizeof(siStartInfo);
1995 // setup new process to use supplied handles for stdin,stdout,stderr
1996 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1997 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1999 siStartInfo.hStdInput = in;
2000 siStartInfo.hStdOutput = out;
2001 siStartInfo.hStdError = err;
2003 // Create the child process.
2004 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2006 // try to execute program
2007 bFuncRetn = p_CreateProcessA(comspec,
2008 cmdLine, // command line
2009 NULL, // process security attributes
2010 NULL, // primary thread security attributes
2011 TRUE, // handles are inherited
2012 0, // creation flags
2013 NULL, // use parent's environment
2014 NULL, // use parent's current directory
2015 &siStartInfo,// STARTUPINFO pointer
2016 hProcInfo);// receives PROCESS_INFORMATION
2021 * OK, so it would seem CreateProcess only handles true executables:
2022 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2025 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2027 static const char *comspec = NULL;
2028 PROCESS_INFORMATION piProcInfo;
2029 BOOL bFuncRetn = FALSE;
2031 if (!p_CreateProcessA || !p_CreateProcessW)
2032 return INVALID_HANDLE_VALUE;
2034 if (comspec == NULL)
2035 comspec = getenv("COMSPEC");
2036 if (comspec == NULL) // should never happen
2037 return INVALID_HANDLE_VALUE;
2039 // Set up members of the PROCESS_INFORMATION structure.
2040 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2042 // if supplied handles are not used the send a copy of our STD_HANDLE
2044 if (in == INVALID_HANDLE_VALUE)
2045 in = GetStdHandle(STD_INPUT_HANDLE);
2047 if (out == INVALID_HANDLE_VALUE)
2048 out = GetStdHandle(STD_OUTPUT_HANDLE);
2050 if (err == INVALID_HANDLE_VALUE)
2051 err = GetStdHandle(STD_ERROR_HANDLE);
2054 const char *argStart;
2056 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2057 return INVALID_HANDLE_VALUE;
2060 POOL_MEM cmdLine(PM_FNAME);
2061 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2065 // New function disabled
2066 if (p_CreateProcessW && p_MultiByteToWideChar) {
2067 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2070 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2074 if (bFuncRetn == 0) {
2075 ErrorExit("CreateProcess failed\n");
2076 const char *err = errorString();
2077 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2078 LocalFree((void *)err);
2079 return INVALID_HANDLE_VALUE;
2081 // we don't need a handle on the process primary thread so we close
2083 CloseHandle(piProcInfo.hThread);
2084 return piProcInfo.hProcess;
2088 ErrorExit (LPCSTR lpszMessage)
2090 Dmsg1(0, "%s", lpszMessage);
2095 typedef struct s_bpipe {
2097 time_t worker_stime;
2106 CloseIfValid(HANDLE handle)
2108 if (handle != INVALID_HANDLE_VALUE)
2109 CloseHandle(handle);
2113 open_bpipe(char *prog, int wait, const char *mode)
2115 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2116 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2119 SECURITY_ATTRIBUTES saAttr;
2123 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2124 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2125 hInputFile = INVALID_HANDLE_VALUE;
2127 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2128 memset((void *)bpipe, 0, sizeof(BPIPE));
2130 int mode_read = (mode[0] == 'r');
2131 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2134 // Set the bInheritHandle flag so pipe handles are inherited.
2136 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2137 saAttr.bInheritHandle = TRUE;
2138 saAttr.lpSecurityDescriptor = NULL;
2142 // Create a pipe for the child process's STDOUT.
2143 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2144 ErrorExit("Stdout pipe creation failed\n");
2147 // Create noninheritable read handle and close the inheritable read
2150 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2151 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2153 DUPLICATE_SAME_ACCESS);
2155 ErrorExit("DuplicateHandle failed");
2159 CloseHandle(hChildStdoutRd);
2160 hChildStdoutRd = INVALID_HANDLE_VALUE;
2165 // Create a pipe for the child process's STDIN.
2167 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2168 ErrorExit("Stdin pipe creation failed\n");
2172 // Duplicate the write handle to the pipe so it is not inherited.
2173 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2174 GetCurrentProcess(), &hChildStdinWrDup,
2176 FALSE, // not inherited
2177 DUPLICATE_SAME_ACCESS);
2179 ErrorExit("DuplicateHandle failed");
2183 CloseHandle(hChildStdinWr);
2184 hChildStdinWr = INVALID_HANDLE_VALUE;
2186 // spawn program with redirected handles as appropriate
2187 bpipe->worker_pid = (pid_t)
2188 CreateChildProcess(prog, // commandline
2189 hChildStdinRd, // stdin HANDLE
2190 hChildStdoutWr, // stdout HANDLE
2191 hChildStdoutWr); // stderr HANDLE
2193 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2197 bpipe->worker_stime = time(NULL);
2200 CloseHandle(hChildStdoutWr); // close our write side so when
2201 // process terminates we can
2203 // ugly but convert WIN32 HANDLE to FILE*
2204 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2206 bpipe->rfd = _fdopen(rfd, "rb");
2210 CloseHandle(hChildStdinRd); // close our read side so as not
2211 // to interfre with child's copy
2212 // ugly but convert WIN32 HANDLE to FILE*
2213 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2215 bpipe->wfd = _fdopen(wfd, "wb");
2220 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2227 CloseIfValid(hChildStdoutRd);
2228 CloseIfValid(hChildStdoutRdDup);
2229 CloseIfValid(hChildStdinWr);
2230 CloseIfValid(hChildStdinWrDup);
2232 free((void *) bpipe);
2233 errno = b_errno_win32; /* do GetLastError() for error code */
2239 kill(int pid, int signal)
2242 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2244 errno = b_errno_win32;
2246 CloseHandle((HANDLE)pid);
2252 close_bpipe(BPIPE *bpipe)
2255 int32_t remaining_wait = bpipe->wait;
2267 if (remaining_wait == 0) { /* wait indefinitely */
2268 remaining_wait = INT32_MAX;
2272 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2273 const char *err = errorString();
2274 rval = b_errno_win32;
2275 Dmsg1(0, "GetExitCode error %s\n", err);
2276 LocalFree((void *)err);
2279 if (exitCode == STILL_ACTIVE) {
2280 if (remaining_wait <= 0) {
2281 rval = ETIME; /* timed out */
2284 bmicrosleep(1, 0); /* wait one second */
2286 } else if (exitCode != 0) {
2287 /* Truncate exit code as it doesn't seem to be correct */
2288 rval = (exitCode & 0xFF) | b_errno_exit;
2291 break; /* Shouldn't get here */
2295 if (bpipe->timer_id) {
2296 stop_child_timer(bpipe->timer_id);
2298 if (bpipe->rfd) fclose(bpipe->rfd);
2299 if (bpipe->wfd) fclose(bpipe->wfd);
2300 free((void *)bpipe);
2305 close_wpipe(BPIPE *bpipe)
2311 if (fclose(bpipe->wfd) != 0) {
2321 utime(const char *fname, struct utimbuf *times)
2326 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2328 cvt_utime_to_ftime(times->actime, acc);
2329 cvt_utime_to_ftime(times->modtime, mod);
2331 HANDLE h = INVALID_HANDLE_VALUE;
2333 if (p_CreateFileW) {
2334 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2335 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2337 h = p_CreateFileW((LPCWSTR)pwszBuf,
2338 FILE_WRITE_ATTRIBUTES,
2339 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2342 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2345 free_pool_memory(pwszBuf);
2346 } else if (p_CreateFileA) {
2347 h = p_CreateFileA(tmpbuf,
2348 FILE_WRITE_ATTRIBUTES,
2349 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2352 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2356 if (h == INVALID_HANDLE_VALUE) {
2357 const char *err = errorString();
2358 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2359 LocalFree((void *)err);
2360 errno = b_errno_win32;
2364 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2367 errno = b_errno_win32;
2375 file_open(const char *file, int flags, int mode)
2378 DWORD shareMode = 0;
2381 HANDLE foo = INVALID_HANDLE_VALUE;
2382 const char *remap = file;
2384 if (flags & O_WRONLY) access = GENERIC_WRITE;
2385 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2386 else access = GENERIC_READ;
2388 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2389 create = CREATE_NEW;
2390 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2391 create = CREATE_ALWAYS;
2392 else if (flags & O_CREAT)
2393 create = OPEN_ALWAYS;
2394 else if (flags & O_TRUNC)
2395 create = TRUNCATE_EXISTING;
2397 create = OPEN_EXISTING;
2401 if (flags & O_APPEND) {
2402 printf("open...APPEND not implemented yet.");
2406 if (p_CreateFileW) {
2407 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2408 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2410 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2411 free_pool_memory(pwszBuf);
2412 } else if (p_CreateFileA)
2413 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2415 if (INVALID_HANDLE_VALUE == foo) {
2416 errno = b_errno_win32;
2427 if (!CloseHandle((HANDLE)fd)) {
2428 errno = b_errno_win32;
2436 file_write(int fd, const void *data, ssize_t len)
2440 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2441 if (status) return bwrite;
2442 errno = b_errno_win32;
2448 file_read(int fd, void *data, ssize_t len)
2453 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2454 if (status) return bread;
2455 errno = b_errno_win32;
2460 file_seek(int fd, boffset_t offset, int whence)
2464 LONG offset_low = (LONG)offset;
2465 LONG offset_high = (LONG)(offset >> 32);
2469 method = FILE_BEGIN;
2472 method = FILE_CURRENT;
2483 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2484 errno = b_errno_win32;
2487 /* ***FIXME*** I doubt this works right */
2501 * Emulation of mmap and unmmap for tokyo dbm
2503 void *mmap(void *start, size_t length, int prot, int flags,
2504 int fd, off_t offset)
2506 DWORD fm_access = 0;
2507 DWORD mv_access = 0;
2518 if (flags & PROT_WRITE) {
2519 fm_access |= PAGE_READWRITE;
2520 } else if (flags & PROT_READ) {
2521 fm_access |= PAGE_READONLY;
2524 if (flags & PROT_READ) {
2525 mv_access |= FILE_MAP_READ;
2527 if (flags & PROT_WRITE) {
2528 mv_access |= FILE_MAP_WRITE;
2531 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2532 NULL /* security */,
2534 0 /* MaximumSizeHigh */,
2535 0 /* MaximumSizeLow */,
2536 NULL /* name of the file mapping object */);
2538 if (!h || h == INVALID_HANDLE_VALUE) {
2542 mv = MapViewOfFile(h, mv_access,
2548 if (!mv || mv == INVALID_HANDLE_VALUE) {
2555 int munmap(void *start, size_t length)
2560 UnmapViewOfFile(start);
2566 /* syslog function, added by Nicolas Boichat */
2567 void openlog(const char *ident, int option, int facility) {}