summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/dir.c')
-rw-r--r--private/nw/rdr/dir.c1672
1 files changed, 1672 insertions, 0 deletions
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
+
+