/*************************************************************************
*
* File: create.c
*
* Module: Sample File System Driver (Kernel mode execution only)
*
* Description:
*	Contains code to handle the "Create"/"Open" dispatch entry point.
*
* Author: R. Nagar
*
* (c) 1996-97 Rajeev Nagar, All Rights Reserved
*
*************************************************************************/

#include			"sfsd.h"

// define the file specific bug-check id
#define			SFSD_BUG_CHECK_ID				SFSD_FILE_CREATE



/*************************************************************************
*
* Function: SFsdCreate()
*
* Description:
*	The I/O Manager will invoke this routine to handle a create/open
*	request
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
*	to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdCreate(
PDEVICE_OBJECT		DeviceObject,		// the logical volume device object
PIRP					Irp)					// I/O Request Packet
{
	NTSTATUS				RC = STATUS_SUCCESS;
   PtrSFsdIrpContext	PtrIrpContext;
	BOOLEAN				AreWeTopLevel = FALSE;

	FsRtlEnterFileSystem();
	ASSERT(DeviceObject);
	ASSERT(Irp);

	// sometimes, we may be called here with the device object representing
	//	the file system (instead of the device object created for a logical
	//	volume. In this case, there is not much we wish to do (this create
	//	typically will happen 'cause some process has to open the FSD device
	//	object so as to be able to send an IOCTL to the FSD)

	//	All of the logical volume device objects we create have a device
	//	extension whereas the device object representing the FSD has no
	//	device extension. This seems like a good enough method to identify
	//	between the two device objects ...
	if (DeviceObject->Size == (unsigned short)(sizeof(DEVICE_OBJECT))) {
		// this is an open of the FSD itself
		Irp->IoStatus.Status = RC;
		Irp->IoStatus.Information = FILE_OPENED;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return(RC);
	}

	// set the top level context
	AreWeTopLevel = SFsdIsIrpTopLevel(Irp);

	try {

		// get an IRP context structure and issue the request
		PtrIrpContext = SFsdAllocateIrpContext(Irp, DeviceObject);
		ASSERT(PtrIrpContext);

		RC = SFsdCommonCreate(PtrIrpContext, Irp);

	} except (SFsdExceptionFilter(PtrIrpContext, GetExceptionInformation())) {

		RC = SFsdExceptionHandler(PtrIrpContext, Irp);

		SFsdLogEvent(SFSD_ERROR_INTERNAL_ERROR, RC);
	}

	if (AreWeTopLevel) {
		IoSetTopLevelIrp(NULL);
	}

	FsRtlExitFileSystem();

	return(RC);
}



/*************************************************************************
*
* Function: SFsdCommonCreate()
*
* Description:
*	The actual work is performed here. This routine may be invoked in one'
*	of the two possible contexts:
*	(a) in the context of a system worker thread
*	(b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdCommonCreate(
PtrSFsdIrpContext			PtrIrpContext,
PIRP							PtrIrp)
{
	NTSTATUS					RC = STATUS_SUCCESS;
	PIO_STACK_LOCATION	PtrIoStackLocation = NULL;
   PIO_SECURITY_CONTEXT	PtrSecurityContext = NULL;
	PFILE_OBJECT			PtrNewFileObject = NULL;
	PFILE_OBJECT			PtrRelatedFileObject = NULL;
	uint32					AllocationSize = 0; 	// if we create a new file
	PFILE_FULL_EA_INFORMATION	PtrExtAttrBuffer = NULL;
	unsigned long			RequestedOptions = 0;
	unsigned long			RequestedDisposition = 0;
	uint8						FileAttributes = 0;
	unsigned short			ShareAccess = 0;
	unsigned long			ExtAttrLength = 0;
	ACCESS_MASK				DesiredAccess;

	BOOLEAN					DeferredProcessing = FALSE;

   PtrSFsdVCB				PtrVCB = NULL;
	BOOLEAN					AcquiredVCB = FALSE;

	BOOLEAN					DirectoryOnlyRequested = FALSE;
	BOOLEAN					FileOnlyRequested = FALSE;
	BOOLEAN					NoBufferingSpecified = FALSE;
	BOOLEAN					WriteThroughRequested = FALSE;
	BOOLEAN					DeleteOnCloseSpecified = FALSE;
	BOOLEAN					NoExtAttrKnowledge = FALSE;
	BOOLEAN					CreateTreeConnection = FALSE;
	BOOLEAN					OpenByFileId = FALSE;

	// Are we dealing with a page file?
	BOOLEAN					PageFileManipulation = FALSE;

	// Is this open for a target directory (used in rename operations)?
	BOOLEAN					OpenTargetDirectory = FALSE;

	// Should we ignore case when attempting to locate the object?
	BOOLEAN					IgnoreCaseWhenChecking = FALSE;

	PtrSFsdCCB				PtrRelatedCCB = NULL, PtrNewCCB = NULL;
	PtrSFsdFCB				PtrRelatedFCB = NULL, PtrNewFCB = NULL;

	unsigned long			ReturnedInformation;

	UNICODE_STRING			TargetObjectName;
	UNICODE_STRING			RelatedObjectName;

	UNICODE_STRING			AbsolutePathName;

	LARGE_INTEGER			FileAllocationSize, FileEndOfFile;


	ASSERT(PtrIrpContext);
	ASSERT(PtrIrp);

	try {

		AbsolutePathName.Buffer = NULL;
		AbsolutePathName.Length = AbsolutePathName.MaximumLength = 0;

		// First, get a pointer to the current I/O stack location
		PtrIoStackLocation = IoGetCurrentIrpStackLocation(PtrIrp);
		ASSERT(PtrIoStackLocation);

		// If the caller cannot block, post the request to be handled
		//	asynchronously
		if (!(PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_CAN_BLOCK)) {
			// We must defer processing of this request since we could
			//	block anytime while performing the create/open ...
			RC = SFsdPostRequest(PtrIrpContext, PtrIrp);
         DeferredProcessing = TRUE;
			try_return(RC);
		}

		// Now, we can obtain the parameters specified by the user.
		//	Note that the file object is the new object created by the
		//	I/O Manager in anticipation that this create/open request
		//	will succeed.
		PtrNewFileObject	= PtrIoStackLocation->FileObject;
      TargetObjectName	= PtrNewFileObject->FileName;
		PtrRelatedFileObject = PtrNewFileObject->RelatedFileObject;

		// If a related file object is present, get the pointers
		//	to the CCB and the FCB for the related file object
		if (PtrRelatedFileObject) {
			PtrRelatedCCB = (PtrSFsdCCB)(PtrRelatedFileObject->FsContext2);
			ASSERT(PtrRelatedCCB);
			ASSERT(PtrRelatedCCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_CCB);
			// each CCB in turn points to a FCB
			PtrRelatedFCB = PtrRelatedCCB->PtrFCB;
			ASSERT(PtrRelatedFCB);
			ASSERT((PtrRelatedFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_FCB)
					 ||
  					 (PtrRelatedFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB));
			RelatedObjectName = PtrRelatedFileObject->FileName;
		}

		// Allocation size is only used if a new file is created
		//	or a file is superseded.
		AllocationSize    = PtrIrp->Overlay.AllocationSize.LowPart;
		// Note: Some FSD implementations support file sizes > 2 GB.
		//	The following check is only valid if your FSD does not support
		//	a large file size. With NT version 5.0, 64 bit support will
		//	become available and your FSD should ideally support large files ...
		if (PtrIrp->Overlay.AllocationSize.HighPart) {
			RC = STATUS_INVALID_PARAMETER;
			try_return(RC);
		}

		// Get a ptr to the supplied security context
		PtrSecurityContext = PtrIoStackLocation->Parameters.Create.SecurityContext;

		// The desired access can be obtained from the SecurityContext
		DesiredAccess = PtrSecurityContext->DesiredAccess;

		// Two values are supplied in the Create.Options field:
		//	(a) the actual user supplied options
		//	(b) the create disposition
		RequestedOptions = (PtrIoStackLocation->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS);

		// The file disposition is packed with the user options ...
		//	Disposition includes FILE_SUPERSEDE, FILE_OPEN_IF, etc.
		RequestedDisposition = ((PtrIoStackLocation->Parameters.Create.Options >> 24) && 0xFF);

		FileAttributes	= (uint8)(PtrIoStackLocation->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS);
		ShareAccess	= PtrIoStackLocation->Parameters.Create.ShareAccess;

		// If your FSD does not support EA manipulation, you might return
		//	invalid parameter if the following are supplied.
		//	EA arguments are only used if a new file is created or a file is
		//	superseded
		PtrExtAttrBuffer	= PtrIrp->AssociatedIrp.SystemBuffer;
		ExtAttrLength		= PtrIoStackLocation->Parameters.Create.EaLength;

		// Get the options supplied by the user

		// User specifies that returned object MUST be a directory.
		//	Lack of presence of this flag does not mean it *cannot* be a
		//	directory *unless* FileOnlyRequested is set (see below)

		//	Presence of the flag however, does require that the returned object be
		//	a directory (container) object.
		DirectoryOnlyRequested = ((RequestedOptions & FILE_DIRECTORY_FILE) ? TRUE : FALSE);

		// User specifies that returned object MUST NOT be a directory.
		//	Lack of presence of this flag does not mean it *cannot* be a
		//	file *unless* DirectoryOnlyRequested is set (see above).

		//	Presence of the flag however does require that the returned object be
		//	a simple file (non-container) object.
		FileOnlyRequested = ((RequestedOptions & FILE_NON_DIRECTORY_FILE) ? TRUE : FALSE);

		// We cannot cache the file if the following flag is set.
		//	However, things do get a little bit interesting if caching
		//	has been already initiated due to a previous open ...
		//	(maintaining consistency then becomes a little bit more
		//	of a headache - see read/write file descriptions)
		NoBufferingSpecified = ((RequestedOptions & FILE_NO_INTERMEDIATE_BUFFERING) ? TRUE : FALSE);
	
		// Write-through simply means that the FSD must *not* return from
		//	a user write request until the data has been flushed to secondary
		//	storage (either to disks directly connected to the node or across
		//	the network in the case of a redirector)
		WriteThroughRequested = ((RequestedOptions & FILE_WRITE_THROUGH) ? TRUE : FALSE);

		// Not all of the native file system implementations support
		//	the delete-on-close option. All this means is that after the
		//	last close on the FCB has been performed, your FSD should
		//	delete the file. It simply saves the caller from issuing a
		//	separate delete request. Also, some FSD implementations might choose
		//	to implement a Windows NT idiosyncratic behavior wherein you
		//	could create such "delete-on-close" marked files under directories
		//	marked for deletion. Ordinarily, a FSD will not allow you to create
		//	a new file under a directory that has been marked for deletion.
		DeleteOnCloseSpecified = ((RequestedOptions & FILE_DELETE_ON_CLOSE) ? TRUE : FALSE);

		NoExtAttrKnowledge = ((RequestedOptions & FILE_NO_EA_KNOWLEDGE) ? TRUE : FALSE);

		// The following flag is only used by the LAN Manager redirector
		//	to	initiate a "new mapping" to a remote share. Typically,
		//	a FSD will not see this flag (especially disk based FSD's)
		CreateTreeConnection = ((RequestedOptions & FILE_CREATE_TREE_CONNECTION) ? TRUE : FALSE);

		// The NTFS file system for exmaple supports the OpenByFileId option.
		//	Your FSD may also be able to associate a unique numerical ID with
		//	an on-disk object. The caller would get this ID in a "query file
		//	information" call.

		//	Later, the caller might decide to reopen the object, this time
		//	though it may supply your FSD with the file identifier instead of
		//	a file/path name.
		OpenByFileId = ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? TRUE : FALSE);

		// Are we dealing with a page file?
		PageFileManipulation = ((PtrIoStackLocation->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE);

		// The open target directory flag is used as part of the sequence of
		//	operations performed by the I/O Manager is response to a file/dir
		//	rename operation. See the explanation in the book for details.
		OpenTargetDirectory = ((PtrIoStackLocation->Flags & SL_OPEN_TARGET_DIRECTORY) ? TRUE : FALSE);

		// If your FSD supports case-sensitive file name checks, you may
		//	choose to honor the following flag ...
      IgnoreCaseWhenChecking = ((PtrIoStackLocation->Flags & SL_CASE_SENSITIVE) ? TRUE : FALSE);

		// Ensure that the operation has been directed to a valid VCB ...
		PtrVCB =	(PtrSFsdVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
		ASSERT(PtrVCB);
		ASSERT(PtrVCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB);

		// Use coarse grained locking and acquire the VCB exclusively. This
		//	will lock out all other concurrent create/open requests
		// If the VCB is also acquired by the cleanup/close dispatch routines,
		// you can count on create/opens being synchronized with these functions
		// as well.
		ExAcquireResourceExclusiveLite(&(PtrVCB->VCBResource), TRUE);
      AcquiredVCB = TRUE;

		// Disk based file systems might decide to verify the logical volume
		//	(if required and only if removable media are supported) at this time

		//	Implement your own volume verification routine ...
		//	Read the DDK for more information on when a FSD must verify a
		//	volume ...
		//	if (!NT_SUCCESS(RC = SFsdVerifyVolume(PtrVCB))) {
		//		try_return(RC);
		//	}

		// If the volume has been locked, fail the request
		if (PtrVCB->VCBFlags & SFSD_VCB_FLAGS_VOLUME_LOCKED) {
			RC = STATUS_ACCESS_DENIED;
			try_return(RC);
		}

		// If a volume open is requested, satisfy it now
		if ((PtrNewFileObject->FileName.Length == 0) && ((PtrRelatedFileObject == NULL) ||
			  (PtrRelatedFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB))) {
			// If the supplied file name is NULL *and* either there exists
			//	no related file object *or* if a related file object was supplied
			//	but it too refers to a previously opened instance of a logical
			//	volume, this open must be for a logical volume.

			//	Note: your FSD might decide to do "special" things (whatever they
			//	might be) in response to an open request for the logical volume.

			//	Logical volume open requests are done primarily to get/set volume
			//	information, lock the volume, dismount the volume (using the IOCTL
			//	FSCTL_DISMOUNT_VOLUME) etc.

			//	If a volume open is requested, perform checks to ensure that
			//	invalid options have not also been specified ...
			if ((OpenTargetDirectory) || (PtrExtAttrBuffer)) {
				RC = STATUS_INVALID_PARAMETER;
				try_return(RC);
			}

			if (DirectoryOnlyRequested) {
				// a volume is not a directory
				RC = STATUS_NOT_A_DIRECTORY;
				try_return(RC);
			}

			if ((RequestedDisposition != FILE_OPEN) && (RequestedDisposition != FILE_OPEN_IF)) {
				// cannot create a new volume, I'm afraid ...
				RC = STATUS_ACCESS_DENIED;
				try_return(RC);
			}

			RC = SFsdOpenVolume(PtrVCB, PtrIrpContext, PtrIrp, ShareAccess, PtrSecurityContext, PtrNewFileObject);
			ReturnedInformation = PtrIrp->IoStatus.Information;

			try_return(RC);
		}

		// Your FSD might wish to implement the open-by-id option. The "id"
		//	is some unique numerical representation of the on-disk object.
		//	The caller then therefore give you this file id and your FSD
		//	should be completely capable of "opening" the object (it must
		//	exist since the caller received an id for the object from your
		//	FSD in a "query file" call ...

		//	If the file has been deleted in the meantime, you can return
		//	"not found"
		if (OpenByFileId) {
			// perform the open ...
			// RC = SFsdOpenByFileId(PtrIrpContext, PtrIrp ....);
			// try_return(RC);
		}

		// Now determine the starting point from which to begin the parsing
		if (PtrRelatedFileObject) {
			// We have a user supplied related file object.
			//	This implies a "relative" open i.e. relative to the directory
			//	represented by the related file object ...

			//	Note: The only purpose FSD implementations ever have for
			//	the related file object is to determine whether this
			//	is a relative open or not. At all other times (including
			//	during I/O operations), this field is meaningless from
			//	the FSD's perspective.
			if (!(PtrRelatedFCB->FCBFlags & SFSD_FCB_DIRECTORY)) {
				// we must have a directory as the "related" object
				RC = STATUS_INVALID_PARAMETER;
				try_return(RC);
			}

			// So we have a directory, ensure that the name begins with
			//	a "\" i.e. begins at the root and does *not* begin with a "\\"
			//	NOTE: This is just an example of the kind of path-name string
			//	validation that a FSD must do. Although the remainder of
			//	the code may not include such checks, any commercial
			//	FSD *must* include such checking (no one else, including
			//	the I/O Manager will perform checks on your FSD's behalf)
			if ((RelatedObjectName.Length == 0) || (RelatedObjectName.Buffer[0] != L'\\')) {
				RC = STATUS_INVALID_PARAMETER;
				try_return(RC);
			}

			// similarly, if the target file name starts with a "\", it
			//	is wrong since the target file name can no longer be absolute
			if ((TargetObjectName.Length != 0) && (TargetObjectName.Buffer[0] == L'\\')) {
				RC = STATUS_INVALID_PARAMETER;
				try_return(RC);
			}

			// Create an absolute path-name. You could potentially use
			//	the absolute path-name if you cache previously opened
			//	file/directory object names.
			{
				AbsolutePathName.MaximumLength = TargetObjectName.Length + RelatedObjectName.Length + sizeof(WCHAR);
				if (!(AbsolutePathName.Buffer = ExAllocatePool(PagedPool, AbsolutePathName.MaximumLength))) {
					RC = STATUS_INSUFFICIENT_RESOURCES;
					try_return(RC);
				}

				RtlZeroMemory(AbsolutePathName.Buffer, AbsolutePathName.MaximumLength);

				RtlCopyMemory((void *)(AbsolutePathName.Buffer), (void *)(RelatedObjectName.Buffer), RelatedObjectName.Length);
				AbsolutePathName.Length = RelatedObjectName.Length;
				RtlAppendUnicodeToString(&AbsolutePathName, L"\\");
				RtlAppendUnicodeToString(&AbsolutePathName, TargetObjectName.Buffer);
			}

		} else {

			// The suplied path-name must be an absolute path-name i.e.
			//	starting at the root of the file system tree
         if (TargetObjectName.Buffer[0] != L'\\') {
				RC = STATUS_INVALID_PARAMETER;
				try_return(RC);
			}

			{
				AbsolutePathName.MaximumLength = TargetObjectName.Length;
				if (!(AbsolutePathName.Buffer = ExAllocatePool(PagedPool, AbsolutePathName.MaximumLength))) {
					RC = STATUS_INSUFFICIENT_RESOURCES;
					try_return(RC);
				}

				RtlZeroMemory(AbsolutePathName.Buffer, AbsolutePathName.MaximumLength);

				RtlCopyMemory((void *)(AbsolutePathName.Buffer), (void *)(TargetObjectName.Buffer), TargetObjectName.Length);
				AbsolutePathName.Length = TargetObjectName.Length;
			}
		}

		// go into a loop parsing the supplied name

		//	Use the algorithm supplied in the book to implement this
		//	loop.

		//	Note that you may have to "open" intermediate directory objects
		//	while traversing the path. You should try to reuse existing code
		//	whenever possible therefore you should consider using a common
		//	open routine regardless of whether the open is on behalf of the
		//	caller or an intermediate (internal) open performed by your driver.

		//	But first, check if the caller simply wishes to open the root
		//	of the file system tree.
		if (AbsolutePathName.Length == 2) {
			// this is an open of the root directory, ensure that	the caller has not requested a file only
			if (FileOnlyRequested || (RequestedDisposition == FILE_SUPERSEDE) || (RequestedDisposition == FILE_OVERWRITE) ||
				 (RequestedDisposition == FILE_OVERWRITE_IF)) {
				RC = STATUS_FILE_IS_A_DIRECTORY;
				try_return(RC);
			}

			// Insert code to open root directory here. Include creation of a new CCB structure.
			//	e.g. RC = SFsdOpenRootDirectory(...);

			try_return(RC);
		}

		if (PtrRelatedFileObject) {
			// Insert code such that your "start directory" is
			//	the one identified by the related file object
		} else {
			// Insert code to start at the root of the file system
		}

		// NOTE: If your FSD does not support access checking i.e.
		//	your FSD does not check "traversal" privileges,
		//	you could easily maintain a "prefix" cache containing
		//	path-names and open FCB pointers. Then, if the requested
		//	path-name is already present in the cache i.e. someone
		//	had opened it earlier, you can avoid the tedious traversal
		//	of the entire path-name performed below and described in
		//	the book ...

		//	If you do not maintain such a prefix table cache of previously
		//	opened object names or if you do not find the name to be opened
		//	in the cache, then:

		//	Get the "next" component in the name to be parsed. Note that
		//	obtaining the next string component is similar to the strtok
		//	library routine where the separator is a "\"

		//	Your FSD should also always check the validity of the token
		//	to ensure that only valid characters comprise the path/file name

		//	Insert code to open the starting directory here.

		while (TRUE) {
			// Insert code to perform the following tasks here:

			//	(a) acquire the parent directory FCB MainResource exclusively
			//	(b) ensure that the parent directory in which you will perform
			//		 a lookup operation is indeed a directory
			//	(c) if there are no more components left after this one in the
			//		 pathname supplied by the user, break;
			//	(d) attempt to lookup the sub-directory in the parent
			//	(e) if not found, return STATUS_OBJECT_PATH_NOT_FOUND
			//	(f) Otherwise, open the new sub-directory and make it
			//		 the "parent"
			//	(g) go back and repeat the loop for the next component in
			//		 the path

			//	NOTE: If your FSD supports it, you should always check
			//	that the caller has appropriate privileges to traverse
			//	the directories being searched.
		}

		// Now we are down to the last component, check it out to see if it exists ...

		// If "open target directory" was specified
		if (OpenTargetDirectory) {
			if (NT_SUCCESS(RC)) {
				// file exists, set this information in the Information field
				ReturnedInformation = FILE_EXISTS;
			} else {
				RC = STATUS_SUCCESS;

				// Tell the I/O Manager that file does not exit
				ReturnedInformation = FILE_DOES_NOT_EXIST;
			}

			// Now, do the following:

			//	(a) Replace the string in the FileName field in the
			//		 PtrNewFileObject to identify the "target name"
			//		 only (sans the path leading to the object)
				{
					unsigned int	Index = (AbsolutePathName.Length - 1);

					// Back up until we come to the last '\'
					// But first, skip any trailing '\' characters

					while (AbsolutePathName.Buffer[Index] == L'\\') {
						ASSERT(Index >= sizeof(WCHAR));
						Index -= sizeof(WCHAR);
						// Skip this length also
						PtrNewFileObject->FileName.Length -= sizeof(WCHAR);
					}

					while (AbsolutePathName.Buffer[Index] != L'\\') {
						// Keep backing up until we hit one
						ASSERT(Index >= sizeof(WCHAR));
						Index -= sizeof(WCHAR);
					}

					// We must be at a '\' character
					ASSERT(AbsolutePathName.Buffer[Index] == L'\\');
					Index++;

					// We can now determine the new length of the filename
					// and copy the name over
					PtrNewFileObject->FileName.Length -= (unsigned short)(Index*sizeof(WCHAR));
					RtlCopyMemory(&(PtrNewFileObject->FileName.Buffer[0]),
									  &(PtrNewFileObject->FileName.Buffer[Index]),
                             PtrNewFileObject->FileName.Length);
				}

			//	(b) Return with the target's parent directory opened
			//	(c) Update the file object FsContext and FsContext2 fields
			//		 to reflect the fact that the parent directory of the
			//		 target has been opened

			try_return(RC);
		}

		if (!NT_SUCCESS(RC)) {
			// Object was not found, create if requested
			if ((RequestedDisposition == FILE_CREATE) || (RequestedDisposition == FILE_OPEN_IF) ||
				 (RequestedDisposition == FILE_OVERWRITE_IF)) {
				// Create a new file/directory here ...
				// Note that a FCB structure will be allocated at this time
				// and so will a CCB structure. Assume that these are called
				// PtrNewFCB and PtrNewCCB respectively.
				// Further, note that since the file is being created, no other
				// thread can have the file stream open at this time.

				// Open the newly created object

				// Set the allocation size for the object is specified
	
				// Set extended attributes for the file ...

				// Set the Share Access for the file stream.
				// The FCBShareAccess field will be set by the I/O Manager.
				IoSetShareAccess(DesiredAccess, ShareAccess, PtrNewFileObject, &(PtrNewFCB->FCBShareAccess));

				RC = STATUS_SUCCESS;
				ReturnedInformation = FILE_CREATED;
			}

			try_return(RC);

		} else {
			if (RequestedDisposition == FILE_CREATE) {
				ReturnedInformation = FILE_EXISTS;
				RC = STATUS_OBJECT_NAME_COLLISION;
				try_return(RC);
			}

			// Insert code to open the target here, return if failed

			// The FSD will allocate a new FCB structure if no such structure
			// currently exists in memory for the file stream.
			// A new CCB will always be allocated.
			// Assume that these structures are named PtrNewFCB and PtrNewCCB
			// respectively.
			// Further, you should obtain the FCB MainResource exclusively
			// at this time.

			// Check if caller wanted a directory only and target object
			//	not a directory, or caller wanted a file only and target
			//	object not a file ...
			if (FileOnlyRequested && (PtrNewFCB->FCBFlags & SFSD_FCB_DIRECTORY)) {
				// Close the new FCB and leave.
				//	SFsdCloseCCB(PtrNewCCB);
				RC = STATUS_FILE_IS_A_DIRECTORY;
				try_return(RC);
			}

			if ((PtrNewFCB->FCBFlags & SFSD_FCB_DIRECTORY) && ((RequestedDisposition == FILE_SUPERSEDE) ||
				  (RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF))) {
				RC = STATUS_FILE_IS_A_DIRECTORY;
				try_return(RC);
			}

			if (DirectoryOnlyRequested && !(PtrNewFCB->FCBFlags & SFSD_FCB_DIRECTORY)) {
				// Close the new FCB and leave.
				//	SFsdCloseCCB(PtrNewCCB);
				RC = STATUS_NOT_A_DIRECTORY;
				try_return(RC);
			}

			// Check share access and fail if the share conflicts with an existing
			// open.
			if (PtrNewFCB->OpenHandleCount > 0) {
				// The FCB is currently in use by some thread.
				// We must check whether the requested access/share access
				// conflicts with the existing open operations.

				if (!NT_SUCCESS(RC = IoCheckShareAccess(DesiredAccess, ShareAccess, PtrNewFileObject,
												&(PtrNewFCB->FCBShareAccess), TRUE))) {
					// SFsdCloseCCB(PtrNewCCB);
					try_return(RC);
				}
			} else {
					IoSetShareAccess(DesiredAccess, ShareAccess, PtrNewFileObject, &(PtrNewFCB->FCBShareAccess));
			}

			ReturnedInformation = FILE_OPENED;

			// If a supersede or overwrite was requested, do so now ...
			if (RequestedDisposition == FILE_SUPERSEDE) {
				// Attempt the operation here ...
				//	RC = SFsdSupersede(...);
				if (NT_SUCCESS(RC)) {
					ReturnedInformation = FILE_SUPERSEDED;
				}
			} else if ((RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF)){
				// Attempt the operation here ...
				//	RC = SFsdOverwrite(...);
				if (NT_SUCCESS(RC)) {
					ReturnedInformation = FILE_OVERWRITTEN;
				}
			}
		}

		try_exit:	NOTHING;

	} finally {
		// Complete the request unless we are here as part of unwinding
		//	when an exception condition was encountered, OR
		//	if the request has been deferred (i.e. posted for later handling)
		if (RC != STATUS_PENDING) {
			// If we acquired any FCB resources, release them now ...

			// If any intermediate (directory) open operations were performed,
			//	implement the corresponding close (do *not* however close
			//	the target you have opened on behalf of the caller ...).

			if (NT_SUCCESS(RC)) {
				// Update the file object such that:
				//	(a) the FsContext field points to the NTRequiredFCB field
				//		 in the FCB
				//	(b) the FsContext2 field points to the CCB created as a
				//		 result of the open operation

				// If write-through was requested, then mark the file object
				//	appropriately
				if (WriteThroughRequested) {
               PtrNewFileObject->Flags |= FO_WRITE_THROUGH;
				}

			} else {
				// Perform failure related post-processing now
			}

			// As long as this unwinding is not being performed as a result of
			//	an exception condition, complete the IRP ...
			if (!(PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_EXCEPTION)) {
				PtrIrp->IoStatus.Status = RC;
				PtrIrp->IoStatus.Information = ReturnedInformation;

				// Free up the Irp Context
				SFsdReleaseIrpContext(PtrIrpContext);
	
				// complete the IRP
				IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT);
			}
		}

		if (AcquiredVCB) {
			ASSERT(PtrVCB);
         SFsdReleaseResource(&(PtrVCB->VCBResource));
			AcquiredVCB = FALSE;
		}

		if (AbsolutePathName.Buffer != NULL) {
			ExFreePool(AbsolutePathName.Buffer);
		}
	}

	return(RC);
}


/*************************************************************************
*
* Function: SFsdOpenVolume()
*
* Description:
*	Open a logical volume for the caller.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdOpenVolume(
PtrSFsdVCB				PtrVCB,					// volume to be opened
PtrSFsdIrpContext		PtrIrpContext,			// IRP context
PIRP						PtrIrp,					// original/user IRP
unsigned short			ShareAccess,			// share access
PIO_SECURITY_CONTEXT	PtrSecurityContext,	// caller's context (incl access)
PFILE_OBJECT			PtrNewFileObject)		// I/O Mgr. created file object
{
	NTSTATUS				RC = STATUS_SUCCESS;
	PtrSFsdCCB			PtrCCB = NULL;

	try {
		// check for exclusive open requests (using share modes supplied)
		//	and determine whether it is even possible to open the volume
		//	with the specified share modes (e.g. if caller does not
		//	wish to share read or share write ...)
	
		//	Use IoCheckShareAccess() and IoSetShareAccess() here ...	
		//	They are defined in the DDK.

		//	You might also wish to check the caller's security context
		//	to see whether you wish to allow the volume open or not.
		//	Use the SeAccessCheck() routine described in the DDK for	this purpose.
	
		// create a new CCB structure
		if (!(PtrCCB = SFsdAllocateCCB())) {
			RC = STATUS_INSUFFICIENT_RESOURCES;
			try_return(RC);
		}

		// initialize the CCB
		PtrCCB->PtrFCB = (PtrSFsdFCB)(PtrVCB);
		InsertTailList(&(PtrVCB->VolumeOpenListHead), &(PtrCCB->NextCCB));

		// initialize the CCB to point to the file object
		PtrCCB->PtrFileObject = PtrNewFileObject;

		SFsdSetFlag(PtrCCB->CCBFlags, SFSD_CCB_VOLUME_OPEN);

		// initialize the file object appropriately
		PtrNewFileObject->FsContext = (void *)(PtrVCB);
		PtrNewFileObject->FsContext2 = (void *)(PtrCCB);

		// increment the number of outstanding open operations on this
		//	logical volume (i.e. volume cannot be dismounted)

		//	You might be concerned about 32 bit wrap-around though I would
		//	argue that it is unlikely ... :-)
		(PtrVCB->VCBOpenCount)++;
	
		// now set the IoStatus Information value correctly in the IRP
		//	(caller will set the status field)
		PtrIrp->IoStatus.Information = FILE_OPENED;
	
		try_exit:	NOTHING;
	} finally {
		NOTHING;
	}

	return(RC);
}


/*************************************************************************
*
* Function: SFsdInitializeFCB()
*
* Description:
*	Initialize a new FCB structure and also the sent-in file object
*	(if supplied)
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdInitializeFCB(
PtrSFsdFCB				PtrNewFCB,		// FCB structure to be initialized
PtrSFsdVCB				PtrVCB,			// logical volume (VCB) pointer
PtrSFsdObjectName		PtrObjectName,	// name of the object
uint32					Flags,			// is this a file/directory, etc.
PFILE_OBJECT			PtrFileObject)	// optional file object to be initialized
{
	// Initialize the disk dependent portion as you see fit

	// Initialize the two ERESOURCE objects
	ExInitializeResourceLite(&(PtrNewFCB->NTRequiredFCB.MainResource));
	ExInitializeResourceLite(&(PtrNewFCB->NTRequiredFCB.PagingIoResource));

	PtrNewFCB->PtrVCB = PtrVCB;

	// caller MUST ensure that VCB has been acquired exclusively
	InsertTailList(&(PtrVCB->NextFCB), &(PtrNewFCB->NextFCB));

	// initialize the various list heads
	InitializeListHead(&(PtrNewFCB->NextCCB));

	PtrNewFCB->ReferenceCount = 1;
	PtrNewFCB->OpenHandleCount = 1;

	PtrNewFCB->FCBFlags = Flags;

	PtrNewFCB->FCBName = PtrObjectName;

	if (PtrFileObject) {
		PtrFileObject->FsContext = (void *)(&(PtrNewFCB->NTRequiredFCB));
	}

	return;
}

