NTSTATUS NtfsCommonSetVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for set Volume Information called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); PAGED_CODE(); // // Get the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonSetVolumeInfo\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.SetVolume.Length) ); DebugTrace( 0, Dbg, ("FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass) ); DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) ); // // Reference our input parameters to make things easier // Length = IrpSp->Parameters.SetVolume.Length; FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer; // // Extract and decode the file object to get the Vcb, we don't really // care what the type of open is. // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); if (TypeOfOpen != UserVolumeOpen && (TypeOfOpen != UserViewIndexOpen || FsInformationClass != FileFsControlInformation || Fcb != Vcb->QuotaTableScb->Fcb)) { NtfsCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); DebugTrace( -1, Dbg2, ("NtfsCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n") ); return STATUS_ACCESS_DENIED; } // // The volume must be writable. // if (NtfsIsVolumeReadOnly( Vcb )) { Status = STATUS_MEDIA_WRITE_PROTECTED; NtfsCompleteRequest( IrpContext, Irp, Status ); DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> %08lx\n", Status) ); return Status; } #ifdef __ND_NTFS_SECONDARY__ if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) { return NtfsPostRequest( IrpContext, Irp ); } #endif // // Acquire exclusive access to the Vcb // NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE ); try { // // Proceed only if the volume is mounted. // if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) { #ifdef __ND_NTFS_SECONDARY__ if(IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) { DebugTrace( 0, DEBUG_TRACE_ALL, ("IrpSp->FileObject is NULL, IrpSp->MajorFunction = %x, IrpSp->MinorFunction = %x\n", IrpSp->MajorFunction, IrpSp->MinorFunction) ); } if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject)) { Status = NdNtfsSecondaryCommonSetVolumeInfo( IrpContext, Irp ); leave; } #endif // // Based on the information class we'll do different actions. Each // of the procedures that we're calling performs the action if // possible and returns true if it successful and false if it couldn't // wait for any I/O to complete. // switch (FsInformationClass) { case FileFsLabelInformation: Status = NtfsSetFsLabelInfo( IrpContext, Vcb, Buffer ); break; case FileFsControlInformation: Status = NtfsSetFsControlInfo( IrpContext, Vcb, Buffer ); break; case FileFsObjectIdInformation: Status = NtfsSetFsVolumeObjectIdInfo( IrpContext, Vcb, Buffer ); DebugTrace( 0, Dbg2, ("NtfsCommonSetVolumeInfo %x, FileFsObjectIdInformation Vcb = %p\n", Status, IrpContext->Vcb) ); break; default: Status = STATUS_INVALID_PARAMETER; break; } } else { Status = STATUS_FILE_INVALID; } // // Abort transaction on error by raising. // NtfsCleanupTransaction( IrpContext, Status, FALSE ); } finally { DebugUnwind( NtfsCommonSetVolumeInfo ); NtfsReleaseVcb( IrpContext, Vcb ); DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> %08lx\n", Status) ); } NtfsCompleteRequest( IrpContext, Irp, Status ); return Status; }
VOID NtfsAcquireAllFiles ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN BOOLEAN Exclusive, IN BOOLEAN AcquirePagingIo ) /*++ Routine Description: This routine non-recursively requires all files on a volume. Arguments: Vcb - Supplies the volume Exclusive - Indicates if we should be acquiring all the files exclusively. If FALSE then we acquire all the files shared except for files with streams which could be part of transactions. AcquirePagingIo - Indicates if we need to acquire the paging io resource exclusively. Only needed if a future call will flush the volume (i.e. shutdown) Return Value: None --*/ { PFCB Fcb; PSCB *Scb; PSCB NextScb; PVOID RestartKey; PAGED_CODE(); SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE ); RestartKey = NULL; while (TRUE) { NtfsAcquireFcbTable( IrpContext, Vcb ); Fcb = NtfsGetNextFcbTableEntry(Vcb, &RestartKey); NtfsReleaseFcbTable( IrpContext, Vcb ); if (Fcb == NULL) { break; } ASSERT_FCB( Fcb ); // // We can skip over the Fcb's for any of the Scb's in the Vcb. // We delay acquiring those to avoid deadlocks. // if (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER) { // // If there is a paging Io resource then acquire this if required. // if (AcquirePagingIo && (Fcb->PagingIoResource != NULL)) { ExAcquireResourceExclusive( Fcb->PagingIoResource, TRUE ); } // // Acquire this Fcb whether or not the underlying file has been deleted. // if (Exclusive || IsDirectory( &Fcb->Info )) { NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, TRUE, FALSE ); } else { // // Assume that we only need this file shared. We will then // look for Lsn related streams. // NtfsAcquireSharedFcb( IrpContext, Fcb, NULL, TRUE ); // // Walk through all of the Scb's for the file and look for // an Lsn protected stream. // NtfsLockFcb( IrpContext, Fcb ); NextScb = NULL; while (NextScb = NtfsGetNextChildScb( Fcb, NextScb )) { if (!(NextScb->AttributeTypeCode == $DATA || NextScb->AttributeTypeCode == $EA)) { break; } } NtfsUnlockFcb( IrpContext, Fcb ); // // If we found a protected Scb then release and reacquire the Fcb // exclusively. // if (NextScb != NULL) { NtfsReleaseFcb( IrpContext, Fcb ); NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, TRUE, FALSE ); } } } } // // Now acquire the Fcb's in the Vcb. // Scb = &Vcb->QuotaTableScb; while (TRUE) { if ((*Scb != NULL) && (*Scb != Vcb->BitmapScb)) { if (AcquirePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) { ExAcquireResourceExclusive( (*Scb)->Fcb->PagingIoResource, TRUE ); } NtfsAcquireExclusiveFcb( IrpContext, (*Scb)->Fcb, NULL, TRUE, FALSE ); } if (Scb == &Vcb->MftScb) { break; } Scb -= 1; } // // Treat the bitmap as an end resource and acquire it last. // if (Vcb->BitmapScb != NULL) { if (AcquirePagingIo && (Vcb->BitmapScb->Fcb->PagingIoResource != NULL)) { ExAcquireResourceExclusive( Vcb->BitmapScb->Fcb->PagingIoResource, TRUE ); } NtfsAcquireExclusiveFcb( IrpContext, Vcb->BitmapScb->Fcb, NULL, TRUE, FALSE ); } return; }
VOID NtfsSpecialDispatch ( PVOID Context ) /*++ Routine Description: This routine is called when a special operation needs to be posted. It is called indirectly by NtfsPostSpecial. It is assumes that the Vcb is protected from going away by incrementing the volemue close counts for a file. If this routine fails nothing is done except to clean up the Vcb. This routine also handles issues log file full and can't wait. The function to be called is stored in the PostSpecialCallout field of the Irp Context, and the context is stored int he OriginatingIrp. Both fields are zeroed before the the callout function is called. Arguments: Context - Supplies a pointer to an IrpContext. Return Value: --*/ { PVCB Vcb; PIRP_CONTEXT IrpContext = Context; TOP_LEVEL_CONTEXT TopLevelContext; PTOP_LEVEL_CONTEXT ThreadTopLevelContext; POST_SPECIAL_CALLOUT PostSpecialCallout; PVOID SpecialContext; ULONG LogFileFullCount; BOOLEAN Retry; PAGED_CODE(); FsRtlEnterFileSystem(); do { Vcb = IrpContext->Vcb; LogFileFullCount = 0; // // Capture the funciton pointer and context before using the IrpContext. // PostSpecialCallout = IrpContext->Union.PostSpecialCallout; SpecialContext = IrpContext->OriginatingIrp; IrpContext->Union.PostSpecialCallout = NULL; IrpContext->OriginatingIrp = NULL; ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE ); ASSERT( ThreadTopLevelContext == &TopLevelContext ); ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_FROM_POOL )); // // Initialize the thread top level structure, if needed. // ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext ); NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); // // Don't let this IrpContext be deleted. // SetFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT ); do { Retry = FALSE; try { // // See if we failed due to a log file full condition, and // if so, then do a clean volume checkpoint if we are the // first ones to get there. If we see a different Lsn and do // not do the checkpoint, the worst that can happen is that we // will fail again if the log file is still full. // if (IrpContext->LastRestartArea.QuadPart != 0) { NtfsCheckpointForLogFileFull( IrpContext ); if (++LogFileFullCount >= 2) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL ); } } // // Call the requested function. // ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); PostSpecialCallout( IrpContext, SpecialContext ); NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) { NTSTATUS ExceptionCode; ExceptionCode = GetExceptionCode(); ExceptionCode = NtfsProcessException( IrpContext, NULL, ExceptionCode ); if ((ExceptionCode == STATUS_CANT_WAIT) || (ExceptionCode == STATUS_LOG_FILE_FULL)) { Retry = TRUE; } } } while (Retry); // // Ok to let this IrpContext be deleted. // ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT ); // // At this point regardless of the status the volume needs to // be cleaned up and the IrpContext freed. // Dereference the Vcb and check to see if it needs to be deleted. // since this call might raise wrap it with a try/execpt. // try { // // Acquire the volume exclusive so the counts can be // updated. // ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )); NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE ); InterlockedDecrement( &Vcb->SystemFileCloseCount ); InterlockedDecrement( &Vcb->CloseCount ); NtfsReleaseVcb( IrpContext, Vcb ); } except( EXCEPTION_EXECUTE_HANDLER ) { ASSERT( FsRtlIsNtstatusExpected( GetExceptionCode() ) ); } // // Free the irp context. // NtfsCleanupIrpContext( IrpContext, TRUE ); // // See if there is more work on the scavenger list. // ExAcquireFastMutexUnsafe( &NtfsScavengerLock ); ASSERT( NtfsScavengerRunning ); IrpContext = NtfsScavengerWorkList; if (IrpContext != NULL) { // // Remove the entry from the list. // NtfsScavengerWorkList = (PIRP_CONTEXT) IrpContext->WorkQueueItem.List.Flink; IrpContext->WorkQueueItem.List.Flink = NULL; } else { NtfsScavengerRunning = FALSE; } ExReleaseFastMutexUnsafe( &NtfsScavengerLock ); } while ( IrpContext != NULL ); FsRtlExitFileSystem(); }
NTSTATUS NtfsCommonSetVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for set Volume Information called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PMyIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); PAGED_CODE(); // // Get the current Irp stack location // IrpSp = (PMyIO_STACK_LOCATION)IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonSetVolumeInfo\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.SetVolume.Length) ); DebugTrace( 0, Dbg, ("FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass) ); DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) ); // // Reference our input parameters to make things easier // Length = IrpSp->Parameters.SetVolume.Length; FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer; // // Extract and decode the file object to get the Vcb, we don't really // care what the type of open is. // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); if (TypeOfOpen != UserVolumeOpen) { NtfsCompleteRequest( &IrpContext, &Irp, STATUS_ACCESS_DENIED ); DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n") ); return STATUS_ACCESS_DENIED; } // // Acquire exclusive access to the Vcb // NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE ); try { // // Proceed only if the volume is mounted. // if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) { // // Based on the information class we'll do different actions. Each // of the procedures that we're calling performs the action if // possible and returns true if it successful and false if it couldn't // wait for any I/O to complete. // switch (FsInformationClass) { case FileFsLabelInformation: Status = NtfsSetFsLabelInfo( IrpContext, Vcb, Buffer ); break; #ifdef _CAIRO_ case FileFsQuotaSetInformation: Status = NtfsFsQuotaSetInfo( IrpContext, Vcb, Buffer, Length ); break; case FileFsControlInformation: Status = NtfsSetFsControlInfo( IrpContext, Vcb, Buffer ); break; #endif // _CAIRO_ default: Status = STATUS_INVALID_PARAMETER; break; } } else { Status = STATUS_FILE_INVALID; } // // Abort transaction on error by raising. // NtfsCleanupTransaction( IrpContext, Status, FALSE ); } finally { DebugUnwind( NtfsCommonSetVolumeInfo ); NtfsReleaseVcb( IrpContext, Vcb ); if (!AbnormalTermination()) { NtfsCompleteRequest( &IrpContext, &Irp, Status ); } DebugTrace( -1, Dbg, ("NtfsCommonSetVolumeInfo -> %08lx\n", Status) ); } return Status; }