2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 // compat.cpp -- compatibilty layer to make bacula-fd run
21 // natively under windows
23 // Copyright transferred from Raider Solutions, Inc to
24 // Kern Sibbald and John Walker by express permission.
26 // Author : Christopher S. Hull
27 // Created On : Sat Jan 31 15:55:00 2004
32 #include "findlib/find.h"
34 /* Note, if you want to see what Windows variables and structures
35 * are defined, bacula.h includes <windows.h>, which is found in:
37 * cross-tools/mingw32/mingw32/include
39 * cross-tools/mingw-w64/x86_64-pc-mingw32/include
41 * depending on whether we are building the 32 bit version or
45 static const int dbglvl = 500;
47 #define b_errno_win32 (1<<29)
49 #define MAX_PATHLENGTH 1024
52 UTF-8 to UCS2 path conversion is expensive,
53 so we cache the conversion. During backup the
54 conversion is called 3 times (lstat, attribs, open),
55 by using the cache this is reduced to 1 time
57 static POOLMEM *g_pWin32ConvUTF8Cache = NULL;
58 static POOLMEM *g_pWin32ConvUCS2Cache = NULL;
59 static DWORD g_dwWin32ConvUTF8strlen = 0;
60 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
62 /* Forward referenced functions */
63 static const char *errorString(void);
65 /* The following functions are available only in the FileDaemon with VSS
66 * These functions uses the VSSObject to resolve a Path to a Snapshot Path,
67 * the VSSObject is available "per job", and some jobs such as Restore or Verify
68 * may not have a VSSObject.
71 static BOOL default_VSSPathConverter()
76 static t_pVSSPathConvert g_pVSSPathConvert = NULL;
77 static t_pVSSPathConvertW g_pVSSPathConvertW = NULL;
78 static t_pVSSPathConverter g_pVSSPathConverter = default_VSSPathConverter; /* To know if we can use the VSSPath functions */
81 void SetVSSPathConvert(t_pVSSPathConverter pPathConverter, t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
83 g_pVSSPathConvert = pPathConvert;
84 g_pVSSPathConvertW = pPathConvertW;
85 g_pVSSPathConverter = pPathConverter;
88 static void Win32ConvInitCache()
90 if (g_pWin32ConvUTF8Cache) {
93 g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME);
94 g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME);
97 void Win32ConvCleanupCache()
100 if (g_pWin32ConvUTF8Cache) {
101 free_pool_memory(g_pWin32ConvUTF8Cache);
102 g_pWin32ConvUTF8Cache = NULL;
105 if (g_pWin32ConvUCS2Cache) {
106 free_pool_memory(g_pWin32ConvUCS2Cache);
107 g_pWin32ConvUCS2Cache = NULL;
110 g_dwWin32ConvUTF8strlen = 0;
115 /* to allow the usage of the original version in this file here */
119 //#define USE_WIN32_COMPAT_IO 1
120 #define USE_WIN32_32KPATHCONVERSION 1
122 extern DWORD g_platform_id;
123 extern DWORD g_MinorVersion;
125 /* From Microsoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 */
127 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL
129 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
132 #define WIN32_FILETIME_SCALE 10000000 // 100ns/second
135 * Convert from UTF-8 to VSS Windows path/file
136 * Used by compatibility layer for Unix system calls
138 static void conv_unix_to_vss_win32_path(const char *name, char *win32_name, DWORD dwSize)
140 const char *fname = name;
141 char *tname = win32_name;
143 Dmsg0(dbglvl, "Enter convert_unix_to_win32_path\n");
145 if (IsPathSeparator(name[0]) &&
146 IsPathSeparator(name[1]) &&
148 IsPathSeparator(name[3])) {
150 *win32_name++ = '\\';
151 *win32_name++ = '\\';
153 *win32_name++ = '\\';
156 } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS && !g_pVSSPathConverter()) {
157 /* allow path to be 32767 bytes */
158 *win32_name++ = '\\';
159 *win32_name++ = '\\';
161 *win32_name++ = '\\';
165 /** Check for Unix separator and convert to Win32 */
166 if (name[0] == '/' && name[1] == '/') { /* double slash? */
167 name++; /* yes, skip first one */
170 *win32_name++ = '\\'; /* convert char */
171 /* If Win32 separator that is "quoted", remove quote */
172 } else if (*name == '\\' && name[1] == '\\') {
173 *win32_name++ = '\\';
174 name++; /* skip first \ */
176 *win32_name++ = *name; /* copy character */
180 /** Strip any trailing slash, if we stored something
181 * but leave "c:\" with backslash (root directory case
183 if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
189 /** here we convert to VSS specific file name which
190 can get longer because VSS will make something like
191 \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
192 from c:\bacula\uninstall.exe
194 Dmsg1(dbglvl, "path=%s\n", tname);
195 if (g_pVSSPathConverter()) {
196 POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
197 pszBuf = check_pool_memory_size(pszBuf, dwSize);
198 bstrncpy(pszBuf, tname, strlen(tname)+1);
199 g_pVSSPathConvert(pszBuf, tname, dwSize);
200 free_pool_memory(pszBuf);
203 Dmsg1(dbglvl, "Leave cvt_u_to_win32_path path=%s\n", tname);
206 /** Conversion of a Unix filename to a Win32 filename */
207 void unix_name_to_win32(POOLMEM **win32_name, const char *name)
209 /* One extra byte should suffice, but we double it */
210 /* add MAX_PATH bytes for VSS shadow copy name */
211 DWORD dwSize = 2*strlen(name)+MAX_PATH;
212 *win32_name = check_pool_memory_size(*win32_name, dwSize);
213 conv_unix_to_vss_win32_path(name, *win32_name, dwSize);
218 * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
219 * will complete the input path to an absolue path of the form \\?\c:\path\file
221 * With this trick, it is possible to have 32K characters long paths.
223 * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
224 * to a raw windows partition.
226 * created 02/27/2006 Thorsten Engel
229 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
232 Dmsg0(dbglvl, "Enter wchar_win32_path\n");
234 *pBIsRawPath = FALSE; /* Initialize, set later */
237 if (!p_GetCurrentDirectoryW) {
238 Dmsg0(dbglvl, "Leave wchar_win32_path no change \n");
242 wchar_t *name = (wchar_t *)pszUCSPath;
244 /* if it has already the desired form, exit without changes */
245 if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
246 Dmsg0(dbglvl, "Leave wchar_win32_path no change \n");
250 wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
251 wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME);
252 DWORD dwCurDirPathSize = 0;
254 /* get buffer with enough size (name+max 6. wchars+1 null terminator */
255 DWORD dwBufCharsNeeded = (wcslen(name)+7);
256 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
258 /* add \\?\ to support 32K long filepaths
259 it is important to make absolute paths, so we add drive and
260 current path if necessary */
262 BOOL bAddDrive = TRUE;
263 BOOL bAddCurrentPath = TRUE;
264 BOOL bAddPrefix = TRUE;
266 /* does path begin with drive? if yes, it is absolute */
267 if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) {
269 bAddCurrentPath = FALSE;
272 /* is path absolute? */
273 if (IsPathSeparator(name[0]))
274 bAddCurrentPath = FALSE;
276 /* is path relative to itself?, if yes, skip ./ */
277 if (name[0] == '.' && IsPathSeparator(name[1])) {
281 /* is path of form '//./'? */
282 if (IsPathSeparator(name[0]) &&
283 IsPathSeparator(name[1]) &&
285 IsPathSeparator(name[3])) {
287 bAddCurrentPath = FALSE;
294 int nParseOffset = 0;
296 /* add 4 bytes header */
299 wcscpy(pwszBuf, L"\\\\?\\");
302 /* get current path if needed */
303 if (bAddDrive || bAddCurrentPath) {
304 dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
305 if (dwCurDirPathSize > 0) {
306 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
307 pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
308 p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf);
310 /* we have no info for doing so */
312 bAddCurrentPath = FALSE;
317 /* add drive if needed */
318 if (bAddDrive && !bAddCurrentPath) {
321 if (IsPathSeparator(pwszCurDirBuf[0]) &&
322 IsPathSeparator(pwszCurDirBuf[1]) &&
323 pwszCurDirBuf[2] == '?' &&
324 IsPathSeparator(pwszCurDirBuf[3])) {
325 /* copy drive character */
326 szDrive[0] = pwszCurDirBuf[4];
328 /* copy drive character */
329 szDrive[0] = pwszCurDirBuf[0];
335 wcscat(pwszBuf, szDrive);
339 /* add path if needed */
340 if (bAddCurrentPath) {
341 /* the 1 add. character is for the eventually added backslash */
342 dwBufCharsNeeded += dwCurDirPathSize+1;
343 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
344 /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
346 if (IsPathSeparator(pwszCurDirBuf[0]) &&
347 IsPathSeparator(pwszCurDirBuf[1]) &&
348 pwszCurDirBuf[2] == '?' &&
349 IsPathSeparator(pwszCurDirBuf[3])) {
350 /* copy complete string */
351 wcscpy(pwszBuf, pwszCurDirBuf);
354 wcscat(pwszBuf, pwszCurDirBuf);
357 nParseOffset = wcslen((LPCWSTR) pwszBuf);
359 /* check if path ends with backslash, if not, add one */
360 if (!IsPathSeparator(pwszBuf[nParseOffset-1])) {
361 wcscat(pwszBuf, L"\\");
366 wchar_t *win32_name = &pwszBuf[nParseOffset];
367 wchar_t *name_start = name;
370 /* Check for Unix separator and convert to Win32, eliminating
371 * duplicate separators.
373 if (IsPathSeparator(*name)) {
374 *win32_name++ = '\\'; /* convert char */
376 /* Eliminate consecutive slashes, but not at the start so that
379 if (name_start != name && IsPathSeparator(name[1])) {
383 *win32_name++ = *name; /* copy character */
388 /* null terminate string */
391 /* here we convert to VSS specific file name which
392 * can get longer because VSS will make something like
393 * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
394 * from c:\bacula\uninstall.exe
396 if (g_pVSSPathConvertW != NULL && g_pVSSPathConverter()) {
397 /* is output buffer large enough? */
398 pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf,
399 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
400 /* create temp. buffer */
401 wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME);
402 pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf,
403 (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
408 wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset);
409 g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH);
410 free_pool_memory((POOLMEM *)pszBuf);
413 free_pool_memory(pszUCSPath);
414 free_pool_memory((POOLMEM *)pwszCurDirBuf);
416 Dmsg1(dbglvl, "Leave wchar_win32_path=%s\n", pwszBuf);
417 return (POOLMEM *)pwszBuf;
421 * Convert from WCHAR (UCS) to UTF-8
424 wchar_2_UTF8(POOLMEM **pszUTF, const wchar_t *pszUCS)
427 * The return value is the number of bytes written to the buffer.
428 * The number includes the byte for the null terminator.
431 if (p_WideCharToMultiByte) {
432 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,NULL,0,NULL,NULL);
433 *pszUTF = check_pool_memory_size(*pszUTF, nRet);
434 return p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,*pszUTF,nRet,NULL,NULL);
441 * Convert from WCHAR (UCS) to UTF-8
444 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
447 * The return value is the number of bytes written to the buffer.
448 * The number includes the byte for the null terminator.
451 if (p_WideCharToMultiByte) {
452 int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
460 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
462 /* the return value is the number of wide characters written to the buffer. */
463 /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
465 if (p_MultiByteToWideChar) {
466 /* strlen of UTF8 +1 is enough */
467 DWORD cchSize = (strlen(pszUTF)+1);
468 *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
470 int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
479 wchar_win32_path(const char *name, wchar_t *win32_name)
481 const char *fname = name;
483 /* Check for Unix separator and convert to Win32 */
485 *win32_name++ = '\\'; /* convert char */
486 /* If Win32 separated that is "quoted", remove quote */
487 } else if (*name == '\\' && name[1] == '\\') {
488 *win32_name++ = '\\';
489 name++; /* skip first \ */
491 *win32_name++ = *name; /* copy character */
495 /* Strip any trailing slash, if we stored something */
496 if (*fname != 0 && win32_name[-1] == '\\') {
504 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
507 /* if we find the utf8 string in cache, we use the cached ucs2 version.
508 we compare the stringlength first (quick check) and then compare the content.
510 if (!g_pWin32ConvUTF8Cache) {
511 Win32ConvInitCache();
512 } else if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
513 if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
514 /* Return cached value */
515 int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
516 *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);
517 wcscpy((LPWSTR) *pszUCS, (LPWSTR)g_pWin32ConvUCS2Cache);
519 return nBufSize / sizeof (WCHAR);
523 /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
524 int nRet = UTF8_2_wchar(pszUCS, pszUTF);
526 #ifdef USE_WIN32_32KPATHCONVERSION
527 /* add \\?\ to support 32K long filepaths */
528 *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
531 *pBIsRawPath = FALSE;
535 g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
536 wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
538 g_dwWin32ConvUTF8strlen = strlen(pszUTF);
539 g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+2);
540 bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
546 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
553 #ifndef LOAD_WITH_ALTERED_SEARCH_PATH
554 #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
557 void *dlopen(const char *file, int mode)
561 handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
565 void *dlsym(void *handle, const char *name)
568 symaddr = (void *)GetProcAddress((HMODULE)handle, name);
572 int dlclose(void *handle)
574 if (handle && !FreeLibrary((HMODULE)handle)) {
575 errno = b_errno_win32;
576 return 1; /* failed */
583 static char buf[200];
584 const char *err = errorString();
585 bstrncpy(buf, (char *)err, sizeof(buf));
586 LocalFree((void *)err);
590 int fcntl(int fd, int cmd)
595 int chown(const char *k, uid_t, gid_t)
600 int lchown(const char *k, uid_t, gid_t)
612 srandom(unsigned int seed)
616 // /////////////////////////////////////////////////////////////////
617 // convert from Windows concept of time to Unix concept of time
618 // /////////////////////////////////////////////////////////////////
620 cvt_utime_to_ftime(const time_t &time, FILETIME &wintime)
622 uint64_t mstime = time;
623 mstime *= WIN32_FILETIME_SCALE;
624 mstime += WIN32_FILETIME_ADJUST;
626 #if defined(_MSC_VER)
627 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
629 wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
631 wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
635 cvt_ftime_to_utime(const FILETIME &time)
637 uint64_t mstime = time.dwHighDateTime;
639 mstime |= time.dwLowDateTime;
641 mstime -= WIN32_FILETIME_ADJUST;
642 mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
644 return (time_t) (mstime & 0xffffffff);
647 static const char *errorString(void)
651 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
652 FORMAT_MESSAGE_FROM_SYSTEM |
653 FORMAT_MESSAGE_IGNORE_INSERTS,
656 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
661 /* Strip any \r or \n */
662 char *rval = (char *) lpMsgBuf;
663 char *cp = strchr(rval, '\r');
667 cp = strchr(rval, '\n');
676 * This is only called for directories, and is used to get the directory
677 * attributes and find out if we have a junction point or a mount point
678 * or other kind of "funny" directory.
681 statDir(const char *file, struct stat *sb, POOLMEM **readlnk=NULL)
683 WIN32_FIND_DATAW info_w; // window's file info
684 WIN32_FIND_DATAA info_a; // window's file info
686 // cache some common vars to make code more transparent
687 DWORD *pdwFileAttributes;
688 DWORD *pnFileSizeHigh;
689 DWORD *pnFileSizeLow;
691 FILETIME *pftLastAccessTime;
692 FILETIME *pftLastWriteTime;
693 HANDLE h = INVALID_HANDLE_VALUE;
696 * Oh, cool, another exception: Microsoft doesn't let us do
697 * FindFile operations on a Drive, so simply fake root attibutes.
699 if (file[1] == ':' && file[2] == 0) {
700 time_t now = time(NULL);
701 Dmsg1(dbglvl, "faking ROOT attrs(%s).\n", file);
702 sb->st_mode = S_IFDIR;
703 sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
704 sb->st_ctime = now; /* File change time (inode change...) */
705 sb->st_mtime = now; /* File modify time */
706 sb->st_atime = now; /* File access time */
713 if (p_FindFirstFileW) {
714 POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
715 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
717 Dmsg1(dbglvl, "FindFirstFileW=%s\n", file);
718 h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w);
719 free_pool_memory(pwszBuf);
721 pdwFileAttributes = &info_w.dwFileAttributes;
722 pdwReserved0 = &info_w.dwReserved0;
723 pnFileSizeHigh = &info_w.nFileSizeHigh;
724 pnFileSizeLow = &info_w.nFileSizeLow;
725 pftLastAccessTime = &info_w.ftLastAccessTime;
726 pftLastWriteTime = &info_w.ftLastWriteTime;
729 } else if (p_FindFirstFileA) {
730 Dmsg1(dbglvl, "FindFirstFileA=%s\n", file);
731 h = p_FindFirstFileA(file, &info_a);
733 pdwFileAttributes = &info_a.dwFileAttributes;
734 pdwReserved0 = &info_a.dwReserved0;
735 pnFileSizeHigh = &info_a.nFileSizeHigh;
736 pnFileSizeLow = &info_a.nFileSizeLow;
737 pftLastAccessTime = &info_a.ftLastAccessTime;
738 pftLastWriteTime = &info_a.ftLastWriteTime;
740 Dmsg0(dbglvl, "No findFirstFile A or W found\n");
743 if (h == INVALID_HANDLE_VALUE) {
744 const char *err = errorString();
746 * Note, in creating leading paths, it is normal that
747 * the file does not exist.
749 Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err);
750 LocalFree((void *)err);
751 errno = b_errno_win32;
757 sb->st_mode = 0777; /* start with everything */
758 if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
759 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
760 if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
761 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
762 if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
763 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
764 if (*pdwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
765 sb->st_mode |= S_ISGID; /* use set group ID -> encrypted */
766 sb->st_mode |= S_IFDIR;
767 sb->st_fattrs = *pdwFileAttributes;
768 Dmsg1(200, "Fattrs=0x%x\n", sb->st_fattrs);
770 * Store reparse/mount point info in st_rdev. Note a
771 * Win32 reparse point (junction point) is like a link
772 * though it can have many properties (directory link,
773 * soft link, hard link, HSM, ...
774 * A mount point is a reparse point where another volume
775 * is mounted, so it is like a Unix mount point (change of
778 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
779 sb->st_rdev = WIN32_MOUNT_POINT;
783 /* This is a lot of work just to know that it is deduped */
784 if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
785 (*pdwReserved0 & IO_REPARSE_TAG_DEDUP)) {
786 sb->st_fattrs |= FILE_ATTRIBUTE_DEDUP; /* add our own bit */
788 if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
789 (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT)) {
790 sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */
792 * Now to find out if the directory is a mount point or
793 * a reparse point, we must do a song and a dance.
794 * Explicitly open the file to read the reparse point, then
795 * call DeviceIoControl to find out if it points to a Volume
798 h = INVALID_HANDLE_VALUE;
799 if (p_GetFileAttributesW) {
800 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
801 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
803 h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
804 FILE_SHARE_READ, NULL, OPEN_EXISTING,
805 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
808 free_pool_memory(pwszBuf);
809 } else if (p_GetFileAttributesA) {
810 h = CreateFileA(file, GENERIC_READ,
811 FILE_SHARE_READ, NULL, OPEN_EXISTING,
812 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
815 if (h != INVALID_HANDLE_VALUE) {
817 REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)dummy;
818 rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
821 ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT,
822 NULL, 0, /* in buffer, bytes */
823 (LPVOID)rdb, (DWORD)sizeof(dummy), /* out buffer, btyes */
824 (LPDWORD)&bytes, (LPOVERLAPPED)0);
826 POOLMEM *utf8 = get_pool_memory(PM_NAME);
827 wchar_2_UTF8(&utf8, (wchar_t *)rdb->SymbolicLinkReparseBuffer.PathBuffer);
828 Dmsg2(dbglvl, "Junction %s points to: %s\n", file, utf8);
829 if (strncasecmp(utf8, "\\??\\volume{", 11) == 0) {
830 sb->st_rdev = WIN32_MOUNT_POINT;
832 /* It points to a directory so we ignore it. */
833 sb->st_rdev = WIN32_JUNCTION_POINT;
835 /* If requested, store the link for future use */
837 pm_strcpy(readlnk, utf8);
839 free_pool_memory(utf8);
843 Dmsg1(dbglvl, "Invalid handle from CreateFile(%s)\n", file);
846 Dmsg2(dbglvl, "st_rdev=%d file=%s\n", sb->st_rdev, file);
847 sb->st_size = *pnFileSizeHigh;
849 sb->st_size |= *pnFileSizeLow;
850 sb->st_blksize = 4096;
851 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
853 sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
854 sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
855 sb->st_ctime = MAX(sb->st_mtime, sb->st_ctime);
856 /* Note ctime is last change time -- not creation time */
857 Dmsg1(200, "Fattrs=0x%x\n", sb->st_fattrs);
862 /* On success, readlink() returns the number of bytes placed in buf. On
863 * error, -1 is returned and errno is set to indicate the error.
865 * TODO: Still need to activate the readlink() call in find_one.c
866 * by returning a S_ISLNK(st_mode) compatible flag, probably
870 readlink(const char *path, char *buf, int bufsiz)
874 POOLMEM *lnk = get_pool_memory(PM_FNAME);
876 if (statDir(path, &sb, &lnk) == 0) {
877 ret = bstrncpy(buf, lnk, bufsiz) - buf - 1; // Don't count the last \0
879 free_pool_memory(lnk);
883 /* symlink() shall return 0; otherwise, it shall return -1 and set errno to
884 * indicate the error.
887 symlink(const char *path1, const char *path2)
892 POOLMEM* pwszBuf = NULL;
893 POOLMEM* pwszBuf2 = NULL;
895 if (stat(path1, &st) == 0) {
896 if (st.st_mode & S_IFDIR) {
900 Dmsg1(200, "Canot find the source directory %s\n", path1);
904 if (p_CreateSymbolicLinkW) {
905 pwszBuf = get_pool_memory (PM_FNAME);
906 make_win32_path_UTF8_2_wchar(&pwszBuf, path1);
908 pwszBuf2 = get_pool_memory (PM_FNAME);
909 make_win32_path_UTF8_2_wchar(&pwszBuf2, path2);
911 Dmsg2(dbglvl, "Trying to symlink (%ls -> %ls)\n", pwszBuf, pwszBuf2);
913 if (!p_CreateSymbolicLinkW((LPCWSTR)pwszBuf2, (LPCWSTR)pwszBuf, isdir)) {
914 const char *err = errorString();
915 Dmsg3(200, "Cannot create symlink (%ls -> %ls):%s\n", pwszBuf, pwszBuf2, err);
916 LocalFree((void *)err);
917 errno = b_errno_win32;
921 } else if (p_CreateSymbolicLinkA) {
923 if (!p_CreateSymbolicLinkA(path2, path1, isdir)) {
924 const char *err = errorString();
925 Dmsg3(200, "Cannot create symlink (%s -> %s):%s\n", path1, path2, err);
926 LocalFree((void *)err);
927 errno = b_errno_win32;
932 Dmsg0(200, "No implementation of CreateSymbolicLink available\n");
937 free_pool_memory(pwszBuf2);
938 free_pool_memory(pwszBuf);
944 /* Do a stat() on a valid HANDLE (opened with CreateFile()) */
945 int hstat(HANDLE h, struct stat *sb)
947 BY_HANDLE_FILE_INFORMATION info;
949 if (!GetFileInformationByHandle(h, &info)) {
950 const char *err = errorString();
951 Dmsg1(dbglvl, "GetfileInformationByHandle: %s\n", err);
952 LocalFree((void *)err);
953 errno = b_errno_win32;
957 /* We should modify only variables that are modified in stat()
958 * everything else should be carefully tested.
961 /* When turned on, we wee a lot of messages such as
962 * C:/PerfLogs is a different filesystem. Will not descend from C:/ into it.
964 //sb->st_dev = info.dwVolumeSerialNumber;
966 /* The st_ino is not used in stat() */
967 sb->st_ino = info.nFileIndexHigh;
969 sb->st_ino |= info.nFileIndexLow;
972 #if 0 // We don't have the link() call right now
973 // TODO: something with CreateHardLinkFunc()
974 sb->st_nlink = (short)info.nNumberOfLinks;
975 if (sb->st_nlink > 1) {
976 Dmsg1(dbglvl, "st_nlink=%d\n", sb->st_nlink);
979 sb->st_mode = 0777; /* start with everything */
980 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
981 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
982 if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
983 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
984 if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
985 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
986 if (info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
987 sb->st_mode |= S_ISGID; /* use set group ID -> encrypted */
988 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
989 sb->st_mode |= S_IFDIR;
991 sb->st_mode |= S_IFREG;
993 sb->st_fattrs = info.dwFileAttributes;
995 /* Use st_rdev to store reparse attribute */
996 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
997 sb->st_rdev = WIN32_REPARSE_POINT;
999 Dmsg3(dbglvl, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino),
1000 (long long)sb->st_ino);
1002 sb->st_size = info.nFileSizeHigh;
1004 sb->st_size |= info.nFileSizeLow;
1005 sb->st_blksize = 4096;
1006 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
1007 sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
1008 sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
1009 sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
1011 /* Get the ChangeTime information with an other API, when attributes are modified
1012 * the ChangeTime is modified while CreationTime and WriteTime are not
1014 FILE_BASIC_INFO file_basic_info;
1015 if (p_GetFileInformationByHandleEx &&
1016 p_GetFileInformationByHandleEx(h, FileBasicInfo, &file_basic_info, sizeof(file_basic_info))) {
1017 FILETIME *pftChangeTime = (FILETIME *)&file_basic_info.ChangeTime;
1018 sb->st_ctime = cvt_ftime_to_utime(*pftChangeTime);
1021 Dmsg1(200, "Fattrs=0x%x\n", sb->st_fattrs);
1026 /* Emulate unix stat() call on windows */
1027 static int stat2(const char *file, struct stat *sb)
1030 HANDLE h = INVALID_HANDLE_VALUE;
1034 memset(sb, 0, sizeof(*sb));
1036 /* We cannot stat a drive */
1037 if (file[1] == ':' && (file[2] == 0 || (IsPathSeparator(file[2]) && file[3] == 0))) {
1038 return statDir(file, sb);
1041 fname = get_pool_memory(PM_FNAME);
1042 unix_name_to_win32(&fname, file);
1044 if (p_CreateFileW) {
1045 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1046 make_win32_path_UTF8_2_wchar(&pwszBuf, fname);
1048 h = p_CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ,
1049 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1050 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1051 free_pool_memory(pwszBuf);
1054 h = CreateFileA(fname, GENERIC_READ,
1055 FILE_SHARE_READ, NULL, OPEN_EXISTING,
1056 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1059 if (h == INVALID_HANDLE_VALUE) {
1060 const char *err = errorString();
1061 Dmsg2(dbglvl, "Cannot open file for stat (%s):%s\n", fname, err);
1062 LocalFree((void *)err);
1063 errno = b_errno_win32;
1068 rval = hstat(h, sb);
1071 if (sb->st_mode & S_IFDIR &&
1072 file[1] == ':' && file[2] != 0) {
1073 rval = statDir(file, sb);
1074 // TODO: See if we really need statDir(), we can probably take only
1075 // the code for the ReparsePoint
1078 free_pool_memory(fname);
1083 stat(const char *file, struct stat *sb)
1086 WIN32_FILE_ATTRIBUTE_DATA data;
1089 memset(sb, 0, sizeof(*sb));
1091 /* We do the first try with a file HANDLER, because we want to use the
1092 * ChangeTime that is only available with GetFileInformationByHandleEx
1094 ret = stat2(file, sb);
1100 /* We were not able to open a filehandler on the file to get attributes, so
1101 * so we try with the name. It may happen of example with encrypted files.
1104 if (p_GetFileAttributesExW) {
1105 /* dynamically allocate enough space for UCS2 filename */
1106 POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
1107 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
1109 BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data);
1110 free_pool_memory(pwszBuf);
1113 const char *err = errorString();
1114 Dmsg2(10, "GetFileAttributesExW(%s):%s\n", file, err);
1115 LocalFree((void *)err);
1119 } else if (p_GetFileAttributesExA) {
1120 if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
1121 const char *err = errorString();
1122 Dmsg2(10, "GetFileAttributesExW(%s):%s\n", file, err);
1123 LocalFree((void *)err);
1127 return -1; // Not implemented
1130 sb->st_mode = 0777; /* start with everything */
1131 if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1132 sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
1134 if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
1135 sb->st_mode &= ~S_IRWXO; /* remove everything for other */
1137 if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
1138 sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
1140 if (data.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
1141 sb->st_mode |= S_ISGID; /* use set group ID -> encrypted */
1143 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1144 sb->st_mode |= S_IFDIR;
1146 sb->st_mode |= S_IFREG;
1148 sb->st_fattrs = data.dwFileAttributes;
1150 /* Use st_rdev to store reparse attribute */
1151 sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0;
1154 sb->st_size = data.nFileSizeHigh;
1156 sb->st_size |= data.nFileSizeLow;
1157 sb->st_blksize = 4096;
1158 sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
1159 sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
1160 sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
1161 sb->st_ctime = sb->st_mtime;
1164 * If we are not at the root, then to distinguish a reparse
1165 * point from a mount point, we must call FindFirstFile() to
1166 * get the WIN32_FIND_DATA, which has the bit that indicates
1167 * that this directory is a mount point -- aren't Win32 APIs
1168 * wonderful? (sarcasm). The code exists in the statDir
1171 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
1172 file[1] == ':' && file[2] != 0) {
1175 Dmsg3(dbglvl, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino),
1176 (long long)sb->st_ino, file);
1177 Dmsg1(200, "Fattrs=0x%x\n", sb->st_fattrs);
1182 fstat(intptr_t fd, struct stat *sb)
1184 return hstat((HANDLE)_get_osfhandle(fd), sb);
1188 * We write our own ftruncate because the one in the
1189 * Microsoft library mrcrt.dll does not truncate
1190 * files greater than 2GB.
1193 int win32_ftruncate(int fd, int64_t length)
1195 /* Set point we want to truncate file */
1196 __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET);
1198 if (pos != (__int64)length) {
1199 errno = EACCES; /* truncation failed, get out */
1204 if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) {
1205 errno = b_errno_win32;
1212 int fcntl(int fd, int cmd, long arg)
1235 lstat(const char *file, struct stat *sb)
1237 return stat(file, sb);
1253 execvp(const char *, char *[]) {
1274 waitpid(int, int*, int)
1282 strcasecmp(const char *s1, const char *s2)
1284 register int ch1, ch2;
1287 return 0; /* strings are equal if same object. */
1297 } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
1304 strncasecmp(const char *s1, const char *s2, int len)
1306 register int ch1 = 0, ch2 = 0;
1309 return 0; /* strings are equal if same object. */
1320 if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
1327 gettimeofday(struct timeval *tv, struct timezone *)
1332 GetSystemTime(&now);
1338 if (!SystemTimeToFileTime(&now, &tmp)) {
1339 errno = b_errno_win32;
1343 int64_t _100nsec = tmp.dwHighDateTime;
1345 _100nsec |= tmp.dwLowDateTime;
1346 _100nsec -= WIN32_FILETIME_ADJUST;
1348 tv->tv_sec = (long)(_100nsec / 10000000);
1349 tv->tv_usec = (long)((_100nsec % 10000000)/10);
1355 * Write in Windows System log
1357 void syslog(int type, const char *fmt, ...)
1363 msg = get_pool_memory(PM_EMSG);
1366 maxlen = sizeof_pool_memory(msg) - 1;
1367 va_start(arg_ptr, fmt);
1368 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
1370 if (len < 0 || len >= (maxlen-5)) {
1371 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
1376 LogErrorMsg((const char *)msg);
1397 // implement opendir/readdir/closedir on top of window's API
1401 WIN32_FIND_DATAA data_a; // window's file info (ansii version)
1402 WIN32_FIND_DATAW data_w; // window's file info (wchar version)
1403 const char *spec; // the directory we're traversing
1404 HANDLE dirh; // the search handle
1405 BOOL valid_a; // the info in data_a field is valid
1406 BOOL valid_w; // the info in data_w field is valid
1407 UINT32 offset; // pseudo offset for d_off
1411 opendir(const char *path)
1413 /* enough space for VSS !*/
1414 int max_len = strlen(path) + MAX_PATH;
1422 Dmsg1(dbglvl, "Opendir path=%s\n", path);
1423 rval = (_dir *)malloc(sizeof(_dir));
1427 memset (rval, 0, sizeof (_dir));
1429 tspec = (char *)malloc(max_len);
1434 conv_unix_to_vss_win32_path(path, tspec, max_len);
1435 Dmsg1(dbglvl, "win32 path=%s\n", tspec);
1437 // add backslash only if there is none yet (think of c:\)
1438 if (tspec[strlen(tspec)-1] != '\\')
1439 bstrncat(tspec, "\\*", max_len);
1441 bstrncat(tspec, "*", max_len);
1445 // convert to wchar_t
1446 if (p_FindFirstFileW) {
1447 POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1448 make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1450 rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1452 free_pool_memory(pwcBuf);
1454 if (rval->dirh != INVALID_HANDLE_VALUE)
1456 } else if (p_FindFirstFileA) {
1457 rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1459 if (rval->dirh != INVALID_HANDLE_VALUE)
1464 Dmsg3(dbglvl, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1465 path, rval->spec, rval->dirh);
1468 if (rval->dirh == INVALID_HANDLE_VALUE)
1471 if (rval->valid_w) {
1472 Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_w.cFileName);
1475 if (rval->valid_a) {
1476 Dmsg1(dbglvl, "\tFirstFile=%s\n", rval->data_a.cFileName);
1488 errno = b_errno_win32;
1495 _dir *dp = (_dir *)dirp;
1496 FindClose(dp->dirh);
1497 free((void *)dp->spec);
1503 typedef struct _WIN32_FIND_DATA {
1504 DWORD dwFileAttributes;
1505 FILETIME ftCreationTime;
1506 FILETIME ftLastAccessTime;
1507 FILETIME ftLastWriteTime;
1508 DWORD nFileSizeHigh;
1512 TCHAR cFileName[MAX_PATH];
1513 TCHAR cAlternateFileName[14];
1514 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1518 copyin(struct dirent &dp, const char *fname)
1522 char *cp = dp.d_name;
1532 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1534 _dir *dp = (_dir *)dirp;
1535 if (dp->valid_w || dp->valid_a) {
1536 entry->d_off = dp->offset;
1540 POOLMEM *szBuf = get_pool_memory(PM_NAME);
1541 wchar_2_UTF8(&szBuf, dp->data_w.cFileName);
1542 dp->offset += copyin(*entry, szBuf);
1543 free_pool_memory(szBuf);
1544 } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1545 dp->offset += copyin(*entry, dp->data_a.cFileName);
1548 *result = entry; /* return entry address */
1549 Dmsg4(dbglvl, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1550 dirp, entry->d_name, entry->d_reclen, entry->d_off);
1552 // Dmsg0(dbglvl, "readdir_r !valid\n");
1553 errno = b_errno_win32;
1557 // get next file, try unicode first
1558 if (p_FindNextFileW)
1559 dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1560 else if (p_FindNextFileA)
1561 dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1563 dp->valid_a = FALSE;
1564 dp->valid_w = FALSE;
1571 * Dotted IP address to network address
1577 inet_aton(const char *a, struct in_addr *inp)
1580 uint32_t acc = 0, tmp = 0;
1583 if (!isdigit(*cp)) { /* first char must be digit */
1584 return 0; /* error */
1588 tmp = (tmp * 10) + (*cp -'0');
1589 } else if (*cp == '.' || *cp == 0) {
1591 return 0; /* error */
1593 acc = (acc << 8) + tmp;
1597 return 0; /* error */
1599 } while (*cp++ != 0);
1600 if (dotc != 4) { /* want 3 .'s plus EOS */
1601 return 0; /* error */
1603 inp->s_addr = htonl(acc); /* store addr in network format */
1609 * Convert from presentation format (which usually means ASCII printable)
1610 * to network format (which is usually some kind of binary format).
1612 * 1 if the address was valid for the specified address family
1613 * 0 if the address wasn't valid (`dst' is untouched in this case)
1616 binet_pton(int af, const char *src, void *dst)
1622 return p_InetPton(af, src, dst);
1632 nanosleep(const struct timespec *req, struct timespec *rem)
1635 rem->tv_sec = rem->tv_nsec = 0;
1636 Sleep((req->tv_sec * 1000) + (req->tv_nsec/1000000));
1641 init_signals(void terminate(int sig))
1647 init_stack_dump(void)
1654 pathconf(const char *path, int name)
1658 if (strncmp(path, "\\\\?\\", 4) == 0)
1670 WORD wVersionRequested = MAKEWORD(2, 2);
1673 int err = WSAStartup(wVersionRequested, &wsaData);
1675 wVersionRequested = MAKEWORD(2, 0);
1676 err = WSAStartup(wVersionRequested, &wsaData);
1678 wVersionRequested = MAKEWORD(1, 1);
1679 err = WSAStartup(wVersionRequested, &wsaData);
1684 printf("Can not start Windows Sockets\n");
1692 static DWORD fill_attribute(DWORD attr, mode_t mode)
1694 Dmsg1(dbglvl, " before attr=%lld\n", (uint64_t) attr);
1695 /* Use Bacula mappings define in stat() above */
1696 if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { // If file is readable
1697 attr &= ~FILE_ATTRIBUTE_READONLY; // then this is not READONLY
1699 attr |= FILE_ATTRIBUTE_READONLY;
1701 if (mode & S_ISVTX) { // The sticky bit <=> HIDDEN
1702 attr |= FILE_ATTRIBUTE_HIDDEN;
1704 attr &= ~FILE_ATTRIBUTE_HIDDEN;
1706 if (mode & S_ISGID) { // The set group ID <=> ENCRYPTED
1707 attr |= FILE_ATTRIBUTE_ENCRYPTED;
1709 attr &= ~FILE_ATTRIBUTE_ENCRYPTED;
1711 if (mode & S_IRWXO) { // Other can read/write/execute ?
1712 attr &= ~FILE_ATTRIBUTE_SYSTEM; // => Not system
1714 attr |= FILE_ATTRIBUTE_SYSTEM;
1716 Dmsg1(dbglvl, " after attr=%lld\n", (uint64_t)attr);
1720 int win32_chmod(const char *path, mode_t mode)
1725 Dmsg2(dbglvl, "win32_chmod(path=%s mode=%lld)\n", path, (uint64_t)mode);
1726 if (p_GetFileAttributesW) {
1727 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1728 make_win32_path_UTF8_2_wchar(&pwszBuf, path);
1730 attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
1731 if (attr != INVALID_FILE_ATTRIBUTES) {
1732 /* Use Bacula mappings define in stat() above */
1733 attr = fill_attribute(attr, mode);
1734 ret = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr);
1736 free_pool_memory(pwszBuf);
1737 Dmsg0(dbglvl, "Leave win32_chmod. AttributesW\n");
1738 } else if (p_GetFileAttributesA) {
1739 attr = p_GetFileAttributesA(path);
1740 if (attr != INVALID_FILE_ATTRIBUTES) {
1741 attr = fill_attribute(attr, mode);
1742 ret = p_SetFileAttributesA(path, attr);
1744 Dmsg0(dbglvl, "Leave win32_chmod did AttributesA\n");
1746 Dmsg0(dbglvl, "Leave win32_chmod did nothing\n");
1750 const char *err = errorString();
1751 Dmsg2(dbglvl, "Get/SetFileAttributes(%s): %s\n", path, err);
1752 LocalFree((void *)err);
1753 errno = b_errno_win32;
1761 win32_chdir(const char *dir)
1763 if (p_SetCurrentDirectoryW) {
1764 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1765 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1767 BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1769 free_pool_memory(pwszBuf);
1772 errno = b_errno_win32;
1775 } else if (p_SetCurrentDirectoryA) {
1776 if (0 == p_SetCurrentDirectoryA(dir)) {
1777 errno = b_errno_win32;
1788 win32_mkdir(const char *dir)
1790 Dmsg1(dbglvl, "enter win32_mkdir. dir=%s\n", dir);
1792 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1793 make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1795 int n = p_wmkdir((LPCWSTR)pwszBuf);
1796 free_pool_memory(pwszBuf);
1797 Dmsg0(dbglvl, "Leave win32_mkdir did wmkdir\n");
1801 Dmsg0(dbglvl, "Leave win32_mkdir did _mkdir\n");
1807 win32_getcwd(char *buf, int maxlen)
1811 if (p_GetCurrentDirectoryW) {
1812 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1813 pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1815 n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1817 n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1818 free_pool_memory(pwszBuf);
1820 } else if (p_GetCurrentDirectoryA)
1821 n = p_GetCurrentDirectoryA(maxlen, buf);
1823 if (n <= 0 || n > maxlen) return NULL;
1825 if (n+1 > maxlen) return NULL;
1834 win32_fputs(const char *string, FILE *stream)
1836 /* we use WriteConsoleA / WriteConsoleA
1837 so we can be sure that unicode support works on win32.
1838 with fallback if something fails
1841 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1842 if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1843 p_MultiByteToWideChar && (stream == stdout)) {
1845 POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1847 DWORD dwCharsWritten;
1850 dwChars = UTF8_2_wchar(&pwszBuf, string);
1852 /* try WriteConsoleW */
1853 if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1854 free_pool_memory(pwszBuf);
1855 return dwCharsWritten;
1858 /* convert to local codepage and try WriteConsoleA */
1859 POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1860 pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1862 dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1863 free_pool_memory(pwszBuf);
1865 if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1866 free_pool_memory(pszBuf);
1867 return dwCharsWritten;
1869 free_pool_memory(pszBuf);
1872 return fputs(string, stream);
1876 win32_cgets (char* buffer, int len)
1878 /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1879 from the win32 console and fallback if seomething fails */
1881 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1882 if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1884 wchar_t wszBuf[1024];
1887 /* nt and unicode conversion */
1888 if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1890 /* null terminate at end */
1891 if (wszBuf[dwRead-1] == L'\n') {
1892 wszBuf[dwRead-1] = L'\0';
1896 if (wszBuf[dwRead-1] == L'\r') {
1897 wszBuf[dwRead-1] = L'\0';
1901 wchar_2_UTF8(buffer, wszBuf, len);
1905 /* win 9x and unicode conversion */
1906 if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1908 /* null terminate at end */
1909 if (szBuf[dwRead-1] == L'\n') {
1910 szBuf[dwRead-1] = L'\0';
1914 if (szBuf[dwRead-1] == L'\r') {
1915 szBuf[dwRead-1] = L'\0';
1919 /* convert from ansii to wchar_t */
1920 p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1921 /* convert from wchar_t to UTF-8 */
1922 if (wchar_2_UTF8(buffer, wszBuf, len))
1928 if (fgets(buffer, len, stdin))
1935 win32_unlink(const char *filename)
1939 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1940 make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1942 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1945 * special case if file is readonly,
1946 * we retry but unset attribute before
1948 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1949 DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf);
1950 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1951 if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1952 nRetCode = _wunlink((LPCWSTR) pwszBuf);
1953 /* reset to original if it didn't help */
1955 p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1959 free_pool_memory(pwszBuf);
1961 nRetCode = _unlink(filename);
1963 /* special case if file is readonly,
1964 we retry but unset attribute before */
1965 if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1966 DWORD dwAttr = p_GetFileAttributesA(filename);
1967 if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1968 if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1969 nRetCode = _unlink(filename);
1970 /* reset to original if it didn't help */
1972 p_SetFileAttributesA(filename, dwAttr);
1981 #include "mswinver.h"
1983 char WIN_VERSION_LONG[64];
1984 char WIN_VERSION[32];
1985 char WIN_RAWVERSION[32];
1992 static winver INIT; // cause constructor to be called before main()
1995 winver::winver(void)
1997 const char *version = "";
1998 const char *platform = "";
1999 OSVERSIONINFO osvinfo;
2000 osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
2002 // Get the current OS version
2003 if (!GetVersionEx(&osvinfo)) {
2004 version = "Unknown";
2005 platform = "Unknown";
2007 const int ver = _mkversion(osvinfo.dwPlatformId,
2008 osvinfo.dwMajorVersion,
2009 osvinfo.dwMinorVersion);
2010 snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
2013 case MS_WINDOWS_95: (version = "Windows 95"); break;
2014 case MS_WINDOWS_98: (version = "Windows 98"); break;
2015 case MS_WINDOWS_ME: (version = "Windows ME"); break;
2016 case MS_WINDOWS_NT4:(version = "Windows NT 4.0"); platform = "NT"; break;
2017 case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break;
2018 case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break;
2019 case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break;
2020 default: version = WIN_RAWVERSION; break;
2023 bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
2024 snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
2025 platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
2028 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
2032 BPIPE *b = open_bpipe("ls -l", 10, "r");
2034 while (!feof(b->rfd)) {
2035 fgets(buf, sizeof(buf), b->rfd);
2041 BOOL CreateChildProcess(VOID);
2042 VOID WriteToPipe(VOID);
2043 VOID ReadFromPipe(VOID);
2044 VOID ErrorExit(LPCSTR);
2045 VOID ErrMsg(LPTSTR, BOOL);
2048 * Check for a quoted path, if an absolute path name is given and it contains
2049 * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe"
2050 * CreateProcess() says the best way to ensure proper results with executables
2051 * with spaces in path or filename is to quote the string.
2054 getArgv0(const char *cmdline)
2059 for (cp = cmdline; *cp; cp++)
2064 if (!inquote && isspace(*cp))
2069 int len = cp - cmdline;
2070 char *rval = (char *)malloc(len+1);
2083 * Extracts the executable or script name from the first string in
2086 * If the name contains blanks then it must be quoted with double quotes,
2087 * otherwise quotes are optional. If the name contains blanks then it
2088 * will be converted to a short name.
2090 * The optional quotes will be removed. The result is copied to a malloc'ed
2091 * buffer and returned through the pexe argument. The pargs parameter is set
2092 * to the address of the character in cmdline located after the name.
2094 * The malloc'ed buffer returned in *pexe must be freed by the caller.
2097 GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
2099 const char *pExeStart = NULL; /* Start of executable name in cmdline */
2100 const char *pExeEnd = NULL; /* Character after executable name (separator) */
2102 const char *pBasename = NULL; /* Character after last path separator */
2103 const char *pExtension = NULL; /* Period at start of extension */
2105 const char *current = cmdline;
2107 bool bQuoted = false;
2109 /* Skip initial whitespace */
2111 while (*current == ' ' || *current == '\t')
2116 /* Calculate start of name and determine if quoted */
2118 if (*current == '"') {
2119 pExeStart = ++current;
2122 pExeStart = current;
2130 * Scan command line looking for path separators (/ and \\) and the
2131 * terminator, either a quote or a blank. The location of the
2132 * extension is also noted.
2135 for ( ; *current != '\0'; current++)
2137 if (*current == '.') {
2138 pExtension = current;
2139 } else if (IsPathSeparator(*current) && current[1] != '\0') {
2140 pBasename = ¤t[1];
2144 /* Check for terminator, either quote or blank */
2146 if (*current != '"') {
2150 if (*current != ' ') {
2156 * Hit terminator, remember end of name (address of terminator) and
2157 * start of arguments
2161 if (bQuoted && *current == '"') {
2162 *pargs = ¤t[1];
2170 if (pBasename == NULL) {
2171 pBasename = pExeStart;
2174 if (pExeEnd == NULL) {
2183 bool bHasPathSeparators = pExeStart != pBasename;
2185 /* We have pointers to all the useful parts of the name */
2187 /* Default extensions in the order cmd.exe uses to search */
2189 static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
2190 DWORD dwBasePathLength = pExeEnd - pExeStart;
2192 DWORD dwAltNameLength = 0;
2193 char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2194 char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
2196 pPathname[MAX_PATHLENGTH] = '\0';
2197 pAltPathname[MAX_PATHLENGTH] = '\0';
2199 memcpy(pPathname, pExeStart, dwBasePathLength);
2200 pPathname[dwBasePathLength] = '\0';
2202 if (pExtension == NULL) {
2203 /* Try appending extensions */
2204 for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
2206 if (!bHasPathSeparators) {
2207 /* There are no path separators, search in the standard locations */
2208 dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
2209 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2210 memcpy(pPathname, pAltPathname, dwAltNameLength);
2211 pPathname[dwAltNameLength] = '\0';
2215 bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
2216 if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
2219 pPathname[dwBasePathLength] = '\0';
2222 } else if (!bHasPathSeparators) {
2223 /* There are no path separators, search in the standard locations */
2224 dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
2225 if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
2226 memcpy(pPathname, pAltPathname, dwAltNameLength);
2227 pPathname[dwAltNameLength] = '\0';
2231 if (strchr(pPathname, ' ') != NULL) {
2232 dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
2234 if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
2235 *pexe = (char *)malloc(dwAltNameLength + 1);
2236 if (*pexe == NULL) {
2239 memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
2243 if (*pexe == NULL) {
2244 DWORD dwPathnameLength = strlen(pPathname);
2245 *pexe = (char *)malloc(dwPathnameLength + 1);
2246 if (*pexe == NULL) {
2249 memcpy(*pexe, pPathname, dwPathnameLength + 1);
2256 * Create the process with WCHAR API
2259 CreateChildProcessW(const char *comspec, const char *cmdLine,
2260 PROCESS_INFORMATION *hProcInfo,
2261 HANDLE in, HANDLE out, HANDLE err)
2263 STARTUPINFOW siStartInfo;
2264 BOOL bFuncRetn = FALSE;
2266 // Set up members of the STARTUPINFO structure.
2267 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2268 siStartInfo.cb = sizeof(siStartInfo);
2269 // setup new process to use supplied handles for stdin,stdout,stderr
2271 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2272 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2274 siStartInfo.hStdInput = in;
2275 siStartInfo.hStdOutput = out;
2276 siStartInfo.hStdError = err;
2278 // Convert argument to WCHAR
2279 POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
2280 POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
2282 UTF8_2_wchar(&cmdLine_wchar, cmdLine);
2283 UTF8_2_wchar(&comspec_wchar, comspec);
2285 // Create the child process.
2286 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
2288 // try to execute program
2289 bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
2290 (WCHAR*)cmdLine_wchar,// command line
2291 NULL, // process security attributes
2292 NULL, // primary thread security attributes
2293 TRUE, // handles are inherited
2294 0, // creation flags
2295 NULL, // use parent's environment
2296 NULL, // use parent's current directory
2297 &siStartInfo, // STARTUPINFO pointer
2298 hProcInfo); // receives PROCESS_INFORMATION
2299 free_pool_memory(cmdLine_wchar);
2300 free_pool_memory(comspec_wchar);
2307 * Create the process with ANSI API
2310 CreateChildProcessA(const char *comspec, char *cmdLine,
2311 PROCESS_INFORMATION *hProcInfo,
2312 HANDLE in, HANDLE out, HANDLE err)
2314 STARTUPINFOA siStartInfo;
2315 BOOL bFuncRetn = FALSE;
2317 // Set up members of the STARTUPINFO structure.
2318 ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
2319 siStartInfo.cb = sizeof(siStartInfo);
2320 // setup new process to use supplied handles for stdin,stdout,stderr
2321 siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
2322 siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
2324 siStartInfo.hStdInput = in;
2325 siStartInfo.hStdOutput = out;
2326 siStartInfo.hStdError = err;
2328 // Create the child process.
2329 Dmsg2(dbglvl, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
2331 // try to execute program
2332 bFuncRetn = p_CreateProcessA(comspec,
2333 cmdLine, // command line
2334 NULL, // process security attributes
2335 NULL, // primary thread security attributes
2336 TRUE, // handles are inherited
2337 0, // creation flags
2338 NULL, // use parent's environment
2339 NULL, // use parent's current directory
2340 &siStartInfo,// STARTUPINFO pointer
2341 hProcInfo);// receives PROCESS_INFORMATION
2346 * OK, so it would seem CreateProcess only handles true executables:
2347 * .com or .exe files. So grab $COMSPEC value and pass command line to it.
2350 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
2352 static const char *comspec = NULL;
2353 PROCESS_INFORMATION piProcInfo;
2354 BOOL bFuncRetn = FALSE;
2356 if (!p_CreateProcessA || !p_CreateProcessW)
2357 return INVALID_HANDLE_VALUE;
2359 if (comspec == NULL)
2360 comspec = getenv("COMSPEC");
2361 if (comspec == NULL) // should never happen
2362 return INVALID_HANDLE_VALUE;
2364 // Set up members of the PROCESS_INFORMATION structure.
2365 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
2367 // if supplied handles are not used the send a copy of our STD_HANDLE
2369 if (in == INVALID_HANDLE_VALUE)
2370 in = GetStdHandle(STD_INPUT_HANDLE);
2372 if (out == INVALID_HANDLE_VALUE)
2373 out = GetStdHandle(STD_OUTPUT_HANDLE);
2375 if (err == INVALID_HANDLE_VALUE)
2376 err = GetStdHandle(STD_ERROR_HANDLE);
2379 const char *argStart;
2381 if (!GetApplicationName(cmdline, &exeFile, &argStart)) {
2382 return INVALID_HANDLE_VALUE;
2385 POOL_MEM cmdLine(PM_FNAME);
2386 Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart);
2390 // New function disabled
2391 if (p_CreateProcessW && p_MultiByteToWideChar) {
2392 bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo,
2395 bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo,
2399 if (bFuncRetn == 0) {
2400 ErrorExit("CreateProcess failed\n");
2401 Dmsg2(dbglvl, " CreateProcess(%s, %s) failed\n",comspec,cmdLine.c_str());
2402 return INVALID_HANDLE_VALUE;
2404 // we don't need a handle on the process primary thread so we close
2406 CloseHandle(piProcInfo.hThread);
2407 return piProcInfo.hProcess;
2411 ErrorExit (LPCSTR lpszMessage)
2413 const char *err = errorString();
2414 Dmsg2(dbglvl, "%s: %s", lpszMessage, err);
2415 LocalFree((void *)err);
2416 errno = b_errno_win32;
2421 typedef struct s_bpipe {
2423 time_t worker_stime;
2432 CloseHandleIfValid(HANDLE handle)
2434 if (handle != INVALID_HANDLE_VALUE) {
2435 CloseHandle(handle);
2440 open_bpipe(char *prog, int wait, const char *mode, char *envp[])
2442 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
2443 hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
2446 SECURITY_ATTRIBUTES saAttr;
2450 hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
2451 hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
2452 hInputFile = INVALID_HANDLE_VALUE;
2454 BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
2455 memset((void *)bpipe, 0, sizeof(BPIPE));
2457 int mode_read = (mode[0] == 'r');
2458 int mode_write = (mode[0] == 'w' || mode[1] == 'w');
2461 // Set the bInheritHandle flag so pipe handles are inherited.
2463 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
2464 saAttr.bInheritHandle = TRUE;
2465 saAttr.lpSecurityDescriptor = NULL;
2469 // Create a pipe for the child process's STDOUT.
2470 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
2471 ErrorExit("Stdout pipe creation failed\n");
2474 // Create noninheritable read handle and close the inheritable read
2477 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
2478 GetCurrentProcess(), &hChildStdoutRdDup , 0,
2480 DUPLICATE_SAME_ACCESS);
2482 ErrorExit("DuplicateHandle failed");
2486 CloseHandle(hChildStdoutRd);
2487 hChildStdoutRd = INVALID_HANDLE_VALUE;
2492 // Create a pipe for the child process's STDIN.
2494 if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
2495 ErrorExit("Stdin pipe creation failed\n");
2499 // Duplicate the write handle to the pipe so it is not inherited.
2500 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
2501 GetCurrentProcess(), &hChildStdinWrDup,
2503 FALSE, // not inherited
2504 DUPLICATE_SAME_ACCESS);
2506 ErrorExit("DuplicateHandle failed");
2510 CloseHandle(hChildStdinWr);
2511 hChildStdinWr = INVALID_HANDLE_VALUE;
2513 // spawn program with redirected handles as appropriate
2514 bpipe->worker_pid = (pid_t)
2515 CreateChildProcess(prog, // commandline
2516 hChildStdinRd, // stdin HANDLE
2517 hChildStdoutWr, // stdout HANDLE
2518 hChildStdoutWr); // stderr HANDLE
2520 if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE) {
2521 ErrorExit("CreateChildProcess failed");
2526 bpipe->worker_stime = time(NULL);
2529 CloseHandle(hChildStdoutWr); // close our write side so when
2530 // process terminates we can
2532 // ugly but convert WIN32 HANDLE to FILE*
2533 int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY);
2535 bpipe->rfd = _fdopen(rfd, "rb");
2539 CloseHandle(hChildStdinRd); // close our read side so as not
2540 // to interfre with child's copy
2541 // ugly but convert WIN32 HANDLE to FILE*
2542 int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY);
2544 bpipe->wfd = _fdopen(wfd, "wb");
2549 bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait);
2556 CloseHandleIfValid(hChildStdoutWr);
2557 CloseHandleIfValid(hChildStdoutRd);
2558 CloseHandleIfValid(hChildStdoutRdDup);
2559 CloseHandleIfValid(hChildStdinWr);
2560 CloseHandleIfValid(hChildStdinRd);
2561 CloseHandleIfValid(hChildStdinWrDup);
2563 free((void *)bpipe);
2564 errno = b_errno_win32; /* do GetLastError() for error code */
2570 kill(pid_t pid, int signal)
2573 if (!TerminateProcess((HANDLE)pid, (UINT)signal)) {
2575 errno = b_errno_win32;
2577 CloseHandle((HANDLE)pid);
2583 close_bpipe(BPIPE *bpipe)
2586 int32_t remaining_wait = bpipe->wait;
2598 if (remaining_wait == 0) { /* wait indefinitely */
2599 remaining_wait = INT32_MAX;
2603 if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
2604 const char *err = errorString();
2605 rval = b_errno_win32;
2606 Dmsg1(dbglvl, "GetExitCode error %s\n", err);
2607 LocalFree((void *)err);
2610 if (exitCode == STILL_ACTIVE) {
2611 if (remaining_wait <= 0) {
2612 rval = ETIME; /* timed out */
2615 bmicrosleep(1, 0); /* wait one second */
2617 } else if (exitCode != 0) {
2618 /* Truncate exit code as it doesn't seem to be correct */
2619 rval = (exitCode & 0xFF) | b_errno_exit;
2622 break; /* Shouldn't get here */
2626 if (bpipe->timer_id) {
2627 stop_child_timer(bpipe->timer_id);
2629 if (bpipe->rfd) fclose(bpipe->rfd);
2630 if (bpipe->wfd) fclose(bpipe->wfd);
2631 free((void *)bpipe);
2636 close_wpipe(BPIPE *bpipe)
2642 if (fclose(bpipe->wfd) != 0) {
2652 utime(const char *fname, struct utimbuf *times)
2657 conv_unix_to_vss_win32_path(fname, tmpbuf, 5000);
2659 cvt_utime_to_ftime(times->actime, acc);
2660 cvt_utime_to_ftime(times->modtime, mod);
2662 HANDLE h = INVALID_HANDLE_VALUE;
2664 if (p_CreateFileW) {
2665 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2666 make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
2668 h = p_CreateFileW((LPCWSTR)pwszBuf,
2669 FILE_WRITE_ATTRIBUTES,
2670 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2673 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2676 free_pool_memory(pwszBuf);
2677 } else if (p_CreateFileA) {
2678 h = p_CreateFileA(tmpbuf,
2679 FILE_WRITE_ATTRIBUTES,
2680 FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
2683 FILE_FLAG_BACKUP_SEMANTICS, // required for directories
2687 if (h == INVALID_HANDLE_VALUE) {
2688 const char *err = errorString();
2689 Dmsg2(dbglvl, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
2690 LocalFree((void *)err);
2691 errno = b_errno_win32;
2695 int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
2698 errno = b_errno_win32;
2706 file_open(const char *file, int flags, int mode)
2709 DWORD shareMode = 0;
2712 HANDLE foo = INVALID_HANDLE_VALUE;
2713 const char *remap = file;
2715 if (flags & O_WRONLY) access = GENERIC_WRITE;
2716 else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
2717 else access = GENERIC_READ;
2719 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2720 create = CREATE_NEW;
2721 else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
2722 create = CREATE_ALWAYS;
2723 else if (flags & O_CREAT)
2724 create = OPEN_ALWAYS;
2725 else if (flags & O_TRUNC)
2726 create = TRUNCATE_EXISTING;
2728 create = OPEN_EXISTING;
2732 if (flags & O_APPEND) {
2733 printf("open...APPEND not implemented yet.");
2737 if (p_CreateFileW) {
2738 POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
2739 make_win32_path_UTF8_2_wchar(&pwszBuf, file);
2741 foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
2742 free_pool_memory(pwszBuf);
2743 } else if (p_CreateFileA)
2744 foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
2746 if (INVALID_HANDLE_VALUE == foo) {
2747 errno = b_errno_win32;
2758 if (!CloseHandle((HANDLE)fd)) {
2759 errno = b_errno_win32;
2767 file_write(int fd, const void *data, ssize_t len)
2771 status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
2772 if (status) return bwrite;
2773 errno = b_errno_win32;
2779 file_read(int fd, void *data, ssize_t len)
2784 status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
2785 if (status) return bread;
2786 errno = b_errno_win32;
2791 file_seek(int fd, boffset_t offset, int whence)
2795 LONG offset_low = (LONG)offset;
2796 LONG offset_high = (LONG)(offset >> 32);
2800 method = FILE_BEGIN;
2803 method = FILE_CURRENT;
2814 if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) {
2815 errno = b_errno_win32;
2818 /* ***FIXME*** I doubt this works right */
2832 * Emulation of mmap and unmmap for tokyo dbm
2834 void *mmap(void *start, size_t length, int prot, int flags,
2835 int fd, off_t offset)
2837 DWORD fm_access = 0;
2838 DWORD mv_access = 0;
2849 if (flags & PROT_WRITE) {
2850 fm_access |= PAGE_READWRITE;
2851 } else if (flags & PROT_READ) {
2852 fm_access |= PAGE_READONLY;
2855 if (flags & PROT_READ) {
2856 mv_access |= FILE_MAP_READ;
2858 if (flags & PROT_WRITE) {
2859 mv_access |= FILE_MAP_WRITE;
2862 h = CreateFileMapping((HANDLE)_get_osfhandle (fd),
2863 NULL /* security */,
2865 0 /* MaximumSizeHigh */,
2866 0 /* MaximumSizeLow */,
2867 NULL /* name of the file mapping object */);
2869 if (!h || h == INVALID_HANDLE_VALUE) {
2873 mv = MapViewOfFile(h, mv_access,
2879 if (!mv || mv == INVALID_HANDLE_VALUE) {
2886 int munmap(void *start, size_t length)
2891 UnmapViewOfFile(start);
2897 /* syslog function, added by Nicolas Boichat */
2898 void openlog(const char *ident, int option, int facility) {}
2901 /* Log an error message */
2902 void LogErrorMsg(const char *message)
2904 HANDLE eventHandler;
2905 const char *strings[2];
2907 /* Use the OS event logging to log the error */
2908 eventHandler = RegisterEventSource(NULL, "Bacula");
2910 strings[0] = _("\n\nBacula ERROR: ");
2911 strings[1] = message;
2914 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
2918 2, /* Number of strings */
2919 0, /* raw data size */
2920 (const char **)strings, /* error strings */
2921 NULL); /* raw data */
2922 DeregisterEventSource(eventHandler);
2927 * Don't allow OS to suspend while backup running
2928 * Note, the OS automatically tracks these for each thread
2930 void prevent_os_suspensions()
2932 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
2935 void allow_os_suspensions()
2937 SetThreadExecutionState(ES_CONTINUOUS);
2940 int mkstemp(char *t)
2942 char *filename = mktemp(t);
2943 if (filename == NULL) {
2946 return open(filename, O_RDWR | O_CREAT, 0600);
2949 void malloc_trim(int)
2951 if (p_EmptyWorkingSet) {
2952 HANDLE hProcess = GetCurrentProcess();
2953 if (!p_EmptyWorkingSet(hProcess)) {
2954 const char *err = errorString();
2955 Dmsg1(dbglvl, "EmptyWorkingSet() = %s\n", err);
2956 LocalFree((void *)err);
2958 CloseHandle( hProcess );
2962 uint64_t get_memory_info(char *buf, int buflen)
2964 char ed1[50], ed2[50], ed3[50], ed4[50];
2966 HANDLE hProcess = GetCurrentProcess();
2967 PROCESS_MEMORY_COUNTERS pmc;
2970 if (p_GetProcessMemoryInfo) {
2971 if (p_GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
2972 bsnprintf(buf, buflen,
2973 "WorkingSetSize: %s QuotaPagedPoolUsage: %s QuotaNonPagedPoolUsage: %s PagefileUsage: %s",
2974 edit_uint64_with_commas(pmc.WorkingSetSize, ed1),
2975 edit_uint64_with_commas(pmc.QuotaPagedPoolUsage, ed2),
2976 edit_uint64_with_commas(pmc.QuotaNonPagedPoolUsage, ed3),
2977 edit_uint64_with_commas(pmc.PagefileUsage, ed4));
2978 ret = pmc.WorkingSetSize;
2981 const char *err = errorString();
2982 bsnprintf(buf, buflen, "%s", err);
2983 LocalFree((void *)err);
2987 CloseHandle( hProcess );