mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1893 lines
55 KiB
1893 lines
55 KiB
/*++
|
|
|
|
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"
|
|
|
|
|
|
//
|
|
// 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_PTR)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 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 = 0;
|
|
|
|
PFILE_BOTH_DIR_INFORMATION dirInfo;
|
|
PFILE_NAMES_INFORMATION namesInfo;
|
|
|
|
BOOLEAN canContinue = FALSE;
|
|
BOOLEAN useCache = FALSE;
|
|
BOOLEAN isSystem = FALSE;
|
|
BOOLEAN lastIndexFromServer = FALSE;
|
|
PNW_DIRECTORY_INFO dirCache;
|
|
PLIST_ENTRY entry;
|
|
ULONG i;
|
|
|
|
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_PTR)Icb);
|
|
DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer);
|
|
DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
|
|
DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG_PTR)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;
|
|
|
|
restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN);
|
|
indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
|
|
returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
|
|
|
|
if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
|
|
fileIndexLow = 0;
|
|
fileIndexHigh = 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.
|
|
//
|
|
|
|
isSystem = TRUE;
|
|
|
|
if( indexSpecified ) {
|
|
fileIndexLow = irpSp->Parameters.QueryDirectory.FileIndex;
|
|
} else {
|
|
fileIndexLow = 0;
|
|
}
|
|
|
|
//
|
|
// See if we can avoid a rewind.
|
|
//
|
|
|
|
if( fileIndexLow != 0 ) {
|
|
|
|
if( fileIndexLow == Icb->SearchIndexLow ) {
|
|
fileIndexHigh = Icb->SearchIndexHigh;
|
|
canContinue = TRUE;
|
|
} else {
|
|
|
|
if( Icb->CacheHint ) {
|
|
entry = Icb->CacheHint;
|
|
searchRetry = TRUE;
|
|
} else {
|
|
entry = Icb->DirCache.Flink;
|
|
searchRetry = FALSE;
|
|
}
|
|
|
|
do {
|
|
|
|
if( entry == &(Icb->DirCache) ) {
|
|
entry = Icb->DirCache.Flink;
|
|
searchRetry = FALSE;
|
|
}
|
|
|
|
while( entry != &(Icb->DirCache) ) {
|
|
|
|
dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
|
|
if( dirCache->FileIndexLow == fileIndexLow ) {
|
|
canContinue = TRUE;
|
|
useCache = TRUE;
|
|
Icb->CacheHint = entry->Flink;
|
|
searchRetry = FALSE;
|
|
break;
|
|
}
|
|
entry = entry->Flink;
|
|
}
|
|
|
|
} while( searchRetry );
|
|
}
|
|
}
|
|
}
|
|
|
|
fileInformationClass =
|
|
irpSp->Parameters.QueryDirectory.FileInformationClass;
|
|
|
|
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_PTR)&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(pIrpContext->pNpScb, 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) {
|
|
|
|
//
|
|
// Make sure we don't use any cached directory info if this is
|
|
// for a system account.
|
|
//
|
|
|
|
useCache = FALSE;
|
|
if( isSystem ) {
|
|
NwFreeDirCacheForIcb( Icb );
|
|
}
|
|
|
|
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) ||
|
|
(canContinue) ) {
|
|
//
|
|
// Continue from the one of the last filenames. If an index is specified then its
|
|
// only allowed for the gateway (and other system services).
|
|
//
|
|
|
|
SearchAttributes = Icb->SearchAttributes;
|
|
if( !indexSpecified ) {
|
|
|
|
//
|
|
// We need to check to see if this is the sytem or not. If it
|
|
// is, we need to continue from the last entry returned which may
|
|
// be from the cache.
|
|
//
|
|
|
|
if( isSystem && Icb->CacheHint ) {
|
|
|
|
entry = Icb->CacheHint;
|
|
dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
|
|
useCache = TRUE;
|
|
|
|
} else {
|
|
|
|
useCache = FALSE;
|
|
fileIndexLow = Icb->SearchIndexLow;
|
|
fileIndexHigh = Icb->SearchIndexHigh;
|
|
|
|
}
|
|
}
|
|
|
|
if ( SearchAttributes == 0xFF && fileIndexLow == Icb->SearchIndexLow ) {
|
|
|
|
//
|
|
// 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;
|
|
PNW_DIRECTORY_INFO nwDirInfo;
|
|
ULONG FileNameLength;
|
|
ULONG entriesToCreate;
|
|
|
|
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 );
|
|
|
|
//
|
|
// See if we have a dir cache. If not, create one.
|
|
//
|
|
|
|
if( !Icb->DirCacheBuffer ) {
|
|
|
|
if( isSystem ) {
|
|
entriesToCreate = DirCacheEntries;
|
|
} else {
|
|
entriesToCreate = 1;
|
|
}
|
|
|
|
Icb->DirCacheBuffer = ALLOCATE_POOL ( PagedPool, (sizeof(NW_DIRECTORY_INFO) * entriesToCreate) );
|
|
if( !Icb->DirCacheBuffer ) {
|
|
try_return( status = STATUS_NO_MEMORY );
|
|
}
|
|
|
|
RtlZeroMemory( Icb->DirCacheBuffer, sizeof(NW_DIRECTORY_INFO) * entriesToCreate );
|
|
|
|
dirCache = (PNW_DIRECTORY_INFO)Icb->DirCacheBuffer;
|
|
|
|
for( i = 0; i < entriesToCreate; i++ ) {
|
|
InsertTailList( &(Icb->DirCache), &(dirCache->ListEntry) );
|
|
dirCache++;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if( useCache ) {
|
|
|
|
//
|
|
// We need to use the data out of the entry we found in the cache.
|
|
// dirCache points to the entry that matches, and the request was not
|
|
// for the last file we read, so the entry after dirCache is the one
|
|
// we want.
|
|
//
|
|
|
|
DebugTrace(0, Dbg, "Using cache\n", 0);
|
|
entry = dirCache->ListEntry.Flink;
|
|
dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
|
|
nwDirInfo = dirCache;
|
|
fileIndexLow = nwDirInfo->FileIndexLow;
|
|
fileIndexHigh = nwDirInfo->FileIndexHigh;
|
|
status = nwDirInfo->Status;
|
|
|
|
//
|
|
// Check to see if we should still keep using the cache or not.
|
|
//
|
|
|
|
if( entry->Flink == &(Icb->DirCache) ) {
|
|
|
|
//
|
|
// This is the last entry. We need to stop using the cache.
|
|
//
|
|
|
|
useCache = FALSE;
|
|
Icb->CacheHint = NULL;
|
|
|
|
} else {
|
|
Icb->CacheHint = entry;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Pull an entry from the dir cache.
|
|
//
|
|
|
|
entry = RemoveHeadList( &(Icb->DirCache) );
|
|
nwDirInfo = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
|
|
|
|
nwDirInfo->FileName.Buffer = nwDirInfo->FileNameBuffer;
|
|
nwDirInfo->FileName.MaximumLength = NW_MAX_FILENAME_SIZE;
|
|
|
|
status = GetNextFile(
|
|
pIrpContext,
|
|
Icb,
|
|
&fileIndexLow,
|
|
&fileIndexHigh,
|
|
SearchAttributes,
|
|
nwDirInfo );
|
|
|
|
//
|
|
// Store the return and the file index number,
|
|
// and then put this entry in the cache.
|
|
//
|
|
|
|
nwDirInfo->FileIndexLow = fileIndexLow;
|
|
nwDirInfo->FileIndexHigh = fileIndexHigh;
|
|
nwDirInfo->Status = status;
|
|
InsertTailList( &(Icb->DirCache), &(nwDirInfo->ListEntry) );
|
|
|
|
lastIndexFromServer = TRUE;
|
|
|
|
// SVR to avoid rescanning from end of dir all
|
|
|
|
if (fileIndexLow != -1) {
|
|
Icb->LastSearchIndexLow = fileIndexLow;
|
|
}
|
|
// SVR end
|
|
|
|
}
|
|
|
|
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 );
|
|
|
|
|
|
if (IsTerminalServer() && (LONG)NW_MAX_FILENAME_SIZE < FileNameLength )
|
|
try_return( status = STATUS_BUFFER_OVERFLOW );
|
|
|
|
//
|
|
// 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 {
|
|
if (IsTerminalServer()) {
|
|
try_return( status = STATUS_BUFFER_OVERFLOW );
|
|
}
|
|
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 (!DisableAltFileName) {
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
// Mapping for Novell's handling of Euro char in file names
|
|
{
|
|
int i = 0;
|
|
WCHAR * pCurrChar = nwDirInfo->FileName.Buffer;
|
|
for (i = 0; i < (nwDirInfo->FileName.Length / 2); i++)
|
|
{
|
|
if (*(pCurrChar + i) == (WCHAR) 0x2560) // Its Novell's mapping of a Euro
|
|
*(pCurrChar + i) = (WCHAR) 0x20AC; // set it to Euro
|
|
}
|
|
}
|
|
|
|
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) || useCache) ) {
|
|
|
|
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.
|
|
//
|
|
|
|
//
|
|
// Update the last search index read as long as it didn't come from cache.
|
|
//
|
|
|
|
if( lastIndexFromServer ) {
|
|
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;
|
|
|
|
// Mapping for Novell's handling of Euro char in file names
|
|
{
|
|
int i = 0;
|
|
WCHAR * pCurrChar = UcSearchMask->Buffer;
|
|
for (i = 0; i < (UcSearchMask->Length / 2); i++)
|
|
{
|
|
if (*(pCurrChar + i) == (WCHAR) 0x20AC) // Its a Euro
|
|
*(pCurrChar + i) = (WCHAR) 0x2560; // set it to Novell's mapping for Euro
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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++ ) {
|
|
|
|
//
|
|
// In fact Novell server seems to convert all 0xBF, 0xAA, 0xAE
|
|
// even if they are DBCS lead or trail byte.
|
|
// We can't single out DBCS case in the conversion.
|
|
//
|
|
|
|
if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) {
|
|
|
|
if((UCHAR)(OemSearchMask->Buffer[i]) == 0xBF ) {
|
|
|
|
OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
|
|
|
|
}else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAE ) {
|
|
|
|
OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
|
|
|
|
}else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAA ) {
|
|
|
|
OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
if((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C ) {
|
|
|
|
//
|
|
// The trailbyte is 0x5C, replace it with 0x13
|
|
//
|
|
|
|
|
|
OemSearchMask->Buffer[i] = (UCHAR)( 0x13 );
|
|
|
|
}
|
|
//
|
|
// Continue to check other conversions for trailbyte.
|
|
//
|
|
}
|
|
|
|
// 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.
|
|
// We allocate an extra byte to account for the null terminator.
|
|
//
|
|
|
|
#ifndef QFE_BUILD
|
|
buffer = ExAllocatePoolWithTag( PagedPool,
|
|
(UcSearchMask->Length) + 1,
|
|
'scwn' );
|
|
#else
|
|
buffer = ExAllocatePool( PagedPool,
|
|
(UcSearchMask->Length) + 1 );
|
|
#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;
|
|
|
|
case 0x20AC: // Euro
|
|
*src.Buffer = (WCHAR)0x2560; // change it to Novell's mapping
|
|
// intentional fall-through to get it mapped to OEM
|
|
|
|
default:
|
|
RtlUnicodeStringToCountedOemString( &dest, &src, FALSE );
|
|
if( FsRtlIsLeadDbcsCharacter( dest.Buffer[0] ) ) {
|
|
dest.Buffer++;
|
|
}
|
|
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
|
|
|
|
|