mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Decode and list EAs in FAT12 and FAT16.
This commit is contained in:
@@ -82,6 +82,7 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
const byte LFN_MASK = 0x1F;
|
const byte LFN_MASK = 0x1F;
|
||||||
const ushort EADATA_MAGIC = 0x4445;
|
const ushort EADATA_MAGIC = 0x4445;
|
||||||
const ushort EASCTR_MAGIC = 0x4145;
|
const ushort EASCTR_MAGIC = 0x4145;
|
||||||
|
const ushort EA_UNUSED = 0xFFFF;
|
||||||
|
|
||||||
readonly (string hash, string name)[] knownBootHashes =
|
readonly (string hash, string name)[] knownBootHashes =
|
||||||
{
|
{
|
||||||
@@ -217,5 +218,12 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
Nt,
|
Nt,
|
||||||
Lfn
|
Lfn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum EaFlags : uint
|
||||||
|
{
|
||||||
|
Normal = 0,
|
||||||
|
Critical = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,7 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
public partial class FAT : IReadOnlyFilesystem
|
public partial class FAT : IReadOnlyFilesystem
|
||||||
{
|
{
|
||||||
uint bytesPerCluster;
|
uint bytesPerCluster;
|
||||||
|
byte[] cachedEaData;
|
||||||
CultureInfo cultureInfo;
|
CultureInfo cultureInfo;
|
||||||
bool debug;
|
bool debug;
|
||||||
Dictionary<string, Dictionary<string, DirectoryEntry>> directoryCache;
|
Dictionary<string, Dictionary<string, DirectoryEntry>> directoryCache;
|
||||||
|
|||||||
@@ -882,5 +882,17 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
public readonly byte[] name3;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -605,13 +605,15 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
// Check it is really an OS/2 EA file
|
// Check it is really an OS/2 EA file
|
||||||
if(eaDirEntry.start_cluster != 0)
|
if(eaDirEntry.start_cluster != 0)
|
||||||
{
|
{
|
||||||
ulong secadd = firstClusterSector + eaDirEntry.start_cluster * sectorsPerCluster;
|
CacheEaData();
|
||||||
byte[] eadata =
|
ushort eamagic = BitConverter.ToUInt16(cachedEaData, 0);
|
||||||
imagePlugin.ReadSectors(firstClusterSector + eaDirEntry.start_cluster * sectorsPerCluster,
|
|
||||||
sectorsPerCluster);
|
|
||||||
ushort eamagic = BitConverter.ToUInt16(eadata, 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;
|
mounted = true;
|
||||||
|
|||||||
@@ -30,13 +30,20 @@
|
|||||||
// Copyright © 2011-2019 Natalia Portillo
|
// Copyright © 2011-2019 Natalia Portillo
|
||||||
// ****************************************************************************/
|
// ****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using DiscImageChef.CommonTypes.Structs;
|
using DiscImageChef.CommonTypes.Structs;
|
||||||
|
using DiscImageChef.Helpers;
|
||||||
|
|
||||||
namespace DiscImageChef.Filesystems.FAT
|
namespace DiscImageChef.Filesystems.FAT
|
||||||
{
|
{
|
||||||
public partial class FAT
|
public partial class FAT
|
||||||
{
|
{
|
||||||
|
Dictionary<string, Dictionary<string, byte[]>> eaCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lists all extended attributes, alternate data streams and forks of the given file.
|
/// Lists all extended attributes, alternate data streams and forks of the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -48,7 +55,81 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
xattrs = null;
|
xattrs = null;
|
||||||
if(!mounted) return Errno.AccessDenied;
|
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>
|
/// <summary>
|
||||||
@@ -64,5 +145,18 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
|
|
||||||
return Errno.NotSupported;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user