// Box Backup Win32 native port by Nick Knight #include "emu.h" #ifdef WIN32 #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include // message resource definitions for syslog() #include "messages.h" DWORD winerrno; struct passwd gTempPasswd; bool EnableBackupRights() { HANDLE hToken; TOKEN_PRIVILEGES token_priv; //open current process to adjust privileges if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { ::syslog(LOG_ERR, "Failed to open process token: %s", GetErrorMessage(GetLastError()).c_str()); return false; } //let's build the token privilege struct - //first, look up the LUID for the backup privilege if (!LookupPrivilegeValue( NULL, //this system SE_BACKUP_NAME, //the name of the privilege &( token_priv.Privileges[0].Luid ))) //result { ::syslog(LOG_ERR, "Failed to lookup backup privilege: %s", GetErrorMessage(GetLastError()).c_str()); CloseHandle(hToken); return false; } token_priv.PrivilegeCount = 1; token_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // now set the privilege // because we're going exit right after dumping the streams, there isn't // any need to save current state if (!AdjustTokenPrivileges( hToken, //our process token false, //we're not disabling everything &token_priv, //address of structure sizeof(token_priv), //size of structure NULL, NULL)) //don't save current state { //this function is a little tricky - if we were adjusting //more than one privilege, it could return success but not //adjust them all - in the general case, you need to trap this ::syslog(LOG_ERR, "Failed to enable backup privilege: %s", GetErrorMessage(GetLastError()).c_str()); CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } // -------------------------------------------------------------------------- // // Function // Name: GetDefaultConfigFilePath(std::string name) // Purpose: Calculates the default configuration file name, // by using the directory location of the currently // executing program, and appending the provided name. // In case of fire, returns an empty string. // Created: 26th May 2007 // // -------------------------------------------------------------------------- std::string GetDefaultConfigFilePath(const std::string& rName) { WCHAR exePathWide[MAX_PATH]; GetModuleFileNameW(NULL, exePathWide, MAX_PATH-1); char* exePathUtf8 = ConvertFromWideString(exePathWide, CP_UTF8); if (exePathUtf8 == NULL) { return ""; } std::string configfile = exePathUtf8; delete [] exePathUtf8; // make the default config file name, // based on the program path configfile = configfile.substr(0, configfile.rfind('\\')); configfile += "\\"; configfile += rName; return configfile; } // -------------------------------------------------------------------------- // // Function // Name: ConvertToWideString // Purpose: Converts a string from specified codepage to // a wide string (WCHAR*). Returns a buffer which // MUST be freed by the caller with delete[]. // In case of fire, logs the error and returns NULL. // Created: 4th February 2006 // // -------------------------------------------------------------------------- WCHAR* ConvertToWideString(const char* pString, unsigned int codepage, bool logErrors) { int len = MultiByteToWideChar ( codepage, // source code page 0, // character-type options pString, // string to map -1, // number of bytes in string - auto detect NULL, // wide-character buffer 0 // size of buffer - work out // how much space we need ); if (len == 0) { winerrno = GetLastError(); if (logErrors) { ::syslog(LOG_WARNING, "Failed to convert string to wide string: " "%s", GetErrorMessage(winerrno).c_str()); } errno = EINVAL; return NULL; } WCHAR* buffer = new WCHAR[len]; if (buffer == NULL) { if (logErrors) { ::syslog(LOG_WARNING, "Failed to convert string to wide string: " "out of memory"); } winerrno = ERROR_OUTOFMEMORY; errno = ENOMEM; return NULL; } len = MultiByteToWideChar ( codepage, // source code page 0, // character-type options pString, // string to map -1, // number of bytes in string - auto detect buffer, // wide-character buffer len // size of buffer ); if (len == 0) { winerrno = GetLastError(); if (logErrors) { ::syslog(LOG_WARNING, "Failed to convert string to wide string: " "%s", GetErrorMessage(winerrno).c_str()); } errno = EACCES; delete [] buffer; return NULL; } return buffer; } // -------------------------------------------------------------------------- // // Function // Name: ConvertUtf8ToWideString // Purpose: Converts a string from UTF-8 to a wide string. // Returns a buffer which MUST be freed by the caller // with delete[]. // In case of fire, logs the error and returns NULL. // Created: 4th February 2006 // // -------------------------------------------------------------------------- WCHAR* ConvertUtf8ToWideString(const char* pString) { return ConvertToWideString(pString, CP_UTF8, true); } // -------------------------------------------------------------------------- // // Function // Name: ConvertFromWideString // Purpose: Converts a wide string to a narrow string in the // specified code page. Returns a buffer which MUST // be freed by the caller with delete[]. // In case of fire, logs the error and returns NULL. // Created: 4th February 2006 // // -------------------------------------------------------------------------- char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage) { int len = WideCharToMultiByte ( codepage, // destination code page 0, // character-type options pString, // string to map -1, // number of bytes in string - auto detect NULL, // output buffer 0, // size of buffer - work out // how much space we need NULL, // replace unknown chars with system default NULL // don't tell us when that happened ); if (len == 0) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EINVAL; return NULL; } char* buffer = new char[len]; if (buffer == NULL) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "out of memory"); errno = ENOMEM; return NULL; } len = WideCharToMultiByte ( codepage, // source code page 0, // character-type options pString, // string to map -1, // number of bytes in string - auto detect buffer, // output buffer len, // size of buffer NULL, // replace unknown chars with system default NULL // don't tell us when that happened ); if (len == 0) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EACCES; delete [] buffer; return NULL; } return buffer; } bool ConvertFromWideString(const std::wstring& rInput, std::string* pOutput, unsigned int codepage) { int len = WideCharToMultiByte ( codepage, // destination code page 0, // character-type options rInput.c_str(), // string to map rInput.size(), // number of bytes in string - auto detect NULL, // output buffer 0, // size of buffer - work out // how much space we need NULL, // replace unknown chars with system default NULL // don't tell us when that happened ); if (len == 0) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EINVAL; return false; } char* buffer = new char[len]; if (buffer == NULL) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "out of memory"); errno = ENOMEM; return false; } len = WideCharToMultiByte ( codepage, // source code page 0, // character-type options rInput.c_str(), // string to map rInput.size(), // number of bytes in string - auto detect buffer, // output buffer len, // size of buffer NULL, // replace unknown chars with system default NULL // don't tell us when that happened ); if (len == 0) { ::syslog(LOG_WARNING, "Failed to convert wide string to narrow: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EACCES; delete [] buffer; return false; } *pOutput = std::string(buffer, len); delete [] buffer; return true; } // -------------------------------------------------------------------------- // // Function // Name: ConvertEncoding(const std::string&, int, // std::string&, int) // Purpose: Converts a string from one code page to another. // On success, replaces contents of rDest and returns // true. In case of fire, logs the error and returns // false. // Created: 15th October 2006 // // -------------------------------------------------------------------------- bool ConvertEncoding(const std::string& rSource, int sourceCodePage, std::string& rDest, int destCodePage) { WCHAR* pWide = ConvertToWideString(rSource.c_str(), sourceCodePage, true); if (pWide == NULL) { ::syslog(LOG_ERR, "Failed to convert string '%s' from " "current code page %d to wide string: %s", rSource.c_str(), sourceCodePage, GetErrorMessage(GetLastError()).c_str()); return false; } char* pConsole = ConvertFromWideString(pWide, destCodePage); delete [] pWide; if (!pConsole) { // Error should have been logged by ConvertFromWideString return false; } rDest = pConsole; delete [] pConsole; return true; } bool ConvertToUtf8(const std::string& rSource, std::string& rDest, int sourceCodePage) { return ConvertEncoding(rSource, sourceCodePage, rDest, CP_UTF8); } bool ConvertFromUtf8(const std::string& rSource, std::string& rDest, int destCodePage) { return ConvertEncoding(rSource, CP_UTF8, rDest, destCodePage); } bool ConvertConsoleToUtf8(const std::string& rSource, std::string& rDest) { return ConvertToUtf8(rSource, rDest, GetConsoleCP()); } bool ConvertUtf8ToConsole(const std::string& rSource, std::string& rDest) { return ConvertFromUtf8(rSource, rDest, GetConsoleOutputCP()); } // -------------------------------------------------------------------------- // // Function // Name: ConvertPathToAbsoluteUnicode // Purpose: Converts relative paths to absolute (with unicode marker) // Created: 4th February 2006 // // -------------------------------------------------------------------------- std::string ConvertPathToAbsoluteUnicode(const char *pFileName) { std::string filename; for (int i = 0; pFileName[i] != 0; i++) { if (pFileName[i] == '/') { filename += '\\'; } else { filename += pFileName[i]; } } std::string tmpStr("\\\\?\\"); // Is the path relative or absolute? // Absolute paths on Windows are always a drive letter // followed by ':' char wd[PATH_MAX]; if (::getcwd(wd, PATH_MAX) == 0) { ::syslog(LOG_WARNING, "Failed to open '%s': path too long", pFileName); errno = ENAMETOOLONG; winerrno = ERROR_INVALID_NAME; tmpStr = ""; return tmpStr; } if (filename.length() > 4 && filename[0] == '\\' && filename[1] == '\\' && filename[2] == '?' && filename[3] == '\\') { // File is already in absolute utf-8 format, e.g. // \\?\GLOBALROOT\... tmpStr = ""; } else if (filename.length() > 2 && filename[0] == '\\' && filename[1] == '\\') { tmpStr += "UNC\\"; filename.replace(0, 2, ""); // \\?\UNC\\ // see http://msdn2.microsoft.com/en-us/library/aa365247.aspx } else if (filename.length() >= 1 && filename[0] == '\\') { // starts with \, i.e. root directory of current drive. tmpStr = wd; tmpStr.resize(2); // drive letter and colon } else if (filename.length() >= 2 && filename[1] != ':') { // Must be a relative path. We need to get the // current directory to make it absolute. tmpStr += wd; if (tmpStr[tmpStr.length()-1] != '\\') { tmpStr += '\\'; } } tmpStr += filename; // We are using direct filename access, which does not support .., // so we need to implement it ourselves. for (std::string::size_type i = 1; i < tmpStr.size() - 3; i++) { if (tmpStr.substr(i, 3) == "\\..") { std::string::size_type lastSlash = tmpStr.rfind('\\', i - 1); if (lastSlash == std::string::npos) { // no previous directory, ignore it, // CreateFile will fail with error 123 } else { tmpStr.replace(lastSlash, i + 3 - lastSlash, ""); } i = lastSlash - 1; } } return tmpStr; } std::string GetErrorMessage(DWORD errorCode) { char* pMsgBuf = NULL; DWORD chars = FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)(&pMsgBuf), 0, NULL ); if (chars == 0 || pMsgBuf == NULL) { return std::string("failed to get error message"); } // remove embedded newline pMsgBuf[chars - 1] = 0; pMsgBuf[chars - 2] = 0; std::ostringstream line; line << pMsgBuf << " (" << errorCode << ")"; LocalFree(pMsgBuf); return line.str(); } // -------------------------------------------------------------------------- // // Function // Name: openfile // Purpose: replacement for any open calls - handles unicode // filenames - supplied in utf8 // Created: 25th October 2004 // // -------------------------------------------------------------------------- HANDLE openfile(const char *pFileName, int flags, int mode) { winerrno = ERROR_INVALID_FUNCTION; std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return INVALID_HANDLE_VALUE; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); // We are responsible for freeing pBuffer if (pBuffer == NULL) { // error already logged by ConvertUtf8ToWideString() return INVALID_HANDLE_VALUE; } // flags could be O_WRONLY | O_CREAT | O_RDONLY DWORD createDisposition = OPEN_EXISTING; DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA; if (flags & O_WRONLY) { accessRights = FILE_WRITE_DATA; } else if (flags & O_RDWR) { accessRights |= FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA; } if (flags & O_CREAT) { createDisposition = OPEN_ALWAYS; } if (flags & O_TRUNC) { createDisposition = CREATE_ALWAYS; } if ((flags & O_CREAT) && (flags & O_EXCL)) { createDisposition = CREATE_NEW; } if (flags & O_LOCK) { shareMode = 0; } DWORD winFlags = FILE_FLAG_BACKUP_SEMANTICS; if (flags & O_TEMPORARY) { winFlags |= FILE_FLAG_DELETE_ON_CLOSE; } HANDLE hdir = CreateFileW(pBuffer, accessRights, shareMode, NULL, createDisposition, winFlags, NULL); delete [] pBuffer; if (hdir == INVALID_HANDLE_VALUE) { winerrno = GetLastError(); switch(winerrno) { case ERROR_SHARING_VIOLATION: errno = EBUSY; break; default: errno = EINVAL; } ::syslog(LOG_WARNING, "Failed to open file '%s': " "%s", pFileName, GetErrorMessage(GetLastError()).c_str()); return INVALID_HANDLE_VALUE; } if (flags & O_APPEND) { if (SetFilePointer(hdir, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { winerrno = GetLastError(); errno = EINVAL; CloseHandle(hdir); return INVALID_HANDLE_VALUE; } } winerrno = NO_ERROR; return hdir; } // -------------------------------------------------------------------------- // // Function // Name: emu_fstat // Purpose: replacement for fstat. Supply a windows handle. // Returns a struct emu_stat to have room for 64-bit // file identifier in st_ino (mingw allows only 16!) // Created: 25th October 2004 // // -------------------------------------------------------------------------- int emu_fstat(HANDLE hdir, struct emu_stat * st) { if (hdir == INVALID_HANDLE_VALUE) { ::syslog(LOG_ERR, "Error: invalid file handle in emu_fstat()"); errno = EBADF; return -1; } BY_HANDLE_FILE_INFORMATION fi; if (!GetFileInformationByHandle(hdir, &fi)) { ::syslog(LOG_WARNING, "Failed to read file information: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EACCES; return -1; } if (INVALID_FILE_ATTRIBUTES == fi.dwFileAttributes) { ::syslog(LOG_WARNING, "Failed to get file attributes: " "%s", GetErrorMessage(GetLastError()).c_str()); errno = EACCES; return -1; } memset(st, 0, sizeof(*st)); // This is how we get our INODE (equivalent) information ULARGE_INTEGER conv; conv.HighPart = fi.nFileIndexHigh; conv.LowPart = fi.nFileIndexLow; st->st_ino = conv.QuadPart; // get the time information st->st_ctime = ConvertFileTimeToTime_t(&fi.ftCreationTime); st->st_atime = ConvertFileTimeToTime_t(&fi.ftLastAccessTime); st->st_mtime = ConvertFileTimeToTime_t(&fi.ftLastWriteTime); if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { st->st_size = 0; } else { conv.HighPart = fi.nFileSizeHigh; conv.LowPart = fi.nFileSizeLow; st->st_size = conv.QuadPart; } // at the mo st->st_uid = 0; st->st_gid = 0; st->st_nlink = 1; // the mode of the file // mode zero will make it impossible to restore on Unix // (no access to anybody, including the owner). // we'll fake a sensible mode: // all objects get user read (0400) // if it's a directory it gets user execute (0100) // if it's not read-only it gets user write (0200) st->st_mode = S_IREAD; if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { st->st_mode |= S_IFDIR | S_IEXEC; } else { st->st_mode |= S_IFREG; } if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { st->st_mode |= S_IWRITE; } // st_dev is normally zero, regardless of the drive letter, // since backup locations can't normally span drives. However, // a reparse point does allow all kinds of weird stuff to happen. // We set st_dev to 1 for a reparse point, so that Box will detect // a change of device number (from 0) and refuse to recurse down // the reparse point (which could lead to havoc). if (fi.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { st->st_dev = 1; } else { st->st_dev = 0; } return 0; } // -------------------------------------------------------------------------- // // Function // Name: OpenFileByNameUtf8 // Purpose: Converts filename to Unicode and returns // a handle to it. In case of error, sets errno, // logs the error and returns NULL. // Created: 10th December 2004 // // -------------------------------------------------------------------------- HANDLE OpenFileByNameUtf8(const char* pFileName, DWORD flags) { std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return NULL; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); // We are responsible for freeing pBuffer if (pBuffer == NULL) { // error already logged by ConvertUtf8ToWideString() return NULL; } HANDLE handle = CreateFileW(pBuffer, flags, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { // if our open fails we should always be able to // open in this mode - to get the inode information // at least one process must have the file open - // in this case someone else does. handle = CreateFileW(pBuffer, READ_CONTROL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); } delete [] pBuffer; if (handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { errno = ENOENT; } else { ::syslog(LOG_WARNING, "Failed to open '%s': " "%s", pFileName, GetErrorMessage(err).c_str()); errno = EACCES; } return NULL; } return handle; } // -------------------------------------------------------------------------- // // Function // Name: emu_stat // Purpose: replacement for the lstat and stat functions. // Works with unicode filenames supplied in utf8. // Returns a struct emu_stat to have room for 64-bit // file identifier in st_ino (mingw allows only 16!) // Created: 25th October 2004 // // -------------------------------------------------------------------------- int emu_stat(const char * pName, struct emu_stat * st) { HANDLE handle = OpenFileByNameUtf8(pName, FILE_READ_ATTRIBUTES | FILE_READ_EA); if (handle == NULL) { // errno already set and error logged by OpenFileByNameUtf8() return -1; } int retVal = emu_fstat(handle, st); if (retVal != 0) { // error logged, but without filename ::syslog(LOG_WARNING, "Failed to get file information " "for '%s'", pName); } // close the handle CloseHandle(handle); return retVal; } // -------------------------------------------------------------------------- // // Function // Name: statfs // Purpose: returns the mount point of where a file is located - // in this case the volume serial number // Created: 25th October 2004 // // -------------------------------------------------------------------------- int statfs(const char * pName, struct statfs * s) { HANDLE handle = OpenFileByNameUtf8(pName, FILE_READ_ATTRIBUTES | FILE_READ_EA); if (handle == NULL) { // errno already set and error logged by OpenFileByNameUtf8() return -1; } BY_HANDLE_FILE_INFORMATION fi; if (!GetFileInformationByHandle(handle, &fi)) { ::syslog(LOG_WARNING, "Failed to get file information " "for '%s': %s", pName, GetErrorMessage(GetLastError()).c_str()); CloseHandle(handle); errno = EACCES; return -1; } // convert volume serial number to a string _ui64toa(fi.dwVolumeSerialNumber, s->f_mntonname + 1, 16); // pseudo unix mount point s->f_mntonname[0] = '\\'; CloseHandle(handle); // close the handle return 0; } // -------------------------------------------------------------------------- // // Function // Name: emu_utimes // Purpose: replacement for the POSIX utimes() function, // works with unicode filenames supplied in utf8 format, // sets creation time instead of last access time. // Created: 25th July 2006 // // -------------------------------------------------------------------------- int emu_utimes(const char * pName, const struct timeval times[]) { FILETIME creationTime; if (!ConvertTime_tToFileTime(times[0].tv_sec, &creationTime)) { errno = EINVAL; return -1; } FILETIME modificationTime; if (!ConvertTime_tToFileTime(times[1].tv_sec, &modificationTime)) { errno = EINVAL; return -1; } HANDLE handle = OpenFileByNameUtf8(pName, FILE_WRITE_ATTRIBUTES); if (handle == NULL) { // errno already set and error logged by OpenFileByNameUtf8() return -1; } if (!SetFileTime(handle, &creationTime, NULL, &modificationTime)) { ::syslog(LOG_ERR, "Failed to set times on '%s': %s", pName, GetErrorMessage(GetLastError()).c_str()); CloseHandle(handle); return 1; } CloseHandle(handle); return 0; } // -------------------------------------------------------------------------- // // Function // Name: emu_chmod // Purpose: replacement for the POSIX chmod function, // works with unicode filenames supplied in utf8 format // Created: 26th July 2006 // // -------------------------------------------------------------------------- int emu_chmod(const char * pName, mode_t mode) { std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return -1; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); // We are responsible for freeing pBuffer if (pBuffer == NULL) { // error already logged by ConvertUtf8ToWideString() free(pBuffer); return -1; } DWORD attribs = GetFileAttributesW(pBuffer); if (attribs == INVALID_FILE_ATTRIBUTES) { ::syslog(LOG_ERR, "Failed to get file attributes of '%s': %s", pName, GetErrorMessage(GetLastError()).c_str()); errno = EACCES; free(pBuffer); return -1; } if (mode & S_IWRITE) { attribs &= ~FILE_ATTRIBUTE_READONLY; } else { attribs |= FILE_ATTRIBUTE_READONLY; } if (!SetFileAttributesW(pBuffer, attribs)) { ::syslog(LOG_ERR, "Failed to set file attributes of '%s': %s", pName, GetErrorMessage(GetLastError()).c_str()); errno = EACCES; free(pBuffer); return -1; } delete [] pBuffer; return 0; } // -------------------------------------------------------------------------- // // Function // Name: opendir // Purpose: replacement for unix function, uses win32 findfirst routines // Created: 25th October 2004 // // -------------------------------------------------------------------------- DIR *opendir(const char *name) { if (!name || !name[0]) { errno = EINVAL; return NULL; } std::string dirName(name); //append a '\' win32 findfirst is sensitive to this if (dirName[dirName.size()-1] != '\\' || dirName[dirName.size()-1] != '/') { dirName += '\\'; } // what is the search string? - everything dirName += '*'; DIR *pDir = new DIR; if (pDir == NULL) { errno = ENOMEM; return NULL; } pDir->name = ConvertUtf8ToWideString(dirName.c_str()); // We are responsible for freeing dir->name with delete[] if (pDir->name == NULL) { delete pDir; return NULL; } pDir->fd = FindFirstFileW(pDir->name, &pDir->info); DWORD tmp = GetLastError(); if (pDir->fd == INVALID_HANDLE_VALUE) { delete [] pDir->name; delete pDir; return NULL; } pDir->result.d_name = 0; return pDir; } // this kinda makes it not thread friendly! // but I don't think it needs to be. char tempbuff[MAX_PATH]; // -------------------------------------------------------------------------- // // Function // Name: readdir // Purpose: as function above // Created: 25th October 2004 // // -------------------------------------------------------------------------- struct dirent *readdir(DIR *dp) { try { struct dirent *den = NULL; if (dp && dp->fd != INVALID_HANDLE_VALUE) { // first time around, when dp->result.d_name == NULL, use // the values returned by FindFirstFile. After that, call // FindNextFileW to return new ones. if (!dp->result.d_name || FindNextFileW(dp->fd, &dp->info) != 0) { den = &dp->result; std::wstring input(dp->info.cFileName); memset(tempbuff, 0, sizeof(tempbuff)); WideCharToMultiByte(CP_UTF8, 0, dp->info.cFileName, -1, &tempbuff[0], sizeof (tempbuff), NULL, NULL); //den->d_name = (char *)dp->info.name; den->d_name = &tempbuff[0]; den->d_type = dp->info.dwFileAttributes; } else // FindNextFileW failed { // Why did it fail? No more files? winerrno = GetLastError(); den = NULL; if (winerrno == ERROR_NO_MORE_FILES) { errno = 0; // no more files } else { errno = ENOSYS; } } } else { errno = EBADF; } return den; } catch (...) { printf("Caught readdir"); } return NULL; } // -------------------------------------------------------------------------- // // Function // Name: closedir // Purpose: as function above // Created: 25th October 2004 // // -------------------------------------------------------------------------- int closedir(DIR *dp) { try { BOOL finres = false; if (dp) { if(dp->fd != INVALID_HANDLE_VALUE) { finres = FindClose(dp->fd); } delete [] dp->name; delete dp; } if (finres == FALSE) // errors go to EBADF { winerrno = GetLastError(); errno = EBADF; } return (finres == TRUE) ? 0 : -1; } catch (...) { printf("Caught closedir"); } return -1; } // -------------------------------------------------------------------------- // // Function // Name: poll // Purpose: a weak implimentation (just enough for box) // of the unix poll for winsock2 // Created: 25th October 2004 // // -------------------------------------------------------------------------- int poll (struct pollfd *ufds, unsigned long nfds, int timeout) { try { fd_set readfd; fd_set writefd; FD_ZERO(&readfd); FD_ZERO(&writefd); // struct pollfd *ufdsTmp = ufds; timeval timOut; timeval *tmpptr; if (timeout == INFTIM) tmpptr = NULL; else tmpptr = &timOut; timOut.tv_sec = timeout / 1000; timOut.tv_usec = timeout * 1000; for (unsigned long i = 0; i < nfds; i++) { struct pollfd* ufd = &(ufds[i]); if (ufd->events & POLLIN) { FD_SET(ufd->fd, &readfd); } if (ufd->events & POLLOUT) { FD_SET(ufd->fd, &writefd); } if (ufd->events & ~(POLLIN | POLLOUT)) { printf("Unsupported poll bits %d", ufd->events); return -1; } } int nready = select(0, &readfd, &writefd, 0, tmpptr); if (nready == SOCKET_ERROR) { // int errval = WSAGetLastError(); struct pollfd* pufd = ufds; for (unsigned long i = 0; i < nfds; i++) { pufd->revents = POLLERR; pufd++; } return (-1); } else if (nready > 0) { for (unsigned long i = 0; i < nfds; i++) { struct pollfd *ufd = &(ufds[i]); if (FD_ISSET(ufd->fd, &readfd)) { ufd->revents |= POLLIN; } if (FD_ISSET(ufd->fd, &writefd)) { ufd->revents |= POLLOUT; } } } return nready; } catch (...) { printf("Caught poll"); } return -1; } // copied from MSDN: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/eventlog/base/adding_a_source_to_the_registry.asp BOOL AddEventSource ( LPTSTR pszSrcName, // event source name DWORD dwNum // number of categories ) { // Work out the executable file name, to register ourselves // as the event source WCHAR cmd[MAX_PATH]; DWORD len = GetModuleFileNameW(NULL, cmd, MAX_PATH); if (len == 0) { ::syslog(LOG_ERR, "Failed to get the program file name: %s", GetErrorMessage(GetLastError()).c_str()); return FALSE; } // Create the event source as a subkey of the log. std::string regkey("SYSTEM\\CurrentControlSet\\Services\\EventLog\\" "Application\\"); regkey += pszSrcName; HKEY hk; DWORD dwDisp; if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, regkey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, &dwDisp)) { ::syslog(LOG_ERR, "Failed to create the registry key: %s", GetErrorMessage(GetLastError()).c_str()); return FALSE; } // Set the name of the message file. if (RegSetValueExW(hk, // subkey handle L"EventMessageFile", // value name 0, // must be zero REG_EXPAND_SZ, // value type (LPBYTE)cmd, // pointer to value data len*sizeof(WCHAR))) // data size { ::syslog(LOG_ERR, "Failed to set the event message file: %s", GetErrorMessage(GetLastError()).c_str()); RegCloseKey(hk); return FALSE; } // Set the supported event types. DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (RegSetValueEx(hk, // subkey handle "TypesSupported", // value name 0, // must be zero REG_DWORD, // value type (LPBYTE) &dwData, // pointer to value data sizeof(DWORD))) // length of value data { ::syslog(LOG_ERR, "Failed to set the supported types: %s", GetErrorMessage(GetLastError()).c_str()); RegCloseKey(hk); return FALSE; } // Set the category message file and number of categories. if (RegSetValueExW(hk, // subkey handle L"CategoryMessageFile", // value name 0, // must be zero REG_EXPAND_SZ, // value type (LPBYTE)cmd, // pointer to value data len*sizeof(WCHAR))) // data size { ::syslog(LOG_ERR, "Failed to set the category message file: " "%s", GetErrorMessage(GetLastError()).c_str()); RegCloseKey(hk); return FALSE; } if (RegSetValueEx(hk, // subkey handle "CategoryCount", // value name 0, // must be zero REG_DWORD, // value type (LPBYTE) &dwNum, // pointer to value data sizeof(DWORD))) // length of value data { ::syslog(LOG_ERR, "Failed to set the category count: %s", GetErrorMessage(GetLastError()).c_str()); RegCloseKey(hk); return FALSE; } RegCloseKey(hk); return TRUE; } static HANDLE gSyslogH = 0; static bool sHaveWarnedEventLogFull = false; void openlog(const char * daemonName, int, int) { std::string nameStr = "Box Backup ("; nameStr += daemonName; nameStr += ")"; // register a default event source, so that we can // log errors with the process of adding or registering our own. gSyslogH = RegisterEventSource( NULL, // uses local computer nameStr.c_str()); // source name if (gSyslogH == NULL) { } char* name = strdup(nameStr.c_str()); BOOL success = AddEventSource(name, 0); free(name); if (!success) { ::syslog(LOG_ERR, "Failed to add our own event source"); return; } HANDLE newSyslogH = RegisterEventSource(NULL, nameStr.c_str()); if (newSyslogH == NULL) { ::syslog(LOG_ERR, "Failed to register our own event source: " "%s", GetErrorMessage(GetLastError()).c_str()); return; } DeregisterEventSource(gSyslogH); gSyslogH = newSyslogH; } void closelog(void) { DeregisterEventSource(gSyslogH); } void syslog(int loglevel, const char *frmt, ...) { WORD errinfo; char buffer[4096]; std::string sixfour(frmt); switch (loglevel) { case LOG_INFO: errinfo = EVENTLOG_INFORMATION_TYPE; break; case LOG_ERR: errinfo = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: errinfo = EVENTLOG_WARNING_TYPE; break; default: errinfo = EVENTLOG_WARNING_TYPE; break; } // taken from MSDN int sixfourpos; while ( (sixfourpos = (int)sixfour.find("%ll")) != -1 ) { // maintain portability - change the 64 bit formater... std::string temp = sixfour.substr(0,sixfourpos); temp += "%I64"; temp += sixfour.substr(sixfourpos+3, sixfour.length()); sixfour = temp; } // printf("parsed string is:%s\r\n", sixfour.c_str()); va_list args; va_start(args, frmt); int len = vsnprintf(buffer, sizeof(buffer)-1, sixfour.c_str(), args); assert(len >= 0); if (len < 0) { printf("%s\r\n", buffer); fflush(stdout); return; } assert((size_t)len < sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; va_end(args); if (gSyslogH == 0) { printf("%s\r\n", buffer); fflush(stdout); return; } WCHAR* pWide = ConvertToWideString(buffer, CP_UTF8, false); // must delete[] pWide DWORD result; if (pWide == NULL) { std::string buffer2 = buffer; buffer2 += " (failed to convert string encoding)"; LPCSTR strings[] = { buffer2.c_str(), NULL }; result = ReportEventA(gSyslogH, // event log handle errinfo, // event type 0, // category zero MSG_ERR, // event identifier - // we will call them all the same NULL, // no user security identifier 1, // one substitution string 0, // no data strings, // pointer to string array NULL); // pointer to data } else { LPCWSTR strings[] = { pWide, NULL }; result = ReportEventW(gSyslogH, // event log handle errinfo, // event type 0, // category zero MSG_ERR, // event identifier - // we will call them all the same NULL, // no user security identifier 1, // one substitution string 0, // no data strings, // pointer to string array NULL); // pointer to data delete [] pWide; } if (result == 0) { DWORD err = GetLastError(); if (err == ERROR_LOG_FILE_FULL) { if (!sHaveWarnedEventLogFull) { printf("Unable to send message to Event Log " "(Event Log is full):\r\n"); fflush(stdout); sHaveWarnedEventLogFull = TRUE; } } else { printf("Unable to send message to Event Log: %s:\r\n", GetErrorMessage(err).c_str()); fflush(stdout); } } else { sHaveWarnedEventLogFull = false; } } int emu_chdir(const char* pDirName) { /* std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pDirName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return -1; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); */ WCHAR* pBuffer = ConvertUtf8ToWideString(pDirName); if (!pBuffer) return -1; int result = SetCurrentDirectoryW(pBuffer); delete [] pBuffer; if (result != 0) return 0; errno = EACCES; fprintf(stderr, "Failed to change directory to '%s': %s\n", pDirName, GetErrorMessage(GetLastError()).c_str()); return -1; } char* emu_getcwd(char* pBuffer, int BufSize) { DWORD len = GetCurrentDirectoryW(0, NULL); if (len == 0) { errno = EINVAL; return NULL; } if ((int)len > BufSize) { errno = ENAMETOOLONG; return NULL; } WCHAR* pWide = new WCHAR [len]; if (!pWide) { errno = ENOMEM; return NULL; } DWORD result = GetCurrentDirectoryW(len, pWide); if (result <= 0 || result >= len) { errno = EACCES; delete [] pWide; return NULL; } char* pUtf8 = ConvertFromWideString(pWide, CP_UTF8); delete [] pWide; if (!pUtf8) { return NULL; } strncpy(pBuffer, pUtf8, BufSize - 1); pBuffer[BufSize - 1] = 0; delete [] pUtf8; return pBuffer; } int emu_mkdir(const char* pPathName) { std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pPathName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return -1; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); if (!pBuffer) { return -1; } BOOL result = CreateDirectoryW(pBuffer, NULL); delete [] pBuffer; if (!result) { errno = EACCES; return -1; } return 0; } int emu_unlink(const char* pFileName) { std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName); if (AbsPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return -1; } WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str()); if (!pBuffer) { return -1; } BOOL result = DeleteFileW(pBuffer); DWORD err = GetLastError(); delete [] pBuffer; if (!result) { if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { errno = ENOENT; } else if (err == ERROR_SHARING_VIOLATION) { errno = EBUSY; } else if (err == ERROR_ACCESS_DENIED) { errno = EACCES; } else { ::syslog(LOG_WARNING, "Failed to delete file " "'%s': %s", pFileName, GetErrorMessage(err).c_str()); errno = ENOSYS; } return -1; } return 0; } int emu_rename(const char* pOldFileName, const char* pNewFileName) { std::string OldPathWithUnicode = ConvertPathToAbsoluteUnicode(pOldFileName); if (OldPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() return -1; } WCHAR* pOldBuffer = ConvertUtf8ToWideString(OldPathWithUnicode.c_str()); if (!pOldBuffer) { return -1; } std::string NewPathWithUnicode = ConvertPathToAbsoluteUnicode(pNewFileName); if (NewPathWithUnicode.size() == 0) { // error already logged by ConvertPathToAbsoluteUnicode() delete [] pOldBuffer; return -1; } WCHAR* pNewBuffer = ConvertUtf8ToWideString(NewPathWithUnicode.c_str()); if (!pNewBuffer) { delete [] pOldBuffer; return -1; } BOOL result = MoveFileW(pOldBuffer, pNewBuffer); DWORD err = GetLastError(); delete [] pOldBuffer; delete [] pNewBuffer; if (!result) { if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { errno = ENOENT; } else if (err == ERROR_SHARING_VIOLATION) { errno = EBUSY; } else if (err == ERROR_ACCESS_DENIED) { errno = EACCES; } else { ::syslog(LOG_WARNING, "Failed to rename file " "'%s' to '%s': %s", pOldFileName, pNewFileName, GetErrorMessage(err).c_str()); errno = ENOSYS; } return -1; } return 0; } int console_read(char* pBuffer, size_t BufferSize) { HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { ::fprintf(stderr, "Failed to get a handle on standard input: " "%s", GetErrorMessage(GetLastError()).c_str()); return -1; } size_t WideSize = BufferSize / 5; WCHAR* pWideBuffer = new WCHAR [WideSize + 1]; if (!pWideBuffer) { ::perror("Failed to allocate wide character buffer"); return -1; } DWORD numCharsRead = 0; if (!ReadConsoleW( hConsole, pWideBuffer, WideSize, // will not be null terminated by ReadConsole &numCharsRead, NULL // reserved )) { ::fprintf(stderr, "Failed to read from console: %s\n", GetErrorMessage(GetLastError()).c_str()); return -1; } pWideBuffer[numCharsRead] = 0; char* pUtf8 = ConvertFromWideString(pWideBuffer, GetConsoleCP()); delete [] pWideBuffer; strncpy(pBuffer, pUtf8, BufferSize); delete [] pUtf8; return strlen(pBuffer); } int readv (int filedes, const struct iovec *vector, size_t count) { int bytes = 0; for (size_t i = 0; i < count; i++) { int result = read(filedes, vector[i].iov_base, vector[i].iov_len); if (result < 0) { return result; } bytes += result; } return bytes; } int writev(int filedes, const struct iovec *vector, size_t count) { int bytes = 0; for (size_t i = 0; i < count; i++) { int result = write(filedes, vector[i].iov_base, vector[i].iov_len); if (result < 0) { return result; } bytes += result; } return bytes; } // need this for conversions time_t ConvertFileTimeToTime_t(FILETIME *fileTime) { SYSTEMTIME stUTC; struct tm timeinfo; // Convert the last-write time to local time. FileTimeToSystemTime(fileTime, &stUTC); memset(&timeinfo, 0, sizeof(timeinfo)); timeinfo.tm_sec = stUTC.wSecond; timeinfo.tm_min = stUTC.wMinute; timeinfo.tm_hour = stUTC.wHour; timeinfo.tm_mday = stUTC.wDay; timeinfo.tm_wday = stUTC.wDayOfWeek; timeinfo.tm_mon = stUTC.wMonth - 1; // timeinfo.tm_yday = ...; timeinfo.tm_year = stUTC.wYear - 1900; time_t retVal = mktime(&timeinfo) - _timezone; return retVal; } bool ConvertTime_tToFileTime(const time_t from, FILETIME *pTo) { time_t adjusted = from + _timezone; struct tm *time_breakdown = gmtime(&adjusted); if (time_breakdown == NULL) { ::syslog(LOG_ERR, "Error: failed to convert time format: " "%d is not a valid time\n", adjusted); return false; } SYSTEMTIME stUTC; stUTC.wSecond = time_breakdown->tm_sec; stUTC.wMinute = time_breakdown->tm_min; stUTC.wHour = time_breakdown->tm_hour; stUTC.wDay = time_breakdown->tm_mday; stUTC.wDayOfWeek = time_breakdown->tm_wday; stUTC.wMonth = time_breakdown->tm_mon + 1; stUTC.wYear = time_breakdown->tm_year + 1900; stUTC.wMilliseconds = 0; // Convert the last-write time to local time. if (!SystemTimeToFileTime(&stUTC, pTo)) { syslog(LOG_ERR, "Failed to convert between time formats: %s", GetErrorMessage(GetLastError()).c_str()); return false; } return true; } #endif // WIN32