📄 dirctrl.c
字号:
/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
DirCtrl.c
Abstract:
This module implements the File Directory Control routines for Cdfs called
by the Fsd/Fsp dispatch drivers.
--*/
#include "CdProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL)
//
// Local support routines
//
NTSTATUS
CdQueryDirectory (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp,
IN PFCB Fcb,
IN PCCB Ccb
);
NTSTATUS
CdNotifyChangeDirectory (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp,
IN PCCB Ccb
);
VOID
CdInitializeEnumeration (
IN PIRP_CONTEXT IrpContext,
IN PIO_STACK_LOCATION IrpSp,
IN PFCB Fcb,
IN OUT PCCB Ccb,
IN OUT PFILE_ENUM_CONTEXT FileContext,
OUT PBOOLEAN ReturnNextEntry,
OUT PBOOLEAN ReturnSingleEntry,
OUT PBOOLEAN InitialQuery
);
BOOLEAN
CdEnumerateIndex (
IN PIRP_CONTEXT IrpContext,
IN PCCB Ccb,
IN OUT PFILE_ENUM_CONTEXT FileContext,
IN BOOLEAN ReturnNextEntry
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CdCommonDirControl)
#pragma alloc_text(PAGE, CdEnumerateIndex)
#pragma alloc_text(PAGE, CdInitializeEnumeration)
#pragma alloc_text(PAGE, CdNotifyChangeDirectory)
#pragma alloc_text(PAGE, CdQueryDirectory)
#endif
NTSTATUS
CdCommonDirControl (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the entry point for the directory control operations. These
are directory enumerations and directory notify calls. We verify the
user's handle is for a directory and then call the appropriate routine.
Arguments:
Irp - Irp for this request.
Return Value:
NTSTATUS - Status returned from the lower level routines.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
PFCB Fcb;
PCCB Ccb;
PAGED_CODE();
//
// Decode the user file object and fail this request if it is not
// a user directory.
//
if (CdDecodeFileObject( IrpContext,
IrpSp->FileObject,
&Fcb,
&Ccb ) != UserDirectoryOpen) {
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
return STATUS_INVALID_PARAMETER;
}
//
// We know this is a directory control so we'll case on the
// minor function, and call a internal worker routine to complete
// the irp.
//
switch (IrpSp->MinorFunction) {
case IRP_MN_QUERY_DIRECTORY:
Status = CdQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb );
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
Status = CdNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb );
break;
default:
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
return Status;
}
//
// Local support routines
//
NTSTATUS
CdQueryDirectory (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp,
IN PFCB Fcb,
IN PCCB Ccb
)
/*++
Routine Description:
This routine performs the query directory operation. It is responsible
for either completing of enqueuing the input Irp. We store the state of the
search in the Ccb.
Arguments:
Irp - Supplies the Irp to process
IrpSp - Stack location for this Irp.
Fcb - Fcb for this directory.
Ccb - Ccb for this directory open.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Information = 0;
ULONG LastEntry = 0;
ULONG NextEntry = 0;
ULONG FileNameBytes;
ULONG SeparatorBytes;
ULONG VersionStringBytes;
FILE_ENUM_CONTEXT FileContext;
PDIRENT ThisDirent;
BOOLEAN InitialQuery;
BOOLEAN ReturnNextEntry;
BOOLEAN ReturnSingleEntry;
BOOLEAN Found;
BOOLEAN DoCcbUpdate = FALSE;
PCHAR UserBuffer;
ULONG BytesRemainingInBuffer;
ULONG BaseLength;
PFILE_BOTH_DIR_INFORMATION DirInfo;
PFILE_NAMES_INFORMATION NamesInfo;
PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
PAGED_CODE();
//
// Check if we support this search mode. Also remember the size of the base part of
// each of these structures.
//
switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
case FileDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
FileName[0] );
break;
case FileFullDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
FileName[0] );
break;
case FileIdFullDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
FileName[0] );
break;
case FileNamesInformation:
BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
FileName[0] );
break;
case FileBothDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
FileName[0] );
break;
case FileIdBothDirectoryInformation:
BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
FileName[0] );
break;
default:
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS );
return STATUS_INVALID_INFO_CLASS;
}
//
// Get the user buffer.
//
CdMapUserBuffer( IrpContext, &UserBuffer);
//
// Initialize our search context.
//
CdInitializeFileContext( IrpContext, &FileContext );
//
// Acquire the directory.
//
CdAcquireFileShared( IrpContext, Fcb );
//
// Use a try-finally to facilitate cleanup.
//
try {
//
// Verify the Fcb is still good.
//
CdVerifyFcbOperation( IrpContext, Fcb );
//
// Start by getting the initial state for the enumeration. This will set up the Ccb with
// the initial search parameters and let us know the starting offset in the directory
// to search.
//
CdInitializeEnumeration( IrpContext,
IrpSp,
Fcb,
Ccb,
&FileContext,
&ReturnNextEntry,
&ReturnSingleEntry,
&InitialQuery );
//
// The current dirent is stored in the InitialDirent field. We capture
// this here so that we have a valid restart point even if we don't
// find a single entry.
//
ThisDirent = &FileContext.InitialDirent->Dirent;
//
// At this point we are about to enter our query loop. We have
// determined the index into the directory file to begin the
// search. LastEntry and NextEntry are used to index into the user
// buffer. LastEntry is the last entry we've added, NextEntry is
// current one we're working on. If NextEntry is non-zero, then
// at least one entry was added.
//
while (TRUE) {
//
// If the user had requested only a single match and we have
// returned that, then we stop at this point. We update the Ccb with
// the status based on the last entry returned.
//
if ((NextEntry != 0) && ReturnSingleEntry) {
DoCcbUpdate = TRUE;
try_leave( Status );
}
//
// We try to locate the next matching dirent. Our search if based on a starting
// dirent offset, whether we should return the current or next entry, whether
// we should be doing a short name search and finally whether we should be
// checking for a version match.
//
Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry );
//
// Initialize the value for the next search.
//
ReturnNextEntry = TRUE;
//
// If we didn't receive a dirent, then we are at the end of the
// directory. If we have returned any files, we exit with
// success, otherwise we return STATUS_NO_MORE_FILES.
//
if (!Found) {
if (NextEntry == 0) {
Status = STATUS_NO_MORE_FILES;
if (InitialQuery) {
Status = STATUS_NO_SUCH_FILE;
}
}
DoCcbUpdate = TRUE;
try_leave( Status );
}
//
// Remember the dirent for the file we just found.
//
ThisDirent = &FileContext.InitialDirent->Dirent;
//
// Here are the rules concerning filling up the buffer:
//
// 1. The Io system garentees that there will always be
// enough room for at least one base record.
//
// 2. If the full first record (including file name) cannot
// fit, as much of the name as possible is copied and
// STATUS_BUFFER_OVERFLOW is returned.
//
// 3. If a subsequent record cannot completely fit into the
// buffer, none of it (as in 0 bytes) is copied, and
// STATUS_SUCCESS is returned. A subsequent query will
// pick up with this record.
//
//
// Let's compute the number of bytes we need to transfer the current entry.
//
SeparatorBytes =
VersionStringBytes = 0;
//
// We can look directly at the dirent that we found.
//
FileNameBytes = ThisDirent->CdFileName.FileName.Length;
//
// Compute the number of bytes for the version string if
// we will return this. Allow directories with illegal ";".
//
if (((Ccb->SearchExpression.VersionString.Length != 0) ||
(FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) &&
(ThisDirent->CdFileName.VersionString.Length != 0)) {
SeparatorBytes = 2;
VersionStringBytes = ThisDirent->CdFileName.VersionString.Length;
}
//
// If the slot for the next entry would be beyond the length of the
// user's buffer just exit (we know we've returned at least one entry
// already). This will happen when we align the pointer past the end.
//
if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) {
ReturnNextEntry = FALSE;
DoCcbUpdate = TRUE;
try_leave( Status = STATUS_SUCCESS );
}
//
// Compute the number of bytes remaining in the buffer. Round this
// down to a WCHAR boundary so we can copy full characters.
//
BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry;
ClearFlag( BytesRemainingInBuffer, 1 );
//
// If this won't fit and we have returned a previous entry then just
// return STATUS_SUCCESS.
//
if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) {
//
// If we already found an entry then just exit.
//
if (NextEntry != 0) {
ReturnNextEntry = FALSE;
DoCcbUpdate = TRUE;
try_leave( Status = STATUS_SUCCESS );
}
//
// Don't even try to return the version string if it doesn't all fit.
// Reduce the FileNameBytes to just fit in the buffer.
//
if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
FileNameBytes = BytesRemainingInBuffer - BaseLength;
}
//
// Don't return any version string bytes.
//
VersionStringBytes =
SeparatorBytes = 0;
//
// Use a status code of STATUS_BUFFER_OVERFLOW. Also set
// ReturnSingleEntry so that we will exit the loop at the top.
//
Status = STATUS_BUFFER_OVERFLOW;
ReturnSingleEntry = TRUE;
}
//
// Protect access to the user buffer with an exception handler.
// Since (at our request) IO doesn't buffer these requests, we have
// to guard against a user messing with the page protection and other
// such trickery.
//
try {
//
// Zero and initialize the base part of the current entry.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -