void UpdateEntry(ref AppliedFileEntry entry, uint speed = 0) { var update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" + entry.Name + "\", \"" + entry.Hash + "\", \'" + entry.State.ToString() + "\', " + speed.ToString() + ")"; // Update database _apply(update); }
AppliedFileStorage ReceiveAppliedFiles() { var map = new Dictionary <string, AppliedFileEntry>(); var result = _retrieve("SELECT `name`, `hash`, `state`, UNIX_TIMESTAMP(`timestamp`) FROM `updates` ORDER BY `name` ASC"); if (result == null) { return(map); } do { var field = result.Fetch(); var entry = new AppliedFileEntry(field[0].GetString(), field[1].GetString(), field[2].GetString().ToEnum <State>(), field[3].GetUInt32()); map.Add(entry.Name, entry); }while (result.NextRow()); result.Dispose(); return(map); }
public UpdateResult Update(bool redundancyChecks, bool allowRehash, bool archivedRedundancy, int cleanDeadReferencesMaxCount) { var available = GetFileList(); AppliedFileStorage applied = ReceiveAppliedFiles(); int countRecentUpdates = 0; int countArchivedUpdates = 0; // Count updates foreach (var entry in applied.Values) { if (entry.State == State.RELEASED) { ++countRecentUpdates; } else { ++countArchivedUpdates; } } // Fill hash to name cache HashToFileNameStorage hashToName = new HashToFileNameStorage(); foreach (var entry in applied) { hashToName[entry.Value.Hash] = entry.Key; } int importedUpdates = 0; foreach (var availableQuery in available) { var filename = availableQuery.GetFileName(); FEL_LOG_DEBUG("sql.updates", "Checking update \"{0}\"...", filename); AppliedFileEntry appliedFile; bool filenameApplied = applied.TryGetValue(filename, out appliedFile); if (filenameApplied) { // If redundancy is disabled, skip it, because the update is already applied. if (!redundancyChecks) { FEL_LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks."); applied.Remove(filename); continue; } // If the update is in an archived directory and is marked as archived in our database, skip redundancy checks (archived updates never change). if (!archivedRedundancy && (appliedFile.State == State.ARCHIVED) && (availableQuery.State == State.ARCHIVED)) { FEL_LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks."); applied.Remove(filename); continue; } } // Calculate a Sha1 hash based on query content. string hash = ByteArrayToHexStr(DigestSHA1(ReadSQLUpdate(availableQuery.FilePath))); UpdateMode mode = UpdateMode.MODE_APPLY; // Update is not in our applied list if (!filenameApplied) { // Catch renames (different filename, but same hash) if (hashToName.TryGetValue(hash, out filename)) { // Check if the original file was removed. If not, we've got a problem. LocaleFileEntry?renameFile = null; foreach (var item in available) { if (item.GetFileName() == filename) { renameFile = item; break; } } // Conflict! if (renameFile != null) { FEL_LOG_WARN("sql.updates", ">> It seems like the update \"{0}\" \'{1}\' was renamed, but the old file is still there! Treating it as a new file! (It is probably an unmodified copy of the file \"{2}\")", availableQuery.GetFileName(), hash.Substring(0, 7), renameFile.Value.GetFileName()); } // It is safe to treat the file as renamed here else { FEL_LOG_INFO("sql.updates", ">> Renaming update \"{0}\" to \"{1}\" \'{2}\'.", filename, availableQuery.GetFileName(), hash.Substring(0, 7)); RenameEntry(filename, availableQuery.GetFileName()); applied.Remove(filename); continue; } } // Apply the update if it was never seen before. else { FEL_LOG_INFO("sql.updates", ">> Applying update \"{0}\" \'{1}\'...", availableQuery.GetFileName(), hash.Substring(0, 7)); } } // Rehash the update entry if it exists in our database with an empty hash. else if (allowRehash && string.IsNullOrEmpty(appliedFile.Hash)) { mode = UpdateMode.MODE_REHASH; FEL_LOG_INFO("sql.updates", ">> Re-hashing update \"{0}\" \'{1}\'...", availableQuery.GetFileName(), hash.Substring(0, 7)); } else { // If the hash of the files differs from the one stored in our database, reapply the update (because it changed). if (appliedFile.Hash != hash) { FEL_LOG_INFO("sql.updates", ">> Reapplying update \"{0}\" \'{1}\' -> \'{2}\' (it changed)...", availableQuery.GetFileName(), appliedFile.Hash.Substring(0, 7), hash.Substring(0, 7)); } else { // If the file wasn't changed and just moved, update its state (if necessary). if (appliedFile.State != availableQuery.State) { FEL_LOG_DEBUG("sql.updates", ">> Updating the state of \"{0}\" to \'{1}\'...", availableQuery.GetFileName(), availableQuery.State); UpdateState(availableQuery.GetFileName(), availableQuery.State); } FEL_LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'{0}\'.", hash.Substring(0, 7)); applied.Remove(appliedFile.Name); continue; } } uint speed = 0; var file = new AppliedFileEntry(availableQuery.GetFileName(), hash, availableQuery.State, 0); switch (mode) { case UpdateMode.MODE_APPLY: speed = Apply(availableQuery.FilePath); goto case UpdateMode.MODE_REHASH; case UpdateMode.MODE_REHASH: UpdateEntry(ref file, speed); break; } if (filenameApplied) { applied.Remove(appliedFile.Name); } if (mode == UpdateMode.MODE_APPLY) { ++importedUpdates; } } // Cleanup up orphaned entries (if enabled) if (applied.Count > 0) { bool doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.Count <= cleanDeadReferencesMaxCount); foreach (var entry in applied) { FEL_LOG_WARN("sql.updates", ">> The file \'{0}\' was applied to the database, but is missing in your update directory now!", entry.Key); if (doCleanup) { FEL_LOG_INFO("sql.updates", "Deleting orphaned entry \'{0}\'...", entry.Key); } } if (doCleanup) { CleanUp(applied); } else { FEL_LOG_ERROR("sql.updates", "Cleanup is disabled! There were {0} dirty files applied to your database, but they are now missing in your source directory!", applied.Count); } } return(new UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates)); }