From 27d1f83d61b8988e92aca3dcdca3e3c7414dfc2f Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 27 Apr 2019 20:19:18 +0100 Subject: [PATCH] Decode and list EAs in FAT12 and FAT16. --- DiscImageChef.Filesystems/FAT/Consts.cs | 8 ++ DiscImageChef.Filesystems/FAT/FAT.cs | 1 + DiscImageChef.Filesystems/FAT/Structs.cs | 12 +++ DiscImageChef.Filesystems/FAT/Super.cs | 14 ++-- DiscImageChef.Filesystems/FAT/Xattr.cs | 96 +++++++++++++++++++++++- 5 files changed, 124 insertions(+), 7 deletions(-) diff --git a/DiscImageChef.Filesystems/FAT/Consts.cs b/DiscImageChef.Filesystems/FAT/Consts.cs index 3a822050d..f4e332178 100644 --- a/DiscImageChef.Filesystems/FAT/Consts.cs +++ b/DiscImageChef.Filesystems/FAT/Consts.cs @@ -82,6 +82,7 @@ namespace DiscImageChef.Filesystems.FAT const byte LFN_MASK = 0x1F; const ushort EADATA_MAGIC = 0x4445; const ushort EASCTR_MAGIC = 0x4145; + const ushort EA_UNUSED = 0xFFFF; readonly (string hash, string name)[] knownBootHashes = { @@ -217,5 +218,12 @@ namespace DiscImageChef.Filesystems.FAT Nt, Lfn } + + [Flags] + enum EaFlags : uint + { + Normal = 0, + Critical = 1 + } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/FAT/FAT.cs b/DiscImageChef.Filesystems/FAT/FAT.cs index 4ced70cf7..b1fa15e76 100644 --- a/DiscImageChef.Filesystems/FAT/FAT.cs +++ b/DiscImageChef.Filesystems/FAT/FAT.cs @@ -45,6 +45,7 @@ namespace DiscImageChef.Filesystems.FAT public partial class FAT : IReadOnlyFilesystem { uint bytesPerCluster; + byte[] cachedEaData; CultureInfo cultureInfo; bool debug; Dictionary> directoryCache; diff --git a/DiscImageChef.Filesystems/FAT/Structs.cs b/DiscImageChef.Filesystems/FAT/Structs.cs index 8f36bf837..36c66ee45 100644 --- a/DiscImageChef.Filesystems/FAT/Structs.cs +++ b/DiscImageChef.Filesystems/FAT/Structs.cs @@ -882,5 +882,17 @@ namespace DiscImageChef.Filesystems.FAT [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] name3; } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct EaHeader + { + public readonly ushort magic; + public readonly ushort cluster; + public readonly EaFlags flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] filename; + public readonly uint unknown; + public readonly ushort zero; + } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/FAT/Super.cs b/DiscImageChef.Filesystems/FAT/Super.cs index 39a4bd97e..29d77aef3 100644 --- a/DiscImageChef.Filesystems/FAT/Super.cs +++ b/DiscImageChef.Filesystems/FAT/Super.cs @@ -605,13 +605,15 @@ namespace DiscImageChef.Filesystems.FAT // Check it is really an OS/2 EA file if(eaDirEntry.start_cluster != 0) { - ulong secadd = firstClusterSector + eaDirEntry.start_cluster * sectorsPerCluster; - byte[] eadata = - imagePlugin.ReadSectors(firstClusterSector + eaDirEntry.start_cluster * sectorsPerCluster, - sectorsPerCluster); - ushort eamagic = BitConverter.ToUInt16(eadata, 0); + CacheEaData(); + ushort eamagic = BitConverter.ToUInt16(cachedEaData, 0); - if(eamagic != EADATA_MAGIC) eaDirEntry = new DirectoryEntry(); + if(eamagic != EADATA_MAGIC) + { + eaDirEntry = new DirectoryEntry(); + cachedEaData = null; + } + else eaCache = new Dictionary>(); } mounted = true; diff --git a/DiscImageChef.Filesystems/FAT/Xattr.cs b/DiscImageChef.Filesystems/FAT/Xattr.cs index 369675120..bd80d5c3e 100644 --- a/DiscImageChef.Filesystems/FAT/Xattr.cs +++ b/DiscImageChef.Filesystems/FAT/Xattr.cs @@ -30,13 +30,20 @@ // Copyright © 2011-2019 Natalia Portillo // ****************************************************************************/ +using System; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; using DiscImageChef.CommonTypes.Structs; +using DiscImageChef.Helpers; namespace DiscImageChef.Filesystems.FAT { public partial class FAT { + Dictionary> eaCache; + /// /// Lists all extended attributes, alternate data streams and forks of the given file. /// @@ -48,7 +55,81 @@ namespace DiscImageChef.Filesystems.FAT xattrs = null; if(!mounted) return Errno.AccessDenied; - return Errno.NotSupported; + // No other xattr recognized yet + if(cachedEaData is null) return Errno.NotSupported; + + if(path[0] == '/') path = path.Substring(1); + + if(eaCache.TryGetValue(path.ToLower(cultureInfo), out Dictionary eas)) + { + xattrs = eas.Keys.ToList(); + return Errno.NoError; + } + + Errno err = GetFileEntry(path, out DirectoryEntry entry); + + if(err != Errno.NoError) return err; + + xattrs = new List(); + + if(entry.ea_handle == 0) return Errno.NoError; + + int aIndex = entry.ea_handle >> 7; + // First 0x20 bytes are the magic number and unused words + ushort a = BitConverter.ToUInt16(cachedEaData, aIndex * 2 + 0x20); + + ushort b = BitConverter.ToUInt16(cachedEaData, entry.ea_handle * 2 + 0x200); + + uint eaCluster = (uint)(a + b); + + if(b == EA_UNUSED) return Errno.NoError; + + EaHeader header = + Marshal.ByteArrayToStructureLittleEndian(cachedEaData, (int)(eaCluster * bytesPerCluster), + Marshal.SizeOf()); + + if(header.magic != 0x4145) return Errno.NoError; + + uint eaLen = BitConverter.ToUInt32(cachedEaData, + (int)(eaCluster * bytesPerCluster) + Marshal.SizeOf()); + + byte[] eaData = new byte[eaLen]; + Array.Copy(cachedEaData, (int)(eaCluster * bytesPerCluster) + Marshal.SizeOf(), eaData, 0, eaLen); + + eas = new Dictionary(); + + if(true) eas.Add("com.microsoft.os2.fea", eaData); + + int pos = 4; + while(pos < eaData.Length) + { + byte fEA = eaData[pos++]; + byte cbName = eaData[pos++]; + ushort cbValue = BitConverter.ToUInt16(eaData, pos); + pos += 2; + + string name = Encoding.ASCII.GetString(eaData, pos, cbName); + pos += cbName; + pos++; + byte[] data = new byte[cbValue]; + + Array.Copy(eaData, pos, data, 0, cbValue); + pos += cbValue; + + // OS/2 System Attributes + if(name[0] == '.') + { + // This is WorkPlace System information so it's IBM + if(name == ".CLASSINFO") name = "com.ibm.os2.classinfo"; + else name = "com.microsoft.os2" + name.ToLower(); + } + + eas.Add(name, data); + } + + eaCache.Add(path.ToLower(cultureInfo), eas); + xattrs = eas.Keys.ToList(); + return Errno.NoError; } /// @@ -64,5 +145,18 @@ namespace DiscImageChef.Filesystems.FAT return Errno.NotSupported; } + + void CacheEaData() + { + if(eaDirEntry.start_cluster == 0) return; + + MemoryStream eaDataMs = new MemoryStream(); + + foreach(byte[] buffer in GetClusters(eaDirEntry.start_cluster) + .Select(cluster => image.ReadSectors(firstClusterSector + cluster * sectorsPerCluster, + sectorsPerCluster))) eaDataMs.Write(buffer, 0, buffer.Length); + + cachedEaData = eaDataMs.ToArray(); + } } } \ No newline at end of file