more loader changes and new fixes to the VFAT driver from jean

svn path=/trunk/; revision=137
This commit is contained in:
Rex Jolliff 1998-12-30 18:43:27 +00:00
parent 6fd4505b6f
commit a984573ad4
10 changed files with 622 additions and 318 deletions

View file

@ -11,6 +11,7 @@
#include <ddk/ntddk.h> #include <ddk/ntddk.h>
#include <internal/string.h> #include <internal/string.h>
#include <wstring.h>
#define NDEBUG #define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
@ -20,7 +21,7 @@
/* FUNCTIONS ***************************************************************/ /* FUNCTIONS ***************************************************************/
BOOLEAN VFATReadSectors(IN PDEVICE_OBJECT pDeviceObject, BOOLEAN VFATReadSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector, IN ULONG DiskSector,
IN ULONG SectorCount, IN ULONG SectorCount,
IN UCHAR* Buffer) IN UCHAR* Buffer)
{ {
@ -31,20 +32,14 @@ BOOLEAN VFATReadSectors(IN PDEVICE_OBJECT pDeviceObject,
NTSTATUS status; NTSTATUS status;
ULONG sectorSize; ULONG sectorSize;
PULONG mbr; PULONG mbr;
int j;
DPRINT("VFATReadSector(pDeviceObject %x, DiskSector %d, Buffer %x)\n", DPRINT("VFATReadSector(pDeviceObject %x, DiskSector %d, Buffer %x)\n",
pDeviceObject,DiskSector,Buffer); pDeviceObject,DiskSector,Buffer);
SET_LARGE_INTEGER_HIGH_PART(sectorNumber, 0); // sectorNumber.HighPart = 0;
SET_LARGE_INTEGER_LOW_PART(sectorNumber, DiskSector * BLOCKSIZE); // sectorNumber.LowPart = DiskSector * BLOCKSIZE;
sectorNumber.LowPart=DiskSector<<9;
DPRINT("DiskSector:%ld BLKSZ:%ld sectorNumber:%ld:%ld\n", sectorNumber.HighPart=DiskSector>>23;
(unsigned long) DiskSector,
(unsigned long) BLOCKSIZE,
(unsigned long) GET_LARGE_INTEGER_HIGH_PART(sectorNumber),
(unsigned long) GET_LARGE_INTEGER_LOW_PART(sectorNumber));
KeInitializeEvent(&event, NotificationEvent, FALSE); KeInitializeEvent(&event, NotificationEvent, FALSE);
sectorSize = BLOCKSIZE*SectorCount; sectorSize = BLOCKSIZE*SectorCount;
@ -87,7 +82,9 @@ DPRINT("DiskSector:%ld BLKSZ:%ld sectorNumber:%ld:%ld\n",
} }
if (!NT_SUCCESS(status)) { if (!NT_SUCCESS(status)) {
DbgPrint("IO failed!!! Error code: %d(%x)\n", status, status); DbgPrint("IO failed!!! VFATREadSectors : Error code: %x\n", status);
DbgPrint("(pDeviceObject %x, DiskSector %x, Buffer %x, offset 0x%x%x)\n",
pDeviceObject,DiskSector,Buffer,sectorNumber.HighPart,sectorNumber.LowPart);
ExFreePool(mbr); ExFreePool(mbr);
return FALSE; return FALSE;
} }
@ -101,7 +98,7 @@ DPRINT("DiskSector:%ld BLKSZ:%ld sectorNumber:%ld:%ld\n",
} }
BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject, BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector, IN ULONG DiskSector,
IN ULONG SectorCount, IN ULONG SectorCount,
IN UCHAR* Buffer) IN UCHAR* Buffer)
{ {
@ -112,13 +109,12 @@ BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject,
NTSTATUS status; NTSTATUS status;
ULONG sectorSize; ULONG sectorSize;
PULONG mbr; PULONG mbr;
int j;
DPRINT("VFATWriteSector(pDeviceObject %x, DiskSector %d, Buffer %x)\n", DPRINT("VFATWriteSector(pDeviceObject %x, DiskSector %d, Buffer %x)\n",
pDeviceObject,DiskSector,Buffer); pDeviceObject,DiskSector,Buffer);
SET_LARGE_INTEGER_HIGH_PART(sectorNumber, 0); sectorNumber.HighPart = 0;
SET_LARGE_INTEGER_LOW_PART(sectorNumber, DiskSector * BLOCKSIZE); sectorNumber.LowPart = DiskSector * BLOCKSIZE;
KeInitializeEvent(&event, NotificationEvent, FALSE); KeInitializeEvent(&event, NotificationEvent, FALSE);
@ -162,7 +158,7 @@ BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject,
} }
if (!NT_SUCCESS(status)) { if (!NT_SUCCESS(status)) {
DbgPrint("IO failed!!! Error code: %d(%x)\n", status, status); DbgPrint("IO failed!!! VFATWriteSectors : Error code: %x\n", status);
ExFreePool(mbr); ExFreePool(mbr);
return FALSE; return FALSE;
} }

View file

@ -31,27 +31,6 @@
#include "vfat.h" #include "vfat.h"
#define FAT16 (1)
#define FAT12 (2)
#define FAT32 (3)
typedef struct
{
PDEVICE_OBJECT StorageDevice;
BootSector *Boot;
int rootDirectorySectors, FATStart, rootStart, dataStart;
int FATEntriesPerSector, FATUnit;
ULONG BytesPerCluster;
ULONG FatType;
unsigned char* FAT;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
typedef struct
{
FATDirEntry entry;
} FCB, *PFCB;
#define ENTRIES_PER_SECTOR (BLOCKSIZE / sizeof(FATDirEntry))
/* GLOBALS *****************************************************************/ /* GLOBALS *****************************************************************/
@ -60,6 +39,10 @@ static PDRIVER_OBJECT DriverObject;
/* FUNCTIONS ****************************************************************/ /* FUNCTIONS ****************************************************************/
ULONG Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster) ULONG Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
* disk read
*/
{ {
ULONG FATsector; ULONG FATsector;
ULONG FATeis; ULONG FATeis;
@ -67,8 +50,8 @@ ULONG Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
Block = ExAllocatePool(NonPagedPool,1024); Block = ExAllocatePool(NonPagedPool,1024);
FATsector=CurrentCluster/(512/sizeof(ULONG)); FATsector=CurrentCluster/(512/sizeof(ULONG));
FATeis=CurrentCluster-(FATsector*(512/sizeof(ULONG))); FATeis=CurrentCluster-(FATsector*(512/sizeof(ULONG)));
VFATReadSectors(DeviceExt->StorageDevice,DeviceExt->FATStart+FATsector, 1, VFATReadSectors(DeviceExt->StorageDevice
Block); ,(ULONG)(DeviceExt->FATStart+FATsector), 1,(UCHAR*) Block);
CurrentCluster = Block[FATeis]; CurrentCluster = Block[FATeis];
if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff) if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
CurrentCluster = 0; CurrentCluster = 0;
@ -77,43 +60,39 @@ ULONG Fat32GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
} }
ULONG Fat16GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster) ULONG Fat16GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next FAT16 cluster from the FAT table from the
* in-memory FAT
*/
{ {
ULONG FATsector; ULONG FATsector;
ULONG FATeis; ULONG FATeis;
PUSHORT Block; PUSHORT Block;
Block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
FATsector=CurrentCluster/(512/sizeof(USHORT)); FATsector=CurrentCluster/(512/sizeof(USHORT));
FATeis=CurrentCluster-(FATsector*256); FATeis=CurrentCluster-(FATsector*256);
memcpy(Block,DeviceExt->FAT+FATsector*BLOCKSIZE, BLOCKSIZE);
// VFATReadSectors(DeviceExt->StorageDevice,DeviceExt->FATStart+FATsector, 1,
// (UCHAR *)Block);
Block = (PUSHORT)(DeviceExt->FAT + (FATsector * BLOCKSIZE));
CurrentCluster = Block[FATeis]; CurrentCluster = Block[FATeis];
if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff) if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
CurrentCluster = 0; CurrentCluster = 0;
ExFreePool(Block);
DPRINT("Returning %x\n",CurrentCluster); DPRINT("Returning %x\n",CurrentCluster);
return(CurrentCluster); return(CurrentCluster);
} }
ULONG Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster) ULONG Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next FAT12 cluster from the FAT table from the
* in-memory FAT
*/
{ {
unsigned char* CBlock; unsigned char* CBlock;
ULONG FATsector; ULONG FATsector;
ULONG FATOffset; ULONG FATOffset;
ULONG Entry; ULONG Entry;
CBlock = ExAllocatePool(NonPagedPool,1024);
FATsector = (CurrentCluster * 12) / (512 * 8); FATsector = (CurrentCluster * 12) / (512 * 8);
memcpy(CBlock,DeviceExt->FAT+FATsector*BLOCKSIZE, BLOCKSIZE);
// VFATReadSectors(DeviceExt->StorageDevice,DeviceExt->FATStart
// +FATsector,1,CBlock);
CBlock = (unsigned char *)(DeviceExt->FAT + (FATsector * BLOCKSIZE));
FATOffset = (CurrentCluster * 12) % (512 * 8); FATOffset = (CurrentCluster * 12) % (512 * 8);
DPRINT("FATSector %d FATOffset %d\n",FATsector,FATOffset); DPRINT("FATSector %d FATOffset %d\n",FATsector,FATOffset);
if ((CurrentCluster % 2) == 0) if ((CurrentCluster % 2) == 0)
@ -130,12 +109,15 @@ ULONG Fat12GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
if (Entry >= 0xff8 && Entry <= 0xfff) if (Entry >= 0xff8 && Entry <= 0xfff)
Entry = 0; Entry = 0;
CurrentCluster = Entry; CurrentCluster = Entry;
ExFreePool(CBlock);
DPRINT("Returning %x\n",CurrentCluster); DPRINT("Returning %x\n",CurrentCluster);
return(CurrentCluster); return(CurrentCluster);
} }
ULONG GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster) ULONG GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next cluster depending on the FAT type
*/
{ {
DPRINT("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n", DPRINT("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
@ -155,6 +137,9 @@ ULONG GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
} }
ULONG FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt) ULONG FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Finds the first available cluster in a FAT16 table
*/
{ {
ULONG sector; ULONG sector;
PUSHORT Block; PUSHORT Block;
@ -184,6 +169,9 @@ ULONG FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt)
void FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite, void FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue) ULONG NewValue)
/*
* FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
*/
{ {
ULONG FATsector; ULONG FATsector;
ULONG FATeis; ULONG FATeis;
@ -208,51 +196,21 @@ void FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ExFreePool(Block); ExFreePool(Block);
} }
void FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue)
{
unsigned char* CBlock;
ULONG FATsector;
ULONG FATOffset;
ULONG Entry;
CBlock = ExAllocatePool(NonPagedPool,1024);
FATsector = (ClusterToWrite * 12) / (512 * 8);
memcpy(CBlock,DeviceExt->FAT+FATsector*BLOCKSIZE, BLOCKSIZE);
FATOffset = (ClusterToWrite * 12) % (512 * 8);
DPRINT("FATSector %d FATOffset %d\n",FATsector,FATOffset);
/*
Write 12-bit entry
if ((CurrentCluster % 2) == 0)
{
Entry = CBlock[((FATOffset / 24)*3)];
Entry |= (CBlock[((FATOffset / 24)*3) + 1] & 0xf);
}
else
{
Entry = (CBlock[((FATOffset / 24)*3) + 1] >> 4);
Entry |= (CBlock[((FATOffset / 24)*3) + 2] << 4);
} */
ExFreePool(CBlock);
}
void WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite, void WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue) ULONG NewValue)
/*
* FUNCTION: Write a changed FAT entry
*/
{ {
if (DeviceExt->FatType == FAT16) { if (DeviceExt->FatType == FAT16) {
FAT16WriteCluster(DeviceExt, ClusterToWrite, NewValue); FAT16WriteCluster(DeviceExt, ClusterToWrite, NewValue);
} else {
FAT12WriteCluster(DeviceExt, ClusterToWrite, NewValue);
} }
} }
ULONG GetNextWriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster) ULONG GetNextWriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Determines the next cluster to be written
*/
{ {
ULONG LastCluster, NewCluster; ULONG LastCluster, NewCluster;
BOOLEAN EOF = FALSE; BOOLEAN EOF = FALSE;
@ -290,7 +248,7 @@ ULONG GetNextWriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
if(DeviceExt->FatType == FAT16) { if(DeviceExt->FatType == FAT16) {
FAT16WriteCluster(DeviceExt, NewCluster, 0xFFFF); FAT16WriteCluster(DeviceExt, NewCluster, 0xFFFF);
} else { } else {
FAT12WriteCluster(DeviceExt, NewCluster, 0xFFF); //FIXME FAT12WriteCluster(DeviceExt, NewCluster, 0xFFF);
} }
/* Now, write the AU of the LastCluster with the value of the newly /* Now, write the AU of the LastCluster with the value of the newly
@ -308,13 +266,20 @@ ULONG GetNextWriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
} }
} }
unsigned long ClusterToSector(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToSector(PDEVICE_EXTENSION DeviceExt,
unsigned long Cluster) unsigned long Cluster)
/*
* FUNCTION: Converts the cluster number to a sector number for this physical
* device
*/
{ {
return DeviceExt->dataStart+((Cluster-2)*DeviceExt->Boot->SectorsPerCluster); return DeviceExt->dataStart+((Cluster-2)*DeviceExt->Boot->SectorsPerCluster);
} }
void RtlAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length) void RtlAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length)
/*
* FUNCTION: Convert an ANSI string to it's Unicode equivalent
*/
{ {
int i; int i;
@ -326,6 +291,10 @@ void RtlAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length)
} }
void RtlCatAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length) void RtlCatAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length)
/*
* FUNCTION: Appends a converted ANSI to Unicode string to the end of an
* existing Unicode string
*/
{ {
ULONG i; ULONG i;
@ -341,6 +310,9 @@ void RtlCatAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length)
} }
void vfat_initstr(wchar_t *wstr, ULONG wsize) void vfat_initstr(wchar_t *wstr, ULONG wsize)
/*
* FUNCTION: Initialize a string for use with a long file name
*/
{ {
int i; int i;
wchar_t nc=0; wchar_t nc=0;
@ -353,6 +325,9 @@ void vfat_initstr(wchar_t *wstr, ULONG wsize)
} }
wchar_t * vfat_wcsncat(wchar_t * dest, const wchar_t * src,size_t wstart, size_t wcount) wchar_t * vfat_wcsncat(wchar_t * dest, const wchar_t * src,size_t wstart, size_t wcount)
/*
* FUNCTION: Append a string for use with a long file name
*/
{ {
int i; int i;
@ -368,6 +343,9 @@ wchar_t * vfat_wcsncat(wchar_t * dest, const wchar_t * src,size_t wstart, size_t
} }
wchar_t * vfat_wcsncpy(wchar_t * dest, const wchar_t *src,size_t wcount) wchar_t * vfat_wcsncpy(wchar_t * dest, const wchar_t *src,size_t wcount)
/*
* FUNCTION: Copy a string for use with long file names
*/
{ {
int i; int i;
@ -383,6 +361,10 @@ wchar_t * vfat_wcsncpy(wchar_t * dest, const wchar_t *src,size_t wcount)
wchar_t * vfat_movstr(wchar_t * dest, const wchar_t *src, ULONG dpos, wchar_t * vfat_movstr(wchar_t * dest, const wchar_t *src, ULONG dpos,
ULONG spos, ULONG len) ULONG spos, ULONG len)
/*
* FUNCTION: Move the characters in a string to a new position in the same
* string
*/
{ {
int i; int i;
@ -398,11 +380,17 @@ wchar_t * vfat_movstr(wchar_t * dest, const wchar_t *src, ULONG dpos,
} }
BOOLEAN IsLastEntry(PVOID Block, ULONG Offset) BOOLEAN IsLastEntry(PVOID Block, ULONG Offset)
/*
* FUNCTION: Determine if the given directory entry is the last
*/
{ {
return(((FATDirEntry *)Block)[Offset].Filename[0] == 0); return(((FATDirEntry *)Block)[Offset].Filename[0] == 0);
} }
BOOLEAN IsDeletedEntry(PVOID Block, ULONG Offset) BOOLEAN IsDeletedEntry(PVOID Block, ULONG Offset)
/*
* FUNCTION: Determines if the given entry is a deleted one
*/
{ {
/* Checks special character (short names) or attrib=0 (long names) */ /* Checks special character (short names) or attrib=0 (long names) */
@ -411,7 +399,10 @@ BOOLEAN IsDeletedEntry(PVOID Block, ULONG Offset)
} }
BOOLEAN GetEntryName(PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop, BOOLEAN GetEntryName(PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
PDEVICE_EXTENSION DeviceExt, PULONG _StartingSector) PDEVICE_EXTENSION DeviceExt, ULONG * _StartingSector)
/*
* FUNCTION: Retrieves the file name, be it in short or long file name format
*/
{ {
FATDirEntry* test; FATDirEntry* test;
slot* test2; slot* test2;
@ -490,8 +481,11 @@ BOOLEAN GetEntryName(PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
} }
BOOLEAN wstrcmpi(PWSTR s1, PWSTR s2) BOOLEAN wstrcmpi(PWSTR s1, PWSTR s2)
/*
* FUNCTION: Compare to wide character strings
* return TRUE if s1==s2
*/
{ {
DPRINT("s1 '%w' s2 '%w'\n",s1,s2);
while (wtolower(*s1)==wtolower(*s2)) while (wtolower(*s1)==wtolower(*s2))
{ {
if ((*s1)==0 && (*s2)==0) if ((*s1)==0 && (*s2)==0)
@ -504,100 +498,143 @@ BOOLEAN wstrcmpi(PWSTR s1, PWSTR s2)
} }
return(FALSE); return(FALSE);
} }
BOOLEAN wstrcmpjoki(PWSTR s1, PWSTR s2)
/*
* FUNCTION: Compare to wide character strings, s2 with jokers (* or ?)
* return TRUE if s1 like s2
*/
{
while ((*s2=='?')||(wtolower(*s1)==wtolower(*s2)))
{
if ((*s1)==0 && (*s2)==0)
return(TRUE);
s1++;
s2++;
}
if(*s2=='*')
{
s2++;
while (*s1)
if (wstrcmpjoki(s1,s2)) return TRUE;
else s1++;
}
if ((*s1)==0 && (*s2)==0)
return(TRUE);
return(FALSE);
}
NTSTATUS FindFile(PDEVICE_EXTENSION DeviceExt, PFCB Fcb, NTSTATUS FindFile(PDEVICE_EXTENSION DeviceExt, PFCB Fcb,
PFCB Parent, PWSTR FileToFind) PFCB Parent, PWSTR FileToFind,ULONG *StartSector,ULONG *Entry)
/*
* FUNCTION: Find a file
*/
{ {
ULONG i, j; ULONG i, j;
ULONG Size; ULONG Size;
char* block; char* block;
WCHAR name[256]; WCHAR name[256];
ULONG StartingSector; ULONG StartingSector;
ULONG NextCluster; ULONG NextCluster;
DPRINT("FileFile(Parent %x, FileToFind %w)\n",Parent,FileToFind); DPRINT("FindFile(Parent %x, FileToFind %w)\n",Parent,FileToFind);
if (Parent == NULL) if (Parent == NULL)
{ {
Size = DeviceExt->rootDirectorySectors;//FIXME : in fat32, no limit Size = DeviceExt->rootDirectorySectors;//FIXME : in fat32, no limit
StartingSector = DeviceExt->rootStart; StartingSector = DeviceExt->rootStart;
if(FileToFind[0]==0 ||(FileToFind[0]=='\\' && FileToFind[1]==0))
{// it's root !
memset(Fcb,0,sizeof(FCB));
memset(Fcb->entry.Filename,' ',11);
if (DeviceExt->FatType == FAT32)
Fcb->entry.FirstCluster=2;
else Fcb->entry.FirstCluster=1;
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=0;
return(STATUS_SUCCESS);
} }
else
{
DPRINT("Parent->entry.FileSize %x\n",Parent->entry.FileSize);
Size = ULONG_MAX;
if (DeviceExt->FatType == FAT32)
NextCluster = Parent->entry.FirstCluster+Parent->entry.FirstClusterHigh*65536;
else
NextCluster = Parent->entry.FirstCluster;
StartingSector = ClusterToSector(DeviceExt, NextCluster);
}
block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
for (j=0; j<Size; j++)
{
VFATReadSectors(DeviceExt->StorageDevice,StartingSector,1,block);
for (i=0; i<ENTRIES_PER_SECTOR; i++)
{
if (IsLastEntry((PVOID)block,i))
{
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
if (GetEntryName((PVOID)block,&i,name,&j,DeviceExt,&StartingSector))
{
DPRINT("Scanning %w\n",name);
DPRINT("Comparing %w %w\n",name,FileToFind);
if (wstrcmpi(name,FileToFind))
{
/* In the case of a long filename, the firstcluster is stored in
the next record -- where it's short name is */
if(DeviceExt->FatType==FAT32)
if(((FATDirEntry *)block)[i].FirstCluster==0
&&((FATDirEntry *)block)[i].FirstClusterHigh==0
) i++;
else
if(((FATDirEntry *)block)[i].FirstCluster==0) i++;
DPRINT("Found it at cluster %u\n", ((FATDirEntry *)block)[i].FirstCluster);
if( i==ENTRIES_PER_SECTOR)
{
VFATReadSectors(DeviceExt->StorageDevice,StartingSector+1,1,block);
i=0;
} }
else
{
DPRINT("Parent->entry.FileSize %x\n",Parent->entry.FileSize);
memcpy(&Fcb->entry,&((FATDirEntry *)block)[i], Size = ULONG_MAX;
sizeof(FATDirEntry)); if (DeviceExt->FatType == FAT32)
NextCluster = Parent->entry.FirstCluster+Parent->entry.FirstClusterHigh*65536;
ExFreePool(block); else
return(STATUS_SUCCESS); NextCluster = Parent->entry.FirstCluster;
} StartingSector = ClusterToSector(DeviceExt, NextCluster);
} }
block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
if (StartSector && (*StartSector)) StartingSector=*StartSector;
i=(Entry)?(*Entry):0;
DPRINT("FindFile : start at sector %lx, entry %ld\n",StartingSector,i);
for (j=0; j<Size; j++)
{
VFATReadSectors(DeviceExt->StorageDevice,StartingSector,1,block);
for (i=(Entry)?(*Entry):0; i<ENTRIES_PER_SECTOR; i++)
{
if (IsLastEntry((PVOID)block,i))
{
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
} }
if (GetEntryName((PVOID)block,&i,name,&j,DeviceExt,&StartingSector))
{
// DPRINT("Comparing %w %w\n",name,FileToFind);
if (wstrcmpjoki(name,FileToFind))
{
/* In the case of a long filename, the firstcluster is stored in
the next record -- where it's short name is */
if(((FATDirEntry *)block)[i].Attrib==0x0f) i++;
// if(DeviceExt->FatType==FAT32)
// {
// if(((FATDirEntry *)block)[i].FirstCluster==0
// &&((FATDirEntry *)block)[i].FirstClusterHigh==0
// ) i++;
// }
// else
// if(((FATDirEntry *)block)[i].FirstCluster==0) i++;
if( i==(ENTRIES_PER_SECTOR))
{// entry is in next sector
StartingSector++;
VFATReadSectors(DeviceExt->StorageDevice,StartingSector,1,block);
i=0;
}
memcpy(&Fcb->entry,&((FATDirEntry *)block)[i],
sizeof(FATDirEntry));
vfat_wcsncpy(Fcb->ObjectName,name,251);
ExFreePool(block);
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=i;
return(STATUS_SUCCESS);
}
}
}
if(Entry) *Entry=0;
// not found in this sector, try next :
/* It seems that directory sectors cannot be fragmented and therefore,
/* It seems that directory sectors cannot be fragmented and therefore,
they only have a first cluster, but the one's after it are marked they only have a first cluster, but the one's after it are marked
with 0xffff. This theory is still not 100% certain, so the following with 0xffff. This theory is still not 100% certain, so the following
lines are commented and not removed */ lines are commented and not removed */
StartingSector++; StartingSector++;
/* if (Parent == NULL) /* if (Parent == NULL)
{ {
StartingSector++; StartingSector++;
}
else
{
NextCluster = GetNextCluster(DeviceExt,NextCluster);
if (NextCluster == 0)
{
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
StartingSector = ClusterToSector(DeviceExt,NextCluster);
} */
} }
else
{
NextCluster = GetNextCluster(DeviceExt,NextCluster);
if (NextCluster == 0)
{
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
StartingSector = ClusterToSector(DeviceExt,NextCluster);
} */
}
ExFreePool(block); ExFreePool(block);
return(STATUS_UNSUCCESSFUL); return(STATUS_UNSUCCESSFUL);
} }
@ -609,6 +646,7 @@ NTSTATUS FsdCloseFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject)
*/ */
{ {
/* NOP */ /* NOP */
return STATUS_SUCCESS;
} }
NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
@ -640,7 +678,7 @@ NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
*next=0; *next=0;
} }
Status = FindFile(DeviceExt,Fcb,ParentFcb,current); Status = FindFile(DeviceExt,Fcb,ParentFcb,current,NULL,NULL);
if (Status != STATUS_SUCCESS) if (Status != STATUS_SUCCESS)
{ {
return(Status); return(Status);
@ -649,15 +687,15 @@ NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
if (ParentFcb == NULL) if (ParentFcb == NULL)
{ {
Fcb = ExAllocatePool(NonPagedPool,sizeof(FCB)); Fcb = ExAllocatePool(NonPagedPool,sizeof(FCB));
ParentFcb = Temp;
} }
else else
{ {
Fcb = ParentFcb; Fcb = ParentFcb;
ParentFcb = Temp;
} }
ParentFcb = Temp;
} }
FileObject->FsContext = ParentFcb; FileObject->FsContext = ParentFcb;
DPRINT("file opn, fcb=%x\n",ParentFcb);
DPRINT("ParentFcb->entry.FileSize %d\n",ParentFcb->entry.FileSize); DPRINT("ParentFcb->entry.FileSize %d\n",ParentFcb->entry.FileSize);
return(STATUS_SUCCESS); return(STATUS_SUCCESS);
@ -666,7 +704,7 @@ NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
BOOLEAN FsdHasFileSystem(PDEVICE_OBJECT DeviceToMount) BOOLEAN FsdHasFileSystem(PDEVICE_OBJECT DeviceToMount)
/* /*
* FUNCTION: Tests if the device contains a filesystem that can be mounted * FUNCTION: Tests if the device contains a filesystem that can be mounted
* by this fsd * by this fsd
*/ */
{ {
BootSector* Boot; BootSector* Boot;
@ -692,8 +730,6 @@ NTSTATUS FsdMountDevice(PDEVICE_EXTENSION DeviceExt,
* FUNCTION: Mounts the device * FUNCTION: Mounts the device
*/ */
{ {
int i;
DPRINT("Mounting VFAT device..."); DPRINT("Mounting VFAT device...");
DPRINT("DeviceExt %x\n",DeviceExt); DPRINT("DeviceExt %x\n",DeviceExt);
@ -738,9 +774,13 @@ NTSTATUS FsdMountDevice(PDEVICE_EXTENSION DeviceExt,
DeviceExt->FAT = ExAllocatePool(NonPagedPool, BLOCKSIZE*DeviceExt->Boot->FATSectors); DeviceExt->FAT = ExAllocatePool(NonPagedPool, BLOCKSIZE*DeviceExt->Boot->FATSectors);
VFATReadSectors(DeviceToMount, DeviceExt->FATStart, DeviceExt->Boot->FATSectors, (UCHAR *)DeviceExt->FAT); VFATReadSectors(DeviceToMount, DeviceExt->FATStart, DeviceExt->Boot->FATSectors, (UCHAR *)DeviceExt->FAT);
} }
return STATUS_SUCCESS;
} }
void VFATLoadCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster) void VFATLoadCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster)
/*
* FUNCTION: Load a cluster from the physical device
*/
{ {
ULONG Sector; ULONG Sector;
@ -756,6 +796,9 @@ void VFATLoadCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster)
} }
void VFATWriteCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster) void VFATWriteCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster)
/*
* FUNCTION: Write a cluster to the physical device
*/
{ {
ULONG Sector; ULONG Sector;
@ -794,6 +837,9 @@ NTSTATUS FsdReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
CurrentCluster = Fcb->entry.FirstCluster+Fcb->entry.FirstClusterHigh*65536; CurrentCluster = Fcb->entry.FirstCluster+Fcb->entry.FirstClusterHigh*65536;
else else
CurrentCluster = Fcb->entry.FirstCluster; CurrentCluster = Fcb->entry.FirstCluster;
if (CurrentCluster<2)
return STATUS_UNSUCCESSFUL;// FIXME : root of FAT16 ?
DPRINT("DeviceExt->BytesPerCluster %x\n",DeviceExt->BytesPerCluster);
if (ReadOffset >= Fcb->entry.FileSize) if (ReadOffset >= Fcb->entry.FileSize)
{ {
@ -804,15 +850,11 @@ NTSTATUS FsdReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
Length = Fcb->entry.FileSize - ReadOffset; Length = Fcb->entry.FileSize - ReadOffset;
} }
*LengthRead = 0; *LengthRead = 0;
DPRINT("DeviceExt->BytesPerCluster %x\n",DeviceExt->BytesPerCluster);
Temp = ExAllocatePool(NonPagedPool,DeviceExt->BytesPerCluster); Temp = ExAllocatePool(NonPagedPool,DeviceExt->BytesPerCluster);
for (FileOffset=0; FileOffset < FirstCluster; FileOffset++) for (FileOffset=0; FileOffset < FirstCluster; FileOffset++)
{ {
CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster); CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster);
} }
CHECKPOINT; CHECKPOINT;
if ((ReadOffset % DeviceExt->BytesPerCluster)!=0) if ((ReadOffset % DeviceExt->BytesPerCluster)!=0)
{ {
@ -949,6 +991,9 @@ NTSTATUS FsdWriteFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
} }
NTSTATUS FsdClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Close a file
*/
{ {
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = Stack->FileObject; PFILE_OBJECT FileObject = Stack->FileObject;
@ -965,6 +1010,9 @@ NTSTATUS FsdClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
} }
NTSTATUS FsdCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Create or open a file
*/
{ {
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = Stack->FileObject; PFILE_OBJECT FileObject = Stack->FileObject;
@ -985,6 +1033,9 @@ NTSTATUS FsdCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Write to a file
*/
{ {
ULONG Length; ULONG Length;
PVOID Buffer; PVOID Buffer;
@ -998,7 +1049,7 @@ NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
Length = Stack->Parameters.Write.Length; Length = Stack->Parameters.Write.Length;
Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress); Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
Offset = GET_LARGE_INTEGER_LOW_PART(Stack->Parameters.Write.ByteOffset); Offset = Stack->Parameters.Write.ByteOffset.LowPart;
Status = FsdWriteFile(DeviceExt,FileObject,Buffer,Length,Offset); Status = FsdWriteFile(DeviceExt,FileObject,Buffer,Length,Offset);
@ -1010,6 +1061,9 @@ NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
} }
NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Read from a file
*/
{ {
ULONG Length; ULONG Length;
PVOID Buffer; PVOID Buffer;
@ -1024,14 +1078,13 @@ NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
Length = Stack->Parameters.Read.Length; Length = Stack->Parameters.Read.Length;
Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress); Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
Offset = GET_LARGE_INTEGER_LOW_PART(Stack->Parameters.Read.ByteOffset); Offset = Stack->Parameters.Read.ByteOffset.LowPart;
Status = FsdReadFile(DeviceExt,FileObject,Buffer,Length,Offset, Status = FsdReadFile(DeviceExt,FileObject,Buffer,Length,Offset,
&LengthRead); &LengthRead);
Irp->IoStatus.Status = Status; Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = LengthRead; Irp->IoStatus.Information = LengthRead;
IoCompleteRequest(Irp,IO_NO_INCREMENT); IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(Status); return(Status);
@ -1039,6 +1092,9 @@ NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
NTSTATUS FsdMount(PDEVICE_OBJECT DeviceToMount) NTSTATUS FsdMount(PDEVICE_OBJECT DeviceToMount)
/*
* FUNCTION: Mount the filesystem
*/
{ {
PDEVICE_OBJECT DeviceObject; PDEVICE_OBJECT DeviceObject;
PDEVICE_EXTENSION DeviceExt; PDEVICE_EXTENSION DeviceExt;
@ -1052,15 +1108,19 @@ NTSTATUS FsdMount(PDEVICE_OBJECT DeviceToMount)
&DeviceObject); &DeviceObject);
DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO; DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
DeviceExt = (PVOID)DeviceObject->DeviceExtension; DeviceExt = (PVOID)DeviceObject->DeviceExtension;
// use same vpb as device disk
DeviceObject->Vpb=DeviceToMount->Vpb;
FsdMountDevice(DeviceExt,DeviceToMount); FsdMountDevice(DeviceExt,DeviceToMount);
DeviceObject->Vpb->Flags |= VPB_MOUNTED;
DeviceExt->StorageDevice = IoAttachDeviceToDeviceStack(DeviceObject, DeviceExt->StorageDevice = IoAttachDeviceToDeviceStack(DeviceObject,
DeviceToMount); DeviceToMount);
return(STATUS_SUCCESS); return(STATUS_SUCCESS);
} }
NTSTATUS FsdFileSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdFileSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: File system control
*/
{ {
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
// PVPB vpb = Stack->Parameters.Mount.Vpb; // PVPB vpb = Stack->Parameters.Mount.Vpb;
@ -1088,7 +1148,10 @@ NTSTATUS FsdFileSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
} }
NTSTATUS FsdGetStandardInformation(PFCB FCB, PDEVICE_OBJECT DeviceObject, NTSTATUS FsdGetStandardInformation(PFCB FCB, PDEVICE_OBJECT DeviceObject,
PFILE_STANDARD_INFORMATION StandardInfo) PFILE_STANDARD_INFORMATION StandardInfo)
/*
* FUNCTION: Retrieve the standard file information
*/
{ {
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
unsigned long AllocSize; unsigned long AllocSize;
@ -1096,10 +1159,9 @@ NTSTATUS FsdGetStandardInformation(PFCB FCB, PDEVICE_OBJECT DeviceObject,
RtlZeroMemory(StandardInfo, sizeof(FILE_STANDARD_INFORMATION)); RtlZeroMemory(StandardInfo, sizeof(FILE_STANDARD_INFORMATION));
/* Make allocsize a rounded up multiple of BytesPerCluster */ /* Make allocsize a rounded up multiple of BytesPerCluster */
AllocSize = 0; AllocSize = ((FCB->entry.FileSize + DeviceExtension->BytesPerCluster - 1) /
while(AllocSize<FCB->entry.FileSize) { DeviceExtension->BytesPerCluster) *
AllocSize+=DeviceExtension->BytesPerCluster; DeviceExtension->BytesPerCluster;
}
StandardInfo->AllocationSize = RtlConvertUlongToLargeInteger(AllocSize); StandardInfo->AllocationSize = RtlConvertUlongToLargeInteger(AllocSize);
StandardInfo->EndOfFile = RtlConvertUlongToLargeInteger(FCB->entry.FileSize); StandardInfo->EndOfFile = RtlConvertUlongToLargeInteger(FCB->entry.FileSize);
@ -1115,20 +1177,24 @@ NTSTATUS FsdGetStandardInformation(PFCB FCB, PDEVICE_OBJECT DeviceObject,
} }
NTSTATUS FsdQueryInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp) NTSTATUS FsdQueryInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Retrieve the specified file information
*/
{ {
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
FILE_INFORMATION_CLASS FileInformationClass = FILE_INFORMATION_CLASS FileInformationClass =
Stack->Parameters.QueryFile.FileInformationClass; Stack->Parameters.QueryFile.FileInformationClass;
PFILE_OBJECT FileObject = NULL; PFILE_OBJECT FileObject = NULL;
PFCB FCB = NULL; PFCB FCB = NULL;
PCCB CCB = NULL; // PCCB CCB = NULL;
NTSTATUS RC = STATUS_SUCCESS; NTSTATUS RC = STATUS_SUCCESS;
void *SystemBuffer; void *SystemBuffer;
FileObject = Stack->FileObject; FileObject = Stack->FileObject;
CCB = (PCCB)(FileObject->FsContext2); // CCB = (PCCB)(FileObject->FsContext2);
FCB = CCB->Buffer; // Should be CCB->FCB??? // FCB = CCB->Buffer; // Should be CCB->FCB???
FCB=(PFCB)(FileObject->FsContext);
SystemBuffer = Irp->AssociatedIrp.SystemBuffer; SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
@ -1136,9 +1202,6 @@ NTSTATUS FsdQueryInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp)
case FileStandardInformation: case FileStandardInformation:
RC = FsdGetStandardInformation(FCB, DeviceObject, SystemBuffer); RC = FsdGetStandardInformation(FCB, DeviceObject, SystemBuffer);
break; break;
default:
RC = STATUS_NOT_IMPLEMENTED;
} }
return RC; return RC;
@ -1159,7 +1222,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT _DriverObject,
UNICODE_STRING ustr; UNICODE_STRING ustr;
ANSI_STRING astr; ANSI_STRING astr;
DbgPrint("VFAT 0.0.4\n"); DbgPrint("VFAT 0.0.5\n");
DriverObject = _DriverObject; DriverObject = _DriverObject;
@ -1181,6 +1244,9 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT _DriverObject,
FsdFileSystemControl; FsdFileSystemControl;
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
FsdQueryInformation; FsdQueryInformation;
DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] =
FsdDirectoryControl;
DriverObject->DriverUnload = NULL; DriverObject->DriverUnload = NULL;
IoRegisterFileSystem(DeviceObject); IoRegisterFileSystem(DeviceObject);

View file

@ -3,7 +3,10 @@
%.o: %.asm %.o: %.asm
$(NASM) $(NFLAGS) $< -o $@ $(NASM) $(NFLAGS) $< -o $@
all: blockdev.o iface.o OBJECTS= blockdev.o iface.o dir.o
$(LD) iface.o blockdev.o -r -o vfatfsd.o
all: $(OBJECTS)
$(LD) $(OBJECTS) -r -o vfatfsd.o
$(NM) --numeric-sort vfatfsd.o > vfatfsd.sym
include ../../../rules.mak include ../../../rules.mak

View file

@ -1,12 +1,5 @@
BOOLEAN VFATReadSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector,
IN ULONG SectorCount,
IN UCHAR* Buffer);
BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector,
IN ULONG SectorCount,
IN UCHAR* Buffer);
struct _BootSector { struct _BootSector {
unsigned char magic0, res0, magic1; unsigned char magic0, res0, magic1;
@ -45,9 +38,11 @@ struct _BootSector32 {
typedef struct _BootSector BootSector; typedef struct _BootSector BootSector;
struct _FATDirEntry { struct _FATDirEntry {
unsigned char Filename[8], Ext[3], Attrib, Res[8]; unsigned char Filename[8], Ext[3], Attrib, Res[2];
unsigned short CreationTime,CreationDate,AccessDate;
unsigned short FirstClusterHigh;// higher unsigned short FirstClusterHigh;// higher
unsigned char Res2[4]; unsigned short UpdateTime;//time create/update
unsigned short UpdateDate;//date create/update
unsigned short FirstCluster; unsigned short FirstCluster;
unsigned long FileSize; unsigned long FileSize;
} __attribute__((packed)); } __attribute__((packed));
@ -108,3 +103,59 @@ typedef struct _SFsdFileControlBlock {
} SFsdFCB, *PtrSFsdFCB; } SFsdFCB, *PtrSFsdFCB;
*/ */
#define FAT16 (1)
#define FAT12 (2)
#define FAT32 (3)
typedef struct
{
PDEVICE_OBJECT StorageDevice;
BootSector *Boot;
int rootDirectorySectors, FATStart, rootStart, dataStart;
int FATEntriesPerSector, FATUnit;
ULONG BytesPerCluster;
ULONG FatType;
unsigned char* FAT;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
typedef struct
{
FATDirEntry entry;
WCHAR ObjectName[251];// filename has 250 characters max
ULONG StartSector;
ULONG StartEntry;//for DirectoryControl
} FCB, *PFCB;
#define ENTRIES_PER_SECTOR (BLOCKSIZE / sizeof(FATDirEntry))
// functions called by i/o manager :
NTSTATUS DriverEntry(PDRIVER_OBJECT _DriverObject,PUNICODE_STRING RegistryPath);
NTSTATUS FsdDirectoryControl(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdClose(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdFileSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS FsdQueryInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp);
// internal functions in blockdev.c
BOOLEAN VFATReadSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector,
IN ULONG SectorCount,
IN UCHAR* Buffer);
BOOLEAN VFATWriteSectors(IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector,
IN ULONG SectorCount,
IN UCHAR* Buffer);
//internal functions in iface.c :
NTSTATUS FsdGetStandardInformation(PFCB FCB, PDEVICE_OBJECT DeviceObject,
PFILE_STANDARD_INFORMATION StandardInfo);
NTSTATUS FindFile(PDEVICE_EXTENSION DeviceExt, PFCB Fcb,
PFCB Parent, PWSTR FileToFind,ULONG *StartSector,ULONG *Entry);
wchar_t * vfat_wcsncpy(wchar_t * dest, const wchar_t *src,size_t wcount);

View file

@ -153,12 +153,17 @@ typedef struct _IO_STACK_LOCATION
} u; } u;
} SetFile; } SetFile;
/*
* This is a guess
*/
struct struct
{ {
ULONG Length;
PUNICODE_STRING FileName;
FILE_INFORMATION_CLASS FileInformationClass; FILE_INFORMATION_CLASS FileInformationClass;
ULONG FileIndex; BOOLEAN ReturnSingleEntry;
PUNICODE_STRING FileName;
BOOLEAN RestartScan;
ULONG BufferLength;
ULONG FileIndex;
} QueryDirectory; } QueryDirectory;
} Parameters; } Parameters;

View file

@ -164,4 +164,124 @@ __OUTS(l)
__inlc_p(port) : \ __inlc_p(port) : \
__inl_p(port)) __inl_p(port))
/* HAL port mapped I/O functions */
#define READ_PORT_UCHAR(port) inb_p(port)
#define READ_PORT_USHORT(port) inw_p(port)
#define READ_PORT_ULONG(port) inl_p(port)
#define READ_PORT_BUFFER_UCHAR(port, buffer, count) insb(port, buffer, count)
#define READ_PORT_BUFFER_USHORT(port, buffer, count) insw(port, buffer, count)
#define READ_PORT_BUFFER_ULONG(port, buffer, count) insl(port, buffer, count)
#define WRITE_PORT_UCHAR(port, value) outb_p(port, value)
#define WRITE_PORT_USHORT(port, value) outw_p(port, value)
#define WRITE_PORT_ULONG(port, value) outl_p(port, value)
#define WRITE_PORT_BUFFER_UCHAR(port, buffer, count) outsb(port, buffer, count)
#define WRITE_PORT_BUFFER_USHORT(port, buffer, count) outsw(port, buffer, count)
#define WRITE_PORT_BUFFER_ULONG(port, buffer, count) outsl(port, buffer, count)
/* HAL Memory mapped I/O functions */
/* FIXME: these ops should be 'lock' prefixed */
extern inline unsigned char
READ_REGISTER_UCHAR(unsigned char *Register)
{
return *Register;
}
extern inline unsigned short
READ_REGISTER_USHORT(unsigned short *Register)
{
return *Register;
}
extern inline unsigned long
READ_REGISTER_ULONG(unsigned long *Register)
{
return *Register;
}
extern inline void
READ_REGISTER_BUFFER_UCHAR(unsigned char *Register,
unsigned char *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
extern inline void
READ_REGISTER_BUFFER_USHORT(unsigned short *Register,
unsigned short *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
extern inline void
READ_REGISTER_BUFFER_ULONG(unsigned long *Register,
unsigned long *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
extern inline void
WRITE_REGISTER_UCHAR(unsigned char *Register, unsigned char Value)
{
*Register = Value;
}
extern inline void
WRITE_REGISTER_USHORT(unsigned short *Register, unsigned short Value)
{
*Register = Value;
}
extern inline void
WRITE_REGISTER_ULONG(unsigned long *Register, unsigned long Value)
{
*Register = Value;
}
extern inline void
WRITE_REGISTER_BUFFER_UCHAR(unsigned char *Register,
unsigned char *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
extern inline void
WRITE_REGISTER_BUFFER_USHORT(unsigned short *Register,
unsigned short *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
extern inline void
WRITE_REGISTER_BUFFER_ULONG(unsigned long *Register,
unsigned long *Buffer,
unsigned long Count)
{
while (Count--)
{
*Buffer++ = *Register++;
}
}
#endif #endif

View file

@ -3,7 +3,7 @@
* PROJECT: ReactOS kernel * PROJECT: ReactOS kernel
* FILE: ntoskrnl/io/dir.c * FILE: ntoskrnl/io/dir.c
* PURPOSE: Directory functions * PURPOSE: Directory functions
* PROGRAMMER: David Welch (welch@cwcom.net) * PROGRAMMER: David Welch (welch@mcmail.com)
* UPDATE HISTORY: * UPDATE HISTORY:
* Created 22/05/98 * Created 22/05/98
*/ */
@ -13,22 +13,25 @@
#include <ddk/ntddk.h> #include <ddk/ntddk.h>
#include <internal/io.h> #include <internal/io.h>
#define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
NTSTATUS STDCALL NtQueryDirectoryFile(IN HANDLE FileHandle, NTSTATUS STDCALL
IN HANDLE Event OPTIONAL, NtQueryDirectoryFile(
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN HANDLE FileHandle,
IN PVOID ApcContext OPTIONAL, IN HANDLE Event OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
OUT PVOID FileInformation, IN PVOID ApcContext OPTIONAL,
IN ULONG Length, OUT PIO_STATUS_BLOCK IoStatusBlock,
IN FILE_INFORMATION_CLASS OUT PVOID FileInformation,
FileInformationClass, IN ULONG Length,
IN BOOLEAN ReturnSingleEntry, IN FILE_INFORMATION_CLASS FileInformationClass,
IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN ReturnSingleEntry,
IN BOOLEAN RestartScan) IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartScan
)
{ {
return(ZwQueryDirectoryFile(FileHandle, return(ZwQueryDirectoryFile(FileHandle,
Event, Event,
@ -131,96 +134,46 @@ NTSTATUS STDCALL ZwQueryDirectoryFile(
NTSTATUS Status; NTSTATUS Status;
KEVENT Event; KEVENT Event;
PIO_STACK_LOCATION IoStack; PIO_STACK_LOCATION IoStack;
BOOLEAN IndexSpecified;
ULONG Idx;
Status = ObReferenceObjectByHandle(FileHandle, Status = ObReferenceObjectByHandle(FileHandle,
FILE_LIST_DIRECTORY, FILE_LIST_DIRECTORY,
IoFileType, IoFileType,
UserMode, UserMode,
&FileObject, (PVOID *)&FileObject,
NULL); NULL);
if (Status != STATUS_SUCCESS) if (Status != STATUS_SUCCESS)
{ {
return(Status); return(Status);
} }
KeInitializeEvent(&Event,NotificationEvent,FALSE);
DeviceObject = FileObject->DeviceObject; DeviceObject = FileObject->DeviceObject;
Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE); Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
if (Irp==NULL)
return STATUS_UNSUCCESSFUL;
KeInitializeEvent(&Event,NotificationEvent,FALSE);
Irp->UserIosb = IoStatusBlock; Irp->UserIosb = IoStatusBlock;
Irp->UserEvent = &Event; Irp->UserEvent = &Event;
Irp->UserBuffer=FileInformation;
IoStack = IoGetNextIrpStackLocation(Irp); IoStack = IoGetNextIrpStackLocation(Irp);
IoStack->Flags = 0;
if (RestartScan)
{
IoStack->Flags = IoStack->Flags | SL_RESTART_SCAN;
}
if (ReturnSingleEntry)
{
IoStack->Flags = IoStack->Flags | SL_RETURN_SINGLE_ENTRY;
}
switch (FileInformationClass)
{
case FileNameInformation:
Idx = ((PFILE_NAMES_INFORMATION)FileInformation)->FileIndex;
if (Idx != 0)
{
IoStack->Parameters.QueryDirectory.FileIndex = Idx;
IoStack->Flags = IoStack->Flags | SL_INDEX_SPECIFIED;
}
break;
case FileDirectoryInformation:
Idx = ((PFILE_DIRECTORY_INFORMATION)FileInformation)->FileIndex;
if (Idx != 0)
{
IoStack->Parameters.QueryDirectory.FileIndex = Idx;
IoStack->Flags = IoStack->Flags | SL_INDEX_SPECIFIED;
}
break;
case FileFullDirectoryInformation:
Idx = ((PFILE_FULL_DIR_INFORMATION)FileInformation)->FileIndex;
if (Idx != 0)
{
IoStack->Parameters.QueryDirectory.FileIndex = Idx;
IoStack->Flags = IoStack->Flags | SL_INDEX_SPECIFIED;
}
break;
case FileBothDirectoryInformation:
Idx = ((PFILE_BOTH_DIR_INFORMATION)FileInformation)->FileIndex;
if (Idx != 0)
{
IoStack->Parameters.QueryDirectory.FileIndex = Idx;
IoStack->Flags = IoStack->Flags | SL_INDEX_SPECIFIED;
}
break;
default:
return(STATUS_UNSUCCESSFUL);
}
IoStack->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; IoStack->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
IoStack->MinorFunction = IRP_MN_QUERY_DIRECTORY; IoStack->MinorFunction = IRP_MN_QUERY_DIRECTORY;
IoStack->Flags = 0;
IoStack->Control = 0; IoStack->Control = 0;
IoStack->DeviceObject = DeviceObject; IoStack->DeviceObject = DeviceObject;
IoStack->FileObject = FileObject; IoStack->FileObject = FileObject;
IoStack->Parameters.QueryDirectory.FileInformationClass = IoStack->Parameters.QueryDirectory.FileInformationClass =
FileInformationClass; FileInformationClass;
IoStack->Parameters.QueryDirectory.ReturnSingleEntry =
ReturnSingleEntry;
IoStack->Parameters.QueryDirectory.FileName = FileName; IoStack->Parameters.QueryDirectory.FileName = FileName;
IoStack->Parameters.QueryDirectory.Length = Length; IoStack->Parameters.QueryDirectory.RestartScan = RestartScan;
IoStack->Parameters.QueryDirectory.BufferLength = Length;
Status = IoCallDriver(FileObject->DeviceObject,Irp); Status = IoCallDriver(FileObject->DeviceObject,Irp);
if (Status==STATUS_PENDING && (FileObject->Flags & FO_SYNCHRONOUS_IO)) if (Status==STATUS_PENDING && (FileObject->Flags & FO_SYNCHRONOUS_IO))

View file

@ -107,7 +107,10 @@ LdrLoadDriver(PUNICODE_STRING Filename)
0, 0,
NULL, NULL,
NULL); NULL);
Status = ZwOpenFile(&FileHandle, 0, &FileObjectAttributes, NULL, 0, 0); Status = ZwOpenFile(&FileHandle,
FILE_ALL_ACCESS,
&FileObjectAttributes,
NULL, 0, 0);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
return Status; return Status;

View file

@ -28,7 +28,9 @@ static unsigned char TstReadLineReadChar(VOID)
KEY_EVENT_RECORD key[2]; KEY_EVENT_RECORD key[2];
IO_STATUS_BLOCK IoStatusBlock; IO_STATUS_BLOCK IoStatusBlock;
ZwReadFile(KeyboardHandle, key[0].AsciiChar=0;
while(!key[0].AsciiChar)
ZwReadFile(KeyboardHandle,
NULL, NULL,
NULL, NULL,
NULL, NULL,
@ -37,7 +39,6 @@ static unsigned char TstReadLineReadChar(VOID)
sizeof(KEY_EVENT_RECORD)*2, sizeof(KEY_EVENT_RECORD)*2,
0, 0,
0); 0);
// DbgPrint("%c",key[0].AsciiChar);
return(key[0].AsciiChar); return(key[0].AsciiChar);
} }
@ -50,7 +51,6 @@ VOID TstReadLine(ULONG Length, PCHAR Buffer)
for (i=0;i<Length;i++) for (i=0;i<Length;i++)
{ {
tmp = TstReadLineReadChar(); tmp = TstReadLineReadChar();
// DbgPrint("%x %x ",tmp,'\n');
switch (tmp) switch (tmp)
{ {
case 0xd: case 0xd:

View file

@ -19,16 +19,21 @@
#define NDEBUG #define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
int ShellChangeDir(char* args); int ShellChangeDirOb(char* args);
int ShellListDir(char* args); int ShellListDirOb(char* args);
int ShellChangeDirFile(char* args);
int ShellListDirFile(char* args);
VOID TstReadLineInit(VOID); VOID TstReadLineInit(VOID);
VOID TstReadLine(ULONG Length, PCHAR Buffer); VOID TstReadLine(ULONG Length, PCHAR Buffer);
/* GLOBALS ******************************************************************/ /* GLOBALS ******************************************************************/
static HANDLE CurrentDirHandle = NULL; static HANDLE CurrentDirHandle = NULL;
static UNICODE_STRING CurrentDirName = {NULL,0,0}; static UNICODE_STRING CurrentDirName = {0,0,NULL};
static char current_dir_name[255] = {0,}; static char current_dir_name[255] = {0,};
static HANDLE CurrentDirHandleF = NULL;
static UNICODE_STRING CurrentDirNameF = {0,0,NULL};
static char current_dir_name_f[255] = {0,};
typedef struct typedef struct
{ {
@ -38,8 +43,10 @@ typedef struct
command commands[]= command commands[]=
{ {
{"cd",ShellChangeDir}, {"fcd",ShellChangeDirFile},
{"dir",ShellListDir}, {"fdir",ShellListDirFile},
{"ocd",ShellChangeDirOb},
{"odir",ShellListDirOb},
{NULL,NULL}, {NULL,NULL},
}; };
@ -54,11 +61,12 @@ char* eat_white_space(char* s)
return(s); return(s);
} }
int ShellChangeDir(char* args) int ShellChangeDirOb(char* args)
{ {
char* end; char* end;
ANSI_STRING astr; ANSI_STRING astr;
OBJECT_ATTRIBUTES attr; OBJECT_ATTRIBUTES attr;
NTSTATUS Status;
DPRINT("ShellChangeDir(args %s)\n",args); DPRINT("ShellChangeDir(args %s)\n",args);
@ -68,7 +76,8 @@ int ShellChangeDir(char* args)
{ {
*end=0; *end=0;
} }
strcat(current_dir_name,args); if (args[0]=='\\') strcpy(current_dir_name,args);
else strcat(current_dir_name,args);
DPRINT("current_dir_name %s\n",current_dir_name); DPRINT("current_dir_name %s\n",current_dir_name);
@ -76,33 +85,130 @@ int ShellChangeDir(char* args)
RtlAnsiStringToUnicodeString(&CurrentDirName,&astr,TRUE); RtlAnsiStringToUnicodeString(&CurrentDirName,&astr,TRUE);
InitializeObjectAttributes(&attr,&CurrentDirName,0,NULL,NULL); InitializeObjectAttributes(&attr,&CurrentDirName,0,NULL,NULL);
ZwClose(CurrentDirHandle); ZwClose(CurrentDirHandle);
ZwOpenDirectoryObject(&CurrentDirHandle,0,&attr); Status=ZwOpenDirectoryObject(&CurrentDirHandle,0,&attr);
DbgPrint("Status=%x,dirhandle=%d\n",Status,CurrentDirHandle);
return 0;
} }
int ShellListDir(char* args) int ShellChangeDirFile(char* args)
{
char* end;
ANSI_STRING astr;
OBJECT_ATTRIBUTES attr;
NTSTATUS Status;
DPRINT("ShellChangeDir(args %s)\n",args);
args = eat_white_space(args);
end = strchr(args,' ');
if (end!=NULL)
{
*end=0;
}
if (args[0]=='\\') strcpy(current_dir_name_f,args);
else if (args[0])
{
strcat(current_dir_name_f,"\\");
strcat(current_dir_name_f,args);
}
DPRINT("current_dir_name_f %s\n",current_dir_name_f);
RtlInitAnsiString(&astr,current_dir_name_f);
RtlAnsiStringToUnicodeString(&CurrentDirNameF,&astr,TRUE);
InitializeObjectAttributes(&attr,&CurrentDirNameF,0,NULL,NULL);
ZwClose(CurrentDirHandleF);
Status=ZwOpenFile(&CurrentDirHandleF,FILE_ALL_ACCESS,&attr,NULL,0,0);
DbgPrint("Status=%x,dirhandle=%d\n",Status,CurrentDirHandleF);
return 0;
}
int ShellListDirOb(char* args)
{ {
OBJDIR_INFORMATION DirObj[50]; OBJDIR_INFORMATION DirObj[50];
ULONG Idx; ULONG Idx=0;
ULONG Length; ULONG Length=0;
ULONG i; ULONG i;
if (args) args = eat_white_space(args);
DbgPrint("ShellListDir(args %s)\n",args); DbgPrint("ShellListDir(args %s)\n",args);
ZwQueryDirectoryObject(CurrentDirHandle, ZwQueryDirectoryObject(CurrentDirHandle,
&(DirObj[0]), &(DirObj[0]),
sizeof(DirObj), sizeof(DirObj),
TRUE, TRUE,
TRUE, TRUE,
&Idx, &Idx,
&Length); &Length);
DbgPrint("read %d bytes,Idx=%d\n",Length,Idx);
for (i=0;i<(Length/sizeof(OBJDIR_INFORMATION));i++) for (i=0;i<(Length/sizeof(OBJDIR_INFORMATION));i++)
{ {
DbgPrint("Scanning %w\n",DirObj[i].ObjectName.Buffer); DbgPrint("i=%d ; ",i);
DbgPrint("Scanning %w\n",DirObj[i].ObjectName.Buffer);
} }
return 0;
} }
VOID ShellDisplayPrompt() int ShellListDirFile(char* args)
{
PFILE_BOTH_DIRECTORY_INFORMATION dirInfo,dirInfo2;
NTSTATUS Status;
ANSI_STRING afilename;
UNICODE_STRING ToFind;
WCHAR Name[251];
short i;
int Length=512;
dirInfo=ExAllocatePool(NonPagedPool,Length);
if (args) args = eat_white_space(args);
if(args && args[0])
RtlInitAnsiString(&afilename,args);
else
RtlInitAnsiString(&afilename,"*");
RtlAnsiStringToUnicodeString(&ToFind,&afilename,TRUE);
DbgPrint("ShellListDir(args %s)\n",args);
DPRINT("before ZwQueryDirectoryFile\n");
Status=ZwQueryDirectoryFile(CurrentDirHandleF,NULL,NULL,NULL
,NULL
, &(dirInfo[0])
,Length
,FileBothDirectoryInformation
,TRUE
,&ToFind
,TRUE);
DPRINT("after ZwQueryDirectoryFile\n");
while(NT_SUCCESS(Status))
{
CHECKPOINT;
dirInfo2=dirInfo;
while(1)
{
memcpy(Name,dirInfo2[0].ShortName,12*sizeof(WCHAR));
Name[(int)(dirInfo2[0].ShortNameLength)]=0;
DbgPrint(" %w",Name);
for (i=dirInfo2[0].ShortNameLength; i<14;i++) DbgPrint(" ");
memcpy(Name,dirInfo2[0].FileName,dirInfo2[0].FileNameLength*sizeof(WCHAR));
Name[dirInfo2[0].FileNameLength]=0;
DbgPrint(" %w\n",Name);
if(!dirInfo2->NextEntryOffset) break;
dirInfo2=(PFILE_BOTH_DIRECTORY_INFORMATION)
(((char *)dirInfo2)+dirInfo2->NextEntryOffset);
}
CHECKPOINT;
Status=ZwQueryDirectoryFile(CurrentDirHandleF,NULL,NULL,NULL
,NULL
, &(dirInfo[0])
,Length
,FileBothDirectoryInformation
, FALSE
,&ToFind
,FALSE);
CHECKPOINT;
}
ExFreePool(dirInfo);
return 0;
}
VOID ShellDisplayPrompt(VOID)
{ {
printk("%w# ",CurrentDirName.Buffer); printk("%w# ",CurrentDirName.Buffer);
} }
@ -110,7 +216,7 @@ VOID ShellDisplayPrompt()
VOID ShellProcessCommand(char* cmd) VOID ShellProcessCommand(char* cmd)
{ {
unsigned int i=0; unsigned int i=0;
DbgPrint("Processing cmd '%s'\n",cmd); DbgPrint("Processing cmd :%s.\n",cmd);
while (commands[i].name!=NULL) while (commands[i].name!=NULL)
{ {
DbgPrint("Scanning %s i %d\n",commands[i].name,i); DbgPrint("Scanning %s i %d\n",commands[i].name,i);
@ -142,6 +248,7 @@ NTSTATUS TstShell(VOID)
InitializeObjectAttributes(&attr,&ufilename,0,NULL,NULL); InitializeObjectAttributes(&attr,&ufilename,0,NULL,NULL);
ZwOpenDirectoryObject(&CurrentDirHandle,0,&attr); ZwOpenDirectoryObject(&CurrentDirHandle,0,&attr);
ShellChangeDirFile("\\??\\C:\\");
TstReadLineInit(); TstReadLineInit();