diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 7354018f0..3b6db496f 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -1,11 +1,10 @@ - - + @@ -1312,6 +1311,7 @@ + diff --git a/DiscImageChef.Filesystems/ISO9660/Dir.cs b/DiscImageChef.Filesystems/ISO9660/Dir.cs index bc3308f72..8c0db3da2 100644 --- a/DiscImageChef.Filesystems/ISO9660/Dir.cs +++ b/DiscImageChef.Filesystems/ISO9660/Dir.cs @@ -75,6 +75,14 @@ namespace DiscImageChef.Filesystems.ISO9660 ? DecodeHighSierraDirectory(directoryBuffer) : DecodeIsoDirectory(directoryBuffer); + if(usePathTable) + foreach(DecodedDirectoryEntry subDirectory in cdi + ? GetSubdirsFromCdiPathTable(currentPath) + : highSierra + ? GetSubdirsFromHighSierraPathTable(currentPath) + : GetSubdirsFromIsoPathTable(currentPath)) + currentDirectory[subDirectory.Filename] = subDirectory; + directoryCache.Add(currentPath, currentDirectory); } @@ -142,6 +150,12 @@ namespace DiscImageChef.Filesystems.ISO9660 Timestamp = DecodeHighSierraDateTime(record.date) }; + if(entry.Flags.HasFlag(FileFlags.Directory) && usePathTable) + { + entryOff += record.length; + continue; + } + if(!entries.ContainsKey(entry.Filename)) entries.Add(entry.Filename, entry); entryOff += record.length; @@ -187,6 +201,12 @@ namespace DiscImageChef.Filesystems.ISO9660 Timestamp = DecodeIsoDateTime(record.date) }; + if(entry.Flags.HasFlag(FileFlags.Directory) && usePathTable) + { + entryOff += record.length; + continue; + } + // TODO: XA int systemAreaStart = entryOff + record.name_len + Marshal.SizeOf(); int systemAreaLength = record.length - record.name_len - Marshal.SizeOf(); @@ -700,5 +720,117 @@ namespace DiscImageChef.Filesystems.ISO9660 } } } + + PathTableEntryInternal[] GetPathTableEntries(string path) + { + IEnumerable tableEntries = new PathTableEntryInternal[0]; + List pathTableList = new List(pathTable); + + if(path == "" || path == "/") tableEntries = pathTable.Where(p => p.Parent == 1 && p != pathTable[0]); + else + { + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); + + string[] pieces = cutPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + + int currentParent = 1; + int currentPiece = 0; + + while(currentPiece < pieces.Length) + { + PathTableEntryInternal currentEntry = pathTable.FirstOrDefault(p => p.Parent == currentParent && + p.Name.ToLower(CultureInfo + .CurrentUICulture) == + pieces[currentPiece]); + + if(currentEntry is null) break; + + currentPiece++; + currentParent = pathTableList.IndexOf(currentEntry) + 1; + } + + tableEntries = pathTable.Where(p => p.Parent == currentParent); + } + + return tableEntries.ToArray(); + } + + DecodedDirectoryEntry[] GetSubdirsFromCdiPathTable(string path) => throw new NotImplementedException(); + + DecodedDirectoryEntry[] GetSubdirsFromIsoPathTable(string path) + { + PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); + List entries = new List(); + foreach(PathTableEntryInternal tEntry in tableEntries) + { + byte[] sector = image.ReadSector(tEntry.Extent); + DirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(sector, 0, + Marshal.SizeOf()); + + if(record.length == 0) break; + + DecodedDirectoryEntry entry = new DecodedDirectoryEntry + { + Extent = record.size == 0 ? 0 : record.extent, + Size = record.size, + Flags = record.flags, + Filename = tEntry.Name, + FileUnitSize = record.file_unit_size, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeIsoDateTime(record.date) + }; + + // TODO: XA + int systemAreaStart = record.name_len + Marshal.SizeOf(); + int systemAreaLength = record.length - record.name_len - Marshal.SizeOf(); + + if(systemAreaStart % 2 != 0) + { + systemAreaStart++; + systemAreaLength--; + } + + DecodeSystemArea(sector, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, out _); + + entries.Add(entry); + } + + return entries.ToArray(); + } + + DecodedDirectoryEntry[] GetSubdirsFromHighSierraPathTable(string path) + { + PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); + List entries = new List(); + foreach(PathTableEntryInternal tEntry in tableEntries) + { + byte[] sector = image.ReadSector(tEntry.Extent); + HighSierraDirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(sector, 0, + Marshal + .SizeOf< + HighSierraDirectoryRecord + >()); + + DecodedDirectoryEntry entry = new DecodedDirectoryEntry + { + Extent = record.size == 0 ? 0 : record.extent, + Size = record.size, + Flags = record.flags, + Filename = tEntry.Name, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeHighSierraDateTime(record.date) + }; + + entries.Add(entry); + } + + return entries.ToArray(); + } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/ISO9660/ISO9660.cs b/DiscImageChef.Filesystems/ISO9660/ISO9660.cs index a6c081a89..b8af4706d 100644 --- a/DiscImageChef.Filesystems/ISO9660/ISO9660.cs +++ b/DiscImageChef.Filesystems/ISO9660/ISO9660.cs @@ -52,6 +52,7 @@ namespace DiscImageChef.Filesystems.ISO9660 PathTableEntryInternal[] pathTable; Dictionary rootDirectoryCache; FileSystemInfo statfs; + bool usePathTable; public FileSystemType XmlFsType { get; private set; } public Encoding Encoding { get; private set; } @@ -60,7 +61,10 @@ namespace DiscImageChef.Filesystems.ISO9660 public string Author => "Natalia Portillo"; public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] { }; + new (string name, Type type, string description)[] + { + ("use_path_table", typeof(bool), "Use path table for directory traversal") + }; public Dictionary Namespaces => new Dictionary diff --git a/DiscImageChef.Filesystems/ISO9660/Super.cs b/DiscImageChef.Filesystems/ISO9660/Super.cs index 64f046520..d93f9d125 100644 --- a/DiscImageChef.Filesystems/ISO9660/Super.cs +++ b/DiscImageChef.Filesystems/ISO9660/Super.cs @@ -22,6 +22,8 @@ namespace DiscImageChef.Filesystems.ISO9660 if(options == null) options = GetDefaultOptions(); if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug); + if(options.TryGetValue("use_path_table", out string usePathTableString)) + bool.TryParse(usePathTableString, out usePathTable); // Default namespace if(@namespace is null) @namespace = "joliet"; @@ -234,6 +236,8 @@ namespace DiscImageChef.Filesystems.ISO9660 Marshal.ByteArrayToStructureBigEndian(firstRootSector); rootSize = rootEntry.size / fsvd.Value.logical_block_size; if(rootEntry.size % fsvd.Value.logical_block_size > 0) rootSize++; + + usePathTable = true; } if(rootLocation + rootSize >= imagePlugin.Info.Sectors) return Errno.InvalidArgument; @@ -248,6 +252,8 @@ namespace DiscImageChef.Filesystems.ISO9660 // TODO: Add IP.BIN to debug root directory // TODO: Add volume descriptors to debug root directory + if(this.@namespace == Namespace.Joliet || this.@namespace == Namespace.Rrip) usePathTable = false; + if(this.@namespace != Namespace.Joliet) rootDirectoryCache = cdi ? DecodeCdiDirectory(rootDir) @@ -382,7 +388,16 @@ namespace DiscImageChef.Filesystems.ISO9660 directoryCache = new Dictionary>(); image = imagePlugin; - mounted = true; + + if(usePathTable) + foreach(DecodedDirectoryEntry subDirectory in cdi + ? GetSubdirsFromCdiPathTable("") + : highSierra + ? GetSubdirsFromHighSierraPathTable("") + : GetSubdirsFromIsoPathTable("")) + rootDirectoryCache[subDirectory.Filename] = subDirectory; + + mounted = true; return Errno.NoError; }