source: trunk/eraser/Eraser.Util.Native/FatApi.cpp @ 2960

Revision 2958, 11.3 KB checked in by gtrant, 12 days ago (diff)

Updated Copyright to 2014
Updated versions to 6.2
Fix for date stamp on erased artefacts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
RevLine 
[1206]1/*
2 * $Id$
[2958]3 * Copyright 2008-2014 The Eraser Project
[1206]4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22#include <stdafx.h>
[1242]23#include <vector>
[1206]24#include <windows.h>
25
26#include "FatApi.h"
27
28using namespace System::Collections::Generic;
29using namespace System::IO;
30using namespace System::Runtime::InteropServices;
31
32namespace Eraser {
33namespace Util {
34    FatApi::FatApi(VolumeInfo^ info)
35    {
36        BootSector = new FatBootSector();
37        memset(BootSector, 0, sizeof(*BootSector));
38        Fat = NULL;
[1207]39
40        //Open the handle to the drive
[1219]41        VolumeStream = info->Open(FileAccess::Read);
[1207]42
43        //Then read the boot sector for information
44        array<Byte>^ bootSector = gcnew array<Byte>(sizeof(*BootSector));
45        VolumeStream->Seek(0, SeekOrigin::Begin);
46        VolumeStream->Read(bootSector, 0, sizeof(*BootSector));
47        Marshal::Copy(bootSector, 0, static_cast<IntPtr>(BootSector), bootSector->Length);
[1214]48
49        //Then load the FAT
50        LoadFat();
[1206]51    }
52
[1602]53    FatApi::FatApi(Stream^ stream)
[1207]54    {
55        BootSector = new FatBootSector();
56        memset(BootSector, 0, sizeof(*BootSector));
57        Fat = NULL;
58
59        //Open the handle to the drive
[1219]60        VolumeStream = stream;
[1207]61
62        //Then read the boot sector for information
63        array<Byte>^ bootSector = gcnew array<Byte>(sizeof(*BootSector));
64        VolumeStream->Seek(0, SeekOrigin::Begin);
65        VolumeStream->Read(bootSector, 0, sizeof(*BootSector));
66        Marshal::Copy(bootSector, 0, static_cast<IntPtr>(BootSector), bootSector->Length);
[1219]67
68        //Then load the FAT
69        LoadFat();
[1207]70    }
71
[2732]72    FatApi::!FatApi()
73    {
74        if (BootSector != NULL)
75        {
76            delete BootSector;
77            BootSector = NULL;
78        }
79    }
80
[1226]81    FatDirectoryBase^ FatApi::LoadDirectory(String^ directory)
[1206]82    {
[1222]83        //Return the root directory if nothing is specified
[1238]84        if (String::IsNullOrEmpty(directory))
[1222]85            return LoadDirectory(DirectoryToCluster(directory), String::Empty, nullptr);
86
[1220]87        array<wchar_t>^ pathSeparators = { Path::DirectorySeparatorChar, Path::AltDirectorySeparatorChar };
[1222]88        int lastIndex = directory->LastIndexOfAny(pathSeparators);
89        return LoadDirectory(DirectoryToCluster(directory), directory->Substring(lastIndex + 1),
[1229]90            LoadDirectory(directory->Substring(0, lastIndex)));
[1206]91    }
92
93    unsigned long long FatApi::SectorToOffset(unsigned long long sector)
94    {
[1239]95        return sector * BootSector->BytesPerSector;
[1206]96    }
97
98    unsigned FatApi::SectorSizeToSize(unsigned size)
99    {
[1239]100        return size * BootSector->BytesPerSector;
[1206]101    }
102
103    unsigned FatApi::ClusterSizeToSize(unsigned size)
104    {
[1239]105        return size * (BootSector->BytesPerSector * BootSector->SectorsPerCluster);
[1206]106    }
107
[1242]108    array<Byte>^ FatApi::GetFileContents(unsigned cluster)
[1206]109    {
110        if (!IsClusterAllocated(cluster))
[2000]111            throw gcnew ArgumentException(S::_(L"The specified cluster is not used."));
[1206]112
[1242]113        array<Byte>^ result = gcnew array<Byte>(FileSize(cluster));
114        const int clusterSize = ClusterSizeToSize(1);
115        int nextIndex = 0;
[1206]116
117        do
118        {
119            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
[1242]120            VolumeStream->Read(result, nextIndex, clusterSize);
121            nextIndex += clusterSize;
[1206]122        }
123        while ((cluster = GetNextCluster(cluster)) != 0xFFFFFFFF);
124
125        return result;
126    }
127
[1242]128    void FatApi::SetFileContents(array<Byte>^ buffer, unsigned cluster)
[1206]129    {
130        if (!IsClusterAllocated(cluster))
[2000]131            throw gcnew ArgumentException(S::_(L"The specified cluster is not used."));
[1242]132        if (static_cast<unsigned>(buffer->Length) != FileSize(cluster))
[2000]133            throw gcnew ArgumentException(S::_(L"The provided file contents will not fit in the "
134                L"allocated file."));
[1206]135
[1252]136        int clusterSize = static_cast<int>(ClusterSizeToSize(1));
[1242]137        for (int i = 0; i < buffer->Length; i += clusterSize)
[1206]138        {
139            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
[1242]140            VolumeStream->Write(buffer, i, clusterSize);
[1206]141            cluster = GetNextCluster(cluster);
142        }
143    }
144
[1226]145    FatDirectoryEntry::FatDirectoryEntry(String^ name, FatDirectoryBase^ parent,
[1238]146        FatDirectoryEntryType type, unsigned cluster)
[1206]147    {
[1214]148        Name = name;
[1220]149        Parent = parent;
[1238]150        EntryType = type;
[1206]151        Cluster = cluster;
[1214]152    }
153
154    String^ FatDirectoryEntry::FullName::get()
155    {
156        String^ result = Name;
157        FatDirectoryEntry^ currentEntry = this;
158
159        while (currentEntry->Parent != nullptr)
160        {
161            currentEntry = currentEntry->Parent;
[1220]162            result = currentEntry->Name + Path::DirectorySeparatorChar + result;
[1214]163        }
164
165        return result;
166    }
167
[1226]168    FatDirectoryBase::FatDirectoryBase(String^ name, FatDirectoryBase^ parent, unsigned cluster)
[1238]169        : FatDirectoryEntry(name, parent, FatDirectoryEntryType::Directory, cluster)
[1214]170    {
171        Entries = gcnew Dictionary<String^, FatDirectoryEntry^>();
[1226]172        ReadDirectory();
173    }
[1206]174
[1226]175    void FatDirectoryBase::ClearDeletedEntries()
176    {
177        std::vector<::FatDirectoryEntry> validEntries;
[1206]178
179        //Parse the directory structures
[1226]180        for (::FatDirectoryEntry* i = Directory; i != Directory + DirectorySize; ++i)
[1206]181        {
182            //Check if we have checked the last valid entry
183            if (i->Short.Name[0] == 0x00)
184                break;
185
186            //Skip deleted entries.
187            if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
188                continue;
189
190            if (i->Short.Attributes == 0x0F)
191            {
192                //This is a long file name.
[1226]193                ::FatDirectoryEntry* longFileNameBegin = i;
[1206]194                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
195                {
[1229]196                    if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
197                        continue;
198                    else if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
[1206]199                    {
200                        //Check that the checksum of the file name is the same as the previous
201                        //long file name entry, to ensure no corruption has taken place
202                        if ((i - 1)->LongFileName.Checksum != i->LongFileName.Checksum)
203                            continue;
204
205                        //Check that the sequence is one less than the previous one.
206                        if (sequence != i->LongFileName.Sequence + 1)
[2000]207                            throw gcnew ArgumentException(S::_(L"Invalid directory entry."));
[1206]208                    }
209                   
210                    sequence = i->LongFileName.Sequence & ~0x40;
211                }
212
213                //Checksum the string
214                unsigned char sum = 0;
215                char* shortFileName = i->Short.Name;
216                for (int j = 11; j; --j)
217                    sum = ((sum & 1) << 7) + (sum >> 1) + *shortFileName++;
218
219                if (sum == (i - 1)->LongFileName.Checksum)
220                {
[1226]221                    //The previous few entries contained the correct file name. Save these entries
222                    validEntries.insert(validEntries.end(), longFileNameBegin, i);
[1206]223                }
[1229]224                else
225                {
226                    --i;
227                    continue;
228                }
[1206]229            }
230
[1226]231            validEntries.push_back(*i);
232        }
[1206]233
[1226]234        //validEntries now contains the compacted list of directory entries. Zero
235        //the memory used.
236        memset(Directory, 0, DirectorySize * sizeof(::FatDirectoryEntry));
[1206]237
[1232]238        //Copy the memory back if we have any valid entries. The root directory can
239        //be empty (no . and .. entries)
240        if (!validEntries.empty())
241            memcpy(Directory, &validEntries.front(), validEntries.size() * sizeof(::FatDirectoryEntry));
242
[1226]243        //Write the entries to disk
244        WriteDirectory();
[1206]245    }
246
[1226]247    void FatDirectoryBase::ParseDirectory()
[1206]248    {
[1226]249        //Clear the list of entries
250        Entries->Clear();
[1206]251
252        //Parse the directory structures
[1226]253        for (::FatDirectoryEntry* i = Directory; i != Directory + DirectorySize; ++i)
[1206]254        {
255            //Check if we have checked the last valid entry
256            if (i->Short.Name[0] == 0x00)
257                break;
258
259            //Skip deleted entries.
260            if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
261                continue;
262
263            if (i->Short.Attributes == 0x0F)
264            {
265                //This is a long file name.
[1226]266                std::wstring longFileName;
[1206]267                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
268                {
[1229]269                    if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
270                        continue;
271                    else if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
[1206]272                    {
273                        //Check that the checksum of the file name is the same as the previous
274                        //long file name entry, to ensure no corruption has taken place
275                        if ((i - 1)->LongFileName.Checksum != i->LongFileName.Checksum)
276                            continue;
277
278                        //Check that the sequence is one less than the previous one.
279                        if (sequence != i->LongFileName.Sequence + 1)
[2000]280                            throw gcnew ArgumentException(S::_(L"Invalid directory entry."));
[1206]281                    }
[1229]282                    else
283                        longFileName.clear();
[1206]284                   
285                    sequence = i->LongFileName.Sequence & ~0x40;
[1226]286                    std::wstring namePart(i->LongFileName.Name1, sizeof(i->LongFileName.Name1) / sizeof(i->LongFileName.Name1[0]));
287                    namePart += std::wstring(i->LongFileName.Name2, sizeof(i->LongFileName.Name2) / sizeof(i->LongFileName.Name2[0]));
288                    namePart += std::wstring(i->LongFileName.Name3, sizeof(i->LongFileName.Name3) / sizeof(i->LongFileName.Name3[0]));
289                    namePart += longFileName;
290                    longFileName = namePart;
[1206]291                }
292
293                //Checksum the string
294                unsigned char sum = 0;
295                char* shortFileName = i->Short.Name;
296                for (int j = 11; j; --j)
297                    sum = ((sum & 1) << 7) + (sum >> 1) + *shortFileName++;
298
299                if (sum == (i - 1)->LongFileName.Checksum)
300                {
[1226]301                    //fileName contains the correct full long file name, strip the file name of the
302                    //invalid characters.
303                    String^ fileName = gcnew String(longFileName.c_str());
304                    Entries->Add(fileName, gcnew FatDirectoryEntry(fileName, this,
305                        (i->Short.Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
[1238]306                            FatDirectoryEntryType::Directory : FatDirectoryEntryType::File,
[1226]307                        GetStartCluster(*i)));
[1206]308                }
[1229]309                else
310                {
311                    --i;
312                    continue;
313                }
[1206]314            }
315
[1226]316            //Skip the dot directories.
317            if (i->Short.Name[0] == '.')
318                continue;
319
320            //Substitute 0x05 with 0xE5
321            if (i->Short.Name[0] == 0x05)
322                i->Short.Name[0] = static_cast<unsigned char>(0xE5);
323           
324            //Then read the 8.3 entry for the file details
325            wchar_t shortFileName[8 + 3 + 2];
[1238]326            if (mbstowcs(shortFileName, i->Short.Name, sizeof(i->Short.Name)) != sizeof(i->Short.Name))
327                continue;
[1226]328
329            //If the extension is blank, don't care about it
330            if (strncmp(i->Short.Extension, "   ", 3) == 0)
331            {
332                shortFileName[8] = '\0';
333            }
334            else
335            {
336                mbstowcs(shortFileName + 9, i->Short.Extension, sizeof(i->Short.Extension) + 1);
337                shortFileName[8] = L'.';
338                shortFileName[8 + 3 + 1] = '\0';
339            }
340
341            String^ fileName = gcnew String(shortFileName);
342            Entries->Add(fileName, gcnew FatDirectoryEntry(fileName, this,
343                (i->Short.Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
[1238]344                    FatDirectoryEntryType::Directory : FatDirectoryEntryType::File,
[1226]345                GetStartCluster(*i)));
[1206]346        }
[1226]347    }
[1206]348
[1226]349    FatDirectory::FatDirectory(String^ name, FatDirectoryBase^ parent, unsigned cluster, FatApi^ api)
350        : Api(api),
351          FatDirectoryBase(name, parent, cluster)
352    {
353    }
[1206]354
[2732]355    FatDirectory::!FatDirectory()
356    {
357        if (Directory != NULL)
358        {
359            delete[] Directory;
360            Directory = NULL;
361            DirectorySize = 0;
362        }
363    }
364
[1226]365    void FatDirectory::ReadDirectory()
366    {
[1242]367        array<Byte>^ dir = Api->GetFileContents(Cluster);
368        DirectorySize = dir->Length / sizeof(::FatDirectoryEntry);
[1226]369        Directory = new ::FatDirectoryEntry[DirectorySize];
[1242]370        Marshal::Copy(dir, 0, static_cast<IntPtr>(Directory), dir->Length);
[1226]371
372        ParseDirectory();
373    }
374
375    void FatDirectory::WriteDirectory()
376    {
[1289]377        array<Byte>^ buffer = gcnew array<Byte>(static_cast<int>(DirectorySize *
378            sizeof(::FatDirectoryEntry)));
[1242]379        Marshal::Copy(static_cast<IntPtr>(Directory), buffer, 0, buffer->Length);
380        Api->SetFileContents(buffer, Cluster);
[1206]381    }
382}
383}
Note: See TracBrowser for help on using the repository browser.