From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/nw/rdr/dir.c | 1672 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1672 insertions(+) create mode 100644 private/nw/rdr/dir.c (limited to 'private/nw/rdr/dir.c') diff --git a/private/nw/rdr/dir.c b/private/nw/rdr/dir.c new file mode 100644 index 000000000..64769b9ac --- /dev/null +++ b/private/nw/rdr/dir.c @@ -0,0 +1,1672 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + dir.c + +Abstract: + + This module implements the file directory routines for the + Netware Redirector. + +Author: + + Manny Weiser (mannyw) 4-Mar-1993 + +Revision History: + +--*/ + +#include "procs.h" + +typedef struct _NW_DIRECTORY_INFO { + WCHAR FileNameBuffer[NW_MAX_FILENAME_LENGTH]; + UNICODE_STRING FileName; + UCHAR Attributes; + USHORT CreationDate; + USHORT CreationTime; + USHORT LastAccessDate; + USHORT LastUpdateDate; + USHORT LastUpdateTime; + ULONG FileSize; + ULONG DosDirectoryEntry; +} NW_DIRECTORY_INFO, *PNW_DIRECTORY_INFO; + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRCTRL) + +NTSTATUS +NwCommonDirectoryControl ( + IN PIRP_CONTEXT pIrpContext + ); + +NTSTATUS +NwQueryDirectory ( + IN PIRP_CONTEXT pIrpContext, + IN PICB pIcb + ); + +NTSTATUS +GetNextFile( + PIRP_CONTEXT pIrpContext, + PICB Icb, + PULONG fileIndexLow, + PULONG fileIndexHigh, + UCHAR SearchAttributes, + PNW_DIRECTORY_INFO NwDirInfo + ); + +NTSTATUS +NtSearchMaskToNw( + IN PUNICODE_STRING UcSearchMask, + IN OUT POEM_STRING OemSearchMask, + IN PICB Icb, + IN BOOLEAN ShortNameSearch + ); + +#if 0 +VOID +NwCancelFindNotify ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, NwFsdDirectoryControl ) +#pragma alloc_text( PAGE, NwQueryDirectory ) +#pragma alloc_text( PAGE, GetNextFile ) +#pragma alloc_text( PAGE, NtSearchMaskToNw ) + +#ifndef QFE_BUILD +#pragma alloc_text( PAGE1, NwCommonDirectoryControl ) +#endif + +#endif + + +#if 0 // Not pageable + +// see ifndef QFE_BUILD above + +#endif + + +NTSTATUS +NwFsdDirectoryControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine is the FSD routine that handles directory control + functions (i.e., query and notify). + +Arguments: + + NwfsDeviceObject - Supplies the device object for the directory function. + + Irp - Supplies the IRP to process. + +Return Value: + + NTSTATUS - The result status. + +--*/ + +{ + PIRP_CONTEXT pIrpContext = NULL; + NTSTATUS status; + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwFsdDirectoryControl\n", 0); + + // + // Call the common directory control routine. + // + + FsRtlEnterFileSystem(); + TopLevel = NwIsIrpTopLevel( Irp ); + + try { + + pIrpContext = AllocateIrpContext( Irp ); + status = NwCommonDirectoryControl( pIrpContext ); + + } except(NwExceptionFilter( Irp, GetExceptionInformation() )) { + + if ( pIrpContext == NULL ) { + + // + // If we couldn't allocate an irp context, just complete + // irp without any fanfare. + // + + status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT ); + + } else { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code. + // + + status = NwProcessException( pIrpContext, GetExceptionCode() ); + } + + } + + if ( pIrpContext ) { + NwCompleteRequest( pIrpContext, status ); + } + + if ( TopLevel ) { + NwSetTopLevelIrp( NULL ); + } + FsRtlExitFileSystem(); + + // + // Return to the caller. + // + + DebugTrace(-1, Dbg, "NwFsdDirectoryControl -> %08lx\n", status ); + + return status; +} + + +NTSTATUS +NwCommonDirectoryControl ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine does the common code for directory control functions. + +Arguments: + + IrpContext - Supplies the request being processed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS status; + + PIRP Irp; + PIO_STACK_LOCATION irpSp; + + NODE_TYPE_CODE nodeTypeCode; + PICB icb; + PDCB dcb; + PVOID fsContext; + + // + // Get the current stack location + // + + Irp = IrpContext->pOriginalIrp; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp); + + // + // Decode the file object to figure out who we are. If the result + // is not an ICB then its an illegal parameter. + // + + if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject, + &fsContext, + (PVOID *)&icb )) != NW_NTC_ICB) { + + DebugTrace(0, Dbg, "Not a directory\n", 0); + + status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status ); + return status; + } + + dcb = (PDCB)icb->SuperType.Fcb; + nodeTypeCode = dcb->NodeTypeCode; + + if ( nodeTypeCode != NW_NTC_DCB ) { + + DebugTrace(0, Dbg, "Not a directory\n", 0); + + status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status ); + return status; + } + + IrpContext->pScb = icb->SuperType.Fcb->Scb; + IrpContext->pNpScb = IrpContext->pScb->pNpScb; + IrpContext->Icb = icb; + + // + // Acquire exclusive access to the DCB. Get to front of queue + // first to avoid deadlock potential. + // + + NwAppendToQueueAndWait( IrpContext ); + NwAcquireExclusiveFcb( dcb->NonPagedFcb, TRUE ); + + try { + + NwVerifyIcb( icb ); + + // + // We know this is a directory control so we'll case on the + // minor function, and call the appropriate work routines. + // + + switch (irpSp->MinorFunction) { + + case IRP_MN_QUERY_DIRECTORY: + + status = NwQueryDirectory( IrpContext, icb ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + +#if 0 + if ( !icb->FailedFindNotify ) { + icb->FailedFindNotify = TRUE; +#endif + status = STATUS_NOT_SUPPORTED; +#if 0 + } else { + + // + // HACKHACK + // Cover for moronic process that keeps trying to use + // find notify even though we don't support it. + // + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + + if ( Irp->Cancel ) { + status = STATUS_CANCELLED; + } else { + InsertTailList( &FnList, &IrpContext->NextRequest ); + IoMarkIrpPending( Irp ); + IoSetCancelRoutine( Irp, NwCancelFindNotify ); + status = STATUS_PENDING; + } + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + NwReleaseRcb( &NwRcb ); + + } +#endif + + break; + + default: + + // + // For all other minor function codes we say they're invalid + // and complete the request. + // + + DebugTrace(0, Dbg, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction); + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + } finally { + + NwDequeueIrpContext( IrpContext, FALSE ); + + NwReleaseFcb( dcb->NonPagedFcb ); + DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status); + } + + return status; +} + + +NTSTATUS +NwQueryDirectory ( + IN PIRP_CONTEXT pIrpContext, + IN PICB Icb + ) + +/*++ + +Routine Description: + + This is the work routine for querying a directory. + +Arugments: + + IrpContext - Supplies the Irp context information. + + Icb - Pointer the ICB for the request. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PIRP Irp; + PIO_STACK_LOCATION irpSp; + PUCHAR buffer; + CLONG systemBufferLength; + UNICODE_STRING searchMask; + ULONG fileIndexLow; + ULONG fileIndexHigh; + FILE_INFORMATION_CLASS fileInformationClass; + BOOLEAN restartScan; + BOOLEAN returnSingleEntry; + BOOLEAN indexSpecified; + PVCB vcb; + + BOOLEAN ansiStringAllocated = FALSE; + UCHAR SearchAttributes; + BOOLEAN searchRetry; + + static WCHAR star[] = L"*"; + + BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive + + ULONG lastEntry; + ULONG nextEntry; + ULONG totalBufferLength; + + PFILE_BOTH_DIR_INFORMATION dirInfo; + PFILE_NAMES_INFORMATION namesInfo; + + PAGED_CODE(); + + // + // Get the current stack location. + // + + Irp = pIrpContext->pOriginalIrp; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + vcb = Icb->SuperType.Fcb->Vcb; + + DebugTrace(+1, Dbg, "NwQueryDirectory\n", 0 ); + DebugTrace( 0, Dbg, "Icb = %08lx\n", (ULONG)Icb); + DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer); + DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length); + DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG)irpSp->Parameters.QueryDirectory.FileName); + DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex); + DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass); + DebugTrace( 0, Dbg, "RestartScan = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN)); + DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY)); + DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED)); + + // + // Make local copies of the input parameters. + // + + systemBufferLength = irpSp->Parameters.QueryDirectory.Length; + + if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) { + fileIndexLow = 0; + } else { + // + // Tell the gateway we do support resume from index so long + // as the index returned is the same as the last file we + // returned. Otherwise the SMB server does a brute force rewind + // on each find next. + // + + fileIndexLow = irpSp->Parameters.QueryDirectory.FileIndex; + } + fileIndexHigh = 0; + + fileInformationClass = + irpSp->Parameters.QueryDirectory.FileInformationClass; + + restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN); + indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED); + returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY); + + if (irpSp->Parameters.QueryDirectory.FileName != NULL) { + searchMask = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName; + } else { + searchMask.Length = 0; + searchMask.Buffer = NULL; + } + + buffer = Irp->UserBuffer; + DebugTrace(0, Dbg, "Users Buffer -> %08lx\n", buffer); + + // + // It is ok to attempt a reconnect if this request fails with a + // connection error. + // + + SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE ); + + // + // Check if the ICB already has a query template attached. If it + // does not already have one then we either use the string we are + // given or we attach our own containing "*" + // + + if ( Icb->NwQueryTemplate.Buffer == NULL ) { + + // + // This is our first time calling query directory so we need + // to either set the query template to the user specified string + // or to "*.*". + // + + if ( searchMask.Buffer == NULL ) { + + DebugTrace(0, Dbg, "Set template to *", 0); + + searchMask.Length = sizeof( star ) - sizeof(WCHAR); + searchMask.Buffer = star; + + } + + DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG)&searchMask); + + // + // Map the NT search names to NCP. Note that this must be + // done after the Unicode to OEM translation. + // + + searchRetry = FALSE; + + do { + + status = NtSearchMaskToNw( + &searchMask, + &Icb->NwQueryTemplate, + Icb, + searchRetry ); + + if ( !NT_SUCCESS( status ) ) { + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status); + return( status ); + } + + Icb->UQueryTemplate.Buffer = ALLOCATE_POOL( PagedPool, searchMask.Length ); + if (Icb->UQueryTemplate.Buffer == NULL ) { + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES ); + return( STATUS_INSUFFICIENT_RESOURCES ); + } + + Icb->UQueryTemplate.MaximumLength = searchMask.Length; + RtlCopyUnicodeString( &Icb->UQueryTemplate, &searchMask ); + + // + // Now send a Search Initialize NCP. + // + // Do a short search if the server doesn't support long names, + // or this is a short-name non-wild card search + // + + if ( !Icb->ShortNameSearch ) { + + status = ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "Lbb-DbC", + NCP_LFN_SEARCH_INITIATE, + vcb->Specific.Disk.LongNameSpace, + vcb->Specific.Disk.VolumeNumber, + vcb->Specific.Disk.Handle, + LFN_FLAG_SHORT_DIRECTORY, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( status ) ) { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nbee", + &Icb->SearchVolume, + &Icb->SearchIndexHigh, + &Icb->SearchIndexLow ); + } + + } else { + + status = ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "FbJ", + NCP_SEARCH_INITIATE, + vcb->Specific.Disk.Handle, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( status ) ) { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nbww-", + &Icb->SearchVolume, + &Icb->SearchHandle, + &Icb->SearchIndexLow ); + } + + } + + // + // If we couldn't find the search path, and we did a long + // name search initiate, try again with a short name. + // + + if ( status == STATUS_OBJECT_PATH_NOT_FOUND && + !Icb->ShortNameSearch ) { + + searchRetry = TRUE; + + if ( Icb->UQueryTemplate.Buffer != NULL ) { + FREE_POOL( Icb->UQueryTemplate.Buffer ); + } + + RtlFreeOemString ( &Icb->NwQueryTemplate ); + + } else { + searchRetry = FALSE; + } + + + } while ( searchRetry ); + + if ( !NT_SUCCESS( status ) ) { + if (status == STATUS_UNSUCCESSFUL) { + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_SUCH_FILE); + return( STATUS_NO_SUCH_FILE ); + } + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status); + return( status ); + } + + // + // Since we are doing a search we will need to send an End Of Job + // for this PID. + // + + NwSetEndOfJobRequired( Icb->Pid ); + + fileIndexLow = Icb->SearchIndexLow; + fileIndexHigh = Icb->SearchIndexHigh; + + // + // We can't ask for both files and directories, so first ask for + // files, then ask for directories. + // + + SearchAttributes = NW_ATTRIBUTE_SYSTEM | + NW_ATTRIBUTE_HIDDEN | + NW_ATTRIBUTE_READ_ONLY; + + // + // If there are no wildcards in the search mask, then setup to + // not generate the . and .. entries. + // + + if ( !FsRtlDoesNameContainWildCards( &Icb->UQueryTemplate ) ) { + Icb->DotReturned = TRUE; + Icb->DotDotReturned = TRUE; + } else { + Icb->DotReturned = FALSE; + Icb->DotDotReturned = FALSE; + } + + + } else { + + // + // Check if we were given an index to start with or if we need to + // restart the scan or if we should use the index that was saved in + // the ICB. + // + + if (restartScan) { + fileIndexLow = (ULONG)-1; + fileIndexHigh = Icb->SearchIndexHigh; + + // + // Send a Search Initialize NCP. The server often times out search + // handles and if this one has been sitting at the end of the + // directory then its likely we would get no files at all! + // + // Do a short search if the server doesn't support long names, + // or this is a short-name non-wild card search + // + + if ( !Icb->ShortNameSearch ) { + + status = ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "Lbb-DbC", + NCP_LFN_SEARCH_INITIATE, + vcb->Specific.Disk.LongNameSpace, + vcb->Specific.Disk.VolumeNumber, + vcb->Specific.Disk.Handle, + LFN_FLAG_SHORT_DIRECTORY, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( status ) ) { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nbee", + &Icb->SearchVolume, + &Icb->SearchIndexHigh, + &Icb->SearchIndexLow ); + } + + } else { + + status = ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "FbJ", + NCP_SEARCH_INITIATE, + vcb->Specific.Disk.Handle, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( status ) ) { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nbww-", + &Icb->SearchVolume, + &Icb->SearchHandle, + &Icb->SearchIndexLow ); + } + + } + + Icb->ReturnedSomething = FALSE; + + // + // We can't ask for both files and directories, so first ask for + // files, then ask for directories. + // + + SearchAttributes = NW_ATTRIBUTE_SYSTEM | + NW_ATTRIBUTE_HIDDEN | + NW_ATTRIBUTE_READ_ONLY; + Icb->SearchAttributes = SearchAttributes; + + Icb->DotReturned = FALSE; + Icb->DotDotReturned = FALSE; + + } else if ((!indexSpecified) || + ((fileIndexLow == Icb->SearchIndexLow) && + (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart))) { + // + // Continue from the last filename. If an index is specified then its + // only allowed for the gateway (and other system services). + // + + fileIndexLow = Icb->SearchIndexLow; + fileIndexHigh = Icb->SearchIndexHigh; + SearchAttributes = Icb->SearchAttributes; + + if ( SearchAttributes == 0xFF ) { + + // + // This is a completed search. + // + + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_MORE_FILES); + return( STATUS_NO_MORE_FILES ); + } + + } else { + + // + // SVR to avoid rescanning from end of dir all + // the way through the directory again. + // + + if ((Icb->SearchIndexLow == -1) && + (Icb->LastSearchIndexLow == fileIndexLow) && + (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart) && + (Icb->SearchAttributes == 0xFF )) { + + DebugTrace(-1, Dbg, "NwQueryDirectory SVR exit-> %08lx\n", STATUS_NO_MORE_FILES); + return( STATUS_NO_MORE_FILES ); + } + // SVR end + + // + // Someone's trying to do a resume from key. The netware + // server doesn't support this, so neither do we. + // + + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NOT_IMPLEMENTED); + return( STATUS_NOT_IMPLEMENTED ); + } + + } + + // + // Now we are committed to completing the Irp, we do that in + // the finally clause of the following try. + // + + try { + + ULONG baseLength; + ULONG lengthAdded; + NW_DIRECTORY_INFO nwDirInfo; + ULONG FileNameLength; + + lastEntry = 0; + nextEntry = 0; + + switch (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 FileNamesInformation: + + baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); + break; + + case FileBothDirectoryInformation: + + baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); + break; + + default: + + try_return( status = STATUS_INVALID_INFO_CLASS ); + } + + // + // It is not ok to attempt a reconnect if this request fails with a + // connection error, since our search handle would be invalid. + // + + ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE ); + + while ( TRUE ) { + + ULONG bytesToCopy; + ULONG bytesRemainingInBuffer; + + DebugTrace(0, Dbg, "Top of Loop\n", 0); + DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", fileIndexLow); + DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry); + DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry); + + nwDirInfo.FileName.Buffer = nwDirInfo.FileNameBuffer; + nwDirInfo.FileName.MaximumLength = NW_MAX_FILENAME_SIZE; + + status = GetNextFile( + pIrpContext, + Icb, + &fileIndexLow, + &fileIndexHigh, + SearchAttributes, + &nwDirInfo ); + + if ( NT_SUCCESS( status ) ) { + + DebugTrace(0, Dbg, "DirFileName = %wZ\n", &nwDirInfo.FileName); + DebugTrace(0, Dbg, "FileIndexLow = %08lx\n", fileIndexLow); + + FileNameLength = nwDirInfo.FileName.Length; + bytesRemainingInBuffer = systemBufferLength - nextEntry; + + ASSERT( bytesRemainingInBuffer >= baseLength ); + + // + // See how much of the name we will be able to copy into + // the system buffer. This also dictates our return + // value. + // + + if ( baseLength + FileNameLength <= bytesRemainingInBuffer ) { + + bytesToCopy = FileNameLength; + status = STATUS_SUCCESS; + + } else { + + bytesToCopy = bytesRemainingInBuffer - baseLength; + status = STATUS_BUFFER_OVERFLOW; + } + + // + // Note how much of buffer we are consuming and zero + // the base part of the structure. + // + + lengthAdded = baseLength + bytesToCopy; + RtlZeroMemory( &buffer[nextEntry], baseLength ); + + switch (fileInformationClass) { + + case FileBothDirectoryInformation: + + // + // Fill in the short name, if this is a LFN volume. + // + + DebugTrace(0, Dbg, "Getting directory both information\n", 0); + +#if 0 + if ( nwDirInfo.DosDirectoryEntry != 0xFFFF && + !IsFatNameValid( &nwDirInfo.FileName ) ) { + + UNICODE_STRING ShortName; + + status = ExchangeWithWait ( + pIrpContext, + SynchronousResponseCallback, + "SbDb", + NCP_DIR_FUNCTION, NCP_GET_SHORT_NAME, + Icb->SearchVolume, + nwDirInfo.DosDirectoryEntry, + 0 ); + + if ( NT_SUCCESS( status ) ) { + + dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry]; + + // + // Short name is in form 8.3 plus nul terminator. + // + + ShortName.MaximumLength = 13 * sizeof(WCHAR) ; + ShortName.Buffer = dirInfo->ShortName; + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "N_P", + 15, + &ShortName ); + + if ( NT_SUCCESS( status ) ) { + dirInfo->ShortNameLength = (CCHAR)ShortName.Length; + } + } + } +#endif + + case FileFullDirectoryInformation: + + // + // We don't use EaLength, so fill in nothing here. + // + + DebugTrace(0, Dbg, "Getting directory full information\n", 0); + + case FileDirectoryInformation: + + DebugTrace(0, Dbg, "Getting directory information\n", 0); + + // + // The eof indicates the number of instances and + // allocation size is the maximum allowed + // + + dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry]; + + dirInfo->FileAttributes = nwDirInfo.Attributes; + dirInfo->FileNameLength = bytesToCopy; + dirInfo->EndOfFile.LowPart = nwDirInfo.FileSize; + dirInfo->EndOfFile.HighPart = 0; + dirInfo->AllocationSize = dirInfo->EndOfFile; + dirInfo->CreationTime = NwDateTimeToNtTime( nwDirInfo.CreationDate, nwDirInfo.CreationTime ); + dirInfo->LastAccessTime = NwDateTimeToNtTime( nwDirInfo.LastAccessDate, 0 ); + dirInfo->LastWriteTime = NwDateTimeToNtTime( nwDirInfo.LastUpdateDate, nwDirInfo.LastUpdateTime ); + dirInfo->ChangeTime = dirInfo->LastWriteTime; + if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) { + dirInfo->FileIndex = 0; + } else { + dirInfo->FileIndex = fileIndexLow; + } + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "Getting names information\n", 0); + + + namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry]; + + namesInfo->FileNameLength = FileNameLength; + if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) { + namesInfo->FileIndex = 0; + } else { + namesInfo->FileIndex = fileIndexLow; + } + + break; + + default: + + KeBugCheck( RDR_FILE_SYSTEM ); + } + + + RtlMoveMemory( &buffer[nextEntry + baseLength], + nwDirInfo.FileName.Buffer, + bytesToCopy ); + + dump( Dbg, &buffer[nextEntry], lengthAdded); + // + // Setup the previous next entry offset. + // + + *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry; + totalBufferLength = nextEntry + lengthAdded; + + // + // Set ourselves up for the next iteration + // + + lastEntry = nextEntry; + nextEntry += (ULONG)QuadAlign( lengthAdded ); + + // + // Check if the last entry didn't completely fit + // + + if ( status == STATUS_BUFFER_OVERFLOW ) { + + try_return( NOTHING ); + } + + // + // Check if we are only to return a single entry + // + + if (returnSingleEntry) { + try_return( status = STATUS_SUCCESS ); + } + + } else { + + // + // The search response contained an error. If we have + // not yet enumerated directories, do them now. Otherwise, + // we are done searching for files. + // + + if ( status == STATUS_UNSUCCESSFUL && + !FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) { + + SetFlag( SearchAttributes, NW_ATTRIBUTE_DIRECTORY ); + fileIndexLow = (ULONG)-1; + continue; + + } else { + + // + // Remember that this is a completed search and + // quit the loop. + // + + SearchAttributes = 0xFF; + break; + } + } + + // + // 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. + // + // Since we cannot rewind a search, we'll guess that the + // next entry is a full length name. If it mightn't fix, + // just bail and re the files we've got. + // + + bytesRemainingInBuffer = systemBufferLength - nextEntry; + + if ( baseLength + NW_MAX_FILENAME_SIZE > bytesRemainingInBuffer ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + try_return( status = STATUS_SUCCESS ); + } + + } // while ( TRUE ) + + try_exit: NOTHING; + } finally { + + // + // At this point we're finished searching for files. + // If the NextEntry is zero then we haven't found anything so we + // will return no more files or no such file. + // + + if ( status == STATUS_NO_MORE_FILES || + status == STATUS_UNSUCCESSFUL || + status == STATUS_SUCCESS ) { + if (nextEntry == 0) { + if (Icb->ReturnedSomething) { + status = STATUS_NO_MORE_FILES; + } else { + status = STATUS_NO_SUCH_FILE; + } + } else { + Icb->ReturnedSomething = TRUE; + status = STATUS_SUCCESS; + } + + } + + // + // Indicate how much of the system buffer we have used up. + // + + Irp->IoStatus.Information = totalBufferLength; + + // + // Remember the last file index, so that we can resume this + // search. + // + + + // SVR to avoid rescanning from end of dir all + + if (fileIndexLow != -1) { + Icb->LastSearchIndexLow = fileIndexLow; + } + // SVR end + + Icb->SearchIndexLow = fileIndexLow; + Icb->SearchIndexHigh = fileIndexHigh; + + Icb->SearchAttributes = SearchAttributes; + + DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status); + } + + return status; +} + +NTSTATUS +GetNextFile( + PIRP_CONTEXT pIrpContext, + PICB Icb, + PULONG FileIndexLow, + PULONG FileIndexHigh, + UCHAR SearchAttributes, + PNW_DIRECTORY_INFO DirInfo + ) +/*++ + +Routine Description: + + Get the next file in the directory being searched. + +Arguments: + + pIrpContext - Supplies the request being processed. + + Icb - A pointer to the ICB for the directory to query. + + FileIndexLow, FileIndexHigh - On entry, the the index of the + previous directory entry. On exit, the index to the directory + entry returned. + + SearchAttributes - Search attributes to use. + + DirInfo - Returns information for the directory entry found. + +Return Value: + + NTSTATUS - The result status. + +--*/ +{ + NTSTATUS status; + PVCB vcb; + + static UNICODE_STRING DotFile = { 2, 2, L"." }; + static UNICODE_STRING DotDotFile = { 4, 4, L".." }; + + PAGED_CODE(); + + DirInfo->DosDirectoryEntry = 0xFFFF; + + if ( !Icb->DotReturned ) { + + Icb->DotReturned = TRUE; + + // + // Return '.' only if it we are not searching in the root directory + // and it matches the search pattern. + // + + if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 && + FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotFile, TRUE, NULL ) ) { + + RtlCopyUnicodeString( &DirInfo->FileName, &DotFile ); + DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY; + DirInfo->FileSize = 0; + DirInfo->CreationDate = DEFAULT_DATE; + DirInfo->LastAccessDate = DEFAULT_DATE; + DirInfo->LastUpdateDate = DEFAULT_DATE; + DirInfo->LastUpdateTime = DEFAULT_TIME; + DirInfo->CreationTime = DEFAULT_TIME; + + return( STATUS_SUCCESS ); + } + } + + if ( !Icb->DotDotReturned ) { + + Icb->DotDotReturned = TRUE; + + // + // Return '..' only if it we are not searching in the root directory + // and it matches the search pattern. + // + + if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 && + FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotDotFile, TRUE, NULL ) ) { + + RtlCopyUnicodeString( &DirInfo->FileName, &DotDotFile ); + DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY; + DirInfo->FileSize = 0; + DirInfo->CreationDate = DEFAULT_DATE; + DirInfo->LastAccessDate = DEFAULT_DATE; + DirInfo->LastUpdateDate = DEFAULT_DATE; + DirInfo->LastUpdateTime = DEFAULT_TIME; + DirInfo->CreationTime = DEFAULT_TIME; + + return( STATUS_SUCCESS ); + } + } + + vcb = Icb->SuperType.Fcb->Vcb; + if ( Icb->ShortNameSearch ) { + + status = ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "Fbwwbp", + NCP_SEARCH_CONTINUE, + Icb->SearchVolume, + Icb->SearchHandle, + *(PUSHORT)FileIndexLow, + SearchAttributes, + Icb->NwQueryTemplate.Buffer + ); + + if ( !NT_SUCCESS( status )) { + return status; + } + + *FileIndexLow = 0; + *FileIndexHigh = 0; + + if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nw=Rb-ww", + FileIndexLow, + &DirInfo->FileName, 14, + &DirInfo->Attributes, + &DirInfo->CreationDate, + &DirInfo->CreationTime + ); + +#if 0 + if ( DirInfo->CreationDate == 0 && DirInfo->CreationTime == 0 ) { + DirInfo->CreationDate = DEFAULT_DATE; + DirInfo->CreationTime = DEFAULT_TIME; + } +#endif + + DirInfo->FileSize = 0; + DirInfo->LastAccessDate = DirInfo->CreationDate; + DirInfo->LastUpdateDate = DirInfo->CreationDate; + DirInfo->LastUpdateTime = DirInfo->CreationTime; + + } else { + + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "Nw=Rb-dwwww", + FileIndexLow, + &DirInfo->FileName, 14, + &DirInfo->Attributes, + &DirInfo->FileSize, + &DirInfo->CreationDate, + &DirInfo->LastAccessDate, + &DirInfo->LastUpdateDate, + &DirInfo->LastUpdateTime + ); + + DirInfo->CreationTime = DEFAULT_TIME; + } + + } else { + + status = ExchangeWithWait ( + pIrpContext, + SynchronousResponseCallback, + "LbbWDbDDp", + NCP_LFN_SEARCH_CONTINUE, + vcb->Specific.Disk.LongNameSpace, + 0, // Data stream + SearchAttributes & SEARCH_ALL_DIRECTORIES, + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME | + LFN_FLAG_INFO_DIR_INFO | + LFN_FLAG_INFO_NAME, + vcb->Specific.Disk.VolumeNumber, + *FileIndexHigh, + *FileIndexLow, + Icb->NwQueryTemplate.Buffer ); + + if ( NT_SUCCESS( status ) ) { + status = ParseResponse( + pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "N-ee_e_e_xx_xx_x_e_P", + FileIndexHigh, + FileIndexLow, + 5, + &DirInfo->Attributes, + 2, + &DirInfo->FileSize, + 6, + &DirInfo->CreationTime, + &DirInfo->CreationDate, + 4, + &DirInfo->LastUpdateTime, + &DirInfo->LastUpdateDate, + 4, + &DirInfo->LastAccessDate, + 14, + &DirInfo->DosDirectoryEntry, + 20, + &DirInfo->FileName ); + } + + if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) { + DirInfo->FileSize = 0; + } + + } + + if ( DirInfo->Attributes == 0 ) { + DirInfo->Attributes = FILE_ATTRIBUTE_NORMAL; + } + + return status; +} + + +NTSTATUS +NtSearchMaskToNw( + IN PUNICODE_STRING UcSearchMask, + IN OUT POEM_STRING OemSearchMask, + IN PICB Icb, + IN BOOLEAN ShortNameSearch + ) +/*++ + +Routine Description: + + This routine maps a netware path name to the correct netware format. + +Arguments: + + UcSearchMask - The search mask in NT format. + + OemSearchMask - The search mask in Netware format. + + Icb - The ICB of the directory in which we are searching. + + ShortNameSearch - If TRUE, always do a short name search. + +Return Value: + + NTSTATUS - The result status. + +--*/ + +{ + USHORT i; + NTSTATUS status; + + PAGED_CODE(); + + // + // Use a short name search if the volume does not support long names. + // or this is a short name ICB, and we are doing a short name, non + // wild-card search. + // + + if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE || + + ShortNameSearch || + + ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) && + !FsRtlDoesNameContainWildCards( UcSearchMask ) && + IsFatNameValid( UcSearchMask ) ) ) { + + Icb->ShortNameSearch = TRUE; + + // + // Allocate space for and initialize the query templates. + // + + status = RtlUpcaseUnicodeStringToOemString( + OemSearchMask, + UcSearchMask, + TRUE ); + + if ( !NT_SUCCESS( status ) ) { + return( status ); + } + + // + // Special case. Map '*.*' to '*'. + // + + if ( OemSearchMask->Length == 3 && + RtlCompareMemory( OemSearchMask->Buffer, "*.*", 3 ) == 3 ) { + + OemSearchMask->Length = 1; + OemSearchMask->Buffer[1] = '\0'; + + } else { + + + for ( i = 0; i < OemSearchMask->Length ; i++ ) { + + if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) { + + i++; // Skip to the trailing byte. + + if (( Japan ) && + ((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C )) { + + // + // The trailbyte is 0x5C, replace it with 0x13 + // + + + OemSearchMask->Buffer[i] = (UCHAR)( 0x13 ); + continue; + + } + + } else { + + // Single byte character that may need modification. + + switch ( (UCHAR)(OemSearchMask->Buffer[i]) ) { + + case ANSI_DOS_STAR: + OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '*' ); + break; + + case ANSI_DOS_QM: + OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '?' ); + break; + + case ANSI_DOS_DOT: + OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '.' ); + break; + + // + // Netware Japanese version The following character is + // replaced with another one if the string is for File + // Name only when sendding from Client to Server. + // + // SO U+0xFF7F SJIS+0xBF -> 0x10 + // SMALL_YO U+0xFF6E SJIS+0xAE -> 0x11 + // SMALL_E U+0xFF64 SJIS+0xAA -> 0x12 + // + // The reason is unknown, Should ask Novell Japan. + // + // See Also exchange.c + + case 0xBF: // ANSI_DOS_KATAKANA_SO: + if (Japan) { + OemSearchMask->Buffer[i] = (UCHAR)( 0x10 ); + } + break; + + case 0xAE: // ANSI_DOS_KATAKANA_SMALL_YO: + if (Japan) { + OemSearchMask->Buffer[i] = (UCHAR)( 0x11 ); + } + break; + + case 0xAA: // ANSI_DOS_KATAKANA_SMALL_E: + if (Japan) { + OemSearchMask->Buffer[i] = (UCHAR)( 0x12 ); + } + break; + + } + } + } + } + + } else { + + USHORT size; + PCHAR buffer; + UNICODE_STRING src; + OEM_STRING dest; + + Icb->ShortNameSearch = FALSE; + + // + // Allocate space for and initialize the query templates. + // + +#ifndef QFE_BUILD + buffer = ExAllocatePoolWithTag( PagedPool, + UcSearchMask->Length, + 'scwn' ); +#else + buffer = ExAllocatePool( PagedPool, + UcSearchMask->Length ); +#endif + if ( buffer == NULL ) { + return( STATUS_INSUFFICIENT_RESOURCES ); + } + + OemSearchMask->Buffer = buffer; + + // + // Special case. Map '????????.???' to '*'. + // + + if ( UcSearchMask->Length == 24 && + RtlCompareMemory( UcSearchMask->Buffer, L">>>>>>>>\">>>", 24 ) == 24 ) { + + OemSearchMask->Length = 3; + OemSearchMask->Buffer[0] = (UCHAR)0xFF; + OemSearchMask->Buffer[1] = '*'; + OemSearchMask->Buffer[2] = '\0'; + + return STATUS_SUCCESS; + } + + // + // Now convert the string, character by character + // + + src.Buffer = UcSearchMask->Buffer; + src.Length = 2; + dest.Buffer = buffer; + dest.MaximumLength = UcSearchMask->Length; + + size = UcSearchMask->Length / 2; + + for ( i = 0; i < size ; i++ ) { + switch ( *src.Buffer ) { + + case L'*': + case L'?': + *dest.Buffer++ = LFN_META_CHARACTER; + *dest.Buffer++ = (UCHAR)*src.Buffer++; + break; + + case L'.': + *dest.Buffer++ = (UCHAR)*src.Buffer++; + break; + + case DOS_DOT: + *dest.Buffer++ = LFN_META_CHARACTER; + *dest.Buffer++ = (UCHAR)( 0x80 | '.' ); + *src.Buffer++; + break; + + case DOS_STAR: + *dest.Buffer++ = LFN_META_CHARACTER; + *dest.Buffer++ = (UCHAR)( 0x80 | '*' ); + *src.Buffer++; + break; + + case DOS_QM: + *dest.Buffer++ = LFN_META_CHARACTER; + *dest.Buffer++ = (UCHAR)( 0x80 | '?' ); + *src.Buffer++; + break; + + default: + RtlUnicodeStringToCountedOemString( &dest, &src, FALSE ); + dest.Buffer++; + src.Buffer++; + } + } + + *dest.Buffer = '\0'; + OemSearchMask->Length = (USHORT)( dest.Buffer - buffer ); + } + + return STATUS_SUCCESS; +} + +#if 0 +VOID +NwCancelFindNotify ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the cancel function for an find notify IRP. + +Arguments: + + DeviceObject - ignored + + Irp - Supplies the Irp being cancelled. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY listEntry; + + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // We now need to void the cancel routine and release the io cancel + // spin-lock. + // + + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) { + + PIRP_CONTEXT IrpContext; + + IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest ); + + if ( IrpContext->pOriginalIrp == Irp ) { + RemoveEntryList( &IrpContext->NextRequest ); + NwCompleteRequest( IrpContext, STATUS_CANCELLED ); + break; + } + } + + NwReleaseRcb( &NwRcb ); +} +#endif + + -- cgit v1.2.3