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


/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====
 *
 *                 Diagnostics Packet Processing
 *
 *
 *  Copyright (C) 2002--2006  QUALCOMM, Incorporated.
 *  All Rights Reserved.
 *
 *  Diagnostic packet processing routines for EFS2
 *
 *
 *====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/


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

                           Edit History

  $Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_diag.c#46 $


when       who     what, where, why
--------   ---     ----------------------------------------------------------
10/26/06    sh     Featurize CEFS Factory Image code (security risk).
10/26/06    sh     Perform Access Validation against READ/WRITE during OPEN.
10/10/06    sh     Allow target conf to override EFS Subsystem ID
09/19/06    sh     Rename all instances of response errno to diag_errno.
07/12/06    sh     Allow target to select processor for FS Diag subsys.
06/29/06    sh     Remove mistaken PACKED.
06/24/06    sh     Replace request->path with a non-__packed ptr.
06/23/06    sh     Fix benchmark packets so they can be built.
06/22/06   dlb     Validate paths for nul termination, for all paths.
06/20/06   dlb     Fix fstat handler for new descriptors.
06/19/06   dlb     Get max rx/tx packets from diagtune.h
06/14/06   dlb     Cleanup of operations involving handles or descriptors.
06/14/06   dlb     Change rsp/req to response/request for clarity.
06/09/06   dlb     More major cleanup and fixing: open, write, read.
06/08/06    sh     Moved fs_diag_efs2_error_rsp into fs_diag.h
05/16/06   dlb     Updates to compile again off target.
05/15/06    sh     Skip any directory entry that errors lstat.
05/08/06    sh     Fixed RVCT warning about packed path
05/07/06    sh     Removed superfluous break/return statements.
04/13/06    sh     Corrected case indenting
03/31/06    sh     Lint cleanup
03/28/06    sh     Removed all EFS1 calls from fs_diag, using only EFS2 now.
02/22/06   nrs     Convert pointers to correct type in new Q&R diag packets
02/06/06    sh     Correct diag seek to use the right file handle
02/01/06    sh     Correcting diag use of stdio
01/26/06    sh     Added packets for chown, and get/set reservation/quota.
12/12/05    sh     Lint cleanup.
12/12/05   nrs     Bug write_handler causes infinite loop
11/28/05   nrs     Merge 7500 changes
11/01/05    sh     Added fs_benchmark handlers
11/01/05   nrs     Fix bug for when MMC is in the build, but not USB
10/31/05   nrs     Fixed USB support in fs_statfs causing checks on MMC
10/04/05   nrs     Initialize file lookup table for stdio interface
10/12/05    sh     Removed some Access Validation checks (READ,WRITE,etc)
10/12/05    sh     Added ZAP macros for debug messages (disabled by default)
10/12/05    sh     Corrected error value for MMC iterator busy.
08/25/05    sh     Featurized stdio interface for MSM7500
08/19/05    sh     Fixed typo '==' instead of assignment.
08/08/05   sch     Added USB support in fs_diag_test_mmc_path
08/05/05    sh     Fix close_status warning in fs_diag_close_handler
08/01/05    sh     fs_diag_open_handler() needs to detect efs_fopen() failure
06/22/05   dlb     Add extended info query packet.
04/27/05   nrs     Re-fix uninitialized close status.
04/26/05   dlb     Fix uninitialized close status.
04/22/05   nrs     Explicitly set close response pointer to 0 in
                   fs_diag_close_handler on success.
04/14/05   nrs     Fix access permission checks in fs_diag_open_handler()
04/07/05   nrs     Implemented write buffering
03/22/05   dlb     Fix RVCT error in efs_get.
03/07/05   nrs     Ignore irrelevant lint warnings
03/04/05   as      Moved validation check before allocating close rsp pkt.
03/03/05   nrs     Fixed lint errors
02/24/05   nrs     Added Generic EFS2 Diag Error packet
02/15/05   nrs     Removed bytes written from put response packet.
02/10/05   nrs     Added packet structures for put and get
01/31/04   sch     Fixed the problem of overwriting an existing file in MMC
12/07/04   nrs     Added temporary fix for delays using QPST
10/14/04   nrs     Fixed leading slash compatability issue from EFS1
10/07/04   dlb     Whitespace cleanup.
06/23/04    gr     Changes to keep the RVCT 2.1 compiler happy.
06/15/04    gr     Disabled security check in the unit test environment.
05/18/04    as     Added security check to fs diag packets.
05/18/04    gr     Fixes for compiler warnings.
05/18/04    gr     Fixed some bad formatting of code.
05/17/04    gr     Set the fstype field correctly in the statfs response.
04/08/04    gr     Modifications to not attempt an MMC enumeration if an MMC
                   card is not present.
10/17/03    gr     Modifications to get this module to work on target as well
                   as in the unit test framework.
09/30/03    pg     Added fs_diag_get_fs_errno to convert efs1 error code to
                   efs2 error code.
                   Modified all handler to return more accurate error codes
                   from MMC operations.
09/14/03   adm     Added support for efs_image_prepare function
08/27/03    gr     Changes to get rid of warnings and errors produced by the
                   RVCT 2.0 compiler.
07/16/03    gr     Moved access validation code into its own file.
07/03/03   spf     Empty directory returned instead of error when MMC/SD card
                   not present.
06/17/03   jkl     Clean up code.
06/16/03   spf     Added factory image support.
                   Minor code cleanup.
06/12/03    gr     Added support for validating access to files and
                   directories.
05/30/03   spf     Added MMC support using EFS1 interface.
04/03/03    gr     Modified the mkdir and rmdir handlers to ignore trailing
                   slashes. This keeps the tools happy.
10/25/02    gr     Created
===========================================================================*/

#include "comdef.h"
#include "customer.h"
#include "fs_util.h"
#include "diagbuf.h"
#include "diagcmd.h"
#include "diagpkt.h"
#include "diagtune.h"
#include "fs_diag.h"
#include "fs_err.h"
#include "fs_efs2.h"
#include "fs_stdio.h"
#include "fs_benchmark.h"
#include "msg.h"
#include "assert.h"

#ifdef FEATURE_EFS2

#include 
#include 
#include "fs_public.h"
#include "fs_errno.h"

/*
 * The diag packets frequently contain a pathname as the last field.
 * This is listed in the structure as char foo[1], while the variable-length
 * pathname extends past the end of the structure.  Not surprisingly, lint
 * sees this as:
 * Error (Warning) 661: Possible access of out-of-bounds pointer
 * (1 beyond end of data) by operator '['
 * This is intentional, so the warning is suppressed here.
 */
/*lint -e661*/

#include "diag.h"

#include "jzap.h"

/*lint -e661 Ignore irrelevant lint errors */

/* -------------------------------------------------------------------------
** Definitions
** ------------------------------------------------------------------------- */

/* External functions for factory image output */
extern int efs_get_fs_data (struct fs_factimage_read_info *image_info_ptr,
                            byte *page_buffer);
extern int efs_get_device_info (struct fs_device_attr *dev_attr);

typedef struct {
  uint32 targ_pkt_window;       /* Target window size in packets            */
  uint32 targ_byte_window;      /* Target window size in bytes              */
  uint32 host_pkt_window;       /* Host window size in packets              */
  uint32 host_byte_window;      /* Host window size in bytes                */
  uint32 iter_pkt_window;       /* Dir iteration window size in packets     */
  uint32 iter_byte_window;      /* Dir iteration window size in bytes       */
  int32  version;               /* Protocol version number                  */
  int32  min_version;           /* Minimum supported protocol version       */
  int32  max_version;           /* Maximum supported protocol version       */
  int32  feature_bits;          /* Bit mask of supported features           */
} fs_diag_params_type;

/* The maximum number of file descriptors open at any time
*/
#define FS_DIAG_MAX_FDS  6

/* The maximum number of directories open for reading at any time
*/
#define FS_DIAG_MAX_OPEN_DIRS  4

PACKED void * fs_diag_hello_handler (PACKED void *, uint16);
PACKED void * fs_diag_query_handler (PACKED void *, uint16);
PACKED void * fs_diag_open_handler (PACKED void *, uint16);
PACKED void * fs_diag_close_handler (PACKED void *, uint16);
PACKED void * fs_diag_read_handler (PACKED void *, uint16);
PACKED void * fs_diag_write_handler (PACKED void *, uint16);
PACKED void * fs_diag_symlink_handler (PACKED void *, uint16);
PACKED void * fs_diag_readlink_handler (PACKED void *, uint16);
PACKED void * fs_diag_unlink_handler (PACKED void *, uint16);
PACKED void * fs_diag_mkdir_handler (PACKED void *, uint16);
PACKED void * fs_diag_rmdir_handler (PACKED void *, uint16);
PACKED void * fs_diag_opendir_handler (PACKED void *, uint16);
PACKED void * fs_diag_readdir_handler (PACKED void *, uint16);
PACKED void * fs_diag_closedir_handler (PACKED void *, uint16);
PACKED void * fs_diag_rename_handler (PACKED void *, uint16);
PACKED void * fs_diag_stat_handler (PACKED void *, uint16);
PACKED void * fs_diag_lstat_handler (PACKED void *, uint16);
PACKED void * fs_diag_fstat_handler (PACKED void *, uint16);
PACKED void * fs_diag_chmod_handler (PACKED void *, uint16);
PACKED void * fs_diag_statfs_handler (PACKED void *, uint16);
PACKED void * fs_diag_access_handler (PACKED void *, uint16);
PACKED void * fs_diag_device_info_handler (PACKED void *, uint16);
PACKED void * fs_diag_factimage_start_handler (PACKED void *, uint16);
PACKED void * fs_diag_factimage_read_handler (PACKED void *, uint16);
PACKED void * fs_diag_factimage_end_handler (PACKED void *, uint16);
PACKED void * fs_diag_prep_factimage_handler (PACKED void *, uint16);
PACKED void * fs_diag_put_handler (PACKED void *, uint16);
PACKED void * fs_diag_get_handler (PACKED void *, uint16);
PACKED void * fs_diag_extended_info_handler (PACKED void *, uint16);
PACKED void * fs_diag_benchmark_start_test_handler (PACKED void *, uint16);
PACKED void * fs_diag_benchmark_get_results_handler (PACKED void *, uint16);
PACKED void * fs_diag_benchmark_init_handler (PACKED void *, uint16);
PACKED void * fs_diag_chown_handler (PACKED void *, uint16);
PACKED void * fs_diag_set_reservation_handler (PACKED void *, uint16);
PACKED void * fs_diag_set_quota_handler (PACKED void *, uint16);
PACKED void * fs_diag_get_group_info_handler (PACKED void *, uint16);

/* -------------------------------------------------------------------------
** Local Data
** ------------------------------------------------------------------------- */
static fs_diag_params_type fs_diag_params;
static const fs_diag_params_type fs_diag_default_params = {
  FS_TARG_PKT_WINDOW_DEFAULT,
  FS_TARG_BYTE_WINDOW_DEFAULT,
  FS_HOST_PKT_WINDOW_DEFAULT,
  FS_HOST_BYTE_WINDOW_DEFAULT,
  FS_ITER_PKT_WINDOW_DEFAULT,
  FS_ITER_BYTE_WINDOW_DEFAULT,
  FS_DIAG_VERSION,
  FS_DIAG_MIN_VERSION,
  FS_DIAG_MAX_VERSION,
  FS_FEATURE_BITS
};

/* The descriptor used external to diag is merely an index into this array,
 * not a real EFS2 descriptor.  We only allow access by diag to files that
 * were opened through diag.  For each opened descriptor, there is a single
 * element here for this.  There will always be an EFS2 descriptor
 * associated with each open file, and that will sometimes be associated
 * with a buffered EFS_FILE pointer.  A -1 for the fd field indicates that
 * this entry is unused. */
typedef struct {
  int       fd;
  EFS_FILE *file;
} desc_entry;
static desc_entry fsd_desc_table[FS_DIAG_MAX_FDS];

/* Directory 'dirp' descriptors are entries into this table.  NULL entries
 * indicate a directory that is not open.  Because the protocol defines a
 * 'dirp' of 0 as an error condition, we access this table with an offset
 * of 1. */
typedef struct {
  EFSDIR        *dirp;
  int32         seqno;
  char          path[FS_PATH_MAX+1];
} dir_table_entry;
static dir_table_entry fsd_dir_table[FS_DIAG_MAX_OPEN_DIRS];

/* The dispatch table for the EFS subsystem.
*/
static const diagpkt_user_table_entry_type fs_diag_tbl[] =
{
  {EFS2_DIAG_HELLO,    EFS2_DIAG_HELLO,    fs_diag_hello_handler},
  {EFS2_DIAG_QUERY,    EFS2_DIAG_QUERY,    fs_diag_query_handler},
  {EFS2_DIAG_OPEN,     EFS2_DIAG_OPEN,     fs_diag_open_handler},
  {EFS2_DIAG_CLOSE,    EFS2_DIAG_CLOSE,    fs_diag_close_handler},
  {EFS2_DIAG_READ,     EFS2_DIAG_READ,     fs_diag_read_handler},
  {EFS2_DIAG_WRITE,    EFS2_DIAG_WRITE,    fs_diag_write_handler},
  {EFS2_DIAG_SYMLINK,  EFS2_DIAG_SYMLINK,  fs_diag_symlink_handler},
  {EFS2_DIAG_READLINK, EFS2_DIAG_READLINK, fs_diag_readlink_handler},
  {EFS2_DIAG_UNLINK,   EFS2_DIAG_UNLINK,   fs_diag_unlink_handler},
  {EFS2_DIAG_MKDIR,    EFS2_DIAG_MKDIR,    fs_diag_mkdir_handler},
  {EFS2_DIAG_RMDIR,    EFS2_DIAG_RMDIR,    fs_diag_rmdir_handler},
  {EFS2_DIAG_OPENDIR,  EFS2_DIAG_OPENDIR,  fs_diag_opendir_handler},
  {EFS2_DIAG_READDIR,  EFS2_DIAG_READDIR,  fs_diag_readdir_handler},
  {EFS2_DIAG_CLOSEDIR, EFS2_DIAG_CLOSEDIR, fs_diag_closedir_handler},
  {EFS2_DIAG_RENAME,   EFS2_DIAG_RENAME,   fs_diag_rename_handler},
  {EFS2_DIAG_STAT,     EFS2_DIAG_STAT,     fs_diag_stat_handler},
  {EFS2_DIAG_LSTAT,    EFS2_DIAG_LSTAT,    fs_diag_lstat_handler},
  {EFS2_DIAG_FSTAT,    EFS2_DIAG_FSTAT,    fs_diag_fstat_handler},
  {EFS2_DIAG_CHMOD,    EFS2_DIAG_CHMOD,    fs_diag_chmod_handler},
  {EFS2_DIAG_STATFS,   EFS2_DIAG_STATFS,   fs_diag_statfs_handler},
  {EFS2_DIAG_ACCESS,   EFS2_DIAG_ACCESS,   fs_diag_access_handler},
  {EFS2_DIAG_NAND_DEV_INFO,
   EFS2_DIAG_NAND_DEV_INFO,
   fs_diag_device_info_handler},
#ifdef FEATURE_EFS_ENABLE_FACTORY_IMAGE_SECURITY_HOLE
  {EFS2_DIAG_FACT_IMAGE_START,
   EFS2_DIAG_FACT_IMAGE_START,
   fs_diag_factimage_start_handler},
  {EFS2_DIAG_FACT_IMAGE_READ,
   EFS2_DIAG_FACT_IMAGE_READ,
   fs_diag_factimage_read_handler},
  {EFS2_DIAG_FACT_IMAGE_END,
   EFS2_DIAG_FACT_IMAGE_END,
   fs_diag_factimage_end_handler},
  {EFS2_DIAG_PREP_FACT_IMAGE,
   EFS2_DIAG_PREP_FACT_IMAGE,
   fs_diag_prep_factimage_handler},
#endif /* FEATURE_EFS_ENABLE_FACTORY_IMAGE_SECURITY_HOLE */
  {EFS2_DIAG_PUT,      EFS2_DIAG_PUT,      fs_diag_put_handler},
  {EFS2_DIAG_GET,      EFS2_DIAG_GET,      fs_diag_get_handler},
  { EFS2_DIAG_EXTENDED_INFO,
    EFS2_DIAG_EXTENDED_INFO,
    fs_diag_extended_info_handler },
  {EFS2_DIAG_BENCHMARK_START_TEST,
   EFS2_DIAG_BENCHMARK_START_TEST,
   fs_diag_benchmark_start_test_handler},
  {EFS2_DIAG_BENCHMARK_GET_RESULTS,
   EFS2_DIAG_BENCHMARK_GET_RESULTS,
   fs_diag_benchmark_get_results_handler},
  {EFS2_DIAG_BENCHMARK_INIT,
   EFS2_DIAG_BENCHMARK_INIT,
   fs_diag_benchmark_init_handler},
  {EFS2_DIAG_CHOWN,
   EFS2_DIAG_CHOWN,
   fs_diag_chown_handler},
  {EFS2_DIAG_SET_RESERVATION,
   EFS2_DIAG_SET_RESERVATION,
   fs_diag_set_reservation_handler},
  {EFS2_DIAG_SET_QUOTA,
   EFS2_DIAG_SET_QUOTA,
   fs_diag_set_quota_handler},
  {EFS2_DIAG_GET_GROUP_INFO,
   EFS2_DIAG_GET_GROUP_INFO,
   fs_diag_get_group_info_handler},
};

/* The dispatch table can be registered before runtime if this file is
** being built as a C++ file.
*/
#ifdef __cplusplus
  DIAGPKT_DISPATCH_AUTOREGISTER (DIAG_SUBSYS_FS, fs_diag_tbl);
#endif

/**********************************************************************
 * Packet memoization.
 *
 * The diag protocol is not robust, and therefore clients may retransmit
 * packets that were not acknowledged.  Unfortunately, this is not hidden
 * from us as it would be in a reasonable protocol.  Since many of the diag
 * EFS2 requests are not idempotent, it is necessary to detect these
 * retransmissions and return the same response, without redoing the work
 * itself.
 **********************************************************************/

/* The size of the largest possible request packet.  */
#define LARGEST_DIAG_PACKET     DIAG_MAX_RX_PKT_SIZ
#define LARGEST_DIAG_RESPONSE   DIAG_MAX_TX_PKT_SIZ

static char fsd_last_packet[LARGEST_DIAG_PACKET];
static unsigned fsd_last_packet_len;
static char fsd_last_response[LARGEST_DIAG_RESPONSE];
static unsigned fsd_last_response_len;

/* Memoize this packet and response. */
static void fsd_memoize_packet (PACKED const void *request, unsigned req_len,
    PACKED const void *response, unsigned rsp_len);

/* Invalidate the last memoized packet.  Used for requests that are
 * idempotent to prevent memoization. */
static void fsd_invalidate_memoization (void);

/* Check if this request is the same as a previous one.  Will allocate a
 * response and return a copy of that last response.  Note that this macro
 * has a 'return' in it that will return a result if no work should be
 * done. */
#define FSD_MEMOIZED_PACKET_CHECK(pkt, len, code) \
  do { \
    if ((len) == fsd_last_packet_len && \
        memcmp ((void *)(pkt), fsd_last_packet, fsd_last_packet_len) == 0) \
    { \
      PACKED void *__rsp = diagpkt_subsys_alloc (DIAG_SUBSYS_FS, \
          (code), fsd_last_response_len); \
      if (__rsp != NULL) \
        memcpy ((void *) __rsp, fsd_last_response, fsd_last_response_len); \
      return __rsp; \
    } \
  } while (0)

static void
fsd_memoize_packet (PACKED const void *request, unsigned req_len,
    PACKED const void *response, unsigned rsp_len)
{
  /* If we somehow get too large of a packet or response, don't remember
   * it. */
  if (req_len > LARGEST_DIAG_PACKET ||
      rsp_len > LARGEST_DIAG_RESPONSE)
  {
    fsd_invalidate_memoization ();
  } else {
    memcpy (fsd_last_packet, (void *) request, req_len);
    fsd_last_packet_len = req_len;

    memcpy (fsd_last_response, (void *) response, rsp_len);
    fsd_last_response_len = rsp_len;
  }
}

/* Invalidate the last memoized packet.  Used for requests that are
 * idempotent to prevent memoization. */
static void
fsd_invalidate_memoization (void)
{
  fsd_last_packet_len = 0;
  fsd_last_response_len = 0;
}

/**********************************************************************
 * Packet pathname validation.
 *********************************************************************/

/* Count the number of nul characters in the given string. */
static unsigned
fsd_count_nulls (PACKED char *name, unsigned length)
{
  unsigned i;
  unsigned result = 0;

  for (i = 0; i < length; i++) {
    if (name[i] == '\0')
      result++;
  }

  return result;
}

/* Convenience macro for checking pathnames.  Takes its argument, and
 * verifies that it contains at least as many nulls.  Arguments are:
 *   request - The typed request packet.
 *   type    - The type of the request packet.
 *   field   - The name of the field containing the pathname
 *   req_len - The total length of the packet
 *   min_nul - Miminum number of nul characters in the string.
 * This macro contains a return, and will return a diag error packet
 * indicating a bad parameter if the request doesn't contain sufficient
 * nulls. */
#define FSD_CHECK_NULLS(_req, _type, _field, _req_len, _min_nul) \
  do { \
    if (fsd_count_nulls ((_req)->_field, \
          (_req_len) - FPOS (_type, _field)) < (_min_nul)) \
    { \
      return diagpkt_err_rsp (DIAG_BAD_PARM_F, \
          (PACKED void *) (_req), (_req_len)); \
    } \
  } while (0)

/**********************************************************************
 * Close all open handles.  Used to reset everything when a new connection
 * is established.
 *********************************************************************/

/* Close a single slot.  Returns zero on success or -1 on error (the result
 * of either efs_close or efs_fclose). */
static int
fsd_close_slot (unsigned slot)
{
  int result;

  ASSERT (slot < FS_DIAG_MAX_FDS);
  ASSERT (fsd_desc_table[slot].fd >= 0);

  if (fsd_desc_table[slot].file != NULL) {
    result = efs_fclose (fsd_desc_table[slot].file);
    /* efs_fclose also calls efs_close, so that isn't needed. */
  } else {
    result = efs_close (fsd_desc_table[slot].fd);
  }

  fsd_desc_table[slot].file = NULL;
  fsd_desc_table[slot].fd = -1;

  return result;
}

static void
fsd_close_open_handles (void)
{
  unsigned slot;

  for (slot = 0; slot < FS_DIAG_MAX_FDS; slot++) {
    if (fsd_desc_table[slot].fd >= 0)
      (void) fsd_close_slot (slot);
  }
}

static void
fsd_close_open_dirs (void)
{
  unsigned slot;

  for (slot = 0; slot < FS_DIAG_MAX_OPEN_DIRS; slot++) {
    if (fsd_dir_table[slot].dirp != NULL) {
      (void) efs_closedir (fsd_dir_table[slot].dirp);
      fsd_dir_table[slot].dirp = NULL;
    }
  }
}

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

FUNCTION FS_DIAG_INIT
DESCRIPTION
  Initialization function for this module. Registers the packet handlers
  and sets up local data structures.
============================================================================*/
void fs_diag_init (void)
{
  unsigned i;

  fs_diag_params = fs_diag_default_params;

  for (i = 0; i < FS_DIAG_MAX_FDS; i++) {
    fsd_desc_table[i].fd = -1;
    fsd_desc_table[i].file = NULL;
  }

  for (i = 0; i < FS_DIAG_MAX_OPEN_DIRS; i++) {
    fsd_dir_table[i].dirp = NULL;
    fsd_dir_table[i].seqno = 0;
  }

  /* --- Register our EFS Diag Subsystem handler. --- */
#ifndef __cplusplus

  /* By default, processor ID "0" will handle the calls.  A build may
     optionally override this. */
#ifndef EFS_DIAG_SUBSYS_PROCESSOR_ID
#define EFS_DIAG_SUBSYS_PROCESSOR_ID 0
#endif

  /* By default, we use the well-defined EFS Subsystem ID (DIAG_SUBSYS_FS)
   * for all EFS diag commands.
   *
   * On multi-processor builds, the build may want to use an alternate
   * EFS Subsystem ID to address the second (slave) processor.  We
   * allow the build configuration to override the EFS Subsystem ID
   * here, by declaring EFS_DIAG_USE_THIS_SUBSYS_ID externally.  If
   * none is defined, we use the well-known value as our default. */
#ifndef EFS_DIAG_USE_THIS_SUBSYS_ID
#define EFS_DIAG_USE_THIS_SUBSYS_ID DIAG_SUBSYS_FS
#endif

  /* On builds using processor 0, there may not exist the new DIAG
     macro to register with a processor number, so we use the old
     macro to be safe.  Nonzero ID always requires the new macro. */
#if EFS_DIAG_SUBSYS_PROCESSOR_ID == 0
  DIAGPKT_DISPATCH_TABLE_REGISTER (EFS_DIAG_USE_THIS_SUBSYS_ID, fs_diag_tbl);
#else
  DIAGPKT_DISPATCH_TABLE_REGISTER_PROC (EFS_DIAG_SUBSYS_PROCESSOR_ID,
                                        EFS_DIAG_USE_THIS_SUBSYS_ID,
                                        fs_diag_tbl);
#endif
#endif /* ! __cplusplus */

  return;
} /* END fs_diag_init */


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

FUNCTION FS_DIAG_HELLO_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_HELLO packet. Initializes the EFS2 Diag interface
  and passes the values of some parameters back to the tool.
============================================================================*/
PACKED void *
fs_diag_hello_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_hello_req_type *request;
  efs2_diag_hello_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_hello_rsp_type);

  ZAP ("_HELLO");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_hello_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_hello_req_type *) req_ptr;

  response = (efs2_diag_hello_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_HELLO,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Initialize this module: close any open handles. */
  fsd_close_open_handles ();
  fsd_close_open_dirs ();

  /*------------------------------------------------------
    Fill in the fields in the response packet
    ------------------------------------------------------*/
  response->targ_pkt_window  = fs_diag_params.targ_pkt_window;
  response->targ_byte_window = fs_diag_params.targ_byte_window;
  response->host_pkt_window  = fs_diag_params.host_pkt_window;
  response->host_byte_window = fs_diag_params.host_byte_window;
  response->iter_pkt_window  = fs_diag_params.iter_pkt_window;
  response->iter_byte_window = fs_diag_params.iter_byte_window;
  response->version          = fs_diag_params.version;
  response->min_version      = fs_diag_params.min_version;
  response->max_version      = fs_diag_params.max_version;
  response->feature_bits     = fs_diag_params.feature_bits;

  /* Update relevant parameters using values specified in the request
   * packet.  _EVERY_ single one of these fields is completely meaningless.
   * Why are we doing any of this?
  */
  if (fs_diag_params.targ_pkt_window > request->targ_pkt_window)
    fs_diag_params.targ_pkt_window = request->targ_pkt_window;
  if (fs_diag_params.targ_byte_window > request->targ_byte_window)
    fs_diag_params.targ_byte_window = request->targ_byte_window;
  if (fs_diag_params.host_pkt_window > request->host_pkt_window)
    fs_diag_params.host_pkt_window = request->host_pkt_window;
  if (fs_diag_params.host_byte_window > request->host_byte_window)
    fs_diag_params.host_byte_window = request->host_byte_window;
  if (fs_diag_params.iter_pkt_window > request->iter_pkt_window)
    fs_diag_params.iter_pkt_window = request->iter_pkt_window;
  if (fs_diag_params.iter_byte_window > request->iter_byte_window)
    fs_diag_params.iter_byte_window = request->iter_byte_window;
  fs_diag_params.version       = request->version;
  fs_diag_params.min_version   = request->min_version;
  fs_diag_params.max_version   = request->max_version;
  fs_diag_params.feature_bits &= request->feature_bits;

  return response;

} /* fs_diag_hello_handler */


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

FUNCTION FS_DIAG_QUERY_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_QUERY packet. Passes back information about some
  ESF2 parameters.
============================================================================*/
PACKED void *
fs_diag_query_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_query_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_query_rsp_type);

  ZAP ("_QUERY");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_query_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  response = (efs2_diag_query_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_QUERY,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /*------------------------------------------------------
    Fill in the fields in the response packet
    ------------------------------------------------------*/
  response->max_name         = FS_NAME_MAX;
  response->max_path         = FS_PATH_MAX;
  response->max_link_depth   = 0;
  response->max_file_size    = 0;
  response->max_dir_entries  = FS_DIR_ENTRY_SIZE;
  response->max_mounts       = FS_MAX_MOUNTS;

  return response;

} /* fs_diag_query_handler */


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

FUNCTION FS_DIAG_OPEN_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_OPEN packet.
============================================================================*/
PACKED void *
fs_diag_open_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_open_req_type *request;
  efs2_diag_open_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_open_rsp_type);
  const char *request_path;
  int32  fd;
  unsigned slot;

#ifndef FEATURE_NO_STDIO_FROM_DIAG
  char *buff_mode = "";
#endif

  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len);
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_OPEN);

  if (pkt_len < sizeof (efs2_diag_query_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_open_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_open_req_type, path, pkt_len, 1);

  ZPRINTF ("_OPEN: %s\n\r", request_path);
  FS_DIAG_VALIDATE_PATH_ACCESS (OPEN, request_path);

  /* Old versions of fs_diag would perform Access Validation for each
   * and every Read/Write request, but this was incurring a
   * significant performance penalty.  Later (Oct 2005), these checks
   * were removed entirely from Read/Write packet handlers, which
   * violated some assumptions made by BREW SPAR.  Now we do one test
   * of READ and one test of WRITE during open to confirm that the
   * file mode is acceptable.  If any of these read/write permissions
   * are denied, then the file can not be opened. */
  switch (request->oflag & O_ACCMODE) {
    case O_RDONLY:
      FS_DIAG_VALIDATE_PATH_ACCESS (READ, request_path);
      break;

    case O_WRONLY:
      FS_DIAG_VALIDATE_PATH_ACCESS (WRITE, request_path);
      break;

    case O_RDWR:
    default:
      FS_DIAG_VALIDATE_PATH_ACCESS (READ, request_path);
      FS_DIAG_VALIDATE_PATH_ACCESS (WRITE, request_path);
      break;
  }

  response = (efs2_diag_open_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_OPEN,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Find a descriptor slot to use. */
  for (slot = 0; slot < FS_DIAG_MAX_FDS; slot++) {
    if (fsd_desc_table[slot].fd == -1)
      break;
  }

  /* If we are out of diag descriptors, don't try opening, just return the
   * error. */
  if (slot >= FS_DIAG_MAX_FDS) {
    response->fd = -1;
    response->diag_errno = FS_DIAG_TOO_MANY_OPEN_FILES;
    goto open_end;
  }

  /* Open the handle. */
  fd = efs_open (request_path, request->oflag, request->mode);

  /* Check for open failure. */
  if (fd < 0) {
    response->fd = -1;
    response->diag_errno = efs_errno;
    goto open_end;
  }

  /* Update the desc slot. */
  fsd_desc_table[slot].fd = fd;
  fsd_desc_table[slot].file = NULL;

  response->fd = slot;
  response->diag_errno = 0;

  goto open_end;

#ifndef FEATURE_NO_STDIO_FROM_DIAG
  /* Open the stdio handle to use for reading and writing.  Determining the
   * mode from the oflags. */
  switch (request->mode & O_ACCMODE) {
    case O_RDONLY:
      buff_mode = "r";
      break;
    case O_WRONLY:
      buff_mode = "w";
      break;
    case O_RDWR:
    default:
      buff_mode = "r+";
      break;
  }

  fsd_desc_table[slot].file = efs_fdopen (fd, buff_mode);
  /* If we didn't get a handle, the file pointer in the slot will be NULL,
   * and the unbuffered operations will be used.  TODO: log a warning about
   * this? */
#endif /* FEATURE_NO_STDIO_FROM_DIAG */

  open_end:
  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;

} /* END fs_diag_open_handler */


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

FUNCTION FS_DIAG_CLOSE_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_CLOSE packet.

============================================================================*/
PACKED void *
fs_diag_close_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_close_req_type *request;
  efs2_diag_close_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_close_rsp_type);
  int result;

  ZAP ("_CLOSE");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_CLOSE);

  if (pkt_len != sizeof (efs2_diag_close_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_close_req_type *) req_ptr;

  response = (efs2_diag_close_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_CLOSE,
                          rsp_len);
  if (response == NULL)
    return NULL;

  response->diag_errno = 0;

  /* If this handle is not open, or is out of range, return an error. */
  if (request->fd < 0 || request->fd >= FS_DIAG_MAX_FDS ||
      fsd_desc_table[request->fd].fd < 0)
  {
    response->diag_errno = EBADF;
    goto close_done;
  }

  result = fsd_close_slot (request->fd);

  if (result != 0)
    response->diag_errno = efs_errno;

  close_done:
  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;
} /* END fs_diag_close_handler */


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

FUNCTION FS_DIAG_READ_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_READ packet.

============================================================================*/
PACKED void *
fs_diag_read_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_read_req_type *request;
  efs2_diag_read_rsp_type *response;
  desc_entry *slot;
  unsigned base_length = FPOS (efs2_diag_read_rsp_type, data);
  unsigned rsp_len = base_length;
  fs_ssize_t bytes_left;
  fs_off_t   offset;
  fs_ssize_t count;

  ZAP ("_READ");
  fsd_invalidate_memoization ();

  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_read_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_read_req_type *) req_ptr;

  /* Validate the requested descriptor. */
  if (request->fd < 0 || request->fd >= FS_DIAG_MAX_FDS ||
      fsd_desc_table[request->fd].fd < 0)
  {
    response = (efs2_diag_read_rsp_type *)
      diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
          EFS2_DIAG_READ, base_length);
    response->fd = request->fd;
    response->offset = request->fd;
    response->bytes_read = -1;
    response->diag_errno = EBADF;
    goto read_end;
  }

  slot = &fsd_desc_table[request->fd];

  /* Begin by allocating a full-sized read packet.  We will shorten it if
   * we get an error, or a short read. */
  bytes_left = request->nbyte;
  if (bytes_left > FS_DIAG_MAX_READ_REQ)
    bytes_left = FS_DIAG_MAX_READ_REQ;

  rsp_len += bytes_left;
  response = (efs2_diag_read_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS, EFS2_DIAG_READ, rsp_len);

  response->fd = request->fd;
  response->offset = request->offset;

  /* Seek to the specified location, and attempt the read. */
  if (slot->file == NULL) {
    /* Unbuffered. */
    offset = efs_lseek (slot->fd, request->offset, SEEK_SET);
    if (offset != (fs_off_t) request->offset) {
      response->bytes_read = -1;
      response->diag_errno = efs_errno;
      diagpkt_shorten (response, base_length);
      goto read_end;
    }

    count = efs_read (slot->fd, (void *) response->data, bytes_left);
  } else {
    if (0 != efs_fseek (slot->file, request->offset, SEEK_SET)) {
      response->bytes_read = -1;
      response->diag_errno = efs_errno;
      diagpkt_shorten (response, base_length);
      goto read_end;
    }

    count = efs_fread ((void *) response->data, 1, bytes_left, slot->file);
  }

  /* Fixup the response packet, depending on the result. */
  if (count < 0) {
    response->bytes_read = -1;
    response->diag_errno = efs_errno;
  } else {
    response->bytes_read = count;
    response->diag_errno = 0;
    if (count < bytes_left)
      diagpkt_shorten (response, base_length + count);
  }

  read_end:
  return response;
} /* END fs_diag_read_handler */


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

FUNCTION FS_DIAG_WRITE_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_WRITE packet.

============================================================================*/
PACKED void *
fs_diag_write_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_write_req_type *request;
  efs2_diag_write_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_write_rsp_type);
  fs_off_t   offset;
  fs_ssize_t nbyte;
  fs_ssize_t bytes_written = 0;
  fs_ssize_t bytes_left;
  char      *write_locn;
  desc_entry *slot;

  ZAP ("_WRITE");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_write_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_write_req_type *) req_ptr;

  response = (efs2_diag_write_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_WRITE,
                          rsp_len);
  if (response == NULL)
    return NULL;

  response->fd     = request->fd;
  response->offset = request->offset;

  nbyte = (fs_ssize_t)
    (pkt_len - (uint16) FPOS (efs2_diag_write_req_type, data));

  if (request->fd < 0 || request->fd >= FS_DIAG_MAX_FDS ||
      fsd_desc_table[request->fd].fd < 0)
  {
    response->diag_errno = EBADF;
    response->bytes_written = 0;
    goto write_end;
  }

  slot = &fsd_desc_table[request->fd];

  /* Seek to the specified offset and write the specified number of bytes. */

  if (slot->file == NULL) {
    /* Unbuffered */
    offset = efs_lseek (slot->fd, request->offset, SEEK_SET);
    if (offset != (fs_off_t) request->offset) {
      response->bytes_written = -1;
      response->diag_errno         = efs_errno;
      goto write_end;
    }
  } else {
    /* Buffered */
    if (0 != efs_fseek (slot->file, request->offset,
                        SEEK_SET)) {
      response->bytes_written = -1;
      response->diag_errno         = efs_errno;
      goto write_end;
    }
  }

  offset = (fs_off_t) request->offset;
  bytes_left   = nbyte;
  write_locn   = (char *) request->data;

  while (bytes_left > 0) {
    if (slot->file == NULL) {
      bytes_written = efs_write (slot->fd, (void *) write_locn, bytes_left);
    } else {
      bytes_written = efs_fwrite ((void *) write_locn, 1, bytes_left,
          slot->file);
    }

    if (bytes_written <= 0) {
      /* XXX: This is an incorrect response, but I suspect at this point
       * there are external tools that depend on this behavior. */
      response->bytes_written = -1;
      response->diag_errno         = efs_errno;
      goto write_end;
    }

    bytes_left -= bytes_written;
    write_locn += bytes_written;
  }

  /* (bytes_written >= 0) when it gets here */
  response->bytes_written = nbyte;
  response->diag_errno         = 0;

  write_end:
  return response;
} /* END fs_diag_write_handler */


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

FUNCTION FS_DIAG_SYMLINK_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_SYMLINK packet.

============================================================================*/
PACKED void *
fs_diag_symlink_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_symlink_req_type *request;
  efs2_diag_symlink_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_symlink_rsp_type);
  const char *oldpath;
  const char *newpath;
  int32  oldpath_len;
  int32  symlink_result;

  ZAP ("_SYMLINK");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_SYMLINK);

  if (pkt_len < sizeof (efs2_diag_symlink_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_symlink_req_type *) req_ptr;

  FSD_CHECK_NULLS (request, efs2_diag_symlink_req_type,
      oldpath, pkt_len, 2);

  /* Do a sanity check on the input parameters.
   */
  oldpath = (const char *) request->oldpath;
  oldpath_len = (int32) strlen (oldpath);
  newpath = (const char *) (request->oldpath + oldpath_len + 1);
  if (oldpath_len + strlen (newpath) + 2 > pkt_len)
  {
    return diagpkt_err_rsp (DIAG_BAD_PARM_F, req_ptr, pkt_len);
  }

  FS_DIAG_VALIDATE_PATH_ACCESS (SYMLINK, oldpath);
  FS_DIAG_VALIDATE_PATH_ACCESS (SYMLINK, newpath);

  response = (efs2_diag_symlink_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_SYMLINK,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Try to create the symbolic link. Do not report an error if the
  ** creation fails because the link already exists.
  */
  symlink_result = efs_symlink (oldpath, newpath);

  if ((symlink_result == 0) || (efs_errno == EEXIST))
    response->diag_errno = 0;
  else
    response->diag_errno = efs_errno;

  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;
} /* END fs_diag_symlink_handler */


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

FUNCTION FS_DIAG_READLINK_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_READLINK packet.

============================================================================*/
PACKED void *
fs_diag_readlink_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_readlink_req_type *request;
  efs2_diag_readlink_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_readlink_rsp_type);
  char   buf[FS_PATH_MAX];
  int32  readlink_result;
  const char *request_path;

  buf[0] = 0;                   /* Praise lint */
  ZAP ("_READLINK");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_readlink_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_readlink_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_readlink_req_type,
      path, pkt_len, 1);



  FS_DIAG_VALIDATE_PATH_ACCESS (READLINK, request_path);

  readlink_result = efs_readlink (request_path, buf, FS_PATH_MAX);

  if (readlink_result > 0)
  {
    rsp_len += readlink_result;
  }

  response = (efs2_diag_readlink_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_READLINK,
                          rsp_len);
  if (response == NULL)
    return NULL;

  if (readlink_result >= 0)
  {
    response->diag_errno  = 0;
    strncpy ((char *)response->name, buf, readlink_result);
    response->name[readlink_result] = '\0';
  }
  else
  {
    response->diag_errno   = efs_errno;
    response->name[0] = '\0';
  }

  return response;
} /* END fs_diag_readlink_handler */


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

FUNCTION FS_DIAG_UNLINK_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_UNLINK packet.

============================================================================*/
PACKED void *
fs_diag_unlink_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_unlink_req_type *request;
  efs2_diag_unlink_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_unlink_rsp_type);
  int32  unlink_result;
  const char *request_path;

  ZAP ("_UNLINK");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_UNLINK);

  if (pkt_len < sizeof (efs2_diag_unlink_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_unlink_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_unlink_req_type,
      path, pkt_len, 1);

  FS_DIAG_VALIDATE_PATH_ACCESS (UNLINK, request_path);

  response = (efs2_diag_unlink_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_UNLINK,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Try to unlink the file. Do not report an error if the operation fails
  ** because the file or link does not exist.
  */
  unlink_result = efs_unlink (request_path);

  if ((unlink_result == 0) || (efs_errno == ENOENT))
    response->diag_errno = 0;
  else
    response->diag_errno = efs_errno;

  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;
} /* END fs_diag_unlink_handler */


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

FUNCTION FS_DIAG_MKDIR_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_MKDIR packet.

============================================================================*/
PACKED void *
fs_diag_mkdir_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_mkdir_req_type *request;
  efs2_diag_mkdir_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_mkdir_rsp_type);
  int32  mkdir_result;
  int dirname_len;
  int i;
  char *request_path;

  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_SYMLINK);

  if (pkt_len < sizeof (efs2_diag_mkdir_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_mkdir_req_type *) req_ptr;
  request_path = (char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_mkdir_req_type,
      path, pkt_len, 1);

  ZPRINTF ("_MKDIR \"%s\"\n\r", request_path);

  /* EFS2 does not ignore trailing slashes at the end of directory names, but
   * tools expect this behavior. So remove trailing slashes here.
   */
  dirname_len = strlen (request_path);
  for (i = dirname_len-1; i > 0 && request_path[i] == '/'; i--)
    request_path[i] = '\0';

  FS_DIAG_VALIDATE_PATH_ACCESS (MKDIR, request_path);

  response = (efs2_diag_mkdir_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_MKDIR,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Remove the directory and then create it. The remove will do nothing if
  ** the directory doesn't exist, but it won't hurt. If the directory exists
  ** and is not empty, the remove will fail and so will the mkdir call that
  ** follows.
  */


  (void) efs_rmdir (request_path);
  mkdir_result = efs_mkdir (request_path, request->mode);
  if (mkdir_result == -1)
    response->diag_errno = efs_errno;
  else
    response->diag_errno = 0;

  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);

  return response;
} /* END fs_diag_mkdir_handler */


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

FUNCTION FS_DIAG_RMDIR_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_RMDIR packet.

============================================================================*/
PACKED void *
fs_diag_rmdir_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_rmdir_req_type *request;
  efs2_diag_rmdir_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_rmdir_rsp_type);
  int32  rmdir_result;
  int i;
  int dirname_len;
  char *request_path;

  ZAP ("_RMDIR");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_RMDIR);

  if (pkt_len < sizeof (efs2_diag_rmdir_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_rmdir_req_type *) req_ptr;
  request_path = (char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_rmdir_req_type,
      path, pkt_len, 1);

  /* EFS2 does not ignore trailing slashes at the end of directory names, but
   * tools expect this behavior. So remove trailing slashes here.
   */
  dirname_len = strlen (request_path);
  for (i = dirname_len-1; i > 0 && request_path[i] == '/'; i--)
    request_path[i] = '\0';

  FS_DIAG_VALIDATE_PATH_ACCESS (RMDIR, request_path);

  response = (efs2_diag_rmdir_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_RMDIR,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Try to remove the directory. If the directory does not exist, do not
  ** report an error.
  */
  rmdir_result = efs_rmdir (request_path);
  if ((rmdir_result == 0) || (efs_errno == ENOENT))
    response->diag_errno = 0;
  else
    response->diag_errno = efs_errno;

  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);

  return response;
} /* END fs_diag_rmdir_handler */


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

FUNCTION FS_DIAG_OPENDIR_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_OPENDIR packet.

============================================================================*/
PACKED void *
fs_diag_opendir_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_opendir_req_type *request;
  efs2_diag_opendir_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_opendir_rsp_type);
  EFSDIR *dirp;
  unsigned slot;
  const char *request_path;

  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_OPENDIR);

  if (pkt_len < sizeof (efs2_diag_opendir_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_opendir_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_opendir_req_type,
      path, pkt_len, 1);

  ZPRINTF ("_OPENDIR %s\n\r", request_path);
  FS_DIAG_VALIDATE_PATH_ACCESS (OPENDIR, request_path);

  response = (efs2_diag_opendir_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_OPENDIR,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Find a descriptor slot to use. */
  for (slot = 0; slot < FS_DIAG_MAX_OPEN_DIRS; slot++) {
    if (fsd_dir_table[slot].dirp == NULL)
      break;
  }

  /* If there are too many open directories, return an error. */
  if (slot >= FS_DIAG_MAX_OPEN_DIRS) {
    response->dirp = 0;
    response->diag_errno = FS_DIAG_TOO_MANY_OPEN_DIRS;
    goto opendir_end;
  }

  /* Check that the name length doesn't exceed the FS_PATH_MAX, since we're
   * about to copy it.
   * TODO: Check for a proper NULL termination of name. */
  if (strlen (request_path) > FS_PATH_MAX) {
    response->dirp = 0;
    response->diag_errno = ENAMETOOLONG;
    goto opendir_end;
  }

  dirp = efs_opendir (request_path);

  if (dirp == 0)
  {
    response->dirp  = 0;
    response->diag_errno = efs_errno;
    goto opendir_end;
  }

  fsd_dir_table[slot].dirp = dirp;

  /* The protocol expects the first directory entry to have a 'seqno' of
   * '1'.  This would give ".." seqno 0, and "." seqno -1.  The
   * fsd_dir_table 'seqno' field indicates the sequence number of the last
   * entry read.  Therefore, we set the last seqno to '-2', since that is
   * the logical entry before ".". */
  fsd_dir_table[slot].seqno = -2;
  strcpy (fsd_dir_table[slot].path, request_path);

  response->dirp = slot + 1;
  response->diag_errno = 0;

  opendir_end:
  ZPRINTF (" >>>OPENDIR: %d\n\r", response->diag_errno);
  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;
} /* END fs_diag_opendir_handler */

/*===========================================================================
 * Allocate a response packet for readdir, with the specified filename
 * length and error code.  'dirp' and 'seqno' will be filled in from the
 * request packet. */
static efs2_diag_readdir_rsp_type *
fsd_readdir_alloc (efs2_diag_readdir_req_type *request,
    int the_errno,
    const char *entry_name)
{
  efs2_diag_readdir_rsp_type *response;
  unsigned name_length = strlen (entry_name);

  response = (efs2_diag_readdir_rsp_type *) diagpkt_subsys_alloc
    (DIAG_SUBSYS_FS, EFS2_DIAG_READDIR,
     FPOS (efs2_diag_readdir_rsp_type, entry_name) + name_length + 1);
  response->dirp = request->dirp;
  response->seqno = request->seqno;
  response->diag_errno = the_errno;
  response->entry_type = 0;
  response->mode = 0;
  response->size = 0;
  response->atime = 0;
  response->mtime = 0;
  response->ctime = 0;
  strcpy ((char *)response->entry_name, entry_name);

  return response;
}

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

FUNCTION FS_DIAG_READDIR_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_READDIR packet.
============================================================================*/
PACKED void *
fs_diag_readdir_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_readdir_req_type *request;
  efs2_diag_readdir_rsp_type *response;
  dir_table_entry *slot;
  struct fs_dirent *dirent = NULL;
  struct fs_stat sbuf;
  static char path_buffer[FS_PATH_MAX + 1];
  int result;

  ZAP ("_READDIR");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_readdir_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_readdir_req_type *) req_ptr;

  /* Validate the passed in dirp. */
  if (request->dirp < 1 || request->dirp > FS_DIAG_MAX_OPEN_DIRS ||
      fsd_dir_table[request->dirp - 1].dirp == NULL)
  {
    response = fsd_readdir_alloc (request, FS_DIAG_DIR_NOT_OPEN, "");
    goto readdir_end;
  }
  slot = &fsd_dir_table[request->dirp - 1];

  /* Validate the seqno. */
  if (request->seqno <= 0) {
    response = fsd_readdir_alloc (request, FS_DIAG_INVALID_SEQ_NO, "");
    goto readdir_end;
  }

  /* If this request is earlier than previous requests, close and reopen,
   * to reset the sequence number. */
  if (request->seqno <= slot->seqno) {
    (void) efs_closedir (slot->dirp);
    slot->dirp = efs_opendir (slot->path);

    /* If the directory vanish in the interim, return as if the dir failed
     * to open. */
    if (slot->dirp == NULL) {
      MSG_MED ("fs_diag_readdir: dir handle failed to reopen", 0, 0, 0);
      response = fsd_readdir_alloc (request, FS_DIAG_DIR_NOT_OPEN, "");
      goto readdir_end;
    }

    /* Start at the ".." entry. */
    slot->seqno = -2;
  }

  /* Advance to the entry we are trying to read. */
  while (request->seqno > slot->seqno) {
    dirent = efs_readdir (slot->dirp);
    if (dirent == NULL) {
      /* Past end of directory.  Could also indicate an error, but the API
       * doesn't distinguish that. */
      response = fsd_readdir_alloc (request, 0, "");
      goto readdir_end;
    }
    slot->seqno++;
  }

  /* Check for logic error above. */
  ASSERT (dirent != NULL);

  /* Construct a full path to lstat the entry with. */
  if (strlen (slot->path) + strlen (dirent->d_name) + 1 > FS_PATH_MAX) {
    response = fsd_readdir_alloc (request, FS_DIAG_PATH_TOO_LONG, "");
    goto readdir_end;
  }

  strcpy (path_buffer, slot->path);
  strcat (path_buffer, "/");
  strcat (path_buffer, dirent->d_name);

  /* We have our filename, so we can allocate a response, no matter what it
   * will be. */
  response = fsd_readdir_alloc (request, 0, dirent->d_name);
  result = efs_lstat (path_buffer, &sbuf);
  if (result != 0) {
    /* We were unable to stat the entry, so we know very little about it.
     * Fill in what we can. */
    response->entry_type = FS_DIAG_FTYPE_UNKNOWN;
  } else {
    /* Copy all of the stat information that we can. */
    response->mode = sbuf.st_mode;
    response->size = sbuf.st_size;
    response->atime = sbuf.st_atime;
    response->mtime = sbuf.st_mtime;
    response->ctime = sbuf.st_ctime;
    switch (sbuf.st_mode & S_IFMT) {
      case S_IFREG:
        response->entry_type = FS_DIAG_FTYPE_REG;
        break;

      case S_IFDIR:
        response->entry_type = FS_DIAG_FTYPE_DIR;
        break;

      case S_IFLNK:
        response->entry_type = FS_DIAG_FTYPE_LINK;
        break;

      default:
        response->entry_type = FS_DIAG_FTYPE_UNKNOWN;

        /* This used to be an error, but shouldn't be. */
        /* response->diag_errno = FS_DIAG_UNKNOWN_FILETYPE; */
        break;
    }
  }

  readdir_end:
  return response;
} /* END fs_diag_readdir_handler */


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

FUNCTION FS_DIAG_CLOSEDIR_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_CLOSEDIR packet.
============================================================================*/
PACKED void *
fs_diag_closedir_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_closedir_req_type *request;
  efs2_diag_closedir_rsp_type *response;
  unsigned rsp_len = sizeof (efs2_diag_closedir_rsp_type);
  dir_table_entry *slot;
  int result;

  ZAP ("_CLOSEDIR");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_CLOSEDIR);

  if (pkt_len != sizeof (efs2_diag_closedir_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_closedir_req_type *) req_ptr;

  response = (efs2_diag_closedir_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_CLOSEDIR,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Validate the slot. */
  if (request->dirp < 1 || request->dirp > FS_DIAG_MAX_OPEN_DIRS ||
      fsd_dir_table[request->dirp - 1].dirp == NULL)
  {
    response->diag_errno = FS_DIAG_DIR_NOT_OPEN;
    goto closedir_end;
  }
  slot = &fsd_dir_table[request->dirp - 1];

  result = efs_closedir (slot->dirp);
  slot->dirp = NULL;

  if (result == 0)
    response->diag_errno = 0;
  else
    response->diag_errno = efs_errno;

  closedir_end:
  fsd_memoize_packet (req_ptr, pkt_len, response, rsp_len);
  return response;
} /* END fs_diag_closedir_handler */


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

FUNCTION FS_DIAG_RENAME_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_RENAME packet.
============================================================================*/
PACKED void *
fs_diag_rename_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_rename_req_type *rename_req_ptr;
  efs2_diag_rename_rsp_type *rename_rsp_ptr;
  uint16 rsp_len = sizeof (efs2_diag_rename_rsp_type);
  const char *oldpath;
  const char *newpath;
  int32  oldpath_len;

  ZAP ("_RENAME");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_RENAME);

  if (pkt_len < sizeof (efs2_diag_rename_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  rename_req_ptr = (efs2_diag_rename_req_type *) req_ptr;

  FSD_CHECK_NULLS (rename_req_ptr, efs2_diag_rename_req_type,
      oldpath, pkt_len, 2);

  /* Do a sanity check on the input parameters.
   */
  oldpath = (const char *) rename_req_ptr->oldpath;
  oldpath_len = (int32) strlen (oldpath);
  newpath = (const char *) (rename_req_ptr->oldpath + oldpath_len + 1);
  if (oldpath_len + strlen (newpath) + 2 > pkt_len)
  {
    return diagpkt_err_rsp (DIAG_BAD_PARM_F, req_ptr, pkt_len);
  }

  FS_DIAG_VALIDATE_PATH_ACCESS (RENAME, oldpath);
  FS_DIAG_VALIDATE_PATH_ACCESS (RENAME, newpath);

  rename_rsp_ptr = (efs2_diag_rename_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_RENAME,
                          rsp_len);
  if (rename_rsp_ptr == NULL)
    return NULL;


  if (efs_rename (oldpath, newpath) == 0)
    rename_rsp_ptr->diag_errno = 0;
  else
    rename_rsp_ptr->diag_errno = efs_errno;

  fsd_memoize_packet (req_ptr, pkt_len, rename_rsp_ptr, rsp_len);
  return rename_rsp_ptr;
} /* END fs_diag_rename_handler */


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

FUNCTION FS_DIAG_STAT_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_STAT packet.
============================================================================*/
PACKED void *
fs_diag_stat_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_stat_req_type *request;
  efs2_diag_stat_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_stat_rsp_type);
  struct fs_stat sbuf;
  const char *request_path;

  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_stat_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_stat_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_stat_req_type,
      path, pkt_len, 1);

  ZPRINTF ("_STAT: %s\n\r", request_path);
  FS_DIAG_VALIDATE_PATH_ACCESS (STAT, request_path);

  response = (efs2_diag_stat_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_STAT,
                          rsp_len);
  if (response == NULL)
    return NULL;

  if (efs_stat (request_path, &sbuf) == 0)
  {
    response->mode   = (int32) sbuf.st_mode;
    response->size   = (int32) sbuf.st_size;
    response->nlink  = (int32) sbuf.st_nlink;
    response->atime  = (int32) sbuf.st_atime;
    response->mtime  = (int32) sbuf.st_mtime;
    response->ctime  = (int32) sbuf.st_ctime;
    response->diag_errno  = 0;
  }
  else
  {
    response->mode   = 0;
    response->size   = 0;
    response->nlink  = 0;
    response->atime  = 0;
    response->mtime  = 0;
    response->ctime  = 0;
    response->diag_errno  = efs_errno;
  }

  return response;
} /* END fs_diag_stat_handler */


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

FUNCTION FS_DIAG_LSTAT_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_LSTAT packet.
============================================================================*/
PACKED void *
fs_diag_lstat_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_lstat_req_type *request;
  efs2_diag_lstat_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_lstat_rsp_type);
  struct fs_stat sbuf;
  const char *request_path;

  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_lstat_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_lstat_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_lstat_req_type,
      path, pkt_len, 1);

  ZPRINTF ("_LSTAT: %s\n\r", request_path);
  FS_DIAG_VALIDATE_PATH_ACCESS (LSTAT, request_path);

  response = (efs2_diag_lstat_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_LSTAT,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* XXX: Figure out which of the following fields make sense for a
  ** symbolic link.
  */

  if (efs_lstat (request_path, &sbuf) == 0)
  {
    response->mode   = (int32) sbuf.st_mode;
    response->atime  = (int32) sbuf.st_atime;
    response->mtime  = (int32) sbuf.st_mtime;
    response->ctime  = (int32) sbuf.st_ctime;
    response->diag_errno  = 0;
  }
  else
  {
    response->mode   = 0;
    response->atime  = 0;
    response->mtime  = 0;
    response->ctime  = 0;
    response->diag_errno  = efs_errno;
  }

  return response;
} /* END fs_diag_lstat_handler */


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

FUNCTION FS_DIAG_FSTAT_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_FSTAT packet.
============================================================================*/
PACKED void *
fs_diag_fstat_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_fstat_req_type *request;
  efs2_diag_fstat_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_fstat_rsp_type);
  desc_entry *slot;
  struct fs_stat sbuf;

  ZAP ("_FSTAT");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_fstat_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_fstat_req_type *) req_ptr;
  response = (efs2_diag_fstat_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_FSTAT,
                          rsp_len);
  if (response == NULL)
    return NULL;

  /* Validate the requested descriptor. */
  if (request->fd < 0 || request->fd >= FS_DIAG_MAX_FDS ||
      fsd_desc_table[request->fd].fd < 0)
  {
    response->diag_errno = EBADF;
    response->mode = 0;
    response->size = 0;
    response->nlink = 0;
    response->atime = 0;
    response->mtime = 0;
    response->ctime = 0;
    goto fstat_end;
  }

  slot = &fsd_desc_table[request->fd];

  if (efs_fstat (slot->fd, &sbuf) == 0) {
    response->mode   = (int32) sbuf.st_mode;
    response->size   = (int32) sbuf.st_size;
    response->nlink  = (int32) sbuf.st_nlink;
    response->atime  = (int32) sbuf.st_atime;
    response->mtime  = (int32) sbuf.st_mtime;
    response->ctime  = (int32) sbuf.st_ctime;
    response->diag_errno  = 0;
  } else {
    response->diag_errno  = efs_errno;
    response->mode = 0;
    response->size = 0;
    response->nlink = 0;
    response->atime = 0;
    response->mtime = 0;
    response->ctime = 0;
  }

  fstat_end:
  return response;
}

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

FUNCTION FS_DIAG_CHMOD_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_CHMOD packet.
============================================================================*/
PACKED void *
fs_diag_chmod_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_chmod_req_type *request;
  efs2_diag_chmod_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_chmod_rsp_type);
  int32  chmod_result;
  const char *request_path;

  ZAP ("_CHMOD");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_chmod_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_chmod_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_chmod_req_type,
      path, pkt_len, 1);

  FS_DIAG_VALIDATE_PATH_ACCESS (CHMOD, request_path);

  response = (efs2_diag_chmod_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_CHMOD,
                          rsp_len);
  if (response == NULL)
    return NULL;

  chmod_result = efs_chmod (request_path, request->mode);
  if (chmod_result == 0)
    response->diag_errno  = 0;
  else
    response->diag_errno  = efs_errno;

  return response;
} /* END fs_diag_chmod_handler */


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

FUNCTION FS_DIAG_STATFS_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_STATFS packet.
============================================================================*/
PACKED void *
fs_diag_statfs_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_statfs_req_type *request;
  efs2_diag_statfs_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_statfs_rsp_type);
  struct fs_statvfs sbuf;
  const char *request_path;

  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_statfs_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_statfs_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_statfs_req_type,
      path, pkt_len, 1);

  ZPRINTF ("_STATFS: %s\n\r", request_path);
  FS_DIAG_VALIDATE_PATH_ACCESS (STATFS, request_path);

  response = (efs2_diag_statfs_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_STATFS,
                          rsp_len);
  if (response == NULL)
    return NULL;

  if (efs_statvfs (request_path, &sbuf) == 0)
  {
    /* XXX: Fill in all the fields here.
     */
    response->diag_errno         = 0;
    response->fs_id         = (int32) sbuf.f_fsid;
    memset ((void *)response->fs_type, 0x00, EFS_FSTYPE_LEN);
    response->block_size    = (int32) sbuf.f_bsize;
    response->total_blocks  = (int32) sbuf.f_blocks;
    response->avail_blocks  = (int32) sbuf.f_bavail;
    response->free_blocks   = (int32) sbuf.f_bfree;
    response->max_file_size = 0;
    response->nfiles        = 0;
    response->max_nfiles    = 0;
  }
  else
  {
    response->diag_errno  = efs_errno;
  }

  return response;
} /* END fs_diag_statfs_handler */


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

FUNCTION FS_DIAG_ACCESS_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_ACCESS packet.
============================================================================*/
PACKED void *
fs_diag_access_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_access_req_type *request;
  efs2_diag_access_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_access_rsp_type);
  const char *request_path;

  ZAP ("_ACCESS");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_access_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_access_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_access_req_type,
      path, pkt_len, 1);

  FS_DIAG_VALIDATE_PATH_ACCESS (ACCESS, request_path);

  response = (efs2_diag_access_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_ACCESS,
                          rsp_len);
  if (response == NULL)
    return NULL;

  if (efs_access (request_path, request->perm_bits) == 0)
    response->diag_errno  = 0;
  else
    response->diag_errno  = efs_errno;

  return response;
} /* END fs_diag_access_handler */


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

FUNCTION FS_DIAG_DEVICE_INFO_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_NAND_DEV_INFO packet.
============================================================================*/
PACKED void *
fs_diag_device_info_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_dev_info_req_type *dev_info_req_ptr;
  efs2_diag_dev_info_rsp_type *dev_info_rsp_ptr = NULL;
  uint16 rsp_len = sizeof (efs2_diag_dev_info_rsp_type);
  struct fs_device_attr device_attr;

  ZAP ("_NAND_DEV_INFO");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_dev_info_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  dev_info_req_ptr = (efs2_diag_dev_info_req_type *)req_ptr;

  if (efs_get_device_info(&device_attr) == 0)
  {
    rsp_len += strlen(device_attr.device_name) + 1;

    dev_info_rsp_ptr = (efs2_diag_dev_info_rsp_type *)
      diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                            EFS2_DIAG_NAND_DEV_INFO,
                            rsp_len);

    if (dev_info_rsp_ptr == NULL)
      return NULL;

    if (device_attr.device_type == FS_DIAG_NAND_DEV)
    {
      dev_info_rsp_ptr->diag_errno = 0;
      dev_info_rsp_ptr->total_no_of_blocks = device_attr.block_count;
      dev_info_rsp_ptr->no_of_pages_per_block = device_attr.block_size;
      dev_info_rsp_ptr->page_size = device_attr.page_size;
      dev_info_rsp_ptr->total_page_size = device_attr.total_page_size;
      dev_info_rsp_ptr->maker_id = device_attr.device_maker_id;
      dev_info_rsp_ptr->device_id = device_attr.device_id;
      strcpy ((char*)dev_info_rsp_ptr->device_name, device_attr.device_name);
    } else
      dev_info_rsp_ptr->diag_errno = FS_DIAG_NOT_NAND_FLASH;
  }
  else
  {
    fs_diag_efs2_error_rsp(-1, dev_info_req_ptr, pkt_len);
  }

  return dev_info_rsp_ptr;

} /* END fs_diag_device_info_handler */


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

FUNCTION FS_DIAG_FACTIMAGE_START_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_FACT_IMAGE_START packet. This function put the phone
  in offline mode prepares for image dump to start.
============================================================================*/
PACKED void *
fs_diag_factimage_start_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_factimage_start_rsp_type *factimage_start_rsp_ptr;
  uint16 rsp_len = sizeof (efs2_diag_factimage_start_rsp_type);

  ZAP ("_FACT_IMAGE_START");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_FACT_IMAGE_START);

  if (pkt_len != sizeof (efs2_diag_factimage_start_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  factimage_start_rsp_ptr = (efs2_diag_factimage_start_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_FACT_IMAGE_START,
                          rsp_len);

  if (factimage_start_rsp_ptr == NULL)
    return NULL;

  /* Acquire exclusive access during factory image output */
  FS_GLOBAL_LOCK();

  factimage_start_rsp_ptr->diag_errno = 0;

  fsd_memoize_packet (req_ptr, pkt_len, factimage_start_rsp_ptr, rsp_len);

  return factimage_start_rsp_ptr;

} /* END fs_diag_factimage_start_handler */


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

FUNCTION FS_DIAG_FACTIMAGE_READ_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_FACT_IMAGE_READ packet.
============================================================================*/
PACKED void *
fs_diag_factimage_read_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  int image_data_errno;
  struct fs_factimage_read_info read_info;
  byte *page_data;

  efs2_diag_factimage_read_req_type *image_data_req_ptr;
  efs2_diag_factimage_read_rsp_type *image_data_rsp_ptr;
  uint16 rsp_len = sizeof (efs2_diag_factimage_read_rsp_type);

  ZAP ("_FACT_IMAGE_READ");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_factimage_read_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  image_data_req_ptr = (efs2_diag_factimage_read_req_type *)req_ptr;

  /* Filesystem page size is 512 bytes */
  rsp_len += 512;

  image_data_rsp_ptr = (efs2_diag_factimage_read_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_FACT_IMAGE_READ,
                          rsp_len);

  if (image_data_rsp_ptr == NULL)
    return NULL;

  read_info.stream_state       = image_data_req_ptr->stream_state;
  read_info.info_cluster_sent  = image_data_req_ptr->info_cluster_sent;
  read_info.cluster_map_seqno  = image_data_req_ptr->cluster_map_seqno;
  read_info.cluster_data_seqno = image_data_req_ptr->cluster_data_seqno;

  page_data = (byte *) image_data_rsp_ptr->page;

  image_data_errno = efs_get_fs_data (&read_info, page_data);

  image_data_rsp_ptr->diag_errno              = image_data_errno;
  image_data_rsp_ptr->stream_state       = read_info.stream_state;
  image_data_rsp_ptr->info_cluster_sent  = read_info.info_cluster_sent;
  image_data_rsp_ptr->cluster_map_seqno  = read_info.cluster_map_seqno;
  image_data_rsp_ptr->cluster_data_seqno = read_info.cluster_data_seqno;

  return image_data_rsp_ptr;

} /* END fs_diag_factimage_read_handler */


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

FUNCTION FS_DIAG_FACTIMAGE_END_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_FACT_IMAGE_END packet. This function handles phone
  state after image dump.
============================================================================*/
PACKED void *
fs_diag_factimage_end_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_factimage_end_rsp_type *factimage_end_rsp_ptr;
  uint16 rsp_len = sizeof (efs2_diag_factimage_end_rsp_type);

  ZAP ("_FACT_IMAGE_END");
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  FSD_MEMOIZED_PACKET_CHECK (req_ptr, pkt_len, EFS2_DIAG_FACT_IMAGE_END);

  if (pkt_len != sizeof (efs2_diag_factimage_end_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  factimage_end_rsp_ptr = (efs2_diag_factimage_end_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_FACT_IMAGE_END,
                          rsp_len);

  if (factimage_end_rsp_ptr == NULL)
    return NULL;

  /* Release exclusive access */
  FS_GLOBAL_UNLOCK();

  factimage_end_rsp_ptr->diag_errno = 0;

  fsd_memoize_packet (req_ptr, pkt_len, factimage_end_rsp_ptr, rsp_len);

  return factimage_end_rsp_ptr;

} /* END fs_diag_factimage_end_handler */

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

FUNCTION FS_DIAG_PREP_FACTIMAGE_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_PREP_FACT_IMAGE packet. This function helps indicate
    to file system to prepare for a factory image dump.
============================================================================*/
PACKED void *
fs_diag_prep_factimage_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  int32 status = 1;
  qword now;
  qword start_time;
  qword elapsed;

  efs2_diag_prep_factimage_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_prep_factimage_rsp_type);

  ZAP ("_PREP_FACT_IMAGE");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len != sizeof (efs2_diag_prep_factimage_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  response = (efs2_diag_prep_factimage_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS, EFS2_DIAG_PREP_FACT_IMAGE,
                          rsp_len);

  if (response == NULL)
    return NULL;

  fs_time_ms (start_time);

  while(status != 0)
  {
    status = efs_image_prepare ();
    if(status == 0)
      break;
    /* If elapsed time is too large (approx greater than 500 millisec)
       return */
    fs_time_ms (now);
    qw_sub (elapsed, now, start_time);
    if(qw_hi (elapsed) != 0 || qw_lo (elapsed) > 400)
      break;
  }

  response->diag_errno = status;

  return response;

} /* END fs_diag_prep_factimage_handler */


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

FUNCTION FS_DIAG_GET_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_GET packet.

============================================================================*/
PACKED void *
fs_diag_get_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_get_req_type *request;
  efs2_diag_get_rsp_type *response;
  unsigned rsp_len = 0;
  unsigned base_length = 0;
  fs_ssize_t bytes_read = 0;
  const char *request_path;

  ZAP ("_GET");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_get_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_get_req_type *) req_ptr;
  request_path = (const char *) request->path;

  FSD_CHECK_NULLS (request, efs2_diag_get_req_type, path,
      pkt_len, 1);

  FS_DIAG_VALIDATE_PATH_ACCESS (READ, request_path);

  if (request->data_length > FS_DIAG_MAX_READ_REQ) {
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  base_length = FPOS (efs2_diag_get_rsp_type, data);
  rsp_len = base_length + request->data_length;

  response = (efs2_diag_get_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_GET,
                          rsp_len);
  if (response == NULL)
    return NULL;

  bytes_read = efs_get (request_path,
                        (void *) response->data,
                        request->data_length);

  if (bytes_read >= 0) {
    response->num_bytes  = bytes_read;
    response->diag_errno      = 0;

    if ((unsigned) bytes_read < request->data_length)
      diagpkt_shorten (response, base_length + bytes_read);
  } else {
    response->num_bytes  = -1;
    response->diag_errno      = efs_errno;
    diagpkt_shorten (response, base_length);
  }

  return response;
} /* END fs_diag_get_handler */


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

FUNCTION FS_DIAG_PUT_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_PUT packet.

============================================================================*/
PACKED void *
fs_diag_put_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_put_req_type *request;
  efs2_diag_put_rsp_type *response;
  uint16 rsp_len = sizeof (efs2_diag_put_rsp_type);
  fs_ssize_t nbyte;
  fs_ssize_t bytes_written = 0;
  unsigned   data_offset;
  char      *put_locn;
  char      *put_path;

  ZAP ("_PUT");
  fsd_invalidate_memoization ();
  /*----------------------------------------------------------------------
    Check security, since this is a secure funciton
    ----------------------------------------------------------------------*/
#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return( diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len) );
  }
#endif

  if (pkt_len < sizeof (efs2_diag_put_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  request = (efs2_diag_put_req_type *) req_ptr;

  put_locn = (char *) request->data;
  nbyte    = request->data_length;
  put_path = (char *) request->data + nbyte;
  data_offset = FPOS (efs2_diag_put_req_type, data);

  /* Validate this packet, more complicated than others, because there is a
   * nul-terminated string after variable length data. */
  if ((nbyte + data_offset >= pkt_len) ||
      (fsd_count_nulls (put_path, pkt_len - (data_offset + nbyte)) < 1))
  {
    return (diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len));
  }

  FS_DIAG_VALIDATE_PATH_ACCESS (WRITE, put_path);

  response = (efs2_diag_put_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_PUT,
                          rsp_len);
  if (response == NULL)
    return NULL;

  bytes_written = efs_put (put_path,
                           (void *) put_locn,
                           nbyte, request->flags,
                           0777);

  if (bytes_written < 0)
  {
    response->diag_errno         = efs_errno;
  }
  else
  {
    /* (bytes_written >= 0) when it gets here */
    response->diag_errno         = 0;
  }

  return response;
} /* END fs_diag_write_handler */

PACKED void *
fs_diag_efs2_error_rsp (int32 errno, PACKED void * req_pkt, uint16 req_len)
{
  efs2_diag_error_rsp_type *response;
  const unsigned int rsp_len =
    MIN (sizeof (efs2_diag_error_rsp_type),
         req_len + FPOS (efs2_diag_error_rsp_type, pkt));

  response = (efs2_diag_error_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_ERROR,
                          rsp_len);

  if (response == NULL) {
    return NULL;
  }

  response->diag_errno = errno;

  memcpy ((void *) response->pkt,
          (void *) req_pkt, rsp_len - FPOS (efs2_diag_error_rsp_type, pkt));

  return ((void *) response);
}/* diagkt_err_rsp */

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

FUNCTION FS_DIAG_BENCHMARK_START_TEST_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_BENCHMARK_START_TEST packet. Initializes the EFS2 Diag
  interface and passes the values of some parameters back to the tool.

============================================================================*/
PACKED void *
fs_diag_benchmark_start_test_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_benchmark_start_test_rsp_type *response;
  efs2_diag_benchmark_start_test_req_type *request =
    (efs2_diag_benchmark_start_test_req_type *) req_ptr;
  char temp_path[FS_PATH_MAX];

  fsd_invalidate_memoization ();

#ifndef FS_UNIT_TEST
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return (diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len));
  }
#endif

  if (pkt_len < sizeof (*request))
  {
    ZPRINTF("Unexpected start_test packet length = %d, SIZEOF = %d",
            pkt_len, sizeof (*request));
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  strncpy (temp_path, (char *)request->cmd.path, sizeof(temp_path));

  /* Make sure they're not benchmarking a forbidden pathname */
  FS_DIAG_VALIDATE_PATH_ACCESS (OPEN, temp_path);

  response = (efs2_diag_benchmark_start_test_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_BENCHMARK_START_TEST,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  response->cmd = request->cmd;
  response->success = fs_benchmark_put_cmd (&request->cmd) ? 0 : 1;

  return response;
}

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

FUNCTION FS_DIAG_BENCHMARK_GET_RESULTS_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_BENCHMARK_GET_RESULTS packet. Polls about the response
  queue from fs_benchmark().

============================================================================*/
PACKED void *
fs_diag_benchmark_get_results_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_benchmark_get_results_rsp_type *response;

  fsd_invalidate_memoization ();

  if (pkt_len != sizeof (efs2_diag_benchmark_get_results_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  response = (efs2_diag_benchmark_get_results_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_BENCHMARK_GET_RESULTS,
                          sizeof(*response));
  if (response == NULL)
    return NULL;

  response->queue_status = fs_benchmark_get_rpt (&response->report);

  return response;
}

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

FUNCTION FS_DIAG_BENCHMARK_INIT_HANDLER
DESCRIPTION
  Handles the EFS2_DIAG_BENCHMARK_INIT packet. Resets the command and report
  queues. Needs further modification in fs_benchmark_reset_all() for proper
  functioning

============================================================================*/
PACKED void *
fs_diag_benchmark_init_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_benchmark_init_rsp_type *response;

  fsd_invalidate_memoization ();

  if (pkt_len != sizeof (efs2_diag_benchmark_init_req_type))
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);

  response = (efs2_diag_benchmark_init_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_BENCHMARK_INIT,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  fs_benchmark_flush();

  /*
    Further implementation of fs_benchmark_reset_all() may address aborting a
    pending/running command.
    The queues are emptied explicitly, but nothing is done to abort a command
    that got off the queue and is running.
  */
  return response;
}

PACKED void *
fs_diag_chown_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_errno_rsp_type *response;
  efs2_diag_chown_req_type *request = (efs2_diag_chown_req_type *) req_ptr;
  const char *request_path;

  fsd_invalidate_memoization ();

  /* Require SPC Unlock */
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return (diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len));
  }

  if (pkt_len < sizeof (*request))
  {
    ZPRINTF("Unexpected packet length = %d, SIZEOF = %d",
            pkt_len, sizeof (*request));
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  FSD_CHECK_NULLS (request, efs2_diag_chown_req_type,
      path, pkt_len, 1);

  request_path = (const char *) request->path;

  /* Make sure we're not chowning a forbidden pathname */
  FS_DIAG_VALIDATE_PATH_ACCESS (OPEN, request_path);

  response = (efs2_diag_errno_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_CHOWN,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  response->diag_errno = (efs_chown (request_path,
                                request->uid_val,
                                request->gid_val) ? efs_errno : 0);

  return response;
}

PACKED void *
fs_diag_set_reservation_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_errno_rsp_type *response;
  efs2_diag_set_reservation_req_type *request =
    (efs2_diag_set_reservation_req_type *) req_ptr;
  const char *request_path;

  fsd_invalidate_memoization ();
  /* Require SPC Unlock */
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return (diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len));
  }

  if (pkt_len < sizeof (*request))
  {
    ZPRINTF("Unexpected packet length = %d, SIZEOF = %d",
            pkt_len, sizeof (*request));
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  FSD_CHECK_NULLS (request, efs2_diag_set_reservation_req_type,
      path, pkt_len, 1);

  request_path = (const char *) request->path;
  response = (efs2_diag_errno_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_SET_RESERVATION,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  response->diag_errno = (efs_set_reservation (request_path,
                                          request->gid,
                                          request->bytes) ? efs_errno : 0);

  return response;
}


PACKED void *
fs_diag_set_quota_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_errno_rsp_type *response;
  efs2_diag_set_quota_req_type *request =
    (efs2_diag_set_quota_req_type *) req_ptr;
  const char *request_path;

  fsd_invalidate_memoization ();
  /* Require SPC Unlock */
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return (diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len));
  }

  if (pkt_len < sizeof (*request))
  {
    ZPRINTF("Unexpected packet length = %d, SIZEOF = %d",
            pkt_len, sizeof (*request));
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  FSD_CHECK_NULLS (request, efs2_diag_set_quota_req_type,
      path, pkt_len, 1);

  request_path = (const char *) request->path;
  response = (efs2_diag_errno_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_SET_QUOTA,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  response->diag_errno = (efs_set_quota (request_path,
                                    request->gid,
                                    request->bytes) ? efs_errno : 0);

  return response;
}

PACKED void *
fs_diag_get_group_info_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_get_group_info_rsp_type *response;
  efs2_diag_get_group_info_req_type *request =
    (efs2_diag_get_group_info_req_type *) req_ptr;
  struct fs_group_info info;
  const char *request_path;

  fsd_invalidate_memoization ();
  /* Require SPC Unlock */
  if (diag_get_security_state() != DIAG_SEC_UNLOCKED) {
    return (diagpkt_err_rsp(DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len));
  }

  if (pkt_len < sizeof (*request))
  {
    ZPRINTF("Unexpected packet length = %d, SIZEOF = %d",
            pkt_len, sizeof (*request));
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  FSD_CHECK_NULLS (request, efs2_diag_get_group_info_req_type,
      path, pkt_len, 1);

  request_path = (const char *) request->path;
  response = (efs2_diag_get_group_info_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_GET_GROUP_INFO,
                          sizeof (*response));
  if (response == NULL)
    return NULL;

  response->diag_errno = (efs_get_group_info (request_path,
                                         request->gid,
                                         &info) ? efs_errno : 0);

  response->info.quota_size = info.quota_size;
  response->info.reservation_size = info.reservation_size;
  response->info.space_used = info.space_used;
  return response;
}

PACKED void *ext_simple_query (efs2_diag_extended_info_req_type *request,
    unsigned len);

/*======================================================================
 * FUNCTION FS_DIAG_EXTENDED_INFO_HANDLER
 * Handle the extended info request packet.
 */
PACKED void *
fs_diag_extended_info_handler (PACKED void *req_ptr, uint16 pkt_len)
{
  efs2_diag_extended_info_req_type *request;

  fsd_invalidate_memoization ();

  /* Check security. */
  if (diag_get_security_state () != DIAG_SEC_UNLOCKED) {
    return diagpkt_err_rsp (DIAG_BAD_SEC_MODE_F, req_ptr, pkt_len);
  }

  if (pkt_len < sizeof (*request)) {
    return diagpkt_err_rsp (DIAG_BAD_LEN_F, req_ptr, pkt_len);
  }

  request = (efs2_diag_extended_info_req_type *) req_ptr;

  /* Validate nul termination of path. */
  FSD_CHECK_NULLS (request, efs2_diag_extended_info_req_type,
      path, pkt_len, 1);

  /* XXX: Most queries are made through the existing efs_get_device_info
   * request, which doesn't take a path.  When multiple device support is
   * added to EFS2, this mechanism will have to change. */
  switch (request->query) {
    case FS_DIAG_EXT_QUERY_DEVICE_NAME:
    case FS_DIAG_EXT_QUERY_DEVICE_ID:
    case FS_DIAG_EXT_QUERY_FILESYSTEM_BLOCKS:
    case FS_DIAG_EXT_QUERY_PAGES_PER_BLOCK:
    case FS_DIAG_EXT_QUERY_PAGE_SIZE:
    case FS_DIAG_EXT_QUERY_FLASH_BUS_WIDTH:
    case FS_DIAG_EXT_QUERY_TOTAL_BLOCKS:
      return ext_simple_query (request, pkt_len);

    default:
      return diagpkt_err_rsp (DIAG_BAD_PARM_F, req_ptr, pkt_len);
  }
}

PACKED void *
ext_simple_query (efs2_diag_extended_info_req_type *request, unsigned len)
{
  int result;
  unsigned rsp_len;
  efs2_diag_extended_info_rsp_type *response = NULL;
  PACKED uint32 *item = NULL;
  static struct fs_device_attr attr;

  result = efs_get_device_info (&attr);
  if (result != 0)
    return fs_diag_efs2_error_rsp (efs_errno, (PACKED void *) request, len);

  if (request->query == FS_DIAG_EXT_QUERY_DEVICE_ID
      && request->data > 1)
    return fs_diag_efs2_error_rsp (FS_DIAG_UNAVAILABLE_INFO,
                                   (PACKED void *) request, len);

  rsp_len = sizeof (*response) + sizeof (uint32);

  if (request->query == FS_DIAG_EXT_QUERY_DEVICE_NAME)
    rsp_len = sizeof (*response) + strlen (attr.device_name) + 1;

  response = (efs2_diag_extended_info_rsp_type *)
    diagpkt_subsys_alloc (DIAG_SUBSYS_FS,
                          EFS2_DIAG_EXTENDED_INFO, rsp_len);

  item = (PACKED uint32 *) (response + 1);   /*lint !e740 */
  response->kind = FS_DIAG_EXT_KIND_INT;

  switch (request->query) {
    case FS_DIAG_EXT_QUERY_DEVICE_ID:
      if (request->data == 0)
        *item = attr.device_maker_id;
      else
        *item = attr.device_id;
      break;

    case FS_DIAG_EXT_QUERY_FILESYSTEM_BLOCKS:
      *item = attr.block_count;
      break;

    case FS_DIAG_EXT_QUERY_PAGES_PER_BLOCK:
      *item = attr.block_size;
      break;

    case FS_DIAG_EXT_QUERY_PAGE_SIZE:
      *item = attr.page_size;
      break;

    case FS_DIAG_EXT_QUERY_TOTAL_BLOCKS:
      *item = attr.total_blocks;
      break;

    case FS_DIAG_EXT_QUERY_FLASH_BUS_WIDTH:
      *item = attr.bus_width;
      break;

    case FS_DIAG_EXT_QUERY_DEVICE_NAME:
      response->kind = FS_DIAG_EXT_KIND_STRING;
      strcpy ((char *) (response + 1), attr.device_name);
      break;

    default:
      /* Something is wrong. */
      MSG_HIGH ("Internal error in ext_num_query", 0, 0, 0);
      *item = 0xFFFFFFFF;
  }

  return response;
}

#endif /* FEATURE_EFS2 */