mirror of
https://github.com/etlegacy/Update-Installer.git
synced 2024-12-03 09:01:52 +00:00
ef6809e0fe
* On Windows FileOps::removeFile() fails for updater.exe since that file is in use by the current process. Whilst it is not possible to remove the file whilst it is in use, it can be moved or scheduled for deletion on reboot. This commit changes FilesOps::removeFile() to simulate the behavior of unlink() on Linux by moving in-use files to a temporary directory and then scheduling for them to be removed on restart.
381 lines
8.6 KiB
C++
381 lines
8.6 KiB
C++
#include "FileOps.h"
|
|
|
|
#include "DirIterator.h"
|
|
#include "Log.h"
|
|
#include "Platform.h"
|
|
#include "StringUtils.h"
|
|
|
|
#include <string.h>
|
|
#include <fstream>
|
|
|
|
#include "minizip/unzip.h"
|
|
|
|
#ifdef PLATFORM_UNIX
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#endif
|
|
|
|
FileOps::IOException::IOException(const std::string& error)
|
|
: m_errno(0)
|
|
{
|
|
m_error = error;
|
|
|
|
#ifdef PLATFORM_UNIX
|
|
m_errno = errno;
|
|
|
|
if (m_errno > 0)
|
|
{
|
|
m_error += " details: " + std::string(strerror(m_errno));
|
|
}
|
|
#endif
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
m_error += " GetLastError returned: " + intToStr(GetLastError());
|
|
#endif
|
|
}
|
|
|
|
FileOps::IOException::~IOException() throw ()
|
|
{
|
|
}
|
|
|
|
bool FileOps::fileExists(const char* path) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
struct stat fileInfo;
|
|
if (stat(path,&fileInfo) != 0)
|
|
{
|
|
if (errno == ENOENT)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
throw IOException("Error checking for file " + std::string(path));
|
|
}
|
|
}
|
|
return true;
|
|
#else
|
|
DWORD result = GetFileAttributes(path);
|
|
if (result == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void FileOps::setQtPermissions(const char* path, int qtPermissions) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
int mode = toUnixPermissions(qtPermissions);
|
|
if (chmod(path,mode) != 0)
|
|
{
|
|
throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(qtPermissions));
|
|
}
|
|
#else
|
|
// TODO - Not implemented under Windows - all files
|
|
// get default permissions
|
|
#endif
|
|
}
|
|
|
|
void FileOps::moveFile(const char* src, const char* dest) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
if (rename(src,dest) != 0)
|
|
{
|
|
throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
|
|
}
|
|
#else
|
|
if (!MoveFile(src,dest))
|
|
{
|
|
throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FileOps::extractFromZip(const char* zipFilePath, const char* src, const char* dest) throw (IOException)
|
|
{
|
|
unzFile zipFile = unzOpen(zipFilePath);
|
|
int result = unzLocateFile(zipFile,src,0);
|
|
if (result == UNZ_OK)
|
|
{
|
|
// found a match which is now the current file
|
|
unzOpenCurrentFile(zipFile);
|
|
const int chunkSize = 4096;
|
|
char buffer[chunkSize];
|
|
|
|
std::ofstream outputFile(dest,std::ofstream::binary);
|
|
if (!outputFile.good())
|
|
{
|
|
throw IOException("Unable to write to file " + std::string(dest));
|
|
}
|
|
while (true)
|
|
{
|
|
int count = unzReadCurrentFile(zipFile,buffer,chunkSize);
|
|
if (count <= 0)
|
|
{
|
|
if (count < 0)
|
|
{
|
|
throw IOException("Error extracting file from archive " + std::string(src));
|
|
}
|
|
break;
|
|
}
|
|
outputFile.write(buffer,count);
|
|
}
|
|
outputFile.close();
|
|
|
|
unzCloseCurrentFile(zipFile);
|
|
}
|
|
else
|
|
{
|
|
throw IOException("Unable to find file " + std::string(src) + " in zip archive " + std::string(zipFilePath));
|
|
}
|
|
unzClose(zipFile);
|
|
}
|
|
|
|
void FileOps::mkdir(const char* dir) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
|
|
{
|
|
throw IOException("Unable to create directory " + std::string(dir));
|
|
}
|
|
#else
|
|
if (!CreateDirectory(dir,0 /* default security attributes */))
|
|
{
|
|
throw IOException("Unable to create directory " + std::string(dir));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FileOps::rmdir(const char* dir) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
if (::rmdir(dir) != 0)
|
|
{
|
|
throw IOException("Unable to remove directory " + std::string(dir));
|
|
}
|
|
#else
|
|
if (!RemoveDirectory(dir))
|
|
{
|
|
throw IOException("Unable to remove directory " + std::string(dir));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FileOps::createSymLink(const char* link, const char* target) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
if (symlink(target,link) != 0)
|
|
{
|
|
throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target));
|
|
}
|
|
#else
|
|
// symlinks are not supported under Windows (at least, not universally.
|
|
// Windows Vista and later do actually support symlinks)
|
|
LOG(Warn,"Skipping symlink creation - not implemented in Windows");
|
|
#endif
|
|
}
|
|
|
|
void FileOps::removeFile(const char* src) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
if (unlink(src) != 0)
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
throw IOException("Unable to remove file " + std::string(src));
|
|
}
|
|
}
|
|
#else
|
|
if (!DeleteFile(src))
|
|
{
|
|
if (GetLastError() == ERROR_ACCESS_DENIED)
|
|
{
|
|
// if another process is using the file, try moving it to
|
|
// a temporary directory and then
|
|
// scheduling it for deletion on reboot
|
|
std::string tempDeletePathBase = tempPath();
|
|
tempDeletePathBase += '/';
|
|
tempDeletePathBase += fileName(src);
|
|
|
|
int suffix = 0;
|
|
std::string tempDeletePath = tempDeletePathBase;
|
|
while (fileExists(tempDeletePath.c_str()))
|
|
{
|
|
++suffix;
|
|
tempDeletePath = tempDeletePathBase + '_' + intToStr(suffix);
|
|
}
|
|
|
|
LOG(Warn,"Unable to remove file " + std::string(src) + " - it may be in use. Moving to "
|
|
+ tempDeletePath + " and scheduling delete on reboot.");
|
|
moveFile(src,tempDeletePath.c_str());
|
|
MoveFileEx(tempDeletePath.c_str(),0,MOVEFILE_DELAY_UNTIL_REBOOT);
|
|
}
|
|
else if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
throw IOException("Unable to remove file " + std::string(src));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::string FileOps::fileName(const char* path)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
char* pathCopy = strdup(path);
|
|
std::string basename = ::basename(pathCopy);
|
|
free(pathCopy);
|
|
return basename;
|
|
#else
|
|
char baseName[MAX_PATH];
|
|
char extension[MAX_PATH];
|
|
_splitpath(path, 0 /* drive */, 0 /* dir */, baseName, extension);
|
|
return std::string(baseName) + std::string(extension);
|
|
#endif
|
|
}
|
|
|
|
std::string FileOps::dirname(const char* path)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
char* pathCopy = strdup(path);
|
|
std::string dirname = ::dirname(pathCopy);
|
|
free(pathCopy);
|
|
return dirname;
|
|
#else
|
|
char dir[MAX_PATH];
|
|
_splitpath(path, 0 /* drive */, dir, 0 /* filename */, 0/* extension */);
|
|
return std::string(dir);
|
|
#endif
|
|
}
|
|
|
|
void FileOps::touch(const char* path) throw (IOException)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
// see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
|
|
int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
if (fd != -1)
|
|
{
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
throw IOException("Unable to touch file " + std::string(path));
|
|
}
|
|
#else
|
|
HANDLE result = CreateFile(path,GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
0,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0);
|
|
if (result == INVALID_HANDLE_VALUE)
|
|
{
|
|
throw IOException("Unable to touch file " + std::string(path));
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(result);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FileOps::rmdirRecursive(const char* path) throw (IOException)
|
|
{
|
|
// remove dir contents
|
|
DirIterator dir(path);
|
|
while (dir.next())
|
|
{
|
|
std::string name = dir.fileName();
|
|
if (name != "." && name != "..")
|
|
{
|
|
if (dir.isDir())
|
|
{
|
|
rmdir(dir.filePath().c_str());
|
|
}
|
|
else
|
|
{
|
|
removeFile(dir.filePath().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove the directory itself
|
|
rmdir(path);
|
|
}
|
|
|
|
std::string FileOps::canonicalPath(const char* path)
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
// on Linux and Mac OS 10.6, realpath() can allocate the required
|
|
// amount of memory automatically, however Mac OS 10.5 does not support
|
|
// this, so we used a fixed-sized buffer on all platforms
|
|
char canonicalPathBuffer[PATH_MAX+1];
|
|
if (realpath(path,canonicalPathBuffer) != 0)
|
|
{
|
|
return std::string(canonicalPathBuffer);
|
|
}
|
|
else
|
|
{
|
|
throw IOException("Error reading canonical path for " + std::string(path));
|
|
}
|
|
#else
|
|
throw IOException("canonicalPath() not implemented");
|
|
#endif
|
|
}
|
|
|
|
template <class InFlags, class OutFlags>
|
|
void addFlag(InFlags inFlags, int testBit, OutFlags& outFlags, int setBit)
|
|
{
|
|
if (inFlags & testBit)
|
|
{
|
|
outFlags |= setBit;
|
|
}
|
|
}
|
|
|
|
#ifdef PLATFORM_UNIX
|
|
int FileOps::toUnixPermissions(int qtPermissions)
|
|
{
|
|
mode_t result = 0;
|
|
addFlag(qtPermissions,ReadUser,result,S_IRUSR);
|
|
addFlag(qtPermissions,WriteUser,result,S_IWUSR);
|
|
addFlag(qtPermissions,ExecUser,result,S_IXUSR);
|
|
addFlag(qtPermissions,ReadGroup,result,S_IRGRP);
|
|
addFlag(qtPermissions,WriteGroup,result,S_IWGRP);
|
|
addFlag(qtPermissions,ExecGroup,result,S_IXGRP);
|
|
addFlag(qtPermissions,ReadOther,result,S_IROTH);
|
|
addFlag(qtPermissions,WriteOther,result,S_IWOTH);
|
|
addFlag(qtPermissions,ExecOther,result,S_IXOTH);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
std::string FileOps::toUnixPathSeparators(const std::string& str)
|
|
{
|
|
std::string result = str;
|
|
for (int i=0; i < result.size(); i++)
|
|
{
|
|
if (result[i] == '\\')
|
|
{
|
|
result[i] = '/';
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string FileOps::tempPath()
|
|
{
|
|
#ifdef PLATFORM_UNIX
|
|
return "/tmp";
|
|
#else
|
|
char buffer[MAX_PATH+1];
|
|
GetTempPath(MAX_PATH+1,buffer);
|
|
return toUnixPathSeparators(buffer);
|
|
#endif
|
|
}
|
|
|