Implemented MmMapViewOfSection().
[reactos.git] / freeldr / freeldr / fs_fat.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1999, 2000 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "freeldr.h"
21 #include "fs.h"
22 #include "stdlib.h"
23 #include "tui.h"
24 #include "asmcode.h"
25
26 int nFATType = FAT12;
27
28 DWORD nBytesPerSector; // Bytes per sector
29 DWORD nSectorsPerCluster; // Number of sectors in a cluster
30 DWORD nReservedSectors; // Reserved sectors, usually 1 (the bootsector)
31 DWORD nNumberOfFATs; // Number of FAT tables
32 DWORD nRootDirEntries; // Number of root directory entries (fat12/16)
33 DWORD nTotalSectors16; // Number of total sectors on the drive, 16-bit
34 DWORD nSectorsPerFAT16; // Sectors per FAT table (fat12/16)
35 DWORD nSectorsPerTrack; // Number of sectors in a track
36 DWORD nNumberOfHeads; // Number of heads on the disk
37 DWORD nHiddenSectors; // Hidden sectors (sectors before the partition start like the partition table)
38 DWORD nTotalSectors32; // Number of total sectors on the drive, 32-bit
39
40 DWORD nSectorsPerFAT32; // Sectors per FAT table (fat32)
41 DWORD nExtendedFlags; // Extended flags (fat32)
42 DWORD nFileSystemVersion; // File system version (fat32)
43 DWORD nRootDirStartCluster; // Starting cluster of the root directory (fat32)
44
45 DWORD nRootDirSectorStart; // Starting sector of the root directory (fat12/16)
46 DWORD nDataSectorStart; // Starting sector of the data area
47 DWORD nSectorsPerFAT; // Sectors per FAT table
48 DWORD nRootDirSectors; // Number of sectors of the root directory (fat32)
49 DWORD nTotalSectors; // Total sectors on the drive
50 DWORD nNumberOfClusters; // Number of clusters on the drive
51
52 BOOL FATReadRootDirectoryEntry(int nDirEntry, void *pDirEntryBuf)
53 {
54 DWORD nDirEntrySector;
55 int nOffsetWithinSector;
56
57 nDirEntrySector = nRootDirSectorStart + ((nDirEntry * 32) / nBytesPerSector);
58
59 if (!ReadOneSector(nDirEntrySector))
60 return FALSE;
61
62 nOffsetWithinSector = (nDirEntry * 32) % nBytesPerSector;
63
64 memcpy(pDirEntryBuf, SectorBuffer + nOffsetWithinSector, 32);
65
66 if (*((char *)pDirEntryBuf) == 0x05)
67 *((char *)pDirEntryBuf) = 0xE5;
68
69 return TRUE;
70 }
71
72 BOOL FATReadDirectoryEntry(DWORD nDirStartCluster, int nDirEntry, void *pDirEntryBuf)
73 {
74 DWORD nRealDirCluster;
75 int nSectorWithinCluster;
76 int nOffsetWithinSector;
77 int nSectorToRead;
78 int i;
79
80 i = (nDirEntry * 32) / (nSectorsPerCluster * nBytesPerSector);
81 //if ((nDirEntry * 32) % (nSectorsPerCluster * nBytesPerSector))
82 // i++;
83
84 for (nRealDirCluster = nDirStartCluster; i; i--)
85 nRealDirCluster = FATGetFATEntry(nRealDirCluster);
86
87 nSectorWithinCluster = ((nDirEntry * 32) % (nSectorsPerCluster * nBytesPerSector)) / nBytesPerSector;
88
89 nSectorToRead = ((nRealDirCluster - 2) * nSectorsPerCluster) + nDataSectorStart + nSectorWithinCluster;
90
91 if (!ReadOneSector(nSectorToRead))
92 return FALSE;
93
94 nOffsetWithinSector = (nDirEntry * 32) % nBytesPerSector;
95
96 memcpy(pDirEntryBuf, SectorBuffer + nOffsetWithinSector, 32);
97
98 if (*((char *)pDirEntryBuf) == 0x05)
99 *((char *)pDirEntryBuf) = 0xE5;
100
101 return TRUE;
102 }
103
104 /*
105 * FATLookupFile()
106 * This function searches the file system for the
107 * specified filename and fills in a FAT_STRUCT structure
108 * with info describing the file, etc. returns true
109 * if the file exists or false otherwise
110 */
111 BOOL FATLookupFile(char *file, PFAT_STRUCT pFatStruct)
112 {
113 int i, j;
114 int numparts;
115 char filename[12];
116 BYTE direntry[32];
117 int nNumDirEntries;
118 FAT_STRUCT fatstruct;
119 BOOL bFound;
120 DWORD cluster;
121
122 memset(pFatStruct, 0, sizeof(FAT_STRUCT));
123
124 // Check and see if the first character is '\' and remove it if so
125 if (*file == '\\')
126 file++;
127
128 // Figure out how many sub-directories we are nested in
129 numparts = FATGetNumPathParts(file);
130
131 // Loop once for each part
132 for (i=0; i<numparts; i++)
133 {
134 // Make filename compatible with MSDOS dir entry
135 if (!FATGetFirstNameFromPath(filename, file))
136 return FALSE;
137 // Advance to the next part of the path
138 for (; (*file != '\\') && (*file != '\0'); file++);
139 file++;
140
141 // If we didn't find the correct sub-directory the fail
142 if ((i != 0) && !bFound)
143 return FALSE;
144
145 bFound = FALSE;
146
147 // Check if we are pulling from the root directory of a fat12/fat16 disk
148 if ((i == 0) && ((nFATType == FAT12) || (nFATType == FAT16)))
149 nNumDirEntries = nRootDirEntries;
150 else if ((i == 0) && (nFATType == FAT32))
151 {
152 cluster = nRootDirStartCluster;
153 fatstruct.dwSize = nSectorsPerCluster * nBytesPerSector;
154 while((cluster = FATGetFATEntry(cluster)) < 0x0FFFFFF8)
155 fatstruct.dwSize += nSectorsPerCluster * nBytesPerSector;
156
157 fatstruct.dwStartCluster = nRootDirStartCluster;
158 nNumDirEntries = fatstruct.dwSize / 32;
159 }
160 else
161 nNumDirEntries = fatstruct.dwSize / 32;
162
163 // Loop through each directory entry
164 for (j=0; j<nNumDirEntries; j++)
165 {
166 // Read the entry
167 if ((i == 0) && ((nFATType == FAT12) || (nFATType == FAT16)))
168 {
169 if (!FATReadRootDirectoryEntry(j, direntry))
170 return FALSE;
171 }
172 else
173 {
174 if (!FATReadDirectoryEntry(fatstruct.dwStartCluster, j, direntry))
175 return FALSE;
176 }
177
178 if (memcmp(direntry, filename, 11) == 0)
179 {
180 fatstruct.dwStartCluster = 0;
181 fatstruct.dwCurrentCluster = 0;
182 memcpy(&fatstruct.dwStartCluster, direntry + 26, sizeof(WORD));
183 memcpy(&fatstruct.dwCurrentCluster, direntry + 20, sizeof(WORD));
184 fatstruct.dwStartCluster += (fatstruct.dwCurrentCluster * 0x10000);
185
186 if (direntry[11] & ATTR_DIRECTORY)
187 {
188 fatstruct.dwSize = nSectorsPerCluster * nBytesPerSector;
189 cluster = fatstruct.dwStartCluster;
190 switch (nFATType)
191 {
192 case FAT12:
193 while((cluster = FATGetFATEntry(cluster)) < 0xFF8)
194 fatstruct.dwSize += nSectorsPerCluster * nBytesPerSector;
195 break;
196 case FAT16:
197 while((cluster = FATGetFATEntry(cluster)) < 0xFFF8)
198 fatstruct.dwSize += nSectorsPerCluster * nBytesPerSector;
199 break;
200 case FAT32:
201 while((cluster = FATGetFATEntry(cluster)) < 0x0FFFFFF8)
202 fatstruct.dwSize += nSectorsPerCluster * nBytesPerSector;
203 break;
204 }
205 }
206
207 // If we have more parts to go and this isn't a directory then fail
208 if ((i < (numparts-1)) && !(direntry[11] & ATTR_DIRECTORY))
209 return FALSE;
210
211 // If this is supposed to be a file and it is a directory then fail
212 if ((i == (numparts-1)) && (direntry[11] & ATTR_DIRECTORY))
213 return FALSE;
214
215 bFound = TRUE;
216 break;
217 }
218 }
219 }
220
221 if(!bFound)
222 return FALSE;
223
224 memcpy(&pFatStruct->dwStartCluster, direntry + 26, sizeof(WORD));
225 memcpy(&pFatStruct->dwCurrentCluster, direntry + 20, sizeof(WORD));
226 pFatStruct->dwStartCluster += (pFatStruct->dwCurrentCluster * 0x10000);
227 pFatStruct->dwCurrentCluster = pFatStruct->dwStartCluster;
228
229 memcpy(&pFatStruct->dwSize, direntry + 28, sizeof(DWORD));
230 pFatStruct->dwCurrentReadOffset = 0;
231
232 return TRUE;
233 }
234
235 /*
236 * FATGetNumPathParts()
237 * This function parses a path in the form of dir1\dir2\file1.ext
238 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
239 */
240 int FATGetNumPathParts(char *name)
241 {
242 int i, num;
243
244 for(i=0,num=0; i<(int)strlen(name); i++)
245 {
246 if(name[i] == '\\')
247 num++;
248 }
249 num++;
250
251 return num;
252 }
253
254 /*
255 * FATGetFirstNameFromPath()
256 * This function parses a path in the form of dir1\dir2\file1.ext
257 * and puts the first name of the path (e.g. "dir1") in buffer
258 * compatible with the MSDOS directory structure
259 */
260 BOOL FATGetFirstNameFromPath(char *buffer, char *name)
261 {
262 int i;
263 char temp[260];
264
265 // Copy all the characters up to the end of the
266 // string or until we hit a '\' character
267 // and put them in temp
268 for(i=0; i<(int)strlen(name); i++)
269 {
270 if(name[i] == '\\')
271 break;
272 else
273 temp[i] = name[i];
274 }
275 temp[i] = 0;
276
277 // If the filename is too long then fail
278 if(strlen(temp) > 12)
279 return FALSE;
280
281 FATParseFileName(buffer, temp);
282
283 return TRUE;
284 }
285
286 /*
287 * FATParseFileName()
288 * This function parses "name" which is in the form of file.ext
289 * and puts it in "buffer" in the form of "FILE EXT" which
290 * is compatible with the MSDOS directory structure
291 */
292 void FATParseFileName(char *buffer, char *name)
293 {
294 int i, j;
295
296 i = 0;
297 j = 0;
298
299 while(i < 8)
300 buffer[i++] = (name[j] && (name[j] != '.')) ? toupper(name[j++]) : ' ';
301
302 if(name[j] == '.')
303 j++;
304
305 while(i < 11)
306 buffer[i++] = name[j] ? toupper(name[j++]) : ' ';
307
308 buffer[i] = 0;
309 }
310
311 /*
312 * FATGetFATEntry()
313 * returns the FAT entry for a given cluster number
314 */
315 DWORD FATGetFATEntry(DWORD nCluster)
316 {
317 DWORD fat;
318 int FATOffset;
319 int ThisFATSecNum;
320 int ThisFATEntOffset;
321 int Idx;
322 BOOL bEntryFound;
323
324 switch(nFATType)
325 {
326 case FAT12:
327 FATOffset = nCluster + (nCluster / 2);
328 ThisFATSecNum = (FATOffset / nBytesPerSector);
329 ThisFATEntOffset = (FATOffset % nBytesPerSector);
330 fat = *((WORD *) (pFileSysData + (ThisFATSecNum * nBytesPerSector) + ThisFATEntOffset));
331 if (nCluster & 0x0001)
332 fat = fat >> 4; /* Cluster number is ODD */
333 else
334 fat = fat & 0x0FFF; /* Cluster number is EVEN */
335
336 return fat;
337 break;
338 case FAT16:
339 FATOffset = (nCluster * 2);
340 //ThisFATSecNum = nReservedSectors + (FATOffset / nBytesPerSector);
341 //ThisFATEntOffset = (FATOffset % nBytesPerSector);
342
343 //if (!ReadOneSector(ThisFATSecNum))
344 // return NULL;
345
346 //fat = *((WORD *) &SectorBuffer[ThisFATEntOffset]);
347 fat = *((WORD *) (pFileSysData + FATOffset));
348
349 return fat;
350 break;
351 case FAT32:
352 //if (!ReadOneSector(ThisFATSecNum))
353 // return NULL;
354
355 //fat = *((DWORD *) &SectorBuffer[ThisFATEntOffset]) & 0x0FFFFFFF;
356 //return fat;
357
358 // This code manages the fat32 fat table entry cache
359 // The cache is at address FILESYSADDR which is 128k in size
360 // The first two sectors will contain an array of DWORDs that
361 // Specify what fat sector is cached. The first two DWORDs
362 // should be zero.
363 FATOffset = (nCluster * 4);
364 ThisFATSecNum = nReservedSectors + (FATOffset / nBytesPerSector);
365 ThisFATEntOffset = (FATOffset % nBytesPerSector);
366
367 // Now we go through our cache and see if we already have the sector cached
368 bEntryFound = FALSE;
369 for (Idx=2; Idx<256; Idx++)
370 {
371 if (((int*)pFat32FATCacheIndex)[Idx] == ThisFATSecNum)
372 {
373 bEntryFound = TRUE;
374 break;
375 }
376 }
377
378 if (bEntryFound)
379 {
380 // Get the fat entry
381 fat = (*((DWORD *) (pFat32FATCacheIndex +
382 (Idx * nBytesPerSector) + ThisFATEntOffset))) & 0x0FFFFFFF;
383 }
384 else
385 {
386 if (!ReadOneSector(ThisFATSecNum))
387 return NULL;
388
389 // Move each sector down in the cache to make room for new sector
390 for (Idx=255; Idx>2; Idx--)
391 {
392 memcpy(pFat32FATCacheIndex + (Idx * nBytesPerSector), pFat32FATCacheIndex + ((Idx - 1) * nBytesPerSector), nBytesPerSector);
393 ((int*)pFat32FATCacheIndex)[Idx] = ((int*)pFat32FATCacheIndex)[Idx - 1];
394 }
395
396 // Insert it into the cache
397 memcpy(pFat32FATCacheIndex + (2 * nBytesPerSector), SectorBuffer, nBytesPerSector);
398 ((int*)pFat32FATCacheIndex)[2] = ThisFATSecNum;
399
400 // Get the fat entry
401 fat = (*((DWORD *) (pFat32FATCacheIndex +
402 (2 * nBytesPerSector) + ThisFATEntOffset))) & 0x0FFFFFFF;
403 }
404
405 return fat;
406 break;
407 }
408
409 return NULL;
410 }
411
412 /*
413 * FATOpenFile()
414 * Tries to open the file 'name' and returns true or false
415 * for success and failure respectively
416 */
417 BOOL FATOpenFile(char *szFileName, PFAT_STRUCT pFatStruct)
418 {
419 if(!FATLookupFile(szFileName, pFatStruct))
420 return FALSE;
421
422 /* Fill in file control information */
423 pFatStruct->dwCurrentCluster = pFatStruct->dwStartCluster;
424 pFatStruct->dwCurrentReadOffset = 0;
425
426 return TRUE;
427 }
428
429 /*
430 * FATReadCluster()
431 * Reads the specified cluster into memory
432 * and returns the number of bytes read
433 */
434 int FATReadCluster(DWORD nCluster, char *cBuffer)
435 {
436 int nStartSector;
437
438 nStartSector = ((nCluster - 2) * nSectorsPerCluster) + nDataSectorStart;
439
440 ReadMultipleSectors(nStartSector, nSectorsPerCluster, cBuffer);
441
442 return (nSectorsPerCluster * nBytesPerSector);
443 }
444
445 /*
446 * FATRead()
447 * Reads nNumBytes from open file and
448 * returns the number of bytes read
449 */
450 int FATRead(PFAT_STRUCT pFatStruct, int nNumBytes, char *cBuffer)
451 {
452 int nSectorWithinCluster;
453 int nOffsetWithinSector;
454 int nOffsetWithinCluster;
455 int nNum;
456 int nBytesRead = 0;
457
458 // If all the data is read return zero
459 if (pFatStruct->dwCurrentReadOffset >= pFatStruct->dwSize)
460 return 0;
461
462 // If they are trying to read more than there is to read
463 // then adjust the amount to read
464 if ((pFatStruct->dwCurrentReadOffset + nNumBytes) > pFatStruct->dwSize)
465 nNumBytes = pFatStruct->dwSize - pFatStruct->dwCurrentReadOffset;
466
467 while (nNumBytes)
468 {
469 // Check and see if the read offset is aligned to a cluster boundary
470 // if so great, if not then read the rest of the current cluster
471 if ((pFatStruct->dwCurrentReadOffset % (nSectorsPerCluster * nBytesPerSector)) != 0)
472 {
473 nSectorWithinCluster = ((pFatStruct->dwCurrentReadOffset / nBytesPerSector) % nSectorsPerCluster);
474 nOffsetWithinSector = (pFatStruct->dwCurrentReadOffset % nBytesPerSector);
475 nOffsetWithinCluster = (pFatStruct->dwCurrentReadOffset % (nSectorsPerCluster * nBytesPerSector));
476
477 // Read the cluster into the scratch area
478 FATReadCluster(pFatStruct->dwCurrentCluster, (char *)FATCLUSTERBUF);
479
480 nNum = (nSectorsPerCluster * nBytesPerSector) - (pFatStruct->dwCurrentReadOffset % (nSectorsPerCluster * nBytesPerSector));
481 if (nNumBytes >= nNum)
482 {
483 memcpy(cBuffer, (char *)(FATCLUSTERBUF + nOffsetWithinCluster), nNum);
484 nBytesRead += nNum;
485 cBuffer += nNum;
486 pFatStruct->dwCurrentReadOffset += nNum;
487 pFatStruct->dwCurrentCluster = FATGetFATEntry(pFatStruct->dwCurrentCluster);
488 nNumBytes -= nNum;
489 }
490 else
491 {
492 memcpy(cBuffer, (char *)(FATCLUSTERBUF + nOffsetWithinCluster), nNumBytes);
493 nBytesRead += nNumBytes;
494 cBuffer += nNumBytes;
495 pFatStruct->dwCurrentReadOffset += nNumBytes;
496 nNumBytes -= nNumBytes;
497 }
498 }
499 else
500 {
501 // Read the cluster into the scratch area
502 FATReadCluster(pFatStruct->dwCurrentCluster, (char *)FATCLUSTERBUF);
503
504 nNum = (nSectorsPerCluster * nBytesPerSector);
505 if (nNumBytes >= nNum)
506 {
507 memcpy(cBuffer, (char *)(FATCLUSTERBUF), nNum);
508 nBytesRead += nNum;
509 cBuffer += nNum;
510 pFatStruct->dwCurrentReadOffset += nNum;
511 pFatStruct->dwCurrentCluster = FATGetFATEntry(pFatStruct->dwCurrentCluster);
512 nNumBytes -= nNum;
513 }
514 else
515 {
516 memcpy(cBuffer, (char *)(FATCLUSTERBUF), nNumBytes);
517 nBytesRead += nNumBytes;
518 cBuffer += nNumBytes;
519 pFatStruct->dwCurrentReadOffset += nNumBytes;
520 nNumBytes -= nNumBytes;
521 }
522 }
523 }
524
525 return nBytesRead;
526 }
527
528 int FATfseek(PFAT_STRUCT pFatStruct, DWORD offset)
529 {
530 DWORD cluster;
531 int numclusters;
532
533 numclusters = offset / (nSectorsPerCluster * nBytesPerSector);
534 for (cluster=pFatStruct->dwStartCluster; numclusters > 0; numclusters--)
535 cluster = FATGetFATEntry(cluster);
536
537 pFatStruct->dwCurrentCluster = cluster;
538 pFatStruct->dwCurrentReadOffset = offset;
539
540 return 0;
541 }