/* * COPYRIGHT: GNU GENERAL PUBLIC LICENSE VERSION 2 * PROJECT: ReiserFs file system driver for Windows NT/2000/XP/Vista. * FILE: dirctl.c * PURPOSE: * PROGRAMMER: Mark Piper, Matt Wu, Bo Brantén. * HOMEPAGE: * UPDATE HISTORY: */ /* INCLUDES *****************************************************************/ #include "rfsd.h" /* GLOBALS ***************************************************************/ extern PRFSD_GLOBAL RfsdGlobal; /* DEFINITIONS *************************************************************/ NTSTATUS RfsdDirectoryCallback( ULONG BlockNumber, PVOID pContext); typedef struct _RFSD_CALLBACK_CONTEXT { PRFSD_VCB Vcb; PRFSD_CCB Ccb; PRFSD_KEY_IN_MEMORY pDirectoryKey; ULONG idxStartingDentry; // The dentry at which the callback should beging triggering output to the Buffer ULONG idxCurrentDentry; // The current dentry (relative to entire set of dentrys, across all spans) // These parameters are forwarded to ProcessDirectoryEntry FILE_INFORMATION_CLASS FileInformationClass; // [s] PVOID Buffer; // [s] ULONG BufferLength; // [s] BOOLEAN ReturnSingleEntry; // [s] PULONG pUsedLength; PVOID pPreviousEntry; } RFSD_CALLBACK_CONTEXT, *PRFSD_CALLBACK_CONTEXT; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, RfsdGetInfoLength) #pragma alloc_text(PAGE, RfsdProcessDirEntry) #pragma alloc_text(PAGE, RfsdQueryDirectory) #pragma alloc_text(PAGE, RfsdNotifyChangeDirectory) #pragma alloc_text(PAGE, RfsdDirectoryControl) #pragma alloc_text(PAGE, RfsdIsDirectoryEmpty) #pragma alloc_text(PAGE, RfsdDirectoryCallback) #endif ULONG RfsdGetInfoLength(IN FILE_INFORMATION_CLASS FileInformationClass) { PAGED_CODE(); switch (FileInformationClass) { case FileDirectoryInformation: return sizeof(FILE_DIRECTORY_INFORMATION); break; case FileFullDirectoryInformation: return sizeof(FILE_FULL_DIR_INFORMATION); break; case FileBothDirectoryInformation: return sizeof(FILE_BOTH_DIR_INFORMATION); break; case FileNamesInformation: return sizeof(FILE_NAMES_INFORMATION); break; default: break; } return 0; } ULONG // Returns 0 on error, or InfoLength + NameLength (the amount of the buffer used for the entry given) RfsdProcessDirEntry( IN PRFSD_VCB Vcb, IN FILE_INFORMATION_CLASS FileInformationClass, // Identifier indicating the type of file information this function should report IN __u32 Key_ParentDirectoryID, IN __u32 Key_ObjectID, IN PVOID Buffer, // The user's buffer, as obtained from the IRP context (it is already gauranteed to be valid) IN ULONG UsedLength, // Length of Buffer used so far IN ULONG Length, // Length of Buffer remaining, beyond UsedLength IN ULONG FileIndex, // Byte offset of the dentry?? (This will just be placed into the file info structure of the same name) IN PUNICODE_STRING pName, // Filled unicode equivalent of the name (as pulled out from the dentry) IN BOOLEAN Single, // Whether or not QueryDirectory is only supposed to return a single entry IN PVOID pPreviousEntry ) // A pointer to the previous dir entry in Buffer, which will be linked into the newly added entry if one is created { RFSD_INODE inode; PFILE_DIRECTORY_INFORMATION FDI; PFILE_FULL_DIR_INFORMATION FFI; PFILE_BOTH_DIR_INFORMATION FBI; PFILE_NAMES_INFORMATION FNI; ULONG InfoLength = 0; ULONG NameLength = 0; ULONG dwBytes = 0; LONGLONG FileSize; LONGLONG AllocationSize; PAGED_CODE(); // Calculate the size of the entry NameLength = pName->Length; InfoLength = RfsdGetInfoLength(FileInformationClass); if (!InfoLength || InfoLength + NameLength - sizeof(WCHAR) > Length) { RfsdPrint((DBG_INFO, "RfsdPricessDirEntry: Buffer is not enough.\n")); return 0; } // Given the incoming key for this dentry, load the corresponding stat data. { RFSD_KEY_IN_MEMORY key; key.k_dir_id = Key_ParentDirectoryID; key.k_objectid = Key_ObjectID; if(!RfsdLoadInode(Vcb, &key, &inode)) { RfsdPrint((DBG_ERROR, "RfsdPricessDirEntry: Loading stat data %xh, %xh error.\n", Key_ParentDirectoryID, Key_ObjectID)); DbgBreak(); return 0; } } FileSize = (LONGLONG) inode.i_size; AllocationSize = CEILING_ALIGNED(FileSize, (ULONGLONG)Vcb->BlockSize); // TODO: THIS ISN'T QUITE RIGHT // Link the previous entry into this entry if (pPreviousEntry) { // NOTE: All entries begin with NextEntryOffset, so it doesn't matter what type I cast to. ((PFILE_NAMES_INFORMATION) (pPreviousEntry))->NextEntryOffset = (ULONG) ((PUCHAR) Buffer + UsedLength - (PUCHAR) (pPreviousEntry)); } switch(FileInformationClass) { case FileDirectoryInformation: FDI = (PFILE_DIRECTORY_INFORMATION) ((PUCHAR)Buffer + UsedLength); FDI->NextEntryOffset = 0; FDI->FileIndex = FileIndex; FDI->CreationTime = RfsdSysTime(inode.i_ctime); FDI->LastAccessTime = RfsdSysTime(inode.i_atime); FDI->LastWriteTime = RfsdSysTime(inode.i_mtime); FDI->ChangeTime = RfsdSysTime(inode.i_mtime); FDI->EndOfFile.QuadPart = FileSize; FDI->AllocationSize.QuadPart = AllocationSize; FDI->FileAttributes = FILE_ATTRIBUTE_NORMAL; if (FlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) { SetFlag(FDI->FileAttributes, FILE_ATTRIBUTE_READONLY); } if (S_ISDIR(inode.i_mode)) FDI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; FDI->FileNameLength = NameLength; RtlCopyMemory(FDI->FileName, pName->Buffer, NameLength); dwBytes = InfoLength + NameLength - sizeof(WCHAR); break; case FileFullDirectoryInformation: FFI = (PFILE_FULL_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength); FFI->NextEntryOffset = 0; FFI->FileIndex = FileIndex; FFI->CreationTime = RfsdSysTime(inode.i_ctime); FFI->LastAccessTime = RfsdSysTime(inode.i_atime); FFI->LastWriteTime = RfsdSysTime(inode.i_mtime); FFI->ChangeTime = RfsdSysTime(inode.i_mtime); FFI->EndOfFile.QuadPart = FileSize; FFI->AllocationSize.QuadPart = AllocationSize; FFI->FileAttributes = FILE_ATTRIBUTE_NORMAL; if (IsFlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) { SetFlag(FFI->FileAttributes, FILE_ATTRIBUTE_READONLY); } if (S_ISDIR(inode.i_mode)) FFI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; FFI->FileNameLength = NameLength; RtlCopyMemory(FFI->FileName, pName->Buffer, NameLength); dwBytes = InfoLength + NameLength - sizeof(WCHAR); break; case FileBothDirectoryInformation: FBI = (PFILE_BOTH_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength); FBI->NextEntryOffset = 0; FBI->CreationTime = RfsdSysTime(inode.i_ctime); FBI->LastAccessTime = RfsdSysTime(inode.i_atime); FBI->LastWriteTime = RfsdSysTime(inode.i_mtime); FBI->ChangeTime = RfsdSysTime(inode.i_mtime); FBI->FileIndex = FileIndex; FBI->EndOfFile.QuadPart = FileSize; FBI->AllocationSize.QuadPart = AllocationSize; FBI->FileAttributes = FILE_ATTRIBUTE_NORMAL; if (FlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) { SetFlag(FBI->FileAttributes, FILE_ATTRIBUTE_READONLY); } if (S_ISDIR(inode.i_mode)) FBI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; FBI->FileNameLength = NameLength; RtlCopyMemory(FBI->FileName, pName->Buffer, NameLength); dwBytes = InfoLength + NameLength - sizeof(WCHAR); break; case FileNamesInformation: FNI = (PFILE_NAMES_INFORMATION) ((PUCHAR)Buffer + UsedLength); FNI->NextEntryOffset = 0; FNI->FileNameLength = NameLength; RtlCopyMemory(FNI->FileName, pName->Buffer, NameLength); dwBytes = InfoLength + NameLength - sizeof(WCHAR); break; default: break; } return dwBytes; } /** caller suplies a ptr to a file obj for an open target dir, a search pattern to use when listing, and a spec of the info requested. FSD expected to search and return matching info [503] The Fcb->RfsdMcb->Key will determine which directory to list the contents of. */ __drv_mustHoldCriticalRegion NTSTATUS RfsdQueryDirectory (IN PRFSD_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status = STATUS_UNSUCCESSFUL; PRFSD_VCB Vcb; PFILE_OBJECT FileObject; PRFSD_FCB Fcb = 0; PRFSD_CCB Ccb; PIRP Irp; PIO_STACK_LOCATION IoStackLocation; FILE_INFORMATION_CLASS FileInformationClass; ULONG Length; PUNICODE_STRING FileName; ULONG FileIndex; BOOLEAN RestartScan; BOOLEAN ReturnSingleEntry; BOOLEAN IndexSpecified; PUCHAR Buffer; BOOLEAN FirstQuery; PRFSD_KEY_IN_MEMORY pQueryKey; // The key of the directory item that is being retrieved BOOLEAN FcbResourceAcquired = FALSE; ULONG UsedLength = 0; PAGED_CODE(); _SEH2_TRY { ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == RFSDICX) && (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT))); DeviceObject = IrpContext->DeviceObject; // // This request is not allowed on the main device object // if (DeviceObject == RfsdGlobal->DeviceObject) { Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == RFSDVCB) && (Vcb->Identifier.Size == sizeof(RFSD_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PRFSD_FCB) FileObject->FsContext; pQueryKey = &(Fcb->RfsdMcb->Key); ASSERT(Fcb); KdPrint(("QueryDirectory on Key {%x,%x,%x,%x}\n", pQueryKey->k_dir_id, pQueryKey->k_objectid, pQueryKey->k_offset, pQueryKey->k_type)); // // This request is not allowed on volumes // if (Fcb->Identifier.Type == RFSDVCB) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == RFSDFCB) && (Fcb->Identifier.Size == sizeof(RFSD_FCB))); if (!IsDirectory(Fcb)) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } Ccb = (PRFSD_CCB) FileObject->FsContext2; ASSERT(Ccb); ASSERT((Ccb->Identifier.Type == RFSDCCB) && (Ccb->Identifier.Size == sizeof(RFSD_CCB))); Irp = IrpContext->Irp; IoStackLocation = IoGetCurrentIrpStackLocation(Irp); #ifndef _GNU_NTIFS_ FileInformationClass = IoStackLocation->Parameters.QueryDirectory.FileInformationClass; Length = IoStackLocation->Parameters.QueryDirectory.Length; FileName = IoStackLocation->Parameters.QueryDirectory.FileName; FileIndex = IoStackLocation->Parameters.QueryDirectory.FileIndex; RestartScan = FlagOn(IoStackLocation->Flags, SL_RESTART_SCAN); ReturnSingleEntry = FlagOn(IoStackLocation->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = FlagOn(IoStackLocation->Flags, SL_INDEX_SPECIFIED); #else // _GNU_NTIFS_ FileInformationClass = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileInformationClass; Length = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.Length; FileName = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileName; FileIndex = ((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Parameters.QueryDirectory.FileIndex; RestartScan = FlagOn(((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Flags, SL_RESTART_SCAN); ReturnSingleEntry = FlagOn(((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Flags, SL_RETURN_SINGLE_ENTRY); IndexSpecified = FlagOn(((PEXTENDED_IO_STACK_LOCATION) IoStackLocation)->Flags, SL_INDEX_SPECIFIED); #endif // _GNU_NTIFS_ /* if (!Irp->MdlAddress && Irp->UserBuffer) { ProbeForWrite(Irp->UserBuffer, Length, 1); } */ // Check that the user's buffer for the output is valid.. Buffer = RfsdGetUserBuffer(Irp); if (Buffer == NULL) { DbgBreak(); Status = STATUS_INVALID_USER_BUFFER; _SEH2_LEAVE; } // Check if we have a synchronous or asynchronous request... if (!IrpContext->IsSynchronous) { Status = STATUS_PENDING; _SEH2_LEAVE; } if (!ExAcquireResourceSharedLite( &Fcb->MainResource, IrpContext->IsSynchronous )) { Status = STATUS_PENDING; _SEH2_LEAVE; } FcbResourceAcquired = TRUE; if (FileName != NULL) { // The caller provided a FileName to search on... if (Ccb->DirectorySearchPattern.Buffer != NULL) { FirstQuery = FALSE; } else { FirstQuery = TRUE; // Set up the DirectorySearchPattern to simply be an uppercase copy of the FileName. Ccb->DirectorySearchPattern.Length = Ccb->DirectorySearchPattern.MaximumLength = FileName->Length; Ccb->DirectorySearchPattern.Buffer = ExAllocatePoolWithTag(PagedPool, FileName->Length, RFSD_POOL_TAG); if (Ccb->DirectorySearchPattern.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } Status = RtlUpcaseUnicodeString( &(Ccb->DirectorySearchPattern), FileName, FALSE); if (!NT_SUCCESS(Status)) _SEH2_LEAVE; } } else if (Ccb->DirectorySearchPattern.Buffer != NULL) { FirstQuery = FALSE; FileName = &Ccb->DirectorySearchPattern; } else { // (The FileName and CCB's DirectorySearchPattern were null) FirstQuery = TRUE; Ccb->DirectorySearchPattern.Length = Ccb->DirectorySearchPattern.MaximumLength = 2; Ccb->DirectorySearchPattern.Buffer = ExAllocatePoolWithTag(PagedPool, 2, RFSD_POOL_TAG); if (Ccb->DirectorySearchPattern.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlCopyMemory( Ccb->DirectorySearchPattern.Buffer, L"*\0", 2); } if (!IndexSpecified) { if (RestartScan || FirstQuery) { FileIndex = Fcb->RfsdMcb->DeOffset = 0; } else { // If we are not starting/restaring a scan, then we must have already started. // Retrieve the byte offset (in the directory . file) where we left off. FileIndex = Ccb->CurrentByteOffset; } } RtlZeroMemory(Buffer, Length); // Leave if a previous query has already read the entire contents of the directory if (Fcb->Inode->i_size <= FileIndex) { Status = STATUS_NO_MORE_FILES; _SEH2_LEAVE; } //////// // Construct a context for the call, and call to parse the entire file system tree. // A callback will be triggered on any direntry span belonging to DirectoryKey. // This callback will fill the requested section of the user buffer. { ULONG CurrentFileIndex; RFSD_CALLBACK_CONTEXT CallbackContext; CallbackContext.Vcb = Vcb; CallbackContext.Ccb = Ccb; CallbackContext.idxStartingDentry = FileIndex / sizeof(RFSD_DENTRY_HEAD); CallbackContext.idxCurrentDentry = 0; CallbackContext.pDirectoryKey = pQueryKey; CallbackContext.FileInformationClass = FileInformationClass; CallbackContext.Buffer = Buffer; CallbackContext.BufferLength = Length; CallbackContext.ReturnSingleEntry = ReturnSingleEntry; CallbackContext.pUsedLength = &UsedLength; CallbackContext.pPreviousEntry = NULL; RfsdPrint((DBG_TRACE, "Calculated idxCurrentDentry to be %i\n", CallbackContext.idxStartingDentry)); RfsdParseFilesystemTree(Vcb, pQueryKey, Vcb->SuperBlock->s_root_block, &RfsdDirectoryCallback, &CallbackContext); } //================================================================ if (!UsedLength) { // No amount of the dsetination buffer has been used (meaning there were no results)... if (FirstQuery) { Status = STATUS_NO_SUCH_FILE; } else { Status = STATUS_NO_MORE_FILES; } } else { Status = STATUS_SUCCESS; } } _SEH2_FINALLY { if (FcbResourceAcquired) { ExReleaseResourceForThreadLite( &Fcb->MainResource, ExGetCurrentResourceThread() ); } if (!IrpContext->ExceptionInProgress) { if (Status == STATUS_PENDING) { Status = RfsdLockUserBuffer( IrpContext->Irp, Length, IoWriteAccess ); if (NT_SUCCESS(Status)) { Status = RfsdQueueRequest(IrpContext); } else { RfsdCompleteIrpContext(IrpContext, Status); } } else { IrpContext->Irp->IoStatus.Information = UsedLength; RfsdCompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } __drv_mustHoldCriticalRegion NTSTATUS RfsdNotifyChangeDirectory ( IN PRFSD_IRP_CONTEXT IrpContext ) { PDEVICE_OBJECT DeviceObject; BOOLEAN CompleteRequest; NTSTATUS Status = STATUS_UNSUCCESSFUL; PRFSD_VCB Vcb; PFILE_OBJECT FileObject; PRFSD_FCB Fcb = 0; PIRP Irp; PIO_STACK_LOCATION IrpSp; ULONG CompletionFilter; BOOLEAN WatchTree; BOOLEAN bFcbAcquired = FALSE; PUNICODE_STRING FullName; PAGED_CODE(); _SEH2_TRY { ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == RFSDICX) && (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT))); // // Always set the wait flag in the Irp context for the original request. // SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); DeviceObject = IrpContext->DeviceObject; if (DeviceObject == RfsdGlobal->DeviceObject) { CompleteRequest = TRUE; Status = STATUS_INVALID_DEVICE_REQUEST; _SEH2_LEAVE; } Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb != NULL); ASSERT((Vcb->Identifier.Type == RFSDVCB) && (Vcb->Identifier.Size == sizeof(RFSD_VCB))); ASSERT(IsMounted(Vcb)); FileObject = IrpContext->FileObject; Fcb = (PRFSD_FCB) FileObject->FsContext; ASSERT(Fcb); if (Fcb->Identifier.Type == RFSDVCB) { DbgBreak(); CompleteRequest = TRUE; Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } ASSERT((Fcb->Identifier.Type == RFSDFCB) && (Fcb->Identifier.Size == sizeof(RFSD_FCB))); if (!IsDirectory(Fcb)) { //- DbgBreak(); // NOTE: Windows (at least I've noticed it with the image previewer), will send this request oftentimes on a file! CompleteRequest = TRUE; Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if (ExAcquireResourceExclusiveLite( &Fcb->MainResource, TRUE )) { bFcbAcquired = TRUE; } else { Status = STATUS_PENDING; _SEH2_LEAVE; } Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); #ifndef _GNU_NTIFS_ CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter; #else // _GNU_NTIFS_ CompletionFilter = ((PEXTENDED_IO_STACK_LOCATION) IrpSp)->Parameters.NotifyDirectory.CompletionFilter; #endif // _GNU_NTIFS_ WatchTree = IsFlagOn(IrpSp->Flags, SL_WATCH_TREE); if (FlagOn(Fcb->Flags, FCB_DELETE_PENDING)) { Status = STATUS_DELETE_PENDING; _SEH2_LEAVE; } FullName = &Fcb->LongName; if (FullName->Buffer == NULL) { if (!RfsdGetFullFileName(Fcb->RfsdMcb, FullName)) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } } FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, &Vcb->NotifyList, FileObject->FsContext2, (PSTRING)FullName, WatchTree, FALSE, CompletionFilter, Irp, NULL, NULL ); CompleteRequest = FALSE; Status = STATUS_PENDING; /* Currently the driver is read-only but here is an example on how to use the FsRtl-functions to report a change: ANSI_STRING TestString; USHORT FileNamePartLength; RtlInitAnsiString(&TestString, "\\ntifs.h"); FileNamePartLength = 7; FsRtlNotifyReportChange( Vcb->NotifySync, // PNOTIFY_SYNC NotifySync &Vcb->NotifyList, // PLIST_ENTRY NotifyList &TestString, // PSTRING FullTargetName &FileNamePartLength, // PUSHORT FileNamePartLength FILE_NOTIFY_CHANGE_NAME // ULONG FilterMatch ); or ANSI_STRING TestString; RtlInitAnsiString(&TestString, "\\ntifs.h"); FsRtlNotifyFullReportChange( Vcb->NotifySync, // PNOTIFY_SYNC NotifySync &Vcb->NotifyList, // PLIST_ENTRY NotifyList &TestString, // PSTRING FullTargetName 1, // USHORT TargetNameOffset NULL, // PSTRING StreamName OPTIONAL NULL, // PSTRING NormalizedParentName OPTIONAL FILE_NOTIFY_CHANGE_NAME, // ULONG FilterMatch 0, // ULONG Action NULL // PVOID TargetContext ); */ } _SEH2_FINALLY { if (bFcbAcquired) { ExReleaseResourceForThreadLite( &Fcb->MainResource, ExGetCurrentResourceThread()); } if (!IrpContext->ExceptionInProgress) { if (!CompleteRequest) { IrpContext->Irp = NULL; } RfsdCompleteIrpContext(IrpContext, Status); } } _SEH2_END; return Status; } VOID RfsdNotifyReportChange ( IN PRFSD_IRP_CONTEXT IrpContext, IN PRFSD_VCB Vcb, IN PRFSD_FCB Fcb, IN ULONG Filter, IN ULONG Action ) { PUNICODE_STRING FullName; USHORT Offset; FullName = &Fcb->LongName; // ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); if (FullName->Buffer == NULL) { if (!RfsdGetFullFileName(Fcb->RfsdMcb, FullName)) { /*Status = STATUS_INSUFFICIENT_RESOURCES;*/ return; } } Offset = (USHORT) ( FullName->Length - Fcb->RfsdMcb->ShortName.Length); FsRtlNotifyFullReportChange( Vcb->NotifySync, &(Vcb->NotifyList), (PSTRING) (FullName), (USHORT) Offset, (PSTRING)NULL, (PSTRING) NULL, (ULONG) Filter, (ULONG) Action, (PVOID) NULL ); // ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); } __drv_mustHoldCriticalRegion NTSTATUS RfsdDirectoryControl (IN PRFSD_IRP_CONTEXT IrpContext) { NTSTATUS Status; PAGED_CODE(); ASSERT(IrpContext); ASSERT((IrpContext->Identifier.Type == RFSDICX) && (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT))); switch (IrpContext->MinorFunction) { case IRP_MN_QUERY_DIRECTORY: Status = RfsdQueryDirectory(IrpContext); break; case IRP_MN_NOTIFY_CHANGE_DIRECTORY: Status = RfsdNotifyChangeDirectory(IrpContext); break; default: Status = STATUS_INVALID_DEVICE_REQUEST; RfsdCompleteIrpContext(IrpContext, Status); } return Status; } #if !RFSD_READ_ONLY BOOLEAN RfsdIsDirectoryEmpty ( PRFSD_VCB Vcb, PRFSD_FCB Dcb ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; //PRFSD_DIR_ENTRY2 pTarget = NULL; ULONG dwBytes = 0; ULONG dwRet; BOOLEAN bRet = FALSE; PAGED_CODE(); #if 0 if (!IsFlagOn(Dcb->RfsdMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } _SEH2_TRY { pTarget = (PRFSD_DIR_ENTRY2) ExAllocatePoolWithTag(PagedPool, RFSD_DIR_REC_LEN(RFSD_NAME_LEN), RFSD_POOL_TAG); if (!pTarget) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } dwBytes = 0; bRet = TRUE; while ((LONGLONG)dwBytes < Dcb->Header.AllocationSize.QuadPart) { RtlZeroMemory(pTarget, RFSD_DIR_REC_LEN(RFSD_NAME_LEN)); Status = RfsdReadInode( NULL, Vcb, &(Dcb->RfsdMcb->Key), Dcb->Inode, dwBytes, (PVOID)pTarget, RFSD_DIR_REC_LEN(RFSD_NAME_LEN), &dwRet); if (!NT_SUCCESS(Status)) { RfsdPrint((DBG_ERROR, "RfsdRemoveEntry: Reading Directory Content error.\n")); bRet = FALSE; _SEH2_LEAVE; } if (pTarget->inode) { if (pTarget->name_len == 1 && pTarget->name[0] == '.') { } else if (pTarget->name_len == 2 && pTarget->name[0] == '.' && pTarget->name[1] == '.') { } else { bRet = FALSE; break; } } else { break; } dwBytes += pTarget->rec_len; } } _SEH2_FINALLY { if (pTarget != NULL) { ExFreePool(pTarget); } } _SEH2_END; #endif // 0 return bRet; } #endif // !RFSD_READ_ONLY /** This callback is triggered when the FS tree parser hits a leaf node that may contain a directory item. The function then reads each dentry in the item, and is reponsible for sending them into to ProcessDirEntry. The callback is doing work on behalf of QueryDir -- the context given is from there. */ // NOTE: This signature has to be changed here, at the top decleration, and in the RFSD_CALLBACK macro definition NTSTATUS RfsdDirectoryCallback( ULONG BlockNumber, PVOID pContext) { PRFSD_CALLBACK_CONTEXT pCallbackContext = (PRFSD_CALLBACK_CONTEXT) pContext; RFSD_KEY_IN_MEMORY DirectoryKey; PUCHAR pBlockBuffer = NULL; PRFSD_ITEM_HEAD pDirectoryItemHeader = NULL; PUCHAR pDirectoryItemBuffer = NULL; NTSTATUS Status; UNICODE_STRING InodeFileName; PAGED_CODE(); InodeFileName.Buffer = NULL; RfsdPrint((DBG_FUNC, /*__FUNCTION__*/ " invoked on block %i\n", BlockNumber)); // Load the block pBlockBuffer = RfsdAllocateAndLoadBlock(pCallbackContext->Vcb, BlockNumber); if (!pBlockBuffer) { Status = STATUS_INSUFFICIENT_RESOURCES; goto out; } // Construct the item key to search for DirectoryKey = *(pCallbackContext->pDirectoryKey); DirectoryKey.k_type = RFSD_KEY_TYPE_v2_DIRENTRY; // Get the item header and its information Status = RfsdFindItemHeaderInBlock( pCallbackContext->Vcb, &DirectoryKey, pBlockBuffer, ( &pDirectoryItemHeader ), //< &CompareKeysWithoutOffset ); // If this block doesn't happen to contain a directory item, skip it. if ( (Status == STATUS_NO_SUCH_MEMBER) || !pDirectoryItemHeader ) { KdPrint(("Block %i did not contain the appropriate diritem header\n", BlockNumber)); Status = STATUS_SUCCESS; goto out; } RfsdPrint((DBG_INFO, "Found %i dentries in block\n", pDirectoryItemHeader->u.ih_entry_count)); // Calculate if the requested result will be from this dentry span // If the end of this span is not greater than the requested start, it can be skipped. if ( !( (pCallbackContext->idxCurrentDentry + (USHORT)(pDirectoryItemHeader->u.ih_entry_count)) > pCallbackContext->idxStartingDentry ) ) { RfsdPrint((DBG_TRACE, "SKIPPING block\n")); pCallbackContext->idxCurrentDentry += pDirectoryItemHeader->u.ih_entry_count; Status = STATUS_SUCCESS; goto out; } // Otherwise, Read out each dentry, and call ProcessDirEntry on it. { BOOLEAN bRun = TRUE; PRFSD_DENTRY_HEAD pPrevDentry = NULL; ULONG offsetDentry_toSequentialSpan; // The byte offset of this dentry, relative to the dentry spans, as though only their DENTRY_HEADs were stitched together sequentially // Skip ahead to the starting dentry in this span. ULONG idxDentryInSpan = pCallbackContext->idxStartingDentry - pCallbackContext->idxCurrentDentry; pCallbackContext->idxCurrentDentry += idxDentryInSpan; RfsdPrint((DBG_TRACE, "Sarting dentry: %i. skipped to %i dentry in span\n", pCallbackContext->idxStartingDentry, idxDentryInSpan)); offsetDentry_toSequentialSpan = pCallbackContext->idxCurrentDentry * sizeof(RFSD_DENTRY_HEAD); // Setup the item buffer pDirectoryItemBuffer = (PUCHAR) pBlockBuffer + pDirectoryItemHeader->ih_item_location; while (bRun && ( *(pCallbackContext->pUsedLength) < (pCallbackContext->BufferLength) ) && (idxDentryInSpan < (pDirectoryItemHeader->u.ih_entry_count)) ) { STRING OemName; // FUTURE: does this support codepages? PRFSD_DENTRY_HEAD pCurrentDentry; USHORT InodeFileNameLength = 0; // Read a directory entry from the buffered directory item (from the file associated with the filled inode) pCurrentDentry = (PRFSD_DENTRY_HEAD) (pDirectoryItemBuffer + (idxDentryInSpan * sizeof(RFSD_DENTRY_HEAD) )); // Skip the directory entry for the parent of the root directory (because it should not be shown, and has no stat data) // (NOTE: Any change made here should also be mirrored in RfsdScanDirCallback) if (pCurrentDentry->deh_dir_id == 0 /*&& pCurrentDentry->deh_objectid == 1*/) { goto ProcessNextEntry; } // Pull the name of the file out from the buffer. // NOTE: The filename is not gauranteed to be null-terminated, and so the end may implicitly be the start of the previous entry. OemName.Buffer = (PUCHAR) pDirectoryItemBuffer + pCurrentDentry->deh_location; OemName.MaximumLength = (pPrevDentry ? pPrevDentry->deh_location : // The end of this entry is the start of the previous pDirectoryItemHeader->ih_item_len // Otherwise this is the first entry, the end of which is the end of the item. ) - pCurrentDentry->deh_location; if (!pPrevDentry && pCallbackContext->idxStartingDentry > 1 && pCallbackContext->Ccb->deh_location) { if (OemName.MaximumLength != pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location) { //KdPrint(("Changed MaximumLength from %d to %d for {%.*s}\n", OemName.MaximumLength, pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location, RfsdStringLength(OemName.Buffer, pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location), OemName.Buffer)); } OemName.MaximumLength = pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location; } OemName.Length = RfsdStringLength(OemName.Buffer, OemName.MaximumLength); // Calculate the name's unicode length, allocate memory, and convert the codepaged name to unicode InodeFileNameLength = (USHORT) RfsdOEMToUnicodeSize(&OemName); InodeFileName.Length = 0; InodeFileName.MaximumLength = InodeFileNameLength + 2; if (InodeFileNameLength <= 0) { break; } InodeFileName.Buffer = ExAllocatePoolWithTag( PagedPool, InodeFileNameLength + 2, RFSD_POOL_TAG); if (!InodeFileName.Buffer) { Status = STATUS_INSUFFICIENT_RESOURCES; goto out; } RtlZeroMemory(InodeFileName.Buffer, InodeFileNameLength + 2); Status = RfsdOEMToUnicode( &InodeFileName, &OemName ); if (!NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; goto out; } // TODO: CHECK IF TIHS OK ////////////// END OF MY PART if (FsRtlDoesNameContainWildCards( &(pCallbackContext->Ccb->DirectorySearchPattern)) ? FsRtlIsNameInExpression( &(pCallbackContext->Ccb->DirectorySearchPattern), &InodeFileName, TRUE, NULL) : !RtlCompareUnicodeString( &(pCallbackContext->Ccb->DirectorySearchPattern), &InodeFileName, TRUE) ) { // The name either contains wild cards, or matches the directory search pattern... { ULONG dwBytesWritten; dwBytesWritten = RfsdProcessDirEntry( pCallbackContext->Vcb, pCallbackContext->FileInformationClass, pCurrentDentry->deh_dir_id, pCurrentDentry->deh_objectid, pCallbackContext->Buffer, *(pCallbackContext->pUsedLength), pCallbackContext->BufferLength - *(pCallbackContext->pUsedLength), // The remaining length in the user's buffer offsetDentry_toSequentialSpan, &InodeFileName, pCallbackContext->ReturnSingleEntry, pCallbackContext->pPreviousEntry); if (dwBytesWritten <= 0) { Status = STATUS_EVENT_DONE; bRun = FALSE; pCallbackContext->Ccb->deh_location = pPrevDentry ? pPrevDentry->deh_location : 0; } else { pCallbackContext->Ccb->deh_location = 0; pCallbackContext->pPreviousEntry = (PUCHAR) (pCallbackContext->Buffer) + *(pCallbackContext->pUsedLength); *(pCallbackContext->pUsedLength) += dwBytesWritten; } } } if (InodeFileName.Buffer) { ExFreePool(InodeFileName.Buffer); InodeFileName.Buffer = NULL; } ProcessNextEntry: pPrevDentry = pCurrentDentry; if (bRun) { ++idxDentryInSpan; ++(pCallbackContext->idxCurrentDentry); ++(pCallbackContext->idxStartingDentry); offsetDentry_toSequentialSpan += sizeof(RFSD_DENTRY_HEAD); // Store the current position, so that it will be available for the next call pCallbackContext->Ccb->CurrentByteOffset = offsetDentry_toSequentialSpan; } if ( ( *(pCallbackContext->pUsedLength) > 0) && pCallbackContext->ReturnSingleEntry) { Status = STATUS_EVENT_DONE; break; } } } out: if (pBlockBuffer) ExFreePool(pBlockBuffer); if (InodeFileName.Buffer) ExFreePool(InodeFileName.Buffer); return Status; }