/*=============================================================================

FILE: OEMFS.c

SERVICES: OEM Functions for Embedded File System

GENERAL DESCRIPTION:
A sample implementation of the OEMFS functions declared in
OEMFS.h that are used in the implementation of AEEFile.
  
PUBLIC CLASSES AND STATIC FUNCTIONS:

   OEMFS_Open
   OEMFS_Close
   OEMFS_Read
   OEMFS_Write
   OEMFS_GetAttributes
   OEMFS_GetOpenFileAttributes
   OEMFS_Seek
   OEMFS_Tell
   OEMFS_Truncate
   OEMFS_Remove
   OEMFS_Rename
   OEMFS_Mkdir
   OEMFS_Rmdir
   OEMFS_Test
   OEMFS_EnumStart
   OEMFS_EnumNext
   OEMFS_EnumEnd
   OEMFS_SpaceAvail
   OEMFS_SpaceUsed

INITIALIZATION AND SEQUENCING REQUIREMENTS:
No additional module-level initialization or sequencing requirements.

        Copyright © 1999-2002 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=============================================================================*/

#include <stdlib.h>
#include "fs.h"
#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
#   ifdef T_MSM6100
#      ifdef FEATURE_EFS2
#         include "fs_diag.h"
#      else
#         include "fsdiag.h"
#      endif
#   else
#      include "diagfs.h"
#   endif
#endif // FEATURE_DIAG_FS_ACCESS_VALIDATION
#include "AEE_OEMComdef.h"
#include "AEE_OEMDispatch.h"
#include "OEMFS.h"
#include "OEMFSPath_priv.h"
#include "AEEComdef.h"
#include "AEE.h"
#include "AEEStdLib.h"
#include "AEEBufBound.h"

// The following value setting is based on two factors
// 1. Usage of QCT EFS v1.0
// 2. Bytes being added to directory name field in the DMSS fs_ops.c file
//    inside the routine fsi_ops_convert_dirname().  Locate the number
//    of bytes that are being added to the passed in directory name length
//    in the routine fsi_ops_convert_dirname() before it performs a check
//    to see if the directory name is > FS_FILENAME_MAX_LENGTH.  Subtract
//    the same number of bytes here to come up with the value of OEMFS_MAX_PATH
// The line of code below is written assuming that 3 bytes are being added
// to the directory name in fsi_ops_convert_dirname().

#define OEMFS_MAX_PATH (FS_FILENAME_MAX_LENGTH - 3)

//Percentage of the total EFS Space that we will reserve in case the OEMs have not already set up
//a reserve space in DMSS FileSystem
#define OEMFS_PERCENT_SCRATCH_SPACE    (10)

#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
#define OEM_NUM_WATCH_DIRS 2
#endif

#define FARF_TRACE  0

#ifdef FARF
#undef FARF
#endif /* #ifdef FARF */

#if defined(AEE_SIMULATOR)
#define FARF_ALWAYS  1
#else
#define FARF_ALWAYS  0
#endif

#define FARF(x, p)  if (1 == FARF_##x) DBGPRINTF p 

#define ASSERT_FARF(x) if (!(x)) { FARF(ALWAYS,("assertion %s failed, "__FILE__":%d", #x, __LINE__)); }


/*********************************************************************
**  PRIVATE DATA STRUCTURES
*********************************************************************/

typedef struct
{
   char                    szFile[FS_FILENAME_MAX_LENGTH];
   int                     nErr;
   int                     bDirs:1;
   fs_enum_iterator_type   iterator;
   fs_enum_data_type       data;
   char                    szDir[1];
} OEMFSEnumCx;


/**********************************************************************
**  LOCAL FUNCTION DECLARATIONS
**********************************************************************/
static int32   OEMFS_GetOpenFileSize(OEMINSTANCE pFileHandle, boolean bRestore, int *pnErr);

static char    OEMFS_MapAttrib(fs_attribute_mask_type fattrib);
static int     OEMFS_FillInfo(OEMINSTANCE pf, const char *cpszName,
                              fs_rsp_msg_type * pr, 
                              char    *pattrib,
                              uint32  *pdwCreationDate,
                              uint32  *pdwSize);
static int     OEMFS_MapError( fs_status_type fs_err );
#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
static boolean OEMFS_ChkRmtAccess( char *szName, uint32 mode);
#endif

/***********************************************************************
**  FILE-SCOPED VARIABLES
***********************************************************************/

// file access restriction callback
static PFNCHKRMTACCESS  gpfnChkRmtAccess = 0;

static const char *OEMFS_NativePath(const char *cpszIn)
{
   static  char apszNativePathBuffers[8][OEMFS_MAX_PATH];
   static  int  nPathBufIdx = 0;
   char   *pszPath = apszNativePathBuffers[nPathBufIdx];
   int     nLen = ARRAY_SIZE(apszNativePathBuffers[nPathBufIdx]);
   
   if (SUCCESS != OEMFS_GetNativePath(cpszIn,pszPath,&nLen)) {
      FARF(TRACE,("OEMFS_NativePath(\"%s\")=>0",cpszIn));
      return 0;
   }

   nPathBufIdx++;
   if (nPathBufIdx >= ARRAY_SIZE(apszNativePathBuffers)) {
      nPathBufIdx = 0;
   }

   FARF(TRACE,("OEMFS_NativePath(\"%s\")=>\"%s\"",cpszIn,pszPath));

   return pszPath;
}

/*==================================================================
Function: OEMFS_Open

Description: Opens a file in the EFS.
==================================================================*/
typedef struct
{
   AEEOpenFileMode         nMode;
   fs_access_option_type   fmode;
} ModeMap;

static fs_handle_type OEMFS_MapHandle(OEMINSTANCE pFileHandle, fs_access_option_type *pao)
{
   /* we hide open flags in the upper word of the dword we return to BREW */
   if ((fs_access_option_type *)0 != pao) {
      *pao = (fs_access_option_type)(((uint32)pFileHandle)>>16);
   }
   return (fs_handle_type)(((uint32)pFileHandle)&0x0ffff);
}

int OEMFS_Open(const char *cpszName, AEEOpenFileMode nMode, OEMINSTANCE *ppFileHandle)
{
   fs_access_option_type         fmode;      
   fs_open_xparms_type           foptions;   
   fs_rsp_msg_type               resp;

   static const ModeMap amm[] =
      {
         {_OFM_READ,        FS_OA_READONLY},
         {_OFM_READWRITE,   FS_OA_READWRITE},
         {_OFM_CREATE,      FS_OA_CREATE},
         {_OFM_APPEND,      FS_OA_READWRITE}, /* fake append to read/write */
         { 0,               0}
      };
   const ModeMap *pmm;
   
   FARF(TRACE,("OEMFS_Open(\"%s\",%d)", cpszName, nMode));

   // Note - We set the options to 0 here...

   MEMSET(&foptions,0,sizeof(foptions));

   /* Map the parameters */

   fmode = FS_OA_READONLY;
   foptions.create.buffering_option = FS_OB_ALLOW;

   for (pmm = amm; 0 != pmm->nMode; pmm++) {
      if (pmm->nMode == nMode) {
         fmode = pmm->fmode;
         break;
      }
   }

   if (_OFM_CREATE == nMode) {
      foptions.create.attribute_mask = FS_FA_UNRESTRICTED;
   } else {
      foptions.create.attribute_mask = (fs_attribute_mask_type)0;
   }

   // Force the cleanup option to most simple...
   foptions.create.cleanup_option = FS_OC_CLOSE;

   // map the filename and call the native open...
   cpszName = OEMFS_NativePath(cpszName);

   if ((char *)0 != cpszName) {
      fs_open(cpszName, fmode, &foptions, NULL, &resp);
   } else {
      resp.open.status = FS_BAD_FILE_NAME_S;
   }

   if (FS_OKAY_S == resp.open.status) {
      uint32 dwFileHandle;

      dwFileHandle = resp.open.handle;

      /* hide append in the upper word of the file handle */
      if (_OFM_APPEND == nMode) {
         dwFileHandle |= (FS_OA_APPEND<<16);
      }

      *ppFileHandle = (OEMINSTANCE)dwFileHandle;
   }

   FARF(TRACE,("OEMFS_Open() fd: %d=>%d", *ppFileHandle,
               OEMFS_MapError(resp.open.status)));

   // Translate results...
   return OEMFS_MapError(resp.open.status);
}


/*==================================================================
Function: OEMFS_Close

Description: Closes a previously opened file in the EFS.
==================================================================*/
int OEMFS_Close(OEMINSTANCE pFileHandle)
{
   fs_handle_type  hf = OEMFS_MapHandle(pFileHandle,0);
   fs_rsp_msg_type resp;

   fs_close(hf, NULL, &resp);
   
   FARF(TRACE,("OEMFS_Close(%d)=>%d", pFileHandle,
               OEMFS_MapError(resp.close.status)));

   return OEMFS_MapError(resp.close.status);
}

/*==================================================================
Function: OEMFS_Read

Description: Reads bytes from a file in the EFS.
==================================================================*/
int32 OEMFS_Read(OEMINSTANCE pFileHandle,void *buffer,dword nBytes, int *pnErr)
{
   fs_handle_type  hf = OEMFS_MapHandle(pFileHandle,0);
   fs_rsp_msg_type resp;

   FARF(TRACE,("OEMFS_Read(%d,%p,%d)", pFileHandle,buffer,nBytes));
   
   fs_read(hf, buffer, nBytes, NULL, &resp );

   if (FS_OKAY_S == resp.read.status) {
      FARF(TRACE,("OEMFS_Read()=>%d", resp.read.count));
      return resp.read.count;
   }

   *pnErr = OEMFS_MapError(resp.read.status);

   FARF(TRACE,("OEMFS_Read() err: %d=>%d", *pnErr, -1));

   return -1;
}

/*==================================================================
Function: OEMFS_Write

Description: Writes bytes to a file in the EFS.
==================================================================*/
int32 OEMFS_Write(OEMINSTANCE pFileHandle,const void *buffer,uint32 nBytes, int *pnErr)
{
   fs_access_option_type ao;
   fs_handle_type        hf;
   fs_rsp_msg_type       resp;
   uint32                dwAvail;

   FARF(TRACE,("OEMFS_Write(%d,%p,%d)",pFileHandle,buffer,nBytes));

   hf = OEMFS_MapHandle(pFileHandle,&ao);

   if (FS_OA_APPEND == ao) { /* append mode is defined as a seek(end)+write() */
      if (-1 == OEMFS_Seek(pFileHandle,_SEEK_END,0,pnErr)) {
         FARF(TRACE,("OEMFS_Write() err: %d=>%d", *pnErr, -1));
         return -1;
      }
   }

   // Adjust the available size by the size that the OEM wants to reserve.

   dwAvail = OEMFS_SpaceAvail();

   // Looks like they are trying to write too much.  Adjust the available
   // size of the FS by the size of the file less the position.  Then 
   // recheck the condition.
   //

   if (nBytes > dwAvail) {
      int32 nSize,nTell;

      nSize = OEMFS_GetOpenFileSize(pFileHandle,TRUE,pnErr);
      
      if (-1 == nSize) {
         FARF(TRACE,("OEMFS_Write() err: %d=>%d", *pnErr, -1));
         return -1;
      }

      nTell = OEMFS_Tell(pFileHandle,pnErr);

      if (-1 == nTell) {
         FARF(TRACE,("OEMFS_Write() err: %d=>%d", *pnErr, -1));
         return -1;
      }

      if (nSize > nTell) {
         dwAvail += (nSize - nTell);
      }

      if (nBytes > dwAvail) {
         nBytes = dwAvail;
         *pnErr = EFSFULL;
      }
   }

   fs_write(hf, buffer, nBytes, NULL, &resp);

   if (FS_OKAY_S == resp.write.status) {
      // early return to preserve *pnErr if is EFSFULL
      FARF(TRACE,("OEMFS_Write()=>%d",resp.write.count));
      return resp.write.count;
   }

   *pnErr = OEMFS_MapError(resp.read.status);

   FARF(TRACE,("OEMFS_Write() err: %d=>%d", *pnErr, -1));

   return -1;   
}

/*==================================================================
Function: OEMFS_GetAttributes

Description: Gets attributes of a file in the EFS.
==================================================================*/
int OEMFS_GetAttributes(const char *cpszName,
                            char       *pattrib,
                            uint32     *pdwCreationDate,
                            uint32     *pdwSize)
{
   fs_rsp_msg_type resp;
   
   FARF(TRACE,("OEMFS_GetAttributes(\"%s\")",cpszName));

   cpszName = OEMFS_NativePath(cpszName);
   
   if ((char *)0 == cpszName) {
      FARF(TRACE,("OEMFS_GetAttributes()=>%d",EBADFILENAME));
      return EBADFILENAME;
   }
   
   /* check to see if is directory */
   fs_nametest(cpszName, FS_TEST_DIR, NULL, &resp);
   
   if (resp.nametest.status != FS_OKAY_S) {
      FARF(TRACE,("OEMFS_GetAttributes()=>%d",
                  OEMFS_MapError(resp.nametest.status)));
      return OEMFS_MapError(resp.nametest.status);
   }
   
   /* "" is a directory... */   
   if (resp.nametest.name_found || '\0' == *cpszName) {
      if ((char *)0 != pattrib) {
         *pattrib = AEE_FA_DIR;
      }
      if ((uint32 *)0 != pdwCreationDate) {
         *pdwCreationDate = 0;         
      }      
      if ((uint32 *)0 != pdwSize) {
         *pdwSize = 0;
      }
      
      FARF(TRACE,("OEMFS_GetAttributes() attrib: AEE_FA_DIR=>0"));
      
      return SUCCESS;
   }

   /* go ahead and do file stuff */   
   fs_get_file_attributes(cpszName, 0, &resp);
   
   return OEMFS_FillInfo(0, cpszName, &resp, pattrib, pdwCreationDate, pdwSize);
}


/*==================================================================
Function: OEMFS_GetOpenFileAttributes

Description: Gets attributes of an open file in the EFS.
==================================================================*/
int OEMFS_GetOpenFileAttributes(OEMINSTANCE  pFileHandle,
                                char        *pattrib,
                                uint32      *pdwCreationDate,
                                uint32      *pdwSize)
{
   fs_handle_type  hf = OEMFS_MapHandle(pFileHandle,0);
   fs_rsp_msg_type resp;

   FARF(TRACE,("OEMFS_GetOpenFileAttributes(%d)",pFileHandle));

   fs_get_open_file_attributes(hf, NULL, &resp);

   return OEMFS_FillInfo(pFileHandle,0,&resp,pattrib,pdwCreationDate,pdwSize);
}

/*==================================================================
Function: OEMFS_Seek

Description: Moves the seek pointer to the specified location in 
              a file in the EFS.
==================================================================*/
int32 OEMFS_Seek(OEMINSTANCE pFileHandle,AEEFileSeekType sType,int32 nOffset, int *pnErr)
{
   fs_handle_type hf = OEMFS_MapHandle(pFileHandle,0);
   int32          nFileSize;
   int32          nTell;

   FARF(TRACE,("OEMFS_Seek(%d,%d,%d)", pFileHandle, sType, nOffset));
   // save where we're at....
   nTell = OEMFS_Tell(pFileHandle,pnErr);
   
   if (-1 == nTell) {
      FARF(TRACE,("OEMFS_Seek() err: %d=>%d", *pnErr, -1));
      return -1;
   }

   // Get file size and LEAVE SEEK POINTER AT END OF FILE
   nFileSize = OEMFS_GetOpenFileSize(pFileHandle,FALSE,pnErr);
   
   if (-1 == nFileSize) {
      FARF(TRACE,("OEMFS_Seek() err: %d=>%d", *pnErr, -1));
      return -1;
   }

   //
   // IMPORTANT
   //
   // DMSS does not support a negative offset parameter.  This means
   // we must internally convert the offset to the appropriate value
   // from the start of the file.
   //
   switch (sType) {
      case _SEEK_END:
         nOffset += nFileSize;
         break;

      case _SEEK_CURRENT: 
         // 0 seek from current is treated as a "tell"...
         if (!nOffset) {
            FARF(TRACE,("OEMFS_Seek()=>%d", nTell));
            return nTell;
         }

         nOffset += nTell;
         break;

      case _SEEK_START:
         break;

      default:
         *pnErr = EBADPARM;
         return -1;
   } 

   // Any negative seek now is an error ...
   if (nOffset < 0) {
      *pnErr = EBADSEEKPOS;
      FARF(TRACE,("OEMFS_Seek() err: %d=>%d", *pnErr, -1));
      return -1;
   }

   // file pointer is currently at end of file - exit if seeking to 
   //  end of file... avoids possible EFS1 bug - can't seek to end of 
   //  file for read only file
   if (nOffset == nFileSize) {
      FARF(TRACE,("OEMFS_Seek()=>%d", nOffset));
      return nOffset;
   }

   // Guard us from growing into the reserved area, trim seek() 
   if ((nOffset > nFileSize) && ((uint32)(nOffset - nFileSize) > OEMFS_SpaceAvail())) {
      nOffset = nFileSize + OEMFS_SpaceAvail();
      *pnErr = EFSFULL;
   }

   {
      fs_rsp_msg_type resp;

      if (nOffset > nFileSize) {
         fs_fmemset(hf, nOffset, 0, 0, &resp);
      } else {
         fs_seek(hf, FS_SEEK_SET, nOffset, 0, &resp);
      }

      /* memset and seek resp types have status in the same place */

      if (FS_OKAY_S == resp.seek.status) {
         // early return to preserve *pnErr if is EFSFULL
         FARF(TRACE,("OEMFS_Seek()=>%d", nOffset));
         return nOffset;
      }

      *pnErr = OEMFS_MapError(resp.seek.status);
   }
   
   FARF(TRACE,("OEMFS_Seek() err: %d=>%d", *pnErr, -1));

   return -1;
}


/*==================================================================
Function: OEMFS_Tell

Description: Returns the current position of the seek pointer in
             a file in the EFS.
==================================================================*/
int32 OEMFS_Tell(OEMINSTANCE pFileHandle, int *pnErr)
{
   fs_handle_type  hf = OEMFS_MapHandle(pFileHandle,0);
   fs_rsp_msg_type resp;
   
   FARF(TRACE,("OEMFS_Tell(%d)", pFileHandle));

   fs_tell(hf, NULL, &resp);

   if (FS_OKAY_S != resp.tell.status){
      *pnErr = OEMFS_MapError(resp.tell.status);
      FARF(TRACE,("OEMFS_Tell() err: %d=>%d", *pnErr, -1));
      return -1;
   }

   FARF(TRACE,("OEMFS_Tell()=>%d", resp.tell.position));
   return resp.tell.position;
}

/*==================================================================
Function: OEMFS_Truncate

Description: Truncates a file in the EFS to the specified size.
==================================================================*/
int OEMFS_Truncate(OEMINSTANCE pFileHandle,uint32 nPos)
{
   int32           nSize;
   int             nErr;

   FARF(TRACE,("OEMFS_Truncate(%d, %d)", pFileHandle, nPos));

   nSize = OEMFS_GetOpenFileSize(pFileHandle,TRUE,&nErr);

   if (-1 == nSize) {
      FARF(TRACE,("OEMFS_Truncate()=>%d", nErr));
      return nErr;
   }

   if (nPos > (uint32)nSize) {
      FARF(TRACE,("OEMFS_Truncate()=>%d", EBADPARM));
      return EBADPARM;
   }

   {
      fs_handle_type  hf = OEMFS_MapHandle(pFileHandle,0);
      fs_rsp_msg_type resp;
      
      fs_truncate(hf, nPos, NULL, &resp);
      
      nErr = OEMFS_MapError(resp.truncate.status);    
   }

   if (SUCCESS == nErr) {
      OEMFS_Seek(pFileHandle,_SEEK_END,0,&nErr);
   }

   FARF(TRACE,("OEMFS_Truncate()=>%d", nErr));
   return nErr;
}

/*==================================================================
Function: OEMFS_Remove

Description: Removes a file from the EFS.
==================================================================*/
int OEMFS_Remove(const char *cpszFile)
{
   fs_rsp_msg_type resp;

   FARF(TRACE,("OEMFS_Remove(%s, %d)", cpszFile));

   cpszFile = OEMFS_NativePath(cpszFile);

   if ((char *)0 != cpszFile) {
      fs_remove(cpszFile, NULL, &resp);
   } else {
      resp.rmfile.status = FS_BAD_FILE_NAME_S;
   }

   FARF(TRACE,("OEMFS_Remove()=>%d", OEMFS_MapError(resp.rmfile.status)));
   return OEMFS_MapError(resp.rmfile.status); 
}


/*==================================================================
Function: OEMFS_Rename

Description: Renames a file in the EFS to the specified new name.
==================================================================*/
int OEMFS_Rename(const char *cpszOldName,const char *cpszNewName)
{
   FARF(TRACE,("OEMFS_Rename(%s,%s)", cpszOldName, cpszNewName));
   // Guard against EFS attempting to rename a file into the 
   // reserved area.  If this happens, the garbage collector
   // can get quite testy...
   {
      char   cAttrib;
      uint32 dwSize;
      int    nErr;
      
      nErr = OEMFS_GetAttributes(cpszOldName,&cAttrib,0,&dwSize);
      
      if (SUCCESS != nErr) {
         FARF(TRACE,("OEMFS_Rename()=>%d", nErr));
         return nErr;
      }
      
      if (cAttrib & AEE_FA_DIR) {
         FARF(TRACE,("OEMFS_Rename()=>%d", EINVALIDOPERATION));
         return EINVALIDOPERATION;
      }

      if (STRLEN(cpszOldName) != STRLEN(cpszNewName)) {
         if (dwSize > OEMFS_SpaceAvail()) {
            FARF(TRACE,("OEMFS_Rename()=>%d", EFSFULL));
            return EFSFULL;
         }
      }
   }
   
   cpszNewName = OEMFS_NativePath(cpszNewName);
   cpszOldName = OEMFS_NativePath(cpszOldName);

   if ((char *)0 != cpszNewName && (char *)0 != cpszOldName) {
      fs_rsp_msg_type resp;

      fs_rename(cpszOldName,cpszNewName, NULL, &resp);
   
      FARF(TRACE,("OEMFS_Rename()=>%d", OEMFS_MapError(resp.rename.status)));
      return OEMFS_MapError(resp.rename.status);
      
   } else {
      FARF(TRACE,("OEMFS_Rename()=>%d", EBADFILENAME));
      return EBADFILENAME;
   }

}

/*==================================================================
Function: OEMFS_Mkdir

Description: Creates a direcory at the specified location in the EFS.
==================================================================*/
int OEMFS_Mkdir(const char *cpszDir)
{
   fs_rsp_msg_type resp;

   FARF(TRACE,("OEMFS_Mkdir(%s)", cpszDir));

   cpszDir = OEMFS_NativePath(cpszDir);
   
   if ((char *)0 == cpszDir) {
      FARF(TRACE,("OEMFS_Mkdir()=>%d", EBADFILENAME));
      return EBADFILENAME;
   }

   if (OEMFS_IsAutoCreate(cpszDir)) {
      int nDirLen, nLen;
      nDirLen = STRLEN(cpszDir); 

      do {
         nLen = nDirLen;

         // try to make whole thing
         fs_mkdir(cpszDir, NULL, &resp);
         
         // parent doesn't exist, try to make all parents
         while (FS_NONEXISTENT_DIR_S == resp.mkdir.status) {
            
            char *pszSlash = MEMRCHR(cpszDir,'/',nLen); 

            // if no previous slash, return error unmolested
            if ((char *)0 == pszSlash) {
               break;
            }
            
            // trim last slash...
            // this is ok because OEMFS_NativePath() returns a 
            // global I can modify...
            *pszSlash = 0;

            // shorten len for next loop iteration
            nLen = STRLEN(cpszDir);
            
            // make parent
            fs_mkdir(cpszDir, NULL, &resp);
            
            // restore slash
            *pszSlash = '/';
         }
         
         // while we successfully made a parent...
      } while (FS_OKAY_S == resp.mkdir.status && nDirLen != nLen);

   } else {
      fs_mkdir(cpszDir, NULL, &resp);
   }

   FARF(TRACE,("OEMFS_Mkdir()=>%d", OEMFS_MapError(resp.mkdir.status)));

   return OEMFS_MapError(resp.mkdir.status);
}

/*==================================================================
Function: OEMFS_Rmdir

Description: Removes the specified directory from the EFS.
==================================================================*/
int OEMFS_Rmdir(const char *cpszDir)
{
   FARF(TRACE,("OEMFS_Rmdir(%s)", cpszDir));

   {
      int     nErr;
      boolean bDirs;

      for (nErr = SUCCESS, bDirs = FALSE; 
           SUCCESS == nErr && bDirs <= TRUE; bDirs++) {
         OEMINSTANCE handle;
         
         // Need to insure that the directory is empty...
         nErr = OEMFS_EnumStart(cpszDir, bDirs, &handle);

         if (SUCCESS == nErr) {
            // See if there are any sub-dirs or files, don't care what
            //  the name is

            if (SUCCESS == OEMFS_EnumNext(handle,0,0)) {
               nErr = EDIRNOTEMPTY;
            }
            
            OEMFS_EnumStop(handle);
         }
      }

      if (SUCCESS != nErr) {
         FARF(TRACE,("OEMFS_Rmdir()=>%d", nErr));
         return nErr;
      }
   }

   cpszDir = OEMFS_NativePath(cpszDir);

   if ((char *)0 != cpszDir) {

      fs_rsp_msg_type resp;

      fs_rmdir(cpszDir, NULL, &resp);

      FARF(TRACE,("OEMFS_Rmdir()=>%d", OEMFS_MapError(resp.mkdir.status)));

      return OEMFS_MapError(resp.rmdir.status);

   } else {

      FARF(TRACE,("OEMFS_Rmdir()=>%d", EBADFILENAME));

      return EBADFILENAME;
   }
}

/*==================================================================
Function: OEMFS_Test

Description: Tests if the specified name is a file or a directory.
==================================================================*/
int OEMFS_Test(const char * cpszName)
{
   fs_rsp_msg_type  resp;

   FARF(TRACE,("OEMFS_Test(%s)", cpszName));
      
   cpszName = OEMFS_NativePath(cpszName);
   
   if ((char *)0 != cpszName) {
      fs_nametest(cpszName, FS_TEST_FILE_OR_DIR, NULL, &resp);
   } else {
      resp.nametest.status = FS_BAD_FILE_NAME_S;
   }

   if (FS_OKAY_S == resp.nametest.status && /* nametest worked */
       !resp.nametest.name_found &&         /* not found */
       '\0' != *cpszName) {          /* "" is a directory, always exists */
      resp.nametest.status = FS_NONEXISTENT_FILE_S;
   }

   FARF(TRACE,("OEMFS_Test()=>%d", OEMFS_MapError(resp.nametest.status)));

   return OEMFS_MapError(resp.nametest.status);
}

/*==================================================================

 Calls the FS layer to do the next forward enumeration.

==================================================================*/
static int OEMFS_EnumCxNext(OEMFSEnumCx * pe)
{
   fs_rsp_msg_type  resp;
   int              nErr;

   // Perform in a loop in case we encounter "." or ".."
   for (;;) {

      fs_enum_next(&pe->data, &pe->iterator, NULL, &resp);

      nErr = OEMFS_MapError(resp.enum_next.status);       

      // Got an entry - move the fields that will be shown to the user.
      if (SUCCESS != nErr) {
         break;
      }

      {
         char *pszEnd = BASENAME(pe->data.fullname);
         
         if (STRCMP(pszEnd,".") && STRCMP(pszEnd,"..")) {
            break;
         }
      }

   }

   /* save error for later */
   return (pe->nErr = nErr);

}

/*==================================================================
Function: OEFS_EnumStart

Description: File enumeration initializer.
==================================================================*/
int OEMFS_EnumStart(const char *cpszDir, char isDir, OEMINSTANCE *ppcxt)
{
   OEMFSEnumCx *pe;
   int          nErr;

   FARF(TRACE,("OEMFS_EnumStart(\"%s\")", cpszDir));

   //
   // Allocate the context that will carry us through this enumeration.
   //
   pe = (OEMFSEnumCx *)MALLOC(sizeof(OEMFSEnumCx) + STRLEN(cpszDir));
   
   if ((OEMFSEnumCx *)0 == pe) {
      FARF(TRACE,("OEMFS_EnumStart()=>%d",ENOMEMORY));
      return ENOMEMORY;
   }

   // hang onto original name
   STRCPY(pe->szDir, cpszDir);

   // Translate the enumeration type
   pe->bDirs = isDir;

   /* map name, call EFS */
   cpszDir = OEMFS_NativePath(cpszDir);

   if ((char *)0 != cpszDir) {
      fs_rsp_msg_type   resp;
      fs_enum_type      enum_kind;
      
      if (isDir) {
         enum_kind = FS_ENUM_DIRECTORIES;
      } else {
         enum_kind = FS_ENUM_FILES;
      }

      /* 
         EFS1 does not return an error if the directory we're 
         trying to enumerate doesn't exist, also, fs_nametest 
         doesn't work for EFS1 root ("" is root)
      */
      if ('\0' != *cpszDir) {
         fs_nametest(cpszDir, FS_TEST_DIR, NULL, &resp);
      
         if (FS_OKAY_S == resp.nametest.status && 
             !resp.nametest.name_found) {
            resp.nametest.status = FS_NONEXISTENT_DIR_S;
         }
      } else {
         resp.nametest.status = FS_OKAY_S;
      }
      
      if (FS_OKAY_S == resp.nametest.status) {
         fs_enum_init(cpszDir, enum_kind, &pe->iterator, 0, &resp);
      }

      nErr = OEMFS_MapError(resp.enum_init.status);

   } else {
      nErr = EBADFILENAME;
   }
   
   /* Check the return code */
   if (SUCCESS == nErr) {
      
      // Handle the first enumeration.  This is done to prevent us from
      // encountering errors in cases where the user removes the file/dir
      // of the current entry they got from EnumNext
      nErr = OEMFS_EnumCxNext(pe);

      if (EFILEEOF == nErr) {
         nErr = SUCCESS;
      }
   }
   
   if (SUCCESS != nErr) {
      // No files here or enum next error, get out...
      FREE(pe);
      pe = 0;
   }

   *ppcxt = (OEMINSTANCE)pe;

   FARF(TRACE,("OEMFS_EnumStart() pcxt: %d=>%d",*ppcxt,nErr));

   return nErr;
}

/*==================================================================
Function: OEMFS_EnumNext

Description: Enumerates through the files in the location specified
             by OEMFS_EnumInit.
==================================================================*/
int OEMFS_EnumNext(OEMINSTANCE pcxt, OEMFSEnum *pFSEnum, int *pnLen)
{
   OEMFSEnumCx *pe = (OEMFSEnumCx *)pcxt;

   FARF(TRACE,("OEMFS_EnumNext(%d,%p,%d)",
               pcxt,pFSEnum,
               pnLen?*pnLen:696969696));
   
   // at end or error?
   if (SUCCESS != pe->nErr) {
      FARF(TRACE,("OEMFS_EnumNext()=>%d",pe->nErr));
      return pe->nErr;
   }
   
   // want info
   if ((int *)0 != pnLen) {

      int nNameLen;

      // want size, DO NOT ADVANCE ENUMERATOR
      if ((OEMFSEnum *)0 == pFSEnum) {
         MAKEPATH(pe->szDir, BASENAME(pe->data.fullname),
                  0, pnLen);
         *pnLen += (sizeof(OEMFSEnum) - 1);
         FARF(TRACE,("OEMFS_EnumNext() nLen: %d=>%d",*pnLen,SUCCESS));
         return SUCCESS;
      }

      // space for name includes the char szName[1] at end of OEMFSEnum
      nNameLen = *pnLen - sizeof(OEMFSEnum) + 1;

      if (nNameLen < 0) {
         FARF(TRACE,("OEMFS_EnumNext()=>%d",EBUFFERTOOSMALL));
         return EBUFFERTOOSMALL;
      }
      
      // Move the fields from the last successful enumeration to the 
      //  client area. 
      //
      pFSEnum->attrib         = OEMFS_MapAttrib(pe->data.attributes);
      pFSEnum->dwCreationDate = pe->data.creation_date;
      pFSEnum->dwSize         = pe->data.logical_size;
      
      MAKEPATH(pe->szDir, BASENAME(pe->data.fullname),
               pFSEnum->szName, &nNameLen);
      
      FARF(TRACE,("OEMFS_EnumNext() name: %s",
                  pFSEnum->szName));
      FARF(TRACE,("OEMFS_EnumNext() attrib: %d",
                  pFSEnum->attrib));
      FARF(TRACE,("OEMFS_EnumNext() date: %d",
                  pFSEnum->dwCreationDate));
      FARF(TRACE,("OEMFS_EnumNext() size: %d",
                  pFSEnum->dwSize));
   }

   // advance enumerator
   OEMFS_EnumCxNext(pe);

   FARF(TRACE,("OEMFS_EnumNext()=>%d",SUCCESS));

   return SUCCESS;
}

/*==================================================================
Function: OEMFS_EnumStop

Description: Indicates that we can free resources related to enumeration
==================================================================*/
int OEMFS_EnumStop(OEMINSTANCE pcxt)
{
   OEMFSEnumCx *pe = (OEMFSEnumCx *)pcxt;
   fs_rsp_msg_type resp;

   FARF(TRACE,("OEMFS_EnumStop(%d)"));

   fs_enum_finish(&pe->iterator, NULL, &resp);
   
   FREE(pe);
   
   FARF(TRACE,("OEMFS_EnumStop()=>%d",
               OEMFS_MapError(resp.enum_finish.status)));

   return OEMFS_MapError(resp.enum_finish.status);
}


/*==================================================================
Function: OEMFS_SpaceAvail

Description: Returns the space available on the EFS.
==================================================================*/
uint32 OEMFS_SpaceAvail(void)
{
   uint32 dwAvail = fs_space_available();

   // Note - protect the OEM in case they failed to reserve space...
   if (0 == fs_space_reserved()) {
      uint32  dwReserve;
 
      // They did not reserve. We need to set aside about 10% of the total
      dwReserve = ((fs_space_used() + dwAvail) * 
                   OEMFS_PERCENT_SCRATCH_SPACE) / 100;

      if (dwAvail > dwReserve) {
         dwAvail -= dwReserve;
      } else {
         dwAvail = 0;
      }
   }

   FARF(TRACE,("OEMFS_SpaceAvail()=>%d",dwAvail));
   
   return dwAvail;
}

/*==================================================================
Function: OEMFS_SpaceUsed

Description: Returns the space already used on the EFS.
==================================================================*/
uint32 OEMFS_SpaceUsed(void)
{
   FARF(TRACE,("OEMFS_SpaceUsed()=>%d",fs_space_used));
   return fs_space_used();
}


/*==================================================================
Function: OEMFS_RegRmtAccessChk

Description: Registers the remote file access directories to watch.
==================================================================*/
void OEMFS_RegRmtAccessChk(const char **pszDirList, uint32 nListElements, PFNCHKRMTACCESS pfn )
{
#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
   static const char *acpszDirNameList[OEM_NUM_WATCH_DIRS];
#endif
   gpfnChkRmtAccess = pfn;
   
#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION

   // We are unable to validate directories 
   // that will not translate into AEE file paths.
   // So only watch BREW directories.
   // For the DMSS implmentation we only 
   // really need to watch these directories
   // since the directory watch is inclusive.
   acpszDirNameList[0] = OEMFS_BREW_DIR;
   acpszDirNameList[1] = OEMFS_RINGERS_DIR;
   
   // call DMSS register access
   diag_fs_register_access_check(acpszDirNameList, OEM_NUM_WATCH_DIRS, (diag_fs_access_check_fptr_type)OEMFS_ChkRmtAccess);
#endif
}

#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
/*==================================================================
Function: OEMFS_ChkRmtAccess

Description: Called when a file access request is made.  Must call
             the AEE callback passed to OEMFS_RegRmtAccessChk and 
             return wether or not the file access is allowed.
==================================================================*/
static boolean OEMFS_ChkRmtAccess(char *szName, uint32 mode)
{
   OEMFSRmtAccessMode faMode;
   
   if (NULL != gpfnChkRmtAccess) {
   
      switch (mode) {
#ifdef FEATURE_DIAG_FS_ACCESS_VALIDATION
         case DIAG_FS_CHECK_READ:
            faMode = FRAM_READ;
            break;
         case DIAG_FS_CHECK_MKDIR:
         case DIAG_FS_CHECK_SET_ATTRIB:
         case DIAG_FS_CHECK_WRITE:
         case DIAG_FS_CHECK_REMOTE_LINK:
            faMode = FRAM_WRITE;
            break;
         case DIAG_FS_CHECK_GET_ATTRIB:
         case DIAG_FS_CHECK_DISP_DIRS:
         case DIAG_FS_CHECK_DISP_FILES:
         case DIAG_FS_CHECK_ITERATE:
            faMode = FRAM_LIST;
            break;
         case DIAG_FS_CHECK_RMDIR:
         case DIAG_FS_CHECK_DELETE:
            faMode = FRAM_DELETE;
            break;
#ifdef FEATURE_EFS2
         case DIAG_FS_CHECK_OPEN:
         case DIAG_FS_CHECK_CLOSE:
         case DIAG_FS_CHECK_READLINK:
            faMode = FRAM_LIST;
            break;
         case DIAG_FS_CHECK_SYMLINK:
         case DIAG_FS_CHECK_RENAME:
         case DIAG_FS_CHECK_CHMOD:
            faMode = FRAM_WRITE;
            break;
         case DIAG_FS_CHECK_OPENDIR:
         case DIAG_FS_CHECK_READDIR:
         case DIAG_FS_CHECK_CLOSEDIR:
         case DIAG_FS_CHECK_STAT:
         case DIAG_FS_CHECK_LSTAT:
         case DIAG_FS_CHECK_FSTAT:
         case DIAG_FS_CHECK_STATFS:
         case DIAG_FS_CHECK_ACCESS:
            faMode = FRAM_LIST;
            break;
         case DIAG_FS_CHECK_UNLINK:
            faMode = FRAM_DELETE;
            break;         
#endif // FEATURE_EFS2
#endif // FEATURE_DIAG_FS_ACCESS_VALIDATION
         default:
            // unknown mode, deny access
            DBGPRINTF("Unknown mode, Denied");
            return FALSE;
      }

      {
         static char szFSName[OEMFS_MAX_PATH];
         int         nLen = ARRAY_SIZE(szFSName);

         if (SUCCESS != OEMFS_GetBREWPath(szName, szFSName, &nLen)) {
            DBGPRINTF("Unable to translate file name, Denied");
            return FALSE;
         }

         BRIDLE_ASSERT_SAFE_PFN(gpfnChkRmtAccess);
         return gpfnChkRmtAccess(szFSName, faMode);
      }
   }
   // we have not registered any directories we must allow full 
   // access for now.  Directories are registered in AEE_Init and 
   // will happen before the serial port is available for remote 
   // file access requests
   DBGPRINTF("Callback pointer not valid, access allowed until BREW initializes");
   return TRUE;
}
#endif

/*==================================================================


  LOCAL ROUTINES


==================================================================*/

/*===========================================================================
Function: 
   OEMFS_MapAttrib

Description:
   Maps fs_attribute_mask_type file attributes removed from EFS to FileAttrib

Parameter(s):
   fattrib [in]: EFS file attribute

Comment: 

fs_attribute_mask_type is defnied as

typedef enum {  
  FS_FA_UNRESTRICTED =0x00FF, // No file access restrictions             
  FS_FA_READONLY     =0x00BF, // File cannot be opened for update access
  FS_FA_PERMANENT    =0x007F, // File cannot be removed nor truncated     
  FS_FA_SYS_PERMANENT=0x0037, // File persists across system reformats
  FS_FA_REMOTE       =0x001F  // Remote file (resides outside the space
} fs_attribute_mask_type;

Because of this only _FA_NORMAL, _FA_READONLY and _FA_SYSTEM file attributes
are supported rendering _FA_DIR and _FA_HIDDEN values of FileAttrib unused.
===========================================================================*/
static char OEMFS_MapAttrib(fs_attribute_mask_type fattrib)
{
   // Read only files...

   if (fattrib == FS_FA_READONLY) {
      return AEE_FA_READONLY;
   }

   // System files...

   if ((fattrib == FS_FA_PERMANENT)     ||
       (fattrib == FS_FA_SYS_PERMANENT) ||
       (fattrib == FS_FA_REMOTE)) {
      return AEE_FA_SYSTEM;
   }

   return AEE_FA_NORMAL;
}

/*===========================================================================
Function: OEMFS_FillInfo

Description: Takes a response from DMSS and puts it into the AEEFileInfo
structure.
===========================================================================*/
static int OEMFS_FillInfo(OEMINSTANCE pFileHandle, 
                          const char *cpszName,
                          fs_rsp_msg_type * pr, 
                          char   *pattrib,
                          uint32 *pdwCreationDate,
                          uint32 *pdwSize)
{
   char attrib;
   uint32 dwCreationDate, dwSize;

   if ((char *)0 == pattrib) {
      pattrib = &attrib;
   }
   if ((uint32 *)0 == pdwCreationDate) {
      pdwCreationDate = &dwCreationDate;
   }
   if ((uint32 *)0 == pdwSize) {
      pdwSize = &dwSize;
   }

   FARF(TRACE,("OEMFS_FillInfo(\"%s\")",cpszName?cpszName:"NULL"));

   if (FS_OKAY_S == pr->get_attribs.status) {

      *pdwCreationDate = pr->get_attribs.creation_date;

      *pattrib = OEMFS_MapAttrib(pr->get_attribs.attributes);

      if ((OEMINSTANCE)0 != pFileHandle) {
         int   nErr;
         int32 nSize = OEMFS_GetOpenFileSize(pFileHandle,TRUE, &nErr);
         
         FARF(TRACE,("OEMFS_FillInfo() size: %d", nSize));
         
         if (-1 != nSize) {
            *pdwSize = (uint32)nSize;
         } else {
            return nErr;
         }
      } else if ((char *)0 != cpszName) {
         FARF(TRACE,("OEMFS_FillInfo() size: %d", pr->file_size.size));
         fs_file_size(cpszName, NULL, pr);
         *pdwSize = pr->file_size.size;
      } else {
         return EBADPARM;
      }
   }
   
   FARF(TRACE,("OEMFS_FillInfo() attrib: %d",
               *pattrib));
   FARF(TRACE,("OEMFS_FillInfo() date: %d",
               *pdwCreationDate));
   FARF(TRACE,("OEMFS_FillInfo() size: %d",
               *pdwSize));
   FARF(TRACE,("OEMFS_FillInfo()=>%d",
               OEMFS_MapError(pr->get_attribs.status)));

   return OEMFS_MapError(pr->get_attribs.status);

}

/*==================================================================
Function: OEMFS_MapError

Description: Translates DMSS errors into BREW errors
==================================================================*/
static int OEMFS_MapError( fs_status_type fs_err)
{
   static struct
   {
      int            err;
      fs_status_type fs_err;
   } aem[] = 
      {
         {SUCCESS          , FS_OKAY_S},
         {EFILEOPEN        , FS_FILE_OPEN_S},
         {EFILEEXISTS      , FS_FILE_ALREADY_EXISTS_S},
         {EFILENOEXISTS    , FS_NONEXISTENT_FILE_S},
         {EDIRNOEXISTS     , FS_NONEXISTENT_DIR_S},
         {EDIRNOTEMPTY     , FS_DIR_NOT_EMPTY_S},
         {EBADPARM         , FS_PARAMETER_ERROR_S},
         {EBADFILENAME     , FS_BAD_FILE_NAME_S},
         {EBADSEEKPOS      , FS_BAD_SEEK_POS_S},
         {EFILEEOF         , FS_EOF_S},
         {EFSFULL          , FS_SPACE_EXHAUSTED_S},
         {EFILEEXISTS      , FS_DIR_ALREADY_EXISTS_S},
         {EINVALIDOPERATION, FS_ILLEGAL_OPERATION_S},
         {EINVALIDOPERATION, FS_UNKNOWN_OPERATION_S},
         {EINVALIDOPERATION, FS_FILE_NOT_OPEN_S},
         {EFILEEOF         , FS_ENUM_DONE_S} 
      };
   int i;

   for (i = 0; i < ARRAY_SIZE(aem); i++ ) {
      if (aem[i].fs_err == fs_err) {
         return aem[i].err;
      }
   }

   return FILE_ERROR;
}


static int32 OEMFS_GetOpenFileSize(OEMINSTANCE pFileHandle, boolean bRestore, int *pnErr)
{
   fs_handle_type    hf = OEMFS_MapHandle(pFileHandle,0);
   fs_rsp_msg_type   resp;
   int32             nRestore,nSize;
   byte              temp;

   FARF(TRACE,("OEMFS_GetOpenFileSize(%d,%d)",pFileHandle,bRestore));

   *pnErr = SUCCESS;

   fs_tell(hf,NULL,&resp);
   nRestore = resp.tell.position;

   fs_seek(hf, FS_SEEK_EOF, 0, NULL, &resp);

   // The call will fail on some DMSS variants...

   if (FS_OKAY_S != resp.seek.status) {

      // This is the ONLY negative seek allowed for DMSS...
      fs_seek(hf, FS_SEEK_EOF, -1, NULL, &resp);

      // Check for empty (0 (zero) size) file
      if (0 == nRestore) {
         fs_read(hf,(PACKED void *)&temp,1,NULL,&resp);
         if ((resp.read.count == 0) && 
             ((resp.read.status == FS_EOF_S) || 
              (resp.read.status == FS_OKAY_S))) {
            FARF(TRACE,("OEMFS_GetOpenFileSize()=>%d",0));
            return 0;
         }
      }

      if (FS_OKAY_S != resp.seek.status) {
         *pnErr = OEMFS_MapError(resp.seek.status);
         FARF(TRACE,("OEMFS_GetOpenFileSize() err: %d=>%d",*pnErr,-1));
         return -1;
      }

      fs_read(hf,(PACKED void *)&temp,1,NULL,&resp);
   }

   // Get the current offset - this is the size...

   fs_tell(hf,NULL,&resp);
   nSize = resp.tell.position;

   if (bRestore) {
      fs_seek(hf,FS_SEEK_SET,nRestore,NULL,&resp);
   }

   FARF(TRACE,("OEMFS_GetOpenFileSize()=>%d",nSize));

   return nSize;
}


