diff --git a/reactos/drivers/filesystems/fastfat_new/cleanup.c b/reactos/drivers/filesystems/fastfat_new/cleanup.c index dac825fa9ea..49ae9556ea8 100644 --- a/reactos/drivers/filesystems/fastfat_new/cleanup.c +++ b/reactos/drivers/filesystems/fastfat_new/cleanup.c @@ -13,13 +13,431 @@ /* FUNCTIONS ****************************************************************/ +/* Last handle to a file object is closed */ +NTSTATUS +NTAPI +FatiCleanup(PFAT_IRP_CONTEXT IrpContext, PIRP Irp) +{ + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + PSHARE_ACCESS ShareAccess; + BOOLEAN SendUnlockNotification = FALSE; + PLARGE_INTEGER TruncateSize = NULL; + //LARGE_INTEGER LocalTruncateSize; + BOOLEAN AcquiredVcb = FALSE, AcquiredFcb = FALSE; + NTSTATUS Status; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DPRINT("FatiCleanup\n"); + DPRINT("\tIrp = %p\n", Irp); + DPRINT("\t->FileObject = %p\n", IrpSp->FileObject); + + FileObject = IrpSp->FileObject; + TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &Fcb, &Ccb); + + if (TypeOfOpen == UnopenedFileObject) + { + DPRINT1("Unopened File Object\n"); + + FatCompleteRequest(IrpContext, Irp, STATUS_SUCCESS); + return STATUS_SUCCESS; + } + + if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) + { + /* Just flush the file */ + + if (FlagOn(Vcb->State, VCB_STATE_FLAG_DEFERRED_FLUSH) && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && + !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED) && + (TypeOfOpen == UserFileOpen)) + { + //Status = FatFlushFile(IrpContext, Fcb, Flush); + //if (!NT_SUCCESS(Status)) FatNormalizeAndRaiseStatus(IrpContext, Status); + UNIMPLEMENTED; + } + + FatCompleteRequest(IrpContext, Irp, STATUS_SUCCESS); + return STATUS_SUCCESS; + } + + if (TypeOfOpen == UserFileOpen || + TypeOfOpen == UserDirectoryOpen) + { + ASSERT(Fcb != NULL); + + (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb); + + AcquiredFcb = TRUE; + + /* Set FCB flags according to DELETE_ON_CLOSE */ + if (FlagOn(Ccb->Flags, CCB_DELETE_ON_CLOSE)) + { + ASSERT(FatNodeType(Fcb) != FAT_NTC_ROOT_DCB); + + SetFlag(Fcb->State, FCB_STATE_DELETE_ON_CLOSE); + + /* Issue a notification */ + if (TypeOfOpen == UserDirectoryOpen) + { + FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, + &Vcb->NotifyList, + FileObject->FsContext, + NULL, + FALSE, + FALSE, + 0, + NULL, + NULL, + NULL); + } + } + + /* If file should be deleted, acquire locks */ + if ((Fcb->UncleanCount == 1) && + FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) && + (Fcb->Condition != FcbBad) && + !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED)) + { + FatReleaseFcb(IrpContext, Fcb); + AcquiredFcb = FALSE; + + (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb); + AcquiredVcb = TRUE; + + (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb); + AcquiredFcb = TRUE; + } + } + + /* Acquire VCB lock if it was a volume open */ + if (TypeOfOpen == UserVolumeOpen) + { + (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb); + AcquiredVcb = TRUE; + } + + /* Cleanup all notifications */ + if (TypeOfOpen == UserDirectoryOpen) + { + FsRtlNotifyCleanup(Vcb->NotifySync, + &Vcb->NotifyList, + Ccb); + } + + if (Fcb) + { + //TODO: FatVerifyFcb + } + + switch (TypeOfOpen) + { + case DirectoryFile: + case VirtualVolumeFile: + DPRINT1("Cleanup VirtualVolumeFile/DirectoryFile\n"); + ShareAccess = NULL; + break; + + case UserVolumeOpen: + DPRINT("Cleanup UserVolumeOpen\n"); + + if (FlagOn(Ccb->Flags, CCB_COMPLETE_DISMOUNT)) + { + FatCheckForDismount( IrpContext, Vcb, TRUE ); + } else if (FileObject->WriteAccess && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) + { + UNIMPLEMENTED; + } + + /* Release the volume and send notification */ + if (FlagOn(Vcb->State, VCB_STATE_FLAG_LOCKED) && + (Vcb->FileObjectWithVcbLocked == FileObject)) + { + UNIMPLEMENTED; + SendUnlockNotification = TRUE; + } + + ShareAccess = &Vcb->ShareAccess; + break; + + case EaFile: + DPRINT1("Cleanup EaFileObject\n"); + ShareAccess = NULL; + break; + + case UserDirectoryOpen: + DPRINT("Cleanup UserDirectoryOpen\n"); + + ShareAccess = &Fcb->ShareAccess; + + /* Should it be a delayed close? */ + if ((Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + (Fcb->Dcb.DirectoryFileOpenCount == 0) && + !FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->Condition == FcbGood) + { + /* Yes, a delayed one */ + SetFlag(Fcb->State, FCB_STATE_DELAY_CLOSE); + } + + if (VcbGood == Vcb->Condition) + { + //FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + //TODO: Actually update dirent + } + + if ((Fcb->UncleanCount == 1) && + (FatNodeType(Fcb) == FAT_NTC_DCB) && + (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE)) && + (Fcb->Condition != FcbBad) && + !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED)) + { + UNIMPLEMENTED; + } + + /* Decrement unclean counter */ + ASSERT(Fcb->UncleanCount != 0); + Fcb->UncleanCount--; + break; + + case UserFileOpen: + DPRINT("Cleanup UserFileOpen\n"); + + ShareAccess = &Fcb->ShareAccess; + + /* Should it be a delayed close? */ + if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) && + (FileObject->SectionObjectPointer->ImageSectionObject == NULL) && + (Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + !FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->Condition == FcbGood) + { + /* Yes, a delayed one */ + SetFlag(Fcb->State, FCB_STATE_DELAY_CLOSE); + } + + /* Unlock all file locks */ + FsRtlFastUnlockAll(&Fcb->Fcb.Lock, + FileObject, + IoGetRequestorProcess(Irp), + NULL); + + if (Vcb->Condition == VcbGood) + { + if (Fcb->Condition != FcbBad) + { + //FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + // TODO: Update on-disk structures + } + + if (Fcb->UncleanCount == 1 && + Fcb->Condition != FcbBad) + { + //DELETE_CONTEXT DeleteContext; + + /* Should this file be deleted on close? */ + if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) && + !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED)) + { + UNIMPLEMENTED; + } + else + { + if (!FlagOn(Fcb->State, FCB_STATE_PAGEFILE) && + (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) + { +#if 0 + ULONG ValidDataLength; + + ValidDataLength = Fcb->Header.ValidDataLength.LowPart; + + if (ValidDataLength < Fcb->ValidDataToDisk) { + ValidDataLength = Fcb->ValidDataToDisk; + } + + if (ValidDataLength < Fcb->Header.FileSize.LowPart) + { + FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataLength, + Fcb->Header.FileSize.LowPart - + ValidDataLength ); + + Fcb->ValidDataToDisk = + Fcb->Header.ValidDataLength.LowPart = + Fcb->Header.FileSize.LowPart; + + if (CcIsFileCached(FileObject)) + { + CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); + } + } +#endif + DPRINT1("Zeroing out data is not implemented\n"); + } + } + + /* Should the file be truncated on close? */ + if (FlagOn(Fcb->State, FCB_STATE_TRUNCATE_ON_CLOSE)) + { + if (Vcb->Condition == VcbGood) + { + // TODO: Actually truncate the file allocation + UNIMPLEMENTED; + } + + /* Remove truncation flag */ + Fcb->State &= ~FCB_STATE_TRUNCATE_ON_CLOSE; + } + + /* Check again if it should be deleted */ + if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->Header.AllocationSize.LowPart == 0) + { + UNIMPLEMENTED; + /*FatNotifyReportChange(IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_FILE_NAME, + FILE_ACTION_REMOVED );*/ + } + + /* Remove the entry from the splay table if the file was deleted */ + if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE)) + { + FatRemoveNames(IrpContext, Fcb); + } + } + } + + ASSERT(Fcb->UncleanCount != 0); + Fcb->UncleanCount--; + if (!FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED)) + { + ASSERT(Fcb->NonCachedUncleanCount != 0); + Fcb->NonCachedUncleanCount--; + } + + if (FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED) && + (Fcb->NonCachedUncleanCount != 0) && + (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) && + (Fcb->SectionObjectPointers.DataSectionObject != NULL)) + { + CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL); + + /* Acquire and release PagingIo to get in sync with lazy writer */ + ExAcquireResourceExclusiveLite(Fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite(Fcb->Header.PagingIoResource); + + CcPurgeCacheSection(&Fcb->SectionObjectPointers, + NULL, + 0, + FALSE); + } + + if (Fcb->Condition == FcbBad) + { + //TruncateSize = &FatLargeZero; + UNIMPLEMENTED; + } + + /* Cleanup the cache map */ + CcUninitializeCacheMap(FileObject, TruncateSize, NULL); + break; + + default: + KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)TypeOfOpen, 0, 0); + } + + /* Cleanup the share access */ + + if (ShareAccess) + { + DPRINT("Cleaning up the share access\n"); + IoRemoveShareAccess(FileObject, ShareAccess); + } + + if (TypeOfOpen == UserFileOpen) + { + /* Update oplocks */ + FsRtlCheckOplock(&Fcb->Fcb.Oplock, + Irp, + IrpContext, + NULL, + NULL); + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible(Fcb); + } + + /* Set the FO_CLEANUP_COMPLETE flag */ + SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE); + + Status = STATUS_SUCCESS; + + // TODO: Unpin repinned BCBs + //FatUnpinRepinnedBcbs(IrpContext); + + /* Flush the volume if necessary */ + if (FlagOn(Vcb->State, VCB_STATE_FLAG_DEFERRED_FLUSH) && + !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED)) + { + UNIMPLEMENTED; + } + + /* Cleanup */ + if (AcquiredFcb) FatReleaseFcb(IrpContext, Fcb); + if (AcquiredVcb) FatReleaseVcb(IrpContext, Vcb); + + /* Send volume notification */ + if (SendUnlockNotification) + FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK); + + return Status; +} + NTSTATUS NTAPI FatCleanup(PDEVICE_OBJECT DeviceObject, PIRP Irp) { - DPRINT1("FatCleanup(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); + PFAT_IRP_CONTEXT IrpContext; + NTSTATUS Status; - return STATUS_NOT_IMPLEMENTED; + DPRINT("FatCleanup(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); + + /* FatCleanup works only with a volume device object */ + if (DeviceObject == FatGlobalData.DiskDeviceObject) + { + /* Complete the request and return success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return STATUS_SUCCESS; + } + + /* Enter FsRtl critical region */ + FsRtlEnterFileSystem(); + + /* Build an irp context */ + IrpContext = FatBuildIrpContext(Irp, TRUE); + + /* Call internal function */ + Status = FatiCleanup(IrpContext, Irp); + + /* Leave FsRtl critical region */ + FsRtlExitFileSystem(); + + return Status; } /* EOF */ diff --git a/reactos/drivers/filesystems/fastfat_new/close.c b/reactos/drivers/filesystems/fastfat_new/close.c index 69346982f9c..0661aeeb912 100644 --- a/reactos/drivers/filesystems/fastfat_new/close.c +++ b/reactos/drivers/filesystems/fastfat_new/close.c @@ -13,13 +13,342 @@ /* FUNCTIONS ****************************************************************/ +NTSTATUS +NTAPI +FatiCommonClose(IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN Wait, + OUT PBOOLEAN VcbDeleted) +{ + NTSTATUS Status; + PFCB ParentDcb; + BOOLEAN RecursiveClose, VcbDeletedLv = FALSE; + FAT_IRP_CONTEXT IrpContext; + + if (VcbDeleted) *VcbDeleted = FALSE; + + if (TypeOfOpen == UnopenedFileObject) + { + DPRINT1("Closing unopened file object\n"); + Status = STATUS_SUCCESS; + return Status; + } + + RtlZeroMemory(&IrpContext, sizeof(FAT_IRP_CONTEXT)); + + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IrpContext); + IrpContext.MajorFunction = IRP_MJ_CLOSE; + + if (Wait) SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT); + + if (!ExAcquireResourceExclusiveLite(&Vcb->Resource, Wait)) return STATUS_PENDING; + + if (Vcb->State & VCB_STATE_FLAG_CLOSE_IN_PROGRESS) + { + RecursiveClose = TRUE; + } + else + { + SetFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); + RecursiveClose = FALSE; + + Vcb->OpenFileCount++; + } + + /* Update on-disk structures */ + switch (TypeOfOpen) + { + case VirtualVolumeFile: + DPRINT1("Close VirtualVolumeFile\n"); + + InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount)); + InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount)); + + Status = STATUS_SUCCESS; + goto close_done; + break; + + case UserVolumeOpen: + DPRINT1("Close UserVolumeOpen\n"); + + Vcb->DirectAccessOpenCount--; + Vcb->OpenFileCount--; + if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount--; + + FatDeleteCcb(&IrpContext, Ccb); + + Status = STATUS_SUCCESS; + goto close_done; + break; + + case EaFile: + UNIMPLEMENTED; + break; + + case DirectoryFile: + DPRINT1("Close DirectoryFile\n"); + + InterlockedDecrement((PLONG)&(Fcb->Dcb.DirectoryFileOpenCount)); + InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount)); + + if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB) + { + InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount)); + } + + if (RecursiveClose) + { + Status = STATUS_SUCCESS; + goto close_done; + } + else + { + break; + } + + case UserDirectoryOpen: + case UserFileOpen: + DPRINT("Close UserFileOpen/UserDirectoryOpen\n"); + + if ((FatNodeType(Fcb) == FAT_NTC_DCB) && + IsListEmpty(&Fcb->Dcb.ParentDcbList) && + (Fcb->OpenCount == 1) && + (Fcb->Dcb.DirectoryFile != NULL)) + { + PFILE_OBJECT DirectoryFileObject = Fcb->Dcb.DirectoryFile; + + DPRINT1("Uninitialize the stream file object\n"); + + CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL); + + Fcb->Dcb.DirectoryFile = NULL; + ObDereferenceObject(DirectoryFileObject); + } + + Fcb->OpenCount--; + Vcb->OpenFileCount--; + if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount --; + + FatDeleteCcb(&IrpContext, Ccb); + break; + + default: + KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)TypeOfOpen, 0, 0); + } + + /* Update in-memory structures */ + if (((FatNodeType(Fcb) == FAT_NTC_FCB) && + (Fcb->OpenCount == 0)) + || + ((FatNodeType(Fcb) == FAT_NTC_DCB) && + (IsListEmpty(&Fcb->Dcb.ParentDcbList)) && + (Fcb->OpenCount == 0) && + (Fcb->Dcb.DirectoryFileOpenCount == 0))) + { + ParentDcb = Fcb->ParentFcb; + + SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB); + + FatDeleteFcb(&IrpContext, Fcb); + + while ((FatNodeType(ParentDcb) == FAT_NTC_DCB) && + IsListEmpty(&ParentDcb->Dcb.ParentDcbList) && + (ParentDcb->OpenCount == 0) && + (ParentDcb->Dcb.DirectoryFile != NULL)) + { + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = ParentDcb->Dcb.DirectoryFile; + + DPRINT1("Uninitialize parent Stream Cache Map\n"); + + CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL); + + ParentDcb->Dcb.DirectoryFile = NULL; + + ObDereferenceObject(DirectoryFileObject); + + if (ParentDcb->Dcb.DirectoryFileOpenCount == 0) + { + PFCB CurrentDcb; + + CurrentDcb = ParentDcb; + ParentDcb = CurrentDcb->ParentFcb; + + SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB); + + FatDeleteFcb(&IrpContext, CurrentDcb); + } + else + { + break; + } + } + } + + Status = STATUS_SUCCESS; + +close_done: + /* Closing is done, check if VCB could be closed too */ + if (!RecursiveClose) + { + /* One open left - yes, VCB can go away */ + if (Vcb->OpenFileCount == 1 && + !FlagOn(Vcb->State, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS) + && VcbDeleted) + { + FatReleaseVcb(&IrpContext, Vcb ); + + SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT); + + FatAcquireExclusiveGlobal(&IrpContext); + + FatAcquireExclusiveVcb(&IrpContext, Vcb); + + Vcb->OpenFileCount--; + + VcbDeletedLv = FatCheckForDismount(&IrpContext, Vcb, FALSE); + + FatReleaseGlobal(&IrpContext); + + if (VcbDeleted) *VcbDeleted = VcbDeletedLv; + } + else + { + /* Remove extra referenec */ + Vcb->OpenFileCount --; + } + + /* Clear recursion flag if necessary */ + if (!VcbDeletedLv) + { + ClearFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); + } + } + + /* Release VCB if it wasn't deleted */ + if (!VcbDeletedLv) + FatReleaseVcb(&IrpContext, Vcb); + + return Status; +} + +NTSTATUS +NTAPI +FatiClose(IN PFAT_IRP_CONTEXT IrpContext, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IrpSp; + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + BOOLEAN TopLevel, Wait, VcbDeleted = FALSE; + NTSTATUS Status; + PCLOSE_CONTEXT CloseContext = NULL; + + TopLevel = FatIsTopLevelIrp(Irp); + + /* Get current IRP stack location */ + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + /* Decode incoming file object */ + TypeOfOpen = FatDecodeFileObject(IrpSp->FileObject, &Vcb, &Fcb, &Ccb); + + /* Set CCB read only flag */ + if (Ccb && IsFileObjectReadOnly(IrpSp->FileObject)) + SetFlag(Ccb->Flags, CCB_READ_ONLY); + + /* It's possible to wait only if we are top level or not a system process */ + Wait = TopLevel && (PsGetCurrentProcess() != FatGlobalData.SystemProcess); + + /* Call the common handler */ + Status = FatiCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted); + + if (((TypeOfOpen == UserFileOpen || + TypeOfOpen == UserDirectoryOpen) && + (Fcb->State & FCB_STATE_DELAY_CLOSE) && + !FatGlobalData.ShutdownStarted) || + Status == STATUS_PENDING) + { + DPRINT1("TODO: Queue a pending close request\n"); + } + else + { + /* Close finished right away */ + if (TypeOfOpen == VirtualVolumeFile || + TypeOfOpen == DirectoryFile || + TypeOfOpen == EaFile) + { + if (TypeOfOpen == VirtualVolumeFile) + { + /* Free close context for the not deleted VCB */ + if (!VcbDeleted) + { + CloseContext = Vcb->CloseContext; + Vcb->CloseContext = NULL; + + ASSERT(CloseContext != NULL); + } + } + else + { + //CloseContext = FatAllocateCloseContext(Vcb); + DPRINT1("TODO: Allocate close context!\n"); + ASSERT(CloseContext != NULL); + } + + /* Free close context */ + if (CloseContext) ExFreePool(CloseContext); + } + } + + /* Complete the request */ + FatCompleteRequest(NULL, Irp, Status); + + /* Reset the top level IRP if necessary */ + if (TopLevel) IoSetTopLevelIrp(NULL); + + return Status; +} + NTSTATUS NTAPI FatClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) { - DPRINT1("FatClose(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); + PFAT_IRP_CONTEXT IrpContext; + NTSTATUS Status; - return STATUS_NOT_IMPLEMENTED; + DPRINT("FatClose(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); + + /* FatClose works only with a volume device object */ + if (DeviceObject == FatGlobalData.DiskDeviceObject) + { + /* Complete the request and return success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return STATUS_SUCCESS; + } + + /* Enter FsRtl critical region */ + FsRtlEnterFileSystem(); + + /* Build an irp context */ + IrpContext = FatBuildIrpContext(Irp, TRUE); + + /* Call internal function */ + Status = FatiClose(IrpContext, Irp); + + /* Leave FsRtl critical region */ + FsRtlExitFileSystem(); + + return Status; } /* EOF */ diff --git a/reactos/drivers/filesystems/fastfat_new/create.c b/reactos/drivers/filesystems/fastfat_new/create.c index db9a794538e..ce8cc90522d 100644 --- a/reactos/drivers/filesystems/fastfat_new/create.c +++ b/reactos/drivers/filesystems/fastfat_new/create.c @@ -72,7 +72,9 @@ FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext, /* Increment counters */ Dcb->OpenCount++; + Dcb->UncleanCount++; Vcb->OpenFileCount++; + if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; /* Set success statuses */ Iosb.Status = STATUS_SUCCESS; @@ -186,7 +188,7 @@ FatiOverwriteFile(PFAT_IRP_CONTEXT IrpContext, CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); // TODO: Actually truncate the file - DPRINT1("TODO: Actually truncate the file with a fullfat handle %x\n", Fcb->FatHandle); + DPRINT1("TODO: Actually truncate file '%wZ' with a fullfat handle %x\n", &Fcb->FullFileName, Fcb->FatHandle); /* Release the paging resource */ ExReleaseResourceLite(Fcb->Header.PagingIoResource); @@ -288,6 +290,12 @@ FatiOpenExistingDir(IN PFAT_IRP_CONTEXT IrpContext, Fcb, FatCreateCcb()); + /* Increase counters */ + Fcb->UncleanCount++; + Fcb->OpenCount++; + Vcb->OpenFileCount++; + if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; + Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; @@ -319,6 +327,7 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext, PFCB Fcb; NTSTATUS Status; FF_FILE *FileHandle; + FF_ERROR FfError; /* Check for create file option and fail */ if (CreateDisposition == FILE_CREATE) @@ -341,20 +350,22 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext, } /* Open the file with FullFAT */ - FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, NULL); + FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &FfError); if (!FileHandle) { + DPRINT1("Failed to open file '%s', error %ld\n", AnsiName.Buffer, FfError); Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now return Iosb; } + DPRINT1("Succeeded opening file '%s'\n", AnsiName.Buffer); /* Create a new FCB for this file */ Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb, FileHandle); // TODO: Check if overwrite is needed - // This is usual file open branch, without overwriting! + // TODO: This is usual file open branch, without overwriting! /* Set context and section object pointers */ FatSetFileObject(FileObject, UserFileOpen, @@ -365,6 +376,13 @@ FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext, Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; + + /* Increase counters */ + Fcb->UncleanCount++; + Fcb->OpenCount++; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++; + if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; + return Iosb; } @@ -397,7 +415,7 @@ FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext, // and opened handles count is not 0 //if (!FlagOn(ShareAccess, FILE_SHARE_READ) - DPRINT1("Exclusive voume open\n"); + DPRINT1("Exclusive volume open\n"); // TODO: Flush the volume VolumeFlushed = TRUE; @@ -450,6 +468,7 @@ FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext, /* Increase direct open count */ Vcb->DirectOpenCount++; Vcb->OpenFileCount++; + if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++; /* Set no buffering flag */ FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; @@ -720,7 +739,7 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext, /* Set parent DCB */ ParentDcb = RelatedDcb; - DPRINT1("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName); + DPRINT("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName); } else { @@ -1137,6 +1156,12 @@ FatiCreate(IN PFAT_IRP_CONTEXT IrpContext, DeleteOnClose, OpenedAsDos); + /* In case of success set cache supported flag */ + if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) + { + SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED); + } + Irp->IoStatus.Information = Iosb.Information; /* Unlock VCB */ diff --git a/reactos/drivers/filesystems/fastfat_new/dir.c b/reactos/drivers/filesystems/fastfat_new/dir.c index ca04e84c191..96198e20349 100644 --- a/reactos/drivers/filesystems/fastfat_new/dir.c +++ b/reactos/drivers/filesystems/fastfat_new/dir.c @@ -82,9 +82,6 @@ FatCreateRootDcb(IN PFAT_IRP_CONTEXT IrpContext, ExInitializeFastMutex(&Dcb->HeaderMutex); FsRtlSetupAdvancedHeader(&Dcb->Header, &Dcb->HeaderMutex); - /* Initialize MCB */ - FsRtlInitializeLargeMcb(&Dcb->Mcb, NonPagedPool); - /* Set up first cluster field depending on FAT type */ if (TRUE/*FatIsFat32(Vcb)*/) { diff --git a/reactos/drivers/filesystems/fastfat_new/fastfat.c b/reactos/drivers/filesystems/fastfat_new/fastfat.c index 64736afc797..94b42ae9bbc 100644 --- a/reactos/drivers/filesystems/fastfat_new/fastfat.c +++ b/reactos/drivers/filesystems/fastfat_new/fastfat.c @@ -42,6 +42,7 @@ DriverEntry(PDRIVER_OBJECT DriverObject, RtlZeroMemory(&FatGlobalData, sizeof(FAT_GLOBAL_DATA)); FatGlobalData.DriverObject = DriverObject; FatGlobalData.DiskDeviceObject = DeviceObject; + FatGlobalData.SystemProcess = PsGetCurrentProcess(); /* Fill major function handlers */ DriverObject->MajorFunction[IRP_MJ_CLOSE] = FatClose; @@ -317,7 +318,7 @@ FatDecodeFileObject(IN PFILE_OBJECT FileObject, TypeOfOpen = (*Ccb == NULL ? DirectoryFile : UserDirectoryOpen); - DPRINT1("Referencing a directory: %wZ\n", &(*FcbOrDcb)->FullFileName); + DPRINT("Referencing a directory: %wZ\n", &(*FcbOrDcb)->FullFileName); break; /* File */ @@ -510,4 +511,17 @@ FatMapUserBuffer(PIRP Irp) return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); } +BOOLEAN +NTAPI +FatIsTopLevelIrp(IN PIRP Irp) +{ + if (!IoGetTopLevelIrp()) + { + IoSetTopLevelIrp(Irp); + return TRUE; + } + + return FALSE; +} + /* EOF */ diff --git a/reactos/drivers/filesystems/fastfat_new/fastfat.h b/reactos/drivers/filesystems/fastfat_new/fastfat.h index a227a91f0ea..dde6aa47b64 100644 --- a/reactos/drivers/filesystems/fastfat_new/fastfat.h +++ b/reactos/drivers/filesystems/fastfat_new/fastfat.h @@ -36,6 +36,22 @@ ExReleaseResourceLite(&(FatGlobalData.Resource)); \ } +#define FatIsFastIoPossible(FCB) ((BOOLEAN) \ + (((FCB)->Condition != FcbGood || !FsRtlOplockIsFastIoPossible(&(FCB)->Fcb.Oplock)) ? \ + FastIoIsNotPossible \ + : \ + (!FsRtlAreThereCurrentFileLocks(&(FCB)->Fcb.Lock) && \ + ((FCB)->OutstandingAsyncWrites == 0) && \ + !FlagOn((FCB)->Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED) ? \ + FastIoIsPossible \ + : \ + FastIoIsQuestionable \ + ) \ + ) \ +) + +#define IsFileObjectReadOnly(FO) (!((FO)->WriteAccess | (FO)->DeleteAccess)) + NTSYSAPI NTSTATUS NTAPI @@ -67,6 +83,12 @@ FatReadStreamFile(PVCB Vcb, PBCB *Bcb, PVOID *Buffer); +BOOLEAN +NTAPI +FatCheckForDismount(IN PFAT_IRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force); + /* ----------------------------------------------------------- dir.c */ NTSTATUS NTAPI @@ -205,6 +227,9 @@ FatSetFileObject(PFILE_OBJECT FileObject, PVOID FASTCALL FatMapUserBuffer(PIRP Irp); +BOOLEAN NTAPI +FatIsTopLevelIrp(IN PIRP Irp); + /* --------------------------------------------------------- fullfat.c */ FF_T_SINT32 @@ -284,6 +309,10 @@ FatCreateFcb( IN PFCB ParentDcb, IN FF_FILE *FileHandle); +VOID NTAPI +FatDeleteFcb(IN PFAT_IRP_CONTEXT IrpContext, + IN PFCB Fcb); + IO_STATUS_BLOCK NTAPI FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, @@ -319,6 +348,10 @@ FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext, PCCB NTAPI FatCreateCcb(); +VOID NTAPI +FatDeleteCcb(IN PFAT_IRP_CONTEXT IrpContext, + IN PCCB Ccb); + VOID NTAPI FatSetFullNameInFcb(PFCB Fcb, PUNICODE_STRING Name); diff --git a/reactos/drivers/filesystems/fastfat_new/fat.c b/reactos/drivers/filesystems/fastfat_new/fat.c index 06b601c48e8..147df8d1325 100644 --- a/reactos/drivers/filesystems/fastfat_new/fat.c +++ b/reactos/drivers/filesystems/fastfat_new/fat.c @@ -201,6 +201,10 @@ FatInitializeVcb(IN PFAT_IRP_CONTEXT IrpContext, goto FatInitializeVcbCleanup; } + /* Increase internal / residual open counter */ + InterlockedIncrement((PLONG)&(Vcb->InternalOpenCount)); + InterlockedIncrement((PLONG)&(Vcb->ResidualOpenCount)); + /* Set up notifications */ FsRtlNotifyInitializeSync(&Vcb->NotifySync); InitializeListHead(&Vcb->NotifyList); diff --git a/reactos/drivers/filesystems/fastfat_new/fatstruc.h b/reactos/drivers/filesystems/fastfat_new/fatstruc.h index dff04c59ec7..f484a211553 100644 --- a/reactos/drivers/filesystems/fastfat_new/fatstruc.h +++ b/reactos/drivers/filesystems/fastfat_new/fatstruc.h @@ -14,15 +14,17 @@ typedef FAT_NODE_TYPE *PFAT_NODE_TYPE; #define FatNodeType(Ptr) (*((PFAT_NODE_TYPE)(Ptr))) /* Node type codes */ -#define FAT_NTC_VCB (CSHORT) '00VF' -#define FAT_NTC_FCB (CSHORT) 'CF' -#define FAT_NTC_DCB (CSHORT) 'DF' -#define FAT_NTC_ROOT_DCB (CSHORT) 'RFD' -#define FAT_NTC_CCB (CSHORT) 'BCC' +#define FAT_NTC_VCB (CSHORT) '00VF' +#define FAT_NTC_FCB (CSHORT) 'CF' +#define FAT_NTC_DCB (CSHORT) 'DF' +#define FAT_NTC_ROOT_DCB (CSHORT) 'RFD' +#define FAT_NTC_CCB (CSHORT) 'BCC' +#define FAT_NTC_IRP_CONTEXT (CSHORT) 'PRI' typedef struct _FAT_GLOBAL_DATA { ERESOURCE Resource; + PEPROCESS SystemProcess; PDRIVER_OBJECT DriverObject; PDEVICE_OBJECT DiskDeviceObject; LIST_ENTRY VcbListHead; @@ -33,6 +35,7 @@ typedef struct _FAT_GLOBAL_DATA CACHE_MANAGER_CALLBACKS CacheMgrCallbacks; CACHE_MANAGER_CALLBACKS CacheMgrNoopCallbacks; BOOLEAN Win31FileSystem; + BOOLEAN ShutdownStarted; /* Jan 1, 1980 System Time */ LARGE_INTEGER DefaultFileTime; @@ -82,6 +85,10 @@ typedef struct _FAT_PAGE_CONTEXT typedef struct _FAT_IRP_CONTEXT { + /* Type and size of this record (must be FAT_NTC_IRP_CONTEXT) */ + FAT_NODE_TYPE NodeTypeCode; + CSHORT NodeByteSize; + PIRP Irp; PDEVICE_OBJECT DeviceObject; UCHAR MajorFunction; @@ -132,10 +139,15 @@ typedef struct _FAT_METHODS { PFAT_SETFAT_VALUE_RUN_ROUTINE SetValueRun; } FAT_METHODS, *PFAT_METHODS; -#define VCB_STATE_FLAG_LOCKED 0x01 -#define VCB_STATE_FLAG_DIRTY 0x02 -#define VCB_STATE_MOUNTED_DIRTY 0x04 -#define VCB_STATE_CREATE_IN_PROGRESS 0x08 +#define VCB_STATE_FLAG_LOCKED 0x001 +#define VCB_STATE_FLAG_DIRTY 0x002 +#define VCB_STATE_MOUNTED_DIRTY 0x004 +#define VCB_STATE_CREATE_IN_PROGRESS 0x008 +#define VCB_STATE_FLAG_CLOSE_IN_PROGRESS 0x010 +#define VCB_STATE_FLAG_DELETED_FCB 0x020 +#define VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS 0x040 +#define VCB_STATE_FLAG_DEFERRED_FLUSH 0x080 +#define VCB_STATE_FLAG_WRITE_PROTECTED 0x100 typedef enum _VCB_CONDITION { @@ -158,10 +170,11 @@ typedef struct _VCB ULONG State; VCB_CONDITION Condition; ERESOURCE Resource; + struct _CLOSE_CONTEXT *CloseContext; /* Direct volume access */ - ULONG DirectOpenCount; SHARE_ACCESS ShareAccess; + PFILE_OBJECT FileObjectWithVcbLocked; /* Notifications support */ PNOTIFY_SYNC NotifySync; @@ -186,8 +199,13 @@ typedef struct _VCB struct _FCB *RootDcb; /* Counters */ - ULONG MediaChangeCount; + ULONG DirectOpenCount; ULONG OpenFileCount; + ULONG ReadOnlyCount; + ULONG InternalOpenCount; + ULONG ResidualOpenCount; + ULONG DirectAccessOpenCount; + ULONG MediaChangeCount; /* FullFAT integration */ FF_IOMAN *Ioman; @@ -252,6 +270,7 @@ typedef enum _FCB_CONDITION #define FCB_STATE_PAGEFILE 0x04 #define FCB_STATE_DELAY_CLOSE 0x08 #define FCB_STATE_TRUNCATE_ON_CLOSE 0x10 +#define FCB_STATE_DELETE_ON_CLOSE 0x20 typedef struct _FCB { @@ -281,8 +300,6 @@ typedef struct _FCB FCB_CONDITION Condition; /* Share access */ SHARE_ACCESS ShareAccess; - /* Mcb mapping Vbo->Lbo */ - LARGE_MCB Mcb; ULONG FirstCluster; /* Links into FCB Tree */ FCB_NAME_LINK ShortName; @@ -307,6 +324,8 @@ typedef struct _FCB PKEVENT OutstandingAsyncEvent; /* Counters */ ULONG OpenCount; + ULONG UncleanCount; + ULONG NonCachedUncleanCount; union { struct @@ -318,10 +337,11 @@ typedef struct _FCB struct { - /* A list of all FCBs/DCBs opened under this DCB */ - LIST_ENTRY ParentDcbList; + LIST_ENTRY ParentDcbList; /* A list of all FCBs/DCBs opened under this DCB */ + ULONG DirectoryFileOpenCount; /* Sector-based access to the dir */ + PFILE_OBJECT DirectoryFile; /* Directory data stream (just handy to have it). */ - PFILE_OBJECT StreamFileObject; + //PFILE_OBJECT StreamFileObject; /* Bitmap to search for free dirents. */ RTL_BITMAP FreeBitmap; /* Names */ @@ -390,6 +410,17 @@ typedef enum _TYPE_OF_OPEN EaFile } TYPE_OF_OPEN; +typedef struct _CLOSE_CONTEXT +{ + LIST_ENTRY GlobalLinks; + LIST_ENTRY VcbLinks; + + PVCB Vcb; + PFCB Fcb; + TYPE_OF_OPEN TypeOfOpen; + BOOLEAN Free; +} CLOSE_CONTEXT, *PCLOSE_CONTEXT; + typedef enum _FILE_TIME_INDEX { FileCreationTime = 0, @@ -402,4 +433,8 @@ typedef enum _FILE_TIME_INDEX #define CCB_SEARCH_PATTERN_LEGAL_8DOT3 0x02 #define CCB_SEARCH_PATTERN_HAS_WILD_CARD 0x04 #define CCB_DASD_IO 0x10 +#define CCB_READ_ONLY 0x20 +#define CCB_DELETE_ON_CLOSE 0x40 +#define CCB_COMPLETE_DISMOUNT 0x80 + extern FAT_GLOBAL_DATA FatGlobalData; diff --git a/reactos/drivers/filesystems/fastfat_new/fcb.c b/reactos/drivers/filesystems/fastfat_new/fcb.c index cb02d53bb16..eeb12dd8c17 100644 --- a/reactos/drivers/filesystems/fastfat_new/fcb.c +++ b/reactos/drivers/filesystems/fastfat_new/fcb.c @@ -149,12 +149,74 @@ FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext, Fcb->Header.ValidDataLength.LowPart = FileHandle->Filesize; Fcb->FatHandle = FileHandle; + /* Initialize locks */ + FsRtlInitializeFileLock(&Fcb->Fcb.Lock, NULL, NULL); + FsRtlInitializeOplock(&Fcb->Fcb.Oplock); + /* Set names */ FatSetFcbNames(IrpContext, Fcb); return Fcb; } +VOID +NTAPI +FatDeleteFcb(IN PFAT_IRP_CONTEXT IrpContext, + IN PFCB Fcb) +{ + DPRINT("FatDeleteFcb %p\n", Fcb); + + if (Fcb->OpenCount != 0) + { + DPRINT1("Trying to delete FCB with OpenCount %d\n", Fcb->OpenCount); + ASSERT(FALSE); + } + + if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || + (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) + { + /* Make sure it's a valid deletion */ + ASSERT(Fcb->Dcb.DirectoryFileOpenCount == 0); + ASSERT(IsListEmpty(&Fcb->Dcb.ParentDcbList)); + ASSERT(Fcb->Dcb.DirectoryFile == NULL); + } + else + { + /* Free locks */ + FsRtlUninitializeFileLock(&Fcb->Fcb.Lock); + FsRtlUninitializeOplock(&Fcb->Fcb.Oplock); + } + + /* Release any possible filter contexts */ + FsRtlTeardownPerStreamContexts(&Fcb->Header); + + /* Remove from parents queue */ + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) + { + RemoveEntryList(&(Fcb->ParentDcbLinks)); + } + + /* Free FullFAT handle */ + if (Fcb->FatHandle) FF_Close(Fcb->FatHandle); + + /* Remove from the splay table */ + if (FlagOn(Fcb->State, FCB_STATE_HAS_NAMES)) + FatRemoveNames(IrpContext, Fcb); + + /* Free file name buffers */ + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) + { + if (Fcb->FullFileName.Buffer) + ExFreePool(Fcb->FullFileName.Buffer); + } + + if (Fcb->ExactCaseLongName.Buffer) + ExFreePool(Fcb->ExactCaseLongName.Buffer); + + /* Free this FCB, finally */ + ExFreePool(Fcb); +} + PCCB NTAPI FatCreateCcb() @@ -172,6 +234,17 @@ FatCreateCcb() return Ccb; } +VOID +NTAPI +FatDeleteCcb(IN PFAT_IRP_CONTEXT IrpContext, + IN PCCB Ccb) +{ + // TODO: Deallocate CCB strings, if any + + /* Free the CCB */ + ExFreePool(Ccb); +} + IO_STATUS_BLOCK NTAPI FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext, @@ -443,8 +516,11 @@ SuccComplete: 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 } diff --git a/reactos/drivers/filesystems/fastfat_new/volume.c b/reactos/drivers/filesystems/fastfat_new/volume.c index 49a67b0a450..087b21100e6 100644 --- a/reactos/drivers/filesystems/fastfat_new/volume.c +++ b/reactos/drivers/filesystems/fastfat_new/volume.c @@ -8,7 +8,7 @@ /* INCLUDES *****************************************************************/ -//#define NDEBUG +#define NDEBUG #include "fastfat.h" /* FUNCTIONS ****************************************************************/ @@ -77,8 +77,8 @@ FatiQueryFsSizeInfo(PVCB Vcb, Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster; Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector; - DPRINT1("Total %d, free %d, SPC %d, BPS %d\n", Partition->FreeClusterCount, - Partition->NumClusters, Vcb->Bpb.SectorsPerCluster, Vcb->Bpb.BytesPerSector); + DPRINT1("Total %d, free %d, SPC %d, BPS %d\n", Partition->NumClusters, + Partition->FreeClusterCount, Vcb->Bpb.SectorsPerCluster, Vcb->Bpb.BytesPerSector); return Status; } @@ -228,4 +228,14 @@ FatReadStreamFile(PVCB Vcb, } } +BOOLEAN +NTAPI +FatCheckForDismount(IN PFAT_IRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force) +{ + /* We never allow deletion of a volume for now */ + return FALSE; +} + /* EOF */