void File::list(FileVector& files, bool bRecursive) const { files.clear(); DirectoryIterator it(*this, bRecursive); while (it.hasNext()) { files.push_back(it.next()); } }
bool Ini_UpdaterLists::saveRollbackInfo(const FileVector &rollbackList) { STRING section_name = SS_KEY_RollbackTree; long k = 0; STRING rollbackFileAndPath; if(!getProductFolder(m_useCurrentFolder, rollbackFileAndPath, pLog)) { TRACE_MESSAGE("Unable to save rollback information, because failed to get product folder"); return false; } rollbackFileAndPath += UPDATER_LISTS_FILENEME; TRACE_MESSAGE2("Saving rollback list to '%s'", rollbackFileAndPath.to_string().c_str()); removeRollbackSection(); // for output saved entries in rows and save space in trace file std::string savedEntries; unsigned long logLineSplitIndex = 0; for(FileVector::const_iterator iter = rollbackList.begin(); iter != rollbackList.end(); ++iter) { if(iter->m_rollbackChange == STRING(SS_KEY_RecentStatusAdded) || iter->m_rollbackChange == STRING(SS_KEY_RecentStatusModified) || iter->m_rollbackChange == STRING(SS_KEY_RecentStatusDeleted)) { STRING newSectionName; const long order_number = k++; makeNewSectionName(order_number, iter->m_filename, newSectionName); if(!saveRollbackListEntry(rollbackFileAndPath.c_str(), section_name.c_str(), order_number, newSectionName, *iter)) { TRACE_MESSAGE2("Unable to save rollback information for file entry '%s'", (iter->m_localPath + iter->m_filename).to_string().c_str()); return false; } // append comma before all items, but the very first one if(logLineSplitIndex++) savedEntries += ", "; // split line each 3 elements, but not before last element if(iter + 1 != rollbackList.end()) { if(!(logLineSplitIndex % 3)) savedEntries += "\n\t"; } savedEntries += (iter->m_localPath + iter->m_filename).to_string(); } } TRACE_MESSAGE3("Rollback information saved successfully to %s, entries: %s", rollbackFileAndPath.to_string().c_str(), savedEntries.c_str()); return true; }
void File::remove(bool bRecursive) { if (bRecursive && !isLink() && isDirectory()) { FileVector files; list(files, false); for (FileVector::iterator it = files.begin(); it != files.end(); ++it) { it->remove(true); } } FileImpl::remove(m_sPath); }
bool Ini_UpdaterLists::readRollbackFileListFromSS(FileVector &changedFiles) { const STRING section_name = SS_KEY_RollbackTree; STRING rollbackFileAndPath; if(!getProductFolder(m_useCurrentFolder, rollbackFileAndPath, pLog)) return false; rollbackFileAndPath += UPDATER_LISTS_FILENEME; TCHAR childSectionName[MAX_PATH + 1]; for(int _i = 0;; ++_i) { TCHAR szKeyName[MAX_PATH + 1]; TCHAR fullSectionName[MAX_PATH + MAX_PATH + 1]; memset(childSectionName, 0, (MAX_PATH + 1) * sizeof(TCHAR)); memset(szKeyName, 0, (MAX_PATH + 1) * sizeof(TCHAR)); memset(fullSectionName, 0, (MAX_PATH + MAX_PATH + 1) * sizeof(TCHAR)); _stprintf(szKeyName, _T("entry%d"), _i); GetPrivateProfileString(section_name.c_str(), szKeyName, _T(""), childSectionName, MAX_PATH, rollbackFileAndPath.c_str()); if(!childSectionName[0]) break; _tcscpy(fullSectionName, section_name.c_str()); _tcscat(fullSectionName, _T(":")); _tcscat(fullSectionName, childSectionName); FileInfo rec; if(!readSingleFileInfoRollback(rollbackFileAndPath.c_str(), fullSectionName, rec)) { TRACE_MESSAGE2("Invalid content of '%s' section, skipping", fullSectionName); // ignore error, try next entry if any continue; } changedFiles.push_back(rec); } return true; }
//------------------------------------------------------------------------------ void DirectoryComparer::copy () { // variables FileCopier copier(safe_mode); FileVector& f0 = _uc[0].f; // for convenience vector<path>& d0 = _uc[0].d; // for convenience path fullpath0; // convenience (updated in loops) path fullpath1; // convenience (updated in loops) FileVector errors; // holds files that we fail to copy // precompute total number of files and bytes to be transferred annotate0(); // prepare batch, print totals unsigned totalFiles = _uc[0].files(); FileSize totalBytes = _uc[0].bytes(); copier.startBatch(totalFiles, totalBytes); cout << "========== Copying Files from A to B ==========\n"; cout << "Copying " << totalFiles << " files totaling " << totalBytes << " from " << workingPath(0) << " to " << workingPath(1) << ".\n"; cout << " Bytes Processed | Current File\n"; // copy files from _uc[0].f for (unsigned i=0; i<f0.size(); ++i) { fullpath0 = groundPath(f0[i], 0); fullpath1 = groundPath(f0[i], 1); if (exists(fullpath1)) { // error! errors.push_back(f0[i], fullpath0); cout << "Warning: Cannot copy " << fullpath0 << " to " << fullpath1 << " because the latter already exists.\n"; } else { copier.copy(fullpath0, fullpath1, f0[i]); } } // copy files from _uc[0].d recursive_directory_iterator end; unsigned depth = 0; path connector; path new_extension; for (unsigned i=0; i<d0.size(); ++i) { // initialize for recursive crawl of this directory recursive_directory_iterator itr(groundPath(d0[i], 0)); depth = 0; connector = d0[i]; cout << copier.status << "Creating directory " << connector << '.' << '\n'; if (!safe_mode) create_directory(_p[1] / connector); while (itr != end) { // update connector if (depth < itr.level()) { connector /= new_extension; cout << copier.status << "Creating directory " << connector << '.' << '\n'; if (!safe_mode) create_directory(_p[1] / connector); ++depth; // this makes depth equal to itr.level() } else while (depth > itr.level()) { connector.remove_filename(); --depth; } // copy file or save name of directory we may iterate into if (is_regular_file(itr->path())) { fullpath0 = itr->path(); fullpath1 = _p[1] / connector / fullpath0.filename(); if (exists(fullpath1)) { // error! errors.push_back(connector / fullpath0.filename(), fullpath0); cout << copier.status << "Warning: Cannot copy " << fullpath0 << " to " << fullpath1 << " because the latter already exists.\n"; } else { copier.copy(fullpath0, fullpath1, connector / fullpath0.filename()); } } else if (is_directory(itr->path())) { new_extension = itr->path().filename(); } ++itr; } } // cleanup _uc[0].f.clear(); _uc[0].d.clear(); _annotations &= ~A0; // print outline cout << setw(9) << totalBytes << '/' << setw(9) << totalBytes << " | "; cout << totalFiles - errors.size() << " of " << totalFiles << " files were copied.\n"; if (errors.size()) { cout << "The following files were not copied:\n"; for (unsigned i=0; i<errors.size(); ++i) { cout << errors[i] << '\n'; } } cout << '\n'; }
bool Ini_UpdaterLists::processChangedFiles(const STRING &rollbackFolder, const bool validLocalFilesForRollback) { // Ini updater can not perform rollback in retranslation mode if(m_retranslation) return false; if(!validLocalFilesForRollback) { TRACE_MESSAGE("Rollback is not created, because inconsistent bases set was locally before update operation started"); removeRollbackSection(); return false; } TRACE_MESSAGE("Saving rollback file list"); FileVector updateList; // set correct status for all entries that have changed std::string rollbackTrace; for(size_t index = 0; index < m_changedFiles.size(); ++index) { // entry copy, because size, signature, and hash tags are not valid here. // There are values not for rollback, but values for new files FileInfo entryCopy; entryCopy.m_filename = m_changedFiles[index].m_filename; entryCopy.m_localPath = m_changedFiles[index].m_localPath; entryCopy.m_rollbackChange; switch(m_changedFiles[index].m_transactionInformation.m_changeStatus) { case FileInfo::added: entryCopy.m_rollbackChange = SS_KEY_RecentStatusAdded; break; case FileInfo::deleted: entryCopy.m_rollbackChange = SS_KEY_RecentStatusDeleted; break; case FileInfo::modified: entryCopy.m_rollbackChange = SS_KEY_RecentStatusModified; break; default: // do not write to rollback settings storage unchanged files continue; } // calculate MD5 for consistency check to restore changed and deleted files if((entryCopy.m_rollbackChange != SS_KEY_RecentStatusAdded) && !calcMD5Hash(STRING(rollbackFolder + entryCopy.m_filename).to_string().c_str(), entryCopy.m_md5)) { TRACE_MESSAGE2("Rollback is not created, failed to calculate md5 for '%s'", (rollbackFolder + entryCopy.m_filename).to_string().c_str()); return false; } updateList.push_back(entryCopy); if(index != 0) rollbackTrace += ", "; if(!(index % 15)) rollbackTrace += "\n\t"; rollbackTrace += entryCopy.m_filename.to_string() + " " + entryCopy.m_rollbackChange.to_string().c_str(); } TRACE_MESSAGE2("Saving rollback files: %s", rollbackTrace.c_str()); // saving rollback information if(!saveRollbackInfo(updateList)) { TRACE_MESSAGE("Failed to save rollback file list"); return false; } TRACE_MESSAGE("Rollback file list saved successfully"); return true; }
KLUPD::CoreError KLUPD::UpdateInfo::parse(const Path &path, FileInfo &fileInfo, const ChangeStateCheck &changeStateCheck, const FileVector &localFilesToCheckAgainst, const bool suppressSuccessLogMessages) { // index is already parsed, files already attached if(fileAlreadyParsed(fileInfo)) return CORE_NO_ERROR; const Path fullFileName = path + fileInfo.m_filename; if(!suppressSuccessLogMessages) TRACE_MESSAGE2("Parsing XML index file '%S'", fullFileName.toWideChar()); std::vector<unsigned char> buffer; const CoreError readFileResult = LocalFile(fullFileName, pLog).read(buffer); if(!KLUPD::isSuccess(readFileResult)) { TRACE_MESSAGE3("Failed to read XML index file '%S', result '%S'", fullFileName.toWideChar(), KLUPD::toString(readFileResult).toWideChar()); return readFileResult; } if(buffer.empty()) { TRACE_MESSAGE3("Index XML file '%S' is corrupted, file size is %d", fullFileName.toWideChar(), buffer.size()); return CORE_UPDATE_DESCRIPTION_DAMAGED; } // a file list obtained from XML FileVector fileList; IndexFileXMLParser xmler(fileInfo.m_filename.m_value, fileList, m_signature6Checker, fileInfo.m_relativeURLPath, m_retranslationMode, m_callbacks, pLog); XmlReader xmlReader(reinterpret_cast<const char *>(&buffer[0]), buffer.size()); if(!xmler.parseXMLRecursively(xmlReader, 0) || fileList.empty()) { TRACE_MESSAGE3("\tXML file parse error '%S', file number found %d", fullFileName.toWideChar(), fileList.size()); return CORE_UPDATE_DESCRIPTION_DAMAGED; } m_parsedIndexCache.push_back(fileInfo); xmler.GetUpdateDate(fileInfo.m_strUpdateDate); xmler.GetBlackDate(fileInfo.m_strBlackDate); // save variables to write into settings storage std::vector<UpdatedSettings> updatedSettings = xmler.updatedSettings(); m_updatedSettings.insert(m_updatedSettings.end(), updatedSettings.begin(), updatedSettings.end()); // copy to match list only those items which suits download criteria for(FileVector::iterator fileIter = fileList.begin(); fileIter != fileList.end(); ++fileIter) { // check download filters criteria should be performed NoCaseString reasonNotMatch; if(!matchesSettings(reasonNotMatch, *fileIter)) { if(!suppressSuccessLogMessages) { TRACE_MESSAGE3("\tFile filtered, download criteria does not matches: '%S', %S", reasonNotMatch.toWideChar(), fileIter->toString().toWideChar()); } continue; } // check download mandatory criteria reasonNotMatch.erase(); if(!isEntryRequired(reasonNotMatch, *fileIter, m_filterFlags)) { if(!suppressSuccessLogMessages) { TRACE_MESSAGE3("\tFile filtered, mandatory criteria does not matches: '%S', %S", reasonNotMatch.toWideChar(), fileIter->toString().toWideChar()); } continue; } // setup transaction folders fileIter->m_transactionInformation.m_currentLocation = m_callbacks.productFolder(*fileIter, m_retranslationMode); fileIter->m_transactionInformation.m_newLocation = m_callbacks.temporaryFolder(*fileIter); NoCaseString statusExplanations; fileIter->m_transactionInformation.m_changeStatus = getFileStatusAgainstLocal(*fileIter, changeStateCheck, localFilesToCheckAgainst, statusExplanations); // insert file with filtering duplicates bool fileDuplicate = false; // insert black list into the beginning of list, because it should be checked before downloading other bases if(fileIter->m_type == FileInfo::blackList) m_matchFileList.insertNewInTheBeginOfListOrUpdateTheSame(*fileIter, *fileIter, fileDuplicate, m_retranslationMode); else m_matchFileList.insertNewOrUpdateTheSame(*fileIter, *fileIter, fileDuplicate, m_retranslationMode); if(!suppressSuccessLogMessages) { // to avoid empty brackets output to trace if(!statusExplanations.empty()) statusExplanations = NoCaseString(L"(") + statusExplanations + L")"; if(fileDuplicate) { TRACE_MESSAGE3("\tDuplicate file information updated: %S %S", fileIter->toString().toWideChar(), statusExplanations.toWideChar()); } else { TRACE_MESSAGE3("\tFile matches download criteria: %S %S", fileIter->toString().toWideChar(), statusExplanations.toWideChar()); } } } return CORE_NO_ERROR; }
KLUPD::FileInfo::ChangeStatus KLUPD::UpdateInfo::getFileStatusAgainstLocal(const FileInfo &etalon, const ChangeStateCheck &changeStateCheck, const FileVector &localFilesToCheckAgainst, NoCaseString &statusExplanations)const { if(changeStateCheck == noStateChangeCheck) return FileInfo::unchanged; statusExplanations.erase(); LocalFile fileInProductFolder(m_callbacks.productFolder(etalon, m_retranslationMode) + etalon.m_filename); ////////////////////////////////////////////////////////////////////////// // check if file in product folder exists if(!fileInProductFolder.exists()) { statusExplanations = L"original files does not exist"; return FileInfo::added; } // inlined file is already downloaded together with index file, can be only compared if it changed if(etalon.isInlined()) { std::vector<unsigned char> buffer; const CoreError readInlinedFileResult = fileInProductFolder.read(buffer); if(!isSuccess(readInlinedFileResult)) { TRACE_MESSAGE3("Failed to detect change state for inlined file '%S', file operation error '%S'", fileInProductFolder.m_name.toWideChar(), toString(readInlinedFileResult).toWideChar()); statusExplanations = L"inlined file can not be read"; return FileInfo::added; } if(buffer == etalon.m_inlinedContent) { statusExplanations = L"inlined file is not changed"; return FileInfo::unchanged; } else { statusExplanations = L"inlined file is not modified"; return FileInfo::modified; } } ////////////////////////////////////////////////////////////////////////// // check against signature 5 if(!etalon.m_signature5.empty()) { if(checkInsideSignature5AgainstEtalon(fileInProductFolder.m_name, etalon.m_signature5, pLog)) { statusExplanations = L"signature 5 is not changed"; return FileInfo::unchanged; } else { statusExplanations = L"signature 5 is changed"; return FileInfo::modified; } } ////////////////////////////////////////////////////////////////////////// // check file against md5 if(!etalon.m_md5.empty()) { std::vector<unsigned char> md5; if(!calcMD5Hash(fileInProductFolder.m_name, md5)) { statusExplanations = L"failed to calculate md5 hash"; return FileInfo::added; } if(md5 == etalon.m_md5) { statusExplanations = L"md5 unchanged"; return FileInfo::unchanged; } else { statusExplanations = L"md5 changed"; return FileInfo::modified; } } ////////////////////////////////////////////////////////////////////////// // check date statusExplanations += NoCaseString(L"date from index '") + etalon.m_dateFromIndex + L"', " + (localFilesToCheckAgainst.empty() ? L"local bases are not consistent" : L"local bases are consistent"); if(!etalon.m_dateFromIndex.empty() // date check is only performed if file presents in consistent local file set, // otherwise file may belong to other Update set and must be updated && !localFilesToCheckAgainst.empty()) { FileInfo::ChangeStatus modifiedIfDatesAreIgnored = FileInfo::unchanged; // check if index date is newer is only performed for Kaspersky sources if(changeStateCheck == checkStateButIgnoreDate) { modifiedIfDatesAreIgnored = FileInfo::modified; statusExplanations = L", dates are ignored for user-specified sources"; } FileInfo localConsistentFile; if(localFilesToCheckAgainst.findTheSameFile(etalon, localConsistentFile, m_retranslationMode)) { bool bIndex = etalon.isIndex(); const NoCaseString& strLocalFileDate = bIndex ? localConsistentFile.m_strUpdateDate : localConsistentFile.m_dateFromIndex; NoCaseString strFileType(bIndex ? L"index" : L"non-index"); // for trace if(!strLocalFileDate.empty()) { // if expected date on source is less, then file is skipped unsigned long nEtalonDate = updateDateStringToSequenceNumber(etalon.m_dateFromIndex); unsigned long nLocalFileDate = updateDateStringToSequenceNumber(strLocalFileDate); if(nEtalonDate < nLocalFileDate) { statusExplanations += NoCaseString(L", ") + FileInfo::toString(modifiedIfDatesAreIgnored).toWideChar() + L" " + strFileType + L" file is found in local consistent set with date '" + localConsistentFile.m_dateFromIndex + L"'"; return modifiedIfDatesAreIgnored; } else if( bIndex || nEtalonDate > nLocalFileDate) { statusExplanations += NoCaseString(L", modified ") + strFileType + L" file is found in local consistent set, date from file '" + strLocalFileDate + L"'"; return FileInfo::modified; } else statusExplanations += NoCaseString(L", ") + strFileType + L" file is found in local consistent set with same date"; } else statusExplanations += NoCaseString(L", ") + strFileType + L" file is found in local consistent set, but there is no date in index specified"; } else statusExplanations += L", file is not found in local consistent set"; } ////////////////////////////////////////////////////////////////////////// // check against signature 6 bool insideSignature = false; NoCaseString errorMessage; if(!m_signature6Checker.checkSignature(fileInProductFolder.m_name, insideSignature, errorMessage)) { statusExplanations += NoCaseString(L", signature 6 check changed (") + errorMessage + L")"; return FileInfo::modified; } // file may contain inside signature6, then there is no way to determine whether // file is different on source, and that is why must always be downloaded if(insideSignature) { statusExplanations += L", inside signature 6 check unchanged (file needs being downloaded)"; return FileInfo::modified; } else { statusExplanations += L", signature 6 check unchanged"; return FileInfo::unchanged; } }