//+---------------------------------------------------------------------------- // // Copyright (C) 1996, Microsoft Corporation // // File: create.c // // Contents: Implements the Create code for the Dfs server. The Dfs server // only allows opening the File System Device object for the // express purpose of FsControlling to the Dfs server. // // Classes: // // Functions: DfsFsdCreate // DfsOpenDevice // //----------------------------------------------------------------------------- #include "dfsprocs.h" #include "attach.h" #include "dfswml.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_CREATE) // // Local procedure prototypes // NTSTATUS DfsOpenFile( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS DfsCompleteOpenFile( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); NTSTATUS DfsOpenDevice ( IN PFILE_OBJECT FileObject, IN PDEVICE_OBJECT DeviceObject, IN ULONG CreateOptions); VOID DfspDoesPathCrossJunctionPoint( IN PUNICODE_STRING Path, OUT BOOLEAN *IsExitPoint, OUT BOOLEAN *CrossesExitPoint); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, DfsFsdCreate ) #pragma alloc_text( PAGE, DfsOpenFile ) #pragma alloc_text( PAGE, DfsOpenDevice ) #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 relative to which // the open is to be processed. // [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; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = irpSp->FileObject; PFILE_OBJECT fileObject; ULONG createOptions; DebugTrace(+1, Dbg, "DfsFsdCreate: Entered\n", 0); DFS_TRACE_HIGH(TRACE_IRP, DfsFsdCreate_Entry, LOGPTR(DeviceObject) LOGPTR(FileObject) LOGUSTR(FileObject->FileName) LOGPTR(Irp)); ASSERT(IoIsOperationSynchronous(Irp) == TRUE); // // If someone is coming in via a device object attached to a file system // device object, pass it through. // if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) { status = DfsVolumePassThrough(DeviceObject, Irp); DebugTrace(-1, Dbg, "DfsFsdCreate: FS Device Pass Through Exit %08lx\n", ULongToPtr( status )); return status; } // // If someone is coming in via a device object attached to a file system // volume, we need to see if they are opening an exit point via its local // file system name. // if (DeviceObject->DeviceType == FILE_DEVICE_DFS_VOLUME) { if (((PDFS_VOLUME_OBJECT)DeviceObject)->DfsEnable == TRUE) { status = DfsOpenFile(DeviceObject, Irp); DebugTrace(-1, Dbg, "DfsFsdCreate: Local File Open Exit %08lx\n", ULongToPtr( status )); } else { status = DfsVolumePassThrough(DeviceObject, Irp); DebugTrace(-1, Dbg, "DfsFsdCreate: (DfsDisable) FS Device Pass Through Exit %08lx\n", ULongToPtr( status )); } return status; } // // The only other create we handle is someone trying to open our own // file system device object. // ASSERT(DeviceObject->DeviceType == FILE_DEVICE_DFS_FILE_SYSTEM); FsRtlEnterFileSystem(); fileObject = irpSp->FileObject; createOptions = irpSp->Parameters.Create.Options; if (fileObject->FileName.Length == 0 && fileObject->RelatedFileObject == NULL) { // // This is the only case we handle // status = DfsOpenDevice( fileObject, DeviceObject, createOptions); } else { status = STATUS_INVALID_DEVICE_REQUEST; } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace(-1, Dbg, "DfsFsdCreate: Exit -> %08lx\n", ULongToPtr( status )); DfsCompleteRequest( Irp, status ); DFS_TRACE_HIGH(TRACE_IRP, DfsFsdCreate_Exit, LOGSTATUS(status) LOGPTR(fileObject) LOGPTR(Irp)); return status; } //+---------------------------------------------------------------------------- // // Function: DfsOpenFile, local // // Synopsis: This routine handles file opens that come in via attached // volumes. The semantics of this open are: // // If the named file is a child of a DfsExitPath, fail it // with access denied. // // If the named file is a DfsExitPath, and CreateOptions specify // DELETE_ON_CLOSE, fail it with access denied. // // In all other cases, allocate an FCB, and pass the open through // to the underlying FS. If the open succeeds, then insert the // FCB in our FCB table. If the open fails, destroy the FCB. // // Arguments: [DeviceObject] -- The attached device object through which // the Create Irp came in. // // [Irp] -- The Create Irp. // // Returns: [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate an FCB. // // [STATUS_ACCESS_DENIED] -- The file is a child of a Dfs exit // path or the file is a Dfs exit path and // DELETE_ON_CLOSE was specified. // // Status from the underlying FS. // //----------------------------------------------------------------------------- NTSTATUS DfsOpenFile( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS status; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION nextIrpSp; PFILE_OBJECT fileObject = irpSp->FileObject; ULONG createOptions = irpSp->Parameters.Create.Options; PDFS_FCB fcb = NULL; DebugTrace(+1, Dbg, "DfsOpenFile - Entered\n", 0); DFS_TRACE_LOW(TRACE_IRP, DfsOpenFile_Entry, LOGPTR(fileObject) LOGPTR(Irp)); // // Optimistically, we allocate an FCB for this open. // status = DfsAllocateFcb(DeviceObject, fileObject, &fcb); if (NT_SUCCESS(status)) { BOOLEAN isExitPoint, crossesExitPoint; DfspDoesPathCrossJunctionPoint( &fcb->FullFileName, &isExitPoint, &crossesExitPoint); if (isExitPoint && (createOptions & FILE_DELETE_ON_CLOSE)) status = STATUS_ACCESS_DENIED; if (crossesExitPoint) status = STATUS_ACCESS_DENIED; } // // If we haven't failed yet, we need to pass this open down to the // underlying file system. If we failed, then we need to complete the // Create Irp. // if (NT_SUCCESS(status)) { PDEVICE_OBJECT vdo; // // Copy the stack from one to the next... // nextIrpSp = IoGetNextIrpStackLocation(Irp); (*nextIrpSp) = (*irpSp); IoSetCompletionRoutine( Irp, DfsCompleteOpenFile, (PVOID) fcb, TRUE, TRUE, TRUE); // // Find out what device to call...and call it // vdo = ((PDFS_VOLUME_OBJECT) DeviceObject)->Provider.DeviceObject; status = IoCallDriver( vdo, Irp ); DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfsOpenFile_Error_IoCallDriver, LOGSTATUS(status) LOGPTR(fileObject) LOGPTR(Irp)); } else { if (fcb != NULL) { DfsDestroyFcb(fcb); } DfsCompleteRequest( Irp, status ); } DebugTrace(-1, Dbg, "DfsOpenFile - Exited %08lx\n", ULongToPtr( status )); DFS_TRACE_LOW(TRACE_IRP, DfsOpenFile_Exit, LOGSTATUS(status) LOGPTR(fileObject) LOGPTR(Irp)); return( status ); } //+---------------------------------------------------------------------------- // // Function: DfsCompleteOpenFile, local // // Synopsis: Completion routine for DfsOpenFile. If the underlying FS // successfully opened the file, we insert the FCB into our // FCB table, else we destroy the preallocated FCB. // // Arguments: [DeviceObject] -- Our device object, unused. // // [Irp] -- The Create Irp that is being completed, unused. // // [Context] -- Actually, a pointer to our pre-allocated FCB // // Returns: [STATUS_SUCCESS] -- This function always succeeds. // //----------------------------------------------------------------------------- NTSTATUS DfsCompleteOpenFile( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PDFS_FCB fcb = (PDFS_FCB) Context; if (Irp->PendingReturned) { // // 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 ); } if (Irp->IoStatus.Status == STATUS_SUCCESS) { DfsAttachFcb( fcb->FileObject, fcb ); } else { DfsDestroyFcb( fcb ); } return( STATUS_SUCCESS ); } //+------------------------------------------------------------------- // // 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 // [CreateOptions] - Supplies the create options for // this operation // // Returns: [IO_STATUS_BLOCK] - Returns the completion status for // the operation // //-------------------------------------------------------------------- NTSTATUS DfsOpenDevice ( IN PFILE_OBJECT FileObject, IN PDEVICE_OBJECT DeviceObject, IN ULONG CreateOptions ) { NTSTATUS status; // // Check to see which type of device is being opened. // We don't permit all open modes on the file system // device object. // ULONG CreateDisposition = (CreateOptions >> 24) & 0x000000ff; DFS_TRACE_LOW(TRACE_IRP, DfsOpenDevice_Entry, LOGPTR(FileObject) LOGPTR(DeviceObject) LOGULONG(CreateOptions)); // // Check for proper desired access and rights // if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF ) { status = STATUS_ACCESS_DENIED; DebugTrace(0, Dbg, "DfsOpenDevice: Invalid CreateDisposition\n", 0); DFS_TRACE_HIGH(ERROR, DfsOpenDevice_Error1, LOGSTATUS(status) LOGPTR(FileObject) LOGPTR(DeviceObject)); return( status ); } // // Check if we were to open a directory // if (CreateOptions & FILE_DIRECTORY_FILE) { status = STATUS_NOT_A_DIRECTORY; DebugTrace(0, Dbg, "DfsOpenDevice: Cannot open device as a directory\n", 0); DFS_TRACE_HIGH(ERROR, DfsOpenDevice_Error3, LOGSTATUS(status) LOGPTR(FileObject) LOGPTR(DeviceObject)); return( status ); } FileObject->FsContext = UIntToPtr( DFS_OPEN_CONTEXT ); status = STATUS_SUCCESS; DFS_TRACE_LOW(TRACE_IRP, DfsOpenDevice_Exit, LOGSTATUS(status) LOGPTR(FileObject) LOGPTR(DeviceObject)); return( status ); } //+---------------------------------------------------------------------------- // // Function: DfspDoesPathCrossJunctionPoint // // Synopsis: Given a fully formed local FS path // (looks like "\DosDevices\C:\foo\bar") this routine figures out // if the path matches an exit point exactly, or crosses an // exit point. // // Arguments: [Path] -- The path to check. // [IsExitPoint] -- The path refers to an exit point. // [CrossesExitPoint] -- The path crosses an exit point. // // Returns: NOTHING. The results are returned in the two out parameters. // //----------------------------------------------------------------------------- VOID DfspDoesPathCrossJunctionPoint( IN PUNICODE_STRING Path, OUT BOOLEAN *IsExitPoint, OUT BOOLEAN *CrossesExitPoint) { PDFS_PKT pkt; PDFS_LOCAL_VOL_ENTRY lv; UNICODE_STRING remPath; *IsExitPoint = FALSE; *CrossesExitPoint = FALSE; pkt = _GetPkt(); PktAcquireShared(pkt, TRUE); lv = PktEntryLookupLocalService(pkt, Path, &remPath); if (lv != NULL && remPath.Length != 0) { PDFS_PKT_ENTRY pktEntry, pktExitEntry; USHORT prefixLength; UNICODE_STRING remExitPt; PUNICODE_STRING exitPrefix; pktEntry = lv->PktEntry; prefixLength = pktEntry->Id.Prefix.Length; pktExitEntry = PktEntryFirstSubordinate(pktEntry); // // As long as there are more exit points see if the path crosses // the exit point. // while (pktExitEntry != NULL && !(*IsExitPoint) && !(*CrossesExitPoint)) { exitPrefix = &pktExitEntry->Id.Prefix; if (exitPrefix->Buffer[prefixLength/sizeof(WCHAR)] == UNICODE_PATH_SEP) prefixLength += sizeof(WCHAR); remExitPt.Length = pktExitEntry->Id.Prefix.Length - prefixLength; remExitPt.MaximumLength = remExitPt.Length + 1; remExitPt.Buffer = &exitPrefix->Buffer[prefixLength/sizeof(WCHAR)]; // // If the Path has the potential of crossing past the junction // point, we have something to return! // if (DfsRtlPrefixPath(&remExitPt, &remPath, TRUE)) { if (remExitPt.Length == remPath.Length) { *IsExitPoint = TRUE; } else { *CrossesExitPoint = TRUE; } } pktExitEntry = PktEntryNextSubordinate(pktEntry, pktExitEntry); } //while exit pt exists } // lv != NULL && remPath.Length != 0 PktRelease(pkt); }