NTSTATUS FatFsdClose ( _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, _Inout_ PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of Close. Arguments: VolumeDeviceObject - Supplies the volume device object where the file exists Irp - Supplies the Irp being processed Return Value: NTSTATUS - The FSD status for the IRP --*/ { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; PVCB Vcb; PFCB Fcb; PCCB Ccb; TYPE_OF_OPEN TypeOfOpen; BOOLEAN TopLevel; BOOLEAN VcbDeleted = FALSE; PAGED_CODE(); // // If we were called with our file system device object instead of a // volume device object, just complete this request with STATUS_SUCCESS // if (FatDeviceIsFatFsdo( VolumeDeviceObject)) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return STATUS_SUCCESS; } DebugTrace(+1, Dbg, "FatFsdClose\n", 0); // // Call the common Close routine // FsRtlEnterFileSystem(); TopLevel = FatIsIrpTopLevel( Irp ); // // Get a pointer to the current stack location and the file object // IrpSp = IoGetCurrentIrpStackLocation( Irp ); FileObject = IrpSp->FileObject; // // Decode the file object and set the read-only bit in the Ccb. // TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); if (Ccb && IsFileObjectReadOnly(FileObject)) { SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY ); } try { PCLOSE_CONTEXT CloseContext = NULL; // // If we are top level, WAIT can be TRUE, otherwise make it FALSE // to avoid deadlocks, unless this is a top // level request not originating from the system process. // BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess); #if (NTDDI_VERSION >= NTDDI_WIN8) // // To catch the odd case where a close comes in without a preceding cleanup, // call the oplock package to get rid of any oplock state. This can only // be safely done in the FSD path. // if ((Fcb != NULL) && !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) && FatIsFileOplockable( Fcb )) { // // This is equivalent to handling cleanup, and it always cleans up any // oplock immediately. Also, we don't need any locking of the FCB here; // the oplock's own lock will be sufficient for this purpose. // FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), Irp, 0, NULL, NULL, NULL ); } #endif // // Call the common Close routine if we are not delaying this close. // if ((((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) && !FatData.ShutdownStarted) || (FatCommonClose( Vcb, Fcb, Ccb, TypeOfOpen, Wait, TopLevel, &VcbDeleted ) == STATUS_PENDING)) { // // Metadata streams have had close contexts preallocated. // if (TypeOfOpen == VirtualVolumeFile) { NT_ASSERT( Vcb->CloseContext != NULL ); CloseContext = Vcb->CloseContext; Vcb->CloseContext = NULL; CloseContext->Free = TRUE; } else if ((TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile)) { CloseContext = FatAllocateCloseContext( Vcb); NT_ASSERT( CloseContext != NULL ); CloseContext->Free = TRUE; } else { // // Free up any query template strings before using the close context fields, // which overlap (union) // FatDeallocateCcbStrings( Ccb ); CloseContext = &Ccb->CloseContext; CloseContext->Free = FALSE; SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT ); } // // If the status is pending, then let's get the information we // need into the close context we already have bagged, complete // the request, and post it. It is important we allocate nothing // in the close path. // CloseContext->Vcb = Vcb; CloseContext->Fcb = Fcb; CloseContext->TypeOfOpen = TypeOfOpen; // // Send it off, either to an ExWorkerThread or to the async // close list. // FatQueueClose( CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE))); } else { // // The close proceeded synchronously, so for the metadata objects we // can now drop the close context we preallocated. // if ((TypeOfOpen == VirtualVolumeFile) || (TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile)) { if (TypeOfOpen == VirtualVolumeFile) { // // If the VCB was deleted during the close, the close context for this // open has already been freed. // if (!VcbDeleted) { CloseContext = Vcb->CloseContext; Vcb->CloseContext = NULL; NT_ASSERT( CloseContext != NULL ); } else { CloseContext = NULL; } } else { CloseContext = FatAllocateCloseContext( (VcbDeleted ? NULL : Vcb) ); NT_ASSERT( CloseContext != NULL ); } if (CloseContext != NULL) { ExFreePool( CloseContext ); } } } FatCompleteRequest( FatNull, Irp, Status ); } except(FatExceptionFilter( NULL, GetExceptionInformation() )) { // // We had some trouble trying to perform the requested // operation, so we'll abort the I/O request with the // error status that we get back from the execption code. // Status = FatProcessException( NULL, Irp, GetExceptionCode() ); } if (TopLevel) { IoSetTopLevelIrp( NULL ); } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status); UNREFERENCED_PARAMETER( VolumeDeviceObject ); return Status; }
IO_STATUS_BLOCK NTAPI FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB Fcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG AllocationSize, IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, IN UCHAR FileAttributes, IN ULONG CreateDisposition, IN BOOLEAN NoEaKnowledge, IN BOOLEAN DeleteOnClose, IN BOOLEAN OpenedAsDos, OUT PBOOLEAN OplockPostIrp) { IO_STATUS_BLOCK Iosb = {{0}}; ACCESS_MASK AddedAccess = 0; BOOLEAN Hidden; BOOLEAN System; PCCB Ccb = NULL; NTSTATUS Status, StatusPrev; /* Acquire exclusive FCB lock */ (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb); *OplockPostIrp = FALSE; /* Check if there is a batch oplock */ if (FsRtlCurrentBatchOplock(&Fcb->Fcb.Oplock)) { /* Return with a special information field */ Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY; /* Check the oplock */ Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock, IrpContext->Irp, IrpContext, FatOplockComplete, FatPrePostIrp); if (Iosb.Status != STATUS_SUCCESS && Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { /* The Irp needs to be queued */ *OplockPostIrp = TRUE; /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } } /* Validate parameters and modify access */ if (CreateDisposition == FILE_CREATE) { Iosb.Status = STATUS_OBJECT_NAME_COLLISION; /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } else if (CreateDisposition == FILE_SUPERSEDE) { SetFlag(AddedAccess, DELETE & ~(*DesiredAccess)); *DesiredAccess |= DELETE; } else if ((CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) { SetFlag(AddedAccess, (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) ); *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES; } // TODO: Check desired access // TODO: Check if this file is readonly and DeleteOnClose is set /* Validate disposition information */ if ((CreateDisposition == FILE_SUPERSEDE) || (CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) { // TODO: Get this attributes from the dirent Hidden = FALSE; System = FALSE; if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) || (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) { DPRINT1("Hidden/system attributes don't match\n"); Iosb.Status = STATUS_ACCESS_DENIED; /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } // TODO: Check for write protected volume } /* Check share access */ Iosb.Status = IoCheckShareAccess(*DesiredAccess, ShareAccess, FileObject, &Fcb->ShareAccess, FALSE); if (!NT_SUCCESS(Iosb.Status)) { /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } /* Check the oplock status after checking for share access */ Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock, IrpContext->Irp, IrpContext, FatOplockComplete, FatPrePostIrp ); if (Iosb.Status != STATUS_SUCCESS && Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { /* The Irp needs to be queued */ *OplockPostIrp = TRUE; /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } /* Set Fast I/O flag */ Fcb->Header.IsFastIoPossible = FALSE; //FatiIsFastIoPossible(Fcb); /* Make sure image is not mapped */ if (DeleteOnClose || FlagOn(*DesiredAccess, FILE_WRITE_DATA)) { /* Try to flush the image section */ if (!MmFlushImageSection(&Fcb->SectionObjectPointers, MmFlushForWrite)) { /* Yes, image section exists, set correct status code */ if (DeleteOnClose) Iosb.Status = STATUS_CANNOT_DELETE; else Iosb.Status = STATUS_SHARING_VIOLATION; /* Release the FCB and return */ FatReleaseFcb(IrpContext, Fcb); return Iosb; } } /* Flush the cache if it's non-cached non-pagefile access */ if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) && Fcb->SectionObjectPointers.DataSectionObject && !FlagOn(Fcb->State, FCB_STATE_PAGEFILE)) { /* Set the flag that create is in progress */ SetFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS); /* Flush the cache */ CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL); /* Acquire and release Paging I/O resource before purging the cache section to let lazy writer finish */ ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); ExReleaseResourceLite( Fcb->Header.PagingIoResource ); /* Delete the cache section */ CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE); /* Clear the flag */ ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS); } /* Check create disposition flags and branch accordingly */ if (CreateDisposition == FILE_OPEN || CreateDisposition == FILE_OPEN_IF) { DPRINT("Opening a file\n"); /* Check if we need to bother with EA */ if (NoEaKnowledge && FALSE /* FatIsFat32(Vcb)*/) { UNIMPLEMENTED; } /* Set up file object */ Ccb = FatCreateCcb(); FatSetFileObject(FileObject, UserFileOpen, Fcb, Ccb); FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; /* The file is opened */ Iosb.Information = FILE_OPENED; goto SuccComplete; } else if ((CreateDisposition == FILE_SUPERSEDE) || (CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) { /* Remember previous status */ StatusPrev = Iosb.Status; // TODO: Check system security access /* Perform overwrite operation */ Iosb = FatiOverwriteFile(IrpContext, FileObject, Fcb, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, NoEaKnowledge); /* Restore previous status in case of success */ if (Iosb.Status == STATUS_SUCCESS) Iosb.Status = StatusPrev; /* Fall down to completion */ } else { /* We can't get here */ KeBugCheckEx(FAT_FILE_SYSTEM, CreateDisposition, 0, 0, 0); } SuccComplete: /* If all is fine */ if (Iosb.Status != STATUS_PENDING && NT_SUCCESS(Iosb.Status)) { /* Update access if needed */ if (AddedAccess) { /* Remove added access flags from desired access */ ClearFlag(*DesiredAccess, AddedAccess); /* Check share access */ Status = IoCheckShareAccess(*DesiredAccess, ShareAccess, FileObject, &Fcb->ShareAccess, TRUE); /* Make sure it's success */ ASSERT(Status == STATUS_SUCCESS); } else { /* Update the share access */ IoUpdateShareAccess(FileObject, &Fcb->ShareAccess); } /* Clear the delay close */ ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE); /* Increase counters */ Fcb->UncleanCount++; Fcb->OpenCount++; Vcb->OpenFileCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++; // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB } return Iosb; }
IO_STATUS_BLOCK NTAPI FatiOpenExistingDcb(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PVCB Vcb, IN PFCB Dcb, IN PACCESS_MASK DesiredAccess, IN USHORT ShareAccess, IN ULONG CreateDisposition, IN BOOLEAN NoEaKnowledge, IN BOOLEAN DeleteOnClose) { IO_STATUS_BLOCK Iosb = {{0}}; PCCB Ccb; /* Exclusively lock this FCB */ FatAcquireExclusiveFcb(IrpContext, Dcb); /* Check if it's a delete-on-close of a root DCB */ if (FatNodeType(Dcb) == FAT_NTC_ROOT_DCB && DeleteOnClose) { Iosb.Status = STATUS_CANNOT_DELETE; /* Release the lock and return */ FatReleaseFcb(IrpContext, Dcb); return Iosb; } /*if (NoEaKnowledge && NodeType(Dcb) != FAT_NTC_ROOT_DCB && !FatIsFat32(Vcb)) { UNIMPLEMENTED; }*/ /* Check the create disposition and desired access */ if ((CreateDisposition != FILE_OPEN) && (CreateDisposition != FILE_OPEN_IF)) { Iosb.Status = STATUS_OBJECT_NAME_COLLISION; /* Release the lock and return */ FatReleaseFcb(IrpContext, Dcb); return Iosb; } #if 0 if (!FatCheckFileAccess(IrpContext, Dcb->DirentFatFlags, DesiredAccess)) { Iosb.Status = STATUS_ACCESS_DENIED; try_return( Iosb ); } #endif /* If it's already opened - check share access */ if (Dcb->OpenCount > 0) { Iosb.Status = IoCheckShareAccess(*DesiredAccess, ShareAccess, FileObject, &Dcb->ShareAccess, TRUE); if (!NT_SUCCESS(Iosb.Status)) { /* Release the lock and return */ FatReleaseFcb(IrpContext, Dcb); return Iosb; } } else { IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Dcb->ShareAccess); } /* Set the file object */ Ccb = FatCreateCcb(); FatSetFileObject(FileObject, UserDirectoryOpen, Dcb, Ccb); /* Increase counters */ Dcb->UncleanCount++; Dcb->OpenCount++; Vcb->OpenFileCount++; if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; /* Set delete on close */ if (DeleteOnClose) SetFlag(Ccb->Flags, CCB_DELETE_ON_CLOSE); /* Clear delay close flag */ ClearFlag(Dcb->State, FCB_STATE_DELAY_CLOSE); /* That's it */ Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; /* Release the lock */ FatReleaseFcb(IrpContext, Dcb); return Iosb; }
NTSTATUS FatFsdClose ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of Close. Arguments: VolumeDeviceObject - Supplies the volume device object where the file exists Irp - Supplies the Irp being processed Return Value: NTSTATUS - The FSD status for the IRP --*/ { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; PVCB Vcb; PFCB Fcb; PCCB Ccb; TYPE_OF_OPEN TypeOfOpen; BOOLEAN TopLevel; BOOLEAN VcbDeleted; #ifdef __ND_FAT__ if ((PVOID)FatControlDeviceObject == VolumeDeviceObject) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return STATUS_SUCCESS; } #endif // // If we were called with our file system device object instead of a // volume device object, just complete this request with STATUS_SUCCESS // if (FatDeviceIsFatFsdo( VolumeDeviceObject)) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return STATUS_SUCCESS; } DebugTrace(+1, Dbg, "FatFsdClose\n", 0); // // Call the common Close routine // FsRtlEnterFileSystem(); TopLevel = FatIsIrpTopLevel( Irp ); // // Get a pointer to the current stack location and the file object // IrpSp = IoGetCurrentIrpStackLocation( Irp ); FileObject = IrpSp->FileObject; // // Decode the file object and set the read-only bit in the Ccb. // TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); if (Ccb && IsFileObjectReadOnly(FileObject)) { SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY ); } try { PCLOSE_CONTEXT CloseContext; // // If we are top level, WAIT can be TRUE, otherwise make it FALSE // to avoid deadlocks, unless this is a top // level request not originating from the system process. // BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess); #ifdef __ND_FAT__ if (Ccb) { SetFlag( Ccb->NdFatFlags, ND_FAT_CCB_FLAG_CLOSE ); ((PCCB)(FileObject->FsContext2))->FileObject = NULL; FileObject->FsContext = NULL; FileObject->FsContext2 = NULL; } #endif // // Call the common Close routine if we are not delaying this close. // if ((((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) && !FatData.ShutdownStarted) || (FatCommonClose( Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted ) == STATUS_PENDING)) { // // Metadata streams have had close contexts preallocated. // if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile || TypeOfOpen == EaFile) { CloseContext = FatAllocateCloseContext(); ASSERT( CloseContext != NULL ); CloseContext->Free = TRUE; } else { // // Free up any query template strings before using the close context fields, // which overlap (union) // FatDeallocateCcbStrings( Ccb); CloseContext = &Ccb->CloseContext; CloseContext->Free = FALSE; SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT ); } // // If the status is pending, then let's get the information we // need into the close context we already have bagged, complete // the request, and post it. It is important we allocate nothing // in the close path. // CloseContext->Vcb = Vcb; CloseContext->Fcb = Fcb; CloseContext->TypeOfOpen = TypeOfOpen; // // Send it off, either to an ExWorkerThread or to the async // close list. // FatQueueClose( CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE))); } else { // // The close proceeded synchronously, so for the metadata objects we // can now drop the close context we preallocated. // if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile || TypeOfOpen == EaFile) { CloseContext = FatAllocateCloseContext(); ASSERT( CloseContext != NULL ); ExFreePool( CloseContext ); } } FatCompleteRequest( FatNull, Irp, Status ); } except(FatExceptionFilter( NULL, GetExceptionInformation() )) { // // We had some trouble trying to perform the requested // operation, so we'll abort the I/O request with the // error status that we get back from the execption code. // Status = FatProcessException( NULL, Irp, GetExceptionCode() ); } if (TopLevel) { IoSetTopLevelIrp( NULL ); } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status); UNREFERENCED_PARAMETER( VolumeDeviceObject ); return Status; }