/*++ Copyright (c) 1989 Microsoft Corporation Module Name: DownLvlI.c Abstract: This module implements downlevel fileinfo, volinfo, and dirctrl. Author: JoeLinn [JoeLi] 7-March-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, MRxSmbFabricateAttributesOnNetRoot) #pragma alloc_text(PAGE, MRxSmbCoreInformation) #pragma alloc_text(PAGE, MRxSmbLoadCoreFileSearchBuffer) #pragma alloc_text(PAGE, MRxSmbCoreFileSearch) #pragma alloc_text(PAGE, MrxSmbOemVolumeInfoToUnicode) #pragma alloc_text(PAGE, MrxSmbCoreQueryFsVolumeInfo) #pragma alloc_text(PAGE, MrxSmbQueryFsVolumeInfo) #pragma alloc_text(PAGE, MrxSmbCoreQueryDiskAttributes) #pragma alloc_text(PAGE, MrxSmbCoreQueryDiskAttributes) #pragma alloc_text(PAGE, SmbPseExchangeStart_CoreInfo) #pragma alloc_text(PAGE, MRxSmbFinishSearch) #pragma alloc_text(PAGE, MRxSmbFinishQueryDiskInfo) #pragma alloc_text(PAGE, MRxSmbExtendForCache) #pragma alloc_text(PAGE, MRxSmbExtendForNonCache) #pragma alloc_text(PAGE, MRxSmbGetNtAllocationInfo) #pragma alloc_text(PAGE, __MRxSmbSimpleSyncTransact2) #pragma alloc_text(PAGE, MRxSmbFinishTransaction2) #endif #define Dbg (DEBUG_TRACE_VOLINFO) //#define FORCE_CORE_GETATTRIBUTES #ifndef FORCE_CORE_GETATTRIBUTES #define MRxSmbForceCoreGetAttributes FALSE #else BOOLEAN MRxSmbForceCoreGetAttributes = TRUE; #endif NTSTATUS SmbPseExchangeStart_CoreInfo( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ); //these structures are used for t2_query_fs_info typedef struct _QFS_INFO { ULONG ulVSN; UCHAR cch; CHAR szVolLabel[12]; //not unicode } QFS_INFO, *PQFS_INFO; #define ACTUAL_QFS_INFO_LENGTH (FIELD_OFFSET(QFS_INFO,szVolLabel[12])) typedef struct _QFS_ALLOCATE { ULONG ulReserved; ULONG cSectorUnit; ULONG cUnit; ULONG cUnitAvail; USHORT cbSector; } QFS_ALLOCATE, *PQFS_ALLOCATE; #define ACTUAL_QFS_ALLOCATE_LENGTH (FIELD_OFFSET(QFS_ALLOCATE,cbSector)+sizeof(((PQFS_ALLOCATE)0)->cbSector)) //++ // // VOID // NAME_LENGTH( // OUT ULONG Length, // IN PUCHAR Ptr // ) // // Routine Description: // // Determines the length of a Core 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 ); typedef struct __NativeFs_Name_Entry { UCHAR Last; UCHAR MaximumComponentNameLength; UCHAR FileSystemAttributes; //this may overflow someday..... UCHAR NameLength; PWCHAR Name; }; struct __NativeFs_Name_Entry NativeFsNameTable[] = { {0,12,0,sizeof(L"FAT")-sizeof(WCHAR),L"FAT"}, #ifdef MRXSMB_BUILD_FOR_CSC_DCON {0,255, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | // FILE_FILE_COMPRESSION | // FILE_SUPPORTS_REPARSE_POINTS | // FILE_SUPPORTS_SPARSE_FILES | FILE_PERSISTENT_ACLS, sizeof(L"*NT5CSC")-sizeof(WCHAR),L"*NT5CSC"}, #endif //#ifdef MRXSMB_BUILD_FOR_CSC_DCON {0,254,FILE_CASE_PRESERVED_NAMES,sizeof(L"HPFS")-sizeof(WCHAR),L"HPFS"}, {1,254,FILE_CASE_PRESERVED_NAMES,sizeof(L"HPFS386")-sizeof(WCHAR),L"HPFS386"} }; NTSTATUS MRxSmbFabricateAttributesOnNetRoot( IN OUT PSMBCE_NET_ROOT psmbNetRoot, IN PSMBCE_SERVER pServer ) /*++ Routine Description: This routine uses information stored in the netroot structure to hallucinate the attributes of the netroot. it may be that the ascii representation of the filesystem name has already been stored in the netroot. If so, expeand it out.....otherwise, it must be FAT. Arguments: Return Value: RXSTATUS - STATUS_SUCCESS --*/ { NTSTATUS Status; WCHAR FileSystemNameBuffer[SMB_MAXIMUM_SUPPORTED_VOLUME_LABEL+1]; //must leave room for the null UNICODE_STRING FileSystemNameU; OEM_STRING FileSystemNameA; UCHAR FileSystemNameALength; PAGED_CODE(); // ASSERT (psmbNetRoot->MaximumComponentNameLength==0); FileSystemNameALength = psmbNetRoot->FileSystemNameALength; if (FileSystemNameALength == 0) { if (pServer->Dialect <= WFW10_DIALECT) { //must be Fat! FileSystemNameALength = 3; psmbNetRoot->FileSystemNameA[0] = 'F'; psmbNetRoot->FileSystemNameA[1] = 'A'; psmbNetRoot->FileSystemNameA[2] = 'T'; } else { FileSystemNameALength = 7; psmbNetRoot->FileSystemNameA[0] = 'U'; psmbNetRoot->FileSystemNameA[1] = 'N'; psmbNetRoot->FileSystemNameA[2] = 'K'; psmbNetRoot->FileSystemNameA[3] = 'N'; psmbNetRoot->FileSystemNameA[4] = 'O'; psmbNetRoot->FileSystemNameA[5] = 'W'; psmbNetRoot->FileSystemNameA[6] = 'N'; } } //now, translate the name to Unicode....... FileSystemNameA.Length = FileSystemNameALength; FileSystemNameA.MaximumLength = FileSystemNameALength; FileSystemNameA.Buffer = &psmbNetRoot->FileSystemNameA[0]; FileSystemNameU.Length = 0; FileSystemNameU.MaximumLength = (USHORT)sizeof(FileSystemNameBuffer); FileSystemNameU.Buffer = &FileSystemNameBuffer[0]; Status = RtlOemStringToUnicodeString(&FileSystemNameU, &FileSystemNameA, FALSE); if (Status != STATUS_SUCCESS) { return Status; } //copy back the name RtlCopyMemory(&psmbNetRoot->FileSystemName[0],FileSystemNameU.Buffer,FileSystemNameU.Length); psmbNetRoot->FileSystemNameLength = FileSystemNameU.Length; if (FALSE) DbgPrint("NativeFs in unicode %wZ (%d/%d) on netroot %08lx\n", &FileSystemNameU,FileSystemNameU.Length,FileSystemNameU.MaximumLength,psmbNetRoot); { struct __NativeFs_Name_Entry *i; for (i=NativeFsNameTable;;i++) { UCHAR NameLength = i->NameLength; if (RtlCompareMemory(&FileSystemNameBuffer[0], i->Name, NameLength) == NameLength) { psmbNetRoot->MaximumComponentNameLength = i->MaximumComponentNameLength; psmbNetRoot->FileSystemAttributes = i->FileSystemAttributes; if (FALSE) { UNICODE_STRING u; u.Buffer = i->Name; u.Length = i->NameLength; DbgPrint("FoundNativeFsStrng %wZ len %d for %d %d\n",&u,i->NameLength, i->MaximumComponentNameLength,i->FileSystemAttributes); } break; } if (i->Last) { //ASSERT(!"Valid Share Type returned in TREE COnnect And X response"); psmbNetRoot->MaximumComponentNameLength = 255; psmbNetRoot->FileSystemAttributes = 0; break; } } } return(STATUS_SUCCESS); //could be a VOID routine..... } NTSTATUS MRxSmbGetFsAttributesFromNetRoot( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine uses information stored in the netroot structure to fill in a FILE FileFsAttributeInformation structure. Arguments: Return Value: RXSTATUS - STATUS_SUCCESS --*/ { RxCaptureFcb; ULONG FileSystemNameLength,LengthNeeded; PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry; PSMBCE_NET_ROOT psmbNetRoot; //FILE_INFORMATION_CLASS FileInformationClass; PBYTE pBuffer; PULONG pBufferLength; //DbgPrint("yeppp!!\n"); pNetRootEntry = SmbCeGetAssociatedNetRootEntry(capFcb->pNetRoot); if (pNetRootEntry == NULL) { return (STATUS_INVALID_PARAMETER); } ASSERT(RxContext->MajorFunction==IRP_MJ_QUERY_VOLUME_INFORMATION); //FileInformationClass = RxContext->Info.FileInformationClass; ASSERT(RxContext->Info.FileInformationClass==FileFsAttributeInformation); pBuffer = RxContext->Info.Buffer; pBufferLength = &RxContext->Info.LengthRemaining; psmbNetRoot = &pNetRootEntry->NetRoot; if (psmbNetRoot->MaximumComponentNameLength==0) { NTSTATUS Status; Status = MRxSmbFabricateAttributesOnNetRoot(psmbNetRoot, &pNetRootEntry->pServerEntry->Server); if (Status != STATUS_SUCCESS) { return Status; } } FileSystemNameLength = psmbNetRoot->FileSystemNameLength; LengthNeeded = FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName)+FileSystemNameLength; if (*pBufferLength < LengthNeeded) { return (STATUS_BUFFER_OVERFLOW); } { PFILE_FS_ATTRIBUTE_INFORMATION pTypedBuffer = (PFILE_FS_ATTRIBUTE_INFORMATION)pBuffer; pTypedBuffer->MaximumComponentNameLength = psmbNetRoot->MaximumComponentNameLength; pTypedBuffer->FileSystemAttributes = psmbNetRoot->FileSystemAttributes; pTypedBuffer->FileSystemNameLength = FileSystemNameLength; RtlCopyMemory(pTypedBuffer->FileSystemName, psmbNetRoot->FileSystemName, FileSystemNameLength); *pBufferLength -= LengthNeeded; } return(STATUS_SUCCESS); } //CODE.IMPROVEMENT 3 args should NOT be passed NTSTATUS MRxSmbCoreInformation( IN OUT PRX_CONTEXT RxContext, IN ULONG InformationClass, IN OUT PVOID pBuffer, IN OUT PULONG pBufferLength, IN SMB_PSE_ORDINARY_EXCHANGE_ENTRYPOINTS EntryPoint ) /*++ Routine Description: This routine does a core level getinfo (vol or fileinfo) a file across 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 = (STATUS_SUCCESS); PUNICODE_STRING RemainingName; RxCaptureFcb; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); BOOLEAN IsFileDispositionInformation = FALSE; PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange; PAGED_CODE(); RxDbgTrace(0, Dbg, ("MRxSmbDownLevelQueryInformation\n", 0 )); //0 instead of +1.....the general entrypoint already inc'd ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN ); //some stuff is early out............catch them here even before we initialize the stufferstate //CODE.IMPROVEMENT we should potentially find a way to flatten these switches....both here and in volinfo_start switch (EntryPoint) { case SMBPSE_OE_FROM_QUERYVOLUMEINFO: switch (InformationClass) { case FileFsVolumeInformation: case FileFsSizeInformation: break; //these are actually implemented on the wire case FileFsAttributeInformation: { Status = MRxSmbGetFsAttributesFromNetRoot(RxContext); goto FINALLY; } //no break needed because of gotofinally case FileFsDeviceInformation: ASSERT(!"this should have been turned away"); //no break; default: Status = STATUS_NOT_IMPLEMENTED; goto FINALLY; } break; case SMBPSE_OE_FROM_QUERYFILEINFO: //notice that the designators are smb_query_info types switch (InformationClass) { case SMB_QUERY_FILE_BASIC_INFO: case SMB_QUERY_FILE_STANDARD_INFO: // go thru to the wire or get it from file information cache break; case SMB_QUERY_FILE_EA_INFO: //downlevel guys have no EAs....turn this backright here ((PFILE_EA_INFORMATION)pBuffer)->EaSize = 0; *pBufferLength -= sizeof(FILE_EA_INFORMATION); goto FINALLY; //case SMB_QUERY_FILE_ALLOCATION_INFO: //case SMB_QUERY_FILE_END_OF_FILEINFO: //case SMB_QUERY_FILE_ALT_NAME_INFO: //case SMB_QUERY_FILE_STREAM_INFO: //case SMB_QUERY_FILE_COMPRESSION_INFO: default: Status = STATUS_NOT_IMPLEMENTED; goto FINALLY; } break; case SMBPSE_OE_FROM_SETFILEINFO: switch (InformationClass) { case FileBasicInformation: case FileEndOfFileInformation: //these go thru to the wire break; case FileDispositionInformation: IsFileDispositionInformation = TRUE; /* Send this through to the wire. We used to delay the delete until close time, but then if the caller does not have the rights to delete they will never get an error. This can result in various error cases. (Like in explorer, delete will remove it from the view, but than the file will come back on the next refresh) if (FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_FILE_DELETED) || !FlagOn(smbSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN)) { // if it is a pseudo open, we send the delete file request to get the result; // otherwise, we delay the delete until close. goto FINALLY; } */ break; case FileRenameInformation: Status = MRxSmbRename(RxContext); goto FINALLY; case FileAllocationInformation: Status = STATUS_SUCCESS; goto FINALLY; default: Status = STATUS_NOT_IMPLEMENTED; goto FINALLY; } break; case SMBPSE_OE_FROM_EXTENDFILEFORCACHEING: case SMBPSE_OE_FROM_QUERYDIRECTORY: break; } Status = SmbPseCreateOrdinaryExchange(RxContext, SrvOpen->pVNetRoot, EntryPoint, SmbPseExchangeStart_CoreInfo, &OrdinaryExchange ); if (Status != STATUS_SUCCESS) { RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n")); goto FINALLY; } //this is redundant since this info is already stored in the rxcontext! CODE.IMPROVEMENT OrdinaryExchange->Info.Buffer = pBuffer; OrdinaryExchange->Info.pBufferLength = pBufferLength; OrdinaryExchange->Info.InfoClass = InformationClass; Status = SmbPseInitiateOrdinaryExchange(OrdinaryExchange); ASSERT (Status != (STATUS_PENDING)); //async was turned away at the top level //NTRAID-455630-2/2/2000 yunlin Possible reconnect point SmbPseFinalizeOrdinaryExchange(OrdinaryExchange); if (Status == STATUS_SHARING_VIOLATION && IsFileDispositionInformation) { // // Some down-level servers do not allow us to delete a file while we have it open. // If we get back a sharing violating, we are going to assume this is the case and // delay the delete until cleanup. By returning success here, the FCB will be marked // as "delete on close" and the correct thing will happen. // Status = STATUS_SUCCESS; } FINALLY: RxDbgTrace(-1, Dbg, ("MRxSmbDownLevelQueryInformation exit with status=%08lx\n", Status )); return(Status); } UNICODE_STRING MRxSmbAll8dot3Files = {sizeof(L"????????.???")-sizeof(WCHAR),sizeof(L"????????.???"),L"????????.???"}; #if DBG #include "stdarg.h" #include "stdio.h" #include "string.h" VOID MRxSmbDumpResumeKey( PSZ text, PSMB_RESUME_KEY ResumeKey ) { PBYTE rk = (PBYTE)ResumeKey; CHAR Buffer[80]; PCHAR b; ULONG i; PAGED_CODE(); for (i=0,b=Buffer;iInfo.CoreSearch.CountRemainingInSmbbuf; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MRxSmbLoadCoreFileSearchBuffer entering.......OE=%08lx\n",OrdinaryExchange)); RxDbgTrace( 0, Dbg, (".......smbFobx/resumekey=%08lx/%08lx\n",smbFobx,smbFobx->Enumeration.CoreResumeKey)); if (!FlagOn(smbFobx->Enumeration.Flags,SMBFOBX_ENUMFLAG_SEARCH_NOT_THE_FIRST)) { PUNICODE_STRING DirectoryName = GET_ALREADY_PREFIXED_NAME(capFobx->pSrvOpen,capFcb); 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.ASHAMED we should see if we can translate the template; otherwise, // we beat the heck out of downlevel servers on an exe lookup // if we do that we should revisit the decision to get 10 entries // as an alternative....in addition, we could keep a sidebuffer the way // that we do for uplevel. don't know if this is worthwhile //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 = 3 * 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)) { ASSERT(FALSE); //this should never happen *((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.CoreResumeKey); } //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 except that we never go for less than 10 unless 10 won't // fit in the smbbuf. ReturnCount = OrdinaryExchange->Info.CoreSearch.CountRemaining; { ULONG t = (*OrdinaryExchange->Info.pBufferLength) / smbFobx->Enumeration.FileNameOffset; if (tMaximumBufferSize - (sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_SEARCH,Buffer[0]) +sizeof(UCHAR)+sizeof(USHORT) //bufferformat,datalength fields ); t = AvailableBufferSize / sizeof(SMB_DIRECTORY_INFORMATION); if (tcount=%08lx\n",ReturnCount)); if (ReturnCount==0) { Status = (STATUS_MORE_PROCESSING_REQUIRED); RxDbgTrace(0, Dbg, ("-->Count==0 EARLY OUT\n")); goto FINALLY; } StufferState = &OrdinaryExchange->AssociatedStufferState; 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.CoreResumeKey ); 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==(STATUS_SUCCESS) && *pCountRemainingInSmbbuf==0) { RxDbgTrace( 0, Dbg, ("MRxSmbLoadCoreFileSearchBuffer: no files returned...switch status\n")); EndOfSearchReached = TRUE; Status = (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, ("MRxSmbLoadCoreFileSearchBuffer: no longer in progress...EOS\n")); smbFobx->Enumeration.Flags &= ~SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS; smbFobx->Enumeration.ErrorStatus = (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!=(STATUS_MORE_PROCESSING_REQUIRED))) { RxDbgTrace( 0, Dbg, ("MRxSmbCoreFileSearch: Failed .. returning %lx\n",Status)); smbFobx->Enumeration.Flags &= ~SMBFOBX_ENUMFLAG_CORE_SEARCH_IN_PROGRESS; smbFobx->Enumeration.ErrorStatus = Status; //keep returning this } RxDbgTrace(-1, Dbg, ("MRxSmbLoadCoreFileSearchBuffer 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 MRxSmbCoreFileSeach_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 MRxSmbCoreFileSeach_AssertFields() #endif NTSTATUS MRxSmbCoreFileSearch( 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 = (STATUS_NOT_IMPLEMENTED); #ifndef WIN9X RxCaptureFcb; RxCaptureFobx; PMRX_SMB_FOBX smbFobx = MRxSmbGetFileObjectExtension(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, ("MRxSmbCoreFileSearch entering.......OE=%08lx\n",OrdinaryExchange)); MRxSmbCoreFileSeach_AssertFields(); OrdinaryExchange->Info.CoreSearch.CountRemaining = RxContext->QueryDirectory.ReturnSingleEntry?1:0x7ffffff; if ( (smbFobx->Enumeration.CoreResumeKey ==NULL ) && ((smbFobx->Enumeration.CoreResumeKey = RxAllocatePoolWithTag(PagedPool,sizeof(SMB_RESUME_KEY),'rbms'))==NULL) ){ RxDbgTrace(0, Dbg, ("...couldn't allocate resume key\n")); Status = (STATUS_INSUFFICIENT_RESOURCES); goto FINALLY; } RxDbgTrace( 0, Dbg, (".......smbFobx/resumekey=%08lx/%08lx\n",smbFobx,smbFobx->Enumeration.CoreResumeKey)); Status = MRxSmbLoadCoreFileSearchBuffer( 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 == STATUS_NO_MORE_FILES) { if (SuccessCount > 0) { Status = (STATUS_SUCCESS); } } else if (Status == STATUS_MORE_PROCESSING_REQUIRED) { if (SuccessCount > 0) { Status = (STATUS_SUCCESS); } else { Status = (STATUS_BUFFER_OVERFLOW); } } goto FINALLY; } ASSERT ( OrdinaryExchange->Info.CoreSearch.CountRemaining>0 ); ASSERT ( OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf>0 ); RxDbgTrace(0, Dbg, ("MRxSmbCoreFileSearch 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, ("MRxSmbCoreFileSearch considering.......filename=%wZ, template=%wZ\n", &FileNameU,&capFobx->UnicodeQueryTemplate)); if (Status != STATUS_SUCCESS) { FileNameU.Length = 0; } // we deal with a conversion failure by skipping this guy Match = (Status==(STATUS_SUCCESS)); if (Match && smbFobx->Enumeration.WildCardsFound ) { //DbgBreakPoint(); try { Match = FsRtlIsNameInExpression( &capFobx->UnicodeQueryTemplate, &FileNameU, TRUE, NULL ); } except(EXCEPTION_EXECUTE_HANDLER) { Match = 0; } } //next issue: will the next dirinfo fit in the user's buffer?!? if (Match) { ULONG SpaceNeeded; PBYTE pRememberBuffer = pBuffer; //QuadAlign!! pBuffer = (PBYTE)LongAlign(pBuffer); //assume that this will fit if (SuccessCount != 0) { pBuffer = (PBYTE)QuadAlignPtr(pBuffer); //assume that this will fit } SpaceNeeded = smbFobx->Enumeration.FileNameOffset+FileNameU.Length; if (pBuffer+SpaceNeeded > pRememberBuffer+*pLengthRemaining) { BufferOverflow = TRUE; pBuffer = pRememberBuffer; //rollback } else { PSMBCEDB_SERVER_ENTRY pServerEntry; PFILE_DIRECTORY_INFORMATION pThisBuffer = (PFILE_DIRECTORY_INFORMATION)pBuffer; SMB_TIME Time; SMB_DATE Date; PSMBCE_SERVER Server; Server = SmbCeGetExchangeServer(Exchange); BufferOverflow = FALSE; if (pPreviousBuffer != NULL) { pPreviousBuffer->NextEntryOffset = (ULONG)(((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(Server, Time, Date); pThisBuffer->EndOfFile.LowPart = SmbGetUlong(&NextDirInfo->FileSize); pThisBuffer->FileAttributes = MRxSmbMapSmbAttributes (NextDirInfo->FileAttributes); break; default: RxDbgTrace( 0, Dbg, ("MRxSmbCoreFileSearch: Invalid FS information class\n")); ASSERT(!"this can't happen"); Status = STATUS_INVALID_PARAMETER; goto FINALLY; } pBuffer += SpaceNeeded; *pLengthRemaining -= (ULONG)(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.CoreResumeKey) = NextDirInfo->ResumeKey; MRxSmbDumpResumeKey("SaveKey: ",smbFobx->Enumeration.CoreResumeKey); OrdinaryExchange->Info.CoreSearch.NextDirInfo = NextDirInfo + 1; OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf--; } if (OrdinaryExchange->Info.CoreSearch.CountRemaining==0) { Status = (STATUS_SUCCESS); goto FINALLY; } //should we jam these together by smashing the countrem to 0 on bufferoverflow??? CODE.IMPROVEMENT if (BufferOverflow) { Status = (SuccessCount==0)?(STATUS_BUFFER_OVERFLOW):(STATUS_SUCCESS); goto FINALLY; } if (OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf==0) { Status = MRxSmbLoadCoreFileSearchBuffer( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS ); } } FINALLY: RxDbgTrace(-1, Dbg, ("MRxSmbCoreFileSearch 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 MrxSmbOemVolumeInfoToUnicode( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE, ULONG *VolumeLabelLengthReturned ) /*++ Routine Description: This routine does a GetFileAttributes and remembers the reponse. Arguments: Return Value: RXSTATUS - The return status for the operation also VolumeLabelLengthReturned is the number of bytes of the label that were stored, if any. Notes: --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING VolumeLabelU; OEM_STRING VolumeLabelA; SMB_DIRECTORY_INFORMATION Buffer; ULONG NameLength; ULONG BytesToCopy; PBYTE VolumeLabel = &OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0]; PAGED_CODE(); 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;iInfo.pBufferLength; PFILE_FS_VOLUME_INFORMATION pBuffer = OrdinaryExchange->Info.Buffer; ULONG BytesToCopy = min((ULONG)VolumeLabelU.Length, (*pBufferLength-sizeof(FILE_FS_VOLUME_INFORMATION))); RtlCopyMemory(&pBuffer->VolumeLabel[0], VolumeLabelU.Buffer, BytesToCopy); *VolumeLabelLengthReturned = BytesToCopy; pBuffer->VolumeLabelLength = VolumeLabelU.Length; IF_DEBUG { UNICODE_STRING FinalLabel; FinalLabel.Buffer = &pBuffer->VolumeLabel[0]; FinalLabel.Length = (USHORT)BytesToCopy; RxDbgTrace(0, Dbg, ("MrxSmbOemVolumeInfoToUnicode vollabel=%wZ\n",&FinalLabel)); } RtlFreeUnicodeString(&VolumeLabelU); } return(Status); } MrxSmbCoreQueryFsVolumeInfo( 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 = (STATUS_NOT_IMPLEMENTED); PSMBSTUFFER_BUFFER_STATE StufferState; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MrxSmbCoreQueryFsVolumeInfo entering.......OE=%08lx\n",OrdinaryExchange)); StufferState = &OrdinaryExchange->AssociatedStufferState; 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"); Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_COREQUERYLABEL ); //Status = RxStatus(NOT_IMPLEMENTED); FINALLY: RxDbgTrace(-1, Dbg, ("MrxSmbCoreQueryFsVolumeInfo exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status)); return(Status); } NTSTATUS MrxSmbQueryFsVolumeInfo( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ) /*++ Routine Description: This routine does a downlevel getvolumeinfo/FS_VOLUME_INFORMATION. Arguments: OrdinaryExchange - duh! Return Value: RXSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PSMBCE_SERVER pServer; BOOLEAN UseTransactVersion; REQ_QUERY_FS_INFORMATION VolInfo; PFILE_FS_VOLUME_INFORMATION pBuffer = OrdinaryExchange->Info.Buffer; PULONG pBufferLength = OrdinaryExchange->Info.pBufferLength; ULONG VolumeLabelLengthReturned = 0; PAGED_CODE(); ASSERT(pBuffer!=NULL); if ( *pBufferLength < FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) ) { return STATUS_BUFFER_OVERFLOW; } pServer = SmbCeGetExchangeServer(OrdinaryExchange); UseTransactVersion = BooleanFlagOn(pServer->DialectFlags,DF_LANMAN20); RxDbgTrace(+1, Dbg, ("MrxSmbQueryFsVolumeInfo entering.......OE=%08lx\n",OrdinaryExchange)); pBuffer->SupportsObjects = FALSE; pBuffer->VolumeCreationTime.LowPart = 0; pBuffer->VolumeCreationTime.HighPart = 0; pBuffer->VolumeSerialNumber = 0; pBuffer->VolumeLabelLength = 0; OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0] = 0; //no label if (!UseTransactVersion) { Status = MrxSmbCoreQueryFsVolumeInfo(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); } else { VolInfo.InformationLevel = SMB_INFO_VOLUME; Status = MRxSmbSimpleSyncTransact2( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_T2_FOR_LANMAN_VOLUMELABEL_INFO, TRANS2_QUERY_FS_INFORMATION, &VolInfo,sizeof(VolInfo), NULL,0 ); } RxDbgTrace(0, Dbg, ("MrxSmbQueryFsVolumeInfo OEstatus=%08lx\n",Status)); if ( (Status==STATUS_SUCCESS) && (OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0] != 0) ) { Status = MrxSmbOemVolumeInfoToUnicode(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,&VolumeLabelLengthReturned); } else if ( (Status == STATUS_NO_SUCH_FILE) || (Status == STATUS_NO_MORE_FILES) ) { // // these statuses indicate that there's no volume label // the remote volume. Return success with no data. // Status = (STATUS_SUCCESS); } if (NT_SUCCESS(Status)) { *pBufferLength -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel); *pBufferLength -= VolumeLabelLengthReturned; } RxDbgTrace(-1, Dbg, ("MrxSmbQueryFsVolumeInfo exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status)); return(Status); } NTSTATUS MrxSmbCoreQueryDiskAttributes( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ) /*++ Routine Description: This routine does a GetDiskAttributes and remembers the reponse in a buffer pointed to by the exchange. this is called from the downlevel queryvolumeinfo AND ALSO from extend-for-cached-write. Arguments: OrdinaryExchange - duh! Return Value: RXSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PSMBSTUFFER_BUFFER_STATE StufferState; PAGED_CODE(); if (OrdinaryExchange->Info.Buffer == NULL) { return STATUS_INVALID_PARAMETER; } RxDbgTrace(+1, Dbg, ("MrxSmbCoreQueryDiskAttributes entering.......OE=%08lx\n",OrdinaryExchange)); StufferState = &OrdinaryExchange->AssociatedStufferState; 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); Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_COREQUERYDISKATTRIBUTES ); FINALLY: RxDbgTrace(-1, Dbg, ("MrxSmbCoreQueryDiskAttributes exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status)); return(Status); } NTSTATUS MrxSmbQueryDiskAttributes( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ) /*++ Routine Description: This routine does a GetDiskAttributes and remembers the reponse in a buffer pointed to by the exchange. this is called from the downlevel queryvolumeinfo AND ALSO from extend-for-cached-write. Arguments: OrdinaryExchange - duh! Return Value: RXSTATUS - The return status for the operation Notes: NTRAID-455631-2/2/2000 yunlin NT transaction should be used --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PSMBCE_SERVER pServer; BOOLEAN UseTransactVersion; REQ_QUERY_FS_INFORMATION VolInfo; PAGED_CODE(); if ((OrdinaryExchange->Info.Buffer == NULL) || (*OrdinaryExchange->Info.pBufferLength < sizeof(FILE_FS_SIZE_INFORMATION))) { return STATUS_INVALID_PARAMETER; } pServer = SmbCeGetExchangeServer(OrdinaryExchange); UseTransactVersion = BooleanFlagOn(pServer->DialectFlags,DF_LANMAN20) && !MRxSmbForceCoreGetAttributes; if (!UseTransactVersion) { return MrxSmbCoreQueryDiskAttributes(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); } RxDbgTrace(+1, Dbg, ("MrxSmbQueryDiskAttributes entering.......OE=%08lx\n",OrdinaryExchange)); VolInfo.InformationLevel = SMB_INFO_ALLOCATION; Status = MRxSmbSimpleSyncTransact2( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_T2_FOR_LANMAN_DISKATTRIBUTES_INFO, TRANS2_QUERY_FS_INFORMATION, &VolInfo,sizeof(VolInfo), NULL,0 ); RxDbgTrace(-1, Dbg, ("MrxSmbQueryDiskAttributes exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status)); return(Status); } NTSTATUS MRxSmbGetNtAllocationInfo ( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ); NTSTATUS SmbPseExchangeStart_CoreInfo( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ) /*++ 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 = (STATUS_NOT_IMPLEMENTED); PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState; ULONG InformationClass = OrdinaryExchange->Info.InfoClass; PBYTE pBuffer = (PBYTE)OrdinaryExchange->Info.Buffer; PULONG pBufferLength = OrdinaryExchange->Info.pBufferLength; RxCaptureFcb; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall); PAGED_CODE(); RxDbgTrace(+1, Dbg, ("SmbPseExchangeStart_CoreInfo\n", 0 )); ASSERT_ORDINARY_EXCHANGE(OrdinaryExchange); MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,'FC')); //ASSERT (StufferState->CurrentCommand == SMB_COM_NO_ANDX_COMMAND); switch (OrdinaryExchange->EntryPoint) { case SMBPSE_OE_FROM_QUERYVOLUMEINFO: switch (InformationClass) { case FileFsVolumeInformation: Status = MrxSmbQueryFsVolumeInfo(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); goto FINALLY; case FileFsSizeInformation: Status = MrxSmbQueryDiskAttributes(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); goto FINALLY; default: ASSERT(!"this should have been turned away"); goto FINALLY; } ASSERT(!"shouldn't get here1"); goto FINALLY; case SMBPSE_OE_FROM_QUERYFILEINFO: Status = MRxSmbGetFileAttributes(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); if (!NT_SUCCESS(Status)) goto FINALLY; switch (InformationClass) { case SMB_QUERY_FILE_BASIC_INFO: *((PFILE_BASIC_INFORMATION)pBuffer) = OrdinaryExchange->Create.FileInfo.Basic; *pBufferLength -= sizeof(FILE_BASIC_INFORMATION); goto FINALLY; case SMB_QUERY_FILE_STANDARD_INFO: *((PFILE_STANDARD_INFORMATION)pBuffer) = OrdinaryExchange->Create.FileInfo.Standard; *pBufferLength -= sizeof(FILE_STANDARD_INFORMATION); goto FINALLY; default: ASSERT(!"this should have been turned away"); goto FINALLY; } ASSERT(!"shouldn't get here2"); goto FINALLY; case SMBPSE_OE_FROM_SETFILEINFO: switch (InformationClass) { case FileBasicInformation: { ULONG SmbAttributes = MRxSmbMapFileAttributes(((PFILE_BASIC_INFORMATION)pBuffer)->FileAttributes); PFILE_BASIC_INFORMATION BasicInfo = (PFILE_BASIC_INFORMATION)pBuffer; if (SmbAttributes != 0 || (BasicInfo->CreationTime.QuadPart == 0 && BasicInfo->LastWriteTime.QuadPart == 0 && BasicInfo->LastAccessTime.QuadPart == 0)) { Status = MRxSmbSetFileAttributes( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SmbAttributes); } if (BasicInfo->LastWriteTime.QuadPart == 0 && FlagOn(pServerEntry->Server.DialectFlags,DF_W95)) { // Win9x server only takes last write time. Status = STATUS_SUCCESS; goto FINALLY; } if (BasicInfo->CreationTime.QuadPart != 0 || BasicInfo->LastWriteTime.QuadPart != 0 || BasicInfo->LastAccessTime.QuadPart != 0) { Status = MRxSmbDeferredCreate(RxContext); if (Status == STATUS_SUCCESS) { Status = MRxSmbSetFileAttributes( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SmbAttributes); } } } goto FINALLY; case FileEndOfFileInformation: if (((PFILE_END_OF_FILE_INFORMATION)pBuffer)->EndOfFile.HighPart) { Status = (STATUS_INVALID_PARAMETER); } else { Status = MRxSmbCoreTruncate( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, smbSrvOpen->Fid, ((PFILE_END_OF_FILE_INFORMATION)pBuffer)->EndOfFile.LowPart); } goto FINALLY; case FileDispositionInformation: OrdinaryExchange->pPathArgument1 = GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb); Status = MRxSmbCoreDeleteForSupercedeOrClose( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, ((BOOLEAN)( NodeType(capFcb)==RDBSS_NTC_STORAGE_TYPE_DIRECTORY ))); goto FINALLY; default: ASSERT(!"this should have been turned away"); goto FINALLY; } ASSERT(!"shouldn't get here3"); goto FINALLY; case SMBPSE_OE_FROM_QUERYDIRECTORY: Status = MRxSmbCoreFileSearch(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); goto FINALLY; case SMBPSE_OE_FROM_EXTENDFILEFORCACHEING: { PSMBCE_NET_ROOT psmbNetRoot = SmbCeGetExchangeNetRoot(OrdinaryExchange); PMRX_V_NET_ROOT pVNetRoot = SmbCeGetExchangeVNetRoot(OrdinaryExchange); PMRX_NET_ROOT pNetRoot = pVNetRoot->pNetRoot; PSMBCE_SERVER psmbServer = SmbCeGetExchangeServer(OrdinaryExchange); ULONG ClusterSize; PLARGE_INTEGER pFileSize = (PLARGE_INTEGER)(OrdinaryExchange->Info.Buffer); PLARGE_INTEGER pAllocationSize = (PLARGE_INTEGER)(OrdinaryExchange->Info.pBufferLength); //we will need the cluster size if (OrdinaryExchange->ServerVersion==pNetRoot->ParameterValidationStamp) { ClusterSize=pNetRoot->DiskParameters.ClusterSize; } else { RxSynchronizeBlockingOperations( RxContext, (PFCB)RxContext->pFcb, &psmbNetRoot->ClusterSizeSerializationQueue ); if (OrdinaryExchange->ServerVersion!=pNetRoot->ParameterValidationStamp) { // //here we have to go find out the clustersize NTSTATUS LocalStatus; FILE_FS_SIZE_INFORMATION UsersBuffer; ULONG BufferLength = sizeof(FILE_FS_SIZE_INFORMATION); //fill in the exchange params so that we can get the params we need OrdinaryExchange->Info.Buffer = &UsersBuffer; OrdinaryExchange->Info.pBufferLength = &BufferLength; LocalStatus = MrxSmbQueryDiskAttributes(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); if (LocalStatus == STATUS_SUCCESS) { ClusterSize = UsersBuffer.BytesPerSector * UsersBuffer.SectorsPerAllocationUnit; pNetRoot->ParameterValidationStamp =OrdinaryExchange->ServerVersion; } else { ClusterSize = 0; } if (ClusterSize==0) { ClusterSize = 1; } pNetRoot->DiskParameters.ClusterSize = ClusterSize; RxDbgTrace(0, Dbg, ("clustersize set to %08lx\n", ClusterSize )); RxLog(("clustersize rx/n/cs %lx %lx %lx\n", OrdinaryExchange->RxContext,pNetRoot,ClusterSize )); SmbLog(LOG, SmbPseExchangeStart_CoreInfo, LOGPTR(OrdinaryExchange->RxContext) LOGPTR(pNetRoot) LOGULONG(ClusterSize)); } else { // someone else went and got the value while i was asleep...just use it ClusterSize=pNetRoot->DiskParameters.ClusterSize; } RxResumeBlockedOperations_Serially(RxContext,&psmbNetRoot->ClusterSizeSerializationQueue); } ASSERT (ClusterSize != 0); if (FlagOn(psmbServer->DialectFlags,DF_NT_SMBS)) { //i'm using this to identify a server that supports 64bit offsets //for these guys, we write a zero at the eof....since the filesystems //extend on writes this will be much better than a set-end-of-file LARGE_INTEGER ByteOffset,AllocationSize,ClusterSizeAsLI; ULONG Buffer = 0; UCHAR WriteCommand; PSMBCE_SERVER pServer = SmbCeGetExchangeServer(OrdinaryExchange); // We have to be smart about this. Ntfs sometimes gets configured // with cluster sizes around 32K. // There is no reason to believe that the // Allocation size has changed if the new File size is less than // previous Allocation size. So, skip the extend and the qfi for // Allocation size. ClusterSizeAsLI.QuadPart = ClusterSize; AllocationSize.QuadPart = (pFileSize->QuadPart+ ClusterSizeAsLI.QuadPart) & ~(ClusterSizeAsLI.QuadPart - 1); if ((AllocationSize.QuadPart <= capFcb->Header.AllocationSize.QuadPart) || ((capFcb->Header.AllocationSize.QuadPart == 0) && (pFileSize->QuadPart < 0x1000))) { // Return the old value. pAllocationSize->QuadPart = AllocationSize.QuadPart; RxDbgTrace(0, Dbg, ("alocatedsiz222e set to %08lx\n", pAllocationSize->LowPart )); // At this point, we know that the written data hasn't been // flushed. This is ok, since the client would have requested // a flush if the data need to be sunk into the server's disk. // Server file size is different from the client file size // at this point, but allocation size is the same. We extend // to make sure the server has space, if we know the extend isn't // going to question space constraint, we won't even try!! } else { if (FlagOn(pServer->DialectFlags,DF_LARGE_FILES)) { WriteCommand = SMB_COM_WRITE_ANDX; } else { WriteCommand = SMB_COM_WRITE; } ByteOffset.QuadPart = pFileSize->QuadPart - 1; MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,0)); COVERED_CALL(MRxSmbBuildWriteRequest( OrdinaryExchange, TRUE, // IsPagingIo WriteCommand, 1, &ByteOffset, (PBYTE)&Buffer, NULL //BufferAsMdl, )); COVERED_CALL(SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_EXTEND_WRITE )); //this is what you do if you can't do better ClusterSizeAsLI.QuadPart = ClusterSize; AllocationSize.QuadPart = (pFileSize->QuadPart+ ClusterSizeAsLI.QuadPart) & ~(ClusterSizeAsLI.QuadPart - 1); if (Status == STATUS_SUCCESS) { Status = MRxSmbGetNtAllocationInfo(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS); } if (NT_SUCCESS(Status)) { pAllocationSize->QuadPart = OrdinaryExchange->Transact2.AllocationSize.QuadPart; RxDbgTrace(0, Dbg, ("alocatedsiz222e set to %08lx\n", pAllocationSize->LowPart )); } } } if ( (!FlagOn(psmbServer->DialectFlags,DF_NT_SMBS)) || (!NT_SUCCESS(Status) && (Status != STATUS_DISK_FULL) && (Status != STATUS_RETRY)) ) { ULONG FileSize,AllocationSize; FileSize = pFileSize->LowPart; COVERED_CALL(MRxSmbCoreTruncate(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, smbSrvOpen->Fid, FileSize )); //this is what you do if you can't do better AllocationSize = (FileSize+ClusterSize)&~(ClusterSize-1); pAllocationSize->QuadPart = AllocationSize; //64bit! RxDbgTrace(0, Dbg, ("alocatedsize set to %08lx\n", pAllocationSize->LowPart )); //if we care a lot about downlevel performance, we could do the same as ntgetallocation //except that we would use a 32bit smb.........like query_information2 } } goto FINALLY; } FINALLY: RxDbgTrace(-1, Dbg, ("SmbPseExchangeStart_CoreInfo exit w %08lx\n", Status )); return Status; } NTSTATUS MRxSmbFinishSearch ( PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, PRESP_SEARCH Response ) /*++ Routine Description: This routine actually gets the stuff out of the VolInfo response and finishes the close. Arguments: OrdinaryExchange - the exchange instance Response - the response Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = (STATUS_SUCCESS); PRX_CONTEXT RxContext = OrdinaryExchange->RxContext; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MRxSmbFinishSearch\n" )); SmbPseOEAssertConsistentLinkageFromOE("MRxSmbFinishSearch:"); if (Response->WordCount != 1) { Status = STATUS_INVALID_NETWORK_RESPONSE; OrdinaryExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; goto FINALLY; } if (OrdinaryExchange->OEType == SMBPSE_OETYPE_COREQUERYLABEL) { //here, all we do is to copy the label to wherever is pointed to by if (SmbGetUshort(&Response->Count)>0) { PBYTE smbDirInfotmp = &Response->Buffer[0] +sizeof(UCHAR) //bufferformat +sizeof(USHORT); //datalength PSMB_DIRECTORY_INFORMATION smbDirInfo = (PSMB_DIRECTORY_INFORMATION)smbDirInfotmp; RxDbgTrace(+1, Dbg, ("MRxSmbFinishSearch corelabl=%s,size=%d\n", smbDirInfo->FileName, sizeof(smbDirInfo->FileName) )); if (sizeof(smbDirInfo->FileName) != 13) { //straightfrom the spec Status = STATUS_INVALID_NETWORK_RESPONSE; OrdinaryExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; goto FINALLY; } RtlCopyMemory(OrdinaryExchange->Info.QFSVolInfo.CoreLabel, smbDirInfo->FileName, sizeof(smbDirInfo->FileName) ); } else { OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0] = 0; //no label } } else if (OrdinaryExchange->OEType == SMBPSE_OETYPE_CORESEARCHFORCHECKEMPTY) { //here, we 're doing a search SMB to see if the directory is empty. we have to read thru the // entries returned (if successful). if we encounter ones that are neither '.' or '..', set // resumekey to null since that will tell the guy above that the directory is nonempty ULONG Count = SmbGetUshort(&Response->Count); PSMB_DIRECTORY_INFORMATION NextDirInfo = (PSMB_DIRECTORY_INFORMATION)(&Response->Buffer[0]+sizeof(UCHAR)+sizeof(USHORT)); for (;Count>0;Count--,NextDirInfo++) { RxDbgTrace(0, Dbg, ("--->emptydirchk: file=%s\n",&NextDirInfo->FileName[0])); /* // Since the DOS Server returns the file name ". " instead of ".", and so does the // ".. ", the following if {...} statements are always past through with no action. // But those statements make the RMDIR not working on OS2 Server since it returns the "." // and ".." without following blanks. After the if {...} statements were removed, the RMDIR // workes on OS2 Server and no impact has been found to access the DOS Server. if (NextDirInfo->FileName[0]=='.') { CHAR c1; if ((c1=NextDirInfo->FileName[1])==0) { continue; //skip past "." } else if ((c1=='.')&&(NextDirInfo->FileName[2]==0)) { continue; //skip past ".." } else { NOTHING; } } */ // here we have found a bad one...make sure there's no resume key and change the status Status = (STATUS_NO_MORE_FILES); OrdinaryExchange->Info.CoreSearch.EmptyCheckResumeKey = NULL; } //if we get here with success, set up the resume key and buffer if (Status == (STATUS_SUCCESS)) { NextDirInfo--; OrdinaryExchange->Info.CoreSearch.EmptyCheckResumeKeyBuffer = NextDirInfo->ResumeKey; OrdinaryExchange->Info.CoreSearch.EmptyCheckResumeKey = &OrdinaryExchange->Info.CoreSearch.EmptyCheckResumeKeyBuffer; } } else { //all that we do here is to setup the nextdirptr and the count in the OE ASSERT(OrdinaryExchange->OEType == SMBPSE_OETYPE_CORESEARCH); OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf = SmbGetUshort(&Response->Count); OrdinaryExchange->Info.CoreSearch.NextDirInfo = (PSMB_DIRECTORY_INFORMATION)(&Response->Buffer[0]+sizeof(UCHAR)+sizeof(USHORT)); IF_DEBUG { ULONG tcount = OrdinaryExchange->Info.CoreSearch.CountRemainingInSmbbuf; PSMB_DIRECTORY_INFORMATION ndi = OrdinaryExchange->Info.CoreSearch.NextDirInfo; RxDbgTrace(0, Dbg, ("--->coresearch: count/ndi=%08lx/%08lx\n",tcount,ndi)); if (tcount) { //DbgBreakPoint(); RxDbgTrace(0, Dbg, ("--->coresearch: firstfile=%s\n",&ndi->FileName[0])); } } } FINALLY: RxDbgTrace(-1, Dbg, ("MRxSmbFinishSearch returning %08lx\n", Status )); return Status; } NTSTATUS MRxSmbFinishQueryDiskInfo ( PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, PRESP_QUERY_INFORMATION_DISK Response ) /*++ Routine Description: This routine actually gets the stuff out of the VolInfo response and finishes the close. Arguments: OrdinaryExchange - the exchange instance Response - the response Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = (STATUS_SUCCESS); PFILE_FS_SIZE_INFORMATION UsersBuffer = OrdinaryExchange->Info.Buffer; PULONG BufferLength = OrdinaryExchange->Info.pBufferLength; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MRxSmbFinishQueryDiskInfo\n" )); SmbPseOEAssertConsistentLinkageFromOE("MRxSmbFinishQueryDiskInfo:"); //CODE.IMPROVEMENT this should be a macro......... IF_DEBUG{ PRX_CONTEXT RxContext = OrdinaryExchange->RxContext; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN ); } if (Response->WordCount != 5 || SmbGetUshort(&Response->ByteCount) != 0) { Status = STATUS_INVALID_NETWORK_RESPONSE; OrdinaryExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; goto FINALLY; } UsersBuffer->TotalAllocationUnits.QuadPart = SmbGetUshort(&Response->TotalUnits); UsersBuffer->AvailableAllocationUnits.QuadPart = SmbGetUshort(&Response->FreeUnits); UsersBuffer->SectorsPerAllocationUnit = SmbGetUshort(&Response->BlocksPerUnit); UsersBuffer->BytesPerSector = SmbGetUshort(&Response->BlockSize); *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION); FINALLY: RxDbgTrace(-1, Dbg, ("MRxSmbFinishQueryDiskInfo returning %08lx\n", Status )); return Status; } NTSTATUS MRxSmbExtendForCache ( IN OUT struct _RX_CONTEXT * RxContext, IN PLARGE_INTEGER pNewFileSize, OUT PLARGE_INTEGER pNewAllocationSize ) /*++ Routine Description: This routine handles network requests to extend the file for cached IO. we just share the core_info skeleton. Arguments: RxContext - the RDBSS context Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen; PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext); PSMB_EXCHANGE Exchange; PAGED_CODE(); if (capFcb->Attributes & FILE_ATTRIBUTE_COMPRESSED) { //here, we just get out since disk reservations don't do us any good.... pNewAllocationSize->QuadPart = (pNewFileSize->QuadPart)<<2; return(STATUS_SUCCESS); } if (capFcb->pNetRoot->Type == NET_ROOT_PRINT) { pNewAllocationSize->QuadPart = pNewFileSize->QuadPart; // invalidate the name based file info cache since it is almost impossible // to know the last write time of the file on the server. MRxSmbInvalidateFileInfoCache(RxContext); return(STATUS_SUCCESS); } RxDbgTrace(+1, Dbg, ("MRxSmbExtendForCache %08lx %08lx %08lx %08lx\n", capFcb->Header.FileSize.LowPart, capFcb->Header.AllocationSize.LowPart, pNewFileSize->LowPart, pNewAllocationSize->LowPart )); ASSERT( NodeType(capFobx->pSrvOpen) == RDBSS_NTC_SRVOPEN ); SrvOpen = capFobx->pSrvOpen; smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); if (FALSE) { DbgPrint("Extend top %08lx %08lx %08lx %08lx\n", capFcb->Header.FileSize.LowPart, capFcb->Header.AllocationSize.LowPart, pNewFileSize->LowPart, pNewAllocationSize->LowPart); } IF_NOT_MRXSMB_BUILD_FOR_DISCONNECTED_CSC{ NOTHING; } else { if (smbSrvOpen->hfShadow != 0){ NTSTATUS ShadowExtendNtStatus; ShadowExtendNtStatus = MRxSmbDCscExtendForCache(RxContext, pNewFileSize, pNewAllocationSize); if (ShadowExtendNtStatus != STATUS_MORE_PROCESSING_REQUIRED) { RxDbgTrace(-1, Dbg, ("MRxSmbExtendForCache returningDCON with status=%08lx\n", ShadowExtendNtStatus )); return(ShadowExtendNtStatus); } else { RxDbgTrace(0, Dbg, ("MRxSmbExtendForCache continueingDCON with status=%08lx\n", ShadowExtendNtStatus )); } } } //we just pass in our info into MRxSmbCoreInformation thru the existing pointers.... //we have two pointers.....the first two params are ptrs...... do { Status = MRxSmbCoreInformation(RxContext,0, (PVOID)pNewFileSize, (PULONG)pNewAllocationSize, SMBPSE_OE_FROM_EXTENDFILEFORCACHEING ); } while (Status == STATUS_RETRY); if (FALSE) { DbgPrint("Extend exit Status %lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", Status, capFcb->Header.FileSize.HighPart, capFcb->Header.FileSize.LowPart, capFcb->Header.AllocationSize.HighPart, capFcb->Header.AllocationSize.LowPart, pNewFileSize->HighPart, pNewFileSize->LowPart, pNewAllocationSize->HighPart, pNewAllocationSize->LowPart); } // Only invalidate if Allocation Size changed. // Unless we close, the Last Access time or Last Written time is // not guaranteed to change for Ntfs (one file system). // If it is not guaranteed for one file system, we needn't guarantee for any. // So, Don't have to Invalidate our FileInfo Cache. if (capFcb->Header.AllocationSize.QuadPart < pNewAllocationSize->QuadPart) { MRxSmbInvalidateFileInfoCache(RxContext); MRxSmbInvalidateFullDirectoryCacheParent(RxContext, TRUE); } RxLog(("Extend exit %lx %lx %lx %lx %lx\n", RxContext, capFcb->Header.FileSize.LowPart, capFcb->Header.AllocationSize.LowPart, pNewFileSize->LowPart, pNewAllocationSize->LowPart)); SmbLog(LOG, MRxSmbExtendForCache, LOGPTR(RxContext) LOGULONG(capFcb->Header.FileSize.LowPart) LOGULONG(capFcb->Header.AllocationSize.LowPart) LOGULONG(pNewFileSize->LowPart) LOGULONG(pNewAllocationSize->LowPart)); RxDbgTrace(-1, Dbg, ("MRxSmbExtendForCache exit with status=%08lx %08lx %08lx\n", Status, pNewFileSize->LowPart, pNewAllocationSize->LowPart)); return(Status); } NTSTATUS MRxSmbExtendForNonCache( IN OUT struct _RX_CONTEXT * RxContext, IN PLARGE_INTEGER pNewFileSize, OUT PLARGE_INTEGER pNewAllocationSize ) /*++ Routine Description: This routine handles network requests to extend the file for noncached IO. since the write itself will extend the file, we can pretty much just get out quickly. Arguments: RxContext - the RDBSS context Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; //RxCaptureFcb; RxCaptureFobx; //PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext); //PSMB_EXCHANGE Exchange; PAGED_CODE(); pNewAllocationSize->QuadPart = pNewFileSize->QuadPart; // invalidate the name based file info cache since it is almost impossible // to know the last write time of the file on the server. MRxSmbInvalidateFileInfoCache(RxContext); MRxSmbInvalidateFullDirectoryCacheParent(RxContext, TRUE); return(Status); } NTSTATUS MRxSmbGetNtAllocationInfo ( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE ) /*++ Routine Description: gets the nt allocation information by doing a simple transact........ Arguments: OrdinaryExchange - an exchange to be used for conducting this open. Return Value: RXSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); REQ_QUERY_FILE_INFORMATION FileInfo; PAGED_CODE(); FileInfo.Fid = smbSrvOpen->Fid; FileInfo.InformationLevel = SMB_QUERY_FILE_STANDARD_INFO; Status = MRxSmbSimpleSyncTransact2( SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, SMBPSE_OETYPE_T2_FOR_NT_FILE_ALLOCATION_INFO, TRANS2_QUERY_FILE_INFORMATION, &FileInfo,sizeof(FileInfo), NULL,0 ); return(Status); } NTSTATUS __MRxSmbSimpleSyncTransact2( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE, IN SMB_PSE_ORDINARY_EXCHANGE_TYPE OEType, IN ULONG TransactSetupCode, IN PVOID Params, IN ULONG ParamsLength, IN PVOID Data, IN ULONG DataLength, IN PSMB_PSE_OE_T2_FIXUP_ROUTINE FixupRoutine ) /*++ Routine Description: This routine does a simple 1-in-1out transact2 Arguments: OrdinaryExchange - an exchange to be used for conducting this open. Return Value: RXSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange; PSMBSTUFFER_BUFFER_STATE StufferState; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MRxSmbSimpleSyncTransact2 entering.......OE=%08lx\n",OrdinaryExchange)); StufferState = &OrdinaryExchange->AssociatedStufferState; ASSERT( StufferState ); COVERED_CALL(MRxSmbStartSMBCommand (StufferState,SetInitialSMB_ForReuse,SMB_COM_TRANSACTION2, SMB_REQUEST_SIZE(TRANSACTION), NO_EXTRA_DATA,SMB_BEST_ALIGNMENT(1,0),RESPONSE_HEADER_SIZE_NOT_SPECIFIED, 0,0,0,0 STUFFERTRACE(Dbg,'FC')) ); MRxSmbDumpStufferState (1100,"SMB w/ pseT2 before stuffing",StufferState); //the return sizes of 100 and 800 are chosen arbitrarily. MRxSmbStuffSMB (StufferState, "0wwwwdD", // 0 UCHAR WordCount; // Count of parameter words; value = (14 + SetupCount) ParamsLength, // w _USHORT( TotalParameterCount ); // Total parameter bytes being sent DataLength, // w _USHORT( TotalDataCount ); // Total data bytes being sent 100, // w _USHORT( MaxParameterCount ); // Max parameter bytes to return 800, // w _USHORT( MaxDataCount ); // Max data bytes to return 0, // d . UCHAR MaxSetupCount; // Max setup words to return // . UCHAR Reserved; // . _USHORT( Flags ); // Additional information: // // bit 0 - also disconnect TID in Tid // // bit 1 - one-way transacion (no resp) // D _ULONG( Timeout ); SMB_OFFSET_CHECK(TRANSACTION,Timeout) 0, STUFFER_CTL_NORMAL, "wwpwQyyw", 0, // w _USHORT( Reserved2 ); ParamsLength, // w _USHORT( ParameterCount ); // Parameter bytes sent this buffer // p _USHORT( ParameterOffset ); // Offset (from header start) to params DataLength, // w _USHORT( DataCount ); // Data bytes sent this buffer // Q _USHORT( DataOffset ); // Offset (from header start) to data SMB_OFFSET_CHECK(TRANSACTION,DataOffset) 1, // y UCHAR SetupCount; // Count of setup words 0, // y UCHAR Reserved3; // Reserved (pad above to word) // UCHAR Buffer[1]; // Buffer containing: TransactSetupCode, // w //USHORT Setup[]; // Setup words (# = SetupWordCount) STUFFER_CTL_NORMAL, "ByS6cS5c!", SMB_WCT_CHECK(15) // B //USHORT ByteCount; // Count of data bytes 0, // y //UCHAR Name[]; // Name of transaction (NULL if Transact2) // S //UCHAR Pad[]; // Pad to SHORT or LONG // 6c //UCHAR Parameters[]; // Parameter bytes (# = ParameterCount) ParamsLength,Params, //CODE.IMPROVEMENT altho every server seems to take it, this should // be conditioned on datalength!=0 // S //UCHAR Pad1[]; // Pad to SHORT or LONG // 5c //UCHAR Data[]; // Data bytes (# = DataCount) DataLength,Data ); MRxSmbDumpStufferState (700,"SMB w/ pseT2 after stuffing",StufferState); //ASSERT(!"Now it's stuffed"); if (FixupRoutine) { Status = FixupRoutine(OrdinaryExchange); if (Status!=STATUS_SUCCESS) { goto FINALLY; } } Status = SmbPseOrdinaryExchange(SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS, OEType ); FINALLY: RxDbgTrace(-1, Dbg, ("MRxSmbSimpleSyncTransact2 exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status)); return(Status); } NTSTATUS MRxSmbFinishTransaction2 ( IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, IN PRESP_TRANSACTION Response ) /*++ Routine Description: This routine finishes a transact2. what it does depends on the OE_TYPE. Arguments: OrdinaryExchange - the exchange instance Response - the response Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = (STATUS_SUCCESS); PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState; PAGED_CODE(); //could actually be nonpaged RxDbgTrace(+1, Dbg, ("MRxSmbFinishTransaction2\n", 0 )); SmbPseOEAssertConsistentLinkageFromOE("MRxSmbFinishTransaction2:"); if (OrdinaryExchange->BytesAvailableCopy < sizeof(SMB_HEADER) + (ULONG)FIELD_OFFSET(RESP_TRANSACTION, Buffer)) { return STATUS_INVALID_NETWORK_RESPONSE; } switch (OrdinaryExchange->OEType) { case SMBPSE_OETYPE_T2_FOR_NT_FILE_ALLOCATION_INFO:{ PFILE_STANDARD_INFORMATION StandardInfo; if ( (Response->WordCount!=10) || (SmbGetUshort(&Response->TotalParameterCount)!=2) || (SmbGetUshort(&Response->ParameterCount)!=2) || (SmbGetUshort(&Response->ParameterDisplacement)!=0) || ((SmbGetUshort(&Response->TotalDataCount)>24) || (SmbGetUshort(&Response->TotalDataCount)<22)) || ((SmbGetUshort(&Response->DataCount)>24)||(SmbGetUshort(&Response->DataCount)<22)) || (SmbGetUshort(&Response->DataDisplacement)!=0)) { Status = STATUS_INVALID_NETWORK_RESPONSE; RxDbgTrace(+1, Dbg, ("Invalid parameter(s) received.\n", 0 )); break; } if (OrdinaryExchange->BytesAvailableCopy < SmbGetUshort(&Response->DataOffset)+FIELD_OFFSET(FILE_STANDARD_INFORMATION, Directory) + sizeof(BOOLEAN)) { Status = STATUS_INVALID_NETWORK_RESPONSE; break; } StandardInfo = (PFILE_STANDARD_INFORMATION) (StufferState->BufferBase+SmbGetUshort(&Response->DataOffset)); OrdinaryExchange->Transact2.AllocationSize.LowPart = SmbGetUlong(&StandardInfo->AllocationSize.LowPart); OrdinaryExchange->Transact2.AllocationSize.HighPart = SmbGetUlong(&StandardInfo->AllocationSize.HighPart); RxDbgTrace(0, Dbg, ("MRxSmbFinishTransaction2 nt allocation %08lx\n", OrdinaryExchange->Transact2.AllocationSize.LowPart )); }break; case SMBPSE_OETYPE_T2_FOR_LANMAN_DISKATTRIBUTES_INFO:{ PFILE_FS_SIZE_INFORMATION UsersBuffer = OrdinaryExchange->Info.Buffer; PULONG BufferLength = OrdinaryExchange->Info.pBufferLength; PQFS_ALLOCATE QfsInfo; if (Response->WordCount != 10 || SmbGetUshort(&Response->DataDisplacement) != 0 || SmbGetUshort(&Response->DataCount) != ACTUAL_QFS_ALLOCATE_LENGTH || SmbGetUshort(&Response->TotalDataCount) != ACTUAL_QFS_ALLOCATE_LENGTH || SmbGetUshort(&Response->TotalParameterCount) != SmbGetUshort(&Response->ParameterCount)) { Status = STATUS_INVALID_NETWORK_RESPONSE; RxDbgTrace(+1, Dbg, ("Invalid parameter(s) received.\n", 0 )); break; } if ( OrdinaryExchange->BytesAvailableCopy < SmbGetUshort( &Response->DataOffset ) + ACTUAL_QFS_ALLOCATE_LENGTH ) { Status = STATUS_INVALID_NETWORK_RESPONSE; break; } QfsInfo = (PQFS_ALLOCATE)(StufferState->BufferBase+SmbGetUshort(&Response->DataOffset)); UsersBuffer->TotalAllocationUnits.QuadPart = SmbGetUlong(&QfsInfo->cUnit); UsersBuffer->AvailableAllocationUnits.QuadPart = SmbGetUlong(&QfsInfo->cUnitAvail); UsersBuffer->SectorsPerAllocationUnit = SmbGetUlong(&QfsInfo->cSectorUnit); UsersBuffer->BytesPerSector = SmbGetUshort(&QfsInfo->cbSector); *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION); //CODE.IMPROVEMENT shouldn't this be done above?? RxDbgTrace(0, Dbg, ("MRxSmbFinishTransaction2 allocation %08lx\n", OrdinaryExchange->Transact2.AllocationSize.LowPart )); }break; case SMBPSE_OETYPE_T2_FOR_LANMAN_VOLUMELABEL_INFO:{ PFILE_FS_VOLUME_INFORMATION UsersBuffer = OrdinaryExchange->Info.Buffer; PULONG BufferLength = OrdinaryExchange->Info.pBufferLength; PQFS_INFO QfsInfo; ULONG LabelLength; PBYTE VolumeLabel = &OrdinaryExchange->Info.QFSVolInfo.CoreLabel[0]; if (Response->WordCount != 10 || SmbGetUshort(&Response->DataDisplacement) != 0 || SmbGetUshort(&Response->TotalParameterCount) != SmbGetUshort(&Response->ParameterCount)) { Status = STATUS_INVALID_NETWORK_RESPONSE; RxDbgTrace(+1, Dbg, ("Invalid parameter(s) received.\n", 0 )); break; } if (OrdinaryExchange->BytesAvailableCopy < SmbGetUshort(&Response->DataOffset) + (ULONG)FIELD_OFFSET(QFS_INFO, szVolLabel)) { Status = STATUS_INVALID_NETWORK_RESPONSE; break; } QfsInfo = (PQFS_INFO)(StufferState->BufferBase+SmbGetUshort(&Response->DataOffset)); UsersBuffer->VolumeSerialNumber = SmbGetUlong(&QfsInfo->ulVSN); //copy the volumelabel to the right place in the OE where it can UNICODE-ized by the routine above LabelLength = min(QfsInfo->cch,12); if (OrdinaryExchange->BytesAvailableCopy < SmbGetUshort(&Response->DataOffset)+FIELD_OFFSET(QFS_INFO, szVolLabel) + LabelLength) { Status = STATUS_INVALID_NETWORK_RESPONSE; break; } RtlCopyMemory(VolumeLabel,&QfsInfo->szVolLabel[0],LabelLength); VolumeLabel[LabelLength] = 0; RxDbgTrace(0, Dbg, ("MRxSmbFinishTransaction2 volinfo serialnum= %08lx\n", UsersBuffer->VolumeSerialNumber )); }break; case SMBPSE_OETYPE_T2_FOR_ONE_FILE_DIRCTRL:{ //do nothing here....everything is done back in the caller with the //whole buffer having been copied..... RxDbgTrace(0, Dbg, ("MRxSmbFinishTransaction2 one file \n")); }break; default: Status = STATUS_INVALID_NETWORK_RESPONSE; } RxDbgTrace(-1, Dbg, ("MRxSmbFinishTransaction2 returning %08lx\n", Status )); return Status; }