2021-06-08 14:56:45 -06:00
|
|
|
using System.Collections.Generic;
|
2020-04-03 01:08:52 +11:00
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2021-07-09 14:50:37 -06:00
|
|
|
using System.Security.Cryptography;
|
2021-02-18 11:06:02 +01:00
|
|
|
using Umbraco.Cms.Core.Logging;
|
2021-07-09 14:50:37 -06:00
|
|
|
using Umbraco.Extensions;
|
2020-04-03 01:08:52 +11:00
|
|
|
|
2021-02-18 11:06:02 +01:00
|
|
|
namespace Umbraco.Cms.Core.Composing
|
2020-04-03 01:08:52 +11:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines the runtime hash based on file system paths to scan
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class RuntimeHash : IRuntimeHash
|
|
|
|
|
{
|
|
|
|
|
private readonly IProfilingLogger _logger;
|
|
|
|
|
private readonly RuntimeHashPaths _paths;
|
2021-12-16 13:44:20 +01:00
|
|
|
private string? _calculated;
|
2020-04-03 01:08:52 +11:00
|
|
|
|
|
|
|
|
public RuntimeHash(IProfilingLogger logger, RuntimeHashPaths paths)
|
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_paths = paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string GetHashValue()
|
|
|
|
|
{
|
2021-07-09 14:50:37 -06:00
|
|
|
if (_calculated != null)
|
|
|
|
|
{
|
|
|
|
|
return _calculated;
|
|
|
|
|
}
|
2020-04-03 01:08:52 +11:00
|
|
|
|
2021-07-09 14:50:37 -06:00
|
|
|
IEnumerable<(FileSystemInfo, bool)> allPaths = _paths.GetFolders()
|
|
|
|
|
.Select(x => ((FileSystemInfo)x, false))
|
|
|
|
|
.Concat(_paths.GetFiles().Select(x => ((FileSystemInfo)x.Key, x.Value)));
|
2020-04-03 01:08:52 +11:00
|
|
|
|
2021-07-09 14:50:37 -06:00
|
|
|
_calculated = GetFileHash(allPaths);
|
|
|
|
|
|
|
|
|
|
return _calculated;
|
2020-04-03 01:08:52 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a unique hash for a combination of FileInfo objects.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filesAndFolders">A collection of files.</param>
|
|
|
|
|
/// <returns>The hash.</returns>
|
|
|
|
|
/// <remarks>Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the
|
|
|
|
|
/// file properties (false) or the file contents (true).</remarks>
|
|
|
|
|
private string GetFileHash(IEnumerable<(FileSystemInfo fileOrFolder, bool scanFileContent)> filesAndFolders)
|
|
|
|
|
{
|
2021-06-08 14:56:45 -06:00
|
|
|
using (_logger.DebugDuration<RuntimeHash>("Determining hash of code files on disk", "Hash determined"))
|
2020-04-03 01:08:52 +11:00
|
|
|
{
|
|
|
|
|
// get the distinct file infos to hash
|
|
|
|
|
var uniqInfos = new HashSet<string>();
|
|
|
|
|
var uniqContent = new HashSet<string>();
|
|
|
|
|
|
|
|
|
|
using var generator = new HashGenerator();
|
|
|
|
|
|
2021-07-09 14:50:37 -06:00
|
|
|
foreach ((FileSystemInfo fileOrFolder, bool scanFileContent) in filesAndFolders)
|
2020-04-03 01:08:52 +11:00
|
|
|
{
|
|
|
|
|
if (scanFileContent)
|
|
|
|
|
{
|
|
|
|
|
// add each unique file's contents to the hash
|
|
|
|
|
// normalize the content for cr/lf and case-sensitivity
|
|
|
|
|
if (uniqContent.Add(fileOrFolder.FullName))
|
|
|
|
|
{
|
2021-07-09 14:50:37 -06:00
|
|
|
if (File.Exists(fileOrFolder.FullName) == false)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (FileStream fileStream = File.OpenRead(fileOrFolder.FullName))
|
|
|
|
|
{
|
|
|
|
|
var hash = fileStream.GetStreamHash();
|
|
|
|
|
generator.AddCaseInsensitiveString(hash);
|
|
|
|
|
}
|
2020-04-03 01:08:52 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// add each unique folder/file to the hash
|
|
|
|
|
if (uniqInfos.Add(fileOrFolder.FullName))
|
|
|
|
|
{
|
|
|
|
|
generator.AddFileSystemItem(fileOrFolder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return generator.GenerateHash();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|