Implement support for EAs in FAT32.

This commit is contained in:
2019-04-28 11:57:54 +01:00
parent b2c008eb02
commit fa2ec74015
9 changed files with 238 additions and 92 deletions

View File

@@ -368,7 +368,7 @@ namespace DiscImageChef.Filesystems.FAT
else fatEntriesPerSector = imagePlugin.Info.SectorSize * 2 / 3;
fatFirstSector = partition.Start + reservedSectors * sectorsPerRealSector;
rootDirectoryCache = new Dictionary<string, DirectoryEntry>();
rootDirectoryCache = new Dictionary<string, CompleteDirectoryEntry>();
byte[] rootDirectory = null;
if(!fat32)
@@ -401,13 +401,15 @@ namespace DiscImageChef.Filesystems.FAT
}
rootDirectory = rootMs.ToArray();
// OS/2 FAT32.IFS uses LFN instead of .LONGNAME
if(this.@namespace == Namespace.Os2) this.@namespace = Namespace.Os2;
}
if(rootDirectory is null) return Errno.InvalidArgument;
byte[] lastLfnName = null;
byte lastLfnChecksum = 0;
List<string> LFNs = new List<string>();
byte[] lastLfnName = null;
byte lastLfnChecksum = 0;
for(int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf<DirectoryEntry>())
{
@@ -468,9 +470,10 @@ namespace DiscImageChef.Filesystems.FAT
Array.Copy(entry.extension, 0, fullname, 8, 3);
string volname = Encoding.GetString(fullname).Trim();
if(!string.IsNullOrEmpty(volname))
XmlFsType.VolumeName = (entry.caseinfo & 0x18) > 0 && this.@namespace == Namespace.Nt
? volname.ToLower()
: volname;
XmlFsType.VolumeName =
entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) && this.@namespace == Namespace.Nt
? volname.ToLower()
: volname;
if(entry.ctime > 0 && entry.cdate > 0)
{
@@ -489,6 +492,8 @@ namespace DiscImageChef.Filesystems.FAT
continue;
}
CompleteDirectoryEntry completeEntry = new CompleteDirectoryEntry {Dirent = entry};
if((this.@namespace == Namespace.Lfn || this.@namespace == Namespace.Ecs) && lastLfnName != null)
{
byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension);
@@ -497,11 +502,9 @@ namespace DiscImageChef.Filesystems.FAT
{
filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true);
LFNs.Add(filename);
rootDirectoryCache[filename] = entry;
lastLfnName = null;
lastLfnChecksum = 0;
continue;
completeEntry.Lfn = filename;
lastLfnName = null;
lastLfnChecksum = 0;
}
}
@@ -512,30 +515,32 @@ namespace DiscImageChef.Filesystems.FAT
if(this.@namespace == Namespace.Nt)
{
if((entry.caseinfo & FASTFAT_LOWERCASE_EXTENSION) > 0)
if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseExtension))
extension = extension.ToLower(CultureInfo.CurrentCulture);
if((entry.caseinfo & FASTFAT_LOWERCASE_BASENAME) > 0)
if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseBasename))
name = name.ToLower(CultureInfo.CurrentCulture);
}
if(extension != "") filename = name + "." + extension;
else filename = name;
completeEntry.Shortname = filename;
if(!fat32 && filename == "EA DATA. SF")
{
eaDirEntry = entry;
lastLfnName = null;
lastLfnChecksum = 0;
if(debug) rootDirectoryCache[filename] = entry;
if(debug) rootDirectoryCache[completeEntry.ToString()] = completeEntry;
continue;
}
rootDirectoryCache[filename] = entry;
lastLfnName = null;
lastLfnChecksum = 0;
rootDirectoryCache[completeEntry.ToString()] = completeEntry;
lastLfnName = null;
lastLfnChecksum = 0;
}
XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim();
@@ -608,7 +613,7 @@ namespace DiscImageChef.Filesystems.FAT
// TODO: Check how this affects international filenames
cultureInfo = new CultureInfo("en-US", false);
directoryCache = new Dictionary<string, Dictionary<string, DirectoryEntry>>();
directoryCache = new Dictionary<string, Dictionary<string, CompleteDirectoryEntry>>();
// Check it is really an OS/2 EA file
if(eaDirEntry.start_cluster != 0)
@@ -623,19 +628,17 @@ namespace DiscImageChef.Filesystems.FAT
}
else eaCache = new Dictionary<string, Dictionary<string, byte[]>>();
}
else if(fat32) eaCache = new Dictionary<string, Dictionary<string, byte[]>>();
// Check OS/2 .LONGNAME
if(eaCache != null && (this.@namespace == Namespace.Os2 || this.@namespace == Namespace.Ecs))
{
List<KeyValuePair<string, DirectoryEntry>> rootFilesWithEas =
rootDirectoryCache.Where(t => t.Value.ea_handle != 0).ToList();
List<KeyValuePair<string, CompleteDirectoryEntry>> rootFilesWithEas =
rootDirectoryCache.Where(t => t.Value.Dirent.ea_handle != 0).ToList();
foreach(KeyValuePair<string, DirectoryEntry> fileWithEa in rootFilesWithEas)
foreach(KeyValuePair<string, CompleteDirectoryEntry> fileWithEa in rootFilesWithEas)
{
// This ensures LFN takes preference when eCS is in use
if(LFNs.Contains(fileWithEa.Key)) continue;
Dictionary<string, byte[]> eas = GetEas(fileWithEa.Value.ea_handle);
Dictionary<string, byte[]> eas = GetEas(fileWithEa.Value.Dirent.ea_handle);
if(eas is null) continue;
@@ -658,8 +661,41 @@ namespace DiscImageChef.Filesystems.FAT
// Forward slash is allowed in .LONGNAME, so change it to visually similar division slash
longname = longname.Replace('/', '\u2215');
fileWithEa.Value.Longname = longname;
rootDirectoryCache.Remove(fileWithEa.Key);
rootDirectoryCache[longname] = fileWithEa.Value;
rootDirectoryCache[fileWithEa.Value.ToString()] = fileWithEa.Value;
}
}
// Check FAT32.IFS EAs
if(fat32 || debug)
{
List<KeyValuePair<string, CompleteDirectoryEntry>> fat32EaSidecars = rootDirectoryCache
.Where(t =>
t.Key
.EndsWith(FAT32_EA_TAIL,
true,
cultureInfo))
.ToList();
foreach(KeyValuePair<string, CompleteDirectoryEntry> sidecar in fat32EaSidecars)
{
// No real file this sidecar accompanies
if(!rootDirectoryCache
.TryGetValue(sidecar.Key.Substring(0, sidecar.Key.Length - FAT32_EA_TAIL.Length),
out CompleteDirectoryEntry fileWithEa)) continue;
// If not in debug mode we will consider the lack of EA bitflags to mean the EAs are corrupted or not real
if(!debug)
if(!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEaOld) &&
!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa) &&
!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEa) &&
!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa))
continue;
fileWithEa.Fat32Ea = sidecar.Value.Dirent;
if(!debug) rootDirectoryCache.Remove(sidecar.Key);
}
}