/*static char *SCCSID = "@(#)file.c	6.4 91/12/11";*/
#define SCCSID "@(#)file.c	6.4 91/12/11"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */

/*
 *  FILE.C
 *
 *    DESCRIPTION
 *
 *      Operations on files.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FileMatch ()        -   Compare filenames and attributes for a match.
 *                              Will do wildcard compares.
 *
 *      DirAccDE ()         -   Create a directory entry for a direct access
 *                              open.
 *
 *      CopyDEtoSFI ()      -   Copy the data from a directory entry into
 *                              the file independent area.
 *
 *      FNextInfoFit ()     -   Checks if the file information for a matching
 *                              file fits into the user's buffer.
 *
 *      CopyDEtoFileStatus () - Copy the information from a directory entry
 *                              into a FILESTATUS structure
 *
 *
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  8/3/90      Brian Andrew    OSO1489: Remove reference to path info
 *                              level 6.
 *
 *  5/21/90     Brian Andrew    Added check for 'must have' bits in file
 *                              matching. (dcr 853)
 *
 *  4/9/90      Brian Andrew    Added param to 'FileMatch'.  String buffer
 *                              to use for FSH_WILDMATCH.
 *
 *  4/4/90      Brian Andrew    Cleanup response to code review.  Some
 *                              cosmetic changes.  FPInfoFit : check buffers
 *                              only as they are accessed.
 *
 *  3/8/90      Brian Andrew    Fixed FileMatch not to pass 0 length
 *                              strings to FSH_WILDMATCH.
 *
 *  1/8/90      Brian Andrew    Cleaned up file.
 *
 *  1/5/90      Brian Andrew    Added cbData parameter to FNextInfoFit
 *                              to check on stated size of user's buffer
 *
 *  1/5/90      Brian Andrew    Added cbData parameter to FPInfoFit to
 *                              check on stated size of user's buffer.
 *
 *  12/20/89    Brian Andrew    Return 0 for allocation for directories.
 *                              this is to match HPFS and FAT.  For files
 *                              we return the allocation units as a multiple
 *                              of the logical block size.
 *
 *  12/14/89    Brian Andrew    Added checks to FileMatch to support version
 *                              numbers.  Also support mixed case names.
 *
 *  12/6/89     Brian Andrew    Added PROBEBUF'S to FNextInfoFit instead of
 *                              doing one check in the outher functions.
 *                              PROBEBUF isn't valid across FSH_YIELD.
 *
 *  12/6/89     Brian Andrew    Added PROBEBUF's to FPInfoFit instead of
 *                              doing one check in the outer functions.
 *                              PROBEBUF isn't valid across FSH_YIELD.
 *
 *  9/29/89     Paul Bingman    Added dummy Level 4 support to FPInfoFit.
 *                              Also added buffer validation checks for
 *                              level 3 and 4 of GEAList and FEAList.
 *
 *  9/22/89     Paul Bingman    Added dummy Level 3 support to FPInfoFit.
 *
 *  9/20/89     Paul Bingman    Added Level 3 section to FNextInfoFit, but
 *                              without any real EA support.
 *
 *  9/14/89     Brian Andrew    Removed extra USHORT in FNextInfoFit, in
 *                              determining size of transferred data.
 *
 *  9/8/89      Brian Andrew    Added CopyDEtoFileStatus
 *
 *  9/7/89      Brian Andrew    Added FNextInfoFit
 *
 *  9/6/89      Brian Andrew    Added DirAccDE and CopyDEtoSFI
 *
 *  8/26/89     Brian Andrew    Wrote FileMatch
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 *  Add real EA support to FNextInfoFit and FPInfoFit.
 */

#define INCL_DOSERRORS
#define INCL_DOS
#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 "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 "file.h"
#include "pr_file.h"
#include "pr_strng.h"
#include "memory.h"

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

/****   FileMatch ()    -   Compare filenames and attributes to see
 *                          if they match.
 *
 *  DESCRIPTION
 *
 *      Attribute matches occur when the attributes in the directory
 *      entry are a subset of the search attributes.
 *
 *      We invert the bits in the search attributes, then do a bitwise
 *      AND with the directory entry file attribute.  If the result is
 *      0 then the directory entry is an attribute subset.
 *
 *      If the attributes match we call FSH_WILDMATCH to compare the
 *      strings.
 *
 *      NOTE -- the strings must be null terminated
 *
 *      If the names match on the first search, we only want to return the
 *      most recent version.  We do this by checking the file version
 *      against the most recent version and return true only if they are
 *      equal.
 *
 *      If the first match fails, its possible that a match including the
 *      version is being asked for.  If the directory entry has a version,
 *      we replace the ';' into the string (the version was added after a
 *      null character) and perform the comparison again.  If they match,
 *      we modify the length of the file ID.  (remember the ';' character).
 *
 *
 *  ENTRY
 *
 *      pSearchName     -   Pointer to search string
 *      afSearchAttr    -   Attributes for search
 *      pdeDirEnt       -   Directory entry to compare with
 *      psbStringBuf    -   String buffer for up-cased string
 *
 *
 *  LOCAL
 *
 *      fMatchFound     -   Return value
 *      cbNameLen       -   Length of the name stored in the DE
 *
 *
 *  EXIT
 *
 *      TRUE    -   If the names and attributes match
 *      FALSE   -   Otherwise
 *
 */

BOOL FileMatch (pSearchName, afSearchAttr, pdeDirEnt, psbStringBuf)
PCHAR           pSearchName;
USHORT          afSearchAttr;
PDIR_ENTRY      pdeDirEnt;
PSTRING_BUF     psbStringBuf;
{
	BOOL   fMatchFound;
	USHORT cbNameLen;

	/* Assume that no match found */
	fMatchFound = FALSE;

	/* Determine length of name, considering version number */
	cbNameLen =     pdeDirEnt->cbFileIDLen +
					(pdeDirEnt->cbVerLen ? 1 : 0) +
					pdeDirEnt->cbVerLen + 1;

	/* Copy the full file ID buffer over */
	memcpy (psbStringBuf, pdeDirEnt->szFileID, cbNameLen);

	/* Files don't match if pszPrcName is a null byte */
	if (*psbStringBuf &&
		!FSH_UPPERCASE (psbStringBuf, sizeof (STRING_BUF),
						psbStringBuf))
	{

		/* If the attributes match, check the must have bits */
		if (!((~afSearchAttr) & pdeDirEnt->afFileAttr))
		{
			USHORT  afMustHave;

			afMustHave = (afSearchAttr & MUST_HAVE_MASK) >> MUST_HAVE_SHIFT;

			/* Check that the 'must have' bits are all set in this file */
			if (afMustHave == (afMustHave & pdeDirEnt->afFileAttr))
			{
				/* Check the filenames for a match */
				if (!FSH_WILDMATCH (pSearchName, psbStringBuf))
				{
					/* Check that this is the most recent version */
					fMatchFound = ( pdeDirEnt->sFileVer ==
									pdeDirEnt->sRecentVer);

				} /* end if names match */

				/* Else test using the full file name */
				else if (pdeDirEnt->sFileVer != NO_VERSION)
				{
					/* Recover the ';' */
					pdeDirEnt->szFileID[pdeDirEnt->cbFileIDLen] =
									*(psbStringBuf + pdeDirEnt->cbFileIDLen) =
									';';

					/* Adjust the length */
					pdeDirEnt->cbFileIDLen += (1 + pdeDirEnt->cbVerLen);

					/* Test the match again */
					fMatchFound = !FSH_WILDMATCH (pSearchName, psbStringBuf);

				}


			}
		} /* end if attributes match */

	}

	return fMatchFound;

} /* end FileMatch */


/***    DirAccDE () -   Create a directory entry for a direct access
 *                      open.
 *
 *  DESCRIPTION
 *
 *      This function takes the description of an entire volume and
 *      fills in a directory entry structure to have it reference the
 *      entire volume.  It will indicate a single entry for the entire
 *      volume.  The file will be non-interleave and the creation date
 *      will be the volume creation date.
 *
 *
 *  ENTRY
 *
 *      pdeDirEntry = Directory entry structure to fill in.
 *      pvdVolDesc    = Pointer to volume descriptor structure.  The
 *                    information for the volume is stored here.
 *
 *
 *  LOCAL
 *
 *      abDateTime  = Build up the packed bits for creation date or time.
 *      usTemp      = Temp storage when converting ASCII strings of
 *                    date/time elements to numerical values.
 *
 *  EXIT
 *
 *      No return value.
 */

VOID DirAccDE (pdeDirEntry, pvdVolDesc)
PDIR_ENTRY      pdeDirEntry;
PVOL_DESC       pvdVolDesc;
{
	USHORT  abDateTime;
	USHORT  usTemp;

	pdeDirEntry->cbStartOffset = 0;
	pdeDirEntry->snSectNum = 0;
	pdeDirEntry->soSectOff = 0;
	pdeDirEntry->rsnRecSeq = 0;
	pdeDirEntry->clbXARLen = 0;
	pdeDirEntry->lbFileLoc = 0;
	pdeDirEntry->cbDataLen = pvdVolDesc->clbVolSpace *
							 pvdVolDesc->cbLogBlk;
	pdeDirEntry->afFileAttr = DOS_ATTR_READ_ONLY;
	pdeDirEntry->fMultiExt = FALSE;
	pdeDirEntry->cbFileSize = pdeDirEntry->cbDataLen;
	pdeDirEntry->clbIntLeaveSize = 0;
	pdeDirEntry->clbIntLeaveSkip = 0;
	pdeDirEntry->vsnVSSN = pvdVolDesc->vsnSNum;
	pdeDirEntry->cbFileIDLen = 0;
	pdeDirEntry->szFileID[0] = '\0';
	pdeDirEntry->sFileVer = NO_VERSION;
	pdeDirEntry->sRecentVer = NO_VERSION;

	/* Covert the volume creation date array to two ushorts */
	abDateTime = 0;

	/* Get the year */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_YEAR_OFF]),
							  CR_YEAR_LEN);

	usTemp = (usTemp > 1980 ? usTemp - 1980 : 0);
	SET_YEAR (abDateTime, usTemp);

	/* Get month */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_MONTH_OFF]),
							  CR_MONTH_LEN);
	SET_MONTH (abDateTime, usTemp);

	/* Get day */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_DAY_OFF]),
							  CR_DAY_LEN);
	SET_DAY (abDateTime, usTemp);

	pdeDirEntry->abDirDate = abDateTime;

	/* Convert the time */
	abDateTime = 0;

	/* Get the hour */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_HOUR_OFF]),
							  CR_HOUR_LEN);
	SET_HOUR (abDateTime, usTemp);

	/* Get minute */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_MINUTE_OFF]),
							  CR_MINUTE_LEN);
	SET_MINUTE (abDateTime, usTemp);

	/* Get second */
	usTemp = VersionToNumber (&(pvdVolDesc->acVolCrDate[CR_SECOND_OFF]),
							  CR_SECOND_LEN);
	SET_SECOND (abDateTime, usTemp);

	pdeDirEntry->abDirTime = abDateTime;

} /* end DirAccDE */


/***    CopyDEtoSFI ()  -   Copy the data from a directory entry to a file
 *                              independent structure maintained by the kernel.
 *
 *  DESCRIPTION
 *
 *      This function is used when opening a file.  It takes a directory
 *      entry which contains the data about a file to open and transfers
 *      it to a sffsi structure.
 *
 *
 *  ENTRY
 *
 *      psffsi      = Pointer to file independent structure.
 *      pdeDirEntry = Pointer to directory entry for opened file.  It
 *                    may be a pseudo entry if this is a direct access
 *                    open.  (Transparent to us)
 *
 *
 *  LOCAL
 *
 *      None
 *
 *
 *  EXIT
 *
 *      No return value
 */

VOID CopyDEtoSFI (psffsi, pdeDirEntry)
PSFFSI          psffsi;
PDIR_ENTRY      pdeDirEntry;
{
	psffsi->sfi_ctime = pdeDirEntry->abDirTime;
	psffsi->sfi_cdate = pdeDirEntry->abDirDate;
	psffsi->sfi_atime = 0;
	psffsi->sfi_adate = 0;
	psffsi->sfi_mtime = pdeDirEntry->abDirTime;
	psffsi->sfi_mdate = pdeDirEntry->abDirDate;
	psffsi->sfi_size = pdeDirEntry->cbFileSize;
	psffsi->sfi_position = 0;

} /* end CopyDEtoSFI */


/***    CopyDEtoFileStatus ()   -  Copy the information from a DE block to
 *                                 a filestatus block.
 *
 *  DESCRIPTION
 *
 *      This function copies the basic information out of a DE block
 *      into a FILESTATUS structure.  This structure is common to
 *      several requests through the FSD.  We copy the file creation
 *      and last write date/time from the creation date in the DE.  The
 *      last access date/time is left at 0.
 *
 *      The 'NEWNAME' bit in the file attributes is cleared before
 *      returning the attributes to the user.  This is used internally
 *      only.  The 'NEWNAME' bit in the search attribute is used to
 *      determine whether or not to uppercase the filenmae string (for 8.3
 *      compatibility).
 *
 *      The file allocation size is based on the logical block size.  We
 *      return the number of bytes in an integral number of logical blocks.
 *
 *
 *  ENTRY
 *
 *      pfsDst      -   Pointer to the destination FILESTATUS struct.
 *      pdeSrc      -   Pointer to the DE block for the desired file.
 *      cbSizeLB    -   Logical block size for the volume.
 *
 *
 *  LOCAL
 *
 *      None
 *
 *
 *  EXIT
 *
 *      No return value
 */

VOID CopyDEtoFileStatus (pfsDst, pdeSrc, cbSizeLB)
FILESTATUS  FAR *pfsDst;
DIR_ENTRY   FAR *pdeSrc;
USHORT          cbSizeLB;
{
	/*
	 * Copy over the date and time information.
	 * The recording date/time is used for the Last Write date/time,
	 * since this is the only date/time we have, and Last Write
	 * is the only one OS/2 displays with the file.
	 */

	*((USHORT FAR *) &(pfsDst->fdateCreation))   = pdeSrc->abDirDate;
	*((USHORT FAR *) &(pfsDst->ftimeCreation))   = pdeSrc->abDirTime;
	*((USHORT FAR *) &(pfsDst->fdateLastAccess)) = 0;
	*((USHORT FAR *) &(pfsDst->ftimeLastAccess)) = 0;
	*((USHORT FAR *) &(pfsDst->fdateLastWrite))  = pdeSrc->abDirDate;
	*((USHORT FAR *) &(pfsDst->ftimeLastWrite))  = pdeSrc->abDirTime;

	/*  If the file is a directory we return 0 as the file size and the
	 *  allocation size.  Copy the attributes at the same time.
	 */
	/* Clear the newname bit, internal only */
	

	if ((pfsDst->attrFile = pdeSrc->afFileAttr & ~DOS_ATTR_NEWNAME) &
		DOS_ATTR_DIRECTORY)
	{
		pfsDst->cbFileAlloc = pfsDst->cbFile = 0;
	}

	
	/* Actual file size in bytes */
	/* Allocation size of the file on the disk */
	else
	{
		pfsDst->cbFileAlloc = ((pfsDst->cbFile = pdeSrc->cbFileSize) +
								cbSizeLB - 1) / cbSizeLB * cbSizeLB;
	}


} /* end CopyDEtoFileStatus */


/***    FNextInfoFit () -   Check if FINDNEXT file fits in buffer, put
 *                              into the buffer if so.
 *
 *  DESCRIPTION
 *
 *      This function is used by FINDNEXT to transfer the information
 *      from a directory entry to the user's buffer to provide information
 *      about a file match.  We first compute the size of the structure
 *      needed, and if there is room in the buffer we add the information
 *      and return to the caller the number of bytes transferred.
 *
 *      We check the value of usFlags, only FF_NOPOS and FF_GETPOS are
 *      supported.
 *
 *
 *  ENTRY
 *
 *      pbDataStart = Pointer to data area for EA
 *      pbData      = Pointer to data area to store file data
 *      cbData      = Stated size of buffer
 *      usLevel     = Indication of type of data to return, refers to
 *                        structures in data area
 *      usFlags     = Flag indicating whether to return file position
 *                        information.
 *      pdeDirEntry =   Pointer to directory entry for matched file.  It
 *                        will not be a direct access entry.
 *      cbSizeLB    = The logical block size.
 *      pcbDataSize = Pointer to the location to return the size of this
 *                        block.
 *      usAttr      = Attributes of search.
 *
 *
 *  LOCAL
 *
 *      usREt       = Return value.
 *      pFEAList    = Pointer into user's buffer.
 *      pGEAList    = Pointer into user's buffer.
 *
 *
 *  EXIT
 *
 *      NO_ERROR    if data fits into buffer.
 *      ERROR_BUFFER_OVERFLOW otherwise.
 *      ERROR_NOT_SUPPORTED for level > 4.
 *      ERROR_INVALID_PARAMETER for invalid usFlags
 */

USHORT FNextInfoFit (pbDataStart, pbData, cbData, usLevel,
						 usFlags, pdeDirEntry,
						 cbSizeLB, pcbDataSize, usAttr)

PCHAR               pbDataStart;
PCHAR               pbData;
USHORT              cbData;
USHORT              usLevel;
USHORT              usFlags;
PDIR_ENTRY          pdeDirEntry;
USHORT              cbSizeLB;
PUSHORT             pcbDataSize;
USHORT              usAttr;
{
	USHORT      usRet;

	/*
	 *  Do a preliminary check of usFlags, return ERROR_INVALID_PARAMETER
	 *  if not a valid value.
	 */
	if (!((usFlags == FF_NOPOS) || (usFlags == FF_GETPOS)))
	{
		return ERROR_INVALID_PARAMETER;

	} /* end if invalid 'usFlags' */

	/*
	 *  Compute the size of the information to store.  Start by looking
	 *  whether the position information is saved (usFlags).  Then add
	 *  the size of the level independent information.  If the level is
	 *  2, then add the size of cbList.
	 *
	 *  At this point, we only support up to level 4.
	 */

	switch (usLevel)
	{
		/**********************************************************************
		*                                                                     *
		*  Levels 1 and 2 - Standard FILEFINDBUF and FILEFINDBUF2             *
		*                                                                     *
		*  Compute the size of the information to store.  Start by looking    *
		*  whether the position information is saved (usFlags).  Then add     *
		*  the size of the level independent information.  If the level is    *
		*  2, then add the size of cbList.                                    *
		*                                                                     *
		**********************************************************************/

		case 1:
		case 2:

			/* compute the size of the data we're about to plug in */

			*pcbDataSize = (((usFlags == FF_GETPOS) ? sizeof (ULONG) : 0) +
							sizeof (FILESTATUS) +
							((usLevel == 2) ? sizeof (ULONG) : 0) +
							sizeof (UCHAR) +
							pdeDirEntry->cbFileIDLen + 1);

			/* Check if fits in buffer */
			if (cbData < *pcbDataSize)
			{
				usRet = ERROR_BUFFER_OVERFLOW;
				break;
			}

			if (!(usRet = FSH_PROBEBUF (PB_OPWRITE, pbData, cbData)))
			{
				/*
				 *  We fill in the users area.  We use pbData as a pointer to
				 *  the various fields and either fill them in directly or
				 *  call some function to do it.
				 */

				/* Fill in the position field */

				*((ULONG FAR *) pbData) = 0L;
				pbData += ((usFlags == FF_GETPOS) ? sizeof (ULONG) : 0);

				/* Call function to fill in the common area */
				CopyDEtoFileStatus ((FILESTATUS FAR *) pbData,
									 pdeDirEntry, cbSizeLB);

				/*
				 *  If at level 2, put a zero in the cbList field and
				 *  complete the filename information.
				 */
				if (usLevel == 2)
				{
					((FILEFINDBUF2 FAR *) pbData)->cbList = sizeof (ULONG);

					/* Copy the ID len */
					((FILEFINDBUF2 FAR *) pbData)->cchName =
						(UCHAR) pdeDirEntry->cbFileIDLen;

					/* Copy the ID name */
					CopyWithNull (
						((FILEFINDBUF2 FAR *) pbData)->achName,
						pdeDirEntry->szFileID,
						pdeDirEntry->cbFileIDLen);

					/* Uppercase the result if 8.3 request */
					if (usAttr & DOS_ATTR_NEWNAME)
					{
						FSH_UPPERCASE (
							((FILEFINDBUF2 FAR *) pbData)->achName,
							pdeDirEntry->cbFileIDLen,
							((FILEFINDBUF2 FAR *) pbData)->achName);
					}

				} /* end if at level 2 */

				/* Else finish with the level 1 info */
				else
				{
					/* Copy the ID len */
					((FILEFINDBUF FAR *) pbData)->cchName =
						(UCHAR) pdeDirEntry->cbFileIDLen;

					/* Copy the ID name */
					CopyWithNull (
						((FILEFINDBUF FAR *) pbData)->achName,
						pdeDirEntry->szFileID,
						pdeDirEntry->cbFileIDLen);

					/* Uppercase the result if 8.3 request */
					if (usAttr & DOS_ATTR_NEWNAME)
					{
						FSH_UPPERCASE (
							((FILEFINDBUF FAR *) pbData)->achName,
							pdeDirEntry->cbFileIDLen,
							((FILEFINDBUF FAR *) pbData)->achName);
					}

				}

			} /* end if fits */

			/* Else show that it doesn't fit */
			else
			{
				*pcbDataSize = 0;

			} /* Else doesn't fit */
			break;  /* out of switch statement */


		/**********************************************************************
		*                                                                     *
		* Level 3 - Subset of the EA Information                              *
		*                                                                     *
		**********************************************************************/

		case 3:
			/*
			* Compute the size of the data we're going to return.
			*
			* This will have to be enhanced for real EAs.
			*/

			*pcbDataSize = (((usFlags == FF_GETPOS) ? sizeof (ULONG) : 0) + /* FFrom position */
								((pbData == pbDataStart) ? sizeof(EAOP) : 0) + /* EAOP */
								sizeof (FILESTATUS) +          /* Level 0x0001 attrs */
								sizeof (ULONG) +               /* Cblist */
								sizeof (UCHAR) +               /* cbName */
								((pdeDirEntry->cbFileIDLen) + 1));/* szName */
			
			/* Check if fits in buffer */
			if (cbData < *pcbDataSize)
			{
				usRet = ERROR_BUFFER_OVERFLOW;
				break;
			}

			if (!(usRet = FSH_PROBEBUF (PB_OPWRITE, pbData,
										*pcbDataSize)))
			{
				/*
				* If this is the first call for series of matches,
				* we need to move the data pointer past the EAOP
				* structure.
				*/

				if (pbData == pbDataStart)
					pbData += sizeof(EAOP);

                /*
                 *  If position bit is set increment the data pointer past
                 *  the size of the position field.
                 */

				*((ULONG FAR *) pbData) = 0L;
				pbData += ((usFlags == FF_GETPOS) ? sizeof (ULONG) : 0);

				/*
				*  We fill in the users area.  We use pbData as a pointer to
				*  the various fields and either fill them in directly or
				*  call some function to do it.
				*/

				/* Call function to fill in the common area */

				CopyDEtoFileStatus ((FILESTATUS FAR *) pbData,
									pdeDirEntry, cbSizeLB);

				/* Step over the file independent area */

				pbData += sizeof (FILESTATUS);

				/*
				* Insert a null FEAList, and step over it.
				*
				* For fully working EAs, this will definitely have
				* to be changed.
				*/

				*((ULONG FAR *) pbData) = 4L;
				pbData += sizeof (ULONG);

				/*
				* This is where the FEA List (array of FEAs) will
				* get plugged in.
				*/

/*				*((ULONG FAR *) pbData) = 0L;
				pbData += sizeof (FEA);
*/

				/* Copy the ID len */

				*(pbData++) = (UCHAR) pdeDirEntry->cbFileIDLen;

				/* Copy the ID name */

				CopyWithNull (
					   pbData,
					   pdeDirEntry->szFileID,
					   pdeDirEntry->cbFileIDLen);

				   /* Check if an 8.3 request */
				   /* Uppercase the result if 8.3 request */
				   if (usAttr & DOS_ATTR_NEWNAME)
				   {
					   FSH_UPPERCASE (
						   pbData,
						   pdeDirEntry->cbFileIDLen,
						   pbData);
				   }


			}
			else  /* Else show that it doesn't fit */
			{
				*pcbDataSize = 0;
			}

			break;


		/**********************************************************************
		*                                                                     *
		* Level 4 - All of the EA Information                                 *
		*                                                                     *
		**********************************************************************/

		case 4:
#ifdef DEBUG
			dprintf("\tFNextInfoFit Level 4, pbData=%lx\n\n", pbData);
#endif
			usRet = NO_ERROR;
			break;

		/*
		 * All other levels are caught here.
		 */

		default:
			*pcbDataSize = 0;
			return (ERROR_NOT_SUPPORTED);

	} /* end switch */

	return usRet;

	/* no errors */
	cbSizeLB;

} /* end FNextInfoFit */



/***    FPInfoFit() - Copy info from DIR_ENTRY to caller's FILESTATUS
 *
 *  DESCRIPTION
 *
 *
 *  ENTRY
 *
 *      pInfo       = address of FILESTATUS structure to receive info.
 *      cbData      = stated size of pInfo buffer.
 *      usLevel     = level of information wanted.
 *      pdeDirEntry = address of directory entry with information in it.
 *      pvdVolDesc  = address of this volume's Volume Descriptor.
 *
 *
 *  LOCAL
 *
 *      usRet   = Return value.
 *      fpGList = Pointer to GEAList.
 *      pucGEA  = Pointer into GEA list.
 *      fpFList = Pointer to FEAList.
 *      pucFEA  = Pointer into FEA list.
 *
 *
 *  EXIT
 *
 *      ERROR_BUFFER_OVERFLOW if user's area is too small (Level 1,2 only
 *          at this time.
 *      ERROR_NOT_SUPPORTED for Level 4.
 *      ERROR_INVALID_LEVEL for Level > 4.
 *      NO_ERROR for successful transfer.
 */

USHORT FPInfoFit(pInfo, cbData, usLevel, pdeDirEntry, pvdVolDesc)
PCHAR               pInfo;
USHORT              cbData;
USHORT              usLevel;
PDIR_ENTRY          pdeDirEntry;
PVOL_DESC           pvdVolDesc;
{
	USHORT      usRet = NO_ERROR;
	PGEALIST    fpGList;
	PUCHAR      pucGEA;
	PFEALIST    fpFList;
	PUCHAR      pucFEA;

	/*
	 * Based on the requested level,
	 * get the information and put it in the caller's data area.
	 * (make sure it will fit first).
	 */

	switch (usLevel)
	{
		/**********************************************************************
		*                                                                     *
		* Level 2 - Standard FILESTATUS stuff PLUS size of the                *
		*           EA list.                                                  *
		*                                                                     *
		**********************************************************************/

		case 0x0002:

			/* make sure the caller gave us enough room */
			if (cbData < sizeof (FILESTATUS2))
			{
				usRet = ERROR_BUFFER_OVERFLOW;
				break;
			}

			if (usRet = FSH_PROBEBUF (PB_OPWRITE, pInfo, cbData))
			{
				break;
			}

			((FILESTATUS2 FAR *) pInfo)->cbList = sizeof (ULONG);
			cbData -= sizeof (ULONG);

			/*
			 * Copy over the date and time information.
			 * The recording date/time is used for the Last Write date/time,
			 * since this is the only date/time we have, and Last Write
			 * is the only one OS/2 displays with the file.
			 */

			CopyDEtoFileStatus ((FILESTATUS FAR *) pInfo, pdeDirEntry,
								pvdVolDesc->cbLogBlk);

			break;

		/**********************************************************************
		*                                                                     *
		* Level 1 - Standard FILESTATUS stuff.                                *
		*           (dates, file size, file allocation, attributes)           *
		*                                                                     *
		**********************************************************************/

		case 0x0001:

			/* make sure the caller gave us enough room */
			if (cbData < sizeof (FILESTATUS))
			{
				usRet = ERROR_BUFFER_OVERFLOW;
				break;

			}

			if (usRet = FSH_PROBEBUF (PB_OPWRITE, pInfo, cbData))
			{
				break;
			}

			/*
			 * Copy over the date and time information.
			 * The recording date/time is used for the Last Write date/time,
			 * since this is the only date/time we have, and Last Write
			 * is the only one OS/2 displays with the file.
			 */

			CopyDEtoFileStatus ((FILESTATUS FAR *) pInfo, pdeDirEntry,
								pvdVolDesc->cbLogBlk);

			break;


		/**********************************************************************
		*                                                                     *
		* Level 3 - Subset of the EA Information                              *
		* Level 4 - All of the EA Information                                 *
		*                                                                     *
		**********************************************************************/

		case 0x0003:
		case 0x0004:

#ifdef DEBUG
			dprintf("\tFPInfoFit: Level %d: pInfo=%lx\n",
				usLevel, pInfo);
#endif

			/*
			 * Validate that the EAOP structure is readable.
			 * If so, get pointers to the GEAList and FEAList.
			 */
			if (cbData < sizeof (EAOP))
			{
				usRet = ERROR_BUFFER_OVERFLOW;
				break;

			}

			if (!(usRet = FSH_PROBEBUF (PB_OPREAD, pInfo, cbData)))
			{
				fpGList = ((PEAOP)pInfo)->fpGEAList;
				fpFList = ((PEAOP)pInfo)->fpFEAList;
			}

#ifdef DEBUG
			dprintf("\tfpGEAList = %lx, fpFEAList = %lx\n", fpGList, fpFList);
#endif

			/*
			 * If this is level 3, verify that the GEAList is accessible.
			 * This is done in two steps:
			 *  First, verify that the cbList can be accessed.
			 *  Then, verify that cbList bytes in the list can be accessed.
			 */

			if (!usRet && usLevel == 3)
			{
				/* verify cbList */

				if (!(usRet = FSH_PROBEBUF(PB_OPREAD,
								(PCHAR)fpGList,
								sizeof(fpGList->cbList))))
				{
					/* verify cbList bytes of GEA list */
					usRet = FSH_PROBEBUF(PB_OPREAD, (PCHAR)fpGList,
										(USHORT)(fpGList->cbList));
				}
			}
			/*
			 * Now go and get the requested information.
			 */

			/*
			 * For now, EAs are not supported by the CD-ROM FSD.
			 * Return a null FEAList.
			 */

			if (!usRet)
			{
				if (usLevel == 3)
					pucGEA = (PUCHAR)&(fpGList->list[0]);

				/*
				 *  For both levels 3 and 4, verify that the FEAList is
				 *  accessible.
				 *
				 *  This is done in two steps:
				 *
				 *  First, verify that the cbList can be accessed.
				 *  Then, verify that cbList bytes in the list can be accessed.
				 */

				if (!usRet)
				{
					/* verify cbList */

					if (!(usRet = FSH_PROBEBUF(PB_OPWRITE, (PCHAR)fpFList,
												sizeof(fpFList->cbList))))
					{
						/* verify cbList bytes of FEA list */
						if (!(usRet = FSH_PROBEBUF(
											PB_OPWRITE,
											(PCHAR)fpFList,
											(USHORT)(fpFList->cbList))))
						{
							pucFEA = (PUCHAR)&(fpFList->list[0]);

							/*
							 * fpGList points to the GEAList.
							 * fpFList points to the FEAList.
							 * pucGEA  points to the first GEA.
							 * pucFEA  points to where the first FEA will go.
							 */

							fpFList->cbList = (ULONG)sizeof(fpFList->cbList);

						}
					}
				}

			}

			break;


		/**********************************************************************
		*                                                                     *
		* Level 5 - Fully-qualified ASCIIZ name.
		*                                                                     *
		**********************************************************************/

		case 0x0005:
			usRet = ERROR_NOT_SUPPORTED;
			break;


		/**********************************************************************
		*                                                                     *
		* All other levels are invalid                                        *
		*                                                                     *
		**********************************************************************/

		default:
			usRet = ERROR_INVALID_LEVEL;
			break;
	}

	return (usRet);

} /* end FPInfoFit */





