//+---------------------------------------------------------------------------- // // 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" #include "mupwml.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_CREATE) // // Local procedure prototypes // 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); PFILE_OBJECT FileObject = IrpSp->FileObject; PDFS_FCB pFcb = NULL; DfsDbgTrace(+1, Dbg, "DfsFsdCreate: Entered\n", 0); MUP_TRACE_HIGH(TRACE_IRP, DfsFsdCreate_Entry, LOGPTR(DeviceObject) LOGPTR(FileObject) LOGUSTR(FileObject->FileName) LOGPTR(Irp)); ASSERT(IoIsOperationSynchronous(Irp) == TRUE); // // Call the common create routine, with block allowed if the operation // is synchronous. // try { IrpContext = DfsCreateIrpContext( Irp, CanFsdWait( Irp ) ); if (IrpContext == NULL) ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 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", ULongToPtr(Status) ); MUP_TRACE_HIGH(TRACE_IRP, DfsFsdCreate_Exit, LOGSTATUS(Status) LOGPTR(DeviceObject) LOGPTR(FileObject) LOGPTR(Irp)); 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 = NULL; PFILE_OBJECT FileObject = IrpSp->FileObject; PFILE_OBJECT RelatedFileObject; UNICODE_STRING FileName; ACCESS_MASK DesiredAccess; ULONG CreateOptions; USHORT ShareAccess; NTSTATUS Status; LARGE_INTEGER StartTime; LARGE_INTEGER EndTime; DfsDbgTrace(+1, Dbg, "DfsCommonCreate\n", 0 ); DfsDbgTrace( 0, Dbg, "Irp = %08lx\n", Irp ); DfsDbgTrace( 0, Dbg, "->Flags = %08lx\n", ULongToPtr(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", ULongToPtr(IrpSp->Parameters.Create.SecurityContext->DesiredAccess) ); DfsDbgTrace( 0, Dbg, "->CreateOptions = %08lx\n", ULongToPtr(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", ULongToPtr(IrpSp->Parameters.Create.EaLength) ); KeQuerySystemTime(&StartTime); #if DBG if (MupVerbose) { KeQuerySystemTime(&EndTime); DbgPrint("[%d] DfsCommonCreate(%wZ)\n", (ULONG)((EndTime.QuadPart - StartTime.QuadPart)/(10 * 1000)), &FileObject->FileName); } #endif // // 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; MUP_TRACE_HIGH(ERROR, DfsCommonCreate_Error_PagingFileNotAllowed, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject) LOGPTR(Irp)); DfsCompleteRequest( IrpContext, Irp, Iosb.Status ); DfsDbgTrace(-1, Dbg, "DfsCommonCreate: Exit -> %08lx\n", ULongToPtr(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) || (DeviceObject != NULL && DeviceObject->DeviceType != FILE_DEVICE_DFS && 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) ExAllocatePoolWithTag( NonPagedPool, NewFileName.MaximumLength, ' puM'); 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 { Iosb.Status = STATUS_INVALID_HANDLE; DfsCompleteRequest( IrpContext, Irp, Iosb.Status ); DfsDbgTrace(0, Dbg, "DfsCommonCreate: Invalid related file object\n", 0); try_return( Iosb.Status ); } (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. // if (Vcb == NULL) { DfsDbgTrace(0, Dbg, "DfsCommonCreate: Null Vcb!\n", 0); Iosb.Status = STATUS_INVALID_PARAMETER; MUP_TRACE_HIGH(ERROR, DfsCommonCreate_Error_NullVcb, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject) LOGPTR(Irp)); DfsCompleteRequest(IrpContext, Irp, Iosb.Status); try_return(Iosb.Status); } Iosb.Status = DnrStartNameResolution(IrpContext, Irp, Vcb); try_exit: NOTHING; } finally { DfsDbgTrace(-1, Dbg, "DfsCommonCreate: Exit -> %08lx\n", ULongToPtr(Iosb.Status)); #if DBG if (MupVerbose) { KeQuerySystemTime(&EndTime); DbgPrint("[%d] DfsCommonCreate exit 0x%x\n", (ULONG)((EndTime.QuadPart - StartTime.QuadPart)/(10 * 1000)), Iosb.Status); } #endif } 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; MUP_TRACE_HIGH(ERROR, DfsOpenDevice_Error_BadDisposition, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject)); 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; MUP_TRACE_HIGH(ERROR, DfsOpenDevice_Error_CannotOpenAsDirectory, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject)); try_return( Iosb ); } DfsSetFileObject( FileObject, FilesystemDeviceOpen, DeviceObject ); Iosb.Status = STATUS_SUCCESS; Iosb.Information = FILE_OPENED; try_return( Iosb ); } ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE); 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) { ExReleaseResourceLite( &DfsData.Resource ); Iosb.Status = STATUS_ACCESS_DENIED; MUP_TRACE_HIGH(ERROR, DfsOpenDevice_Error_FileInUse, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject)); 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 ))) { ExReleaseResourceLite( &DfsData.Resource ); MUP_TRACE_ERROR_HIGH(Iosb.Status, ALL_ERROR, DfsOpenDevice_Error_IoCheckShareAccess, LOGSTATUS(Iosb.Status) LOGPTR(DeviceObject) LOGPTR(FileObject)); try_return( Iosb ); } } else { IoSetShareAccess( DesiredAccess, ShareAccess, FileObject, &Vcb->ShareAccess ); } UnwindShareAccess = TRUE; // // Bug: 425017. Update the counters with lock held to avoid race between multiple processors. // InterlockedIncrement(&Vcb->DirectAccessOpenCount); InterlockedIncrement(&Vcb->OpenFileCount); ExReleaseResourceLite( &DfsData.Resource ); // // Setup the context pointers, and update // our reference counts // DfsSetFileObject( FileObject, LogicalRootDeviceOpen, Vcb ); // // 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() && (Vcb != NULL)) { 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", ULongToPtr(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; PFILE_OBJECT FileObject; PDFS_FCB NewFcb; UNICODE_STRING NewFileName; DfsDbgTrace(+1, Dbg, "DfsPassThroughRelativeOpen: Entered\n", 0); IrpSp = IoGetCurrentIrpStackLocation(Irp); FileObject = IrpSp->FileObject; // // 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", ULongToPtr(Status) ); DfsCompleteRequest( IrpContext, Irp, Status ); return( Status ); } NewFcb = DfsCreateFcb( NULL, ParentFcb->Vcb, &NewFileName ); if (NewFcb == NULL) { if (NewFileName.Buffer != NULL) ExFreePool(NewFileName.Buffer); Status = STATUS_INSUFFICIENT_RESOURCES; DfsDbgTrace(-1, Dbg, "DfsPassThroughRelativeOpen: Exited %08lx\n", ULongToPtr(Status)); DfsCompleteRequest( IrpContext, Irp, Status ); return( Status ); } // Changes for 426540. Do all the right logic for CSC. // since DFS does not "failover" for relative names, allow CSC to go // offline if necessary to serve the name. This does mean that the DFS // namespace will be served by the CSC even when one of the DFS alternates // exists. NewFcb->DfsNameContext.Flags = DFS_FLAG_LAST_ALTERNATE; if (NewFcb->Vcb != NULL) { if (NewFcb->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME) { NewFcb->DfsNameContext.NameContextType = DFS_CSCAGENT_NAME_CONTEXT; } else { NewFcb->DfsNameContext.NameContextType = DFS_USER_NAME_CONTEXT; } } NewFcb->TargetDevice = ParentFcb->TargetDevice; NewFcb->ProviderId = ParentFcb->ProviderId; NewFcb->DfsMachineEntry = ParentFcb->DfsMachineEntry; NewFcb->FileObject = IrpSp->FileObject; DfsSetFileObject(IrpSp->FileObject, RedirectedFileOpen, NewFcb ); IrpSp->FileObject->FsContext = &(NewFcb->DfsNameContext); if (ParentFcb->ProviderId == PROV_ID_DFS_RDR) { IrpSp->FileObject->FsContext2 = UIntToPtr(DFS_OPEN_CONTEXT); } if (NewFileName.Buffer != NULL) ExFreePool( NewFileName.Buffer ); // // 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 ); MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DfsPassThroughRelativeOpen_Error_IoCallDriver, LOGSTATUS(Status) LOGPTR(FileObject) LOGPTR(Irp)); DfsDbgTrace(0, Dbg, "IoCallDriver returned %08lx\n", ULongToPtr(Status)); if (Status != STATUS_PENDING) { Status = DfsPostProcessRelativeOpen( Irp, IrpContext, NewFcb); } DfsDbgTrace(-1, Dbg, "DfsPassThroughRelativeOpen: Exited %08lx\n", ULongToPtr(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 (Status == STATUS_SUCCESS) { // // 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); InterlockedIncrement(&Fcb->DfsMachineEntry->UseCount); // // Now that a File Open has succeeded, we need to bump up OpenCnt // on the DFS_VCB. // InterlockedIncrement(&Fcb->Vcb->OpenFileCount); } 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 = ExAllocatePoolWithTag( NonPagedPool, Fcb->FullFileName.MaximumLength, ' puM'); 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 ); DfsDetachFcb( FileObject, Fcb ); DfsDeleteFcb( IrpContext, Fcb ); if (FileObject->FileName.Buffer) { ExFreePool( FileObject->FileName.Buffer ); } FileObject->FileName = NewFileName; // OFS apparently sets the FileObject->Vpb even though it failed // the open. Reset it to NULL. // if (FileObject->Vpb != NULL) { FileObject->Vpb = NULL; } DfsDbgTrace(0, Dbg, "Absolute path == %wZ\n", &NewFileName); fCompleteRequest = FALSE; ASSERT( Vcb != NULL ); Status = DnrStartNameResolution( IrpContext, Irp, Vcb ); } else { DfsDetachFcb( FileObject, Fcb ); DfsDeleteFcb( IrpContext, Fcb ); Status = STATUS_INSUFFICIENT_RESOURCES; DfsDbgTrace(0, Dbg, "Unable to allocate full name!\n", 0); } } else { DfsDetachFcb( FileObject, Fcb ); DfsDeleteFcb( IrpContext, Fcb ); } if (fCompleteRequest) { DfsCompleteRequest( IrpContext, Irp, Status ); } DfsDbgTrace(-1, Dbg, "DfsPostProcessRelativeOpen: Exited %08lx\n", ULongToPtr(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) { ULONG nameLen; NTSTATUS status; nameLen = ParentName->Length + sizeof (WCHAR) + // For backslash RelativeName->Length; if (nameLen > MAXUSHORT) { status = STATUS_NAME_TOO_LONG; MUP_TRACE_HIGH(ERROR, DfsComposeFullName_Error1, LOGUSTR(*ParentName) LOGSTATUS(status)); return status; } FullName->Buffer = (PWCHAR) ExAllocatePoolWithTag( NonPagedPool, nameLen, ' puM'); if (FullName->Buffer == NULL) { return (STATUS_INSUFFICIENT_RESOURCES); } FullName->Length = ParentName->Length; FullName->MaximumLength = (USHORT)nameLen; RtlMoveMemory (FullName->Buffer, ParentName->Buffer, ParentName->Length); if (RelativeName->Length > 0) { (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", ULongToPtr(status)); return( status ); }