/// <summary> /// Checks whether an implementation directory matches the expected digest. /// Throws <see cref="DigestMismatchException"/> if it does not match. /// </summary> /// <param name="path">The path of the directory ot check.</param> /// <param name="manifestDigest">The expected digest.</param> /// <param name="handler">A callback object used when the the user is to be informed about progress.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="NotSupportedException"><paramref name="manifestDigest"/> does not list any supported digests.</exception> /// <exception cref="IOException">The directory could not be processed.</exception> /// <exception cref="UnauthorizedAccessException">Read access to the directory is not permitted.</exception> /// <exception cref="DigestMismatchException">The directory does not match the expected digest</exception> public static void Verify(string path, ManifestDigest manifestDigest, ITaskHandler handler) { #region Sanity checks if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion string expectedDigest = manifestDigest.Best ?? throw new NotSupportedException(Resources.NoKnownDigestMethod); var format = ManifestFormat.FromPrefix(expectedDigest); var builder = new ManifestBuilder(format); handler.RunTask(new ReadDirectory(path, builder)); if (Verify(builder.Manifest, expectedDigest) == null) { string manifestFilePath = Path.Combine(path, Manifest.ManifestFile); var expectedManifest = File.Exists(manifestFilePath) ? Manifest.Load(manifestFilePath, format) : null; throw new DigestMismatchException( expectedDigest, actualDigest: builder.Manifest.CalculateDigest(), expectedManifest, actualManifest: builder.Manifest); } }
/// <inheritdoc/> public void Add(ManifestDigest manifestDigest, Action <IBuilder> build) { #region Sanity checks if (build == null) { throw new ArgumentNullException(nameof(build)); } #endregion if (manifestDigest.AvailableDigests.Any(digest => Directory.Exists(System.IO.Path.Combine(Path, digest)))) { throw new ImplementationAlreadyInStoreException(manifestDigest); } string expectedDigest = manifestDigest.Best ?? throw new NotSupportedException(Resources.NoKnownDigestMethod); Log.Debug($"Storing implementation {expectedDigest} in {this}"); var format = ManifestFormat.FromPrefix(manifestDigest.Best); // Place files in temp directory until digest is verified string tempDir = GetTempDir(); using var _ = new Disposable(() => DeleteTempDir(tempDir)); var builder = new ManifestBuilder(format); build(new DirectoryBuilder(tempDir, builder)); var manifest = ImplementationStoreUtils.Verify(builder.Manifest, expectedDigest); if (manifest == null) { throw new DigestMismatchException( expectedDigest, actualDigest: builder.Manifest.CalculateDigest(), actualManifest: builder.Manifest); } manifest.Save(System.IO.Path.Combine(tempDir, Manifest.ManifestFile)); string target = System.IO.Path.Combine(Path, expectedDigest); lock (_renameLock) // Prevent race-conditions when adding the same digest twice { if (Directory.Exists(target)) { throw new ImplementationAlreadyInStoreException(manifestDigest); } // Move directory to final destination try { Directory.Move(tempDir, target); } catch (IOException ex) when(ex.Message.Contains("already exists") || Directory.Exists(target)) { throw new ImplementationAlreadyInStoreException(manifestDigest); } } // Prevent any further changes to the directory if (UseWriteProtection) { EnableWriteProtection(target); } }
/// <inheritdoc/> public Digest([NotNull] ICommandHandler handler) : base(handler) { Options.Add("manifest", () => Resources.OptionManifest, _ => _printManifest = true); Options.Add("digest", () => Resources.OptionDigest, _ => _printDigest = true); Options.Add("algorithm=", () => Resources.OptionAlgorithm + Environment.NewLine + SupportedValues(ManifestFormat.All), delegate(string algorithm) { try { _algorithm = ManifestFormat.FromPrefix(algorithm); } #region Error handling catch (ArgumentException ex) { // Wrap exception since only certain exception types are allowed throw new OptionException(ex.Message, algorithm); } #endregion }); }
/// <summary> /// Creates a new implementation node. /// </summary> /// <param name="digest">The digest identifying the implementation.</param> /// <param name="store">The <see cref="IStore"/> the implementation is located in.</param> /// <exception cref="FormatException">The manifest file is not valid.</exception> /// <exception cref="IOException">The manifest file could not be read.</exception> /// <exception cref="UnauthorizedAccessException">Read access to the file is not permitted.</exception> protected ImplementationNode(ManifestDigest digest, [NotNull] IStore store) : base(store) { #region Sanity checks if (store == null) { throw new ArgumentNullException("store"); } #endregion _digest = digest; // Determine the total size of an implementation via its manifest file string path = store.GetPath(digest); if (path == null) { return; } string manifestPath = System.IO.Path.Combine(path, Manifest.ManifestFile); Size = Manifest.Load(manifestPath, ManifestFormat.FromPrefix(digest.AvailableDigests.FirstOrDefault())).TotalSize; }
public void FromPrefix() { ManifestFormat.FromPrefix("sha1new=abc").Should().BeSameAs(ManifestFormat.Sha1New); ManifestFormat.FromPrefix("sha256=abc").Should().BeSameAs(ManifestFormat.Sha256); ManifestFormat.FromPrefix("sha256new_abc").Should().BeSameAs(ManifestFormat.Sha256New); }