Decode and list EAs in FAT12 and FAT16.

This commit is contained in:
2019-04-27 20:19:18 +01:00
parent 3d8f0f2380
commit 27d1f83d61
5 changed files with 124 additions and 7 deletions

View File

@@ -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
}
}
}

View File

@@ -45,6 +45,7 @@ namespace DiscImageChef.Filesystems.FAT
public partial class FAT : IReadOnlyFilesystem
{
uint bytesPerCluster;
byte[] cachedEaData;
CultureInfo cultureInfo;
bool debug;
Dictionary<string, Dictionary<string, DirectoryEntry>> directoryCache;

View File

@@ -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;
}
}
}

View File

@@ -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<string, Dictionary<string, byte[]>>();
}
mounted = true;

View File

@@ -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<string, Dictionary<string, byte[]>> eaCache;
/// <summary>
/// Lists all extended attributes, alternate data streams and forks of the given file.
/// </summary>
@@ -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<string, byte[]> eas))
{
xattrs = eas.Keys.ToList();
return Errno.NoError;
}
Errno err = GetFileEntry(path, out DirectoryEntry entry);
if(err != Errno.NoError) return err;
xattrs = new List<string>();
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<EaHeader>(cachedEaData, (int)(eaCluster * bytesPerCluster),
Marshal.SizeOf<EaHeader>());
if(header.magic != 0x4145) return Errno.NoError;
uint eaLen = BitConverter.ToUInt32(cachedEaData,
(int)(eaCluster * bytesPerCluster) + Marshal.SizeOf<EaHeader>());
byte[] eaData = new byte[eaLen];
Array.Copy(cachedEaData, (int)(eaCluster * bytesPerCluster) + Marshal.SizeOf<EaHeader>(), eaData, 0, eaLen);
eas = new Dictionary<string, byte[]>();
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;
}
/// <summary>
@@ -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();
}
}
}