1174 lines
35 KiB
C
1174 lines
35 KiB
C
//+----------------------------------------------------------------------------
|
||
//
|
||
// File: create.c
|
||
//
|
||
// Contents:
|
||
//
|
||
// This module implements the File Create routine for Dfs called by the
|
||
// dispatch driver. Unlike traditional disk-based FSDs, there is only
|
||
// one entry point, DfsFsdCreate. The request is assumed to be
|
||
// synchronous (whether the user thread requests it or not).
|
||
// Of course, since we will typically be calling out to some other
|
||
// FSD, that FSD may post the request and return to us with a
|
||
// STATUS_PENDING.
|
||
//
|
||
// Functions: DfsFsdCreate - FSD entry point for NtCreateFile/NtOpenFile
|
||
// DfsCommonCreate, local
|
||
// DfsPassThroughRelativeOpen, local
|
||
// DfsCompleteRelativeOpen, local
|
||
// DfsPostProcessRelativeOpen, local
|
||
// DfsRestartRelativeOpen, local
|
||
// DfsComposeFullName, local
|
||
// DfsAreFilesOnSameLocalVolume, local
|
||
//
|
||
// History: 27 Jan 1992 AlanW Created.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
#include "dfsprocs.h"
|
||
#include "dnr.h"
|
||
#include "fcbsup.h"
|
||
|
||
//
|
||
// The debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_CREATE)
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
static NTSTATUS
|
||
DfsCommonCreate (
|
||
OPTIONAL IN PIRP_CONTEXT IrpContext,
|
||
PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp);
|
||
|
||
IO_STATUS_BLOCK
|
||
DfsOpenDevice (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN USHORT ShareAccess,
|
||
IN ULONG CreateOptions);
|
||
|
||
NTSTATUS
|
||
DfsPassThroughRelativeOpen(
|
||
IN PIRP Irp,
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDFS_FCB ParentFcb);
|
||
|
||
NTSTATUS
|
||
DfsCompleteRelativeOpen(
|
||
IN PDEVICE_OBJECT pDevice,
|
||
IN PIRP Irp,
|
||
IN PVOID Context);
|
||
|
||
NTSTATUS
|
||
DfsPostProcessRelativeOpen(
|
||
IN PIRP Irp,
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDFS_FCB ParentFcb);
|
||
|
||
VOID
|
||
DfsRestartRelativeOpen(
|
||
IN PIRP_CONTEXT IrpContext);
|
||
|
||
NTSTATUS
|
||
DfsComposeFullName(
|
||
IN PUNICODE_STRING ParentName,
|
||
IN PUNICODE_STRING RelativeName,
|
||
OUT PUNICODE_STRING FullName);
|
||
|
||
NTSTATUS
|
||
DfsAreFilesOnSameLocalVolume(
|
||
IN PUNICODE_STRING ParentName,
|
||
IN PUNICODE_STRING FileName);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text( PAGE, DfsFsdCreate )
|
||
#pragma alloc_text( PAGE, DfsCommonCreate )
|
||
#pragma alloc_text( PAGE, DfsOpenDevice )
|
||
#pragma alloc_text( PAGE, DfsPassThroughRelativeOpen )
|
||
#pragma alloc_text( PAGE, DfsPostProcessRelativeOpen )
|
||
#pragma alloc_text( PAGE, DfsRestartRelativeOpen )
|
||
#pragma alloc_text( PAGE, DfsComposeFullName )
|
||
#pragma alloc_text( PAGE, DfsAreFilesOnSameLocalVolume )
|
||
|
||
//
|
||
// The following are not pageable since they can be called at DPC level
|
||
//
|
||
// DfsCompleteRelativeOpen
|
||
//
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------
|
||
//
|
||
// Function: DfsFsdCreate, public
|
||
//
|
||
// Synopsis: This routine implements the FSD part of the NtCreateFile
|
||
// and NtOpenFile API calls.
|
||
//
|
||
// Arguments: [DeviceObject] -- Supplies the device object where
|
||
// the file/directory exists that we are trying
|
||
// to open/create exists
|
||
// [Irp] - Supplies the Irp being processed
|
||
//
|
||
// Returns: NTSTATUS - The Fsd status for the Irp
|
||
//
|
||
//--------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsFsdCreate (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PDFS_FCB pFcb = NULL;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsFsdCreate: Entered\n", 0);
|
||
|
||
ASSERT(IoIsOperationSynchronous(Irp) == TRUE);
|
||
|
||
//
|
||
// Call the common create routine, with block allowed if the operation
|
||
// is synchronous.
|
||
//
|
||
|
||
try {
|
||
|
||
IrpContext = DfsCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
|
||
Status = DfsCommonCreate( IrpContext, DeviceObject, Irp );
|
||
|
||
} except( DfsExceptionFilter( IrpContext, GetExceptionCode(), GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = DfsProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsFsdCreate: Exit -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------
|
||
// Function: DfsCommonCreate, private
|
||
//
|
||
// Synopsis: This is the common routine for creating/opening a file
|
||
// called by both the FSD and FSP threads.
|
||
//
|
||
// Arguments: [DeviceObject] - The device object associated with
|
||
// the request.
|
||
// [Irp] -- Supplies the Irp to process
|
||
//
|
||
// Returns: NTSTATUS - the return status for the operation
|
||
//
|
||
//--------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsCommonCreate (
|
||
OPTIONAL IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
|
||
IO_STATUS_BLOCK Iosb;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
PDFS_VCB Vcb = NULL;
|
||
PDFS_FCB Fcb;
|
||
|
||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||
PFILE_OBJECT RelatedFileObject;
|
||
UNICODE_STRING FileName;
|
||
ACCESS_MASK DesiredAccess;
|
||
ULONG CreateOptions;
|
||
USHORT ShareAccess;
|
||
NTSTATUS Status;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsCommonCreate\n", 0 );
|
||
DfsDbgTrace( 0, Dbg, "Irp = %08lx\n", Irp );
|
||
DfsDbgTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags );
|
||
DfsDbgTrace( 0, Dbg, "->FileObject = %08lx\n", FileObject );
|
||
DfsDbgTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", FileObject->RelatedFileObject );
|
||
DfsDbgTrace( 0, Dbg, " ->FileName = %wZ\n", &FileObject->FileName );
|
||
DfsDbgTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
|
||
DfsDbgTrace( 0, Dbg, "->CreateOptions = %08lx\n", IrpSp->Parameters.Create.Options );
|
||
DfsDbgTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
|
||
DfsDbgTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
|
||
DfsDbgTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
|
||
|
||
//
|
||
// Reference our input parameters to make things easier
|
||
//
|
||
|
||
RelatedFileObject = IrpSp->FileObject->RelatedFileObject;
|
||
FileName = *((PUNICODE_STRING) &IrpSp->FileObject->FileName);
|
||
DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
|
||
CreateOptions = IrpSp->Parameters.Create.Options;
|
||
ShareAccess = IrpSp->Parameters.Create.ShareAccess;
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Short circuit known invalid opens.
|
||
//
|
||
|
||
if ((IrpSp->Flags & SL_OPEN_PAGING_FILE) != 0) {
|
||
|
||
DfsDbgTrace(0, Dbg,
|
||
"DfsCommonCreate: Paging file not allowed on Dfs\n", 0);
|
||
|
||
Iosb.Status = STATUS_INVALID_PARAMETER;
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, Iosb.Status );
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsCommonCreate: Exit -> %08lx\n", Iosb.Status);
|
||
|
||
return Iosb.Status;
|
||
|
||
}
|
||
|
||
//
|
||
// There are several cases we need to handle here.
|
||
//
|
||
// 1. FileName is 0 length
|
||
//
|
||
// If the filename length is 0, then someone really wants to open the
|
||
// device object itself.
|
||
//
|
||
// 2. This is a Relative open and the parent is on the same volume,
|
||
// either local or remote.
|
||
//
|
||
// We pass through the relative open to the driver that opened the
|
||
// parent.
|
||
//
|
||
// 3. This is a relative open and the parent is on a different volume.
|
||
//
|
||
// Form the full name of the file by concatenating the parent's
|
||
// name with the relative file name. Stick this name in the FileObject
|
||
// and do DNR on the full name.
|
||
//
|
||
// 4. This is a relative open and the parent is a device object (ie,
|
||
// the parent was opened via case 1)
|
||
//
|
||
// Assume the parent name is \, so concatenate \ with the relative
|
||
// file name. Stick this name in the FileObject and do DNR on the
|
||
// the full name.
|
||
//
|
||
// 5. This is an absolute open, (or a case 3/4 converted to an absolute
|
||
// open), and the SL_OPEN_TARGET_DIRECTORY bis *is* set.
|
||
//
|
||
// a. If the file's immediate parent directory is on the same local
|
||
// volume as the file, then do a regular DNR, and let the
|
||
// underlying FS handle the SL_OPEN_TARGET_DIRECTORY.
|
||
//
|
||
// b. If the file's immediate parent directory is on a local volume
|
||
// and the file is not on the same local volume, then immediately
|
||
// return STATUS_NOT_SAME_DEVICE.
|
||
//
|
||
// c. If the file's immediate parent directory is on a remote volume,
|
||
// then do a full DNR. This will pass through the
|
||
// SL_OPEN_TARGET_DIRECTORY to the remote Dfs driver, which will
|
||
// handle it as case 5a. or 5b.
|
||
//
|
||
// 6. This is an absolute open, (or a case 3/4 converted to an absolute
|
||
// open), and the SL_OPEN_TARGET_DIRECTORY bit is *not* set.
|
||
//
|
||
// Do a DNR on the FileObject's name.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Check to see if we are opening a device object. If so, and the
|
||
// file is being opened on the File system device object, it will
|
||
// only permit FsCtl and Close operations to be performed.
|
||
//
|
||
|
||
if (FileName.Length == 0 && RelatedFileObject == NULL) {
|
||
|
||
//
|
||
// This is case 1.
|
||
//
|
||
// In this case there had better be a DeviceObject
|
||
//
|
||
|
||
ASSERT(ARGUMENT_PRESENT(DeviceObject));
|
||
|
||
DfsDbgTrace(0, Dbg,
|
||
"DfsCommonCreate: Opening the device, DevObj = %08lx\n",
|
||
DeviceObject);
|
||
|
||
Iosb = DfsOpenDevice( IrpContext,
|
||
FileObject,
|
||
DeviceObject,
|
||
DesiredAccess,
|
||
ShareAccess,
|
||
CreateOptions);
|
||
|
||
Irp->IoStatus.Information = Iosb.Information;
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, Iosb.Status );
|
||
|
||
try_return( Iosb.Status );
|
||
|
||
}
|
||
|
||
if (DeviceObject != NULL && DeviceObject->DeviceType == FILE_DEVICE_DFS) {
|
||
Vcb = &(((PLOGICAL_ROOT_DEVICE_OBJECT)DeviceObject)->Vcb);
|
||
}
|
||
|
||
//
|
||
// If there is a related file object, then this is a relative open.
|
||
//
|
||
|
||
if (RelatedFileObject != NULL) {
|
||
|
||
//
|
||
// This is case 2, 3, or 4.
|
||
//
|
||
|
||
PDFS_VCB TempVcb;
|
||
TYPE_OF_OPEN OpenType;
|
||
UNICODE_STRING NewFileName;
|
||
|
||
OpenType = DfsDecodeFileObject( RelatedFileObject,
|
||
&TempVcb,
|
||
&Fcb);
|
||
|
||
if (OpenType == RedirectedFileOpen) {
|
||
|
||
DfsDbgTrace(0, Dbg, "Relative file open: DFS_FCB = %08x\n", Fcb);
|
||
DfsDbgTrace(0, Dbg, " Directory: %wZ\n", &Fcb->FullFileName);
|
||
DfsDbgTrace(0, Dbg, " Relative file: %wZ\n", &FileName);
|
||
|
||
//
|
||
// This is case 2.
|
||
//
|
||
|
||
DfsDbgTrace(0, Dbg,
|
||
"Trying pass through of relative open\n", 0);
|
||
|
||
Iosb.Status = DfsPassThroughRelativeOpen(
|
||
Irp,
|
||
IrpContext,
|
||
Fcb
|
||
);
|
||
|
||
try_return( Iosb.Status );
|
||
|
||
|
||
} else if (OpenType == LogicalRootDeviceOpen) {
|
||
|
||
//
|
||
// This is case 4.
|
||
//
|
||
// If the open is relative to a logical root open, then we
|
||
// are forced to convert it to an absolute open, since there
|
||
// is no underlying FS backing up the logical root to pass
|
||
// the relative open to first.
|
||
//
|
||
|
||
DfsDbgTrace( 0, Dbg, "DfsCommonCreate: Open relative to Logical Root\n", 0);
|
||
|
||
ASSERT (TempVcb == Vcb);
|
||
|
||
NewFileName.MaximumLength = sizeof (WCHAR) +
|
||
FileName.Length;
|
||
|
||
NewFileName.Buffer = (PWCHAR) ExAllocatePool(
|
||
NonPagedPool,
|
||
NewFileName.MaximumLength);
|
||
|
||
if (NewFileName.Buffer == NULL) {
|
||
|
||
Iosb.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, Iosb.Status );
|
||
|
||
try_return( Iosb.Status );
|
||
|
||
}
|
||
|
||
NewFileName.Buffer[0] = L'\\';
|
||
|
||
NewFileName.Length = sizeof (WCHAR);
|
||
|
||
} else {
|
||
|
||
DfsDbgTrace(0, Dbg, "DfsCommonCreate: Invalid related file object\n", 0);
|
||
|
||
try_return( Iosb.Status = STATUS_INVALID_HANDLE );
|
||
|
||
}
|
||
|
||
(void) DnrConcatenateFilePath (
|
||
&NewFileName,
|
||
FileName.Buffer,
|
||
FileName.Length);
|
||
|
||
if (IrpSp->FileObject->FileName.Buffer)
|
||
ExFreePool( IrpSp->FileObject->FileName.Buffer );
|
||
|
||
FileName = IrpSp->FileObject->FileName = NewFileName;
|
||
|
||
}
|
||
|
||
ASSERT(FileName.Length != 0);
|
||
|
||
//
|
||
// This is case 5b, 5c, or 6 - Do a full DNR.
|
||
//
|
||
|
||
Iosb.Status = DnrStartNameResolution(IrpContext, Irp, Vcb);
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsCommonCreate: Exit -> %08lx\n", Iosb.Status);
|
||
}
|
||
|
||
return Iosb.Status;
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------
|
||
//
|
||
// Function: DfsOpenDevice, local
|
||
//
|
||
// Synopsis: This routine opens the specified device for direct
|
||
// access.
|
||
//
|
||
// Arguments: [FileObject] - Supplies the File object
|
||
// [DeviceObject] - Supplies the object denoting the device
|
||
// being opened
|
||
// [DesiredAccess] - Supplies the desired access of the caller
|
||
// [ShareAccess] - Supplies the share access of the caller
|
||
// [CreateOptions] - Supplies the create options for
|
||
// this operation
|
||
//
|
||
// Returns: [IO_STATUS_BLOCK] - Returns the completion status for
|
||
// the operation
|
||
//
|
||
//--------------------------------------------------------------------
|
||
|
||
IO_STATUS_BLOCK
|
||
DfsOpenDevice (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN USHORT ShareAccess,
|
||
IN ULONG CreateOptions
|
||
) {
|
||
IO_STATUS_BLOCK Iosb;
|
||
PDFS_VCB Vcb = NULL;
|
||
|
||
//
|
||
// The following variables are for abnormal termination
|
||
//
|
||
BOOLEAN UnwindShareAccess = FALSE;
|
||
BOOLEAN UnwindVolumeLock = FALSE;
|
||
|
||
DfsDbgTrace( +1, Dbg, "DfsOpenDevice: Entered\n", 0 );
|
||
|
||
try {
|
||
//
|
||
// Check to see which type of device is being opened.
|
||
// We don't permit all open modes on the file system
|
||
// device object.
|
||
//
|
||
|
||
if (DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM ) {
|
||
ULONG CreateDisposition = (CreateOptions >> 24) & 0x000000ff;
|
||
|
||
//
|
||
// Check for proper desired access and rights
|
||
//
|
||
if (CreateDisposition != FILE_OPEN
|
||
&& CreateDisposition != FILE_OPEN_IF ) {
|
||
|
||
Iosb.Status = STATUS_ACCESS_DENIED;
|
||
try_return( Iosb );
|
||
}
|
||
|
||
//
|
||
// Check if we were to open a directory
|
||
//
|
||
|
||
if (CreateOptions & FILE_DIRECTORY_FILE) {
|
||
DfsDbgTrace(0, Dbg, "DfsOpenDevice: Cannot open device as a directory\n", 0);
|
||
|
||
Iosb.Status = STATUS_NOT_A_DIRECTORY;
|
||
try_return( Iosb );
|
||
}
|
||
|
||
DfsSetFileObject( FileObject,
|
||
FilesystemDeviceOpen,
|
||
DeviceObject
|
||
);
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
Iosb.Information = FILE_OPENED;
|
||
try_return( Iosb );
|
||
}
|
||
|
||
Vcb = & (((PLOGICAL_ROOT_DEVICE_OBJECT)DeviceObject)->Vcb);
|
||
|
||
//
|
||
// If the user does not want to share anything then we will try and
|
||
// take out a lock on the volume. We check if the volume is already
|
||
// in use, and if it is then we deny the open
|
||
//
|
||
|
||
if ((ShareAccess & (
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) == 0 ) {
|
||
|
||
if (Vcb->OpenFileCount != 0) {
|
||
|
||
Iosb.Status = STATUS_ACCESS_DENIED;
|
||
try_return( Iosb );
|
||
}
|
||
|
||
//
|
||
// Lock the volume
|
||
//
|
||
|
||
Vcb->VcbState |= VCB_STATE_FLAG_LOCKED;
|
||
Vcb->FileObjectWithVcbLocked = FileObject;
|
||
UnwindVolumeLock = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the volume is already opened by someone then we need to check
|
||
// the share access
|
||
//
|
||
|
||
if (Vcb->DirectAccessOpenCount > 0) {
|
||
|
||
if ( !NT_SUCCESS( Iosb.Status
|
||
= IoCheckShareAccess( DesiredAccess,
|
||
ShareAccess,
|
||
FileObject,
|
||
&Vcb->ShareAccess,
|
||
TRUE ))) {
|
||
|
||
try_return( Iosb );
|
||
}
|
||
|
||
} else {
|
||
|
||
IoSetShareAccess( DesiredAccess,
|
||
ShareAccess,
|
||
FileObject,
|
||
&Vcb->ShareAccess );
|
||
}
|
||
|
||
UnwindShareAccess = TRUE;
|
||
|
||
//
|
||
// Setup the context pointers, and update
|
||
// our reference counts
|
||
//
|
||
|
||
DfsSetFileObject( FileObject,
|
||
LogicalRootDeviceOpen,
|
||
Vcb
|
||
);
|
||
|
||
ExInterlockedIncrementLong(&Vcb->DirectAccessOpenCount, &DfsData.DfsLock);
|
||
ExInterlockedIncrementLong(&Vcb->OpenFileCount, &DfsData.DfsLock);
|
||
|
||
//
|
||
// And set our status to success
|
||
//
|
||
|
||
Iosb.Status = STATUS_SUCCESS;
|
||
Iosb.Information = FILE_OPENED;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// If this is an abnormal termination then undo our work
|
||
//
|
||
|
||
if (AbnormalTermination()) {
|
||
|
||
if (UnwindShareAccess) {
|
||
IoRemoveShareAccess( FileObject, &Vcb->ShareAccess );
|
||
}
|
||
|
||
if (UnwindVolumeLock) {
|
||
Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED;
|
||
Vcb->FileObjectWithVcbLocked = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsOpenDevice: Exit -> Iosb.Status = %08lx\n", Iosb.Status);
|
||
}
|
||
|
||
return Iosb;
|
||
}
|
||
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsPassThroughRelativeOpen
|
||
//
|
||
// Synopsis: Passes through a relative open call to the device handling
|
||
// the parent. This is required for structured storages on OFS
|
||
// to work, for replication's Do-not-cross-JP sematics to work,
|
||
// and as an optimization.
|
||
//
|
||
// Arguments: [Irp] -- The open Irp, which we will pass through.
|
||
// [IrpContext] -- Associated with the above Irp.
|
||
// [ParentFcb] -- Fcb of related file object.
|
||
//
|
||
// Returns: Status returned by the underlying FS, or by DNR if
|
||
// the underlying FS complained about STATUS_DFS_EXIT_PATH_FOUND.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsPassThroughRelativeOpen(
|
||
IN PIRP Irp,
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDFS_FCB ParentFcb)
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp, NextIrpSp;
|
||
PDFS_FCB NewFcb;
|
||
UNICODE_STRING NewFileName;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsPassThroughRelativeOpen: Entered\n", 0);
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Prepare to pass the request to the device handling the parent open.
|
||
//
|
||
|
||
//
|
||
// First, we preallocate an DFS_FCB, assuming that the relative open will
|
||
// succeed. We need to do this at this point in time because the
|
||
// FileObject->FileName is still intact; after we pass through, the
|
||
// underlying can do as it wishes with the FileName field, and we will
|
||
// be unable to construct the full file name for the DFS_FCB.
|
||
//
|
||
|
||
Status = DfsComposeFullName(
|
||
&ParentFcb->FullFileName,
|
||
&IrpSp->FileObject->FileName,
|
||
&NewFileName);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
DfsDbgTrace(-1, Dbg, "DfsPassThroughRelativeOpen: Unable to create full Name %08lx\n", Status );
|
||
DfsCompleteRequest( IrpContext, Irp, Status );
|
||
return( Status );
|
||
}
|
||
|
||
|
||
NewFcb = DfsCreateFcb( NULL, ParentFcb->Vcb, &NewFileName );
|
||
|
||
NewFcb->TargetDevice = ParentFcb->TargetDevice;
|
||
|
||
NewFcb->ProviderId = ParentFcb->ProviderId;
|
||
|
||
NewFcb->DfsMachineEntry = ParentFcb->DfsMachineEntry;
|
||
|
||
NewFcb->FileObject = IrpSp->FileObject;
|
||
|
||
if (ParentFcb->ProviderId == PROV_ID_DFS_RDR) {
|
||
IrpSp->FileObject->FsContext2 = (PVOID) DFS_OPEN_CONTEXT;
|
||
}
|
||
|
||
ExFreePool( NewFileName.Buffer );
|
||
|
||
if (NewFcb == NULL) {
|
||
DfsDbgTrace(-1, Dbg, "DfsPassThroughRelativeOpen: Unable to create DFS_FCB\n", 0);
|
||
DfsCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// Next, setup the IRP stack location
|
||
//
|
||
|
||
NextIrpSp = IoGetNextIrpStackLocation(Irp);
|
||
(*NextIrpSp) = (*IrpSp);
|
||
|
||
//
|
||
// Put the parent DFS_FCB pointer in the IrpContext.
|
||
//
|
||
|
||
IrpContext->Context = (PVOID) NewFcb;
|
||
|
||
IoSetCompletionRoutine(
|
||
Irp,
|
||
DfsCompleteRelativeOpen,
|
||
IrpContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
Status = IoCallDriver( ParentFcb->TargetDevice, Irp );
|
||
|
||
DfsDbgTrace(0, Dbg, "IoCallDriver returned %08lx\n", Status);
|
||
|
||
if (Status != STATUS_PENDING) {
|
||
|
||
Status = DfsPostProcessRelativeOpen(
|
||
Irp,
|
||
IrpContext,
|
||
NewFcb);
|
||
|
||
}
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsPassThroughRelativeOpen: Exited %08lx\n", Status);
|
||
|
||
return( Status );
|
||
|
||
}
|
||
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsCompleteRelativeOpen
|
||
//
|
||
// Synopsis: Completion routine for DfsPassThroughRelativeOpen. It is
|
||
// interesting only in case where STATUS_PENDING was originally
|
||
// returned from IoCallDriver. If so, then this routine simply
|
||
// queues DfsRestartRelativeOpen to a work queue. Note that it
|
||
// must queue an item at this stage instead of doing the work
|
||
// itself because this routine is executed at DPC level.
|
||
//
|
||
// Arguments: [pDevice] -- Our device object.
|
||
// [Irp] -- The Irp being completed.
|
||
// [IrpContext] -- Context associated with Irp.
|
||
//
|
||
// Returns: STATUS_MORE_PROCESSING_REQUIRED. Either the posted routine
|
||
// or DfsPassThroughRelativeOpen must complete the IRP for real.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsCompleteRelativeOpen(
|
||
IN PDEVICE_OBJECT pDevice,
|
||
IN PIRP Irp,
|
||
IN PIRP_CONTEXT IrpContext)
|
||
{
|
||
|
||
DfsDbgTrace( +1, Dbg, "DfsCompleteRelativeOpen: Entered\n", 0);
|
||
|
||
//
|
||
// We are only interested in the case when the pass through of relative
|
||
// opens returned STATUS_PENDING. In that case, the original thread has
|
||
// popped all the way back to the caller of NtCreateFile, and we need
|
||
// to finish the open in an asynchronous manner.
|
||
//
|
||
|
||
if (Irp->PendingReturned) {
|
||
|
||
DfsDbgTrace(0, Dbg, "Pending returned : Queuing DfsRestartRelativeOpen\n", 0);
|
||
|
||
//
|
||
// We need to call IpMarkIrpPending so the IoSubsystem will realize
|
||
// that our FSD routine returned STATUS_PENDING. We can't call this
|
||
// from the FSD routine itself because the FSD routine doesn't have
|
||
// access to the stack location when the underlying guy returns
|
||
// STATUS_PENDING
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
ExInitializeWorkItem( &(IrpContext->WorkQueueItem),
|
||
DfsRestartRelativeOpen,
|
||
(PVOID) IrpContext );
|
||
|
||
ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue );
|
||
|
||
}
|
||
|
||
//
|
||
// We MUST return STATUS_MORE_PROCESSING_REQUIRED to halt the completion
|
||
// of the Irp. Either DfsRestartRelativeOpen that we queued above or
|
||
// DfsPassThroughRelativeOpen will complete the IRP after it is done.
|
||
//
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsCompleteRelativeOpen: Exited\n", 0);
|
||
|
||
return( STATUS_MORE_PROCESSING_REQUIRED );
|
||
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsPostProcessRelativeOpen
|
||
//
|
||
// Synopsis: Continues a relative open after it has already been passed
|
||
// to the device of the parent. One of three things could have
|
||
// happened -
|
||
//
|
||
// a) The device of the parent successfully handled the open.
|
||
// We create an fcb and return.
|
||
// b) The device of the parent could not do the open for some
|
||
// reason other than STATUS_DFS_EXIT_PATH_FOUND. We return
|
||
// the error to the caller.
|
||
// c) The device of the parent returned STATUS_DFS_EXIT_PATH
|
||
// found. In that case, we convert the relative open to an
|
||
// absolute open and do a full Dnr.
|
||
//
|
||
// Arguments: [Irp] -- Pointer to Irp
|
||
// [IrpContext] -- Pointer to IrpContext associated with Irp
|
||
// [Fcb] -- Preallocated Fcb of this file.
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsPostProcessRelativeOpen(
|
||
IN PIRP Irp,
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDFS_FCB Fcb)
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PFILE_OBJECT FileObject;
|
||
UNICODE_STRING NewFileName;
|
||
BOOLEAN fCompleteRequest = TRUE;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsPostProcessRelativeOpen: Entered\n", 0);
|
||
|
||
ASSERT( ARGUMENT_PRESENT( Irp ) );
|
||
ASSERT( ARGUMENT_PRESENT( IrpContext ) );
|
||
ASSERT( ARGUMENT_PRESENT( Fcb ) );
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
FileObject = IrpSp->FileObject;
|
||
|
||
ASSERT( Fcb->Vcb != NULL );
|
||
ASSERT( NodeType(Fcb->Vcb) == DSFS_NTC_VCB );
|
||
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Just set the DFS_FCB for the FileObject.
|
||
//
|
||
|
||
DfsDbgTrace( 0, Dbg, "Relative Open pass through succeeded\n", 0 );
|
||
|
||
DfsDbgTrace(0, Dbg, "Fcb = %08lx\n", Fcb);
|
||
|
||
DfsSetFileObject(FileObject,
|
||
RedirectedFileOpen,
|
||
Fcb
|
||
);
|
||
|
||
ExInterlockedIncrementLong(&Fcb->DfsMachineEntry->UseCount, &DfsData.Pkt.UseCountLock);
|
||
|
||
//
|
||
// Now that a File Open has succeeded, we need to bump up OpenCnt
|
||
// on the DFS_VCB.
|
||
//
|
||
|
||
ExInterlockedIncrementLong(&Fcb->Vcb->OpenFileCount, &DfsData.DfsLock);
|
||
|
||
} else if ( Status == STATUS_DFS_EXIT_PATH_FOUND ||
|
||
Status == STATUS_PATH_NOT_COVERED ) {
|
||
|
||
PDFS_VCB Vcb;
|
||
|
||
//
|
||
// Exit path was found. We'll have to convert this relative open to
|
||
// an absolute open, and do a normal dnr on it.
|
||
//
|
||
|
||
DfsDbgTrace(0, Dbg, "Exit point found! Trying absolute open\n", 0);
|
||
|
||
Vcb = Fcb->Vcb;
|
||
|
||
NewFileName.Buffer = ExAllocatePool( NonPagedPool, Fcb->FullFileName.MaximumLength );
|
||
|
||
if (NewFileName.Buffer != NULL) {
|
||
|
||
NewFileName.Length = Fcb->FullFileName.Length;
|
||
NewFileName.MaximumLength = Fcb->FullFileName.MaximumLength;
|
||
|
||
RtlMoveMemory(
|
||
(PVOID) NewFileName.Buffer,
|
||
(PVOID) Fcb->FullFileName.Buffer,
|
||
Fcb->FullFileName.Length );
|
||
|
||
DfsDeleteFcb( IrpContext, Fcb );
|
||
|
||
if (FileObject->FileName.Buffer) {
|
||
|
||
ExFreePool( FileObject->FileName.Buffer );
|
||
|
||
}
|
||
|
||
FileObject->FileName = NewFileName;
|
||
|
||
//
|
||
// Bugbug: OFS sets the FileObject->Vpb even though it failed
|
||
// the open. Reset it to NULL.
|
||
//
|
||
|
||
if (FileObject->Vpb == NULL) {
|
||
DfsDbgTrace(0, Dbg, "Dfs: Ofs set Vpb to NULL! Remove Dfs workaround!\n", 0);
|
||
} else {
|
||
FileObject->Vpb = NULL;
|
||
}
|
||
|
||
DfsDbgTrace(0, Dbg, "Absolute path == %wZ\n", &NewFileName);
|
||
|
||
fCompleteRequest = FALSE;
|
||
|
||
ASSERT( Vcb != NULL );
|
||
|
||
Status = DnrStartNameResolution( IrpContext, Irp, Vcb );
|
||
|
||
} else {
|
||
|
||
DfsDeleteFcb( IrpContext, Fcb );
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
DfsDbgTrace(0, Dbg, "Unable to allocate full name!\n", 0);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (fCompleteRequest) {
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
}
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsPostProcessRelativeOpen: Exited %08lx\n", Status);
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsRestartRelativeOpen
|
||
//
|
||
// Synopsis: This function is intended to be queued to complete processing
|
||
// of a relative open IRP that was passed through and originally
|
||
// came back with STATUS_PENDING.
|
||
//
|
||
// Arguments: [IrpContext]
|
||
//
|
||
// Returns: Nothing
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
DfsRestartRelativeOpen(
|
||
IN PIRP_CONTEXT IrpContext)
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsRestartRelativeOpen: Entered IrpContext == %08lx\n", IrpContext);
|
||
|
||
Status = DfsPostProcessRelativeOpen(
|
||
IrpContext->OriginatingIrp,
|
||
IrpContext,
|
||
(PDFS_FCB) IrpContext->Context);
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsRestartRelativeOpen: Exited\n", 0);
|
||
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsComposeFullName
|
||
//
|
||
// Synopsis: Given a fully qualified name and a relative name, this
|
||
// function allocates room for the concatenation of the two, and
|
||
// fills up the buffer with the concatenated name.
|
||
//
|
||
// Arguments: [ParentName] -- Pointer to fully qualified parent name.
|
||
// [RelativeName] -- Pointer to name relative to parent.
|
||
// [FullName] -- Pointer to UNICODE_STRING structure that will
|
||
// get filled up with the full name.
|
||
//
|
||
// Returns: STATUS_INSUFFICIENT_RESOURCES if memory allocation fails.
|
||
// STAUS_SUCCESS otherwise.
|
||
//
|
||
// Notes: This routine uses an appropriate allocator so that the
|
||
// returned FullName can be put into a FILE_OBJECT.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsComposeFullName(
|
||
IN PUNICODE_STRING ParentName,
|
||
IN PUNICODE_STRING RelativeName,
|
||
OUT PUNICODE_STRING FullName)
|
||
{
|
||
|
||
FullName->MaximumLength = ParentName->Length +
|
||
sizeof (WCHAR) + // For backslash
|
||
RelativeName->Length;
|
||
|
||
FullName->Buffer = (PWCHAR) ExAllocatePool(NonPagedPool,
|
||
FullName->MaximumLength);
|
||
|
||
if (FullName->Buffer == NULL) {
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
RtlMoveMemory (FullName->Buffer, ParentName->Buffer, ParentName->Length);
|
||
|
||
FullName->Length = ParentName->Length;
|
||
|
||
(void) DnrConcatenateFilePath (FullName,
|
||
RelativeName->Buffer,
|
||
RelativeName->Length);
|
||
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsAreFilesOnSameLocalVolume
|
||
//
|
||
// Synopsis: Given a file name and a name relative to it, this routine
|
||
// will determine if both files live on the same local volume.
|
||
//
|
||
// Arguments: [ParentName] -- The name of the parent file.
|
||
// [FileName] -- Name relative to parent of the other file.
|
||
//
|
||
// Returns: [STATUS_SUCCESS] -- The two files should indeed be on the
|
||
// same local volume.
|
||
//
|
||
// [STATUS_NOT_SAME_DEVICE] -- The two files are not on the
|
||
// same local volume.
|
||
//
|
||
// [STATUS_OBJECT_TYPE_MISMATCH] -- ustrParentName is not on
|
||
// a local volume.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsAreFilesOnSameLocalVolume(
|
||
IN PUNICODE_STRING ParentName,
|
||
IN PUNICODE_STRING FileName)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDFS_PKT pkt;
|
||
PDFS_PKT_ENTRY pktEntryParent;
|
||
PDFS_PKT_ENTRY pktEntryFile;
|
||
UNICODE_STRING remPath;
|
||
BOOLEAN pktLocked;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsAreFilesOnSameLocalVolume entered\n", 0);
|
||
|
||
DfsDbgTrace(0, Dbg, "Parent = [%wZ]\n", ParentName);
|
||
DfsDbgTrace(0, Dbg, "File = [%wZ]\n", FileName);
|
||
|
||
pkt = _GetPkt();
|
||
|
||
PktAcquireShared( TRUE, &pktLocked );
|
||
|
||
//
|
||
// First, see if the parent is on a local volume at all.
|
||
//
|
||
|
||
pktEntryParent = PktLookupEntryByPrefix( pkt, ParentName, &remPath );
|
||
|
||
DfsDbgTrace(0, Dbg, "Parent Entry @%08lx\n", pktEntryParent);
|
||
|
||
if (pktEntryParent == NULL ||
|
||
!(pktEntryParent->Type & PKT_ENTRY_TYPE_LOCAL)) {
|
||
|
||
status = STATUS_OBJECT_TYPE_MISMATCH;
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
USHORT parentLen;
|
||
|
||
//
|
||
// Parent is local, verify that the relative file does not cross a
|
||
// junction point. We'll iterate through the list of exit points on
|
||
// the parent's local volume pkt entry, comparing the remaing path
|
||
// of the exit point with the FileName argument
|
||
//
|
||
|
||
ASSERT(pktEntryParent != NULL);
|
||
|
||
parentLen = pktEntryParent->Id.Prefix.Length +
|
||
sizeof(UNICODE_PATH_SEP);
|
||
|
||
for (pktEntryFile = PktEntryFirstSubordinate(pktEntryParent);
|
||
pktEntryFile != NULL && NT_SUCCESS(status);
|
||
pktEntryFile = PktEntryNextSubordinate(
|
||
pktEntryParent, pktEntryFile)) {
|
||
|
||
remPath = pktEntryFile->Id.Prefix;
|
||
remPath.Length -= parentLen;
|
||
remPath.Buffer += (parentLen/sizeof(WCHAR));
|
||
|
||
if (DfsRtlPrefixPath( &remPath, FileName, FALSE)) {
|
||
|
||
DfsDbgTrace(0, Dbg,
|
||
"Found entry %08lx for File\n", pktEntryFile);
|
||
|
||
//
|
||
// FileName is on another volume.
|
||
//
|
||
|
||
status = STATUS_NOT_SAME_DEVICE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
PktRelease();
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsAreFilesOnSameLocalVolume exit %08lx\n", status);
|
||
|
||
return( status );
|
||
}
|
||
|