diff options
Diffstat (limited to 'mmc_updater/src/FileUtils.cpp')
-rw-r--r-- | mmc_updater/src/FileUtils.cpp | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/mmc_updater/src/FileUtils.cpp b/mmc_updater/src/FileUtils.cpp new file mode 100644 index 00000000..712c0c5d --- /dev/null +++ b/mmc_updater/src/FileUtils.cpp @@ -0,0 +1,517 @@ +#include "FileUtils.h" + +#include "DirIterator.h" +#include "Log.h" +#include "Platform.h" +#include "StringUtils.h" + +#include <algorithm> +#include <assert.h> +#include <string.h> +#include <fstream> +#include <iostream> +// this actually works with mingw32, which we use. +#include <libgen.h> + +#ifdef PLATFORM_UNIX +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#endif + +FileUtils::IOException::IOException(const std::string& error) +{ + init(errno,error); +} + +FileUtils::IOException::IOException(int errorCode, const std::string& error) +{ + init(errorCode,error); +} + +void FileUtils::IOException::init(int errorCode, const std::string& error) +{ + m_error = error; + +#ifdef PLATFORM_UNIX + m_errorCode = errorCode; + + if (m_errorCode > 0) + { + m_error += " details: " + std::string(strerror(m_errorCode)); + } +#endif + +#ifdef PLATFORM_WINDOWS + m_errorCode = 0; + m_error += " GetLastError returned: " + intToStr(GetLastError()); +#endif +} + +FileUtils::IOException::~IOException() throw () +{ +} + +FileUtils::IOException::Type FileUtils::IOException::type() const +{ +#ifdef PLATFORM_UNIX + switch (m_errorCode) + { + case 0: + return NoError; + case EROFS: + return ReadOnlyFileSystem; + case ENOSPC: + return DiskFull; + default: + return Unknown; + } +#else + return Unknown; +#endif +} + +bool FileUtils::fileExists(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (lstat(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 +} + +int FileUtils::fileMode(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (stat(path,&fileInfo) != 0) + { + throw IOException("Error reading file permissions for " + std::string(path)); + } + return fileInfo.st_mode; +#else + // not implemented for Windows + return 0; +#endif +} + +void FileUtils::chmod(const char* path, int mode) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::chmod(path,static_cast<mode_t>(mode)) != 0) + { + throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(mode)); + } +#else + // TODO - Not implemented under Windows - all files + // get default permissions +#endif +} + +void FileUtils::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 FileUtils::mkpath(const char* dir) throw (IOException) +{ + std::string currentPath; + std::istringstream stream(dir); + while (!stream.eof()) + { + std::string segment; + std::getline(stream,segment,'/'); + currentPath += segment; + if (!currentPath.empty() && !fileExists(currentPath.c_str())) + { + mkdir(currentPath.c_str()); + } + currentPath += '/'; + } +} + +void FileUtils::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 FileUtils::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 FileUtils::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 FileUtils::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 FileUtils::fileName(const char* path) +{ + char* pathCopy = strdup(path); + std::string basename = ::basename(pathCopy); + free(pathCopy); + return basename; +} + +std::string FileUtils::dirname(const char* path) +{ + char* pathCopy = strdup(path); + std::string dirname = ::dirname(pathCopy); + free(pathCopy); + return dirname; +} + +void FileUtils::touch(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + // see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html + // + // we use utimes/futimes instead of utimensat/futimens for compatibility + // with older Linux and Mac + + if (fileExists(path)) + { + utimes(path,0 /* use current date/time */); + } + else + { + int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd != -1) + { + futimes(fd,0 /* use current date/time */); + 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 FileUtils::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 FileUtils::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 +} + +std::string FileUtils::toWindowsPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'/','\\'); + return result; +} + +std::string FileUtils::toUnixPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'\\','/'); + return result; +} + +std::string FileUtils::tempPath() +{ +#ifdef PLATFORM_UNIX + std::string tmpDir(notNullString(getenv("TMPDIR"))); + if (tmpDir.empty()) + { + tmpDir = "/tmp"; + } + return tmpDir; +#else + char buffer[MAX_PATH+1]; + GetTempPath(MAX_PATH+1,buffer); + return toUnixPathSeparators(buffer); +#endif +} + +bool startsWithDriveLetter(const char* path) +{ + return strlen(path) >= 2 && + (isalpha(path[0])) && + path[1] == ':'; +} + +bool FileUtils::isRelative(const char* path) +{ +#ifdef PLATFORM_UNIX + return strlen(path) == 0 || path[0] != '/'; +#else + // on Windows, a path is relative if it does not start with: + // - '\\' (a UNC name) + // - '[Drive Letter]:\' + // - A single backslash + // + // the input path is assumed to have already been converted to use + // Unix-style path separators + + std::string pathStr(path); + + if ((!pathStr.empty() && pathStr.at(0) == '/') || + (startsWith(pathStr,"//")) || + (startsWithDriveLetter(pathStr.c_str()))) + { + return false; + } + else + { + return true; + } +#endif +} + +void FileUtils::writeFile(const char* path, const char* data, int length) throw (IOException) +{ + std::ofstream stream(path,std::ios::binary | std::ios::trunc); + stream.write(data,length); +} + +std::string FileUtils::readFile(const char* path) throw (IOException) +{ + std::ifstream inputFile(path, std::ios::in | std::ios::binary); + std::string content; + inputFile.seekg(0, std::ios::end); + content.resize(static_cast<unsigned int>(inputFile.tellg())); + inputFile.seekg(0, std::ios::beg); + inputFile.read(&content[0], static_cast<int>(content.size())); + return content; +} + +void FileUtils::copyFile(const char* src, const char* dest) throw (IOException) +{ +#ifdef PLATFORM_UNIX + std::ifstream inputFile(src,std::ios::binary); + std::ofstream outputFile(dest,std::ios::binary | std::ios::trunc); + + if (!inputFile.good()) + { + throw IOException("Failed to read file " + std::string(src)); + } + if (!outputFile.good()) + { + throw IOException("Failed to write file " + std::string(dest)); + } + + outputFile << inputFile.rdbuf(); + + if (inputFile.bad()) + { + throw IOException("Error reading file " + std::string(src)); + } + if (outputFile.bad()) + { + throw IOException("Error writing file " + std::string(dest)); + } + + chmod(dest,fileMode(src)); +#else + if (!CopyFile(src,dest,FALSE)) + { + throw IOException("Failed to copy " + std::string(src) + " to " + std::string(dest)); + } +#endif +} + +std::string FileUtils::makeAbsolute(const char* path, const char* basePath) +{ + if (isRelative(path)) + { + assert(!isRelative(basePath)); + return std::string(basePath) + '/' + std::string(path); + } + else + { + return path; + } +} + +void FileUtils::chdir(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::chdir(path) != 0) + { + throw FileUtils::IOException("Unable to change directory"); + } +#else + if (!SetCurrentDirectory(path)) + { + throw FileUtils::IOException("Unable to change directory"); + } +#endif +} + +std::string FileUtils::getcwd() throw (IOException) +{ +#ifdef PLATFORM_UNIX + char path[PATH_MAX]; + if (!::getcwd(path,PATH_MAX)) + { + throw FileUtils::IOException("Failed to get current directory"); + } + return std::string(path); +#else + char path[MAX_PATH]; + if (GetCurrentDirectory(MAX_PATH,path) == 0) + { + throw FileUtils::IOException("Failed to get current directory"); + } + return toUnixPathSeparators(std::string(path)); +#endif +} + |