2020-09-30 17:17:25 +02:00

930 lines
27 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
create.c
Abstract:
This module implements routines related to handling IRP_MJ_CREATE.
--*/
#include "udfx.h"
//
// Structure that wraps up all of the variables related to walk through a
// directory stream.
//
typedef struct _UDF_DIRECTORY_MAPPING {
PUDF_VOLUME_EXTENSION VolumeExtension;
PIRP Irp;
PUDF_FCB DirectoryFcb;
ULONG LogicalSectorNumber;
ULONG FileBytesRemaining;
ULONG CacheBytesRemaining;
PVOID CacheBuffer;
PUCHAR CurrentBuffer;
} UDF_DIRECTORY_MAPPING, *PUDF_DIRECTORY_MAPPING;
NTSTATUS
UdfxMapNextDirectorySector(
IN OUT PUDF_DIRECTORY_MAPPING DirectoryMapping
)
/*++
Routine Description:
This routine maps the next directory sector into the cache using the state
in the supplied mapping structure.
Arguments:
DirectoryMapping - Specifies a structure that describes the current sector
mapping and how to map the next sector.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PUDF_VOLUME_EXTENSION VolumeExtension;
PUCHAR LocalCurrentBuffer;
ULONG LocalCacheBytesRemaining;
//
// If another sector has already been mapped, then unmap it now.
//
if (DirectoryMapping->CacheBuffer != NULL) {
FscUnmapBuffer(DirectoryMapping->CacheBuffer);
DirectoryMapping->CacheBuffer = NULL;
}
//
// If there aren't any more bytes remaining in the directory stream, then
// bail out now.
//
if (DirectoryMapping->FileBytesRemaining == 0) {
return STATUS_OBJECT_NAME_NOT_FOUND;
}
//
// Map the directory sector into the cache.
//
VolumeExtension = DirectoryMapping->VolumeExtension;
status = UdfxMapLogicalSector(VolumeExtension, DirectoryMapping->Irp,
DirectoryMapping->LogicalSectorNumber, &DirectoryMapping->CacheBuffer);
if (!NT_SUCCESS(status)) {
return status;
}
DirectoryMapping->LogicalSectorNumber++;
LocalCurrentBuffer = (PUCHAR)DirectoryMapping->CacheBuffer;
//
// If the data is part of the file descriptor, then adjust the current
// buffer to point to the real start of the data. The number of remaining
// bytes will be adjusted below.
//
if (UdfxIsFlagSet(DirectoryMapping->DirectoryFcb->Flags, UDF_FCB_EMBEDDED_DATA)) {
LocalCurrentBuffer += DirectoryMapping->DirectoryFcb->EmbeddedDataOffset;
ASSERT(DirectoryMapping->FileBytesRemaining < UDF_CD_SECTOR_SIZE);
}
//
// Compute the number of bytes remaining in the file and in this cache
// buffer taking into account whether or not this is the last sector of the
// directory stream.
//
if (DirectoryMapping->FileBytesRemaining > UDF_CD_SECTOR_SIZE) {
LocalCacheBytesRemaining = UDF_CD_SECTOR_SIZE;
DirectoryMapping->FileBytesRemaining -= UDF_CD_SECTOR_SIZE;
} else {
LocalCacheBytesRemaining = DirectoryMapping->FileBytesRemaining;
DirectoryMapping->FileBytesRemaining = 0;
}
//
// If we're to skip over bytes from a descriptor that crosses a sector
// boundary, then do that adjustment here.
//
LocalCurrentBuffer -= DirectoryMapping->CacheBytesRemaining;
LocalCacheBytesRemaining += DirectoryMapping->CacheBytesRemaining;
//
// Return the results back to the caller.
//
DirectoryMapping->CurrentBuffer = LocalCurrentBuffer;
DirectoryMapping->CacheBytesRemaining = LocalCacheBytesRemaining;
return STATUS_SUCCESS;
}
VOID
UdfxCopyPartialDirectoryBuffer(
IN OUT PUDF_DIRECTORY_MAPPING DirectoryMapping,
OUT PVOID Destination,
IN PVOID Source,
IN SIZE_T Length
)
/*++
Routine Description:
This routine is a wrapper for RtlCopyMemory that asserts that the arguments
are valid cache pointers in a debug build and reduces the code bloat in a
retail build caused by RtlCopyMemory inlining.
Arguments:
DirectoryMapping - Specifies a structure that describes the current sector
mapping and how to map the next sector.
Destination - Specifies the destination of the move.
Source - Specifies the memory to be copied.
Length - Specifies the number of bytes to be copied.
Return Value:
Status of operation.
--*/
{
ASSERT((PUCHAR)Source >= (PUCHAR)DirectoryMapping->CacheBuffer);
ASSERT((PUCHAR)Source < (PUCHAR)DirectoryMapping->CacheBuffer +
UDF_CD_SECTOR_SIZE);
ASSERT((PUCHAR)Source + Length <= (PUCHAR)DirectoryMapping->CacheBuffer +
UDF_CD_SECTOR_SIZE);
ASSERT(Length <= UDF_CD_SECTOR_SIZE);
RtlCopyMemory(Destination, Source, Length);
}
NTSTATUS
UdfxLookupElementNameInDirectory(
IN PUDF_VOLUME_EXTENSION VolumeExtension,
IN PIRP Irp,
IN PUDF_FCB DirectoryFcb,
IN POBJECT_STRING ElementName,
OUT PLONGAD ElementIcb
)
/*++
Routine Description:
This routine looks up the supplied file name in the supplied directory
file control block.
Arguments:
VolumeExtension - Specifies the extension that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
DirectoryFcb - Specifies the file control block that describes where to
read the directory stream from.
ElementName - Specifies the name to search for in the directory.
ElementIcb - Specifies the buffer to receive the extent of the found file.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
ULONG ExpectedFileID8BitLength;
UDF_DIRECTORY_MAPPING DirectoryMapping;
union {
NSR_FID FileIdentifierDescriptor;
UCHAR FileID[256];
} Stack;
ULONG PartialStackBufferBytes;
PNSR_FID FileIdentifierDescriptor;
ULONG FileIdentifierDescriptorLength;
ULONG FileIDLength;
ULONG FileIDStartingOffset;
PUCHAR FileID;
ASSERT(UdfxIsFlagSet(DirectoryFcb->Flags, UDF_FCB_DIRECTORY));
//
// Compute the expected lengths for a file identifier that will match the
// supplied file name that we're searching for. We only care about 8-bit
// encodings.
//
ExpectedFileID8BitLength = sizeof(UCHAR) + ElementName->Length / sizeof(OCHAR);
//
// Initialize the directory mapping structure.
//
DirectoryMapping.VolumeExtension = VolumeExtension;
DirectoryMapping.Irp = Irp;
DirectoryMapping.DirectoryFcb = DirectoryFcb;
DirectoryMapping.LogicalSectorNumber = DirectoryFcb->AllocationSectorStart;
DirectoryMapping.FileBytesRemaining = DirectoryFcb->FileSize.LowPart;
DirectoryMapping.CacheBytesRemaining = 0;
DirectoryMapping.CacheBuffer = NULL;
DirectoryMapping.CurrentBuffer = NULL;
//
// Process the directory stream.
//
for (;;) {
//
// If we're out of bytes in the current cache buffer, then map in the
// next sector.
//
if ((LONG)DirectoryMapping.CacheBytesRemaining <= 0) {
status = UdfxMapNextDirectorySector(&DirectoryMapping);
if (!NT_SUCCESS(status)) {
break;
}
}
//
// If the current cache buffer doesn't have enough bytes to contain a
// full descriptor, then we'll have to buffer the descriptor on the
// stack and carefully cross sector boundaries.
//
if (DirectoryMapping.CacheBytesRemaining < ISONsrFidConstantSize) {
//
// If the bytes from this cache buffer and what remains in the file
// won't be enough to contain a full descriptor, then bail out now.
//
if (DirectoryMapping.CacheBytesRemaining +
DirectoryMapping.FileBytesRemaining < ISONsrFidConstantSize) {
status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
//
// Copy the first part of the descriptor to the stack.
//
PartialStackBufferBytes = DirectoryMapping.CacheBytesRemaining;
UdfxCopyPartialDirectoryBuffer(&DirectoryMapping,
&Stack.FileIdentifierDescriptor, DirectoryMapping.CurrentBuffer,
PartialStackBufferBytes);
//
// Map in the next sector. Note that on return, the CurrentBuffer
// CurrentBuffer and CacheBytesRemaining fields have been adjusted
// adjusted to point back in memory as if the previous sector was
// virtually mapped in order to make the below math easier.
//
status = UdfxMapNextDirectorySector(&DirectoryMapping);
if (!NT_SUCCESS(status)) {
break;
}
//
// Copy the last part of the descriptor to the stack.
//
UdfxCopyPartialDirectoryBuffer(&DirectoryMapping,
(PUCHAR)&Stack.FileIdentifierDescriptor + PartialStackBufferBytes,
DirectoryMapping.CurrentBuffer + PartialStackBufferBytes,
ISONsrFidConstantSize - PartialStackBufferBytes);
FileIdentifierDescriptor = &Stack.FileIdentifierDescriptor;
} else {
//
// There's enough bytes in the current buffer to contain at least
// the header, so point directly into the cache buffer.
//
FileIdentifierDescriptor = (PNSR_FID)DirectoryMapping.CurrentBuffer;
}
//
// Validate that the descriptor header looks valid and that its size
// doesn't exceed a sector as specified by UDF or the number of
// remaining bytes in the directory stream. We can't use
// UdfxVerifyDescriptor here because we don't have a contiguous
// descriptor in memory.
//
FileIdentifierDescriptorLength = ISONsrFidSize(FileIdentifierDescriptor);
if ((FileIdentifierDescriptor->Destag.Version != DESTAG_VER_CURRENT) ||
(FileIdentifierDescriptor->Destag.Ident != DESTAG_ID_NSR_FID) ||
(FileIdentifierDescriptorLength > UDF_CD_SECTOR_SIZE) ||
(FileIdentifierDescriptorLength > DirectoryMapping.FileBytesRemaining +
(ULONG)DirectoryMapping.CacheBytesRemaining)) {
UdfxDbgPrint(("UDFX: file identifier descriptor doesn't verify\n"));
status = STATUS_DISK_CORRUPT_ERROR;
break;
}
FileIDLength = FileIdentifierDescriptor->FileIDLen;
//
// Only bother checking the file identifier descriptor if the file
// identifier length matches the length of the element name we're
// looking for.
//
if (FileIDLength == ExpectedFileID8BitLength) {
//
// Fill in the output buffer now since we'll shortly lose access to
// the file identifier descriptor.
//
*ElementIcb = FileIdentifierDescriptor->Icb;
//
// Compute the byte offset of the file identifier.
//
FileIDStartingOffset = ISONsrFidConstantSize +
FileIdentifierDescriptor->ImpUseLen;
//
// The below code can cause the current sector buffer to become
// invalid, so we can no longer safely touch the
// FileIdentifierDescriptor.
//
FileIdentifierDescriptor = NULL;
//
// If the current cache buffer doesn't have enough bytes to contain
// the string, then we'll have to buffer the string on the stack and
// carefully cross sector boundaries.
//
if (DirectoryMapping.CacheBytesRemaining < FileIDStartingOffset +
FileIDLength) {
//
// Check if any of the string is inside the current cache
// buffer.
//
if (DirectoryMapping.CacheBytesRemaining > FileIDStartingOffset) {
//
// Copy the first part of the string to the stack.
//
PartialStackBufferBytes =
DirectoryMapping.CacheBytesRemaining - FileIDStartingOffset;
UdfxCopyPartialDirectoryBuffer(&DirectoryMapping,
&Stack.FileID, DirectoryMapping.CurrentBuffer +
FileIDStartingOffset, PartialStackBufferBytes);
} else {
PartialStackBufferBytes = 0;
}
//
// Map in the next sector. Note that on return, the
// CurrentBuffer and CacheBytesRemaining fields have been
// adjusted to point back in memory as if the previous sector
// was virtually mapped in order to make the below math easier.
//
status = UdfxMapNextDirectorySector(&DirectoryMapping);
if (!NT_SUCCESS(status)) {
break;
}
//
// Copy the last part of the string to the stack.
//
UdfxCopyPartialDirectoryBuffer(&DirectoryMapping,
Stack.FileID + PartialStackBufferBytes,
DirectoryMapping.CurrentBuffer + FileIDStartingOffset +
PartialStackBufferBytes, FileIDLength -
PartialStackBufferBytes);
FileID = Stack.FileID;
} else {
//
// There's enough bytes in the current buffer to contain all of
// the string, so point directly into the cache.
//
FileID = DirectoryMapping.CurrentBuffer + FileIDStartingOffset;
}
if (UdfxEqualOSTACS0StringAndObjectString(FileID, FileIDLength,
ElementName)) {
status = STATUS_SUCCESS;
break;
}
}
//
// Advance to the next descriptor.
//
DirectoryMapping.CacheBytesRemaining -= FileIdentifierDescriptorLength;
DirectoryMapping.CurrentBuffer += FileIdentifierDescriptorLength;
}
if (DirectoryMapping.CacheBuffer != NULL) {
FscUnmapBuffer(DirectoryMapping.CacheBuffer);
}
return status;
}
NTSTATUS
UdfxFsdCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O manager to handle IRP_MJ_CREATE requests.
Arguments:
DeviceObject - Specifies the device object that the I/O request is for.
Irp - Specifies the packet that describes the I/O request.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PUDF_VOLUME_EXTENSION VolumeExtension;
PIO_STACK_LOCATION IrpSp;
ACCESS_MASK DesiredAccess;
ULONG CreateOptions;
PFILE_OBJECT FileObject;
OBJECT_STRING RemainingName;
PFILE_OBJECT RelatedFileObject;
ULONG CreateDisposition;
PUDF_FCB CurrentFcb;
SHARE_ACCESS ShareAccess;
BOOLEAN TrailingBackslash;
BOOLEAN CreateFcbCalled;
OBJECT_STRING ElementName;
PUDF_FCB FoundOrNewFcb;
LONGAD ElementIcb;
VolumeExtension = (PUDF_VOLUME_EXTENSION)DeviceObject->DeviceExtension;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
DesiredAccess = IrpSp->Parameters.Create.DesiredAccess;
CreateOptions = IrpSp->Parameters.Create.Options;
FileObject = IrpSp->FileObject;
RemainingName = *IrpSp->Parameters.Create.RemainingName;
RelatedFileObject = FileObject->RelatedFileObject;
//
// Synchronize access to the file control block lists by acquiring the
// global mutex.
//
UdfxAcquireGlobalMutexExclusive();
//
// Check if the volume has been dismounted.
//
if (VolumeExtension->Dismounted) {
status = STATUS_VOLUME_DISMOUNTED;
goto CleanupAndExit;
}
//
// Don't allow the target directory to be opened. This is only used by the
// I/O manager when it wants to rename a file across a directory on the same
// volume.
//
if (UdfxIsFlagSet(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY)) {
status = STATUS_ACCESS_DENIED;
goto CleanupAndExit;
}
//
// Check the desired access mask to make sure that no write rights are being
// requested.
//
if (UdfxIsFlagSet(DesiredAccess, FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA |
FILE_WRITE_EA | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY |
FILE_APPEND_DATA | FILE_DELETE_CHILD | DELETE | WRITE_DAC)) {
status = STATUS_ACCESS_DENIED;
goto CleanupAndExit;
}
//
// Don't allow a file to be opened based on its file ID.
//
if (UdfxIsFlagSet(CreateOptions, FILE_OPEN_BY_FILE_ID)) {
status = STATUS_NOT_IMPLEMENTED;
goto CleanupAndExit;
}
//
// Verify that the create disposition is for open access only.
//
CreateDisposition = (CreateOptions >> 24) & 0xFF;
if ((CreateDisposition != FILE_OPEN) && (CreateDisposition != FILE_OPEN_IF)) {
status = STATUS_ACCESS_DENIED;
goto CleanupAndExit;
}
//
// Determine whether we parse the file name from the root of the volume or
// from a subdirectory by looking at RelatedFileObject.
//
if (RelatedFileObject != NULL) {
//
// Grab the file control block out of the related file object.
//
CurrentFcb = (PUDF_FCB)RelatedFileObject->FsContext;
//
// Verify that the related file object is really a directory object.
// Note that the file control block could be NULL if the file object was
// opened as a result of a direct device open in the I/O manager.
//
if ((CurrentFcb == NULL) ||
UdfxIsFlagClear(CurrentFcb->Flags, UDF_FCB_DIRECTORY)) {
status = STATUS_INVALID_PARAMETER;
goto CleanupAndExit;
}
//
// Check if we're supposed to open the related directory.
//
if (RemainingName.Length == 0) {
CurrentFcb->ReferenceCount++;
status = STATUS_SUCCESS;
goto OpenStartDirectoryFcb;
}
//
// Verify that this is not an absolute path.
//
if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
status = STATUS_OBJECT_NAME_INVALID;
goto CleanupAndExit;
}
} else {
//
// Check if we're supposed to open the physical volume.
//
if (RemainingName.Length == 0) {
CurrentFcb = VolumeExtension->VolumeFcb;
//
// The caller shouldn't be expecting to see a directory file.
//
if (UdfxIsFlagSet(CreateOptions, FILE_DIRECTORY_FILE)) {
status = STATUS_NOT_A_DIRECTORY;
goto CleanupAndExit;
}
//
// Physical volume access is always non-cached. Mark the file
// object as non-cached so that the I/O manager enforces alignment
// requirements.
//
FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING;
CurrentFcb->ReferenceCount++;
goto InitializeFileObject;
}
//
// Start searching relative to the root directory.
//
CurrentFcb = VolumeExtension->RootDirectoryFcb;
//
// Verify that this is an absolute path.
//
if (RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR) {
status = STATUS_OBJECT_NAME_INVALID;
goto CleanupAndExit;
}
//
// Check if we're supposed to open the root directory.
//
if (RemainingName.Length == sizeof(OCHAR)) {
CurrentFcb->ReferenceCount++;
status = STATUS_SUCCESS;
goto OpenStartDirectoryFcb;
}
}
//
// Check if the file name ends in a backslash. If so, strip it off and set
// a flag so that we can later verify that the target file is a directory.
//
// We've already checked for an empty file name or a file name that consists
// of a single backslash above, so we know that before and after this check
// that the remaining name will still have some characters in it.
//
ASSERT(RemainingName.Length > 0);
if (RemainingName.Buffer[(RemainingName.Length / sizeof(OCHAR)) - 1] ==
OBJ_NAME_PATH_SEPARATOR) {
RemainingName.Length -= sizeof(OCHAR);
TrailingBackslash = TRUE;
} else {
TrailingBackslash = FALSE;
}
ASSERT(RemainingName.Length > 0);
//
// Process the file name. At this point, we're only walking the open file
// control block list.
//
for (;;) {
//
// Pull off the next element of the file name.
//
ObDissectName(RemainingName, &ElementName, &RemainingName);
//
// Verify that there aren't multiple backslashes in the name.
//
if ((RemainingName.Length != 0) && (RemainingName.Buffer[0] ==
OBJ_NAME_PATH_SEPARATOR)) {
status = STATUS_OBJECT_NAME_INVALID;
break;
}
//
// Check if a file control block already exists for this file name.
//
if (!UdfxFindOpenChildFcb(CurrentFcb, &ElementName, &FoundOrNewFcb)) {
break;
}
CurrentFcb = FoundOrNewFcb;
//
// If we have consumed the entire name, then the file is already open.
// Bump up the reference count and skip past the on-disk search loop.
//
if (RemainingName.Length == 0) {
CurrentFcb->ReferenceCount++;
status = STATUS_SUCCESS;
goto OpenCurrentFcb;
}
}
//
// Continue processing the file name. At this point, we're searching
// directory streams for the requested file.
//
CreateFcbCalled = FALSE;
do {
//
// On the first iteration of the loop, we've already dissected the name
// we're looking for so don't dissect another piece of the name.
//
if (CreateFcbCalled) {
//
// Pull off the next element of the file name.
//
ObDissectName(RemainingName, &ElementName, &RemainingName);
//
// Verify that there aren't multiple backslashes in the name.
//
if ((RemainingName.Length != 0) && (RemainingName.Buffer[0] ==
OBJ_NAME_PATH_SEPARATOR)) {
status = STATUS_OBJECT_NAME_INVALID;
break;
}
}
//
// Lookup the element in the directory.
//
status = UdfxLookupElementNameInDirectory(VolumeExtension, Irp,
CurrentFcb, &ElementName, &ElementIcb);
if (!NT_SUCCESS(status)) {
//
// If we failed to find the element and there's more of a path name
// to process, then convert the error from "file not found" to
// "path not found".
//
if ((status == STATUS_OBJECT_NAME_NOT_FOUND) &&
(RemainingName.Length != 0)) {
status = STATUS_OBJECT_PATH_NOT_FOUND;
}
break;
}
//
// Create a file control block for the file we found.
//
status = UdfxCreateFcbFromFileEntry(VolumeExtension, Irp, &ElementIcb,
CurrentFcb, &ElementName, &FoundOrNewFcb);
if (!NT_SUCCESS(status)) {
break;
}
//
// If this is the second or greater iteration of this loop, then we want
// to release the reference to the parent directory from FatxCreateFcb.
// The parent directory already has a reference count of one from when
// we created that file control block.
//
if (CreateFcbCalled) {
ASSERT(CurrentFcb->ReferenceCount >= 2);
CurrentFcb->ReferenceCount--;
}
CreateFcbCalled = TRUE;
CurrentFcb = FoundOrNewFcb;
//
// If there's still more of a path name to process, then the file that
// we found had better be a directory.
//
if ((RemainingName.Length != 0) &&
UdfxIsFlagClear(CurrentFcb->Flags, UDF_FCB_DIRECTORY)) {
status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
} while (RemainingName.Length != 0);
//
// If we failed to open the file, then before bailing out, we may need to
// dereference the current file control block. If we haven't created any
// file control blocks, then there's no file control blocks to clean up.
//
if (!NT_SUCCESS(status)) {
if (CreateFcbCalled) {
UdfxDereferenceFcb(CurrentFcb);
}
goto CleanupAndExit;
}
//
// If the caller is expecting to open only a file or directory file, then
// verify that the file type matches.
//
OpenCurrentFcb:
if (UdfxIsFlagSet(CurrentFcb->Flags, UDF_FCB_DIRECTORY)) {
OpenStartDirectoryFcb:
if (UdfxIsFlagSet(CreateOptions, FILE_NON_DIRECTORY_FILE)) {
status = STATUS_FILE_IS_A_DIRECTORY;
}
} else {
if (TrailingBackslash ||
UdfxIsFlagSet(CreateOptions, FILE_DIRECTORY_FILE)) {
status = STATUS_NOT_A_DIRECTORY;
}
}
//
// If the resulting file looks acceptable, then fill out the file object
// and return success.
//
if (NT_SUCCESS(status)) {
InitializeFileObject:
//
// We don't care about sharing modes for this file system.
//
IoSetShareAccess(DesiredAccess, 0, FileObject, &ShareAccess);
//
// Fill in the file object with the file control block that we
// created.
//
FileObject->FsContext = CurrentFcb;
FileObject->FsContext2 = NULL;
VolumeExtension->FileObjectCount++;
//
// Indicate to the caller that we opened the file as opposed to
// creating or overwriting the file.
//
Irp->IoStatus.Information = FILE_OPENED;
status = STATUS_SUCCESS;
goto CleanupAndExit;
}
//
// If we reach this point, then we're going to be failing the call so
// cleanup any file control block we're still holding on to.
//
UdfxDereferenceFcb(CurrentFcb);
CleanupAndExit:
UdfxReleaseGlobalMutex();
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}