930 lines
27 KiB
C
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;
|
|
}
|