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
44 #define b_errno_win32 (1<<29)
46 #define MAX_PATHLENGTH 1024
48 /* UTF-8 to UCS2 path conversion is expensive,
49 so we cache the conversion. During backup the
50 conversion is called 3 times (lstat, attribs, open),
51 by using the cache this is reduced to 1 time */
53 static POOLMEM *g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
54 static POOLMEM *g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
55 static DWORD g_dwWin32ConvUTF8strlen = 0;
56 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
58 static t_pVSSPathConvert g_pVSSPathConvert;
59 static t_pVSSPathConvertW g_pVSSPathConvertW;
61 /* Forward referenced functions */
62 static const char *errorString(void);
65 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
67 g_pVSSPathConvert = pPathConvert;
68 g_pVSSPathConvertW = pPathConvertW;
71 void Win32ConvCleanupCache()
73 if (g_pWin32ConvUTF8Cache) {
74 free_pool_memory(g_pWin32ConvUTF8Cache);
75 g_pWin32ConvUTF8Cache = NULL;
78 if (g_pWin32ConvUCS2Cache) {
79 free_pool_memory(g_pWin32ConvUCS2Cache);
80 g_pWin32ConvUCS2Cache = NULL;
83 g_dwWin32ConvUTF8strlen = 0;
87 /* to allow the usage of the original version in this file here */
91 //#define USE_WIN32_COMPAT_IO 1
92 #define USE_WIN32_32KPATHCONVERSION 1
94 extern DWORD g_platform_id;
95 extern DWORD g_MinorVersion;
97 // from MicroSoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970
99 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
101 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
104 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
106 void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
108 const char *fname = name;
109 char *tname = win32_name;
111 Dmsg0(100, "Enter convert_unix_to_win32_path\n");
113 if (IsPathSeparator(name[0]) &&
114 IsPathSeparator(name[1]) &&
116 IsPathSeparator(name[3])) {
118 *win32_name++ = '\\';
119 *win32_name++ = '\\';
121 *win32_name++ = '\\';
124 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
125 g_pVSSPathConvert == NULL) {
126 /* allow path to be 32767 bytes */
127 *win32_name++ = '\\';
128 *win32_name++ = '\\';
130 *win32_name++ = '\\';
134 /* Check for Unix separator and convert to Win32 */
135 if (name[0] == '/' && name[1] == '/') { /* double slash? */
136 name++; /* yes, skip first one */
139 *win32_name++ = '\\'; /* convert char */
140 /* If Win32 separator that is "quoted", remove quote */
141 } else if (*name == '\\' && name[1] == '\\') {
142 *win32_name++ = '\\';
143 name++; /* skip first \ */
145 *win32_name++ = *name; /* copy character */
149 /* Strip any trailing slash, if we stored something */
150 /* but leave "c:\" with backslash (root directory case */
151 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
157 /* here we convert to VSS specific file name which
158 can get longer because VSS will make something like
159 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
160 from c:\bacula\uninstall.exe
162 Dmsg1(100, "path=%s\n", tname);
163 if (g_pVSSPathConvert != NULL) {
164 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
165 pszBuf = check_pool_memory_size(pszBuf, dwSize);
166 bstrncpy(pszBuf, tname, strlen(tname)+1);
167 g_pVSSPathConvert(pszBuf, tname, dwSize);
168 free_pool_memory(pszBuf);
171 Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
174 /* Conversion of a Unix filename to a Win32 filename */
175 void unix_name_to_win32(POOLMEM **win32_name, char *name)
177 /* One extra byte should suffice, but we double it */
178 /* add MAX_PATH bytes for VSS shadow copy name */
179 DWORD dwSize = 2*strlen(name)+MAX_PATH;
180 *win32_name = check_pool_memory_size(*win32_name, dwSize);
181 conv_unix_to_win32_path(name, *win32_name, dwSize);
185 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
187 /* created 02/27/2006 Thorsten Engel
189 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
190 * will complete the input path to an absolue path of the form \\?\c:\path\file
192 * With this trick, it is possible to have 32K characters long paths.
194 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
195 * to a raw windows partition.
198 Dmsg0(100, "Enter wchar_win32_path\n");
200 *pBIsRawPath = FALSE; /* Initialize, set later */
203 if (!p_GetCurrentDirectoryW) {
204 Dmsg0(100, "Leave wchar_win32_path no change \n");
208 wchar_t *name = (wchar_t *)pszUCSPath;
210 /* if it has already the desired form, exit without changes */
211 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
212 Dmsg0(100, "Leave wchar_win32_path no change \n");
216 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
217 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
218 DWORD dwCurDirPathSize = 0;
220 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
221 DWORD dwBufCharsNeeded = (wcslen(name)+7);
222 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
224 /* add \\?\ to support 32K long filepaths
225 it is important to make absolute paths, so we add drive and
226 current path if necessary */
228 BOOL bAddDrive = TRUE;
229 BOOL bAddCurrentPath = TRUE;
230 BOOL bAddPrefix = TRUE;
232 /* does path begin with drive? if yes, it is absolute */
233 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
235 bAddCurrentPath = FALSE;
238 /* is path absolute? */
239 if (IsPathSeparator(name[0]))
240 bAddCurrentPath = FALSE;
242 /* is path relative to itself?, if yes, skip ./ */
243 if (name[0] == '.' && IsPathSeparator(name[1])) {
247 /* is path of form '//./'? */
248 if (IsPathSeparator(name[0]) &&
249 IsPathSeparator(name[1]) &&
251 IsPathSeparator(name[3])) {
253 bAddCurrentPath = FALSE;
260 int nParseOffset = 0;
262 /* add 4 bytes header */
265 wcscpy(pwszBuf, L"\\\\?\\");
268 /* get current path if needed */
269 if (bAddDrive || bAddCurrentPath) {
270 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
271 if (dwCurDirPathSize > 0) {
272 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
273 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
274 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
276 /* we have no info for doing so */
278 bAddCurrentPath = FALSE;
283 /* add drive if needed */
284 if (bAddDrive && !bAddCurrentPath) {
287 if (IsPathSeparator(pwszCurDirBuf[0]) &&
288 IsPathSeparator(pwszCurDirBuf[1]) &&
289 pwszCurDirBuf[2] == '?' &&
290 IsPathSeparator(pwszCurDirBuf[3])) {
291 /* copy drive character */
292 szDrive[0] = pwszCurDirBuf[4];
294 /* copy drive character */
295 szDrive[0] = pwszCurDirBuf[0];
301 wcscat(pwszBuf, szDrive);
305 /* add path if needed */
306 if (bAddCurrentPath) {
307 /* the 1 add. character is for the eventually added backslash */
308 dwBufCharsNeeded += dwCurDirPathSize+1;
309 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
310 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
312 if (IsPathSeparator(pwszCurDirBuf[0]) &&
313 IsPathSeparator(pwszCurDirBuf[1]) &&
314 pwszCurDirBuf[2] == '?' &&
315 IsPathSeparator(pwszCurDirBuf[3])) {
316 /* copy complete string */
317 wcscpy(pwszBuf, pwszCurDirBuf);
320 wcscat(pwszBuf, pwszCurDirBuf);
323 nParseOffset = wcslen((LPCWSTR) pwszBuf);
325 /* check if path ends with backslash, if not, add one */
326 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
327 wcscat(pwszBuf, L"\\");
332 wchar_t *win32_name = &pwszBuf[nParseOffset];
333 wchar_t *name_start = name;
336 /* Check for Unix separator and convert to Win32, eliminating
337 * duplicate separators.
339 if (IsPathSeparator(*name)) {
340 *win32_name++ = '\\'; /* convert char */
342 /* Eliminate consecutive slashes, but not at the start so that
345 if (name_start != name && IsPathSeparator(name[1])) {
349 *win32_name++ = *name; /* copy character */
354 /* null terminate string */
357 /* here we convert to VSS specific file name which
358 * can get longer because VSS will make something like
359 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
360 * from c:\bacula\uninstall.exe
362 if (g_pVSSPathConvertW != NULL) {
363 /* is output buffer large enough? */
364 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
365 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
366 /* create temp. buffer */
367 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
368 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
369 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
374 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
375 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
376 free_pool_memory((POOLMEM *)pszBuf);
379 free_pool_memory(pszUCSPath);
380 free_pool_memory((POOLMEM *)pwszCurDirBuf);
382 Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
383 return (POOLMEM *)pwszBuf;
387 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
389 /* the return value is the number of bytes written to the buffer.
390 The number includes the byte for the null terminator. */
392 if (p_WideCharToMultiByte) {
393 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
402 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
404 /* the return value is the number of wide characters written to the buffer. */
405 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
407 if (p_MultiByteToWideChar) {
408 /* strlen of UTF8 +1 is enough */
409 DWORD cchSize = (strlen(pszUTF)+1);
410 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
412 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
422 wchar_win32_path(const char *name, wchar_t *win32_name)
424 const char *fname = name;
426 /* Check for Unix separator and convert to Win32 */
428 *win32_name++ = '\\'; /* convert char */
429 /* If Win32 separated that is "quoted", remove quote */
430 } else if (*name == '\\' && name[1] == '\\') {
431 *win32_name++ = '\\';
432 name++; /* skip first \ */
434 *win32_name++ = *name; /* copy character */
438 /* Strip any trailing slash, if we stored something */
439 if (*fname != 0 && win32_name[-1] == '\\') {
447 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
450 /* if we find the utf8 string in cache, we use the cached ucs2 version.
451 we compare the stringlength first (quick check) and then compare the content.
453 if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
454 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
455 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
456 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
457 wcscpy((LPWSTR) *pszUCS, (LPWSTR) g_pWin32ConvUCS2Cache);
459 return nBufSize / sizeof (WCHAR);
463 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
464 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
466 #ifdef USE_WIN32_32KPATHCONVERSION
467 /* add \\?\ to support 32K long filepaths */
468 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
471 *pBIsRawPath = FALSE;
475 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
476 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
478 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
479 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+1);
480 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
486 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
493 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
494 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
497 void *dlopen(const char *file, int mode)
501 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
505 void *dlsym(void *handle, const char *name)
508 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
512 int dlclose(void *handle)
514 if (handle && !FreeLibrary((HMODULE)handle)) {
515 errno = b_errno_win32;
516 return 1; /* failed */
523 static char buf[200];
524 const char *err = errorString();
525 bstrncpy(buf, (char *)err, sizeof(buf));
526 LocalFree((void *)err);
530 int fcntl(int fd, int cmd)
535 int chmod(const char *, mode_t)
540 int chown(const char *k, uid_t, gid_t)
545 int lchown(const char *k, uid_t, gid_t)
557 srandom(unsigned int seed)
561 // /////////////////////////////////////////////////////////////////
562 // convert from Windows concept of time to Unix concept of time
563 // /////////////////////////////////////////////////////////////////
565 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
567 uint64_t mstime = time;
568 mstime *= WIN32_FILETIME_SCALE;
569 mstime += WIN32_FILETIME_ADJUST;
571 #if defined(_MSC_VER)
572 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
574 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
576 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
580 cvt_ftime_to_utime(const FILETIME &time)
582 uint64_t mstime = time.dwHighDateTime;
584 mstime |= time.dwLowDateTime;
586 mstime -= WIN32_FILETIME_ADJUST;
587 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
589 return (time_t) (mstime & 0xffffffff);
592 static const char *errorString(void)
596 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
597 FORMAT_MESSAGE_FROM_SYSTEM |
598 FORMAT_MESSAGE_IGNORE_INSERTS,
601 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
606 /* Strip any \r or \n */
607 char *rval = (char *) lpMsgBuf;
608 char *cp = strchr(rval, '\r');
612 cp = strchr(rval, '\n');
621 statDir(const char *file, struct stat *sb)
623 WIN32_FIND_DATAW info_w; // window's file info
624 WIN32_FIND_DATAA info_a; // window's file info
626 // cache some common vars to make code more transparent
627 DWORD *pdwFileAttributes;
628 DWORD *pnFileSizeHigh;
629 DWORD *pnFileSizeLow;
631 FILETIME *pftLastAccessTime;
632 FILETIME *pftLastWriteTime;
633 FILETIME *pftCreationTime;
636 * Oh, cool, another exception: Microsoft doesn't let us do
637 * FindFile operations on a Drive, so simply fake root attibutes.
639 if (file[1] == ':' && file[2] == 0) {
640 time_t now = time(NULL);
641 Dmsg1(99, "faking ROOT attrs(%s).\n", file);
642 sb->st_mode = S_IFDIR;
643 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
651 HANDLE h = INVALID_HANDLE_VALUE;
654 if (p_FindFirstFileW) {
655 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
656 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
658 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
659 free_pool_memory(pwszBuf);
661 pdwFileAttributes = &info_w.dwFileAttributes;
662 pdwReserved0 = &info_w.dwReserved0;
663 pnFileSizeHigh = &info_w.nFileSizeHigh;
664 pnFileSizeLow = &info_w.nFileSizeLow;
665 pftLastAccessTime = &info_w.ftLastAccessTime;
666 pftLastWriteTime = &info_w.ftLastWriteTime;
667 pftCreationTime = &info_w.ftCreationTime;
670 } else if (p_FindFirstFileA) {
671 h = p_FindFirstFileA(file, &info_a);
673 pdwFileAttributes = &info_a.dwFileAttributes;
674 pdwReserved0 = &info_a.dwReserved0;
675 pnFileSizeHigh = &info_a.nFileSizeHigh;
676 pnFileSizeLow = &info_a.nFileSizeLow;
677 pftLastAccessTime = &info_a.ftLastAccessTime;
678 pftLastWriteTime = &info_a.ftLastWriteTime;
679 pftCreationTime = &info_a.ftCreationTime;
682 if (h == INVALID_HANDLE_VALUE) {
683 const char *err = errorString();
684 Dmsg2(99, "FindFirstFile(%s):%s\n", file, err);
685 LocalFree((void *)err);
686 errno = b_errno_win32;
692 sb->st_mode = 0777; /* start with everything */
693 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
694 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
695 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
696 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
697 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
698 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
699 sb->st_mode |= S_IFDIR;
702 * Store reparse/mount point info in st_rdev. Note a
703 * Win32 reparse point (junction point) is like a link
704 * though it can have many properties (directory link,
705 * soft link, hard link, HSM, ...
706 * A mount point is a reparse point where another volume
707 * is mounted, so it is like a Unix mount point (change of
710 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
711 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
712 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
714 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
717 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
718 sb->st_size = *pnFileSizeHigh;
720 sb->st_size |= *pnFileSizeLow;
721 sb->st_blksize = 4096;
722 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
724 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
725 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
726 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
732 fstat(int fd, struct stat *sb)
734 BY_HANDLE_FILE_INFORMATION info;
736 if (!GetFileInformationByHandle((HANDLE)fd, &info)) {
737 const char *err = errorString();
738 Dmsg1(99, "GetfileInformationByHandle: %s\n", err);
739 LocalFree((void *)err);
740 errno = b_errno_win32;
744 sb->st_dev = info.dwVolumeSerialNumber;
745 sb->st_ino = info.nFileIndexHigh;
747 sb->st_ino |= info.nFileIndexLow;
748 sb->st_nlink = (short)info.nNumberOfLinks;
749 if (sb->st_nlink > 1) {
750 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
753 sb->st_mode = 0777; /* start with everything */
754 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
755 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
756 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
757 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
758 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
759 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
760 sb->st_mode |= S_IFREG;
762 /* Use st_rdev to store reparse attribute */
763 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
764 sb->st_rdev = WIN32_REPARSE_POINT;
766 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
767 (long long)sb->st_ino);
769 sb->st_size = info.nFileSizeHigh;
771 sb->st_size |= info.nFileSizeLow;
772 sb->st_blksize = 4096;
773 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
774 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
775 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
776 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
782 stat2(const char *file, struct stat *sb)
787 conv_unix_to_win32_path(file, tmpbuf, 5000);
789 DWORD attr = (DWORD)-1;
791 if (p_GetFileAttributesW) {
792 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
793 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
795 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
796 free_pool_memory(pwszBuf);
797 } else if (p_GetFileAttributesA) {
798 attr = p_GetFileAttributesA(tmpbuf);
801 if (attr == (DWORD)-1) {
802 const char *err = errorString();
803 Dmsg2(99, "GetFileAttributes(%s): %s\n", tmpbuf, err);
804 LocalFree((void *)err);
805 errno = b_errno_win32;
809 h = CreateFileA(tmpbuf, GENERIC_READ,
810 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
812 if (h == INVALID_HANDLE_VALUE) {
813 const char *err = errorString();
814 Dmsg2(99, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
815 LocalFree((void *)err);
816 errno = b_errno_win32;
820 rval = fstat((int)h, sb);
823 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
824 file[1] == ':' && file[2] != 0) {
832 stat(const char *file, struct stat *sb)
834 WIN32_FILE_ATTRIBUTE_DATA data;
837 memset(sb, 0, sizeof(*sb));
839 if (p_GetFileAttributesExW) {
840 /* dynamically allocate enough space for UCS2 filename */
841 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
842 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
844 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
845 free_pool_memory(pwszBuf);
848 return stat2(file, sb);
851 } else if (p_GetFileAttributesExA) {
852 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
853 return stat2(file, sb);
856 return stat2(file, sb);
859 sb->st_mode = 0777; /* start with everything */
860 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
861 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
863 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
864 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
866 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
867 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
869 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
870 sb->st_mode |= S_IFDIR;
872 sb->st_mode |= S_IFREG;
875 /* Use st_rdev to store reparse attribute */
876 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
879 sb->st_size = data.nFileSizeHigh;
881 sb->st_size |= data.nFileSizeLow;
882 sb->st_blksize = 4096;
883 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
884 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
885 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
886 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
889 * If we are not at the root, then to distinguish a reparse
890 * point from a mount point, we must call FindFirstFile() to
891 * get the WIN32_FIND_DATA, which has the bit that indicates
892 * that this directory is a mount point -- aren't Win32 APIs
893 * wonderful? (sarcasm). The code exists in the statDir
896 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
897 file[1] == ':' && file[2] != 0) {
900 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
901 (long long)sb->st_ino, file);
906 * We write our own ftruncate because the one in the
907 * Microsoft library mrcrt.dll does not truncate
908 * files greater than 2GB.
911 int win32_ftruncate(int fd, int64_t length)
913 /* Set point we want to truncate file */
914 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
916 if (pos != (__int64)length) {
917 errno = EACCES; /* truncation failed, get out */
922 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
923 errno = b_errno_win32;
930 int fcntl(int fd, int cmd, long arg)
953 lstat(const char *file, struct stat *sb)
955 return stat(file, sb);
971 execvp(const char *, char *[]) {
992 waitpid(int, int*, int)
999 readlink(const char *, char *, int)
1008 strcasecmp(const char *s1, const char *s2)
1010 register int ch1, ch2;
1013 return 0; /* strings are equal if same object. */
1023 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1030 strncasecmp(const char *s1, const char *s2, int len)
1032 register int ch1 = 0, ch2 = 0;
1035 return 0; /* strings are equal if same object. */
1046 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1053 gettimeofday(struct timeval *tv, struct timezone *)
1058 GetSystemTime(&now);
1064 if (!SystemTimeToFileTime(&now, &tmp)) {
1065 errno = b_errno_win32;
1069 int64_t _100nsec = tmp.dwHighDateTime;
1071 _100nsec |= tmp.dwLowDateTime;
1072 _100nsec -= WIN32_FILETIME_ADJUST;
1074 tv->tv_sec = (long)(_100nsec / 10000000);
1075 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1080 /* For apcupsd this is in src/lib/wincompat.c */
1081 extern "C" void syslog(int type, const char *fmt, ...)
1083 /*#ifndef HAVE_CONSOLE
1084 MessageBox(NULL, msg, "Bacula", MB_OK);
1105 // implement opendir/readdir/closedir on top of window's API
1109 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1110 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1111 const char *spec; // the directory we're traversing
1112 HANDLE dirh; // the search handle
1113 BOOL valid_a; // the info in data_a field is valid
1114 BOOL valid_w; // the info in data_w field is valid
1115 UINT32 offset; // pseudo offset for d_off
1119 opendir(const char *path)
1121 /* enough space for VSS !*/
1122 int max_len = strlen(path) + MAX_PATH;
1129 Dmsg1(100, "Opendir path=%s\n", path);
1130 rval = (_dir *)malloc(sizeof(_dir));
1131 memset (rval, 0, sizeof (_dir));
1132 if (rval == NULL) return NULL;
1133 char *tspec = (char *)malloc(max_len);
1134 if (tspec == NULL) return NULL;
1136 conv_unix_to_win32_path(path, tspec, max_len);
1137 Dmsg1(100, "win32 path=%s\n", tspec);
1139 // add backslash only if there is none yet (think of c:\)
1140 if (tspec[strlen(tspec)-1] != '\\')
1141 bstrncat(tspec, "\\*", max_len);
1143 bstrncat(tspec, "*", max_len);
1147 // convert to wchar_t
1148 if (p_FindFirstFileW) {
1149 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1150 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1152 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1154 free_pool_memory(pwcBuf);
1156 if (rval->dirh != INVALID_HANDLE_VALUE)
1158 } else if (p_FindFirstFileA) {
1159 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1161 if (rval->dirh != INVALID_HANDLE_VALUE)
1166 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1167 path, rval->spec, rval->dirh);
1170 if (rval->dirh == INVALID_HANDLE_VALUE)
1173 if (rval->valid_w) {
1174 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1177 if (rval->valid_a) {
1178 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1184 free((void *)rval->spec);
1186 errno = b_errno_win32;
1193 _dir *dp = (_dir *)dirp;
1194 FindClose(dp->dirh);
1195 free((void *)dp->spec);
1201 typedef struct _WIN32_FIND_DATA {
1202 DWORD dwFileAttributes;
1203 FILETIME ftCreationTime;
1204 FILETIME ftLastAccessTime;
1205 FILETIME ftLastWriteTime;
1206 DWORD nFileSizeHigh;
1210 TCHAR cFileName[MAX_PATH];
1211 TCHAR cAlternateFileName[14];
1212 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1216 copyin(struct dirent &dp, const char *fname)
1220 char *cp = dp.d_name;
1230 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1232 _dir *dp = (_dir *)dirp;
1233 if (dp->valid_w || dp->valid_a) {
1234 entry->d_off = dp->offset;
1238 char szBuf[MAX_PATH_UTF8+1];
1239 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1240 dp->offset += copyin(*entry, szBuf);
1241 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1242 dp->offset += copyin(*entry, dp->data_a.cFileName);
1245 *result = entry; /* return entry address */
1246 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1247 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1249 // Dmsg0(99, "readdir_r !valid\n");
1250 errno = b_errno_win32;
1254 // get next file, try unicode first
1255 if (p_FindNextFileW)
1256 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1257 else if (p_FindNextFileA)
1258 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1260 dp->valid_a = FALSE;
1261 dp->valid_w = FALSE;
1268 * Dotted IP address to network address
1274 inet_aton(const char *a, struct in_addr *inp)
1277 uint32_t acc = 0, tmp = 0;
1280 if (!isdigit(*cp)) { /* first char must be digit */
1281 return 0; /* error */
1285 tmp = (tmp * 10) + (*cp -'0');
1286 } else if (*cp == '.' || *cp == 0) {
1288 return 0; /* error */
1290 acc = (acc << 8) + tmp;
1294 return 0; /* error */
1296 } while (*cp++ != 0);
1297 if (dotc != 4) { /* want 3 .'s plus EOS */
1298 return 0; /* error */
1300 inp->s_addr = htonl(acc); /* store addr in network format */
1305 nanosleep(const struct timespec *req, struct timespec *rem)
1308 rem->tv_sec = rem->tv_nsec = 0;
1309 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1314 init_signals(void terminate(int sig))
1320 init_stack_dump(void)
1327 pathconf(const char *path, int name)
1331 if (strncmp(path, "\\\\?\\", 4) == 0)
1343 WORD wVersionRequested = MAKEWORD( 1, 1);
1346 int err = WSAStartup(wVersionRequested, &wsaData);
1350 printf("Can not start Windows Sockets\n");
1360 win32_chdir(const char *dir)
1362 if (p_SetCurrentDirectoryW) {
1363 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1364 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1366 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1368 free_pool_memory(pwszBuf);
1371 errno = b_errno_win32;
1375 else if (p_SetCurrentDirectoryA) {
1376 if (0 == p_SetCurrentDirectoryA(dir)) {
1377 errno = b_errno_win32;
1387 win32_mkdir(const char *dir)
1390 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1391 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1393 int n = p_wmkdir((LPCWSTR)pwszBuf);
1394 free_pool_memory(pwszBuf);
1403 win32_getcwd(char *buf, int maxlen)
1407 if (p_GetCurrentDirectoryW) {
1408 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1409 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1411 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1413 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1414 free_pool_memory(pwszBuf);
1416 } else if (p_GetCurrentDirectoryA)
1417 n = p_GetCurrentDirectoryA(maxlen, buf);
1419 if (n == 0 || n > maxlen) return NULL;
1421 if (n+1 > maxlen) return NULL;
1430 win32_fputs(const char *string, FILE *stream)
1432 /* we use WriteConsoleA / WriteConsoleA
1433 so we can be sure that unicode support works on win32.
1434 with fallback if something fails
1437 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1438 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1439 p_MultiByteToWideChar && (stream == stdout)) {
1441 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1443 DWORD dwCharsWritten;
1446 dwChars = UTF8_2_wchar(&pwszBuf, string);
1448 /* try WriteConsoleW */
1449 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1450 free_pool_memory(pwszBuf);
1451 return dwCharsWritten;
1454 /* convert to local codepage and try WriteConsoleA */
1455 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1456 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1458 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1459 free_pool_memory(pwszBuf);
1461 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1462 free_pool_memory(pszBuf);
1463 return dwCharsWritten;
1465 free_pool_memory(pszBuf);
1468 return fputs(string, stream);
1472 win32_cgets (char* buffer, int len)
1474 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1475 from the win32 console and fallback if seomething fails */
1477 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1478 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1480 wchar_t wszBuf[1024];
1483 /* nt and unicode conversion */
1484 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1486 /* null terminate at end */
1487 if (wszBuf[dwRead-1] == L'\n') {
1488 wszBuf[dwRead-1] = L'\0';
1492 if (wszBuf[dwRead-1] == L'\r') {
1493 wszBuf[dwRead-1] = L'\0';
1497 wchar_2_UTF8(buffer, wszBuf, len);
1501 /* win 9x and unicode conversion */
1502 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1504 /* null terminate at end */
1505 if (szBuf[dwRead-1] == L'\n') {
1506 szBuf[dwRead-1] = L'\0';
1510 if (szBuf[dwRead-1] == L'\r') {
1511 szBuf[dwRead-1] = L'\0';
1515 /* convert from ansii to wchar_t */
1516 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1517 /* convert from wchar_t to UTF-8 */
1518 if (wchar_2_UTF8(buffer, wszBuf, len))
1524 if (fgets(buffer, len, stdin))
1531 win32_unlink(const char *filename)
1535 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1536 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1538 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1540 /* special case if file is readonly,
1541 we retry but unset attribute before */
1542 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1543 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1544 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1545 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1546 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1547 /* reset to original if it didn't help */
1549 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1553 free_pool_memory(pwszBuf);
1555 nRetCode = _unlink(filename);
1557 /* special case if file is readonly,
1558 we retry but unset attribute before */
1559 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1560 DWORD dwAttr = p_GetFileAttributesA(filename);
1561 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1562 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1563 nRetCode = _unlink(filename);
1564 /* reset to original if it didn't help */
1566 p_SetFileAttributesA(filename, dwAttr);
1575 #include "mswinver.h"
1577 char WIN_VERSION_LONG[64];
1578 char WIN_VERSION[32];
1579 char WIN_RAWVERSION[32];
1586 static winver INIT; // cause constructor to be called before main()
1589 winver::winver(void)
1591 const char *version = "";
1592 const char *platform = "";
1593 OSVERSIONINFO osvinfo;
1594 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1596 // Get the current OS version
1597 if (!GetVersionEx(&osvinfo)) {
1598 version = "Unknown";
1599 platform = "Unknown";
1601 const int ver = _mkversion(osvinfo.dwPlatformId,
1602 osvinfo.dwMajorVersion,
1603 osvinfo.dwMinorVersion);
1604 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1607 case MS_WINDOWS_95: (version = "Windows 95"); break;
1608 case MS_WINDOWS_98: (version = "Windows 98"); break;
1609 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1610 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1611 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1612 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1613 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1614 default: version = WIN_RAWVERSION; break;
1617 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1618 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1619 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1622 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1626 BPIPE *b = open_bpipe("ls -l", 10, "r");
1628 while (!feof(b->rfd)) {
1629 fgets(buf, sizeof(buf), b->rfd);
1635 BOOL CreateChildProcess(VOID);
1636 VOID WriteToPipe(VOID);
1637 VOID ReadFromPipe(VOID);
1638 VOID ErrorExit(LPCSTR);
1639 VOID ErrMsg(LPTSTR, BOOL);
1642 * Check for a quoted path, if an absolute path name is given and it contains
1643 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1644 * CreateProcess() says the best way to ensure proper results with executables
1645 * with spaces in path or filename is to quote the string.
1648 getArgv0(const char *cmdline)
1653 for (cp = cmdline; *cp; cp++)
1658 if (!inquote && isspace(*cp))
1663 int len = cp - cmdline;
1664 char *rval = (char *)malloc(len+1);
1677 * Extracts the executable or script name from the first string in
1680 * If the name contains blanks then it must be quoted with double quotes,
1681 * otherwise quotes are optional. If the name contains blanks then it
1682 * will be converted to a short name.
1684 * The optional quotes will be removed. The result is copied to a malloc'ed
1685 * buffer and returned through the pexe argument. The pargs parameter is set
1686 * to the address of the character in cmdline located after the name.
1688 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1691 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1693 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1694 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1696 const char *pBasename = NULL; /* Character after last path separator */
1697 const char *pExtension = NULL; /* Period at start of extension */
1699 const char *current = cmdline;
1701 bool bQuoted = false;
1703 /* Skip initial whitespace */
1705 while (*current == ' ' || *current == '\t')
1710 /* Calculate start of name and determine if quoted */
1712 if (*current == '"') {
1713 pExeStart = ++current;
1716 pExeStart = current;
1724 * Scan command line looking for path separators (/ and \\) and the
1725 * terminator, either a quote or a blank. The location of the
1726 * extension is also noted.
1729 for ( ; *current != '\0'; current++)
1731 if (*current == '.') {
1732 pExtension = current;
1733 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1734 pBasename = ¤t[1];
1738 /* Check for terminator, either quote or blank */
1740 if (*current != '"') {
1744 if (*current != ' ') {
1750 * Hit terminator, remember end of name (address of terminator) and
1751 * start of arguments
1755 if (bQuoted && *current == '"') {
1756 *pargs = ¤t[1];
1764 if (pBasename == NULL) {
1765 pBasename = pExeStart;
1768 if (pExeEnd == NULL) {
1777 bool bHasPathSeparators = pExeStart != pBasename;
1779 /* We have pointers to all the useful parts of the name */
1781 /* Default extensions in the order cmd.exe uses to search */
1783 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1784 DWORD dwBasePathLength = pExeEnd - pExeStart;
1786 DWORD dwAltNameLength = 0;
1787 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1788 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1790 pPathname[MAX_PATHLENGTH] = '\0';
1791 pAltPathname[MAX_PATHLENGTH] = '\0';
1793 memcpy(pPathname, pExeStart, dwBasePathLength);
1794 pPathname[dwBasePathLength] = '\0';
1796 if (pExtension == NULL) {
1797 /* Try appending extensions */
1798 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1800 if (!bHasPathSeparators) {
1801 /* There are no path separators, search in the standard locations */
1802 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1803 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1804 memcpy(pPathname, pAltPathname, dwAltNameLength);
1805 pPathname[dwAltNameLength] = '\0';
1809 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1810 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1813 pPathname[dwBasePathLength] = '\0';
1816 } else if (!bHasPathSeparators) {
1817 /* There are no path separators, search in the standard locations */
1818 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1819 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1820 memcpy(pPathname, pAltPathname, dwAltNameLength);
1821 pPathname[dwAltNameLength] = '\0';
1825 if (strchr(pPathname, ' ') != NULL) {
1826 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1828 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1829 *pexe = (char *)malloc(dwAltNameLength + 1);
1830 if (*pexe == NULL) {
1833 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1837 if (*pexe == NULL) {
1838 DWORD dwPathnameLength = strlen(pPathname);
1839 *pexe = (char *)malloc(dwPathnameLength + 1);
1840 if (*pexe == NULL) {
1843 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1850 * Create the process with UTF8 API
1853 CreateChildProcessW(const char *comspec, const char *cmdLine,
1854 PROCESS_INFORMATION *hProcInfo,
1855 HANDLE in, HANDLE out, HANDLE err)
1857 STARTUPINFOW siStartInfo;
1858 BOOL bFuncRetn = FALSE;
1860 // Set up members of the STARTUPINFO structure.
1861 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1862 siStartInfo.cb = sizeof(siStartInfo);
1863 // setup new process to use supplied handles for stdin,stdout,stderr
1865 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1866 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1868 siStartInfo.hStdInput = in;
1869 siStartInfo.hStdOutput = out;
1870 siStartInfo.hStdError = err;
1872 // Convert argument to WCHAR
1873 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1874 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1876 make_win32_path_UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1877 make_win32_path_UTF8_2_wchar(&comspec_wchar, comspec);
1879 // Create the child process.
1880 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
1882 // try to execute program
1883 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1884 (WCHAR*)cmdLine_wchar,// command line
1885 NULL, // process security attributes
1886 NULL, // primary thread security attributes
1887 TRUE, // handles are inherited
1888 0, // creation flags
1889 NULL, // use parent's environment
1890 NULL, // use parent's current directory
1891 &siStartInfo, // STARTUPINFO pointer
1892 hProcInfo); // receives PROCESS_INFORMATION
1894 free_pool_memory(cmdLine_wchar);
1895 free_pool_memory(comspec_wchar);
1902 * Create the process with ANSI API
1905 CreateChildProcessA(const char *comspec, char *cmdLine,
1906 PROCESS_INFORMATION *hProcInfo,
1907 HANDLE in, HANDLE out, HANDLE err)
1909 STARTUPINFOA siStartInfo;
1910 BOOL bFuncRetn = FALSE;
1912 // Set up members of the STARTUPINFO structure.
1913 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1914 siStartInfo.cb = sizeof(siStartInfo);
1915 // setup new process to use supplied handles for stdin,stdout,stderr
1916 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1917 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1919 siStartInfo.hStdInput = in;
1920 siStartInfo.hStdOutput = out;
1921 siStartInfo.hStdError = err;
1923 // Create the child process.
1924 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
1926 // try to execute program
1927 bFuncRetn = p_CreateProcessA(comspec,
1928 cmdLine, // command line
1929 NULL, // process security attributes
1930 NULL, // primary thread security attributes
1931 TRUE, // handles are inherited
1932 0, // creation flags
1933 NULL, // use parent's environment
1934 NULL, // use parent's current directory
1935 &siStartInfo,// STARTUPINFO pointer
1936 hProcInfo);// receives PROCESS_INFORMATION
1941 * OK, so it would seem CreateProcess only handles true executables:
1942 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
1945 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
1947 static const char *comspec = NULL;
1948 PROCESS_INFORMATION piProcInfo;
1949 BOOL bFuncRetn = FALSE;
1951 if (!p_CreateProcessA || !p_CreateProcessW)
1952 return INVALID_HANDLE_VALUE;
1954 if (comspec == NULL)
1955 comspec = getenv("COMSPEC");
1956 if (comspec == NULL) // should never happen
1957 return INVALID_HANDLE_VALUE;
1959 // Set up members of the PROCESS_INFORMATION structure.
1960 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
1962 // if supplied handles are not used the send a copy of our STD_HANDLE
1964 if (in == INVALID_HANDLE_VALUE)
1965 in = GetStdHandle(STD_INPUT_HANDLE);
1967 if (out == INVALID_HANDLE_VALUE)
1968 out = GetStdHandle(STD_OUTPUT_HANDLE);
1970 if (err == INVALID_HANDLE_VALUE)
1971 err = GetStdHandle(STD_ERROR_HANDLE);
1974 const char *argStart;
1976 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
1977 return INVALID_HANDLE_VALUE;
1980 POOL_MEM cmdLine(PM_FNAME);
1981 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
1985 // New function disabled
1986 if (false && p_CreateProcessW && p_MultiByteToWideChar) {
1987 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
1990 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
1994 if (bFuncRetn == 0) {
1995 ErrorExit("CreateProcess failed\n");
1996 const char *err = errorString();
1997 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
1998 LocalFree((void *)err);
1999 return INVALID_HANDLE_VALUE;
2001 // we don't need a handle on the process primary thread so we close
2003 CloseHandle(piProcInfo.hThread);
2004 return piProcInfo.hProcess;
2008 ErrorExit (LPCSTR lpszMessage)
2010 Dmsg1(0, "%s", lpszMessage);
2015 typedef struct s_bpipe {
2017 time_t worker_stime;
2026 CloseIfValid(HANDLE handle)
2028 if (handle != INVALID_HANDLE_VALUE)
2029 CloseHandle(handle);
2033 open_bpipe(char *prog, int wait, const char *mode)
2035 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2036 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2039 SECURITY_ATTRIBUTES saAttr;
2043 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2044 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2045 hInputFile = INVALID_HANDLE_VALUE;
2047 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2048 memset((void *)bpipe, 0, sizeof(BPIPE));
2050 int mode_read = (mode[0] == 'r');
2051 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2054 // Set the bInheritHandle flag so pipe handles are inherited.
2056 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2057 saAttr.bInheritHandle = TRUE;
2058 saAttr.lpSecurityDescriptor = NULL;
2062 // Create a pipe for the child process's STDOUT.
2063 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2064 ErrorExit("Stdout pipe creation failed\n");
2067 // Create noninheritable read handle and close the inheritable read
2070 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2071 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2073 DUPLICATE_SAME_ACCESS);
2075 ErrorExit("DuplicateHandle failed");
2079 CloseHandle(hChildStdoutRd);
2080 hChildStdoutRd = INVALID_HANDLE_VALUE;
2085 // Create a pipe for the child process's STDIN.
2087 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2088 ErrorExit("Stdin pipe creation failed\n");
2092 // Duplicate the write handle to the pipe so it is not inherited.
2093 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2094 GetCurrentProcess(), &hChildStdinWrDup,
2096 FALSE, // not inherited
2097 DUPLICATE_SAME_ACCESS);
2099 ErrorExit("DuplicateHandle failed");
2103 CloseHandle(hChildStdinWr);
2104 hChildStdinWr = INVALID_HANDLE_VALUE;
2106 // spawn program with redirected handles as appropriate
2107 bpipe->worker_pid = (pid_t)
2108 CreateChildProcess(prog, // commandline
2109 hChildStdinRd, // stdin HANDLE
2110 hChildStdoutWr, // stdout HANDLE
2111 hChildStdoutWr); // stderr HANDLE
2113 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2117 bpipe->worker_stime = time(NULL);
2120 CloseHandle(hChildStdoutWr); // close our write side so when
2121 // process terminates we can
2123 // ugly but convert WIN32 HANDLE to FILE*
2124 int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2126 bpipe->rfd = _fdopen(rfd, "rb");
2130 CloseHandle(hChildStdinRd); // close our read side so as not
2131 // to interfre with child's copy
2132 // ugly but convert WIN32 HANDLE to FILE*
2133 int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY | O_BINARY);
2135 bpipe->wfd = _fdopen(wfd, "wb");
2140 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2147 CloseIfValid(hChildStdoutRd);
2148 CloseIfValid(hChildStdoutRdDup);
2149 CloseIfValid(hChildStdinWr);
2150 CloseIfValid(hChildStdinWrDup);
2152 free((void *) bpipe);
2153 errno = b_errno_win32; /* do GetLastError() for error code */
2159 kill(int pid, int signal)
2162 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2164 errno = b_errno_win32;
2166 CloseHandle((HANDLE)pid);
2172 close_bpipe(BPIPE *bpipe)
2175 int32_t remaining_wait = bpipe->wait;
2187 if (remaining_wait == 0) { /* wait indefinitely */
2188 remaining_wait = INT32_MAX;
2192 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2193 const char *err = errorString();
2194 rval = b_errno_win32;
2195 Dmsg1(0, "GetExitCode error %s\n", err);
2196 LocalFree((void *)err);
2199 if (exitCode == STILL_ACTIVE) {
2200 if (remaining_wait <= 0) {
2201 rval = ETIME; /* timed out */
2204 bmicrosleep(1, 0); /* wait one second */
2206 } else if (exitCode != 0) {
2207 /* Truncate exit code as it doesn't seem to be correct */
2208 rval = (exitCode & 0xFF) | b_errno_exit;
2211 break; /* Shouldn't get here */
2215 if (bpipe->timer_id) {
2216 stop_child_timer(bpipe->timer_id);
2218 if (bpipe->rfd) fclose(bpipe->rfd);
2219 if (bpipe->wfd) fclose(bpipe->wfd);
2220 free((void *)bpipe);
2225 close_wpipe(BPIPE *bpipe)
2231 if (fclose(bpipe->wfd) != 0) {
2239 #include "findlib/find.h"
2242 utime(const char *fname, struct utimbuf *times)
2247 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2249 cvt_utime_to_ftime(times->actime, acc);
2250 cvt_utime_to_ftime(times->modtime, mod);
2252 HANDLE h = INVALID_HANDLE_VALUE;
2254 if (p_CreateFileW) {
2255 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2256 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2258 h = p_CreateFileW((LPCWSTR)pwszBuf,
2259 FILE_WRITE_ATTRIBUTES,
2260 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2263 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2266 free_pool_memory(pwszBuf);
2267 } else if (p_CreateFileA) {
2268 h = p_CreateFileA(tmpbuf,
2269 FILE_WRITE_ATTRIBUTES,
2270 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2273 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2277 if (h == INVALID_HANDLE_VALUE) {
2278 const char *err = errorString();
2279 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2280 LocalFree((void *)err);
2281 errno = b_errno_win32;
2285 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2288 errno = b_errno_win32;
2295 file_open(const char *file, int flags, int mode)
2298 DWORD shareMode = 0;
2301 HANDLE foo = INVALID_HANDLE_VALUE;
2302 const char *remap = file;
2304 if (flags & O_WRONLY) access = GENERIC_WRITE;
2305 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2306 else access = GENERIC_READ;
2308 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2309 create = CREATE_NEW;
2310 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2311 create = CREATE_ALWAYS;
2312 else if (flags & O_CREAT)
2313 create = OPEN_ALWAYS;
2314 else if (flags & O_TRUNC)
2315 create = TRUNCATE_EXISTING;
2317 create = OPEN_EXISTING;
2321 if (flags & O_APPEND) {
2322 printf("open...APPEND not implemented yet.");
2326 if (p_CreateFileW) {
2327 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2328 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2330 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2331 free_pool_memory(pwszBuf);
2332 } else if (p_CreateFileA)
2333 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2335 if (INVALID_HANDLE_VALUE == foo) {
2336 errno = b_errno_win32;
2347 if (!CloseHandle((HANDLE)fd)) {
2348 errno = b_errno_win32;
2356 file_write(int fd, const void *data, ssize_t len)
2360 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2361 if (status) return bwrite;
2362 errno = b_errno_win32;
2368 file_read(int fd, void *data, ssize_t len)
2373 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2374 if (status) return bread;
2375 errno = b_errno_win32;
2380 file_seek(int fd, boffset_t offset, int whence)
2384 LONG offset_low = (LONG)offset;
2385 LONG offset_high = (LONG)(offset >> 32);
2389 method = FILE_BEGIN;
2392 method = FILE_CURRENT;
2403 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2404 errno = b_errno_win32;
2407 /* ***FIXME*** I doubt this works right */
2420 * Emulation of mmap and unmmap for tokyo dbm
2422 void *mmap(void *start, size_t length, int prot, int flags,
2423 int fd, off_t offset)
2425 DWORD fm_access = 0;
2426 DWORD mv_access = 0;
2437 if (flags & PROT_WRITE) {
2438 fm_access |= PAGE_READWRITE;
2439 } else if (flags & PROT_READ) {
2440 fm_access |= PAGE_READONLY;
2443 if (flags & PROT_READ) {
2444 mv_access |= FILE_MAP_READ;
2446 if (flags & PROT_WRITE) {
2447 mv_access |= FILE_MAP_WRITE;
2450 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2451 NULL /* security */,
2453 0 /* MaximumSizeHigh */,
2454 0 /* MaximumSizeLow */,
2455 NULL /* name of the file mapping object */);
2457 if (!h || h == INVALID_HANDLE_VALUE) {
2461 mv = MapViewOfFile(h, mv_access,
2467 if (!mv || mv == INVALID_HANDLE_VALUE) {
2474 int munmap(void *start, size_t length)
2479 UnmapViewOfFile(start);
2484 /* syslog function, added by Nicolas Boichat */
2485 void openlog(const char *ident, int option, int facility) {}