/// <summary> /// Reads the existing backup manifests matching the given source directory. /// </summary> /// <remarks> /// Manifests are matched by comparing their source directories to <paramref name="sourcePath"/>. <br/> /// The comparison ignores case, so this only works on Windows. <br/> /// The comparison only considers the literal path, e.g. symbolic links are not resolved. /// </remarks> /// <param name="sourcePath">The source directory to match.</param> /// <param name="targetPath">The target directory which is being examined.</param> /// <param name="index">The backup index detailing all the existing backups in <paramref name="targetPath"/>. /// If <c>null</c>, no manifests are matched.</param> /// <returns>A list of the matched backups, in unspecified order.</returns> private List <BackupMetadata> ReadPreviousBackups(string sourcePath, string targetPath, BackupIndex?index) { if (index is null) { return(new()); } List <BackupMetadata> previousBackups = new(); foreach (var pair in index.Backups) { var backupName = pair.Key; var backupSourcePath = pair.Value; // Paths are assumed to be already normalised. if (Utility.PathEqual(sourcePath, backupSourcePath)) { var backupPath = Path.Join(targetPath, backupName); var startInfoFilePath = BackupMeta.StartInfoFilePath(backupPath); BackupStartInfo startInfo; try { startInfo = BackupStartInfoReader.Read(startInfoFilePath); } catch (BackupStartInfoFileException e) { Logger.Warning($"Failed to read metadata of previous backup \"{backupName}\": {e.Message}"); continue; } // We could just assume the index file and start info file are consistent, but it might be a // good idea to check just in case something goes particularly wrong. if (!Utility.PathEqual(sourcePath, startInfo.SourcePath)) { Logger.Warning( $"Source directory of backup start info in previous backup \"{backupName}\" doesn't match backup index"); continue; } var manifestFilePath = BackupMeta.ManifestFilePath(backupPath); BackupManifest manifest; try { manifest = BackupManifestReader.Read(manifestFilePath); } catch (BackupManifestFileException e) { Logger.Warning($"Failed to read metadata of previous backup \"{backupName}\": {e.Message}"); continue; } previousBackups.Add(new(backupName, startInfo, manifest)); } } Logger.Info( $"{previousBackups.Count} previous backups found in target directory for this source directory"); return(previousBackups); }
/// <summary> /// Creates the backup directory, the log file, the start info file, and the manifest writer. /// </summary> /// <param name="sourcePath">The path of the backup source directory.</param> /// <param name="targetPath">The path of the backup target directory.</param> /// <returns>A tuple of the backup directory name and manifest writer.</returns> /// <exception cref="ApplicationRuntimeError">If the backup directory, start info file, or manifest writer /// can't be created.</exception> public (string, BackupManifestWriter) InitialiseBackup(string sourcePath, string targetPath) { string backupName; try { backupName = BackupMeta.CreateBackupDirectory(targetPath); } catch (BackupDirectoryCreateException) { throw new ApplicationRuntimeError("Failed to create new backup directory"); } Logger.Info($"Backup name: {backupName}"); var backupPath = BackupMeta.BackupPath(targetPath, backupName); Logger.Info($"Created backup directory \"{backupPath}\""); var logFilePath = BackupMeta.LogFilePath(backupPath); try { Logger.FileHandler = new(logFilePath); Logger.Info($"Created log file \"{logFilePath}\""); } catch (LoggingException e) { // Not much we can do if we can't create the log file, just ignore and continue. Logger.Warning(e.Message); } var manifestFilePath = BackupMeta.ManifestFilePath(backupPath); BackupManifestWriter manifestWriter; try { manifestWriter = new(manifestFilePath); } catch (BackupManifestFileIOException e) { throw new ApplicationRuntimeError( $"Failed to create backup manifest file \"{manifestFilePath}\": {e.InnerException.Reason}"); } Logger.Info($"Created backup manifest file \"{manifestFilePath}\""); BackupStartInfo startInfo = new(sourcePath, DateTime.UtcNow); var startInfoFilePath = BackupMeta.StartInfoFilePath(backupPath); try { BackupStartInfoWriter.Write(startInfoFilePath, startInfo); } catch (BackupStartInfoFileIOException e) { throw new ApplicationRuntimeError( $"Failed to write backup start info file \"{startInfoFilePath}\": {e.InnerException.Reason}"); } Logger.Info($"Created backup start info file \"{startInfoFilePath}\""); return(backupName, manifestWriter); }