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

Revision 2958, 11.3 KB checked in by gtrant, 5 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
Line 
1/*
2 * $Id$
3 * Copyright 2008-2014 The Eraser Project
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>
23#include <vector>
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;
39
40        //Open the handle to the drive
41        VolumeStream = info->Open(FileAccess::Read);
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);
48
49        //Then load the FAT
50        LoadFat();
51    }
52
53    FatApi::FatApi(Stream^ stream)
54    {
55        BootSector = new FatBootSector();
56        memset(BootSector, 0, sizeof(*BootSector));
57        Fat = NULL;
58
59        //Open the handle to the drive
60        VolumeStream = stream;
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);
67
68        //Then load the FAT
69        LoadFat();
70    }
71
72    FatApi::!FatApi()
73    {
74        if (BootSector != NULL)
75        {
76            delete BootSector;
77            BootSector = NULL;
78        }
79    }
80
81    FatDirectoryBase^ FatApi::LoadDirectory(String^ directory)
82    {
83        //Return the root directory if nothing is specified
84        if (String::IsNullOrEmpty(directory))
85            return LoadDirectory(DirectoryToCluster(directory), String::Empty, nullptr);
86
87        array<wchar_t>^ pathSeparators = { Path::DirectorySeparatorChar, Path::AltDirectorySeparatorChar };
88        int lastIndex = directory->LastIndexOfAny(pathSeparators);
89        return LoadDirectory(DirectoryToCluster(directory), directory->Substring(lastIndex + 1),
90            LoadDirectory(directory->Substring(0, lastIndex)));
91    }
92
93    unsigned long long FatApi::SectorToOffset(unsigned long long sector)
94    {
95        return sector * BootSector->BytesPerSector;
96    }
97
98    unsigned FatApi::SectorSizeToSize(unsigned size)
99    {
100        return size * BootSector->BytesPerSector;
101    }
102
103    unsigned FatApi::ClusterSizeToSize(unsigned size)
104    {
105        return size * (BootSector->BytesPerSector * BootSector->SectorsPerCluster);
106    }
107
108    array<Byte>^ FatApi::GetFileContents(unsigned cluster)
109    {
110        if (!IsClusterAllocated(cluster))
111            throw gcnew ArgumentException(S::_(L"The specified cluster is not used."));
112
113        array<Byte>^ result = gcnew array<Byte>(FileSize(cluster));
114        const int clusterSize = ClusterSizeToSize(1);
115        int nextIndex = 0;
116
117        do
118        {
119            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
120            VolumeStream->Read(result, nextIndex, clusterSize);
121            nextIndex += clusterSize;
122        }
123        while ((cluster = GetNextCluster(cluster)) != 0xFFFFFFFF);
124
125        return result;
126    }
127
128    void FatApi::SetFileContents(array<Byte>^ buffer, unsigned cluster)
129    {
130        if (!IsClusterAllocated(cluster))
131            throw gcnew ArgumentException(S::_(L"The specified cluster is not used."));
132        if (static_cast<unsigned>(buffer->Length) != FileSize(cluster))
133            throw gcnew ArgumentException(S::_(L"The provided file contents will not fit in the "
134                L"allocated file."));
135
136        int clusterSize = static_cast<int>(ClusterSizeToSize(1));
137        for (int i = 0; i < buffer->Length; i += clusterSize)
138        {
139            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
140            VolumeStream->Write(buffer, i, clusterSize);
141            cluster = GetNextCluster(cluster);
142        }
143    }
144
145    FatDirectoryEntry::FatDirectoryEntry(String^ name, FatDirectoryBase^ parent,
146        FatDirectoryEntryType type, unsigned cluster)
147    {
148        Name = name;
149        Parent = parent;
150        EntryType = type;
151        Cluster = cluster;
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;
162            result = currentEntry->Name + Path::DirectorySeparatorChar + result;
163        }
164
165        return result;
166    }
167
168    FatDirectoryBase::FatDirectoryBase(String^ name, FatDirectoryBase^ parent, unsigned cluster)
169        : FatDirectoryEntry(name, parent, FatDirectoryEntryType::Directory, cluster)
170    {
171        Entries = gcnew Dictionary<String^, FatDirectoryEntry^>();
172        ReadDirectory();
173    }
174
175    void FatDirectoryBase::ClearDeletedEntries()
176    {
177        std::vector<::FatDirectoryEntry> validEntries;
178
179        //Parse the directory structures
180        for (::FatDirectoryEntry* i = Directory; i != Directory + DirectorySize; ++i)
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.
193                ::FatDirectoryEntry* longFileNameBegin = i;
194                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
195                {
196                    if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
197                        continue;
198                    else if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
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)
207                            throw gcnew ArgumentException(S::_(L"Invalid directory entry."));
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                {
221                    //The previous few entries contained the correct file name. Save these entries
222                    validEntries.insert(validEntries.end(), longFileNameBegin, i);
223                }
224                else
225                {
226                    --i;
227                    continue;
228                }
229            }
230
231            validEntries.push_back(*i);
232        }
233
234        //validEntries now contains the compacted list of directory entries. Zero
235        //the memory used.
236        memset(Directory, 0, DirectorySize * sizeof(::FatDirectoryEntry));
237
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
243        //Write the entries to disk
244        WriteDirectory();
245    }
246
247    void FatDirectoryBase::ParseDirectory()
248    {
249        //Clear the list of entries
250        Entries->Clear();
251
252        //Parse the directory structures
253        for (::FatDirectoryEntry* i = Directory; i != Directory + DirectorySize; ++i)
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.
266                std::wstring longFileName;
267                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
268                {
269                    if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
270                        continue;
271                    else if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
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)
280                            throw gcnew ArgumentException(S::_(L"Invalid directory entry."));
281                    }
282                    else
283                        longFileName.clear();
284                   
285                    sequence = i->LongFileName.Sequence & ~0x40;
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;
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                {
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) ?
306                            FatDirectoryEntryType::Directory : FatDirectoryEntryType::File,
307                        GetStartCluster(*i)));
308                }
309                else
310                {
311                    --i;
312                    continue;
313                }
314            }
315
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];
326            if (mbstowcs(shortFileName, i->Short.Name, sizeof(i->Short.Name)) != sizeof(i->Short.Name))
327                continue;
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) ?
344                    FatDirectoryEntryType::Directory : FatDirectoryEntryType::File,
345                GetStartCluster(*i)));
346        }
347    }
348
349    FatDirectory::FatDirectory(String^ name, FatDirectoryBase^ parent, unsigned cluster, FatApi^ api)
350        : Api(api),
351          FatDirectoryBase(name, parent, cluster)
352    {
353    }
354
355    FatDirectory::!FatDirectory()
356    {
357        if (Directory != NULL)
358        {
359            delete[] Directory;
360            Directory = NULL;
361            DirectorySize = 0;
362        }
363    }
364
365    void FatDirectory::ReadDirectory()
366    {
367        array<Byte>^ dir = Api->GetFileContents(Cluster);
368        DirectorySize = dir->Length / sizeof(::FatDirectoryEntry);
369        Directory = new ::FatDirectoryEntry[DirectorySize];
370        Marshal::Copy(dir, 0, static_cast<IntPtr>(Directory), dir->Length);
371
372        ParseDirectory();
373    }
374
375    void FatDirectory::WriteDirectory()
376    {
377        array<Byte>^ buffer = gcnew array<Byte>(static_cast<int>(DirectorySize *
378            sizeof(::FatDirectoryEntry)));
379        Marshal::Copy(static_cast<IntPtr>(Directory), buffer, 0, buffer->Length);
380        Api->SetFileContents(buffer, Cluster);
381    }
382}
383}
Note: See TracBrowser for help on using the repository browser.