2313 lines
64 KiB
C
2313 lines
64 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Create.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File Create routine for Udfs called by the
|
||
Fsd/Fsp dispatch routines.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Dan Lovinger [DanLo] 9-October-1996
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "UdfProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (UDFS_BUG_CHECK_CREATE)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (UDFS_DEBUG_LEVEL_CREATE)
|
||
|
||
//
|
||
// Local support routines
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfNormalizeFileNames (
|
||
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 PUNICODE_STRING RemainingName
|
||
);
|
||
|
||
NTSTATUS
|
||
UdfOpenExistingFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PLCB OpenLcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
UdfOpenObjectByFileId (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb
|
||
);
|
||
|
||
NTSTATUS
|
||
UdfOpenObjectFromDirContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN BOOLEAN PerformUserOpen,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
UdfCompleteFcbOpen (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PLCB OpenLcb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN ULONG UserCcbFlags,
|
||
IN ACCESS_MASK DesiredAccess
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, UdfCommonCreate)
|
||
#pragma alloc_text(PAGE, UdfCompleteFcbOpen)
|
||
#pragma alloc_text(PAGE, UdfNormalizeFileNames)
|
||
#pragma alloc_text(PAGE, UdfOpenObjectByFileId)
|
||
#pragma alloc_text(PAGE, UdfOpenExistingFcb)
|
||
#pragma alloc_text(PAGE, UdfOpenObjectFromDirContext)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
UdfCommonCreate (
|
||
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 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;
|
||
|
||
DIR_ENUM_CONTEXT DirContext;
|
||
BOOLEAN CleanupDirContext = FALSE;
|
||
|
||
BOOLEAN FoundEntry;
|
||
|
||
PVCB Vcb;
|
||
|
||
BOOLEAN OpenByFileId;
|
||
BOOLEAN IgnoreCase;
|
||
ULONG CreateDisposition;
|
||
|
||
BOOLEAN ShortNameMatch;
|
||
|
||
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.
|
||
//
|
||
// CurrentLcb - represents the name of the CurrentFcb.
|
||
//
|
||
|
||
TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject;
|
||
PFILE_OBJECT RelatedFileObject;
|
||
PCCB RelatedCcb = NULL;
|
||
|
||
PFCB NextFcb;
|
||
PFCB CurrentFcb = NULL;
|
||
|
||
PLCB CurrentLcb = 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.
|
||
//
|
||
|
||
PUNICODE_STRING FileName;
|
||
PUNICODE_STRING RelatedFileName;
|
||
|
||
UNICODE_STRING RemainingName;
|
||
UNICODE_STRING FinalName;
|
||
|
||
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) {
|
||
|
||
UdfCompleteRequest( 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)) {
|
||
|
||
UdfCompleteRequest( 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 = UdfDecodeFileObject( RelatedFileObject, &NextFcb, &RelatedCcb );
|
||
|
||
//
|
||
// Fail the request if this is not a user file object.
|
||
//
|
||
|
||
if (RelatedTypeOfOpen < UserVolumeOpen) {
|
||
|
||
UdfCompleteRequest( 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 = UdfNormalizeFileNames( IrpContext,
|
||
Vcb,
|
||
OpenByFileId,
|
||
IgnoreCase,
|
||
RelatedTypeOfOpen,
|
||
RelatedCcb,
|
||
RelatedFileName,
|
||
FileName,
|
||
&RemainingName );
|
||
|
||
//
|
||
// Return the error code if not successful.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
UdfCompleteRequest( 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;
|
||
UdfAcquireVcbExclusive( IrpContext, Vcb, FALSE );
|
||
|
||
} else {
|
||
|
||
UdfAcquireVcbShared( 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.
|
||
//
|
||
|
||
UdfVerifyVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// If the Vcb is locked then we cannot open another file
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) {
|
||
|
||
try_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If we are opening this file by FileId then process this immediately
|
||
// and exit.
|
||
//
|
||
|
||
if (OpenByFileId) {
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_leave( Status = UdfOpenObjectByFileId( 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_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If they wanted to open a directory, surprise.
|
||
//
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_leave( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// Acquire the Fcb first.
|
||
//
|
||
|
||
CurrentFcb = Vcb->VolumeDasdFcb;
|
||
UdfAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE );
|
||
|
||
try_leave( Status = UdfOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
NULL,
|
||
UserVolumeOpen,
|
||
FALSE,
|
||
NULL ));
|
||
}
|
||
|
||
//
|
||
// Acquire the Fcb at the beginning of our search to keep it from being
|
||
// deleted beneath us.
|
||
//
|
||
|
||
UdfAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Do a prefix search if there is more of the name to parse.
|
||
//
|
||
|
||
if (RemainingName.Length != 0) {
|
||
|
||
//
|
||
// Do the prefix search to find the longest matching name.
|
||
//
|
||
|
||
CurrentLcb = UdfFindPrefix( IrpContext,
|
||
&CurrentFcb,
|
||
&RemainingName,
|
||
IgnoreCase );
|
||
}
|
||
|
||
//
|
||
// At this point CurrentFcb points at the lowest Fcb in the tree for this
|
||
// file name, CurrentLcb is that name, and RemainingName is the rest of the
|
||
// name we have to do any directory traversals for.
|
||
//
|
||
|
||
//
|
||
// If the remaining name length is zero then we have found our
|
||
// target.
|
||
//
|
||
|
||
if (RemainingName.Length == 0) {
|
||
|
||
//
|
||
// If this is a file so verify the user didn't want to open
|
||
// a directory.
|
||
//
|
||
|
||
if (SafeNodeType( CurrentFcb ) == UDFS_NTC_FCB_DATA) {
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_leave( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_leave( Status = UdfOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
CurrentLcb,
|
||
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_leave( 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_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
try_leave( Status = UdfOpenExistingFcb( IrpContext,
|
||
IrpSp,
|
||
&CurrentFcb,
|
||
CurrentLcb,
|
||
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_leave( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// If we can't wait then post this request.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
//
|
||
// Prepare the enumeration context for use.
|
||
//
|
||
|
||
UdfInitializeDirContext( IrpContext, &DirContext );
|
||
CleanupDirContext = TRUE;
|
||
|
||
while (TRUE) {
|
||
|
||
ShortNameMatch = FALSE;
|
||
|
||
//
|
||
// Split off the next component from the name.
|
||
//
|
||
|
||
UdfDissectName( IrpContext,
|
||
&RemainingName,
|
||
&FinalName );
|
||
|
||
//
|
||
// Go ahead and look this entry up in the directory.
|
||
//
|
||
|
||
FoundEntry = UdfFindDirEntry( IrpContext,
|
||
CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
FALSE,
|
||
&DirContext );
|
||
|
||
//
|
||
// If we didn't find the entry then check if the current name
|
||
// is a possible short name.
|
||
//
|
||
|
||
if (!FoundEntry && UdfCandidateShortName( IrpContext, &FinalName)) {
|
||
|
||
//
|
||
// If the name looks like it could be a short name, try to find
|
||
// a matching real directory entry.
|
||
//
|
||
|
||
ShortNameMatch =
|
||
FoundEntry = UdfFindDirEntry( IrpContext,
|
||
CurrentFcb,
|
||
&FinalName,
|
||
IgnoreCase,
|
||
TRUE,
|
||
&DirContext );
|
||
}
|
||
|
||
//
|
||
// If we didn't find a match then check what the caller was trying to do to
|
||
// determine which error code to return.
|
||
//
|
||
|
||
if (!FoundEntry) {
|
||
|
||
if ((CreateDisposition == FILE_OPEN) ||
|
||
(CreateDisposition == FILE_OVERWRITE)) {
|
||
|
||
try_leave( Status = STATUS_OBJECT_NAME_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// Any other operation return STATUS_ACCESS_DENIED.
|
||
//
|
||
|
||
try_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// If this is an ignore case open then copy the exact case
|
||
// in the file object name.
|
||
//
|
||
|
||
if (IgnoreCase && !ShortNameMatch) {
|
||
|
||
ASSERT( FinalName.Length == DirContext.ObjectName.Length );
|
||
|
||
RtlCopyMemory( FinalName.Buffer,
|
||
DirContext.ObjectName.Buffer,
|
||
DirContext.ObjectName.Length );
|
||
}
|
||
|
||
//
|
||
// If we have found the last component then break out to open for the caller.
|
||
//
|
||
|
||
if (RemainingName.Length == 0) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// The object we just found must be a directory.
|
||
//
|
||
|
||
if (!FlagOn( DirContext.Fid->Flags, NSR_FID_F_DIRECTORY )) {
|
||
|
||
try_leave( Status = STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// Now open an Fcb for this intermediate index Fcb.
|
||
//
|
||
|
||
UdfOpenObjectFromDirContext( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb,
|
||
ShortNameMatch,
|
||
IgnoreCase,
|
||
&DirContext,
|
||
FALSE,
|
||
NULL );
|
||
}
|
||
|
||
//
|
||
// Make sure our opener is about to get what they expect.
|
||
//
|
||
|
||
if ((FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) &&
|
||
!FlagOn( DirContext.Fid->Flags, NSR_FID_F_DIRECTORY )) {
|
||
|
||
try_leave( Status = STATUS_NOT_A_DIRECTORY );
|
||
|
||
}
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE ) &&
|
||
FlagOn( DirContext.Fid->Flags, NSR_FID_F_DIRECTORY )) {
|
||
|
||
try_leave( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// The only create disposition we allow is OPEN.
|
||
//
|
||
|
||
if ((CreateDisposition != FILE_OPEN) &&
|
||
(CreateDisposition != FILE_OPEN_IF)) {
|
||
|
||
try_leave( Status = STATUS_ACCESS_DENIED );
|
||
}
|
||
|
||
//
|
||
// Open the object for the caller.
|
||
//
|
||
|
||
try_leave( Status = UdfOpenObjectFromDirContext( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
&CurrentFcb,
|
||
ShortNameMatch,
|
||
IgnoreCase,
|
||
&DirContext,
|
||
TRUE,
|
||
RelatedCcb ));
|
||
} finally {
|
||
|
||
//
|
||
// Cleanup the enumeration context if initialized.
|
||
//
|
||
|
||
if (CleanupDirContext) {
|
||
|
||
UdfCleanupDirContext( IrpContext, &DirContext );
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
UdfTeardownStructures( IrpContext, CurrentFcb, FALSE, &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) {
|
||
|
||
UdfReleaseFcb( IrpContext, CurrentFcb );
|
||
}
|
||
|
||
//
|
||
// Release the Vcb.
|
||
//
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// Call our completion routine. It will handle the case where either
|
||
// the Irp and/or IrpContext are gone.
|
||
//
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfNormalizeFileNames (
|
||
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 PUNICODE_STRING 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( UdfPagedPool,
|
||
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->MaximumLength =
|
||
RemainingName->Length = (USHORT) RemainingNameLength;
|
||
|
||
RemainingName->Buffer = Add2Ptr( FileName->Buffer,
|
||
RelatedNameLength + SeparatorLength,
|
||
PWCHAR );
|
||
|
||
//
|
||
// Upcase the name if necessary.
|
||
//
|
||
|
||
if (IgnoreCase && (RemainingNameLength != 0)) {
|
||
|
||
UdfUpcaseName( IrpContext,
|
||
RemainingName,
|
||
RemainingName );
|
||
}
|
||
|
||
//
|
||
// Do a quick check to make sure there are no wildcards.
|
||
//
|
||
|
||
if (FsRtlDoesNameContainWildCards( RemainingName )) {
|
||
|
||
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;
|
||
|
||
//
|
||
// Nothing to do if the name length is zero.
|
||
//
|
||
|
||
if (RemainingName->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->Buffer = Add2Ptr( RemainingName->Buffer,
|
||
RelatedFileName->Length,
|
||
PWCHAR );
|
||
|
||
RemainingName->Length -= RelatedFileName->Length;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we are pointing at a separator character then step past that.
|
||
//
|
||
|
||
if (RemainingName->Length != 0) {
|
||
|
||
if (*(RemainingName->Buffer) == L'\\') {
|
||
|
||
RemainingName->Buffer = Add2Ptr( RemainingName->Buffer,
|
||
sizeof( WCHAR ),
|
||
PWCHAR );
|
||
|
||
RemainingName->Length -= sizeof( WCHAR );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Upcase the name if necessary.
|
||
//
|
||
|
||
if (IgnoreCase && (RemainingName->Length != 0)) {
|
||
|
||
UdfUpcaseName( IrpContext,
|
||
RemainingName,
|
||
RemainingName );
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfOpenObjectByFileId (
|
||
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 take what is effectively a wild-a** guess.
|
||
Since we would need more than 64bits to contain the root extent length along
|
||
with the partition, lbn and dir/file flag we have to speculate that the
|
||
opener knows what they are doing and try to crack an ICB hierarchy at the
|
||
specified location. This can fail for any number of reasons, which then have
|
||
to be mapped to an open failure.
|
||
|
||
If found then build the Fcb from this entry and store the new Fcb in the
|
||
tree.
|
||
|
||
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;
|
||
BOOLEAN FcbExisted;
|
||
|
||
ICB_SEARCH_CONTEXT IcbContext;
|
||
BOOLEAN CleanupIcbContext = FALSE;
|
||
|
||
NODE_TYPE_CODE NodeTypeCode;
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
|
||
FILE_ID FileId;
|
||
|
||
PFCB NextFcb = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_VCB( Vcb );
|
||
|
||
//
|
||
// Extract the FileId from the FileObject.
|
||
//
|
||
|
||
RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID ));
|
||
|
||
//
|
||
// Now do a quick check that the reserved, unused chunk of the fileid is
|
||
// unused in this specimen.
|
||
//
|
||
|
||
if (UdfGetFidReservedZero( FileId )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Go ahead and figure out the TypeOfOpen and NodeType. We can
|
||
// get these from the input FileId.
|
||
//
|
||
|
||
if (UdfIsFidDirectory( FileId )) {
|
||
|
||
TypeOfOpen = UserDirectoryOpen;
|
||
NodeTypeCode = UDFS_NTC_FCB_INDEX;
|
||
|
||
} else {
|
||
|
||
TypeOfOpen = UserFileOpen;
|
||
NodeTypeCode = UDFS_NTC_FCB_DATA;
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Acquire the Vcb and check if there is already an Fcb.
|
||
// If not we will need to carefully hunt for the on-disc
|
||
// structures.
|
||
//
|
||
// We will post the request if we don't find the Fcb and this
|
||
// request can't wait.
|
||
//
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
NextFcb = UdfCreateFcb( IrpContext, FileId, NodeTypeCode, &FcbExisted );
|
||
|
||
//
|
||
// Now, if the Fcb was not already here we have some work to do.
|
||
//
|
||
|
||
if (!FcbExisted) {
|
||
|
||
//
|
||
// If we can't wait then post this request.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to transform errors we get as a result of going
|
||
// off on a wild goose chase into a simple open failure.
|
||
//
|
||
|
||
try {
|
||
|
||
NextFcb->FileId = FileId;
|
||
|
||
UdfInitializeIcbContextFromFcb( IrpContext, &IcbContext, NextFcb );
|
||
CleanupIcbContext = TRUE;
|
||
|
||
UdfLookupActiveIcb( IrpContext,
|
||
&IcbContext,
|
||
NextFcb->RootExtentLength);
|
||
|
||
UdfInitializeFcbFromIcbContext( IrpContext,
|
||
NextFcb,
|
||
&IcbContext,
|
||
NULL);
|
||
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
CleanupIcbContext = FALSE;
|
||
|
||
} except( UdfExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
//
|
||
// Any error we receive is an indication that the given fileid is
|
||
// not valid.
|
||
//
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Do a little dance to leave the exception handler if we had problems.
|
||
//
|
||
|
||
if (Status == STATUS_INVALID_PARAMETER) {
|
||
|
||
try_leave( NOTHING );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have the Fcb. Check that the type of the file is compatible with
|
||
// the desired type of file to open.
|
||
//
|
||
|
||
if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
|
||
|
||
if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
|
||
|
||
try_leave( Status = STATUS_FILE_IS_A_DIRECTORY );
|
||
}
|
||
|
||
} else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
|
||
|
||
try_leave( Status = STATUS_NOT_A_DIRECTORY );
|
||
}
|
||
|
||
//
|
||
// 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, the
|
||
// Vcb and then dereference the Fcb.
|
||
//
|
||
|
||
if (!UdfAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
UdfAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
}
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Move to this Fcb.
|
||
//
|
||
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Check the requested access on this Fcb.
|
||
//
|
||
|
||
if (!UdfIllegalFcbAccess( IrpContext,
|
||
TypeOfOpen,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
Status = UdfCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
CurrentFcb,
|
||
NULL,
|
||
TypeOfOpen,
|
||
CCB_FLAG_OPEN_BY_ID,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (UnlockVcb) {
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
if (CleanupIcbContext) {
|
||
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
}
|
||
|
||
//
|
||
// Destroy the new Fcb if it was not fully initialized.
|
||
//
|
||
|
||
if (NextFcb && !FlagOn( NextFcb->FcbState, FCB_STATE_INITIALIZED )) {
|
||
|
||
UdfDeleteFcb( IrpContext, NextFcb );
|
||
}
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfOpenExistingFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PLCB OpenLcb,
|
||
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.
|
||
|
||
OpenLcb - Lcb used to find this Fcb.
|
||
|
||
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 inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_EXCLUSIVE_FCB( *CurrentFcb );
|
||
ASSERT_OPTIONAL_CCB( RelatedCcb );
|
||
|
||
//
|
||
// Check that the desired access is legal.
|
||
//
|
||
|
||
if (!UdfIllegalFcbAccess( 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 )) {
|
||
|
||
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 = UdfCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
(*CurrentFcb)->Vcb,
|
||
CurrentFcb,
|
||
OpenLcb,
|
||
TypeOfOpen,
|
||
CcbFlags,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfOpenObjectFromDirContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN BOOLEAN ShortNameMatch,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN BOOLEAN PerformUserOpen,
|
||
IN PCCB RelatedCcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to open an object found in a directory scan. This
|
||
can be a directory or a file as indicated in the scan's results.
|
||
|
||
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.
|
||
Lookup the active ICB, 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.
|
||
|
||
ShortNameMatch - Indicates whether this object was opened by the shortname.
|
||
|
||
IgnoreCase - Indicates the case sensitivity of the caller.
|
||
|
||
DirContext - This is the context used to find the object.
|
||
|
||
PerformUserOpen - Indicates if we are at the object the user wants to finally open.
|
||
|
||
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 = NULL;
|
||
PFCB ParentFcb = NULL;
|
||
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
NODE_TYPE_CODE NodeTypeCode;
|
||
|
||
ICB_SEARCH_CONTEXT IcbContext;
|
||
BOOLEAN CleanupIcbContext = FALSE;
|
||
|
||
PLCB OpenLcb;
|
||
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Figure out what kind of open we will be performing here. The caller has already insured
|
||
// that the user is expecting us to do this.
|
||
//
|
||
|
||
if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_DIRECTORY )) {
|
||
|
||
TypeOfOpen = UserDirectoryOpen;
|
||
NodeTypeCode = UDFS_NTC_FCB_INDEX;
|
||
|
||
} else {
|
||
|
||
TypeOfOpen = UserFileOpen;
|
||
NodeTypeCode = UDFS_NTC_FCB_DATA;
|
||
}
|
||
|
||
//
|
||
// Check for illegal access to this file.
|
||
//
|
||
|
||
if (PerformUserOpen &&
|
||
UdfIllegalFcbAccess( IrpContext,
|
||
TypeOfOpen,
|
||
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 )) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
|
||
}
|
||
|
||
if (IgnoreCase) {
|
||
|
||
SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
|
||
}
|
||
|
||
//
|
||
// Build the file Id for this object.
|
||
//
|
||
|
||
UdfSetFidFromLbAddr( FileId, DirContext->Fid->Icb.Start );
|
||
|
||
if (TypeOfOpen == UserDirectoryOpen) {
|
||
|
||
UdfSetFidDirectory( FileId );
|
||
}
|
||
|
||
//
|
||
// Lock the Vcb so we can examine the Fcb Table.
|
||
//
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
UnlockVcb = TRUE;
|
||
|
||
//
|
||
// Get the Fcb for this file.
|
||
//
|
||
|
||
NextFcb = UdfCreateFcb( IrpContext, FileId, NodeTypeCode, &FcbExisted );
|
||
|
||
//
|
||
// If the Fcb was created here then initialize from the values in the
|
||
// dirent. We have optimistically assumed that there isn't any corrupt
|
||
// information to this point - we're about to discover it if there is.
|
||
//
|
||
|
||
if (!FcbExisted) {
|
||
|
||
//
|
||
// Set the root extent length and go get the active ICB, initialize.
|
||
//
|
||
|
||
NextFcb->RootExtentLength = DirContext->Fid->Icb.Length.Length;
|
||
|
||
UdfInitializeIcbContextFromFcb( IrpContext, &IcbContext, NextFcb );
|
||
CleanupIcbContext = TRUE;
|
||
|
||
UdfLookupActiveIcb( IrpContext,
|
||
&IcbContext,
|
||
NextFcb->RootExtentLength );
|
||
|
||
UdfInitializeFcbFromIcbContext( IrpContext,
|
||
NextFcb,
|
||
&IcbContext,
|
||
*CurrentFcb);
|
||
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
CleanupIcbContext = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Now try to acquire the new Fcb without waiting. We will reference
|
||
// the Fcb and retry with wait if unsuccessful.
|
||
//
|
||
|
||
if (!UdfAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
|
||
|
||
NextFcb->FcbReference += 1;
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
UdfReleaseFcb( IrpContext, *CurrentFcb );
|
||
UdfAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
|
||
UdfAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
NextFcb->FcbReference -= 1;
|
||
}
|
||
|
||
//
|
||
// Move down to this new Fcb. Remember that we still own the parent however.
|
||
//
|
||
|
||
ParentFcb = *CurrentFcb;
|
||
*CurrentFcb = NextFcb;
|
||
|
||
//
|
||
// Store this name into the prefix table for the parent.
|
||
//
|
||
|
||
OpenLcb = UdfInsertPrefix( IrpContext,
|
||
NextFcb,
|
||
( ShortNameMatch?
|
||
&DirContext->ShortObjectName :
|
||
&DirContext->CaseObjectName ),
|
||
ShortNameMatch,
|
||
IgnoreCase,
|
||
ParentFcb );
|
||
|
||
//
|
||
// Now increment the reference counts for the parent and drop the Vcb.
|
||
//
|
||
|
||
DebugTrace(( +1, Dbg,
|
||
"UdfOpenObjectFromDirContext, PFcb %08x Vcb %d/%d Fcb %d/%d\n", ParentFcb,
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
ParentFcb->FcbReference,
|
||
ParentFcb->FcbUserReference ));
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, ParentFcb, 1, 1 );
|
||
|
||
DebugTrace(( -1, Dbg,
|
||
"UdfOpenObjectFromDirContext, Vcb %d/%d Fcb %d/%d\n",
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
ParentFcb->FcbReference,
|
||
ParentFcb->FcbUserReference ));
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
|
||
//
|
||
// Perform initialization associated with the directory context.
|
||
//
|
||
|
||
UdfInitializeLcbFromDirContext( IrpContext,
|
||
OpenLcb,
|
||
DirContext );
|
||
|
||
//
|
||
// If we just opened VIDEO_TS directory, on a UDF1.02 file system,
|
||
// then mark the Fcb to allow the >=1Gb single AD workaround
|
||
// to be used on it's children (works around some corrupt DVD-Videos)
|
||
//
|
||
|
||
if ((NextFcb->NodeTypeCode == UDFS_NTC_FCB_INDEX) &&
|
||
(ParentFcb == Vcb->RootIndexFcb) &&
|
||
(Vcb->UdfRevision == UDF_VERSION_102) &&
|
||
(OpenLcb->FileName.Length == 16) &&
|
||
(!_wcsnicmp( OpenLcb->FileName.Buffer, L"VIDEO_TS", 8))) {
|
||
|
||
DebugTrace(( 0, Dbg, "Enabled >= 1gig AD workaround\n"));
|
||
|
||
SetFlag( NextFcb->FcbState, FCB_STATE_ALLOW_ONEGIG_WORKAROUND);
|
||
}
|
||
|
||
//
|
||
// Release the parent Fcb at this point.
|
||
//
|
||
|
||
UdfReleaseFcb( IrpContext, ParentFcb );
|
||
ParentFcb = NULL;
|
||
|
||
//
|
||
// Call our worker routine to complete the open.
|
||
//
|
||
|
||
if (PerformUserOpen) {
|
||
|
||
Status = UdfCompleteFcbOpen( IrpContext,
|
||
IrpSp,
|
||
Vcb,
|
||
CurrentFcb,
|
||
OpenLcb,
|
||
TypeOfOpen,
|
||
CcbFlags,
|
||
IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Unlock the Vcb if held.
|
||
//
|
||
|
||
if (UnlockVcb) {
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Release the parent if held.
|
||
//
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
UdfReleaseFcb( IrpContext, ParentFcb );
|
||
}
|
||
|
||
//
|
||
// Destroy the new Fcb if it was not fully initialized.
|
||
//
|
||
|
||
if (NextFcb && !FlagOn( NextFcb->FcbState, FCB_STATE_INITIALIZED )) {
|
||
|
||
UdfDeleteFcb( IrpContext, NextFcb );
|
||
}
|
||
|
||
//
|
||
// Clean up the Icb context if used.
|
||
//
|
||
|
||
if (CleanupIcbContext) {
|
||
|
||
UdfCleanupIcbContext( IrpContext, &IcbContext );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
UdfCompleteFcbOpen (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
PIO_STACK_LOCATION IrpSp,
|
||
IN PVCB Vcb,
|
||
IN OUT PFCB *CurrentFcb,
|
||
IN PLCB OpenLcb OPTIONAL,
|
||
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.
|
||
|
||
OpenLcb - Lcb this Fcb is being opened by
|
||
|
||
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 )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_CANT_WAIT );
|
||
}
|
||
|
||
LockVolume = TRUE;
|
||
|
||
//
|
||
// Purge the volume and make sure all of the user references
|
||
// are gone.
|
||
//
|
||
|
||
Status = UdfPurgeVolume( IrpContext, Vcb, FALSE );
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now force all of the delayed close operations to go away.
|
||
//
|
||
|
||
UdfFspClose( Vcb );
|
||
|
||
if (Vcb->VcbUserReference > Vcb->VcbResidualUserReference) {
|
||
|
||
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,
|
||
UdfOplockComplete,
|
||
UdfPrePostIrp );
|
||
|
||
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,
|
||
UdfOplockComplete,
|
||
UdfPrePostIrp );
|
||
|
||
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 = UdfCreateCcb( IrpContext, Fcb, OpenLcb, 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.
|
||
//
|
||
|
||
UdfSetFileObject( 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 );
|
||
}
|
||
}
|
||
else if (TypeOfOpen == UserVolumeOpen) {
|
||
|
||
//
|
||
// DASD access is always noncached
|
||
//
|
||
|
||
SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
|
||
}
|
||
|
||
//
|
||
// Update the open and cleanup counts. Check the fast io state here.
|
||
//
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
UdfIncrementCleanupCounts( IrpContext, Fcb );
|
||
|
||
DebugTrace(( +1, Dbg,
|
||
"UdfCompleteFcbOpen, Fcb %08x Vcb %d/%d Fcb %d/%d\n", Fcb,
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
Fcb->FcbReference,
|
||
Fcb->FcbUserReference ));
|
||
|
||
UdfIncrementReferenceCounts( IrpContext, Fcb, 1, 1 );
|
||
|
||
DebugTrace(( -1, Dbg,
|
||
"UdfCompleteFcbOpen, Vcb %d/%d Fcb %d/%d\n",
|
||
Vcb->VcbReference,
|
||
Vcb->VcbUserReference,
|
||
Fcb->FcbReference,
|
||
Fcb->FcbUserReference ));
|
||
|
||
if (LockVolume) {
|
||
|
||
Vcb->VolumeLockFileObject = IrpSp->FileObject;
|
||
SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
|
||
}
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
UdfLockFcb( IrpContext, Fcb );
|
||
|
||
if (TypeOfOpen == UserFileOpen) {
|
||
|
||
Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
|
||
|
||
} else {
|
||
|
||
Fcb->IsFastIoPossible = FastIoIsNotPossible;
|
||
}
|
||
|
||
UdfUnlockFcb( 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;
|
||
}
|
||
|