source: trunk/eraser6/Eraser.Util.FileSystem/FatApi.cpp @ 1211

Revision 1211, 9.5 KB checked in by lowjoel, 5 years ago (diff)

Split the Fat32 API from the Fat API source file to keep source file sizes manageable

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2009 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 <windows.h>
24#include <atlstr.h>
25
26#include "FatApi.h"
27
28using namespace System::Collections::Generic;
29using namespace System::IO;
30using namespace System::Runtime::InteropServices;
31using namespace Microsoft::Win32::SafeHandles;
32
33namespace Eraser {
34namespace Util {
35    FatApi::FatApi(VolumeInfo^ info)
36    {
37        SectorSize = info->SectorSize;
38        ClusterSize = info->ClusterSize;
39
40        BootSector = new FatBootSector();
41        memset(BootSector, 0, sizeof(*BootSector));
42        Fat = NULL;
43
44        //Open the handle to the drive
45        CString volumeName(info->VolumeId);
46        volumeName.Truncate(volumeName.GetLength() - 1);
47        VolumeHandle = gcnew SafeFileHandle(static_cast<IntPtr>(CreateFile(volumeName,
48                    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL)),
49            true);
50        VolumeStream = gcnew FileStream(VolumeHandle, FileAccess::Read);
51
52        //Then read the boot sector for information
53        array<Byte>^ bootSector = gcnew array<Byte>(sizeof(*BootSector));
54        VolumeStream->Seek(0, SeekOrigin::Begin);
55        VolumeStream->Read(bootSector, 0, sizeof(*BootSector));
56        Marshal::Copy(bootSector, 0, static_cast<IntPtr>(BootSector), bootSector->Length);
57    }
58
59    FatApi::FatApi(VolumeInfo^ info, Microsoft::Win32::SafeHandles::SafeFileHandle^ handle,
60        IO::FileAccess access)
61    {
62        SectorSize = info->SectorSize;
63        ClusterSize = info->ClusterSize;
64
65        BootSector = new FatBootSector();
66        memset(BootSector, 0, sizeof(*BootSector));
67        Fat = NULL;
68
69        //Open the handle to the drive
70        VolumeHandle = handle;
71        VolumeStream = gcnew FileStream(VolumeHandle, access);
72
73        //Then read the boot sector for information
74        array<Byte>^ bootSector = gcnew array<Byte>(sizeof(*BootSector));
75        VolumeStream->Seek(0, SeekOrigin::Begin);
76        VolumeStream->Read(bootSector, 0, sizeof(*BootSector));
77        Marshal::Copy(bootSector, 0, static_cast<IntPtr>(BootSector), bootSector->Length);
78    }
79
80    FatDirectory^ FatApi::LoadDirectory(String^ directory)
81    {
82        return LoadDirectory(DirectoryToCluster(directory));
83    }
84
85    unsigned long long FatApi::SectorToOffset(unsigned long long sector)
86    {
87        return sector * SectorSize;
88    }
89
90    unsigned FatApi::SectorSizeToSize(unsigned size)
91    {
92        return size * SectorSize;
93    }
94
95    unsigned FatApi::ClusterSizeToSize(unsigned size)
96    {
97        return size * ClusterSize;
98    }
99
100    std::vector<char> FatApi::GetFileContents(unsigned cluster)
101    {
102        if (!IsClusterAllocated(cluster))
103            throw gcnew ArgumentException(L"The specified cluster is not used.");
104
105        std::vector<char> result;
106        result.reserve(FileSize(cluster));
107        array<Byte>^ buffer = gcnew array<Byte>(ClusterSize);
108
109        do
110        {
111            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
112            VolumeStream->Read(buffer, 0, ClusterSize);
113
114            result.insert(result.end(), ClusterSize, 0);
115            Marshal::Copy(buffer, 0, static_cast<IntPtr>(&result.back() - ClusterSize + 1),
116                ClusterSize);
117        }
118        while ((cluster = GetNextCluster(cluster)) != 0xFFFFFFFF);
119
120        return result;
121    }
122
123    void FatApi::SetFileContents(const void* data, size_t length, unsigned cluster)
124    {
125        if (!IsClusterAllocated(cluster))
126            throw gcnew ArgumentException(L"The specified cluster is not used.");
127        if (length != FileSize(cluster))
128            throw gcnew ArgumentException(L"The provided file contents will not fit in the " +
129                gcnew String(L"allocated file."));
130
131        array<Byte>^ buffer = gcnew array<Byte>(ClusterSize);
132        for (size_t i = 0; i < length; i += ClusterSize)
133        {
134            Marshal::Copy(static_cast<IntPtr>(reinterpret_cast<intptr_t>(static_cast<const char*>(data) + i)),
135                buffer, 0, ClusterSize);
136            VolumeStream->Seek(ClusterToOffset(cluster), SeekOrigin::Begin);
137            VolumeStream->Write(buffer, 0, ClusterSize);
138            cluster = GetNextCluster(cluster);
139        }
140    }
141
142    void FatApi::SetFileContents(const std::vector<char>& contents, unsigned cluster)
143    {
144        SetFileContents(&contents.front(), contents.size(), cluster);
145    }
146
147    FatDirectory::FatDirectory(unsigned cluster, FatApi^ api)
148    {
149        Cluster = cluster;
150        Entries = gcnew Dictionary<String^, unsigned>();
151        Api = api;
152
153        //Get the size of the directory list and read it to memory
154        std::vector<char> dir = api->GetFileContents(cluster);
155        const size_t dirCount = dir.size() / sizeof(::FatDirectory);
156        Directory = new ::FatDirectory[dirCount];
157        memcpy(Directory, &dir.front(), dir.size());
158
159        //Parse the directory structures
160        for (::FatDirectory* i = Directory; i != Directory + dirCount; ++i)
161        {
162            //Check if we have checked the last valid entry
163            if (i->Short.Name[0] == 0x00)
164                break;
165
166            //Skip deleted entries.
167            if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
168                continue;
169
170            if (i->Short.Attributes == 0x0F)
171            {
172                //This is a long file name.
173                std::wstring longFileName;
174                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
175                {
176                    if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
177                    {
178                        //Check that the checksum of the file name is the same as the previous
179                        //long file name entry, to ensure no corruption has taken place
180                        if ((i - 1)->LongFileName.Checksum != i->LongFileName.Checksum)
181                            continue;
182
183                        //Check that the sequence is one less than the previous one.
184                        if (sequence != i->LongFileName.Sequence + 1)
185                            throw gcnew ArgumentException(L"Invalid directory entry.");
186                    }
187                   
188                    sequence = i->LongFileName.Sequence & ~0x40;
189                    std::wstring namePart(i->LongFileName.Name1, sizeof(i->LongFileName.Name1) / sizeof(i->LongFileName.Name1[0]));
190                    namePart += std::wstring(i->LongFileName.Name2, sizeof(i->LongFileName.Name2) / sizeof(i->LongFileName.Name2[0]));
191                    namePart += std::wstring(i->LongFileName.Name3, sizeof(i->LongFileName.Name3) / sizeof(i->LongFileName.Name3[0]));
192                    namePart += longFileName;
193                    longFileName = namePart;
194                }
195
196                //Checksum the string
197                unsigned char sum = 0;
198                char* shortFileName = i->Short.Name;
199                for (int j = 11; j; --j)
200                    sum = ((sum & 1) << 7) + (sum >> 1) + *shortFileName++;
201
202                if (sum == (i - 1)->LongFileName.Checksum)
203                {
204                    //fileName contains the correct full long file name, strip the file name of the
205                    //invalid characters.
206                    Entries->Add(gcnew String(longFileName.c_str()), GetStartCluster(*i));
207                }
208            }
209
210            //Skip the dot directories.
211            if (i->Short.Name[0] == '.')
212                continue;
213
214            //Substitute 0x05 with 0xE5
215            if (i->Short.Name[0] == 0x05)
216                i->Short.Name[0] = static_cast<unsigned char>(0xE5);
217           
218            //Then read the 8.3 entry for the file details
219            wchar_t shortFileName[8 + 3 + 2];
220            mbstowcs(shortFileName, i->Short.Name, sizeof(i->Short.Name));
221
222            //If the extension is blank, don't care about it
223            if (strncmp(i->Short.Extension, "   ", 3) == 0)
224            {
225                shortFileName[8] = '\0';
226            }
227            else
228            {
229                mbstowcs(shortFileName + 9, i->Short.Extension, sizeof(i->Short.Extension) + 1);
230                shortFileName[8] = L'.';
231                shortFileName[8 + 3 + 1] = '\0';
232            }
233
234            Entries->Add(gcnew String(shortFileName), GetStartCluster(*i));
235        }
236    }
237
238    unsigned FatDirectory::GetStartCluster(String^ file)
239    {
240        return Entries[file];
241    }
242
243    void FatDirectory::ClearDeletedEntries()
244    {
245        std::vector<::FatDirectory> validEntries;
246        size_t entryCount = Api->FileSize(Cluster) / sizeof(::FatDirectory);
247
248        //Parse the directory structures
249        for (::FatDirectory* i = Directory; i != Directory + entryCount; ++i)
250        {
251            //Check if we have checked the last valid entry
252            if (i->Short.Name[0] == 0x00)
253                break;
254
255            //Skip deleted entries.
256            if (static_cast<unsigned char>(i->Short.Name[0]) == 0xE5)
257                continue;
258
259            if (i->Short.Attributes == 0x0F)
260            {
261                //This is a long file name.
262                ::FatDirectory* longFileNameBegin = i;
263                for (unsigned char sequence = 0; i->Short.Attributes == 0x0F; ++i)
264                {
265                    if (!(i->LongFileName.Sequence & 0x40)) //Second entry onwards
266                    {
267                        //Check that the checksum of the file name is the same as the previous
268                        //long file name entry, to ensure no corruption has taken place
269                        if ((i - 1)->LongFileName.Checksum != i->LongFileName.Checksum)
270                            continue;
271
272                        //Check that the sequence is one less than the previous one.
273                        if (sequence != i->LongFileName.Sequence + 1)
274                            throw gcnew ArgumentException(L"Invalid directory entry.");
275                    }
276                   
277                    sequence = i->LongFileName.Sequence & ~0x40;
278                }
279
280                //Checksum the string
281                unsigned char sum = 0;
282                char* shortFileName = i->Short.Name;
283                for (int j = 11; j; --j)
284                    sum = ((sum & 1) << 7) + (sum >> 1) + *shortFileName++;
285
286                if (sum == (i - 1)->LongFileName.Checksum)
287                {
288                    //The previous few entries contained the correct file name. Save these entries
289                    validEntries.insert(validEntries.end(), longFileNameBegin, i);
290                }
291            }
292
293            validEntries.push_back(*i);
294        }
295
296        //validEntries now contains the compacted list of directory entries. Zero
297        //the memory used.
298        memset(Directory, 0, Api->FileSize(Cluster));
299        memcpy(Directory, &validEntries.front(), validEntries.size() * sizeof(::FatDirectory));
300
301        //Write the entries to disk
302        Api->SetFileContents(Directory, Api->FileSize(Cluster), Cluster);
303    }
304}
305}
Note: See TracBrowser for help on using the repository browser.