Files
Aaru/Aaru.Tests/Issues/FsExtractHashIssueTest.cs

280 lines
12 KiB
C#
Raw Normal View History

2021-03-07 21:10:27 +00:00
using System;
using System.Collections.Generic;
using System.IO;
2021-03-07 21:10:27 +00:00
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
2021-03-07 21:10:27 +00:00
using Aaru.Checksums;
using Aaru.CommonTypes;
2021-09-16 04:42:14 +01:00
using Aaru.CommonTypes.Enums;
2021-03-07 21:10:27 +00:00
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Core;
using FluentAssertions;
using NUnit.Framework;
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
2021-03-07 21:10:27 +00:00
namespace Aaru.Tests.Issues;
2022-03-06 13:29:38 +00:00
/// <summary>This will extract (and discard data) all files in all filesystems detected in an image.</summary>
public abstract class FsExtractHashIssueTest
2021-03-07 21:10:27 +00:00
{
2022-03-06 13:29:38 +00:00
protected abstract string DataFolder { get; }
protected abstract string TestFile { get; }
protected abstract Dictionary<string, string> ParsedOptions { get; }
protected abstract bool Debug { get; }
protected abstract bool Xattrs { get; }
protected abstract string Encoding { get; }
protected abstract bool ExpectPartitions { get; }
protected abstract string Namespace { get; }
[Test]
public void Test()
2021-03-07 21:10:27 +00:00
{
2022-03-06 13:29:38 +00:00
Environment.CurrentDirectory = DataFolder;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(TestFile);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Dictionary<string, string> options = ParsedOptions;
options["debug"] = Debug.ToString();
2021-03-07 21:10:27 +00:00
Assert.IsNotNull(inputFilter, Localization.Cannot_open_specified_file);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Encoding encodingClass = null;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
if(Encoding != null)
encodingClass = Claunia.Encoding.Encoding.GetEncoding(Encoding);
2021-03-07 21:10:27 +00:00
2022-12-17 20:50:17 +00:00
PluginBase plugins = PluginBase.Singleton;
2021-03-07 21:10:27 +00:00
2022-03-07 07:36:44 +00:00
var imageFormat = ImageFormat.Detect(inputFilter) as IMediaImage;
2021-03-07 21:10:27 +00:00
Assert.NotNull(imageFormat, Localization.Image_format_not_identified_not_proceeding_with_analysis);
2021-03-07 21:10:27 +00:00
Assert.AreEqual(ErrorNumber.NoError, imageFormat.Open(inputFilter), Localization.Unable_to_open_image_format);
2021-03-07 21:10:27 +00:00
List<Partition> partitions = Core.Partitions.GetAll(imageFormat);
2022-03-06 13:29:38 +00:00
if(partitions.Count == 0)
{
Assert.IsFalse(ExpectPartitions, Localization.No_partitions_found);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
partitions.Add(new Partition
2021-03-07 21:10:27 +00:00
{
2022-03-06 13:29:38 +00:00
Description = "Whole device",
Length = imageFormat.Info.Sectors,
Offset = 0,
Size = imageFormat.Info.SectorSize * imageFormat.Info.Sectors,
Sequence = 1,
Start = 0
});
}
2021-03-07 21:10:27 +00:00
bool filesystemFound = false;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert.True(File.Exists($"{TestFile}.unittest.json"));
2021-03-07 21:10:27 +00:00
var serializerOptions = new JsonSerializerOptions
2022-03-06 13:29:38 +00:00
{
Converters =
{
new JsonStringEnumConverter()
},
MaxDepth = 1536, // More than this an we get a StackOverflowException
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true,
IncludeFields = true
2022-03-06 13:29:38 +00:00
};
var sr = new FileStream($"{TestFile}.unittest.json", FileMode.Open);
FsExtractHashData expectedData = JsonSerializer.Deserialize<FsExtractHashData>(sr, serializerOptions);
2022-03-06 13:29:38 +00:00
Assert.NotNull(expectedData);
2022-03-06 13:29:38 +00:00
Assert.AreEqual(expectedData.Partitions.Length, partitions.Count,
string.Format(Localization.Excepted_0_partitions_but_found_1, expectedData.Partitions.Length,
partitions.Count));
for(int i = 0; i < partitions.Count; i++)
2022-03-06 13:29:38 +00:00
{
Core.Filesystems.Identify(imageFormat, out List<string> idPlugins, partitions[i]);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
if(idPlugins.Count == 0)
2021-03-07 21:10:27 +00:00
{
2022-03-06 13:29:38 +00:00
Assert.IsNull(expectedData.Partitions[i],
string.Format(Localization.Expected_no_filesystems_identified_in_partition_0_but_found_1,
i, idPlugins.Count));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
continue;
}
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
if(expectedData.Partitions[i].Volumes is null)
continue;
2021-03-07 22:25:57 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(expectedData.Partitions[i].Volumes.Length, idPlugins.Count,
string.Format(Localization.Expected_0_filesystems_identified_in_partition_1_but_found_2,
expectedData.Partitions[i].Volumes.Length, i, idPlugins.Count));
2021-03-07 21:10:27 +00:00
for(int j = 0; j < idPlugins.Count; j++)
2022-03-06 13:29:38 +00:00
{
string pluginName = idPlugins[j];
2021-03-07 21:10:27 +00:00
2022-12-17 14:56:37 +00:00
if(!plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out Type pluginType))
2022-03-06 13:29:38 +00:00
continue;
2021-03-07 21:10:27 +00:00
2022-12-17 14:56:37 +00:00
Assert.IsNotNull(pluginType, Localization.Could_not_instantiate_filesystem_plugin);
2021-03-07 21:10:27 +00:00
2022-12-17 14:56:37 +00:00
var fs = Activator.CreateInstance(pluginType) as IReadOnlyFilesystem;
2021-03-07 21:10:27 +00:00
Assert.IsNotNull(fs, string.Format(Localization.Could_not_instantiate_filesystem_0, pluginName));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
filesystemFound = true;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
ErrorNumber error = fs.Mount(imageFormat, partitions[i], encodingClass, options, Namespace);
2021-03-07 21:10:27 +00:00
Assert.AreEqual(ErrorNumber.NoError, error,
string.Format(Localization.Could_not_mount_0_in_partition_1, pluginName, i));
2021-03-07 21:10:27 +00:00
Assert.AreEqual(expectedData.Partitions[i].Volumes[j].VolumeName, fs.Metadata.VolumeName,
string.
Format(Localization.Excepted_volume_name_0_for_filesystem_1_in_partition_2_but_found_3,
expectedData.Partitions[i].Volumes[j].VolumeName, j, i,
fs.Metadata.VolumeName));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
VolumeData volumeData = expectedData.Partitions[i].Volumes[j];
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
ExtractFilesInDir("/", fs, Xattrs, volumeData);
2021-03-07 21:10:27 +00:00
volumeData.Directories.Should().
BeEmpty(Localization.Expected_directories_not_found, volumeData.Directories);
volumeData.Files.Should().BeEmpty(Localization.Expected_files_not_found, volumeData.Files.Keys);
2021-03-07 21:10:27 +00:00
}
}
Assert.IsTrue(filesystemFound, Localization.No_filesystems_found);
2022-03-06 13:29:38 +00:00
}
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
static void ExtractFilesInDir(string path, IReadOnlyFilesystem fs, bool doXattrs, VolumeData volumeData)
{
if(path.StartsWith('/'))
path = path[1..];
2021-03-07 21:10:27 +00:00
ErrorNumber error = fs.OpenDir(path, out IDirNode node);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, error,
string.Format(Localization.Error_0_reading_root_directory, error.ToString()));
2021-03-07 21:10:27 +00:00
while(fs.ReadDir(node, out string entry) == ErrorNumber.NoError &&
entry is not null)
2022-03-06 13:29:38 +00:00
{
error = fs.Stat(path + "/" + entry, out FileEntryInfo stat);
2021-03-07 21:10:27 +00:00
Assert.AreEqual(ErrorNumber.NoError, error,
string.Format(Localization.Error_getting_stat_for_entry_0, entry));
2022-03-06 13:29:38 +00:00
if(stat.Attributes.HasFlag(FileAttributes.Directory))
{
if(string.IsNullOrWhiteSpace(path))
{
Assert.True(volumeData.Directories.Contains(entry),
string.Format(Localization.Found_unexpected_directory_0, entry));
2022-03-06 13:29:38 +00:00
volumeData.Directories.Remove(entry);
}
else
{
Assert.True(volumeData.Directories.Contains(path + "/" + entry),
string.Format(Localization.Found_unexpected_directory_0, path + "/" + entry));
2022-03-06 13:29:38 +00:00
volumeData.Directories.Remove(path + "/" + entry);
}
2022-03-06 13:29:38 +00:00
ExtractFilesInDir(path + "/" + entry, fs, doXattrs, volumeData);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
continue;
}
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
FileData fileData;
2022-03-06 13:29:38 +00:00
if(string.IsNullOrWhiteSpace(path))
{
Assert.IsTrue(volumeData.Files.TryGetValue(entry, out fileData),
string.Format(Localization.Found_unexpected_file_0, entry));
2022-03-06 13:29:38 +00:00
volumeData.Files.Remove(entry);
}
else
{
Assert.IsTrue(volumeData.Files.TryGetValue(path + "/" + entry, out fileData),
string.Format(Localization.Found_unexpected_file_0, path + "/" + entry));
2022-03-06 13:29:38 +00:00
volumeData.Files.Remove(path + "/" + entry);
}
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
if(doXattrs)
{
error = fs.ListXAttr(path + "/" + entry, out List<string> xattrs);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, error,
string.Format(Localization.Error_0_getting_extended_attributes_for_entry_1, error,
path + "/" + entry));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Dictionary<string, string> expectedXattrs = fileData.XattrsWithMd5;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
if(error == ErrorNumber.NoError)
foreach(string xattr in xattrs)
{
Assert.IsTrue(expectedXattrs.TryGetValue(xattr, out string expectedXattrMd5),
string.Format(Localization.Found_unexpected_extended_attribute_0_in_file_1, xattr,
entry));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
expectedXattrs.Remove(xattr);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
byte[] xattrBuf = Array.Empty<byte>();
error = fs.GetXattr(path + "/" + entry, xattr, ref xattrBuf);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, error,
string.Format(Localization.Error_0_reading_extended_attributes_for_entry_1,
error, path + "/" + entry));
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
string xattrMd5 = Md5Context.Data(xattrBuf, out _);
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(expectedXattrMd5, xattrMd5,
string.Format(Localization.Invalid_checksum_for_xattr_0_for_file_1, xattr,
path + "/" + entry));
2022-03-06 13:29:38 +00:00
}
expectedXattrs.Should().
BeEmpty(string.Format(Localization.Expected_extended_attributes_not_found_for_file_0, path + "/" + entry),
2022-03-06 13:29:38 +00:00
expectedXattrs);
2021-03-07 21:10:27 +00:00
}
2022-03-06 13:29:38 +00:00
byte[] buffer = new byte[stat.Length];
ErrorNumber ret = fs.OpenFile(path + "/" + entry, out IFileNode fileNode);
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, ret,
string.Format(Localization.Error_0_reading_file_1, ret, path + "/" + entry));
2022-03-06 13:29:38 +00:00
ret = fs.ReadFile(fileNode, stat.Length, buffer, out long readBytes);
Assert.AreEqual(ErrorNumber.NoError, ret,
string.Format(Localization.Error_0_reading_file_1, ret, path + "/" + entry));
Assert.AreEqual(stat.Length, readBytes,
string.Format(Localization.Error_0_reading_file_1, readBytes, stat.Length,
path + "/" + entry));
fs.CloseFile(fileNode);
2022-03-06 13:29:38 +00:00
string calculatedMd5 = Md5Context.Data(buffer, out _);
2022-03-06 13:29:38 +00:00
Assert.AreEqual(fileData.Md5, calculatedMd5,
string.Format(Localization.Invalid_checksum_for_file_0, path + "/" + entry));
2021-03-07 21:10:27 +00:00
}
fs.CloseDir(node);
2021-03-07 21:10:27 +00:00
}
}