mirror of
https://github.com/reactos/reactos.git
synced 2025-06-20 00:06:26 +00:00

When we're about to close a file (ie, forget everything about it
and release any associated structure), actually delay it.
This allows keep data fresh in memory for faster reuse in case
it would be required. The effective closing will only happen after some time.
For specific operations, this will produce a real speed up in ReactOS.
For instance, with that patch, Winamp starts within seconds, instead of dozen
of minutes.
In most cases, it will bring ReactOS to performances it had before fixing
the huge leak in FastFAT (commit 94ead99
) without leaking the whole FS.
For now, due to regressions, this is only activated for files and not
for directories. Once it gets fixed, it will be enabled for both.
CORE-14826
CORE-14917
1111 lines
36 KiB
C
1111 lines
36 KiB
C
/*
|
|
* ReactOS kernel
|
|
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/filesystems/fastfat/create.c
|
|
* PURPOSE: VFAT Filesystem
|
|
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "vfat.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
VOID
|
|
vfat8Dot3ToString(
|
|
PFAT_DIR_ENTRY pEntry,
|
|
PUNICODE_STRING NameU)
|
|
{
|
|
OEM_STRING StringA;
|
|
USHORT Length;
|
|
CHAR cString[12];
|
|
|
|
RtlCopyMemory(cString, pEntry->ShortName, 11);
|
|
cString[11] = 0;
|
|
if (cString[0] == 0x05)
|
|
{
|
|
cString[0] = 0xe5;
|
|
}
|
|
|
|
StringA.Buffer = cString;
|
|
for (StringA.Length = 0;
|
|
StringA.Length < 8 && StringA.Buffer[StringA.Length] != ' ';
|
|
StringA.Length++);
|
|
StringA.MaximumLength = StringA.Length;
|
|
|
|
RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
|
|
|
|
if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_BASE))
|
|
{
|
|
RtlDowncaseUnicodeString(NameU, NameU, FALSE);
|
|
}
|
|
|
|
if (cString[8] != ' ')
|
|
{
|
|
Length = NameU->Length;
|
|
NameU->Buffer += Length / sizeof(WCHAR);
|
|
if (!FAT_ENTRY_VOLUME(pEntry))
|
|
{
|
|
Length += sizeof(WCHAR);
|
|
NameU->Buffer[0] = L'.';
|
|
NameU->Buffer++;
|
|
}
|
|
NameU->Length = 0;
|
|
NameU->MaximumLength -= Length;
|
|
|
|
StringA.Buffer = &cString[8];
|
|
for (StringA.Length = 0;
|
|
StringA.Length < 3 && StringA.Buffer[StringA.Length] != ' ';
|
|
StringA.Length++);
|
|
StringA.MaximumLength = StringA.Length;
|
|
RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
|
|
if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_EXT))
|
|
{
|
|
RtlDowncaseUnicodeString(NameU, NameU, FALSE);
|
|
}
|
|
NameU->Buffer -= Length / sizeof(WCHAR);
|
|
NameU->Length += Length;
|
|
NameU->MaximumLength += Length;
|
|
}
|
|
|
|
NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
|
|
DPRINT("'%wZ'\n", NameU);
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Find a file
|
|
*/
|
|
NTSTATUS
|
|
FindFile(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PVFATFCB Parent,
|
|
PUNICODE_STRING FileToFindU,
|
|
PVFAT_DIRENTRY_CONTEXT DirContext,
|
|
BOOLEAN First)
|
|
{
|
|
PWCHAR PathNameBuffer;
|
|
USHORT PathNameBufferLength;
|
|
NTSTATUS Status;
|
|
PVOID Context = NULL;
|
|
PVOID Page;
|
|
PVFATFCB rcFcb;
|
|
BOOLEAN Found;
|
|
UNICODE_STRING PathNameU;
|
|
UNICODE_STRING FileToFindUpcase;
|
|
BOOLEAN WildCard;
|
|
BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt);
|
|
|
|
DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n",
|
|
Parent, FileToFindU, DirContext->DirIndex);
|
|
DPRINT("FindFile: Path %wZ\n",&Parent->PathNameU);
|
|
|
|
PathNameBufferLength = LONGNAME_MAX_LENGTH * sizeof(WCHAR);
|
|
PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength + sizeof(WCHAR), TAG_VFAT);
|
|
if (!PathNameBuffer)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
PathNameU.Buffer = PathNameBuffer;
|
|
PathNameU.Length = 0;
|
|
PathNameU.MaximumLength = PathNameBufferLength;
|
|
|
|
DirContext->LongNameU.Length = 0;
|
|
DirContext->ShortNameU.Length = 0;
|
|
|
|
WildCard = FsRtlDoesNameContainWildCards(FileToFindU);
|
|
|
|
if (WildCard == FALSE)
|
|
{
|
|
/* if there is no '*?' in the search name, than look first for an existing fcb */
|
|
RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
|
|
if (!vfatFCBIsRoot(Parent))
|
|
{
|
|
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
|
|
PathNameU.Length += sizeof(WCHAR);
|
|
}
|
|
RtlAppendUnicodeStringToString(&PathNameU, FileToFindU);
|
|
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
|
|
rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
|
|
if (rcFcb)
|
|
{
|
|
ULONG startIndex = rcFcb->startIndex;
|
|
if (IsFatX && !vfatFCBIsRoot(Parent))
|
|
{
|
|
startIndex += 2;
|
|
}
|
|
if(startIndex >= DirContext->DirIndex)
|
|
{
|
|
RtlCopyUnicodeString(&DirContext->LongNameU, &rcFcb->LongNameU);
|
|
RtlCopyUnicodeString(&DirContext->ShortNameU, &rcFcb->ShortNameU);
|
|
RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
|
|
DirContext->StartIndex = rcFcb->startIndex;
|
|
DirContext->DirIndex = rcFcb->dirIndex;
|
|
DPRINT("FindFile: new Name %wZ, DirIndex %u (%u)\n",
|
|
&DirContext->LongNameU, DirContext->DirIndex, DirContext->StartIndex);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("FCB not found for %wZ\n", &PathNameU);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
vfatReleaseFCB(DeviceExt, rcFcb);
|
|
ExFreePool(PathNameBuffer);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* FsRtlIsNameInExpression need the searched string to be upcase,
|
|
* even if IgnoreCase is specified */
|
|
Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePool(PathNameBuffer);
|
|
return Status;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
Status = VfatGetNextDirEntry(DeviceExt, &Context, &Page, Parent, DirContext, First);
|
|
First = FALSE;
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
break;
|
|
}
|
|
if (ENTRY_VOLUME(IsFatX, &DirContext->DirEntry))
|
|
{
|
|
DirContext->DirIndex++;
|
|
continue;
|
|
}
|
|
if (DirContext->LongNameU.Length == 0 ||
|
|
DirContext->ShortNameU.Length == 0)
|
|
{
|
|
DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
|
|
if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
|
|
{
|
|
ASSERT(DirContext->LongNameU.Length != 0 &&
|
|
DirContext->ShortNameU.Length != 0);
|
|
}
|
|
DirContext->DirIndex++;
|
|
continue;
|
|
}
|
|
if (WildCard)
|
|
{
|
|
Found = FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->LongNameU, TRUE, NULL) ||
|
|
FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->ShortNameU, TRUE, NULL);
|
|
}
|
|
else
|
|
{
|
|
Found = FsRtlAreNamesEqual(&DirContext->LongNameU, FileToFindU, TRUE, NULL) ||
|
|
FsRtlAreNamesEqual(&DirContext->ShortNameU, FileToFindU, TRUE, NULL);
|
|
}
|
|
|
|
if (Found)
|
|
{
|
|
if (WildCard)
|
|
{
|
|
RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
|
|
if (!vfatFCBIsRoot(Parent))
|
|
{
|
|
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
|
|
PathNameU.Length += sizeof(WCHAR);
|
|
}
|
|
RtlAppendUnicodeStringToString(&PathNameU, &DirContext->LongNameU);
|
|
PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
|
|
rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
|
|
if (rcFcb != NULL)
|
|
{
|
|
RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
|
|
vfatReleaseFCB(DeviceExt, rcFcb);
|
|
}
|
|
}
|
|
DPRINT("%u\n", DirContext->LongNameU.Length);
|
|
DPRINT("FindFile: new Name %wZ, DirIndex %u\n",
|
|
&DirContext->LongNameU, DirContext->DirIndex);
|
|
|
|
if (Context)
|
|
{
|
|
CcUnpinData(Context);
|
|
}
|
|
RtlFreeUnicodeString(&FileToFindUpcase);
|
|
ExFreePool(PathNameBuffer);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
DirContext->DirIndex++;
|
|
}
|
|
|
|
if (Context)
|
|
{
|
|
CcUnpinData(Context);
|
|
}
|
|
|
|
RtlFreeUnicodeString(&FileToFindUpcase);
|
|
ExFreePool(PathNameBuffer);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Opens a file
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
VfatOpenFile(
|
|
PDEVICE_EXTENSION DeviceExt,
|
|
PUNICODE_STRING PathNameU,
|
|
PFILE_OBJECT FileObject,
|
|
ULONG RequestedDisposition,
|
|
ULONG RequestedOptions,
|
|
PVFATFCB *ParentFcb)
|
|
{
|
|
PVFATFCB Fcb;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("VfatOpenFile(%p, '%wZ', %p, %p)\n", DeviceExt, PathNameU, FileObject, ParentFcb);
|
|
|
|
if (FileObject->RelatedFileObject)
|
|
{
|
|
DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName);
|
|
|
|
*ParentFcb = FileObject->RelatedFileObject->FsContext;
|
|
}
|
|
else
|
|
{
|
|
*ParentFcb = NULL;
|
|
}
|
|
|
|
if (!DeviceExt->FatInfo.FixedMedia)
|
|
{
|
|
Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
|
|
IOCTL_DISK_CHECK_VERIFY,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Status %lx\n", Status);
|
|
*ParentFcb = NULL;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (*ParentFcb)
|
|
{
|
|
vfatGrabFCB(DeviceExt, *ParentFcb);
|
|
}
|
|
|
|
/* try first to find an existing FCB in memory */
|
|
DPRINT("Checking for existing FCB in memory\n");
|
|
|
|
Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT ("Could not make a new FCB, status: %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Fail, if we try to overwrite an existing directory */
|
|
if ((!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && vfatFCBIsDirectory(Fcb)) &&
|
|
(RequestedDisposition == FILE_OVERWRITE ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
|
RequestedDisposition == FILE_SUPERSEDE))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
if (BooleanFlagOn(Fcb->Flags, FCB_DELETE_PENDING))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
return STATUS_DELETE_PENDING;
|
|
}
|
|
|
|
/* Fail, if we try to overwrite a read-only file */
|
|
if (vfatFCBIsReadOnly(Fcb) &&
|
|
(RequestedDisposition == FILE_OVERWRITE ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (vfatFCBIsReadOnly(Fcb) &&
|
|
(RequestedOptions & FILE_DELETE_ON_CLOSE))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
|
|
if ((vfatFCBIsRoot(Fcb) ||
|
|
(Fcb->LongNameU.Length == sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.') ||
|
|
(Fcb->LongNameU.Length == 2 * sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.' && Fcb->LongNameU.Buffer[1] == L'.')) &&
|
|
BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
|
|
{
|
|
// we cannot delete a '.', '..' or the root directory
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
|
|
/* If that one was marked for closing, remove it */
|
|
if (BooleanFlagOn(Fcb->Flags, FCB_DELAYED_CLOSE))
|
|
{
|
|
BOOLEAN ConcurrentDeletion;
|
|
PVFAT_CLOSE_CONTEXT CloseContext;
|
|
|
|
/* Get the context */
|
|
CloseContext = Fcb->CloseContext;
|
|
/* Is someone already taking over? */
|
|
if (CloseContext != NULL)
|
|
{
|
|
ConcurrentDeletion = FALSE;
|
|
/* Lock list */
|
|
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
|
|
/* Check whether it was already removed, if not, do it */
|
|
if (!IsListEmpty(&CloseContext->CloseListEntry))
|
|
{
|
|
RemoveEntryList(&CloseContext->CloseListEntry);
|
|
--VfatGlobalData->CloseCount;
|
|
ConcurrentDeletion = TRUE;
|
|
}
|
|
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
|
|
|
|
/* It's not delayed anymore! */
|
|
ClearFlag(Fcb->Flags, FCB_DELAYED_CLOSE);
|
|
/* Release the extra reference (would have been removed by IRP_MJ_CLOSE) */
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
Fcb->CloseContext = NULL;
|
|
/* If no concurrent deletion, free work item */
|
|
if (!ConcurrentDeletion)
|
|
{
|
|
ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
|
|
}
|
|
}
|
|
|
|
DPRINT("Reusing delayed close FCB for %wZ\n", &Fcb->PathNameU);
|
|
}
|
|
|
|
DPRINT("Attaching FCB to fileObject\n");
|
|
Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, Fcb);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Create or open a file
|
|
*/
|
|
static NTSTATUS
|
|
VfatCreateFile(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION Stack;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
ULONG RequestedDisposition, RequestedOptions;
|
|
PVFATFCB pFcb = NULL;
|
|
PVFATFCB ParentFcb = NULL;
|
|
PVFATCCB pCcb = NULL;
|
|
PWCHAR c, last;
|
|
BOOLEAN PagingFileCreate;
|
|
BOOLEAN Dots;
|
|
BOOLEAN OpenTargetDir;
|
|
BOOLEAN TrailingBackslash;
|
|
UNICODE_STRING FileNameU;
|
|
UNICODE_STRING PathNameU;
|
|
ULONG Attributes;
|
|
|
|
/* Unpack the various parameters. */
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
|
|
RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
|
|
PagingFileCreate = BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE);
|
|
OpenTargetDir = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY);
|
|
|
|
FileObject = Stack->FileObject;
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID))
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Check their validity. */
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
|
|
RequestedDisposition == FILE_SUPERSEDE)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
|
|
BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Deny create if the volume is locked */
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* This a open operation for the volume itself */
|
|
if (FileObject->FileName.Length == 0 &&
|
|
(FileObject->RelatedFileObject == NULL ||
|
|
FileObject->RelatedFileObject->FsContext2 != NULL ||
|
|
FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
|
|
{
|
|
DPRINT("Volume opening\n");
|
|
|
|
if (RequestedDisposition != FILE_OPEN &&
|
|
RequestedDisposition != FILE_OPEN_IF)
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
|
|
(FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 == NULL || FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
|
|
{
|
|
return STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
|
|
if (OpenTargetDir)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
|
|
{
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
|
|
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
|
|
|
|
pFcb = DeviceExt->VolumeFcb;
|
|
|
|
if (pFcb->OpenHandleCount == 0)
|
|
{
|
|
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess);
|
|
}
|
|
else
|
|
{
|
|
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
|
|
DeviceExt->OpenHandleCount++;
|
|
pFcb->OpenHandleCount++;
|
|
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
|
|
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (FileObject->RelatedFileObject != NULL &&
|
|
FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)
|
|
{
|
|
ASSERT(FileObject->FileName.Length != 0);
|
|
return STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
/* Check for illegal characters and illegal dot sequences in the file name */
|
|
PathNameU = FileObject->FileName;
|
|
c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR);
|
|
last = c - 1;
|
|
|
|
Dots = TRUE;
|
|
while (c-- > PathNameU.Buffer)
|
|
{
|
|
if (*c == L'\\' || c == PathNameU.Buffer)
|
|
{
|
|
if (Dots && last > c)
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
if (*c == L'\\' && (c - 1) > PathNameU.Buffer &&
|
|
*(c - 1) == L'\\')
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
last = c - 1;
|
|
Dots = TRUE;
|
|
}
|
|
else if (*c != L'.')
|
|
{
|
|
Dots = FALSE;
|
|
}
|
|
|
|
if (*c != '\\' && vfatIsLongIllegal(*c))
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
}
|
|
|
|
/* Check if we try to open target directory of root dir */
|
|
if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
|
|
PathNameU.Buffer[0] == L'\\')
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
TrailingBackslash = FALSE;
|
|
if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
|
|
{
|
|
PathNameU.Length -= sizeof(WCHAR);
|
|
TrailingBackslash = TRUE;
|
|
}
|
|
|
|
if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
|
|
{
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
/* Try opening the file. */
|
|
if (!OpenTargetDir)
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
|
|
|
|
Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, RequestedOptions, &ParentFcb);
|
|
}
|
|
else
|
|
{
|
|
PVFATFCB TargetFcb;
|
|
LONG idx, FileNameLen;
|
|
|
|
vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
|
|
|
|
ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL;
|
|
if (ParentFcb)
|
|
{
|
|
vfatGrabFCB(DeviceExt, ParentFcb);
|
|
}
|
|
|
|
Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, TargetFcb);
|
|
Irp->IoStatus.Information = FILE_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
|
|
|
|
/* Skip trailing \ - if any */
|
|
if (PathNameU.Buffer[idx] == L'\\')
|
|
{
|
|
--idx;
|
|
PathNameU.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
/* Get file name */
|
|
while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
|
|
{
|
|
--idx;
|
|
}
|
|
|
|
if (idx > 0 || PathNameU.Buffer[0] == L'\\')
|
|
{
|
|
/* We don't want to include / in the name */
|
|
FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
|
|
|
|
/* Update FO just to keep file name */
|
|
/* Skip first slash */
|
|
++idx;
|
|
FileObject->FileName.Length = FileNameLen;
|
|
RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
|
|
#if 0
|
|
/* Terminate the string at the last backslash */
|
|
PathNameU.Buffer[idx + 1] = UNICODE_NULL;
|
|
PathNameU.Length = (idx + 1) * sizeof(WCHAR);
|
|
PathNameU.MaximumLength = PathNameU.Length + sizeof(WCHAR);
|
|
|
|
/* Update the file object as well */
|
|
FileObject->FileName.Length = PathNameU.Length;
|
|
FileObject->FileName.MaximumLength = PathNameU.MaximumLength;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* This is a relative open and we have only the filename, so open the parent directory
|
|
* It is in RelatedFileObject
|
|
*/
|
|
ASSERT(FileObject->RelatedFileObject != NULL);
|
|
|
|
/* No need to modify the FO, it already has the name */
|
|
}
|
|
|
|
/* We're done with opening! */
|
|
if (ParentFcb != NULL)
|
|
{
|
|
Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
pFcb = FileObject->FsContext;
|
|
ASSERT(pFcb == ParentFcb);
|
|
|
|
if (pFcb->OpenHandleCount == 0)
|
|
{
|
|
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess);
|
|
}
|
|
else
|
|
{
|
|
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
pCcb = FileObject->FsContext2;
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
|
|
{
|
|
pCcb->Flags |= CCB_DELETE_ON_CLOSE;
|
|
}
|
|
|
|
pFcb->OpenHandleCount++;
|
|
DeviceExt->OpenHandleCount++;
|
|
}
|
|
else if (ParentFcb != NULL)
|
|
{
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
|
|
}
|
|
else
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* If the directory containing the file to open doesn't exist then
|
|
* fail immediately
|
|
*/
|
|
if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
|
|
Status == STATUS_INVALID_PARAMETER ||
|
|
Status == STATUS_DELETE_PENDING ||
|
|
Status == STATUS_ACCESS_DENIED ||
|
|
Status == STATUS_OBJECT_NAME_COLLISION)
|
|
{
|
|
if (ParentFcb)
|
|
{
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
}
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && ParentFcb == NULL)
|
|
{
|
|
DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
|
|
Attributes = (Stack->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_DIRECTORY |
|
|
FILE_ATTRIBUTE_READONLY));
|
|
|
|
/* If the file open failed then create the required file */
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
if (RequestedDisposition == FILE_CREATE ||
|
|
RequestedDisposition == FILE_OPEN_IF ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
|
RequestedDisposition == FILE_SUPERSEDE)
|
|
{
|
|
if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
|
|
{
|
|
if (TrailingBackslash)
|
|
{
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
Attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
}
|
|
vfatSplitPathName(&PathNameU, NULL, &FileNameU);
|
|
Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
|
|
Attributes, NULL);
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
vfatReleaseFCB(DeviceExt, pFcb);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
|
|
Irp->IoStatus.Information = FILE_CREATED;
|
|
VfatSetAllocationSizeInformation(FileObject,
|
|
pFcb,
|
|
DeviceExt,
|
|
&Irp->Overlay.AllocationSize);
|
|
VfatSetExtendedAttributes(FileObject,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
Stack->Parameters.Create.EaLength);
|
|
|
|
if (PagingFileCreate)
|
|
{
|
|
pFcb->Flags |= FCB_IS_PAGE_FILE;
|
|
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ParentFcb)
|
|
{
|
|
vfatReleaseFCB(DeviceExt, ParentFcb);
|
|
}
|
|
|
|
pFcb = FileObject->FsContext;
|
|
|
|
/* Otherwise fail if the caller wanted to create a new file */
|
|
if (RequestedDisposition == FILE_CREATE)
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
Irp->IoStatus.Information = FILE_EXISTS;
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
if (pFcb->OpenHandleCount != 0)
|
|
{
|
|
Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check the file has the requested attributes
|
|
*/
|
|
if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) &&
|
|
vfatFCBIsDirectory(pFcb))
|
|
{
|
|
VfatCloseFile (DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
|
|
!vfatFCBIsDirectory(pFcb))
|
|
{
|
|
VfatCloseFile (DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
|
|
{
|
|
VfatCloseFile (DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
#ifndef USE_ROS_CC_AND_FS
|
|
if (!vfatFCBIsDirectory(pFcb))
|
|
{
|
|
if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) ||
|
|
RequestedDisposition == FILE_OVERWRITE ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
|
(RequestedOptions & FILE_DELETE_ON_CLOSE))
|
|
{
|
|
if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
|
|
{
|
|
DPRINT1("%wZ\n", &pFcb->PathNameU);
|
|
DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
|
|
RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
|
|
VfatCloseFile (DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE
|
|
: STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (PagingFileCreate)
|
|
{
|
|
/* FIXME:
|
|
* Do more checking for page files. It is possible,
|
|
* that the file was opened and closed previously
|
|
* as a normal cached file. In this case, the cache
|
|
* manager has referenced the fileobject and the fcb
|
|
* is held in memory. Try to remove the fileobject
|
|
* from cache manager and use the fcb.
|
|
*/
|
|
if (pFcb->RefCount > 1)
|
|
{
|
|
if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFcb->Flags |= FCB_IS_PAGE_FILE;
|
|
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (RequestedDisposition == FILE_OVERWRITE ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF ||
|
|
RequestedDisposition == FILE_SUPERSEDE)
|
|
{
|
|
if ((BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_HIDDEN) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_HIDDEN)) ||
|
|
(BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_SYSTEM) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_SYSTEM)))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!vfatFCBIsDirectory(pFcb))
|
|
{
|
|
LARGE_INTEGER SystemTime;
|
|
|
|
if (RequestedDisposition == FILE_SUPERSEDE)
|
|
{
|
|
*pFcb->Attributes = Attributes;
|
|
}
|
|
else
|
|
{
|
|
*pFcb->Attributes |= Attributes;
|
|
}
|
|
*pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
KeQuerySystemTime(&SystemTime);
|
|
if (vfatVolumeIsFatX(DeviceExt))
|
|
{
|
|
FsdSystemTimeToDosDateTime(DeviceExt,
|
|
&SystemTime, &pFcb->entry.FatX.UpdateDate,
|
|
&pFcb->entry.FatX.UpdateTime);
|
|
}
|
|
else
|
|
{
|
|
FsdSystemTimeToDosDateTime(DeviceExt,
|
|
&SystemTime, &pFcb->entry.Fat.UpdateDate,
|
|
&pFcb->entry.Fat.UpdateTime);
|
|
}
|
|
|
|
VfatUpdateEntry(DeviceExt, pFcb);
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
|
|
Status = VfatSetAllocationSizeInformation(FileObject,
|
|
pFcb,
|
|
DeviceExt,
|
|
&Irp->Overlay.AllocationSize);
|
|
ExReleaseResourceLite(&(pFcb->MainResource));
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
VfatCloseFile(DeviceExt, FileObject);
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (RequestedDisposition == FILE_SUPERSEDE)
|
|
{
|
|
Irp->IoStatus.Information = FILE_SUPERSEDED;
|
|
}
|
|
else if (RequestedDisposition == FILE_OVERWRITE ||
|
|
RequestedDisposition == FILE_OVERWRITE_IF)
|
|
{
|
|
Irp->IoStatus.Information = FILE_OVERWRITTEN;
|
|
}
|
|
else
|
|
{
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
}
|
|
}
|
|
|
|
if (pFcb->OpenHandleCount == 0)
|
|
{
|
|
IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
|
|
Stack->Parameters.Create.ShareAccess,
|
|
FileObject,
|
|
&pFcb->FCBShareAccess);
|
|
}
|
|
else
|
|
{
|
|
IoUpdateShareAccess(FileObject,
|
|
&pFcb->FCBShareAccess);
|
|
}
|
|
|
|
pCcb = FileObject->FsContext2;
|
|
if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
|
|
{
|
|
pCcb->Flags |= CCB_DELETE_ON_CLOSE;
|
|
}
|
|
|
|
if (Irp->IoStatus.Information == FILE_CREATED)
|
|
{
|
|
vfatReportChange(DeviceExt,
|
|
pFcb,
|
|
(vfatFCBIsDirectory(pFcb) ?
|
|
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
|
|
FILE_ACTION_ADDED);
|
|
}
|
|
else if (Irp->IoStatus.Information == FILE_OVERWRITTEN ||
|
|
Irp->IoStatus.Information == FILE_SUPERSEDED)
|
|
{
|
|
vfatReportChange(DeviceExt,
|
|
pFcb,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE,
|
|
FILE_ACTION_MODIFIED);
|
|
}
|
|
|
|
pFcb->OpenHandleCount++;
|
|
DeviceExt->OpenHandleCount++;
|
|
|
|
/* FIXME : test write access if requested */
|
|
|
|
/* FIXME: That is broken, we cannot reach this code path with failure */
|
|
ASSERT(NT_SUCCESS(Status));
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
|
|
}
|
|
else
|
|
{
|
|
vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Create or open a file
|
|
*/
|
|
NTSTATUS
|
|
VfatCreate(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(IrpContext);
|
|
|
|
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
|
|
{
|
|
/* DeviceObject represents FileSystem instead of logical volume */
|
|
DPRINT ("FsdCreate called with file system\n");
|
|
IrpContext->Irp->IoStatus.Information = FILE_OPENED;
|
|
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
|
|
Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
|
|
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
IrpContext->PriorityBoost = IO_DISK_INCREMENT;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|