reactos/reactos/drivers/fs/vfat/iface.c

2171 lines
60 KiB
C
Raw Normal View History

/* $Id: iface.c,v 1.38 1999/12/04 20:58:42 ea Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: services/fs/vfat/iface.c
* PURPOSE: VFAT Filesystem
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
* UPDATE HISTORY:
?? Created
24-10-1998 Fixed bugs in long filename support
Fixed a bug that prevented unsuccessful file open requests being reported
Now works with long filenames that span over a sector boundary
28-10-1998 Reads entire FAT into memory
VFatReadSector modified to read in more than one sector at a time
7-11-1998 Fixed bug that assumed that directory data could be fragmented
8-12-1998 Added FAT32 support
Added initial writability functions
WARNING: DO NOT ATTEMPT TO TEST WRITABILITY FUNCTIONS!!!
12-12-1998 Added basic support for FILE_STANDARD_INFORMATION request
*/
/* INCLUDES *****************************************************************/
#include <wchar.h>
#include <internal/string.h>
#include <ddk/ntddk.h>
#include <ddk/cctypes.h>
#define NDEBUG
#include <internal/debug.h>
#include "vfat.h"
//#include "dbgpool.c"
/* GLOBALS *****************************************************************/
static PDRIVER_OBJECT VFATDriverObject;
PVfatFCB pFirstFcb;
/* FUNCTIONS ****************************************************************/
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 FATeis;
PULONG Block;
Block = ExAllocatePool(NonPagedPool,1024);
FATsector=CurrentCluster/(512/sizeof(ULONG));
FATeis=CurrentCluster-(FATsector*(512/sizeof(ULONG)));
VFATReadSectors(DeviceExt->StorageDevice
,(ULONG)(DeviceExt->FATStart+FATsector), 1,(UCHAR*) Block);
CurrentCluster = Block[FATeis];
if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
CurrentCluster = 0xffffffff;
ExFreePool(Block);
return(CurrentCluster);
}
ULONG Fat16GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next FAT16 cluster from the FAT table from the
* in-memory FAT
*/
{
PUSHORT Block;
Block=(PUSHORT)DeviceExt->FAT;
CurrentCluster = Block[CurrentCluster];
if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
CurrentCluster = 0xffffffff;
DPRINT("Returning %x\n",CurrentCluster);
return(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;
ULONG FATOffset;
ULONG Entry;
CBlock = DeviceExt->FAT;
FATOffset = (CurrentCluster * 12)/ 8;//first byte containing value
if ((CurrentCluster % 2) == 0)
{
Entry = CBlock[FATOffset];
Entry |= ((CBlock[FATOffset+1] & 0xf)<<8);
}
else
{
Entry = (CBlock[FATOffset] >> 4);
Entry |= (CBlock[FATOffset+1] << 4);
}
DPRINT("Entry %x\n",Entry);
if (Entry >= 0xff8 && Entry <= 0xfff)
Entry = 0xffffffff;
DPRINT("Returning %x\n",Entry);
return(Entry);
}
ULONG GetNextCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Retrieve the next cluster depending on the FAT type
*/
{
DPRINT("GetNextCluster(DeviceExt %x, CurrentCluster %x)\n",
DeviceExt,CurrentCluster);
if (DeviceExt->FatType == FAT16)
return(Fat16GetNextCluster(DeviceExt, CurrentCluster));
else if (DeviceExt->FatType == FAT32)
return(Fat32GetNextCluster(DeviceExt, CurrentCluster));
else
return(Fat12GetNextCluster(DeviceExt, CurrentCluster));
}
ULONG FAT16FindAvailableCluster(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Finds the first available cluster in a FAT16 table
*/
{
PUSHORT Block;
int i;
Block=(PUSHORT)DeviceExt->FAT;
for(i=2;i<(DeviceExt->Boot->FATSectors*256) ;i++)
if(Block[i]==0)
return (i);
/* Give an error message (out of disk space) if we reach here) */
return 0;
}
ULONG FAT12FindAvailableCluster(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Finds the first available cluster in a FAT12 table
*/
{
ULONG FATOffset;
ULONG Entry;
PUCHAR CBlock=DeviceExt->FAT;
ULONG i;
for(i=2;i<((DeviceExt->Boot->FATSectors*512*8)/12) ;i++)
{
FATOffset = (i * 12)/8;
if ((i % 2) == 0)
{
Entry = CBlock[FATOffset];
Entry |= ((CBlock[FATOffset + 1] & 0xf)<<8);
}
else
{
Entry = (CBlock[FATOffset] >> 4);
Entry |= (CBlock[FATOffset + 1] << 4);
}
if(Entry==0)
return (i);
}
/* Give an error message (out of disk space) if we reach here) */
DbgPrint("Disk full, %d clusters used\n",i);
return 0;
}
ULONG FAT32FindAvailableCluster(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Finds the first available cluster in a FAT32 table
*/
{
ULONG sector;
PULONG Block;
int i;
Block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
for(sector=0
;sector< ((struct _BootSector32*)(DeviceExt->Boot))->FATSectors32
;sector++)
{
VFATReadSectors(DeviceExt->StorageDevice
,(ULONG)(DeviceExt->FATStart+sector), 1,(UCHAR*) Block);
for(i=0; i<512; i++)
{
if(Block[i]==0)
{
ExFreePool(Block);
return (i+sector*128);
}
}
}
/* Give an error message (out of disk space) if we reach here) */
ExFreePool(Block);
return 0;
}
ULONG FAT12CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Counts free cluster in a FAT12 table
*/
{
ULONG FATOffset;
ULONG Entry;
PUCHAR CBlock=DeviceExt->FAT;
ULONG ulCount = 0;
ULONG i;
for(i=2;i<((DeviceExt->Boot->FATSectors*512*8)/12) ;i++)
{
FATOffset = (i * 12)/8;
if ((i % 2) == 0)
{
Entry = CBlock[FATOffset];
Entry |= ((CBlock[FATOffset + 1] & 0xf)<<8);
}
else
{
Entry = (CBlock[FATOffset] >> 4);
Entry |= (CBlock[FATOffset + 1] << 4);
}
if(Entry==0)
ulCount++;
}
return ulCount;
}
ULONG FAT16CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Counts free clusters in a FAT16 table
*/
{
PUSHORT Block;
ULONG ulCount = 0;
ULONG i;
Block=(PUSHORT)DeviceExt->FAT;
for(i=2;i<(DeviceExt->Boot->FATSectors*256) ;i++)
if(Block[i]==0)
ulCount++;
return ulCount;
}
ULONG FAT32CountAvailableClusters(PDEVICE_EXTENSION DeviceExt)
/*
* FUNCTION: Counts free clusters in a FAT32 table
*/
{
ULONG sector;
PULONG Block;
ULONG ulCount = 0;
ULONG i;
Block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
for(sector=0
;sector< ((struct _BootSector32*)(DeviceExt->Boot))->FATSectors32
;sector++)
{
VFATReadSectors(DeviceExt->StorageDevice
,(ULONG)(DeviceExt->FATStart+sector), 1,(UCHAR*) Block);
for(i=0; i<512; i++)
{
if(Block[i]==0)
ulCount++;
}
}
/* Give an error message (out of disk space) if we reach here) */
ExFreePool(Block);
return ulCount;
}
void FAT12WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue)
/*
* FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
*/
{
ULONG FATsector;
ULONG FATOffset;
PUCHAR CBlock=DeviceExt->FAT;
int i;
FATOffset = (ClusterToWrite * 12)/8;
if ((ClusterToWrite % 2) == 0)
{
CBlock[FATOffset]=NewValue;
CBlock[FATOffset + 1] &=0xf0;
CBlock[FATOffset + 1]
|= (NewValue&0xf00)>>8;
}
else
{
CBlock[FATOffset] &=0x0f;
CBlock[FATOffset]
|= (NewValue&0xf)<<4;
CBlock[FATOffset+1]=NewValue>>4;
}
/* Write the changed FAT sector(s) to disk */
FATsector=FATOffset/BLOCKSIZE;
for(i=0;i<DeviceExt->Boot->FATCount;i++)
{
if( (FATOffset%BLOCKSIZE)==(BLOCKSIZE-1))//entry is on 2 sectors
{
VFATWriteSectors(DeviceExt->StorageDevice,
DeviceExt->FATStart+FATsector
+i*DeviceExt->Boot->FATSectors,
2,
CBlock+FATsector*512);
}
else
{
VFATWriteSectors(DeviceExt->StorageDevice,
DeviceExt->FATStart+FATsector
+i*DeviceExt->Boot->FATSectors,
1,
CBlock+FATsector*512);
}
}
}
void FAT16WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue)
/*
* FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
*/
{
ULONG FATsector;
PUSHORT Block;
DbgPrint("FAT16WriteCluster %u : %u\n",ClusterToWrite,NewValue);
Block=(PUSHORT)DeviceExt->FAT;
FATsector=ClusterToWrite/(512/sizeof(USHORT));
/* Update the in-memory FAT */
Block[ClusterToWrite] = NewValue;
/* Write the changed FAT sector to disk */
VFATWriteSectors(DeviceExt->StorageDevice,
DeviceExt->FATStart+FATsector,
1,
(UCHAR *)Block);
}
void FAT32WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue)
/*
* FUNCTION: Writes a cluster to the FAT32 physical tables
*/
{
ULONG FATsector;
ULONG FATeis;
PUSHORT Block;
DbgPrint("FAT32WriteCluster %u : %u\n",ClusterToWrite,NewValue);
Block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
FATsector=ClusterToWrite/128;
FATeis=ClusterToWrite-(FATsector*128);
/* load sector, change value, then rewrite sector */
VFATReadSectors(DeviceExt->StorageDevice,
DeviceExt->FATStart+FATsector,
1,
(UCHAR *)Block);
Block[FATeis] = NewValue;
VFATWriteSectors(DeviceExt->StorageDevice,
DeviceExt->FATStart+FATsector,
1,
(UCHAR *)Block);
ExFreePool(Block);
}
void WriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite,
ULONG NewValue)
/*
* FUNCTION: Write a changed FAT entry
*/
{
if(DeviceExt->FatType==FAT16)
FAT16WriteCluster(DeviceExt, ClusterToWrite, NewValue);
else if(DeviceExt->FatType==FAT32)
FAT32WriteCluster(DeviceExt, ClusterToWrite, NewValue);
else
FAT12WriteCluster(DeviceExt, ClusterToWrite, NewValue);
}
ULONG GetNextWriteCluster(PDEVICE_EXTENSION DeviceExt, ULONG CurrentCluster)
/*
* FUNCTION: Determines the next cluster to be written
*/
{
ULONG LastCluster, NewCluster;
DPRINT("GetNextWriteCluster(DeviceExt %x, CurrentCluster %x)\n",
DeviceExt,CurrentCluster);
/* Find out what was happening in the last cluster's AU */
LastCluster=GetNextCluster(DeviceExt,CurrentCluster);
/* Check to see if we must append or overwrite */
if (LastCluster==0xffffffff)
{//we are after last existing cluster : we must add one to file
/* Append */
/* Firstly, find the next available open allocation unit */
if(DeviceExt->FatType == FAT16)
NewCluster = FAT16FindAvailableCluster(DeviceExt);
else if(DeviceExt->FatType == FAT32)
NewCluster = FAT32FindAvailableCluster(DeviceExt);
else
NewCluster = FAT12FindAvailableCluster(DeviceExt);
/* Mark the new AU as the EOF */
WriteCluster(DeviceExt, NewCluster, 0xFFFFFFFF);
/* Now, write the AU of the LastCluster with the value of the newly
found AU */
if(CurrentCluster)
WriteCluster(DeviceExt, CurrentCluster, NewCluster);
/* Return NewCluster as CurrentCluster */
return NewCluster;
}
else
{
/* Overwrite: Return LastCluster as CurrentCluster */
return LastCluster;
}
}
ULONG ClusterToSector(PDEVICE_EXTENSION DeviceExt,
unsigned long Cluster)
/*
* FUNCTION: Converts the cluster number to a sector number for this physical
* device
*/
{
return DeviceExt->dataStart+((Cluster-2)*DeviceExt->Boot->SectorsPerCluster);
}
void RtlAnsiToUnicode(PWSTR Dest, PCH Source, ULONG Length)
/*
* FUNCTION: Convert an ANSI string to it's Unicode equivalent
*/
{
int i;
for (i=0; (i<Length && Source[i] != ' '); i++)
{
Dest[i] = Source[i];
}
Dest[i]=0;
}
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;
while((*Dest)!=0)
{
Dest++;
}
for (i=0; (i<Length && Source[i] != ' '); i++)
{
Dest[i] = Source[i];
}
Dest[i]=0;
}
void vfat_initstr(wchar_t *wstr, ULONG wsize)
/*
* FUNCTION: Initialize a string for use with a long file name
*/
{
int i;
wchar_t nc=0;
for(i=0; i<wsize; i++)
{
*wstr=nc;
wstr++;
}
wstr=wstr-wsize;
}
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;
dest+=wstart;
for(i=0; i<wcount; i++)
{
*dest=src[i];
dest++;
}
dest=dest-(wcount+wstart);
return dest;
}
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;
for (i=0;i<wcount;i++)
{
dest[i]=src[i];
if(!dest[i]) break;
}
return(dest);
}
wchar_t * vfat_movstr(const wchar_t *src, ULONG dpos,
ULONG spos, ULONG len)
/*
* FUNCTION: Move the characters in a string to a new position in the same
* string
*/
{
int i;
if(dpos<=spos)
{
for(i=0; i<len; i++)
{
src[dpos++]=src[spos++];
}
}
else
{
dpos+=len-1;
spos+=len-1;
for(i=0; i<len; i++)
{
src[dpos--]=src[spos--];
}
}
return(src);
}
BOOLEAN IsLastEntry(PVOID Block, ULONG Offset)
/*
* FUNCTION: Determine if the given directory entry is the last
*/
{
return(((FATDirEntry *)Block)[Offset].Filename[0] == 0);
}
BOOLEAN IsVolEntry(PVOID Block, ULONG Offset)
/*
* FUNCTION: Determine if the given directory entry is a vol entry
*/
{
if( (((FATDirEntry *)Block)[Offset].Attrib)==0x28 ) return TRUE;
else return FALSE;
}
BOOLEAN IsDeletedEntry(PVOID Block, ULONG Offset)
/*
* FUNCTION: Determines if the given entry is a deleted one
*/
{
/* Checks special character */
return ((((FATDirEntry *)Block)[Offset].Filename[0] == 0xe5));
}
BOOLEAN GetEntryName(PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
PDEVICE_EXTENSION DeviceExt, ULONG * _StartingSector)
/*
* FUNCTION: Retrieves the file name, be it in short or long file name format
*/
{
FATDirEntry* test;
slot* test2;
ULONG Offset = *_Offset;
ULONG StartingSector = *_StartingSector;
ULONG jloop = *_jloop;
ULONG cpos;
WCHAR tmp[256];
test = (FATDirEntry *)Block;
test2 = (slot *)Block;
*Name = 0;
if (IsDeletedEntry(Block,Offset))
{
return(FALSE);
}
if(test2[Offset].attr == 0x0f)
{
vfat_initstr(Name, 256);
vfat_wcsncpy(Name,test2[Offset].name0_4,5);
vfat_wcsncat(Name,test2[Offset].name5_10,5,6);
vfat_wcsncat(Name,test2[Offset].name11_12,11,2);
cpos=0;
while((test2[Offset].id!=0x41) && (test2[Offset].id!=0x01) &&
(test2[Offset].attr>0))
{
Offset++;
if(Offset==ENTRIES_PER_SECTOR) {
Offset=0;
StartingSector++;//FIXME : nor always the next sector
jloop++;
VFATReadSectors(DeviceExt->StorageDevice,StartingSector,1,Block);
test2 = (slot *)Block;
}
cpos++;
vfat_movstr(Name, 13, 0, cpos*13);
vfat_wcsncpy(Name, test2[Offset].name0_4, 5);
vfat_wcsncat(Name,test2[Offset].name5_10,5,6);
vfat_wcsncat(Name,test2[Offset].name11_12,11,2);
}
if (IsDeletedEntry(Block,Offset+1))
{
Offset++;
*_Offset = Offset;
*_jloop = jloop;
*_StartingSector = StartingSector;
return(FALSE);
}
*_Offset = Offset;
*_jloop = jloop;
*_StartingSector = StartingSector;
return(TRUE);
}
RtlAnsiToUnicode(Name,test[Offset].Filename,8);
if (test[Offset].Ext[0]!=' ')
{
RtlCatAnsiToUnicode(Name,".",1);
}
RtlCatAnsiToUnicode(Name,test[Offset].Ext,3);
*_Offset = Offset;
return(TRUE);
}
BOOLEAN wstrcmpi(PWSTR s1, PWSTR s2)
/*
* FUNCTION: Compare to wide character strings
* return TRUE if s1==s2
*/
{
while (towlower(*s1)==towlower(*s2))
{
if ((*s1)==0 && (*s2)==0)
{
return(TRUE);
}
s1++;
s2++;
}
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=='?')||(towlower(*s1)==towlower(*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 ReadVolumeLabel(PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
/*
* FUNCTION: Read the volume label
*/
{
ULONG i, j;
ULONG Size;
char* block;
ULONG StartingSector;
ULONG NextCluster;
Size = DeviceExt->rootDirectorySectors;//FIXME : in fat32, no limit
StartingSector = DeviceExt->rootStart;
NextCluster=0;
block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
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=0; i<ENTRIES_PER_SECTOR; i++)
{
if (IsVolEntry((PVOID)block,i))
{
FATDirEntry *test = (FATDirEntry *)block;
/* copy volume label */
RtlAnsiToUnicode(Vpb->VolumeLabel,test[i].Filename,8);
RtlCatAnsiToUnicode(Vpb->VolumeLabel,test[i].Ext,3);
Vpb->VolumeLabelLength = wcslen(Vpb->VolumeLabel);
ExFreePool(block);
return(STATUS_SUCCESS);
}
if (IsLastEntry((PVOID)block,i))
{
*(Vpb->VolumeLabel) = 0;
Vpb->VolumeLabelLength = 0;
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
}
// not found in this sector, try next :
/* directory can be fragmented although it is best to keep them
unfragmented */
StartingSector++;
if (DeviceExt->FatType ==FAT32)
{
if(StartingSector==ClusterToSector(DeviceExt,NextCluster+1))
{
NextCluster = GetNextCluster(DeviceExt,NextCluster);
if (NextCluster == 0||NextCluster==0xffffffff)
{
*(Vpb->VolumeLabel) = 0;
Vpb->VolumeLabelLength = 0;
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
StartingSector = ClusterToSector(DeviceExt,NextCluster);
}
}
}
*(Vpb->VolumeLabel) = 0;
Vpb->VolumeLabelLength = 0;
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
NTSTATUS FindFile(PDEVICE_EXTENSION DeviceExt, PVfatFCB Fcb,
PVfatFCB Parent, PWSTR FileToFind,ULONG *StartSector,ULONG *Entry)
/*
* FUNCTION: Find a file
*/
{
ULONG i, j;
ULONG Size;
char* block;
WCHAR name[256];
ULONG StartingSector;
ULONG NextCluster;
WCHAR TempStr[2];
DPRINT("FindFile(Parent %x, FileToFind '%w')\n",Parent,FileToFind);
if (wcslen(FileToFind)==0)
{
CHECKPOINT;
TempStr[0] = (WCHAR)'.';
TempStr[1] = 0;
FileToFind=&TempStr;
}
if (Parent != NULL)
{
DPRINT("Parent->entry.FirstCluster %d\n",Parent->entry.FirstCluster);
}
DPRINT("FindFile '%w'\n", FileToFind);
if (Parent == NULL||Parent->entry.FirstCluster==1)
{
CHECKPOINT;
Size = DeviceExt->rootDirectorySectors;//FIXME : in fat32, no limit
StartingSector = DeviceExt->rootStart;
NextCluster=0;
if(FileToFind[0]==0 ||(FileToFind[0]=='\\' && FileToFind[1]==0) ||
(FileToFind[0]=='.' && FileToFind[1]==0))
{// it's root : complete essentials fields then return ok
CHECKPOINT;
memset(Fcb,0,sizeof(VfatFCB));
memset(Fcb->entry.Filename,' ',11);
Fcb->entry.FileSize=DeviceExt->rootDirectorySectors*BLOCKSIZE;
Fcb->entry.Attrib=FILE_ATTRIBUTE_DIRECTORY;
if (DeviceExt->FatType == FAT32)
Fcb->entry.FirstCluster=2;
else
Fcb->entry.FirstCluster=1;//FIXME : is 1 the good value for mark root?
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);
if(Parent->entry.FirstCluster==1 && DeviceExt->FatType!=FAT32)
{// read of root directory in FAT16 or FAT12
StartingSector=DeviceExt->rootStart;
}
}
CHECKPOINT;
block = ExAllocatePool(NonPagedPool,BLOCKSIZE);
CHECKPOINT;
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 (IsVolEntry((PVOID)block,i))
continue;
if (IsLastEntry((PVOID)block,i))
{
ExFreePool(block);
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=i;
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( i==(ENTRIES_PER_SECTOR))
{// entry is in next sector
StartingSector++;
//FIXME : treat case of next sector fragmented
VFATReadSectors(DeviceExt->StorageDevice,StartingSector,1,block);
i=0;
}
memcpy(&Fcb->entry,&((FATDirEntry *)block)[i],
sizeof(FATDirEntry));
vfat_wcsncpy(Fcb->ObjectName,name,MAX_PATH);
ExFreePool(block);
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=i;
return(STATUS_SUCCESS);
}
}
}
// not found in this sector, try next :
/* directory can be fragmented although it is best to keep them
unfragmented */
if(Entry) *Entry=0;
StartingSector++;
if ((Parent != NULL && Parent->entry.FirstCluster!=1)
|| DeviceExt->FatType ==FAT32)
{
if(StartingSector==ClusterToSector(DeviceExt,NextCluster+1))
{
NextCluster = GetNextCluster(DeviceExt,NextCluster);
if (NextCluster == 0||NextCluster==0xffffffff)
{
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=i;
ExFreePool(block);
return(STATUS_UNSUCCESSFUL);
}
StartingSector = ClusterToSector(DeviceExt,NextCluster);
}
}
}
ExFreePool(block);
if(StartSector) *StartSector=StartingSector;
if(Entry) *Entry=i;
return(STATUS_UNSUCCESSFUL);
}
NTSTATUS FsdCloseFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject)
/*
* FUNCTION: Closes a file
*/
{
PVfatFCB pFcb;
PVfatCCB pCcb;
DPRINT("FsdCloseFile(DeviceExt %x, FileObject %x)\n",
DeviceExt,FileObject);
//FIXME : update entry in directory ?
pCcb = (PVfatCCB)(FileObject->FsContext2);
DPRINT("pCcb %x\n",pCcb);
if (pCcb == NULL)
{
return(STATUS_SUCCESS);
}
pFcb = pCcb->pFcb;
pFcb->RefCount--;
if(pFcb->RefCount<=0)
{
if(pFcb->prevFcb)
pFcb->prevFcb->nextFcb=pFcb->nextFcb;
else
pFirstFcb=pFcb->nextFcb;
if(pFcb->nextFcb)
pFcb->nextFcb->prevFcb=pFcb->prevFcb;
ExFreePool(pFcb);
}
ExFreePool(pCcb);
return STATUS_SUCCESS;
}
NTSTATUS FsdOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
PWSTR FileName)
/*
* FUNCTION: Opens a file
*/
{
PWSTR current;
PWSTR next;
PWSTR string;
PVfatFCB ParentFcb;
PVfatFCB Fcb,pRelFcb;
PVfatFCB Temp;
PVfatCCB newCCB,pRelCcb;
NTSTATUS Status;
PFILE_OBJECT pRelFileObject;
PWSTR AbsFileName=NULL;
short i,j;
DPRINT("FsdOpenFile(%08lx, %08lx, %w)\n",
DeviceExt,
FileObject,
FileName);
//FIXME : treat relative name
if(FileObject->RelatedFileObject)
{
DbgPrint("try related for %w\n",FileName);
pRelFileObject=FileObject->RelatedFileObject;
pRelCcb=pRelFileObject->FsContext2;
assert(pRelCcb);
pRelFcb=pRelCcb->pFcb;
assert(pRelFcb);
// verify related object is a directory and target name don't start with \.
if( !(pRelFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
|| (FileName[0]!= '\\') )
{
Status=STATUS_INVALID_PARAMETER;
return Status;
}
// construct absolute path name
AbsFileName=ExAllocatePool(NonPagedPool,MAX_PATH);
for (i=0;pRelFcb->PathName[i];i++)
AbsFileName[i]=pRelFcb->PathName[i];
AbsFileName[i++]='\\';
for (j=0;FileName[j]&&i<MAX_PATH;j++)
AbsFileName[i++]=FileName[j];
assert(i<MAX_PATH);
AbsFileName[i]=0;
FileName=AbsFileName;
}
// try first to find an existing FCB in memory
for (Fcb=pFirstFcb;Fcb; Fcb=Fcb->nextFcb)
{
if (DeviceExt==Fcb->pDevExt
&& wstrcmpi(FileName,Fcb->PathName))
{
Fcb->RefCount++;
FileObject->FsContext =(PVOID) &Fcb->NTRequiredFCB;
newCCB = ExAllocatePool(NonPagedPool,sizeof(VfatCCB));
memset(newCCB,0,sizeof(VfatCCB));
FileObject->FsContext2 = newCCB;
newCCB->pFcb=Fcb;
newCCB->PtrFileObject=FileObject;
if(AbsFileName)ExFreePool(AbsFileName);
return(STATUS_SUCCESS);
}
}
string = FileName;
ParentFcb = NULL;
Fcb = ExAllocatePool(NonPagedPool, sizeof(VfatFCB));
memset(Fcb,0,sizeof(VfatFCB));
Fcb->ObjectName=Fcb->PathName;
next = &string[0];
while (next!=NULL)
{
*next = '\\';
current = next+1;
next = wcschr(next+1,'\\');
if (next!=NULL)
{
*next=0;
}
DPRINT("current '%w'\n",current);
Status = FindFile(DeviceExt,Fcb,ParentFcb,current,NULL,NULL);
if (Status != STATUS_SUCCESS)
{
if (Fcb != NULL)
ExFreePool(Fcb);
if (ParentFcb != NULL)
ExFreePool(ParentFcb);
if(AbsFileName)
ExFreePool(AbsFileName);
return(Status);
}
Temp = Fcb;
CHECKPOINT;
if (ParentFcb == NULL)
{
Fcb = ExAllocatePool(NonPagedPool,sizeof(VfatFCB));
memset(Fcb,0,sizeof(VfatFCB));
Fcb->ObjectName=Fcb->PathName;
}
else
Fcb = ParentFcb;
CHECKPOINT;
ParentFcb = Temp;
}
CHECKPOINT;
FileObject->FsContext =(PVOID) &ParentFcb->NTRequiredFCB;
newCCB = ExAllocatePool(NonPagedPool,sizeof(VfatCCB));
memset(newCCB,0,sizeof(VfatCCB));
FileObject->FsContext2 = newCCB;
newCCB->pFcb=ParentFcb;
newCCB->PtrFileObject=FileObject;
ParentFcb->RefCount++;
//FIXME : initialize all fields in FCB and CCB
ParentFcb->nextFcb=pFirstFcb;
pFirstFcb=ParentFcb;
vfat_wcsncpy(ParentFcb->PathName,FileName,MAX_PATH);
ParentFcb->ObjectName=ParentFcb->PathName+(current-FileName);
ParentFcb->pDevExt=DeviceExt;
DPRINT("file open, fcb=%x\n",ParentFcb);
DPRINT("FileSize %d\n",ParentFcb->entry.FileSize);
if(Fcb) ExFreePool(Fcb);
if(AbsFileName)ExFreePool(AbsFileName);
return(STATUS_SUCCESS);
}
BOOLEAN FsdHasFileSystem(PDEVICE_OBJECT DeviceToMount)
/*
* FUNCTION: Tests if the device contains a filesystem that can be mounted
* by this fsd
*/
{
BootSector* Boot;
Boot = ExAllocatePool(NonPagedPool,512);
VFATReadSectors(DeviceToMount, 0, 1, (UCHAR *)Boot);
if (strncmp(Boot->SysType,"FAT12",5)==0 ||
strncmp(Boot->SysType,"FAT16",5)==0 ||
strncmp(((struct _BootSector32 *)(Boot))->SysType,"FAT32",5)==0)
{
ExFreePool(Boot);
return(TRUE);
}
ExFreePool(Boot);
return(FALSE);
}
NTSTATUS FsdMountDevice(PDEVICE_EXTENSION DeviceExt,
PDEVICE_OBJECT DeviceToMount)
/*
* FUNCTION: Mounts the device
*/
{
DPRINT("Mounting VFAT device...");
DPRINT("DeviceExt %x\n",DeviceExt);
DeviceExt->Boot = ExAllocatePool(NonPagedPool,512);
VFATReadSectors(DeviceToMount, 0, 1, (UCHAR *)DeviceExt->Boot);
DPRINT("DeviceExt->Boot->BytesPerSector %x\n",
DeviceExt->Boot->BytesPerSector);
DeviceExt->FATStart=DeviceExt->Boot->ReservedSectors;
DeviceExt->rootDirectorySectors=
(DeviceExt->Boot->RootEntries*32)/DeviceExt->Boot->BytesPerSector;
DeviceExt->rootStart=
DeviceExt->FATStart+DeviceExt->Boot->FATCount*DeviceExt->Boot->FATSectors;
DeviceExt->dataStart=DeviceExt->rootStart+DeviceExt->rootDirectorySectors;
DeviceExt->FATEntriesPerSector=DeviceExt->Boot->BytesPerSector/32;
DeviceExt->BytesPerCluster = DeviceExt->Boot->SectorsPerCluster *
DeviceExt->Boot->BytesPerSector;
if (strncmp(DeviceExt->Boot->SysType,"FAT12",5)==0)
{
DeviceExt->FatType = FAT12;
}
else if (strncmp(((struct _BootSector32 *)(DeviceExt->Boot))->SysType,"FAT32",5)==0)
{
DeviceExt->FatType = FAT32;
DeviceExt->rootDirectorySectors=DeviceExt->Boot->SectorsPerCluster;
DeviceExt->rootStart=
DeviceExt->FATStart+DeviceExt->Boot->FATCount
*((struct _BootSector32 *)( DeviceExt->Boot))->FATSectors32;
DeviceExt->dataStart=DeviceExt->rootStart;
}
else
{
DeviceExt->FatType = FAT16;
}
// with FAT32 it's not a good idea to load always fat in memory
// because on a 8GB partition with 2 KO clusters, the fat = 8 MO
if(DeviceExt->FatType!=FAT32)
{
DeviceExt->FAT = ExAllocatePool(NonPagedPool, BLOCKSIZE*DeviceExt->Boot->FATSectors);
VFATReadSectors(DeviceToMount, DeviceExt->FATStart, DeviceExt->Boot->FATSectors, (UCHAR *)DeviceExt->FAT);
}
return STATUS_SUCCESS;
}
void VFATLoadCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster)
/*
* FUNCTION: Load a cluster from the physical device
*/
{
ULONG Sector;
DPRINT("VFATLoadCluster(DeviceExt %x, Buffer %x, Cluster %d)\n",
DeviceExt,Buffer,Cluster);
Sector = ClusterToSector(DeviceExt, Cluster);
VFATReadSectors(DeviceExt->StorageDevice,
Sector,
DeviceExt->Boot->SectorsPerCluster,
Buffer);
DPRINT("Finished VFATReadSectors\n");
}
void VFATWriteCluster(PDEVICE_EXTENSION DeviceExt, PVOID Buffer, ULONG Cluster)
/*
* FUNCTION: Write a cluster to the physical device
*/
{
ULONG Sector;
DPRINT("VFATWriteCluster(DeviceExt %x, Buffer %x, Cluster %d)\n",
DeviceExt,Buffer,Cluster);
Sector = ClusterToSector(DeviceExt, Cluster);
VFATWriteSectors(DeviceExt->StorageDevice,
Sector,
DeviceExt->Boot->SectorsPerCluster,
Buffer);
}
NTSTATUS FsdReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
PVOID Buffer, ULONG Length, ULONG ReadOffset,
PULONG LengthRead)
/*
* FUNCTION: Reads data from a file
*/
{
ULONG CurrentCluster;
ULONG FileOffset;
ULONG FirstCluster;
PVfatFCB Fcb;
PVOID Temp;
ULONG TempLength;
/* PRECONDITION */
assert(DeviceExt != NULL);
assert(DeviceExt->BytesPerCluster != 0);
assert(FileObject != NULL);
assert(FileObject->FsContext != NULL);
DPRINT("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
"Length %d, ReadOffset %d)\n",DeviceExt,FileObject,Buffer,
Length,ReadOffset);
Fcb = ((PVfatCCB)(FileObject->FsContext2))->pFcb;
if (DeviceExt->FatType == FAT32)
CurrentCluster = Fcb->entry.FirstCluster
+Fcb->entry.FirstClusterHigh*65536;
else
CurrentCluster = Fcb->entry.FirstCluster;
FirstCluster=CurrentCluster;
DPRINT("DeviceExt->BytesPerCluster %x\n",DeviceExt->BytesPerCluster);
if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
{
return(STATUS_FILE_IS_A_DIRECTORY);
}
if (ReadOffset >= Fcb->entry.FileSize
&& !(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
{
return(STATUS_END_OF_FILE);
}
if ((ReadOffset + Length) > Fcb->entry.FileSize
&& !(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
{
Length = Fcb->entry.FileSize - ReadOffset;
}
*LengthRead = 0;
/* FIXME: optimize by remembering the last cluster read and using if possible */
Temp = ExAllocatePool(NonPagedPool,DeviceExt->BytesPerCluster);
if(!Temp) return STATUS_UNSUCCESSFUL;
if (FirstCluster==1)
{ //root of FAT16 or FAT12
CurrentCluster=DeviceExt->rootStart+ReadOffset
/(DeviceExt->BytesPerCluster)*DeviceExt->Boot->SectorsPerCluster;
}
else
for (FileOffset=0; FileOffset < ReadOffset / DeviceExt->BytesPerCluster
; FileOffset++)
{
CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster);
}
CHECKPOINT;
if ((ReadOffset % DeviceExt->BytesPerCluster)!=0)
{
if (FirstCluster==1)
{
VFATReadSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
}
else
{
VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
}
TempLength = min(Length,DeviceExt->BytesPerCluster -
(ReadOffset % DeviceExt->BytesPerCluster));
memcpy(Buffer, Temp + ReadOffset % DeviceExt->BytesPerCluster,
TempLength);
(*LengthRead) = (*LengthRead) + TempLength;
Length = Length - TempLength;
Buffer = Buffer + TempLength;
}
CHECKPOINT;
while (Length >= DeviceExt->BytesPerCluster)
{
if (FirstCluster==1)
{
VFATReadSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Buffer);
CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
}
else
{
VFATLoadCluster(DeviceExt,Buffer,CurrentCluster);
CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
}
if (CurrentCluster == 0xffffffff)
{
ExFreePool(Temp);
return(STATUS_SUCCESS);
}
(*LengthRead) = (*LengthRead) + DeviceExt->BytesPerCluster;
Buffer = Buffer + DeviceExt->BytesPerCluster;
Length = Length - DeviceExt->BytesPerCluster;
}
CHECKPOINT;
if (Length > 0)
{
(*LengthRead) = (*LengthRead) + Length;
if (FirstCluster==1)
{
VFATReadSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
}
else
{
VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
}
memcpy(Buffer, Temp, Length);
}
ExFreePool(Temp);
return(STATUS_SUCCESS);
}
NTSTATUS FsdWriteFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
PVOID Buffer, ULONG Length, ULONG WriteOffset)
/*
* FUNCTION: Writes data to file
*/
{
ULONG CurrentCluster;
ULONG FileOffset;
ULONG FirstCluster;
PVfatFCB Fcb;
PVfatCCB pCcb;
PVOID Temp;
ULONG TempLength,Length2=Length;
/* Locate the first cluster of the file */
assert(FileObject);
pCcb=(PVfatCCB)(FileObject->FsContext2);
assert(pCcb);
Fcb = pCcb->pFcb;
assert(Fcb);
if (DeviceExt->FatType == FAT32)
CurrentCluster = Fcb->entry.FirstCluster+Fcb->entry.FirstClusterHigh*65536;
else
CurrentCluster = Fcb->entry.FirstCluster;
FirstCluster=CurrentCluster;
/* Allocate a buffer to hold 1 cluster of data */
Temp = ExAllocatePool(NonPagedPool,DeviceExt->BytesPerCluster);
assert(Temp);
/* Find the cluster according to the offset in the file */
if (CurrentCluster==1)
{ //root of FAT16 or FAT12
CurrentCluster=DeviceExt->rootStart+WriteOffset
/DeviceExt->BytesPerCluster*DeviceExt->Boot->SectorsPerCluster;
}
else
if (CurrentCluster==0)
{// file of size 0 : allocate first cluster
CurrentCluster=GetNextWriteCluster(DeviceExt,0);
if (DeviceExt->FatType == FAT32)
{
Fcb->entry.FirstClusterHigh=CurrentCluster>>16;
Fcb->entry.FirstCluster=CurrentCluster;
}
else
Fcb->entry.FirstCluster=CurrentCluster;
}
else
for (FileOffset=0; FileOffset < WriteOffset / DeviceExt->BytesPerCluster; FileOffset++)
{
CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster);
}
CHECKPOINT;
/*
If the offset in the cluster doesn't fall on the cluster boundary then
we have to write only from the specified offset
*/
if ((WriteOffset % DeviceExt->BytesPerCluster)!=0)
{
CHECKPOINT;
TempLength = min(Length,DeviceExt->BytesPerCluster -
(WriteOffset % DeviceExt->BytesPerCluster));
/* Read in the existing cluster data */
if (FirstCluster==1)
VFATReadSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
else
VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
/* Overwrite the last parts of the data as necessary */
memcpy(Temp + (WriteOffset % DeviceExt->BytesPerCluster), Buffer,
TempLength);
/* Write the cluster back */
if (FirstCluster==1)
{
VFATWriteSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
}
else
{
VFATWriteCluster(DeviceExt,Temp,CurrentCluster);
CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
}
Length2 -= TempLength;
Buffer = Buffer + TempLength;
}
CHECKPOINT;
/* Write the buffer in chunks of 1 cluster */
while (Length2 >= DeviceExt->BytesPerCluster)
{
CHECKPOINT;
if (CurrentCluster == 0)
{
ExFreePool(Temp);
return(STATUS_UNSUCCESSFUL);
}
if (FirstCluster==1)
{
VFATWriteSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Buffer);
CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
}
else
{
VFATWriteCluster(DeviceExt,Buffer,CurrentCluster);
CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
}
Buffer = Buffer + DeviceExt->BytesPerCluster;
Length2 -= DeviceExt->BytesPerCluster;
}
CHECKPOINT;
/* Write the remainder */
if (Length2 > 0)
{
CHECKPOINT;
if (CurrentCluster == 0)
{
ExFreePool(Temp);
return(STATUS_UNSUCCESSFUL);
}
CHECKPOINT;
/* Read in the existing cluster data */
if (FirstCluster==1)
VFATReadSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
else
VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
CHECKPOINT;
memcpy(Temp, Buffer, Length2);
CHECKPOINT;
if (FirstCluster==1)
{
VFATWriteSectors(DeviceExt->StorageDevice,CurrentCluster
,DeviceExt->Boot->SectorsPerCluster,Temp);
}
else
VFATWriteCluster(DeviceExt,Temp,CurrentCluster);
}
CHECKPOINT;
//FIXME : set last write time and date
if(Fcb->entry.FileSize<WriteOffset+Length
&& !(Fcb->entry.Attrib &FILE_ATTRIBUTE_DIRECTORY))
{
Fcb->entry.FileSize=WriteOffset+Length;
// update entry in directory
updEntry(DeviceExt,FileObject);
}
ExFreePool(Temp);
return(STATUS_SUCCESS);
}
NTSTATUS FsdClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Close a file
*/
{
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = Stack->FileObject;
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
NTSTATUS Status;
DPRINT("FsdClose(DeviceObject %x, Irp %x)\n",DeviceObject, Irp);
Status = FsdCloseFile(DeviceExtension,FileObject);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(Status);
}
NTSTATUS FsdCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Create or open a file
*/
{
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject;
NTSTATUS Status=STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExt;
ULONG RequestedDisposition,RequestedOptions;
PVfatCCB pCcb;
PVfatFCB pFcb;
assert(DeviceObject);
assert(Irp);
if(DeviceObject->Size==sizeof(DEVICE_OBJECT))
{// DevieObject represent FileSystem instead of logical volume
DbgPrint("FsdCreate called with file system\n");
Irp->IoStatus.Status=Status;
Irp->IoStatus.Information=FILE_OPENED;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(Status);
}
Stack = IoGetCurrentIrpStackLocation(Irp);
assert(Stack);
RequestedDisposition = ((Stack->Parameters.Create.Options>>24)&0xff);
RequestedOptions=Stack->Parameters.Create.Options&FILE_VALID_OPTION_FLAGS;
FileObject = Stack->FileObject;
DeviceExt = DeviceObject->DeviceExtension;
assert(DeviceExt);
ExAcquireResourceExclusiveLite(&(DeviceExt->Resource),TRUE);
Status = FsdOpenFile(DeviceExt,FileObject,FileObject->FileName.Buffer);
CHECKPOINT;
Irp->IoStatus.Information = 0;
if(!NT_SUCCESS(Status))
{
if(RequestedDisposition==FILE_CREATE
||RequestedDisposition==FILE_OPEN_IF
||RequestedDisposition==FILE_OVERWRITE_IF)
{
CHECKPOINT;
Status=addEntry(DeviceExt,FileObject,RequestedOptions
,(Stack->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS));
if(NT_SUCCESS(Status))
Irp->IoStatus.Information = FILE_CREATED;
// FIXME set size if AllocationSize requested
// FIXME set extended attributes ?
// FIXME set share access
// IoSetShareAccess(DesiredAccess,ShareAccess,FileObject
// ,((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
}
}
else
{
if(RequestedDisposition==FILE_CREATE)
{
Irp->IoStatus.Information = FILE_EXISTS;
Status=STATUS_OBJECT_NAME_COLLISION;
}
pCcb=FileObject->FsContext2;
pFcb=pCcb->pFcb;
if( (RequestedOptions&FILE_NON_DIRECTORY_FILE)
&& (pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
{
Status=STATUS_FILE_IS_A_DIRECTORY;
}
if( (RequestedOptions&FILE_DIRECTORY_FILE)
&& !(pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
{
Status=STATUS_NOT_A_DIRECTORY;
}
// FIXME : test share access
// FIXME : test write access if requested
if(!NT_SUCCESS(Status))
FsdCloseFile(DeviceExt,FileObject);
else Irp->IoStatus.Information = FILE_OPENED;
// FIXME : make supersed or overwrite if requested
}
CHECKPOINT;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ExReleaseResourceForThreadLite(&(DeviceExt->Resource),ExGetCurrentResourceThread());
return Status;
}
NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Write to a file
*/
{
ULONG Length;
PVOID Buffer;
ULONG Offset;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = Stack->FileObject;
PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
NTSTATUS Status;
DPRINT("FsdWrite(DeviceObject %x Irp %x)\n",DeviceObject,Irp);
Length = Stack->Parameters.Write.Length;
Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
Offset = Stack->Parameters.Write.ByteOffset.u.LowPart;
Status = FsdWriteFile(DeviceExt,FileObject,Buffer,Length,Offset);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Length;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(Status);
}
NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Read from a file
*/
{
ULONG Length;
PVOID Buffer;
ULONG Offset;
PIO_STACK_LOCATION Stack;
PFILE_OBJECT FileObject;
PDEVICE_EXTENSION DeviceExt;
NTSTATUS Status;
ULONG LengthRead;
DPRINT("FsdRead(DeviceObject %x, Irp %x)\n",DeviceObject,Irp);
/* Precondition / Initialization */
assert(Irp != NULL);
Stack = IoGetCurrentIrpStackLocation(Irp);
assert(Stack != NULL);
FileObject = Stack->FileObject;
assert(FileObject != NULL);
DeviceExt = DeviceObject->DeviceExtension;
assert(DeviceExt != NULL);
Length = Stack->Parameters.Read.Length;
Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
Offset = Stack->Parameters.Read.ByteOffset.u.LowPart;
Status = FsdReadFile(DeviceExt,FileObject,Buffer,Length,Offset,
&LengthRead);
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = LengthRead;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(Status);
}
NTSTATUS FsdMount(PDEVICE_OBJECT DeviceToMount)
/*
* FUNCTION: Mount the filesystem
*/
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_EXTENSION DeviceExt;
IoCreateDevice(VFATDriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_FILE_SYSTEM,
0,
FALSE,
&DeviceObject);
DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
DeviceExt = (PVOID)DeviceObject->DeviceExtension;
// use same vpb as device disk
DeviceObject->Vpb=DeviceToMount->Vpb;
FsdMountDevice(DeviceExt,DeviceToMount);
DeviceObject->Vpb->Flags |= VPB_MOUNTED;
DeviceExt->StorageDevice = IoAttachDeviceToDeviceStack(DeviceObject,
DeviceToMount);
/* read serial number */
if (DeviceExt->FatType == FAT12 || DeviceExt->FatType == FAT16)
DeviceObject->Vpb->SerialNumber =
((struct _BootSector *)(DeviceExt->Boot))->VolumeID;
else if (DeviceExt->FatType == FAT32)
DeviceObject->Vpb->SerialNumber =
((struct _BootSector32 *)(DeviceExt->Boot))->VolumeID;
/* read volume label */
ReadVolumeLabel (DeviceExt, DeviceObject->Vpb);
return(STATUS_SUCCESS);
}
NTSTATUS FsdFileSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: File system control
*/
{
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
// PVPB vpb = Stack->Parameters.Mount.Vpb;
PDEVICE_OBJECT DeviceToMount = Stack->Parameters.Mount.DeviceObject;
NTSTATUS Status;
// DPRINT("VFAT FSC\n");
DbgPrint("VFAT FSC\n");
/* FIXME: should make sure that this is actually a mount request! */
if (FsdHasFileSystem(DeviceToMount))
{
Status = FsdMount(DeviceToMount);
}
else
{
DPRINT("VFAT: Unrecognized Volume\n");
Status = STATUS_UNRECOGNIZED_VOLUME;
}
DPRINT("VFAT File system successfully mounted\n");
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(Status);
}
NTSTATUS FsdGetStandardInformation(PVfatFCB FCB, PDEVICE_OBJECT DeviceObject,
PFILE_STANDARD_INFORMATION StandardInfo)
/*
* FUNCTION: Retrieve the standard file information
*/
{
PDEVICE_EXTENSION DeviceExtension;
unsigned long AllocSize;
DeviceExtension = DeviceObject->DeviceExtension;
/* PRECONDITION */
assert(DeviceExtension != NULL);
assert(DeviceExtension->BytesPerCluster != 0);
assert(StandardInfo != NULL);
assert(FCB != NULL);
RtlZeroMemory(StandardInfo, sizeof(FILE_STANDARD_INFORMATION));
/* Make allocsize a rounded up multiple of BytesPerCluster */
AllocSize = ((FCB->entry.FileSize + DeviceExtension->BytesPerCluster - 1) /
DeviceExtension->BytesPerCluster) *
DeviceExtension->BytesPerCluster;
StandardInfo->AllocationSize = RtlConvertUlongToLargeInteger(AllocSize);
StandardInfo->EndOfFile = RtlConvertUlongToLargeInteger(FCB->entry.FileSize);
StandardInfo->NumberOfLinks = 0;
StandardInfo->DeletePending = FALSE;
if((FCB->entry.Attrib & 0x10)>0) {
StandardInfo->Directory = TRUE;
} else {
StandardInfo->Directory = FALSE;
}
return STATUS_SUCCESS;
}
NTSTATUS FsdSetPositionInformation(PFILE_OBJECT FileObject,
PVfatFCB FCB,
PDEVICE_OBJECT DeviceObject,
PFILE_POSITION_INFORMATION PositionInfo)
{
DPRINT("FsdSetPositionInformation()\n");
DPRINT("PositionInfo %x\n", PositionInfo);
DPRINT("Setting position %d\n", PositionInfo->CurrentByteOffset.u.LowPart);
memcpy(&FileObject->CurrentByteOffset,&PositionInfo->CurrentByteOffset,
sizeof(LARGE_INTEGER));
return(STATUS_SUCCESS);
}
NTSTATUS FsdGetPositionInformation(PFILE_OBJECT FileObject,
PVfatFCB FCB,
PDEVICE_OBJECT DeviceObject,
PFILE_POSITION_INFORMATION PositionInfo)
{
DPRINT("FsdGetPositionInformation()\n");
memcpy(&PositionInfo->CurrentByteOffset, &FileObject->CurrentByteOffset,
sizeof(LARGE_INTEGER));
DPRINT("Getting position %x\n", PositionInfo->CurrentByteOffset.u.LowPart);
return(STATUS_SUCCESS);
}
NTSTATUS FsdGetBasicInformation(PFILE_OBJECT FileObject,
PVfatFCB FCB,
PDEVICE_OBJECT DeviceObject,
PFILE_BASIC_INFORMATION BasicInfo)
{
DPRINT("FsdGetBasicInformation()\n");
FsdDosDateTimeToFileTime(FCB->entry.CreationDate,FCB->entry.CreationTime,
&BasicInfo->CreationTime);
FsdDosDateTimeToFileTime(FCB->entry.AccessDate,0,
&BasicInfo->LastAccessTime);
FsdDosDateTimeToFileTime(FCB->entry.UpdateDate,FCB->entry.UpdateTime,
&BasicInfo->LastWriteTime);
FsdDosDateTimeToFileTime(FCB->entry.UpdateDate,FCB->entry.UpdateTime,
&BasicInfo->ChangeTime);
BasicInfo->FileAttributes = FCB->entry.Attrib;
DPRINT("Getting attributes %x\n", BasicInfo->FileAttributes);
return(STATUS_SUCCESS);
}
NTSTATUS FsdSetDispositionInformation(PFILE_OBJECT FileObject,
PVfatFCB FCB,
PDEVICE_OBJECT DeviceObject,
PFILE_DISPOSITION_INFORMATION DispositionInfo)
{
DPRINT("FsdSetDispositionInformation()\n");
FileObject->DeletePending = DispositionInfo->DeleteFile;
return(STATUS_SUCCESS);
}
NTSTATUS FsdQueryInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Retrieve the specified file information
*/
{
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
FILE_INFORMATION_CLASS FileInformationClass =
Stack->Parameters.QueryFile.FileInformationClass;
PFILE_OBJECT FileObject = NULL;
PVfatFCB FCB = NULL;
// PVfatCCB CCB = NULL;
NTSTATUS RC = STATUS_SUCCESS;
void *SystemBuffer;
/* PRECONDITION */
assert(DeviceObject != NULL);
assert(Irp != NULL);
/* INITIALIZATION */
Stack = IoGetCurrentIrpStackLocation(Irp);
FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
FileObject = Stack->FileObject;
// CCB = (PVfatCCB)(FileObject->FsContext2);
// FCB = CCB->Buffer; // Should be CCB->FCB???
FCB = ((PVfatCCB)(FileObject->FsContext2))->pFcb;
// FIXME : determine Buffer for result :
if (Irp->MdlAddress)
SystemBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
else
SystemBuffer = Irp->UserBuffer;
// SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
switch(FileInformationClass) {
case FileStandardInformation:
RC = FsdGetStandardInformation(FCB, DeviceObject, SystemBuffer);
break;
case FilePositionInformation:
RC = FsdGetPositionInformation(FileObject,
FCB,
DeviceObject,
SystemBuffer);
break;
case FileBasicInformation:
RC = FsdGetBasicInformation(FileObject,
FCB,
DeviceObject,
SystemBuffer);
break;
default:
RC=STATUS_NOT_IMPLEMENTED;
}
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return RC;
}
NTSTATUS FsdSetInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Retrieve the specified file information
*/
{
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
FILE_INFORMATION_CLASS FileInformationClass;
PFILE_OBJECT FileObject = NULL;
PVfatFCB FCB = NULL;
// PVfatCCB CCB = NULL;
NTSTATUS RC = STATUS_SUCCESS;
PVOID SystemBuffer;
/* PRECONDITION */
assert(DeviceObject != NULL);
assert(Irp != NULL);
DPRINT("FsdSetInformation(DeviceObject %x, Irp %x)\n",
DeviceObject,Irp);
/* INITIALIZATION */
Stack = IoGetCurrentIrpStackLocation(Irp);
FileInformationClass = Stack->Parameters.SetFile.FileInformationClass;
FileObject = Stack->FileObject;
FCB = ((PVfatCCB)(FileObject->FsContext2))->pFcb;
// FIXME : determine Buffer for result :
if (Irp->MdlAddress)
SystemBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
else
SystemBuffer = Irp->UserBuffer;
// SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
DPRINT("FileInformationClass %d\n",FileInformationClass);
DPRINT("SystemBuffer %x\n",SystemBuffer);
switch(FileInformationClass)
{
case FilePositionInformation:
RC = FsdSetPositionInformation(FileObject,
FCB,
DeviceObject,
SystemBuffer);
break;
case FileDispositionInformation:
RC = FsdSetDispositionInformation(FileObject,
FCB,
DeviceObject,
SystemBuffer);
break;
default:
RC = STATUS_NOT_IMPLEMENTED;
}
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return RC;
}
NTSTATUS FsdGetFsVolumeInformation(PFILE_OBJECT FileObject,
PVfatFCB FCB,
PDEVICE_OBJECT DeviceObject,
PFILE_FS_VOLUME_INFORMATION FsVolumeInfo)
{
DPRINT("FsdGetFsVolumeInformation()\n");
DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo);
if (!FsVolumeInfo)
return(STATUS_SUCCESS);
/* valid entries */
FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber;
FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength;
wcscpy (FsVolumeInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabel);
/* dummy entries */
FsVolumeInfo->VolumeCreationTime.QuadPart = 0;
FsVolumeInfo->SupportsObjects = FALSE;
DPRINT("Finished FsdGetFsVolumeInformation()\n");
return(STATUS_SUCCESS);
}
NTSTATUS FsdGetFsAttributeInformation(PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo)
{
DPRINT("FsdGetFsAttributeInformation()\n");
DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo);
if (!FsAttributeInfo)
return(STATUS_SUCCESS);
FsAttributeInfo->FileSystemAttributes = FS_CASE_IS_PRESERVED;
FsAttributeInfo->MaximumComponentNameLength = 255;
FsAttributeInfo->FileSystemNameLength = 3;
wcscpy (FsAttributeInfo->FileSystemName, L"FAT");
DPRINT("Finished FsdGetFsAttributeInformation()\n");
return(STATUS_SUCCESS);
}
NTSTATUS FsdGetFsSizeInformation(PDEVICE_OBJECT DeviceObject,
PFILE_FS_SIZE_INFORMATION FsSizeInfo)
{
PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
DPRINT("FsdGetFsSizeInformation()\n");
DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
if (!FsSizeInfo)
return(STATUS_SUCCESS);
if (DeviceExt->FatType == FAT32)
{
struct _BootSector32 *BootSect = (struct _BootSector32 *)DeviceExt->Boot;
if (BootSect->Sectors)
FsSizeInfo->TotalAllocationUnits.QuadPart = BootSect->Sectors;
else
FsSizeInfo->TotalAllocationUnits.QuadPart = BootSect->SectorsHuge;
FsSizeInfo->AvailableAllocationUnits.QuadPart =
FAT32CountAvailableClusters(DeviceExt);
FsSizeInfo->SectorsPerAllocationUnit = BootSect->SectorsPerCluster;
FsSizeInfo->BytesPerSector = BootSect->BytesPerSector;
}
else
{
struct _BootSector *BootSect = (struct _BootSector *)DeviceExt->Boot;
if (BootSect->Sectors)
FsSizeInfo->TotalAllocationUnits.QuadPart = BootSect->Sectors;
else
FsSizeInfo->TotalAllocationUnits.QuadPart = BootSect->SectorsHuge;
if (DeviceExt->FatType == FAT16)
FsSizeInfo->AvailableAllocationUnits.QuadPart =
FAT16CountAvailableClusters(DeviceExt);
else
FsSizeInfo->AvailableAllocationUnits.QuadPart =
FAT12CountAvailableClusters(DeviceExt);
FsSizeInfo->SectorsPerAllocationUnit = BootSect->SectorsPerCluster;
FsSizeInfo->BytesPerSector = BootSect->BytesPerSector;
}
DPRINT("Finished FsdGetFsSizeInformation()\n");
return(STATUS_SUCCESS);
}
NTSTATUS FsdQueryVolumeInformation(PDEVICE_OBJECT DeviceObject, PIRP Irp)
/*
* FUNCTION: Retrieve the specified file information
*/
{
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
FILE_INFORMATION_CLASS FileInformationClass =
Stack->Parameters.QueryVolume.FileInformationClass;
PFILE_OBJECT FileObject = NULL;
PVfatFCB FCB = NULL;
// PVfatCCB CCB = NULL;
NTSTATUS RC = STATUS_SUCCESS;
void *SystemBuffer;
/* PRECONDITION */
assert(DeviceObject != NULL);
assert(Irp != NULL);
DPRINT("FsdQueryVolumeInformation(DeviceObject %x, Irp %x)\n",
DeviceObject,Irp);
/* INITIALIZATION */
Stack = IoGetCurrentIrpStackLocation(Irp);
FileInformationClass = Stack->Parameters.QueryVolume.FileInformationClass;
FileObject = Stack->FileObject;
// CCB = (PVfatCCB)(FileObject->FsContext2);
// FCB = CCB->Buffer; // Should be CCB->FCB???
FCB = ((PVfatCCB)(FileObject->FsContext2))->pFcb;
// FIXME : determine Buffer for result :
if (Irp->MdlAddress)
SystemBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
else
SystemBuffer = Irp->UserBuffer;
// SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
DPRINT("FileInformationClass %d\n",FileInformationClass);
DPRINT("SystemBuffer %x\n",SystemBuffer);
switch (FileInformationClass)
{
case FileFsVolumeInformation:
RC = FsdGetFsVolumeInformation(FileObject,
FCB,
DeviceObject,
SystemBuffer);
break;
case FileFsAttributeInformation:
RC = FsdGetFsAttributeInformation(SystemBuffer);
break;
case FileFsSizeInformation:
RC = FsdGetFsSizeInformation(DeviceObject, SystemBuffer);
break;
default:
RC=STATUS_NOT_IMPLEMENTED;
}
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return RC;
}
NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT _DriverObject, PUNICODE_STRING RegistryPath)
/*
* FUNCTION: Called by the system to initalize the driver
* ARGUMENTS:
* DriverObject = object describing this driver
* RegistryPath = path to our configuration entries
* RETURNS: Success or failure
*/
{
PDEVICE_OBJECT DeviceObject;
NTSTATUS ret;
UNICODE_STRING ustr;
ANSI_STRING astr;
DbgPrint("VFAT 0.0.6\n");
pFirstFcb=NULL;
VFATDriverObject = _DriverObject;
RtlInitAnsiString(&astr,"\\Device\\VFAT");
RtlAnsiStringToUnicodeString(&ustr,&astr,TRUE);
ret = IoCreateDevice(VFATDriverObject,0,&ustr,
FILE_DEVICE_FILE_SYSTEM,0,FALSE,&DeviceObject);
if (ret!=STATUS_SUCCESS)
{
return(ret);
}
DeviceObject->Flags = DO_DIRECT_IO;
VFATDriverObject->MajorFunction[IRP_MJ_CLOSE] = FsdClose;
VFATDriverObject->MajorFunction[IRP_MJ_CREATE] = FsdCreate;
VFATDriverObject->MajorFunction[IRP_MJ_READ] = FsdRead;
VFATDriverObject->MajorFunction[IRP_MJ_WRITE] = FsdWrite;
VFATDriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] =
FsdFileSystemControl;
VFATDriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
FsdQueryInformation;
VFATDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] =
FsdSetInformation;
VFATDriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] =
FsdDirectoryControl;
VFATDriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] =
FsdQueryVolumeInformation;
VFATDriverObject->DriverUnload = NULL;
IoRegisterFileSystem(DeviceObject);
return(STATUS_SUCCESS);
}