mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Implement ISO9660 readdir for subdirectories.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DiscImageChef.CommonTypes.Structs;
|
||||
@@ -9,77 +10,136 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
{
|
||||
public partial class ISO9660
|
||||
{
|
||||
Dictionary<string, Dictionary<string, DecodedDirectoryEntry>> directoryCache;
|
||||
|
||||
// TODO: Implement path table traversal
|
||||
public Errno ReadDir(string path, out List<string> contents)
|
||||
{
|
||||
contents = null;
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
contents = new List<string>();
|
||||
if(string.IsNullOrWhiteSpace(path) || path == "/")
|
||||
{
|
||||
foreach(DecodedDirectoryEntry entry in rootDirectory)
|
||||
{
|
||||
switch(@namespace)
|
||||
{
|
||||
case Namespace.Normal:
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
|
||||
break;
|
||||
case Namespace.Vms:
|
||||
contents.Add(entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.Joliet:
|
||||
// TODO: Implement Joliet
|
||||
break;
|
||||
case Namespace.JolietNormal:
|
||||
// TODO: Implement Joliet
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.Rrip:
|
||||
// TODO: Implement RRIP
|
||||
break;
|
||||
case Namespace.RripNormal:
|
||||
// TODO: Implement RRIP
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.RripJoliet:
|
||||
// TODO: Implement RRIP
|
||||
// TODO: Implement Joliet
|
||||
break;
|
||||
case Namespace.RripJolietNormal:
|
||||
// TODO: Implement RRIP
|
||||
// TODO: Implement Joliet
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
contents = GetFilenames(rootDirectoryCache);
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
// TODO: Implement subdirectories
|
||||
throw new NotImplementedException();
|
||||
string cutPath = path.StartsWith("/", StringComparison.Ordinal)
|
||||
? path.Substring(1).ToLower(CultureInfo.CurrentUICulture)
|
||||
: path.ToLower(CultureInfo.CurrentUICulture);
|
||||
|
||||
if(directoryCache.TryGetValue(cutPath, out Dictionary<string, DecodedDirectoryEntry> currentDirectory))
|
||||
{
|
||||
contents = currentDirectory.Keys.ToList();
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
string[] pieces = cutPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
KeyValuePair<string, DecodedDirectoryEntry> entry =
|
||||
rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]);
|
||||
|
||||
if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile;
|
||||
|
||||
if(!entry.Value.Flags.HasFlag(FileFlags.Directory)) return Errno.NotDirectory;
|
||||
|
||||
string currentPath = pieces[0];
|
||||
|
||||
currentDirectory = rootDirectoryCache;
|
||||
|
||||
for(int p = 0; p < pieces.Length; p++)
|
||||
{
|
||||
entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]);
|
||||
|
||||
if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile;
|
||||
|
||||
if(!entry.Value.Flags.HasFlag(FileFlags.Directory)) return Errno.NotDirectory;
|
||||
|
||||
currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}";
|
||||
uint currentExtent = entry.Value.Extent;
|
||||
|
||||
if(directoryCache.TryGetValue(currentPath, out currentDirectory)) continue;
|
||||
|
||||
if(currentExtent == 0) return Errno.InvalidArgument;
|
||||
|
||||
// TODO: XA, High Sierra
|
||||
byte[] directoryBuffer = image.ReadSectors(currentExtent, entry.Value.Size / 2048);
|
||||
|
||||
// TODO: Decode Joliet
|
||||
currentDirectory = cdi
|
||||
? DecodeCdiDirectory(directoryBuffer)
|
||||
: highSierra
|
||||
? DecodeHighSierraDirectory(directoryBuffer)
|
||||
: DecodeIsoDirectory(directoryBuffer);
|
||||
|
||||
directoryCache.Add(currentPath, currentDirectory);
|
||||
}
|
||||
|
||||
contents = GetFilenames(currentDirectory);
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
List<DecodedDirectoryEntry> DecodeCdiDirectory(byte[] data) => throw new NotImplementedException();
|
||||
List<string> GetFilenames(Dictionary<string, DecodedDirectoryEntry> dirents)
|
||||
{
|
||||
List<string> contents = new List<string>();
|
||||
foreach(DecodedDirectoryEntry entry in dirents.Values)
|
||||
switch(@namespace)
|
||||
{
|
||||
case Namespace.Normal:
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
|
||||
List<DecodedDirectoryEntry> DecodeHighSierraDirectory(byte[] data) => throw new NotImplementedException();
|
||||
break;
|
||||
case Namespace.Vms:
|
||||
contents.Add(entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.Joliet:
|
||||
// TODO: Implement Joliet
|
||||
break;
|
||||
case Namespace.JolietNormal:
|
||||
// TODO: Implement Joliet
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.Rrip:
|
||||
// TODO: Implement RRIP
|
||||
break;
|
||||
case Namespace.RripNormal:
|
||||
// TODO: Implement RRIP
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
case Namespace.RripJoliet:
|
||||
// TODO: Implement RRIP
|
||||
// TODO: Implement Joliet
|
||||
break;
|
||||
case Namespace.RripJolietNormal:
|
||||
// TODO: Implement RRIP
|
||||
// TODO: Implement Joliet
|
||||
contents.Add(entry.IsoFilename.EndsWith(";1", StringComparison.Ordinal)
|
||||
? entry.IsoFilename.Substring(0, entry.IsoFilename.Length - 2)
|
||||
: entry.IsoFilename);
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
Dictionary<string, DecodedDirectoryEntry> DecodeCdiDirectory(byte[] data) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
Dictionary<string, DecodedDirectoryEntry> DecodeHighSierraDirectory(byte[] data) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
// TODO: Implement system area
|
||||
List<DecodedDirectoryEntry> DecodeIsoDirectory(byte[] data)
|
||||
Dictionary<string, DecodedDirectoryEntry> DecodeIsoDirectory(byte[] data)
|
||||
{
|
||||
List<DecodedDirectoryEntry> entries = new List<DecodedDirectoryEntry>();
|
||||
int entryOff = 0;
|
||||
Dictionary<string, DecodedDirectoryEntry> entries = new Dictionary<string, DecodedDirectoryEntry>();
|
||||
int entryOff = 0;
|
||||
|
||||
while(entryOff + DirectoryRecordSize < data.Length)
|
||||
{
|
||||
@@ -97,20 +157,21 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
continue;
|
||||
}
|
||||
|
||||
DecodedDirectoryEntry entry = new DecodedDirectoryEntry();
|
||||
|
||||
entry.Extent = record.size == 0 ? 0 : record.extent;
|
||||
entry.Size = record.size;
|
||||
entry.Flags = record.flags;
|
||||
entry.FileUnitSize = record.file_unit_size;
|
||||
entry.Interleave = record.interleave;
|
||||
entry.VolumeSequenceNumber = record.volume_sequence_number;
|
||||
entry.IsoFilename =
|
||||
Encoding.ASCII.GetString(data, entryOff + DirectoryRecordSize, record.name_len);
|
||||
entry.Timestamp = DecodeIsoDateTime(record.date);
|
||||
DecodedDirectoryEntry entry = new DecodedDirectoryEntry
|
||||
{
|
||||
Extent = record.size == 0 ? 0 : record.extent,
|
||||
Size = record.size,
|
||||
Flags = record.flags,
|
||||
FileUnitSize = record.file_unit_size,
|
||||
Interleave = record.interleave,
|
||||
VolumeSequenceNumber = record.volume_sequence_number,
|
||||
IsoFilename =
|
||||
Encoding.ASCII.GetString(data, entryOff + DirectoryRecordSize, record.name_len),
|
||||
Timestamp = DecodeIsoDateTime(record.date)
|
||||
};
|
||||
|
||||
// TODO: Multi-extent files
|
||||
if(entries.All(e => e.IsoFilename != entry.IsoFilename)) entries.Add(entry);
|
||||
if(!entries.ContainsKey(entry.IsoFilename)) entries.Add(entry.IsoFilename, entry);
|
||||
|
||||
entryOff += record.length;
|
||||
}
|
||||
|
||||
@@ -42,12 +42,14 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
// This is coded following ECMA-119.
|
||||
public partial class ISO9660 : IReadOnlyFilesystem
|
||||
{
|
||||
bool debug;
|
||||
IMediaImage image;
|
||||
bool mounted;
|
||||
Namespace @namespace;
|
||||
List<DecodedDirectoryEntry> rootDirectory;
|
||||
FileSystemInfo statfs;
|
||||
bool cdi;
|
||||
bool debug;
|
||||
bool highSierra;
|
||||
IMediaImage image;
|
||||
bool mounted;
|
||||
Namespace @namespace;
|
||||
Dictionary<string, DecodedDirectoryEntry> rootDirectoryCache;
|
||||
FileSystemInfo statfs;
|
||||
|
||||
public FileSystemType XmlFsType { get; private set; }
|
||||
public Encoding Encoding { get; private set; }
|
||||
|
||||
@@ -55,16 +55,16 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
public uint Blocks;
|
||||
}
|
||||
|
||||
struct DecodedDirectoryEntry
|
||||
class DecodedDirectoryEntry
|
||||
{
|
||||
public uint Extent;
|
||||
public uint Size;
|
||||
public FileFlags Flags;
|
||||
public byte FileUnitSize;
|
||||
public FileFlags Flags;
|
||||
public byte Interleave;
|
||||
public ushort VolumeSequenceNumber;
|
||||
public string IsoFilename;
|
||||
public uint Size;
|
||||
public DateTime? Timestamp;
|
||||
public ushort VolumeSequenceNumber;
|
||||
|
||||
public override string ToString() => IsoFilename;
|
||||
}
|
||||
|
||||
@@ -72,10 +72,10 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start);
|
||||
int xaOff = vdSector.Length == 2336 ? 8 : 0;
|
||||
Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5);
|
||||
bool highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
|
||||
int hsOff = 0;
|
||||
highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
|
||||
int hsOff = 0;
|
||||
if(highSierra) hsOff = 8;
|
||||
bool cdi = false;
|
||||
cdi = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
@@ -184,7 +184,6 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
this.@namespace = Namespace.Normal;
|
||||
|
||||
if(jolietvd is null)
|
||||
{
|
||||
switch(this.@namespace)
|
||||
{
|
||||
case Namespace.Joliet:
|
||||
@@ -198,7 +197,6 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
this.@namespace = Namespace.RripNormal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint rootLocation = 0;
|
||||
uint rootSize = 0;
|
||||
@@ -235,11 +233,11 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
// TODO: Add volume descriptors to debug root directory
|
||||
// TODO: Decode Joliet directory
|
||||
|
||||
rootDirectory = cdi
|
||||
? DecodeCdiDirectory(rootDir)
|
||||
: highSierra
|
||||
? DecodeHighSierraDirectory(rootDir)
|
||||
: DecodeIsoDirectory(rootDir);
|
||||
rootDirectoryCache = cdi
|
||||
? DecodeCdiDirectory(rootDir)
|
||||
: highSierra
|
||||
? DecodeHighSierraDirectory(rootDir)
|
||||
: DecodeIsoDirectory(rootDir);
|
||||
|
||||
XmlFsType.Type = fsFormat;
|
||||
|
||||
@@ -354,8 +352,10 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
Type = fsFormat
|
||||
};
|
||||
|
||||
image = imagePlugin;
|
||||
mounted = true;
|
||||
directoryCache = new Dictionary<string, Dictionary<string, DecodedDirectoryEntry>>();
|
||||
image = imagePlugin;
|
||||
mounted = true;
|
||||
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
@@ -363,8 +363,9 @@ namespace DiscImageChef.Filesystems.ISO9660
|
||||
{
|
||||
if(!mounted) return Errno.AccessDenied;
|
||||
|
||||
rootDirectory = null;
|
||||
mounted = false;
|
||||
rootDirectoryCache = null;
|
||||
directoryCache = null;
|
||||
mounted = false;
|
||||
|
||||
return Errno.NoError;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user