如何提取FAT磁盘映像?使用C#

8

我实际上正在尝试使用DiskUtils提取一个Fat磁盘映像,但是我没有得到正确的文件名...

我会得到"\TURNER~3\TOPPER~1.P~1"代替"\TURNEROVER\TOPPERSHEATH.PPTX"

FatFileSystem FatImg = new FatFileSystem(MS); //MS = Fat Image MemoryStream
foreach(DiscDirectoryInfo Di in FatImg.Root.GetDirectories())
{
    foreach(DiscFileInfo Fi in Di.GetFiles())
    {
        Stream St = Fi.OpenRead(); // Correct Stream
        string FName = Fi.Name; //Wrong Name
    }
}

这是因为DiscUtils不支持长文件名(LFN)...

所以我正在寻找一个完美的库来提取这些文件,然后再尝试自己制作一个...

有没有办法可以提取它(也许通过DiscUtils),而不会出现文件名错误...


你有现成的FAT镜像文件可以拿来玩吗? - Simon Mourier
1
获取示例Fat镜像 - Writwick
5个回答

3
以下是与DiscUtils支持FAT LFN相关的修改内容:
首先,您需要对Fat\Directory.cs文件进行如下更改(需要添加_lfns变量、GetLfnChunk函数,并修改现有的LoadEntries函数以添加下面标有//+++的行):
internal Dictionary<string, string> _lfns = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

private static string GetLfnChunk(byte[] buffer)
{
    // see http://home.teleport.com/~brainy/lfn.htm
    // NOTE: we assume ordinals are ok here.
    char[] chars = new char[13];
    chars[0] = (char)(256 * buffer[2] + buffer[1]);
    chars[1] = (char)(256 * buffer[4] + buffer[3]);
    chars[2] = (char)(256 * buffer[6] + buffer[5]);
    chars[3] = (char)(256 * buffer[8] + buffer[7]);
    chars[4] = (char)(256 * buffer[10] + buffer[9]);

    chars[5] = (char)(256 * buffer[15] + buffer[14]);
    chars[6] = (char)(256 * buffer[17] + buffer[16]);
    chars[7] = (char)(256 * buffer[19] + buffer[18]);
    chars[8] = (char)(256 * buffer[21] + buffer[20]);
    chars[9] = (char)(256 * buffer[23] + buffer[22]);
    chars[10] = (char)(256 * buffer[25] + buffer[24]);

    chars[11] = (char)(256 * buffer[29] + buffer[28]);
    chars[12] = (char)(256 * buffer[31] + buffer[30]);
    string chunk = new string(chars);
    int zero = chunk.IndexOf('\0');
    return zero >= 0 ? chunk.Substring(0, zero) : chunk;
}

private void LoadEntries()
{
    _entries = new Dictionary<long, DirectoryEntry>();
    _freeEntries = new List<long>();

    _selfEntryLocation = -1;
    _parentEntryLocation = -1;

    string lfn = null;  //+++
    while (_dirStream.Position < _dirStream.Length)
    {
        long streamPos = _dirStream.Position;
        DirectoryEntry entry = new DirectoryEntry(_fileSystem.FatOptions, _dirStream);

        if (entry.Attributes == (FatAttributes.ReadOnly | FatAttributes.Hidden | FatAttributes.System | FatAttributes.VolumeId))
        {
            // Long File Name entry
            _dirStream.Position = streamPos;  //+++
            lfn = GetLfnChunk(Utilities.ReadFully(_dirStream, 32)) + lfn;  //+++
        }
        else if (entry.Name.IsDeleted())
        {
            // E5 = Free Entry
            _freeEntries.Add(streamPos);
            lfn = null; //+++
        }
        else if (entry.Name == FileName.SelfEntryName)
        {
            _selfEntry = entry;
            _selfEntryLocation = streamPos;
            lfn = null; //+++
        }
        else if (entry.Name == FileName.ParentEntryName)
        {
            _parentEntry = entry;
            _parentEntryLocation = streamPos;
            lfn = null; //+++
        }
        else if (entry.Name == FileName.Null)
        {
            // Free Entry, no more entries available
            _endOfEntries = streamPos;
            lfn = null; //+++
            break;
        }
        else
        {
            if (lfn != null) //+++
            { //+++
                _lfns.Add(entry.Name.GetDisplayName(_fileSystem.FatOptions.FileNameEncoding), lfn); //+++
            } //+++
            _entries.Add(streamPos, entry);
            lfn = null; //+++
        }
    }
}

其次,在Fat\FatFileSystem.cs文件中添加以下两个公共函数。它们将成为查询LFN的新API:

/// <summary>
/// Gets the long name of a given file.
/// </summary>
/// <param name="shortFullPath">The short full path to the file. Input path segments must be short names.</param>
/// <returns>The corresponding long file name.</returns>
public string GetLongFileName(string shortFullPath)
{
    if (shortFullPath == null)
        throw new ArgumentNullException("shortFullPath");

    string dirPath = Path.GetDirectoryName(shortFullPath);
    string fileName = Path.GetFileName(shortFullPath);
    Directory dir = GetDirectory(dirPath);
    if (dir == null)
        return fileName;

    string lfn;
    if (dir._lfns.TryGetValue(Path.GetFileName(shortFullPath), out lfn))
        return lfn;

    return fileName;
}

/// <summary>
/// Gets the long path to a given file.
/// </summary>
/// <param name="shortFullPath">The short full path to the file. Input path segments must be short names.</param>
/// <returns>The corresponding long file path to the file or null if not found.</returns>
public string GetLongFilePath(string shortFullPath)
{
    if (shortFullPath == null)
        throw new ArgumentNullException("shortFullPath");

    string path = null;
    string current = null;
    foreach (string segment in shortFullPath.Split(Path.DirectorySeparatorChar))
    {
        if (current == null)
        {
            current = segment;
            path = GetLongFileName(current);
        }
        else
        {
            current = Path.Combine(current, segment);
            path = Path.Combine(path, GetLongFileName(current));
        }
    }
    return path;
}

就是这样。现在,你可以像下面这样递归地转储整个FAT磁盘:

static void Main(string[] args)
{
    using (FileStream fs = File.Open("fat.ima", FileMode.Open))
    {
        using (FatFileSystem floppy = new FatFileSystem(fs))
        {
            Dump(floppy.Root);
        }
    }
}

static void Dump(DiscDirectoryInfo di)
{
    foreach (DiscDirectoryInfo subdi in di.GetDirectories())
    {
        Dump(subdi);
    }
    foreach (DiscFileInfo fi in di.GetFiles())
    {
        Console.WriteLine(fi.FullName);
        // get LFN name
        Console.WriteLine(" " + ((FatFileSystem)di.FileSystem).GetLongFileName(fi.FullName));


        // get LFN-ed full path
        Console.WriteLine(" " + ((FatFileSystem)di.FileSystem).GetLongFilePath(fi.FullName));
    }
}

请自行承担风险!:)


你在处理这个吗??我已经决定在赏金结束后编辑DiscUtils文件!无论如何,谢谢!一旦赏金结束,将会增加60 [赏金+10(+1)]! - Writwick
@WritwickDas - 没有,但我觉得这个挑战很有趣,而且我是赏金猎人 :-) - Simon Mourier
赏金归于“赏金猎人”!! - Writwick

2

你确定它可以提取任何类型的文件系统吗?例如Fat16或者Fat12...我有一个FAT16文件,我试图用GUI打开它,但是报告了无效归档的错误...但我会尝试一下...[我之前知道SevenZipSharp,但由于7-Zip本身无法打开该文件,所以我没有尝试过] - Writwick
我已经使用它来处理软盘镜像,并且它支持vhd硬盘镜像,因此我期望它可以提取任何类型的FAT。 - Arne
精确的错误信息是 “无法将'文件名'打开为存档!” 刚刚检查过!Winimage可以正确地编辑我拥有的Fat! - Writwick
这确实表明了另一种情况。不幸的是,我没有可用于测试的FAT16镜像。恐怕你必须找到另一个解决方案。 - Arne

1

使用命令行的最佳方法是使用以下命令:

7z "x " + 源路径 + " -o" + 目标路径 + " * -r"

  • "x " = 提取;保留文件夹结构
  • 源路径 = 磁盘映像路径
  • " -o [目标路径]" = 提取到的目标位置
  • " *" = 所有文件
  • " -r" = 递归

在C#中,您可以使用我制作的此方法(我将7z.exe复制到了应用程序的Debug文件夹中)。

public void ExtractDiskImage(string pathToDiskImage, string extractPath, bool WaitForFinish)
{
    ProcessStartInfo UnzipDiskImage = new ProcessStartInfo("7z.exe");
    StringBuilder str = new StringBuilder();
    str.Append("x ");
    str.Append(pathToDiskImage);
    str.Append(" -o");
    str.Append(extractPath);
    str.Append(" * -r");
    UnzipDiskImage.Arguments = str.ToString();
    UnzipDiskImage.WindowStyle = ProcessWindowStyle.Hidden;
    Process process = Process.Start(UnzipDiskImage);
    if(WaitForFinish == true)
    {
        process.WaitForExit(); //My app had to wait for the extract to finish
    }
}

1

看起来 DiscUtils 中的 FAT 没有长文件名支持。看看这篇帖子吧。 我相信你已经意识到了,因为看起来你是提出了这个问题。


你说得对,我在那里问了这个问题...所以我在这里问是否有任何我错过的替代方案... - Writwick

0

DOS DIR命令使用/X开关显示长文件名和短文件名。

否则,有一个实用程序可以保存LFN:DOSLFNBk.exe,它可以帮助创建映射表。

我知道这不是你要找的答案,但除了手动创建表格以便在没有DiskUtil的情况下进行映射之外,我想不到其他实用程序或方法来完成你的目标 - 但正如你提到的,在SO上可能会有人知道替代方法。

来自DiskUtils的Kevin提到,如果他包括对LFN的支持,他将侵犯Mirosoft的专利,我不建议您侵犯(绝对不是商业项目),但如果这只是个人或者如果您能找到像7-Zip这样的许可证库......

你的截图显示VNext.. RTM版本是否有同样的错误?


“你的截图显示VNext.. RTM版本也有同样的错误?”这句话是什么意思?我不明白...我该如何在FAT磁盘映像上运行带/X开关的DOS DIR命令?另外,据我所知,“DOS LFN Backup”不是免费的[我以前找到过这个实用程序,但不知道它不是免费的,也不能在磁盘映像上使用]。无论如何,感谢您的建议。 - Writwick
现在已经确认DOSLFNBK有一个免费的1.6版本,但是该版本不支持FAT32... FAT32出现在2.0和2.3版本之间。 - Writwick
vNext == VS 2011,抱歉下次我会做更多研究。 - Jeremy Thompson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接