/*++ Copyright (c) 1992-4 Microsoft Corporation Module Name: Close.c Abstract: This module implements the File Close routine for the NetWare redirector called by the dispatch driver. Author: Colin Watson [ColinW] 19-Dec-1992 Revision History: --*/ #include "Procs.h" // // The local debug trace level // #define Dbg (DEBUG_TRACE_CLOSE) // // Local procedure prototypes // NTSTATUS NwCommonClose ( IN PIRP_CONTEXT IrpContext ); NTSTATUS NwCloseRcb ( IN PIRP_CONTEXT IrpContext, IN PRCB Rcb ); NTSTATUS NwCloseIcb ( IN PIRP_CONTEXT IrpContext, IN PICB Icb ); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, NwFsdClose ) #pragma alloc_text( PAGE, NwCommonClose ) #pragma alloc_text( PAGE, NwCloseRcb ) #pragma alloc_text( PAGE, NwCloseIcb ) #endif NTSTATUS NwFsdClose ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of Close. Arguments: DeviceObject - Supplies the redirector device object. Irp - Supplies the Irp being processed Return Value: NTSTATUS - The FSD status for the IRP --*/ { NTSTATUS Status; PIRP_CONTEXT IrpContext = NULL; BOOLEAN TopLevel; PAGED_CODE(); DebugTrace(+1, Dbg, "NwFsdClose\n", 0); FsRtlEnterFileSystem(); NwReferenceUnlockableCodeSection (); // // Call the common Close routine // TopLevel = NwIsIrpTopLevel( Irp ); try { IrpContext = AllocateIrpContext( Irp ); Status = NwCommonClose( IrpContext ); } except(NwExceptionFilter( Irp, GetExceptionInformation() )) { if ( IrpContext == NULL ) { // // If we couldn't allocate an irp context, just complete // irp without any fanfare. // Status = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT ); } else { // // We had some trouble trying to perform the requested // operation, so we'll abort the I/O request with // the error status that we get back from the // execption code. // Status = NwProcessException( IrpContext, GetExceptionCode() ); } } if ( IrpContext ) { NwDequeueIrpContext( IrpContext, FALSE ); NwCompleteRequest( IrpContext, Status ); } if ( TopLevel ) { NwSetTopLevelIrp( NULL ); } // // And return to our caller // DebugTrace(-1, Dbg, "NwFsdClose -> %08lx\n", Status); NwDereferenceUnlockableCodeSection (); UNREFERENCED_PARAMETER( DeviceObject ); FsRtlExitFileSystem(); return Status; } NTSTATUS NwCommonClose ( IN PIRP_CONTEXT IrpContext ) /*++ Routine Description: This is the common routine for closing a file. Arguments: IrpContext - Supplies the Irp to process Return Value: NTSTATUS - the return status for the operation --*/ { PIRP Irp; PIO_STACK_LOCATION irpSp; NTSTATUS status; NODE_TYPE_CODE nodeTypeCode; PVOID fsContext, fsContext2; PAGED_CODE(); Irp = IrpContext->pOriginalIrp; irpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "NwCommonClose\n", 0); DebugTrace( 0, Dbg, "IrpContext = %08lx\n", (ULONG_PTR)IrpContext); DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG_PTR)Irp); DebugTrace( 0, Dbg, "FileObject = %08lx\n", (ULONG_PTR)irpSp->FileObject); try { // // Get the a referenced pointer to the node and make sure it is // not being closed. // if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject, &fsContext, &fsContext2 )) == NTC_UNDEFINED) { DebugTrace(0, Dbg, "The file is disconnected\n", 0); status = STATUS_INVALID_HANDLE; DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status ); try_return( NOTHING ); } // // Decide how to handle this IRP. // switch (nodeTypeCode) { case NW_NTC_RCB: // Close the file system status = NwCloseRcb( IrpContext, (PRCB)fsContext2 ); status = STATUS_SUCCESS; break; case NW_NTC_ICB: // Close the remote file case NW_NTC_ICB_SCB: // Close the SCB status = NwCloseIcb( IrpContext, (PICB)fsContext2 ); NwDereferenceUnlockableCodeSection (); break; #ifdef NWDBG default: // // This is not one of ours. // KeBugCheck( RDR_FILE_SYSTEM ); break; #endif } try_exit: NOTHING; } finally { // // Just in-case this handle was the last one before we unload. // NwUnlockCodeSections(TRUE); DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status); } return status; } NTSTATUS NwCloseRcb ( IN PIRP_CONTEXT IrpContext, IN PRCB Rcb ) /*++ Routine Description: The routine cleans up a RCB. Arguments: IrpContext - Supplies the IRP context pointers for this close. Rcb - Supplies the RCB for MSFS. Return Value: NTSTATUS - An appropriate completion status --*/ { NTSTATUS status; PAGED_CODE(); DebugTrace(+1, Dbg, "NwCloseRcb...\n", 0); // // Now acquire exclusive access to the Rcb // NwAcquireExclusiveRcb( Rcb, TRUE ); status = STATUS_SUCCESS; --Rcb->OpenCount; NwReleaseRcb( Rcb ); DebugTrace(-1, Dbg, "MsCloseRcb -> %08lx\n", status); // // And return to our caller // return status; } NTSTATUS NwCloseIcb ( IN PIRP_CONTEXT IrpContext, IN PICB Icb ) /*++ Routine Description: The routine cleans up an ICB. Arguments: IrpContext - Supplies the IRP context pointers for this close. Rcb - Supplies the RCB for MSFS. Return Value: NTSTATUS - An appropriate completion status --*/ { NTSTATUS Status; PNONPAGED_SCB pNpScb; PVCB Vcb; PFCB Fcb; PAGED_CODE(); DebugTrace(+1, Dbg, "NwCloseIcb...\n", 0); ASSERT( Icb->State == ICB_STATE_CLEANED_UP || Icb->State == ICB_STATE_CLOSE_PENDING ); // // If this is a remote file close the remote handle. // Status = STATUS_SUCCESS; IrpContext->Icb = Icb; Fcb = Icb->SuperType.Fcb; if (( Icb->NodeTypeCode == NW_NTC_ICB ) || ( Icb->NodeTypeCode == NW_NTC_DCB )) { pNpScb = Fcb->Scb->pNpScb; IrpContext->pNpScb = pNpScb; if ( Icb->HasRemoteHandle ) { Vcb = Fcb->Vcb; // // Dump the write behind cache. // Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb ); if ( !NT_SUCCESS( Status ) ) { IoRaiseInformationalHardError( STATUS_LOST_WRITEBEHIND_DATA, &Fcb->FullFileName, (PKTHREAD)IrpContext->pOriginalIrp->Tail.Overlay.Thread ); } // // Is this a print job? // Icb->IsPrintJob will be false if a 16 bit app is // responsible for sending the job. // if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) && Icb->IsPrintJob ) { // // Yes, did we print? // if ( Icb->ActuallyPrinted ) { // // Yes. Send a close file and start queue job NCP // Status = ExchangeWithWait( IrpContext, SynchronousResponseCallback, "Sdw", NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_START_JOB, Vcb->Specific.Print.QueueId, Icb->JobId ); } else { // // No. Cancel the job. // Status = ExchangeWithWait( IrpContext, SynchronousResponseCallback, "Sdw", NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB, Vcb->Specific.Print.QueueId, Icb->JobId ); } } else { if ( Icb->SuperType.Fcb->NodeTypeCode != NW_NTC_DCB ) { // // No, send a close file NCP. // ASSERT( IrpContext->pTdiStruct == NULL ); Status = ExchangeWithWait( IrpContext, SynchronousResponseCallback, "F-r", NCP_CLOSE, Icb->Handle, sizeof( Icb->Handle ) ); // If this is in the long file name space and // the last access flag has been set, we have to // reset the last access time _after_ closing the file. if ( Icb->UserSetLastAccessTime && BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { Status = ExchangeWithWait( IrpContext, SynchronousResponseCallback, "LbbWD_W_bDbC", NCP_LFN_SET_INFO, Fcb->Vcb->Specific.Disk.LongNameSpace, Fcb->Vcb->Specific.Disk.LongNameSpace, SEARCH_ALL_FILES, LFN_FLAG_SET_INFO_LASTACCESS_DATE, 28, Fcb->LastAccessDate, 8, Fcb->Vcb->Specific.Disk.VolumeNumber, Fcb->Vcb->Specific.Disk.Handle, 0, &Fcb->RelativeFileName ); } // // If someone set the shareable bit, then // see if we can send the NCP over the wire (all // instances of the file need to be closed). // if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE ) ) { LazySetShareable( IrpContext, Icb, Fcb ); } } else { Status = ExchangeWithWait ( IrpContext, SynchronousResponseCallback, "Sb", NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE, Icb->Handle[0]); } } Icb->HasRemoteHandle = FALSE; } } else { pNpScb = Icb->SuperType.Scb->pNpScb; IrpContext->pNpScb = pNpScb; IrpContext->pScb = pNpScb->pScb; if ( Icb->HasRemoteHandle ) { // // If we have a remote handle this is a file stream ICB. We // need to close the remote handle. The exchange will get us // to the head of the queue to protect the SCB state. // Status = ExchangeWithWait( IrpContext, SynchronousResponseCallback, "F-r", NCP_CLOSE, Icb->Handle, sizeof( Icb->Handle ) ); Icb->HasRemoteHandle = FALSE; pNpScb->pScb->OpenNdsStreams--; ASSERT( pNpScb->pScb->MajorVersion > 3 ); // // Do we need to unlicense this connection? // if ( ( pNpScb->pScb->UserName.Length == 0 ) && ( pNpScb->pScb->VcbCount == 0 ) && ( pNpScb->pScb->OpenNdsStreams == 0 ) ) { NdsUnlicenseConnection( IrpContext ); } NwDequeueIrpContext( IrpContext, FALSE ); } if ( Icb->IsExCredentialHandle ) { ExCreateDereferenceCredentials( IrpContext, Icb->pContext ); } } if ( Icb->Pid != INVALID_PID ) { // // This ICB was involved in a search, send the end job, // then free the PID. // NwUnmapPid(pNpScb, Icb->Pid, IrpContext ); } // // Update the time the SCB was last used. // KeQuerySystemTime( &pNpScb->LastUsedTime ); // // Wait the SCB queue. We do this now since NwDeleteIcb may cause // a packet to be sent by this thread (from NwCleanupVcb()) while // holding the RCB. To eliminate this potential source of deadlock, // queue this IrpContext to the SCB queue before acquiring the RCB. // // Also, we mark this IRP context not reconnectable, since the // reconnect logic, will try to acquire the RCB. // NwAppendToQueueAndWait( IrpContext ); ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE ); // // Delete the ICB. // NwDeleteIcb( IrpContext, Icb ); // // And return to our caller // DebugTrace(-1, Dbg, "NwCloseIcb -> %08lx\n", Status); return Status; }