void CleanUp(AppliedFileStorage storage) { if (storage.Count == 0) { return; } using (var update = ZString.CreateStringBuilder(true)) { var remaining = storage.Count; update.Append("DELETE FROM `updates` WHERE `name` IN("); foreach (var item in storage) { update.Append("\""); update.Append(item.Key); update.Append("\""); if ((--remaining) > 0) { update.Append(", "); } } update.Append(")"); // Update database _apply(update.ToString()); } }
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)); }