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.
985 lines
36 KiB
985 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
DownLvlI.c
|
|
|
|
Abstract:
|
|
|
|
This module implements downlevel fileinfo, volinfo, and dirctrl.
|
|
|
|
Author:
|
|
|
|
Jim McNelis [JimMcN] 15-November-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, MRxSmbDosPathFunction)
|
|
#pragma alloc_text(PAGE, MRxSmbLoadPathFileSearchBuffer)
|
|
#pragma alloc_text(PAGE, MRxSmbPathFileSearch)
|
|
#pragma alloc_text(PAGE, MrxSmbPathQueryFsVolumeInfo)
|
|
#pragma alloc_text(PAGE, MrxSmbPathQueryDiskAttributes)
|
|
#pragma alloc_text(PAGE, SmbPseExchangeStart_PathFunction)
|
|
#endif
|
|
|
|
#define Dbg (DEBUG_TRACE_VOLINFO)
|
|
|
|
|
|
extern SMB_EXCHANGE_DISPATCH_VECTOR SmbPseDispatch_PathFunction;
|
|
|
|
//++
|
|
//
|
|
// VOID
|
|
// NAME_LENGTH(
|
|
// OUT ULONG Length,
|
|
// IN PUCHAR Ptr
|
|
// )
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Determines the length of a Path filename returned by search. This
|
|
// is normally a NULL terminated string less than MAXIMUM_COMPONENT_CORE.
|
|
// In some cases this is Non-null teminated and space filled.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Length - Returns the string length
|
|
// Ptr - The filename to be measured
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
//--
|
|
#define NAME_LENGTH( Length, Ptr, Max ) { \
|
|
Length = 0; \
|
|
while( ((PCHAR)Ptr)[Length] != '\0' ) { \
|
|
Length++; \
|
|
if ( Length == Max ) { \
|
|
break; \
|
|
} \
|
|
} \
|
|
while( ((PCHAR)Ptr)[Length-1] == ' ' && Length ) { \
|
|
Length--; \
|
|
} \
|
|
}
|
|
MRxSmbSetDeleteDisposition(
|
|
IN PRX_CONTEXT RxContext
|
|
);
|
|
NTSTATUS
|
|
MRxSmbDosPathFunction(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine perfoms one of several single path functions to the network
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
InformationClass - a class variable that is specific to the call.
|
|
sometimes it's a SMB class; sometimes an NT class.
|
|
CODE.IMPROVEMENT.ASHAMED we should always use the NT
|
|
guy OR we should define some other enumeration that
|
|
we like better. consideration of the latter has kept
|
|
me from proceeding here..........
|
|
|
|
pBuffer - pointer to the user's buffer
|
|
pBufferLength - a pointer to a ulong containing the bufferlength that is
|
|
updated as we go; if it's a setinfo then we deref and
|
|
place the actual bufferlength in the OE.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(SUCCESS);
|
|
RxCaptureRequestPacket;
|
|
// RxCaptureFcb;
|
|
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = NULL;
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = NULL;
|
|
PSMB_EXCHANGE Exchange;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(1, Dbg, ("MRxSmbDownLevelQueryInformation\n", 0 ));
|
|
|
|
switch (RxContext->MajorFunction) {
|
|
case DOSBASED_DELETE:
|
|
case DOSBASED_DIRFUNCTION:
|
|
break;
|
|
|
|
default:
|
|
ASSERT(!"Supposed to be here");
|
|
|
|
}
|
|
|
|
StufferState = MRxSmbCreateSmbStufferState(RxContext,
|
|
RxContext->DosVolumeFunction.VNetRoot,
|
|
RxContext->DosVolumeFunction.NetRoot,
|
|
ORDINARY_EXCHANGE,CREATE_SMB_SIZE,
|
|
&SmbPseDispatch_PathFunction
|
|
);
|
|
if (StufferState==NULL) {
|
|
RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n"));
|
|
return(RX_MAP_STATUS(INSUFFICIENT_RESOURCES));
|
|
}
|
|
|
|
OrdinaryExchange =
|
|
(PSMB_PSE_ORDINARY_EXCHANGE)( Exchange = StufferState->Exchange );
|
|
|
|
OrdinaryExchange->pPathArgument1 = RxContext->DosVolumeFunction.pUniStringParam1;
|
|
Status = SmbCeInitiateExchange(Exchange);
|
|
|
|
//async was turned away at the top level
|
|
|
|
ASSERT (Status != RX_MAP_STATUS(PENDING));
|
|
|
|
//NTRAID-455630-2/2/2000-yunlin possible reconnect point
|
|
|
|
MRxSmbFinalizeSmbStufferState(StufferState);
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg,
|
|
("MRxSmbDownLevelQueryInformation exit with status=%08lx\n",
|
|
Status ));
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
extern UNICODE_STRING MRxSmbAll8dot3Files;
|
|
|
|
#if DBG
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
extern
|
|
VOID
|
|
MRxSmbDumpResumeKey(
|
|
PSZ text,
|
|
PSMB_RESUME_KEY ResumeKey
|
|
);
|
|
#else
|
|
#define MRxSmbDumpResumeKey(x,y)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbLoadPathFileSearchBuffer(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a CORE_SMB_SEARCH and leaves the result in the SMBbuf.
|
|
|
|
Arguments:
|
|
|
|
OrdinaryExchange - an exchange to be used for conducting this open.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(NOT_IMPLEMENTED);
|
|
//SMBbuf_STATUS SMBbufStatus;
|
|
|
|
#ifndef WIN9X
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
|
|
// RxCaptureFileObject;
|
|
PMRX_SMB_FOBX smbFobx = (PMRX_SMB_FOBX)capFobx;
|
|
//PSRV_OPEN SrvOpen = capFobx->SrvOpen;
|
|
//PMRX_SMB_SRV_OPEN smbSrvOpen = (PMRX_SMB_SRV_OPEN)SrvOpen;
|
|
|
|
PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange;
|
|
|
|
PSMBSTUFFER_BUFFER_STATE StufferState;
|
|
PSMB_HEADER SmbHeader;
|
|
|
|
//PUNICODE_STRING DirectoryName;
|
|
//PUNICODE_STRING Template;
|
|
BOOLEAN FindFirst;
|
|
UNICODE_STRING FindFirstPattern;
|
|
USHORT ResumeKeyLength;
|
|
ULONG ReturnCount;
|
|
BOOLEAN EndOfSearchReached;
|
|
// SearchAttributes is hardcoded to the magic number 0x16
|
|
ULONG SearchAttributes =
|
|
(SMB_FILE_ATTRIBUTE_DIRECTORY
|
|
| SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN);
|
|
PULONG pCountRemainingInSmbbuf = &OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbLoadPathFileSearchBuffer entering.......OE=%08lx\n",OrdinaryExchange));
|
|
RxDbgTrace( 0, Dbg, (".......smbFobx/resumekey=%08lx/%08lx\n",smbFobx,smbFobx->Enumeration.PathResumeKey));
|
|
|
|
if (!FlagOn(smbFobx->Enumeration.Flags,SMBFOBX_ENUMFLAG_SEARCH_NOT_THE_FIRST)) {
|
|
|
|
PUNICODE_STRING DirectoryName = &capFcb->AlreadyPrefixedName;
|
|
PUNICODE_STRING Template = &capFobx->UnicodeQueryTemplate;
|
|
ULONG DirectoryNameLength,TemplateLength,AllocationLength;
|
|
PBYTE SmbFileName;
|
|
|
|
//this is the first time thru....the stuffer cannot handle the intricate logic here so we
|
|
//will have to preallocate for the name
|
|
|
|
if (smbFobx->Enumeration.WildCardsFound = FsRtlDoesNameContainWildCards(Template)){
|
|
// we will need to have an upcased template for compares; we do this in place
|
|
RtlUpcaseUnicodeString( Template, Template, FALSE );
|
|
//CODE.IMPROVEMENT but we should specialcase *.* (altho the fsrtl routine also does it)
|
|
Template = &MRxSmbAll8dot3Files; //we will have to filter on this side
|
|
}
|
|
DirectoryNameLength = DirectoryName->Length;
|
|
TemplateLength = Template->Length;
|
|
AllocationLength = sizeof(WCHAR) // backslash separator
|
|
+DirectoryNameLength
|
|
+TemplateLength;
|
|
RxDbgTrace(0, Dbg, (" --> d/t/dl/tl/al <%wZ><%wZ>%08lx/%08lx/%08lx!\n",
|
|
DirectoryName,Template,
|
|
DirectoryNameLength,TemplateLength,AllocationLength));
|
|
|
|
FindFirstPattern.Buffer = (PWCHAR)RxAllocatePoolWithTag( PagedPool,AllocationLength,'0SxR');
|
|
if (FindFirstPattern.Buffer==NULL) {
|
|
RxDbgTrace(0, Dbg, (" --> Couldn't get the findfind pattern buffer!\n"));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FINALLY;
|
|
}
|
|
|
|
SmbFileName = (PBYTE)FindFirstPattern.Buffer;
|
|
RtlCopyMemory(SmbFileName,DirectoryName->Buffer,DirectoryNameLength);
|
|
SmbFileName += DirectoryNameLength;
|
|
if (*((PWCHAR)(SmbFileName-sizeof(WCHAR))) != L'\\') {
|
|
*((PWCHAR)SmbFileName) = L'\\'; SmbFileName+= sizeof(WCHAR);
|
|
}
|
|
RtlCopyMemory(SmbFileName,Template->Buffer,TemplateLength);
|
|
SmbFileName += TemplateLength;
|
|
if ((TemplateLength == sizeof(WCHAR)) && (Template->Buffer[0]==DOS_STAR)) {
|
|
*((PWCHAR)SmbFileName) = L'.'; SmbFileName+= sizeof(WCHAR);
|
|
*((PWCHAR)SmbFileName) = L'*'; SmbFileName+= sizeof(WCHAR);
|
|
}
|
|
//*((PWCHAR)SmbFileName) = 0; SmbFileName+= sizeof(WCHAR); //trailing NULL;
|
|
//CODE.IMPROVEMENT we should potentially 8.3ize the string here
|
|
FindFirstPattern.Length = (USHORT)(SmbFileName - (PBYTE)FindFirstPattern.Buffer);
|
|
RxDbgTrace(0, Dbg, (" --> find1stpattern <%wZ>!\n",&FindFirstPattern));
|
|
FindFirst = TRUE;
|
|
ResumeKeyLength = 0;
|
|
|
|
} else {
|
|
|
|
RxDbgTrace(0, Dbg, ("-->FINDNEXT\n"));
|
|
FindFirstPattern.Buffer = NULL;
|
|
if (!FlagOn(smbFobx->Enumeration.Flags,SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS)) {
|
|
Status = smbFobx->Enumeration.ErrorStatus;
|
|
RxDbgTrace(0, Dbg, ("-->ERROR EARLY OUT\n"));
|
|
goto FINALLY;
|
|
}
|
|
FindFirst = FALSE;
|
|
FindFirstPattern.Length = 0;
|
|
ResumeKeyLength = sizeof(SMB_RESUME_KEY);
|
|
MRxSmbDumpResumeKey("FindNext:",smbFobx->Enumeration.PathResumeKey);
|
|
|
|
}
|
|
|
|
//get the correct return count. there are three factors: countremaining from the OE,
|
|
// how many could fit the the user's buffer, and how many could fit in a negotiated buffer.
|
|
// we pick the smallest of the three.
|
|
ReturnCount = OrdinaryExchange->Info.CoreSearch.CountRemaining;
|
|
{ ULONG t = (*OrdinaryExchange->Info.pBufferLength) / smbFobx->Enumeration.FileNameOffset;
|
|
if (t<ReturnCount) { ReturnCount = t; }
|
|
}
|
|
{ PSMBCE_SERVER pServer = &((PSMB_EXCHANGE)OrdinaryExchange)->SmbCeContext.pServerEntry->Server;
|
|
ULONG AvailableBufferSize = pServer->MaximumBufferSize -
|
|
(sizeof(SMB_HEADER) +
|
|
FIELD_OFFSET(RESP_SEARCH,Buffer[0])
|
|
+sizeof(UCHAR)+sizeof(USHORT) //bufferformat,datalength fields
|
|
);
|
|
ULONG t = AvailableBufferSize / sizeof(SMB_DIRECTORY_INFORMATION);
|
|
if (t<ReturnCount) { ReturnCount = t; }
|
|
}
|
|
RxDbgTrace( 0, Dbg, ("-------->count=%08lx\n",ReturnCount));
|
|
if (ReturnCount==0) {
|
|
Status = RX_MAP_STATUS(MORE_PROCESSING_REQUIRED);
|
|
RxDbgTrace(0, Dbg, ("-->Count==0 EARLY OUT\n"));
|
|
goto FINALLY;
|
|
}
|
|
|
|
StufferState = OrdinaryExchange->StufferState;
|
|
ASSERT( StufferState );
|
|
|
|
*pCountRemainingInSmbbuf = 0;
|
|
OrdinaryExchange->Info.CoreSearch.NextDirInfo = NULL;
|
|
|
|
COVERED_CALL(MRxSmbStartSMBCommand (StufferState,SetInitialSMB_ForReuse,
|
|
SMB_COM_SEARCH, SMB_REQUEST_SIZE(SEARCH),
|
|
NO_EXTRA_DATA,SMB_BEST_ALIGNMENT(1,0),RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
|
|
0,0,0,0 STUFFERTRACE(Dbg,'FC'))
|
|
);
|
|
|
|
RxDbgTrace(0, Dbg,("core search command initial status = %lu\n",Status));
|
|
MRxSmbDumpStufferState (1100,"SMB w/ core search before stuffing",StufferState);
|
|
|
|
MRxSmbStuffSMB (StufferState,
|
|
"0wwB4ywc!",
|
|
// 0 UCHAR WordCount; // Count of parameter words = 2
|
|
ReturnCount, // w _USHORT( MaxCount ); // Number of dir. entries to return
|
|
SearchAttributes, // w _USHORT( SearchAttributes );
|
|
SMB_WCT_CHECK(2) // B _USHORT( ByteCount ); // Count of data bytes; min = 5
|
|
// UCHAR Buffer[1]; // Buffer containing:
|
|
&FindFirstPattern, // 4 //UCHAR BufferFormat1; // 0x04 -- ASCII
|
|
// //UCHAR FileName[]; // File name, may be null
|
|
0x05, // y //UCHAR BufferFormat2; // 0x05 -- Variable block
|
|
ResumeKeyLength, // w //USHORT ResumeKeyLength; // Length of resume key, may be 0
|
|
// c //UCHAR SearchStatus[]; // Resume key
|
|
ResumeKeyLength,smbFobx->Enumeration.PathResumeKey
|
|
);
|
|
|
|
|
|
MRxSmbDumpStufferState (700,"SMB w/ core search after stuffing",StufferState);
|
|
//ASSERT(!"Now it's stuffed");
|
|
|
|
Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_CORESEARCH
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) goto FINALLY;
|
|
|
|
smbFobx->Enumeration.Flags |= SMBFOBX_ENUMFLAG_SEARCH_NOT_THE_FIRST|SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS;
|
|
//if (Status==RxStatus(SUCCESS) && FilesReturned==0) {
|
|
// RxDbgTrace( 0, Dbg, ("MRxSmbQueryDirectory: no files returned...switch status\n"));
|
|
// EndOfSearchReached = TRUE;
|
|
// Status = RxStatus(NO_MORE_FILES);
|
|
//}
|
|
if (Status==RX_MAP_STATUS(SUCCESS) && *pCountRemainingInSmbbuf==0) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbLoadPathFileSearchBuffer: no files returned...switch status\n"));
|
|
EndOfSearchReached = TRUE;
|
|
Status = RX_MAP_STATUS(NO_MORE_FILES);
|
|
} else {
|
|
//CODE.IMPROVEMENT a possible improvement here is to know that the search is closed
|
|
// based on a "smaller-than-normal" return; we would key this off of the
|
|
// operatingsystem return string i guess. for NT systems, we don't do this
|
|
EndOfSearchReached = FALSE;
|
|
}
|
|
if (EndOfSearchReached) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbLoadPathFileSearchBuffer: no longer in progress...EOS\n"));
|
|
smbFobx->Enumeration.Flags &= ~SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS;
|
|
smbFobx->Enumeration.ErrorStatus = RX_MAP_STATUS(NO_MORE_FILES);
|
|
}
|
|
//we dont save a resume key here since each individual copy operation will have to do that
|
|
|
|
|
|
FINALLY:
|
|
if (FindFirstPattern.Buffer != NULL) {
|
|
RxFreePool(FindFirstPattern.Buffer);
|
|
}
|
|
if (!NT_SUCCESS(Status)&&(Status!=RX_MAP_STATUS(MORE_PROCESSING_REQUIRED))) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbPathFileSearch: Failed .. returning %lx\n",Status));
|
|
smbFobx->Enumeration.Flags &= ~SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS;
|
|
smbFobx->Enumeration.ErrorStatus = Status; //keep returning this
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbLoadPathFileSearchBuffer exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status));
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
#define ASSERT_SAME_FIELD(__field,__t1,__t2) { \
|
|
ASSERT(FIELD_OFFSET(__t1,__field)==FIELD_OFFSET(__t2,__field)); \
|
|
}
|
|
|
|
#define ASSERT_SAME_DIRINFO_FIELDS(__t1,__t2) {\
|
|
ASSERT_SAME_FIELD(LastWriteTime,__t1,__t2); \
|
|
ASSERT_SAME_FIELD(EndOfFile,__t1,__t2); \
|
|
ASSERT_SAME_FIELD(AllocationSize,__t1,__t2); \
|
|
ASSERT_SAME_FIELD(FileAttributes,__t1,__t2); \
|
|
}
|
|
#if DBG
|
|
VOID MRxSmbPathFileSeach_AssertFields(void){
|
|
//just move this out of the main execution path so that we don't have to look at it while
|
|
//we Uing the code
|
|
ASSERT_SAME_DIRINFO_FIELDS(FILE_DIRECTORY_INFORMATION,FILE_FULL_DIR_INFORMATION);
|
|
ASSERT_SAME_DIRINFO_FIELDS(FILE_DIRECTORY_INFORMATION,FILE_BOTH_DIR_INFORMATION);
|
|
}
|
|
#else
|
|
#define MRxSmbPathFileSeach_AssertFields()
|
|
#endif
|
|
|
|
NTSTATUS
|
|
MRxSmbPathFileSearch(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a GetFileAttributes and remembers the reponse.
|
|
|
|
Arguments:
|
|
|
|
OrdinaryExchange - an exchange to be used for conducting this open.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(NOT_IMPLEMENTED);
|
|
//SMBbuf_STATUS SMBbufStatus;
|
|
#ifndef WIN9X
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
|
|
// RxCaptureFileObject;
|
|
PMRX_SMB_FOBX smbFobx = (PMRX_SMB_FOBX)capFobx;
|
|
|
|
PBYTE pBuffer = OrdinaryExchange->Info.Buffer;
|
|
PULONG pLengthRemaining = OrdinaryExchange->Info.pBufferLength;
|
|
ULONG InformationClass = OrdinaryExchange->Info.InfoClass;
|
|
|
|
PFILE_DIRECTORY_INFORMATION pPreviousBuffer = NULL;
|
|
|
|
PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange;
|
|
|
|
ULONG SuccessCount = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbPathFileSearch entering.......OE=%08lx\n",OrdinaryExchange));
|
|
MRxSmbPathFileSeach_AssertFields();
|
|
|
|
OrdinaryExchange->Info.CoreSearch.CountRemaining =
|
|
RxContext->QueryD.ReturnSingleEntry?1:0x7ffffff;
|
|
|
|
if ( (smbFobx->Enumeration.PathResumeKey ==NULL )
|
|
&& ((smbFobx->Enumeration.PathResumeKey = RxAllocatePoolWithTag(PagedPool,sizeof(SMB_RESUME_KEY),'rbms'))==NULL) ){
|
|
RxDbgTrace(0, Dbg, ("...couldn't allocate resume key\n"));
|
|
Status = RX_MAP_STATUS(INSUFFICIENT_RESOURCES);
|
|
goto FINALLY;
|
|
}
|
|
RxDbgTrace( 0, Dbg, (".......smbFobx/resumekey=%08lx/%08lx\n",smbFobx,smbFobx->Enumeration.PathResumeKey));
|
|
|
|
Status = MRxSmbLoadPathFileSearchBuffer( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS );
|
|
|
|
for (;;) {
|
|
BOOLEAN BufferOverflow = FALSE;
|
|
PSMB_DIRECTORY_INFORMATION NextDirInfo;
|
|
UNICODE_STRING FileNameU;
|
|
OEM_STRING FileNameA;
|
|
WCHAR FileNameU_buffer[14];
|
|
ULONG NameLength;
|
|
PBYTE NextFileName;
|
|
BOOLEAN Match,BufferOverFlow;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status = RX_MAP_STATUS(NO_MORE_FILES)) {
|
|
if (SuccessCount > 0) {
|
|
Status = RX_MAP_STATUS(SUCCESS);
|
|
}
|
|
} else if (Status = RX_MAP_STATUS(MORE_PROCESSING_REQUIRED)) {
|
|
if (SuccessCount > 0) {
|
|
Status = RX_MAP_STATUS(SUCCESS);
|
|
} else {
|
|
Status = RX_MAP_STATUS(BUFFER_OVERFLOW);
|
|
}
|
|
}
|
|
|
|
goto FINALLY;
|
|
}
|
|
ASSERT ( OrdinaryExchange->Info.CoreSearch.CountRemaining>0 );
|
|
ASSERT ( OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf>0 );
|
|
RxDbgTrace(0, Dbg, ("MRxSmbPathFileSearch looptopcheck counts=%08lx,%08lx\n",
|
|
OrdinaryExchange->Info.CoreSearch.CountRemaining,
|
|
OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf
|
|
));
|
|
|
|
//next issue: does the next dirinfo match the criteria?!?
|
|
|
|
NextDirInfo = OrdinaryExchange->Info.CoreSearch.NextDirInfo;
|
|
NextFileName = &NextDirInfo->FileName[0];
|
|
|
|
// According to colinw, some core servers do not remember to insert the null at the end of the name...
|
|
// but the namelength macro handles this correctly. some servers (Xenix, apparently) pad the
|
|
// names with spaces. again, the macro handles it....
|
|
//
|
|
|
|
NAME_LENGTH(NameLength, NextFileName,sizeof(NextDirInfo->FileName));
|
|
|
|
FileNameA.Length = (USHORT)NameLength;
|
|
FileNameA.MaximumLength = (USHORT)NameLength;
|
|
FileNameA.Buffer = NextFileName;
|
|
FileNameU.Length = sizeof(FileNameU_buffer);
|
|
FileNameU.MaximumLength = sizeof(FileNameU_buffer);
|
|
FileNameU.Buffer = &FileNameU_buffer[0];
|
|
|
|
Status = RtlOemStringToUnicodeString(&FileNameU, &FileNameA, TRUE);
|
|
RxDbgTrace(0, Dbg, ("MRxSmbPathFileSearch considering.......filename=%wZ, template=%wZ\n",
|
|
&FileNameU,&capFobx->UnicodeQueryTemplate));
|
|
|
|
ASSERT(Status==RX_MAP_STATUS(SUCCESS));
|
|
|
|
// we deal with a conversion failure by skipping this guy
|
|
Match = (Status==RX_MAP_STATUS(SUCCESS));
|
|
if (Match && smbFobx->Enumeration.WildCardsFound ) {
|
|
//DbgBreakPoint();
|
|
try
|
|
{
|
|
Match = FsRtlIsNameInExpression( &capFobx->UnicodeQueryTemplate,
|
|
&FileNameU, TRUE, NULL );
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Match = FALSE;
|
|
}
|
|
}
|
|
|
|
//next issue: will the next dirinfo fit in the user's buffer?!?
|
|
if (Match) {
|
|
ULONG SpaceNeeded;
|
|
PBYTE pRememberBuffer = pBuffer;
|
|
pBuffer = (PBYTE)LongAlign(pBuffer); //assume that this will fit
|
|
SpaceNeeded = smbFobx->Enumeration.FileNameOffset+FileNameU.Length;
|
|
if (pBuffer+SpaceNeeded > pRememberBuffer+*pLengthRemaining) {
|
|
BufferOverflow = TRUE;
|
|
pBuffer = pRememberBuffer; //rollback
|
|
} else {
|
|
PFILE_DIRECTORY_INFORMATION pThisBuffer = (PFILE_DIRECTORY_INFORMATION)pBuffer;
|
|
SMB_TIME Time;
|
|
SMB_DATE Date;
|
|
BufferOverflow = FALSE;
|
|
if (pPreviousBuffer != NULL) {
|
|
pPreviousBuffer->NextEntryOffset = ((PBYTE)pThisBuffer)-((PBYTE)pPreviousBuffer);
|
|
}
|
|
pPreviousBuffer = pThisBuffer;
|
|
RtlZeroMemory(pBuffer,smbFobx->Enumeration.FileNameOffset);
|
|
RtlCopyMemory(pBuffer+smbFobx->Enumeration.FileNameOffset, FileNameU.Buffer,FileNameU.Length);
|
|
*((PULONG)(pBuffer+smbFobx->Enumeration.FileNameLengthOffset)) = FileNameU.Length;
|
|
//hallucinate the record based on specific return type
|
|
switch (InformationClass) {
|
|
case SMB_FIND_FILE_NAMES_INFO:
|
|
break;
|
|
case SMB_FIND_FILE_DIRECTORY_INFO:
|
|
case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
|
|
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
|
|
//just fill what we have...we do not go to a lot of trouble on allocinfo as rdr1 did.
|
|
// actually, rdr1 didn't do that here...only on getfielinfo.......
|
|
SmbMoveTime (&Time, &NextDirInfo->LastWriteTime);
|
|
SmbMoveDate (&Date, &NextDirInfo->LastWriteDate);
|
|
pThisBuffer->LastWriteTime = MRxSmbConvertSmbTimeToTime(Exchange, Time, Date);
|
|
pThisBuffer->EndOfFile.LowPart = SmbGetUlong(&NextDirInfo->FileSize);
|
|
pThisBuffer->FileAttributes = MRxSmbMapSmbAttributes (NextDirInfo->FileAttributes);
|
|
break;
|
|
default:
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbPathFileSearch: Invalid FS information class\n"));
|
|
ASSERT(!"this can't happen");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto FINALLY;
|
|
}
|
|
pBuffer += SpaceNeeded;
|
|
*pLengthRemaining -= pBuffer-pRememberBuffer;
|
|
OrdinaryExchange->Info.CoreSearch.CountRemaining--;
|
|
SuccessCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if no match or no overflow, move up in the buffer. this means not only juggling the
|
|
// pointers but also saving the resume key
|
|
|
|
if (!Match || !BufferOverflow) {
|
|
MRxSmbDumpResumeKey("BufferKey:",&NextDirInfo->ResumeKey);
|
|
*(smbFobx->Enumeration.PathResumeKey) = NextDirInfo->ResumeKey;
|
|
MRxSmbDumpResumeKey("SaveKey: ",smbFobx->Enumeration.PathResumeKey);
|
|
OrdinaryExchange->Info.CoreSearch.NextDirInfo = NextDirInfo + 1;
|
|
OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf--;
|
|
}
|
|
|
|
if (OrdinaryExchange->Info.CoreSearch.CountRemaining==0) {
|
|
Status = RX_MAP_STATUS(SUCCESS);
|
|
goto FINALLY;
|
|
}
|
|
|
|
//should we jam these together by smashing the countrem to 0 on bufferoverflow??? CODE.IMPROVEMENT
|
|
if (BufferOverflow) {
|
|
Status = (SuccessCount==0)?RX_MAP_STATUS(BUFFER_OVERFLOW):RX_MAP_STATUS(SUCCESS);
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf==0) {
|
|
|
|
Status = MRxSmbLoadPathFileSearchBuffer( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbPathFileSearch exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status));
|
|
//CODE.IMPROVEMENT if we're done with the resume key we could free it!
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
MrxSmbPathQueryFsVolumeInfo(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a GetFileAttributes and remembers the reponse.
|
|
|
|
Arguments:
|
|
|
|
OrdinaryExchange - an exchange to be used for conducting this open.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(NOT_IMPLEMENTED);
|
|
//SMBbuf_STATUS SMBbufStatus;
|
|
|
|
#ifndef WIN9X
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
|
|
// RxCaptureFileObject;
|
|
|
|
PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange;
|
|
PFILE_FS_VOLUME_INFORMATION pBuffer = OrdinaryExchange->Info.Buffer;
|
|
PULONG pBufferLength = OrdinaryExchange->Info.pBufferLength;
|
|
|
|
PSMBSTUFFER_BUFFER_STATE StufferState;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MrxSmbPathQueryFsVolumeInfo entering.......OE=%08lx\n",OrdinaryExchange));
|
|
|
|
StufferState = OrdinaryExchange->StufferState;
|
|
|
|
ASSERT( StufferState );
|
|
|
|
COVERED_CALL(MRxSmbStartSMBCommand (StufferState,SetInitialSMB_ForReuse,SMB_COM_SEARCH,
|
|
SMB_REQUEST_SIZE(SEARCH),
|
|
NO_EXTRA_DATA,SMB_BEST_ALIGNMENT(1,0),RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
|
|
0,0,0,0 STUFFERTRACE(Dbg,'FC'))
|
|
);
|
|
|
|
MRxSmbDumpStufferState (1100,"SMB w/ searchvolumelabel before stuffing",StufferState);
|
|
|
|
//CODE.IMPROVEMENT if this is truly core, we have to copy the name since its in UNICODE
|
|
// otherwise, we don't need to copy the name here, we can just Mdl like in writes
|
|
MRxSmbStuffSMB (StufferState,
|
|
"0wwB4yw!",
|
|
// 0 UCHAR WordCount; // Count of parameter words = 2
|
|
1, // w _USHORT( MaxCount ); // Number of dir. entries to return
|
|
// w _USHORT( SearchAttributes );
|
|
SMB_FILE_ATTRIBUTE_VOLUME,
|
|
SMB_WCT_CHECK(2) // B _USHORT( ByteCount ); // Count of data bytes; min = 5
|
|
// UCHAR Buffer[1]; // Buffer containing:
|
|
&MRxSmbAll8dot3Files,// 4 //UCHAR BufferFormat1; // 0x04 -- ASCII
|
|
// //UCHAR FileName[]; // File name, may be null
|
|
0x05, // y //UCHAR BufferFormat2; // 0x05 -- Variable block
|
|
0 // w //USHORT ResumeKeyLength; // Length of resume key, may be 0
|
|
// //UCHAR SearchStatus[]; // Resume key
|
|
);
|
|
|
|
|
|
MRxSmbDumpStufferState (700,"SMB w/ searchvolumelabel after stuffing",StufferState);
|
|
//ASSERT(!"Now it's stuffed");
|
|
|
|
OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0] = 0; //no label
|
|
|
|
Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_COREQUERYLABEL
|
|
);
|
|
|
|
//Status = RxStatus(NOT_IMPLEMENTED);
|
|
ASSERT ( *pBufferLength>=sizeof(FILE_FS_VOLUME_INFORMATION));
|
|
RxDbgTrace(0, Dbg, ("MrxSmbPathQueryFsVolumeInfo OEstatus=%08lx\n",Status));
|
|
//DbgBreakPoint();
|
|
|
|
pBuffer->SupportsObjects = FALSE;
|
|
pBuffer->VolumeCreationTime.LowPart = 0;
|
|
pBuffer->VolumeCreationTime.HighPart = 0;
|
|
pBuffer->VolumeSerialNumber = 0;
|
|
pBuffer->VolumeLabelLength = 0;
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
(OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0] != 0) ) {
|
|
UNICODE_STRING VolumeLabelU;
|
|
OEM_STRING VolumeLabelA;
|
|
SMB_DIRECTORY_INFORMATION Buffer;
|
|
ULONG NameLength;
|
|
ULONG BytesToCopy;
|
|
PBYTE VolumeLabel = &OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0];
|
|
|
|
//pBuffer->VolumeSerialNumber =
|
|
// (((SmbGetUshort(&Buffer.LastWriteTime.Ushort)) << 16) |
|
|
// (SmbGetUshort(&Buffer.LastWriteDate.Ushort)));
|
|
|
|
NAME_LENGTH(NameLength, VolumeLabel,
|
|
sizeof(OrdinaryExchange->Info.QFSVolInfo.CoreLabel));
|
|
|
|
VolumeLabelA.Length = (USHORT)NameLength;
|
|
VolumeLabelA.MaximumLength = (USHORT)NameLength;
|
|
VolumeLabelA.Buffer = VolumeLabel;
|
|
|
|
//some core servers put a '.' in the labelname.....if it's there then remove it
|
|
if ((NameLength>8)&& (VolumeLabel[8]=='.') ) {
|
|
ULONG i;
|
|
for (i=8;i<NameLength;i++) {
|
|
VolumeLabel[i] = VolumeLabel[i+1];
|
|
}
|
|
}
|
|
|
|
Status = RtlOemStringToUnicodeString(&VolumeLabelU, &VolumeLabelA, TRUE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ULONG BytesToCopy = min((ULONG)VolumeLabelU.Length, (*pBufferLength-sizeof(FILE_FS_VOLUME_INFORMATION)));
|
|
|
|
RtlCopyMemory(&pBuffer->VolumeLabel[0],
|
|
VolumeLabelU.Buffer,
|
|
BytesToCopy);
|
|
|
|
*pBufferLength -= BytesToCopy;
|
|
pBuffer->VolumeLabelLength = VolumeLabelU.Length;
|
|
IF_DEBUG {
|
|
UNICODE_STRING FinalLabel;
|
|
FinalLabel.Buffer = &pBuffer->VolumeLabel[0];
|
|
FinalLabel.Length = (USHORT)BytesToCopy;
|
|
RxDbgTrace(0, Dbg, ("MrxSmbPathQueryFsVolumeInfo vollabel=%wZ\n",&FinalLabel));
|
|
}
|
|
|
|
RtlFreeUnicodeString(&VolumeLabelU);
|
|
}
|
|
|
|
} else if (Status == RX_MAP_STATUS(NO_SUCH_FILE)) {
|
|
//
|
|
// If we got no such file, this means that there's no volume label
|
|
// the remote volume. Return success with no data.
|
|
//
|
|
|
|
Status = RX_MAP_STATUS(SUCCESS);
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
*pBufferLength -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel);
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MrxSmbPathQueryFsVolumeInfo exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status));
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MrxSmbPathQueryDiskAttributes(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a GetDiskAttributes and remembers the reponse.
|
|
|
|
Arguments:
|
|
|
|
OrdinaryExchange - an exchange to be used for conducting this open.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
//SMBbuf_STATUS SMBbufStatus;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
|
|
// RxCaptureFileObject;
|
|
|
|
PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange;
|
|
PFILE_FS_VOLUME_INFORMATION pBuffer = OrdinaryExchange->Info.Buffer;
|
|
PULONG pBufferLength = OrdinaryExchange->Info.pBufferLength;
|
|
|
|
PSMBSTUFFER_BUFFER_STATE StufferState;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MrxSmbPathQueryDiskAttributes entering.......OE=%08lx\n",OrdinaryExchange));
|
|
|
|
StufferState = OrdinaryExchange->StufferState;
|
|
|
|
ASSERT( StufferState );
|
|
COVERED_CALL(MRxSmbStartSMBCommand (StufferState,SetInitialSMB_ForReuse,SMB_COM_QUERY_INFORMATION_DISK,
|
|
SMB_REQUEST_SIZE(QUERY_INFORMATION_DISK),
|
|
NO_EXTRA_DATA,SMB_BEST_ALIGNMENT(1,0),RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
|
|
0,0,0,0 STUFFERTRACE(Dbg,'FC'))
|
|
);
|
|
|
|
RxDbgTrace(0, Dbg,("querydiskattribs command initial status = %lu\n",Status));
|
|
MRxSmbDumpStufferState (1100,"SMB w/ querydiskattribs before stuffing",StufferState);
|
|
|
|
MRxSmbStuffSMB (StufferState,
|
|
"0B!",
|
|
// 0 UCHAR WordCount; // Count of parameter words = 0
|
|
SMB_WCT_CHECK(0) 0 // B _USHORT( ByteCount ); // Count of data bytes = 0
|
|
// UCHAR Buffer[1]; // empty
|
|
);
|
|
|
|
MRxSmbDumpStufferState (700,"SMB w/ querydiskattribs after stuffing",StufferState);
|
|
//ASSERT(!"Now it's stuffed");
|
|
|
|
Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_COREQUERYDISKATTRIBUTES
|
|
);
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MrxSmbPathQueryDiskAttributes exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status));
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbGetFileAttributes(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbCoreDeleteForSupercedeOrClose(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE,
|
|
BOOLEAN DeleteDirectory
|
|
);
|
|
|
|
NTSTATUS
|
|
SmbPseExchangeStart_PathFunction(
|
|
PSMB_EXCHANGE pExchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the start routine for VOLINFO.
|
|
|
|
Arguments:
|
|
|
|
pExchange - the exchange instance
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(NOT_IMPLEMENTED);
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange =
|
|
(PSMB_PSE_ORDINARY_EXCHANGE)pExchange;
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = OrdinaryExchange->StufferState;
|
|
PRX_CONTEXT RxContext = StufferState->RxContext;
|
|
|
|
PAGED_CODE();
|
|
RxDbgTrace(+1, Dbg, ("SmbPseExchangeStart_PathFunction\n", 0 ));
|
|
|
|
ASSERT(pExchange->Type == ORDINARY_EXCHANGE);
|
|
|
|
MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,'FC'));
|
|
|
|
//ASSERT (StufferState->CurrentCommand == SMB_COM_NO_ANDX_COMMAND);
|
|
|
|
switch (RxContext->MajorFunction) {
|
|
case DOSBASED_DELETE:
|
|
|
|
Status = MRxSmbCoreDeleteForSupercedeOrClose(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, FALSE);
|
|
goto FINALLY;
|
|
case DOSBASED_DIRFUNCTION:
|
|
switch (RxContext->MinorFunction) {
|
|
case DOSBASED_CREATEDIR:
|
|
break;
|
|
case DOSBASED_DELETEDIR:
|
|
Status = MRxSmbCoreDeleteForSupercedeOrClose(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, TRUE);
|
|
goto FINALLY;
|
|
case DOSBASED_CHECKDIR:
|
|
Status = MRxSmbCoreCheckDirFunction(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS);
|
|
goto FINALLY;
|
|
case DOSBASED_QUERY83DIR:
|
|
break;
|
|
default:
|
|
ASSERT(!"DIR MINOR FUNCTION SUPPOSED TO BE HERE!");
|
|
}
|
|
default:
|
|
ASSERT(!"Supposed to be here");
|
|
|
|
}
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("SmbPseExchangeStart_PathFunction exit w %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbFinishSearch (
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange,
|
|
PRESP_SEARCH Response
|
|
);
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbFinishQueryDiskInfo (
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange,
|
|
PRESP_QUERY_INFORMATION_DISK Response
|
|
);
|
|
|
|
SMB_EXCHANGE_DISPATCH_VECTOR
|
|
SmbPseDispatch_PathFunction = {
|
|
SmbPseExchangeStart_PathFunction,
|
|
SmbPseExchangeReceive_default,
|
|
SmbPseExchangeCopyDataHandler_default,
|
|
SmbPseExchangeSendCallbackHandler_default,
|
|
SmbPseExchangeFinalize_default,
|
|
NULL
|
|
};
|
|
|
|
|
|
|
|
|
|
|