From 62191fba3644b3c57e497618c8632b64dd76d0f2 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 22 Mar 2016 16:39:12 -0700 Subject: [PATCH] sys: IRP_MJ_QUERY_DIRECTORY --- src/sys/dirctl.c | 821 +++++++++++++++++++++++++++++++---------------- src/sys/driver.h | 1 + src/sys/util.c | 2 + 3 files changed, 545 insertions(+), 279 deletions(-) diff --git a/src/sys/dirctl.c b/src/sys/dirctl.c index 850bae33..077b7aa2 100644 --- a/src/sys/dirctl.c +++ b/src/sys/dirctl.c @@ -6,16 +6,26 @@ #include -static NTSTATUS FspFsvolQueryDirectory( - PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +static NTSTATUS FspFsvolQueryDirectoryCopy( + PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive, PUINT64 PDirectoryOffset, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO **PDirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen); +static NTSTATUS FspFsvolQueryDirectoryCopyCache( + FSP_FILE_DESC *FileDesc, BOOLEAN ResetCache, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen); +static NTSTATUS FspFsvolQueryDirectoryCopyInPlace( + FSP_FILE_DESC *FileDesc, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen); static NTSTATUS FspFsvolQueryDirectoryRetry( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, BOOLEAN CanWait); -static NTSTATUS FspFsvolQueryDirectoryCopy( - FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, - PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive, - PVOID DestBuf, PULONG PDestLen, - FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize); +static NTSTATUS FspFsvolQueryDirectory( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvolNotifyChangeDirectory( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); static NTSTATUS FspFsvolDirectoryControl( @@ -26,9 +36,11 @@ static FSP_IOP_REQUEST_FINI FspFsvolQueryDirectoryRequestFini; FSP_DRIVER_DISPATCH FspDirectoryControl; #ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, FspFsvolQueryDirectory) -#pragma alloc_text(PAGE, FspFsvolQueryDirectoryRetry) #pragma alloc_text(PAGE, FspFsvolQueryDirectoryCopy) +#pragma alloc_text(PAGE, FspFsvolQueryDirectoryCopyCache) +#pragma alloc_text(PAGE, FspFsvolQueryDirectoryCopyInPlace) +#pragma alloc_text(PAGE, FspFsvolQueryDirectoryRetry) +#pragma alloc_text(PAGE, FspFsvolQueryDirectory) #pragma alloc_text(PAGE, FspFsvolNotifyChangeDirectory) #pragma alloc_text(PAGE, FspFsvolDirectoryControl) #pragma alloc_text(PAGE, FspFsvolDirectoryControlPrepare) @@ -37,13 +49,392 @@ FSP_DRIVER_DISPATCH FspDirectoryControl; #pragma alloc_text(PAGE, FspDirectoryControl) #endif +#define FILE_INDEX_FROM_OFFSET(v) ((ULONG)((v) >> 32)) +#define OFFSET_FROM_FILE_INDEX(v) ((UINT64)(v) << 32) + enum { /* QueryDirectory */ RequestFileNode = 0, - RequestDirectoryChangeNumber = 1, + RequestMdl = 1, + RequestAddress = 2, + RequestProcess = 3, + + /* DirectoryControlComplete retry */ + RequestDirInfoChangeNumber = 0, }; +static NTSTATUS FspFsvolQueryDirectoryCopy( + PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive, PUINT64 PDirectoryOffset, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO **PDirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen) +{ +#define FILL_INFO(Info, DirInfo)\ + Info->NextEntryOffset = 0;\ + Info->FileIndex = FILE_INDEX_FROM_OFFSET(DirInfo->NextOffset);\ + Info->CreationTime.QuadPart = DirInfo->FileInfo.CreationTime;\ + Info->LastAccessTime.QuadPart = DirInfo->FileInfo.LastAccessTime;\ + Info->LastWriteTime.QuadPart = DirInfo->FileInfo.LastWriteTime;\ + Info->ChangeTime.QuadPart = DirInfo->FileInfo.ChangeTime;\ + Info->EndOfFile.QuadPart = DirInfo->FileInfo.FileSize;\ + Info->AllocationSize.QuadPart = DirInfo->FileInfo.AllocationSize;\ + Info->FileAttributes = 0 != DirInfo->FileInfo.FileAttributes ?\ + DirInfo->FileInfo.FileAttributes : FILE_ATTRIBUTE_NORMAL;\ + Info->FileNameLength = FileName.Length;\ + RtlCopyMemory(Info->FileName, DirInfo->FileNameBuf, FileName.Length) + + PAGED_CODE(); + + BOOLEAN MatchAll = FspFileDescDirectoryPatternMatchAll == DirectoryPattern->Buffer; + UINT64 DirectoryOffset = *PDirectoryOffset; + BOOLEAN DirectoryOffsetFound = FALSE; + FSP_FSCTL_DIR_INFO *DirInfo = *PDirInfo; + PUINT8 DirInfoEnd = (PUINT8)DirInfo + DirInfoSize; + PUINT8 DestBufBgn = (PUINT8)DestBuf; + PUINT8 DestBufEnd = (PUINT8)DestBuf + *PDestLen; + PVOID PrevDestBuf = 0; + ULONG BaseInfoLen; + UNICODE_STRING FileName; + + *PDestLen = 0; + + switch (FileInformationClass) + { + case FileDirectoryInformation: + BaseInfoLen = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName); + break; + case FileFullDirectoryInformation: + BaseInfoLen = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName); + break; + case FileIdFullDirectoryInformation: + BaseInfoLen = FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION, FileName); + break; + case FileNamesInformation: + BaseInfoLen = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName); + break; + case FileBothDirectoryInformation: + BaseInfoLen = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName); + break; + case FileIdBothDirectoryInformation: + BaseInfoLen = FIELD_OFFSET(FILE_ID_BOTH_DIR_INFORMATION, FileName); + break; + default: + return STATUS_INVALID_INFO_CLASS; + } + + try + { + for (;;) + { + if ((PUINT8)DirInfo + sizeof(DirInfo->Size) > DirInfoEnd) + break; + if (sizeof(FSP_FSCTL_DIR_INFO) > DirInfo->Size) + return 0 != *PDestLen ? STATUS_SUCCESS : + (0 == DirectoryOffset ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES); + + if (0 != DirectoryOffset && !DirectoryOffsetFound) + { + DirectoryOffsetFound = DirInfo->NextOffset == DirectoryOffset; + goto NextDirInfo; + } + + FileName.Length = + FileName.MaximumLength = (USHORT)(DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + FileName.Buffer = DirInfo->FileNameBuf; + + if (MatchAll || FsRtlIsNameInExpression(DirectoryPattern, &FileName, CaseInsensitive, 0)) + { + if ((PUINT8)DestBuf + + FSP_FSCTL_ALIGN_UP(BaseInfoLen + FileName.Length, sizeof(LONGLONG)) > DestBufEnd) + { + if (DestBufBgn != DestBuf) + break; + else + { + *PDestLen = BaseInfoLen + FileName.Length; + return STATUS_BUFFER_OVERFLOW; + } + } + + switch (FileInformationClass) + { + case FileDirectoryInformation: + { + FILE_DIRECTORY_INFORMATION *Info = DestBuf; + + FILL_INFO(Info, DirInfo); + } + break; + case FileFullDirectoryInformation: + { + FILE_FULL_DIR_INFORMATION *Info = DestBuf; + + FILL_INFO(Info, DirInfo); + Info->EaSize = 0; + } + break; + case FileIdFullDirectoryInformation: + { + FILE_ID_FULL_DIR_INFORMATION *Info = DestBuf; + + FILL_INFO(Info, DirInfo); + Info->EaSize = 0; + Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber; + } + break; + case FileNamesInformation: + { + FILE_NAMES_INFORMATION *Info = DestBuf; + + Info->NextEntryOffset = 0; + Info->FileIndex = FILE_INDEX_FROM_OFFSET(DirInfo->NextOffset); + Info->FileNameLength = FileName.Length; + RtlCopyMemory(Info->FileName, DirInfo->FileNameBuf, FileName.Length); + } + break; + case FileBothDirectoryInformation: + { + FILE_BOTH_DIR_INFORMATION *Info = DestBuf; + + FILL_INFO(Info, DirInfo); + Info->EaSize = 0; + Info->ShortNameLength = 0; + RtlZeroMemory(Info->ShortName, sizeof Info->ShortName); + } + break; + case FileIdBothDirectoryInformation: + { + FILE_ID_BOTH_DIR_INFORMATION *Info = DestBuf; + + FILL_INFO(Info, DirInfo); + Info->EaSize = 0; + Info->ShortNameLength = 0; + RtlZeroMemory(Info->ShortName, sizeof Info->ShortName); + Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber; + } + break; + default: + ASSERT(0); + return STATUS_INVALID_INFO_CLASS; + } + + if (0 != PrevDestBuf) + *(PULONG)PrevDestBuf = (ULONG)((PUINT8)DestBuf - DestBufBgn); + PrevDestBuf = DestBuf; + + *PDirectoryOffset = DirInfo->NextOffset; + *PDestLen = (ULONG)((PUINT8)DestBuf + BaseInfoLen + FileName.Length - DestBufBgn); + + if (ReturnSingleEntry) + break; + + DestBuf = (PVOID)((PUINT8)DestBuf + + FSP_FSCTL_ALIGN_UP(BaseInfoLen + FileName.Length, sizeof(LONGLONG))); + } + + NextDirInfo: + DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)); + } + } + except (EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS Result = GetExceptionCode(); + if (STATUS_INSUFFICIENT_RESOURCES == Result) + return Result; + return FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; + } + + *PDirInfo = DirInfo; + + return STATUS_SUCCESS; +#undef FILL_INFO +} + +static NTSTATUS FspFsvolQueryDirectoryCopyCache( + FSP_FILE_DESC *FileDesc, BOOLEAN ResetCache, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen) +{ + /* FileNode/FileDesc assumed acquired exclusive (Main or Full) */ + + PAGED_CODE(); + + FSP_FILE_NODE *FileNode = FileDesc->FileNode; + + if (ResetCache || FileDesc->DirInfo != FileNode->NonPaged->DirInfo) + FileDesc->DirInfoCacheHint = 0; /* reset the DirInfo hint if anything looks fishy! */ + + FileDesc->DirInfo = FileNode->NonPaged->DirInfo; + + NTSTATUS Result; + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + BOOLEAN CaseInsensitive = 0 == FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; + PUNICODE_STRING DirectoryPattern = &FileDesc->DirectoryPattern; + UINT64 DirectoryOffset = 0 == FileDesc->DirInfoCacheHint ? + FileDesc->DirectoryOffset : 0; + PUINT8 DirInfoBgn = (PUINT8)DirInfo; + PUINT8 DirInfoEnd = (PUINT8)DirInfo + DirInfoSize; + + DirInfo = (PVOID)(DirInfoBgn + FileDesc->DirInfoCacheHint); + DirInfoSize = (ULONG)(DirInfoEnd - (PUINT8)DirInfo); + + Result = FspFsvolQueryDirectoryCopy(DirectoryPattern, CaseInsensitive, &DirectoryOffset, + FileInformationClass, ReturnSingleEntry, + &DirInfo, DirInfoSize, + DestBuf, PDestLen); + + if (NT_SUCCESS(Result)) + { + FileDesc->DirectoryOffset = DirectoryOffset; + FileDesc->DirInfoCacheHint = (ULONG)((PUINT8)DirInfo - DirInfoBgn); + } + + return Result; +} + +static NTSTATUS FspFsvolQueryDirectoryCopyInPlace( + FSP_FILE_DESC *FileDesc, + FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, + FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize, + PVOID DestBuf, PULONG PDestLen) +{ + /* FileNode/FileDesc assumed acquired exclusive (Main or Full) */ + + PAGED_CODE(); + + NTSTATUS Result; + FSP_FILE_NODE *FileNode = FileDesc->FileNode; + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + BOOLEAN CaseInsensitive = 0 == FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; + PUNICODE_STRING DirectoryPattern = &FileDesc->DirectoryPattern; + UINT64 DirectoryOffset = FileDesc->DirectoryOffset; + + ASSERT(DirInfo == DestBuf); + ASSERT(sizeof(FSP_FSCTL_DIR_INFO) > sizeof(FILE_ID_BOTH_DIR_INFORMATION)); + + Result = FspFsvolQueryDirectoryCopy(DirectoryPattern, CaseInsensitive, &DirectoryOffset, + FileInformationClass, ReturnSingleEntry, + &DirInfo, DirInfoSize, + DestBuf, PDestLen); + + if (NT_SUCCESS(Result)) + FileDesc->DirectoryOffset = DirectoryOffset; + + return Result; +} + +static NTSTATUS FspFsvolQueryDirectoryRetry( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + BOOLEAN CanWait) +{ + PAGED_CODE(); + + NTSTATUS Result; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + BOOLEAN RestartScan = BooleanFlagOn(Irp->Flags, SL_RESTART_SCAN); + BOOLEAN IndexSpecified = BooleanFlagOn(Irp->Flags, SL_INDEX_SPECIFIED); + BOOLEAN ReturnSingleEntry = BooleanFlagOn(Irp->Flags, SL_RETURN_SINGLE_ENTRY); + FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; + PUNICODE_STRING FileName = IrpSp->Parameters.QueryDirectory.FileName; + ULONG FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; + PVOID Buffer = 0 != Irp->AssociatedIrp.SystemBuffer ? + Irp->AssociatedIrp.SystemBuffer : Irp->UserBuffer; + ULONG Length = IrpSp->Parameters.QueryDirectory.Length; + ULONG SystemBufferLength; + PVOID DirInfoBuffer; + ULONG DirInfoSize; + FSP_FSCTL_TRANSACT_REQ *Request; + BOOLEAN Success; + + ASSERT(FileNode == FileDesc->FileNode); + + /* try to acquire the FileNode exclusive; Full because we may need to send a Request */ + Success = DEBUGTEST(90, TRUE) && + FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireFull, CanWait); + if (!Success) + return FspWqRepostIrpWorkItem(Irp, FspFsvolQueryDirectoryRetry, 0); + + /* set the DirectoryPattern in the FileDesc */ + Result = FspFileDescResetDirectoryPattern(FileDesc, FileName, RestartScan); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + /* determine where to (re)start */ + if (IndexSpecified) + FileDesc->DirectoryOffset = OFFSET_FROM_FILE_INDEX(FileIndex); + else if (RestartScan) + FileDesc->DirectoryOffset = 0; + + /* see if the required information is still in the cache and valid! */ + if (FspFileNodeReferenceDirInfo(FileNode, &DirInfoBuffer, &DirInfoSize)) + { + SystemBufferLength = Length; + + Result = FspFsvolQueryDirectoryCopyCache(FileDesc, + IndexSpecified || RestartScan, + FileInformationClass, ReturnSingleEntry, + DirInfoBuffer, DirInfoSize, Buffer, &Length); + + FspFileNodeDereferenceDirInfo(DirInfoBuffer); + + if (!NT_SUCCESS(Result) || 0 != Length) + { + FspFileNodeRelease(FileNode, Full); + Irp->IoStatus.Information = Length; + return Result; + } + } + else + SystemBufferLength = 0 != FsvolDeviceExtension->VolumeParams.FileInfoTimeout ? + FspFsvolDeviceDirInfoCacheItemSizeMax : Length; + + FspFileNodeConvertExclusiveToShared(FileNode, Full); + + /* buffer the user buffer! */ + if (SystemBufferLength > FspFsvolDeviceDirInfoCacheItemSizeMax) + SystemBufferLength = FspFsvolDeviceDirInfoCacheItemSizeMax; + else if (SystemBufferLength < sizeof(FSP_FSCTL_DIR_INFO) + + FsvolDeviceExtension->VolumeParams.MaxComponentLength * sizeof(WCHAR)) + SystemBufferLength = sizeof(FSP_FSCTL_DIR_INFO) + + FsvolDeviceExtension->VolumeParams.MaxComponentLength * sizeof(WCHAR); + SystemBufferLength = FSP_FSCTL_ALIGN_UP(SystemBufferLength, PAGE_SIZE); + Result = FspBufferUserBuffer(Irp, Length, IoWriteAccess); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + /* create request */ + Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolQueryDirectoryRequestFini, &Request); + if (!NT_SUCCESS(Result)) + { + FspFileNodeRelease(FileNode, Full); + return Result; + } + + Request->Kind = FspFsctlTransactQueryDirectoryKind; + Request->Req.QueryDirectory.UserContext = FileNode->UserContext; + Request->Req.QueryDirectory.UserContext2 = FileDesc->UserContext2; + Request->Req.QueryDirectory.Offset = FileDesc->DirectoryOffset; + Request->Req.QueryDirectory.Length = SystemBufferLength; + + FspFileNodeSetOwner(FileNode, Full, Request); + FspIopRequestContext(Request, RequestFileNode) = FileNode; + + return FSP_STATUS_IOQ_POST; +} + static NTSTATUS FspFsvolQueryDirectory( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -86,273 +477,6 @@ static NTSTATUS FspFsvolQueryDirectory( return Result; } -static NTSTATUS FspFsvolQueryDirectoryRetry( - PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, - BOOLEAN CanWait) -{ - PAGED_CODE(); - - NTSTATUS Result; - PFILE_OBJECT FileObject = IrpSp->FileObject; - FSP_FILE_NODE *FileNode = FileObject->FsContext; - FSP_FILE_DESC *FileDesc = FileObject->FsContext2; - BOOLEAN RestartScan = BooleanFlagOn(Irp->Flags, SL_RESTART_SCAN); - BOOLEAN IndexSpecified = BooleanFlagOn(Irp->Flags, SL_INDEX_SPECIFIED); - BOOLEAN ReturnSingleEntry = BooleanFlagOn(Irp->Flags, SL_RETURN_SINGLE_ENTRY); - FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; - PUNICODE_STRING FileName = IrpSp->Parameters.QueryDirectory.FileName; - PVOID Buffer = 0 != Irp->AssociatedIrp.SystemBuffer ? - Irp->AssociatedIrp.SystemBuffer : Irp->UserBuffer; - ULONG Length = IrpSp->Parameters.QueryDirectory.Length; - PVOID DirInfoBuffer; - ULONG DirInfoSize; - FSP_FSCTL_TRANSACT_REQ *Request; - BOOLEAN InitialQuery; - BOOLEAN Success; - - ASSERT(FileNode == FileDesc->FileNode); - - /* try to acquire the FileNode exclusive; Full because we may need to send a Request */ - Success = DEBUGTEST(90, TRUE) && - FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireFull, CanWait); - if (!Success) - return FspWqRepostIrpWorkItem(Irp, FspFsvolQueryDirectoryRetry, 0); - - InitialQuery = 0 == FileDesc->DirectoryPattern.Buffer; - - /* set the DirectoryPattern in the FileDesc */ - Result = FspFileDescResetDirectoryPattern(FileDesc, FileName, RestartScan); - if (!NT_SUCCESS(Result)) - { - FspFileNodeRelease(FileNode, Full); - return Result; - } - - /* determine where to (re)start */ - if (IndexSpecified) - FileDesc->DirectoryOffset = (UINT64)IrpSp->Parameters.QueryDirectory.FileIndex << 32; - else if (RestartScan) - FileDesc->DirectoryOffset = 0; - - FspFileNodeConvertExclusiveToShared(FileNode, Full); - - /* see if the required information is still in the cache and valid! */ - if (FspFileNodeReferenceDirInfo(FileNode, &DirInfoBuffer, &DirInfoSize)) - { - FspFileNodeRelease(FileNode, Full); - - FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); - BOOLEAN CaseInsensitive = 0 == FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; - - Result = FspFsvolQueryDirectoryCopy(FileInformationClass, ReturnSingleEntry, - &FileDesc->DirectoryPattern, CaseInsensitive, - Buffer, &Length, DirInfoBuffer, DirInfoSize); - FspFileNodeDereferenceDirInfo(DirInfoBuffer); - - if (NT_SUCCESS(Result) && 0 == Length) - Result = InitialQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES; - - Irp->IoStatus.Information = Length; - return Result; - } - - /* buffer the user buffer! */ - Result = FspBufferUserBuffer(Irp, Length, IoWriteAccess); - if (!NT_SUCCESS(Result)) - { - FspFileNodeRelease(FileNode, Full); - return Result; - } - - /* create request */ - Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolQueryDirectoryRequestFini, &Request); - if (!NT_SUCCESS(Result)) - { - FspFileNodeRelease(FileNode, Full); - return Result; - } - - Request->Kind = FspFsctlTransactQueryDirectoryKind; - Request->Req.QueryDirectory.UserContext = FileNode->UserContext; - Request->Req.QueryDirectory.UserContext2 = FileDesc->UserContext2; - - FspFileNodeSetOwner(FileNode, Full, Request); - FspIopRequestContext(Request, RequestFileNode) = FileNode; - - return FSP_STATUS_IOQ_POST; -} - -static NTSTATUS FspFsvolQueryDirectoryCopy( - FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, - PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive, - PVOID DestBuf, PULONG PDestLen, - FSP_FSCTL_DIR_INFO *DirInfo, ULONG DirInfoSize) -{ -#define FILL_INFO(Info, DirInfo)\ - Info->NextEntryOffset = 0;\ - Info->FileIndex = DirInfo->NextOffset >> 32;\ - Info->CreationTime.QuadPart = DirInfo->FileInfo.CreationTime;\ - Info->LastAccessTime.QuadPart = DirInfo->FileInfo.LastAccessTime;\ - Info->LastWriteTime.QuadPart = DirInfo->FileInfo.LastWriteTime;\ - Info->ChangeTime.QuadPart = DirInfo->FileInfo.ChangeTime;\ - Info->EndOfFile.QuadPart = DirInfo->FileInfo.FileSize;\ - Info->AllocationSize.QuadPart = DirInfo->FileInfo.AllocationSize;\ - Info->FileAttributes = 0 != DirInfo->FileInfo.FileAttributes ?\ - DirInfo->FileInfo.FileAttributes : FILE_ATTRIBUTE_NORMAL;\ - Info->FileNameLength = FileNameLen;\ - RtlCopyMemory(Info->FileName, DirInfo->FileNameBuf, FileNameLen) - - PAGED_CODE(); - - BOOLEAN MatchAll = FspFileDescDirectoryPatternMatchAll == DirectoryPattern->Buffer; - PVOID PrevDestBuf = 0; - PUINT8 DestBufBgn = (PUINT8)DestBuf; - PUINT8 DestBufEnd = (PUINT8)DestBuf + *PDestLen; - PUINT8 DirInfoEnd = (PUINT8)DirInfo + DirInfoSize; - ULONG BaseInfoLen, FileNameLen; - UNICODE_STRING FileName; - - *PDestLen = 0; - - switch (FileInformationClass) - { - case FileDirectoryInformation: - BaseInfoLen = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName); - break; - case FileFullDirectoryInformation: - BaseInfoLen = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName); - break; - case FileIdFullDirectoryInformation: - BaseInfoLen = FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION, FileName); - break; - case FileNamesInformation: - BaseInfoLen = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName); - break; - case FileBothDirectoryInformation: - BaseInfoLen = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName); - break; - case FileIdBothDirectoryInformation: - BaseInfoLen = FIELD_OFFSET(FILE_ID_BOTH_DIR_INFORMATION, FileName); - break; - default: - return STATUS_INVALID_INFO_CLASS; - } - - try - { - for (;;) - { - if ((PUINT8)DirInfo + sizeof(DirInfo->Size) > DirInfoEnd || - sizeof(FSP_FSCTL_DIR_INFO) > DirInfo->Size) - break; - - FileNameLen = DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO); - FileName.Length = FileName.MaximumLength = (USHORT)FileNameLen; - FileName.Buffer = DirInfo->FileNameBuf; - - if (MatchAll || FsRtlIsNameInExpression(DirectoryPattern, &FileName, CaseInsensitive, 0)) - { - if ((PUINT8)DestBuf + FSP_FSCTL_ALIGN_UP(BaseInfoLen + FileNameLen, sizeof(LONGLONG)) > - DestBufEnd) - { - if (DestBufBgn == DestBuf) - { - *PDestLen = BaseInfoLen + FileNameLen; - return STATUS_BUFFER_OVERFLOW; - } - break; - } - - if (0 != PrevDestBuf) - *(PULONG)PrevDestBuf = (ULONG)((PUINT8)DestBuf - DestBufBgn); - PrevDestBuf = DestBuf; - - switch (FileInformationClass) - { - case FileDirectoryInformation: - { - FILE_DIRECTORY_INFORMATION *Info = DestBuf; - - FILL_INFO(Info, DirInfo); - } - break; - case FileFullDirectoryInformation: - { - FILE_FULL_DIR_INFORMATION *Info = DestBuf; - - FILL_INFO(Info, DirInfo); - Info->EaSize = 0; - } - break; - case FileIdFullDirectoryInformation: - { - FILE_ID_FULL_DIR_INFORMATION *Info = DestBuf; - - FILL_INFO(Info, DirInfo); - Info->EaSize = 0; - Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber; - } - break; - case FileNamesInformation: - { - FILE_NAMES_INFORMATION *Info = DestBuf; - - Info->NextEntryOffset = 0; - Info->FileIndex = DirInfo->NextOffset >> 32; - Info->FileNameLength = FileNameLen; - RtlCopyMemory(Info->FileName, DirInfo->FileNameBuf, FileNameLen); - } - break; - case FileBothDirectoryInformation: - { - FILE_BOTH_DIR_INFORMATION *Info = DestBuf; - - FILL_INFO(Info, DirInfo); - Info->EaSize = 0; - Info->ShortNameLength = 0; - RtlZeroMemory(Info->ShortName, sizeof Info->ShortName); - } - break; - case FileIdBothDirectoryInformation: - { - FILE_ID_BOTH_DIR_INFORMATION *Info = DestBuf; - - FILL_INFO(Info, DirInfo); - Info->EaSize = 0; - Info->ShortNameLength = 0; - RtlZeroMemory(Info->ShortName, sizeof Info->ShortName); - Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber; - } - break; - default: - ASSERT(0); - return STATUS_INVALID_INFO_CLASS; - } - - *PDestLen = (ULONG)((PUINT8)DestBuf + BaseInfoLen + FileNameLen - DestBufBgn); - - if (ReturnSingleEntry) - break; - - DestBuf = (PVOID)((PUINT8)DestBuf + - FSP_FSCTL_ALIGN_UP(BaseInfoLen + FileNameLen, sizeof(LONGLONG))); - } - - DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)); - } - } - except (EXCEPTION_EXECUTE_HANDLER) - { - NTSTATUS Result = GetExceptionCode(); - if (STATUS_INSUFFICIENT_RESOURCES == Result) - return Result; - return FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; - } - - return STATUS_SUCCESS; -#undef FILL_INFO -} - static NTSTATUS FspFsvolNotifyChangeDirectory( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -385,6 +509,40 @@ NTSTATUS FspFsvolDirectoryControlPrepare( { PAGED_CODE(); + NTSTATUS Result; + PMDL Mdl = 0; + PVOID Address; + PEPROCESS Process; + + Mdl = IoAllocateMdl( + Irp->AssociatedIrp.SystemBuffer, + Request->Req.QueryDirectory.Length, + FALSE, FALSE, 0); + if (0 == Mdl) + return STATUS_INSUFFICIENT_RESOURCES; + + MmBuildMdlForNonPagedPool(Mdl); + + /* map the MDL into user-mode */ + Result = FspMapLockedPagesInUserMode(Mdl, &Address); + if (!NT_SUCCESS(Result)) + { + if (0 != Mdl) + IoFreeMdl(Mdl); + + return Result; + } + + /* get a pointer to the current process so that we can unmap the address later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestMdl) = Mdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + return STATUS_SUCCESS; } @@ -393,7 +551,85 @@ NTSTATUS FspFsvolDirectoryControlComplete( { FSP_ENTER_IOC(PAGED_CODE()); - FSP_LEAVE_IOC("%s", ""); + if (!NT_SUCCESS(Response->IoStatus.Status)) + { + Irp->IoStatus.Information = 0; + Result = Response->IoStatus.Status; + FSP_RETURN(); + } + + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + PFILE_OBJECT FileObject = IrpSp->FileObject; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + BOOLEAN ReturnSingleEntry = BooleanFlagOn(Irp->Flags, SL_RETURN_SINGLE_ENTRY); + FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG Length = IrpSp->Parameters.QueryDirectory.Length; + ULONG DirInfoChangeNumber; + PVOID DirInfoBuffer; + ULONG DirInfoSize; + BOOLEAN Success; + + ASSERT(FileNode == FileDesc->FileNode); + + if (0 == Response->IoStatus.Information) + { + Result = 0 == Request->Req.QueryDirectory.Offset ? + STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES; + FSP_RETURN(); + } + + if (FspFsctlTransactQueryDirectoryKind == Request->Kind) + { + DirInfoChangeNumber = FileNode->DirInfoChangeNumber; + Request->Kind = FspFsctlTransactReservedKind; + FspIopResetRequest(Request, 0); + FspIopRequestContext(Request, RequestDirInfoChangeNumber) = (PVOID)DirInfoChangeNumber; + } + else + DirInfoChangeNumber = + (ULONG)(UINT_PTR)FspIopRequestContext(Request, RequestDirInfoChangeNumber); + + Success = DEBUGTEST(90, TRUE) && FspFileNodeTryAcquireExclusive(FileNode, Main); + if (!Success) + { + FspIopRetryCompleteIrp(Irp, Response, &Result); + FSP_RETURN(); + } + + Success = 0 == Request->Req.QueryDirectory.Offset && + FspFileNodeTrySetDirInfo(FileNode, + Irp->AssociatedIrp.SystemBuffer, + (ULONG)Irp->IoStatus.Information, + DirInfoChangeNumber); + if (Success && FspFileNodeReferenceDirInfo(FileNode, &DirInfoBuffer, &DirInfoSize)) + { + Result = FspFsvolQueryDirectoryCopyCache(FileDesc, + TRUE, + FileInformationClass, ReturnSingleEntry, + DirInfoBuffer, DirInfoSize, Buffer, &Length); + + FspFileNodeDereferenceDirInfo(DirInfoBuffer); + } + else + { + DirInfoBuffer = Irp->AssociatedIrp.SystemBuffer; + DirInfoSize = (ULONG)Irp->IoStatus.Information; + Result = FspFsvolQueryDirectoryCopyInPlace(FileDesc, + FileInformationClass, ReturnSingleEntry, + DirInfoBuffer, DirInfoSize, Buffer, &Length); + } + + FspFileNodeRelease(FileNode, Main); + + Irp->IoStatus.Information = Length; + + FSP_LEAVE_IOC("%s%sFileObject=%p", + IRP_MN_QUERY_DIRECTORY == IrpSp->MinorFunction ? + FileInformationClassSym(IrpSp->Parameters.QueryDirectory.FileInformationClass) : "", + IRP_MN_QUERY_DIRECTORY == IrpSp->MinorFunction ? ", " : "", + IrpSp->FileObject); } static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]) @@ -401,6 +637,29 @@ static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P PAGED_CODE(); FSP_FILE_NODE *FileNode = Context[RequestFileNode]; + PMDL Mdl = Context[RequestMdl]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; + + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); + MmUnmapLockedPages(Address, Mdl); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } + + if (0 != Mdl) + IoFreeMdl(Mdl); if (0 != FileNode) FspFileNodeReleaseOwner(FileNode, Full, Request); @@ -419,5 +678,9 @@ NTSTATUS FspDirectoryControl( FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST); } - FSP_LEAVE_MJ("%s", ""); + FSP_LEAVE_MJ("%s%sFileObject=%p", + IRP_MN_QUERY_DIRECTORY == IrpSp->MinorFunction ? + FileInformationClassSym(IrpSp->Parameters.QueryDirectory.FileInformationClass) : "", + IRP_MN_QUERY_DIRECTORY == IrpSp->MinorFunction ? ", " : "", + IrpSp->FileObject); } diff --git a/src/sys/driver.h b/src/sys/driver.h index fe294dcb..3c50c727 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -836,6 +836,7 @@ typedef struct BOOLEAN DeleteOnClose; UNICODE_STRING DirectoryPattern; UINT64 DirectoryOffset; + UINT64 DirInfo; ULONG DirInfoCacheHint; } FSP_FILE_DESC; NTSTATUS FspFileNodeCreate(PDEVICE_OBJECT DeviceObject, diff --git a/src/sys/util.c b/src/sys/util.c index 66ad70c3..ece0dd2e 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -121,6 +121,8 @@ PVOID FspAllocateIrpMustSucceed(CCHAR StackSize) BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams) { + /* this does NOT check if the Path contains invalid file name chars (*, ?, etc.) */ + PAGED_CODE(); if (0 != Path->Length % sizeof(WCHAR))