www.pudn.com > efs.rar > fs_compat.c


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

                  E M B E D D E D   F I L E   S Y S T E M

GENERAL DESCRIPTION
  Compatibility interface for EFS2. Users of EFS2 should use the POSIX
  interface as much as possible because this interface is deprecated.

INITIALIZATION AND SEQUENCING REQUIREMENTS
  fs_compat_init should be called before any calls to the API are made.
  FEATURE_EFS_ACCESS_METHODS needs to be turned on.

Copyright (C) 2002,2003,2004,2005,2006 by QUALCOMM Inc. All Rights Reserved.

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

                           EDIT HISTORY FOR FILE

  $Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_compat.c#16 $ $DateTime: 2006/11/16 13:34:24 $ $Author: davidb $

when       who    what, where, why
--------   ---    ----------------------------------------------------------
11/16/06   umr    Increase fs_compat task's stack size
11/10/06   umr    Backoff Changes to fs_compat layer to execute as API
10/30/06   sh     Ensure that no FS Command Signals are received.
10/30/06   umr    Change fs_compat layer to execute as API
09/01/06   yg     Fixed compilation issue for non FTL enabled builds.
08/28/06   yg     Added code to handle sync on power down, offline and timer.
07/24/06   sch    Passed in the file name to efs_statvfs()
                  in fs_compat_statfs
06/29/06   dlb    Remove check for seek past end (too slow on SFAT).
06/27/06   dlb    Optimize directory iteration for SFAT.
06/07/06   sh     Featurized hotplug_format()
05/07/06   sh     Removed superfluous break/return statements.
03/28/06   sh     Added FORMAT operation handling
10/26/05   sh     Lint cleanup.
05/18/05   sh     Return the error code in failed fs_compat_seek_read/write
04/25/05   nrs    Fixed bug in rename check
04/04/05   nrs    Added check to see if file already exists prior to renaming
03/23/05   nrs    Added TIMETEST code for 6275u
10/15/04   dlb    Adjust copyright line.
10/07/04   dlb    Whitespace cleanup.
06/14/04   gr     Fixed compiler warnings.
05/11/04   gr     Return an error on an attempt to seek past the end of a file.
04/15/04   gr     Renamed errno to efs2_errno to avoid a conflict with the
                  errno macro.
11/17/03   gr     Fixed bugs in the read and write functions.
11/14/03   gr     Fixed a bug in fs_compat_get_attribs.
11/11/03   gr     Sets creation_date correctly rather than to a bogus constant.
10/18/03   gr     Changes to better mimic EFS1.
09/22/03   gr     Undid previous change. Apparently EFS1 does not overwrite
                  files by default. compat_open now returns the right error
                  code instead.
09/09/03   gr     Removed O_EXCL as a default option when creating a file. EFS1
                  allows users to overwrite existing files by default.
06/18/03   gr     Fixed a bug in fs_compat_file_size.
06/17/03  jkl     Clean up code.
04/18/03   gr     Fixed directory listing and enhanced trailing slashes code.
04/03/03   gr     The mkdir and rmdir functions now ignore trailing slashes,
                  to match EFS1 behavior.
03/14/03   gr     Fixed the read and write functions and the file and
                  directory removal functions to better mimic EFS1 return
                  values.
03/06/03   gr     Bug fix in the file_size function. Also got rid of a warning.
09/15/02   gr     Bug fixes in the directory iteration functions.
08/06/02   gr     Created.

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

#include "customer.h"

#ifdef FEATURE_EFS_COMPATIBILITY

#include "fs_sys_types.h"
#include "fs_err.h"
#include "fs_public.h"
#include "fs_errno.h"
#include "fs_hotplug.h"
#include "fs_util.h"
#include "queue.h"
#include "fsi.h"
#include "msg.h"
#include "fs_am.h"
#include "rex.h"
#include "task.h"
#include "dog.h"
#include "fs_compat.h"
#include "fs_ftl_cache.h"

#include 
#include 

#ifdef TIMETEST
#include "timetest.h"
#endif

#ifdef FEATURE_EFS_ACCESS_METHODS

/* The stack size of the compatibility task.
 */
#define FS_COMPAT_STACK_SIZ         4096

#define FS_COMPAT_RPT_TIMER_SIG   0x0001
#define FS_COMPAT_CACHE_SYNC_SIG  0x0002

#define FTL_CACHE_SYNC_TIME       5000

#define FS_COMPAT_KICK_WATCHDOG()

/* TCB for the compatibility task.
 * Since this compatibility task doesn't have header file
 * the tcb is not included anywhere in headers, but since
 * tmc needs to access this tcb in order to send the power
 * down or offline signal this has been made global.
 * NOTE: Look at file "tmc.c" where this is accessed */
rex_tcb_type fs_compat_tcb;

/* Stack for the compatibility task */
static unsigned char fs_compat_stack [FS_COMPAT_STACK_SIZ];

/* Watchdog report timer for the compatibility task. */
static rex_timer_type fs_compat_rpt_timer;
static rex_timer_type fs_cache_sync_timer;

/* Our commands are placed in this queue. */
static q_type fs_compat_queue;

typedef enum {
  FS_COMPAT_TYPE_FILE,
  FS_COMPAT_TYPE_DIR
} fs_compat_file_type;

/***********************************************************************
 * FUNCTION      fs_compat_map_err
 *
 * DESCRIPTION   Maps EFS2 error codes to EFS1 error codes
 *
 ***********************************************************************/
static fs_status_type
fs_compat_map_err (int efs2_errno, fs_compat_file_type filetype)
{
  fs_status_type efs1_errno = FS_FAIL_S;

  if (filetype == FS_COMPAT_TYPE_FILE) {
    switch (efs2_errno) {
      case EBUSY:
        efs1_errno = FS_BUSY_S;
        break;

      case EBADF:
        efs1_errno = FS_FILE_NOT_OPEN_S;
        break;

      case ETXTBSY:
        efs1_errno = FS_FILE_OPEN_S;
        break;

      case EEXIST:
        efs1_errno = FS_FILE_ALREADY_EXISTS_S;
        break;

      case ENOENT:
      case EACCES:
      case EISDIR:
        efs1_errno = FS_NONEXISTENT_FILE_S;
        break;

      case EINVAL:
        efs1_errno = FS_ILLEGAL_OPERATION_S;
        break;

      case EPERM:
        efs1_errno = FS_PARAMETER_ERROR_S;
        break;

      case ENAMETOOLONG:
        efs1_errno = FS_BAD_FILE_NAME_S;
        break;

      case EEOF:
        efs1_errno = FS_EOF_S;
        break;

      case ENOSPC:
        efs1_errno = FS_SPACE_EXHAUSTED_S;
        break;

      case EMFILE:
        efs1_errno = FS_OPEN_TABLE_FULL_S;
        break;

      default:
        efs1_errno = FS_FAIL_S;
        break;
    }
  }
  else if (filetype == FS_COMPAT_TYPE_DIR) {
    switch (efs2_errno) {
      case EBUSY:
        efs1_errno = FS_BUSY_S;
        break;

      case EEXIST:
        efs1_errno = FS_DIR_ALREADY_EXISTS_S;
        break;

      case ENOENT:
      case ENOTDIR:
      case EACCES:
        efs1_errno = FS_NONEXISTENT_DIR_S;
        break;

      case ENOTEMPTY:
        efs1_errno = FS_DIR_NOT_EMPTY_S;
        break;

      case EINVAL:
        efs1_errno = FS_ILLEGAL_OPERATION_S;
        break;

      case EPERM:
        efs1_errno = FS_PARAMETER_ERROR_S;
        break;

      case ENOSPC:
        efs1_errno = FS_DIR_SPACE_EXHAUSTED_S;
        break;

      default:
        efs1_errno = FS_FAIL_S;
        break;
    }
  } else {
    FS_ERR_FATAL ("Unknown file type %d", filetype, 0, 0);
  }

  return efs1_errno;
}

/***********************************************************************
 * FUNCTION      fs_compat_open
 *
 * DESCRIPTION   Handles the FS_OPEN_OP command.
 *
 ***********************************************************************/
static fs_handle_type
fs_compat_open (fs_cmd_type *cmd_ptr)
{
  int            fs_compat_handle;
  fs_handle_type handle = FS_NULL_HANDLE;
  int            oflag;
  fs_mode_t      mode;

  switch (cmd_ptr->fs_op.parms.open.access_kind)
  {
    case FS_OA_CREATE:
      oflag = O_CREAT | O_RDWR | O_EXCL;
      break;

    case FS_OA_APPEND:
      oflag = O_APPEND | O_RDWR;
      break;

    case FS_OA_READONLY:
      oflag = O_RDONLY;
      break;

    case FS_OA_READWRITE:
      oflag = O_RDWR;
      break;

    case FS_OA_TRUNCATE:
      oflag = O_RDWR | O_TRUNC;
      break;

    case FS_OA_TEST:
      return FS_FAIL_S;

    default:
      FS_ERR ("Invalid access kind %d given to open request",
              cmd_ptr->fs_op.parms.open.access_kind, 0, 0);
      return FS_FAIL_S;
  }

  mode = S_IWRITE | S_IREAD;

  fs_compat_handle = efs_open (
      cmd_ptr->fs_op.parms.open.option.create.filename,
      oflag, mode);
  if (fs_compat_handle >= 0)
  {
    handle = (fs_handle_type) fs_compat_handle;
    cmd_ptr->fs_op.status = FS_OKAY_S;
  }
  else
    cmd_ptr->fs_op.status = fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  return handle;
} /* END fs_compat_open */

/***********************************************************************
 * FUNCTION      fs_compat_write
 *
 * DESCRIPTION   Handles the FS_WRITE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_write (fs_cmd_type *cmd_ptr)
{
  int        fs_compat_handle;
  fs_ssize_t result = 0;
  fs_size_t  nbytes;
  fs_size_t  bytes_written;
  char      *write_buf;

  fs_compat_handle = (int) cmd_ptr->fs_op.parms.write.fhandle;

  nbytes = (fs_size_t) cmd_ptr->fs_op.parms.write.length;
  bytes_written = 0;
  write_buf = (char *) cmd_ptr->fs_op.parms.write.buf;
  while (bytes_written < nbytes)
  {
    result = efs_write (fs_compat_handle,
                        (const void *) (write_buf + bytes_written),
                        nbytes - bytes_written);
    if (result <= 0)
      break;
    bytes_written += (fs_size_t) result;
  }

  cmd_ptr->rsp_msg->write.count = bytes_written;

  if ((result < 0) && (efs_errno != ENOSPC))
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_write */

/***********************************************************************
 * FUNCTION      fs_compat_read
 *
 * DESCRIPTION   Handles the FS_READ_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_read (fs_cmd_type *cmd_ptr)
{
  int        fs_compat_handle;
  fs_ssize_t result = 0;
  fs_size_t nbytes;
  fs_size_t bytes_read;
  char *read_buf;

  fs_compat_handle = (int) cmd_ptr->fs_op.parms.read.fhandle;

  nbytes = (fs_size_t) cmd_ptr->fs_op.parms.read.length;
  bytes_read = 0;
  read_buf = (char *) cmd_ptr->fs_op.parms.read.buf;
  while (bytes_read < nbytes)
  {
    result = efs_read (fs_compat_handle,
                       (void *) (read_buf + bytes_read),
                       nbytes - bytes_read);
    if (result <= 0)
      break;
    bytes_read += (fs_size_t) result;
  }

  cmd_ptr->rsp_msg->read.count = (dword) bytes_read;

  if (result < 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_read */

/***********************************************************************
 * FUNCTION      fs_compat_seek_read
 *
 * DESCRIPTION   Handles the FS_SEEK_READ_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_seek_read (fs_cmd_type *cmd_ptr)
{
  int        handle;
  fs_ssize_t result = 0;
  int        origin;
  fs_size_t  nbytes;
  fs_size_t  bytes_read;
  char      *read_buf;
  fs_off_t   offset;

  handle = (int) cmd_ptr->fs_op.parms.seek_read.fhandle;
  offset = (fs_off_t) cmd_ptr->fs_op.parms.seek_read.position;

  switch (cmd_ptr->fs_op.parms.seek_read.origin) {
    case FS_SEEK_SET:
      origin = SEEK_SET;
      break;

    case FS_SEEK_CURRENT:
      origin = SEEK_CUR;
      break;

    case FS_SEEK_EOF:
      origin = SEEK_END;
      break;

    default:
      return FS_BAD_SEEK_POS_S;
  }

  if (efs_lseek (handle, offset, origin) == -1)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);

  nbytes = (fs_size_t) cmd_ptr->fs_op.parms.seek_read.length;
  bytes_read = 0;
  read_buf = (char *) cmd_ptr->fs_op.parms.seek_read.buf;
  while (bytes_read < nbytes)
  {
    result = efs_read (handle,
                       (void *) (read_buf + bytes_read),
                       nbytes - bytes_read);
    if (result <= 0)
      break;
    bytes_read += (fs_size_t) result;
  }

  cmd_ptr->rsp_msg->seek_read.count = (dword) bytes_read;

  if (result < 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_seek_read */

/***********************************************************************
 * FUNCTION      fs_compat_seek_write
 *
 * DESCRIPTION   Handles the FS_SEEK_WRITE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_seek_write (fs_cmd_type *cmd_ptr)
{
  int        handle;
  fs_ssize_t result = 0;
  int        origin;
  fs_size_t  nbytes;
  fs_size_t  bytes_written;
  char      *write_buf;
  fs_off_t   offset;

  handle = (int) cmd_ptr->fs_op.parms.seek_write.fhandle;
  offset = (fs_off_t) cmd_ptr->fs_op.parms.seek_write.position;

  switch (cmd_ptr->fs_op.parms.seek_write.origin) {
    case FS_SEEK_SET:
      origin = SEEK_SET;
      break;

    case FS_SEEK_CURRENT:
      origin = SEEK_CUR;
      break;

    case FS_SEEK_EOF:
      origin = SEEK_END;
      break;

    default:
      return FS_BAD_SEEK_POS_S;
  }

  if (efs_lseek (handle, offset, origin) == -1)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);

  nbytes = (fs_size_t) cmd_ptr->fs_op.parms.seek_write.length;
  bytes_written = 0;
  write_buf = (char *) cmd_ptr->fs_op.parms.seek_write.buf;
  while (bytes_written < nbytes)
  {
    result = efs_write (handle,
                        (const void *) (write_buf + bytes_written),
                        nbytes - bytes_written);
    if (result <= 0)
      break;
    bytes_written += (fs_size_t) result;
  }

  cmd_ptr->rsp_msg->seek_write.count = (dword) bytes_written;

  if ((result < 0) && (efs_errno != ENOSPC))
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_seek_write */

/***********************************************************************
 * FUNCTION      fs_compat_close
 *
 * DESCRIPTION   Handles the FS_CLOSE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_close (fs_cmd_type *cmd_ptr)
{
  int handle;

  handle = (int) cmd_ptr->fs_op.parms.close.fhandle;

  if (efs_close (handle) == 0)
    return FS_OKAY_S;
  else
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
} /* END fs_compat_close */

/***********************************************************************
 * FUNCTION      fs_compat_tell
 *
 * DESCRIPTION   Handles the FS_TELL_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_tell (fs_cmd_type *cmd_ptr)
{
  int      handle;
  fs_off_t result;

  handle = (int) cmd_ptr->fs_op.parms.tell.fhandle;

  result = efs_lseek (handle, 0, SEEK_CUR);
  if (result == -1)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
  {
    cmd_ptr->rsp_msg->tell.position = (fs_file_position_type) result;
    return FS_OKAY_S;
  }
} /* END fs_compat_tell */

/***********************************************************************
 * FUNCTION      fs_compat_seek
 *
 * DESCRIPTION   Handles the FS_SEEK_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_seek (fs_cmd_type *cmd_ptr)
{
  int      handle;
  int      origin;
  fs_off_t offset;

  handle = (int) cmd_ptr->fs_op.parms.seek.fhandle;
  offset = (fs_off_t) cmd_ptr->fs_op.parms.seek.position;

  switch (cmd_ptr->fs_op.parms.seek.origin) {
    case FS_SEEK_SET:
      origin = SEEK_SET;
      break;

    case FS_SEEK_CURRENT:
      origin = SEEK_CUR;
      break;

    case FS_SEEK_EOF:
      origin = SEEK_END;
      break;

    default:
      return FS_BAD_SEEK_POS_S;
  }

  if (efs_lseek (handle, offset, origin) == -1)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_seek */

/***********************************************************************
 * FUNCTION      fs_compat_file_size
 *
 * DESCRIPTION   Handles the FS_FILE_SIZE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_file_size (fs_cmd_type *cmd_ptr)
{
  int            result;
  struct fs_stat sbuf;

  result = efs_stat (cmd_ptr->fs_op.parms.file_size.filename, &sbuf);
  if (result != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);

  cmd_ptr->rsp_msg->file_size.size = (dword) sbuf.st_size;
  return FS_OKAY_S;
} /* END fs_compat_file_size */

/***********************************************************************
 * FUNCTION      fs_compat_nametest
 *
 * DESCRIPTION   Handles the FS_NAMETEST_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_nametest (fs_cmd_type *cmd_ptr)
{
  int              result;
  struct fs_stat   sbuf;
  boolean          name_found = FALSE;
  fs_nametest_type the_type = cmd_ptr->fs_op.parms.nametest.type;

  result = efs_stat (cmd_ptr->fs_op.parms.nametest.filename, &sbuf);

  /* Need to verify this. What file types are valid on EFS2?
   */
  if (result != 0)
  {
    /* If there was an error, it is definitely not present. */
    /* leave name_found false. */
  }
  else if (!S_ISDIR (sbuf.st_mode) && !S_ISREG (sbuf.st_mode))
  {
  }
  else if (the_type == FS_TEST_FILE_OR_DIR
              || (the_type == FS_TEST_FILE && S_ISREG (sbuf.st_mode))
              || (the_type == FS_TEST_DIR  && S_ISDIR (sbuf.st_mode)))
  {
    name_found = TRUE;
  }
  else
  {
    /* Otherwise, the mode doesn't match. */
  }

  cmd_ptr->rsp_msg->nametest.name_found = name_found;
  return FS_OKAY_S;
} /* END fs_compat_nametest */

/***********************************************************************
 * FUNCTION      fs_compat_format
 *
 * DESCRIPTION   Handles the FS_FORMAT_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_format (fs_cmd_type *cmd_ptr)
{
#if defined FEATURE_EFS_HOTPLUG || defined FEATURE_EFS_COLDPLUG
  return ((hotplug_format (cmd_ptr->fs_op.parms.format.dirname) == 0)
          ? FS_OKAY_S : FS_FAIL_S);
#else
  return FS_FAIL_S;
#endif
} /* END fs_compat_format */

/***********************************************************************
 * FUNCTION      fs_compat_get_attribs
 *
 * DESCRIPTION   Handles the FS_GET_ATTRIBS_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_get_attribs (fs_cmd_type *cmd_ptr)
{
  int            result;
  struct fs_stat sbuf;
  int            handle;
  fs_time_t      ctime;

  if (cmd_ptr->fs_op.parms.get_attribs.filename == NULL)
  {
    /* Get open file attributes. */
    handle = (int) cmd_ptr->fs_op.parms.get_attribs.fhandle;

    result = efs_fstat (handle, &sbuf);
  }
  else
    result = efs_stat (cmd_ptr->fs_op.parms.get_attribs.filename, &sbuf);
  if (result != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);

  /* XXX: Set the following fields correctly.
   */
  cmd_ptr->rsp_msg->get_attribs.attributes = FS_FA_UNRESTRICTED;
  ctime = sbuf.st_ctime;
  if (ctime > CDMA_TO_FS_TIME_DELTA)
    ctime -= CDMA_TO_FS_TIME_DELTA;
  else
    ctime  = 0;
  cmd_ptr->rsp_msg->get_attribs.creation_date = ctime;
  cmd_ptr->rsp_msg->get_attribs.buffering_option = FS_OB_PROHIBIT;
  cmd_ptr->rsp_msg->get_attribs.cleanup_option = FS_OC_CLOSE;
  return FS_OKAY_S;
} /* END fs_compat_get_attribs */

/***********************************************************************
 * FUNCTION      fs_compat_set_attribs
 *
 * DESCRIPTION   Handles the FS_SET_ATTRIBS_OP command.
 *
 ***********************************************************************/
/* Nothing for now. */
static fs_status_type
fs_compat_set_attribs (fs_cmd_type *cmd_ptr)
{
  /* XXX: Not implemented yet.
   */
  (void) cmd_ptr;
  return FS_OKAY_S;
} /* END fs_compat_set_attribs */

/***********************************************************************
 * FUNCTION      fs_compat_fmemset
 *
 * DESCRIPTION   Handles the FS_FMEMSET_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_fmemset (fs_cmd_type *cmd_ptr)
{
  static unsigned char buffer[256];   /* A bigger buffer would be faster,
                                         but would use more RAM. */
  int handle;
  fs_off_t result;
  fs_size_t count;
  fs_size_t this;

  handle = cmd_ptr->fs_op.parms.fmemset.fhandle;

  /* Find out where we are. */
  result = efs_lseek (handle, 0, SEEK_CUR);
  if (result == -1)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);

  if (result >= (fs_off_t) cmd_ptr->fs_op.parms.fmemset.position)
  {
    /* XXX: Verify this behavior.
     * If we are already at or past this position, there is nothing more to
     * do, so just return success.  I have not determined if the EFS will
     * cause a seek to the new position, so no guarantees that this
     * behavior is the same as EFS. */

    return FS_OKAY_S;
  }

  count = cmd_ptr->fs_op.parms.fmemset.position - result;

  /* Now write out blocks of data until we have data. */
  memset (buffer, cmd_ptr->fs_op.parms.fmemset.fill_value, sizeof (buffer));

  while (count != 0) {
    this = sizeof (buffer);
    if (this > count)
      this = count;

    result = efs_write (handle, buffer, this);

    if (result != (int) this)
      /* We didn't write enough, or else some other kind of error. */
      return FS_FAIL_S;

    count -= this;
  }

  return FS_OKAY_S;
} /* END fs_compat_fmemset */

/***********************************************************************
 * FUNCTION      fs_compat_rmfile
 *
 * DESCRIPTION   Handles the FS_RMFILE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_rmfile (fs_cmd_type *cmd_ptr)
{
  if (efs_unlink (cmd_ptr->fs_op.parms.rmfile.filename) != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_rmfile */

/***********************************************************************
 * FUNCTION      fs_compat_rename
 *
 * DESCRIPTION   Handles the FS_RENAME_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_rename (fs_cmd_type *cmd_ptr)
{
  struct fs_stat sbuf;
  if (efs_stat (cmd_ptr->fs_op.parms.rename.new_filename, &sbuf) == 0)
  {
    return FS_FILE_ALREADY_EXISTS_S;
  }
  if ( efs_rename (cmd_ptr->fs_op.parms.rename.old_filename,
                   cmd_ptr->fs_op.parms.rename.new_filename) != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_rename */

/***********************************************************************
 * FUNCTION      fs_compat_mkdir
 *
 * DESCRIPTION   Handles the FS_MKDIR_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_mkdir (fs_cmd_type *cmd_ptr)
{
  fs_status_type mkdir_status;
  char *dirname;
  int dirname_len;
  int i;
  int result;

  /* EFS1 ignores trailing slashes at the end of directory names, but
   * EFS2 does not. So remove trailing slashes before calling efs_mkdir.
   */
  dirname = cmd_ptr->fs_op.parms.mkdir.dirname;
  dirname_len = strlen (dirname);
  for (i = dirname_len-1; i > 0 && dirname[i] == '/'; i--)
    dirname[i] = '\0';

  result = efs_mkdir (dirname, S_IREAD|S_IWRITE|S_IEXEC);
  if (result == 0)
    mkdir_status = FS_OKAY_S;
  else
    mkdir_status = fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_DIR);

  return mkdir_status;
} /* END fs_compat_mkdir */

/***********************************************************************
 * FUNCTION      fs_compat_rmdir
 *
 * DESCRIPTION   Handles the FS_RMDIR_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_rmdir (fs_cmd_type *cmd_ptr)
{
  char *dirname;
  int dirname_len;
  int i;

  /* EFS1 ignores trailing slashes at the end of directory names, but
   * EFS2 does not. So remove trailing slashes before calling efs_rmdir.
   */
  dirname = cmd_ptr->fs_op.parms.mkdir.dirname;
  dirname_len = strlen (cmd_ptr->fs_op.parms.rmdir.dirname);
  for (i = dirname_len-1; i > 0 && dirname[i] == '/'; i--)
    dirname[i] = '\0';

  if (efs_rmdir (dirname) != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_DIR);
  else
    return FS_OKAY_S;
} /* END fs_compat_rmdir */

/***********************************************************************
 * FUNCTION      fs_compat_truncate
 *
 * DESCRIPTION   Handles the FS_TRUNCATE_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_truncate (fs_cmd_type *cmd_ptr)
{
  int fs_compat_handle;

  fs_compat_handle = (int) cmd_ptr->fs_op.parms.truncate.fhandle;
  if (efs_ftruncate (fs_compat_handle,
                     cmd_ptr->fs_op.parms.truncate.position) != 0)
    return fs_compat_map_err (efs_errno, FS_COMPAT_TYPE_FILE);
  else
    return FS_OKAY_S;
} /* END fs_compat_truncate */

/* The following variable is used by fs_compat_list_files_or_dirs,
 * fs_compat_num_files_or_dirs and fs_compat_enum_move. Consider making
 * this a local variable in each of those functions.
 */
static char pathbuf[FS_FILENAME_MAX_LENGTH + 1];

static fs_status_type
fs_compat_list_files_or_dirs (
  fs_cmd_type *cmd_ptr,
  fs_enum_type file_type)
{
  char              *dirname;
  dword              copy_index;
  dword              buf_limit;
  dword              list_buf_index = (dword)0;
  char              *list_buf;
  EFSDIR            *dir_stream_ptr;
  struct fs_dirent  *result;
  struct fs_stat     stat_buf;
  fs_status_type     status = FS_OKAY_S;

  if (file_type == FS_ENUM_FILES)
  {
    dirname = cmd_ptr->fs_op.parms.list_files.dirname;
  }
  else
  {
    dirname = cmd_ptr->fs_op.parms.list_dirs.dirname;
  }

  dir_stream_ptr = efs_opendir (dirname);
  if (dir_stream_ptr == NULL)
  {
    status = FS_NONEXISTENT_DIR_S;
    goto list_end;
  }

  /* The first two entries returned will be "." and "..". Ignore these.
   */
  result = efs_readdir (dir_stream_ptr);
  if (result == NULL)
  {
    /* This is bad. A directory without a . entry???? */
    status = FS_NONEXISTENT_DIR_S;
    goto list_end;
  }
  result = efs_readdir (dir_stream_ptr);
  if (result == NULL)
  {
    /* This is bad. A directory without a .. entry???? */
    status = FS_NONEXISTENT_DIR_S;
    goto list_end;
  }

  /* Copy to caller's buffer (allow for 2 terminating NULLs) */
  if (file_type == FS_ENUM_FILES)
  {
    buf_limit = cmd_ptr->fs_op.parms.list_files.length - 2;
    list_buf  = (char *) cmd_ptr->fs_op.parms.list_files.buf;
  }
  else
  {
    buf_limit = cmd_ptr->fs_op.parms.list_dirs.length - 2;
    list_buf  = (char *) cmd_ptr->fs_op.parms.list_dirs.buf;
  }

  list_buf[0] = 0x00;

  while (1) {
    result = efs_readdir (dir_stream_ptr);
    if (result == NULL)
    {
      break;
    }

    if (strlen (dirname) + 1 + strlen (result->d_name)
        > FS_FILENAME_MAX_LENGTH)
    {
      /* The pathname is too long. Return an error. Check if there is a
       * better error we can return. */
      status = FS_NONEXISTENT_DIR_S;
      goto list_end;
    }

    strcpy (pathbuf, dirname);
    strcat (pathbuf, "/");
    strcat (pathbuf, result->d_name);
    if (efs_stat (pathbuf, &stat_buf) == -1)
    {
      /* Unable to stat the file or directory. Return an error. Check if there
       * is a better error we can return. */
      status = FS_NONEXISTENT_DIR_S;
      goto list_end;
    }

    if (((file_type == FS_ENUM_FILES) && S_ISREG (stat_buf.st_mode))
       || ((file_type == FS_ENUM_DIRECTORIES) && S_ISDIR (stat_buf.st_mode)))
    {
      copy_index = 0;
      while (list_buf_index < buf_limit) {
        list_buf[list_buf_index++] = pathbuf[copy_index++];
        if (pathbuf[copy_index] == 0x00)
        {
          /* Done with this file name */
          list_buf[list_buf_index++] = 0x00;
          break;
        }
      }
    }
    if (list_buf_index >= buf_limit)
    {
      break;
    }
  }

  /* Adjust index so that a name is not truncated */
  while ((list_buf_index != 0) && (list_buf[--list_buf_index] != 0x00)) {}
  list_buf_index++;
  list_buf[list_buf_index++] = 0x00;

  /* Set return parameter to number of bytes copied */
  if (file_type == FS_ENUM_FILES)
  {
    cmd_ptr->rsp_msg->list_files.count = list_buf_index;
  }
  else
  {
    cmd_ptr->rsp_msg->list_dirs.count = list_buf_index;
  }

list_end:
  if (dir_stream_ptr != NULL)
    (void) efs_closedir (dir_stream_ptr);
  return status;
} /* END fs_compat_list_files_or_dirs */

static fs_status_type
fs_compat_num_files_or_dirs (
  fs_cmd_type *cmd_ptr,
  fs_enum_type file_type)
{
  EFSDIR           *dir_stream_ptr;
  struct fs_dirent *result;
  struct fs_stat    stat_buf;
  int               num_entries = 0;
  char             *dirname;
  fs_status_type    status = FS_OKAY_S;

  if (file_type == FS_ENUM_FILES)
  {
    dirname = cmd_ptr->fs_op.parms.num_files.dirname;
  }
  else
  {
    dirname = cmd_ptr->fs_op.parms.num_dirs.dirname;
  }

  dir_stream_ptr = efs_opendir (dirname);
  if (dir_stream_ptr == NULL)
  {
    status = FS_NONEXISTENT_DIR_S;
    goto num_end;
  }

  /* The first two entries returned will be "." and "..". Ignore these.
   */
  result = efs_readdir (dir_stream_ptr);
  if (result == NULL)
  {
    /* This is bad. A directory without a . entry???? */
    status = FS_NONEXISTENT_DIR_S;
    goto num_end;
  }
  result = efs_readdir (dir_stream_ptr);
  if (result == NULL)
  {
    /* This is bad. A directory without a .. entry???? */
    status = FS_NONEXISTENT_DIR_S;
    goto num_end;
  }

  while (1) {
    result = efs_readdir (dir_stream_ptr);
    if (result == NULL)
    {
      break;
    }

    if (strlen (dirname) + 1 + strlen (result->d_name)
        > FS_FILENAME_MAX_LENGTH)
    {
      /* Pathname too long. Maybe return a better error here? */
      status = FS_NONEXISTENT_DIR_S;
      goto num_end;
    }

    strcpy (pathbuf, dirname);
    strcat (pathbuf, "/");
    strcat (pathbuf, result->d_name);
    if (efs_stat (pathbuf, &stat_buf) == -1)
    {
      /* Unable to stat file. Maybe return a better error here? */
      status = FS_NONEXISTENT_DIR_S;
      goto num_end;
    }

    if (((file_type == FS_ENUM_FILES) && S_ISREG (stat_buf.st_mode))
       || ((file_type == FS_ENUM_DIRECTORIES) && S_ISDIR (stat_buf.st_mode)))
    {
      num_entries++;
    }
  }

  if (file_type == FS_ENUM_FILES)
  {
    cmd_ptr->rsp_msg->num_files.num_files = num_entries;
  }
  else
  {
    cmd_ptr->rsp_msg->num_dirs.num_dirs = num_entries;
  }

num_end:
  if (dir_stream_ptr != NULL)
    (void) efs_closedir (dir_stream_ptr);
  return status;
} /* END fs_compat_num_files_or_dirs */

/***********************************************************************
 * FUNCTION      fs_compat_num_files
 *
 * DESCRIPTION   Handles the FS_NUM_FILES_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_num_files (fs_cmd_type *cmd_ptr)
{
  return fs_compat_num_files_or_dirs (cmd_ptr, FS_ENUM_FILES);
} /* END fs_compat_num_files */

/***********************************************************************
 * FUNCTION      fs_compat_num_dirs
 *
 * DESCRIPTION   Handles the FS_NUM_DIRS_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_num_dirs (fs_cmd_type *cmd_ptr)
{
  return fs_compat_num_files_or_dirs (cmd_ptr, FS_ENUM_DIRECTORIES);
} /* END fs_compat_num_dirs */

/***********************************************************************
 * FUNCTION      fs_compat_list_files
 *
 * DESCRIPTION   Handles the FS_LIST_FILES_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_list_files (fs_cmd_type *cmd_ptr)
{
  return fs_compat_list_files_or_dirs (cmd_ptr, FS_ENUM_FILES);
} /* END fs_compat_list_files */

/***********************************************************************
 * FUNCTION      fs_compat_list_dirs
 *
 * DESCRIPTION   Handles the FS_LIST_DIRS_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_list_dirs (fs_cmd_type *cmd_ptr)
{
  return fs_compat_list_files_or_dirs (cmd_ptr, FS_ENUM_DIRECTORIES);
} /* END fs_compat_list_dirs */

/* Enumeration operations.  POSIX and the EFS have differing semantics
 * for directory enumeration.  With EFS, you specify a
 * directory name and whether you want directories, files, or both.  It
 * then returns the first name, if present.  There are next and prev
 * operators for iterating through the directory.  The behavior is
 * undefined if the directory contents are changed while iterating.
 *
 * POSIX provides the opendir, readdir, rewinddir and closedir operations.
 * Also, the specific data is larger than for the EFS.  Rather than require
 * the EFS iterator data block to be large enough to contain the POSIX data
 * as well, we keep a local copy here.  This could be thought of as a cache.
 * This also handles the case of going in reverse, a reverse lookup is
 * simply a new search. */

typedef struct {
  EFSDIR                *dir_ptr;    /* Pointer to POSIX dir stream.      */
  struct fs_dirent      *dirent_ptr; /* Pointer to entry in dir stream    */
  dword                  index;      /* Which file are we "on".           */
  fs_enum_iterator_type *iterator;   /* Iterator used to match operation. */
} local_fscompat_iterate_t;

static local_fscompat_iterate_t idata;

static void
closeup_iterator (void)
{
  if (idata.dir_ptr)
  {
    efs_closedir (idata.dir_ptr);
    idata.dir_ptr    = NULL;
    idata.dirent_ptr = NULL;
  }
} /* END closeup_iterator */

/* Open the given iterator.  This does not seek or any such thing. */
static fs_status_type
open_iterator (fs_enum_iterator_type *iterator)
{
  EFSDIR *dir_stream_ptr;

  if (iterator->rootname[0] == 0)
  {
    dir_stream_ptr = efs_opendir ("/");
  }
  else
  {
    dir_stream_ptr = efs_opendir ((char *) iterator->rootname);
  }
  if (!dir_stream_ptr)
  {
    return FS_NONEXISTENT_DIR_S;
  }

  /* The first two entries in any directory are "." and "..". Ignore
   * these entries. */
  if (efs_readdir (dir_stream_ptr) == NULL)
  {
    return FS_NONEXISTENT_DIR_S;
  }
  if ((idata.dirent_ptr = efs_readdir (dir_stream_ptr)) == NULL)
  {
    return FS_NONEXISTENT_DIR_S;
  }

  idata.dir_ptr    = dir_stream_ptr;
  idata.index      = 0;
  idata.iterator   = iterator;
  return FS_OKAY_S;

} /* END open_iterator */

/* Given a particular iterator, "activate" it.  This makes sure that this
 * iterator is the open iterator, and that it is advanced properly to the
 * entry just before the one we want.  Note that all directory entries are
 * numbered, even the hidden ones that we do not see at the top level.  0
 * is the space before the first entry. */

static fs_status_type
setup_iterator (fs_enum_iterator_type *iterator)
{
  fs_status_type it_result;

  /* First, check if we are already set up exactly like we want to be. */
  if (idata.dir_ptr
      && (idata.iterator == iterator)
      && (idata.index == iterator->fsequence))
  {
    return FS_OKAY_S;
  }

  /* Next, check if we are on the previous entry.  This handles the normal
   * case of consecutive directory reads. */
  if (idata.dir_ptr
      && (idata.iterator == iterator)
      && (idata.index + 1 == iterator->fsequence))
  {
    idata.dirent_ptr = efs_readdir (idata.dir_ptr);
    idata.index++;

    if (idata.dirent_ptr == NULL)
    {
      closeup_iterator ();
      return FS_ENUM_DONE_S;
    }
    else
    {
      return FS_OKAY_S;
    }
  }

  /* Otherwise, this iterator is not correct, close any current iterator
   * down, and start over. */
  closeup_iterator ();

  it_result = open_iterator (iterator);
  if (it_result != FS_OKAY_S)
    return it_result;

  /* Now we must seek until we are at the correct position. */
  while (idata.index < iterator->fsequence) {
    idata.index++;
    idata.dirent_ptr = efs_readdir (idata.dir_ptr);

    if (idata.dirent_ptr == NULL)
    {
      /* Close the iterator to force failure in the future. */
      closeup_iterator ();
      return FS_ENUM_DONE_S;
    }
  }

  return FS_OKAY_S;
} /* END setup_iterator */

/***********************************************************************
 * FUNCTION      fs_compat_enum_init
 *
 * DESCRIPTION   Handles the FS_ENUM_INIT_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_enum_init (fs_cmd_type *cmd_ptr)
{
  fs_enum_iterator_type *it = cmd_ptr->fs_op.parms.enum_init.iterator_ptr;

  /* XXX: confirm that this is the correct return value. */
  if (strlen (cmd_ptr->fs_op.parms.enum_init.dirname) >=
      sizeof (it->rootname))
  {
    return FS_FAIL_S;
  }

  /* Init just copies over the information. */
  strcpy ((char *) it->rootname, cmd_ptr->fs_op.parms.enum_init.dirname);
  it->enum_kind = cmd_ptr->fs_op.parms.enum_init.enum_kind;
  it->fsequence = 0;
  it->remote_file_processed_index = 0;

  return FS_OKAY_S;
} /* END fs_compat_enum_init */

static fs_status_type
fs_compat_enum_move (fs_cmd_type *cmd_ptr, int direction)
{
  fs_status_type         result;
  fs_enum_data_type     *data_ptr;
  fs_enum_iterator_type *iterator;
  int                    finished = 0;
  int                    root_length;
  int                    our_length;
  dword                  last_good_sequence;
  struct fs_stat         stat_buf;

  iterator = cmd_ptr->fs_op.parms.enum_next.iterator_ptr;
  data_ptr = cmd_ptr->fs_op.parms.enum_next.data_ptr;

  /* Remember the current sequence position and back up to it if we run off
   * the end. */
  last_good_sequence = iterator->fsequence;

  do {
    if (direction < 0)
    {
      if (iterator->fsequence == 0)
      {
        /* Restore the sequence number. */
        iterator->fsequence = last_good_sequence;
        return FS_ENUM_DONE_S;
      }
    }
    iterator->fsequence += direction;

    result = setup_iterator (iterator);

    if (result == FS_ENUM_DONE_S)
    {
      /* We "fell" off the end, so backup the iterator. */
      iterator->fsequence = last_good_sequence;
    }

    if (result != FS_OKAY_S)
      return result;

    if (strlen ((char *) iterator->rootname) + 1 +
        strlen (idata.dirent_ptr->d_name) > FS_FILENAME_MAX_LENGTH)
    {
      // ERR_FATAL ("Figure out what to do here", 0, 0, 0);
      return FS_BAD_FILE_NAME_S;
    }

    if (FS_MASK_CHECK (idata.dirent_ptr->d_stats_present,
          FS_DIRENT_HAS_TYPE |
          FS_DIRENT_HAS_ST_SIZE))
    {
      stat_buf.st_mode = idata.dirent_ptr->d_stat.st_mode;
      stat_buf.st_size = idata.dirent_ptr->d_stat.st_size;
    } else {
      strcpy (pathbuf, (char *) iterator->rootname);
      strcat (pathbuf, "/");
      strcat (pathbuf, idata.dirent_ptr->d_name);
      if (efs_stat (pathbuf, &stat_buf) == -1)
      {
        // ERR_FATAL ("Figure out what to do here", 0, 0, 0);
        return FS_BAD_FILE_NAME_S;
      }
    }

    strcpy (data_ptr->fullname, idata.dirent_ptr->d_name);

    data_ptr->fullname_length = strlen (data_ptr->fullname);

    /* We need to determine if we are done or not. */
    if (iterator->enum_kind == FS_ENUM_FILES)
    {
      if (S_ISREG (stat_buf.st_mode))
      {
        finished = 1;
      }
    }
    else if (S_ISDIR (stat_buf.st_mode))
    {
      /* Directory. */
      finished = 1;
      data_ptr->dirname_length = 0;
    }
  } while (!finished);

  /* Fixup the pathname.  Prepend the rootname to our current name, and
   * then call the access manager to fixup the entire name. */
  root_length = strlen ((char *) iterator->rootname);
  our_length  = strlen (data_ptr->fullname);

  if (root_length + our_length + 1 + 1 > FS_FILENAME_MAX_LENGTH)
  {
    /* Hmm.  What is best to do here.  Let's log an error, so at
     * least this can be debugged. */
    FS_ERR ("fs_compat_enum_next: filename too long", 0, 0, 0);
    return FS_BAD_FILE_NAME_S;
  }

  memmove (data_ptr->fullname + root_length + 1,
           data_ptr->fullname, our_length + 1);
  memmove (data_ptr->fullname, iterator->rootname, root_length);
  data_ptr->fullname[root_length] = '/';

  data_ptr->fullname_length = root_length + our_length + 1;
  data_ptr->dirname_length  = root_length + 1;
  data_ptr->logical_size = stat_buf.st_size;

  /* Now fixup the name that the user sees. */
  fs_am_fixup_enum (data_ptr, iterator);

  return FS_OKAY_S;
} /* END fs_compat_enum_move */

/***********************************************************************
 * FUNCTION      fs_compat_enum_next
 *
 * DESCRIPTION   Handles the FS_ENUM_NEXT_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_enum_next (fs_cmd_type *cmd_ptr)
{
  return fs_compat_enum_move (cmd_ptr, 1);
} /* END fs_compat_enum_next */

/***********************************************************************
 * FUNCTION      fs_compat_enum_prev
 *
 * DESCRIPTION   Handles the FS_ENUM_PREV_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_enum_prev (fs_cmd_type *cmd_ptr)
{
  return fs_compat_enum_move (cmd_ptr, -1);
} /* END fs_compat_enum_prev */

/***********************************************************************
 * FUNCTION      fs_compat_enum_finish
 *
 * DESCRIPTION   Handles the FS_ENUM_FINISH_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_enum_finish (fs_cmd_type *cmd_ptr)
{
  (void) cmd_ptr;

  closeup_iterator ();

  return FS_OKAY_S;
} /* END fs_compat_enum_finish */

/***********************************************************************
 * FUNCTION      fs_compat_statfs
 *
 * DESCRIPTION   Handles the FS_STATFS_OP command.
 *
 ***********************************************************************/
static fs_status_type
fs_compat_statfs (fs_cmd_type *cmd_ptr)
{
  statfs_info      *info = cmd_ptr->fs_op.parms.statfs.info;
  struct fs_statvfs efs_info;

  if (efs_statvfs (cmd_ptr->fs_op.parms.statfs.filename, &efs_info) == 0)
  {
    /* XXX: The f_type field setting is not correct. Ensure that the
     * other fields are being set correctly.
     */
    info->f_type    = STATFS_TYPE_FLASH;
    info->f_bsize   = efs_info.f_bsize;
    info->f_blocks  = efs_info.f_blocks;
    info->f_bfree   = efs_info.f_bfree;
    info->f_bavail  = efs_info.f_bavail;
    info->f_files   = efs_info.f_files;
    info->f_ffree   = efs_info.f_ffree;
    info->f_fsid    = efs_info.f_fsid;
    info->f_namelen = efs_info.f_namemax;
    return FS_OKAY_S;
  }
  else
    return FS_FAIL_S;
} /* END fs_compat_statfs */

/***********************************************************************
 * FUNCTION      fs_compat_process_commands
 *
 * DESCRIPTION   Top-level command processing function. Calls the
 *               appropriate handler depending on the operation code.
 *
 ***********************************************************************/
static void
fs_compat_process_commands (void)
{
  fs_cmd_type     *cmd_ptr;
  rex_tcb_type    *task_ptr;
  rex_sigs_type    task_sigs;
  fs_status_type   status;

  while ((cmd_ptr = (fs_cmd_type *) q_get (&fs_compat_queue)) != NULL) {

    switch (cmd_ptr->fs_op.op) {
      case FS_OPEN_OP:
        cmd_ptr->rsp_msg->open.handle = fs_compat_open (cmd_ptr);
        status = cmd_ptr->fs_op.status;
        cmd_ptr->rsp_msg->open.status = status;
        break;

      case FS_WRITE_OP:
      case FS_RSVD_WRITE_OP:
        /* Eventually, we may want to break this request up into pieces so
         * that other operations may proceed. */
        status = fs_compat_write (cmd_ptr);
        cmd_ptr->rsp_msg->write.status = status;
        break;

      case FS_READ_OP:
        /* May want to break this into pieces so that other operations may
         * be interleaved. */
        status = fs_compat_read (cmd_ptr);
        cmd_ptr->rsp_msg->read.status = status;
        break;

      case FS_CLOSE_OP:
        status = fs_compat_close (cmd_ptr);
        cmd_ptr->rsp_msg->close.status = status;
        break;

      case FS_TELL_OP:
        status = fs_compat_tell (cmd_ptr);
        cmd_ptr->rsp_msg->tell.status = status;
        break;

      case FS_FILE_SIZE_OP:
        status = fs_compat_file_size (cmd_ptr);
        cmd_ptr->rsp_msg->file_size.status = status;
        break;

      case FS_RMFILE_OP:
        status = fs_compat_rmfile (cmd_ptr);
        cmd_ptr->rsp_msg->file_size.status = status;
        break;

      case FS_RENAME_OP:
        status = fs_compat_rename (cmd_ptr);
        cmd_ptr->rsp_msg->rename.status = status;
        break;

      case FS_TRUNCATE_OP:
        status = fs_compat_truncate (cmd_ptr);
        cmd_ptr->rsp_msg->truncate.status = status;
        break;

      case FS_GET_ATTRIBS_OP:
        status = fs_compat_get_attribs (cmd_ptr);
        cmd_ptr->rsp_msg->get_attribs.status = status;
        break;

      case FS_SET_ATTRIBS_OP:
        status = fs_compat_set_attribs (cmd_ptr);
        cmd_ptr->rsp_msg->set_attribs.status = status;
        break;

      case FS_FMEMSET_OP:
        status = fs_compat_fmemset (cmd_ptr);
        cmd_ptr->rsp_msg->fmemset.status = status;
        break;

      case FS_SEEK_OP:
        status = fs_compat_seek (cmd_ptr);
        cmd_ptr->rsp_msg->seek.status = status;
        break;

      case FS_MKDIR_OP:
        status = fs_compat_mkdir (cmd_ptr);
        cmd_ptr->rsp_msg->mkdir.status = status;
        break;

      case FS_RMDIR_OP:
        status = fs_compat_rmdir (cmd_ptr);
        cmd_ptr->rsp_msg->rmdir.status = status;
        break;

      case FS_NAMETEST_OP:
        status = fs_compat_nametest (cmd_ptr);
        cmd_ptr->rsp_msg->nametest.status = status;
        break;

      case FS_FORMAT_OP:
        status = fs_compat_format (cmd_ptr);
        cmd_ptr->rsp_msg->format.status = status;
        break;

      case FS_NUM_FILES_OP:
        status = fs_compat_num_files (cmd_ptr);
        cmd_ptr->rsp_msg->num_files.status = status;
        break;

      case FS_NUM_DIRS_OP:
        status = fs_compat_num_dirs (cmd_ptr);
        cmd_ptr->rsp_msg->num_dirs.status = status;
        break;

      case FS_LIST_FILES_OP:
        status = fs_compat_list_files (cmd_ptr);
        cmd_ptr->rsp_msg->list_files.status = status;
        break;

      case FS_LIST_DIRS_OP:
        status = fs_compat_list_dirs (cmd_ptr);
        cmd_ptr->rsp_msg->list_dirs.status = status;
        break;

      case FS_ENUM_INIT_OP:
        status = fs_compat_enum_init (cmd_ptr);
        cmd_ptr->rsp_msg->enum_init.status = status;
        break;

      case FS_ENUM_NEXT_OP:
        status = fs_compat_enum_next (cmd_ptr);
        cmd_ptr->rsp_msg->enum_next.status = status;
        break;

      case FS_ENUM_PREV_OP:
        status = fs_compat_enum_prev (cmd_ptr);
        cmd_ptr->rsp_msg->enum_prev.status = status;
        break;

      case FS_ENUM_FINISH_OP:
        status = fs_compat_enum_finish (cmd_ptr);
        cmd_ptr->rsp_msg->enum_finish.status = status;
        break;

      case FS_SEEK_READ_OP:
        status = fs_compat_seek_read (cmd_ptr);
        cmd_ptr->rsp_msg->seek_read.status = status;
        break;

      case FS_SEEK_WRITE_OP:
        status = fs_compat_seek_write (cmd_ptr);
        cmd_ptr->rsp_msg->seek_write.status = status;
        break;

      case FS_STATFS_OP:
        status = fs_compat_statfs (cmd_ptr);
        cmd_ptr->rsp_msg->statfs.status = status;
        break;

      default:
        cmd_ptr->rsp_msg->open.status = FS_UNKNOWN_OPERATION_S;
        break;
    }

    fs_am_cleanup_after_command (cmd_ptr);

    /* Let them know it is finished. */
    task_ptr  = cmd_ptr->tcb_ptr;
    task_sigs = cmd_ptr->sigs;

    if (task_ptr == NULL)
    {
      cmd_ptr->callback_p (cmd_ptr->rsp_msg);
    }
    else
    {
      (void) rex_set_sigs (task_ptr, task_sigs);
    }

    /* Restore the buffer to available. */
    fsi_free_cmd_buffer (cmd_ptr->index);
  }
} /* END fs_compat_process_commands */

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

FUNCTION FS_COMPAT_TASK

DESCRIPTION
  This is the task that handles the compatibility interface.  The task enters
  an infinite loop, awaiting commands received  on its command queue.  Each
  command received is processed to completion  before the next command is
  processed. While in the main loop, the task kicks the watchdog periodically.

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

static void
fs_compat_task
(
  dword         parm            /* from REX - ignored. */
)
{
  rex_sigs_type rex_sigs;

  (void) parm;

  // MSG_MED ("Starting FS Compatibility task", 0, 0, 0);

  rex_def_timer (&fs_compat_rpt_timer, &fs_compat_tcb,
      FS_COMPAT_RPT_TIMER_SIG);
  rex_def_timer (&fs_cache_sync_timer, &fs_compat_tcb,
      FS_COMPAT_CACHE_SYNC_SIG);

  /* Loop forever handling commands. */
  while (1) {
    rex_clr_sigs (&fs_compat_tcb, FS_CMD_Q_SIG);
    fs_compat_process_commands ();

    rex_sigs = rex_wait (FS_COMPAT_RPT_TIMER_SIG |
                         FS_COMPAT_CACHE_SYNC_SIG |
                         FS_CMD_Q_SIG |
                         TASK_STOP_SIG |
                         TASK_OFFLINE_SIG);

    /* Check if we need to kick. */
    if ((rex_sigs & FS_COMPAT_RPT_TIMER_SIG) != 0)
    {
      (void) rex_clr_sigs (&fs_compat_tcb, FS_COMPAT_RPT_TIMER_SIG);
    }

    /* If asked to go offline, acknowledge, and continue. */
    if ((rex_sigs & TASK_OFFLINE_SIG) != 0)
    {
      (void) rex_clr_sigs (&fs_compat_tcb, TASK_OFFLINE_SIG);
#ifdef FEATURE_EFS_FTL
      ftl_cache_force_sync_all ();
#endif
      task_offline ();
    }

    /* If asked to powerdown, acknowledge. */
    if ((rex_sigs & TASK_STOP_SIG) != 0)
    {
      (void) rex_clr_sigs (&fs_compat_tcb, TASK_STOP_SIG);
#ifdef FEATURE_EFS_FTL
      ftl_cache_force_sync_all ();
#endif
      task_stop ();
    }

    /* First let's kick watchdog, sync might take long time */
    FS_COMPAT_KICK_WATCHDOG ();

    if ((rex_sigs & FS_COMPAT_CACHE_SYNC_SIG) != 0)
    {
      (void) rex_clr_sigs (&fs_compat_tcb, FS_COMPAT_CACHE_SYNC_SIG);

#ifdef FEATURE_EFS_FTL
      if (ftl_cache_periodic_sync () != 0)
      {
        rex_set_timer (&fs_cache_sync_timer, FTL_CACHE_SYNC_TIME);
      }
#endif
    }

  }
} /* END fs_compat_task */

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

FUNCTION FS_COMPAT_OPS_CMD

DESCRIPTION
  This is the actual real entry point from EFS.  The EFS calls this
  routine to add an entry to our queue.

===========================================================================*/
void
fs_compat_ops_cmd (fs_cmd_type *cmd_ptr)
{
  /* Set the request status to busy.  Initialize and link the command onto
   * the command queue, send a signal to the compatibility task and return. */

  cmd_ptr->status = FS_BUSY_S;
  cmd_ptr->rsp_msg->open.status = FS_BUSY_S;
  (void) q_link (cmd_ptr, &cmd_ptr->link);
  q_put (&fs_compat_queue, &cmd_ptr->link);

  (void) rex_set_sigs (&fs_compat_tcb, FS_CMD_Q_SIG);

  return;
} /* END fs_compat_ops_cmd */

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

FUNCTION FS_COMPAT_INIT

DESCRIPTION
   Initialization function for this module. This needs to be called
   before the rest of this module is used. Initializes some local data
   structures and starts up the fs_compat task.

===========================================================================*/
void
fs_compat_init (void)
{
  static int init_complete = 0;

  if (!init_complete)
  {
    /* Initialize. */
    (void) q_init (&fs_compat_queue);

    /* Mark the iterator as inactive */
    idata.dir_ptr = NULL;

#ifdef TIMETEST_FS_COMPAT_TASK_ID
    fs_compat_tcb.leds = TIMETEST_FS_COMPAT_TASK_ID;
#endif

    /* Start up the fs_compat task */
    rex_def_task_ext (&fs_compat_tcb,
                      fs_compat_stack,
                      FS_COMPAT_STACK_SIZ,
                      FS_COMPAT_PRI,
                      fs_compat_task,
                      0,
                      "FS Compat Task",
                      FALSE);
    init_complete = 1;
  }
  return;
} /* END fs_compat_init */


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

FUNCTION FS_START_FTL_SYNC_TIMER

DESCRIPTION
   Start the FTL cache sync timer to poll to check if no write activity
   is found since last poll, it can sync the cache contents into flash.

===========================================================================*/
void fs_start_ftl_sync_timer (void)
{
  rex_set_timer (&fs_cache_sync_timer, FTL_CACHE_SYNC_TIME);
}

#endif /* FEATURE_EFS_ACCESS_METHODS */

#endif /* FEATURE_EFS_COMPATIBILITY */