NTSTATUS FatCommonQueryVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for querying volume information called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp being processed Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PVCB Vcb; PFCB Fcb; PCCB Ccb; ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer; BOOLEAN WeAcquiredVcb = FALSE; // // Get the current stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "FatCommonQueryVolumeInfo...\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length); DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass); DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); // // Reference our input parameters to make things easier // Length = IrpSp->Parameters.QueryVolume.Length; FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass; Buffer = Irp->AssociatedIrp.SystemBuffer; // // Decode the file object to get the Vcb // (VOID) FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); ASSERT( Vcb != NULL ); try { // // Make sure the vcb is in a usable condition. This will raise // and error condition if the volume is unusable // // Also verify the Root Dcb since we need info from there. // FatVerifyFcb( IrpContext, Vcb->RootDcb ); // // Based on the information class we'll do different actions. Each // of the procedures that we're calling fills up the output buffer // if possible and returns true if it successfully filled the buffer // and false if it couldn't wait for any I/O to complete. // switch (FsInformationClass) { case FileFsVolumeInformation: // // This is the only routine we need the Vcb shared because of // copying the volume label. All other routines copy fields that // cannot change or are just manifest constants. // if (!FatAcquireSharedVcb( IrpContext, Vcb )) { DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); Status = FatFsdPostRequest( IrpContext, Irp ); IrpContext = NULL; Irp = NULL; } else { WeAcquiredVcb = TRUE; Status = FatQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length ); } break; case FileFsSizeInformation: Status = FatQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsDeviceInformation: Status = FatQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsAttributeInformation: Status = FatQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsFullSizeInformation: Status = FatQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length ); break; default: Status = STATUS_INVALID_PARAMETER; break; } // // Set the information field to the number of bytes actually filled in. // if (Irp != NULL) { Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; } } finally { DebugUnwind( FatCommonQueryVolumeInfo ); if ( WeAcquiredVcb ) { FatReleaseVcb( IrpContext, Vcb ); } if (!AbnormalTermination()) { FatCompleteRequest( IrpContext, Irp, Status ); } DebugTrace(-1, Dbg, "FatCommonQueryVolumeInfo -> %08lx\n", Status); } return Status; }
VOID LfsReadLogRecord ( IN LFS_LOG_HANDLE LogHandle, IN LSN FirstLsn, IN LFS_CONTEXT_MODE ContextMode, OUT PLFS_LOG_CONTEXT Context, OUT PLFS_RECORD_TYPE RecordType, OUT TRANSACTION_ID *TransactionId, OUT PLSN UndoNextLsn, OUT PLSN PreviousLsn, OUT PULONG BufferLength, OUT PVOID *Buffer ) /*++ Routine Description: This routine initiates the query operation. It returns the log record in question and a context structure used by the Lfs to return related log records. The caller specifies what mode of query to use. He may walk backwards through the file by Undo records or all records for this client linked through the previous Lsn fields. He may also look forwards through the file for all records for the issuing client. Arguments: LogHandle - Pointer to private Lfs structure used to identify this client. FirstLsn - Starting record for this query operation. ContextMode - Method of query. Context - Supplies the address to store a pointer to the Lfs created context structure. RecordType - Supplies the address to store the record type of this log record. TransactionId - Supplies the address to store the transaction Id of this log record. UndoNextLsn - Supplies the address to store the Undo Next Lsn for this log record. PreviousLsn - Supplies the address to store the Previous Lsn for this log record. BufferLength - This is the length of the log data. Buffer - This is a pointer to the start of the log data. Return Value: None --*/ { volatile NTSTATUS Status = STATUS_SUCCESS; PLFS_CLIENT_RECORD ClientRecord; PLCH Lch; PLFCB Lfcb; PLfsLCB Lcb = NULL; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsReadLogRecord: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle ); LfsDebugTrace( 0, Dbg, "First Lsn (Low) -> %08lx\n", FirstLsn.LowPart ); LfsDebugTrace( 0, Dbg, "First Lsn (High) -> %08lx\n", FirstLsn.HighPart ); LfsDebugTrace( 0, Dbg, "Context Mode -> %08lx\n", ContextMode ); Lch = (PLCH) LogHandle; // // Check that the context mode is valid. // switch (ContextMode) { case LfsContextUndoNext : case LfsContextPrevious : case LfsContextForward : break; default: LfsDebugTrace( 0, Dbg, "Invalid context mode -> %08x\n", ContextMode ); ExRaiseStatus( STATUS_INVALID_PARAMETER ); } // // Check that the structure is a valid log handle structure. // LfsValidateLch( Lch ); // // Use a try-except to catch errors. // try { // // Use a try-finally to facilitate cleanup. // try { // // Acquire the log file control block for this log file. // LfsAcquireLch( Lch ); Lfcb = Lch->Lfcb; // // If the Log file has been closed then refuse access. // if (Lfcb == NULL) { ExRaiseStatus( STATUS_ACCESS_DENIED ); } // // Check that the client Id is valid. // LfsValidateClientId( Lfcb, Lch ); // // Check that the given Lsn is in the legal range for this client. // ClientRecord = LfsAdd2Ptr( Lfcb->ClientArray, Lch->ClientArrayByteOffset, PLFS_CLIENT_RECORD ); if (!LfsVerifyClientLsnInRange( Lfcb, ClientRecord, FirstLsn )) { ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR ); } // // We can give up the Lfcb as we know the Lsn is within the file. // LfsReleaseLch( Lch ); // // Allocate and initialize a context structure. // LfsAllocateLcb( &Lcb ); LfsInitializeLcb( Lcb, Lch->ClientId, ContextMode ); // // Find the log record indicated by the given Lsn. // LfsFindLogRecord( Lfcb, Lcb, FirstLsn, RecordType, TransactionId, UndoNextLsn, PreviousLsn, BufferLength, Buffer ); // // Update the client's arguments. // *Context = Lcb; Lcb = NULL; } finally { DebugUnwind( LfsReadLogRecord ); // // Release the log file control block if held. // LfsReleaseLch( Lch ); // // Deallocate the context block if an error occurred. // if (Lcb != NULL) { LfsDeallocateLcb( Lcb ); } LfsDebugTrace( 0, Dbg, "Context -> %08lx\n", *Context ); LfsDebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength ); LfsDebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer ); LfsDebugTrace( -1, Dbg, "LfsReadLogRecord: Exit\n", 0 ); } } except (LfsExceptionFilter( GetExceptionInformation() )) { Status = GetExceptionCode(); } if (Status != STATUS_SUCCESS) { ExRaiseStatus( Status ); } return; }
VOID LfsFindLogRecord ( IN PLFCB Lfcb, IN OUT PLfsLCB Lcb, IN LSN Lsn, OUT PLFS_RECORD_TYPE RecordType, OUT TRANSACTION_ID *TransactionId, OUT PLSN UndoNextLsn, OUT PLSN PreviousLsn, OUT PULONG BufferLength, OUT PVOID *Buffer ) /*++ Routine Description: This routine is called recover a log record for a client. Arguments: Lfcb - Log file control block for this file. Lcb - Pointer to the context block to update. Lsn - This is the Lsn for the log record. RecordType - Supplies the address to store the record type of this log record. TransactionId - Supplies the address to store the transaction Id of this log record. UndoNextLsn - Supplies the address to store the Undo Next Lsn for this log record. PreviousLsn - Supplies the address to store the Previous Lsn for this log record. BufferLength - Pointer to address to store the length in bytes of the log record. Buffer - Pointer to store the address where the log record data begins. Return Value: None --*/ { PCHAR NewBuffer; BOOLEAN UsaError; LONGLONG LogRecordLength; ULONG PageOffset; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsFindLogRecord: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); LfsDebugTrace( 0, Dbg, "Context Block -> %08lx\n", Lcb ); LfsDebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart ); NewBuffer = NULL; // // Use a try-finally to facilitate cleanup. // try { // // Map the record header for this Lsn if we haven't already. // if (Lcb->RecordHeader == NULL) { LfsPinOrMapLogRecordHeader( Lfcb, Lsn, FALSE, FALSE, &UsaError, &Lcb->RecordHeader, &Lcb->RecordHeaderBcb ); } // // We now have the log record desired. If the Lsn in the // log record doesn't match the desired Lsn then the disk is // corrupt. // if ( Lsn.QuadPart != Lcb->RecordHeader->ThisLsn.QuadPart ) { //**** xxNeq( Lsn, Lcb->RecordHeader->ThisLsn ) ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR ); } // // Check that the length field isn't greater than the total available space // in the log file. // LogRecordLength = Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength; //**** xxFromUlong( Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength ); if ( LogRecordLength >= Lfcb->TotalAvailable ) { //**** xxGeq( LogRecordLength, Lfcb->TotalAvailable ) ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR ); } // // If the entire log record is on this log page, put a pointer to // the log record in the context block. // if (!FlagOn( Lcb->RecordHeader->Flags, LOG_RECORD_MULTI_PAGE )) { // // If client size indicates that we have to go beyond the end of the current // page, we raise an error. // PageOffset = LfsLsnToPageOffset( Lfcb, Lsn ); if ((PageOffset + Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength) > (ULONG)Lfcb->LogPageSize) { ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR ); } Lcb->CurrentLogRecord = LfsAdd2Ptr( Lcb->RecordHeader, LFS_RECORD_HEADER_SIZE, PVOID ); Lcb->AuxilaryBuffer = FALSE; // // Else we copy the data and remember that we allocated a buffer. // } else { NewBuffer = FsRtlAllocatePool( PagedPool, Lcb->RecordHeader->ClientDataLength ); LfsCopyReadLogRecord( Lfcb, Lcb->RecordHeader, NewBuffer ); Lcb->CurrentLogRecord = NewBuffer; Lcb->AuxilaryBuffer = TRUE; NewBuffer = NULL; } // // We need to update the caller's parameters and the context block. // *RecordType = Lcb->RecordHeader->RecordType; *TransactionId = Lcb->RecordHeader->TransactionId; *UndoNextLsn = Lcb->RecordHeader->ClientUndoNextLsn; *PreviousLsn = Lcb->RecordHeader->ClientPreviousLsn; *Buffer = Lcb->CurrentLogRecord; *BufferLength = Lcb->RecordHeader->ClientDataLength; } finally { DebugUnwind( LfsFindLogRecord ); // // If an error occurred we unpin the record header and the log // We also free the buffer if allocated by us. // if (NewBuffer != NULL) { ExFreePool( NewBuffer ); } LfsDebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength ); LfsDebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer ); LfsDebugTrace( -1, Dbg, "LfsFindLogRecord: Exit\n", 0 ); } return; }
NTSTATUS FatCommonLockControl ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for doing Lock control operations 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; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PCCB Ccb; BOOLEAN OplockPostIrp = FALSE; // // Get a pointer to the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "FatCommonLockControl\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); // // Decode the type of file object we're being asked to process // TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); // // If the file is not a user file open then we reject the request // as an invalid parameter // if (TypeOfOpen != UserFileOpen) { FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "FatCommonLockControl -> STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; } // // Acquire exclusive access to the Fcb and enqueue the Irp if we didn't // get access // if (!FatAcquireSharedFcb( IrpContext, Fcb )) { Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); return Status; } try { // // We check whether we can proceed // based on the state of the file oplocks. // Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, Irp, IrpContext, FatOplockComplete, NULL ); if (Status != STATUS_SUCCESS) { OplockPostIrp = TRUE; try_return( NOTHING ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request // Status = FsRtlProcessFileLock( &Fcb->Specific.Fcb.FileLock, Irp, NULL ); // // Set the flag indicating if Fast I/O is possible // Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); try_exit: NOTHING; } finally { DebugUnwind( FatCommonLockControl ); // // Only if this is not an abnormal termination do we delete the // irp context // if (!AbnormalTermination() && !OplockPostIrp) { FatCompleteRequest( IrpContext, FatNull, 0 ); } // // Release the Fcb, and return to our caller // FatReleaseFcb( IrpContext, Fcb ); DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); } return Status; }
BOOLEAN LfsReadNextLogRecord ( IN LFS_LOG_HANDLE LogHandle, IN OUT LFS_LOG_CONTEXT Context, OUT PLFS_RECORD_TYPE RecordType, OUT TRANSACTION_ID *TransactionId, OUT PLSN UndoNextLsn, OUT PLSN PreviousLsn, OUT PLSN Lsn, OUT PULONG BufferLength, OUT PVOID *Buffer ) /*++ Routine Description: This routine is called to continue a query operation. The Lfs uses private information stored in the context structure to determine the next log record to return to the caller. Arguments: LogHandle - Pointer to private Lfs structure used to identify this client. Context - Supplies the address to store a pointer to the Lfs created context structure. Lsn - Lsn for this log record. RecordType - Supplies the address to store the record type of this log record. TransactionId - Supplies the address to store the transaction Id of this log record. UndoNextLsn - Supplies the address to store the Undo Next Lsn for this log record. PreviousLsn - Supplies the address to store the Previous Lsn for this log record. BufferLength - This is the length of the log data. Buffer - This is a pointer to the start of the log data. Return Value: None --*/ { volatile NTSTATUS Status = STATUS_SUCCESS; PLCH Lch; PLFCB Lfcb; PLfsLCB Lcb; BOOLEAN FoundNextLsn; BOOLEAN UnwindRememberLcbFields; PBCB UnwindRecordHeaderBcb; PLFS_RECORD_HEADER UnwindRecordHeader; PVOID UnwindCurrentLogRecord; BOOLEAN UnwindAuxilaryBuffer; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsReadNextLogRecord: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle ); LfsDebugTrace( 0, Dbg, "Context -> %08lx\n", Context ); FoundNextLsn = FALSE; UnwindRememberLcbFields = FALSE; Lch = (PLCH) LogHandle; Lcb = (PLfsLCB) Context; // // Check that the structure is a valid log handle structure. // LfsValidateLch( Lch ); // // Use a try-except to catch errors. // try { // // Use a try-finally to facilitate cleanup. // try { // // Acquire the log file control block for this log file. // LfsAcquireLch( Lch ); Lfcb = Lch->Lfcb; // // If the Log file has been closed then refuse access. // if (Lfcb == NULL) { ExRaiseStatus( STATUS_ACCESS_DENIED ); } // // Check that the client Id is valid. // LfsValidateClientId( Lfcb, Lch ); // // Check that the context structure is valid. // LfsValidateLcb( Lcb, Lch ); // // Remember any context fields to be overwritten. // UnwindRememberLcbFields = TRUE; UnwindRecordHeaderBcb = Lcb->RecordHeaderBcb; Lcb->RecordHeaderBcb = NULL; UnwindRecordHeader = Lcb->RecordHeader; UnwindCurrentLogRecord = Lcb->CurrentLogRecord; UnwindAuxilaryBuffer = Lcb->AuxilaryBuffer; Lcb->AuxilaryBuffer = FALSE; // // Find the next Lsn number based on the current Lsn number in // the context block. // if (LfsFindClientNextLsn( Lfcb, Lcb, Lsn )) { // // We can give up the Lfcb as we know the Lsn is within the file. // LfsReleaseLfcb( Lfcb ); // // Cleanup the context block so we can do the next search. // Lcb->CurrentLogRecord = NULL; Lcb->AuxilaryBuffer = FALSE; // // Perform the work of getting the log record. // LfsFindLogRecord( Lfcb, Lcb, *Lsn, RecordType, TransactionId, UndoNextLsn, PreviousLsn, BufferLength, Buffer ); FoundNextLsn = TRUE; } } finally { DebugUnwind( LfsReadNextLogRecord ); // // If we exited due to an error, we have to restore the context // block. // if (UnwindRememberLcbFields) { if (AbnormalTermination()) { // // If the record header in the context block is not // the same as we started with. Then we unpin that // data. // if (Lcb->RecordHeaderBcb != NULL) { CcUnpinData( Lcb->RecordHeaderBcb ); } if (Lcb->CurrentLogRecord != NULL && Lcb->AuxilaryBuffer == TRUE) { ExFreePool( Lcb->CurrentLogRecord ); } Lcb->RecordHeaderBcb = UnwindRecordHeaderBcb; Lcb->RecordHeader = UnwindRecordHeader; Lcb->CurrentLogRecord = UnwindCurrentLogRecord; Lcb->AuxilaryBuffer = UnwindAuxilaryBuffer; // // Otherwise, if we have successfully found the next Lsn, // we free up any resources being held from the previous search. // } else if (FoundNextLsn ) { if (UnwindRecordHeaderBcb != NULL) { CcUnpinData( UnwindRecordHeaderBcb ); } if (UnwindCurrentLogRecord != NULL && UnwindAuxilaryBuffer == TRUE) { ExFreePool( UnwindCurrentLogRecord ); } // // Restore the Bcb and auxilary buffer field for the final // cleanup. // } else { if (UnwindRecordHeaderBcb != NULL) { if (Lcb->RecordHeaderBcb != NULL) { CcUnpinData( UnwindRecordHeaderBcb ); } else { Lcb->RecordHeaderBcb = UnwindRecordHeaderBcb; } } if (UnwindAuxilaryBuffer) { if (Lcb->CurrentLogRecord == UnwindCurrentLogRecord) { Lcb->AuxilaryBuffer = TRUE; } else { ExFreePool( UnwindCurrentLogRecord ); } } } } // // Release the log file control block if held. // LfsReleaseLch( Lch ); LfsDebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart ); LfsDebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart ); LfsDebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength ); LfsDebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer ); LfsDebugTrace( -1, Dbg, "LfsReadNextLogRecord: Exit\n", 0 ); } } except (LfsExceptionFilter( GetExceptionInformation() )) { Status = GetExceptionCode(); } if (Status != STATUS_SUCCESS) { ExRaiseStatus( Status ); } return FoundNextLsn; }
VOID LfsWriteLfsRestart ( IN PLFCB Lfcb, IN ULONG ThisRestartSize, IN BOOLEAN WaitForIo ) /*++ Routine Description: This routine puts the Lfs restart area on the queue of operations to write to the file. We do this by allocating a second restart area and attaching it to the Lfcb. We also allocate a buffer control block to use for this write. We look at the WaitForIo boolean to determine whether this thread can perform the I/O. This also indicates whether this thread gives up the Lfcb. Arguments: Lfcb - A pointer to the log file control block for this operation. ThisRestartSize - This is the size to use for the restart area. WaitForIo - Indicates if this thread is to perform the work. Return Value: None. --*/ { PLBCB NewLbcb = NULL; PLFS_RESTART_AREA NewRestart = NULL; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsWriteLfsRestart: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); LfsDebugTrace( 0, Dbg, "Write Chkdsk -> %04x\n", WriteChkdsk ); LfsDebugTrace( 0, Dbg, "Restart Size -> %08lx\n", ThisRestartSize ); LfsDebugTrace( 0, Dbg, "WaitForIo -> %08lx\n", WaitForIo ); // // Use a try-finally to facilitate cleanup. // __try { PLBCB ActiveLbcb; // // We allocate another restart area and // copy the current area into it. Attach the new area to the Lfcb. // LfsAllocateRestartArea( &NewRestart, ThisRestartSize ); // // We allocate a Lbcb structure and update the values to // reflect this restart area. // LfsAllocateLbcb( Lfcb, &NewLbcb ); SetFlag( NewLbcb->LbcbFlags, LBCB_RESTART_LBCB ); // // If this is the second page, then add a system page to the offset. // if (!Lfcb->InitialRestartArea) { NewLbcb->FileOffset = Lfcb->SystemPageSize + NewLbcb->FileOffset; } (ULONG)NewLbcb->Length = ThisRestartSize; NewLbcb->PageHeader = (PVOID) Lfcb->RestartArea; // // Lets put the current lsn in the Lbcb. // NewLbcb->LastEndLsn = NewLbcb->LastLsn = Lfcb->NextRestartLsn; Lfcb->NextRestartLsn.QuadPart = 1 + Lfcb->NextRestartLsn.QuadPart; // // Copy the existing restart area into the new area. // RtlCopyMemory( NewRestart, Lfcb->RestartArea, ThisRestartSize ); Lfcb->RestartArea = NewRestart; Lfcb->ClientArray = LfsAdd2Ptr( NewRestart, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD ); NewRestart = NULL; // // Update the Lfcb to indicate that the other restart area // on the disk is to be used. // Lfcb->InitialRestartArea = !Lfcb->InitialRestartArea; // // Add this Lbcb to the end of the workque and flush to that point. // InsertTailList( &Lfcb->LbcbWorkque, &NewLbcb->WorkqueLinks ); // // If we don't support a packed log file then we need to make // sure that all file records written out ahead of this // restart area make it out to disk and we don't add anything // to this page. // if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) && !IsListEmpty( &Lfcb->LbcbActive )) { ActiveLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink, LBCB, ActiveLinks ); if (FlagOn( ActiveLbcb->LbcbFlags, LBCB_NOT_EMPTY )) { RemoveEntryList( &ActiveLbcb->ActiveLinks ); ClearFlag( ActiveLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ); } } if (WaitForIo) { LfsFlushLbcb( Lfcb, NewLbcb ); } } __finally { DebugUnwind( LfsWriteLfsRestart ); if (NewRestart != NULL) { ExFreePool( NewRestart ); } LfsDebugTrace( -1, Dbg, "LfsWriteLfsRestart: Exit\n", 0 ); } return; }
BOOLEAN FatFastLock ( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This is a call back routine for doing the fast lock call. Arguments: FileObject - Supplies the file object used in this operation FileOffset - Supplies the file offset used in this operation Length - Supplies the length used in this operation ProcessId - Supplies the process ID used in this operation Key - Supplies the key used in this operation FailImmediately - Indicates if the request should fail immediately if the lock cannot be granted. ExclusiveLock - Indicates if this is a request for an exclusive or shared lock IoStatus - Receives the Status if this operation is successful Return Value: BOOLEAN - TRUE if this operation completed and FALSE if caller needs to take the long route. --*/ { BOOLEAN Results; PVCB Vcb; PFCB Fcb; PCCB Ccb; DebugTrace(+1, Dbg, "FatFastLock\n", 0); // // Decode the type of file object we're being asked to process and make // sure it is only a user file open. // if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { IoStatus->Status = STATUS_INVALID_PARAMETER; IoStatus->Information = 0; DebugTrace(-1, Dbg, "FatFastLock -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); return TRUE; } // // Acquire exclusive access to the Fcb this operation can always wait // FsRtlEnterFileSystem(); try { // // We check whether we can proceed // based on the state of the file oplocks. // if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { try_return( Results = FALSE ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request // if (Results = FsRtlFastLock( &Fcb->Specific.Fcb.FileLock, FileObject, FileOffset, Length, ProcessId, Key, FailImmediately, ExclusiveLock, IoStatus, NULL, FALSE )) { // // Set the flag indicating if Fast I/O is possible // Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); } try_exit: NOTHING; } finally { DebugUnwind( FatFastLock ); // // Release the Fcb, and return to our caller // FsRtlExitFileSystem(); DebugTrace(-1, Dbg, "FatFastLock -> %08lx\n", Results); } return Results; }
BOOLEAN LfsFindNextLsn ( IN PLFCB Lfcb, IN PLFS_RECORD_HEADER RecordHeader, OUT PLSN Lsn ) /*++ Routine Description: This routine takes as a starting point the log record header of an Lsn in the log file. It searches for the next Lsn in the file and returns that value in the 'Lsn' argument. The boolean return value indicates whether there is another Lsn in the file. Arguments: Lfcb - This is the file control block for the log file. RecordHeader - This is the log record for the Lsn starting point. Lsn - This supplies the address to store the next Lsn, if found. Return Value: BOOLEAN - Indicates whether the next Lsn was found. --*/ { BOOLEAN FoundNextLsn; LONGLONG LsnOffset; LONGLONG EndOfLogRecord; LONGLONG LogHeaderOffset; LONGLONG SequenceNumber; PLFS_RECORD_PAGE_HEADER LogRecordPage; PBCB LogRecordPageBcb; BOOLEAN UsaError; PAGED_CODE(); DebugTrace( +1, Dbg, "LfsFindNextLsn: Entered\n", 0 ); DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); DebugTrace( 0, Dbg, "Record Header -> %08lx\n", RecordHeader ); LogRecordPageBcb = NULL; FoundNextLsn = FALSE; // // Use a try-finally to facilitate cleanup. // try { // // Find the file offset of the log page which contains the end // of the log record for this Lsn. // LsnOffset = LfsLsnToFileOffset( Lfcb, RecordHeader->ThisLsn ); LfsLsnFinalOffset( Lfcb, RecordHeader->ThisLsn, RecordHeader->ClientDataLength, &EndOfLogRecord ); LfsTruncateOffsetToLogPage( Lfcb, EndOfLogRecord, &LogHeaderOffset ); // // Remember the sequence number for this page. // SequenceNumber = LfsLsnToSeqNumber( Lfcb, RecordHeader->ThisLsn ); // // Remember if we wrapped. // if ( EndOfLogRecord <= LsnOffset ) { //**** xxLeq( EndOfLogRecord, LsnOffset ) SequenceNumber = SequenceNumber + 1; //**** xxAdd( SequenceNumber, LfsLi1 ); } // // Pin the log page header for this page. // LfsPinOrMapData( Lfcb, LogHeaderOffset, (ULONG)Lfcb->LogPageSize, FALSE, FALSE, FALSE, &UsaError, (PVOID *)&LogRecordPage, &LogRecordPageBcb ); // // If the Lsn we were given was not the last Lsn on this page, then // the starting offset for the next Lsn is on a quad word boundary // following the last file offset for the current Lsn. Otherwise // the file offset is the start of the data on the next page. // if ( RecordHeader->ThisLsn.QuadPart == LogRecordPage->Copy.LastLsn.QuadPart ) { //**** xxEql( RecordHeader->ThisLsn, LogRecordPage->Copy.LastLsn ) BOOLEAN Wrapped; LfsNextLogPageOffset( Lfcb, LogHeaderOffset, &LogHeaderOffset, &Wrapped ); LsnOffset = LogHeaderOffset + Lfcb->LogPageDataOffset; //**** xxAdd( LogHeaderOffset, Lfcb->LogPageDataOffset ); // // If we wrapped, we need to increment the sequence number. // if (Wrapped) { SequenceNumber = SequenceNumber + 1; //**** xxAdd( SequenceNumber, LfsLi1 ); } } else { LiQuadAlign( EndOfLogRecord, &LsnOffset ); } // // Compute the Lsn based on the file offset and the sequence count. // Lsn->QuadPart = LfsFileOffsetToLsn( Lfcb, LsnOffset, SequenceNumber ); // // If this Lsn is within the legal range for the file, we return TRUE. // Otherwise FALSE indicates that there are no more Lsn's. // if (LfsIsLsnInFile( Lfcb, *Lsn )) { FoundNextLsn = TRUE; } } finally { DebugUnwind( LfsFindNextLsn ); // // Unpin the log page header if held. // if (LogRecordPageBcb != NULL) { CcUnpinData( LogRecordPageBcb ); } DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart ); DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart ); DebugTrace( -1, Dbg, "LfsFindNextLsn: Exit -> %08x\n", FoundNextLsn ); } return FoundNextLsn; }
BOOLEAN NtfsFastUnlockSingle ( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This is a call back routine for doing the fast unlock single call. Arguments: FileObject - Supplies the file object used in this operation FileOffset - Supplies the file offset used in this operation Length - Supplies the length used in this operation ProcessId - Supplies the process ID used in this operation Key - Supplies the key used in this operation Status - Receives the Status if this operation is successful Return Value: BOOLEAN - TRUE if this operation completed and FALSE if caller needs to take the long route. --*/ { BOOLEAN Results; PFCB Fcb; PSCB Scb; BOOLEAN ResourceAcquired = FALSE; UNREFERENCED_PARAMETER( DeviceObject ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsFastUnlockSingle\n") ); IoStatus->Information = 0; // // Decode the type of file object we're being asked to process and // make sure that is is only a user file open. // if ((Scb = NtfsFastDecodeUserFileOpen( FileObject )) == NULL) { IoStatus->Status = STATUS_INVALID_PARAMETER; DebugTrace( -1, Dbg, ("NtfsFastUnlockSingle -> TRUE (STATUS_INVALID_PARAMETER)\n") ); return TRUE; } Fcb = Scb->Fcb; // // Acquire exclusive access to the Fcb this operation can always wait // FsRtlEnterFileSystem(); if (Scb->ScbType.Data.FileLock == NULL) { (VOID) ExAcquireResourceExclusive( Fcb->Resource, TRUE ); ResourceAcquired = TRUE; } else { //(VOID) ExAcquireResourceShared( Fcb->Resource, TRUE ); } try { // // We check whether we can proceed based on the state of the file oplocks. // if ((Scb->ScbType.Data.Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Scb->ScbType.Data.Oplock )) { try_return( Results = FALSE ); } // // If we don't have a file lock, then get one now. // if (Scb->ScbType.Data.FileLock == NULL && !NtfsCreateFileLock( Scb, FALSE )) { try_return( Results = FALSE ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request. The call will always succeed. // Results = TRUE; IoStatus->Status = FsRtlFastUnlockSingle( Scb->ScbType.Data.FileLock, FileObject, FileOffset, Length, ProcessId, Key, NULL, FALSE ); // // Set the flag indicating if Fast I/O is possible. We are // only concerned if there are no longer any filelocks on this // file. // if (!FsRtlAreThereCurrentFileLocks( Scb->ScbType.Data.FileLock ) && (Scb->Header.IsFastIoPossible != FastIoIsPossible)) { NtfsAcquireFsrtlHeader( Scb ); Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb ); NtfsReleaseFsrtlHeader( Scb ); } try_exit: NOTHING; } finally { DebugUnwind( NtfsFastUnlockSingle ); // // Release the Fcb, and return to our caller // if (ResourceAcquired) { ExReleaseResource( Fcb->Resource ); } FsRtlExitFileSystem(); DebugTrace( -1, Dbg, ("NtfsFastUnlockSingle -> %08lx\n", Results) ); } return Results; }
NTSTATUS FatCommonLockControl ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for doing Lock control operations 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 = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PCCB Ccb; BOOLEAN OplockPostIrp = FALSE; PAGED_CODE(); // // Get a pointer to the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "FatCommonLockControl\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); // // Decode the type of file object we're being asked to process // TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); // // If the file is not a user file open then we reject the request // as an invalid parameter // if (TypeOfOpen != UserFileOpen) { FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); DebugTrace(-1, Dbg, "FatCommonLockControl -> STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; } // // Acquire exclusive access to the Fcb and enqueue the Irp if we didn't // get access // if (!FatAcquireSharedFcb( IrpContext, Fcb )) { Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); return Status; } try { // // We check whether we can proceed // based on the state of the file oplocks. // #if (NTDDI_VERSION >= NTDDI_WIN8) if (((IRP_MN_LOCK == IrpSp->MinorFunction) && ((ULONGLONG)IrpSp->Parameters.LockControl.ByteOffset.QuadPart < (ULONGLONG)Fcb->Header.AllocationSize.QuadPart)) || ((IRP_MN_LOCK != IrpSp->MinorFunction) && FsRtlAreThereWaitingFileLocks( &Fcb->Specific.Fcb.FileLock ))) { // // Check whether we can proceed based on the state of file oplocks if doing // an operation that interferes with oplocks. Those operations are: // // 1. Lock a range within the file's AllocationSize. // 2. Unlock a range when there are waiting locks on the file. This one // is not guaranteed to interfere with oplocks, but it could, as // unlocking this range might cause a waiting lock to be granted // within AllocationSize! // #endif Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), Irp, IrpContext, FatOplockComplete, NULL ); #if (NTDDI_VERSION >= NTDDI_WIN8) } #endif if (Status != STATUS_SUCCESS) { OplockPostIrp = TRUE; try_return( NOTHING ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request // Status = FsRtlProcessFileLock( &Fcb->Specific.Fcb.FileLock, Irp, NULL ); // // Set the flag indicating if Fast I/O is possible // Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); try_exit: NOTHING; } finally { DebugUnwind( FatCommonLockControl ); // // Only if this is not an abnormal termination do we delete the // irp context // if (!AbnormalTermination() && !OplockPostIrp) { FatCompleteRequest( IrpContext, FatNull, 0 ); } // // Release the Fcb, and return to our caller // FatReleaseFcb( IrpContext, Fcb ); DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); } return Status; }
VOID FatPrepareWriteDirectoryFile ( IN PIRP_CONTEXT IrpContext, IN PDCB Dcb, IN VBO StartingVbo, IN ULONG ByteCount, OUT PBCB *Bcb, OUT PVOID *Buffer, IN BOOLEAN Zero, IN BOOLEAN Reversible, OUT PNTSTATUS Status ) /*++ Routine Description: This routine first looks to see if the specified range of sectors is already in the cache. If so, it increments the BCB PinCount, sets the BCB dirty, and returns TRUE with the location of the sectors. The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to FatPrepareWriteVolumeFile() above. Arguments: Dcb - Pointer to the DCB for the directory StartingVbo - The virtual offset of the first byte to be written ByteCount - Number of bytes to be written Bcb - Returns a pointer to the BCB which is valid until unpinned Buffer - Returns a pointer to the sectors, which is valid until unpinned Zero - Supplies TRUE if the specified range of bytes should be zeroed Reversible - Supplies TRUE if the specified range of modification should be repinned so that the operation can be reversed in a controlled fashion if errors are encountered. Status - Returns the status of the operation. --*/ { LARGE_INTEGER Vbo; ULONG InitialAllocation; BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; ULONG ClusterSize; PVOID LocalBuffer; PAGED_CODE(); DebugTrace(+1, Dbg, "FatPrepareWriteDirectoryFile\n", 0); DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); *Bcb = NULL; *Buffer = NULL; // // If we need to create a directory file and initialize the // cachemap, do so. // FatOpenDirectoryFile( IrpContext, Dcb ); // // If the transfer is beyond the allocation size we need to // extend the directory's allocation. The call to // AddFileAllocation will raise a condition if // it runs out of disk space. Note that the root directory // cannot be extended. // Vbo.QuadPart = StartingVbo; try { if (StartingVbo + ByteCount > Dcb->Header.AllocationSize.LowPart) { if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Dcb->Vcb)) { FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); } DebugTrace(0, Dbg, "Try extending normal directory\n", 0); InitialAllocation = Dcb->Header.AllocationSize.LowPart; FatAddFileAllocation( IrpContext, Dcb, Dcb->Specific.Dcb.DirectoryFile, StartingVbo + ByteCount ); UnwindWeAllocatedDiskSpace = TRUE; // // Inform the cache manager of the new allocation // Dcb->Header.FileSize.LowPart = Dcb->Header.AllocationSize.LowPart; CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); // // Set up the Bitmap buffer if it is not big enough already // FatCheckFreeDirentBitmap( IrpContext, Dcb ); // // The newly allocated clusters should be zeroed starting at // the previous allocation size // Zero = TRUE; Vbo.QuadPart = InitialAllocation; ByteCount = Dcb->Header.AllocationSize.LowPart - InitialAllocation; } // // Call the Cache Manager to attempt the transfer, going one cluster // at a time to avoid pinning across a page boundary. // ClusterSize = 1 << Dcb->Vcb->AllocationSupport.LogOfBytesPerCluster; while (ByteCount > 0) { ULONG BytesToPin; *Bcb = NULL; if (ByteCount > ClusterSize) { BytesToPin = ClusterSize; } else { BytesToPin = ByteCount; } ASSERT( (Vbo.QuadPart / ClusterSize) == (Vbo.QuadPart + BytesToPin - 1)/ClusterSize ); if (!CcPinRead( Dcb->Specific.Dcb.DirectoryFile, &Vbo, BytesToPin, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), Bcb, &LocalBuffer )) { // // Could not read the data without waiting (cache miss). // FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } // // Update our caller with the beginning of their request. // if (*Buffer == NULL) { *Buffer = LocalBuffer; } DbgDoit( IrpContext->PinCount += 1 ) if (Zero) { // // We set this guy dirty right now so that we can raise CANT_WAIT when // it needs to be done. It'd be beautiful if we could noop the read IO // since we know we don't care about it. // RtlZeroMemory( LocalBuffer, BytesToPin ); CcSetDirtyPinnedData( *Bcb, NULL ); } ByteCount -= BytesToPin; Vbo.QuadPart += BytesToPin; if (ByteCount > 0) { FatUnpinBcb( IrpContext, *Bcb ); } } // // This lets us get the data pinned until we complete the request // and writes the dirty bit through to the disk. // FatSetDirtyBcb( IrpContext, *Bcb, Dcb->Vcb, Reversible ); *Status = STATUS_SUCCESS; } finally { DebugUnwind( FatPrepareWriteDirectoryFile ); if (AbnormalTermination()) { // // These steps are carefully arranged - FatTruncateFileAllocation can raise. // Make sure we unpin the buffer. If FTFA raises, the effect should be benign. // FatUnpinBcb(IrpContext, *Bcb); if (UnwindWeAllocatedDiskSpace == TRUE) { // // Inform the cache manager of the change. // FatTruncateFileAllocation( IrpContext, Dcb, InitialAllocation ); Dcb->Header.FileSize.LowPart = Dcb->Header.AllocationSize.LowPart; CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); } } DebugTrace(-1, Dbg, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %08lx\n", *Bcb); } return; }
PLBCB LfsGetLbcb ( IN PLFCB Lfcb ) /*++ Routine Description: This routine is called to add a Lbcb to the active queue. Arguments: Lfcb - This is the file control block for the log file. Return Value: PLBCB - Pointer to the Lbcb allocated. --*/ { PLBCB Lbcb = NULL; PVOID PageHeader; PBCB PageHeaderBcb = NULL; BOOLEAN WrappedOrUsaError; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsGetLbcb: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); // // Use a try-finally to facilitate cleanup. // try { // // Pin the desired record page. // LfsPreparePinWriteData( Lfcb, Lfcb->NextLogPage, (ULONG)Lfcb->LogPageSize, &PageHeader, &PageHeaderBcb ); // // Put our signature into the page so we won't fail if we // see a previous 'BAAD' signature. // *((PULONG) PageHeader) = LFS_SIGNATURE_RECORD_PAGE_ULONG; // // Now allocate an Lbcb. // LfsAllocateLbcb( Lfcb, &Lbcb ); // // If we are at the beginning of the file we test that the // sequence number won't wrap to 0. // if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL ) && ( Lfcb->NextLogPage == Lfcb->FirstLogPage )) { Lfcb->SeqNumber = Lfcb->SeqNumber + 1; // // If the sequence number is going from 0 to 1, then // this is the first time the log file has wrapped. We want // to remember this because it means that we can now do // large spiral writes. // if (Int64ShllMod32( Lfcb->SeqNumber, Lfcb->FileDataBits ) == 0) { LfsDebugTrace( 0, Dbg, "Log sequence number about to wrap: Lfcb -> %08lx\n", Lfcb ); KeBugCheck( FILE_SYSTEM ); } // // If this number is greater or equal to the wrap sequence number in // the Lfcb, set the wrap flag in the Lbcb. // if (!FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED ) && ( Lfcb->SeqNumber >= Lfcb->SeqNumberForWrap )) { SetFlag( Lbcb->LbcbFlags, LBCB_LOG_WRAPPED ); SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED ); } } // // Now initialize the rest of the Lbcb fields. // Lbcb->FileOffset = Lfcb->NextLogPage; Lbcb->SeqNumber = Lfcb->SeqNumber; Lbcb->BufferOffset = Lfcb->LogPageDataOffset; // // Store the next page in the Lfcb. // LfsNextLogPageOffset( Lfcb, Lfcb->NextLogPage, &Lfcb->NextLogPage, &WrappedOrUsaError ); Lbcb->Length = Lfcb->LogPageSize; Lbcb->PageHeader = PageHeader; Lbcb->LogPageBcb = PageHeaderBcb; Lbcb->ResourceThread = ExGetCurrentResourceThread(); // // If we are reusing a previous page then set a flag in // the Lbcb to indicate that we should flush a copy // first. // if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) { SetFlag( Lbcb->LbcbFlags, LBCB_FLUSH_COPY ); ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL ); (ULONG)Lbcb->BufferOffset = Lfcb->ReusePageOffset; Lbcb->Flags = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Flags; Lbcb->LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn; Lbcb->LastEndLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Header.Packed.LastEndLsn; } // // Put the Lbcb on the active queue // InsertTailList( &Lfcb->LbcbActive, &Lbcb->ActiveLinks ); SetFlag( Lbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ); } finally { DebugUnwind( LfsGetLbcb ); // // If an error occurred, we need to clean up any blocks which // have not been added to the active queue. // if (AbnormalTermination()) { if (Lbcb != NULL) { LfsDeallocateLbcb( Lfcb, Lbcb ); Lbcb = NULL; } // // Unpin the system page if pinned. // if (PageHeaderBcb != NULL) { CcUnpinData( PageHeaderBcb ); } } LfsDebugTrace( -1, Dbg, "LfsGetLbcb: Exit\n", 0 ); } return Lbcb; }
NTSTATUS FatSetFsLabelInfo ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN PFILE_FS_LABEL_INFORMATION Buffer ) /*++ Routine Description: This routine implements the set volume label call Arguments: Vcb - Supplies the Vcb being queried Buffer - Supplies the input where the information is stored. Return Value: NTSTATUS - Returns the status for the operation --*/ { NTSTATUS Status; PDIRENT Dirent; PBCB DirentBcb = NULL; ULONG ByteOffset; WCHAR TmpBuffer[11]; UCHAR OemBuffer[11]; OEM_STRING OemLabel; UNICODE_STRING UnicodeString; UNICODE_STRING UpcasedLabel; DebugTrace(+1, Dbg, "FatSetFsLabelInfo...\n", 0); // // Setup our local variable // UnicodeString.Length = (USHORT)Buffer->VolumeLabelLength; UnicodeString.MaximumLength = UnicodeString.Length; UnicodeString.Buffer = (PWSTR) &Buffer->VolumeLabel[0]; // // Make sure the name can fit into the stack buffer // if ( UnicodeString.Length > 11*sizeof(WCHAR) ) { return STATUS_INVALID_VOLUME_LABEL; } // // Upcase the name and convert it to the Oem code page. // OemLabel.Buffer = &OemBuffer[0]; OemLabel.Length = 0; OemLabel.MaximumLength = 11; Status = RtlUpcaseUnicodeStringToCountedOemString( &OemLabel, &UnicodeString, FALSE ); // // Volume label that fits in 11 unicode character length limit // is not necessary within 11 characters in OEM character set. // if (!NT_SUCCESS( Status )) { DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); return STATUS_INVALID_VOLUME_LABEL; } // // Strip spaces off of the label. // if (OemLabel.Length > 0) { USHORT i; USHORT LastSpaceIndex = MAXUSHORT; // // Check the label for illegal characters // for ( i = 0; i < (ULONG)OemLabel.Length; i += 1 ) { if ( FsRtlIsLeadDbcsCharacter( OemLabel.Buffer[i] ) ) { LastSpaceIndex = MAXUSHORT; i += 1; continue; } if (!FsRtlIsAnsiCharacterLegalFat(OemLabel.Buffer[i], FALSE) || (OemLabel.Buffer[i] == '.')) { return STATUS_INVALID_VOLUME_LABEL; } // // Watch for the last run of spaces, so we can strip them. // if (OemLabel.Buffer[i] == ' ' && LastSpaceIndex == MAXUSHORT) { LastSpaceIndex = i; } else { LastSpaceIndex = MAXUSHORT; } } if (LastSpaceIndex != MAXUSHORT) { OemLabel.Length = LastSpaceIndex; } } // // Get the Unicode upcased string to store in the VPB. // UpcasedLabel.Length = UnicodeString.Length; UpcasedLabel.MaximumLength = 11*sizeof(WCHAR); UpcasedLabel.Buffer = &TmpBuffer[0]; Status = RtlOemStringToCountedUnicodeString( &UpcasedLabel, &OemLabel, FALSE ); if (!NT_SUCCESS( Status )) { DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); return STATUS_INVALID_VOLUME_LABEL; } DirentBcb = NULL; // // Make this look like a write through to disk. This is important to // avoid a unpleasant window where it looks like we have the wrong volume. // SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); try { // // Are we setting or removing the label? Note that shaving spaces could // make this different than wondering if the input buffer is non-zero length. // if (OemLabel.Length > 0) { // // Locate the volume label if there already is one // FatLocateVolumeLabel( IrpContext, Vcb, &Dirent, &DirentBcb, &ByteOffset ); // // Check that we really got one, if not then we need to create // a new one. The procedure we call will raise an appropriate // status if we are not able to allocate a new dirent // if (Dirent == NULL) { ByteOffset = FatCreateNewDirent( IrpContext, Vcb->RootDcb, 1 ); FatPrepareWriteDirectoryFile( IrpContext, Vcb->RootDcb, ByteOffset, sizeof(DIRENT), &DirentBcb, &Dirent, FALSE, TRUE, &Status ); ASSERT( NT_SUCCESS( Status )); } else { // // Just mark this guy dirty now. // FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); } // // Now reconstruct the volume label dirent. // FatConstructLabelDirent( IrpContext, Dirent, &OemLabel ); // // Unpin the Bcb here so that we will get any IO errors // here before changing the VPB label. // FatUnpinBcb( IrpContext, DirentBcb ); FatUnpinRepinnedBcbs( IrpContext ); // // Now set the upcased label in the VPB // RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0], &UpcasedLabel.Buffer[0], UpcasedLabel.Length ); Vcb->Vpb->VolumeLabelLength = UpcasedLabel.Length; } else { // // Otherwise we're trying to delete the label // Locate the current volume label if there already is one // FatLocateVolumeLabel( IrpContext, Vcb, &Dirent, &DirentBcb, &ByteOffset ); // // Check that we really got one // if (Dirent == NULL) { try_return( Status = STATUS_SUCCESS ); } // // Now delete the current label. // Dirent->FileName[0] = FAT_DIRENT_DELETED; ASSERT( (Vcb->RootDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || RtlAreBitsSet( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, ByteOffset / sizeof(DIRENT), 1 ) ); RtlClearBits( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, ByteOffset / sizeof(DIRENT), 1 ); FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); // // Unpin the Bcb here so that we will get any IO errors // here before changing the VPB label. // FatUnpinBcb( IrpContext, DirentBcb ); FatUnpinRepinnedBcbs( IrpContext ); // // Now set the label in the VPB // Vcb->Vpb->VolumeLabelLength = 0; } Status = STATUS_SUCCESS; FatSortDirectory(IrpContext, Vcb->RootDcb); try_exit: NOTHING; } finally { DebugUnwind( FatSetFsALabelInfo ); FatUnpinBcb( IrpContext, DirentBcb ); DebugTrace(-1, Dbg, "FatSetFsALabelInfo -> STATUS_SUCCESS\n", 0); } return Status; }
NTSTATUS FatCommonSetVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for setting Volume Information called by both the fsd and fsp threads. Arguments: Irp - Supplies the Irp being processed Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp; PVCB Vcb; PFCB Fcb; PCCB Ccb; TYPE_OF_OPEN TypeOfOpen; ULONG Length; FS_INFORMATION_CLASS FsInformationClass; PVOID Buffer; // // Get the current stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "FatCommonSetVolumeInfo...\n", 0); 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; // // Decode the file object to get the Vcb // TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); if (TypeOfOpen != UserVolumeOpen) { FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n", 0); return STATUS_ACCESS_DENIED; } // // Acquire exclusive access to the Vcb and enqueue the Irp if we didn't // get access // if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status ); return Status; } try { // // Make sure the vcb is in a usable condition. This will raise // and error condition if the volume is unusable // // Also verify the Root Dcb since we need info from there. // FatVerifyFcb( IrpContext, Vcb->RootDcb ); // // 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 = FatSetFsLabelInfo( IrpContext, Vcb, Buffer ); break; default: Status = STATUS_INVALID_PARAMETER; break; } FatUnpinRepinnedBcbs( IrpContext ); } finally { DebugUnwind( FatCommonSetVolumeInfo ); FatReleaseVcb( IrpContext, Vcb ); if (!AbnormalTermination()) { FatCompleteRequest( IrpContext, Irp, Status ); } DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status); } return Status; }
NTSTATUS UdfLoadSparingTables( PIRP_CONTEXT IrpContext, PVCB Vcb, PPCB Pcb, ULONG Reference ) /*++ Routine Description: This routine reads the sparing tables for a partition and fills in the sparing Mcb. Arguments: Vcb - the volume hosting the spared partition Pcb - the partion block corresponding to the volume Reference - the partition reference being pulled in Return Value: NTSTATUS according to whether the sparing tables were loaded --*/ { NTSTATUS Status; ULONG SparingTable; PULONG SectorBuffer; ULONG Psn; ULONG RemainingBytes; ULONG ByteOffset; ULONG TotalBytes; BOOLEAN Complete; PSPARING_TABLE_HEADER Header; PSPARING_TABLE_ENTRY Entry; PPARTITION Partition = &Pcb->Partition[Reference]; PPARTMAP_SPARABLE Map = Partition->Physical.SparingMap; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb ); ASSERT( Map != NULL ); DebugTrace(( +1, Dbg, "UdfLoadSparingTables, Vcb %08x, PcbPartition %08x, Map @ %08x\n", Vcb, Partition, Map )); DebugTrace(( 0, Dbg, "UdfLoadSparingTables, Map sez: PacketLen %u, NTables %u, TableSize %u\n", Map->PacketLength, Map->NumSparingTables, Map->TableSize)); // // Check that the sparale map appears sane. If there are no sparing tables that // is pretty OK, and it'll wind up looking like a regular physical partition. // if (Map->NumSparingTables == 0) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, no sparing tables claimed!\n" )); DebugTrace(( -1, Dbg, "UdfLoadSparingTables -> STATUS_SUCCESS\n" )); return STATUS_SUCCESS; } if (Map->NumSparingTables > sizeof(Map->TableLocation)/sizeof(ULONG)) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, too many claimed tables to fit! (max %u)\n", sizeof(Map->TableLocation)/sizeof(ULONG))); DebugTrace(( -1, Dbg, "UdfLoadSparingTables -> STATUS_DISK_CORRUPT_ERROR\n" )); return STATUS_DISK_CORRUPT_ERROR; } if (Map->PacketLength != UDF_SPARING_PACKET_LENGTH) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, packet size is %u (not %u!\n", Map->PacketLength, UDF_SPARING_PACKET_LENGTH )); DebugTrace(( -1, Dbg, "UdfLoadSparingTables -> STATUS_DISK_CORRUPT_ERROR\n" )); return STATUS_DISK_CORRUPT_ERROR; } if (Map->TableSize < sizeof(SPARING_TABLE_HEADER) || (Map->TableSize - sizeof(SPARING_TABLE_HEADER)) % sizeof(SPARING_TABLE_ENTRY) != 0) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, sparing table size is too small or unaligned!\n" )); DebugTrace(( -1, Dbg, "UdfLoadSparingTables -> STATUS_DISK_CORRUPT_ERROR\n" )); return STATUS_DISK_CORRUPT_ERROR; } #ifdef UDF_SANITY DebugTrace(( 0, Dbg, "UdfLoadSparingTables" )); for (SparingTable = 0; SparingTable < Map->NumSparingTables; SparingTable++) { DebugTrace(( 0, Dbg, ", Table %u @ %x", SparingTable, Map->TableLocation[SparingTable] )); } DebugTrace(( 0, Dbg, "\n" )); #endif // // If a sparing mcb doesn't exist, manufacture one. // if (Pcb->SparingMcb == NULL) { Pcb->SparingMcb = FsRtlAllocatePoolWithTag( PagedPool, sizeof(LARGE_MCB), TAG_SPARING_MCB ); FsRtlInitializeLargeMcb( Pcb->SparingMcb, PagedPool ); } SectorBuffer = FsRtlAllocatePoolWithTag( PagedPool, PAGE_SIZE, TAG_NSR_FSD ); // // Now loop across the sparing tables and pull the data in. // try { for (Complete = FALSE, SparingTable = 0; SparingTable < Map->NumSparingTables; SparingTable++) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, loading sparing table %u!\n", SparingTable )); ByteOffset = 0; TotalBytes = 0; RemainingBytes = 0; do { if (RemainingBytes == 0) { (VOID) UdfReadSectors( IrpContext, BytesFromSectors( Vcb, Map->TableLocation[SparingTable] ) + ByteOffset, SectorSize( Vcb ), FALSE, SectorBuffer, Vcb->TargetDeviceObject ); // // Verify the descriptor at the head of the sparing table. If it is not // valid, we just break out for a chance at the next table, if any. // if (ByteOffset == 0) { Header = (PSPARING_TABLE_HEADER) SectorBuffer; if (!UdfVerifyDescriptor( IrpContext, &Header->Destag, 0, SectorSize( Vcb ), Header->Destag.Lbn, TRUE )) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, sparing table %u didn't verify destag!\n", SparingTable )); break; } if (!UdfUdfIdentifierContained( &Header->RegID, &UdfSparingTableIdentifier, UDF_VERSION_150, UDF_VERSION_RECOGNIZED, OSCLASS_INVALID, OSIDENTIFIER_INVALID)) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, sparing table %u didn't verify regid!\n", SparingTable )); break; } // // Calculate the total number bytes this map spans and check it against what // we were told the sparing table sizes are. // DebugTrace(( 0, Dbg, "UdfLoadSparingTables, Sparing table %u has %u entries\n", SparingTable, Header->TableEntries )); TotalBytes = sizeof(SPARING_TABLE_HEADER) + Header->TableEntries * sizeof(SPARING_TABLE_ENTRY); if (Map->TableSize < TotalBytes) { DebugTrace(( 0, Dbg, "UdfLoadSparingTables, sparing table #ents %u overflows allocation!\n", Header->TableEntries )); break; } // // So far so good, advance past the header. // ByteOffset = sizeof(SPARING_TABLE_HEADER); Entry = Add2Ptr( SectorBuffer, sizeof(SPARING_TABLE_HEADER), PSPARING_TABLE_ENTRY ); } else { // // Pick up in the new sector. // Entry = (PSPARING_TABLE_ENTRY) SectorBuffer; } RemainingBytes = Min( SectorSize( Vcb ), TotalBytes - ByteOffset ); } // // Add the mapping. Since sparing tables are an Lbn->Psn mapping, // very odd, and I want to simplify things by putting the sparing // in right at IO dispatch, translate this to a Psn->Psn mapping. // if (Entry->Original != UDF_SPARING_AVALIABLE && Entry->Original != UDF_SPARING_DEFECTIVE) { Psn = Partition->Physical.Start + SectorsFromBlocks( Vcb, Entry->Original ); DebugTrace(( 0, Dbg, "UdfLoadSparingTables, mapping from Psn %x (Lbn %x) -> Psn %x\n", Psn, Entry->Original, Entry->Mapped )); FsRtlAddLargeMcbEntry( Pcb->SparingMcb, Psn, Entry->Mapped, UDF_SPARING_PACKET_LENGTH ); } // // Advance to the next, and drop out if we've hit the end. // ByteOffset += sizeof(SPARING_TABLE_ENTRY); RemainingBytes -= sizeof(SPARING_TABLE_ENTRY); Entry++; } while ( ByteOffset < TotalBytes ); } } finally { DebugUnwind( UdfLoadSparingTables ); UdfFreePool( &SectorBuffer ); } DebugTrace(( -1, Dbg, "UdfLoadSparingTables -> STATUS_SUCCESS\n" )); return STATUS_SUCCESS; }
BOOLEAN NtfsFastUnlockAllByKey ( IN PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This is a call back routine for doing the fast unlock all by key call. Arguments: FileObject - Supplies the file object used in this operation ProcessId - Supplies the process ID used in this operation Key - Supplies the key used in this operation Status - Receives the Status if this operation is successful Return Value: BOOLEAN - TRUE if this operation completed and FALSE if caller needs to take the long route. --*/ { BOOLEAN Results; IRP_CONTEXT IrpContext; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; UNREFERENCED_PARAMETER( DeviceObject ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsFastUnlockAllByKey\n") ); IoStatus->Information = 0; // // Decode the type of file object we're being asked to process and // make sure that is is only a user file open. // TypeOfOpen = NtfsDecodeFileObject( &IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE ); if (TypeOfOpen != UserFileOpen) { IoStatus->Status = STATUS_INVALID_PARAMETER; IoStatus->Information = 0; DebugTrace( -1, Dbg, ("NtfsFastUnlockAllByKey -> TRUE (STATUS_INVALID_PARAMETER)\n") ); return TRUE; } // // Acquire exclusive access to the Fcb this operation can always wait // FsRtlEnterFileSystem(); if (Scb->ScbType.Data.FileLock == NULL) { (VOID) ExAcquireResourceExclusive( Fcb->Resource, TRUE ); } else { (VOID) ExAcquireResourceShared( Fcb->Resource, TRUE ); } try { // // We check whether we can proceed based on the state of the file oplocks. // if (!FsRtlOplockIsFastIoPossible( &Scb->ScbType.Data.Oplock )) { try_return( Results = FALSE ); } // // If we don't have a file lock, then get one now. // if (Scb->ScbType.Data.FileLock == NULL && !NtfsCreateFileLock( Scb, FALSE )) { try_return( Results = FALSE ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request. The call will always succeed. // Results = TRUE; IoStatus->Status = FsRtlFastUnlockAllByKey( Scb->ScbType.Data.FileLock, FileObject, ProcessId, Key, NULL ); // // Set the flag indicating if Fast I/O is possible // NtfsAcquireFsrtlHeader( Scb ); Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb ); NtfsReleaseFsrtlHeader( Scb ); try_exit: NOTHING; } finally { DebugUnwind( NtfsFastUnlockAllByKey ); // // Release the Fcb, and return to our caller // ExReleaseResource( Fcb->Resource ); FsRtlExitFileSystem(); DebugTrace( -1, Dbg, ("NtfsFastUnlockAllByKey -> %08lx\n", Results) ); } return Results; }
NTSTATUS FatCommonClose ( IN PVCB Vcb, IN PFCB Fcb, IN PCCB Ccb, IN TYPE_OF_OPEN TypeOfOpen, IN BOOLEAN Wait, OUT PBOOLEAN VcbDeleted OPTIONAL ) /*++ Routine Description: This is the common routine for closing a file/directory called by both the fsd and fsp threads. Close is invoked whenever the last reference to a file object is deleted. Cleanup is invoked when the last handle to a file object is closed, and is called before close. The function of close is to completely tear down and remove the fcb/dcb/ccb structures associated with the file object. Arguments: Fcb - Supplies the file to process. Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE then we must try to acquire the Vcb anyway. VcbDeleted - Returns whether the VCB was deleted by this call. Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status; PDCB ParentDcb; BOOLEAN RecursiveClose; BOOLEAN LocalVcbDeleted; IRP_CONTEXT IrpContext; #if __NDAS_FAT_SECONDARY__ BOOLEAN volDoResourceAcquired = FALSE; BOOLEAN send2Primary = FALSE; BOOLEAN volDoCcb = FALSE; _U64 primaryFileHandle; PVOLUME_DEVICE_OBJECT volDo = CONTAINING_RECORD( Vcb, VOLUME_DEVICE_OBJECT, Vcb ); BOOLEAN volDoSessionResourceAcquired = FALSE; PSECONDARY_REQUEST secondaryRequest = NULL; PNDFS_REQUEST_HEADER ndfsRequestHeader; PNDFS_WINXP_REQUEST_HEADER ndfsWinxpRequestHeader; PNDFS_WINXP_REPLY_HEADER ndfsWinxpReplytHeader; LARGE_INTEGER timeOut; #endif PAGED_CODE(); DebugTrace(+1, Dbg, "FatCommonClose...\n", 0); // // Initailize the callers variable, if needed. // LocalVcbDeleted = FALSE; if (ARGUMENT_PRESENT( VcbDeleted )) { *VcbDeleted = LocalVcbDeleted; } // // Special case the unopened file object // if (TypeOfOpen == UnopenedFileObject) { DebugTrace(0, Dbg, "Close unopened file object\n", 0); Status = STATUS_SUCCESS; DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); return Status; } // // Set up our stack IrpContext. // RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; IrpContext.NodeByteSize = sizeof( IrpContext ); IrpContext.MajorFunction = IRP_MJ_CLOSE; IrpContext.Vcb = Vcb; if (Wait) { SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); } #if __NDAS_FAT_SECONDARY__ if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) SetFlag( IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT ); if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { if (!FlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT)) { return STATUS_PENDING; } volDoResourceAcquired = SecondaryAcquireResourceSharedStarveExclusiveLite( &IrpContext, &volDo->Resource, BooleanFlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT) ); if (volDoResourceAcquired == FALSE) { ASSERT( FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ); // It's not always garented continue; return STATUS_PENDING; } } if (volDo->NetdiskEnableMode == NETDISK_SECONDARY && !FlagOn(volDo->NetdiskPartitionInformation.Flags, NETDISK_PARTITION_INFORMATION_FLAG_INDIRECT)) { if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile) { SetFlag( IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT ); } } #endif // // Acquire exclusive access to the Vcb and enqueue the irp if we didn't // get access. // #if __NDAS_FAT_SECONDARY__ if (!(FlagOn(IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT) ? ExAcquireResourceExclusiveLite( &Vcb->SecondaryResource, Wait ) : ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait ))) { if (volDoResourceAcquired) { ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) ); SecondaryReleaseResourceLite( NULL, &volDo->Resource ); } return STATUS_PENDING; } #else if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) { return STATUS_PENDING; } #endif // // The following test makes sure that we don't blow away an Fcb if we // are trying to do a Supersede/Overwrite open above us. This test // does not apply for the EA file. // if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) && Vcb->EaFcb != Fcb) { #if __NDAS_FAT_SECONDARY__ if (FlagOn(IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT)) ExReleaseResourceLite( &Vcb->SecondaryResource ); else ExReleaseResourceLite( &Vcb->Resource ); if (volDoResourceAcquired) { ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) ); SecondaryReleaseResourceLite( NULL, &volDo->Resource ); } #else ExReleaseResourceLite( &Vcb->Resource ); #endif return STATUS_PENDING; } // // Setting the following flag prevents recursive closes of directory file // objects, which are handled in a special case loop. // if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) { RecursiveClose = TRUE; } else { SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); RecursiveClose = FALSE; // // Since we are at the top of the close chain, we need to add // a reference to the VCB. This will keep it from going away // on us until we are ready to check for a dismount below. // Vcb->OpenFileCount += 1; } try { // // Case on the type of open that we are trying to close. // switch (TypeOfOpen) { case VirtualVolumeFile: DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0); // // Remove this internal, residual open from the count. // InterlockedDecrement( &(Vcb->InternalOpenCount) ); InterlockedDecrement( &(Vcb->ResidualOpenCount) ); try_return( Status = STATUS_SUCCESS ); break; case UserVolumeOpen: DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0); #if __NDAS_FAT_SECONDARY__ if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { volDoCcb = TRUE; if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) { if ( FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_CORRUPTED) ) Fcb->CorruptedCcbCloseCount --; send2Primary = FALSE; } else send2Primary = TRUE; primaryFileHandle = Ccb->PrimaryFileHandle; ExAcquireFastMutex( &volDo->Secondary->RecoveryCcbQMutex ); RemoveEntryList( &Ccb->ListEntry ); ExReleaseFastMutex( &volDo->Secondary->RecoveryCcbQMutex ); InitializeListHead( &Ccb->ListEntry ); Ccb->FileObject = NULL; if (Ccb->Buffer) ExFreePool( Ccb->Buffer ); Ccb->FileObject = NULL; InterlockedDecrement( &Vcb->SecondaryOpenFileCount ); } else { Vcb->DirectAccessOpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } } if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_OPEN_BY_PRIMARY_SESSION)) { InterlockedDecrement( &Vcb->PrimaryOpenFileCount ); } FatDeleteCcb( &IrpContext, &Ccb ); if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { InterlockedDecrement( &Fcb->OpenCount ); if (Fcb->OpenCount == 0) { ExAcquireFastMutex( &volDo->Secondary->FcbQMutex ); RemoveEntryList( &Fcb->ListEntry ); InitializeListHead( &Fcb->ListEntry ); ExReleaseFastMutex( &volDo->Secondary->FcbQMutex ); Fcb->Header.NodeTypeCode = FAT_NTC_FCB; FatDeleteFcb( &IrpContext, &Fcb ); Secondary_Dereference( volDo->Secondary ); } } else { try_return( Status = STATUS_SUCCESS ); } #else Vcb->DirectAccessOpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } FatDeleteCcb( &IrpContext, &Ccb ); try_return( Status = STATUS_SUCCESS ); #endif break; case EaFile: DebugTrace(0, Dbg, "Close EaFile\n", 0); // // Remove this internal, residual open from the count. // InterlockedDecrement( &(Vcb->InternalOpenCount) ); InterlockedDecrement( &(Vcb->ResidualOpenCount) ); try_return( Status = STATUS_SUCCESS ); break; case DirectoryFile: DebugTrace(0, Dbg, "Close DirectoryFile\n", 0); InterlockedDecrement( &Fcb->Specific.Dcb.DirectoryFileOpenCount ); // // Remove this internal open from the count. // InterlockedDecrement( &(Vcb->InternalOpenCount) ); // // If this is the root directory, it is a residual open // as well. // if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) { InterlockedDecrement( &(Vcb->ResidualOpenCount) ); } // // If this is a recursive close, just return here. // if ( RecursiveClose ) { try_return( Status = STATUS_SUCCESS ); } else { break; } case UserDirectoryOpen: case UserFileOpen: DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0); // // Uninitialize the cache map if we no longer need to use it // if ((NodeType(Fcb) == FAT_NTC_DCB) && IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) && (Fcb->OpenCount == 1) && (Fcb->Specific.Dcb.DirectoryFile != NULL)) { PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile; DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0); CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); // // Dereference the directory file. This may cause a close // Irp to be processed, so we need to do this before we destory // the Fcb. // Fcb->Specific.Dcb.DirectoryFile = NULL; ObDereferenceObject( DirectoryFileObject ); } #if __NDAS_FAT__ if (TypeOfOpen == UserFileOpen) { ExAcquireFastMutex( &Fcb->NonPaged->CcbQMutex ); Ccb->FileObject = NULL; Ccb->Fcb = NULL; RemoveEntryList( &Ccb->FcbListEntry ); InitializeListHead( &Ccb->FcbListEntry ); ExReleaseFastMutex( &Fcb->NonPaged->CcbQMutex ); } #endif Fcb->OpenCount -= 1; #if __NDAS_FAT_SECONDARY__ if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { volDoCcb = TRUE; if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) { if ( FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_CORRUPTED) ) Fcb->CorruptedCcbCloseCount --; send2Primary = FALSE; } else send2Primary = TRUE; primaryFileHandle = Ccb->PrimaryFileHandle; ExAcquireFastMutex( &volDo->Secondary->RecoveryCcbQMutex ); RemoveEntryList( &Ccb->ListEntry ); ExReleaseFastMutex( &volDo->Secondary->RecoveryCcbQMutex ); InitializeListHead( &Ccb->ListEntry ); Ccb->FileObject = NULL; if (Ccb->Buffer) ExFreePool( Ccb->Buffer ); InterlockedDecrement( &Vcb->SecondaryOpenFileCount ); Ccb->FileObject = NULL; } else { Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } } if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_OPEN_BY_PRIMARY_SESSION)) { InterlockedDecrement( &Vcb->PrimaryOpenFileCount ); } FatDeleteCcb( &IrpContext, &Ccb ); if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { if (Fcb->OpenCount == 0) { ExAcquireFastMutex( &volDo->Secondary->FcbQMutex ); RemoveEntryList( &Fcb->ListEntry ); InitializeListHead( &Fcb->ListEntry ); ExReleaseFastMutex( &volDo->Secondary->FcbQMutex ); Fcb->Header.NodeTypeCode = FAT_NTC_FCB; FatDeleteFcb( &IrpContext, &Fcb ); Secondary_Dereference( volDo->Secondary ); } } #else Fcb->OpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } FatDeleteCcb( &IrpContext, &Ccb ); #endif break; default: FatBugCheck( TypeOfOpen, 0, 0 ); } #if __NDAS_FAT_SECONDARY__ if (send2Primary) { Status = STATUS_SUCCESS; volDoSessionResourceAcquired = SecondaryAcquireResourceExclusiveLite( &IrpContext, &volDo->SessionResource, BooleanFlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT) ); if (FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) || FlagOn(volDo->Secondary->Flags, SECONDARY_FLAG_RECONNECTING)) { Status = STATUS_SUCCESS; leave; } secondaryRequest = AllocateWinxpSecondaryRequest( volDo->Secondary, IRP_MJ_CLOSE, 0 ); if (secondaryRequest == NULL) { ASSERT( FALSE ); leave; } ndfsRequestHeader = &secondaryRequest->NdfsRequestHeader; INITIALIZE_NDFS_REQUEST_HEADER( ndfsRequestHeader, NDFS_COMMAND_EXECUTE, volDo->Secondary, IRP_MJ_CLOSE, 0 ); ndfsWinxpRequestHeader = (PNDFS_WINXP_REQUEST_HEADER)(ndfsRequestHeader+1); ASSERT( ndfsWinxpRequestHeader == (PNDFS_WINXP_REQUEST_HEADER)secondaryRequest->NdfsRequestData ); //ndfsWinxpRequestHeader->IrpTag = (_U32)Fcb; ndfsWinxpRequestHeader->IrpMajorFunction = IRP_MJ_CLOSE; ndfsWinxpRequestHeader->IrpMinorFunction = 0; ndfsWinxpRequestHeader->FileHandle = primaryFileHandle; ndfsWinxpRequestHeader->IrpFlags = 0; ndfsWinxpRequestHeader->IrpSpFlags = 0; secondaryRequest->RequestType = SECONDARY_REQ_SEND_MESSAGE; QueueingSecondaryRequest( volDo->Secondary, secondaryRequest ); timeOut.QuadPart = -NDASFAT_TIME_OUT; Status = KeWaitForSingleObject( &secondaryRequest->CompleteEvent, Executive, KernelMode, FALSE, &timeOut ); KeClearEvent( &secondaryRequest->CompleteEvent ); if (Status != STATUS_SUCCESS) { ASSERT( NDASFAT_BUG ); } if (secondaryRequest->ExecuteStatus == STATUS_SUCCESS) { ndfsWinxpReplytHeader = (PNDFS_WINXP_REPLY_HEADER)secondaryRequest->NdfsReplyData; ASSERT( ndfsWinxpReplytHeader->Status == STATUS_SUCCESS ); leave; } ASSERT( secondaryRequest->ExecuteStatus != STATUS_SUCCESS ); } if (volDoCcb == TRUE) { Status = STATUS_SUCCESS; leave; } #endif // // At this point we've cleaned up any on-disk structure that needs // to be done, and we can now update the in-memory structures. // Now if this is an unreferenced FCB or if it is // an unreferenced DCB (not the root) then we can remove // the fcb and set our ParentDcb to non null. // if (((NodeType(Fcb) == FAT_NTC_FCB) && (Fcb->OpenCount == 0)) || ((NodeType(Fcb) == FAT_NTC_DCB) && (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) && (Fcb->OpenCount == 0) && (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) { ParentDcb = Fcb->ParentDcb; SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); FatDeleteFcb( &IrpContext, &Fcb ); // // Uninitialize our parent's cache map if we no longer need // to use it. // while ((NodeType(ParentDcb) == FAT_NTC_DCB) && IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) && (ParentDcb->OpenCount == 0) && (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) { PFILE_OBJECT DirectoryFileObject; DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile; DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); ParentDcb->Specific.Dcb.DirectoryFile = NULL; ObDereferenceObject( DirectoryFileObject ); // // Now, if the ObDereferenceObject() caused the final close // to come in, then blow away the Fcb and continue up, // otherwise wait for Mm to to dereference its file objects // and stop here.. // if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) { PDCB CurrentDcb; CurrentDcb = ParentDcb; ParentDcb = CurrentDcb->ParentDcb; SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); FatDeleteFcb( &IrpContext, &CurrentDcb ); } else { break; } } } Status = STATUS_SUCCESS; try_exit: NOTHING; } finally { DebugUnwind( FatCommonClose ); #if __NDAS_FAT_SECONDARY__ if (secondaryRequest) DereferenceSecondaryRequest( secondaryRequest ); if (volDoSessionResourceAcquired) { SecondaryReleaseResourceLite( &IrpContext, &volDo->SessionResource ); } if (volDoResourceAcquired) { ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) ); SecondaryReleaseResourceLite( NULL, &volDo->Resource ); } #endif // // We are done processing the close. If we are the top of the close // chain, see if the VCB can go away. We have biased the open count by // one, so we need to take that into account. // if (!RecursiveClose) { // // See if there is only one open left. If so, it is ours. We only want // to check for a dismount if a dismount is not already in progress. // We also only do this if the caller can handle the VCB going away. // This is determined by whether they passed in the VcbDeleted argument. // if (Vcb->OpenFileCount == 1 && !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) && ARGUMENT_PRESENT( VcbDeleted )) { // // We need the global lock, which must be acquired before the // VCB. Since we already have the VCB, we have to drop and // reaquire here. Note that we always want to wait from this // point on. Note that the VCB cannot go away, since we have // biased the open file count. // FatReleaseVcb( &IrpContext, Vcb ); SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); FatAcquireExclusiveGlobal( &IrpContext ); FatAcquireExclusiveVcb( &IrpContext, Vcb ); // // We have our locks in the correct order. Remove our // extra open and check for a dismount. Note that if // something changed while we dropped the lock, it will // not matter, since the dismount code does the correct // checks to make sure the volume can really go away. // Vcb->OpenFileCount -= 1; LocalVcbDeleted = FatCheckForDismount( &IrpContext, Vcb, FALSE ); FatReleaseGlobal( &IrpContext ); // // Let the caller know what happened, if they want this information. // if (ARGUMENT_PRESENT( VcbDeleted )) { *VcbDeleted = LocalVcbDeleted; } } else { // // The volume cannot go away now. Just remove our extra reference. // Vcb->OpenFileCount -= 1; } // // If the VCB is still around, clear our recursion flag. // if (!LocalVcbDeleted) { ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS ); } } // // Only release the VCB if it did not go away. // if (!LocalVcbDeleted) { FatReleaseVcb( &IrpContext, Vcb ); } DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); } return Status; }
NTSTATUS NtfsCommonLockControl ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for Lock Control 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; BOOLEAN FcbAcquired = FALSE; BOOLEAN OplockPostIrp; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); PAGED_CODE(); // // Get a pointer to the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonLockControl\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("MinorFunction = %08lx\n", IrpSp->MinorFunction) ); // // Extract and decode the type of file object we're being asked to process // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); // // If the file is not a user file open then we reject the request // as an invalid parameter // if (TypeOfOpen != UserFileOpen) { NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_PARAMETER ); DebugTrace( -1, Dbg, ("NtfsCommonLockControl -> STATUS_INVALID_PARAMETER\n") ); return STATUS_INVALID_PARAMETER; } // // Acquire exclusive access to the Fcb // if (Scb->ScbType.Data.FileLock == NULL) { NtfsAcquireExclusiveFcb( IrpContext, Fcb, Scb, FALSE, FALSE ); FcbAcquired = TRUE; } else { //NtfsAcquireSharedFcb( IrpContext, Fcb, Scb ); } OplockPostIrp = FALSE; try { // // We check whether we can proceed based on the state of the file oplocks. // This call might post the irp for us. // Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock, Irp, IrpContext, NtfsOplockComplete, NULL ); if (Status != STATUS_SUCCESS) { OplockPostIrp = TRUE; try_return( NOTHING ); } // // If we don't have a file lock, then get one now. // if (Scb->ScbType.Data.FileLock == NULL) { NtfsCreateFileLock( Scb, TRUE ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request // Status = FsRtlProcessFileLock( Scb->ScbType.Data.FileLock, Irp, NULL ); // // Set the flag indicating if Fast I/O is possible // NtfsAcquireFsrtlHeader( Scb ); Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb ); NtfsReleaseFsrtlHeader( Scb ); try_exit: NOTHING; } finally { DebugUnwind( NtfsCommonLockControl ); // // Release the Fcb, and return to our caller // if (FcbAcquired) { NtfsReleaseFcb( IrpContext, Fcb ); } // // Only if this is not an abnormal termination and we did not post the irp // do we delete the irp context // if (!AbnormalTermination() && !OplockPostIrp) { NtfsCompleteRequest( &IrpContext, NULL, 0 ); } DebugTrace( -1, Dbg, ("NtfsCommonLockControl -> %08lx\n", Status) ); } return Status; }
NTSTATUS NdFatCommonFlushBuffers ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for flushing a buffer. 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; PCCB Ccb; BOOLEAN VcbAcquired = FALSE; BOOLEAN FcbAcquired = FALSE; PDIRENT Dirent; PBCB DirentBcb = NULL; PVOLUME_DEVICE_OBJECT volDo = CONTAINING_RECORD( IrpContext->Vcb, VOLUME_DEVICE_OBJECT, Vcb ); BOOLEAN secondarySessionResourceAcquired = FALSE; PSECONDARY_REQUEST secondaryRequest = NULL; PNDFS_REQUEST_HEADER ndfsRequestHeader; PNDFS_WINXP_REQUEST_HEADER ndfsWinxpRequestHeader; PNDFS_WINXP_REPLY_HEADER ndfsWinxpReplytHeader; LARGE_INTEGER timeOut; PAGED_CODE(); IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject); // // Extract and decode the file object // FileObject = IrpSp->FileObject; TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); // // CcFlushCache is always synchronous, so if we can't wait enqueue // the irp to the Fsp. // if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { Status = FatFsdPostRequest( IrpContext, Irp ); DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status ); return Status; } Status = STATUS_SUCCESS; try { if (!FlagOn(Ccb->NdFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) { do { secondarySessionResourceAcquired = SecondaryAcquireResourceExclusiveLite( IrpContext, &volDo->Secondary->SessionResource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); if (FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ) { PrintIrp( Dbg2, "SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED", NULL, IrpContext->OriginatingIrp ); FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } secondaryRequest = ALLOC_WINXP_SECONDARY_REQUEST( volDo->Secondary, IRP_MJ_FLUSH_BUFFERS, 0 ); if (secondaryRequest == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } ndfsRequestHeader = &secondaryRequest->NdfsRequestHeader; INITIALIZE_NDFS_REQUEST_HEADER( ndfsRequestHeader, NDFS_COMMAND_EXECUTE, volDo->Secondary, IRP_MJ_FLUSH_BUFFERS, 0 ); ndfsWinxpRequestHeader = (PNDFS_WINXP_REQUEST_HEADER)(ndfsRequestHeader+1); ASSERT( ndfsWinxpRequestHeader == (PNDFS_WINXP_REQUEST_HEADER)secondaryRequest->NdfsRequestData ); INITIALIZE_NDFS_WINXP_REQUEST_HEADER( ndfsWinxpRequestHeader, IrpContext->OriginatingIrp, IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp), Ccb->PrimaryFileHandle ); ASSERT( !ExIsResourceAcquiredSharedLite(&IrpContext->Vcb->Resource) ); secondaryRequest->RequestType = SECONDARY_REQ_SEND_MESSAGE; QueueingSecondaryRequest( volDo->Secondary, secondaryRequest ); timeOut.QuadPart = -NDFAT_TIME_OUT; Status = KeWaitForSingleObject( &secondaryRequest->CompleteEvent, Executive, KernelMode, FALSE, &timeOut ); if (Status != STATUS_SUCCESS) { ASSERT( NDFAT_BUG ); break; } KeClearEvent (&secondaryRequest->CompleteEvent); if (BooleanFlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) { FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } if (secondaryRequest->ExecuteStatus == STATUS_SUCCESS) { ndfsWinxpReplytHeader = (PNDFS_WINXP_REPLY_HEADER)secondaryRequest->NdfsReplyData; ASSERT(ndfsWinxpReplytHeader->Status == STATUS_SUCCESS); } if (secondaryRequest) { DereferenceSecondaryRequest( secondaryRequest ); secondaryRequest = NULL; } if ( secondarySessionResourceAcquired == TRUE ) { SecondaryReleaseResourceLite( IrpContext, &volDo->Secondary->SessionResource ); secondarySessionResourceAcquired = FALSE; } break; } while(0); } Status = STATUS_SUCCESS; // // Case on the type of open that we are trying to flush // switch (TypeOfOpen) { case VirtualVolumeFile: case EaFile: case DirectoryFile: DebugTrace(0, Dbg, "Flush that does nothing\n", 0); break; case UserFileOpen: DebugTrace(0, Dbg, "Flush User File Open\n", 0); (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); FcbAcquired = TRUE; FatVerifyFcb( IrpContext, Fcb ); // // If the file is cached then flush its cache // Status = FatFlushFile( IrpContext, Fcb, Flush ); // // Also update and flush the file's dirent in the parent directory if the // file flush worked. // if (NT_SUCCESS( Status )) { // // Insure that we get the filesize to disk correctly. This is // benign if it was already good. // // (why do we need to do this?) // SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); #if 0 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); #endif // // Flush the volume file to get any allocation information // updates to disk. // if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { Status = FatFlushFat( IrpContext, Vcb ); ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT); } // // Set the write through bit so that these modifications // will be completed with the request. // SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); } break; case UserDirectoryOpen: // // If the user had opened the root directory then we'll // oblige by flushing the volume. // if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0); break; } case UserVolumeOpen: DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0); // // Acquire exclusive access to the Vcb. // { BOOLEAN Finished; Finished = FatAcquireExclusiveSecondaryVcb( IrpContext, Vcb ); ASSERT( Finished ); } VcbAcquired = TRUE; // // Mark the volume clean and then flush the volume file, // and then all directories // Status = FatFlushVolume( IrpContext, Vcb, Flush ); // // If the volume was dirty, do the processing that the delayed // callback would have done. // if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { // // Cancel any pending clean volumes. // (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); // // The volume is now clean, note it. // if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { FatMarkVolume( IrpContext, Vcb, VolumeClean ); ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); } // // Unlock the volume if it is removable. // if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); } } break; default: FatBugCheck( TypeOfOpen, 0, 0 ); } FatUnpinBcb( IrpContext, DirentBcb ); FatUnpinRepinnedBcbs( IrpContext ); } finally { DebugUnwind( FatCommonFlushBuffers ); if (secondaryRequest) DereferenceSecondaryRequest( secondaryRequest ); if (secondarySessionResourceAcquired) { SecondaryReleaseResourceLite( IrpContext, &volDo->Secondary->SessionResource ); } FatUnpinBcb( IrpContext, DirentBcb ); if (VcbAcquired) { FatReleaseSecondaryVcb( IrpContext, Vcb ); } if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } // // If this is a normal termination then pass the request on // to the target device object. // if (!AbnormalTermination()) { NTSTATUS DriverStatus; PIO_STACK_LOCATION NextIrpSp; // // Get the next stack location, and copy over the stack location // NextIrpSp = IoGetNextIrpStackLocation( Irp ); *NextIrpSp = *IrpSp; // // Set up the completion routine // IoSetCompletionRoutine( Irp, FatFlushCompletionRoutine, ULongToPtr( Status ), TRUE, TRUE, TRUE ); // // Send the request. // DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp); Status = (DriverStatus == STATUS_INVALID_DEVICE_REQUEST) ? Status : DriverStatus; // // Free the IrpContext and return to the caller. // FatCompleteRequest( IrpContext, FatNull, STATUS_SUCCESS ); } DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status); } return Status; }
NTSTATUS NtfsCommonQueryVolumeInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for query 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; BOOLEAN AcquiredVcb = FALSE; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )); PAGED_CODE(); // // Get the current stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonQueryVolumeInfo...\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length) ); DebugTrace( 0, Dbg, ("FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass) ); DebugTrace( 0, Dbg, ("Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer) ); // // Reference our input parameters to make things easier // Length = IrpSp->Parameters.QueryVolume.Length; FsInformationClass = IrpSp->Parameters.QueryVolume.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 ); // // Let's kill invalid vol. query requests. // if (UnopenedFileObject == TypeOfOpen) { DebugTrace( 0, Dbg2, ("Invalid file object for write\n") ); DebugTrace( -1, Dbg2, ("NtfsCommonQueryVolume: Exit -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST) ); NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); return STATUS_INVALID_DEVICE_REQUEST; } #ifdef __ND_NTFS_SECONDARY__ if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) { return NtfsPostRequest( IrpContext, Irp ); } #endif // // Get the Vcb shared and raise if we can't wait for the resource. // We're only using $Volume Scb for the query size calls because the info // it gets is static and we only need to protect against dismount // Doing this prevents a deadlock with commit extensions from mm which use // this call. However for system files like the mft we always need the vcb to avoid deadlock // if ((FsInformationClass != FileFsSizeInformation) || (FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ))) { NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE ); AcquiredVcb = TRUE; } else { NtfsAcquireSharedScb( IrpContext, Scb ); } try { // // Make sure the volume is mounted. // if ((AcquiredVcb && !FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) || (!AcquiredVcb && FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED))) { Irp->IoStatus.Information = 0; Status = STATUS_VOLUME_DISMOUNTED; leave; } #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 = NdNtfsSecondaryCommonQueryVolumeInfo( IrpContext, Irp ); leave; } #endif // // Based on the information class we'll do different actions. Each // of the procedures that we're calling fills up the output buffer // if possible and returns true if it successfully filled the buffer // and false if it couldn't wait for any I/O to complete. // switch (FsInformationClass) { case FileFsVolumeInformation: Status = NtfsQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsSizeInformation: Status = NtfsQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsDeviceInformation: Status = NtfsQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsAttributeInformation: Status = NtfsQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsControlInformation: Status = NtfsQueryFsControlInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsFullSizeInformation: Status = NtfsQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length ); break; case FileFsObjectIdInformation: Status = NtfsQueryFsVolumeObjectIdInfo( IrpContext, Vcb, Buffer, &Length ); break; default: Status = STATUS_INVALID_PARAMETER; break; } // // Set the information field to the number of bytes actually filled in // Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; // // Abort transaction on error by raising. // NtfsCleanupTransaction( IrpContext, Status, FALSE ); } finally { DebugUnwind( NtfsCommonQueryVolumeInfo ); if (AcquiredVcb) { NtfsReleaseVcb( IrpContext, Vcb ); } else { NtfsReleaseScb( IrpContext, Scb ); } DebugTrace( -1, Dbg, ("NtfsCommonQueryVolumeInfo -> %08lx\n", Status) ); } if (Status != STATUS_SUCCESS && Status != STATUS_BUFFER_OVERFLOW) DebugTrace( 0, Dbg2, ("NtfsCommonQueryVolumeInfo %x, FsInformationClass = %d Vcb = %p\n", Status, FsInformationClass, IrpContext->Vcb) ); NtfsCompleteRequest( IrpContext, Irp, Status ); return Status; }
BOOLEAN FatFastUnlockAllByKey ( IN PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This is a call back routine for doing the fast unlock all by key call. Arguments: FileObject - Supplies the file object used in this operation ProcessId - Supplies the process ID used in this operation Key - Supplies the key used in this operation Status - Receives the Status if this operation is successful Return Value: BOOLEAN - TRUE if this operation completed and FALSE if caller needs to take the long route. --*/ { BOOLEAN Results; PVCB Vcb; PFCB Fcb; PCCB Ccb; DebugTrace(+1, Dbg, "FatFastUnlockAllByKey\n", 0); IoStatus->Information = 0; // // Decode the type of file object we're being asked to process and make sure // it is only a user file open. // if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { IoStatus->Status = STATUS_INVALID_PARAMETER; DebugTrace(-1, Dbg, "FatFastUnlockAll -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); return TRUE; } // // Acquire exclusive access to the Fcb this operation can always wait // FsRtlEnterFileSystem(); (VOID) ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); try { // // We check whether we can proceed based on the state of the file oplocks. // if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { try_return( Results = FALSE ); } // // Now call the FsRtl routine to do the actual processing of the // Lock request. The call will always succeed. // Results = TRUE; IoStatus->Status = FsRtlFastUnlockAllByKey( &Fcb->Specific.Fcb.FileLock, FileObject, ProcessId, Key, NULL ); // // Set the flag indicating if Fast I/O is possible // Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); try_exit: NOTHING; } finally { DebugUnwind( FatFastUnlockAllByKey ); // // Release the Fcb, and return to our caller // ExReleaseResourceLite( (Fcb)->Header.Resource ); FsRtlExitFileSystem(); DebugTrace(-1, Dbg, "FatFastUnlockAllByKey -> %08lx\n", Results); } return Results; }
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; }
BOOLEAN LfsSearchForwardByClient ( IN PLFCB Lfcb, IN OUT PLfsLCB Lcb, OUT PLSN Lsn ) /*++ Routine Description: This routine will attempt to find the next Lsn for this client by searching forward in the file, looking for a match. Arguments: Lfcb - Pointer to the file control block for this log file. Lcb - Pointer to the context block for this query operation. Lsn - Points to the location to store the next Lsn if found. Return Value: BOOLEAN - TRUE if another Lsn for this client is found. FALSE otherwise. --*/ { PLFS_RECORD_HEADER CurrentRecordHeader; PBCB CurrentBcb; BOOLEAN FoundNextLsn; LSN CurrentLsn; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsSearchForwardByClient: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Lcb -> %08lx\n", Lcb ); // // The log record header is in the log context // block. We set the current Bcb to NULL so that we don't // unpin the log record in the context block until we're sure // of success. // CurrentRecordHeader = Lcb->RecordHeader; CurrentBcb = NULL; // // We use a try-finally to facilitate cleanup. // try { // // We assume we won't find another Lsn. // FoundNextLsn = FALSE; // // Loop as long as another Lsn can be found. // while (LfsFindNextLsn( Lfcb, CurrentRecordHeader, &CurrentLsn )) { BOOLEAN UsaError; // // Unpin the previous log record header. // if (CurrentBcb != NULL) { CcUnpinData( CurrentBcb ); CurrentBcb = NULL; } // // Pin the log record header for this Lsn. // LfsPinOrMapLogRecordHeader( Lfcb, CurrentLsn, FALSE, FALSE, &UsaError, &CurrentRecordHeader, &CurrentBcb ); // // If the client values match, then we update the // context block and exit. // if (LfsClientIdMatch( &CurrentRecordHeader->ClientId, &Lcb->ClientId ) && CurrentRecordHeader->RecordType == LfsClientRecord) { // // We remember this one. // Lcb->RecordHeader = CurrentRecordHeader; Lcb->RecordHeaderBcb = CurrentBcb; CurrentBcb = NULL; FoundNextLsn = TRUE; *Lsn = CurrentLsn; break; } } } finally { DebugUnwind( LfsSearchForwardByClient ); // // Unpin any log record headers still pinned for no reason. // if (CurrentBcb != NULL) { CcUnpinData( CurrentBcb ); } LfsDebugTrace( 0, Dbg, "NextLsn (Low) -> %08lx\n", Lsn->LowPart ); LfsDebugTrace( 0, Dbg, "NextLsn (High) -> %08lx\n", Lsn->HighPart ); LfsDebugTrace( -1, Dbg, "LfsSearchForwardByClient: Exit -> %08x\n", FoundNextLsn ); } return FoundNextLsn; }
BOOLEAN FatCheckFileAccess ( PIRP_CONTEXT IrpContext, IN UCHAR DirentAttributes, IN ULONG DesiredAccess ) /*++ Routine Description: This routine checks if a desired access is allowed to a file represented by the specified DirentAttriubutes. Arguments: DirentAttributes - Supplies the Dirent attributes to check access for DesiredAccess - Supplies the desired access mask that we are checking for Return Value: BOOLEAN - TRUE if access is allowed and FALSE otherwise --*/ { BOOLEAN Result; DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0); DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes); DebugTrace( 0, Dbg, "DesiredAccess = %8lx\n", DesiredAccess); // // This procedures is programmed like a string of filters each // filter checks to see if some access is allowed, if it is not allowed // the filter return FALSE to the user without further checks otherwise // it moves on to the next filter. The filter check is to check for // desired access flags that are not allowed for a particular dirent // Result = TRUE; try { // // Check for Volume ID or Device Dirents, these are not allowed user // access at all // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) || FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) { DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0); try_return( Result = FALSE ); } // // Check for a directory Dirent or non directory dirent // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) { // // check the desired access for directory dirent // if (FlagOn(DesiredAccess, ~(DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_WRITE_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_APPEND_DATA))) { DebugTrace(0, Dbg, "Cannot open directory\n", 0); try_return( Result = FALSE ); } } else { // // check the desired access for a non-directory dirent, we // blackball // FILE_LIST_DIRECTORY, FILE_ADD_FILE, FILE_TRAVERSE, // FILE_ADD_SUBDIRECTORY, and FILE_DELETE_CHILD // if (FlagOn(DesiredAccess, ~(DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_APPEND_DATA))) { DebugTrace(0, Dbg, "Cannot open file\n", 0); try_return( Result = FALSE ); } } // // Check for a read-only Dirent // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { // // Check the desired access for a read-only dirent, we blackball // WRITE, FILE_APPEND_DATA, FILE_ADD_FILE, // FILE_ADD_SUBDIRECTORY, and FILE_DELETE_CHILD // if (FlagOn(DesiredAccess, ~(DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY | FILE_TRAVERSE))) { DebugTrace(0, Dbg, "Cannot open readonly\n", 0); try_return( Result = FALSE ); } } try_exit: NOTHING; } finally { DebugUnwind( FatCheckFileAccess ); DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result); } UNREFERENCED_PARAMETER( IrpContext ); return Result; }
VOID LfsTerminateLogQuery ( IN LFS_LOG_HANDLE LogHandle, IN LFS_LOG_CONTEXT Context ) /*++ Routine Description: This routine is called when a client has completed his query operation and wishes to deallocate any resources acquired by the Lfs to perform the log file query. Arguments: LogHandle - Pointer to private Lfs structure used to identify this client. Context - Supplies the address to store a pointer to the Lfs created context structure. Return Value: None --*/ { PLCH Lch; PLfsLCB Lcb; PLFCB Lfcb; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsTerminateLogQuery: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle ); LfsDebugTrace( 0, Dbg, "Context -> %08lx\n", Context ); Lch = (PLCH) LogHandle; Lcb = (PLfsLCB) Context; // // Check that the structure is a valid log handle structure. // LfsValidateLch( Lch ); // // Use a try-finally to facilitate cleanup. // try { // // Acquire the log file control block for this log file. // LfsAcquireLch( Lch ); Lfcb = Lch->Lfcb; // // If the Log file has been closed then refuse access. // if (Lfcb == NULL) { try_return( NOTHING ); } // // Check that the client Id is valid. // LfsValidateClientId( Lfcb, Lch ); // // Check that the context structure is valid. // LfsValidateLcb( Lcb, Lch ); // // Deallocate the context block. // LfsDeallocateLcb( Lcb ); try_exit: NOTHING; } finally { DebugUnwind( LfsTerminateLogQuery ); // // Release the Lfcb if acquired. // LfsReleaseLch( Lch ); LfsDebugTrace( -1, Dbg, "LfsTerminateLogQuery: Exit\n", 0 ); } return; }
BOOLEAN FatCheckFileAccess ( PIRP_CONTEXT IrpContext, IN UCHAR DirentAttributes, IN PACCESS_MASK DesiredAccess ) /*++ Routine Description: This routine checks if a desired access is allowed to a file represented by the specified DirentAttriubutes. Arguments: DirentAttributes - Supplies the Dirent attributes to check access for DesiredAccess - Supplies the desired access mask that we are checking for Return Value: BOOLEAN - TRUE if access is allowed and FALSE otherwise --*/ { BOOLEAN Result; DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0); DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes); DebugTrace( 0, Dbg, "DesiredAccess = %8lx\n", *DesiredAccess); PAGED_CODE(); // // This procedures is programmed like a string of filters each // filter checks to see if some access is allowed, if it is not allowed // the filter return FALSE to the user without further checks otherwise // it moves on to the next filter. The filter check is to check for // desired access flags that are not allowed for a particular dirent // Result = TRUE; _SEH2_TRY { // // Check for Volume ID or Device Dirents, these are not allowed user // access at all // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) || FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) { DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0); try_return( Result = FALSE ); } // // Check the desired access for the object - we only blackball that // we do not understand. The model of filesystems using ACLs is that // they do not type the ACL to the object the ACL is on. Permissions // are not checked for consistency vs. the object type - dir/file. // if (FlagOn(*DesiredAccess, ~(DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_WRITE_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_APPEND_DATA | MAXIMUM_ALLOWED))) { DebugTrace(0, Dbg, "Cannot open object\n", 0); try_return( Result = FALSE ); } // // Check for a read-only Dirent // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { // // Check the desired access for a read-only dirent. AccessMask will contain // the flags we're going to allow. // ACCESS_MASK AccessMask = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY | FILE_TRAVERSE; // // If this is a subdirectory also allow add file/directory and delete. // if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) { AccessMask |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD; } if (FlagOn(*DesiredAccess, ~AccessMask)) { DebugTrace(0, Dbg, "Cannot open readonly\n", 0); try_return( Result = FALSE ); } } try_exit: NOTHING; } _SEH2_FINALLY { DebugUnwind( FatCheckFileAccess ); DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result); } _SEH2_END; UNREFERENCED_PARAMETER( IrpContext ); return Result; }
LSN LfsQueryLastLsn ( IN LFS_LOG_HANDLE LogHandle ) /*++ Routine Description: This routine will return the most recent Lsn for this log record. Arguments: LogHandle - Pointer to private Lfs structure used to identify this client. Return Value: LSN - This is the last Lsn assigned in this log file. --*/ { PLCH Lch; PLFCB Lfcb; LSN LastLsn; PAGED_CODE(); LfsDebugTrace( +1, Dbg, "LfsQueryLastLsn: Entered\n", 0 ); LfsDebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle ); Lch = (PLCH) LogHandle; // // Check that the structure is a valid log handle structure. // LfsValidateLch( Lch ); // // Use a try-finally to facilitate cleanup. // try { // // Acquire the log file control block for this log file. // LfsAcquireLch( Lch ); Lfcb = Lch->Lfcb; // // If the Log file has been closed then refuse access. // if (Lfcb == NULL) { ExRaiseStatus( STATUS_ACCESS_DENIED ); } // // Check that the client Id is valid. // LfsValidateClientId( Lfcb, Lch ); // // Copy the last Lsn out of the Lfcb. If the last Lsn is // does not correspond to a log record, we will return the // zero Lsn. // if (FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN )) { LastLsn = LfsZeroLsn; } else { LastLsn = Lfcb->RestartArea->CurrentLsn; } } finally { DebugUnwind( LfsQueryLastLsn ); // // Release the Lfcb if acquired. // LfsReleaseLch( Lch ); LfsDebugTrace( 0, Dbg, "Last Lsn (Low) -> %08lx\n", LastLsn.LowPart ); LfsDebugTrace( 0, Dbg, "Last Lsn (High) -> %08lx\n", LastLsn.HighPart ); LfsDebugTrace( -1, Dbg, "LfsQueryLastLsn: Exit\n", 0 ); } return LastLsn; }
ULONG UdfLookupPsnOfExtent ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN USHORT Reference, IN ULONG Lbn, IN ULONG Len ) /*++ Routine Description: This routine maps the input logical block extent on a given partition to a starting physical sector. It doubles as a bounds checker - if the routine does not raise, the caller is guaranteed that the extent lies within the partition. Arguments: Vcb - Vcb of logical volume Reference - Partition reference to use in the mapping Lbn - Logical block number Len - Length of extent in bytes Return Value: ULONG physical sector number --*/ { PPCB Pcb = Vcb->Pcb; ULONG Psn; PBCB Bcb; LARGE_INTEGER Offset; PULONG MappedLbn; PAGED_CODE(); // // Check inputs // ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_VCB( Vcb ); ASSERT_PCB( Pcb ); DebugTrace(( +1, Dbg, "UdfLookupPsnOfExtent, [%04x/%08x, +%08x)\n", Reference, Lbn, Len )); if (Reference < Pcb->Partitions) { while (TRUE) { switch (Pcb->Partition[Reference].Type) { case Physical: // // Check that the input extent lies inside the partition. Calculate the // Lbn of the last block and see that it is interior. // if (SectorsFromBlocks( Vcb, Lbn ) + SectorsFromBytes( Vcb, Len ) > Pcb->Partition[Reference].Physical.Length) { goto NoGood; } Psn = Pcb->Partition[Reference].Physical.Start + SectorsFromBlocks( Vcb, Lbn ); DebugTrace(( -1, Dbg, "UdfLookupPsnOfExtent -> %08x\n", Psn )); return Psn; case Virtual: // // Bounds check. Per UDF 2.00 2.3.10 and implied in UDF 1.50, virtual // extent lengths cannot be greater than one block in size. // if (Lbn + BlocksFromBytes( Vcb, Len ) > Pcb->Partition[Reference].Virtual.Length || Len > BlockSize( Vcb )) { goto NoGood; } try { // // Calculate the location of the mapping element in the VAT // and retrieve. // Offset.QuadPart = Lbn * sizeof(ULONG); CcMapData( Vcb->VatFcb->FileObject, &Offset, sizeof(ULONG), TRUE, &Bcb, &MappedLbn ); // // Now rewrite the inputs in terms of the virtual mapping. We // will reloop to perform the logical -> physical mapping. // DebugTrace(( 0, Dbg, "UdfLookupPsnOfExtent, Mapping V %04x/%08x -> L %04x/%08x\n", Reference, Lbn, Pcb->Partition[Reference].Virtual.RelatedReference, *MappedLbn )); Lbn = *MappedLbn; Reference = Pcb->Partition[Reference].Virtual.RelatedReference; } finally { DebugUnwind( UdfLookupPsnOfExtent ); UdfUnpinData( IrpContext, &Bcb ); } // // An Lbn of ~0 in the VAT is defined to indicate that the sector is unused, // so we should never see such a thing. // if (Lbn == ~0) { goto NoGood; } break; default: ASSERT(FALSE); break; } } } NoGood: // // Some people have misinterpreted a partition number to equal a // partition reference, or perhaps this is just corrupt media. // UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); }
NTSTATUS FatCommonClose ( IN PVCB Vcb, IN PFCB Fcb, IN PCCB Ccb, IN TYPE_OF_OPEN TypeOfOpen, IN BOOLEAN Wait, IN BOOLEAN TopLevel, OUT PBOOLEAN VcbDeleted OPTIONAL ) /*++ Routine Description: This is the common routine for closing a file/directory called by both the fsd and fsp threads. Close is invoked whenever the last reference to a file object is deleted. Cleanup is invoked when the last handle to a file object is closed, and is called before close. The function of close is to completely tear down and remove the fcb/dcb/ccb structures associated with the file object. Arguments: Fcb - Supplies the file to process. Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE then we must try to acquire the Vcb anyway. TopLevel - If this is TRUE this is a top level request. VcbDeleted - Returns whether the VCB was deleted by this call. Return Value: NTSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; PDCB ParentDcb; BOOLEAN RecursiveClose; BOOLEAN LocalVcbDeleted; IRP_CONTEXT IrpContext; PAGED_CODE(); DebugTrace(+1, Dbg, "FatCommonClose...\n", 0); // // Initailize the callers variable, if needed. // LocalVcbDeleted = FALSE; if (ARGUMENT_PRESENT( VcbDeleted )) { *VcbDeleted = LocalVcbDeleted; } // // Special case the unopened file object // if (TypeOfOpen == UnopenedFileObject) { DebugTrace(0, Dbg, "Close unopened file object\n", 0); Status = STATUS_SUCCESS; DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); return Status; } // // Set up our stack IrpContext. // RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; IrpContext.NodeByteSize = sizeof( IrpContext ); IrpContext.MajorFunction = IRP_MJ_CLOSE; IrpContext.Vcb = Vcb; if (Wait) { SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); } // // Acquire exclusive access to the Vcb and enqueue the irp if we didn't // get access. // #pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" ) if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) { return STATUS_PENDING; } // // The following test makes sure that we don't blow away an Fcb if we // are trying to do a Supersede/Overwrite open above us. This test // does not apply for the EA file. // if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) && Vcb->EaFcb != Fcb) { ExReleaseResourceLite( &Vcb->Resource ); return STATUS_PENDING; } // // Setting the following flag prevents recursive closes of directory file // objects, which are handled in a special case loop. // if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) { RecursiveClose = TRUE; } else { SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); RecursiveClose = FALSE; // // Since we are at the top of the close chain, we need to add // a reference to the VCB. This will keep it from going away // on us until we are ready to check for a dismount below. // Vcb->OpenFileCount += 1; } try { // // Case on the type of open that we are trying to close. // switch (TypeOfOpen) { case VirtualVolumeFile: DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0); // // Remove this internal, residual open from the count. // InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); try_return( Status = STATUS_SUCCESS ); break; case UserVolumeOpen: DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0); Vcb->DirectAccessOpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } FatDeleteCcb( &IrpContext, &Ccb ); try_return( Status = STATUS_SUCCESS ); break; case EaFile: DebugTrace(0, Dbg, "Close EaFile\n", 0); // // Remove this internal, residual open from the count. // InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); try_return( Status = STATUS_SUCCESS ); break; case DirectoryFile: DebugTrace(0, Dbg, "Close DirectoryFile\n", 0); InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount ); // // Remove this internal open from the count. // InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); // // If this is the root directory, it is a residual open // as well. // if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) { InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); } // // If this is a recursive close, just return here. // if ( RecursiveClose ) { try_return( Status = STATUS_SUCCESS ); } else { break; } case UserDirectoryOpen: case UserFileOpen: DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0); // // Uninitialize the cache map if we no longer need to use it // if ((NodeType(Fcb) == FAT_NTC_DCB) && IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) && (Fcb->OpenCount == 1) && (Fcb->Specific.Dcb.DirectoryFile != NULL)) { PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile; DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0); CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); // // Dereference the directory file. This may cause a close // Irp to be processed, so we need to do this before we destory // the Fcb. // Fcb->Specific.Dcb.DirectoryFile = NULL; ObDereferenceObject( DirectoryFileObject ); } Fcb->OpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } FatDeleteCcb( &IrpContext, &Ccb ); break; default: #pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." ) FatBugCheck( TypeOfOpen, 0, 0 ); } // // At this point we've cleaned up any on-disk structure that needs // to be done, and we can now update the in-memory structures. // Now if this is an unreferenced FCB or if it is // an unreferenced DCB (not the root) then we can remove // the fcb and set our ParentDcb to non null. // if (((NodeType(Fcb) == FAT_NTC_FCB) && (Fcb->OpenCount == 0)) || ((NodeType(Fcb) == FAT_NTC_DCB) && (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) && (Fcb->OpenCount == 0) && (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) { ParentDcb = Fcb->ParentDcb; SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); FatDeleteFcb( &IrpContext, &Fcb ); // // Uninitialize our parent's cache map if we no longer need // to use it. // while ((NodeType(ParentDcb) == FAT_NTC_DCB) && IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) && (ParentDcb->OpenCount == 0) && (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) { PFILE_OBJECT DirectoryFileObject; DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile; DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); ParentDcb->Specific.Dcb.DirectoryFile = NULL; ObDereferenceObject( DirectoryFileObject ); // // Now, if the ObDereferenceObject() caused the final close // to come in, then blow away the Fcb and continue up, // otherwise wait for Mm to to dereference its file objects // and stop here.. // if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) { PDCB CurrentDcb; CurrentDcb = ParentDcb; ParentDcb = CurrentDcb->ParentDcb; SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); FatDeleteFcb( &IrpContext, &CurrentDcb ); } else { break; } } } Status = STATUS_SUCCESS; try_exit: NOTHING; } finally { DebugUnwind( FatCommonClose ); // // We are done processing the close. If we are the top of the close // chain, see if the VCB can go away. We have biased the open count by // one, so we need to take that into account. // if (!RecursiveClose) { // // See if there is only one open left. If so, it is ours. We only want // to check for a dismount if a dismount is not already in progress. // We also only do this if the Vcb condition is not VcbGood and the // caller can handle the VCB going away. This is determined by whether // they passed in the VcbDeleted argument. This request also needs // to be top level. // if (Vcb->OpenFileCount == 1 && Vcb->VcbCondition != VcbGood && !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) && ARGUMENT_PRESENT( VcbDeleted ) && TopLevel) { // // We need the global lock, which must be acquired before the // VCB. Since we already have the VCB, we have to drop and // reaquire here. Note that we always want to wait from this // point on. Note that the VCB cannot go away, since we have // biased the open file count. // FatReleaseVcb( &IrpContext, Vcb ); SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); #pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" ) FatAcquireExclusiveGlobal( &IrpContext ); FatAcquireExclusiveVcb( &IrpContext, Vcb ); // // We have our locks in the correct order. Remove our // extra open and check for a dismount. Note that if // something changed while we dropped the lock, it will // not matter, since the dismount code does the correct // checks to make sure the volume can really go away. // Vcb->OpenFileCount -= 1; LocalVcbDeleted = FatCheckForDismount( &IrpContext, Vcb, FALSE ); FatReleaseGlobal( &IrpContext ); // // Let the caller know what happened, if they want this information. // if (ARGUMENT_PRESENT( VcbDeleted )) { *VcbDeleted = LocalVcbDeleted; } } else { // // The volume cannot go away now. Just remove our extra reference. // Vcb->OpenFileCount -= 1; } // // If the VCB is still around, clear our recursion flag. // if (!LocalVcbDeleted) { ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS ); } } // // Only release the VCB if it did not go away. // if (!LocalVcbDeleted) { FatReleaseVcb( &IrpContext, Vcb ); } DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); } return Status; }
NTSTATUS NtfsCommonSetSecurityInfo ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for Setting security 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; #ifdef _CAIRO_ PQUOTA_CONTROL_BLOCK OldQuotaControl; ULONG OldOwnerId; ULONG LargeStdInfo; #endif // _CAIRO_ TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); PAGED_CODE(); // // Get the current Irp stack location // IrpSp = IoGetCurrentIrpStackLocation( Irp ); DebugTrace( +1, Dbg, ("NtfsCommonSetSecurityInfo") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) ); // // Extract and decode the file object // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE ); // // The only type of opens we accept are user file and directory opens // if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { Status = STATUS_INVALID_PARAMETER; // // If the this handle does not open the entire file then refuse access. // } else if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) { Status = STATUS_INVALID_PARAMETER; } else { // // Our operation is to acquire the fcb, do the operation and then // release the fcb // NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, FALSE, FALSE ); try { #ifdef _CAIRO_ // // Capture the current OwnerId, Qutoa Control Block and // size of standard information. // OldQuotaControl = Fcb->QuotaControl; OldOwnerId = Fcb->OwnerId; LargeStdInfo = Fcb->FcbState & FCB_STATE_LARGE_STD_INFO; #endif // _CAIRO_ Status = NtfsModifySecurity( IrpContext, Fcb, &IrpSp->Parameters.SetSecurity.SecurityInformation, IrpSp->Parameters.SetSecurity.SecurityDescriptor ); if (NT_SUCCESS( Status )) { #ifdef _CAIRO_ // // Make sure the new security descriptor Id is written out. // NtfsUpdateStandardInformation( IrpContext, Fcb ); #endif } // // Abort transaction on error by raising. // NtfsCleanupTransaction( IrpContext, Status, FALSE ); // // Set the flag in the Ccb to indicate this change occurred. // SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE ); } finally { DebugUnwind( NtfsCommonSetSecurityInfo ); #ifdef _CAIRO_ if (AbnormalTermination()) { // // The request failed. Restore the owner and // QuotaControl are restored. // if (Fcb->QuotaControl != OldQuotaControl && Fcb->QuotaControl != NULL) { // // A new quota control block was assigned. // Dereference it. // NtfsDereferenceQuotaControlBlock( Fcb->Vcb, &Fcb->QuotaControl ); } Fcb->QuotaControl = OldQuotaControl; Fcb->OwnerId = OldOwnerId; if (LargeStdInfo == 0) { // // The standard information has be returned to // its orginal size. // ClearFlag( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO ); } } else { // // The request succeed. If the quota control block was // changed then derefence the old block. // if (Fcb->QuotaControl != OldQuotaControl && OldQuotaControl != NULL) { NtfsDereferenceQuotaControlBlock( Fcb->Vcb, &OldQuotaControl); } } #endif // _CAIRO_ NtfsReleaseFcb( IrpContext, Fcb ); } } // // Now complete the request and return to our caller // NtfsCompleteRequest( &IrpContext, &Irp, Status ); DebugTrace( -1, Dbg, ("NtfsCommonSetSecurityInfo -> %08lx", Status) ); return Status; }