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 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
655 free_pool_memory(pwszBuf);
657 pdwFileAttributes = &info_w.dwFileAttributes;
658 pdwReserved0 = &info_w.dwReserved0;
659 pnFileSizeHigh = &info_w.nFileSizeHigh;
660 pnFileSizeLow = &info_w.nFileSizeLow;
661 pftLastAccessTime = &info_w.ftLastAccessTime;
662 pftLastWriteTime = &info_w.ftLastWriteTime;
663 pftCreationTime = &info_w.ftCreationTime;
666 } else if (p_FindFirstFileA) {
667 h = p_FindFirstFileA(file, &info_a);
669 pdwFileAttributes = &info_a.dwFileAttributes;
670 pdwReserved0 = &info_a.dwReserved0;
671 pnFileSizeHigh = &info_a.nFileSizeHigh;
672 pnFileSizeLow = &info_a.nFileSizeLow;
673 pftLastAccessTime = &info_a.ftLastAccessTime;
674 pftLastWriteTime = &info_a.ftLastWriteTime;
675 pftCreationTime = &info_a.ftCreationTime;
678 if (h == INVALID_HANDLE_VALUE) {
679 const char *err = errorString();
680 Dmsg2(99, "FindFirstFile(%s):%s\n", file, err);
681 LocalFree((void *)err);
682 errno = b_errno_win32;
688 sb->st_mode = 0777; /* start with everything */
689 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
690 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
691 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
692 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
693 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
694 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
695 sb->st_mode |= S_IFDIR;
698 * Store reparse/mount point info in st_rdev. Note a
699 * Win32 reparse point (junction point) is like a link
700 * though it can have many properties (directory link,
701 * soft link, hard link, HSM, ...
702 * A mount point is a reparse point where another volume
703 * is mounted, so it is like a Unix mount point (change of
706 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
707 if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) {
708 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
710 sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */
713 Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file);
714 sb->st_size = *pnFileSizeHigh;
716 sb->st_size |= *pnFileSizeLow;
717 sb->st_blksize = 4096;
718 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
720 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
721 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
722 sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
728 fstat(int fd, struct stat *sb)
730 BY_HANDLE_FILE_INFORMATION info;
732 if (!GetFileInformationByHandle((HANDLE)fd, &info)) {
733 const char *err = errorString();
734 Dmsg1(99, "GetfileInformationByHandle: %s\n", err);
735 LocalFree((void *)err);
736 errno = b_errno_win32;
740 sb->st_dev = info.dwVolumeSerialNumber;
741 sb->st_ino = info.nFileIndexHigh;
743 sb->st_ino |= info.nFileIndexLow;
744 sb->st_nlink = (short)info.nNumberOfLinks;
745 if (sb->st_nlink > 1) {
746 Dmsg1(99, "st_nlink=%d\n", sb->st_nlink);
749 sb->st_mode = 0777; /* start with everything */
750 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
751 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
752 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
753 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
754 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
755 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
756 sb->st_mode |= S_IFREG;
758 /* Use st_rdev to store reparse attribute */
759 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
760 sb->st_rdev = WIN32_REPARSE_POINT;
762 Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
763 (long long)sb->st_ino);
765 sb->st_size = info.nFileSizeHigh;
767 sb->st_size |= info.nFileSizeLow;
768 sb->st_blksize = 4096;
769 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
770 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
771 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
772 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
778 stat2(const char *file, struct stat *sb)
783 conv_unix_to_win32_path(file, tmpbuf, 5000);
785 DWORD attr = (DWORD)-1;
787 if (p_GetFileAttributesW) {
788 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
789 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
791 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
792 free_pool_memory(pwszBuf);
793 } else if (p_GetFileAttributesA) {
794 attr = p_GetFileAttributesA(tmpbuf);
797 if (attr == (DWORD)-1) {
798 const char *err = errorString();
799 Dmsg2(99, "GetFileAttributes(%s): %s\n", tmpbuf, err);
800 LocalFree((void *)err);
801 errno = b_errno_win32;
805 h = CreateFileA(tmpbuf, GENERIC_READ,
806 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
808 if (h == INVALID_HANDLE_VALUE) {
809 const char *err = errorString();
810 Dmsg2(99, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
811 LocalFree((void *)err);
812 errno = b_errno_win32;
816 rval = fstat((int)h, sb);
819 if (attr & FILE_ATTRIBUTE_DIRECTORY &&
820 file[1] == ':' && file[2] != 0) {
828 stat(const char *file, struct stat *sb)
830 WIN32_FILE_ATTRIBUTE_DATA data;
833 memset(sb, 0, sizeof(*sb));
835 if (p_GetFileAttributesExW) {
836 /* dynamically allocate enough space for UCS2 filename */
837 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
838 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
840 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
841 free_pool_memory(pwszBuf);
844 return stat2(file, sb);
847 } else if (p_GetFileAttributesExA) {
848 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
849 return stat2(file, sb);
852 return stat2(file, sb);
855 sb->st_mode = 0777; /* start with everything */
856 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
857 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
859 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
860 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
862 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
863 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
865 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
866 sb->st_mode |= S_IFDIR;
868 sb->st_mode |= S_IFREG;
871 /* Use st_rdev to store reparse attribute */
872 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
875 sb->st_size = data.nFileSizeHigh;
877 sb->st_size |= data.nFileSizeLow;
878 sb->st_blksize = 4096;
879 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
880 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
881 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
882 sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
885 * If we are not at the root, then to distinguish a reparse
886 * point from a mount point, we must call FindFirstFile() to
887 * get the WIN32_FIND_DATA, which has the bit that indicates
888 * that this directory is a mount point -- aren't Win32 APIs
889 * wonderful? (sarcasm). The code exists in the statDir
892 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
893 file[1] == ':' && file[2] != 0) {
896 Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
897 (long long)sb->st_ino, file);
902 * We write our own ftruncate because the one in the
903 * Microsoft library mrcrt.dll does not truncate
904 * files greater than 2GB.
907 int win32_ftruncate(int fd, int64_t length)
909 /* Set point we want to truncate file */
910 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
912 if (pos != (__int64)length) {
913 errno = EACCES; /* truncation failed, get out */
918 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
919 errno = b_errno_win32;
926 int fcntl(int fd, int cmd, long arg)
949 lstat(const char *file, struct stat *sb)
951 return stat(file, sb);
967 execvp(const char *, char *[]) {
988 waitpid(int, int*, int)
995 readlink(const char *, char *, int)
1004 strcasecmp(const char *s1, const char *s2)
1006 register int ch1, ch2;
1009 return 0; /* strings are equal if same object. */
1019 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1026 strncasecmp(const char *s1, const char *s2, int len)
1028 register int ch1 = 0, ch2 = 0;
1031 return 0; /* strings are equal if same object. */
1042 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1049 gettimeofday(struct timeval *tv, struct timezone *)
1054 GetSystemTime(&now);
1060 if (!SystemTimeToFileTime(&now, &tmp)) {
1061 errno = b_errno_win32;
1065 int64_t _100nsec = tmp.dwHighDateTime;
1067 _100nsec |= tmp.dwLowDateTime;
1068 _100nsec -= WIN32_FILETIME_ADJUST;
1070 tv->tv_sec = (long)(_100nsec / 10000000);
1071 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1076 /* For apcupsd this is in src/lib/wincompat.c */
1077 extern "C" void syslog(int type, const char *fmt, ...)
1079 /*#ifndef HAVE_CONSOLE
1080 MessageBox(NULL, msg, "Bacula", MB_OK);
1101 // implement opendir/readdir/closedir on top of window's API
1105 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1106 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1107 const char *spec; // the directory we're traversing
1108 HANDLE dirh; // the search handle
1109 BOOL valid_a; // the info in data_a field is valid
1110 BOOL valid_w; // the info in data_w field is valid
1111 UINT32 offset; // pseudo offset for d_off
1115 opendir(const char *path)
1117 /* enough space for VSS !*/
1118 int max_len = strlen(path) + MAX_PATH;
1125 Dmsg1(100, "Opendir path=%s\n", path);
1126 rval = (_dir *)malloc(sizeof(_dir));
1127 memset (rval, 0, sizeof (_dir));
1128 if (rval == NULL) return NULL;
1129 char *tspec = (char *)malloc(max_len);
1130 if (tspec == NULL) return NULL;
1132 conv_unix_to_win32_path(path, tspec, max_len);
1133 Dmsg1(100, "win32 path=%s\n", tspec);
1135 // add backslash only if there is none yet (think of c:\)
1136 if (tspec[strlen(tspec)-1] != '\\')
1137 bstrncat(tspec, "\\*", max_len);
1139 bstrncat(tspec, "*", max_len);
1143 // convert to wchar_t
1144 if (p_FindFirstFileW) {
1145 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1146 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1148 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1150 free_pool_memory(pwcBuf);
1152 if (rval->dirh != INVALID_HANDLE_VALUE)
1154 } else if (p_FindFirstFileA) {
1155 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1157 if (rval->dirh != INVALID_HANDLE_VALUE)
1162 Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1163 path, rval->spec, rval->dirh);
1166 if (rval->dirh == INVALID_HANDLE_VALUE)
1169 if (rval->valid_w) {
1170 Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1173 if (rval->valid_a) {
1174 Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1180 free((void *)rval->spec);
1182 errno = b_errno_win32;
1189 _dir *dp = (_dir *)dirp;
1190 FindClose(dp->dirh);
1191 free((void *)dp->spec);
1197 typedef struct _WIN32_FIND_DATA {
1198 DWORD dwFileAttributes;
1199 FILETIME ftCreationTime;
1200 FILETIME ftLastAccessTime;
1201 FILETIME ftLastWriteTime;
1202 DWORD nFileSizeHigh;
1206 TCHAR cFileName[MAX_PATH];
1207 TCHAR cAlternateFileName[14];
1208 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1212 copyin(struct dirent &dp, const char *fname)
1216 char *cp = dp.d_name;
1226 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1228 _dir *dp = (_dir *)dirp;
1229 if (dp->valid_w || dp->valid_a) {
1230 entry->d_off = dp->offset;
1234 char szBuf[MAX_PATH_UTF8+1];
1235 wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1236 dp->offset += copyin(*entry, szBuf);
1237 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1238 dp->offset += copyin(*entry, dp->data_a.cFileName);
1241 *result = entry; /* return entry address */
1242 Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1243 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1245 // Dmsg0(99, "readdir_r !valid\n");
1246 errno = b_errno_win32;
1250 // get next file, try unicode first
1251 if (p_FindNextFileW)
1252 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1253 else if (p_FindNextFileA)
1254 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1256 dp->valid_a = FALSE;
1257 dp->valid_w = FALSE;
1264 * Dotted IP address to network address
1270 inet_aton(const char *a, struct in_addr *inp)
1273 uint32_t acc = 0, tmp = 0;
1276 if (!isdigit(*cp)) { /* first char must be digit */
1277 return 0; /* error */
1281 tmp = (tmp * 10) + (*cp -'0');
1282 } else if (*cp == '.' || *cp == 0) {
1284 return 0; /* error */
1286 acc = (acc << 8) + tmp;
1290 return 0; /* error */
1292 } while (*cp++ != 0);
1293 if (dotc != 4) { /* want 3 .'s plus EOS */
1294 return 0; /* error */
1296 inp->s_addr = htonl(acc); /* store addr in network format */
1301 nanosleep(const struct timespec *req, struct timespec *rem)
1304 rem->tv_sec = rem->tv_nsec = 0;
1305 Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1310 init_signals(void terminate(int sig))
1316 init_stack_dump(void)
1323 pathconf(const char *path, int name)
1327 if (strncmp(path, "\\\\?\\", 4) == 0)
1339 WORD wVersionRequested = MAKEWORD( 1, 1);
1342 int err = WSAStartup(wVersionRequested, &wsaData);
1346 printf("Can not start Windows Sockets\n");
1354 int win32_chmod(const char *path, mode_t mode)
1356 DWORD attr = (DWORD)-1;
1358 if (p_GetFileAttributesW) {
1359 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1360 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1362 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1363 if (attr != INVALID_FILE_ATTRIBUTES) {
1364 /* Use Bacula mappings define in stat() above */
1365 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1366 attr |= FILE_ATTRIBUTE_READONLY;
1368 attr &= ~FILE_ATTRIBUTE_READONLY;
1370 if (mode & S_ISVTX) {
1371 attr |= FILE_ATTRIBUTE_HIDDEN;
1373 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1375 if (mode & S_IRWXO) {
1376 attr |= FILE_ATTRIBUTE_SYSTEM;
1378 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1380 attr = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1382 free_pool_memory(pwszBuf);
1383 } else if (p_GetFileAttributesA) {
1384 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) {
1385 attr |= FILE_ATTRIBUTE_READONLY;
1387 attr &= ~FILE_ATTRIBUTE_READONLY;
1389 if (mode & S_ISVTX) {
1390 attr |= FILE_ATTRIBUTE_HIDDEN;
1392 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1394 if (mode & S_IRWXO) {
1395 attr |= FILE_ATTRIBUTE_SYSTEM;
1397 attr &= ~FILE_ATTRIBUTE_SYSTEM;
1399 attr = p_GetFileAttributesA(path);
1400 if (attr != INVALID_FILE_ATTRIBUTES) {
1401 attr = p_SetFileAttributesA(path, attr);
1405 if (attr == (DWORD)-1) {
1406 const char *err = errorString();
1407 Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err);
1408 LocalFree((void *)err);
1409 errno = b_errno_win32;
1417 win32_chdir(const char *dir)
1419 if (p_SetCurrentDirectoryW) {
1420 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1421 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1423 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1425 free_pool_memory(pwszBuf);
1428 errno = b_errno_win32;
1431 } else if (p_SetCurrentDirectoryA) {
1432 if (0 == p_SetCurrentDirectoryA(dir)) {
1433 errno = b_errno_win32;
1444 win32_mkdir(const char *dir)
1447 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1448 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1450 int n = p_wmkdir((LPCWSTR)pwszBuf);
1451 free_pool_memory(pwszBuf);
1460 win32_getcwd(char *buf, int maxlen)
1464 if (p_GetCurrentDirectoryW) {
1465 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1466 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1468 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1470 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1471 free_pool_memory(pwszBuf);
1473 } else if (p_GetCurrentDirectoryA)
1474 n = p_GetCurrentDirectoryA(maxlen, buf);
1476 if (n == 0 || n > maxlen) return NULL;
1478 if (n+1 > maxlen) return NULL;
1487 win32_fputs(const char *string, FILE *stream)
1489 /* we use WriteConsoleA / WriteConsoleA
1490 so we can be sure that unicode support works on win32.
1491 with fallback if something fails
1494 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1495 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1496 p_MultiByteToWideChar && (stream == stdout)) {
1498 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1500 DWORD dwCharsWritten;
1503 dwChars = UTF8_2_wchar(&pwszBuf, string);
1505 /* try WriteConsoleW */
1506 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1507 free_pool_memory(pwszBuf);
1508 return dwCharsWritten;
1511 /* convert to local codepage and try WriteConsoleA */
1512 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1513 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1515 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1516 free_pool_memory(pwszBuf);
1518 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1519 free_pool_memory(pszBuf);
1520 return dwCharsWritten;
1522 free_pool_memory(pszBuf);
1525 return fputs(string, stream);
1529 win32_cgets (char* buffer, int len)
1531 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1532 from the win32 console and fallback if seomething fails */
1534 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1535 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1537 wchar_t wszBuf[1024];
1540 /* nt and unicode conversion */
1541 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1543 /* null terminate at end */
1544 if (wszBuf[dwRead-1] == L'\n') {
1545 wszBuf[dwRead-1] = L'\0';
1549 if (wszBuf[dwRead-1] == L'\r') {
1550 wszBuf[dwRead-1] = L'\0';
1554 wchar_2_UTF8(buffer, wszBuf, len);
1558 /* win 9x and unicode conversion */
1559 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1561 /* null terminate at end */
1562 if (szBuf[dwRead-1] == L'\n') {
1563 szBuf[dwRead-1] = L'\0';
1567 if (szBuf[dwRead-1] == L'\r') {
1568 szBuf[dwRead-1] = L'\0';
1572 /* convert from ansii to wchar_t */
1573 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1574 /* convert from wchar_t to UTF-8 */
1575 if (wchar_2_UTF8(buffer, wszBuf, len))
1581 if (fgets(buffer, len, stdin))
1588 win32_unlink(const char *filename)
1592 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1593 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1595 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1598 * special case if file is readonly,
1599 * we retry but unset attribute before
1601 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1602 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1603 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1604 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1605 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1606 /* reset to original if it didn't help */
1608 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1612 free_pool_memory(pwszBuf);
1614 nRetCode = _unlink(filename);
1616 /* special case if file is readonly,
1617 we retry but unset attribute before */
1618 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1619 DWORD dwAttr = p_GetFileAttributesA(filename);
1620 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1621 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1622 nRetCode = _unlink(filename);
1623 /* reset to original if it didn't help */
1625 p_SetFileAttributesA(filename, dwAttr);
1634 #include "mswinver.h"
1636 char WIN_VERSION_LONG[64];
1637 char WIN_VERSION[32];
1638 char WIN_RAWVERSION[32];
1645 static winver INIT; // cause constructor to be called before main()
1648 winver::winver(void)
1650 const char *version = "";
1651 const char *platform = "";
1652 OSVERSIONINFO osvinfo;
1653 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1655 // Get the current OS version
1656 if (!GetVersionEx(&osvinfo)) {
1657 version = "Unknown";
1658 platform = "Unknown";
1660 const int ver = _mkversion(osvinfo.dwPlatformId,
1661 osvinfo.dwMajorVersion,
1662 osvinfo.dwMinorVersion);
1663 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1666 case MS_WINDOWS_95: (version = "Windows 95"); break;
1667 case MS_WINDOWS_98: (version = "Windows 98"); break;
1668 case MS_WINDOWS_ME: (version = "Windows ME"); break;
1669 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
1670 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
1671 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
1672 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
1673 default: version = WIN_RAWVERSION; break;
1676 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1677 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1678 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1681 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1685 BPIPE *b = open_bpipe("ls -l", 10, "r");
1687 while (!feof(b->rfd)) {
1688 fgets(buf, sizeof(buf), b->rfd);
1694 BOOL CreateChildProcess(VOID);
1695 VOID WriteToPipe(VOID);
1696 VOID ReadFromPipe(VOID);
1697 VOID ErrorExit(LPCSTR);
1698 VOID ErrMsg(LPTSTR, BOOL);
1701 * Check for a quoted path, if an absolute path name is given and it contains
1702 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
1703 * CreateProcess() says the best way to ensure proper results with executables
1704 * with spaces in path or filename is to quote the string.
1707 getArgv0(const char *cmdline)
1712 for (cp = cmdline; *cp; cp++)
1717 if (!inquote && isspace(*cp))
1722 int len = cp - cmdline;
1723 char *rval = (char *)malloc(len+1);
1736 * Extracts the executable or script name from the first string in
1739 * If the name contains blanks then it must be quoted with double quotes,
1740 * otherwise quotes are optional. If the name contains blanks then it
1741 * will be converted to a short name.
1743 * The optional quotes will be removed. The result is copied to a malloc'ed
1744 * buffer and returned through the pexe argument. The pargs parameter is set
1745 * to the address of the character in cmdline located after the name.
1747 * The malloc'ed buffer returned in *pexe must be freed by the caller.
1750 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
1752 const char *pExeStart = NULL; /* Start of executable name in cmdline */
1753 const char *pExeEnd = NULL; /* Character after executable name (separator) */
1755 const char *pBasename = NULL; /* Character after last path separator */
1756 const char *pExtension = NULL; /* Period at start of extension */
1758 const char *current = cmdline;
1760 bool bQuoted = false;
1762 /* Skip initial whitespace */
1764 while (*current == ' ' || *current == '\t')
1769 /* Calculate start of name and determine if quoted */
1771 if (*current == '"') {
1772 pExeStart = ++current;
1775 pExeStart = current;
1783 * Scan command line looking for path separators (/ and \\) and the
1784 * terminator, either a quote or a blank. The location of the
1785 * extension is also noted.
1788 for ( ; *current != '\0'; current++)
1790 if (*current == '.') {
1791 pExtension = current;
1792 } else if (IsPathSeparator(*current) && current[1] != '\0') {
1793 pBasename = ¤t[1];
1797 /* Check for terminator, either quote or blank */
1799 if (*current != '"') {
1803 if (*current != ' ') {
1809 * Hit terminator, remember end of name (address of terminator) and
1810 * start of arguments
1814 if (bQuoted && *current == '"') {
1815 *pargs = ¤t[1];
1823 if (pBasename == NULL) {
1824 pBasename = pExeStart;
1827 if (pExeEnd == NULL) {
1836 bool bHasPathSeparators = pExeStart != pBasename;
1838 /* We have pointers to all the useful parts of the name */
1840 /* Default extensions in the order cmd.exe uses to search */
1842 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
1843 DWORD dwBasePathLength = pExeEnd - pExeStart;
1845 DWORD dwAltNameLength = 0;
1846 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1847 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
1849 pPathname[MAX_PATHLENGTH] = '\0';
1850 pAltPathname[MAX_PATHLENGTH] = '\0';
1852 memcpy(pPathname, pExeStart, dwBasePathLength);
1853 pPathname[dwBasePathLength] = '\0';
1855 if (pExtension == NULL) {
1856 /* Try appending extensions */
1857 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
1859 if (!bHasPathSeparators) {
1860 /* There are no path separators, search in the standard locations */
1861 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
1862 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1863 memcpy(pPathname, pAltPathname, dwAltNameLength);
1864 pPathname[dwAltNameLength] = '\0';
1868 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
1869 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
1872 pPathname[dwBasePathLength] = '\0';
1875 } else if (!bHasPathSeparators) {
1876 /* There are no path separators, search in the standard locations */
1877 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
1878 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
1879 memcpy(pPathname, pAltPathname, dwAltNameLength);
1880 pPathname[dwAltNameLength] = '\0';
1884 if (strchr(pPathname, ' ') != NULL) {
1885 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
1887 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
1888 *pexe = (char *)malloc(dwAltNameLength + 1);
1889 if (*pexe == NULL) {
1892 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
1896 if (*pexe == NULL) {
1897 DWORD dwPathnameLength = strlen(pPathname);
1898 *pexe = (char *)malloc(dwPathnameLength + 1);
1899 if (*pexe == NULL) {
1902 memcpy(*pexe, pPathname, dwPathnameLength + 1);
1909 * Create the process with WCHAR API
1912 CreateChildProcessW(const char *comspec, const char *cmdLine,
1913 PROCESS_INFORMATION *hProcInfo,
1914 HANDLE in, HANDLE out, HANDLE err)
1916 STARTUPINFOW siStartInfo;
1917 BOOL bFuncRetn = FALSE;
1919 // Set up members of the STARTUPINFO structure.
1920 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1921 siStartInfo.cb = sizeof(siStartInfo);
1922 // setup new process to use supplied handles for stdin,stdout,stderr
1924 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1925 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1927 siStartInfo.hStdInput = in;
1928 siStartInfo.hStdOutput = out;
1929 siStartInfo.hStdError = err;
1931 // Convert argument to WCHAR
1932 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
1933 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
1935 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
1936 UTF8_2_wchar(&comspec_wchar, comspec);
1938 // Create the child process.
1939 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
1941 // try to execute program
1942 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
1943 (WCHAR*)cmdLine_wchar,// command line
1944 NULL, // process security attributes
1945 NULL, // primary thread security attributes
1946 TRUE, // handles are inherited
1947 0, // creation flags
1948 NULL, // use parent's environment
1949 NULL, // use parent's current directory
1950 &siStartInfo, // STARTUPINFO pointer
1951 hProcInfo); // receives PROCESS_INFORMATION
1952 free_pool_memory(cmdLine_wchar);
1953 free_pool_memory(comspec_wchar);
1960 * Create the process with ANSI API
1963 CreateChildProcessA(const char *comspec, char *cmdLine,
1964 PROCESS_INFORMATION *hProcInfo,
1965 HANDLE in, HANDLE out, HANDLE err)
1967 STARTUPINFOA siStartInfo;
1968 BOOL bFuncRetn = FALSE;
1970 // Set up members of the STARTUPINFO structure.
1971 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
1972 siStartInfo.cb = sizeof(siStartInfo);
1973 // setup new process to use supplied handles for stdin,stdout,stderr
1974 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1975 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1977 siStartInfo.hStdInput = in;
1978 siStartInfo.hStdOutput = out;
1979 siStartInfo.hStdError = err;
1981 // Create the child process.
1982 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
1984 // try to execute program
1985 bFuncRetn = p_CreateProcessA(comspec,
1986 cmdLine, // command line
1987 NULL, // process security attributes
1988 NULL, // primary thread security attributes
1989 TRUE, // handles are inherited
1990 0, // creation flags
1991 NULL, // use parent's environment
1992 NULL, // use parent's current directory
1993 &siStartInfo,// STARTUPINFO pointer
1994 hProcInfo);// receives PROCESS_INFORMATION
1999 * OK, so it would seem CreateProcess only handles true executables:
2000 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2003 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2005 static const char *comspec = NULL;
2006 PROCESS_INFORMATION piProcInfo;
2007 BOOL bFuncRetn = FALSE;
2009 if (!p_CreateProcessA || !p_CreateProcessW)
2010 return INVALID_HANDLE_VALUE;
2012 if (comspec == NULL)
2013 comspec = getenv("COMSPEC");
2014 if (comspec == NULL) // should never happen
2015 return INVALID_HANDLE_VALUE;
2017 // Set up members of the PROCESS_INFORMATION structure.
2018 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2020 // if supplied handles are not used the send a copy of our STD_HANDLE
2022 if (in == INVALID_HANDLE_VALUE)
2023 in = GetStdHandle(STD_INPUT_HANDLE);
2025 if (out == INVALID_HANDLE_VALUE)
2026 out = GetStdHandle(STD_OUTPUT_HANDLE);
2028 if (err == INVALID_HANDLE_VALUE)
2029 err = GetStdHandle(STD_ERROR_HANDLE);
2032 const char *argStart;
2034 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2035 return INVALID_HANDLE_VALUE;
2038 POOL_MEM cmdLine(PM_FNAME);
2039 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2043 // New function disabled
2044 if (p_CreateProcessW && p_MultiByteToWideChar) {
2045 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2048 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2052 if (bFuncRetn == 0) {
2053 ErrorExit("CreateProcess failed\n");
2054 const char *err = errorString();
2055 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err);
2056 LocalFree((void *)err);
2057 return INVALID_HANDLE_VALUE;
2059 // we don't need a handle on the process primary thread so we close
2061 CloseHandle(piProcInfo.hThread);
2062 return piProcInfo.hProcess;
2066 ErrorExit (LPCSTR lpszMessage)
2068 Dmsg1(0, "%s", lpszMessage);
2073 typedef struct s_bpipe {
2075 time_t worker_stime;
2084 CloseIfValid(HANDLE handle)
2086 if (handle != INVALID_HANDLE_VALUE)
2087 CloseHandle(handle);
2091 open_bpipe(char *prog, int wait, const char *mode)
2093 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2094 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2097 SECURITY_ATTRIBUTES saAttr;
2101 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2102 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2103 hInputFile = INVALID_HANDLE_VALUE;
2105 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2106 memset((void *)bpipe, 0, sizeof(BPIPE));
2108 int mode_read = (mode[0] == 'r');
2109 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2112 // Set the bInheritHandle flag so pipe handles are inherited.
2114 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2115 saAttr.bInheritHandle = TRUE;
2116 saAttr.lpSecurityDescriptor = NULL;
2120 // Create a pipe for the child process's STDOUT.
2121 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2122 ErrorExit("Stdout pipe creation failed\n");
2125 // Create noninheritable read handle and close the inheritable read
2128 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2129 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2131 DUPLICATE_SAME_ACCESS);
2133 ErrorExit("DuplicateHandle failed");
2137 CloseHandle(hChildStdoutRd);
2138 hChildStdoutRd = INVALID_HANDLE_VALUE;
2143 // Create a pipe for the child process's STDIN.
2145 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2146 ErrorExit("Stdin pipe creation failed\n");
2150 // Duplicate the write handle to the pipe so it is not inherited.
2151 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2152 GetCurrentProcess(), &hChildStdinWrDup,
2154 FALSE, // not inherited
2155 DUPLICATE_SAME_ACCESS);
2157 ErrorExit("DuplicateHandle failed");
2161 CloseHandle(hChildStdinWr);
2162 hChildStdinWr = INVALID_HANDLE_VALUE;
2164 // spawn program with redirected handles as appropriate
2165 bpipe->worker_pid = (pid_t)
2166 CreateChildProcess(prog, // commandline
2167 hChildStdinRd, // stdin HANDLE
2168 hChildStdoutWr, // stdout HANDLE
2169 hChildStdoutWr); // stderr HANDLE
2171 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2175 bpipe->worker_stime = time(NULL);
2178 CloseHandle(hChildStdoutWr); // close our write side so when
2179 // process terminates we can
2181 // ugly but convert WIN32 HANDLE to FILE*
2182 int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2184 bpipe->rfd = _fdopen(rfd, "rb");
2188 CloseHandle(hChildStdinRd); // close our read side so as not
2189 // to interfre with child's copy
2190 // ugly but convert WIN32 HANDLE to FILE*
2191 int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY | O_BINARY);
2193 bpipe->wfd = _fdopen(wfd, "wb");
2198 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2205 CloseIfValid(hChildStdoutRd);
2206 CloseIfValid(hChildStdoutRdDup);
2207 CloseIfValid(hChildStdinWr);
2208 CloseIfValid(hChildStdinWrDup);
2210 free((void *) bpipe);
2211 errno = b_errno_win32; /* do GetLastError() for error code */
2217 kill(int pid, int signal)
2220 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2222 errno = b_errno_win32;
2224 CloseHandle((HANDLE)pid);
2230 close_bpipe(BPIPE *bpipe)
2233 int32_t remaining_wait = bpipe->wait;
2245 if (remaining_wait == 0) { /* wait indefinitely */
2246 remaining_wait = INT32_MAX;
2250 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2251 const char *err = errorString();
2252 rval = b_errno_win32;
2253 Dmsg1(0, "GetExitCode error %s\n", err);
2254 LocalFree((void *)err);
2257 if (exitCode == STILL_ACTIVE) {
2258 if (remaining_wait <= 0) {
2259 rval = ETIME; /* timed out */
2262 bmicrosleep(1, 0); /* wait one second */
2264 } else if (exitCode != 0) {
2265 /* Truncate exit code as it doesn't seem to be correct */
2266 rval = (exitCode & 0xFF) | b_errno_exit;
2269 break; /* Shouldn't get here */
2273 if (bpipe->timer_id) {
2274 stop_child_timer(bpipe->timer_id);
2276 if (bpipe->rfd) fclose(bpipe->rfd);
2277 if (bpipe->wfd) fclose(bpipe->wfd);
2278 free((void *)bpipe);
2283 close_wpipe(BPIPE *bpipe)
2289 if (fclose(bpipe->wfd) != 0) {
2298 utime(const char *fname, struct utimbuf *times)
2303 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2305 cvt_utime_to_ftime(times->actime, acc);
2306 cvt_utime_to_ftime(times->modtime, mod);
2308 HANDLE h = INVALID_HANDLE_VALUE;
2310 if (p_CreateFileW) {
2311 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2312 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2314 h = p_CreateFileW((LPCWSTR)pwszBuf,
2315 FILE_WRITE_ATTRIBUTES,
2316 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2319 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2322 free_pool_memory(pwszBuf);
2323 } else if (p_CreateFileA) {
2324 h = p_CreateFileA(tmpbuf,
2325 FILE_WRITE_ATTRIBUTES,
2326 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2329 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2333 if (h == INVALID_HANDLE_VALUE) {
2334 const char *err = errorString();
2335 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2336 LocalFree((void *)err);
2337 errno = b_errno_win32;
2341 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2344 errno = b_errno_win32;
2351 file_open(const char *file, int flags, int mode)
2354 DWORD shareMode = 0;
2357 HANDLE foo = INVALID_HANDLE_VALUE;
2358 const char *remap = file;
2360 if (flags & O_WRONLY) access = GENERIC_WRITE;
2361 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2362 else access = GENERIC_READ;
2364 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2365 create = CREATE_NEW;
2366 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2367 create = CREATE_ALWAYS;
2368 else if (flags & O_CREAT)
2369 create = OPEN_ALWAYS;
2370 else if (flags & O_TRUNC)
2371 create = TRUNCATE_EXISTING;
2373 create = OPEN_EXISTING;
2377 if (flags & O_APPEND) {
2378 printf("open...APPEND not implemented yet.");
2382 if (p_CreateFileW) {
2383 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2384 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2386 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2387 free_pool_memory(pwszBuf);
2388 } else if (p_CreateFileA)
2389 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2391 if (INVALID_HANDLE_VALUE == foo) {
2392 errno = b_errno_win32;
2403 if (!CloseHandle((HANDLE)fd)) {
2404 errno = b_errno_win32;
2412 file_write(int fd, const void *data, ssize_t len)
2416 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2417 if (status) return bwrite;
2418 errno = b_errno_win32;
2424 file_read(int fd, void *data, ssize_t len)
2429 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2430 if (status) return bread;
2431 errno = b_errno_win32;
2436 file_seek(int fd, boffset_t offset, int whence)
2440 LONG offset_low = (LONG)offset;
2441 LONG offset_high = (LONG)(offset >> 32);
2445 method = FILE_BEGIN;
2448 method = FILE_CURRENT;
2459 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2460 errno = b_errno_win32;
2463 /* ***FIXME*** I doubt this works right */
2476 * Emulation of mmap and unmmap for tokyo dbm
2478 void *mmap(void *start, size_t length, int prot, int flags,
2479 int fd, off_t offset)
2481 DWORD fm_access = 0;
2482 DWORD mv_access = 0;
2493 if (flags & PROT_WRITE) {
2494 fm_access |= PAGE_READWRITE;
2495 } else if (flags & PROT_READ) {
2496 fm_access |= PAGE_READONLY;
2499 if (flags & PROT_READ) {
2500 mv_access |= FILE_MAP_READ;
2502 if (flags & PROT_WRITE) {
2503 mv_access |= FILE_MAP_WRITE;
2506 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2507 NULL /* security */,
2509 0 /* MaximumSizeHigh */,
2510 0 /* MaximumSizeLow */,
2511 NULL /* name of the file mapping object */);
2513 if (!h || h == INVALID_HANDLE_VALUE) {
2517 mv = MapViewOfFile(h, mv_access,
2523 if (!mv || mv == INVALID_HANDLE_VALUE) {
2530 int munmap(void *start, size_t length)
2535 UnmapViewOfFile(start);
2540 /* syslog function, added by Nicolas Boichat */
2541 void openlog(const char *ident, int option, int facility) {}