mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
547 lines
15 KiB
547 lines
15 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
|
|
}
|
|
|