492 lines
15 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
dirctrl.c
Abstract:
This module implements routines related to handling
IRP_MJ_DIRECTORY_CONTROL.
--*/
#include "gdfx.h"
NTSTATUS
GdfxFindNextDirectoryEntry(
IN PGDF_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PGDF_FCB DirectoryFcb,
IN PDIRECTORY_ENUM_CONTEXT DirectoryEnumContext,
OUT PGDF_DIRECTORY_ENTRY *ReturnedDirectoryEntry,
OUT PULONG ReturnedDirectoryByteOffset
)
/*++
Routine Description:
This routine finds the next directory entry that matches the query template
specification.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
DirectoryFcb - Specifies the file control block that describes where to
read the directory stream from.
DirectoryEnumContext - Specifies the directory enumeration context.
ReturnedDirectoryEntry - Specifies the buffer to receive the pointer to the
directory entry for the file if found.
ReturnedDirectoryByteOffset - Specifies the buffer to receive the byte
offset of the entry in the directory stream.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
ULONG DirectoryByteOffset;
POBJECT_STRING TemplateFileName;
PVOID CacheBuffer;
ULONG CacheBufferByteOffset;
PGDF_DIRECTORY_ENTRY DirectoryEntry;
OBJECT_STRING DirectoryEntryFileName;
ASSERT(GdfxIsFlagSet(DirectoryFcb->Flags, GDF_FCB_DIRECTORY));
DirectoryByteOffset = DirectoryEnumContext->QueryOffset;
TemplateFileName = &DirectoryEnumContext->TemplateFileName;
//
// If this is an empty directory, then return now that the name isn't found.
//
if (DirectoryFcb->FileSize == 0) {
return STATUS_END_OF_FILE;
}
//
// Process the directory stream.
//
CacheBuffer = NULL;
CacheBufferByteOffset = 0;
while (DirectoryByteOffset < DirectoryFcb->FileSize) {
//
// If we haven't mapped in a cache buffer yet or if we're switching
// sectors, then we need to switch cache buffers.
//
if ((CacheBuffer == NULL) ||
(CacheBufferByteOffset != (DirectoryByteOffset & ~GDF_CD_SECTOR_MASK))) {
if (CacheBuffer != NULL) {
FscUnmapBuffer(CacheBuffer);
CacheBuffer = NULL;
}
CacheBufferByteOffset = (DirectoryByteOffset & ~GDF_CD_SECTOR_MASK);
status = FscMapBuffer(&VolumeExtension->CacheExtension, Irp,
GdfxSectorToPhysicalByteOffset(DirectoryFcb->FirstSector) +
CacheBufferByteOffset, FALSE, &CacheBuffer);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
}
//
// Make the directory byte offset relative to the current cache buffer.
//
DirectoryByteOffset &= GDF_CD_SECTOR_MASK;
//
// Compute the cache buffer relative pointer to the directory entry.
//
DirectoryEntry = (PGDF_DIRECTORY_ENTRY)((PUCHAR)CacheBuffer +
DirectoryByteOffset);
//
// If the left and right entry indexes are negative one, then this is
// sector padding, so we should advance to the next directory sector and
// continue.
//
if ((DirectoryEntry->LeftEntryIndex == (USHORT)-1) &&
(DirectoryEntry->RightEntryIndex == (USHORT)-1)) {
DirectoryByteOffset = CacheBufferByteOffset + GDF_CD_SECTOR_SIZE;
continue;
}
//
// If there's not enough space in the current sector to hold the header
// of a directory entry, then the directory entry is invalid and the
// disk is corrupt.
//
// We check for these types of disk corruption in order to guard against
// taking a potential page fault if we extend past the current sector.
//
if (DirectoryByteOffset > (GDF_CD_SECTOR_SIZE -
FIELD_OFFSET(GDF_DIRECTORY_ENTRY, FileName))) {
GdfxDbgPrint(("GDFX: found invalid directory byte offset\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// If there's not enough space in the current sector to hold the header
// and the file name, then the directory entry is invalid and the disk
// is corrupt.
//
if ((DirectoryByteOffset + DirectoryEntry->FileNameLength) >
(GDF_CD_SECTOR_SIZE - FIELD_OFFSET(GDF_DIRECTORY_ENTRY, FileName))) {
GdfxDbgPrint(("GDFX: found invalid directory byte offset\n"));
status = STATUS_DISK_CORRUPT_ERROR;
goto CleanupAndExit;
}
//
// Check if the file name matches the name we're looking for.
//
DirectoryEntryFileName.Length = DirectoryEntry->FileNameLength;
DirectoryEntryFileName.Buffer = DirectoryEntry->FileName;
if (((TemplateFileName->Buffer == NULL) ||
IoIsNameInExpression(TemplateFileName, &DirectoryEntryFileName))) {
//
// The file name matches the template file name. Leave the cache
// buffer mapped and return a pointer to the directory entry and its
// directory byte offset to the caller. The caller is responsible
// for unmapping the cache buffer.
//
*ReturnedDirectoryEntry = DirectoryEntry;
*ReturnedDirectoryByteOffset = CacheBufferByteOffset +
DirectoryByteOffset;
return STATUS_SUCCESS;
}
//
// Advance to the next directory byte offset.
//
DirectoryByteOffset = CacheBufferByteOffset + DirectoryByteOffset +
ALIGN_UP(FIELD_OFFSET(GDF_DIRECTORY_ENTRY, FileName) +
DirectoryEntry->FileNameLength, sizeof(ULONG));
}
//
// We reached the end of the file without finding the template file name.
//
status = STATUS_END_OF_FILE;
CleanupAndExit:
if (CacheBuffer != NULL) {
FscUnmapBuffer(CacheBuffer);
}
return status;
}
NTSTATUS
GdfxFsdDirectoryControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O manager to handle IRP_MJ_DIRECTORY_CONTROL
requests.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PGDF_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject;
PGDF_FCB DirectoryFcb;
ULONG FileInformationClass;
SIZE_T FileInformationBaseLength;
PDIRECTORY_ENUM_CONTEXT DirectoryEnumContext;
POBJECT_STRING TemplateFileName;
BOOLEAN InitialQuery;
PGDF_DIRECTORY_ENTRY DirectoryEntry;
ULONG DirectoryByteOffset;
PFILE_DIRECTORY_INFORMATION DirectoryInformation;
LARGE_INTEGER TimeStamp;
ULONG FileSize;
ULONG FileNameBytesToCopy;
ULONG OutputBytesRemaining;
VolumeExtension = (PGDF_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
DirectoryFcb = (PGDF_FCB)FileObject->FsContext;
//
// Synchronize the creation and access of the directory context control
// block by acquiring the global mutex.
//
GdfxAcquireGlobalMutexExclusive();
//
// Ensure that the file object is for a directory.
//
if (GdfxIsFlagClear(DirectoryFcb->Flags, GDF_FCB_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
//
// Verify that this is a supported information class.
//
FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
switch (FileInformationClass) {
case FileDirectoryInformation:
FileInformationBaseLength = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION,
FileName[0]);
break;
case FileNamesInformation:
FileInformationBaseLength = FIELD_OFFSET(FILE_NAMES_INFORMATION,
FileName[0]);
break;
default:
status = STATUS_INVALID_INFO_CLASS;
goto CleanupAndExit;
}
//
// The query cannot be started relative to a starting index.
//
if (GdfxIsFlagSet(IrpSp->Flags, SL_INDEX_SPECIFIED)) {
status = STATUS_NOT_IMPLEMENTED;
goto CleanupAndExit;
}
//
// If this is the first query for this directory, then prepare the template
// file name.
//
DirectoryEnumContext = (PDIRECTORY_ENUM_CONTEXT)FileObject->FsContext2;
if (DirectoryEnumContext == NULL) {
TemplateFileName = IrpSp->Parameters.QueryDirectory.FileName;
status = IoCreateDirectoryEnumContext(TemplateFileName,
&DirectoryEnumContext);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Connect the directory enumeration context to the file object.
//
FileObject->FsContext2 = DirectoryEnumContext;
InitialQuery = TRUE;
} else {
InitialQuery = FALSE;
}
//
// If we're to restart the directory scan, then reset the current index to
// zero.
//
if (GdfxIsFlagSet(IrpSp->Flags, SL_RESTART_SCAN)) {
DirectoryEnumContext->QueryOffset = 0;
}
//
// Find the next file identifier descriptor that matches our query criteria.
//
// On return, DirectoryEnumContext->QueryOffset still points at the original
// identifier. It's only updated after we're about to successfully return
// so that no entries are lost in the event of an invalid parameter or pool
// allocation failure.
//
status = GdfxFindNextDirectoryEntry(VolumeExtension, Irp, DirectoryFcb,
DirectoryEnumContext, &DirectoryEntry, &DirectoryByteOffset);
if (NT_SUCCESS(status)) {
//
// The I/O manager has already checked that the user's buffer has enough
// space to contain at least the header.
//
ASSERT(IrpSp->Parameters.QueryDirectory.Length >= FileInformationBaseLength);
//
// Zero out the header.
//
DirectoryInformation = (PFILE_DIRECTORY_INFORMATION)Irp->UserBuffer;
RtlZeroMemory(DirectoryInformation, FileInformationBaseLength);
//
// For FileDirectoryInformation and FileNamesInformation, the
// FileNameLength field is immediately before the FileName buffer.
//
*((PULONG)((PUCHAR)DirectoryInformation + FileInformationBaseLength -
sizeof(ULONG))) = DirectoryEntry->FileNameLength;
//
// If this is a FileDirectoryInformation request, then fill in more
// information. We have to go to dig into the file entry descriptor
// to get the information we need, so we'll construct a file control
// block to get the attributes.
//
if (FileInformationClass == FileDirectoryInformation) {
TimeStamp = VolumeExtension->TimeStamp;
DirectoryInformation->CreationTime = TimeStamp;
DirectoryInformation->LastAccessTime = TimeStamp;
DirectoryInformation->LastWriteTime = TimeStamp;
DirectoryInformation->ChangeTime = TimeStamp;
if (GdfxIsFlagSet(DirectoryEntry->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
DirectoryInformation->FileAttributes = FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_DIRECTORY;
DirectoryInformation->EndOfFile.QuadPart = 0;
DirectoryInformation->AllocationSize.QuadPart = 0;
} else {
DirectoryInformation->FileAttributes = FILE_ATTRIBUTE_READONLY;
FileSize = DirectoryEntry->FileSize;
DirectoryInformation->EndOfFile.QuadPart = (ULONGLONG)FileSize;
DirectoryInformation->AllocationSize.QuadPart = (ULONGLONG)FileSize;
}
}
//
// If nothing has gone wrong yet, then copy the file name to the user's
// buffer.
//
if (NT_SUCCESS(status)) {
OutputBytesRemaining =
ALIGN_DOWN(IrpSp->Parameters.QueryDirectory.Length -
FileInformationBaseLength, sizeof(OCHAR));
FileNameBytesToCopy = DirectoryEntry->FileNameLength;
if (FileNameBytesToCopy > OutputBytesRemaining) {
FileNameBytesToCopy = OutputBytesRemaining;
status = STATUS_BUFFER_OVERFLOW;
} else {
status = STATUS_SUCCESS;
}
RtlCopyMemory((PUCHAR)DirectoryInformation + FileInformationBaseLength,
DirectoryEntry->FileName, FileNameBytesToCopy);
//
// Fill in the number of bytes that we wrote to the user's buffer.
//
Irp->IoStatus.Information = FileInformationBaseLength +
FileNameBytesToCopy;
//
// Check that we didn't overflow the user's buffer. The I/O manager
// does the initial check to make sure there's enough space for the
// static structure for a given information class, but we might
// overflow the buffer when copying in the variable length file
// name.
//
ASSERT(Irp->IoStatus.Information <=
IrpSp->Parameters.QueryDirectory.Length);
//
// Update the query offset.
//
DirectoryEnumContext->QueryOffset = DirectoryByteOffset +
ALIGN_UP(FIELD_OFFSET(GDF_DIRECTORY_ENTRY, FileName) +
DirectoryEntry->FileNameLength, sizeof(ULONG));
}
//
// Unmap the buffer holding the directory entry.
//
FscUnmapBuffer(DirectoryEntry);
} else if (status == STATUS_END_OF_FILE) {
//
// If we hit the end of the directory stream, then return an appropriate
// status code depending on whether this was the first pass through this
// routine for this handle or not.
//
status = InitialQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
}
CleanupAndExit:
GdfxReleaseGlobalMutex();
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}