summaryrefslogtreecommitdiff
path: root/lib/win32/emu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/win32/emu.cpp')
-rw-r--r--lib/win32/emu.cpp1035
1 files changed, 848 insertions, 187 deletions
diff --git a/lib/win32/emu.cpp b/lib/win32/emu.cpp
index c3c0b6f2..e41430c6 100644
--- a/lib/win32/emu.cpp
+++ b/lib/win32/emu.cpp
@@ -1,4 +1,4 @@
-// distribution boxbackup-0.10 (svn version: 494)
+// distribution boxbackup-0.11rc1 (svn version: 2023_2024)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
@@ -41,29 +41,31 @@
// Need at least 0x0500 to use GetFileSizeEx on Cygwin/MinGW
#define WINVER 0x0500
-#include "Box.h"
+#include "emu.h"
#ifdef WIN32
-// #include "emu.h"
-
-#include <windows.h>
+#include <assert.h>
#include <fcntl.h>
-// #include <atlenc.h>
+#include <process.h>
+#include <windows.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef HAVE_PROCESS_H
- #include <process.h>
-#endif
#include <string>
#include <list>
+#include <sstream>
+
+// message resource definitions for syslog()
+
+#include "messages.h"
// our implementation for a timer, based on a
// simple thread which sleeps for a period of time
+static bool gTimerInitialised = false;
static bool gFinishTimer;
static CRITICAL_SECTION gLock;
@@ -79,24 +81,31 @@ static void (__cdecl *gTimerFunc) (int) = NULL;
int setitimer(int type, struct itimerval *timeout, void *arg)
{
- if (ITIMER_VIRTUAL == type)
+ assert(gTimerInitialised);
+
+ if (ITIMER_REAL != type)
{
- EnterCriticalSection(&gLock);
- // we only need seconds for the mo!
- if (timeout->it_value.tv_sec == 0 &&
- timeout->it_value.tv_usec == 0)
- {
- gTimerList.clear();
- }
- else
- {
- Timer_t ourTimer;
- ourTimer.countDown = timeout->it_value.tv_sec;
- ourTimer.interval = timeout->it_interval.tv_sec;
- gTimerList.push_back(ourTimer);
- }
- LeaveCriticalSection(&gLock);
+ errno = ENOSYS;
+ return -1;
}
+
+ EnterCriticalSection(&gLock);
+
+ // we only need seconds for the mo!
+ if (timeout->it_value.tv_sec == 0 &&
+ timeout->it_value.tv_usec == 0)
+ {
+ gTimerList.clear();
+ }
+ else
+ {
+ Timer_t ourTimer;
+ ourTimer.countDown = timeout->it_value.tv_sec;
+ ourTimer.interval = timeout->it_interval.tv_sec;
+ gTimerList.push_back(ourTimer);
+ }
+
+ LeaveCriticalSection(&gLock);
// indicate success
return 0;
@@ -167,20 +176,26 @@ int SetTimerHandler(void (__cdecl *func ) (int))
void InitTimer(void)
{
- InitializeCriticalSection(&gLock);
+ assert(!gTimerInitialised);
+ InitializeCriticalSection(&gLock);
+
// create our thread
HANDLE ourThread = (HANDLE)_beginthreadex(NULL, 0, RunTimer, 0,
CREATE_SUSPENDED, NULL);
SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST);
ResumeThread(ourThread);
+
+ gTimerInitialised = true;
}
void FiniTimer(void)
{
+ assert(gTimerInitialised);
gFinishTimer = true;
EnterCriticalSection(&gLock);
DeleteCriticalSection(&gLock);
+ gTimerInitialised = false;
}
//Our constants we need to keep track of
@@ -241,6 +256,44 @@ bool EnableBackupRights( void )
}
}
+// 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
@@ -398,27 +451,34 @@ char* ConvertFromWideString(const WCHAR* pString, unsigned int codepage)
// --------------------------------------------------------------------------
//
// Function
-// Name: ConvertUtf8ToConsole
-// Purpose: Converts a string from UTF-8 to the console
-// code page. On success, replaces contents of rDest
-// and returns true. In case of fire, logs the error
-// and returns false.
-// Created: 4th February 2006
+// 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 ConvertUtf8ToConsole(const char* pString, std::string& rDest)
+bool ConvertEncoding(const std::string& rSource, int sourceCodePage,
+ std::string& rDest, int destCodePage)
{
- WCHAR* pWide = ConvertToWideString(pString, CP_UTF8);
+ WCHAR* pWide = ConvertToWideString(rSource.c_str(), sourceCodePage);
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, GetConsoleOutputCP());
+ char* pConsole = ConvertFromWideString(pWide, destCodePage);
delete [] pWide;
if (!pConsole)
{
+ // Error should have been logged by ConvertFromWideString
return false;
}
@@ -428,39 +488,25 @@ bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
return true;
}
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: ConvertConsoleToUtf8
-// Purpose: Converts a string from the console code page
-// to UTF-8. On success, replaces contents of rDest
-// and returns true. In case of fire, logs the error
-// and returns false.
-// Created: 4th February 2006
-//
-// --------------------------------------------------------------------------
-bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
+bool ConvertToUtf8(const char* pString, std::string& rDest, int sourceCodePage)
{
- WCHAR* pWide = ConvertToWideString(pString, GetConsoleCP());
- if (pWide == NULL)
- {
- return false;
- }
-
- char* pConsole = ConvertFromWideString(pWide, CP_UTF8);
- delete [] pWide;
-
- if (!pConsole)
- {
- return false;
- }
+ return ConvertEncoding(pString, sourceCodePage, rDest, CP_UTF8);
+}
- rDest = pConsole;
- delete [] pConsole;
+bool ConvertFromUtf8(const char* pString, std::string& rDest, int destCodePage)
+{
+ return ConvertEncoding(pString, CP_UTF8, rDest, destCodePage);
+}
- return true;
+bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
+{
+ return ConvertEncoding(pString, GetConsoleCP(), rDest, CP_UTF8);
}
+bool ConvertUtf8ToConsole(const char* pString, std::string& rDest)
+{
+ return ConvertEncoding(pString, CP_UTF8, rDest, GetConsoleOutputCP());
+}
// --------------------------------------------------------------------------
//
@@ -472,28 +518,54 @@ bool ConvertConsoleToUtf8(const char* pString, std::string& rDest)
// --------------------------------------------------------------------------
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 ':'
-
- if (pFileName[1] != ':')
+
+ char wd[PATH_MAX];
+ if (::getcwd(wd, PATH_MAX) == 0)
+ {
+ ::syslog(LOG_WARNING,
+ "Failed to open '%s': path too long",
+ pFileName);
+ errno = ENAMETOOLONG;
+ 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.
-
- char wd[PATH_MAX];
- if (::getcwd(wd, PATH_MAX) == 0)
- {
- ::syslog(LOG_WARNING,
- "Failed to open '%s': path too long",
- pFileName);
- errno = ENAMETOOLONG;
- tmpStr = "";
- return tmpStr;
- }
-
tmpStr += wd;
if (tmpStr[tmpStr.length()] != '\\')
{
@@ -501,26 +573,59 @@ std::string ConvertPathToAbsoluteUnicode(const char *pFileName)
}
}
- tmpStr += pFileName;
+ tmpStr += filename;
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
+// 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)
{
- std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName);
+ std::string AbsPathWithUnicode =
+ ConvertPathToAbsoluteUnicode(pFileName);
if (AbsPathWithUnicode.size() == 0)
{
// error already logged by ConvertPathToAbsoluteUnicode()
- return NULL;
+ return INVALID_HANDLE_VALUE;
}
WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str());
@@ -529,68 +634,73 @@ HANDLE openfile(const char *pFileName, int flags, int mode)
if (pBuffer == NULL)
{
// error already logged by ConvertUtf8ToWideString()
- return NULL;
+ return INVALID_HANDLE_VALUE;
}
// flags could be O_WRONLY | O_CREAT | O_RDONLY
DWORD createDisposition = OPEN_EXISTING;
- DWORD shareMode = FILE_SHARE_READ;
- DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA;
+ 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)
{
- shareMode = FILE_SHARE_WRITE;
+ accessRights = FILE_WRITE_DATA;
}
- if (flags & O_RDWR)
+ else if (flags & O_RDWR)
{
- shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ accessRights |= FILE_WRITE_ATTRIBUTES
+ | FILE_WRITE_DATA | FILE_WRITE_EA;
}
+
if (flags & O_CREAT)
{
createDisposition = OPEN_ALWAYS;
- shareMode |= FILE_SHARE_WRITE;
- accessRights |= FILE_WRITE_ATTRIBUTES
- | FILE_WRITE_DATA | FILE_WRITE_EA
- | FILE_ALL_ACCESS;
}
+
if (flags & O_TRUNC)
{
createDisposition = CREATE_ALWAYS;
}
- if (flags & O_EXCL)
+
+ 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,
- FILE_FLAG_BACKUP_SEMANTICS,
+ winFlags,
NULL);
delete [] pBuffer;
if (hdir == INVALID_HANDLE_VALUE)
{
- ::syslog(LOG_WARNING, "Failed to open file %s: "
- "error %i", pFileName, GetLastError());
- return NULL;
+ ::syslog(LOG_WARNING, "Failed to open file '%s': "
+ "%s", pFileName,
+ GetErrorMessage(GetLastError()).c_str());
+ return INVALID_HANDLE_VALUE;
}
return hdir;
}
-// MinGW provides a getopt implementation
-#ifndef __MINGW32__
-//works with getopt
-char *optarg;
-//optind looks like an index into the string - how far we have moved along
-int optind = 1;
-char nextchar = -1;
-#endif
-
// --------------------------------------------------------------------------
//
// Function
@@ -601,8 +711,6 @@ char nextchar = -1;
// --------------------------------------------------------------------------
int emu_fstat(HANDLE hdir, struct stat * st)
{
- ULARGE_INTEGER conv;
-
if (hdir == INVALID_HANDLE_VALUE)
{
::syslog(LOG_ERR, "Error: invalid file handle in emu_fstat()");
@@ -614,14 +722,23 @@ int emu_fstat(HANDLE hdir, struct stat * st)
if (!GetFileInformationByHandle(hdir, &fi))
{
::syslog(LOG_WARNING, "Failed to read file information: "
- "error %d", GetLastError());
+ "%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 next example is how we get our INODE (equivalent) information
+ // This is how we get our INODE (equivalent) information
+ ULARGE_INTEGER conv;
conv.HighPart = fi.nFileIndexHigh;
conv.LowPart = fi.nFileIndexLow;
st->st_ino = (_ino_t)conv.QuadPart;
@@ -631,41 +748,71 @@ int emu_fstat(HANDLE hdir, struct stat * st)
st->st_atime = ConvertFileTimeToTime_t(&fi.ftLastAccessTime);
st->st_mtime = ConvertFileTimeToTime_t(&fi.ftLastWriteTime);
- // size of the file
- LARGE_INTEGER st_size;
- if (!GetFileSizeEx(hdir, &st_size))
+ if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
- ::syslog(LOG_WARNING, "Failed to get file size: error %d",
- GetLastError());
- errno = EACCES;
- return -1;
+ st->st_size = 0;
+ }
+ else
+ {
+ // size of the file
+ LARGE_INTEGER st_size;
+ memset(&st_size, 0, sizeof(st_size));
+
+ if (!GetFileSizeEx(hdir, &st_size))
+ {
+ ::syslog(LOG_WARNING, "Failed to get file size: "
+ "%s", GetErrorMessage(GetLastError()).c_str());
+ errno = EACCES;
+ return -1;
+ }
+
+ conv.HighPart = st_size.HighPart;
+ conv.LowPart = st_size.LowPart;
+ st->st_size = (_off_t)conv.QuadPart;
}
- conv.HighPart = st_size.HighPart;
- conv.LowPart = st_size.LowPart;
- 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
- st->st_mode = 0;
- //DWORD res = GetFileAttributes((LPCSTR)tmpStr.c_str());
+ // 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 (INVALID_FILE_ATTRIBUTES != fi.dwFileAttributes)
+ if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
- if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- {
- st->st_mode |= S_IFDIR;
- }
- else
- {
- st->st_mode |= S_IFREG;
- }
+ st->st_mode |= S_IFDIR | S_IEXEC;
}
else
{
- ::syslog(LOG_WARNING, "Failed to get file attributes: "
- "error %d", GetLastError());
- errno = EACCES;
- return -1;
+ 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;
@@ -681,9 +828,10 @@ int emu_fstat(HANDLE hdir, struct stat * st)
// Created: 10th December 2004
//
// --------------------------------------------------------------------------
-HANDLE OpenFileByNameUtf8(const char* pFileName)
+HANDLE OpenFileByNameUtf8(const char* pFileName, DWORD flags)
{
- std::string AbsPathWithUnicode = ConvertPathToAbsoluteUnicode(pFileName);
+ std::string AbsPathWithUnicode =
+ ConvertPathToAbsoluteUnicode(pFileName);
if (AbsPathWithUnicode.size() == 0)
{
@@ -701,7 +849,7 @@ HANDLE OpenFileByNameUtf8(const char* pFileName)
}
HANDLE handle = CreateFileW(pBuffer,
- FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA,
+ flags,
FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
@@ -715,7 +863,7 @@ HANDLE OpenFileByNameUtf8(const char* pFileName)
// at least one process must have the file open -
// in this case someone else does.
handle = CreateFileW(pBuffer,
- 0,
+ READ_CONTROL,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
@@ -729,14 +877,16 @@ HANDLE OpenFileByNameUtf8(const char* pFileName)
{
DWORD err = GetLastError();
- if (err == ERROR_FILE_NOT_FOUND)
+ if (err == ERROR_FILE_NOT_FOUND ||
+ err == ERROR_PATH_NOT_FOUND)
{
errno = ENOENT;
}
else
{
- ::syslog(LOG_WARNING,
- "Failed to open '%s': error %d", pFileName, err);
+ ::syslog(LOG_WARNING, "Failed to open '%s': "
+ "%s", pFileName,
+ GetErrorMessage(err).c_str());
errno = EACCES;
}
@@ -757,12 +907,8 @@ HANDLE OpenFileByNameUtf8(const char* pFileName)
// --------------------------------------------------------------------------
int emu_stat(const char * pName, struct stat * st)
{
- // at the mo
- st->st_uid = 0;
- st->st_gid = 0;
- st->st_nlink = 1;
-
- HANDLE handle = OpenFileByNameUtf8(pName);
+ HANDLE handle = OpenFileByNameUtf8(pName,
+ FILE_READ_ATTRIBUTES | FILE_READ_EA);
if (handle == NULL)
{
@@ -795,7 +941,8 @@ int emu_stat(const char * pName, struct stat * st)
// --------------------------------------------------------------------------
int statfs(const char * pName, struct statfs * s)
{
- HANDLE handle = OpenFileByNameUtf8(pName);
+ HANDLE handle = OpenFileByNameUtf8(pName,
+ FILE_READ_ATTRIBUTES | FILE_READ_EA);
if (handle == NULL)
{
@@ -807,7 +954,8 @@ int statfs(const char * pName, struct statfs * s)
if (!GetFileInformationByHandle(handle, &fi))
{
::syslog(LOG_WARNING, "Failed to get file information "
- "for '%s': error %d", pName, GetLastError());
+ "for '%s': %s", pName,
+ GetErrorMessage(GetLastError()).c_str());
CloseHandle(handle);
errno = EACCES;
return -1;
@@ -817,7 +965,7 @@ int statfs(const char * pName, struct statfs * s)
_ui64toa(fi.dwVolumeSerialNumber, s->f_mntonname + 1, 16);
// pseudo unix mount point
- s->f_mntonname[0] = DIRECTORY_SEPARATOR_ASCHAR;
+ s->f_mntonname[0] = '\\';
CloseHandle(handle); // close the handle
@@ -827,6 +975,115 @@ int statfs(const char * pName, struct statfs * s)
// --------------------------------------------------------------------------
//
// 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
@@ -903,7 +1160,7 @@ struct dirent *readdir(DIR *dp)
if (!dp->result.d_name ||
_wfindnext(dp->fd, &dp->info) != -1)
{
- den = &dp->result;
+ den = &dp->result;
std::wstring input(dp->info.name);
memset(tempbuff, 0, sizeof(tempbuff));
WideCharToMultiByte(CP_UTF8, 0, dp->info.name,
@@ -911,6 +1168,14 @@ struct dirent *readdir(DIR *dp)
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
@@ -980,10 +1245,10 @@ int poll (struct pollfd *ufds, unsigned long nfds, int timeout)
fd_set readfd;
fd_set writefd;
- readfd.fd_count = 0;
- writefd.fd_count = 0;
+ FD_ZERO(&readfd);
+ FD_ZERO(&writefd);
- struct pollfd *ufdsTmp = ufds;
+ // struct pollfd *ufdsTmp = ufds;
timeval timOut;
timeval *tmpptr;
@@ -996,41 +1261,61 @@ int poll (struct pollfd *ufds, unsigned long nfds, int timeout)
timOut.tv_sec = timeout / 1000;
timOut.tv_usec = timeout * 1000;
- if (ufds->events & POLLIN)
+ for (unsigned long i = 0; i < nfds; i++)
{
- for (unsigned long i = 0; i < nfds; i++)
+ struct pollfd* ufd = &(ufds[i]);
+
+ if (ufd->events & POLLIN)
{
- readfd.fd_array[i] = ufdsTmp->fd;
- readfd.fd_count++;
+ FD_SET(ufd->fd, &readfd);
}
- }
- if (ufds->events & POLLOUT)
- {
- for (unsigned long i = 0; i < nfds; i++)
+ if (ufd->events & POLLOUT)
{
+ FD_SET(ufd->fd, &writefd);
+ }
- writefd.fd_array[i]=ufdsTmp->fd;
- writefd.fd_count++;
+ if (ufd->events & ~(POLLIN | POLLOUT))
+ {
+ printf("Unsupported poll bits %d",
+ ufd->events);
+ return -1;
}
}
- int noffds = select(0, &readfd, &writefd, 0, tmpptr);
+ int nready = select(0, &readfd, &writefd, 0, tmpptr);
- if (noffds == SOCKET_ERROR)
+ if (nready == SOCKET_ERROR)
{
// int errval = WSAGetLastError();
- ufdsTmp = ufds;
+ struct pollfd* pufd = ufds;
for (unsigned long i = 0; i < nfds; i++)
{
- ufdsTmp->revents = POLLERR;
- ufdsTmp++;
+ pufd->revents = POLLERR;
+ pufd++;
}
return (-1);
}
+ else if (nready > 0)
+ {
+ for (unsigned long i = 0; i < nfds; i++)
+ {
+ struct pollfd *ufd = &(ufds[i]);
- return noffds;
+ if (FD_ISSET(ufd->fd, &readfd))
+ {
+ ufd->revents |= POLLIN;
+ }
+
+ if (FD_ISSET(ufd->fd, &writefd))
+ {
+ ufd->revents |= POLLOUT;
+ }
+ }
+ }
+
+ return nready;
}
catch (...)
{
@@ -1040,9 +1325,151 @@ int poll (struct pollfd *ufds, unsigned long nfds, int timeout)
return -1;
}
-HANDLE gSyslogH = 0;
+// 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)
+{
+ // 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
+ daemonName); // source name
+ if (gSyslogH == NULL)
+ {
+ }
+
+ char* name = strdup(daemonName);
+ BOOL success = AddEventSource(name, 0);
+ free(name);
+
+ if (!success)
+ {
+ ::syslog(LOG_ERR, "Failed to add our own event source");
+ return;
+ }
+
+ HANDLE newSyslogH = RegisterEventSource(NULL, daemonName);
+ 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;
@@ -1082,17 +1509,32 @@ void syslog(int loglevel, const char *frmt, ...)
va_start(args, frmt);
int len = vsnprintf(buffer, sizeof(buffer)-1, sixfour.c_str(), args);
- ASSERT(len < sizeof(buffer))
+ 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);
LPCSTR strings[] = { buffer, NULL };
+ if (gSyslogH == 0)
+ {
+ printf("%s\r\n", buffer);
+ fflush(stdout);
+ return;
+ }
+
if (!ReportEvent(gSyslogH, // event log handle
errinfo, // event type
0, // category zero
- MSG_ERR_EXIST, // event identifier -
+ MSG_ERR, // event identifier -
// we will call them all the same
NULL, // no user security identifier
1, // one substitution string
@@ -1108,13 +1550,15 @@ void syslog(int loglevel, const char *frmt, ...)
{
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: "
- "error %i:\r\n", (int)err);
+ printf("Unable to send message to Event Log: %s:\r\n",
+ GetErrorMessage(err).c_str());
+ fflush(stdout);
}
}
else
@@ -1122,17 +1566,36 @@ void syslog(int loglevel, const char *frmt, ...)
sHaveWarnedEventLogFull = false;
}
- printf("%s\r\n", buffer);
+ // printf("%s\r\n", buffer);
+ // fflush(stdout);
}
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;
}
@@ -1145,7 +1608,7 @@ char* emu_getcwd(char* pBuffer, int BufSize)
return NULL;
}
- if (len > BufSize)
+ if ((int)len > BufSize)
{
errno = ENAMETOOLONG;
return NULL;
@@ -1162,6 +1625,7 @@ char* emu_getcwd(char* pBuffer, int BufSize)
if (result <= 0 || result >= len)
{
errno = EACCES;
+ delete [] pWide;
return NULL;
}
@@ -1182,7 +1646,16 @@ char* emu_getcwd(char* pBuffer, int BufSize)
int emu_mkdir(const char* pPathName)
{
- WCHAR* pBuffer = ConvertToWideString(pPathName, CP_UTF8);
+ 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;
@@ -1202,18 +1675,112 @@ int emu_mkdir(const char* pPathName)
int emu_unlink(const char* pFileName)
{
- WCHAR* pBuffer = ConvertToWideString(pFileName, CP_UTF8);
+ 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)
{
- errno = EACCES;
+ 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;
}
@@ -1227,12 +1794,12 @@ int console_read(char* pBuffer, size_t BufferSize)
if (hConsole == INVALID_HANDLE_VALUE)
{
::fprintf(stderr, "Failed to get a handle on standard input: "
- "error %d\n", GetLastError());
+ "%s", GetErrorMessage(GetLastError()).c_str());
return -1;
}
- int WideSize = BufferSize / 5;
- WCHAR* pWideBuffer = new WCHAR [WideSize];
+ size_t WideSize = BufferSize / 5;
+ WCHAR* pWideBuffer = new WCHAR [WideSize + 1];
if (!pWideBuffer)
{
@@ -1245,23 +1812,117 @@ int console_read(char* pBuffer, size_t BufferSize)
if (!ReadConsoleW(
hConsole,
pWideBuffer,
- WideSize - 1,
+ WideSize, // will not be null terminated by ReadConsole
&numCharsRead,
NULL // reserved
))
{
- ::fprintf(stderr, "Failed to read from console: error %d\n",
- GetLastError());
+ ::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", from);
+ 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
+