2921 lines
82 KiB
C
2921 lines
82 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Create.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File Create routine for Cdfs called by the
|
||
Fsd/Fsp dispatch routines.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Brian Andrew [BrianAn] 01-July-1995
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "CdProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (CDFS_BUG_CHECK_CREATE)
|
||
|
||
//
|
||
// Local support routines
|
||
//
|
||
|
||
NTSTATUS
|
||
CdNormalizeFileNames (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN OpenByFileId,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN TYPE_OF_OPEN RelatedTypeOfOpen,
|
||
IN PCCB RelatedCcb OPTIONAL,
|
||
IN PUNICODE_STRING RelatedFileName OPTIONAL,
|
||
IN OUT PUNICODE_STRING FileName,
|
||
IN OUT PCD_NAME RemainingName
|
||
);
|
||
|
||
NTSTATUS
|
||
CdOpenByFileId (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb
|
||
);
|
||
|
||
NTSTATUS
|
||
CdOpenExistingFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
CdOpenDirectoryFromPathEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PCD_NAME DirName,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN PPATH_ENTRY PathEntry,
|
||
IN BOOLEAN PerformUserOpen,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
CdOpenFileFromFileContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PCD_NAME FileName,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN PFILE_ENUM_CONTEXT FileContext,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
CdCompleteFcbOpen (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN ULONG UserCcbFlags,
|
||
IN ACCESS_MASK DesiredAccess
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, CdCommonCreate)
|
||
#pragma alloc_text(PAGE, CdCompleteFcbOpen)
|
||
#pragma alloc_text(PAGE, CdNormalizeFileNames)
|
||
#pragma alloc_text(PAGE, CdOpenByFileId)
|
||
#pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry)
|
||
#pragma alloc_text(PAGE, CdOpenExistingFcb)
|
||
#pragma alloc_text(PAGE, CdOpenFileFromFileContext)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
CdCommonCreate (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for opening a file called by both the
|
||
Fsp and Fsd threads.
|
||
|
||
The file can be opened either by name or by file Id either with or without
|
||
a relative name. The file name field in the file object passed to this routine
|
||
contains either a unicode string or a 64 bit value which is the file Id.
|
||
If this is not a Joliet disk then we will convert the unicode name to
|
||
an Oem string in this routine. If there is a related file object with
|
||
a name then we will already have converted that name to Oem.
|
||
|
||
We will store the full name for the file in the file object on a successful
|
||
open. We will allocate a larger buffer if necessary and combine the
|
||
related and file object names. The only exception is the relative open
|
||
when the related file object is for an OpenByFileId file. If we need to
|
||
allocate a buffer for a case insensitive name then we allocate it at
|
||
the tail of the buffer we will store into the file object. The upcased
|
||
portion will begin immediately after the name defined by the FileName
|
||
in the file object.
|
||
|
||
Once we have the full name in the file object we don't want to split the
|
||
name in the event of a retry. We use a flag in the IrpContext to indicate
|
||
that the name has been split.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - This is the status from this open operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
PFILE_OBJECT FileObject;
|
||
|
||
COMPOUND_PATH_ENTRY CompoundPathEntry;
|
||
BOOLEAN CleanupCompoundPathEntry = FALSE;
|
||
|
||
FILE_ENUM_CONTEXT FileContext;
|
||
BOOLEAN CleanupFileContext = FALSE;
|
||
BOOLEAN FoundEntry;
|
||
|
||
PVCB Vcb;
|
||
|
||
BOOLEAN OpenByFileId;
|
||
BOOLEAN IgnoreCase;
|
||
ULONG CreateDisposition;
|
||
|
||
BOOLEAN ShortNameMatch;
|
||
ULONG ShortNameDirentOffset;
|
||
|
||
BOOLEAN VolumeOpen = FALSE;
|
||
|
||
//
|
||
// We will be acquiring and releasing file Fcb's as we move down the
|
||
// directory tree during opens. At any time we need to know the deepest
|
||
// point we have traversed down in the tree in case we need to cleanup
|
||
// any structures created here.
|
||
//
|
||
// CurrentFcb - represents this point. If non-null it means we have
|
||
// acquired it and need to release it in finally clause.
|
||
//
|
||
// NextFcb - represents the NextFcb to walk to but haven't acquired yet.
|
||
//
|
||
|
||
TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject;
|
||
PFILE_OBJECT RelatedFileObject;
|
||
PCCB RelatedCcb = NULL;
|
||
|
||
PFCB NextFcb;
|
||
PFCB CurrentFcb = NULL;
|
||
|
||
//
|
||
// During the open we need to combine the related file object name
|
||
// with the remaining name. We also may need to upcase the file name
|
||
// in order to do a case-insensitive name comparison. We also need
|
||
// to restore the name in the file object in the event that we retry
|
||
// the request. We use the following string variables to manage the
|
||
// name. We will can put these strings into either Unicode or Ansi
|
||
// form.
|
||
//
|
||
// FileName - Pointer to name as currently stored in the file
|
||
// object. We store the full name into the file object early in
|
||
// the open operation.
|
||
//
|
||
// RelatedFileName - Pointer to the name in the related file object.
|
||
//
|
||
// RemainingName - String containing remaining name to parse.
|
||
//
|
||
// MatchingName - Address of name structure in FileContext which matched.
|
||
// We need this to know whether we matched the long or short name.
|
||
//
|
||
|
||
PUNICODE_STRING FileName;
|
||
PUNICODE_STRING RelatedFileName = NULL;
|
||
|
||
CD_NAME RemainingName;
|
||
CD_NAME FinalName;
|
||
PCD_NAME MatchingName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If we were called with our file system device object instead of a
|
||
// volume device object, just complete this request with STATUS_SUCCESS.
|
||
//
|
||
|
||
if (IrpContext->Vcb == NULL) {
|
||
|
||
CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Get create parameters from the Irp.
|
||
//
|
||
|
||
OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID );
|
||
IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE );
|
||
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
||
|
||
//
|
||
// Do some preliminary checks to make sure the operation is supported.
|
||
// We fail in the following cases immediately.
|
||
//
|
||
// - Open a paging file.
|
||
// - Open a target directory.
|
||
// - Open a file with Eas.
|
||
// - Create a file.
|
||
//
|
||
|
||
if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) ||
|
||
(IrpSp->Parameters.Create.EaLength != 0) ||
|
||
(CreateDisposition == FILE_CREATE)) {
|
||
|
||
CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Copy the Vcb to a local. Assume the starting directory is the root.
|
||
//
|
||
|
||
Vcb = IrpContext->Vcb;
|
||
NextFcb = Vcb->RootIndexFcb;
|
||
|
||
//
|
||
// Reference our input parameters to make things easier
|
||
//
|
||
|
||
FileObject = IrpSp->FileObject;
|
||
RelatedFileObject = NULL;
|
||
|
||
FileName = &FileObject->FileName;
|
||
|
||
//
|
||
// Set up the file object's Vpb pointer in case anything happens.
|
||
// This will allow us to get a reasonable pop-up.
|
||
//
|
||
|
||
if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) {
|
||
|
||
RelatedFileObject = FileObject->RelatedFileObject;
|
||
FileObject->Vpb = RelatedFileObject->Vpb;
|
||
|
||
RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb );
|
||
|
||
//
|
||
// Fail the request if this is not a user file object.
|
||
//
|
||
|
||
if (RelatedTypeOfOpen < UserVolumeOpen) {
|
||
|
||
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Remember the name in the related file object.
|
||
//
|
||
|
||
RelatedFileName = &RelatedFileObject->FileName;
|
||
}
|
||
|
||
//
|
||
// If we haven't initialized the names then make sure the strings are valid.
|
||
// If this an OpenByFileId then verify the file id buffer.
|
||
//
|
||
// After this routine returns we know that the full name is in the
|
||
// FileName buffer and the buffer will hold the upcased portion
|
||
// of the name yet to parse immediately after the full name in the
|
||
// buffer. Any trailing backslash has been removed and the flag
|
||
// in the IrpContext will indicate whether we removed the
|
||
// backslash.
|
||
//
|
||
|
||
Status = CdNormalizeFileNames( IrpContext,
|
||
Vcb,
|
||
OpenByFileId,
|
||
IgnoreCase,
|
||
RelatedTypeOfOpen,
|
||
RelatedCcb,
|
||
RelatedFileName,
|
||
FileName,
|
||
&RemainingName );
|
||
|
||
//
|
||
// Return the error code if not successful.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
CdCompleteRequest( IrpContext, Irp, Status );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// We want to acquire the Vcb. Exclusively for a volume open, shared otherwise.
|
||
// The file name is empty for a volume open.
|
||
//
|
||
|
||
if ((FileName->Length == 0) &&
|
||
(RelatedTypeOfOpen <= UserVolumeOpen) &&
|
||
!OpenByFileId) {
|
||
|
||
VolumeOpen = TRUE;
|
||
CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
|
||
|
||
} else {
|
||
|
||
CdAcquireVcbShared( IrpContext, Vcb, FALSE );
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Verify that the Vcb is not in an unusable condition. This routine
|
||
// will raise if not usable.
|
||
//
|
||
|
||
CdVerifyVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// If the Vcb is locked then we cannot open another file
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If we are opening this file by FileId then process this immediately
|
||
// and exit.
|
||
//
|
||
|
||
if (OpenByFileId) {
|
||
|
||
//
|
||
// We only allow Dasd opens of audio disks. Fail this request at
|
||
// this point.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) {
|
||
|
||
try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// Make sure we can wait for this request.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
try_return( Status = CdOpenByFileId( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb ));
|
||
}
|
||
|
||
//
|
||
// If we are opening this volume Dasd then process this immediately
|
||
// and exit.
|
||
//
|
||
|
||
if (VolumeOpen) {
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If they wanted to open a directory, surprise.
|
||
//
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// Acquire the Fcb first.
|
||
//
|
||
|
||
CurrentFcb = Vcb->VolumeDasdFcb;
|
||
CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE );
|
||
|
||
try_return( Status = CdOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
UserVolumeOpen,
|
||
FALSE,
|
||
NULL ));
|
||
}
|
||
|
||
//
|
||
// At this point CurrentFcb points to the deepest Fcb for this open
|
||
// in the tree. Let's acquire this Fcb to keep it from being deleted
|
||
// beneath us.
|
||
//
|
||
|
||
CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Do a prefix search if there is more of the name to parse.
|
||
//
|
||
|
||
if (RemainingName.FileName.Length != 0) {
|
||
|
||
//
|
||
// Do the prefix search to find the longest matching name.
|
||
//
|
||
|
||
CdFindPrefix( IrpContext,
|
||
&CurrentFcb,
|
||
&RemainingName.FileName,
|
||
IgnoreCase );
|
||
}
|
||
|
||
//
|
||
// If the remaining name length is zero then we have found our
|
||
// target.
|
||
//
|
||
|
||
if (RemainingName.FileName.Length == 0) {
|
||
|
||
//
|
||
// If this is a file so verify the user didn't want to open
|
||
// a directory.
|
||
//
|
||
|
||
if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) {
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_return( Status = CdOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
UserFileOpen,
|
||
IgnoreCase,
|
||
RelatedCcb ));
|
||
|
||
//
|
||
// This is a directory. Verify the user didn't want to open
|
||
// as a file.
|
||
//
|
||
|
||
} else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
|
||
//
|
||
// Open the file as a directory.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_return( Status = CdOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
UserDirectoryOpen,
|
||
IgnoreCase,
|
||
RelatedCcb ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have more work to do. We have a starting Fcb which we own shared.
|
||
// We also have the remaining name to parse. Walk through the name
|
||
// component by component looking for the full name.
|
||
//
|
||
|
||
//
|
||
// Our starting Fcb better be a directory.
|
||
//
|
||
|
||
if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// If we can't wait then post this request.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
//
|
||
// Make sure the final name has no version string.
|
||
//
|
||
|
||
FinalName.VersionString.Length = 0;
|
||
|
||
while (TRUE) {
|
||
|
||
ShortNameMatch = FALSE;
|
||
|
||
//
|
||
// Split off the next component from the name.
|
||
//
|
||
|
||
CdDissectName( IrpContext,
|
||
&RemainingName.FileName,
|
||
&FinalName.FileName );
|
||
|
||
//
|
||
// Go ahead and look this entry up in the path table.
|
||
//
|
||
|
||
CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
CleanupCompoundPathEntry = TRUE;
|
||
|
||
FoundEntry = CdFindPathEntry( IrpContext,
|
||
CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
&CompoundPathEntry );
|
||
|
||
//
|
||
// If we didn't find the entry then check if the current name
|
||
// is a possible short name.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName );
|
||
|
||
//
|
||
// If there is an embedded short name offset then look for the
|
||
// matching long name in the directory.
|
||
//
|
||
|
||
if (ShortNameDirentOffset != MAXULONG) {
|
||
|
||
if (CleanupFileContext) {
|
||
|
||
CdCleanupFileContext( IrpContext, &FileContext );
|
||
}
|
||
|
||
CdInitializeFileContext( IrpContext, &FileContext );
|
||
CleanupFileContext = TRUE;
|
||
|
||
FoundEntry = CdFindFileByShortName( IrpContext,
|
||
CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
ShortNameDirentOffset,
|
||
&FileContext );
|
||
|
||
//
|
||
// If we found an entry and it is a directory then look
|
||
// this up in the path table.
|
||
//
|
||
|
||
if (FoundEntry) {
|
||
|
||
ShortNameMatch = TRUE;
|
||
|
||
if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
|
||
CD_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
|
||
FoundEntry = CdFindPathEntry( IrpContext,
|
||
CurrentFcb,
|
||
&FileContext.InitialDirent->Dirent.CdCaseFileName,
|
||
IgnoreCase,
|
||
&CompoundPathEntry );
|
||
|
||
//
|
||
// We better find this entry.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// Upcase the name with the short name if case
|
||
// insensitive.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
CdUpcaseName( IrpContext, &FinalName, &FinalName );
|
||
}
|
||
|
||
//
|
||
// We found a matching file. If we are at the last
|
||
// entry then break out of the loop and open the
|
||
// file below. Otherwise we return an error.
|
||
//
|
||
|
||
} else if (RemainingName.FileName.Length == 0) {
|
||
|
||
//
|
||
// Break out of the loop. We will process the dirent
|
||
// below.
|
||
//
|
||
|
||
MatchingName = &FileContext.ShortName;
|
||
break;
|
||
|
||
} else {
|
||
|
||
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// We didn't find the name in either the path table or as
|
||
// a short name in a directory. If the remaining name
|
||
// length is zero then break out of the loop to search
|
||
// the directory.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
if (RemainingName.FileName.Length == 0) {
|
||
|
||
break;
|
||
|
||
//
|
||
// Otherwise this path could not be cracked.
|
||
//
|
||
|
||
} else {
|
||
|
||
try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is an ignore case open then copy the exact case
|
||
// in the file object name. If it was a short name match then
|
||
// the name must be upcase already.
|
||
//
|
||
|
||
if (IgnoreCase && !ShortNameMatch) {
|
||
|
||
RtlCopyMemory( FinalName.FileName.Buffer,
|
||
CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer,
|
||
CompoundPathEntry.PathEntry.CdDirName.FileName.Length );
|
||
}
|
||
|
||
//
|
||
// If we have found the last component then open this as a directory
|
||
// and return to our caller.
|
||
//
|
||
|
||
if (RemainingName.FileName.Length == 0) {
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
ShortNameMatch,
|
||
&CompoundPathEntry.PathEntry,
|
||
TRUE,
|
||
RelatedCcb ));
|
||
}
|
||
|
||
//
|
||
// Otherwise open an Fcb for this intermediate index Fcb.
|
||
//
|
||
|
||
CdOpenDirectoryFromPathEntry( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
ShortNameMatch,
|
||
&CompoundPathEntry.PathEntry,
|
||
FALSE,
|
||
NULL );
|
||
|
||
CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
CleanupCompoundPathEntry = FALSE;
|
||
}
|
||
|
||
//
|
||
// We need to scan the current directory for a matching file name
|
||
// if we don't already have one.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
if (CleanupFileContext) {
|
||
|
||
CdCleanupFileContext( IrpContext, &FileContext );
|
||
}
|
||
|
||
CdInitializeFileContext( IrpContext, &FileContext );
|
||
CleanupFileContext = TRUE;
|
||
|
||
//
|
||
// Split our search name into separate components.
|
||
//
|
||
|
||
CdConvertNameToCdName( IrpContext, &FinalName );
|
||
|
||
FoundEntry = CdFindFile( IrpContext,
|
||
CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
&FileContext,
|
||
&MatchingName );
|
||
}
|
||
|
||
//
|
||
// If we didn't find a match then check if the name is invalid to
|
||
// determine which error code to return.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
if ((CreateDisposition == FILE_OPEN) ||
|
||
(CreateDisposition == FILE_OVERWRITE)) {
|
||
|
||
try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// Any other operation return STATUS_ACCESS_DENIED.
|
||
//
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If this is a directory then the disk is corrupt because it wasn't
|
||
// in the Path Table.
|
||
//
|
||
|
||
if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// Make sure our opener didn't want a directory.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_return( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If this is an ignore case open then copy the exact case
|
||
// in the file object name. Any version portion should
|
||
// already be upcased.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
RtlCopyMemory( FinalName.FileName.Buffer,
|
||
MatchingName->FileName.Buffer,
|
||
MatchingName->FileName.Length );
|
||
}
|
||
|
||
//
|
||
// Open the file using the file context. We already have the
|
||
// first and last dirents.
|
||
//
|
||
|
||
try_return( Status = CdOpenFileFromFileContext( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
(BOOLEAN) (MatchingName == &FileContext.ShortName),
|
||
&FileContext,
|
||
RelatedCcb ));
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Cleanup the PathEntry if initialized.
|
||
//
|
||
|
||
if (CleanupCompoundPathEntry) {
|
||
|
||
CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
}
|
||
|
||
//
|
||
// Cleanup the FileContext if initialized.
|
||
//
|
||
|
||
if (CleanupFileContext) {
|
||
|
||
CdCleanupFileContext( IrpContext, &FileContext );
|
||
}
|
||
|
||
//
|
||
// The result of this open could be success, pending or some error
|
||
// condition.
|
||
//
|
||
|
||
if (AbnormalTermination()) {
|
||
|
||
|
||
//
|
||
// In the error path we start by calling our teardown routine if we
|
||
// have a CurrentFcb.
|
||
//
|
||
|
||
if (CurrentFcb != NULL) {
|
||
|
||
BOOLEAN RemovedFcb;
|
||
|
||
CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb );
|
||
|
||
if (RemovedFcb) {
|
||
|
||
CurrentFcb = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// No need to complete the request.
|
||
//
|
||
|
||
IrpContext = NULL;
|
||
Irp = NULL;
|
||
|
||
//
|
||
// If we posted this request through the oplock package we need
|
||
// to show that there is no reason to complete the request.
|
||
//
|
||
|
||
} else if (Status == STATUS_PENDING) {
|
||
|
||
IrpContext = NULL;
|
||
Irp = NULL;
|
||
}
|
||
|
||
//
|
||
// Release the Current Fcb if still acquired.
|
||
//
|
||
|
||
if (CurrentFcb != NULL) {
|
||
|
||
CdReleaseFcb( IrpContext, CurrentFcb );
|
||
}
|
||
|
||
//
|
||
// Release the Vcb.
|
||
//
|
||
|
||
CdReleaseVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// Call our completion routine. It will handle the case where either
|
||
// the Irp and/or IrpContext are gone.
|
||
//
|
||
|
||
CdCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdNormalizeFileNames (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN OpenByFileId,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN TYPE_OF_OPEN RelatedTypeOfOpen,
|
||
IN PCCB RelatedCcb OPTIONAL,
|
||
IN PUNICODE_STRING RelatedFileName OPTIONAL,
|
||
IN OUT PUNICODE_STRING FileName,
|
||
IN OUT PCD_NAME RemainingName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to store the full name and upcased name into the
|
||
filename buffer. We only upcase the portion yet to parse. We also
|
||
check for a trailing backslash and lead-in double backslashes. This
|
||
routine also verifies the mode of the related open against the name
|
||
currently in the filename.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for this volume.
|
||
|
||
OpenByFileId - Indicates if the filename should be a 64 bit FileId.
|
||
|
||
IgnoreCase - Indicates if this open is a case-insensitive operation.
|
||
|
||
RelatedTypeOfOpen - Indicates the type of the related file object.
|
||
|
||
RelatedCcb - Ccb for the related open. Ignored if no relative open.
|
||
|
||
RelatedFileName - FileName buffer for related open. Ignored if no
|
||
relative open.
|
||
|
||
FileName - FileName to update in this routine. The name should
|
||
either be a 64-bit FileId or a Unicode string.
|
||
|
||
RemainingName - Name with the remaining portion of the name. This
|
||
will begin after the related name and any separator. For a
|
||
non-relative open we also step over the initial separator.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG RemainingNameLength;
|
||
ULONG RelatedNameLength = 0;
|
||
ULONG SeparatorLength = 0;
|
||
|
||
ULONG BufferLength;
|
||
|
||
UNICODE_STRING NewFileName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If this is the first pass then we need to build the full name and
|
||
// check for name compatibility.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) {
|
||
|
||
//
|
||
// Deal with the regular file name case first.
|
||
//
|
||
|
||
if (!OpenByFileId) {
|
||
|
||
//
|
||
// This is here because the Win32 layer can't avoid sending me double
|
||
// beginning backslashes.
|
||
//
|
||
|
||
if ((FileName->Length > sizeof( WCHAR )) &&
|
||
(FileName->Buffer[1] == L'\\') &&
|
||
(FileName->Buffer[0] == L'\\')) {
|
||
|
||
//
|
||
// If there are still two beginning backslashes, the name is bogus.
|
||
//
|
||
|
||
if ((FileName->Length > 2 * sizeof( WCHAR )) &&
|
||
(FileName->Buffer[2] == L'\\')) {
|
||
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
//
|
||
// Slide the name down in the buffer.
|
||
//
|
||
|
||
FileName->Length -= sizeof( WCHAR );
|
||
|
||
RtlMoveMemory( FileName->Buffer,
|
||
FileName->Buffer + 1,
|
||
FileName->Length );
|
||
}
|
||
|
||
//
|
||
// Check for a trailing backslash. Don't strip off if only character
|
||
// in the full name or for relative opens where this is illegal.
|
||
//
|
||
|
||
if (((FileName->Length > sizeof( WCHAR)) ||
|
||
((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) &&
|
||
(FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) {
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH );
|
||
FileName->Length -= sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// Remember the length we need for this portion of the name.
|
||
//
|
||
|
||
RemainingNameLength = FileName->Length;
|
||
|
||
//
|
||
// If this is a related file object then we verify the compatibility
|
||
// of the name in the file object with the relative file object.
|
||
//
|
||
|
||
if (RelatedTypeOfOpen != UnopenedFileObject) {
|
||
|
||
//
|
||
// If the filename length was zero then it must be legal.
|
||
// If there are characters then check with the related
|
||
// type of open.
|
||
//
|
||
|
||
if (FileName->Length != 0) {
|
||
|
||
//
|
||
// The name length must always be zero for a volume open.
|
||
//
|
||
|
||
if (RelatedTypeOfOpen <= UserVolumeOpen) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
//
|
||
// The remaining name cannot begin with a backslash.
|
||
//
|
||
|
||
} else if (FileName->Buffer[0] == L'\\' ) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
//
|
||
// If the related file is a user file then there
|
||
// is no file with this path.
|
||
//
|
||
|
||
} else if (RelatedTypeOfOpen == UserFileOpen) {
|
||
|
||
return STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remember the length of the related name when building
|
||
// the full name. We leave the RelatedNameLength and
|
||
// SeparatorLength at zero if the relative file is opened
|
||
// by Id.
|
||
//
|
||
|
||
if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
|
||
|
||
//
|
||
// Add a separator if the name length is non-zero
|
||
// unless the relative Fcb is at the root.
|
||
//
|
||
|
||
if ((FileName->Length != 0) &&
|
||
(RelatedCcb->Fcb != Vcb->RootIndexFcb)) {
|
||
|
||
SeparatorLength = sizeof( WCHAR );
|
||
}
|
||
|
||
RelatedNameLength = RelatedFileName->Length;
|
||
}
|
||
|
||
//
|
||
// The full name is already in the filename. It must either
|
||
// be length 0 or begin with a backslash.
|
||
//
|
||
|
||
} else if (FileName->Length != 0) {
|
||
|
||
if (FileName->Buffer[0] != L'\\') {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// We will want to trim the leading backslash from the
|
||
// remaining name we return.
|
||
//
|
||
|
||
RemainingNameLength -= sizeof( WCHAR );
|
||
SeparatorLength = sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// Now see if the buffer is large enough to hold the full name.
|
||
//
|
||
|
||
BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength;
|
||
|
||
//
|
||
// Check for an overflow of the maximum filename size.
|
||
//
|
||
|
||
if (BufferLength > MAXUSHORT) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Now see if we need to allocate a new buffer.
|
||
//
|
||
|
||
if (FileName->MaximumLength < BufferLength) {
|
||
|
||
NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
|
||
BufferLength,
|
||
TAG_FILE_NAME );
|
||
|
||
NewFileName.MaximumLength = (USHORT) BufferLength;
|
||
|
||
} else {
|
||
|
||
NewFileName.Buffer = FileName->Buffer;
|
||
NewFileName.MaximumLength = FileName->MaximumLength;
|
||
}
|
||
|
||
//
|
||
// If there is a related name then we need to slide the remaining bytes up and
|
||
// insert the related name. Otherwise the name is in the correct position
|
||
// already.
|
||
//
|
||
|
||
if (RelatedNameLength != 0) {
|
||
|
||
//
|
||
// Store the remaining name in its correct position.
|
||
//
|
||
|
||
if (RemainingNameLength != 0) {
|
||
|
||
RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ),
|
||
FileName->Buffer,
|
||
RemainingNameLength );
|
||
}
|
||
|
||
RtlCopyMemory( NewFileName.Buffer,
|
||
RelatedFileName->Buffer,
|
||
RelatedNameLength );
|
||
|
||
//
|
||
// Add the separator if needed.
|
||
//
|
||
|
||
if (SeparatorLength != 0) {
|
||
|
||
*(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\';
|
||
}
|
||
|
||
//
|
||
// Update the filename value we got from the user.
|
||
//
|
||
|
||
if (NewFileName.Buffer != FileName->Buffer) {
|
||
|
||
if (FileName->Buffer != NULL) {
|
||
|
||
ExFreePool( FileName->Buffer );
|
||
}
|
||
|
||
FileName->Buffer = NewFileName.Buffer;
|
||
FileName->MaximumLength = NewFileName.MaximumLength;
|
||
}
|
||
|
||
//
|
||
// Copy the name length to the user's filename.
|
||
//
|
||
|
||
FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength);
|
||
}
|
||
|
||
//
|
||
// Now update the remaining name to parse.
|
||
//
|
||
|
||
RemainingName->FileName.MaximumLength =
|
||
RemainingName->FileName.Length = (USHORT) RemainingNameLength;
|
||
RemainingName->VersionString.Length = 0;
|
||
|
||
RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer,
|
||
RelatedNameLength + SeparatorLength,
|
||
PWCHAR );
|
||
|
||
//
|
||
// Upcase the name if necessary.
|
||
//
|
||
|
||
if (IgnoreCase && (RemainingNameLength != 0)) {
|
||
|
||
CdUpcaseName( IrpContext,
|
||
RemainingName,
|
||
RemainingName );
|
||
}
|
||
|
||
//
|
||
// Do a quick check to make sure there are no wildcards.
|
||
//
|
||
|
||
if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) {
|
||
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
}
|
||
|
||
//
|
||
// For the open by file Id case we verify the name really contains
|
||
// a 64 bit value.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check for validity of the buffer.
|
||
//
|
||
|
||
if (FileName->Length != sizeof( FILE_ID )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME );
|
||
|
||
//
|
||
// If we are in the retry path then the full name is already in the
|
||
// file object name. If this is a case-sensitive operation then
|
||
// we need to upcase the name from the end of any related file name already stored
|
||
// there.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Assume there is no relative name.
|
||
//
|
||
|
||
RemainingName->FileName = *FileName;
|
||
RemainingName->VersionString.Length = 0;
|
||
|
||
//
|
||
// Nothing to do if the name length is zero.
|
||
//
|
||
|
||
if (RemainingName->FileName.Length != 0) {
|
||
|
||
//
|
||
// If there is a relative name then we need to walk past it.
|
||
//
|
||
|
||
if (RelatedTypeOfOpen != UnopenedFileObject) {
|
||
|
||
//
|
||
// Nothing to walk past if the RelatedCcb is opened by FileId.
|
||
//
|
||
|
||
|
||
if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
|
||
|
||
//
|
||
// Related file name is a proper prefix of the full name.
|
||
// We step over the related name and if we are then
|
||
// pointing at a separator character we step over that.
|
||
//
|
||
|
||
RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
|
||
RelatedFileName->Length,
|
||
PWCHAR );
|
||
|
||
RemainingName->FileName.Length -= RelatedFileName->Length;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we are pointing at a separator character then step past that.
|
||
//
|
||
|
||
if (RemainingName->FileName.Length != 0) {
|
||
|
||
if (*(RemainingName->FileName.Buffer) == L'\\') {
|
||
|
||
RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
|
||
sizeof( WCHAR ),
|
||
PWCHAR );
|
||
|
||
RemainingName->FileName.Length -= sizeof( WCHAR );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Upcase the name if necessary.
|
||
//
|
||
|
||
if (IgnoreCase && (RemainingName->FileName.Length != 0)) {
|
||
|
||
CdUpcaseName( IrpContext,
|
||
RemainingName,
|
||
RemainingName );
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdOpenByFileId (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open a file by the FileId. The file Id is in
|
||
the FileObject name buffer and has been verified to be 64 bits.
|
||
|
||
We extract the Id number and then check to see whether we are opening a
|
||
file or directory and compare that with the create options. If this
|
||
generates no error then optimistically look up the Fcb in the Fcb Table.
|
||
|
||
If we don't find the Fcb then we need to carefully verify there is a file
|
||
at this offset. First check whether the Parent Fcb is in the table. If
|
||
not then lookup the parent at the path table offset given by file ID.
|
||
|
||
If found then build the Fcb from this entry and store the new Fcb in the
|
||
tree.
|
||
|
||
We know have the parent Fcb. Do a directory scan to find the dirent at
|
||
the given offset in this stream. This must point to the first entry
|
||
of a valid file.
|
||
|
||
Finally we call our worker routine to complete the open on this Fcb.
|
||
|
||
Arguments:
|
||
|
||
IrpSp - Stack location within the create Irp.
|
||
|
||
Vcb - Vcb for this volume.
|
||
|
||
CurrentFcb - Address to store the Fcb for this open. We only store the
|
||
CurrentFcb here when we have acquired it so our caller knows to
|
||
free or deallocate it.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status indicating the result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_ACCESS_DENIED;
|
||
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
BOOLEAN Found;
|
||
|
||
ULONG StreamOffset;
|
||
|
||
NODE_TYPE_CODE NodeTypeCode;
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
|
||
FILE_ENUM_CONTEXT FileContext;
|
||
BOOLEAN CleanupFileContext = FALSE;
|
||
|
||
COMPOUND_PATH_ENTRY CompoundPathEntry;
|
||
BOOLEAN CleanupCompoundPathEntry = FALSE;
|
||
|
||
FILE_ID FileId;
|
||
FILE_ID ParentFileId;
|
||
|
||
PFCB NextFcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Extract the FileId from the FileObject.
|
||
//
|
||
|
||
RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID ));
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Go ahead and figure out the TypeOfOpen and NodeType. We can
|
||
// get these from the input FileId.
|
||
//
|
||
|
||
if (CdFidIsDirectory( FileId )) {
|
||
|
||
TypeOfOpen = UserDirectoryOpen;
|
||
NodeTypeCode = CDFS_NTC_FCB_INDEX;
|
||
|
||
//
|
||
// If the offset isn't zero then the file Id is bad.
|
||
//
|
||
|
||
if (CdQueryFidDirentOffset( FileId ) != 0) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
} else {
|
||
|
||
TypeOfOpen = UserFileOpen;
|
||
NodeTypeCode = CDFS_NTC_FCB_DATA;
|
||
}
|
||
|
||
//
|
||
// Acquire the Vcb and check if there is already an Fcb.
|
||
// If not we will need to carefully verify the Fcb.
|
||
// We will post the request if we don't find the Fcb and this
|
||
// request can't wait.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId );
|
||
|
||
if (NextFcb == NULL) {
|
||
|
||
//
|
||
// Get the path table offset from the file id.
|
||
//
|
||
|
||
StreamOffset = CdQueryFidPathTableOffset( FileId );
|
||
|
||
//
|
||
// Build the parent FileId for this and try looking it
|
||
// up in the PathTable.
|
||
//
|
||
|
||
CdSetFidDirentOffset( ParentFileId, 0 );
|
||
CdSetFidPathTableOffset( ParentFileId, StreamOffset );
|
||
CdFidSetDirectory( ParentFileId );
|
||
|
||
NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId );
|
||
|
||
//
|
||
// If not present then walk through the PathTable to this point.
|
||
//
|
||
|
||
if (NextFcb == NULL) {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Check that the path table offset lies within the path
|
||
// table.
|
||
//
|
||
|
||
if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
CleanupCompoundPathEntry = TRUE;
|
||
|
||
//
|
||
// Start at the first entry in the PathTable.
|
||
//
|
||
|
||
CdLookupPathEntry( IrpContext,
|
||
Vcb->PathTableFcb->StreamOffset,
|
||
1,
|
||
TRUE,
|
||
&CompoundPathEntry );
|
||
|
||
//
|
||
// Continue looking until we have passed our target offset.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Move to the next entry.
|
||
//
|
||
|
||
Found = CdLookupNextPathEntry( IrpContext,
|
||
&CompoundPathEntry.PathContext,
|
||
&CompoundPathEntry.PathEntry );
|
||
|
||
//
|
||
// If we didn't find the entry or are beyond it then the
|
||
// input Id is invalid.
|
||
//
|
||
|
||
if (!Found ||
|
||
(CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the FileId specified a directory then we have found
|
||
// the entry. Make sure our caller wanted to open a directory.
|
||
//
|
||
|
||
if ((TypeOfOpen == UserDirectoryOpen) &&
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// Lock the Vcb and create the Fcb if necessary.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found );
|
||
|
||
//
|
||
// It's possible that someone got in here ahead of us.
|
||
//
|
||
|
||
if (!Found) {
|
||
|
||
CdInitializeFcbFromPathEntry( IrpContext,
|
||
NextFcb,
|
||
NULL,
|
||
&CompoundPathEntry.PathEntry );
|
||
}
|
||
|
||
//
|
||
// If the user wanted to open a directory then we have found
|
||
// it. Store this Fcb into the CurrentFcb and skip the
|
||
// directory scan.
|
||
//
|
||
|
||
if (TypeOfOpen == UserDirectoryOpen) {
|
||
|
||
*CurrentFcb = NextFcb;
|
||
NextFcb = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Perform the directory scan if we don't already have our target.
|
||
//
|
||
|
||
if (NextFcb != NULL) {
|
||
|
||
//
|
||
// Acquire the parent. We currently own the Vcb lock so
|
||
// do this without waiting first.
|
||
//
|
||
|
||
if (!CdAcquireFcbExclusive( IrpContext,
|
||
NextFcb,
|
||
TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
} else {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Set up the CurrentFcb pointers. We know there was
|
||
// no previous parent in this case.
|
||
//
|
||
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Calculate the offset in the stream.
|
||
//
|
||
|
||
StreamOffset = CdQueryFidDirentOffset( FileId );
|
||
|
||
//
|
||
// Create the stream file if it doesn't exist. This will update
|
||
// the Fcb with the size from the self entry.
|
||
//
|
||
|
||
if (NextFcb->FileObject == NULL) {
|
||
|
||
CdCreateInternalStream( IrpContext, Vcb, NextFcb );
|
||
}
|
||
|
||
//
|
||
// If our offset is beyond the end of the directory then the
|
||
// FileId is invalid.
|
||
//
|
||
|
||
if (StreamOffset > NextFcb->FileSize.LowPart) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Otherwise position ourselves at the self entry and walk
|
||
// through dirent by dirent until this location is found.
|
||
//
|
||
|
||
CdInitializeFileContext( IrpContext, &FileContext );
|
||
CdLookupInitialFileDirent( IrpContext,
|
||
NextFcb,
|
||
&FileContext,
|
||
NextFcb->StreamOffset );
|
||
|
||
CleanupFileContext = TRUE;
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Move to the first entry of the next file.
|
||
//
|
||
|
||
Found = CdLookupNextInitialFileDirent( IrpContext,
|
||
NextFcb,
|
||
&FileContext );
|
||
|
||
//
|
||
// If we didn't find the entry or are beyond it then the
|
||
// input Id is invalid.
|
||
//
|
||
|
||
if (!Found ||
|
||
(FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
//
|
||
// This better not be a directory. Directory FileIds must
|
||
// refer to the self entry for directories.
|
||
//
|
||
|
||
if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
|
||
CD_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
try_return( Status = STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Check that our caller wanted to open a file.
|
||
//
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// Otherwise we want to collect all of the dirents for this file
|
||
// and create an Fcb with this.
|
||
//
|
||
|
||
CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext );
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found );
|
||
|
||
//
|
||
// It's possible that someone has since created this Fcb since we
|
||
// first checked. If so then can simply use this. Otherwise
|
||
// we need to initialize a new Fcb and attach it to our parent
|
||
// and insert it into the Fcb Table.
|
||
//
|
||
|
||
if (!Found) {
|
||
|
||
CdInitializeFcbFromFileContext( IrpContext,
|
||
NextFcb,
|
||
*CurrentFcb,
|
||
&FileContext );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have the Fcb. Check that the type of the file is compatible with
|
||
// the desired type of file to open.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
}
|
||
|
||
} else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_return( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have a the previous Fcb and have inserted the next Fcb into
|
||
// the Fcb Table. It is safe to release the current Fcb if present
|
||
// since it is referenced through the child Fcb.
|
||
//
|
||
|
||
if (*CurrentFcb != NULL) {
|
||
|
||
CdReleaseFcb( IrpContext, *CurrentFcb );
|
||
}
|
||
|
||
//
|
||
// We now know the Fcb and currently hold the Vcb lock.
|
||
// Try to acquire this Fcb without waiting. Otherwise we
|
||
// need to reference it, drop the Vcb, acquire the Fcb and
|
||
// then dereference the Fcb.
|
||
//
|
||
|
||
if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
} else {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Move to this Fcb.
|
||
//
|
||
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Check the requested access on this Fcb.
|
||
//
|
||
|
||
if (!CdIllegalFcbAccess( IrpContext,
|
||
TypeOfOpen,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
Status = CdCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
CurrentFcb,
|
||
TypeOfOpen,
|
||
CCB_FLAG_OPEN_BY_ID,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (UnlockVcb) {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
if (CleanupFileContext) {
|
||
|
||
CdCleanupFileContext( IrpContext, &FileContext );
|
||
}
|
||
|
||
if (CleanupCompoundPathEntry) {
|
||
|
||
CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdOpenExistingFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open an Fcb which is already in the Fcb table.
|
||
We will verify the access to the file and then call our worker routine
|
||
to perform the final operations.
|
||
|
||
Arguments:
|
||
|
||
IrpSp - Pointer to the stack location for this open.
|
||
|
||
CurrentFcb - Address of Fcb to open. We will clear this if the Fcb
|
||
is released here.
|
||
|
||
TypeOfOpen - Indicates whether we are opening a file, directory or volume.
|
||
|
||
IgnoreCase - Indicates if this open is case-insensitive.
|
||
|
||
RelatedCcb - Ccb for related file object if relative open. We use
|
||
this when setting the Ccb flags for this open. It will tell
|
||
us whether the name currently in the file object is relative or
|
||
absolute.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status indicating the result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG CcbFlags = 0;
|
||
|
||
NTSTATUS Status = STATUS_ACCESS_DENIED;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check that the desired access is legal.
|
||
//
|
||
|
||
if (!CdIllegalFcbAccess( IrpContext,
|
||
TypeOfOpen,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
|
||
|
||
//
|
||
// Set the Ignore case.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
|
||
}
|
||
|
||
//
|
||
// Check the related Ccb to see if this was an OpenByFileId and
|
||
// whether there was a version.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( RelatedCcb )) {
|
||
|
||
SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION ));
|
||
|
||
|
||
if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
Status = CdCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
(*CurrentFcb)->Vcb,
|
||
CurrentFcb,
|
||
TypeOfOpen,
|
||
CcbFlags,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdOpenDirectoryFromPathEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PCD_NAME DirName,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN PPATH_ENTRY PathEntry,
|
||
IN BOOLEAN PerformUserOpen,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open a directory where the directory was found
|
||
in the path table. This routine is called in the case where this is the
|
||
file to open for the user and where this is an intermediate node in the
|
||
full path to open.
|
||
|
||
We first check that the desired access is legal for a directory. Then we
|
||
construct the FileId for this and do a check to see if it is the Fcb
|
||
Table. It is always possible that either it was created since or simply
|
||
wasn't in the prefix table at the time of the prefix table search.
|
||
Initialize the Fcb and store into the FcbTable if not present.
|
||
|
||
Next we will add this to the prefix table of our parent if needed.
|
||
|
||
Once we know that the new Fcb has been initialized then we move our pointer
|
||
in the tree down to this position.
|
||
|
||
This routine does not own the Vcb lock on entry. We must be sure to release
|
||
it on exit.
|
||
|
||
Arguments:
|
||
|
||
IrpSp - Stack location for this request.
|
||
|
||
Vcb - Vcb for this volume.
|
||
|
||
CurrentFcb - On input this is the parent of the Fcb to open. On output we
|
||
store the Fcb for the file being opened.
|
||
|
||
DirName - This is always the exact name used to reach this file.
|
||
|
||
IgnoreCase - Indicates the type of case match for the open.
|
||
|
||
ShortNameMatch - Indicates if we are opening via the short name.
|
||
|
||
PathEntry - Path entry for the entry found.
|
||
|
||
PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise.
|
||
|
||
RelatedCcb - RelatedCcb for relative file object used to make this open.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status indicating the result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG CcbFlags = 0;
|
||
FILE_ID FileId;
|
||
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
BOOLEAN FcbExisted;
|
||
|
||
PFCB NextFcb;
|
||
PFCB ParentFcb = NULL;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check for illegal access to this file.
|
||
//
|
||
|
||
if (PerformUserOpen &&
|
||
CdIllegalFcbAccess( IrpContext,
|
||
UserDirectoryOpen,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
|
||
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Check the related Ccb to see if this was an OpenByFileId.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( RelatedCcb ) &&
|
||
FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
|
||
|
||
CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID;
|
||
}
|
||
|
||
if (IgnoreCase) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
|
||
}
|
||
|
||
//
|
||
// Build the file Id for this file.
|
||
//
|
||
|
||
FileId.QuadPart = 0;
|
||
CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset );
|
||
CdFidSetDirectory( FileId );
|
||
|
||
//
|
||
// Lock the Vcb so we can examine the Fcb Table.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
//
|
||
// Get the Fcb for this directory.
|
||
//
|
||
|
||
NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted );
|
||
|
||
//
|
||
// If the Fcb was created here then initialize from the values in the
|
||
// path table entry.
|
||
//
|
||
|
||
if (!FcbExisted) {
|
||
|
||
CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry );
|
||
}
|
||
|
||
//
|
||
// Now try to acquire the new Fcb without waiting. We will reference
|
||
// the Fcb and retry with wait if unsuccessful.
|
||
//
|
||
|
||
if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
CdReleaseFcb( IrpContext, *CurrentFcb );
|
||
CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Unlock the Vcb and move down to this new Fcb. Remember that we still
|
||
// own the parent however.
|
||
//
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
UnlockVcb = FALSE;
|
||
|
||
ParentFcb = *CurrentFcb;
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Store this name into the prefix table for the parent.
|
||
//
|
||
|
||
if (ShortNameMatch) {
|
||
|
||
//
|
||
// Make sure the exact case is always in the tree.
|
||
//
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
DirName,
|
||
FALSE,
|
||
TRUE,
|
||
ParentFcb );
|
||
|
||
if (IgnoreCase) {
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
DirName,
|
||
TRUE,
|
||
TRUE,
|
||
ParentFcb );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure the exact case is always in the tree.
|
||
//
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
&PathEntry->CdDirName,
|
||
FALSE,
|
||
FALSE,
|
||
ParentFcb );
|
||
|
||
if (IgnoreCase) {
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
&PathEntry->CdCaseDirName,
|
||
TRUE,
|
||
FALSE,
|
||
ParentFcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release the parent Fcb at this point.
|
||
//
|
||
|
||
CdReleaseFcb( IrpContext, ParentFcb );
|
||
ParentFcb = NULL;
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
if (PerformUserOpen) {
|
||
|
||
Status = CdCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
CurrentFcb,
|
||
UserDirectoryOpen,
|
||
CcbFlags,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Unlock the Vcb if held.
|
||
//
|
||
|
||
if (UnlockVcb) {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Release the parent if held.
|
||
//
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
CdReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdOpenFileFromFileContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PCD_NAME FileName,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN PFILE_ENUM_CONTEXT FileContext,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open a file where the file was found in a directory scan.
|
||
This should only be for a file in the case since we will find the directories in the
|
||
path table.
|
||
|
||
We first check that the desired access is legal for this file. Then we
|
||
construct the FileId for this and do a check to see if it is the Fcb
|
||
Table. It is always possible that either it was created since or simply
|
||
wasn't in the prefix table at the time of the prefix table search.
|
||
Initialize the Fcb and store into the FcbTable if not present.
|
||
|
||
Next we will add this to the prefix table of our parent if needed.
|
||
|
||
Once we know that the new Fcb has been initialized then we move our pointer
|
||
in the tree down to this position.
|
||
|
||
This routine does not own the Vcb lock on entry. We must be sure to release
|
||
it on exit.
|
||
|
||
Arguments:
|
||
|
||
IrpSp - Stack location for this request.
|
||
|
||
Vcb - Vcb for the current volume.
|
||
|
||
CurrentFcb - On input this is the parent of the Fcb to open. On output we
|
||
store the Fcb for the file being opened.
|
||
|
||
FileName - This is always the exact name used to reach this file.
|
||
|
||
IgnoreCase - Indicates the type of case of CaseName above.
|
||
|
||
ShortNameMatch - Indicates if we are opening via the short name.
|
||
|
||
FileContext - This is the context used to find the file.
|
||
|
||
RelatedCcb - RelatedCcb for relative file object used to make this open.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status indicating the result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG CcbFlags = 0;
|
||
FILE_ID FileId;
|
||
|
||
BOOLEAN UnlockVcb = FALSE;
|
||
BOOLEAN FcbExisted;
|
||
|
||
PFCB NextFcb;
|
||
PFCB ParentFcb = NULL;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check for illegal access to this file.
|
||
//
|
||
|
||
if (CdIllegalFcbAccess( IrpContext,
|
||
UserFileOpen,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
|
||
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Check if a version number was used to open this file.
|
||
//
|
||
|
||
if (FileName->VersionString.Length != 0) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION );
|
||
}
|
||
|
||
//
|
||
// Check the related Ccb to see if this was an OpenByFileId.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( RelatedCcb ) &&
|
||
FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
|
||
}
|
||
|
||
if (IgnoreCase) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
|
||
}
|
||
|
||
//
|
||
// Build the file Id for this file. We can use the path table offset from the
|
||
// parent and the directory offset from the dirent.
|
||
//
|
||
|
||
CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId ));
|
||
CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset );
|
||
|
||
//
|
||
// Lock the Vcb so we can examine the Fcb Table.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
//
|
||
// Get the Fcb for this file.
|
||
//
|
||
|
||
NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted );
|
||
|
||
//
|
||
// If the Fcb was created here then initialize from the values in the
|
||
// dirent.
|
||
//
|
||
|
||
if (!FcbExisted) {
|
||
|
||
CdInitializeFcbFromFileContext( IrpContext,
|
||
NextFcb,
|
||
*CurrentFcb,
|
||
FileContext );
|
||
}
|
||
|
||
//
|
||
// Now try to acquire the new Fcb without waiting. We will reference
|
||
// the Fcb and retry with wait if unsuccessful.
|
||
//
|
||
|
||
if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
CdReleaseFcb( IrpContext, *CurrentFcb );
|
||
CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Unlock the Vcb and move down to this new Fcb. Remember that we still
|
||
// own the parent however.
|
||
//
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
UnlockVcb = FALSE;
|
||
|
||
ParentFcb = *CurrentFcb;
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Store this name into the prefix table for the parent.
|
||
//
|
||
|
||
|
||
if (ShortNameMatch) {
|
||
|
||
//
|
||
// Make sure the exact case is always in the tree.
|
||
//
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
FileName,
|
||
FALSE,
|
||
TRUE,
|
||
ParentFcb );
|
||
|
||
if (IgnoreCase) {
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
FileName,
|
||
TRUE,
|
||
TRUE,
|
||
ParentFcb );
|
||
}
|
||
|
||
//
|
||
// Insert this into the prefix table if we found this without
|
||
// using a version string.
|
||
//
|
||
|
||
} else if (FileName->VersionString.Length == 0) {
|
||
|
||
//
|
||
// Make sure the exact case is always in the tree.
|
||
//
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
&FileContext->InitialDirent->Dirent.CdFileName,
|
||
FALSE,
|
||
FALSE,
|
||
ParentFcb );
|
||
|
||
if (IgnoreCase) {
|
||
|
||
CdInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
&FileContext->InitialDirent->Dirent.CdCaseFileName,
|
||
TRUE,
|
||
FALSE,
|
||
ParentFcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release the parent Fcb at this point.
|
||
//
|
||
|
||
CdReleaseFcb( IrpContext, ParentFcb );
|
||
ParentFcb = NULL;
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
Status = CdCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
CurrentFcb,
|
||
UserFileOpen,
|
||
CcbFlags,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Unlock the Vcb if held.
|
||
//
|
||
|
||
if (UnlockVcb) {
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Release the parent if held.
|
||
//
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
CdReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
CdCompleteFcbOpen (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN ULONG UserCcbFlags,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the worker routine which takes an existing Fcb and completes
|
||
the open. We will do any necessary oplock checks and sharing checks.
|
||
Finally we will create the Ccb and update the file object and any
|
||
file object flags.
|
||
|
||
Arguments:
|
||
|
||
IrpSp - Stack location for the current request.
|
||
|
||
Vcb - Vcb for the current volume.
|
||
|
||
CurrentFcb - Address of pointer to Fcb to open. We clear this field if
|
||
we release the resource for this file.
|
||
|
||
TypeOfOpen - Type of open for this request.
|
||
|
||
UserCcbFlags - Flags to OR into the Ccb flags.
|
||
|
||
DesiredAccess - Desired access for this open.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if
|
||
the oplock package takes the Irp or SHARING_VIOLATION if there is a
|
||
sharing check conflict.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
NTSTATUS OplockStatus = STATUS_SUCCESS;
|
||
ULONG Information = FILE_OPENED;
|
||
|
||
BOOLEAN LockVolume = FALSE;
|
||
|
||
PFCB Fcb = *CurrentFcb;
|
||
PCCB Ccb;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Expand maximum allowed to something sensible for share access checking
|
||
//
|
||
|
||
if (MAXIMUM_ALLOWED == DesiredAccess) {
|
||
|
||
DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ?
|
||
(FILE_WRITE_ATTRIBUTES |
|
||
FILE_WRITE_DATA |
|
||
FILE_WRITE_EA |
|
||
FILE_ADD_FILE |
|
||
FILE_ADD_SUBDIRECTORY |
|
||
FILE_APPEND_DATA) : 0) |
|
||
FILE_DELETE_CHILD |
|
||
DELETE |
|
||
WRITE_DAC );
|
||
}
|
||
|
||
//
|
||
// If this a volume open and the user wants to lock the volume then
|
||
// purge and lock the volume.
|
||
//
|
||
|
||
if ((TypeOfOpen <= UserVolumeOpen) &&
|
||
!FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) {
|
||
|
||
//
|
||
// If there are open handles then fail this immediately.
|
||
//
|
||
|
||
if (Vcb->VcbCleanup != 0) {
|
||
|
||
return STATUS_SHARING_VIOLATION;
|
||
}
|
||
|
||
//
|
||
// If we can't wait then force this to be posted.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
LockVolume = TRUE;
|
||
|
||
//
|
||
// Purge the volume and make sure all of the user references
|
||
// are gone.
|
||
//
|
||
|
||
Status = CdPurgeVolume( IrpContext, Vcb, FALSE );
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now force all of the delayed close operations to go away.
|
||
//
|
||
|
||
CdFspClose( Vcb );
|
||
|
||
if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) {
|
||
|
||
return STATUS_SHARING_VIOLATION;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the Fcb already existed then we need to check the oplocks and
|
||
// the share access.
|
||
//
|
||
|
||
if (Fcb->FcbCleanup != 0) {
|
||
|
||
//
|
||
// If this is a user file open then check whether there are any
|
||
// batch oplock.
|
||
//
|
||
|
||
if (TypeOfOpen == UserFileOpen) {
|
||
|
||
//
|
||
// Store the address of the Fcb for a possible teardown into
|
||
// the IrpContext. We will release this in the call to
|
||
// prepost the Irp.
|
||
//
|
||
|
||
IrpContext->TeardownFcb = CurrentFcb;
|
||
|
||
if (FsRtlCurrentBatchOplock( &Fcb->Oplock )) {
|
||
|
||
//
|
||
// We remember if a batch oplock break is underway for the
|
||
// case where the sharing check fails.
|
||
//
|
||
|
||
Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
||
|
||
OplockStatus = FsRtlCheckOplock( &Fcb->Oplock,
|
||
IrpContext->Irp,
|
||
IrpContext,
|
||
CdOplockComplete,
|
||
CdPrePostIrp );
|
||
|
||
if (OplockStatus == STATUS_PENDING) {
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check the share access before breaking any exclusive oplocks.
|
||
//
|
||
|
||
Status = IoCheckShareAccess( DesiredAccess,
|
||
IrpSp->Parameters.Create.ShareAccess,
|
||
IrpSp->FileObject,
|
||
&Fcb->ShareAccess,
|
||
FALSE );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now check that we can continue based on the oplock state of the
|
||
// file.
|
||
//
|
||
|
||
OplockStatus = FsRtlCheckOplock( &Fcb->Oplock,
|
||
IrpContext->Irp,
|
||
IrpContext,
|
||
CdOplockComplete,
|
||
CdPrePostIrp );
|
||
|
||
if (OplockStatus == STATUS_PENDING) {
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
IrpContext->TeardownFcb = NULL;
|
||
|
||
//
|
||
// Otherwise just do the sharing check.
|
||
//
|
||
|
||
} else {
|
||
|
||
Status = IoCheckShareAccess( DesiredAccess,
|
||
IrpSp->Parameters.Create.ShareAccess,
|
||
IrpSp->FileObject,
|
||
&Fcb->ShareAccess,
|
||
FALSE );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Create the Ccb now.
|
||
//
|
||
|
||
Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags );
|
||
|
||
//
|
||
// Update the share access.
|
||
//
|
||
|
||
if (Fcb->FcbCleanup == 0) {
|
||
|
||
IoSetShareAccess( DesiredAccess,
|
||
IrpSp->Parameters.Create.ShareAccess,
|
||
IrpSp->FileObject,
|
||
&Fcb->ShareAccess );
|
||
|
||
} else {
|
||
|
||
IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess );
|
||
}
|
||
|
||
//
|
||
// Set the file object type.
|
||
//
|
||
|
||
CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb );
|
||
|
||
//
|
||
// Set the appropriate cache flags for a user file object.
|
||
//
|
||
|
||
if (TypeOfOpen == UserFileOpen) {
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) {
|
||
|
||
SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
|
||
|
||
} else {
|
||
|
||
SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the open and cleanup counts. Check the fast io state here.
|
||
//
|
||
|
||
CdLockVcb( IrpContext, Vcb );
|
||
|
||
CdIncrementCleanupCounts( IrpContext, Fcb );
|
||
CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 );
|
||
|
||
if (LockVolume) {
|
||
|
||
Vcb->VolumeLockFileObject = IrpSp->FileObject;
|
||
SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
|
||
}
|
||
|
||
CdUnlockVcb( IrpContext, Vcb );
|
||
|
||
CdLockFcb( IrpContext, Fcb );
|
||
|
||
if (TypeOfOpen == UserFileOpen) {
|
||
|
||
Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb );
|
||
|
||
} else {
|
||
|
||
Fcb->IsFastIoPossible = FastIoIsNotPossible;
|
||
}
|
||
|
||
CdUnlockFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// Show that we opened the file.
|
||
//
|
||
|
||
IrpContext->Irp->IoStatus.Information = Information;
|
||
|
||
//
|
||
// Point to the section object pointer in the non-paged Fcb.
|
||
//
|
||
|
||
IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject;
|
||
return OplockStatus;
|
||
}
|
||
|
||
|
||
|
||
|