// ********************************************************************** // // Copyright (c) 2004 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. // // ********************************************************************** #include #include #include #include #include #ifdef _WIN32 # include # include # define S_ISDIR(mode) ((mode) & _S_IFDIR) # define S_ISREG(mode) ((mode) & _S_IFREG) #else # include # include # include # include # include #endif // // Sun-OS doesn't have scandir() or alphasort(). // #ifdef __sun static int 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) { 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) { 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; } static int 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; 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(*p); } return s.str(); } 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; bytes.push_back(static_cast(byte)); } return bytes; } string IcePatch2::normalize(const string& path) { string result = path; string::size_type pos; for(pos = 0; pos < result.size(); ++pos) { if(result[pos] == '\\') { result[pos] = '/'; } } 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); } if(result.substr(0, 2) == "./") { result.erase(0, 2); } if(result.size() >= 2 && result.substr(result.size() - 2, 2) == "/.") { result.erase(result.size() - 2, 2); } if(result.size() >= 1 && result[result.size() - 1] == '/') { result.erase(result.size() - 1); } return result; } string IcePatch2::getSuffix(const string& pa) { const string path = normalize(pa); string::size_type pos = path.rfind('.'); if(pos == string::npos) { return string(); } else { return path.substr(pos + 1); } } string IcePatch2::getWithoutSuffix(const string& pa) { const string path = normalize(pa); string::size_type pos = path.rfind('.'); if(pos == string::npos) { return path; } else { return path.substr(0, pos); } } bool IcePatch2::ignoreSuffix(const string& path) { string suffix = getSuffix(path); return suffix == "bz2"; } string IcePatch2::getBasename(const string& pa) { const string path = normalize(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 = normalize(pa); string::size_type pos = path.rfind('/'); if(pos == string::npos) { return string(); } else { return path.substr(0, pos); } } void IcePatch2::remove(const string& pa) { const string path = normalize(pa); if(::remove(path.c_str()) == -1) { throw "cannot remove file `" + path + "': " + strerror(errno); } } void IcePatch2::removeRecursive(const string& pa) { const string path = normalize(pa); struct stat buf; if(stat(path.c_str(), &buf) == -1) { throw "cannot stat `" + path + "': " + strerror(errno); } if(S_ISDIR(buf.st_mode)) { StringSeq paths = readDirectory(path); for(StringSeq::const_iterator p = paths.begin(); p != paths.end(); ++p) { removeRecursive(*p); } #ifdef _WIN32 if(_rmdir(path.c_str()) == -1) #else if(rmdir(path.c_str()) == -1) #endif { throw "cannot remove directory `" + path + "': " + strerror(errno); } } else { remove(path); } } StringSeq IcePatch2::readDirectory(const string& pa) { const string path = normalize(pa); #ifdef _WIN32 struct _finddata_t data; long h = _findfirst((path + "/*").c_str(), &data); if(h == -1) { throw "cannot read directory `" + path + "': " + strerror(errno); } StringSeq result; while(true) { string name = data.name; assert(!name.empty()); if(name != ".." && name != ".") { result.push_back(name); } if(_findnext(h, &data) == -1) { if(errno == ENOENT) { break; } string ex = "cannot read directory `" + path + "': " + strerror(errno); _findclose(h); throw ex; } } _findclose(h); sort(result.begin(), result.end()); return result; #else struct dirent **namelist; int n = scandir(path.c_str(), &namelist, 0, alphasort); if(n < 0) { throw "cannot read directory `" + path + "': " + strerror(errno); } 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::createDirectoryRecursive(const string& pa) { const string path = normalize(pa); string dir = getDirname(path); if(!dir.empty()) { createDirectoryRecursive(dir); } if(mkdir(path.c_str(), 0777) == -1) { if(errno != EEXIST) { throw "cannot create directory `" + path + "': " + strerror(errno); } } } void IcePatch2::compressToFile(const string& path, const ByteSeq& bytes, Int pos) { FILE* stdioFileBZ2 = fopen(path.c_str(), "wb"); if(!stdioFileBZ2) { throw "cannot open `" + path + "' for writing: " + strerror(errno); } int bzError; BZFILE* bzFile = BZ2_bzWriteOpen(&bzError, stdioFileBZ2, 5, 0, 0); if(bzError != BZ_OK) { string ex = "BZ2_bzWriteOpen failed"; if(bzError == BZ_IO_ERROR) { ex += string(": ") + strerror(errno); } fclose(stdioFileBZ2); throw ex; } BZ2_bzWrite(&bzError, bzFile, const_cast(&bytes[pos]), bytes.size() - pos); if(bzError != BZ_OK) { string ex = "BZ2_bzWrite failed"; if(bzError == BZ_IO_ERROR) { ex += string(": ") + strerror(errno); } BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); fclose(stdioFileBZ2); 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(": ") + strerror(errno); } fclose(stdioFileBZ2); throw ex; } fclose(stdioFileBZ2); } void IcePatch2::uncompressToFile(const string& path, const ByteSeq& bytes, Int pos) { FILE* stdioFileBZ2 = fopen(path.c_str(), "wb"); if(!stdioFileBZ2) { throw "cannot open `" + path + "' for writing: " + strerror(errno); } int bzError; BZFILE* bzFile = BZ2_bzWriteOpen(&bzError, stdioFileBZ2, 5, 0, 0); if(bzError != BZ_OK) { string ex = "BZ2_bzWriteOpen failed"; if(bzError == BZ_IO_ERROR) { ex += string(": ") + strerror(errno); } fclose(stdioFileBZ2); throw ex; } BZ2_bzWrite(&bzError, bzFile, const_cast(&bytes[pos]), bytes.size() - pos); if(bzError != BZ_OK) { string ex = "BZ2_bzWrite failed"; if(bzError == BZ_IO_ERROR) { ex += string(": ") + strerror(errno); } BZ2_bzWriteClose(&bzError, bzFile, 0, 0, 0); fclose(stdioFileBZ2); 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(": ") + strerror(errno); } fclose(stdioFileBZ2); throw ex; } fclose(stdioFileBZ2); } void IcePatch2::getFileInfoSeq(const string& pa, FileInfoSeq& infoSeq, bool compress, bool verbose) { const string path = normalize(pa); if(ignoreSuffix(path)) { string pathWithoutSuffix = getWithoutSuffix(path); if(ignoreSuffix(pathWithoutSuffix)) { if(verbose) { cout << path << ": removing " << getSuffix(path) << " file for " << getSuffix(pathWithoutSuffix) << " file" << endl; } remove(path); } else { struct stat buf; if(stat(getWithoutSuffix(path).c_str(), &buf) == -1) { if(errno == ENOENT) { if(verbose) { cout << path << ": removing orphaned " << getSuffix(path) << " file" << endl; } remove(path); } else { throw "cannot stat `" + path + "': " + strerror(errno); } } else if(buf.st_size == 0) { if(verbose) { cout << path << ": removing " << getSuffix(path) << " file for empty file" << endl; } remove(path); } } return; } struct stat buf; if(stat(path.c_str(), &buf) == -1) { throw "cannot stat `" + path + "': " + strerror(errno); } if(S_ISDIR(buf.st_mode)) { FileInfo info; info.path = path; info.size = -1; ByteSeq bytes(path.size()); copy(path.begin(), path.end(), bytes.begin()); ByteSeq bytesSHA(20); SHA1(reinterpret_cast(&bytes[0]), bytes.size(), reinterpret_cast(&bytesSHA[0])); info.checksum.swap(bytesSHA); infoSeq.push_back(info); StringSeq content = readDirectory(path); for(StringSeq::const_iterator p = content.begin(); p != content.end() ; ++p) { getFileInfoSeq(path + '/' + *p, infoSeq, compress, verbose); } } else if(S_ISREG(buf.st_mode)) { FileInfo info; info.path = path; info.size = 0; ByteSeq bytes(path.size() + buf.st_size); copy(path.begin(), path.end(), bytes.begin()); if(buf.st_size != 0) { if(verbose) { if(compress) { cout << path << ": calculating checksum and compressing file" << endl; } else { cout << path << ": calculating checksum" << endl; } } int fd = open(path.c_str(), O_RDONLY); if(fd == -1) { throw "cannot open `" + path + "' for reading: " + strerror(errno); } if(read(fd, &bytes[path.size()], buf.st_size) == -1) { close(fd); throw "cannot read `" + path + "': " + strerror(errno); } close(fd); if(compress) { string pathBZ2 = path + ".bz2"; compressToFile(pathBZ2, bytes, path.size()); struct stat bufBZ2; if(stat(pathBZ2.c_str(), &bufBZ2) == -1) { throw "cannot stat `" + pathBZ2 + "': " + strerror(errno); } info.size = bufBZ2.st_size; } } else { if(verbose) { cout << path << ": calculating checksum for empty file" << endl; } } ByteSeq bytesSHA(20); SHA1(reinterpret_cast(&bytes[0]), bytes.size(), reinterpret_cast(&bytesSHA[0])); info.checksum.swap(bytesSHA); infoSeq.push_back(info); } else { if(verbose) { cout << path << ": ignoring unknown file type" << endl; } } } void IcePatch2::saveFileInfoSeq(const string& path, const FileInfoSeq& infoSeq) { ofstream os(path.c_str()); if(!os) { throw "cannot open `" + path + "' for writing: " + strerror(errno); } for(FileInfoSeq::const_iterator p = infoSeq.begin(); p != infoSeq.end(); ++p) { os << *p << '\n'; } } void IcePatch2::loadFileInfoSeq(const string& path, FileInfoSeq& infoSeq) { ifstream is(path.c_str()); if(!is) { throw "cannot open `" + path + "' for reading: " + strerror(errno); } while(is.good()) { FileInfo info; is >> info; if(is.good()) { infoSeq.push_back(info); } } } void IcePatch2::getFileTree0(const FileInfoSeq& infoSeq, FileTree0& tree0) { tree0.nodes.resize(256); ByteSeq allChecksums; allChecksums.resize(256 * 20); ByteSeq::iterator q = allChecksums.begin(); for(int i = 0; i < 256; ++i) { FileInfoSeq infoSeq1; for(FileInfoSeq::const_iterator p = infoSeq.begin(); p != infoSeq.end(); ++p) { if(i == static_cast(p->checksum[20 - 2])) { infoSeq1.push_back(*p); } } getFileTree1(infoSeq1, tree0.nodes[i]); assert(tree0.nodes[i].checksum.size() == 20); copy(tree0.nodes[i].checksum.begin(), tree0.nodes[i].checksum.end(), q); q += 20; } tree0.checksum.resize(20); SHA1(reinterpret_cast(&allChecksums[0]), allChecksums.size(), reinterpret_cast(&tree0.checksum[0])); } void IcePatch2::getFileTree1(const FileInfoSeq& infoSeq, FileTree1& tree1) { tree1.nodes.resize(256); ByteSeq allChecksums; allChecksums.resize(256 * 20); ByteSeq::iterator q = allChecksums.begin(); for(int i = 0; i < 256; ++i) { FileInfoSeq infoSeq2; for(FileInfoSeq::const_iterator p = infoSeq.begin(); p != infoSeq.end(); ++p) { if(i == static_cast(p->checksum[20 - 1])) { infoSeq2.push_back(*p); } } getFileTree2(infoSeq2, tree1.nodes[i]); assert(tree1.nodes[i].checksum.size() == 20); copy(tree1.nodes[i].checksum.begin(), tree1.nodes[i].checksum.end(), q); q += 20; } tree1.checksum.resize(20); SHA1(reinterpret_cast(&allChecksums[0]), allChecksums.size(), reinterpret_cast(&tree1.checksum[0])); } void IcePatch2::getFileTree2(const FileInfoSeq& infoSeq, FileTree2& tree2) { if(infoSeq.empty()) { tree2.files.clear(); tree2.checksum.resize(20, 0); } else { tree2.files.reserve(infoSeq.size()); FileInfoSeq::const_iterator p = infoSeq.begin(); ByteSeq allChecksums; allChecksums.resize(infoSeq.size() * 20); ByteSeq::iterator q = allChecksums.begin(); while(p < infoSeq.end()) { tree2.files.push_back(*p); assert(p->checksum.size() == 20); copy(p->checksum.begin(), p->checksum.end(), q); ++p; q += 20; } sort(tree2.files.begin(), tree2.files.end(), FileInfoCompare()); tree2.checksum.resize(20); SHA1(reinterpret_cast(&allChecksums[0]), allChecksums.size(), reinterpret_cast(&tree2.checksum[0])); } } ostream& IcePatch2::operator<<(ostream& os, const FileInfo& info) { os << IceUtil::escapeString(info.path, "") << '\t' << bytesToString(info.checksum) << '\t' << info.size; return os; } istream& IcePatch2::operator>>(istream& is, FileInfo& info) { string s; getline(is, s, '\t'); info.path = s; getline(is, s, '\t'); info.checksum = stringToBytes(s); getline(is, s, '\n'); info.size = atoi(s.c_str()); return is; }