diff options
Diffstat (limited to 'cpp/src/IcePatch2Lib/Util.cpp')
-rw-r--r-- | cpp/src/IcePatch2Lib/Util.cpp | 1442 |
1 files changed, 1442 insertions, 0 deletions
diff --git a/cpp/src/IcePatch2Lib/Util.cpp b/cpp/src/IcePatch2Lib/Util.cpp new file mode 100644 index 00000000000..41d422e8556 --- /dev/null +++ b/cpp/src/IcePatch2Lib/Util.cpp @@ -0,0 +1,1442 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// We need to include io.h first to get the proper signature for +// _wfindfirst +// +#ifdef _WIN32 +# include <io.h> +#endif + +#include <IceUtil/DisableWarnings.h> +#include <IceUtil/IceUtil.h> +#include <IceUtil/StringUtil.h> +#include <IceUtil/FileUtil.h> +#define ICE_PATCH2_API_EXPORTS +#include <IcePatch2/Util.h> +#include <openssl/sha.h> +#include <bzlib.h> +#include <iomanip> + +#ifdef _WIN32 +# include <direct.h> +#else +# include <unistd.h> +# include <dirent.h> +#endif + +#ifdef __BCPLUSPLUS__ +# include <iterator> +#endif + +const char* IcePatch2::checksumFile = "IcePatch2.sum"; +const char* IcePatch2::logFile = "IcePatch2.log"; + +// +// Solaris 9 and before doesn't have scandir() or alphasort(). +// +#ifdef __sun + +extern "C" static int +ice_scandir(const char* dir, struct dirent*** namelist, + int (*select)(const struct dirent*), + int (*compar)(const void*, const void*)) +{ + DIR* d; + struct dirent* entry; + register int i = 0; + size_t entrysize; + + if((d = opendir(dir)) == 0) + { + return -1; + } + + *namelist = 0; + while((entry = readdir(d)) != 0) + { + if(select == 0 || (select != 0 && (*select)(entry))) + { + *namelist = (struct dirent**)realloc((void*)(*namelist), (size_t)((i + 1) * sizeof(struct dirent*))); + if(*namelist == 0) + { + closedir(d); + return -1; + } + + entrysize = sizeof(struct dirent) - sizeof(entry->d_name) + strlen(entry->d_name) + 1; + (*namelist)[i] = (struct dirent*)malloc(entrysize); + if((*namelist)[i] == 0) + { + closedir(d); + return -1; + } + memcpy((*namelist)[i], entry, entrysize); + ++i; + } + } + + if(closedir(d)) + { + return -1; + } + + if(i == 0) + { + return -1; + } + + if(compar != 0) + { + qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar); + } + + return i; +} + +extern "C" static int +ice_alphasort(const void* v1, const void* v2) +{ + const struct dirent **a = (const struct dirent **)v1; + const struct dirent **b = (const struct dirent **)v2; + return(strcmp((*a)->d_name, (*b)->d_name)); +} + +#endif + +using namespace std; +using namespace Ice; +using namespace IcePatch2; + +bool +IcePatch2::writeFileInfo(FILE* fp, const FileInfo& info) +{ + int rc = fprintf(fp, "%s\t%s\t%d\t%d\n", + IceUtilInternal::escapeString(info.path, "").c_str(), + bytesToString(info.checksum).c_str(), + info.size, + static_cast<int>(info.executable)); + return rc > 0; +} + +bool +IcePatch2::readFileInfo(FILE* fp, FileInfo& info) +{ + string data; + char buf[BUFSIZ]; + while(fgets(buf, static_cast<int>(sizeof(buf)), fp) != 0) + { + data += buf; + + size_t len = strlen(buf); + if(buf[len - 1] == '\n') + { + break; + } + } + if(data.empty()) + { + return false; + } + + istringstream is(data); + + string s; + getline(is, s, '\t'); + IceUtilInternal::unescapeString(s, 0, s.size(), info.path); + + getline(is, s, '\t'); + info.checksum = stringToBytes(s); + + is >> info.size; + is >> info.executable; + + return true; +} + +string +IcePatch2::bytesToString(const ByteSeq& bytes) +{ +/* + ostringstream s; + + for(ByteSeq::const_iterator p = bytes.begin(); p != bytes.end(); ++p) + { + s << setw(2) << setfill('0') << hex << static_cast<int>(*p); + } + + return s.str(); +*/ + + static const char* toHex = "0123456789abcdef"; + + string s; + s.resize(bytes.size() * 2); + + for(unsigned int i = 0; i < bytes.size(); ++i) + { + s[i * 2] = toHex[(bytes[i] >> 4) & 0xf]; + s[i * 2 + 1] = toHex[bytes[i] & 0xf]; + } + + return s; +} + +ByteSeq +IcePatch2::stringToBytes(const string& str) +{ + ByteSeq bytes; + bytes.reserve((str.size() + 1) / 2); + + for(unsigned int i = 0; i + 1 < str.size(); i += 2) + { +/* + istringstream is(str.substr(i, 2)); + int byte; + is >> hex >> byte; +*/ + + int byte = 0; + + for(int j = 0; j < 2; ++j) + { + char c = str[i + j]; + + if(c >= '0' && c <= '9') + { + byte |= c - '0'; + } + else if(c >= 'a' && c <= 'f') + { + byte |= 10 + c - 'a'; + } + else if(c >= 'A' && c <= 'F') + { + byte |= 10 + c - 'A'; + } + + if(j == 0) + { + byte <<= 4; + } + } + + bytes.push_back(static_cast<Byte>(byte)); + } + + return bytes; +} + +string +IcePatch2::simplify(const string& path) +{ + string result = path; + + string::size_type pos; + +#ifdef _WIN32 + pos = 0; + if(result.find("\\\\") == 0) + { + pos = 2; + } + + for(; pos < result.size(); ++pos) + { + if(result[pos] == '\\') + { + result[pos] = '/'; + } + } +#endif + + pos = 0; + while((pos = result.find("//", pos)) != string::npos) + { + result.erase(pos, 1); + } + + pos = 0; + while((pos = result.find("/./", pos)) != string::npos) + { + result.erase(pos, 2); + } + + while(result.substr(0, 4) == "/../") + { + result.erase(0, 3); + } + + if(result.substr(0, 2) == "./") + { + result.erase(0, 2); + } + + if(result == "/." || + (result.size() == 4 && isalpha(static_cast<unsigned char>(result[0])) && result[1] == ':' && + result[2] == '/' && result[3] == '.')) + { + return result.substr(0, result.size() - 1); + } + + if(result.size() >= 2 && result.substr(result.size() - 2, 2) == "/.") + { + result.erase(result.size() - 2, 2); + } + + if(result == "/" || (result.size() == 3 && isalpha(static_cast<unsigned char>(result[0])) && result[1] == ':' && + result[2] == '/')) + { + return result; + } + + if(result.size() >= 1 && result[result.size() - 1] == '/') + { + result.erase(result.size() - 1); + } + + if(result == "/..") + { + result = "/"; + } + + return result; +} + +bool +IcePatch2::isRoot(const string& pa) +{ + string path = simplify(pa); +#ifdef _WIN32 + return path == "/" || path.size() == 3 && isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':' && + path[2] == '/'; +#else + return path == "/"; +#endif +} + +string +IcePatch2::getSuffix(const string& pa) +{ + const string path = simplify(pa); + + string::size_type dotPos = path.rfind('.'); + string::size_type slashPos = path.rfind('/'); + + if(dotPos == string::npos || (slashPos != string::npos && slashPos > dotPos)) + { + return string(); + } + + return path.substr(dotPos + 1); +} + +string +IcePatch2::getWithoutSuffix(const string& pa) +{ + const string path = simplify(pa); + + string::size_type dotPos = path.rfind('.'); + string::size_type slashPos = path.rfind('/'); + + if(dotPos == string::npos || (slashPos != string::npos && slashPos > dotPos)) + { + return path; + } + + return path.substr(0, dotPos); +} + +bool +IcePatch2::ignoreSuffix(const string& path) +{ + string suffix = getSuffix(path); + return suffix == "md5" // For legacy IcePatch. + || suffix == "tot" // For legacy IcePatch. + || suffix == "bz2" + || suffix == "bz2temp"; +} + +string +IcePatch2::getBasename(const string& pa) +{ + const string path = simplify(pa); + + string::size_type pos = path.rfind('/'); + if(pos == string::npos) + { + return path; + } + else + { + return path.substr(pos + 1); + } +} + +string +IcePatch2::getDirname(const string& pa) +{ + const string path = simplify(pa); + + string::size_type pos = path.rfind('/'); + if(pos == string::npos) + { + return string(); + } + else + { + return path.substr(0, pos); + } +} + +void +IcePatch2::rename(const string& fromPa, const string& toPa) +{ + + const string fromPath = simplify(fromPa); + const string toPath = simplify(toPa); + + IceUtilInternal::remove(toPath); // We ignore errors, as the file we are renaming to might not exist. + + if(IceUtilInternal::rename(fromPath ,toPath) == -1) + { + throw "cannot rename `" + fromPath + "' to `" + toPath + "': " + IceUtilInternal::lastErrorToString(); + } +} + +void +IcePatch2::remove(const string& pa) +{ + const string path = simplify(pa); + + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(path, &buf) == -1) + { + throw "cannot stat `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + if(S_ISDIR(buf.st_mode)) + { + if(IceUtilInternal::rmdir(path) == -1) + { + if(errno == EACCES) + { + assert(false); + } + throw "cannot remove directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + else + { + if(IceUtilInternal::remove(path) == -1) + { + throw "cannot remove file `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } +} + +void +IcePatch2::removeRecursive(const string& pa) +{ + const string path = simplify(pa); + + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(path, &buf) == -1) + { + throw "cannot stat `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + if(S_ISDIR(buf.st_mode)) + { + StringSeq paths = readDirectory(path); + for(StringSeq::const_iterator p = paths.begin(); p != paths.end(); ++p) + { + removeRecursive(path + '/' + *p); + } + + if(!isRoot(path)) + { + if(IceUtilInternal::rmdir(path) == -1) + { + throw "cannot remove directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + } + else + { + if(IceUtilInternal::remove(path) == -1) + { + throw "cannot remove file `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } +} + +StringSeq +IcePatch2::readDirectory(const string& pa) +{ + const string path = simplify(pa); + +#ifdef _WIN32 + + StringSeq result; + const wstring fs = IceUtil::stringToWstring(simplify(path + "/*")); + +# ifdef __BCPLUSPLUS__ + struct _wffblk data; + int h = _wfindfirst(fs.c_str(), &data, FA_DIREC); + if(h == -1) + { + if(_doserrno == ENMFILE) + { + return result; + } + throw "cannot read directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + + while(true) + { + string name = IceUtil::wstringToString(data.ff_name); + assert(!name.empty()); + + if(name != ".." && name != ".") + { + result.push_back(name); + } + + if(_wfindnext(&data) == -1) + { + if(errno == ENOENT) + { + break; + } + + string ex = "cannot read directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + _wfindclose(&data); + throw ex; + } + } + + _wfindclose(&data); +# else + struct _wfinddata_t data; + +# if defined(_MSC_VER) && (_MSC_VER < 1300) + long h = _wfindfirst(fs.c_str(), &data); +# else + intptr_t h = _wfindfirst(fs.c_str(), &data); +# endif + if(h == -1) + { + throw "cannot read directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + while(true) + { + string name = IceUtil::wstringToString(data.name); + assert(!name.empty()); + + if(name != ".." && name != ".") + { + result.push_back(name); + } + + if(_wfindnext(h, &data) == -1) + { + if(errno == ENOENT) + { + break; + } + + string ex = "cannot read directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + _findclose(h); + throw ex; + } + } + + _findclose(h); +# endif + + sort(result.begin(), result.end()); + return result; + +#else + + struct dirent **namelist; +#ifdef __sun + int n = ice_scandir(path.c_str(), &namelist, 0, ice_alphasort); +#else + int n = scandir(path.c_str(), &namelist, 0, alphasort); +#endif + if(n < 0) + { + throw "cannot read directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + StringSeq result; + result.reserve(n - 2); + + for(int i = 0; i < n; ++i) + { + string name = namelist[i]->d_name; + assert(!name.empty()); + + free(namelist[i]); + + if(name != ".." && name != ".") + { + result.push_back(name); + } + } + + free(namelist); + return result; + +#endif +} + +void +IcePatch2::createDirectory(const string& pa) +{ + const string path = simplify(pa); + + if(IceUtilInternal::mkdir(path, 0777) == -1) + { + if(errno != EEXIST) + { + throw "cannot create directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } +} + +void +IcePatch2::createDirectoryRecursive(const string& pa) +{ + const string path = simplify(pa); + + string dir = getDirname(path); + if(!dir.empty()) + { + createDirectoryRecursive(dir); + } + + if(!isRoot(path + "/")) + { + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(path, &buf) != -1) + { + if(S_ISDIR(buf.st_mode)) + { + return; + } + } + + if(IceUtilInternal::mkdir(path, 0777) == -1) + { + if(errno != EEXIST) + { + throw "cannot create directory `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + } +} + +void +IcePatch2::compressBytesToFile(const string& pa, const ByteSeq& bytes, Int pos) +{ + const string path = simplify(pa); + + FILE* stdioFile = IceUtilInternal::fopen(path, "wb"); + if(!stdioFile) + { + throw "cannot open `" + path + "' for writing:\n" + IceUtilInternal::lastErrorToString(); + } + + int bzError; + BZFILE* bzFile = BZ2_bzWriteOpen(&bzError, stdioFile, 5, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWriteOpen failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + fclose(stdioFile); + throw ex; + } + + BZ2_bzWrite(&bzError, bzFile, const_cast<Byte*>(&bytes[pos]), static_cast<int>(bytes.size() - pos)); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWrite failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); + fclose(stdioFile); + throw ex; + } + + BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWriteClose failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + fclose(stdioFile); + throw ex; + } + + fclose(stdioFile); +} + +void +IcePatch2::decompressFile(const string& pa) +{ + const string path = simplify(pa); + const string pathBZ2 = path + ".bz2"; + + FILE* fp = 0; + FILE* stdioFileBZ2 = 0; + int bzError; + BZFILE* bzFile = 0; + + try + { + fp = IceUtilInternal::fopen(path, "wb"); + if(!fp) + { + throw "cannot open `" + path + "' for writing:\n" + IceUtilInternal::lastErrorToString(); + } + + stdioFileBZ2 = IceUtilInternal::fopen(pathBZ2, "rb"); + if(!stdioFileBZ2) + { + throw "cannot open `" + pathBZ2 + "' for reading:\n" + IceUtilInternal::lastErrorToString(); + } + +#ifdef __BCPLUSPLUS__ + // + // The BZ2_bzReadOpen/BZ2_bzRead/BZ2_bzReadClose functions fail with BCC + // + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(pathBZ2, &buf) == -1) + { + throw "cannot stat `" + pathBZ2 + "':\n" + IceUtilInternal::lastErrorToString(); + } + + ByteSeq compressedBytes(buf.st_size); + if(fread(&compressedBytes[0], buf.st_size, 1, stdioFileBZ2) != 1) + { + throw "cannot read from `" + pathBZ2 + "':\n" + IceUtilInternal::lastErrorToString(); + } + + ByteSeq uncompressedBytes; + unsigned int uncompressedLen = buf.st_size * 2; + while(true) + { + uncompressedBytes.resize(uncompressedLen); + int bzError = BZ2_bzBuffToBuffDecompress(&uncompressedBytes[0], &uncompressedLen, &compressedBytes[0], + buf.st_size, 0, 0); + if(bzError == BZ_OK) + { + break; + } + else if(bzError == BZ_OUTBUFF_FULL) + { + uncompressedLen *= 2; + continue; + } + else + { + string ex = "BZ2_bzBuffToBuffDecompress failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + throw ex; + } + } + + if(fwrite(&uncompressedBytes[0], uncompressedLen, 1, fp) != 1) + { + throw "cannot write to `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } +#else + bzFile = BZ2_bzReadOpen(&bzError, stdioFileBZ2, 0, 0, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzReadOpen failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + throw ex; + } + + const Int numBZ2 = 64 * 1024; + Byte bytesBZ2[numBZ2]; + + while(bzError != BZ_STREAM_END) + { + int sz = BZ2_bzRead(&bzError, bzFile, bytesBZ2, numBZ2); + if(bzError != BZ_OK && bzError != BZ_STREAM_END) + { + string ex = "BZ2_bzRead failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + throw ex; + } + + if(sz > 0) + { + long pos = ftell(stdioFileBZ2); + if(pos == -1) + { + throw "cannot get read position for `" + pathBZ2 + "':\n" + IceUtilInternal::lastErrorToString(); + } + + if(fwrite(bytesBZ2, sz, 1, fp) != 1) + { + throw "cannot write to `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + } + + BZ2_bzReadClose(&bzError, bzFile); + bzFile = 0; + if(bzError != BZ_OK) + { + string ex = "BZ2_bzReadClose failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + throw ex; + } +#endif + } + catch(...) + { + if(bzFile != 0) + { + BZ2_bzReadClose(&bzError, bzFile); + } + if(stdioFileBZ2 != 0) + { + fclose(stdioFileBZ2); + } + if(fp != 0) + { + fclose(fp); + } + throw; + } + + fclose(stdioFileBZ2); + fclose(fp); +} + +void +IcePatch2::setFileFlags(const string& pa, const FileInfo& info) +{ +#ifndef _WIN32 // Windows doesn't support the executable flag + const string path = simplify(pa); + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(path, &buf) == -1) + { + throw "cannot stat `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + chmod(path.c_str(), info.executable ? buf.st_mode | S_IXUSR : buf.st_mode & ~S_IXUSR); +#endif +} + +static bool +getFileInfoSeqInt(const string& basePath, const string& relPath, int compress, GetFileInfoSeqCB* cb, + FileInfoSeq& infoSeq) +{ + if(relPath == checksumFile || relPath == logFile) + { + return true; + } + + const string path = simplify(basePath + '/' + relPath); + + if(ignoreSuffix(path)) + { + const string pathWithoutSuffix = getWithoutSuffix(path); + + if(ignoreSuffix(pathWithoutSuffix)) + { + if(cb && !cb->remove(relPath)) + { + return false; + } + + remove(path); // Removing file with suffix for another file that already has a suffix. + } + else + { + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(getWithoutSuffix(path), &buf) == -1) + { + if(errno == ENOENT) + { + if(cb && !cb->remove(relPath)) + { + return false; + } + + remove(path); // Removing orphaned file. + } + else + { + throw "cannot stat `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + else if(buf.st_size == 0) + { + if(cb && !cb->remove(relPath)) + { + return false; + } + + remove(path); // Removing file with suffix for empty file. + } + } + } + else + { + + IceUtilInternal::structstat buf; + if(IceUtilInternal::stat(path, &buf) == -1) + { + throw "cannot stat `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + if(S_ISDIR(buf.st_mode)) + { + FileInfo info; + info.path = relPath; + info.size = -1; + info.executable = false; + + ByteSeq bytes(relPath.size()); + copy(relPath.begin(), relPath.end(), bytes.begin()); + + ByteSeq bytesSHA(20); + if(!bytes.empty()) + { + SHA1(reinterpret_cast<unsigned char*>(&bytes[0]), bytes.size(), + reinterpret_cast<unsigned char*>(&bytesSHA[0])); + } + else + { + fill(bytesSHA.begin(), bytesSHA.end(), 0); + } + info.checksum.swap(bytesSHA); + + infoSeq.push_back(info); + + StringSeq content = readDirectory(path); + for(StringSeq::const_iterator p = content.begin(); p != content.end() ; ++p) + { + if(!getFileInfoSeqInt(basePath, simplify(relPath + '/' + *p), compress, cb, infoSeq)) + { + return false; + } + } + } + else if(S_ISREG(buf.st_mode)) + { + FileInfo info; + info.path = relPath; + info.size = 0; +#ifdef _WIN32 + info.executable = false; // Windows doesn't support the executable flag +#else + info.executable = buf.st_mode & S_IXUSR; +#endif + + IceUtilInternal::structstat bufBZ2; + const string pathBZ2 = path + ".bz2"; + bool doCompress = false; + if(buf.st_size != 0 && compress > 0) + { + // + // compress == 0: Never compress. + // compress == 1: Compress if necessary. + // compress >= 2: Always compress. + // + if(compress >= 2 || IceUtilInternal::stat(pathBZ2, &bufBZ2) == -1 || buf.st_mtime >= bufBZ2.st_mtime) + { + if(cb && !cb->compress(relPath)) + { + return false; + } + + doCompress = true; + } + else + { + info.size = static_cast<Int>(bufBZ2.st_size); + } + } + + if(cb && !cb->checksum(relPath)) + { + return false; + } + + ByteSeq bytesSHA(20); + + if(relPath.size() + buf.st_size == 0) + { + fill(bytesSHA.begin(), bytesSHA.end(), 0); + } + else + { + SHA_CTX ctx; + SHA1_Init(&ctx); + if(relPath.size() != 0) + { + SHA1_Update(&ctx, reinterpret_cast<const void*>(relPath.c_str()), relPath.size()); + } + + if(buf.st_size != 0) + { +#ifdef __BCPLUSPLUS__ + // + // The BZ2_bzWriteOpen/BZ2_bzWrite/BZ2_bzWriteClose functions fail with BCC + // + if(doCompress) + { + int fd = IceUtilInternal::open(path.c_str(), O_BINARY|O_RDONLY); + if(fd == -1) + { + throw "cannot open `" + path + "' for reading:\n" + IceUtilInternal::lastErrorToString(); + } + + ByteSeq uncompressedBytes(buf.st_size); + + if(read(fd, &uncompressedBytes[0], buf.st_size) == -1) + { + close(fd); + throw "cannot read from `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + + unsigned int compressedLen = buf.st_size * 1.01 + 600; + ByteSeq compressedBytes(compressedLen); + + int bzError = BZ2_bzBuffToBuffCompress(&compressedBytes[0], &compressedLen, + &uncompressedBytes[0], buf.st_size, 5, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzBuffToBuffCompress failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + close(fd); + throw ex; + } + close(fd); + + const string pathBZ2Temp = path + ".bz2temp"; + FILE* stdioFile = IceUtilInternal::fopen(pathBZ2Temp, "wb"); + if(fwrite(&compressedBytes[0], compressedLen, 1, stdioFile) != 1) + { + fclose(stdioFile); + throw "cannot write to `" + pathBZ2Temp + "':\n" + IceUtilInternal::lastErrorToString(); + } + fclose(stdioFile); + + rename(pathBZ2Temp, pathBZ2); + + info.size = compressedLen; + } +#endif + + int fd = IceUtilInternal::open(path.c_str(), O_BINARY|O_RDONLY); + if(fd == -1) + { + throw "cannot open `" + path + "' for reading:\n" + IceUtilInternal::lastErrorToString(); + } + +#ifndef __BCPLUSPLUS__ + const string pathBZ2Temp = path + ".bz2temp"; + FILE* stdioFile = 0; + int bzError = 0; + BZFILE* bzFile = 0; + if(doCompress) + { + stdioFile = IceUtilInternal::fopen(simplify(pathBZ2Temp), "wb"); + if(!stdioFile) + { +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _close(fd); +#else + close(fd); +#endif + throw "cannot open `" + pathBZ2Temp + "' for writing:\n" + IceUtilInternal::lastErrorToString(); + } + + bzFile = BZ2_bzWriteOpen(&bzError, stdioFile, 5, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWriteOpen failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + fclose(stdioFile); +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _close(fd); +#else + close(fd); +#endif + throw ex; + } + } +#endif + + unsigned int bytesLeft = static_cast<unsigned int>(buf.st_size); + while(bytesLeft > 0) + { + ByteSeq bytes(min(bytesLeft, 1024u*1024)); + if( +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _read(fd, &bytes[0], static_cast<unsigned int>(bytes.size())) +#else + read(fd, &bytes[0], static_cast<unsigned int>(bytes.size())) +#endif + == -1) + { +#ifndef __BCPLUSPLUS__ + if(doCompress) + { + fclose(stdioFile); + } +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _close(fd); +#else + close(fd); +#endif + throw "cannot read from `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + bytesLeft -= static_cast<unsigned int>(bytes.size()); + +#ifndef __BCPLUSPLUS__ + if(doCompress) + { + BZ2_bzWrite(&bzError, bzFile, const_cast<Byte*>(&bytes[0]), static_cast<int>(bytes.size())); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWrite failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); + fclose(stdioFile); +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _close(fd); +#else + close(fd); +#endif + throw ex; + } + } +#endif + + SHA1_Update(&ctx, reinterpret_cast<const void*>(&bytes[0]), bytes.size()); + } + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + _close(fd); +#else + close(fd); +#endif + +#ifndef __BCPLUSPLUS__ + if(doCompress) + { + BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); + if(bzError != BZ_OK) + { + string ex = "BZ2_bzWriteClose failed"; + if(bzError == BZ_IO_ERROR) + { + ex += string(": ") + IceUtilInternal::lastErrorToString(); + } + fclose(stdioFile); + throw ex; + } + + fclose(stdioFile); + + rename(pathBZ2Temp, pathBZ2); + + if(IceUtilInternal::stat(pathBZ2, &bufBZ2) == -1) + { + throw "cannot stat `" + pathBZ2 + "':\n" + IceUtilInternal::lastErrorToString(); + } + + info.size = static_cast<Int>(bufBZ2.st_size); + } +#endif + } + + SHA1_Final(reinterpret_cast<unsigned char*>(&bytesSHA[0]), &ctx); + } + + info.checksum.swap(bytesSHA); + + infoSeq.push_back(info); + } + } + + return true; +} + +bool +IcePatch2::getFileInfoSeq(const string& basePath, int compress, GetFileInfoSeqCB* cb, + FileInfoSeq& infoSeq) +{ + return getFileInfoSeqSubDir(basePath, ".", compress, cb, infoSeq); +} + +bool +IcePatch2::getFileInfoSeqSubDir(const string& basePa, const string& relPa, int compress, GetFileInfoSeqCB* cb, + FileInfoSeq& infoSeq) +{ + const string basePath = simplify(basePa); + const string relPath = simplify(relPa); + + if(!getFileInfoSeqInt(basePath, relPath, compress, cb, infoSeq)) + { + return false; + } + + sort(infoSeq.begin(), infoSeq.end(), FileInfoLess()); + infoSeq.erase(unique(infoSeq.begin(), infoSeq.end(), FileInfoEqual()), infoSeq.end()); + + return true; +} + +void +IcePatch2::saveFileInfoSeq(const string& pa, const FileInfoSeq& infoSeq) +{ + { + const string path = simplify(pa + '/' + checksumFile); + + FILE* fp = IceUtilInternal::fopen(path, "w"); + if(!fp) + { + throw "cannot open `" + path + "' for writing:\n" + IceUtilInternal::lastErrorToString(); + } + try + { + for(FileInfoSeq::const_iterator p = infoSeq.begin(); p != infoSeq.end(); ++p) + { + if(!writeFileInfo(fp, *p)) + { + throw "error writing `" + path + "':\n" + IceUtilInternal::lastErrorToString(); + } + } + } + catch(...) + { + fclose(fp); + throw; + } + fclose(fp); + } + + { + const string pathLog = simplify(pa + '/' + logFile); + + try + { + remove(pathLog); + } + catch(...) + { + } + } +} + +void +IcePatch2::loadFileInfoSeq(const string& pa, FileInfoSeq& infoSeq) +{ + { + const string path = simplify(pa + '/' + checksumFile); + + FILE* fp = IceUtilInternal::fopen(path, "r"); + if(!fp) + { + throw "cannot open `" + path + "' for reading:\n" + IceUtilInternal::lastErrorToString(); + } + + while(true) + { + FileInfo info; + if(readFileInfo(fp, info)) + { + infoSeq.push_back(info); + } + else + { + break; + } + } + fclose(fp); + + sort(infoSeq.begin(), infoSeq.end(), FileInfoLess()); + infoSeq.erase(unique(infoSeq.begin(), infoSeq.end(), FileInfoEqual()), infoSeq.end()); + } + + { + const string pathLog = simplify(pa + '/' + logFile); + + FILE* fp = IceUtilInternal::fopen(pathLog, "r"); + if(fp != 0) + { + FileInfoSeq remove; + FileInfoSeq update; + + while(true) + { + int c = fgetc(fp); + if(c == EOF) + { + break; + } + + FileInfo info; + if(!readFileInfo(fp, info)) + { + break; + } + + if(c == '-') + { + remove.push_back(info); + } + else if(c == '+') + { + update.push_back(info); + } + } + fclose(fp); + + sort(remove.begin(), remove.end(), FileInfoLess()); + remove.erase(unique(remove.begin(), remove.end(), FileInfoEqual()), remove.end()); + + sort(update.begin(), update.end(), FileInfoLess()); + update.erase(unique(update.begin(), update.end(), FileInfoEqual()), update.end()); + + FileInfoSeq newInfoSeq; + newInfoSeq.reserve(infoSeq.size()); + + set_difference(infoSeq.begin(), + infoSeq.end(), + remove.begin(), + remove.end(), + back_inserter(newInfoSeq), + FileInfoLess()); + + infoSeq.swap(newInfoSeq); + + newInfoSeq.clear(); + newInfoSeq.reserve(infoSeq.size()); + + set_union(infoSeq.begin(), + infoSeq.end(), + update.begin(), + update.end(), + back_inserter(newInfoSeq), + FileInfoLess()); + + infoSeq.swap(newInfoSeq); + + saveFileInfoSeq(pa, infoSeq); + } + } +} + +void +IcePatch2::getFileTree0(const FileInfoSeq& infoSeq, FileTree0& tree0) +{ + tree0.nodes.resize(256); + tree0.checksum.resize(20); + + ByteSeq allChecksums0; + allChecksums0.resize(256 * 20); + ByteSeq::iterator c0 = allChecksums0.begin(); + + for(int i = 0; i < 256; ++i, c0 += 20) + { + FileTree1& tree1 = tree0.nodes[i]; + + tree1.files.clear(); + tree1.checksum.resize(20); + + FileInfoSeq::const_iterator p; + + for(p = infoSeq.begin(); p != infoSeq.end(); ++p) + { + if(i == static_cast<int>(p->checksum[0])) + { + tree1.files.push_back(*p); + } + } + + ByteSeq allChecksums1; + allChecksums1.resize(tree1.files.size() * 21); // 20 bytes for the checksum + 1 byte for the flag + ByteSeq::iterator c1 = allChecksums1.begin(); + + for(p = tree1.files.begin(); p != tree1.files.end(); ++p, c1 += 21) + { + copy(p->checksum.begin(), p->checksum.end(), c1); + *(c1 + 20) = p->executable; + } + + if(!allChecksums1.empty()) + { + SHA1(reinterpret_cast<unsigned char*>(&allChecksums1[0]), allChecksums1.size(), + reinterpret_cast<unsigned char*>(&tree1.checksum[0])); + } + else + { + fill(tree1.checksum.begin(), tree1.checksum.end(), 0); + } + + copy(tree1.checksum.begin(), tree1.checksum.end(), c0); + } + + if(!allChecksums0.empty()) + { + SHA1(reinterpret_cast<unsigned char*>(&allChecksums0[0]), allChecksums0.size(), + reinterpret_cast<unsigned char*>(&tree0.checksum[0])); + } + else + { + fill(tree0.checksum.begin(), tree0.checksum.end(), 0); + } +} + |