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