diff options
Diffstat (limited to 'lib/win32/emu.cpp')
-rw-r--r-- | lib/win32/emu.cpp | 1843 |
1 files changed, 1843 insertions, 0 deletions
diff --git a/lib/win32/emu.cpp b/lib/win32/emu.cpp new file mode 100644 index 00000000..db9974d2 --- /dev/null +++ b/lib/win32/emu.cpp @@ -0,0 +1,1843 @@ +// Box Backup Win32 native port by Nick Knight + +// Need at least 0x0500 to use GetFileSizeEx on Cygwin/MinGW +#define WINVER 0x0500 + +#include "emu.h" + +#ifdef WIN32 + +#include <assert.h> +#include <fcntl.h> +#include <process.h> +#include <windows.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include <string> +#include <list> +#include <sstream> + +// 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; +} + +// forward declaration +char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage); + +// -------------------------------------------------------------------------- +// +// 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: " + "error %d", GetLastError()); + 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: " + "error %i", GetLastError()); + errno = EACCES; + delete [] buffer; + return NULL; + } + + return buffer; +} + +// -------------------------------------------------------------------------- +// +// 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() > 2 && filename[0] == '\\' && + filename[1] == '\\') + { + tmpStr += "UNC\\"; + filename.replace(0, 2, ""); + // \\?\UNC\<server>\<share> + // see http://msdn2.microsoft.com/en-us/library/aa365247.aspx + } + else if (filename.length() >= 1 && filename[0] == '\\') + { + // root directory of current drive. + tmpStr = wd; + tmpStr.resize(2); // drive letter and colon + } + else if (filename.length() >= 2 && filename[1] != ':') + { + // Must be relative. We need to get the + // current directory to make it absolute. + tmpStr += wd; + if (tmpStr[tmpStr.length()] != '\\') + { + 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; + } + } + + 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 = (_off_t)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()] != '\\' || dirName[dirName.size()] != '/' ) + { + 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 = _wfindfirst((const wchar_t*)pDir->name, &(pDir->info)); + + if (pDir->fd == -1) + { + 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 != -1) + { + if (!dp->result.d_name || + _wfindnext(dp->fd, &dp->info) != -1) + { + den = &dp->result; + std::wstring input(dp->info.name); + memset(tempbuff, 0, sizeof(tempbuff)); + WideCharToMultiByte(CP_UTF8, 0, dp->info.name, + -1, &tempbuff[0], sizeof (tempbuff), + NULL, NULL); + //den->d_name = (char *)dp->info.name; + den->d_name = &tempbuff[0]; + if (dp->info.attrib & FILE_ATTRIBUTE_DIRECTORY) + { + den->d_type = S_IFDIR; + } + else + { + den->d_type = S_IFREG; + } + } + } + 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 + { + int finres = -1; + if (dp) + { + if(dp->fd != -1) + { + finres = _findclose(dp->fd); + } + + delete [] dp->name; + delete dp; + } + + if (finres == -1) // errors go to EBADF + { + errno = EBADF; + } + + return finres; + } + 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 + |