/*
- * FreeLoader
- * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
- * Copyright (C) 2009 Hervé Poussineau
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * PROJECT: FreeLoader
+ * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: FAT filesystem driver for FreeLoader
+ * COPYRIGHT: Copyright 1998-2003 Brian Palmer (brianp@sginet.com)
+ * Copyright 2009 Hervé Poussineau
+ * Copyright 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
*/
#include <freeldr.h>
BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG EntryCount, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer);
ARC_STATUS FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, PFAT_FILE_INFO FatFileInfoPointer);
void FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry);
-BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer);
-ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
-ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
-BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer);
+static BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, UINT32 Cluster, PUINT32 ClusterPointer);
+static ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, UINT32 StartCluster);
+static BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, UINT32 StartClusterNumber, UINT32 NumberOfClusters, PVOID Buffer, PUINT32 LastClusterNumber);
BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer);
BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer);
+#define FAT_IS_END_CLUSTER(clnumber) \
+ (((Volume->FatType == FAT12) && (clnumber >= 0xff8)) || \
+ ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (clnumber >= 0xfff8)) || \
+ ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (clnumber >= 0x0ffffff8)))
+
#define TAG_FAT_CHAIN 'CtaT'
#define TAG_FAT_FILE 'FtaF'
#define TAG_FAT_VOLUME 'VtaF'
}
else
{
- if (!FatReadClusterChain(Volume, DirectoryStartCluster, 0xFFFFFFFF, DirectoryBuffer->Data))
+ if (!FatReadClusterChain(Volume, DirectoryStartCluster, 0xFFFFFFFF, DirectoryBuffer->Data, NULL))
{
FrLdrTempFree(DirectoryBuffer, TAG_FAT_BUFFER);
return NULL;
FatFileInfoPointer->Attributes = DirEntry->Attr;
FatFileInfoPointer->FileSize = DirEntry->Size;
FatFileInfoPointer->FilePointer = 0;
+ StartCluster = ((ULONG)DirEntry->ClusterHigh << 16) + DirEntry->ClusterLow;
+ FatFileInfoPointer->CurrentCluster = StartCluster;
+ FatFileInfoPointer->StartCluster = StartCluster;
TRACE("MSDOS Directory Entry:\n");
TRACE("FileName[11] = %c%c%c%c%c%c%c%c%c%c%c\n", DirEntry->FileName[0], DirEntry->FileName[1], DirEntry->FileName[2], DirEntry->FileName[3], DirEntry->FileName[4], DirEntry->FileName[5], DirEntry->FileName[6], DirEntry->FileName[7], DirEntry->FileName[8], DirEntry->FileName[9], DirEntry->FileName[10]);
TRACE("Date = %d\n", DirEntry->Date);
TRACE("ClusterLow = 0x%x\n", DirEntry->ClusterLow);
TRACE("Size = %d\n", DirEntry->Size);
-
- //
- // Get the cluster chain
- //
- StartCluster = ((ULONG)DirEntry->ClusterHigh << 16) + DirEntry->ClusterLow;
TRACE("StartCluster = 0x%x\n", StartCluster);
- FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, StartCluster);
-
- //
- // See if memory allocation failed
- //
- if (FatFileInfoPointer->FileFatChain == NULL)
- {
- return FALSE;
- }
return TRUE;
}
FatFileInfoPointer->Attributes = DirEntry->Attr;
FatFileInfoPointer->FileSize = DirEntry->Size;
FatFileInfoPointer->FilePointer = 0;
+ FatFileInfoPointer->CurrentCluster = DirEntry->StartCluster;
+ FatFileInfoPointer->StartCluster = DirEntry->StartCluster;
TRACE("FATX Directory Entry:\n");
TRACE("FileNameSize = %d\n", DirEntry->FileNameSize);
TRACE("LastAccessTime = %d\n", DirEntry->LastAccessTime);
TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
- /*
- * Get the cluster chain
- */
- FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, DirEntry->StartCluster);
-
- /*
- * See if memory allocation failed
- */
- if (NULL == FatFileInfoPointer->FileFatChain)
- {
- return FALSE;
- }
-
return TRUE;
}
}
//
if (!(FatFileInfo.Attributes & ATTR_DIRECTORY))
{
- FrLdrTempFree(FatFileInfo.FileFatChain, TAG_FAT_CHAIN);
return ENOTDIR;
}
- DirectoryStartCluster = FatFileInfo.FileFatChain[0];
- FrLdrTempFree(FatFileInfo.FileFatChain, TAG_FAT_CHAIN);
- FatFileInfo.FileFatChain = NULL;
+ DirectoryStartCluster = FatFileInfo.StartCluster;
}
}
* FatGetFatEntry()
* returns the Fat entry for a given cluster number
*/
-BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer)
+static
+BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, UINT32 Cluster, PUINT32 ClusterPointer)
{
UINT32 FatOffset, ThisFatSecNum, ThisFatEntOffset, fat;
PUCHAR ReadBuffer;
ReadBuffer = FatGetFatSector(Volume, ThisFatSecNum);
if (!ReadBuffer)
{
- Success = FALSE;
- break;
+ return FALSE;
}
// Get the fat entry
return TRUE;
}
-ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
+static
+ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, UINT32 StartCluster)
{
ULONG ClusterCount = 0;
//
// If end of chain then break out of our cluster counting loop
//
- if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
- ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
- ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
+ if (FAT_IS_END_CLUSTER(StartCluster))
{
break;
}
return ClusterCount;
}
-ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
+static
+BOOLEAN FatReadAdjacentClusters(
+ PFAT_VOLUME_INFO Volume,
+ UINT32 StartClusterNumber,
+ UINT32 MaxClusters,
+ PVOID Buffer,
+ PUINT32 ClustersRead,
+ PUINT32 LastClusterNumber)
{
- ULONG ClusterCount;
- ULONG ArraySize;
- ULONG* ArrayPointer;
- ULONG Idx;
+ UINT32 NextClusterNumber;
+ UINT32 ClustersToRead = 1;
+ UINT32 PrevClusterNumber = StartClusterNumber;
+ UINT32 ClusterStartSector = ((PrevClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
- TRACE("FatGetClusterChainArray() StartCluster = %d\n", StartCluster);
-
- ClusterCount = FatCountClustersInChain(Volume, StartCluster) + 1; // Lets get the 0x0ffffff8 on the end of the array
- ArraySize = ClusterCount * sizeof(ULONG);
-
- //
- // Allocate array memory
- //
- ArrayPointer = FrLdrTempAlloc(ArraySize, TAG_FAT_CHAIN);
+ *ClustersRead = 0;
+ *LastClusterNumber = 0;
- if (ArrayPointer == NULL)
+ if (!FatGetFatEntry(Volume, StartClusterNumber, &NextClusterNumber))
{
- return NULL;
+ return FALSE;
}
- //
- // Loop through and set array values
- //
- for (Idx=0; Idx<ClusterCount; Idx++)
+ // getting the number of adjacent clusters
+ while (!FAT_IS_END_CLUSTER(NextClusterNumber) && ClustersToRead < MaxClusters && (NextClusterNumber == PrevClusterNumber + 1))
{
- //
- // Set current cluster
- //
- ArrayPointer[Idx] = StartCluster;
-
- //
- // Don't try to get next cluster for last cluster
- //
- if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
- ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
- ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
+ ClustersToRead++;
+ PrevClusterNumber = NextClusterNumber;
+ if (!FatGetFatEntry(Volume, PrevClusterNumber, &NextClusterNumber))
{
- Idx++;
- break;
+ return FALSE;
}
+ }
- //
- // Get next cluster
- //
- if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
- {
- FrLdrTempFree(ArrayPointer, TAG_FAT_CHAIN);
- return NULL;
- }
+ if (!FatReadVolumeSectors(Volume, ClusterStartSector, ClustersToRead * Volume->SectorsPerCluster, Buffer))
+ {
+ return FALSE;
}
- return ArrayPointer;
+ *ClustersRead = ClustersToRead;
+ *LastClusterNumber = NextClusterNumber;
+
+ return !FAT_IS_END_CLUSTER(NextClusterNumber) && ClustersToRead < MaxClusters;
}
/*
* FatReadClusterChain()
* Reads the specified clusters into memory
*/
-BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer)
+static
+BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, UINT32 StartClusterNumber, UINT32 NumberOfClusters, PVOID Buffer, PUINT32 LastClusterNumber)
{
- ULONG ClusterStartSector;
+ UINT32 ClustersRead, NextClusterNumber, ClustersLeft = NumberOfClusters;
TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
- while (NumberOfClusters > 0)
- {
+ ASSERT(NumberOfClusters > 0);
- //TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
- //
- // Calculate starting sector for cluster
- //
- ClusterStartSector = ((StartClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
-
- //
- // Read cluster into memory
- //
- if (!FatReadVolumeSectors(Volume, ClusterStartSector, Volume->SectorsPerCluster, Buffer))
- {
- return FALSE;
- }
-
- //
- // Decrement count of clusters left to read
- //
- NumberOfClusters--;
-
- //
- // Increment buffer address by cluster size
- //
- Buffer = (PVOID)((ULONG_PTR)Buffer + (Volume->SectorsPerCluster * Volume->BytesPerSector));
-
- //
- // Get next cluster
- //
- if (!FatGetFatEntry(Volume, StartClusterNumber, &StartClusterNumber))
- {
- return FALSE;
- }
+ while (FatReadAdjacentClusters(Volume, StartClusterNumber, ClustersLeft, Buffer, &ClustersRead, &NextClusterNumber))
+ {
+ ClustersLeft -= ClustersRead;
+ Buffer = (PVOID)((ULONG_PTR)Buffer + (ClustersRead * Volume->SectorsPerCluster * Volume->BytesPerSector));
+ StartClusterNumber = NextClusterNumber;
+ }
- //
- // If end of chain then break out of our cluster reading loop
- //
- if (((Volume->FatType == FAT12) && (StartClusterNumber >= 0xff8)) ||
- ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartClusterNumber >= 0xfff8)) ||
- ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartClusterNumber >= 0x0ffffff8)))
- {
- break;
- }
+ if (LastClusterNumber)
+ {
+ *LastClusterNumber = NextClusterNumber;
}
- return TRUE;
+ return (ClustersRead > 0);
}
/*
* Reads BytesToRead from open file and
* returns the number of bytes read in BytesRead
*/
+static
BOOLEAN FatReadFile(PFAT_FILE_INFO FatFileInfo, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
{
PFAT_VOLUME_INFO Volume = FatFileInfo->Volume;
- ULONG ClusterNumber;
- ULONG OffsetInCluster;
- ULONG LengthInCluster;
- ULONG NumberOfClusters;
- ULONG BytesPerCluster;
+ UINT32 NextClusterNumber, BytesPerCluster;
TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer);
//
// Do the math for our first read
//
- ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
- ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
- OffsetInCluster = (FatFileInfo->FilePointer % BytesPerCluster);
- LengthInCluster = (BytesToRead > (BytesPerCluster - OffsetInCluster)) ? (BytesPerCluster - OffsetInCluster) : BytesToRead;
+ UINT32 OffsetInCluster = FatFileInfo->FilePointer % BytesPerCluster;
+ UINT32 LengthInCluster = min(BytesToRead, BytesPerCluster - OffsetInCluster);
+
+ ASSERT(LengthInCluster <= BytesPerCluster && LengthInCluster > 0);
//
// Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
//
- if (!FatReadPartialCluster(Volume, ClusterNumber, OffsetInCluster, LengthInCluster, Buffer))
+ if (!FatReadPartialCluster(Volume, FatFileInfo->CurrentCluster, OffsetInCluster, LengthInCluster, Buffer))
{
return FALSE;
}
BytesToRead -= LengthInCluster;
FatFileInfo->FilePointer += LengthInCluster;
Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInCluster);
+
+ // get the next cluster if needed
+ if ((LengthInCluster + OffsetInCluster) == BytesPerCluster)
+ {
+ if (!FatGetFatEntry(Volume, FatFileInfo->CurrentCluster, &NextClusterNumber))
+ {
+ return FALSE;
+ }
+
+ FatFileInfo->CurrentCluster = NextClusterNumber;
+ TRACE("FatReadFile() FatFileInfo->CurrentCluster = 0x%x\n", FatFileInfo->CurrentCluster);
+ }
}
//
//
// Determine how many full clusters we need to read
//
- NumberOfClusters = (BytesToRead / BytesPerCluster);
+ UINT32 NumberOfClusters = BytesToRead / BytesPerCluster;
+
+ TRACE("Going to read: %u clusters\n", NumberOfClusters);
if (NumberOfClusters > 0)
{
- ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
- ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
+ UINT32 BytesReadHere = NumberOfClusters * BytesPerCluster;
- //
- // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
- //
- if (!FatReadClusterChain(Volume, ClusterNumber, NumberOfClusters, Buffer))
+ ASSERT(!FAT_IS_END_CLUSTER(FatFileInfo->CurrentCluster));
+
+ if (!FatReadClusterChain(Volume, FatFileInfo->CurrentCluster, NumberOfClusters, Buffer, &NextClusterNumber))
{
return FALSE;
}
+
if (BytesRead != NULL)
{
- *BytesRead += (NumberOfClusters * BytesPerCluster);
+ *BytesRead += BytesReadHere;
}
- BytesToRead -= (NumberOfClusters * BytesPerCluster);
- FatFileInfo->FilePointer += (NumberOfClusters * BytesPerCluster);
- Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfClusters * BytesPerCluster));
+ BytesToRead -= BytesReadHere;
+ Buffer = (PVOID)((ULONG_PTR)Buffer + BytesReadHere);
+
+ ASSERT(!FAT_IS_END_CLUSTER(NextClusterNumber) || BytesToRead == 0);
+
+ FatFileInfo->FilePointer += BytesReadHere;
+ FatFileInfo->CurrentCluster = NextClusterNumber;
+ TRACE("FatReadFile() FatFileInfo->CurrentCluster = 0x%x\n", FatFileInfo->CurrentCluster);
}
}
//
if (BytesToRead > 0)
{
- ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
- ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
+ ASSERT(!FAT_IS_END_CLUSTER(FatFileInfo->CurrentCluster));
//
// Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
//
- if (!FatReadPartialCluster(Volume, ClusterNumber, 0, BytesToRead, Buffer))
+ if (!FatReadPartialCluster(Volume, FatFileInfo->CurrentCluster, 0, BytesToRead, Buffer))
{
return FALSE;
}
{
PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
- if (FileHandle->FileFatChain) FrLdrTempFree(FileHandle->FileFatChain, TAG_FAT_CHAIN);
FrLdrTempFree(FileHandle, TAG_FAT_FILE);
return ESUCCESS;
ARC_STATUS FatSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
{
PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
+ PFAT_VOLUME_INFO Volume = FileHandle->Volume;
LARGE_INTEGER NewPosition = *Position;
switch (SeekMode)
case SeekAbsolute:
break;
case SeekRelative:
- NewPosition.QuadPart += (ULONGLONG)FileHandle->FilePointer;
+ NewPosition.QuadPart += (UINT64)FileHandle->FilePointer;
break;
default:
ASSERT(FALSE);
if (NewPosition.LowPart >= FileHandle->FileSize)
return EINVAL;
+ TRACE("FatSeek() NewPosition = %u, OldPointer = %u, SeekMode = %d\n", NewPosition.LowPart, FileHandle->FilePointer, SeekMode);
+
+ {
+ UINT32 OldClusterIdx = FileHandle->FilePointer / (Volume->SectorsPerCluster * Volume->BytesPerSector);
+ UINT32 NewClusterIdx = NewPosition.LowPart / (Volume->SectorsPerCluster * Volume->BytesPerSector);
+
+ TRACE("FatSeek() OldClusterIdx: %u, NewClusterIdx: %u\n", OldClusterIdx, NewClusterIdx);
+
+ if (NewClusterIdx != OldClusterIdx)
+ {
+ UINT32 CurrentClusterIdx, ClusterNumber;
+
+ if (NewClusterIdx > OldClusterIdx)
+ {
+ CurrentClusterIdx = OldClusterIdx;
+ ClusterNumber = FileHandle->CurrentCluster;
+ }
+ else
+ {
+ CurrentClusterIdx = 0;
+ ClusterNumber = FileHandle->StartCluster;
+ }
+
+ for (; CurrentClusterIdx < NewClusterIdx; CurrentClusterIdx++)
+ {
+ if (!FatGetFatEntry(Volume, ClusterNumber, &ClusterNumber))
+ {
+ return EIO;
+ }
+ }
+ FileHandle->CurrentCluster = ClusterNumber;
+ }
+ }
+
FileHandle->FilePointer = NewPosition.LowPart;
+
return ESUCCESS;
}