/*++ Copyright (c) 1990 Microsoft Corporation Module Name: print.c Abstract: This module implements the routines that support remote print devices. Author: Colin Watson (ColinW) 24-Dec-1990 Revision History: 24-Dec-1990 ColinW Created Notes: --*/ #define INCLUDE_SMB_PRINT #define INCLUDE_SMB_READ_WRITE #define INCLUDE_SMB_TRANSACTION #include "precomp.h" #pragma hdrstop #define SPOOLER_DEVICE 0x53 #define GET_PRINTER_ID 0x60 typedef struct _PrintOpenContext { TRANCEIVE_HEADER Header; // Generic transaction context header PICB Icb; // ICB to fill in. ULONG OpenAction; // Action taken on open. } PRINTOPENCONTEXT, *PPRINTOPENCONTEXT; typedef struct _PRINT_WRITE_CONTEXT { TRANCEIVE_HEADER Header; ULONG WriteAmount; // Number of bytes actually written. } PRINT_WRITE_CONTEXT, *PPRINT_WRITE_CONTEXT; // // Force misalignment of the following structure // #ifndef NO_PACKING #include #endif // ndef NO_PACKING typedef struct _PRINT_QUEUE_INFORMATION { RESP_GET_PRINT_QUEUE Header; _USHORT( DataLength ); // Length of data SMB_DATE CreationDate; SMB_TIME CreationTime; UCHAR Status; _USHORT( FileNumber ); _ULONG( FileDataSize ); // File end of data UCHAR Reserved; UCHAR UserName[UNLEN]; } PRINT_QUEUE_INFORMATION, *PPRINT_QUEUE_INFORMATION; typedef struct _SMB_RESP_PRINT_JOB_ID { USHORT JobId; UCHAR ServerName[LM20_CNLEN+1]; UCHAR QueueName[LM20_QNLEN+1]; UCHAR Padding; // Unknown what this padding is.. } SMB_RESP_PRINT_JOB_ID, *PSMB_RESP_PRINT_JOB_ID; // // Turn structure packing back off // #ifndef NO_PACKING #include #endif // ndef NO_PACKING typedef struct _GET_JOBID_CONTEXT { TRANCEIVE_HEADER Header; PICB Icb; SMB_RESP_PRINT_JOB_ID Response; } GET_JOBID_CONTEXT, *PGET_JOBID_CONTEXT; DBGSTATIC STANDARD_CALLBACK_HEADER ( CreatePrintCallback ); DBGSTATIC STANDARD_CALLBACK_HEADER( GetJobIdCallback ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, RdrCreatePrintFile) #pragma alloc_text(PAGE, RdrWritePrintFile) #pragma alloc_text(PAGE, RdrEnumPrintFile) #pragma alloc_text(PAGE, RdrGetPrintJobId) #pragma alloc_text(PAGE3FILE, GetJobIdCallback) #pragma alloc_text(PAGE3FILE, CreatePrintCallback) #endif NTSTATUS RdrCreatePrintFile ( IN PICB Icb, IN PIRP Irp ) /*++ Routine Description: This routine creates a print file over the network. Arguments: IN PICB Icb - Supplies the ICB containing the file information IN PIRP Irp - Supplies the IRP to be used for the Open request Return Value: NTSTATUS - Status of open. --*/ { PSMB_BUFFER SmbBuffer; PSMB_HEADER Smb; PREQ_OPEN_PRINT_FILE Open; PSZ Buffer; PPRINTOPENCONTEXT OpenContext = NULL; PCONNECTLISTENTRY Connection = Icb->Fcb->Connection; PSECURITY_ENTRY Se = Icb->Se; NTSTATUS Status; PAGED_CODE(); dprintf(DPRT_CREATE, ("Create Print file %wZ.\n",&Icb->Fcb->FileName)); ASSERT(Se->Signature == STRUCTURE_SIGNATURE_SECURITYENTRY); ASSERT(Connection->Signature == STRUCTURE_SIGNATURE_CONNECTLISTENTRY); if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) { return STATUS_INSUFFICIENT_RESOURCES; } OpenContext = ALLOCATE_POOL(NonPagedPool, sizeof(PRINTOPENCONTEXT), POOL_OPENPRINTCONTEXT); if (OpenContext == NULL) { RdrFreeSMBBuffer(SmbBuffer); return STATUS_INSUFFICIENT_RESOURCES; } Smb = (PSMB_HEADER )SmbBuffer->Buffer; Smb->Command = SMB_COM_OPEN_PRINT_FILE; Open = (PREQ_OPEN_PRINT_FILE) (Smb+1); Open->WordCount = 2; SmbPutUshort(&Open->Mode, 1); // Hard code to Graphics- dont expand tabs Buffer = (PSZ)Open->Buffer; *Buffer++ = SMB_FORMAT_ASCII; // // Copy the username into the buffer. // if (Connection->Server->Capabilities & DF_UNICODE) { Buffer = ALIGN_SMB_WSTR(Buffer); RdrCopyUnicodeUserName((PWSTR *)&Buffer, Se); *((PWCH)Buffer)++ = UNICODE_NULL; // Null terminate the user's name. } else { RdrCopyUserName(&Buffer, Se); *Buffer++ = 0; // Null terminate the users name. } SmbPutUshort(&Open->SetupLength, 0); // No setup data supported SmbPutUshort(&Open->ByteCount, (USHORT )(Buffer-(PUCHAR )Open->Buffer)); SmbBuffer->Mdl->ByteCount = Buffer - (PUCHAR )Smb; OpenContext->Header.Type = CONTEXT_PRINT_OPEN; OpenContext->Header.TransferSize = SmbBuffer->Mdl->ByteCount + sizeof(RESP_OPEN_PRINT_FILE); OpenContext->Icb = Icb; Status = RdrNetTranceiveWithCallback( NT_NORMAL, Irp, // Irp Connection, SmbBuffer->Mdl, OpenContext, CreatePrintCallback, Se, NULL); RdrFreeSMBBuffer(SmbBuffer); if (!NT_SUCCESS(Status)) { FREE_POOL(OpenContext); return Status; } Irp->IoStatus.Information = RdrUnmapDisposition(FILE_OPENED); Icb->Type = PrinterFile; Icb->NonPagedFcb->Type = PrinterFile; Icb->NonPagedFcb->FileType = FileTypePrinter; Icb->Fcb->ServerFileId = 0; Icb->Fcb->AccessGranted = SMB_ACCESS_WRITE_ONLY; Icb->Fcb->Attribute = FILE_ATTRIBUTE_NORMAL; // // The SpoolOpen SMB doesn't give us these other time fields. // Icb->Fcb->LastWriteTime.HighPart = 0; Icb->Fcb->LastWriteTime.LowPart = 0; Icb->Fcb->CreationTime.HighPart = 0; Icb->Fcb->CreationTime.LowPart = 0; Icb->Fcb->LastAccessTime.HighPart = 0; Icb->Fcb->LastAccessTime.LowPart = 0; Icb->Fcb->ChangeTime.HighPart = 0; Icb->Fcb->ChangeTime.LowPart = 0; Icb->Fcb->Header.FileSize.HighPart = 0x7fffffff; Icb->Fcb->Header.FileSize.LowPart = 0xffffffff; // // Flag that this ICB has a handle associated with it, and // thus that it must be closed when the local file is closed. // Icb->Flags |= ICB_HASHANDLE; FREE_POOL(OpenContext); return Status; } DBGSTATIC STANDARD_CALLBACK_HEADER ( CreatePrintCallback ) /*++ Routine Description: This routine is the callback routine for the processing of an Open SMB. It copies the resulting information from the Open SMB into either the context block or the ICB supplied directly. Arguments: IN PSMB_HEADER Smb - SMB response from server. IN PMPX_ENTRY MpxTable - MPX table entry for request. IN POPENANDXCONTEXT Context- Context from caller. IN BOOLEAN ErrorIndicator - TRUE if error indication IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication. IN OUT PIRP *Irp - IRP from TDI Return Value: NTSTATUS - STATUS_PENDING if we are to complete the request --*/ { PRESP_OPEN_PRINT_FILE OpenResponse; PPRINTOPENCONTEXT Context = Ctx; PFCB Fcb = Context->Icb->Fcb; NTSTATUS Status; ASSERT(Context->Header.Type == CONTEXT_PRINT_OPEN); DISCARDABLE_CODE(RdrFileDiscardableSection); dprintf(DPRT_CREATE, ("OpenComplete\n")); Context->Header.ErrorType = NoError; // Assume no error at first. // // If we are called because the VC dropped, indicate it in the response // if (ErrorIndicator) { Context->Header.ErrorType = NetError; Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode); goto ReturnStatus; } if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) { Context->Header.ErrorType = SMBError; Context->Header.ErrorCode = Status; goto ReturnStatus; } OpenResponse = (PRESP_OPEN_PRINT_FILE)(Smb+1); Context->Icb->FileId = SmbGetUshort(&OpenResponse->Fid); ReturnStatus: KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE); return STATUS_SUCCESS; UNREFERENCED_PARAMETER(MpxEntry); UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(SmbLength); UNREFERENCED_PARAMETER(Server); } NTSTATUS RdrWritePrintFile( IN PIRP Irp OPTIONAL, IN PFILE_OBJECT FileObject, IN PMDL DataMdl, IN PCHAR TransferStart, IN ULONG Length, IN BOOLEAN WaitForCompletion, IN PWRITE_COMPLETION_ROUTINE CompletionRoutine, IN PVOID CompletionContext, OUT PBOOLEAN AllDataWritten, OUT PULONG AmountActuallyWritten ) /*++ Routine Description: This routine uses the core SMB write protocol to write from the specified file. Arguments: IN PIRP Irp - Supplies an IRP to use for the raw write request. IN PICB Icb - Supplies an ICB for the file to write. IN PMDL DataMdl - Supplies the Mdl containing the data to write. IN PCHAR TransferStart - Supplies the address of the start of the Xfer. IN ULONG Length - Supplies the total number of bytes to write. OUT PBOOLEAN AllDataWritten - Returns true if all the data requested were written OUT PULONG AmountActuallyWritten - Returns the number of bytes written. Return Value: NTSTATUS - Status of write request. --*/ { PSMB_BUFFER SmbBuffer = NULL; PSMB_HEADER Smb; PREQ_WRITE_PRINT_FILE Write; NTSTATUS Status; PMDL PartialMdl; PICB Icb = FileObject->FsContext2; ULONG SrvWriteSize = Icb->Fcb->Connection->Server->BufferSize - (sizeof(SMB_HEADER)+sizeof(REQ_WRITE)); ULONG AmountRequestedToWrite = MIN(Length, SrvWriteSize); PAGED_CODE(); // // Allocate an SMB buffer for the write operation. // // Since write is a "little data" operation, we can use // NetTranceiveWithCallback and forgo creating an SMB buffer // for the receive. This is ok, since we are only interested in the // amount of data actually written anyway. // if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnError; } Smb = (PSMB_HEADER )(SmbBuffer->Buffer); Smb->Command = SMB_COM_WRITE_PRINT_FILE; Write = (PREQ_WRITE_PRINT_FILE )(Smb+1); Write->WordCount = 1; SmbPutUshort(&Write->Fid, Icb->FileId); SmbPutUshort(&Write->ByteCount, (USHORT)( AmountRequestedToWrite + 3)); Write->Buffer[0] = SMB_FORMAT_DATA; SmbPutUshort((PUSHORT)&Write->Buffer[1], (USHORT)AmountRequestedToWrite); // // Set the number of bytes to send in this request. // SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+ FIELD_OFFSET(REQ_WRITE_PRINT_FILE, Buffer[3]); // // Allocate an MDL large enough to hold this piece of // the request. // PartialMdl = IoAllocateMdl(TransferStart, AmountRequestedToWrite, // Length FALSE, // Secondary Buffer FALSE, // Charge Quota NULL); if (PartialMdl == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnError; } // // If there is no MDL for this read, probe the data MDL to lock it's // pages down. // // Otherwise, use the data MDL as a partial MDL and lock the pages // accordingly // if (!ARGUMENT_PRESENT(DataMdl)) { try { if (ARGUMENT_PRESENT(Irp)) { MmProbeAndLockPages(PartialMdl, Irp->RequestorMode, IoReadAccess); } else { MmProbeAndLockPages(PartialMdl, KernelMode, IoReadAccess); } } except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(PartialMdl); Status = GetExceptionCode(); goto ReturnError; } } else { IoBuildPartialMdl(DataMdl, PartialMdl, TransferStart, AmountRequestedToWrite); } ASSERT( PartialMdl->ByteCount==AmountRequestedToWrite ); // // Now link this new MDL into the SMB buffer we allocated for // the receive. // SmbBuffer->Mdl->Next = PartialMdl; Status = RdrNetTranceive(NT_NORMAL, // Flags Irp, Icb->Fcb->Connection, SmbBuffer->Mdl, NULL, // Only interested in the error code. Icb->Se); if (Status == STATUS_INVALID_HANDLE) { RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId); } if (!ARGUMENT_PRESENT(DataMdl)) { MmUnlockPages(PartialMdl); } IoFreeMdl(PartialMdl); if (!NT_SUCCESS(Status)) { // // Bail out on failure. // goto ReturnError; } *AllDataWritten = TRUE; *AmountActuallyWritten = AmountRequestedToWrite; ReturnError: if (SmbBuffer!=NULL) { RdrFreeSMBBuffer(SmbBuffer); } return Status; } NTSTATUS RdrEnumPrintFile ( IN PICB Icb, IN PIRP Irp, IN ULONG Index, OUT PLMR_GET_PRINT_QUEUE UserBuffer ) /*++ Routine Description: This routine returns information on a particular print file over the network from any server. It is intended for core servers but will work on any server. Arguments: IN PICB Icb - Supplies the ICB containing the file information IN PIRP Irp - Supplies the IRP to be used for the request IN ULONG Index - Supplies the entry in the print queue to return OUT PLMR_GET_PRINT_QUEUE UserBuffer Return Value: NTSTATUS - Status of operation. --*/ { PSMB_BUFFER SmbBuffer; PSMB_BUFFER ReceiveSmbBuffer; PSMB_HEADER Smb; PSMB_HEADER ReceiveSmb; PREQ_GET_PRINT_QUEUE Request; PPRINT_QUEUE_INFORMATION Response; SMB_TIME Time; SMB_DATE Date; NTSTATUS Status; PAGED_CODE(); if (((SmbBuffer = RdrAllocateSMBBuffer())==NULL) || ((ReceiveSmbBuffer = RdrAllocateSMBBuffer())==NULL)) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ReturnError; } Smb = (PSMB_HEADER )SmbBuffer->Buffer; Smb->Command = SMB_COM_GET_PRINT_QUEUE; Request = (PREQ_GET_PRINT_QUEUE) (Smb+1); Request->WordCount = 2; SmbPutUshort(&Request->MaxCount, 1); SmbPutUshort(&Request->StartIndex, (USHORT)Index); SmbPutUshort(&Request->ByteCount, 0 ); SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_GET_PRINT_QUEUE); // // Set the size of the data to be received into the SMB buffer. // ReceiveSmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) + sizeof(PRINT_QUEUE_INFORMATION); Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, Irp, Icb->Fcb->Connection, SmbBuffer->Mdl, ReceiveSmbBuffer->Mdl, Icb->Se); ReceiveSmb = (PSMB_HEADER )ReceiveSmbBuffer->Buffer; Response = (PPRINT_QUEUE_INFORMATION) (ReceiveSmb+1); if (NT_SUCCESS(Status) && (SmbGetUshort(&Response->Header.Count) == 1)) { // Copy in the UserName as a string UserBuffer->OriginatorName.MaximumLength = UNLEN; UserBuffer->OriginatorName.Length = UNLEN; UserBuffer->OriginatorName.Buffer = (PCHAR)Irp->UserBuffer + sizeof(LMR_GET_PRINT_QUEUE); // // Calculate the real length of the user name. Take whatever // came from the server and remove trailing null and space // characters. // NAME_LENGTH(UserBuffer->OriginatorName.Length, Response->UserName, MIN( UNLEN, SmbGetUshort(&Response->DataLength)-12)); RtlCopyMemory( (PCHAR)UserBuffer + sizeof(LMR_GET_PRINT_QUEUE), Response->UserName, UserBuffer->OriginatorName.Length); // Now we have the username, fill in all other values SmbMoveTime (&Time, &Response->CreationTime); SmbMoveDate (&Date, &Response->CreationDate); UserBuffer->CreateTime = RdrConvertSmbTimeToTime(Time, Date, Icb->Fcb->Connection->Server); UserBuffer->EntryStatus = Response->Status; UserBuffer->FileNumber = SmbGetUshort( &Response->FileNumber ); UserBuffer->FileSize = SmbGetUlong( &Response->FileDataSize ); UserBuffer->RestartIndex = SmbGetUshort(&Response->Header.RestartIndex); } else { // Some down level servers forget to return an error at end of queue Status = STATUS_NO_MORE_FILES; } ReturnError: if (SmbBuffer!=NULL) { RdrFreeSMBBuffer(SmbBuffer); } if (ReceiveSmbBuffer!=NULL) { RdrFreeSMBBuffer(ReceiveSmbBuffer); } return Status; } NTSTATUS RdrGetPrintJobId ( IN PICB Icb, IN PIRP Irp, OUT PQUERY_PRINT_JOB_INFO OutputBuffer ) /*++ Routine Description: This routine returns the print job ID of the specified print job on the remote server. Arguments: IN PICB Icb - Supplies the ICB containing the file information IN PIRP Irp - Supplies the IRP to be used for the request OUT PULONG JobId Return Value: NTSTATUS - Status of operation. --*/ { PSMB_BUFFER SmbBuffer = NULL; PSMB_HEADER Smb; PREQ_IOCTL Request; NTSTATUS Status; GET_JOBID_CONTEXT Context; UNICODE_STRING UnicodeString; OEM_STRING OemString; BOOLEAN FcbLocked = FALSE; PAGED_CODE(); // // This API is only supported to OS/2 servers. // try { if (!FlagOn(Icb->Fcb->Connection->Server->Capabilities, DF_LANMAN10)) { try_return(Status = STATUS_NOT_SUPPORTED); } if (Icb->Flags & ICB_DEFERREDOPEN) { RdrAcquireFcbLock(Icb->Fcb, ExclusiveLock, TRUE); FcbLocked = TRUE; // // If this print file isn't opened yet, create a print job. // if (!NT_SUCCESS(Status = RdrCreatePrintFile(Icb, Irp))) { try_return(Status); } Icb->Flags &= ~ICB_DEFERREDOPEN; } else { RdrAcquireFcbLock(Icb->Fcb, SharedLock, TRUE); FcbLocked = TRUE; } Context.Header.Type = CONTEXT_GET_JOBID; Context.Icb = Icb; if ((SmbBuffer = RdrAllocateSMBBuffer()) == NULL) { try_return(Status = STATUS_INSUFFICIENT_RESOURCES); } Smb = (PSMB_HEADER )SmbBuffer->Buffer; Smb->Command = SMB_COM_IOCTL; Request = (PREQ_IOCTL) (Smb+1); RtlZeroMemory( Request, sizeof(REQ_IOCTL) ); Request->WordCount = 14; SmbPutUshort( &Request->Fid, Icb->FileId ); SmbPutUshort( &Request->Category, SPOOLER_DEVICE ); SmbPutUshort( &Request->Function, GET_PRINTER_ID ); // // Set the size of the data to be received into the SMB buffer. // SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+FIELD_OFFSET(REQ_IOCTL, Buffer); Context.Header.TransferSize = sizeof(SMB_HEADER)+FIELD_OFFSET(REQ_IOCTL, Buffer) + sizeof(SMB_HEADER)+FIELD_OFFSET(RESP_IOCTL, Buffer) + sizeof(SMB_RESP_PRINT_JOB_ID); Status = RdrNetTranceiveWithCallback(NT_NORMAL | NT_NORECONNECT, Irp, Icb->Fcb->Connection, SmbBuffer->Mdl, &Context, GetJobIdCallback, Icb->Se, NULL); if (!NT_SUCCESS(Status)) { try_return(Status); } // // Now convert the output buffer into something we can use on NT. // OutputBuffer->JobId = Context.Response.JobId; RtlInitAnsiString(&OemString, Context.Response.ServerName); UnicodeString.Buffer = OutputBuffer->ServerName; UnicodeString.MaximumLength = sizeof(OutputBuffer->ServerName); Status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE); if (!NT_SUCCESS(Status)) { try_return(Status); } RtlInitAnsiString(&OemString, Context.Response.QueueName); UnicodeString.Buffer = OutputBuffer->QueueName; UnicodeString.MaximumLength = sizeof(OutputBuffer->QueueName); Status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE); if (!NT_SUCCESS(Status)) { try_return(Status); } try_exit:NOTHING; } finally { if (SmbBuffer!=NULL) { RdrFreeSMBBuffer(SmbBuffer); } if (FcbLocked) { RdrReleaseFcbLock(Icb->Fcb); } } return Status; } DBGSTATIC STANDARD_CALLBACK_HEADER( GetJobIdCallback ) /*++ Routine Description: This routine is the callback routine for the processing of a Write SMB. It copies the interesting information from the Write SMB response into into the context structure. As of now, we are only interested in amount written in the SMB. Arguments: IN PSMB_HEADER Smb - SMB response from server. IN PMPX_ENTRY MpxTable - MPX table entry for request. IN PWRITE_CONTEXT Context - Context from caller. IN BOOLEAN ErrorIndicator - TRUE if error indication IN NTSTATUS NetworkErrorCode OPTIONAL - Network error if error indication. IN OUT PIRP *Irp - IRP from TDI Return Value: NTSTATUS - STATUS_PENDING if we are to complete the request --*/ { PRESP_IOCTL IoctlResponse; PGET_JOBID_CONTEXT Context = Ctx; NTSTATUS Status; DISCARDABLE_CODE(RdrFileDiscardableSection); ASSERT(Context->Header.Type == CONTEXT_GET_JOBID); dprintf(DPRT_PRINT, ("GetPrintJobId\n")); Context->Header.ErrorType = NoError; // Assume no error at first. // // If we are called because the VC dropped, indicate it in the response // if (ErrorIndicator) { Context->Header.ErrorType = NetError; Context->Header.ErrorCode = RdrMapNetworkError(NetworkErrorCode); goto ReturnStatus; } if (!NT_SUCCESS(Status = RdrMapSmbError(Smb, Server))) { if (Status == STATUS_INVALID_HANDLE) { RdrInvalidateFileId(Context->Icb->NonPagedFcb, Context->Icb->FileId); } Context->Header.ErrorType = SMBError; Context->Header.ErrorCode = Status; goto ReturnStatus; } IoctlResponse = (PRESP_IOCTL )(Smb+1); ASSERT (IoctlResponse->WordCount==8); ASSERT (SmbGetUshort(&IoctlResponse->DataCount) == sizeof(SMB_RESP_PRINT_JOB_ID)); if (IoctlResponse->DataCount == sizeof(SMB_RESP_PRINT_JOB_ID)) { TdiCopyLookaheadData(&Context->Response, (PCHAR)Smb+SmbGetUshort(&IoctlResponse->DataOffset), SmbGetUshort(&IoctlResponse->DataCount), ReceiveFlags); Context->Header.ErrorCode = STATUS_SUCCESS; } else { Context->Header.ErrorCode = STATUS_UNEXPECTED_NETWORK_ERROR; Context->Header.ErrorType = SMBError; } ReturnStatus: KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE); return STATUS_SUCCESS; if (SmbLength || MpxEntry || Irp || Server); }