2001-03-06 23:31:44 +00:00
|
|
|
/* $Id: create.c,v 1.20 2001/03/06 23:31:44 cnettel Exp $
|
1999-12-11 21:14:49 +00:00
|
|
|
*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS kernel
|
|
|
|
* FILE: services/fs/vfat/create.c
|
|
|
|
* PURPOSE: VFAT Filesystem
|
|
|
|
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
|
|
|
#include <ddk/ntddk.h>
|
2000-02-22 02:02:08 +00:00
|
|
|
#include <wchar.h>
|
2000-06-29 23:35:53 +00:00
|
|
|
#include <limits.h>
|
1999-12-11 21:14:49 +00:00
|
|
|
|
2001-03-06 23:31:44 +00:00
|
|
|
#define NDEBUG
|
2000-06-29 23:35:53 +00:00
|
|
|
#include <debug.h>
|
1999-12-11 21:14:49 +00:00
|
|
|
|
|
|
|
#include "vfat.h"
|
|
|
|
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
BOOLEAN
|
|
|
|
IsLastEntry (PVOID Block, ULONG Offset)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Determine if the given directory entry is the last
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
return (((FATDirEntry *) Block)[Offset].Filename[0] == 0);
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
BOOLEAN
|
|
|
|
IsVolEntry (PVOID Block, ULONG Offset)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Determine if the given directory entry is a vol entry
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
if ((((FATDirEntry *) Block)[Offset].Attrib) == 0x28)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
BOOLEAN
|
|
|
|
IsDeletedEntry (PVOID Block, ULONG Offset)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Determines if the given entry is a deleted one
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
/* Checks special character */
|
1999-12-11 21:14:49 +00:00
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
return ((((FATDirEntry *) Block)[Offset].Filename[0] == 0xe5) ||
|
|
|
|
(((FATDirEntry *) Block)[Offset].Filename[0] == 0));
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
BOOLEAN
|
|
|
|
GetEntryName (PVOID Block, PULONG _Offset, PWSTR Name, PULONG _jloop,
|
|
|
|
PDEVICE_EXTENSION DeviceExt, ULONG * _StartingSector)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Retrieves the file name, be it in short or long file name format
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
FATDirEntry *test;
|
|
|
|
slot *test2;
|
|
|
|
ULONG Offset = *_Offset;
|
|
|
|
ULONG StartingSector = *_StartingSector;
|
|
|
|
ULONG jloop = *_jloop;
|
|
|
|
ULONG cpos;
|
|
|
|
|
|
|
|
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;
|
2001-03-02 15:59:16 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
GetNextSector (DeviceExt, StartingSector, &StartingSector, FALSE);
|
2000-12-29 23:17:12 +00:00
|
|
|
jloop++;
|
2001-01-08 02:14:06 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
VfatReadSectors (DeviceExt->StorageDevice,
|
2000-12-29 23:17:12 +00:00
|
|
|
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);
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS
|
|
|
|
ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Read the volume label
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
ULONG i = 0;
|
|
|
|
ULONG j;
|
|
|
|
ULONG Size;
|
|
|
|
char *block;
|
|
|
|
ULONG StartingSector;
|
|
|
|
ULONG NextCluster;
|
2001-01-12 21:00:08 +00:00
|
|
|
NTSTATUS Status;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2001-01-16 09:55:02 +00:00
|
|
|
Size = DeviceExt->rootDirectorySectors; /* FIXME : in fat32, no limit */
|
2000-12-29 23:17:12 +00:00
|
|
|
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++)
|
|
|
|
{
|
2001-01-08 02:14:06 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
VfatReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2001-01-16 09:55:02 +00:00
|
|
|
/* not found in this sector, try next : */
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
/* directory can be fragmented although it is best to keep them
|
2001-03-02 15:59:16 +00:00
|
|
|
unfragmented.*/
|
2000-12-29 23:17:12 +00:00
|
|
|
StartingSector++;
|
2001-03-02 15:59:16 +00:00
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
if (DeviceExt->FatType == FAT32)
|
|
|
|
{
|
|
|
|
if (StartingSector == ClusterToSector (DeviceExt, NextCluster + 1))
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
Status = GetNextCluster (DeviceExt, NextCluster, &NextCluster,
|
|
|
|
FALSE);
|
2000-12-29 23:17:12 +00:00
|
|
|
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);
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS
|
|
|
|
FindFile (PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb,
|
|
|
|
PVFATFCB Parent, PWSTR FileToFind, ULONG * StartSector,
|
|
|
|
ULONG * Entry)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Find a file
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
ULONG i, j;
|
|
|
|
ULONG Size;
|
|
|
|
char *block;
|
|
|
|
WCHAR name[256];
|
|
|
|
ULONG StartingSector;
|
|
|
|
ULONG NextCluster;
|
|
|
|
WCHAR TempStr[2];
|
2001-01-12 21:00:08 +00:00
|
|
|
NTSTATUS Status;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent, FileToFind);
|
|
|
|
|
|
|
|
if (wcslen (FileToFind) == 0)
|
|
|
|
{
|
|
|
|
CHECKPOINT;
|
|
|
|
TempStr[0] = (WCHAR) '.';
|
|
|
|
TempStr[1] = 0;
|
2001-01-16 09:55:02 +00:00
|
|
|
FileToFind = (PWSTR)&TempStr;
|
2000-12-29 23:17:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Parent == NULL || Parent->entry.FirstCluster == 1)
|
|
|
|
{
|
|
|
|
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 */
|
|
|
|
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
|
2001-01-16 09:55:02 +00:00
|
|
|
Fcb->entry.FirstCluster = 1;
|
2000-12-29 23:17:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
block = ExAllocatePool (NonPagedPool, BLOCKSIZE);
|
|
|
|
if (StartSector && (*StartSector))
|
|
|
|
StartingSector = *StartSector;
|
|
|
|
i = (Entry) ? (*Entry) : 0;
|
|
|
|
for (j = 0; j < Size; j++)
|
|
|
|
{
|
2001-01-08 02:14:06 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
VfatReadSectors (DeviceExt->StorageDevice, StartingSector, 1, block);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
for (i = (Entry) ? (*Entry) : 0; i < ENTRIES_PER_SECTOR; i++)
|
|
|
|
{
|
|
|
|
if (IsVolEntry ((PVOID) block, i))
|
|
|
|
continue;
|
|
|
|
if (IsLastEntry ((PVOID) block, i))
|
|
|
|
{
|
|
|
|
if (StartSector)
|
|
|
|
*StartSector = StartingSector;
|
|
|
|
if (Entry)
|
|
|
|
*Entry = i;
|
|
|
|
ExFreePool (block);
|
|
|
|
return (STATUS_UNSUCCESSFUL);
|
|
|
|
}
|
|
|
|
if (GetEntryName
|
|
|
|
((PVOID) block, &i, name, &j, DeviceExt, &StartingSector))
|
|
|
|
{
|
|
|
|
if (wstrcmpjoki (name, FileToFind))
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
/* In the case of a long filename, the firstcluster is
|
|
|
|
stored in the next record -- where it's short name is */
|
2000-12-29 23:17:12 +00:00
|
|
|
if (((FATDirEntry *) block)[i].Attrib == 0x0f)
|
|
|
|
i++;
|
|
|
|
if (i == (ENTRIES_PER_SECTOR))
|
1999-12-11 21:14:49 +00:00
|
|
|
{
|
2001-03-02 15:59:16 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
GetNextSector (DeviceExt, StartingSector, &StartingSector, FALSE);
|
|
|
|
|
2001-01-08 02:14:06 +00:00
|
|
|
/* FIXME: Check status */
|
|
|
|
VfatReadSectors (DeviceExt->StorageDevice,
|
2000-12-29 23:17:12 +00:00
|
|
|
StartingSector, 1, block);
|
|
|
|
i = 0;
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
2000-12-29 23:17:12 +00:00
|
|
|
memcpy (&Fcb->entry, &((FATDirEntry *) block)[i],
|
|
|
|
sizeof (FATDirEntry));
|
|
|
|
vfat_wcsncpy (Fcb->ObjectName, name, MAX_PATH);
|
|
|
|
if (StartSector)
|
|
|
|
*StartSector = StartingSector;
|
|
|
|
if (Entry)
|
|
|
|
*Entry = i;
|
|
|
|
ExFreePool (block);
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* not found in this sector, try next : */
|
|
|
|
|
|
|
|
/* directory can be fragmented although it is best to keep them
|
2001-03-02 15:59:16 +00:00
|
|
|
unfragmented. Should we change this to also use GetNextSector?
|
|
|
|
GetNextSector was originally implemented to handle the case above */
|
2000-12-29 23:17:12 +00:00
|
|
|
if (Entry)
|
|
|
|
*Entry = 0;
|
|
|
|
StartingSector++;
|
|
|
|
if ((Parent != NULL && Parent->entry.FirstCluster != 1)
|
|
|
|
|| DeviceExt->FatType == FAT32)
|
|
|
|
{
|
|
|
|
if (StartingSector == ClusterToSector (DeviceExt, NextCluster + 1))
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
Status = GetNextCluster (DeviceExt, NextCluster, &NextCluster,
|
|
|
|
FALSE);
|
2000-12-29 23:17:12 +00:00
|
|
|
if (NextCluster == 0 || NextCluster == 0xffffffff)
|
|
|
|
{
|
|
|
|
if (StartSector)
|
|
|
|
*StartSector = StartingSector;
|
|
|
|
if (Entry)
|
|
|
|
*Entry = i;
|
|
|
|
ExFreePool (block);
|
|
|
|
return (STATUS_UNSUCCESSFUL);
|
|
|
|
}
|
|
|
|
StartingSector = ClusterToSector (DeviceExt, NextCluster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (StartSector)
|
|
|
|
*StartSector = StartingSector;
|
|
|
|
if (Entry)
|
|
|
|
*Entry = i;
|
|
|
|
ExFreePool (block);
|
|
|
|
return (STATUS_UNSUCCESSFUL);
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS
|
2001-01-16 09:55:02 +00:00
|
|
|
VfatOpenFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
|
2000-12-29 23:17:12 +00:00
|
|
|
PWSTR FileName)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Opens a file
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
PWSTR current = NULL;
|
|
|
|
PWSTR next;
|
|
|
|
PWSTR string;
|
2001-03-06 23:28:42 +00:00
|
|
|
PWSTR buffer; // used to store a pointer while checking MAX_PATH conformance
|
2000-12-29 23:17:12 +00:00
|
|
|
PVFATFCB ParentFcb;
|
|
|
|
PVFATFCB Fcb, pRelFcb;
|
|
|
|
PVFATFCB Temp;
|
|
|
|
PVFATCCB newCCB, pRelCcb;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PFILE_OBJECT pRelFileObject;
|
|
|
|
PWSTR AbsFileName = NULL;
|
|
|
|
short i, j;
|
|
|
|
PLIST_ENTRY current_entry;
|
|
|
|
KIRQL oldIrql;
|
2001-01-01 04:42:12 +00:00
|
|
|
ULONG BytesPerCluster;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2001-01-16 09:55:02 +00:00
|
|
|
DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt, FileObject, FileName);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
/* FIXME : treat relative name */
|
|
|
|
if (FileObject->RelatedFileObject)
|
|
|
|
{
|
|
|
|
DbgPrint ("try related for %S\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] != '\\'))
|
1999-12-11 21:14:49 +00:00
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
return Status;
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
2000-12-29 23:17:12 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
CHECKPOINT;
|
|
|
|
|
|
|
|
KeAcquireSpinLock (&DeviceExt->FcbListLock, &oldIrql);
|
|
|
|
current_entry = DeviceExt->FcbListHead.Flink;
|
|
|
|
while (current_entry != &DeviceExt->FcbListHead)
|
|
|
|
{
|
|
|
|
Fcb = CONTAINING_RECORD (current_entry, VFATFCB, FcbListEntry);
|
|
|
|
|
|
|
|
DPRINT ("Scanning %x\n", Fcb);
|
|
|
|
DPRINT ("Scanning %S\n", Fcb->PathName);
|
|
|
|
|
|
|
|
if (DeviceExt == Fcb->pDevExt && wstrcmpi (FileName, Fcb->PathName))
|
|
|
|
{
|
|
|
|
Fcb->RefCount++;
|
|
|
|
KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
|
|
|
|
FileObject->FsContext = (PVOID)&Fcb->RFCB;
|
|
|
|
newCCB = ExAllocatePool (NonPagedPool, sizeof (VFATCCB));
|
|
|
|
memset (newCCB, 0, sizeof (VFATCCB));
|
2001-02-10 22:51:11 +00:00
|
|
|
FileObject->Flags = FileObject->Flags |
|
|
|
|
FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
|
|
|
|
FileObject->SectionObjectPointers =
|
|
|
|
&Fcb->SectionObjectPointers;
|
2000-12-29 23:17:12 +00:00
|
|
|
FileObject->FsContext2 = newCCB;
|
|
|
|
newCCB->pFcb = Fcb;
|
|
|
|
newCCB->PtrFileObject = FileObject;
|
|
|
|
if (AbsFileName)
|
|
|
|
ExFreePool (AbsFileName);
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
current_entry = current_entry->Flink;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
|
|
|
|
|
|
|
|
CHECKPOINT;
|
|
|
|
DPRINT ("FileName %S\n", FileName);
|
|
|
|
|
|
|
|
string = FileName;
|
|
|
|
ParentFcb = NULL;
|
|
|
|
Fcb = ExAllocatePool (NonPagedPool, sizeof (VFATFCB));
|
|
|
|
memset (Fcb, 0, sizeof (VFATFCB));
|
2001-03-06 23:28:42 +00:00
|
|
|
Fcb->ObjectName = &Fcb->PathName[1];
|
|
|
|
Fcb->PathName[0]='\\';
|
2000-12-29 23:17:12 +00:00
|
|
|
next = &string[0];
|
|
|
|
|
|
|
|
CHECKPOINT;
|
2001-03-06 23:28:42 +00:00
|
|
|
if (*next == 0 || *(next+1) == 0) // root
|
2000-12-29 23:17:12 +00:00
|
|
|
{
|
|
|
|
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;
|
2000-07-07 02:14:14 +00:00
|
|
|
else
|
2000-12-29 23:17:12 +00:00
|
|
|
Fcb->entry.FirstCluster = 1;
|
|
|
|
/* FIXME : is 1 the good value for mark root? */
|
2001-03-06 23:28:42 +00:00
|
|
|
Fcb->ObjectName--;
|
2000-12-29 23:17:12 +00:00
|
|
|
ParentFcb = Fcb;
|
2001-03-06 23:28:42 +00:00
|
|
|
DPRINT("%S filename, PathName: %S \n",FileName, ParentFcb->PathName);
|
2000-12-29 23:17:12 +00:00
|
|
|
Fcb = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (TRUE)
|
2000-07-07 02:14:14 +00:00
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
CHECKPOINT;
|
|
|
|
*next = '\\';
|
|
|
|
current = next + 1;
|
|
|
|
next = wcschr (next + 1, '\\');
|
|
|
|
if (next != NULL)
|
|
|
|
{
|
|
|
|
*next = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* reached the last path component */
|
|
|
|
DPRINT ("exiting: current '%S'\n", current);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINT ("current '%S'\n", current);
|
|
|
|
Status = FindFile (DeviceExt, Fcb, ParentFcb, current, NULL, NULL);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
|
|
{
|
|
|
|
CHECKPOINT;
|
|
|
|
if (Fcb != NULL)
|
|
|
|
ExFreePool (Fcb);
|
|
|
|
if (ParentFcb != NULL)
|
|
|
|
ExFreePool (ParentFcb);
|
|
|
|
if (AbsFileName)
|
|
|
|
ExFreePool (AbsFileName);
|
|
|
|
|
|
|
|
DPRINT ("error STATUS_OBJECT_PATH_NOT_FOUND\n");
|
|
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
|
|
}
|
|
|
|
Temp = Fcb;
|
|
|
|
CHECKPOINT;
|
|
|
|
if (ParentFcb == NULL)
|
|
|
|
{
|
|
|
|
CHECKPOINT;
|
|
|
|
Fcb = ExAllocatePool (NonPagedPool, sizeof (VFATFCB));
|
|
|
|
memset (Fcb, 0, sizeof (VFATFCB));
|
2001-03-06 23:28:42 +00:00
|
|
|
Fcb->ObjectName = &Fcb->PathName[1];
|
|
|
|
Fcb->PathName[0] = '\\';
|
2000-12-29 23:17:12 +00:00
|
|
|
}
|
|
|
|
else
|
2001-03-06 23:28:42 +00:00
|
|
|
Fcb = ParentFcb;
|
|
|
|
|
|
|
|
buffer=Fcb->ObjectName;
|
|
|
|
Fcb->ObjectName = Fcb->PathName + (Temp->ObjectName-Temp->PathName) + wcslen(Temp->ObjectName)+1;
|
|
|
|
// The line above should be possible to optimize. I was tired when writing it and always did something wrong
|
|
|
|
if (Fcb->ObjectName - Fcb->PathName >= MAX_PATH)
|
|
|
|
{
|
|
|
|
if (Fcb != NULL)
|
|
|
|
ExFreePool (Fcb);
|
|
|
|
if (ParentFcb != NULL)
|
|
|
|
ExFreePool (ParentFcb);
|
|
|
|
if (AbsFileName)
|
|
|
|
ExFreePool (AbsFileName);
|
|
|
|
|
|
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
|
|
// Which error code? It's no input buffer limit, it's the MAX_PATH limit.
|
|
|
|
// However, the current one is still wrong. Fix it!
|
|
|
|
}
|
|
|
|
wcscat(buffer, Temp->ObjectName-1);
|
|
|
|
Fcb->ObjectName[-1]='\\';
|
|
|
|
Fcb->ObjectName[0]=0;
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
CHECKPOINT;
|
|
|
|
ParentFcb = Temp;
|
2000-07-07 02:14:14 +00:00
|
|
|
}
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
/* searching for last path component */
|
|
|
|
DPRINT ("current '%S'\n", current);
|
|
|
|
Status = FindFile (DeviceExt, Fcb, ParentFcb, current, NULL, NULL);
|
1999-12-11 21:14:49 +00:00
|
|
|
if (Status != STATUS_SUCCESS)
|
2001-03-06 23:28:42 +00:00
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
/* file does not exist */
|
|
|
|
CHECKPOINT;
|
|
|
|
if (Fcb != NULL)
|
|
|
|
ExFreePool (Fcb);
|
|
|
|
if (ParentFcb != NULL)
|
|
|
|
ExFreePool (ParentFcb);
|
|
|
|
if (AbsFileName)
|
|
|
|
ExFreePool (AbsFileName);
|
|
|
|
|
2001-03-06 23:28:42 +00:00
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
2000-12-29 23:17:12 +00:00
|
|
|
|
1999-12-11 21:14:49 +00:00
|
|
|
Temp = Fcb;
|
2001-03-06 23:28:42 +00:00
|
|
|
|
|
|
|
Fcb = ParentFcb;
|
1999-12-11 21:14:49 +00:00
|
|
|
ParentFcb = Temp;
|
2001-03-06 23:28:42 +00:00
|
|
|
ParentFcb->ObjectName = wcschr (ParentFcb->ObjectName, '\\');
|
2000-12-29 23:17:12 +00:00
|
|
|
}
|
|
|
|
|
2001-01-08 02:14:06 +00:00
|
|
|
FileObject->Flags = FileObject->Flags |
|
|
|
|
FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
|
2001-02-10 22:51:11 +00:00
|
|
|
FileObject->SectionObjectPointers = &ParentFcb->SectionObjectPointers;
|
|
|
|
memset(FileObject->SectionObjectPointers, 0,
|
|
|
|
sizeof(SECTION_OBJECT_POINTERS));
|
2000-12-29 23:17:12 +00:00
|
|
|
FileObject->FsContext = (PVOID)&ParentFcb->RFCB;
|
|
|
|
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 */
|
|
|
|
|
|
|
|
KeAcquireSpinLock (&DeviceExt->FcbListLock, &oldIrql);
|
|
|
|
InsertTailList (&DeviceExt->FcbListHead, &ParentFcb->FcbListEntry);
|
|
|
|
KeReleaseSpinLock (&DeviceExt->FcbListLock, oldIrql);
|
|
|
|
|
2001-03-06 23:28:42 +00:00
|
|
|
/* vfat_wcsncpy (ParentFcb->PathName, FileName, MAX_PATH);
|
|
|
|
ParentFcb->ObjectName = ParentFcb->PathName + (current - FileName); */
|
2000-12-29 23:17:12 +00:00
|
|
|
ParentFcb->pDevExt = DeviceExt;
|
2001-01-01 04:42:12 +00:00
|
|
|
BytesPerCluster = DeviceExt->Boot->SectorsPerCluster * BLOCKSIZE;
|
|
|
|
if (BytesPerCluster >= PAGESIZE)
|
|
|
|
{
|
|
|
|
Status = CcInitializeFileCache(FileObject, &ParentFcb->RFCB.Bcb,
|
|
|
|
BytesPerCluster);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = CcInitializeFileCache(FileObject, &ParentFcb->RFCB.Bcb,
|
|
|
|
PAGESIZE);
|
|
|
|
}
|
2000-12-29 23:17:12 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DbgPrint("CcInitializeFileCache failed\n");
|
|
|
|
KeBugCheck(0);
|
|
|
|
}
|
|
|
|
DPRINT ("file open, fcb=%x\n", ParentFcb);
|
|
|
|
DPRINT ("FileSize %d\n", ParentFcb->entry.FileSize);
|
|
|
|
if (Fcb)
|
|
|
|
ExFreePool (Fcb);
|
|
|
|
if (AbsFileName)
|
|
|
|
ExFreePool (AbsFileName);
|
|
|
|
CHECKPOINT;
|
|
|
|
|
|
|
|
return (STATUS_SUCCESS);
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS
|
|
|
|
VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
1999-12-11 21:14:49 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Create or open a file
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
PIO_STACK_LOCATION Stack;
|
|
|
|
PFILE_OBJECT FileObject;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
|
|
ULONG RequestedDisposition, RequestedOptions;
|
|
|
|
PVFATCCB pCcb;
|
|
|
|
PVFATFCB pFcb;
|
2001-03-06 17:28:25 +00:00
|
|
|
PWCHAR c;
|
2000-12-29 23:17:12 +00:00
|
|
|
|
|
|
|
Stack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
assert (Stack);
|
|
|
|
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
|
|
|
|
RequestedOptions =
|
|
|
|
Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
|
|
|
|
if ((RequestedOptions & FILE_DIRECTORY_FILE)
|
|
|
|
&& RequestedDisposition == FILE_SUPERSEDE)
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
FileObject = Stack->FileObject;
|
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
assert (DeviceExt);
|
2001-01-12 21:00:08 +00:00
|
|
|
|
2001-03-06 17:28:25 +00:00
|
|
|
/*
|
|
|
|
* Check for illegal characters in the file name
|
|
|
|
*/
|
|
|
|
c = FileObject->FileName.Buffer;
|
|
|
|
while (*c != 0)
|
|
|
|
{
|
|
|
|
if (*c == L'*' || *c == L'?')
|
|
|
|
{
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
Irp->IoStatus.Status = STATUS_OBJECT_NAME_INVALID;
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
|
2001-01-16 09:55:02 +00:00
|
|
|
Status = VfatOpenFile (DeviceExt, FileObject, FileObject->FileName.Buffer);
|
2000-12-29 23:17:12 +00:00
|
|
|
|
2001-01-16 09:55:02 +00:00
|
|
|
/*
|
|
|
|
* If the directory containing the file to open doesn't exist then
|
|
|
|
* fail immediately
|
|
|
|
*/
|
2000-12-29 23:17:12 +00:00
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (Status == STATUS_OBJECT_PATH_NOT_FOUND)
|
|
|
|
{
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
/*
|
|
|
|
* If the file open failed then create the required file
|
|
|
|
*/
|
|
|
|
if (RequestedDisposition == FILE_CREATE ||
|
|
|
|
RequestedDisposition == FILE_OPEN_IF ||
|
|
|
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
|
|
|
RequestedDisposition == FILE_SUPERSEDE)
|
2000-12-29 23:17:12 +00:00
|
|
|
{
|
|
|
|
CHECKPOINT;
|
2001-01-16 09:55:02 +00:00
|
|
|
Status =
|
2000-12-29 23:17:12 +00:00
|
|
|
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
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
/*
|
|
|
|
* Otherwise fail if the caller wanted to create a new file
|
|
|
|
*/
|
2000-12-29 23:17:12 +00:00
|
|
|
if (RequestedDisposition == FILE_CREATE)
|
|
|
|
{
|
|
|
|
Irp->IoStatus.Information = FILE_EXISTS;
|
|
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
2001-01-16 09:55:02 +00:00
|
|
|
}
|
2000-12-29 23:17:12 +00:00
|
|
|
pCcb = FileObject->FsContext2;
|
|
|
|
pFcb = pCcb->pFcb;
|
2001-01-16 09:55:02 +00:00
|
|
|
/*
|
|
|
|
* If requested then delete the file and create a new one with the
|
|
|
|
* same name
|
|
|
|
*/
|
2000-12-29 23:17:12 +00:00
|
|
|
if (RequestedDisposition == FILE_SUPERSEDE)
|
|
|
|
{
|
|
|
|
ULONG Cluster, NextCluster;
|
|
|
|
/* FIXME set size to 0 and free clusters */
|
|
|
|
pFcb->entry.FileSize = 0;
|
|
|
|
if (DeviceExt->FatType == FAT32)
|
|
|
|
Cluster = pFcb->entry.FirstCluster
|
|
|
|
+ pFcb->entry.FirstClusterHigh * 65536;
|
|
|
|
else
|
|
|
|
Cluster = pFcb->entry.FirstCluster;
|
|
|
|
pFcb->entry.FirstCluster = 0;
|
|
|
|
pFcb->entry.FirstClusterHigh = 0;
|
2001-01-16 09:55:02 +00:00
|
|
|
updEntry (DeviceExt, FileObject);
|
2000-12-29 23:17:12 +00:00
|
|
|
while (Cluster != 0xffffffff && Cluster > 1)
|
|
|
|
{
|
2001-01-16 09:55:02 +00:00
|
|
|
Status = GetNextCluster (DeviceExt, Cluster, &NextCluster, TRUE);
|
2001-02-14 02:53:54 +00:00
|
|
|
WriteCluster (DeviceExt, Cluster, 0);
|
2000-12-29 23:17:12 +00:00
|
|
|
Cluster = NextCluster;
|
|
|
|
}
|
|
|
|
}
|
2001-01-16 09:55:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the file has the requested attributes
|
|
|
|
*/
|
2000-12-29 23:17:12 +00:00
|
|
|
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))
|
|
|
|
VfatCloseFile (DeviceExt, FileObject);
|
|
|
|
else
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
|
|
/* FIXME : make supersed or overwrite if requested */
|
|
|
|
}
|
|
|
|
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
|
|
|
|
return Status;
|
1999-12-11 21:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS STDCALL
|
|
|
|
VfatCreate (PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
2000-07-07 02:14:14 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Create or open a file
|
|
|
|
*/
|
|
|
|
{
|
2000-12-29 23:17:12 +00:00
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
|
|
|
|
|
|
assert (DeviceObject);
|
|
|
|
assert (Irp);
|
|
|
|
|
|
|
|
if (DeviceObject->Size == sizeof (DEVICE_OBJECT))
|
|
|
|
{
|
|
|
|
/* DeviceObject represents 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
assert (DeviceExt);
|
|
|
|
ExAcquireResourceExclusiveLite (&DeviceExt->DirResource, TRUE);
|
|
|
|
|
|
|
|
Status = VfatCreateFile (DeviceObject, Irp);
|
|
|
|
|
|
|
|
ExReleaseResourceLite (&DeviceExt->DirResource);
|
|
|
|
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
|
|
|
|
return Status;
|
2000-07-07 02:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|