From 79d7441db489a97ad97a1bdb29297e33617b57e7 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Fri, 26 Apr 2019 19:57:19 +0100 Subject: [PATCH] Read FAT32 root directory. --- DiscImageChef.Filesystems/FAT/Super.cs | 177 ++++++++++++++----------- 1 file changed, 102 insertions(+), 75 deletions(-) diff --git a/DiscImageChef.Filesystems/FAT/Super.cs b/DiscImageChef.Filesystems/FAT/Super.cs index bb991d55e..f2e61532e 100644 --- a/DiscImageChef.Filesystems/FAT/Super.cs +++ b/DiscImageChef.Filesystems/FAT/Super.cs @@ -49,6 +49,10 @@ namespace DiscImageChef.Filesystems.FAT { public partial class FAT { + uint fatEntriesPerSector; + + IMediaImage image; + /// /// Mounts an Apple Lisa filesystem /// @@ -84,6 +88,7 @@ namespace DiscImageChef.Filesystems.FAT uint sectorsPerRealSector = 1; // This is needed because some OSes don't put volume label as first entry in the root directory uint sectorsForRootDirectory = 0; + uint rootDirectoryCluster = 0; switch(bpbKind) { @@ -102,6 +107,8 @@ namespace DiscImageChef.Filesystems.FAT Fat32ParameterBlockShort shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + rootDirectoryCluster = fat32Bpb.root_cluster; + // This is to support FAT partitions on hybrid ISO/USB images if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { @@ -308,92 +315,112 @@ namespace DiscImageChef.Filesystems.FAT firstClusterSector += partition.Start; + image = imagePlugin; + + if(fat32) fatEntriesPerSector = imagePlugin.Info.SectorSize / 4; + else if(fat16) fatEntriesPerSector = imagePlugin.Info.SectorSize / 2; + else fatEntriesPerSector = imagePlugin.Info.SectorSize * 2 / 3; + fatFirstSector = partition.Start + reservedSectors * sectorsPerRealSector; + rootDirectoryCache = new Dictionary(); + byte[] rootDirectory = null; if(!fat32) - if(firstClusterSector + partition.Start < partition.End && - imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) + { + rootDirectory = imagePlugin.ReadSectors(firstClusterSector, sectorsForRootDirectory); + + if(bpbKind == BpbKind.DecRainbow) { - byte[] rootDirectory = imagePlugin.ReadSectors(firstClusterSector, sectorsForRootDirectory); + MemoryStream rootMs = new MemoryStream(); + foreach(byte[] tmp in from ulong rootSector in new[] {0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20} + select imagePlugin.ReadSector(rootSector)) rootMs.Write(tmp, 0, tmp.Length); - if(bpbKind == BpbKind.DecRainbow) - { - MemoryStream rootMs = new MemoryStream(); - foreach(byte[] tmp in from ulong rootSector in new[] {0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20} - select imagePlugin.ReadSector(rootSector)) - rootMs.Write(tmp, 0, tmp.Length); + rootDirectory = rootMs.ToArray(); + } + } + else + { + if(rootDirectoryCluster == 0) return Errno.InvalidArgument; - rootDirectory = rootMs.ToArray(); - } + MemoryStream rootMs = new MemoryStream(); + uint[] rootDirectoryClusters = GetClusters(rootDirectoryCluster); - for(int i = 0; i < rootDirectory.Length; i += 32) - { - DirectoryEntry entry = - Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, 32); + foreach(uint cluster in rootDirectoryClusters) + { + byte[] buffer = + imagePlugin.ReadSectors(firstClusterSector + (cluster - 2) * sectorsPerCluster, + sectorsPerCluster); - if(entry.filename[0] == DIRENT_FINISHED) break; - - // Not a correct entry - if(entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) continue; - - // Deleted or subdirectory entry - if(entry.filename[0] == DIRENT_SUBDIR || entry.filename[0] == DIRENT_DELETED) continue; - - // TODO: LFN namespace - if(entry.attributes.HasFlag(FatAttributes.LFN)) continue; - - string filename; - - if(entry.attributes.HasFlag(FatAttributes.VolumeLabel)) - { - byte[] fullname = new byte[11]; - Array.Copy(entry.filename, 0, fullname, 0, 8); - Array.Copy(entry.extension, 0, fullname, 8, 3); - string volname = Encoding.GetString(fullname).Trim(); - if(!string.IsNullOrEmpty(volname)) - XmlFsType.VolumeName = (entry.caseinfo & 0x18) > 0 ? volname.ToLower() : volname; - - if(entry.ctime > 0 && entry.cdate > 0) - { - XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - if(entry.ctime_ms > 0) - XmlFsType.CreationDate = - XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); - XmlFsType.CreationDateSpecified = true; - } - - if(entry.mtime > 0 && entry.mdate > 0) - { - XmlFsType.ModificationDate = - DateHandlers.DosToDateTime(entry.mdate, entry.mtime); - XmlFsType.ModificationDateSpecified = true; - } - - continue; - } - - if(entry.filename[0] == DIRENT_E5) entry.filename[0] = DIRENT_DELETED; - - string name = Encoding.GetString(entry.filename).Trim(); - string extension = Encoding.GetString(entry.extension).Trim(); - - if((entry.caseinfo & FASTFAT_LOWERCASE_EXTENSION) > 0) - extension = extension.ToLower(CultureInfo.CurrentCulture); - - if((entry.caseinfo & FASTFAT_LOWERCASE_BASENAME) > 0) - name = name.ToLower(CultureInfo.CurrentCulture); - - if(extension != "") filename = name + "." + extension; - else filename = name; - - rootDirectoryCache.Add(filename, entry); - } + rootMs.Write(buffer, 0, buffer.Length); } - XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim(); - fatFirstSector = partition.Start + reservedSectors * sectorsPerRealSector; + rootDirectory = rootMs.ToArray(); + } + + if(rootDirectory is null) return Errno.InvalidArgument; + + for(int i = 0; i < rootDirectory.Length; i += 32) + { + DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, 32); + + if(entry.filename[0] == DIRENT_FINISHED) break; + + // Not a correct entry + if(entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) continue; + + // Deleted or subdirectory entry + if(entry.filename[0] == DIRENT_SUBDIR || entry.filename[0] == DIRENT_DELETED) continue; + + // TODO: LFN namespace + if(entry.attributes.HasFlag(FatAttributes.LFN)) continue; + + string filename; + + if(entry.attributes.HasFlag(FatAttributes.VolumeLabel)) + { + byte[] fullname = new byte[11]; + Array.Copy(entry.filename, 0, fullname, 0, 8); + Array.Copy(entry.extension, 0, fullname, 8, 3); + string volname = Encoding.GetString(fullname).Trim(); + if(!string.IsNullOrEmpty(volname)) + XmlFsType.VolumeName = (entry.caseinfo & 0x18) > 0 ? volname.ToLower() : volname; + + if(entry.ctime > 0 && entry.cdate > 0) + { + XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); + if(entry.ctime_ms > 0) + XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); + XmlFsType.CreationDateSpecified = true; + } + + if(entry.mtime > 0 && entry.mdate > 0) + { + XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); + XmlFsType.ModificationDateSpecified = true; + } + + continue; + } + + if(entry.filename[0] == DIRENT_E5) entry.filename[0] = DIRENT_DELETED; + + string name = Encoding.GetString(entry.filename).Trim(); + string extension = Encoding.GetString(entry.extension).Trim(); + + if((entry.caseinfo & FASTFAT_LOWERCASE_EXTENSION) > 0) + extension = extension.ToLower(CultureInfo.CurrentCulture); + + if((entry.caseinfo & FASTFAT_LOWERCASE_BASENAME) > 0) name = name.ToLower(CultureInfo.CurrentCulture); + + if(extension != "") filename = name + "." + extension; + else filename = name; + + rootDirectoryCache.Add(filename, entry); + } + + XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim(); + mounted = true; - mounted = true; return Errno.NoError; }