2022-03-07 07:36:44 +00:00
namespace Aaru.Tests.Issues ;
2021-03-07 21:10:27 +00:00
using System ;
using System.Collections.Generic ;
2021-06-02 18:20:21 +01:00
using System.IO ;
2021-03-07 21:10:27 +00:00
using System.Text ;
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 ;
2021-06-02 18:20:21 +01:00
using Newtonsoft.Json ;
using Newtonsoft.Json.Converters ;
2021-03-07 21:10:27 +00:00
using NUnit.Framework ;
2021-06-02 18:20:21 +01:00
using FileAttributes = Aaru . CommonTypes . Structs . FileAttributes ;
2021-03-07 21:10:27 +00:00
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
2022-03-06 13:29:38 +00:00
Assert . IsNotNull ( inputFilter , "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-03-06 13:29:38 +00:00
PluginBase plugins = GetPluginBase . Instance ;
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
2022-03-06 13:29:38 +00:00
Assert . NotNull ( imageFormat , "Image format not identified, not proceeding with analysis." ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( ErrorNumber . NoError , imageFormat . Open ( inputFilter ) , "Unable to open image format" ) ;
2021-03-07 21:10:27 +00:00
2022-03-07 07:36:44 +00:00
List < Partition > partitions = Partitions . GetAll ( imageFormat ) ;
2022-03-06 13:29:38 +00:00
if ( partitions . Count = = 0 )
{
Assert . IsFalse ( ExpectPartitions , "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
2022-03-07 07:36:44 +00:00
var 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
2022-03-06 13:29:38 +00:00
var serializer = new JsonSerializer
{
Formatting = Formatting . Indented ,
MaxDepth = 16384 ,
NullValueHandling = NullValueHandling . Ignore
} ;
2021-06-02 18:20:21 +01:00
2022-03-06 13:29:38 +00:00
serializer . Converters . Add ( new StringEnumConverter ( ) ) ;
2021-06-02 18:20:21 +01:00
2022-03-06 13:29:38 +00:00
var sr = new StreamReader ( $"{TestFile}.unittest.json" ) ;
FsExtractHashData expectedData = serializer . Deserialize < FsExtractHashData > ( new JsonTextReader ( sr ) ) ;
2021-06-02 18:20:21 +01:00
2022-03-06 13:29:38 +00:00
Assert . NotNull ( expectedData ) ;
2021-06-02 18:20:21 +01:00
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( expectedData . Partitions . Length , partitions . Count ,
$"Excepted {expectedData.Partitions.Length} partitions but found {partitions.Count}" ) ;
2021-06-02 18:20:21 +01:00
2022-03-07 07:36:44 +00:00
for ( var i = 0 ; i < partitions . Count ; i + + )
2022-03-06 13:29:38 +00:00
{
2022-03-07 07:36:44 +00:00
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 ] ,
$"Expected no filesystems identified in partition {i} but found {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 ,
$"Expected {expectedData.Partitions[i].Volumes.Length} filesystems identified in partition {i} but found {idPlugins.Count}" ) ;
2021-03-07 21:10:27 +00:00
2022-03-07 07:36:44 +00:00
for ( var 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-03-06 13:29:38 +00:00
if ( ! plugins . ReadOnlyFilesystems . TryGetValue ( pluginName , out IReadOnlyFilesystem plugin ) )
continue ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . IsNotNull ( plugin , "Could not instantiate filesystem plugin" ) ;
2021-03-07 21:10:27 +00:00
2022-03-16 23:43:36 +00:00
var fs = ( IReadOnlyFilesystem ) plugin . GetType ( ) . GetConstructor ( Type . EmptyTypes ) ? . Invoke ( Array . Empty < object > ( ) ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . IsNotNull ( fs , $"Could not instantiate filesystem {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
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( ErrorNumber . NoError , error , $"Could not mount {pluginName} in partition {i}." ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( expectedData . Partitions [ i ] . Volumes [ j ] . VolumeName , fs . XmlFsType . VolumeName ,
$"Excepted volume name \" { expectedData . Partitions [ i ] . Volumes [ j ] . VolumeName } \ " for filesystem {j} in partition {i} but found \"{fs.XmlFsType.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
2022-03-06 13:29:38 +00:00
volumeData . Directories . Should ( ) . BeEmpty ( "Expected directories not found:" , volumeData . Directories ) ;
volumeData . Files . Should ( ) . BeEmpty ( "Expected files not found:" , volumeData . Files . Keys ) ;
2021-03-07 21:10:27 +00:00
}
}
2022-03-06 13:29:38 +00:00
Assert . IsTrue ( filesystemFound , "No filesystems found." ) ;
}
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
2022-03-06 13:29:38 +00:00
ErrorNumber error = fs . ReadDir ( path , out List < string > directory ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( ErrorNumber . NoError , error ,
string . Format ( "Error {0} reading root directory {0}" , error . ToString ( ) ) ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
foreach ( string entry in directory )
{
error = fs . Stat ( path + "/" + entry , out FileEntryInfo stat ) ;
2021-03-07 21:10:27 +00:00
2022-03-06 13:29:38 +00:00
Assert . AreEqual ( ErrorNumber . NoError , error , $"Error getting stat for entry {entry}" ) ;
2021-03-07 23:04:58 +00:00
2022-03-06 13:29:38 +00:00
if ( stat . Attributes . HasFlag ( FileAttributes . Directory ) )
{
2021-03-07 23:04:58 +00:00
if ( string . IsNullOrWhiteSpace ( path ) )
{
2022-03-06 13:29:38 +00:00
Assert . True ( volumeData . Directories . Contains ( entry ) , $"Found unexpected directory {entry}" ) ;
volumeData . Directories . Remove ( entry ) ;
2021-03-07 23:04:58 +00:00
}
else
{
2022-03-06 13:29:38 +00:00
Assert . True ( volumeData . Directories . Contains ( path + "/" + entry ) ,
$"Found unexpected directory {path + " / " + entry}" ) ;
2021-03-07 23:04:58 +00:00
2022-03-06 13:29:38 +00:00
volumeData . Directories . Remove ( path + "/" + entry ) ;
2021-03-07 23:04:58 +00:00
}
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 ;
2021-03-07 23:04:58 +00:00
2022-03-06 13:29:38 +00:00
if ( string . IsNullOrWhiteSpace ( path ) )
{
Assert . IsTrue ( volumeData . Files . TryGetValue ( entry , out fileData ) , $"Found unexpected file {entry}" ) ;
2021-03-07 23:04:58 +00:00
2022-03-06 13:29:38 +00:00
volumeData . Files . Remove ( entry ) ;
}
else
{
Assert . IsTrue ( volumeData . Files . TryGetValue ( path + "/" + entry , out fileData ) ,
$"Found unexpected file {path + " / " + entry}" ) ;
2021-03-07 23:04:58 +00:00
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 ,
$"Error {error} getting extended attributes for entry {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 ) ,
$"Found unexpected extended attribute {xattr} in file {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 ,
$"Error {error} reading extended attributes for entry {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 ,
$"Invalid checksum for xattr {xattr} for file {path + " / " + entry}" ) ;
}
expectedXattrs . Should ( ) .
BeEmpty ( $"Expected extended attributes not found for file {path + " / " + entry}:" ,
expectedXattrs ) ;
2021-03-07 21:10:27 +00:00
}
2022-03-06 13:29:38 +00:00
byte [ ] outBuf = Array . Empty < byte > ( ) ;
error = fs . Read ( path + "/" + entry , 0 , stat . Length , ref outBuf ) ;
Assert . AreEqual ( ErrorNumber . NoError , error , $"Error {error} reading file {path + " / " + entry}" ) ;
string calculatedMd5 = Md5Context . Data ( outBuf , out _ ) ;
2022-03-15 01:37:37 +00:00
Assert . AreEqual ( fileData . Md5 , calculatedMd5 , $"Invalid checksum for file {path + " / " + entry}" ) ;
2021-03-07 21:10:27 +00:00
}
}
}