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); }
VOID FatInsertName ( IN PIRP_CONTEXT IrpContext, IN PRTL_SPLAY_LINKS *RootNode, IN PFILE_NAME_NODE Name ) /*++ Routine Description: This routine will insert a name in the splay tree pointed to by RootNode. The name must not already exist in the splay tree. Arguments: RootNode - Supplies a pointer to the table. Name - Contains the New name to enter. Return Value: None. --*/ { COMPARISON Comparison; PFILE_NAME_NODE Node; PAGED_CODE(); RtlInitializeSplayLinks(&Name->Links); Restart: // // If we are the first entry in the tree, just become the root. // if (*RootNode == NULL) { *RootNode = &Name->Links; return; } Node = CONTAINING_RECORD( *RootNode, FILE_NAME_NODE, Links ); while (TRUE) { // // Compare the prefix in the tree with the prefix we want // to insert. Note that Oem here doesn't mean anything. // Comparison = CompareNames(&Node->Name.Oem, &Name->Name.Oem); // // We should never find the name in the table already. // if (Comparison == IsEqual) { // // Almost. If the removable media was taken to another machine and // back, and we have something like: // // Old: foobar~1 / foobarbaz // New: foobar~1 / foobarbazbaz // // but a handle was kept open to foobarbaz so we couldn't purge // away the Fcb in the verify path ... opening foobarbazbaz will // try to insert a duplicate shortname. Bang! // // Invalidate it and the horse it came in on. This new one wins. // The old one is gone. Only if the old one is in normal state // do we really have a problem. // if (Node->Fcb->FcbState == FcbGood) { FatBugCheck( (ULONG_PTR)*RootNode, (ULONG_PTR)Name, (ULONG_PTR)Node ); } // // Note, once we zap the prefix links we need to restart our walk // of the tree. // FatMarkFcbCondition( IrpContext, Node->Fcb, FcbBad, TRUE ); FatRemoveNames( IrpContext, Node->Fcb ); goto Restart; } // // If the tree prefix is greater than the new prefix then // we go down the left subtree // if (Comparison == IsGreaterThan) { // // We want to go down the left subtree, first check to see // if we have a left subtree // if (RtlLeftChild(&Node->Links) == NULL) { // // there isn't a left child so we insert ourselves as the // new left child // RtlInsertAsLeftChild(&Node->Links, &Name->Links); // // and exit the while loop // break; } else { // // there is a left child so simply go down that path, and // go back to the top of the loop // Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links), FILE_NAME_NODE, Links ); } } else { // // The tree prefix is either less than or a proper prefix // of the new string. We treat both cases a less than when // we do insert. So we want to go down the right subtree, // first check to see if we have a right subtree // if (RtlRightChild(&Node->Links) == NULL) { // // These isn't a right child so we insert ourselves as the // new right child // RtlInsertAsRightChild(&Node->Links, &Name->Links); // // and exit the while loop // break; } else { // // there is a right child so simply go down that path, and // go back to the top of the loop // Node = CONTAINING_RECORD( RtlRightChild(&Node->Links), FILE_NAME_NODE, Links ); } } } return; }
/* 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); DPRINT1("Setting a delay on close for some reason for FCB %p, FF handle %p, file name '%wZ'\n", Fcb, Fcb->FatHandle, &Fcb->FullFileName); } /* 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) { 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; }