summaryrefslogtreecommitdiffstats
path: root/private/ntos/fw/duobase/mips/fatboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/fw/duobase/mips/fatboot.c')
-rw-r--r--private/ntos/fw/duobase/mips/fatboot.c2307
1 files changed, 2307 insertions, 0 deletions
diff --git a/private/ntos/fw/duobase/mips/fatboot.c b/private/ntos/fw/duobase/mips/fatboot.c
new file mode 100644
index 000000000..a4facb64d
--- /dev/null
+++ b/private/ntos/fw/duobase/mips/fatboot.c
@@ -0,0 +1,2307 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ fatboot.c
+
+Abstract:
+
+ This module implements the FAT boot file system used by the operating
+ system loader.
+
+Author:
+
+ Gary Kimura (garyki) 29-Aug-1989
+
+Revision History:
+
+--*/
+
+#include "fwp.h"
+#include "stdio.h"
+
+//
+// Conditional debug print routine
+//
+
+#ifdef FATBOOTDBG
+
+#define FatDebugOutput(X,Y,Z) { \
+ if (BlConsoleOutDeviceId) { \
+ CHAR _b[128]; \
+ ULONG _c; \
+ sprintf(&_b[0], X, Y, Z); \
+ ArcWrite(BlConsoleOutDeviceId, &_b[0], strlen(&_b[0]), &_c); \
+ } \
+}
+
+#else
+
+#define FatDebugOutput(X,Y,Z) {NOTHING;}
+
+#endif // FATBOOTDBG
+
+
+//
+// Low level disk I/O procedure prototypes
+//
+
+ARC_STATUS
+FatDiskRead (
+ IN ULONG DeviceId,
+ IN LBO Lbo,
+ IN ULONG ByteCount,
+ IN PVOID Buffer
+ );
+
+#define DiskRead(A,B,C,D) { ARC_STATUS _s; \
+ if ((_s = FatDiskRead(A,B,C,D)) != ESUCCESS) { return _s; } \
+}
+
+
+//
+// Cluster/Index routines
+//
+
+typedef enum _CLUSTER_TYPE {
+ FatClusterAvailable,
+ FatClusterReserved,
+ FatClusterBad,
+ FatClusterLast,
+ FatClusterNext
+} CLUSTER_TYPE;
+
+CLUSTER_TYPE
+FatInterpretClusterType (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN FAT_ENTRY Entry
+ );
+
+ARC_STATUS
+FatLookupFatEntry (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN FAT_ENTRY FatIndex,
+ OUT PFAT_ENTRY FatEntry
+ );
+
+
+LBO
+FatIndexToLbo (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN FAT_ENTRY FatIndex
+ );
+
+VOID
+FatLboToIndex (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN LBO Lbo,
+ OUT PFAT_ENTRY FatIndex,
+ OUT PULONG ByteOffset
+ );
+
+#define LookupFatEntry(A,B,C,D) { ARC_STATUS _s; \
+ if ((_s = FatLookupFatEntry(A,B,C,D)) != ESUCCESS) { return _s; } \
+}
+
+
+
+//
+// Directory routines
+//
+
+ARC_STATUS
+FatSearchForDirent (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN FAT_ENTRY DirectoriesStartingIndex,
+ IN PFAT8DOT3 FileName,
+ OUT PDIRENT Dirent,
+ OUT PLBO Lbo
+ );
+
+ARC_STATUS
+FatCreateDirent (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN FAT_ENTRY DirectoriesStartingIndex,
+ IN PDIRENT Dirent,
+ OUT PLBO Lbo
+ );
+
+VOID
+FatSetDirent (
+ IN PFAT8DOT3 FileName,
+ IN OUT PDIRENT Dirent,
+ IN UCHAR Attributes
+ );
+
+#define SearchForDirent(A,B,C,D,E,F) { ARC_STATUS _s; \
+ if ((_s = FatSearchForDirent(A,B,C,D,E,F)) != ESUCCESS) { return _s; } \
+}
+
+#define CreateDirent(A,B,C,D,E) { ARC_STATUS _s; \
+ if ((_s = FatCreateDirent(A,B,C,D,E)) != ESUCCESS) { return _s; } \
+}
+
+
+//
+// Allocation and mcb routines
+//
+
+ARC_STATUS
+FatLoadMcb (
+ IN ULONG FileId,
+ IN VBO StartingVbo
+ );
+
+ARC_STATUS
+FatVboToLbo (
+ IN ULONG FileId,
+ IN VBO Vbo,
+ OUT PLBO Lbo,
+ OUT PULONG ByteCount
+ );
+
+ARC_STATUS
+FatIncreaseFileAllocation (
+ IN ULONG FileId,
+ IN ULONG ByteSize
+ );
+
+ARC_STATUS
+FatAllocateClusters (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN ULONG ClusterCount,
+ IN FAT_ENTRY Hint,
+ OUT PFAT_ENTRY AllocatedEntry
+ );
+
+#define LoadMcb(A,B) { ARC_STATUS _s; \
+ if ((_s = FatLoadMcb(A,B)) != ESUCCESS) { return _s; } \
+}
+
+#define VboToLbo(A,B,C,D) { ARC_STATUS _s; \
+ if ((_s = FatVboToLbo(A,B,C,D)) != ESUCCESS) { return _s; } \
+}
+
+#define IncreaseFileAllocation(A,B) { ARC_STATUS _s; \
+ if ((_s = FatIncreaseFileAllocation(A,B)) != ESUCCESS) { return _s; } \
+}
+
+#define AllocateClusters(A,B,C,D,E) { ARC_STATUS _s; \
+ if ((_s = FatAllocateClusters(A,B,C,D,E)) != ESUCCESS) { return _s; } \
+}
+
+
+//
+// Miscellaneous routines
+//
+
+VOID
+FatFirstComponent (
+ IN OUT PSTRING String,
+ OUT PFAT8DOT3 FirstComponent
+ );
+
+#define AreNamesEqual(X,Y) ( \
+ ((*(X))[0]==(*(Y))[0]) && ((*(X))[1]==(*(Y))[1]) && ((*(X))[2]==(*(Y))[2]) && \
+ ((*(X))[3]==(*(Y))[3]) && ((*(X))[4]==(*(Y))[4]) && ((*(X))[5]==(*(Y))[5]) && \
+ ((*(X))[6]==(*(Y))[6]) && ((*(X))[7]==(*(Y))[7]) && ((*(X))[8]==(*(Y))[8]) && \
+ ((*(X))[9]==(*(Y))[9]) && ((*(X))[10]==(*(Y))[10]) ? TRUE : FALSE \
+)
+
+#define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
+
+#define FlagOn(Flags,SingleFlag) ((BOOLEAN)(((Flags) & (SingleFlag)) != 0 ? TRUE : FALSE))
+#define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); }
+#define ClearFlag(Flags,SingleFlag) { (Flags) &= ~(SingleFlag); }
+
+#define FatFirstFatAreaLbo(B) ( (B)->ReservedSectors * (B)->BytesPerSector )
+
+#define Minimum(X,Y) ((X) < (Y) ? (X) : (Y))
+
+//
+// The following types and macros are used to help unpack the packed and
+// misaligned fields found in the Bios parameter block
+//
+
+typedef union _UCHAR1 { UCHAR Uchar[1]; UCHAR ForceAlignment; } UCHAR1, *PUCHAR1;
+typedef union _UCHAR2 { UCHAR Uchar[2]; USHORT ForceAlignment; } UCHAR2, *PUCHAR2;
+typedef union _UCHAR4 { UCHAR Uchar[4]; ULONG ForceAlignment; } UCHAR4, *PUCHAR4;
+
+#define CopyUchar1(Dst,Src) { \
+ ((PUCHAR1)(Dst))->Uchar[0] = ((PUCHAR1)(Src))->Uchar[0]; \
+ }
+
+#define CopyUchar2(Dst,Src) { \
+ ((PUCHAR2)(Dst))->Uchar[0] = ((PUCHAR2)(Src))->Uchar[0]; \
+ ((PUCHAR2)(Dst))->Uchar[1] = ((PUCHAR2)(Src))->Uchar[1]; \
+ }
+
+#define CopyUchar4(Dst,Src) { \
+ ((PUCHAR4)(Dst))->Uchar[0] = ((PUCHAR4)(Src))->Uchar[0]; \
+ ((PUCHAR4)(Dst))->Uchar[1] = ((PUCHAR4)(Src))->Uchar[1]; \
+ ((PUCHAR4)(Dst))->Uchar[2] = ((PUCHAR4)(Src))->Uchar[2]; \
+ ((PUCHAR4)(Dst))->Uchar[3] = ((PUCHAR4)(Src))->Uchar[3]; \
+ }
+
+//
+// DirectoryEntry routines
+//
+
+VOID
+FatDirToArcDir
+ (
+ IN PDIRENT FatDirEnt,
+ OUT PDIRECTORY_ENTRY ArcDirEnt
+ );
+
+VOID
+FatNameToArcName
+ (
+ IN FAT8DOT3 FatName,
+ OUT PCHAR ArcName,
+ OUT PULONG ArcNameLength
+ );
+
+
+//
+// Define global data.
+//
+
+//
+// File entry table - This is a structure that provides entry to the FAT
+// file system procedures. It is exported when a FAT file structure
+// is recognized.
+//
+
+BL_DEVICE_ENTRY_TABLE FatDeviceEntryTable;
+
+
+PBL_DEVICE_ENTRY_TABLE
+IsFatFileStructure (
+ IN ULONG DeviceId,
+ IN PVOID StructureContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine determines if the partition on the specified channel
+ contains a FAT file system volume.
+
+Arguments:
+
+ DeviceId - Supplies the file table index for the device on which
+ read operations are to be performed.
+
+ StructureContext - Supplies a pointer to a FAT file structure context.
+
+Return Value:
+
+ A pointer to the FAT entry table is returned if the partition is
+ recognized as containing a FAT volume. Otherwise, NULL is returned.
+
+--*/
+
+{
+ PPACKED_BOOT_SECTOR BootSector;
+ UCHAR Buffer[sizeof(PACKED_BOOT_SECTOR)+256];
+
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+
+ FatDebugOutput("IsFatFileStructure\r\n", 0, 0);
+
+ //
+ // Clear the file system context block for the specified channel and
+ // establish a pointer to the context structure that can be used by other
+ // routines
+ //
+
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)StructureContext;
+ RtlZeroMemory(FatStructureContext, sizeof(FAT_STRUCTURE_CONTEXT));
+
+ //
+ // Setup and read in the boot sector for the potential fat partition
+ //
+
+ BootSector = (PPACKED_BOOT_SECTOR)ALIGN_BUFFER( &Buffer[0] );
+
+ if (FatDiskRead(DeviceId, 0, sizeof(PACKED_BOOT_SECTOR), BootSector) != ESUCCESS) {
+
+ return NULL;
+ }
+
+ //
+ // Unpack the Bios parameter block
+ //
+
+ FatUnpackBios(&FatStructureContext->Bpb, &BootSector->PackedBpb);
+
+ //
+ // Check if it is fat
+ //
+
+ if ((BootSector->Jump[0] != 0xeb) &&
+ (BootSector->Jump[0] != 0xe9)) {
+
+ return NULL;
+
+ } else if ((FatStructureContext->Bpb.BytesPerSector != 128) &&
+ (FatStructureContext->Bpb.BytesPerSector != 256) &&
+ (FatStructureContext->Bpb.BytesPerSector != 512) &&
+ (FatStructureContext->Bpb.BytesPerSector != 1024)) {
+
+ return NULL;
+
+ } else if ((FatStructureContext->Bpb.SectorsPerCluster != 1) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 2) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 4) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 8) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 16) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 32) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 64) &&
+ (FatStructureContext->Bpb.SectorsPerCluster != 128)) {
+
+ return NULL;
+
+ } else if (FatStructureContext->Bpb.ReservedSectors == 0) {
+
+ return NULL;
+
+ } else if (FatStructureContext->Bpb.Fats == 0) {
+
+ return NULL;
+
+ } else if (FatStructureContext->Bpb.RootEntries == 0) {
+
+ return NULL;
+
+ } else if (((FatStructureContext->Bpb.Sectors == 0) && (FatStructureContext->Bpb.LargeSectors == 0)) ||
+ ((FatStructureContext->Bpb.Sectors != 0) && (FatStructureContext->Bpb.LargeSectors != 0))) {
+
+ return NULL;
+
+ } else if (FatStructureContext->Bpb.SectorsPerFat == 0) {
+
+ return NULL;
+
+ } else if ((FatStructureContext->Bpb.Media != 0xf0) &&
+ (FatStructureContext->Bpb.Media != 0xf8) &&
+ (FatStructureContext->Bpb.Media != 0xf9) &&
+ (FatStructureContext->Bpb.Media != 0xfc) &&
+ (FatStructureContext->Bpb.Media != 0xfd) &&
+ (FatStructureContext->Bpb.Media != 0xfe) &&
+ (FatStructureContext->Bpb.Media != 0xff)) {
+
+ return NULL;
+ }
+
+ //
+ // Initialize the file entry table and return the address of the table.
+ //
+
+ FatDeviceEntryTable.Open = FatOpen;
+ FatDeviceEntryTable.Close = FatClose;
+ FatDeviceEntryTable.Read = FatRead;
+ FatDeviceEntryTable.Seek = FatSeek;
+ FatDeviceEntryTable.Write = NULL;
+ FatDeviceEntryTable.GetFileInformation = FatGetFileInformation;
+ FatDeviceEntryTable.SetFileInformation = NULL;
+ FatDeviceEntryTable.Rename = NULL; ;
+ FatDeviceEntryTable.GetDirectoryEntry = FatGetDirectoryEntry;
+
+ return &FatDeviceEntryTable;
+}
+
+ARC_STATUS
+FatClose (
+ IN ULONG FileId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine closes the file specified by the file id.
+
+Arguments:
+
+ FileId - Supplies the file table index.
+
+Return Value:
+
+ ESUCCESS if returned as the function value.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+ ULONG DeviceId;
+
+ FatDebugOutput("FatClose\r\n", 0, 0);
+
+ //
+ // Load our local variables
+ //
+
+ FileTableEntry = &BlFileTable[FileId];
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
+ DeviceId = FileTableEntry->DeviceId;
+
+ //
+ // Mark the file closed
+ //
+
+ BlFileTable[FileId].Flags.Open = 0;
+
+ //
+ // Check if the current mcb is for this file and if it is then zero it out.
+ // By setting the file id for the mcb to be the table size we guarantee that
+ // we've just set it to an invalid file id.
+ //
+
+ if (FatStructureContext->FileId == FileId) {
+
+ FatStructureContext->FileId = BL_FILE_TABLE_SIZE;
+ FatStructureContext->Mcb.InUse = 0;
+ }
+
+ return ESUCCESS;
+}
+
+
+ARC_STATUS
+FatGetFileInformation (
+ IN ULONG FileId,
+ OUT PFILE_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure returns to the user a buffer filled with file information
+
+Arguments:
+
+ FileId - Supplies the File id for the operation
+
+ Buffer - Supplies the buffer to receive the file information. Note that
+ it must be large enough to hold the full file name
+
+Return Value:
+
+ ESUCCESS is returned if the open operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ UCHAR Attributes;
+ ULONG i;
+
+ FatDebugOutput("FatGetFileInformation\r\n", 0, 0);
+
+ //
+ // Load our local variables
+ //
+
+ FileTableEntry = &BlFileTable[FileId];
+ Attributes = FileTableEntry->u.FatFileContext.Dirent.Attributes;
+
+ //
+ // Zero out the buffer, and fill in its non-zero values.
+ //
+
+ RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
+
+ Buffer->EndingAddress.LowPart = FileTableEntry->u.FatFileContext.Dirent.FileSize;
+
+ Buffer->CurrentPosition.LowPart = FileTableEntry->Position.LowPart;
+ Buffer->CurrentPosition.HighPart = 0;
+
+ if (FlagOn(Attributes, FAT_DIRENT_ATTR_READ_ONLY)) { SetFlag(Buffer->Attributes, ArcReadOnlyFile) };
+ if (FlagOn(Attributes, FAT_DIRENT_ATTR_HIDDEN)) { SetFlag(Buffer->Attributes, ArcHiddenFile) };
+ if (FlagOn(Attributes, FAT_DIRENT_ATTR_SYSTEM)) { SetFlag(Buffer->Attributes, ArcSystemFile) };
+ if (FlagOn(Attributes, FAT_DIRENT_ATTR_ARCHIVE)) { SetFlag(Buffer->Attributes, ArcArchiveFile) };
+ if (FlagOn(Attributes, FAT_DIRENT_ATTR_DIRECTORY)) { SetFlag(Buffer->Attributes, ArcDirectoryFile) };
+
+ Buffer->FileNameLength = FileTableEntry->FileNameLength;
+
+ for (i = 0; i < FileTableEntry->FileNameLength; i += 1) {
+
+ Buffer->FileName[i] = FileTableEntry->FileName[i];
+ }
+
+ return ESUCCESS;
+}
+
+
+ARC_STATUS
+FatOpen (
+ IN PCHAR FileName,
+ IN OPEN_MODE OpenMode,
+ IN PULONG FileId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine searches the device for a file matching FileName.
+ If a match is found the dirent for the file is saved and the file is
+ opened.
+
+Arguments:
+
+ FileName - Supplies a pointer to a zero terminated file name.
+
+ OpenMode - Supplies the mode of the open.
+
+ FileId - Supplies a pointer to a variable that specifies the file
+ table entry that is to be filled in if the open is successful.
+
+Return Value:
+
+ ESUCCESS is returned if the open operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+ ULONG DeviceId;
+
+ FAT_ENTRY CurrentDirectoryIndex;
+ BOOLEAN SearchSucceeded;
+ BOOLEAN IsDirectory;
+ BOOLEAN IsReadOnly;
+
+ STRING PathName;
+ FAT8DOT3 Name;
+
+ FatDebugOutput("FatOpen\r\n", 0, 0);
+
+ //
+ // Load our local variables
+ //
+
+ FileTableEntry = &BlFileTable[*FileId];
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
+ DeviceId = FileTableEntry->DeviceId;
+
+ //
+ // Construct a file name descriptor from the input file name
+ //
+
+ RtlInitString( &PathName, FileName );
+
+ //
+ // While the path name has some characters in it we'll go through our loop
+ // which extracts the first part of the path name and searches the current
+ // directory for an entry. If what we find is a directory then we have to
+ // continue looping until we're done with the path name.
+ //
+
+ FileTableEntry->u.FatFileContext.DirentLbo = 0;
+ FileTableEntry->Position.LowPart = 0;
+ FileTableEntry->Position.HighPart = 0;
+
+ CurrentDirectoryIndex = 0;
+ SearchSucceeded = TRUE;
+ IsDirectory = TRUE;
+ IsReadOnly = TRUE;
+
+ if ((PathName.Buffer[0] == '\\') && (PathName.Length == 1)) {
+
+ //
+ // We are opening the root directory.
+ //
+ // N.B.: IsDirectory and SearchSucceeded are already TRUE.
+ //
+
+ PathName.Length = 0;
+
+ FileTableEntry->FileNameLength = 1;
+ FileTableEntry->FileName[0] = PathName.Buffer[0];
+
+ //
+ // Root dirent is all zeroes with a directory attribute.
+ //
+
+ RtlZeroMemory(&FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT));
+
+ FileTableEntry->u.FatFileContext.Dirent.Attributes = FAT_DIRENT_ATTR_DIRECTORY;
+
+ FileTableEntry->u.FatFileContext.DirentLbo = 0;
+
+ IsReadOnly = FALSE;
+
+ CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
+
+ } else {
+
+ //
+ // We are not opening the root directory.
+ //
+
+ while ((PathName.Length > 0) && IsDirectory) {
+
+ ARC_STATUS Status;
+
+ //
+ // Extract the first component and search the directory for a match, but
+ // first copy the first part to the file name buffer in the file table entry
+ //
+
+ if (PathName.Buffer[0] == '\\') {
+ PathName.Buffer +=1;
+ PathName.Length -=1;
+ }
+
+ for (FileTableEntry->FileNameLength = 0;
+ (((USHORT)FileTableEntry->FileNameLength < PathName.Length) &&
+ (PathName.Buffer[FileTableEntry->FileNameLength] != '\\'));
+ FileTableEntry->FileNameLength += 1) {
+
+ FileTableEntry->FileName[FileTableEntry->FileNameLength] =
+ PathName.Buffer[FileTableEntry->FileNameLength];
+ }
+
+ FatFirstComponent( &PathName, &Name );
+
+ Status = FatSearchForDirent( FatStructureContext,
+ DeviceId,
+ CurrentDirectoryIndex,
+ &Name,
+ &FileTableEntry->u.FatFileContext.Dirent,
+ &FileTableEntry->u.FatFileContext.DirentLbo );
+
+ if (Status == ENOENT) {
+
+ SearchSucceeded = FALSE;
+ break;
+ }
+
+ if (Status != ESUCCESS) {
+
+ return Status;
+ }
+
+ //
+ // We have a match now check to see if it is a directory, and also
+ // if it is readonly
+ //
+
+ IsDirectory = FlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes, FAT_DIRENT_ATTR_DIRECTORY );
+
+ IsReadOnly = FlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes, FAT_DIRENT_ATTR_READ_ONLY );
+
+ if (IsDirectory) {
+
+ CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
+ }
+ }
+ }
+
+ //
+ // If the path name length is not zero then we were trying to crack a path
+ // with an nonexistent (or non directory) name in it. For example, we tried
+ // to crack a\b\c\d and b is not a directory or does not exist (then the path
+ // name will still contain c\d).
+ //
+
+ if (PathName.Length != 0) {
+
+ return ENOTDIR;
+ }
+
+ //
+ // At this point we've cracked the name up to (an maybe including the last
+ // component). We located the last component if the SearchSucceeded flag is
+ // true, otherwise the last component does not exist. If we located the last
+ // component then this is like an open or a supersede, but not a create.
+ //
+
+ if (SearchSucceeded) {
+
+ //
+ // Check if the last component is a directory
+ //
+
+ if (IsDirectory) {
+
+ //
+ // For an existing directory the only valid open mode is OpenDirectory
+ // all other modes return an error
+ //
+
+ switch (OpenMode) {
+
+ case ArcOpenReadOnly:
+ case ArcOpenWriteOnly:
+ case ArcOpenReadWrite:
+ case ArcCreateWriteOnly:
+ case ArcCreateReadWrite:
+ case ArcSupersedeWriteOnly:
+ case ArcSupersedeReadWrite:
+
+ //
+ // If we reach here then the caller got a directory but didn't
+ // want to open a directory
+ //
+
+ return EISDIR;
+
+ case ArcOpenDirectory:
+
+ //
+ // If we reach here then the caller got a directory and wanted
+ // to open a directory.
+ //
+
+ FileTableEntry->Flags.Open = 1;
+ FileTableEntry->Flags.Read = 1;
+
+ return ESUCCESS;
+
+ case ArcCreateDirectory:
+
+ //
+ // If we reach here then the caller got a directory and wanted
+ // to create a new directory
+ //
+
+ return EACCES;
+ }
+ }
+
+ //
+ // If we get there then we have an existing file that is being opened.
+ // We can open existing files through a lot of different open modes in
+ // some cases we need to check the read only part of file and/or truncate
+ // the file.
+ //
+
+ switch (OpenMode) {
+
+ case ArcOpenReadOnly:
+
+ //
+ // If we reach here then the user got a file and wanted to open the
+ // file read only
+ //
+
+ FileTableEntry->Flags.Open = 1;
+ FileTableEntry->Flags.Read = 1;
+
+ return ESUCCESS;
+
+ case ArcOpenWriteOnly:
+ case ArcOpenReadWrite:
+ case ArcCreateWriteOnly:
+ case ArcCreateReadWrite:
+ case ArcSupersedeWriteOnly:
+ case ArcSupersedeReadWrite:
+
+ //
+ // If we reach here then the user got a file and wanted to create a new
+ // file or open a file for write.
+ //
+
+ return EACCES;
+
+ case ArcOpenDirectory:
+ case ArcCreateDirectory:
+
+ //
+ // If we reach here then the user got a file and wanted a directory
+ //
+
+ return ENOTDIR;
+ }
+ }
+
+ //
+ // If we get here the last component does not exist so we are trying to create
+ // either a new file or a directory.
+ //
+
+ return EACCES;
+}
+
+
+ARC_STATUS
+FatRead (
+ IN ULONG FileId,
+ OUT PVOID Buffer,
+ IN ULONG Length,
+ OUT PULONG Transfer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads data from the specified file.
+
+Arguments:
+
+ FileId - Supplies the file table index.
+
+ Buffer - Supplies a pointer to the buffer that receives the data
+ read.
+
+ Length - Supplies the number of bytes that are to be read.
+
+ Transfer - Supplies a pointer to a variable that receives the number
+ of bytes actually transfered.
+
+Return Value:
+
+ ESUCCESS is returned if the read operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+ ULONG DeviceId;
+
+ FatDebugOutput("FatRead\r\n", 0, 0);
+
+ //
+ // Load out local variables
+ //
+
+ FileTableEntry = &BlFileTable[FileId];
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
+ DeviceId = FileTableEntry->DeviceId;
+
+ //
+ // Clear the transfer count
+ //
+
+ *Transfer = 0;
+
+ //
+ // Read in runs (i.e., bytes) until the byte count goes to zero
+ //
+
+ while (Length > 0) {
+
+ LBO Lbo;
+
+ ULONG CurrentRunByteCount;
+
+ //
+ // Lookup the corresponding Lbo and run length for the current position
+ // (i.e., Vbo).
+ //
+
+ if (FatVboToLbo( FileId, FileTableEntry->Position.LowPart, &Lbo, &CurrentRunByteCount ) != ESUCCESS) {
+
+ return ESUCCESS;
+ }
+
+ //
+ // while there are bytes to be read in from the current run
+ // length and we haven't exhausted the request we loop reading
+ // in bytes. The biggest request we'll handle is only 32KB
+ // contiguous bytes per physical read. So we might need to loop
+ // through the run.
+ //
+
+ while ((Length > 0) && (CurrentRunByteCount > 0)) {
+
+ LONG SingleReadSize;
+
+ //
+ // Compute the size of the next physical read
+ //
+
+ SingleReadSize = Minimum(Length, 32 * 1024);
+ SingleReadSize = Minimum((ULONG)SingleReadSize, CurrentRunByteCount);
+
+ //
+ // Don't read beyond the eof
+ //
+
+ if (((ULONG)SingleReadSize + FileTableEntry->Position.LowPart) >
+ FileTableEntry->u.FatFileContext.Dirent.FileSize) {
+
+ SingleReadSize = FileTableEntry->u.FatFileContext.Dirent.FileSize -
+ FileTableEntry->Position.LowPart;
+
+ //
+ // If the readjusted read length is now zero then we're done.
+ //
+
+ if (SingleReadSize <= 0) {
+
+ return ESUCCESS;
+ }
+
+ //
+ // By also setting length here we'll make sure that this is our last
+ // read
+ //
+
+ Length = SingleReadSize;
+ }
+
+ //
+ // Issue the read
+ //
+
+ DiskRead( DeviceId, Lbo, SingleReadSize, Buffer);
+
+ //
+ // Update the remaining length, Current run byte count
+ // and new Lbo offset
+ //
+
+ Length -= SingleReadSize;
+ CurrentRunByteCount -= SingleReadSize;
+ Lbo += SingleReadSize;
+
+ //
+ // Update the current position and the number of bytes transfered
+ //
+
+ FileTableEntry->Position.LowPart += SingleReadSize;
+ *Transfer += SingleReadSize;
+
+ //
+ // Update buffer to point to the next byte location to fill in
+ //
+
+ Buffer = (PCHAR)Buffer + SingleReadSize;
+ }
+ }
+
+ //
+ // If we get here then remaining sector count is zero so we can
+ // return success to our caller
+ //
+
+ return ESUCCESS;
+}
+
+
+ARC_STATUS
+FatSeek (
+ IN ULONG FileId,
+ IN PLARGE_INTEGER Offset,
+ IN SEEK_MODE SeekMode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine seeks to the specified position for the file specified
+ by the file id.
+
+Arguments:
+
+ FileId - Supplies the file table index.
+
+ Offset - Supplies the offset in the file to position to.
+
+ SeekMode - Supplies the mode of the seek operation.
+
+Return Value:
+
+ ESUCCESS is returned if the seek operation is successful. Otherwise,
+ EINVAL is returned.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ ULONG NewPosition;
+
+ FatDebugOutput("FatSeek\r\n", 0, 0);
+
+ //
+ // Load our local variables
+ //
+
+ FileTableEntry = &BlFileTable[FileId];
+
+ //
+ // Compute the new position
+ //
+
+ if (SeekMode == SeekAbsolute) {
+
+ NewPosition = Offset->LowPart;
+
+ } else {
+
+ NewPosition = FileTableEntry->Position.LowPart + Offset->LowPart;
+ }
+
+ //
+ // If the new position is greater than the file size then return
+ // an error
+ //
+
+ if (NewPosition > FileTableEntry->u.FatFileContext.Dirent.FileSize) {
+
+ return EINVAL;
+ }
+
+ //
+ // Otherwise set the new position and return to our caller
+ //
+
+ FileTableEntry->Position.LowPart = NewPosition;
+ return ESUCCESS;
+}
+
+
+//
+// Internal support routine
+//
+
+ARC_STATUS
+FatDiskRead (
+ IN ULONG DeviceId,
+ IN LBO Lbo,
+ IN ULONG ByteCount,
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads in zero or more bytes from the specified device.
+
+Arguments:
+
+ DeviceId - Supplies the device id to use in the arc calls.
+
+ Lbo - Supplies the LBO to start reading from.
+
+ ByteCount - Supplies the number of bytes to read.
+
+ Buffer - Supplies a pointer to the buffer to read the bytes into.
+
+Return Value:
+
+ ESUCCESS is returned if the read operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ LARGE_INTEGER LargeLbo;
+ ARC_STATUS Status;
+ ULONG i;
+
+ //
+ // Special case the zero byte read request
+ //
+
+ if (ByteCount == 0) {
+
+ return ESUCCESS;
+ }
+
+ //
+ // Seek to the appropriate offset on the volume
+ //
+
+ LargeLbo.LowPart = (ULONG)Lbo;
+ LargeLbo.HighPart = 0;
+
+ if ((Status = ArcSeek( DeviceId, &LargeLbo, SeekAbsolute )) != ESUCCESS) {
+
+ return Status;
+ }
+
+ //
+ // Issue the arc read request
+ //
+
+ if ((Status = ArcRead( DeviceId, Buffer, ByteCount, &i)) != ESUCCESS) {
+
+ return Status;
+ }
+
+ //
+ // Make sure we got back the amount requested
+ //
+
+ if (ByteCount != i) {
+
+ return EIO;
+ }
+
+ //
+ // Everything is fine so return success to our caller
+ //
+
+ return ESUCCESS;
+}
+
+
+//
+// Internal support routine
+//
+
+CLUSTER_TYPE
+FatInterpretClusterType (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN FAT_ENTRY Entry
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure tells the caller how to interpret a fat table entry. It will
+ indicate if the fat cluster is available, reserved, bad, the last one, or another
+ fat index.
+
+Arguments:
+
+ FatStructureContext - Supplies the volume structure for the operation
+
+ DeviceId - Supplies the DeviceId for the volume being used.
+
+ Entry - Supplies the fat entry to examine.
+
+Return Value:
+
+ The type of the input fat entry is returned
+
+--*/
+
+{
+ //
+ // Check for 12 or 16 bit fat.
+ //
+
+ if (FatIndexBitSize(&FatStructureContext->Bpb) == 12) {
+
+ //
+ // For 12 bit fat check for one of the cluster types, but first
+ // make sure we only looking at 12 bits of the entry
+ //
+
+ Entry &= 0x00000fff;
+
+ if (Entry == 0x000) { return FatClusterAvailable; }
+ else if ((Entry >= 0xff0) && (Entry <= 0xff6)) { return FatClusterReserved; }
+ else if (Entry == 0xff7) { return FatClusterBad; }
+ else if ((Entry >= 0xff8) && (Entry <= 0xfff)) { return FatClusterLast; }
+ else { return FatClusterNext; }
+
+ } else {
+
+ //
+ // For 16 bit fat check for one of the cluster types, but first
+ // make sure we are only looking at 16 bits of the entry
+ //
+
+ Entry &= 0x0000ffff;
+
+ if (Entry == 0x0000) { return FatClusterAvailable; }
+ else if ((Entry >= 0xfff0) && (Entry <= 0xfff6)) { return FatClusterReserved; }
+ else if (Entry == 0xfff7) { return FatClusterBad; }
+ else if ((Entry >= 0xfff8) && (Entry <= 0xffff)) { return FatClusterLast; }
+ else { return FatClusterNext; }
+ }
+}
+
+
+//
+// Internal support routine
+//
+
+ARC_STATUS
+FatLookupFatEntry (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN FAT_ENTRY FatIndex,
+ OUT PFAT_ENTRY FatEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the value stored within the fat table and the specified
+ fat index. It is semantically equivalent to doing
+
+ x = Fat[FatIndex]
+
+Arguments:
+
+ FatStrutureContext - Supplies the volume struture being used
+
+ DeviceId - Supplies the device being used
+
+ FatIndex - Supplies the index being looked up.
+
+ FatEntry - Receives the value stored at the specified fat index
+
+Return Value:
+
+ ESUCCESS is returned if the operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ BOOLEAN TwelveBitFat;
+ VBO Vbo;
+
+ //
+ // Calculate the Vbo of the word in the fat we need and
+ // also figure out if this is a 12 or 16 bit fat
+ //
+
+ if (FatIndexBitSize( &FatStructureContext->Bpb ) == 12) {
+
+ TwelveBitFat = TRUE;
+ Vbo = (FatIndex * 3) / 2;
+
+ } else {
+
+ TwelveBitFat = FALSE;
+ Vbo = FatIndex * 2;
+ }
+
+ //
+ // Check if the Vbo we need is already in the cached fat
+ //
+
+ if ((FatStructureContext->CachedFat == NULL) ||
+ (Vbo < FatStructureContext->CachedFatVbo) ||
+ ((Vbo+1) > (FatStructureContext->CachedFatVbo + FAT_CACHE_SIZE))) {
+
+ //
+ // Set the aligned cached fat buffer in the structure context
+ //
+
+ FatStructureContext->CachedFat = ALIGN_BUFFER( &FatStructureContext->CachedFatBuffer[0] );
+
+ //
+ // Now set the new cached Vbo to be the Vbo of the cache sized section that
+ // we're trying to map. Each time we read in the cache we only read in
+ // cache sized and cached aligned pieces of the fat. So first compute an
+ // aligned cached fat vbo and then do the read.
+ //
+
+ FatStructureContext->CachedFatVbo = (Vbo / FAT_CACHE_SIZE) * FAT_CACHE_SIZE;
+
+ DiskRead( DeviceId,
+ FatStructureContext->CachedFatVbo + FatFirstFatAreaLbo(&FatStructureContext->Bpb),
+ FAT_CACHE_SIZE,
+ FatStructureContext->CachedFat );
+ }
+
+ //
+ // At this point the cached fat contains the vbo we're after so simply
+ // extract the word
+ //
+
+ CopyUchar2( FatEntry,
+ &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo] );
+
+ //
+ // Now if this is a 12 bit fat then check if the index is odd or even
+ // If it is odd then we need to shift it over 4 bits, and in all
+ // cases we need to mask out the high 4 bits.
+ //
+
+ if (TwelveBitFat) {
+
+ if ((FatIndex % 2) == 1) { *FatEntry >>= 4; }
+
+ *FatEntry &= 0x0fff;
+ }
+
+ return ESUCCESS;
+}
+
+
+//
+// Internal support routine
+//
+
+LBO
+FatIndexToLbo (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN FAT_ENTRY FatIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure translates a fat index into its corresponding lbo.
+
+Arguments:
+
+ FatStructureContext - Supplies the volume structure for the operation
+
+ Entry - Supplies the fat entry to examine.
+
+Return Value:
+
+ The LBO for the input fat index is returned
+
+--*/
+
+{
+ //
+ // The formula for translating an index into an lbo is to take the index subtract
+ // 2 (because index values 0 and 1 are reserved) multiply that by the bytes per
+ // cluster and add the results to the first file area lbo.
+ //
+
+ return ((FatIndex-2) * FatBytesPerCluster(&FatStructureContext->Bpb))
+ + FatFileAreaLbo(&FatStructureContext->Bpb);
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+FatLboToIndex (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN LBO Lbo,
+ OUT PFAT_ENTRY FatIndex,
+ OUT PULONG ByteOffset
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure translates an lbo into its corresponding fat index and byte offset.
+
+Arguments:
+
+ FatStructureContext - Supplies the volume structure for the operation
+
+ Lbo - Supplies the lbo to translate from.
+
+ FatIndex - Receives the fat index of the cluster containing the input lbo.
+
+ ByteOffset - Receives the bytes offset of the lbo within the specified cluster.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // The formula to translate an lbo into and index is to subtract out the
+ // file area lbo offset, divide by the number of bytes per cluster and then add 2.
+ //
+
+ *FatIndex = (FAT_ENTRY)(((Lbo - FatFileAreaLbo(&FatStructureContext->Bpb))
+ / FatBytesPerCluster(&FatStructureContext->Bpb)) + 2);
+
+ //
+ // The byte offset if simply the lbo modulo the number of bytes per cluster
+ //
+
+ *ByteOffset = Lbo % FatBytesPerCluster(&FatStructureContext->Bpb);
+
+ return;
+}
+
+
+//
+// Internal support routine
+//
+
+ARC_STATUS
+FatSearchForDirent (
+ IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
+ IN ULONG DeviceId,
+ IN FAT_ENTRY DirectoriesStartingIndex,
+ IN PFAT8DOT3 FileName,
+ OUT PDIRENT Dirent,
+ OUT PLBO Lbo
+ )
+
+/*++
+
+Routine Description:
+
+ The procedure searches the indicated directory for a dirent that matches
+ the input file name.
+
+Arguments:
+
+ FatStructureContext - Supplies the structure context for the operation
+
+ DeviceId - Supplies the Device id for the operation
+
+ DirectoriesStartingIndex - Supplies the fat index of the directory we are
+ to search. A value of zero indicates that we are searching the root directory
+
+ FileName - Supplies the file name to look for. The name must have already been
+ biased by the 0xe5 transmogrification
+
+ Dirent - The caller supplies the memory for a dirent and this procedure will
+ fill in the dirent if one is located
+
+ Lbo - Receives the Lbo of the dirent if one is located
+
+Return Value:
+
+ ESUCCESS is returned if the operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PDIRENT DirentBuffer;
+ UCHAR Buffer[ 16 * sizeof(DIRENT) + 256 ];
+
+ ULONG i;
+ ULONG j;
+
+ ULONG BytesPerCluster;
+ FAT_ENTRY FatEntry;
+ CLUSTER_TYPE ClusterType;
+
+ DirentBuffer = (PDIRENT)ALIGN_BUFFER( &Buffer[0] );
+
+ //
+ // Check if this is the root directory that is being searched
+ //
+
+ if (DirectoriesStartingIndex == FAT_CLUSTER_AVAILABLE) {
+
+ VBO Vbo;
+
+ ULONG RootLbo = FatRootDirectoryLbo(&FatStructureContext->Bpb);
+ ULONG RootSize = FatRootDirectorySize(&FatStructureContext->Bpb);
+
+ //
+ // For the root directory we'll zoom down the dirents until we find
+ // a match, or run out of dirents or hit the never used dirent.
+ // The outer loop reads in 512 bytes of the directory at a time into
+ // dirent buffer.
+ //
+
+ for (Vbo = 0; Vbo < RootSize; Vbo += 16 * sizeof(DIRENT)) {
+
+ *Lbo = Vbo + RootLbo;
+
+ DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer );
+
+ //
+ // The inner loop cycles through the 16 dirents that we've just read in
+ //
+
+ for (i = 0; i < 16; i += 1) {
+
+ //
+ // Check if we've found a non label match for file name, and if so
+ // then copy the buffer into the dirent and set the real lbo
+ // of the dirent and return
+ //
+
+ if (!FlagOn(DirentBuffer[i].Attributes, FAT_DIRENT_ATTR_VOLUME_ID ) &&
+ AreNamesEqual(&DirentBuffer[i].FileName, FileName)) {
+
+ for (j = 0; j < sizeof(DIRENT); j += 1) {
+
+ ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j];
+ }
+
+ *Lbo = Vbo + RootLbo + (i * sizeof(DIRENT));
+
+ return ESUCCESS;
+ }
+
+ if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) {
+
+ return ENOENT;
+ }
+ }
+ }
+
+ return ENOENT;
+ }
+
+ //
+ // If we get here we need to search a non-root directory. The alrogithm
+ // for doing the search is that for each cluster we read in each dirent
+ // until we find a match, or run out of clusters, or hit the never used
+ // dirent. First set some local variables and then get the cluster type
+ // of the first cluster
+ //
+
+ BytesPerCluster = FatBytesPerCluster( &FatStructureContext->Bpb );
+ FatEntry = DirectoriesStartingIndex;
+ ClusterType = FatInterpretClusterType( FatStructureContext, FatEntry );
+
+ //
+ // Now loop through each cluster, and compute the starting Lbo for each cluster
+ // that we encounter
+ //
+
+ while (ClusterType == FatClusterNext) {
+
+ LBO ClusterLbo;
+ ULONG Offset;
+
+ ClusterLbo = FatIndexToLbo( FatStructureContext, FatEntry );
+
+ //
+ // Now for each dirent in the cluster compute the lbo, read in the dirent
+ // and check for a match, the outer loop reads in 512 bytes of dirents at
+ // a time.
+ //
+
+ for (Offset = 0; Offset < BytesPerCluster; Offset += 16 * sizeof(DIRENT)) {
+
+ *Lbo = Offset + ClusterLbo;
+
+ DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer );
+
+ //
+ // The inner loop cycles through the 16 dirents that we've just read in
+ //
+
+ for (i = 0; i < 16; i += 1) {
+
+ //
+ // Check if we've found a for file name, and if so
+ // then copy the buffer into the dirent and set the real lbo
+ // of the dirent and return
+ //
+
+ if (AreNamesEqual(&DirentBuffer[i].FileName, FileName)) {
+
+ for (j = 0; j < sizeof(DIRENT); j += 1) {
+
+ ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j];
+ }
+
+ *Lbo = Offset + ClusterLbo + (i * sizeof(DIRENT));
+
+ return ESUCCESS;
+ }
+
+ if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) {
+
+ return ENOENT;
+ }
+ }
+ }
+
+ //
+ // Now that we've exhausted the current cluster we need to read
+ // in the next cluster. So locate the next fat entry in the chain
+ // and go back to the top of the while loop.
+ //
+
+ LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry );
+
+ ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
+ }
+
+ return ENOENT;
+}
+
+
+//
+// Internal support routine
+//
+
+ARC_STATUS
+FatLoadMcb (
+ IN ULONG FileId,
+ IN VBO StartingVbo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine loads into the cached mcb table the the retrival information for
+ the starting vbo.
+
+Arguments:
+
+ FileId - Supplies the FileId for the operation
+
+ StartingVbo - Supplies the starting vbo to use when loading the mcb
+
+Return Value:
+
+ ESUCCESS is returned if the operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PBL_FILE_TABLE FileTableEntry;
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+ PFAT_MCB Mcb;
+ ULONG DeviceId;
+ ULONG BytesPerCluster;
+
+ FAT_ENTRY FatEntry;
+ CLUSTER_TYPE ClusterType;
+ VBO Vbo;
+
+ //
+ // Preload some of the local variables
+ //
+
+ FileTableEntry = &BlFileTable[FileId];
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
+ Mcb = &FatStructureContext->Mcb;
+ DeviceId = FileTableEntry->DeviceId;
+ BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb);
+
+ //
+ // Set the file id in the structure context, and also set the mcb to be initially
+ // empty
+ //
+
+ FatStructureContext->FileId = FileId;
+ Mcb->InUse = 0;
+ Mcb->Vbo[0] = 0;
+
+ //
+ // Check if this is the root directory. If it is then we build the single
+ // run mcb entry for the root directory.
+ //
+
+ if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
+
+ Mcb->InUse = 1;
+ Mcb->Lbo[0] = FatRootDirectoryLbo(&FatStructureContext->Bpb);
+ Mcb->Vbo[1] = FatRootDirectorySize(&FatStructureContext->Bpb);
+
+ return ESUCCESS;
+ }
+
+ //
+ // For all other files/directories we need to do some work. First get the fat
+ // entry and cluster type of the fat entry stored in the dirent
+ //
+
+ FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
+ ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
+
+ //
+ // Scan through the fat until we reach the vbo we're after and then build the
+ // mcb for the file
+ //
+
+ for (Vbo = BytesPerCluster; Vbo < StartingVbo; Vbo += BytesPerCluster) {
+
+ //
+ // Check if the file does not have any allocation beyond this point in which
+ // case the mcb we return is empty
+ //
+
+ if (ClusterType != FatClusterNext) {
+
+ return ESUCCESS;
+ }
+
+ LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry );
+
+ ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
+ }
+
+ //
+ // We need to check again if the file does not have any allocation beyond this
+ // point in which case the mcb we return is empty
+ //
+
+ if (ClusterType != FatClusterNext) {
+
+ return ESUCCESS;
+ }
+
+ //
+ // At this point FatEntry denotes another cluster, and it happens to be the
+ // cluster we want to start loading into the mcb. So set up the first run in
+ // the mcb to be this cluster, with a size of a single cluster.
+ //
+
+ Mcb->InUse = 1;
+ Mcb->Vbo[0] = Vbo - BytesPerCluster;
+ Mcb->Lbo[0] = FatIndexToLbo( FatStructureContext, FatEntry );
+ Mcb->Vbo[1] = Vbo;
+
+ //
+ // Now we'll scan through the fat chain until we either exhaust the fat chain
+ // or we fill up the mcb
+ //
+
+ while (TRUE) {
+
+ LBO Lbo;
+
+ //
+ // Get the next fat entry and interpret its cluster type
+ //
+
+ LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry );
+
+ ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
+
+ if (ClusterType != FatClusterNext) {
+
+ return ESUCCESS;
+ }
+
+ //
+ // Now calculate the lbo for this cluster and determine if it
+ // is a continuation of the previous run or a start of a new run
+ //
+
+ Lbo = FatIndexToLbo(FatStructureContext, FatEntry);
+
+ //
+ // It is a continuation if the lbo of the last run plus the current
+ // size of the run is equal to the lbo for the next cluster. If it
+ // is a contination then we only need to add a cluster amount to the
+ // last vbo to increase the run size. If it is a new run then
+ // we need to check if the run will fit, and if so then add in the
+ // new run.
+ //
+
+ if ((Mcb->Lbo[Mcb->InUse-1] + (Mcb->Vbo[Mcb->InUse] - Mcb->Vbo[Mcb->InUse-1])) == Lbo) {
+
+ Mcb->Vbo[Mcb->InUse] += BytesPerCluster;
+
+ } else {
+
+ if ((Mcb->InUse + 1) >= FAT_MAXIMUM_MCB) {
+
+ return ESUCCESS;
+ }
+
+ Mcb->InUse += 1;
+ Mcb->Lbo[Mcb->InUse-1] = Lbo;
+ Mcb->Vbo[Mcb->InUse] = Mcb->Vbo[Mcb->InUse-1] + BytesPerCluster;
+ }
+ }
+
+ return ESUCCESS;
+}
+
+
+//
+// Internal support routine
+//
+
+ARC_STATUS
+FatVboToLbo (
+ IN ULONG FileId,
+ IN VBO Vbo,
+ OUT PLBO Lbo,
+ OUT PULONG ByteCount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine computes the run denoted by the input vbo to into its
+ corresponding lbo and also returns the number of bytes remaining in
+ the run.
+
+Arguments:
+
+ Vbo - Supplies the Vbo to match
+
+ Lbo - Recieves the corresponding Lbo
+
+ ByteCount - Receives the number of bytes remaining in the run
+
+Return Value:
+
+ ESUCCESS is returned if the operation is successful. Otherwise,
+ an unsuccessful status is returned that describes the reason for failure.
+
+--*/
+
+{
+ PFAT_STRUCTURE_CONTEXT FatStructureContext;
+ PFAT_MCB Mcb;
+ ULONG i;
+
+ FatStructureContext = (PFAT_STRUCTURE_CONTEXT)BlFileTable[FileId].StructureContext;
+ Mcb = &FatStructureContext->Mcb;
+
+ //
+ // Check if the mcb is for the correct file id and has the range we're asking for.
+ // If it doesn't then call load mcb to load in the right range.
+ //
+
+ if ((FileId != FatStructureContext->FileId) ||
+ (Vbo < Mcb->Vbo[0]) || (Vbo >= Mcb->Vbo[Mcb->InUse])) {
+
+ LoadMcb(FileId, Vbo);
+ }
+
+ //
+ // Now search for the slot where the Vbo fits in the mcb. Note that
+ // we could also do a binary search here but because the run count
+ // is probably small the extra overhead of a binary search doesn't
+ // buy us anything
+ //
+
+ for (i = 0; i < Mcb->InUse; i += 1) {
+
+ //
+ // We found our slot if the vbo we're after is less then the
+ // next mcb's vbo
+ //
+
+ if (Vbo < Mcb->Vbo[i+1]) {
+
+ //
+ // Compute the corresponding lbo which is the stored lbo plus
+ // the difference between the stored vbo and the vbo we're
+ // looking up. Also compute the byte count which is the
+ // difference between the current vbo we're looking up and
+ // the vbo for the next run.
+ //
+
+ *Lbo = Mcb->Lbo[i] + (Vbo - Mcb->Vbo[i]);
+
+ *ByteCount = Mcb->Vbo[i+1] - Vbo;
+
+ //
+ // and return success to our caller
+ //
+
+ return ESUCCESS;
+ }
+ }
+
+ //
+ // If we really reach here we have an error, most likely because the file is
+ // not large enough for the requested Vbo.
+ //
+
+ return EINVAL;
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+FatFirstComponent (
+ IN OUT PSTRING String,
+ OUT PFAT8DOT3 FirstComponent
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a string into fat 8.3 format and advance the input string
+ descriptor to point to the next file name component.
+
+Arguments:
+
+ InputString - Supplies a pointer to the input string descriptor.
+
+ Output8dot3 - Supplies a pointer to the converted string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG Extension;
+ ULONG Index;
+
+ //
+ // Fill the output name with blanks.
+ //
+
+ for (Index = 0; Index < 11; Index += 1) { (*FirstComponent)[Index] = ' '; }
+
+ //
+ // Copy the first part of the file name up to eight characters and
+ // skip to the end of the name or the input string as appropriate.
+ //
+
+ for (Index = 0; Index < String->Length; Index += 1) {
+
+ if ((String->Buffer[Index] == '\\') || (String->Buffer[Index] == '.')) {
+
+ break;
+ }
+
+ if (Index < 8) {
+
+ (*FirstComponent)[Index] = (CHAR)ToUpper(String->Buffer[Index]);
+ }
+ }
+
+ //
+ // Check if the end of the string was reached, an extension was specified,
+ // or a subdirectory was specified..
+ //
+
+ if (Index < String->Length) {
+
+ if (String->Buffer[Index] == '.') {
+
+ //
+ // Skip over the extension separator and add the extension to
+ // the file name.
+ //
+
+ Index += 1;
+ Extension = 8;
+
+ while (Index < String->Length) {
+
+ if (String->Buffer[Index] == '\\') {
+
+ break;
+ }
+
+ if (Extension < 11) {
+
+ (*FirstComponent)[Extension] = (CHAR)ToUpper(String->Buffer[Index]);
+ Extension += 1;
+ }
+
+ Index += 1;
+ }
+ }
+ }
+
+ //
+ // Now we'll bias the first component by the 0xe5 factor so that all our tests
+ // to names on the disk will be ready for a straight 11 byte comparison
+ //
+
+ if ((*FirstComponent)[0] == 0xe5) {
+
+ (*FirstComponent)[0] = FAT_DIRENT_REALLY_0E5;
+ }
+
+ //
+ // Update string descriptor.
+ //
+
+ String->Buffer += Index;
+ String->Length -= Index;
+
+ return;
+}
+
+
+ARC_STATUS
+FatGetDirectoryEntry (
+ IN ULONG FileId,
+ IN DIRECTORY_ENTRY *DirEntry,
+ IN ULONG NumberDir,
+ OUT PULONG CountDir
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the GetDirectoryEntry operation for the
+ FAT file system.
+
+Arguments:
+
+ FileId - Supplies the file table index.
+
+ DirEntry - Supplies a pointer to a directory entry structure.
+
+ NumberDir - Supplies the number of directory entries to read.
+
+ Count - Supplies a pointer to a variable to receive the number
+ of entries read.
+
+Return Value:
+
+ ESUCCESS is returned if the read was successful, otherwise
+ an error code is returned.
+
+--*/
+
+{
+ //
+ // define local variables
+ //
+
+ ARC_STATUS Status; // ARC status
+ ULONG Count = 0; // # of bytes read
+ ULONG Position; // file position
+ PFAT_FILE_CONTEXT pContext; // FAT file context
+ ULONG RunByteCount = 0; // max sequential bytes
+ ULONG RunDirCount; // max dir entries to read per time
+ ULONG i; // general index
+ PDIRENT FatDirEnt; // directory entry pointer
+ UCHAR Buffer[ 16 * sizeof(DIRENT) + 32 ];
+ LBO Lbo;
+ BOOLEAN EofDir = FALSE; // not end of file
+
+ //
+ // initialize local variables
+ //
+
+ pContext = &BlFileTable[ FileId ].u.FatFileContext;
+ FatDirEnt = (PDIRENT)ALIGN_BUFFER( &Buffer[0] );
+
+ //
+ // if not directory entry, exit with error
+ //
+
+ if ( !(pContext->Dirent.Attributes & FAT_DIRENT_ATTR_DIRECTORY) ) {
+ return EBADF;
+ }
+
+ //
+ // Initialize the output count to zero
+ //
+
+ *CountDir = 0;
+
+ //
+ // if NumberDir is zero, return ESUCCESS.
+ //
+
+ if ( !NumberDir ) {
+ return ESUCCESS;
+ }
+
+ //
+ // read one directory at a time.
+ //
+
+ do
+ {
+ //
+ // save position
+ //
+
+ Position = BlFileTable[ FileId ].Position.LowPart;
+
+ //
+ // Lookup the corresponding Lbo and run length for the current position
+ //
+
+ if ( !RunByteCount ) {
+ if (Status = FatVboToLbo( FileId, Position, &Lbo, &RunByteCount )) {
+ if ( Status == EINVAL ) {
+ break; // eof has been reached
+ } else {
+ return Status; // I/O error
+ }
+ }
+ }
+
+ //
+ // validate the # of bytes readable in sequance (exit loop if eof)
+ // the block is always multiple of a directory entry size.
+ //
+
+ if ( !(RunDirCount = Minimum( RunByteCount/sizeof(DIRENT), 16)) ) {
+ break;
+ }
+
+ //
+ // issue the read
+ //
+
+ if ( Status = FatDiskRead( BlFileTable[ FileId ].DeviceId,
+ Lbo,
+ RunDirCount * sizeof(DIRENT),
+ (PVOID)FatDirEnt )) {
+ BlFileTable[ FileId ].Position.LowPart = Position;
+ return Status;
+ }
+
+ for ( i=0; i<RunDirCount; i++ ) {
+ //
+ // exit from loop if logical end of directory
+ //
+
+ if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_NEVER_USED ) {
+ EofDir = TRUE;
+ break;
+ }
+
+ //
+ // update the current position and the number of bytes transfered
+ //
+
+ BlFileTable[ FileId ].Position.LowPart += sizeof(DIRENT);
+ Lbo += sizeof(DIRENT);
+ RunByteCount -= sizeof(DIRENT);
+
+ //
+ // skip this entry if the file or directory has been erased
+ //
+
+ if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_DELETED ) {
+ continue;
+ }
+
+ //
+ // skip this entry if this is a valume label
+ //
+
+ if ( FatDirEnt[i].Attributes & FAT_DIRENT_ATTR_VOLUME_ID ) {
+ continue;
+ }
+
+ //
+ // convert FAT directory entry in ARC directory entry
+ //
+
+ FatDirToArcDir( &FatDirEnt[i], DirEntry++ );
+
+ //
+ // update pointers
+ //
+
+ if ( ++*CountDir >= NumberDir ) {
+ break;
+ }
+ }
+ }
+ while ( !EofDir && *CountDir < NumberDir );
+
+ //
+ // all done
+ //
+
+ return *CountDir ? ESUCCESS : ENOTDIR;
+}
+
+
+/*++
+
+Routine Description:
+
+ This routine converts a FAT directory entry into an ARC
+ directory entry.
+
+Arguments:
+
+ FatDirEntry - supplies a pointer to a FAT directory entry.
+
+ ArcDirEntry - supplies a pointer to an ARC directory entry.
+
+Return Value:
+
+ None.
+
+--*/
+
+VOID
+FatDirToArcDir (
+ IN PDIRENT FatDirEnt,
+ OUT PDIRECTORY_ENTRY ArcDirEnt
+ )
+{
+ ULONG i, e;
+
+ //
+ // clear info area
+ //
+
+ RtlZeroMemory( ArcDirEnt, sizeof(DIRECTORY_ENTRY) );
+
+ //
+ // check the directory flag
+ //
+
+ if ( FatDirEnt->Attributes & FAT_DIRENT_ATTR_DIRECTORY ) {
+ ArcDirEnt->FileAttribute |= ArcDirectoryFile;
+ }
+
+ //
+ // check the read-only flag
+ //
+
+ if ( FatDirEnt->Attributes & FAT_DIRENT_ATTR_READ_ONLY ) {
+ ArcDirEnt->FileAttribute |= ArcReadOnlyFile;
+ }
+
+ //
+ // clear name string
+ //
+
+ RtlZeroMemory( ArcDirEnt->FileName, 32 );
+
+ //
+ // copy first portion of file name
+ //
+
+ for ( i = 0; i < 8 && FatDirEnt->FileName[i] != ' '; i++ ) {
+ ArcDirEnt->FileName[i] = FatDirEnt->FileName[i];
+ }
+
+ //
+ // check for an extension
+ //
+
+ if ( FatDirEnt->FileName[8] != ' ' ) {
+
+ //
+ // store the dot char
+ //
+
+ ArcDirEnt->FileName[i++] = '.';
+
+ //
+ // add the extension
+ //
+
+ for ( e=8; e<11 && FatDirEnt->FileName[e] != ' '; e++ ) {
+ ArcDirEnt->FileName[i++] = FatDirEnt->FileName[e];
+ }
+ }
+
+ //
+ // set file name length before returning
+ //
+
+ ArcDirEnt->FileNameLength = i;
+
+ return;
+}