mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
761 lines
28 KiB
C
761 lines
28 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/fs/vfat/dir.c
|
|
* PURPOSE: VFAT Filesystem : directory control
|
|
* UPDATE HISTORY:
|
|
19-12-1998 : created
|
|
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "vfat.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/* Function like DosDateTimeToFileTime */
|
|
BOOLEAN
|
|
FsdDosDateTimeToSystemTime(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
USHORT DosDate,
|
|
USHORT DosTime,
|
|
PLARGE_INTEGER SystemTime)
|
|
{
|
|
PDOSTIME pdtime = (PDOSTIME)&DosTime;
|
|
PDOSDATE pddate = (PDOSDATE)&DosDate;
|
|
TIME_FIELDS TimeFields;
|
|
LARGE_INTEGER LocalTime;
|
|
|
|
if (SystemTime == NULL)
|
|
return FALSE;
|
|
|
|
TimeFields.Milliseconds = 0;
|
|
TimeFields.Second = pdtime->Second * 2;
|
|
TimeFields.Minute = pdtime->Minute;
|
|
TimeFields.Hour = pdtime->Hour;
|
|
|
|
TimeFields.Day = pddate->Day;
|
|
TimeFields.Month = pddate->Month;
|
|
TimeFields.Year = (CSHORT)(DeviceExt->BaseDateYear + pddate->Year);
|
|
|
|
RtlTimeFieldsToTime(&TimeFields, &LocalTime);
|
|
ExLocalTimeToSystemTime(&LocalTime, SystemTime);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Function like FileTimeToDosDateTime */
|
|
BOOLEAN
|
|
FsdSystemTimeToDosDateTime(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PLARGE_INTEGER SystemTime,
|
|
PUSHORT pDosDate,
|
|
PUSHORT pDosTime)
|
|
{
|
|
PDOSTIME pdtime = (PDOSTIME)pDosTime;
|
|
PDOSDATE pddate = (PDOSDATE)pDosDate;
|
|
TIME_FIELDS TimeFields;
|
|
LARGE_INTEGER LocalTime;
|
|
|
|
if (SystemTime == NULL)
|
|
return FALSE;
|
|
|
|
ExSystemTimeToLocalTime(SystemTime, &LocalTime);
|
|
RtlTimeToTimeFields(&LocalTime, &TimeFields);
|
|
|
|
if (pdtime)
|
|
{
|
|
pdtime->Second = TimeFields.Second / 2;
|
|
pdtime->Minute = TimeFields.Minute;
|
|
pdtime->Hour = TimeFields.Hour;
|
|
}
|
|
|
|
if (pddate)
|
|
{
|
|
pddate->Day = TimeFields.Day;
|
|
pddate->Month = TimeFields.Month;
|
|
pddate->Year = (USHORT) (TimeFields.Year - DeviceExt->BaseDateYear);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define ULONG_ROUND_UP(x) ROUND_UP((x), (sizeof(ULONG)))
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetFileNamesInformation(
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PFILE_NAMES_INFORMATION pInfo,
|
|
ULONG BufferLength,
|
|
PULONG Written,
|
|
BOOLEAN First)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BytesToCopy = 0;
|
|
|
|
*Written = 0;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
|
|
return Status;
|
|
|
|
if (First || (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + DirContext->LongNameU.Length))
|
|
{
|
|
pInfo->FileNameLength = DirContext->LongNameU.Length;
|
|
|
|
*Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
|
|
pInfo->NextEntryOffset = 0;
|
|
if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
|
|
{
|
|
BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
|
|
RtlCopyMemory(pInfo->FileName,
|
|
DirContext->LongNameU.Buffer,
|
|
BytesToCopy);
|
|
*Written += BytesToCopy;
|
|
|
|
if (BytesToCopy == DirContext->LongNameU.Length)
|
|
{
|
|
pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
|
|
BytesToCopy);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetFileDirectoryInformation(
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_DIRECTORY_INFORMATION pInfo,
|
|
ULONG BufferLength,
|
|
PULONG Written,
|
|
BOOLEAN First)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BytesToCopy = 0;
|
|
|
|
*Written = 0;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
|
|
return Status;
|
|
|
|
if (First || (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + DirContext->LongNameU.Length))
|
|
{
|
|
pInfo->FileNameLength = DirContext->LongNameU.Length;
|
|
/* pInfo->FileIndex = ; */
|
|
|
|
*Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
|
|
pInfo->NextEntryOffset = 0;
|
|
if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
|
|
{
|
|
BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
|
|
RtlCopyMemory(pInfo->FileName,
|
|
DirContext->LongNameU.Buffer,
|
|
BytesToCopy);
|
|
*Written += BytesToCopy;
|
|
|
|
if (BytesToCopy == DirContext->LongNameU.Length)
|
|
{
|
|
pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
|
|
BytesToCopy);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (vfatVolumeIsFatX(DeviceExt))
|
|
{
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.CreationDate,
|
|
DirContext->DirEntry.FatX.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.AccessDate,
|
|
DirContext->DirEntry.FatX.AccessTime,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.UpdateDate,
|
|
DirContext->DirEntry.FatX.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
|
|
if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
pInfo->EndOfFile.QuadPart = 0;
|
|
pInfo->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
|
|
DeviceExt->FatInfo.BytesPerCluster);
|
|
}
|
|
|
|
pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
|
|
}
|
|
else
|
|
{
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.CreationDate,
|
|
DirContext->DirEntry.Fat.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.AccessDate,
|
|
0,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.UpdateDate,
|
|
DirContext->DirEntry.Fat.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
|
|
if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
pInfo->EndOfFile.QuadPart = 0;
|
|
pInfo->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
|
|
DeviceExt->FatInfo.BytesPerCluster);
|
|
}
|
|
|
|
pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetFileFullDirectoryInformation(
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_FULL_DIR_INFORMATION pInfo,
|
|
ULONG BufferLength,
|
|
PULONG Written,
|
|
BOOLEAN First)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BytesToCopy = 0;
|
|
|
|
*Written = 0;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
|
|
return Status;
|
|
|
|
if (First || (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
|
|
{
|
|
pInfo->FileNameLength = DirContext->LongNameU.Length;
|
|
/* pInfo->FileIndex = ; */
|
|
pInfo->EaSize = 0;
|
|
|
|
*Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
|
|
pInfo->NextEntryOffset = 0;
|
|
if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
|
|
{
|
|
BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
|
|
RtlCopyMemory(pInfo->FileName,
|
|
DirContext->LongNameU.Buffer,
|
|
BytesToCopy);
|
|
*Written += BytesToCopy;
|
|
|
|
if (BytesToCopy == DirContext->LongNameU.Length)
|
|
{
|
|
pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
|
|
BytesToCopy);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (vfatVolumeIsFatX(DeviceExt))
|
|
{
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.CreationDate,
|
|
DirContext->DirEntry.FatX.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.AccessDate,
|
|
DirContext->DirEntry.FatX.AccessTime,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.UpdateDate,
|
|
DirContext->DirEntry.FatX.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
|
|
DeviceExt->FatInfo.BytesPerCluster);
|
|
pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
|
|
}
|
|
else
|
|
{
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.CreationDate,
|
|
DirContext->DirEntry.Fat.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.AccessDate,
|
|
0,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.UpdateDate,
|
|
DirContext->DirEntry.Fat.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
|
|
DeviceExt->FatInfo.BytesPerCluster);
|
|
pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetFileBothInformation(
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PFILE_BOTH_DIR_INFORMATION pInfo,
|
|
ULONG BufferLength,
|
|
PULONG Written,
|
|
BOOLEAN First)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BytesToCopy = 0;
|
|
|
|
*Written = 0;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
|
|
return Status;
|
|
|
|
if (First || (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
|
|
{
|
|
pInfo->FileNameLength = DirContext->LongNameU.Length;
|
|
pInfo->EaSize = 0;
|
|
|
|
*Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
|
|
pInfo->NextEntryOffset = 0;
|
|
if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
|
|
{
|
|
BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
|
|
RtlCopyMemory(pInfo->FileName,
|
|
DirContext->LongNameU.Buffer,
|
|
BytesToCopy);
|
|
*Written += BytesToCopy;
|
|
|
|
if (BytesToCopy == DirContext->LongNameU.Length)
|
|
{
|
|
pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
|
|
BytesToCopy);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (vfatVolumeIsFatX(DeviceExt))
|
|
{
|
|
pInfo->ShortName[0] = 0;
|
|
pInfo->ShortNameLength = 0;
|
|
/* pInfo->FileIndex = ; */
|
|
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.CreationDate,
|
|
DirContext->DirEntry.FatX.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.AccessDate,
|
|
DirContext->DirEntry.FatX.AccessTime,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.FatX.UpdateDate,
|
|
DirContext->DirEntry.FatX.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
|
|
if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
pInfo->EndOfFile.QuadPart = 0;
|
|
pInfo->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
|
|
DeviceExt->FatInfo.BytesPerCluster);
|
|
}
|
|
|
|
pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
|
|
}
|
|
else
|
|
{
|
|
pInfo->ShortNameLength = (CCHAR)DirContext->ShortNameU.Length;
|
|
|
|
ASSERT(pInfo->ShortNameLength / sizeof(WCHAR) <= 12);
|
|
RtlCopyMemory(pInfo->ShortName,
|
|
DirContext->ShortNameU.Buffer,
|
|
DirContext->ShortNameU.Length);
|
|
|
|
/* pInfo->FileIndex = ; */
|
|
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.CreationDate,
|
|
DirContext->DirEntry.Fat.CreationTime,
|
|
&pInfo->CreationTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.AccessDate,
|
|
0,
|
|
&pInfo->LastAccessTime);
|
|
FsdDosDateTimeToSystemTime(DeviceExt,
|
|
DirContext->DirEntry.Fat.UpdateDate,
|
|
DirContext->DirEntry.Fat.UpdateTime,
|
|
&pInfo->LastWriteTime);
|
|
|
|
pInfo->ChangeTime = pInfo->LastWriteTime;
|
|
|
|
if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
pInfo->EndOfFile.QuadPart = 0;
|
|
pInfo->AllocationSize.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
pInfo->EndOfFile.u.HighPart = 0;
|
|
pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
|
|
/* Make allocsize a rounded up multiple of BytesPerCluster */
|
|
pInfo->AllocationSize.u.HighPart = 0;
|
|
pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize, DeviceExt->FatInfo.BytesPerCluster);
|
|
}
|
|
|
|
pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
DoQuery(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LONG BufferLength = 0;
|
|
PUNICODE_STRING pSearchPattern = NULL;
|
|
FILE_INFORMATION_CLASS FileInformationClass;
|
|
PUCHAR Buffer = NULL;
|
|
PFILE_NAMES_INFORMATION Buffer0 = NULL;
|
|
PVFATFCB pFcb;
|
|
PVFATCCB pCcb;
|
|
BOOLEAN FirstQuery = FALSE;
|
|
BOOLEAN FirstCall = TRUE;
|
|
VFAT_DIRENTRY_CONTEXT DirContext;
|
|
WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
|
|
WCHAR ShortNameBuffer[13];
|
|
ULONG Written;
|
|
|
|
PIO_STACK_LOCATION Stack = IrpContext->Stack;
|
|
|
|
pCcb = (PVFATCCB)IrpContext->FileObject->FsContext2;
|
|
pFcb = (PVFATFCB)IrpContext->FileObject->FsContext;
|
|
|
|
/* Determine Buffer for result : */
|
|
BufferLength = Stack->Parameters.QueryDirectory.Length;
|
|
#if 0
|
|
/* Do not probe the user buffer until SEH is available */
|
|
if (IrpContext->Irp->RequestorMode != KernelMode &&
|
|
IrpContext->Irp->MdlAddress == NULL &&
|
|
IrpContext->Irp->UserBuffer != NULL)
|
|
{
|
|
ProbeForWrite(IrpContext->Irp->UserBuffer, BufferLength, 1);
|
|
}
|
|
#endif
|
|
Buffer = VfatGetUserBuffer(IrpContext->Irp, FALSE);
|
|
|
|
if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource,
|
|
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
|
|
{
|
|
Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
|
|
if (NT_SUCCESS(Status))
|
|
Status = STATUS_PENDING;
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (!ExAcquireResourceSharedLite(&pFcb->MainResource,
|
|
BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
|
|
{
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
|
|
if (NT_SUCCESS(Status))
|
|
Status = STATUS_PENDING;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* Obtain the callers parameters */
|
|
pSearchPattern = (PUNICODE_STRING)Stack->Parameters.QueryDirectory.FileName;
|
|
FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
|
|
|
|
/* Allocate search pattern in case:
|
|
* -> We don't have one already in context
|
|
* -> We have been given an input pattern
|
|
* -> The pattern length is not null
|
|
* -> The pattern buffer is not null
|
|
* Otherwise, we'll fall later and allocate a match all (*) pattern
|
|
*/
|
|
if (pSearchPattern &&
|
|
pSearchPattern->Length != 0 && pSearchPattern->Buffer != NULL)
|
|
{
|
|
if (!pCcb->SearchPattern.Buffer)
|
|
{
|
|
FirstQuery = TRUE;
|
|
pCcb->SearchPattern.MaximumLength = pSearchPattern->Length + sizeof(WCHAR);
|
|
pCcb->SearchPattern.Buffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
pCcb->SearchPattern.MaximumLength,
|
|
TAG_SEARCH);
|
|
if (!pCcb->SearchPattern.Buffer)
|
|
{
|
|
ExReleaseResourceLite(&pFcb->MainResource);
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlCopyUnicodeString(&pCcb->SearchPattern, pSearchPattern);
|
|
pCcb->SearchPattern.Buffer[pCcb->SearchPattern.Length / sizeof(WCHAR)] = 0;
|
|
}
|
|
}
|
|
else if (!pCcb->SearchPattern.Buffer)
|
|
{
|
|
FirstQuery = TRUE;
|
|
pCcb->SearchPattern.MaximumLength = 2 * sizeof(WCHAR);
|
|
pCcb->SearchPattern.Buffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
2 * sizeof(WCHAR),
|
|
TAG_SEARCH);
|
|
if (!pCcb->SearchPattern.Buffer)
|
|
{
|
|
ExReleaseResourceLite(&pFcb->MainResource);
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
pCcb->SearchPattern.Buffer[0] = L'*';
|
|
pCcb->SearchPattern.Buffer[1] = 0;
|
|
pCcb->SearchPattern.Length = sizeof(WCHAR);
|
|
}
|
|
|
|
if (BooleanFlagOn(IrpContext->Stack->Flags, SL_INDEX_SPECIFIED))
|
|
{
|
|
DirContext.DirIndex = pCcb->Entry = Stack->Parameters.QueryDirectory.FileIndex;
|
|
}
|
|
else if (FirstQuery || BooleanFlagOn(IrpContext->Stack->Flags, SL_RESTART_SCAN))
|
|
{
|
|
DirContext.DirIndex = pCcb->Entry = 0;
|
|
}
|
|
else
|
|
{
|
|
DirContext.DirIndex = pCcb->Entry;
|
|
}
|
|
|
|
DPRINT("Buffer=%p tofind=%wZ\n", Buffer, &pCcb->SearchPattern);
|
|
|
|
DirContext.DeviceExt = IrpContext->DeviceExt;
|
|
DirContext.LongNameU.Buffer = LongNameBuffer;
|
|
DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
|
|
DirContext.ShortNameU.Buffer = ShortNameBuffer;
|
|
DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
|
|
|
|
Written = 0;
|
|
while ((Status == STATUS_SUCCESS) && (BufferLength > 0))
|
|
{
|
|
Status = FindFile(IrpContext->DeviceExt,
|
|
pFcb,
|
|
&pCcb->SearchPattern,
|
|
&DirContext,
|
|
FirstCall);
|
|
pCcb->Entry = DirContext.DirIndex;
|
|
|
|
DPRINT("Found %wZ, Status=%x, entry %x\n", &DirContext.LongNameU, Status, pCcb->Entry);
|
|
|
|
FirstCall = FALSE;
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
switch (FileInformationClass)
|
|
{
|
|
case FileDirectoryInformation:
|
|
Status = VfatGetFileDirectoryInformation(&DirContext,
|
|
IrpContext->DeviceExt,
|
|
(PFILE_DIRECTORY_INFORMATION)Buffer,
|
|
BufferLength,
|
|
&Written,
|
|
Buffer0 == NULL);
|
|
break;
|
|
|
|
case FileFullDirectoryInformation:
|
|
Status = VfatGetFileFullDirectoryInformation(&DirContext,
|
|
IrpContext->DeviceExt,
|
|
(PFILE_FULL_DIR_INFORMATION)Buffer,
|
|
BufferLength,
|
|
&Written,
|
|
Buffer0 == NULL);
|
|
break;
|
|
|
|
case FileBothDirectoryInformation:
|
|
Status = VfatGetFileBothInformation(&DirContext,
|
|
IrpContext->DeviceExt,
|
|
(PFILE_BOTH_DIR_INFORMATION)Buffer,
|
|
BufferLength,
|
|
&Written,
|
|
Buffer0 == NULL);
|
|
break;
|
|
|
|
case FileNamesInformation:
|
|
Status = VfatGetFileNamesInformation(&DirContext,
|
|
(PFILE_NAMES_INFORMATION)Buffer,
|
|
BufferLength,
|
|
&Written,
|
|
Buffer0 == NULL);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Status = (FirstQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
|
|
break;
|
|
}
|
|
|
|
Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
|
|
Buffer0->FileIndex = DirContext.DirIndex;
|
|
pCcb->Entry = ++DirContext.DirIndex;
|
|
BufferLength -= Buffer0->NextEntryOffset;
|
|
|
|
if (BooleanFlagOn(IrpContext->Stack->Flags, SL_RETURN_SINGLE_ENTRY))
|
|
break;
|
|
|
|
Buffer += Buffer0->NextEntryOffset;
|
|
}
|
|
|
|
if (Buffer0)
|
|
{
|
|
Buffer0->NextEntryOffset = 0;
|
|
Status = STATUS_SUCCESS;
|
|
IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
|
|
ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
|
|
IrpContext->Irp->IoStatus.Information = Written;
|
|
}
|
|
|
|
ExReleaseResourceLite(&pFcb->MainResource);
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS VfatNotifyChangeDirectory(PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PVCB pVcb;
|
|
PVFATFCB pFcb;
|
|
PIO_STACK_LOCATION Stack;
|
|
Stack = IrpContext->Stack;
|
|
pVcb = IrpContext->DeviceExt;
|
|
pFcb = (PVFATFCB) IrpContext->FileObject->FsContext;
|
|
|
|
FsRtlNotifyFullChangeDirectory(pVcb->NotifySync,
|
|
&(pVcb->NotifyList),
|
|
IrpContext->FileObject->FsContext2,
|
|
(PSTRING)&(pFcb->PathNameU),
|
|
BooleanFlagOn(Stack->Flags, SL_WATCH_TREE),
|
|
FALSE,
|
|
Stack->Parameters.NotifyDirectory.CompletionFilter,
|
|
IrpContext->Irp,
|
|
NULL,
|
|
NULL);
|
|
|
|
/* We won't handle IRP completion */
|
|
IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: directory control : read/write directory informations
|
|
*/
|
|
NTSTATUS
|
|
VfatDirectoryControl(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
|
|
switch (IrpContext->MinorFunction)
|
|
{
|
|
case IRP_MN_QUERY_DIRECTORY:
|
|
Status = DoQuery (IrpContext);
|
|
break;
|
|
|
|
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
|
|
Status = VfatNotifyChangeDirectory(IrpContext);
|
|
break;
|
|
|
|
default:
|
|
/* Error */
|
|
DPRINT("Unexpected minor function %x in VFAT driver\n",
|
|
IrpContext->MinorFunction);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING && BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE))
|
|
{
|
|
return VfatMarkIrpContextForQueue(IrpContext);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|