X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fwin32%2Fcompat%2Fcompat.cpp;h=e93ce9c45b39d3beb4d2fc3f13f724f1892c5b7e;hb=a33d3671776f81795bb6bd649572d845a4bcea10;hp=e6c197aed3b94576d3f0787278b6fcf000f33213;hpb=bef1658117eea9f489ba321fcb5a5e1d587541b0;p=bacula%2Fbacula diff --git a/bacula/src/win32/compat/compat.cpp b/bacula/src/win32/compat/compat.cpp index e6c197aed3..e93ce9c45b 100644 --- a/bacula/src/win32/compat/compat.cpp +++ b/bacula/src/win32/compat/compat.cpp @@ -1,3 +1,30 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2004-2008 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ // -*- Mode: C++ -*- // compat.cpp -- compatibilty layer to make bacula-fd run // natively under windows @@ -5,57 +32,113 @@ // Copyright transferred from Raider Solutions, Inc to // Kern Sibbald and John Walker by express permission. // -// Copyright (C) 2004 Kern Sibbald and John Walker -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of -// the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public -// License along with this program; if not, write to the Free -// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -// MA 02111-1307, USA. -// // Author : Christopher S. Hull // Created On : Sat Jan 31 15:55:00 2004 // $Id$ -#include +#include "bacula.h" #include "compat.h" -#include "pthread.h" +#include "jcr.h" +#include "findlib/find.h" -#define USE_WIN32_COMPAT_IO 1 +#define b_errno_win32 (1<<29) + +#define MAX_PATHLENGTH 1024 + +/* UTF-8 to UCS2 path conversion is expensive, + so we cache the conversion. During backup the + conversion is called 3 times (lstat, attribs, open), + by using the cache this is reduced to 1 time */ + +static POOLMEM *g_pWin32ConvUTF8Cache = get_pool_memory(PM_FNAME); +static POOLMEM *g_pWin32ConvUCS2Cache = get_pool_memory(PM_FNAME); +static DWORD g_dwWin32ConvUTF8strlen = 0; +static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER; + +static t_pVSSPathConvert g_pVSSPathConvert; +static t_pVSSPathConvertW g_pVSSPathConvertW; + +/* Forward referenced functions */ +static const char *errorString(void); + + +void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW) +{ + g_pVSSPathConvert = pPathConvert; + g_pVSSPathConvertW = pPathConvertW; +} + +void Win32ConvCleanupCache() +{ + if (g_pWin32ConvUTF8Cache) { + free_pool_memory(g_pWin32ConvUTF8Cache); + g_pWin32ConvUTF8Cache = NULL; + } + + if (g_pWin32ConvUCS2Cache) { + free_pool_memory(g_pWin32ConvUCS2Cache); + g_pWin32ConvUCS2Cache = NULL; + } + + g_dwWin32ConvUTF8strlen = 0; +} + + +/* to allow the usage of the original version in this file here */ +#undef fputs + + +//#define USE_WIN32_COMPAT_IO 1 +#define USE_WIN32_32KPATHCONVERSION 1 -extern void d_msg(const char *file, int line, int level, const char *fmt,...); -#ifndef HAVE_MINGW extern DWORD g_platform_id; -#endif +extern DWORD g_MinorVersion; -// from CYGWIN (should be diff between Jan 1 1601 and Jan 1 1970 +// from MicroSoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970 #ifdef HAVE_MINGW -#define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000UL //Not sure it works +#define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL #else #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64 #endif #define WIN32_FILETIME_SCALE 10000000 // 100ns/second -extern "C" void -cygwin_conv_to_win32_path(const char *name, char *win32_name) +void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize) { const char *fname = name; + char *tname = win32_name; + + Dmsg0(100, "Enter convert_unix_to_win32_path\n"); + + if (IsPathSeparator(name[0]) && + IsPathSeparator(name[1]) && + name[2] == '.' && + IsPathSeparator(name[3])) { + + *win32_name++ = '\\'; + *win32_name++ = '\\'; + *win32_name++ = '.'; + *win32_name++ = '\\'; + + name += 4; + } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS && + g_pVSSPathConvert == NULL) { + /* allow path to be 32767 bytes */ + *win32_name++ = '\\'; + *win32_name++ = '\\'; + *win32_name++ = '?'; + *win32_name++ = '\\'; + } + while (*name) { /* Check for Unix separator and convert to Win32 */ + if (name[0] == '/' && name[1] == '/') { /* double slash? */ + name++; /* yes, skip first one */ + } if (*name == '/') { *win32_name++ = '\\'; /* convert char */ - /* If Win32 separated that is "quoted", remove quote */ + /* If Win32 separator that is "quoted", remove quote */ } else if (*name == '\\' && name[1] == '\\') { *win32_name++ = '\\'; name++; /* skip first \ */ @@ -65,16 +148,279 @@ cygwin_conv_to_win32_path(const char *name, char *win32_name) name++; } /* Strip any trailing slash, if we stored something */ - if (*fname != 0 && win32_name[-1] == '\\') { + /* but leave "c:\" with backslash (root directory case */ + if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) { win32_name[-1] = 0; } else { *win32_name = 0; } + + /* here we convert to VSS specific file name which + can get longer because VSS will make something like + \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe + from c:\bacula\uninstall.exe + */ + Dmsg1(100, "path=%s\n", tname); + if (g_pVSSPathConvert != NULL) { + POOLMEM *pszBuf = get_pool_memory (PM_FNAME); + pszBuf = check_pool_memory_size(pszBuf, dwSize); + bstrncpy(pszBuf, tname, strlen(tname)+1); + g_pVSSPathConvert(pszBuf, tname, dwSize); + free_pool_memory(pszBuf); + } + + Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname); +} + +/* Conversion of a Unix filename to a Win32 filename */ +void unix_name_to_win32(POOLMEM **win32_name, char *name) +{ + /* One extra byte should suffice, but we double it */ + /* add MAX_PATH bytes for VSS shadow copy name */ + DWORD dwSize = 2*strlen(name)+MAX_PATH; + *win32_name = check_pool_memory_size(*win32_name, dwSize); + conv_unix_to_win32_path(name, *win32_name, dwSize); +} + +POOLMEM* +make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/) +{ + /* created 02/27/2006 Thorsten Engel + * + * This function expects an UCS-encoded standard wchar_t in pszUCSPath and + * will complete the input path to an absolue path of the form \\?\c:\path\file + * + * With this trick, it is possible to have 32K characters long paths. + * + * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path + * to a raw windows partition. + */ + + Dmsg0(100, "Enter wchar_win32_path\n"); + if (pBIsRawPath) { + *pBIsRawPath = FALSE; /* Initialize, set later */ + } + + if (!p_GetCurrentDirectoryW) { + Dmsg0(100, "Leave wchar_win32_path no change \n"); + return pszUCSPath; + } + + wchar_t *name = (wchar_t *)pszUCSPath; + + /* if it has already the desired form, exit without changes */ + if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) { + Dmsg0(100, "Leave wchar_win32_path no change \n"); + return pszUCSPath; + } + + wchar_t *pwszBuf = (wchar_t *)get_pool_memory(PM_FNAME); + wchar_t *pwszCurDirBuf = (wchar_t *)get_pool_memory(PM_FNAME); + DWORD dwCurDirPathSize = 0; + + /* get buffer with enough size (name+max 6. wchars+1 null terminator */ + DWORD dwBufCharsNeeded = (wcslen(name)+7); + pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t)); + + /* add \\?\ to support 32K long filepaths + it is important to make absolute paths, so we add drive and + current path if necessary */ + + BOOL bAddDrive = TRUE; + BOOL bAddCurrentPath = TRUE; + BOOL bAddPrefix = TRUE; + + /* does path begin with drive? if yes, it is absolute */ + if (iswalpha(name[0]) && name[1] == ':' && IsPathSeparator(name[2])) { + bAddDrive = FALSE; + bAddCurrentPath = FALSE; + } + + /* is path absolute? */ + if (IsPathSeparator(name[0])) + bAddCurrentPath = FALSE; + + /* is path relative to itself?, if yes, skip ./ */ + if (name[0] == '.' && IsPathSeparator(name[1])) { + name += 2; + } + + /* is path of form '//./'? */ + if (IsPathSeparator(name[0]) && + IsPathSeparator(name[1]) && + name[2] == '.' && + IsPathSeparator(name[3])) { + bAddDrive = FALSE; + bAddCurrentPath = FALSE; + bAddPrefix = FALSE; + if (pBIsRawPath) { + *pBIsRawPath = TRUE; + } + } + + int nParseOffset = 0; + + /* add 4 bytes header */ + if (bAddPrefix) { + nParseOffset = 4; + wcscpy(pwszBuf, L"\\\\?\\"); + } + + /* get current path if needed */ + if (bAddDrive || bAddCurrentPath) { + dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL); + if (dwCurDirPathSize > 0) { + /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */ + pwszCurDirBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t)); + p_GetCurrentDirectoryW(dwCurDirPathSize, pwszCurDirBuf); + } else { + /* we have no info for doing so */ + bAddDrive = FALSE; + bAddCurrentPath = FALSE; + } + } + + + /* add drive if needed */ + if (bAddDrive && !bAddCurrentPath) { + wchar_t szDrive[3]; + + if (IsPathSeparator(pwszCurDirBuf[0]) && + IsPathSeparator(pwszCurDirBuf[1]) && + pwszCurDirBuf[2] == '?' && + IsPathSeparator(pwszCurDirBuf[3])) { + /* copy drive character */ + szDrive[0] = pwszCurDirBuf[4]; + } else { + /* copy drive character */ + szDrive[0] = pwszCurDirBuf[0]; + } + + szDrive[1] = ':'; + szDrive[2] = 0; + + wcscat(pwszBuf, szDrive); + nParseOffset +=2; + } + + /* add path if needed */ + if (bAddCurrentPath) { + /* the 1 add. character is for the eventually added backslash */ + dwBufCharsNeeded += dwCurDirPathSize+1; + pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, dwBufCharsNeeded*sizeof(wchar_t)); + /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */ + + if (IsPathSeparator(pwszCurDirBuf[0]) && + IsPathSeparator(pwszCurDirBuf[1]) && + pwszCurDirBuf[2] == '?' && + IsPathSeparator(pwszCurDirBuf[3])) { + /* copy complete string */ + wcscpy(pwszBuf, pwszCurDirBuf); + } else { + /* append path */ + wcscat(pwszBuf, pwszCurDirBuf); + } + + nParseOffset = wcslen((LPCWSTR) pwszBuf); + + /* check if path ends with backslash, if not, add one */ + if (!IsPathSeparator(pwszBuf[nParseOffset-1])) { + wcscat(pwszBuf, L"\\"); + nParseOffset++; + } + } + + wchar_t *win32_name = &pwszBuf[nParseOffset]; + wchar_t *name_start = name; + + while (*name) { + /* Check for Unix separator and convert to Win32, eliminating + * duplicate separators. + */ + if (IsPathSeparator(*name)) { + *win32_name++ = '\\'; /* convert char */ + + /* Eliminate consecutive slashes, but not at the start so that + * \\.\ still works. + */ + if (name_start != name && IsPathSeparator(name[1])) { + name++; + } + } else { + *win32_name++ = *name; /* copy character */ + } + name++; + } + + /* null terminate string */ + *win32_name = 0; + + /* here we convert to VSS specific file name which + * can get longer because VSS will make something like + * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe + * from c:\bacula\uninstall.exe + */ + if (g_pVSSPathConvertW != NULL) { + /* is output buffer large enough? */ + pwszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pwszBuf, + (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t)); + /* create temp. buffer */ + wchar_t *pszBuf = (wchar_t *)get_pool_memory(PM_FNAME); + pszBuf = (wchar_t *)check_pool_memory_size((POOLMEM *)pszBuf, + (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t)); + if (bAddPrefix) + nParseOffset = 4; + else + nParseOffset = 0; + wcsncpy(pszBuf, &pwszBuf[nParseOffset], wcslen(pwszBuf)+1-nParseOffset); + g_pVSSPathConvertW(pszBuf, pwszBuf, dwBufCharsNeeded+MAX_PATH); + free_pool_memory((POOLMEM *)pszBuf); + } + + free_pool_memory(pszUCSPath); + free_pool_memory((POOLMEM *)pwszCurDirBuf); + + Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf); + return (POOLMEM *)pwszBuf; +} + +int +wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar) +{ + /* the return value is the number of bytes written to the buffer. + The number includes the byte for the null terminator. */ + + if (p_WideCharToMultiByte) { + int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL); + ASSERT (nRet > 0); + return nRet; + } + else + return 0; +} + +int +UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF) +{ + /* the return value is the number of wide characters written to the buffer. */ + /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */ + + if (p_MultiByteToWideChar) { + /* strlen of UTF8 +1 is enough */ + DWORD cchSize = (strlen(pszUTF)+1); + *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t)); + + int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize); + ASSERT (nRet > 0); + return nRet; + } + else + return 0; } void -wchar_win32_path(const char *name, WCHAR *win32_name) +wchar_win32_path(const char *name, wchar_t *win32_name) { const char *fname = name; while (*name) { @@ -98,31 +444,104 @@ wchar_win32_path(const char *name, WCHAR *win32_name) } } -int -umask(int) +int +make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/) { - return 0; + P(Win32Convmutex); + /* if we find the utf8 string in cache, we use the cached ucs2 version. + we compare the stringlength first (quick check) and then compare the content. + */ + if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) { + if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) { + int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache); + *pszUCS = check_pool_memory_size(*pszUCS, nBufSize); + wcscpy((LPWSTR) *pszUCS, (LPWSTR) g_pWin32ConvUCS2Cache); + V(Win32Convmutex); + return nBufSize / sizeof (WCHAR); + } + } + + /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */ + int nRet = UTF8_2_wchar(pszUCS, pszUTF); + +#ifdef USE_WIN32_32KPATHCONVERSION + /* add \\?\ to support 32K long filepaths */ + *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath); +#else + if (pBIsRawPath) + *pBIsRawPath = FALSE; +#endif + + /* populate cache */ + g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS)); + wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS); + + g_dwWin32ConvUTF8strlen = strlen(pszUTF); + g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+1); + bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1); + V(Win32Convmutex); + + return nRet; } -int -chmod(const char *, mode_t) +#if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+ +int umask(int) { - return 0; + return 0; } +#endif -int -chown(const char *k, uid_t, gid_t) +#ifndef LOAD_WITH_ALTERED_SEARCH_PATH +#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 +#endif + +void *dlopen(const char *file, int mode) { - return 0; + void *handle; + + handle = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + return handle; } -int -lchown(const char *k, uid_t, gid_t) +void *dlsym(void *handle, const char *name) { - return 0; + void *symaddr; + symaddr = (void *)GetProcAddress((HMODULE)handle, name); + return symaddr; +} + +int dlclose(void *handle) +{ + if (handle && !FreeLibrary((HMODULE)handle)) { + errno = b_errno_win32; + return 1; /* failed */ + } + return 0; /* OK */ +} + +char *dlerror(void) +{ + static char buf[200]; + const char *err = errorString(); + bstrncpy(buf, (char *)err, sizeof(buf)); + LocalFree((void *)err); + return buf; +} + +int fcntl(int fd, int cmd) +{ + return 0; } +int chown(const char *k, uid_t, gid_t) +{ + return 0; +} +int lchown(const char *k, uid_t, gid_t) +{ + return 0; +} long int random(void) @@ -133,7 +552,7 @@ random(void) void srandom(unsigned int seed) { - srand(seed); + srand(seed); } // ///////////////////////////////////////////////////////////////// // convert from Windows concept of time to Unix concept of time @@ -141,16 +560,16 @@ srandom(unsigned int seed) void cvt_utime_to_ftime(const time_t &time, FILETIME &wintime) { - uint64_t mstime = time; - mstime *= WIN32_FILETIME_SCALE; - mstime += WIN32_FILETIME_ADJUST; + uint64_t mstime = time; + mstime *= WIN32_FILETIME_SCALE; + mstime += WIN32_FILETIME_ADJUST; - #ifdef HAVE_MINGW - wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL); - #else - wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64); - #endif - wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL); +#if defined(_MSC_VER) + wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64); +#else + wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL); +#endif + wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL); } time_t @@ -166,250 +585,430 @@ cvt_ftime_to_utime(const FILETIME &time) return (time_t) (mstime & 0xffffffff); } -static const char * -errorString(void) +static const char *errorString(void) { - LPVOID lpMsgBuf; - - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang - (LPTSTR) &lpMsgBuf, - 0, - NULL); - - return (const char *) lpMsgBuf; + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang + (LPTSTR) &lpMsgBuf, + 0, + NULL); + + /* Strip any \r or \n */ + char *rval = (char *) lpMsgBuf; + char *cp = strchr(rval, '\r'); + if (cp != NULL) { + *cp = 0; + } else { + cp = strchr(rval, '\n'); + if (cp != NULL) + *cp = 0; + } + return rval; } -#ifndef HAVE_MINGW static int statDir(const char *file, struct stat *sb) { - WIN32_FIND_DATA info; // window's file info - - if (file[1] == ':' && file[2] == 0) { - d_msg(__FILE__, __LINE__, 99, "faking ROOT attrs(%s).\n", file); - sb->st_mode = S_IFDIR; - sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE; - time(&sb->st_ctime); - time(&sb->st_mtime); - time(&sb->st_atime); - return 0; + WIN32_FIND_DATAW info_w; // window's file info + WIN32_FIND_DATAA info_a; // window's file info + + // cache some common vars to make code more transparent + DWORD *pdwFileAttributes; + DWORD *pnFileSizeHigh; + DWORD *pnFileSizeLow; + DWORD *pdwReserved0; + FILETIME *pftLastAccessTime; + FILETIME *pftLastWriteTime; + FILETIME *pftCreationTime; + + /* + * Oh, cool, another exception: Microsoft doesn't let us do + * FindFile operations on a Drive, so simply fake root attibutes. + */ + if (file[1] == ':' && file[2] == 0) { + time_t now = time(NULL); + Dmsg1(99, "faking ROOT attrs(%s).\n", file); + sb->st_mode = S_IFDIR; + sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE; + sb->st_ctime = now; + sb->st_mtime = now; + sb->st_atime = now; + sb->st_rdev = 0; + return 0; } - HANDLE h = FindFirstFile(file, &info); - if (h == INVALID_HANDLE_VALUE) { - const char *err = errorString(); - d_msg(__FILE__, __LINE__, 99, "FindFirstFile(%s):%s\n", file, err); - LocalFree((void *)err); - errno = GetLastError(); - return -1; - } + HANDLE h = INVALID_HANDLE_VALUE; + + // use unicode + if (p_FindFirstFileW) { + POOLMEM* pwszBuf = get_pool_memory (PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, file); + + Dmsg1(100, "FindFirstFileW=%s\n", file); + h = p_FindFirstFileW((LPCWSTR)pwszBuf, &info_w); + free_pool_memory(pwszBuf); + + pdwFileAttributes = &info_w.dwFileAttributes; + pdwReserved0 = &info_w.dwReserved0; + pnFileSizeHigh = &info_w.nFileSizeHigh; + pnFileSizeLow = &info_w.nFileSizeLow; + pftLastAccessTime = &info_w.ftLastAccessTime; + pftLastWriteTime = &info_w.ftLastWriteTime; + pftCreationTime = &info_w.ftCreationTime; + + // use ASCII + } else if (p_FindFirstFileA) { + Dmsg1(100, "FindFirstFileA=%s\n", file); + h = p_FindFirstFileA(file, &info_a); + + pdwFileAttributes = &info_a.dwFileAttributes; + pdwReserved0 = &info_a.dwReserved0; + pnFileSizeHigh = &info_a.nFileSizeHigh; + pnFileSizeLow = &info_a.nFileSizeLow; + pftLastAccessTime = &info_a.ftLastAccessTime; + pftLastWriteTime = &info_a.ftLastWriteTime; + pftCreationTime = &info_a.ftCreationTime; + } else { + Dmsg0(100, "No findFirstFile A or W found\n"); + } - sb->st_mode = 0777; /* start with everything */ - if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); - if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) - sb->st_mode &= ~S_IRWXO; /* remove everything for other */ - if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) - sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ - sb->st_mode |= S_IFDIR; - - sb->st_size = info.nFileSizeHigh; - sb->st_size <<= 32; - sb->st_size |= info.nFileSizeLow; - sb->st_blksize = 4096; - sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; - - sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime); - sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime); - sb->st_ctime = cvt_ftime_to_utime(info.ftLastWriteTime); - FindClose(h); + if (h == INVALID_HANDLE_VALUE) { + const char *err = errorString(); + /* + * Note, in creating leading paths, it is normal that + * the file does not exist. + */ + Dmsg2(2099, "FindFirstFile(%s):%s\n", file, err); + LocalFree((void *)err); + errno = b_errno_win32; + return -1; + } else { + FindClose(h); + } - return 0; + sb->st_mode = 0777; /* start with everything */ + if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY) + sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); + if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + sb->st_mode &= ~S_IRWXO; /* remove everything for other */ + if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ + sb->st_mode |= S_IFDIR; + + /* + * Store reparse/mount point info in st_rdev. Note a + * Win32 reparse point (junction point) is like a link + * though it can have many properties (directory link, + * soft link, hard link, HSM, ... + * A mount point is a reparse point where another volume + * is mounted, so it is like a Unix mount point (change of + * filesystem). + */ + if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) { + sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */ + } else { + sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */ + } + } + Dmsg2(100, "st_rdev=%d file=%s\n", sb->st_rdev, file); + sb->st_size = *pnFileSizeHigh; + sb->st_size <<= 32; + sb->st_size |= *pnFileSizeLow; + sb->st_blksize = 4096; + sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; + + sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime); + sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime); + sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime); + + return 0; +} + +int +fstat(intptr_t fd, struct stat *sb) +{ + BY_HANDLE_FILE_INFORMATION info; + + if (!GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &info)) { + const char *err = errorString(); + Dmsg1(2099, "GetfileInformationByHandle: %s\n", err); + LocalFree((void *)err); + errno = b_errno_win32; + return -1; + } + + sb->st_dev = info.dwVolumeSerialNumber; + sb->st_ino = info.nFileIndexHigh; + sb->st_ino <<= 32; + sb->st_ino |= info.nFileIndexLow; + sb->st_nlink = (short)info.nNumberOfLinks; + if (sb->st_nlink > 1) { + Dmsg1(99, "st_nlink=%d\n", sb->st_nlink); + } + + sb->st_mode = 0777; /* start with everything */ + if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); + if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) + sb->st_mode &= ~S_IRWXO; /* remove everything for other */ + if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ + sb->st_mode |= S_IFREG; + + /* Use st_rdev to store reparse attribute */ + if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + sb->st_rdev = WIN32_REPARSE_POINT; + } + Dmsg3(100, "st_rdev=%d sizino=%d ino=%lld\n", sb->st_rdev, sizeof(sb->st_ino), + (long long)sb->st_ino); + + sb->st_size = info.nFileSizeHigh; + sb->st_size <<= 32; + sb->st_size |= info.nFileSizeLow; + sb->st_blksize = 4096; + sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; + sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime); + sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime); + sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime); + + return 0; } static int stat2(const char *file, struct stat *sb) { - BY_HANDLE_FILE_INFORMATION info; - HANDLE h; - int rval = 0; - char tmpbuf[1024]; - cygwin_conv_to_win32_path(file, tmpbuf); + HANDLE h = INVALID_HANDLE_VALUE; + int rval = 0; + char tmpbuf[5000]; + conv_unix_to_win32_path(file, tmpbuf, 5000); - DWORD attr = GetFileAttributes(tmpbuf); + DWORD attr = (DWORD)-1; - if (attr == -1) { - const char *err = errorString(); - d_msg(__FILE__, __LINE__, 99, - "GetFileAttrubtes(%s): %s\n", tmpbuf, err); - LocalFree((void *)err); - errno = GetLastError(); - return -1; - } + if (p_GetFileAttributesW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf); - if (attr & FILE_ATTRIBUTE_DIRECTORY) - return statDir(tmpbuf, sb); + attr = p_GetFileAttributesW((LPCWSTR) pwszBuf); + if (p_CreateFileW) { + h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + } + free_pool_memory(pwszBuf); + } else if (p_GetFileAttributesA) { + attr = p_GetFileAttributesA(tmpbuf); + h = CreateFileA(tmpbuf, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + } - h = CreateFile(tmpbuf, GENERIC_READ, - FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (attr == (DWORD)-1) { + const char *err = errorString(); + Dmsg2(2099, "GetFileAttributes(%s): %s\n", tmpbuf, err); + LocalFree((void *)err); + if (h != INVALID_HANDLE_VALUE) { + CloseHandle(h); + } + errno = b_errno_win32; + return -1; + } - if (h == INVALID_HANDLE_VALUE) { - const char *err = errorString(); - d_msg(__FILE__, __LINE__, 99, - "Cannot open file for stat (%s):%s\n", tmpbuf, err); - LocalFree((void *)err); - rval = -1; - errno = GetLastError(); - goto error; - } + if (h == INVALID_HANDLE_VALUE) { + const char *err = errorString(); + Dmsg2(2099, "Cannot open file for stat (%s):%s\n", tmpbuf, err); + LocalFree((void *)err); + errno = b_errno_win32; + return -1; + } - if (!GetFileInformationByHandle(h, &info)) { - const char *err = errorString(); - d_msg(__FILE__, __LINE__, 99, - "GetfileInformationByHandle(%s): %s\n", tmpbuf, err); - LocalFree((void *)err); - rval = -1; - errno = GetLastError(); - goto error; - } + rval = fstat((intptr_t)h, sb); + CloseHandle(h); - sb->st_dev = info.dwVolumeSerialNumber; - sb->st_ino = info.nFileIndexHigh; - sb->st_ino <<= 32; - sb->st_ino |= info.nFileIndexLow; - sb->st_nlink = (short)info.nNumberOfLinks; - - sb->st_mode = 0777; /* start with everything */ - if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); - if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) - sb->st_mode &= ~S_IRWXO; /* remove everything for other */ - if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) - sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ - sb->st_mode |= S_IFREG; - - sb->st_size = info.nFileSizeHigh; - sb->st_size <<= 32; - sb->st_size |= info.nFileSizeLow; - sb->st_blksize = 4096; - sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; - sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime); - sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime); - sb->st_ctime = cvt_ftime_to_utime(info.ftLastWriteTime); - -error: - CloseHandle(h); - return rval; + if (attr & FILE_ATTRIBUTE_DIRECTORY && + file[1] == ':' && file[2] != 0) { + rval = statDir(file, sb); + } + return rval; } int stat(const char *file, struct stat *sb) { - WIN32_FILE_ATTRIBUTE_DATA data; - errno = 0; + WIN32_FILE_ATTRIBUTE_DATA data; + errno = 0; + memset(sb, 0, sizeof(*sb)); - memset(sb, 0, sizeof(*sb)); + if (p_GetFileAttributesExW) { + /* dynamically allocate enough space for UCS2 filename */ + POOLMEM *pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, file); - if (g_platform_id == VER_PLATFORM_WIN32_WINDOWS) - return stat2(file, sb); + BOOL b = p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard, &data); + free_pool_memory(pwszBuf); - // otherwise we're on NT -#if 0 - WCHAR buf[32767]; - buf[0] = '\\'; - buf[1] = '\\'; - buf[2] = '?'; - buf[3] = '\\'; + if (!b) { + return stat2(file, sb); + } - wchar_win32_path(file, buf+4); + } else if (p_GetFileAttributesExA) { + if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) { + return stat2(file, sb); + } + } else { + return stat2(file, sb); + } - if (!GetFileAttributesExW((WCHAR *)buf, GetFileExInfoStandard, &data)) - return stat2(file, sb); -#else - if (!GetFileAttributesEx(file, GetFileExInfoStandard, &data)) - return stat2(file, sb); -#endif + sb->st_mode = 0777; /* start with everything */ + if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { + sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); + } + if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { + sb->st_mode &= ~S_IRWXO; /* remove everything for other */ + } + if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { + sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ + } + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + sb->st_mode |= S_IFDIR; + } else { + sb->st_mode |= S_IFREG; + } - sb->st_mode = 0777; /* start with everything */ - if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH); - if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) - sb->st_mode &= ~S_IRWXO; /* remove everything for other */ - if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) - sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */ + /* Use st_rdev to store reparse attribute */ + sb->st_rdev = (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0; + + sb->st_nlink = 1; + sb->st_size = data.nFileSizeHigh; + sb->st_size <<= 32; + sb->st_size |= data.nFileSizeLow; + sb->st_blksize = 4096; + sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; + sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime); + sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime); + sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime); + + /* + * If we are not at the root, then to distinguish a reparse + * point from a mount point, we must call FindFirstFile() to + * get the WIN32_FIND_DATA, which has the bit that indicates + * that this directory is a mount point -- aren't Win32 APIs + * wonderful? (sarcasm). The code exists in the statDir + * subroutine. + */ + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + file[1] == ':' && file[2] != 0) { + statDir(file, sb); + } + Dmsg3(100, "sizino=%d ino=%lld file=%s\n", sizeof(sb->st_ino), + (long long)sb->st_ino, file); + return 0; +} - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - sb->st_mode |= S_IFDIR; - else - sb->st_mode |= S_IFREG; - - sb->st_nlink = 1; - sb->st_size = data.nFileSizeHigh; - sb->st_size <<= 32; - sb->st_size |= data.nFileSizeLow; - sb->st_blksize = 4096; - sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096; - sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime); - sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime); - sb->st_ctime = cvt_ftime_to_utime(data.ftLastWriteTime); - return 0; +/* + * We write our own ftruncate because the one in the + * Microsoft library mrcrt.dll does not truncate + * files greater than 2GB. + * KES - May 2007 + */ +int win32_ftruncate(int fd, int64_t length) +{ + /* Set point we want to truncate file */ + __int64 pos = _lseeki64(fd, (__int64)length, SEEK_SET); + + if (pos != (__int64)length) { + errno = EACCES; /* truncation failed, get out */ + return -1; + } + + /* Truncate file */ + if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0) { + errno = b_errno_win32; + return -1; + } + errno = 0; + return 0; } -#endif //HAVE_MINGW +int fcntl(int fd, int cmd, long arg) +{ + int rval = 0; + + switch (cmd) { + case F_GETFL: + rval = O_NONBLOCK; + break; + + case F_SETFL: + rval = 0; + break; + + default: + errno = EINVAL; + rval = -1; + break; + } + + return rval; +} int lstat(const char *file, struct stat *sb) { - return stat(file, sb); + return stat(file, sb); } void sleep(int sec) { - Sleep(sec * 1000); + Sleep(sec * 1000); } int geteuid(void) { - return 0; + return 0; } int execvp(const char *, char *[]) { - return -1; + errno = ENOSYS; + return -1; } int fork(void) { - return -1; + errno = ENOSYS; + return -1; } int pipe(int[]) { - return -1; + errno = ENOSYS; + return -1; } int waitpid(int, int*, int) { - return -1; + errno = ENOSYS; + return -1; } int readlink(const char *, char *, int) { - return -1; + errno = ENOSYS; + return -1; } @@ -417,46 +1016,46 @@ readlink(const char *, char *, int) int strcasecmp(const char *s1, const char *s2) { - register int ch1, ch2; - - if (s1==s2) - return 0; /* strings are equal if same object. */ - else if (!s1) - return -1; - else if (!s2) - return 1; - do - { - ch1 = *s1; - ch2 = *s2; - s1++; - s2++; - } while (ch1 != 0 && tolower(ch1) == tolower(ch2)); - - return(ch1 - ch2); + register int ch1, ch2; + + if (s1==s2) + return 0; /* strings are equal if same object. */ + else if (!s1) + return -1; + else if (!s2) + return 1; + do { + ch1 = *s1; + ch2 = *s2; + s1++; + s2++; + } while (ch1 != 0 && tolower(ch1) == tolower(ch2)); + + return(ch1 - ch2); } #endif //HAVE_MINGW int strncasecmp(const char *s1, const char *s2, int len) { - register int ch1, ch2; - - if (s1==s2) - return 0; /* strings are equal if same object. */ - else if (!s1) - return -1; - else if (!s2) - return 1; - do - { - ch1 = *s1; - ch2 = *s2; - s1++; - s2++; - } while (len-- && ch1 != 0 && tolower(ch1) == tolower(ch2)); + register int ch1 = 0, ch2 = 0; + + if (s1==s2) + return 0; /* strings are equal if same object. */ + else if (!s1) + return -1; + else if (!s2) + return 1; + + while (len--) { + ch1 = *s1; + ch2 = *s2; + s1++; + s2++; + if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break; + } - return(ch1 - ch2); + return (ch1 - ch2); } int @@ -464,26 +1063,40 @@ gettimeofday(struct timeval *tv, struct timezone *) { SYSTEMTIME now; FILETIME tmp; + GetSystemTime(&now); - if (tv == NULL) return -1; - if (!SystemTimeToFileTime(&now, &tmp)) return -1; + if (tv == NULL) { + errno = EINVAL; + return -1; + } + if (!SystemTimeToFileTime(&now, &tmp)) { + errno = b_errno_win32; + return -1; + } int64_t _100nsec = tmp.dwHighDateTime; _100nsec <<= 32; _100nsec |= tmp.dwLowDateTime; _100nsec -= WIN32_FILETIME_ADJUST; - tv->tv_sec =(long) (_100nsec / 10000000); - tv->tv_usec = (long) ((_100nsec % 10000000)/10); + tv->tv_sec = (long)(_100nsec / 10000000); + tv->tv_usec = (long)((_100nsec % 10000000)/10); return 0; } -int -syslog(int, const char *, const char *) +/* For apcupsd this is in src/lib/wincompat.c */ +extern "C" void syslog(int type, const char *fmt, ...) +{ +/*#ifndef HAVE_CONSOLE + MessageBox(NULL, msg, "Bacula", MB_OK); +#endif*/ +} + +void +closelog() { - return 0; } struct passwd * @@ -499,61 +1112,87 @@ getgrgid(uid_t) } // implement opendir/readdir/closedir on top of window's API + typedef struct _dir { - WIN32_FIND_DATA data; // window's file info + WIN32_FIND_DATAA data_a; // window's file info (ansii version) + WIN32_FIND_DATAW data_w; // window's file info (wchar version) const char *spec; // the directory we're traversing HANDLE dirh; // the search handle - BOOL valid; // the info in data field is valid + BOOL valid_a; // the info in data_a field is valid + BOOL valid_w; // the info in data_w field is valid UINT32 offset; // pseudo offset for d_off } _dir; DIR * opendir(const char *path) { - int max_len = strlen(path) + 16; + /* enough space for VSS !*/ + int max_len = strlen(path) + MAX_PATH; _dir *rval = NULL; - if (path == NULL) return NULL; + if (path == NULL) { + errno = ENOENT; + return NULL; + } + Dmsg1(100, "Opendir path=%s\n", path); rval = (_dir *)malloc(sizeof(_dir)); + memset (rval, 0, sizeof (_dir)); if (rval == NULL) return NULL; char *tspec = (char *)malloc(max_len); - if (tspec == NULL) goto err1; + if (tspec == NULL) return NULL; + + conv_unix_to_win32_path(path, tspec, max_len); + Dmsg1(100, "win32 path=%s\n", tspec); + + // add backslash only if there is none yet (think of c:\) + if (tspec[strlen(tspec)-1] != '\\') + bstrncat(tspec, "\\*", max_len); + else + bstrncat(tspec, "*", max_len); -#ifndef HAVE_MINGW - if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS) { - // allow path to be 32767 bytes - tspec[0] = '\\'; - tspec[1] = '\\'; - tspec[2] = '?'; - tspec[3] = '\\'; - tspec[4] = 0; - cygwin_conv_to_win32_path(path, tspec+4); - } else { - cygwin_conv_to_win32_path(path, tspec); - } -#else - cygwin_conv_to_win32_path(path, tspec); -#endif - strncat(tspec, "\\*", max_len); rval->spec = tspec; - rval->dirh = FindFirstFile(rval->spec, &rval->data); - d_msg(__FILE__, __LINE__, - 99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n", + // convert to wchar_t + if (p_FindFirstFileW) { + POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);; + make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec); + + rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w); + + free_pool_memory(pwcBuf); + + if (rval->dirh != INVALID_HANDLE_VALUE) + rval->valid_w = 1; + } else if (p_FindFirstFileA) { + rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a); + + if (rval->dirh != INVALID_HANDLE_VALUE) + rval->valid_a = 1; + } else goto err; + + + Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n", path, rval->spec, rval->dirh); rval->offset = 0; if (rval->dirh == INVALID_HANDLE_VALUE) goto err; - rval->valid = 1; - d_msg(__FILE__, __LINE__, - 99, "\tFirstFile=%s\n", rval->data.cFileName); + + if (rval->valid_w) { + Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName); + } + + if (rval->valid_a) { + Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName); + } + return (DIR *)rval; + err: free((void *)rval->spec); -err1: free(rval); + errno = b_errno_win32; return NULL; } @@ -599,45 +1238,76 @@ copyin(struct dirent &dp, const char *fname) int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { - _dir *dp = (_dir *)dirp; - if (dp->valid) { - entry->d_off = dp->offset; - dp->offset += copyin(*entry, dp->data.cFileName); - *result = entry; /* return entry address */ - d_msg(__FILE__, __LINE__, - 99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n", - dirp, entry->d_name, entry->d_reclen, entry->d_off); + if (dp->valid_w || dp->valid_a) { + entry->d_off = dp->offset; + + // copy unicode + if (dp->valid_w) { + char szBuf[MAX_PATH_UTF8+1]; + wchar_2_UTF8(szBuf,dp->data_w.cFileName); + dp->offset += copyin(*entry, szBuf); + } else if (dp->valid_a) { // copy ansi (only 1 will be valid) + dp->offset += copyin(*entry, dp->data_a.cFileName); + } + + *result = entry; /* return entry address */ + Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n", + dirp, entry->d_name, entry->d_reclen, entry->d_off); } else { -// d_msg(__FILE__, __LINE__, 99, "readdir_r !valid\n"); +// Dmsg0(99, "readdir_r !valid\n"); + errno = b_errno_win32; return -1; } - dp->valid = FindNextFile(dp->dirh, &dp->data); + + // get next file, try unicode first + if (p_FindNextFileW) + dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w); + else if (p_FindNextFileA) + dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a); + else { + dp->valid_a = FALSE; + dp->valid_w = FALSE; + } + return 0; } +/* + * Dotted IP address to network address + * + * Returns 1 if OK + * 0 on error + */ int inet_aton(const char *a, struct in_addr *inp) { + const char *cp = a; + uint32_t acc = 0, tmp = 0; + int dotc = 0; - const char *cp = a; - uint32_t acc = 0, tmp = 0; - int dotc = 0; - if (!isdigit(*a)) return 0; - while (*cp) { - if (isdigit(*cp)) - tmp = (tmp * 10) + (*cp -'0'); - else if (*cp == '.') { - if (tmp > 255) return 0; - acc = (acc << 8) + tmp; - dotc++; - } - else return 0; - } - - if (dotc != 3) return 0; - inp->s_addr = acc; - return 1; + if (!isdigit(*cp)) { /* first char must be digit */ + return 0; /* error */ + } + do { + if (isdigit(*cp)) { + tmp = (tmp * 10) + (*cp -'0'); + } else if (*cp == '.' || *cp == 0) { + if (tmp > 255) { + return 0; /* error */ + } + acc = (acc << 8) + tmp; + dotc++; + tmp = 0; + } else { + return 0; /* error */ + } + } while (*cp++ != 0); + if (dotc != 4) { /* want 3 .'s plus EOS */ + return 0; /* error */ + } + inp->s_addr = htonl(acc); /* store addr in network format */ + return 1; } int @@ -672,7 +1342,7 @@ pathconf(const char *path, int name) case _PC_NAME_MAX : return 255; } - + errno = ENOSYS; return -1; } @@ -685,27 +1355,145 @@ WSA_Init(void) int err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) - { + if (err != 0) { printf("Can not start Windows Sockets\n"); + errno = ENOSYS; return -1; } return 0; } +int win32_chmod(const char *path, mode_t mode) +{ + DWORD attr = (DWORD)-1; + + Dmsg1(100, "Enter win32_chmod. path=%s\n", path); + if (p_GetFileAttributesW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, path); + + attr = p_GetFileAttributesW((LPCWSTR) pwszBuf); + if (attr != INVALID_FILE_ATTRIBUTES) { + /* Use Bacula mappings define in stat() above */ + if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { + attr |= FILE_ATTRIBUTE_READONLY; + } else { + attr &= ~FILE_ATTRIBUTE_READONLY; + } + if (mode & S_ISVTX) { + attr |= FILE_ATTRIBUTE_HIDDEN; + } else { + attr &= ~FILE_ATTRIBUTE_HIDDEN; + } + if (mode & S_IRWXO) { + attr |= FILE_ATTRIBUTE_SYSTEM; + } else { + attr &= ~FILE_ATTRIBUTE_SYSTEM; + } + attr = p_SetFileAttributesW((LPCWSTR)pwszBuf, attr); + } + free_pool_memory(pwszBuf); + Dmsg0(100, "Leave win32_chmod. AttributesW\n"); + } else if (p_GetFileAttributesA) { + if (mode & (S_IRUSR|S_IRGRP|S_IROTH)) { + attr |= FILE_ATTRIBUTE_READONLY; + } else { + attr &= ~FILE_ATTRIBUTE_READONLY; + } + if (mode & S_ISVTX) { + attr |= FILE_ATTRIBUTE_HIDDEN; + } else { + attr &= ~FILE_ATTRIBUTE_HIDDEN; + } + if (mode & S_IRWXO) { + attr |= FILE_ATTRIBUTE_SYSTEM; + } else { + attr &= ~FILE_ATTRIBUTE_SYSTEM; + } + attr = p_GetFileAttributesA(path); + if (attr != INVALID_FILE_ATTRIBUTES) { + attr = p_SetFileAttributesA(path, attr); + } + Dmsg0(100, "Leave win32_chmod did AttributesA\n"); + } else { + Dmsg0(100, "Leave win32_chmod did nothing\n"); + } + + + if (attr == (DWORD)-1) { + const char *err = errorString(); + Dmsg2(99, "Get/SetFileAttributes(%s): %s\n", path, err); + LocalFree((void *)err); + errno = b_errno_win32; + return -1; + } + return 0; +} + int win32_chdir(const char *dir) { - if (0 == SetCurrentDirectory(dir)) return -1; - return 0; + if (p_SetCurrentDirectoryW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, dir); + + BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf); + + free_pool_memory(pwszBuf); + + if (!b) { + errno = b_errno_win32; + return -1; + } + } else if (p_SetCurrentDirectoryA) { + if (0 == p_SetCurrentDirectoryA(dir)) { + errno = b_errno_win32; + return -1; + } + } else { + return -1; + } + + return 0; } +int +win32_mkdir(const char *dir) +{ + Dmsg1(100, "enter win32_mkdir. dir=%s\n", dir); + if (p_wmkdir){ + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, dir); + + int n = p_wmkdir((LPCWSTR)pwszBuf); + free_pool_memory(pwszBuf); + Dmsg0(100, "Leave win32_mkdir did wmkdir\n"); + return n; + } + + Dmsg0(100, "Leave win32_mkdir did _mkdir\n"); + return _mkdir(dir); +} + + char * win32_getcwd(char *buf, int maxlen) { - int n = GetCurrentDirectory(maxlen, buf); + int n=0; + + if (p_GetCurrentDirectoryW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t)); + + n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf); + if (n!=0) + n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1; + free_pool_memory(pwszBuf); + + } else if (p_GetCurrentDirectoryA) + n = p_GetCurrentDirectoryA(maxlen, buf); if (n == 0 || n > maxlen) return NULL; @@ -714,14 +1502,162 @@ win32_getcwd(char *buf, int maxlen) buf[n] = '\\'; buf[n+1] = 0; } - return buf; } +int +win32_fputs(const char *string, FILE *stream) +{ + /* we use WriteConsoleA / WriteConsoleA + so we can be sure that unicode support works on win32. + with fallback if something fails + */ + + HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE); + if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && + p_MultiByteToWideChar && (stream == stdout)) { + + POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE); + + DWORD dwCharsWritten; + DWORD dwChars; + + dwChars = UTF8_2_wchar(&pwszBuf, string); + + /* try WriteConsoleW */ + if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) { + free_pool_memory(pwszBuf); + return dwCharsWritten; + } + + /* convert to local codepage and try WriteConsoleA */ + POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE); + pszBuf = check_pool_memory_size(pszBuf, dwChars+1); + + dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR)pwszBuf,-1,pszBuf,dwChars,NULL,NULL); + free_pool_memory(pwszBuf); + + if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) { + free_pool_memory(pszBuf); + return dwCharsWritten; + } + free_pool_memory(pszBuf); + } + /* Fall back */ + return fputs(string, stream); +} + +char* +win32_cgets (char* buffer, int len) +{ + /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode + from the win32 console and fallback if seomething fails */ + + HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE); + if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) { + DWORD dwRead; + wchar_t wszBuf[1024]; + char szBuf[1024]; + + /* nt and unicode conversion */ + if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) { + + /* null terminate at end */ + if (wszBuf[dwRead-1] == L'\n') { + wszBuf[dwRead-1] = L'\0'; + dwRead --; + } + + if (wszBuf[dwRead-1] == L'\r') { + wszBuf[dwRead-1] = L'\0'; + dwRead --; + } + + wchar_2_UTF8(buffer, wszBuf, len); + return buffer; + } + + /* win 9x and unicode conversion */ + if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) { + + /* null terminate at end */ + if (szBuf[dwRead-1] == L'\n') { + szBuf[dwRead-1] = L'\0'; + dwRead --; + } + + if (szBuf[dwRead-1] == L'\r') { + szBuf[dwRead-1] = L'\0'; + dwRead --; + } + + /* convert from ansii to wchar_t */ + p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024); + /* convert from wchar_t to UTF-8 */ + if (wchar_2_UTF8(buffer, wszBuf, len)) + return buffer; + } + } + + /* fallback */ + if (fgets(buffer, len, stdin)) + return buffer; + else + return NULL; +} + +int +win32_unlink(const char *filename) +{ + int nRetCode; + if (p_wunlink) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, filename); + + nRetCode = _wunlink((LPCWSTR) pwszBuf); + + /* + * special case if file is readonly, + * we retry but unset attribute before + */ + if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) { + DWORD dwAttr = p_GetFileAttributesW((LPCWSTR)pwszBuf); + if (dwAttr != INVALID_FILE_ATTRIBUTES) { + if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) { + nRetCode = _wunlink((LPCWSTR) pwszBuf); + /* reset to original if it didn't help */ + if (nRetCode == -1) + p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr); + } + } + } + free_pool_memory(pwszBuf); + } else { + nRetCode = _unlink(filename); + + /* special case if file is readonly, + we retry but unset attribute before */ + if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) { + DWORD dwAttr = p_GetFileAttributesA(filename); + if (dwAttr != INVALID_FILE_ATTRIBUTES) { + if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) { + nRetCode = _unlink(filename); + /* reset to original if it didn't help */ + if (nRetCode == -1) + p_SetFileAttributesA(filename, dwAttr); + } + } + } + } + return nRetCode; +} + + #include "mswinver.h" char WIN_VERSION_LONG[64]; char WIN_VERSION[32]; +char WIN_RAWVERSION[32]; class winver { public: @@ -730,8 +1666,6 @@ public: static winver INIT; // cause constructor to be called before main() -#include "bacula.h" -#include "jcr.h" winver::winver(void) { @@ -745,8 +1679,11 @@ winver::winver(void) version = "Unknown"; platform = "Unknown"; } - else - switch (_mkversion(osvinfo.dwPlatformId, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion)) + const int ver = _mkversion(osvinfo.dwPlatformId, + osvinfo.dwMajorVersion, + osvinfo.dwMinorVersion); + snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver); + switch (ver) { case MS_WINDOWS_95: (version = "Windows 95"); break; case MS_WINDOWS_98: (version = "Windows 98"); break; @@ -755,14 +1692,14 @@ winver::winver(void) case MS_WINDOWS_2K: (version = "Windows 2000");platform = "NT"; break; case MS_WINDOWS_XP: (version = "Windows XP");platform = "NT"; break; case MS_WINDOWS_S2003: (version = "Windows Server 2003");platform = "NT"; break; - default: version = "Windows ??"; break; + default: version = WIN_RAWVERSION; break; } bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG)); - snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %d.%d.%d", + snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu", platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber); -#if 1 +#if 0 HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); CloseHandle(h); #endif @@ -779,22 +1716,37 @@ winver::winver(void) BOOL CreateChildProcess(VOID); VOID WriteToPipe(VOID); VOID ReadFromPipe(VOID); -VOID ErrorExit(LPTSTR); +VOID ErrorExit(LPCSTR); VOID ErrMsg(LPTSTR, BOOL); - +/** + * Check for a quoted path, if an absolute path name is given and it contains + * spaces it will need to be quoted. i.e. "c:/Program Files/foo/bar.exe" + * CreateProcess() says the best way to ensure proper results with executables + * with spaces in path or filename is to quote the string. + */ const char * getArgv0(const char *cmdline) { - const char *cp = cmdline; - while (*cp && !isspace(*cp)) cp++; + int inquote = 0; + const char *cp; + for (cp = cmdline; *cp; cp++) + { + if (*cp == '"') { + inquote = !inquote; + } + if (!inquote && isspace(*cp)) + break; + } + int len = cp - cmdline; char *rval = (char *)malloc(len+1); cp = cmdline; char *rp = rval; + while (len--) *rp++ = *cp++; @@ -802,100 +1754,340 @@ getArgv0(const char *cmdline) return rval; } +/* + * Extracts the executable or script name from the first string in + * cmdline. + * + * If the name contains blanks then it must be quoted with double quotes, + * otherwise quotes are optional. If the name contains blanks then it + * will be converted to a short name. + * + * The optional quotes will be removed. The result is copied to a malloc'ed + * buffer and returned through the pexe argument. The pargs parameter is set + * to the address of the character in cmdline located after the name. + * + * The malloc'ed buffer returned in *pexe must be freed by the caller. + */ +bool +GetApplicationName(const char *cmdline, char **pexe, const char **pargs) +{ + const char *pExeStart = NULL; /* Start of executable name in cmdline */ + const char *pExeEnd = NULL; /* Character after executable name (separator) */ + + const char *pBasename = NULL; /* Character after last path separator */ + const char *pExtension = NULL; /* Period at start of extension */ + + const char *current = cmdline; + + bool bQuoted = false; + + /* Skip initial whitespace */ + + while (*current == ' ' || *current == '\t') + { + current++; + } + + /* Calculate start of name and determine if quoted */ + + if (*current == '"') { + pExeStart = ++current; + bQuoted = true; + } else { + pExeStart = current; + bQuoted = false; + } + + *pargs = NULL; + *pexe = NULL; + + /* + * Scan command line looking for path separators (/ and \\) and the + * terminator, either a quote or a blank. The location of the + * extension is also noted. + */ + + for ( ; *current != '\0'; current++) + { + if (*current == '.') { + pExtension = current; + } else if (IsPathSeparator(*current) && current[1] != '\0') { + pBasename = ¤t[1]; + pExtension = NULL; + } + + /* Check for terminator, either quote or blank */ + if (bQuoted) { + if (*current != '"') { + continue; + } + } else { + if (*current != ' ') { + continue; + } + } + + /* + * Hit terminator, remember end of name (address of terminator) and + * start of arguments + */ + pExeEnd = current; + + if (bQuoted && *current == '"') { + *pargs = ¤t[1]; + } else { + *pargs = current; + } + + break; + } + + if (pBasename == NULL) { + pBasename = pExeStart; + } + + if (pExeEnd == NULL) { + pExeEnd = current; + } + + if (*pargs == NULL) + { + *pargs = current; + } + + bool bHasPathSeparators = pExeStart != pBasename; + + /* We have pointers to all the useful parts of the name */ + + /* Default extensions in the order cmd.exe uses to search */ + + static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" }; + DWORD dwBasePathLength = pExeEnd - pExeStart; + + DWORD dwAltNameLength = 0; + char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1); + char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1); + + pPathname[MAX_PATHLENGTH] = '\0'; + pAltPathname[MAX_PATHLENGTH] = '\0'; + + memcpy(pPathname, pExeStart, dwBasePathLength); + pPathname[dwBasePathLength] = '\0'; + + if (pExtension == NULL) { + /* Try appending extensions */ + for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) { + + if (!bHasPathSeparators) { + /* There are no path separators, search in the standard locations */ + dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL); + if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) { + memcpy(pPathname, pAltPathname, dwAltNameLength); + pPathname[dwAltNameLength] = '\0'; + break; + } + } else { + bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength); + if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) { + break; + } + pPathname[dwBasePathLength] = '\0'; + } + } + } else if (!bHasPathSeparators) { + /* There are no path separators, search in the standard locations */ + dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL); + if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) { + memcpy(pPathname, pAltPathname, dwAltNameLength); + pPathname[dwAltNameLength] = '\0'; + } + } + + if (strchr(pPathname, ' ') != NULL) { + dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH); + + if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) { + *pexe = (char *)malloc(dwAltNameLength + 1); + if (*pexe == NULL) { + return false; + } + memcpy(*pexe, pAltPathname, dwAltNameLength + 1); + } + } + + if (*pexe == NULL) { + DWORD dwPathnameLength = strlen(pPathname); + *pexe = (char *)malloc(dwPathnameLength + 1); + if (*pexe == NULL) { + return false; + } + memcpy(*pexe, pPathname, dwPathnameLength + 1); + } + + return true; +} + +/** + * Create the process with WCHAR API + */ +static BOOL +CreateChildProcessW(const char *comspec, const char *cmdLine, + PROCESS_INFORMATION *hProcInfo, + HANDLE in, HANDLE out, HANDLE err) +{ + STARTUPINFOW siStartInfo; + BOOL bFuncRetn = FALSE; + + // Set up members of the STARTUPINFO structure. + ZeroMemory( &siStartInfo, sizeof(siStartInfo) ); + siStartInfo.cb = sizeof(siStartInfo); + // setup new process to use supplied handles for stdin,stdout,stderr + + siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE; + + siStartInfo.hStdInput = in; + siStartInfo.hStdOutput = out; + siStartInfo.hStdError = err; + + // Convert argument to WCHAR + POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME); + POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME); + + UTF8_2_wchar(&cmdLine_wchar, cmdLine); + UTF8_2_wchar(&comspec_wchar, comspec); + + // Create the child process. + Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar); + + // try to execute program + bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar, + (WCHAR*)cmdLine_wchar,// command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + hProcInfo); // receives PROCESS_INFORMATION + free_pool_memory(cmdLine_wchar); + free_pool_memory(comspec_wchar); + + return bFuncRetn; +} + + +/** + * Create the process with ANSI API + */ +static BOOL +CreateChildProcessA(const char *comspec, char *cmdLine, + PROCESS_INFORMATION *hProcInfo, + HANDLE in, HANDLE out, HANDLE err) +{ + STARTUPINFOA siStartInfo; + BOOL bFuncRetn = FALSE; + + // Set up members of the STARTUPINFO structure. + ZeroMemory( &siStartInfo, sizeof(siStartInfo) ); + siStartInfo.cb = sizeof(siStartInfo); + // setup new process to use supplied handles for stdin,stdout,stderr + siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE; + + siStartInfo.hStdInput = in; + siStartInfo.hStdOutput = out; + siStartInfo.hStdError = err; + + // Create the child process. + Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine); + + // try to execute program + bFuncRetn = p_CreateProcessA(comspec, + cmdLine, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo,// STARTUPINFO pointer + hProcInfo);// receives PROCESS_INFORMATION + return bFuncRetn; +} + +/** + * OK, so it would seem CreateProcess only handles true executables: + * .com or .exe files. So grab $COMSPEC value and pass command line to it. + */ HANDLE CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err) { - PROCESS_INFORMATION piProcInfo; - STARTUPINFO siStartInfo; - BOOL bFuncRetn = FALSE; + static const char *comspec = NULL; + PROCESS_INFORMATION piProcInfo; + BOOL bFuncRetn = FALSE; - // Set up members of the PROCESS_INFORMATION structure. + if (!p_CreateProcessA || !p_CreateProcessW) + return INVALID_HANDLE_VALUE; - ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); + if (comspec == NULL) + comspec = getenv("COMSPEC"); + if (comspec == NULL) // should never happen + return INVALID_HANDLE_VALUE; - // Set up members of the STARTUPINFO structure. + // Set up members of the PROCESS_INFORMATION structure. + ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); - ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); - siStartInfo.cb = sizeof(STARTUPINFO); - // setup new process to use supplied handles for stdin,stdout,stderr - // if supplied handles are not used the send a copy of our STD_HANDLE - // as appropriate - siStartInfo.dwFlags = STARTF_USESTDHANDLES; + // if supplied handles are not used the send a copy of our STD_HANDLE + // as appropriate + if (in == INVALID_HANDLE_VALUE) + in = GetStdHandle(STD_INPUT_HANDLE); - if (in != INVALID_HANDLE_VALUE) - siStartInfo.hStdInput = in; - else - siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + if (out == INVALID_HANDLE_VALUE) + out = GetStdHandle(STD_OUTPUT_HANDLE); - if (out != INVALID_HANDLE_VALUE) - siStartInfo.hStdOutput = out; - else - siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - if (err != INVALID_HANDLE_VALUE) - siStartInfo.hStdError = err; - else - siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); - // Create the child process. - - char cmdLine[1024]; - char exeFile[1024]; - // retrive the first compont of the command line which should be the - // executable - const char *exeName = getArgv0(cmdline); - // check to see if absolute path was passed to us already? - if (exeName[1] != ':' - || (strchr(cmdline, '/') == NULL - && strchr(cmdline, '\\') == NULL)) - { - // only command name so perform search of PATH to find - char *file; - DWORD rval = SearchPath(NULL, - exeName, - ".exe", - sizeof(exeFile), - exeFile, - &file); - if (rval == 0) - return INVALID_HANDLE_VALUE; - if (rval > sizeof(exeFile)) - return INVALID_HANDLE_VALUE; + if (err == INVALID_HANDLE_VALUE) + err = GetStdHandle(STD_ERROR_HANDLE); - } - else - strcpy(exeFile, exeName); - - // exeFile now has absolute path to program to execute. - free((void *)exeName); - // copy original command line to pass to create process - strcpy(cmdLine, cmdline); - // try to execute program - bFuncRetn = CreateProcess(exeFile, - cmdLine, // command line - NULL, // process security attributes - NULL, // primary thread security attributes - TRUE, // handles are inherited - 0, // creation flags - NULL, // use parent's environment - NULL, // use parent's current directory - &siStartInfo, // STARTUPINFO pointer - &piProcInfo); // receives PROCESS_INFORMATION - - if (bFuncRetn == 0) { - ErrorExit("CreateProcess failed\n"); - return INVALID_HANDLE_VALUE; - } - // we don't need a handle on the process primary thread so we close - // this now. - CloseHandle(piProcInfo.hThread); + char *exeFile; + const char *argStart; - return piProcInfo.hProcess; -} + if (!GetApplicationName(cmdline, &exeFile, &argStart)) { + return INVALID_HANDLE_VALUE; + } + + POOL_MEM cmdLine(PM_FNAME); + Mmsg(cmdLine, "%s /c %s%s", comspec, exeFile, argStart); + free(exeFile); + + // New function disabled + if (p_CreateProcessW && p_MultiByteToWideChar) { + bFuncRetn = CreateChildProcessW(comspec, cmdLine.c_str(), &piProcInfo, + in, out, err); + } else { + bFuncRetn = CreateChildProcessA(comspec, cmdLine.c_str(), &piProcInfo, + in, out, err); + } + + if (bFuncRetn == 0) { + ErrorExit("CreateProcess failed\n"); + const char *err = errorString(); + Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n",comspec,cmdLine.c_str(),err); + LocalFree((void *)err); + return INVALID_HANDLE_VALUE; + } + // we don't need a handle on the process primary thread so we close + // this now. + CloseHandle(piProcInfo.hThread); + return piProcInfo.hProcess; +} void -ErrorExit (LPTSTR lpszMessage) +ErrorExit (LPCSTR lpszMessage) { - d_msg(__FILE__, __LINE__, 0, "%s", lpszMessage); + Dmsg1(0, "%s", lpszMessage); } @@ -917,7 +2109,6 @@ CloseIfValid(HANDLE handle) CloseHandle(handle); } -#ifndef HAVE_MINGW BPIPE * open_bpipe(char *prog, int wait, const char *mode) { @@ -966,13 +2157,14 @@ open_bpipe(char *prog, int wait, const char *mode) } CloseHandle(hChildStdoutRd); + hChildStdoutRd = INVALID_HANDLE_VALUE; } if (mode_write) { // Create a pipe for the child process's STDIN. - if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) { + if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) { ErrorExit("Stdin pipe creation failed\n"); goto cleanup; } @@ -989,13 +2181,14 @@ open_bpipe(char *prog, int wait, const char *mode) } CloseHandle(hChildStdinWr); + hChildStdinWr = INVALID_HANDLE_VALUE; } // spawn program with redirected handles as appropriate bpipe->worker_pid = (pid_t) - CreateChildProcess(prog, // commandline - hChildStdinRd, // stdin HANDLE - hChildStdoutWr, // stdout HANDLE - hChildStdoutWr);// stderr HANDLE + CreateChildProcess(prog, // commandline + hChildStdinRd, // stdin HANDLE + hChildStdoutWr, // stdout HANDLE + hChildStdoutWr); // stderr HANDLE if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE) goto cleanup; @@ -1008,19 +2201,23 @@ open_bpipe(char *prog, int wait, const char *mode) // process terminates we can // detect eof. // ugly but convert WIN32 HANDLE to FILE* - int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY); - bpipe->rfd = _fdopen(rfd, "r"); + int rfd = _open_osfhandle((intptr_t)hChildStdoutRdDup, O_RDONLY | O_BINARY); + if (rfd >= 0) { + bpipe->rfd = _fdopen(rfd, "rb"); + } } if (mode_write) { CloseHandle(hChildStdinRd); // close our read side so as not // to interfre with child's copy // ugly but convert WIN32 HANDLE to FILE* - int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY); - bpipe->wfd = _fdopen(wfd, "w"); + int wfd = _open_osfhandle((intptr_t)hChildStdinWrDup, O_WRONLY | O_BINARY); + if (wfd >= 0) { + bpipe->wfd = _fdopen(wfd, "wb"); + } } if (wait > 0) { - bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait); + bpipe->timer_id = start_child_timer(NULL, bpipe->worker_pid, wait); } return bpipe; @@ -1033,223 +2230,240 @@ cleanup: CloseIfValid(hChildStdinWrDup); free((void *) bpipe); - + errno = b_errno_win32; /* do GetLastError() for error code */ return NULL; } -#endif //HAVE_MINGW int kill(int pid, int signal) { - int rval = 0; - if (!TerminateProcess((HANDLE)pid, (UINT) signal)) - rval = -1; - CloseHandle((HANDLE)pid); - return rval; + int rval = 0; + if (!TerminateProcess((HANDLE)pid, (UINT) signal)) { + rval = -1; + errno = b_errno_win32; + } + CloseHandle((HANDLE)pid); + return rval; } -#ifndef HAVE_MINGW int close_bpipe(BPIPE *bpipe) { - int rval = 0; - if (bpipe->rfd) fclose(bpipe->rfd); - if (bpipe->wfd) fclose(bpipe->wfd); + int rval = 0; + int32_t remaining_wait = bpipe->wait; - if (bpipe->wait) { - int remaining_wait = bpipe->wait; - do - { - DWORD exitCode; - if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) - { - const char *err = errorString(); - rval = GetLastError(); - d_msg(__FILE__, __LINE__, 0, - "GetExitCode error %s\n", err); - LocalFree((void *)err); - break; - } + /* Close pipes */ + if (bpipe->rfd) { + fclose(bpipe->rfd); + bpipe->rfd = NULL; + } + if (bpipe->wfd) { + fclose(bpipe->wfd); + bpipe->wfd = NULL; + } - if (exitCode == STILL_ACTIVE) { - bmicrosleep(1, 0); /* wait one second */ - remaining_wait--; - } - else break; - } while(remaining_wait); - } + if (remaining_wait == 0) { /* wait indefinitely */ + remaining_wait = INT32_MAX; + } + for ( ;; ) { + DWORD exitCode; + if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) { + const char *err = errorString(); + rval = b_errno_win32; + Dmsg1(0, "GetExitCode error %s\n", err); + LocalFree((void *)err); + break; + } + if (exitCode == STILL_ACTIVE) { + if (remaining_wait <= 0) { + rval = ETIME; /* timed out */ + break; + } + bmicrosleep(1, 0); /* wait one second */ + remaining_wait--; + } else if (exitCode != 0) { + /* Truncate exit code as it doesn't seem to be correct */ + rval = (exitCode & 0xFF) | b_errno_exit; + break; + } else { + break; /* Shouldn't get here */ + } + } - if (bpipe->timer_id) { - stop_child_timer(bpipe->timer_id); - } - free((void *)bpipe); - return rval; + if (bpipe->timer_id) { + stop_child_timer(bpipe->timer_id); + } + if (bpipe->rfd) fclose(bpipe->rfd); + if (bpipe->wfd) fclose(bpipe->wfd); + free((void *)bpipe); + return rval; } int close_wpipe(BPIPE *bpipe) { - int stat = 1; + int result = 1; if (bpipe->wfd) { fflush(bpipe->wfd); if (fclose(bpipe->wfd) != 0) { - stat = 0; - } + result = 0; + } bpipe->wfd = NULL; } - return stat; + return result; } -#include "findlib/find.h" - +#ifndef MINGW64 int utime(const char *fname, struct utimbuf *times) { FILETIME acc, mod; - char tmpbuf[1024]; + char tmpbuf[5000]; - cygwin_conv_to_win32_path(fname, tmpbuf); + conv_unix_to_win32_path(fname, tmpbuf, 5000); cvt_utime_to_ftime(times->actime, acc); cvt_utime_to_ftime(times->modtime, mod); - HANDLE h = CreateFile(tmpbuf, - FILE_WRITE_ATTRIBUTES, - FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, - NULL); + HANDLE h = INVALID_HANDLE_VALUE; + + if (p_CreateFileW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf); + + h = p_CreateFileW((LPCWSTR)pwszBuf, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, // required for directories + NULL); + + free_pool_memory(pwszBuf); + } else if (p_CreateFileA) { + h = p_CreateFileA(tmpbuf, + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, // required for directories + NULL); + } if (h == INVALID_HANDLE_VALUE) { - const char *err = errorString(); - d_msg(__FILE__, __LINE__, 99, - "Cannot open file for utime(%s,...):%s\n", tmpbuf, err); - LocalFree((void *)err); - return -1; + const char *err = errorString(); + Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err); + LocalFree((void *)err); + errno = b_errno_win32; + return -1; } int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1; - CloseHandle(h); - + if (rval == -1) { + errno = b_errno_win32; + } return rval; } +#endif -#if USE_WIN32_COMPAT_IO - -int -open(const char *file, int flags, int mode) -{ - return _open(file, flags|_O_BINARY, mode); - -} - -int -close(int fd) -{ - return _close(fd); -} - -ssize_t -read(int fd, void *buf, size_t len) -{ - return _read(fd, buf, len); -} - -ssize_t -write(int fd, const void *buf, size_t len) -{ - return _write(fd, buf, len); -} - -off_t -lseek(int fd, off_t offset, int whence) -{ - return _lseeki64(fd, offset, whence); -} - -int -dup2(int fd1, int fd2) -{ - return _dup2(fd1, fd2); -} -#else +#if 0 int -open(const char *file, int flags, int mode) +file_open(const char *file, int flags, int mode) { - DWORD access = 0; - DWORD shareMode = 0; - DWORD create = 0; - DWORD msflags = 0; - HANDLE foo; - const char *remap = file; - - if (flags & O_WRONLY) access = GENERIC_WRITE; - else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE; - else access = GENERIC_READ; - - if (flags & O_CREAT) create = CREATE_NEW; - else create = OPEN_EXISTING; + DWORD access = 0; + DWORD shareMode = 0; + DWORD create = 0; + DWORD msflags = 0; + HANDLE foo = INVALID_HANDLE_VALUE; + const char *remap = file; + + if (flags & O_WRONLY) access = GENERIC_WRITE; + else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE; + else access = GENERIC_READ; + + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + create = CREATE_NEW; + else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + create = CREATE_ALWAYS; + else if (flags & O_CREAT) + create = OPEN_ALWAYS; + else if (flags & O_TRUNC) + create = TRUNCATE_EXISTING; + else + create = OPEN_EXISTING; + + shareMode = 0; + + if (flags & O_APPEND) { + printf("open...APPEND not implemented yet."); + exit(-1); + } - if (flags & O_TRUNC) create = TRUNCATE_EXISTING; + if (p_CreateFileW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, file); - if (!(flags & O_EXCL)) - shareMode = FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE; + foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL); + free_pool_memory(pwszBuf); + } else if (p_CreateFileA) + foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL); - if (flags & O_APPEND) - { - printf("open...APPEND not implemented yet."); - exit(-1); - } - - foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL); - if (INVALID_HANDLE_VALUE == foo) - return(int) -1; - - return (int)foo; + if (INVALID_HANDLE_VALUE == foo) { + errno = b_errno_win32; + return (int)-1; + } + return (int)foo; } int -close(int fd) +file_close(int fd) { - if (!CloseHandle((HANDLE)fd)) + if (!CloseHandle((HANDLE)fd)) { + errno = b_errno_win32; return -1; + } return 0; } ssize_t -write(int fd, const void *data, size_t len) +file_write(int fd, const void *data, ssize_t len) { BOOL status; DWORD bwrite; status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL); if (status) return bwrite; + errno = b_errno_win32; return -1; } ssize_t -read(int fd, void *data, size_t len) +file_read(int fd, void *data, ssize_t len) { BOOL status; DWORD bread; status = ReadFile((HANDLE)fd, data, len, &bread, NULL); if (status) return bread; + errno = b_errno_win32; return -1; } -off_t -lseek(int fd, off_t offset, int whence) +boffset_t +file_seek(int fd, boffset_t offset, int whence) { DWORD method = 0; + DWORD val; + LONG offset_low = (LONG)offset; + LONG offset_high = (LONG)(offset >> 32); + switch (whence) { case SEEK_SET : method = FILE_BEGIN; @@ -1261,24 +2475,94 @@ lseek(int fd, off_t offset, int whence) method = FILE_END; break; default: + errno = EINVAL; return -1; } - return SetFilePointer((HANDLE)fd, (DWORD)offset, NULL, method); + + if ((val=SetFilePointer((HANDLE)fd, offset_low, &offset_high, method)) == INVALID_SET_FILE_POINTER) { + errno = b_errno_win32; + return -1; + } + /* ***FIXME*** I doubt this works right */ + return val; } int -dup2(int, int) +file_dup2(int, int) { + errno = ENOSYS; return -1; } +#endif +#ifdef xxx +/* + * Emulation of mmap and unmmap for tokyo dbm + */ +void *mmap(void *start, size_t length, int prot, int flags, + int fd, off_t offset) +{ + DWORD fm_access = 0; + DWORD mv_access = 0; + HANDLE h; + HANDLE mv; -#endif + if (length == 0) { + return MAP_FAILED; + } + if (!fd) { + return MAP_FAILED; + } -#endif //HAVE_MINGW + if (flags & PROT_WRITE) { + fm_access |= PAGE_READWRITE; + } else if (flags & PROT_READ) { + fm_access |= PAGE_READONLY; + } + + if (flags & PROT_READ) { + mv_access |= FILE_MAP_READ; + } + if (flags & PROT_WRITE) { + mv_access |= FILE_MAP_WRITE; + } + + h = CreateFileMapping((HANDLE)_get_osfhandle (fd), + NULL /* security */, + fm_access, + 0 /* MaximumSizeHigh */, + 0 /* MaximumSizeLow */, + NULL /* name of the file mapping object */); + + if (!h || h == INVALID_HANDLE_VALUE) { + return MAP_FAILED; + } + + mv = MapViewOfFile(h, mv_access, + 0 /* offset hi */, + 0 /* offset lo */, + length); + CloseHandle(h); + + if (!mv || mv == INVALID_HANDLE_VALUE) { + return MAP_FAILED; + } + + return (void *) mv; +} + +int munmap(void *start, size_t length) +{ + if (!start) { + return -1; + } + UnmapViewOfFile(start); + return 0; +} +#endif #ifdef HAVE_MINGW /* syslog function, added by Nicolas Boichat */ -void closelog() {} +void openlog(const char *ident, int option, int facility) {} #endif //HAVE_MINGW