/* * @implemented */ NTSTATUS NTAPI IopMountVolume(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN AllowRawMount, IN BOOLEAN DeviceIsLocked, IN BOOLEAN Alertable, OUT PVPB *Vpb) { KEVENT Event; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PIRP Irp; PIO_STACK_LOCATION StackPtr; PLIST_ENTRY FsList, ListEntry; LIST_ENTRY LocalList; PDEVICE_OBJECT AttachedDeviceObject = DeviceObject; PDEVICE_OBJECT FileSystemDeviceObject, ParentFsDeviceObject; ULONG FsStackOverhead, RegistrationOps; PAGED_CODE(); /* Check if the device isn't already locked */ if (!DeviceIsLocked) { /* Lock it ourselves */ Status = KeWaitForSingleObject(&DeviceObject->DeviceLock, Executive, KeGetPreviousMode(), Alertable, NULL); if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC)) { /* Don't mount if we were interrupted */ return Status; } } /* Acquire the FS Lock*/ KeEnterCriticalRegion(); ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE); /* Make sure we weren't already mounted */ if (!(DeviceObject->Vpb->Flags & (VPB_MOUNTED | VPB_REMOVE_PENDING))) { /* Initialize the event to wait on */ KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Remove the verify flag and get the actual device to mount */ DeviceObject->Flags &= ~DO_VERIFY_VOLUME; while (AttachedDeviceObject->AttachedDevice) { /* Get the next one */ AttachedDeviceObject = AttachedDeviceObject->AttachedDevice; } /* Reference it */ ObReferenceObject(AttachedDeviceObject); /* For a mount operation, this can only be a Disk, CD-ROM or tape */ if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) || (DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK)) { /* Use the disk list */ FsList = &IopDiskFileSystemQueueHead; } else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) { /* Use the CD-ROM list */ FsList = &IopCdRomFileSystemQueueHead; } else { /* It's gotta be a tape... */ FsList = &IopTapeFileSystemQueueHead; } /* Now loop the fs list until one of the file systems accepts us */ Status = STATUS_UNSUCCESSFUL; ListEntry = FsList->Flink; while ((ListEntry != FsList) && !(NT_SUCCESS(Status))) { /* * If we're not allowed to mount this volume and this is our last * (but not only) chance to mount it... */ if (!(AllowRawMount) && (ListEntry->Flink == FsList) && (ListEntry != FsList->Flink)) { /* Then fail this mount request */ break; } /* * Also check if this is a raw mount and there are other file * systems on the list. */ if ((DeviceObject->Vpb->Flags & VPB_RAW_MOUNT) && (ListEntry->Flink != FsList)) { /* Then skip this entry */ continue; } /* Get the Device Object for this FS */ FileSystemDeviceObject = CONTAINING_RECORD(ListEntry, DEVICE_OBJECT, Queue.ListEntry); ParentFsDeviceObject = FileSystemDeviceObject; /* * If this file system device is attached to some other device, * then we must make sure to increase the stack size for the IRP. * The default is +1, for the FS device itself. */ FsStackOverhead = 1; while (FileSystemDeviceObject->AttachedDevice) { /* Get the next attached device and increase overhead */ FileSystemDeviceObject = FileSystemDeviceObject-> AttachedDevice; FsStackOverhead++; } /* Clear the event */ KeClearEvent(&Event); /* Allocate the IRP */ Irp = IoAllocateIrp(AttachedDeviceObject->StackSize + (UCHAR)FsStackOverhead, TRUE); if (!Irp) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Setup the IRP */ Irp->UserIosb = &IoStatusBlock; Irp->UserEvent = &Event; Irp->Tail.Overlay.Thread = PsGetCurrentThread(); Irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO; Irp->RequestorMode = KernelMode; /* Get the I/O Stack location and set it up */ StackPtr = IoGetNextIrpStackLocation(Irp); StackPtr->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; StackPtr->MinorFunction = IRP_MN_MOUNT_VOLUME; StackPtr->Flags = AllowRawMount; StackPtr->Parameters.MountVolume.Vpb = DeviceObject->Vpb; StackPtr->Parameters.MountVolume.DeviceObject = AttachedDeviceObject; /* Save registration operations */ RegistrationOps = IopFsRegistrationOps; /* Release locks */ IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount); ExReleaseResourceLite(&IopDatabaseResource); /* Call the driver */ Status = IoCallDriver(FileSystemDeviceObject, Irp); if (Status == STATUS_PENDING) { /* Wait on it */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE); IopInterlockedDecrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount); /* Check if mounting was successful */ if (NT_SUCCESS(Status)) { /* Mount the VPB */ *Vpb = IopMountInitializeVpb(DeviceObject, AttachedDeviceObject, (DeviceObject->Vpb->Flags & VPB_RAW_MOUNT)); } else { /* Check if we failed because of the user */ if ((IoIsErrorUserInduced(Status)) && (IoStatusBlock.Information == 1)) { /* Break out and fail */ break; } /* If there were registration operations in the meanwhile */ if (RegistrationOps != IopFsRegistrationOps) { /* We need to setup a local list to pickup where we left */ LocalList.Flink = FsList->Flink; ListEntry = &LocalList; Status = STATUS_UNRECOGNIZED_VOLUME; } /* Otherwise, check if we need to load the FS driver */ if (Status == STATUS_FS_DRIVER_REQUIRED) { /* We need to release the lock */ IopInterlockedIncrementUlong(LockQueueIoDatabaseLock, (PULONG)&DeviceObject->ReferenceCount); ExReleaseResourceLite(&IopDatabaseResource); /* Release the device lock if we're holding it */ if (!DeviceIsLocked) { KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE); } /* Leave critical section */ KeLeaveCriticalRegion(); /* Load the FS */ IopLoadFileSystemDriver(ParentFsDeviceObject); /* Check if the device isn't already locked */ if (!DeviceIsLocked) { /* Lock it ourselves */ Status = KeWaitForSingleObject(&DeviceObject-> DeviceLock, Executive, KeGetPreviousMode(), Alertable, NULL); if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC)) { /* Don't mount if we were interrupted */ ObDereferenceObject(AttachedDeviceObject); return Status; } } /* Reacquire the lock */ KeEnterCriticalRegion(); ExAcquireResourceSharedLite(&IopDatabaseResource, TRUE); /* When we released the lock, make sure nobody beat us */ if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { /* Someone did, break out */ Status = STATUS_SUCCESS; break; } /* Start over by setting a failure */ Status = STATUS_UNRECOGNIZED_VOLUME; /* We need to setup a local list to pickup where we left */ LocalList.Flink = FsList->Flink; ListEntry = &LocalList; } /* * Check if we failed with any other error then an unrecognized * volume, and if this request doesn't allow mounting the raw * file system. */ if (!(AllowRawMount) && (Status != STATUS_UNRECOGNIZED_VOLUME) && (FsRtlIsTotalDeviceFailure(Status))) { /* Break out and give up */ break; } } /* Go to the next FS entry */ ListEntry = ListEntry->Flink; } /* Dereference the device if we failed */ if (!NT_SUCCESS(Status)) ObDereferenceObject(AttachedDeviceObject); } else if (DeviceObject->Vpb->Flags & VPB_REMOVE_PENDING) { /* Someone wants to remove us */ Status = STATUS_DEVICE_DOES_NOT_EXIST; } else { /* Someone already mounted us */ Status = STATUS_SUCCESS; } /* Release the FS lock */ ExReleaseResourceLite(&IopDatabaseResource); KeLeaveCriticalRegion(); /* Release the device lock if we're holding it */ if (!DeviceIsLocked) KeSetEvent(&DeviceObject->DeviceLock, 0, FALSE); /* Check if we failed to mount the boot partition */ if ((!NT_SUCCESS(Status)) && (DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) && ExpInitializationPhase < 2) { /* Bugcheck the system */ KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE, (ULONG_PTR)DeviceObject, Status, 0, 0); } /* Return the mount status */ return Status; }
NTSTATUS FatProcessException ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN NTSTATUS ExceptionCode ) /*++ Routine Description: This routine process an exception. It either completes the request with the saved exception status or it sends it off to IoRaiseHardError() Arguments: Irp - Supplies the Irp being processed ExceptionCode - Supplies the normalized exception status being handled Return Value: NTSTATUS - Returns the results of either posting the Irp or the saved completion status. --*/ { PVCB Vcb; PIO_STACK_LOCATION IrpSp; FAT_VOLUME_STATE TransitionState = VolumeDirty; ULONG SavedFlags; DebugTrace(0, Dbg, "FatProcessException\n", 0); // // If there is not an irp context, we must have had insufficient resources. // if ( !ARGUMENT_PRESENT( IrpContext ) ) { FatCompleteRequest( FatNull, Irp, ExceptionCode ); return ExceptionCode; } // // Get the real exception status from IrpContext->ExceptionStatus, and // reset it. // ExceptionCode = IrpContext->ExceptionStatus; FatResetExceptionState( IrpContext ); // // If this is an Mdl write request, then take care of the Mdl // here so that things get cleaned up properly. Cc now leaves // the MDL in place so a filesystem can retry after clearing an // internal condition (FAT does not). // #if __NDAS_FAT_WIN2K_SUPPORT__ if (NdFatCcMdlWriteAbort && (IrpContext->MajorFunction == IRP_MJ_WRITE) && (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE_MDL ) == IRP_MN_COMPLETE_MDL) && (Irp->MdlAddress != NULL)) { PIO_STACK_LOCATION LocalIrpSp = IoGetCurrentIrpStackLocation(Irp); NdFatCcMdlWriteAbort( LocalIrpSp->FileObject, Irp->MdlAddress ); Irp->MdlAddress = NULL; } #else if ((IrpContext->MajorFunction == IRP_MJ_WRITE) && (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE_MDL ) == IRP_MN_COMPLETE_MDL) && (Irp->MdlAddress != NULL)) { PIO_STACK_LOCATION LocalIrpSp = IoGetCurrentIrpStackLocation(Irp); CcMdlWriteAbort( LocalIrpSp->FileObject, Irp->MdlAddress ); Irp->MdlAddress = NULL; } #endif // // If we are going to post the request, we may have to lock down the // user's buffer, so do it here in a try except so that we failed the // request if the LockPages fails. // // Also unpin any repinned Bcbs, protected by the try {} except {} filter. // try { SavedFlags = IrpContext->Flags; // // Make sure we don't try to write through Bcbs // SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); FatUnpinRepinnedBcbs( IrpContext ); IrpContext->Flags = SavedFlags; // // If we will have to post the request, do it here. Note // that the last thing FatPrePostIrp() does is mark the Irp pending, // so it is critical that we actually return PENDING. Nothing // from this point to return can fail, so we are OK. // // We cannot do a verify operations at APC level because we // have to wait for Io operations to complete. // #if __NDAS_FAT__ if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) && ExceptionCode == STATUS_CANT_WAIT))) { ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); } #else if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || (ExceptionCode == STATUS_CANT_WAIT))) { ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); } #endif } except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { ExceptionCode = IrpContext->ExceptionStatus; IrpContext->ExceptionStatus = 0; IrpContext->Flags = SavedFlags; } // // If we posted the request, just return here. // if (ExceptionCode == STATUS_PENDING) { return ExceptionCode; } Irp->IoStatus.Status = ExceptionCode; // // If this request is not a "top-level" irp, just complete it. // if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { // // If there is a cache operation above us, commute verify // to a lock conflict. This will cause retries so that // we have a chance of getting through without needing // to return an unaesthetic error for the operation. // if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP && ExceptionCode == STATUS_VERIFY_REQUIRED) { ExceptionCode = STATUS_FILE_LOCK_CONFLICT; } FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } if (IoIsErrorUserInduced(ExceptionCode)) { // // Check for the various error conditions that can be caused by, // and possibly resolved by the user. // if (ExceptionCode == STATUS_VERIFY_REQUIRED) { PDEVICE_OBJECT Device; DebugTrace(0, Dbg, "Perform Verify Operation\n", 0); // // Now we are at the top level file system entry point. // // Grab the device to verify from the thread local storage // and stick it in the information field for transportation // to the fsp. We also clear the field at this time. // Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); if ( Device == NULL ) { Device = IoGetDeviceToVerify( PsGetCurrentThread() ); IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); ASSERT( Device != NULL ); } // // Let's not BugCheck just because the driver messed up. // if (Device == NULL) { ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // FatPerformVerify() will do the right thing with the Irp. return FatPerformVerify( IrpContext, Irp, Device ); } // // The other user induced conditions generate an error unless // they have been disabled for this request. // if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS)) { FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } else { // // Generate a pop-up // PDEVICE_OBJECT RealDevice; PVPB Vpb; PETHREAD Thread; if (IoGetCurrentIrpStackLocation(Irp)->FileObject != NULL) { Vpb = IoGetCurrentIrpStackLocation(Irp)->FileObject->Vpb; } else { Vpb = NULL; } // // The device to verify is either in my thread local storage // or that of the thread that owns the Irp. // Thread = Irp->Tail.Overlay.Thread; RealDevice = IoGetDeviceToVerify( Thread ); if ( RealDevice == NULL ) { Thread = PsGetCurrentThread(); RealDevice = IoGetDeviceToVerify( Thread ); ASSERT( RealDevice != NULL ); } // // Let's not BugCheck just because the driver messed up. // if (RealDevice == NULL) { FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // This routine actually causes the pop-up. It usually // does this by queuing an APC to the callers thread, // but in some cases it will complete the request immediately, // so it is very important to IoMarkIrpPending() first. // IoMarkIrpPending( Irp ); IoRaiseHardError( Irp, Vpb, RealDevice ); // // We will be handing control back to the caller here, so // reset the saved device object. // IoSetDeviceToVerify( Thread, NULL ); // // The Irp will be completed by Io or resubmitted. In either // case we must clean up the IrpContext here. // FatDeleteIrpContext( IrpContext ); return STATUS_PENDING; } } // // This is just a run of the mill error. If is a STATUS that we // raised ourselves, and the information would be use for the // user, raise an informational pop-up. // IrpSp = IoGetCurrentIrpStackLocation( Irp ); Vcb = IrpContext->Vcb; // // Now, if the Vcb is unknown to us this means that the error was raised // in the process of a mount and before we even had a chance to build // a full Vcb - and was really handled there. // if (Vcb != NULL) { if ( !FatDeviceIsFatFsdo( IrpSp->DeviceObject) && !NT_SUCCESS(ExceptionCode) && !FsRtlIsTotalDeviceFailure(ExceptionCode) ) { TransitionState = VolumeDirtyWithSurfaceTest; } // // If this was a STATUS_FILE_CORRUPT or similar error indicating some // nastiness out on the media, then mark the volume permanently dirty. // if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS) && ( TransitionState == VolumeDirtyWithSurfaceTest || (ExceptionCode == STATUS_FILE_CORRUPT_ERROR) || (ExceptionCode == STATUS_DISK_CORRUPT_ERROR) || (ExceptionCode == STATUS_EA_CORRUPT_ERROR) || (ExceptionCode == STATUS_INVALID_EA_NAME) || (ExceptionCode == STATUS_EA_LIST_INCONSISTENT) || (ExceptionCode == STATUS_NO_EAS_ON_FILE) )) { ASSERT( NodeType(Vcb) == FAT_NTC_VCB ); SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); // // Do the "dirty" work, ignoring any error. // try { FatMarkVolume( IrpContext, Vcb, TransitionState ); } except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { NOTHING; } } }