/*static char *SCCSID = "@(#)fs_ocrea.c	6.2 91/01/14";*/
#define SCCSID "@(#)fs_ocrea.c	6.2 91/01/14"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */

/*
 *  FS_OCREA.C
 *
 *    DESCRIPTION
 *    
 *      Open either a file or the entire volume.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FS_OPENCREATE ()    -   Open a file or the entire volume
 *
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  1/30/96     Steven Pratt    Add init for CD ReadAhead FileDepData  
 * 
 *  6/25/90     Brian Andrew    Allow files to be opened for execution.
 * 
 *  5/21/90     Brian Andrew    Cleared 'must_have' bits before opening
 *                              file (dcr 853)
 * 
 *  5/1/90      Brian Andrew    Added check for access type for DASD opens.
 * 
 *  2/22/90     Brian Andrew    Search for file first before checking
 *                              whether accessing for write or read/write.
 *                              Follow the HPFS/FAT error path.  Only if
 *                              the eventual action is to open a file for
 *                              writing (i.e. a new file) do we return
 *                              a CDROM specific error.
 *  12/7/89     Brian Andrew    Return ERROR_ACCESS_DENIED if looking for
 *                              a directory and the directory is found.
 * 
 *  11/14/89    Brian Andrew    Added DBCS support (ParentEndLenDBCS)
 * 
 *  10/1/89     Brian Andrew    Do PROBEBUF on 'pusRetAction'
 * 
 *  9/28/89     Brian Andrew    ERROR_INVALID_ACCESS for opening directories.
 * 
 *  9/10/89     Brian Andrew    Removed param pEABuf to try to correct stack
 *                              problem
 * 
 *  9/8/89      Brian Andrew    Converted NO MORE FILES to FILE NOT FOUND
 * 
 *  9/5/89      Brian Andrew    First working version
 * 
 *  8/4/89      Brian Andrew    Initial Blank Template
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 *  Check on applications which open files with read\write mode, even though
 *  they plan on legal read operations only.
 *
 *  Check on what type of object a direct access opens, I assuming it's
 *  a 'device'
 *
 */


#define INCL_DOSERRORS
#define INCL_DOSINFOSEG
#define INCL_NOPMAPI
#include <os2.h>
#include <fsd.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "file.h"
#include "buffer.h"
#include "mem.h"
#include "heap.h"
#include "message.h"

#ifdef STATS_H
#include "stats.h"
#endif

#include "fsdata.h"
#include "pr_memin.h"
#include "pr_file.h"
#include "pr_dir.h"
#include "pr_strng.h"
#include "pr_dbcs.h"
#include "memory.h"

#ifdef DEBUG
#include "pr_DEBUG.h"
#endif



/***    FS_OPENCREATE ()    -   Open either a file or the entire volume.
 *      
 *  DESCRIPTION
 *
 *      This function is used to access either a file on the disk or the
 *      entire volume via a direct access only.  At this point we support
 *      access for the purpose of reading only, although we may have to
 *      permit an open for read/write to support applications which
 *      use that method for all file access.  (MSCDEX allows this)
 *
 *      If the direct access bit is set, we do all of the action specific
 *      to a direct access open.  For a direct access open, we create
 *      a directory entry that refers to a single file which encompasses
 *      the entire disk.  See the function 'CreateDirAccDE' in the
 *      file 'dir\dir.c' for details.
 *      
 *      For a regular open, we first try to find the file.  If successful,
 *      this will give us the first directory entry for the file.  We check
 *      the OpenFlag to see what action to take depending on the result of
 *      the search.  If the action is to create or replace the file, we
 *      generate an error.  We also set the type of object opened to 'file'
 *      in the file independent area.
 *
 *      If we didn't find the file and are supposed to create it, we return
 *      CDROM_WRITE_PROTECT.
 *      
 *      For successful direct access or regular file open operations, we
 *      take the following actions.
 *      
 *          Copy the creation and modification time stamps into the file
 *          independent structure (copied from the file's directory entry).
 *          Also from the directory entry, copy the file size.  Finally
 *          set the current position to 0 and return the action 'file
 *          opened' to the user.
 *      
 *      
 *  ENTRY
 *
 *      pcdfsi      = Pointer to file system independent directory
 *                      information.  Contains the bits indicating if the
 *                      dependent area is vilaid.
 *      pcdfsd      = Pointer to the directory dependent area.  Contains
 *                      the pathe number of the current directory.
 *      pcName      = Pointer to the full path name of the desired file.
 *      usCurDirEnd = Index of the end of the current directory in 'pcName'.
 *                      If the current directory is not in the path, this
 *                      value is -1.
 *      psffsi      = Pointer to file independent area.  Contains the file's
 *                      time stamps, size and position information.
 *      psffsd      = Pointer to file dependent area.  Contains the
 *                      directory entry for the first and most recently accessed
 *                      extents.  It also contains the semaphores and flags
 *                      which control the access to this data.
 *      ulOpenMode  = Array of bits, indicating whether this is a direct
 *                      access only and also the sharing/access modes.
 *      usOpenFlag  = Indicates the action to take when attempting to open
 *                      a file.  Include actions for whether the file being
 *                      searched for existed or didn't exist.
 *      pusRetAction = Pointer to area to store action taken.  If we
 *                      encounter no errors we return 'open'.
 *      usAttr      = Attributes of the file to open.
 *      pEABuf      = Pointer to extended attribute buffer.
 *      pUnused     = Unused by us.
 *
 *
 *  LOCAL
 *
 *      usRet       = Return value from function.
 *      pdeFirstDE  = Pointer to directory entry struct.  Used during search,
 *                      first to store the entry for the parent directory,
 *                      then to store the entry for the target file.  Stored
 *                      in the file dependent area as the first extent of
 *                      the file.
 *      pdeLastDE   = Pointer to directory entry structure.  Used after
 *                      successful search to store copy of the FirstDE in
 *                      the file dependent area.  Used to indicate the last
 *                      accessed extent of the file.
 *      pvpfsi      = Pointer to the volume independent information.  Not
 *                      used by us.
 *      pvddVolData = Pointer to volume dependent information.  Contains
 *                      the volume descriptor, the path table and the start
 *                      of all the cached sectors for this volume.
 *      cbNameLen   = Used to store the length of pcName.
 *      cbBaseOffset = Offset in 'pcName' of the last '\' character before
 *                      the target file name.
 *      snLastSect   = Stores the last sector containing directory entries
 *                      for the parent directory of the search target.
 *      usAccessMode = Result of clearing upper 28 bytes of ulOpenMode to
 *                      get just the access bits.
 *
 *
 *  EXIT
 *
 *      NO_ERROR if search and open operation were successful.
 *      Return value from FSH_PROBEBUF if error.
 *      Return value from FSH_GETVOLPARMS if error.
 *      Return value from FindNextFile if error.
 *      Return value from allocation routines if allocation fails.
 *      ERROR_INVALID_ACCESS if a directory is found.
 */

int far pascal
FS_OPENCREATE (pcdfsi, pcdfsd, pcName, usCurDirEnd, psffsi, psffsd, ulOpenMode,
			  usOpenFlag, pusRetAction, usAttr, pEABuf, pUnused)
PCDFSI      pcdfsi;
PCDFSD      pcdfsd;
PCHAR       pcName;
USHORT      usCurDirEnd;
PSFFSI      psffsi;
PSFFSD      psffsd;
ULONG       ulOpenMode;
USHORT      usOpenFlag;
PUSHORT     pusRetAction;
USHORT      usAttr;
PCHAR       pEABuf;
PUSHORT     pUnused;
{

	USHORT          usRet;
	PDIR_ENTRY      pdeFirstDE;
	PDIR_ENTRY      pdeLastDE;
	PVPFSI          pvpfsi;
	PVOL_DEP_DATA   pvddVolData;
	USHORT          cbNameLen;
	USHORT          cbBaseOffset;
	USHORT          usAccessMode;

#ifdef DEBUG
	dprintf ("FS_OCREA: Mode=%lx, Flag=%x, Attr=%x, DirectAccess=%d",
			 ulOpenMode, usOpenFlag, usAttr,
			 (USHORT) (ulOpenMode & OPEN_DIR_ACC));

	if (!(ulOpenMode & OPEN_DIR_ACC))
		dprintf (", File=%Fs", pcName);

	dprintf ("\n");

#endif

#ifdef STATS_H
		CurSt.st_open++;
#endif

	/* Clear the 'must have' bits in the file attribute */
	usAttr &= (~MUST_HAVE_MASK);

	/* Check if first time initialization */
	if (usRet = CHECK_INIT)
		return usRet;

	/* Try to get the volume parameters */
	if (!(usRet = FSH_GETVOLPARM (  pcdfsi->cdi_hVPB,
									&pvpfsi,
									(PPVPFSD) &pvddVolData)))
	{

		/*
		 *  The following structures are going to be needed
		 *
		 *      DIR_ENTRY       for the directory entry for the parent
		 *                      directory (pdeFirstDE)
		 *
		 *      DIR_ENTRY       for the directory entry in the file
		 *                      dependent area referring to the most
		 *                      recent extent
		 *
		 *  pdeFirstDE is needed to compute the last sector to search in
		 *  for the directory.  
		 */

		/* Try to allocate the last directory entry struct */
		if (!(usRet = GetDirEntry (&pdeLastDE)))
		{
			/* Try to allocate the first extent DIR_ENTRY */
			if (usRet = GetDirEntry (&pdeFirstDE))
			{
#ifdef DEBUG                                                                           
				dprintf ("\tFS_OCREA:  First GetDirEntry  --  %d\n",
							usRet);
#endif                                                                              
				FREE_DIR_ENTRY (pdeLastDE);

			} /* end if failed first DIR_ENTRY */

		} /* end if got last DIR_ENTRY */

#ifdef DEBUG                                                                           
		/* Else couldn't allocate LastDE */
		else
		{
			dprintf ("\tFS_OCREA:  Last GetDirEntry  --  %d\n",
						usRet);

		} /* end else couldn't allocate LastDE */
#endif                                                                              

	} /* end if get volume parms */

#ifdef DEBUG                                                                           
	else
	{
		dprintf ("\tFS_OCREA:  GETVOLPARMS  --  %d\n",
				 usRet);

	}
#endif                                                                                 

	/*
	 *  If no errors, we continue with the open process.  For a direct
	 *  access open we create a directory entry that simulates a single file
	 *  on the entire disk.  See the function 'DirAccDE' in file.c for
	 *  details.
	 */
	if (!usRet)
	{
		/* Assume open is NOT direct access */
		((PFILE_DEP_DATA) psffsd)->fDirAccess = FALSE;

		/* Check for direct access */
		if (ulOpenMode & OPEN_DIR_ACC)
		{
			/* Fill in the directory entry by calling 'DirAccDE' */
			DirAccDE (pdeLastDE, pvddVolData->pvdVolDesc);

			/* Show that this open direct access */
			((PFILE_DEP_DATA) psffsd)->fDirAccess = TRUE;

		} /* end if open direct access */

		/*
		 *  If necessary, take whatever action is needed to deal with
		 *  someone trying to open a directory now.  Clear the bit in
		 *  usAttr or simply return an error.
		 */

		/* Else try to find the specific file */
		else
		{
			cbNameLen = istrlen (pcName);

			cbBaseOffset = ParentEndLenDBCS (pcName, cbNameLen);


			/*
			 *  We check whether this search file is the root directory.
			 *  If the base offset is one less than the string length.
			 *  In this case the desired file is the root directory in
			 *  in the volume descriptor.  It is illegal to open a dir.
			 */

			if (cbBaseOffset == (cbNameLen - 1))
			{
				usRet = ERROR_ACCESS_DENIED;

			} /* end if root directory */


			/* Get the directory entry for the directory to search in */
			else if (!(usRet = GetParentDE (pvddVolData, pcdfsi,
									   pdeFirstDE,
									   cbBaseOffset, pcName, cbNameLen,
									   usCurDirEnd,
									   (PDIR_DEP_DATA) pcdfsd)))
			{
				/*
				 *  If no errors have occurred, the directory to search is in
				 *  pdeFirstDE.
				 *
				 *  We call FindNextFile with the path entry of the parent and
				 *  the following computed last sector value.
				 *
				 *  If a match is made we can copy the attribute word directly.
				 */

				/* Now call FindNext */
				if ((usRet = FindNextFile (pcName + cbBaseOffset + 1,
										   ATTR_MATCH_ALL,
										   pdeLastDE,
										   pdeFirstDE,
										   TRUE,
										   pvddVolData, pcdfsi->cdi_hVPB)))
				{
#ifdef DEBUG                                                                           
					dprintf
					("\tFS_OCREA:   FindNextFile  --  %d\n",
							 usRet);
#endif                                                                                 

					/* Convert ERROR_NO_MORE_FILES to ERROR_FILE_NOT_FOUND */
					if (usRet == ERROR_NO_MORE_FILES)
					{
						usRet = ERROR_FILE_NOT_FOUND;

					} /* end covert error */

				} /* end if couldn't find a match */

				/*
				 *  If the file returned is a directory, we return
				 *  ERROR_INVALID_ACCESS.  It is illegal to open
				 *  a directory.
				 */

				else if (pdeLastDE->afFileAttr & DOS_ATTR_DIRECTORY)
				{
					usRet = ERROR_ACCESS_DENIED;

				} /* end else if directory */

			} /* end if no error, getting parent directory */

#ifdef DEBUG                                                                           
			/* Else we couldn't find the search directory */
			else
			{
				dprintf
				("\tFS_OCREA:  GetParentDE  --  %d\n",
						 usRet);

			} /* end else couldn't find the search DE */
#endif                                                     
			
		} /* end else look for file */

		/*
		 *  If there are no errors, we have the matching file.  We
		 *  perform the following tests in conjuction with the
		 *  usOpenFlag
		 *
		 *      If file found and
		 *
		 *          usOpen = fail       return ERROR_OPEN_FAILED
		 *          usOpen = replace    return ERROR_NOT_SUPPORTED
		 *          usOpen = open
		 *              if access is R/W or W
		 *                              return ERROR_ACCESS_DENIED
		 *              if access is R
		 *                              return NO_ERROR
		 *          else                return ERROR_INVALID_PARM
		 */
		if (!usRet)
		{
			/* Mask out the access mode. */
			usAccessMode = (USHORT) (ulOpenMode & OPEN_ACCESS);

			switch (usOpenFlag & OPEN_FLAG)
			{
				case OPEN_FLAG_FAIL:
					usRet = ERROR_OPEN_FAILED;
					break;

				case OPEN_FLAG_OPEN:

					switch (usAccessMode)
					{
						case OPEN_ACC_RW :
						case OPEN_ACC_WRITE :
							usRet = ERROR_ACCESS_DENIED;
							break;

						case OPEN_ACC_READ :
						case OPEN_ACC_EXE :
							psffsi->sfi_type =  (   psffsi->sfi_type &
													STYPE_FCB) |
								
												STYPE_FILE;

							break;

						default :
							usRet = ERROR_INVALID_PARAMETER;
					}

					break;

				case OPEN_FLAG_RPL:
					switch (usAccessMode)
					{
						case OPEN_ACC_RW :
						case OPEN_ACC_WRITE :
						case OPEN_ACC_READ :
						case OPEN_ACC_EXE :
							usRet = ERROR_ACCESS_DENIED;
							break;

						default :
							usRet = ERROR_INVALID_PARAMETER;
							break;
					}

					break;

				default:
					usRet = ERROR_INVALID_PARAMETER;
					break;

			} /* end switch */

		} /* end if found file */

		/*
			*  Else if we didn't find the file.  We then look at the Open
			*  flag bits to what we do in the case where the file doesn't
			*  exist.  If 'usRet' is ERROR_ACCESS_DENIED we skip this
			*  part.
			*/
		else if (usRet != ERROR_ACCESS_DENIED)
		{
			switch (usOpenFlag & OPEN_FLAGD)
			{
				case OPEN_FLAGD_FAIL:
					usRet = ERROR_OPEN_FAILED;
					break;

				case OPEN_FLAGD_CR :
					usRet = CDROM_WRITE_PROTECT;
					break;

				default :
					usRet = ERROR_INVALID_PARAMETER;
					break;

			} /* end switch */

		} /* end else didn't find the file */


		/*
		 *  At this point, if the open was successful, the file information
		 *  is stored in pdeThisDE.  All the special direct access stuff has
		 *  been accounted for.  All we need to do is to transfer the data
		 *  from the directory entry to the file independent area, copy the
		 *  the directory entry into pdeLastDE and initialize the directory
		 *  independent area.
		 */

		if (!usRet)
		{
			/* Copy the information into the file independent area */
			CopyDEtoSFI (psffsi, pdeLastDE);

			/* Clear some fields in the DE */
			pdeLastDE->snLastSectorFile = NULL_BUF;

			/* Copy the directory entry into LastDE */
			*pdeFirstDE = *pdeLastDE;

			/* Initialize the file dependent area */
			((PFILE_DEP_DATA) psffsd)->pdeFirstDirRec = pdeFirstDE;
			((PFILE_DEP_DATA) psffsd)->pdeLastDirRec = pdeLastDE;
			((PFILE_DEP_DATA) psffsd)->usCountRead = 0;
			((PFILE_DEP_DATA) psffsd)->fCloseFlag = FALSE;
			((PFILE_DEP_DATA) psffsd)->sFileSem = 0;
			((PFILE_DEP_DATA) psffsd)->usSqntlCount = 0xFFFF;
			((PFILE_DEP_DATA) psffsd)->snNextSqntl = 0;
			((PFILE_DEP_DATA) psffsd)->snNextReadAhead = 0;

			/* Return action as file opened */
			*pusRetAction = FILE_EXISTED;

		} /* end if successful open */

		/* Else free up the directory structures */
		else
		{
			FREE_DIR_ENTRY (pdeFirstDE);
			FREE_DIR_ENTRY (pdeLastDE);

		} /* else open was unsuccessful */

	} /* end if no allocation errors */


#ifdef DEBUG
	dprintf ("    FS_OCREA:  Exit  --  %d\n", usRet);
#endif

	return usRet;

	/* no warnings */
	pcdfsi; pcdfsd; psffsi; psffsd; pEABuf; pUnused; usAttr;

}   /* end FS_OPENCREATE */
