in Core/src/Impl/Commands/StorageManager.cs [184:285]
public async Task<(Statistics statistics, long deleted)> ValidateAndFixAsync(
int degreeOfParallelism,
IEnumerable<TagFileData> items,
IEnumerable<SymbolStoragePath> files,
StorageFormat storageFormat,
ValidateMode mode,
bool verifyAcl = false)
{
myLogger.Info($"[{DateTime.Now:s}] Validating storage{myId}...");
var fix = mode == ValidateMode.Fix || mode == ValidateMode.Delete;
var statistics = new Statistics();
ILogger logger = new LoggerWithStatistics(myLogger, statistics);
files = await ValidateAndFixDataFilesAsync(logger, degreeOfParallelism, files, storageFormat, fix);
var tree = CreateDirectoryTree(degreeOfParallelism, files);
await items.ParallelForAsync(degreeOfParallelism, async item =>
{
var tagFile = item.TagFile;
var tag = item.Tag;
logger.Verbose($" Validating {tagFile}");
var isDirty = false;
if (!TagUtil.ValidateProduct(tag.Product))
logger.Error($"Invalid product {tag.Product} in file {tagFile}");
if (!TagUtil.ValidateVersion(tag.Version))
logger.Error($"Invalid version {tag.Version} in file {tagFile}");
if (tag.CreationUtcTime == DateTime.MinValue)
{
logger.Error($"The empty creation time in {tagFile}");
if (fix)
{
var newCreationUtcTime = TryFixCreationTime(tag);
if (newCreationUtcTime != null)
{
logger.Fix($"The creation time will be assigned to tag {tagFile}");
isDirty = true;
tag.CreationUtcTime = newCreationUtcTime.Value;
}
}
}
if (tag.Directories.Length == 0)
{
logger.Error($"The empty directory list in {tagFile}");
if (fix)
{
logger.Fix($"The tag will be deleted {tagFile}");
await myStorage.DeleteAsync(tagFile);
return;
}
}
else
{
for (var index = 0; index < tag.Directories.Length; index++)
{
var dir = tag.Directories[index];
switch (dir.ValidateAndFixDataPath(StorageFormat.Normal, out var fixedDir))
{
case PathUtil.ValidateAndFixErrors.Ok:
break;
case PathUtil.ValidateAndFixErrors.Error:
logger.Error($"The tag directory {dir} from {tagFile} has invalid format");
break;
case PathUtil.ValidateAndFixErrors.CanBeFixed:
logger.Error($"The tag directory {dir} from {tagFile} has invalid format");
if (fix)
{
isDirty = true;
tag.Directories[index] = fixedDir;
}
break;
default:
throw new ArgumentException("Unknown ValidateAndFixErrors value");
}
var dstDir = dir.ValidateAndFixDataPath(storageFormat, out fixedDir) == PathUtil.ValidateAndFixErrors.CanBeFixed ? fixedDir : dir;
var node = tree.LookupPathRecursive(dstDir);
if (node == null)
logger.Error($"The directory {dir} from {tagFile} id wasn't found");
else
node.IncrementReferences();
}
}
if (isDirty)
{
logger.Info($"The tag file {tagFile} will be overwritten");
using var stream = new MemoryStream();
await TagUtil.WriteTagScriptAsync(tag, stream);
await myStorage.CreateForWritingAsync(tagFile, AccessMode.Private, stream);
}
});
var deleted = await ValidateAndDeleteUnreachableAsync(logger, tree, mode);
if (verifyAcl)
await ValidateAndFixAclAsync(logger, degreeOfParallelism, files, fix);
return (statistics, deleted);
}