diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
index 86f905480..80a5f44a8 100644
--- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml
+++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
@@ -285,6 +285,7 @@
+
diff --git a/DiscImageChef.Core/DiscImageChef.Core.csproj b/DiscImageChef.Core/DiscImageChef.Core.csproj
index 1a59f56de..e7e77b297 100644
--- a/DiscImageChef.Core/DiscImageChef.Core.csproj
+++ b/DiscImageChef.Core/DiscImageChef.Core.csproj
@@ -66,6 +66,7 @@
+
diff --git a/DiscImageChef.Core/Sidecar/BlockMedia.cs b/DiscImageChef.Core/Sidecar/BlockMedia.cs
index cf46f624c..ec555c4e7 100644
--- a/DiscImageChef.Core/Sidecar/BlockMedia.cs
+++ b/DiscImageChef.Core/Sidecar/BlockMedia.cs
@@ -544,7 +544,17 @@ namespace DiscImageChef.Core
if(!plugin.Identify(image, partitions[i])) continue;
- plugin.GetInformation(image, partitions[i], out _, encoding);
+ if(plugin is IReadOnlyFilesystem fsPlugin &&
+ fsPlugin.Mount(image, partitions[i], encoding, null, null) == Errno.NoError)
+ {
+ UpdateStatus($"Mounting {fsPlugin.XmlFsType.Type}");
+
+ fsPlugin.XmlFsType.Contents = Files(fsPlugin);
+
+ fsPlugin.Unmount();
+ }
+ else plugin.GetInformation(image, partitions[i], out _, encoding);
+
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
@@ -581,7 +591,17 @@ namespace DiscImageChef.Core
if(!plugin.Identify(image, wholePart)) continue;
- plugin.GetInformation(image, wholePart, out _, encoding);
+ if(plugin is IReadOnlyFilesystem fsPlugin &&
+ fsPlugin.Mount(image, wholePart, encoding, null, null) == Errno.NoError)
+ {
+ UpdateStatus($"Mounting {fsPlugin.XmlFsType.Type}");
+
+ fsPlugin.XmlFsType.Contents = Files(fsPlugin);
+
+ fsPlugin.Unmount();
+ }
+ else plugin.GetInformation(image, wholePart, out _, encoding);
+
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
diff --git a/DiscImageChef.Core/Sidecar/Files.cs b/DiscImageChef.Core/Sidecar/Files.cs
new file mode 100644
index 000000000..106b5b20a
--- /dev/null
+++ b/DiscImageChef.Core/Sidecar/Files.cs
@@ -0,0 +1,273 @@
+using System.Collections.Generic;
+using System.Linq;
+using DiscImageChef.CommonTypes.Interfaces;
+using DiscImageChef.CommonTypes.Structs;
+using DiscImageChef.Console;
+using Schemas;
+
+namespace DiscImageChef.Core
+{
+ public partial class Sidecar
+ {
+ FilesystemContentsType Files(IReadOnlyFilesystem filesystem)
+ {
+ FilesystemContentsType contents = new FilesystemContentsType();
+
+ Errno ret = filesystem.ReadDir("/", out List dirents);
+
+ if(ret != Errno.NoError) return null;
+
+ List directories = new List();
+ List files = new List();
+
+ foreach(string dirent in dirents)
+ {
+ ret = filesystem.Stat(dirent, out FileEntryInfo stat);
+
+ if(ret != Errno.NoError)
+ {
+ DicConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent);
+ continue;
+ }
+
+ if(stat.Attributes.HasFlag(FileAttributes.Directory))
+ {
+ directories.Add(SidecarDirectory(filesystem, "", dirent, stat));
+ continue;
+ }
+
+ files.Add(SidecarFile(filesystem, "", dirent, stat));
+ }
+
+ if(files.Count > 0) contents.File = files.OrderBy(f => f.name).ToArray();
+ if(directories.Count > 0) contents.Directory = directories.OrderBy(d => d.name).ToArray();
+
+ return contents;
+ }
+
+ DirectoryType SidecarDirectory(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat)
+ {
+ DirectoryType directory = new DirectoryType();
+
+ if(stat.AccessTimeUtc.HasValue)
+ {
+ directory.accessTime = stat.AccessTimeUtc.Value;
+ directory.accessTimeSpecified = true;
+ }
+
+ directory.attributes = (ulong)stat.Attributes;
+
+ if(stat.BackupTimeUtc.HasValue)
+ {
+ directory.backupTime = stat.BackupTimeUtc.Value;
+ directory.backupTimeSpecified = true;
+ }
+
+ if(stat.CreationTimeUtc.HasValue)
+ {
+ directory.creationTime = stat.CreationTimeUtc.Value;
+ directory.creationTimeSpecified = true;
+ }
+
+ if(stat.DeviceNo.HasValue)
+ {
+ directory.deviceNumber = stat.DeviceNo.Value;
+ directory.deviceNumberSpecified = true;
+ }
+
+ directory.inode = stat.Inode;
+
+ if(stat.LastWriteTimeUtc.HasValue)
+ {
+ directory.lastWriteTime = stat.LastWriteTimeUtc.Value;
+ directory.lastWriteTimeSpecified = true;
+ }
+
+ directory.links = stat.Links;
+ directory.name = filename;
+
+ if(stat.GID.HasValue)
+ {
+ directory.posixGroupId = stat.GID.Value;
+ directory.posixGroupIdSpecified = true;
+ }
+
+ if(stat.Mode.HasValue)
+ {
+ directory.posixMode = stat.Mode.Value;
+ directory.posixModeSpecified = true;
+ }
+
+ if(stat.UID.HasValue)
+ {
+ directory.posixUserId = stat.UID.Value;
+ directory.posixUserIdSpecified = true;
+ }
+
+ if(stat.StatusChangeTimeUtc.HasValue)
+ {
+ directory.statusChangeTime = stat.StatusChangeTimeUtc.Value;
+ directory.statusChangeTimeSpecified = true;
+ }
+
+ Errno ret = filesystem.ReadDir(path + "/" + filename, out List dirents);
+
+ if(ret != Errno.NoError) return null;
+
+ List directories = new List();
+ List files = new List();
+
+ foreach(string dirent in dirents)
+ {
+ ret = filesystem.Stat(path + "/" + filename + "/" + dirent, out FileEntryInfo entryStat);
+
+ if(ret != Errno.NoError)
+ {
+ DicConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent);
+ continue;
+ }
+
+ if(entryStat.Attributes.HasFlag(FileAttributes.Directory))
+ {
+ directories.Add(SidecarDirectory(filesystem, path + "/" + filename, dirent, entryStat));
+ continue;
+ }
+
+ files.Add(SidecarFile(filesystem, path + "/" + filename, dirent, entryStat));
+ }
+
+ if(files.Count > 0) directory.File = files.OrderBy(f => f.name).ToArray();
+ if(directories.Count > 0) directory.Directory = directories.OrderBy(d => d.name).ToArray();
+
+ return directory;
+ }
+
+ ContentsFileType SidecarFile(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat)
+ {
+ ContentsFileType file = new ContentsFileType();
+ Checksum fileChkWorker = new Checksum();
+
+ if(stat.AccessTimeUtc.HasValue)
+ {
+ file.accessTime = stat.AccessTimeUtc.Value;
+ file.accessTimeSpecified = true;
+ }
+
+ file.attributes = (ulong)stat.Attributes;
+
+ if(stat.BackupTimeUtc.HasValue)
+ {
+ file.backupTime = stat.BackupTimeUtc.Value;
+ file.backupTimeSpecified = true;
+ }
+
+ if(stat.CreationTimeUtc.HasValue)
+ {
+ file.creationTime = stat.CreationTimeUtc.Value;
+ file.creationTimeSpecified = true;
+ }
+
+ if(stat.DeviceNo.HasValue)
+ {
+ file.deviceNumber = stat.DeviceNo.Value;
+ file.deviceNumberSpecified = true;
+ }
+
+ file.inode = stat.Inode;
+
+ if(stat.LastWriteTimeUtc.HasValue)
+ {
+ file.lastWriteTime = stat.LastWriteTimeUtc.Value;
+ file.lastWriteTimeSpecified = true;
+ }
+
+ file.length = (ulong)stat.Length;
+ file.links = stat.Links;
+ file.name = filename;
+
+ if(stat.GID.HasValue)
+ {
+ file.posixGroupId = stat.GID.Value;
+ file.posixGroupIdSpecified = true;
+ }
+
+ if(stat.Mode.HasValue)
+ {
+ file.posixMode = stat.Mode.Value;
+ file.posixModeSpecified = true;
+ }
+
+ if(stat.UID.HasValue)
+ {
+ file.posixUserId = stat.UID.Value;
+ file.posixUserIdSpecified = true;
+ }
+
+ if(stat.StatusChangeTimeUtc.HasValue)
+ {
+ file.statusChangeTime = stat.StatusChangeTimeUtc.Value;
+ file.statusChangeTimeSpecified = true;
+ }
+
+ byte[] data = new byte[0];
+ if(stat.Length > 0)
+ {
+ long position = 0;
+ UpdateStatus($"Hashing file {path}/{filename}...");
+ InitProgress2();
+ while(position < stat.Length - 1048576)
+ {
+ if(aborted) return file;
+
+ data = new byte[1048576];
+ filesystem.Read(path + "/" + filename, position, 1048576, ref data);
+
+ UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length);
+
+ fileChkWorker.Update(data);
+
+ position += 1048576;
+ }
+
+ data = new byte[stat.Length - position];
+ filesystem.Read(path + "/" + filename, position, stat.Length - position, ref data);
+
+ UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length);
+
+ fileChkWorker.Update(data);
+
+ EndProgress();
+
+ file.Checksums = fileChkWorker.End().ToArray();
+ }
+ else file.Checksums = emptyChecksums;
+
+ Errno ret = filesystem.ListXAttr(path + "/" + filename, out List xattrs);
+
+ if(ret != Errno.NoError) return file;
+
+ List xattrTypes = new List();
+
+ foreach(string xattr in xattrs)
+ {
+ ret = filesystem.GetXattr(path + "/" + filename, xattr, ref data);
+
+ if(ret != Errno.NoError) continue;
+
+ Checksum xattrChkWorker = new Checksum();
+ xattrChkWorker.Update(data);
+
+ xattrTypes.Add(new ExtendedAttributeType
+ {
+ Checksums = xattrChkWorker.End().ToArray(),
+ length = (ulong)data.Length,
+ name = xattr
+ });
+ }
+
+ if(xattrTypes.Count > 0) file.ExtendedAttributes = xattrTypes.OrderBy(x => x.name).ToArray();
+
+ return file;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DiscImageChef.Core/Sidecar/Sidecar.cs b/DiscImageChef.Core/Sidecar/Sidecar.cs
index 7f6013aac..1c6048b53 100644
--- a/DiscImageChef.Core/Sidecar/Sidecar.cs
+++ b/DiscImageChef.Core/Sidecar/Sidecar.cs
@@ -44,22 +44,27 @@ namespace DiscImageChef.Core
{
public partial class Sidecar
{
- readonly Encoding encoding;
- readonly FileInfo fi;
- readonly Guid filterId;
- readonly IMediaImage image;
- readonly string imagePath;
- readonly Checksum imgChkWorker;
- readonly PluginBase plugins;
- bool aborted;
- FileStream fs;
- CICMMetadataType sidecar;
+ readonly Encoding encoding;
+ readonly FileInfo fi;
+ readonly Guid filterId;
+ readonly IMediaImage image;
+ readonly string imagePath;
+ readonly Checksum imgChkWorker;
+ readonly PluginBase plugins;
+ bool aborted;
+ readonly ChecksumType[] emptyChecksums;
+ FileStream fs;
+ CICMMetadataType sidecar;
public Sidecar()
{
plugins = GetPluginBase.Instance;
imgChkWorker = new Checksum();
aborted = false;
+
+ Checksum emptyChkWorker = new Checksum();
+ emptyChkWorker.Update(new byte[0]);
+ emptyChecksums = emptyChkWorker.End().ToArray();
}
/// Image