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 John Walker.
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 * OK, so it would seem CreateProcess only handles true executables:
1851 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
1854 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
1856 static const char *comspec = NULL;
1857 PROCESS_INFORMATION piProcInfo;
1858 STARTUPINFOA siStartInfo;
1859 BOOL bFuncRetn = FALSE;
1861 if (comspec == NULL) {
1862 comspec = getenv("COMSPEC");
1864 if (comspec == NULL) // should never happen
1865 return INVALID_HANDLE_VALUE;
1867 // Set up members of the PROCESS_INFORMATION structure.
1868 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
1870 // Set up members of the STARTUPINFO structure.
1872 ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
1873 siStartInfo.cb = sizeof(STARTUPINFO);
1874 // setup new process to use supplied handles for stdin,stdout,stderr
1875 // if supplied handles are not used the send a copy of our STD_HANDLE
1877 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1878 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
1880 if (in != INVALID_HANDLE_VALUE)
1881 siStartInfo.hStdInput = in;
1883 siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1885 if (out != INVALID_HANDLE_VALUE)
1886 siStartInfo.hStdOutput = out;
1888 siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1889 if (err != INVALID_HANDLE_VALUE)
1890 siStartInfo.hStdError = err;
1892 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1894 // Create the child process.
1897 const char *argStart;
1899 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
1900 return INVALID_HANDLE_VALUE;
1903 int cmdLen = strlen(comspec) + 4 + strlen(exeFile) + strlen(argStart) + 1;
1905 char *cmdLine = (char *)alloca(cmdLen);
1907 snprintf(cmdLine, cmdLen, "%s /c %s%s", comspec, exeFile, argStart);
1911 Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
1913 // try to execute program
1914 bFuncRetn = CreateProcessA(comspec,
1915 cmdLine, // command line
1916 NULL, // process security attributes
1917 NULL, // primary thread security attributes
1918 TRUE, // handles are inherited
1919 0, // creation flags
1920 NULL, // use parent's environment
1921 NULL, // use parent's current directory
1922 &siStartInfo, // STARTUPINFO pointer
1923 &piProcInfo); // receives PROCESS_INFORMATION
1925 if (bFuncRetn == 0) {
1926 ErrorExit("CreateProcess failed\n");
1927 const char *err = errorString();
1928 Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n", comspec, cmdLine, err);
1929 LocalFree((void *)err);
1930 return INVALID_HANDLE_VALUE;
1932 // we don't need a handle on the process primary thread so we close
1934 CloseHandle(piProcInfo.hThread);
1936 return piProcInfo.hProcess;
1941 ErrorExit (LPCSTR lpszMessage)
1943 Dmsg1(0, "%s", lpszMessage);
1948 typedef struct s_bpipe {
1950 time_t worker_stime;
1959 CloseIfValid(HANDLE handle)
1961 if (handle != INVALID_HANDLE_VALUE)
1962 CloseHandle(handle);
1966 open_bpipe(char *prog, int wait, const char *mode)
1968 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
1969 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
1972 SECURITY_ATTRIBUTES saAttr;
1976 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
1977 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
1978 hInputFile = INVALID_HANDLE_VALUE;
1980 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
1981 memset((void *)bpipe, 0, sizeof(BPIPE));
1983 int mode_read = (mode[0] == 'r');
1984 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
1987 // Set the bInheritHandle flag so pipe handles are inherited.
1989 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1990 saAttr.bInheritHandle = TRUE;
1991 saAttr.lpSecurityDescriptor = NULL;
1995 // Create a pipe for the child process's STDOUT.
1996 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
1997 ErrorExit("Stdout pipe creation failed\n");
2000 // Create noninheritable read handle and close the inheritable read
2003 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2004 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2006 DUPLICATE_SAME_ACCESS);
2008 ErrorExit("DuplicateHandle failed");
2012 CloseHandle(hChildStdoutRd);
2013 hChildStdoutRd = INVALID_HANDLE_VALUE;
2018 // Create a pipe for the child process's STDIN.
2020 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2021 ErrorExit("Stdin pipe creation failed\n");
2025 // Duplicate the write handle to the pipe so it is not inherited.
2026 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2027 GetCurrentProcess(), &hChildStdinWrDup,
2029 FALSE, // not inherited
2030 DUPLICATE_SAME_ACCESS);
2032 ErrorExit("DuplicateHandle failed");
2036 CloseHandle(hChildStdinWr);
2037 hChildStdinWr = INVALID_HANDLE_VALUE;
2039 // spawn program with redirected handles as appropriate
2040 bpipe->worker_pid = (pid_t)
2041 CreateChildProcess(prog, // commandline
2042 hChildStdinRd, // stdin HANDLE
2043 hChildStdoutWr, // stdout HANDLE
2044 hChildStdoutWr); // stderr HANDLE
2046 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
2050 bpipe->worker_stime = time(NULL);
2053 CloseHandle(hChildStdoutWr); // close our write side so when
2054 // process terminates we can
2056 // ugly but convert WIN32 HANDLE to FILE*
2057 int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2059 bpipe->rfd = _fdopen(rfd, "rb");
2063 CloseHandle(hChildStdinRd); // close our read side so as not
2064 // to interfre with child's copy
2065 // ugly but convert WIN32 HANDLE to FILE*
2066 int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY | O_BINARY);
2068 bpipe->wfd = _fdopen(wfd, "wb");
2073 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2080 CloseIfValid(hChildStdoutRd);
2081 CloseIfValid(hChildStdoutRdDup);
2082 CloseIfValid(hChildStdinWr);
2083 CloseIfValid(hChildStdinWrDup);
2085 free((void *) bpipe);
2086 errno = b_errno_win32; /* do GetLastError() for error code */
2092 kill(int pid, int signal)
2095 if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
2097 errno = b_errno_win32;
2099 CloseHandle((HANDLE)pid);
2105 close_bpipe(BPIPE *bpipe)
2108 int32_t remaining_wait = bpipe->wait;
2120 if (remaining_wait == 0) { /* wait indefinitely */
2121 remaining_wait = INT32_MAX;
2125 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2126 const char *err = errorString();
2127 rval = b_errno_win32;
2128 Dmsg1(0, "GetExitCode error %s\n", err);
2129 LocalFree((void *)err);
2132 if (exitCode == STILL_ACTIVE) {
2133 if (remaining_wait <= 0) {
2134 rval = ETIME; /* timed out */
2137 bmicrosleep(1, 0); /* wait one second */
2139 } else if (exitCode != 0) {
2140 /* Truncate exit code as it doesn't seem to be correct */
2141 rval = (exitCode & 0xFF) | b_errno_exit;
2144 break; /* Shouldn't get here */
2148 if (bpipe->timer_id) {
2149 stop_child_timer(bpipe->timer_id);
2151 if (bpipe->rfd) fclose(bpipe->rfd);
2152 if (bpipe->wfd) fclose(bpipe->wfd);
2153 free((void *)bpipe);
2158 close_wpipe(BPIPE *bpipe)
2164 if (fclose(bpipe->wfd) != 0) {
2172 #include "findlib/find.h"
2175 utime(const char *fname, struct utimbuf *times)
2180 conv_unix_to_win32_path(fname, tmpbuf, 5000);
2182 cvt_utime_to_ftime(times->actime, acc);
2183 cvt_utime_to_ftime(times->modtime, mod);
2185 HANDLE h = INVALID_HANDLE_VALUE;
2187 if (p_CreateFileW) {
2188 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2189 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2191 h = p_CreateFileW((LPCWSTR)pwszBuf,
2192 FILE_WRITE_ATTRIBUTES,
2193 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2196 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2199 free_pool_memory(pwszBuf);
2200 } else if (p_CreateFileA) {
2201 h = p_CreateFileA(tmpbuf,
2202 FILE_WRITE_ATTRIBUTES,
2203 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2206 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2210 if (h == INVALID_HANDLE_VALUE) {
2211 const char *err = errorString();
2212 Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2213 LocalFree((void *)err);
2214 errno = b_errno_win32;
2218 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2221 errno = b_errno_win32;
2228 file_open(const char *file, int flags, int mode)
2231 DWORD shareMode = 0;
2234 HANDLE foo = INVALID_HANDLE_VALUE;
2235 const char *remap = file;
2237 if (flags & O_WRONLY) access = GENERIC_WRITE;
2238 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2239 else access = GENERIC_READ;
2241 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2242 create = CREATE_NEW;
2243 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2244 create = CREATE_ALWAYS;
2245 else if (flags & O_CREAT)
2246 create = OPEN_ALWAYS;
2247 else if (flags & O_TRUNC)
2248 create = TRUNCATE_EXISTING;
2250 create = OPEN_EXISTING;
2254 if (flags & O_APPEND) {
2255 printf("open...APPEND not implemented yet.");
2259 if (p_CreateFileW) {
2260 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2261 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2263 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2264 free_pool_memory(pwszBuf);
2265 } else if (p_CreateFileA)
2266 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2268 if (INVALID_HANDLE_VALUE == foo) {
2269 errno = b_errno_win32;
2280 if (!CloseHandle((HANDLE)fd)) {
2281 errno = b_errno_win32;
2289 file_write(int fd, const void *data, ssize_t len)
2293 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2294 if (status) return bwrite;
2295 errno = b_errno_win32;
2301 file_read(int fd, void *data, ssize_t len)
2306 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2307 if (status) return bread;
2308 errno = b_errno_win32;
2313 file_seek(int fd, boffset_t offset, int whence)
2317 LONG offset_low = (LONG)offset;
2318 LONG offset_high = (LONG)(offset >> 32);
2322 method = FILE_BEGIN;
2325 method = FILE_CURRENT;
2336 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2337 errno = b_errno_win32;
2340 /* ***FIXME*** I doubt this works right */
2353 /* syslog function, added by Nicolas Boichat */
2354 void openlog(const char *ident, int option, int facility) {}