Ejemplo n.º 1
0
VOID
NTAPI
FatSetFileObject(PFILE_OBJECT FileObject,
                 TYPE_OF_OPEN TypeOfOpen,
                 PVOID Fcb,
                 PCCB Ccb)
{
    if (Fcb)
    {
        /* Check Fcb's type  */
        if (FatNodeType(Fcb) == FAT_NTC_VCB)
        {
            FileObject->Vpb = ((PVCB)Fcb)->Vpb;
        }
        else
        {
            FileObject->Vpb = ((PFCB)Fcb)->Vcb->Vpb;
        }
    }

    /* Set FsContext */
    if (FileObject)
    {
        FileObject->FsContext  = Fcb;
        FileObject->FsContext2 = Ccb;
    }
}
Ejemplo n.º 2
0
TYPE_OF_OPEN
NTAPI
FatDecodeFileObject(IN PFILE_OBJECT FileObject,
                    OUT PVCB *Vcb,
                    OUT PFCB *FcbOrDcb,
                    OUT PCCB *Ccb)
{
    TYPE_OF_OPEN TypeOfOpen = UnopenedFileObject;
    PVOID FsContext = FileObject->FsContext;
    PVOID FsContext2 = FileObject->FsContext2;

    /* If FsContext is NULL, then everything is NULL */
    if (!FsContext)
    {
        *Ccb = NULL;
        *FcbOrDcb = NULL;
        *Vcb = NULL;

        return TypeOfOpen;
    }

    /* CCB is always stored in FsContext2 */
    *Ccb = FsContext2;

    /* Switch according to the NodeType */
    switch (FatNodeType(FsContext))
    {
        /* Volume */
        case FAT_NTC_VCB:
            *FcbOrDcb = NULL;
            *Vcb = FsContext;

            TypeOfOpen = ( *Ccb == NULL ? VirtualVolumeFile : UserVolumeOpen );

            break;

        /* Root or normal directory*/
        case FAT_NTC_ROOT_DCB:
        case FAT_NTC_DCB:
            *FcbOrDcb = FsContext;
            *Vcb = (*FcbOrDcb)->Vcb;

            TypeOfOpen = (*Ccb == NULL ? DirectoryFile : UserDirectoryOpen);

            DPRINT("Referencing a directory: %wZ\n", &(*FcbOrDcb)->FullFileName);
            break;

        /* File */
        case FAT_NTC_FCB:
            *FcbOrDcb = FsContext;
            *Vcb = (*FcbOrDcb)->Vcb;

            TypeOfOpen = (*Ccb == NULL ? EaFile : UserFileOpen);

            DPRINT("Referencing a file: %wZ\n", &(*FcbOrDcb)->FullFileName);

            break;

        default:
            DPRINT1("Unknown node type %x\n", FatNodeType(FsContext));
            ASSERT(FALSE);
    }

    return TypeOfOpen;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
VOID
NTAPI
FatSetFullNameInFcb(PFCB Fcb,
                    PUNICODE_STRING Name)
{
    PUNICODE_STRING ParentName;

    /* Make sure this FCB's name wasn't already set */
    ASSERT(Fcb->FullFileName.Buffer == NULL);

    /* First of all, check exact case name */
    if (Fcb->ExactCaseLongName.Buffer)
    {
        ASSERT(Fcb->ExactCaseLongName.Length != 0);

        /* Use exact case name */
        Name = &Fcb->ExactCaseLongName;
    }

    /* Treat root dir different */
    if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
    {
        /* Set lengths */
        Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
        Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;

        /* Allocate a buffer */
        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
                                                            Fcb->FullFileName.Length,
                                                            TAG_FILENAME);

        /* Prefix with a backslash */
        Fcb->FullFileName.Buffer[0] = L'\\';

        /* Copy the name here */
        RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
                      &Name->Buffer[0],
                       Name->Length );
    }
    else
    {
        ParentName = &Fcb->ParentFcb->FullFileName;

        /* Check if parent's name is set */
        if (!ParentName->Buffer)
            return;

        /* Set lengths */
        Fcb->FullFileName.MaximumLength =
            ParentName->Length + sizeof(WCHAR) + Name->Length;
        Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;

        /* Allocate a buffer */
        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
                                                            Fcb->FullFileName.Length,
                                                            TAG_FILENAME );

        /* Copy parent's name here */
        RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
                      &ParentName->Buffer[0],
                      ParentName->Length );

        /* Add a backslash */
        Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';

        /* Copy given name here */
        RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
                      &Name->Buffer[0],
                      Name->Length );
    }
}
Ejemplo n.º 5
0
VOID
NTAPI
FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
                     IN PFCB Fcb,
                     OUT PUNICODE_STRING LongName)
{
    FF_DIRENT DirEnt;
    FF_ERROR Err;
    OEM_STRING ShortName;
    CHAR ShortNameBuf[13];
    UCHAR EntryBuffer[32];
    UCHAR NumLFNs;
    OEM_STRING LongNameOem;
    NTSTATUS Status;

    /* Make sure this FCB has a FullFAT handle associated with it */
    if (Fcb->FatHandle == NULL &&
        FatNodeType(Fcb) == FAT_NTC_DCB)
    {
        /* Open the dir with FullFAT */
        Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
        if (!Fcb->FatHandle)
        {
            ASSERT(FALSE);
        }
    }

    /* Get the dir entry */
    Err = FF_GetEntry(Fcb->Vcb->Ioman,
                      Fcb->FatHandle->DirEntry,
                      Fcb->FatHandle->DirCluster,
                      &DirEnt);

    if (Err != FF_ERR_NONE)
    {
        DPRINT1("Error %d getting dirent of a file\n", Err);
        return;
    }

    /* Read the dirent to fetch the raw short name */
    FF_FetchEntry(Fcb->Vcb->Ioman,
                  Fcb->FatHandle->DirCluster,
                  Fcb->FatHandle->DirEntry,
                  EntryBuffer);
    NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);

    /* Check if we only have a short name.
       Convert it to unicode and return if that's the case */
    if (NumLFNs == 0)
    {
        /* Initialize short name string */
        ShortName.Buffer = ShortNameBuf;
        ShortName.Length = 0;
        ShortName.MaximumLength = 12;

        /* Convert raw short name to a proper string */
        Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);

        /* Convert it to unicode */
        Status = RtlOemStringToCountedUnicodeString(LongName,
                                                    &ShortName,
                                                    FALSE);

        /* Ensure conversion was successful */
        ASSERT(Status == STATUS_SUCCESS);

        /* Exit */
        return;
    }

    /* Convert LFN from OEM to unicode and return */
    LongNameOem.Buffer = DirEnt.FileName;
    LongNameOem.MaximumLength = FF_MAX_FILENAME;
    LongNameOem.Length = strlen(DirEnt.FileName);

    /* Convert it to unicode */
    Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);

    /* Ensure conversion was successful */
    ASSERT(Status == STATUS_SUCCESS);
}
Ejemplo n.º 6
0
/* 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;
}