/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
  * Edit History
  * 
  * Date        Initials        Change description
  * 3/3/04      JZW             In function parse_file_path added code
  *                             to remove leading / from directory
  *                             Clean up the format of the data returned
  *                             on a list command to conform to IE
  *                             requirements
  * 06/16/04     JZW             Changes made to support passive mode
  *                              functions changed were FS_RetrieveFile, FS_ChangeDirectory
  *                              functions added MatchUsingToken, CleanUpFileName
  * 10/19/04     JZW             In function list_dir the groupid was being shifted. The problem
  *                              is that it was already shifted so this was constituting a double
  *                              shift - removed. A new function was added - getGroupNumber. This 
  *                              converts the internal file system group number into a human
  *                              readable/understandable form (1 - 8). This routine is called
  *                              during a list function to display the group number as an integer 1 - 8
  * 03/02/05    MLC             In list_dir, we removed a space in between the "group" and group id.
  *                             groupId must be sent as a single item (no space).
  * 03/24/05    MLC             Removed MatchUsingToken call in list_dir (just use strcmp).
  *                             Fixed FS_RootDirectory for going root directory by passing
  *                             "/" argument.
  *                             Fixed FS_RenameFrom & FS_RenameTo to accept full path
  *                             name.
  */

/*ad*
 * This is the interface between the NET+OS FTP Server and the NET+OS file system.
 * It allows the user to store, retrieve, delete, and rename a file and
 * list, change, remove and display a directory in the file system. 
 *
 * The @link FSInstallFileSystem   is used to setup the interface.
 * It supports the following commands:
 *  <list type="unordered">
 *      <item> STOR - store a file </item>
 *      <item> RETR - retrieve a file </item>
 *      <item> DELE - delete a file </item>
 *      <item> RNFR - rename a file from </item>
 *      <item> RNTO - rename a file to </item>
 *      <item> PWD - display the current working directory </item>
 *      <item> CWD - change the current working directory</item>
 *      <item> LIST - list a directory </item>
 *      <item> NLST - list a directory </item>
 *      <item> RMD - remove a directory </item>
 *  </list>
 *
 * FSInstallFileSystem() must be called after @link FSInitialize   to initalize 
 * the FTP server and before @link FSStartServer   to start the FTP server. If multiple volumes
 * are created, use slash \ before the volume name "\VOL1" to change to a
 * different volume.
 *
 * The following definition is used for file creation in the file system:
 *      <list type="unordered">
 *          <item>@link _FTPS_RESTRICTED_FILE_CREATION - Disabled by default, it will allow
 *                  a file to be overwritten. </item>
 *      </list>
 *
 * @external
 * @overview FTP Server & File System Interface
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0
 * @see @link Networking:FTP_Server
 * @see @link SystemSWServices:fileSystem
 * @platform all
 * @library filesys,ftpsvr
 * @include "fservapi.h"
*ad*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netosIo.h>
#include <Npttypes.h>
#include <fservapi.h>
#include <fclntapi.h>
#include <fs_api.h>
#include <fs.h>
#include <errno.h>
#include <sockapi.h>

#include "naflash.h"
#include "tx_api.h"
#include <Flash.h>
#include "bsp_api.h"
#include "reg_def.h"
#include "cs.h"
#include "gpiomux_def.h"
#include "yaffs_api.h"
#include "bootldr.h"
#include "fsintflocal.h"

static char gFtpSvrFileRootDir[NAFS_DIR_NAME_SIZE] = "\0";
static FS_FILESYSTEM_T *gFtpSvrFileHandles = NULL;
static TX_MUTEX gFtpSvrFileMutex;

/*ad*
 * Takes an internally formatted group id (bit mask) and transforms it into a more
 * human readable form (an integer in in the range of 1 - 8)
 *
 *
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.2 
 *
 * @param    groupid                unsigned integer representing an internal-format
 *                                  group id, to be converted into a human readable
 *                                  form
 *                        
 * @return  groupid                 The group id in a human readable form
 * @return  0                       Unable to convert the group id
 *
*ad*/
static int getGroupNumber(unsigned int groupid)
{
    
    int count=0;

    /* only check the first 8 bits */   
    while(count < 8){
	    /* 
	     * algorithmic explaination:
		 * group ids are stored as a bit mask where the first bit is called bit 1. The bit
		 * that is on is representative of the group number of the file. So bit one on means group
		 * 1. Bit two on means group 2.
		 * So we continually shift the group number in question (to the right) by an increasing number of 
		 * bits and use the 0x1 mask to find out which bit was on. Since the variable count started at
		 * 0, we increment count by one when returning the value to the caller. Thus when bit one is
		 * on, count is still 0, so we inccrement count by 1 to return 1...etc
		 */
	    if((groupid >> (count)) & 0x1){
	        return count+1;
	    }
		/* no on bit found yet, check next bit  */
	    count++;
    }
	/* no on bit found at all, input group id was probably bogus */
    return 0;
}


/*ad*
 * Tries to remove the file name from a file name and date string object returned from
 * the file system. 
 *
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.1 
 *
 * @param   FileNameToClean  Pointer to string contained the string that needs cleaning
 *                        
 * @return  FileNameToClean     Cleaned string is returned
 *                     
 *
 * @return  1      Operation was successful, string was cleaned
 *
 * @return 0       Operation was unsuccessful, string could not be cleaned
 *
*ad*/

int CleanUpFileName(char ** FileNameToClean)
{
	char * TokenizedString;
	char * ResultString;
	int Iteration = 0;
	int FileNameFound = 0;

    /* if any part of the input arg is null, fail */
    if(FileNameToClean == NULL)
	{
	    return -1;
	}

    if(*FileNameToClean == NULL)
	{
	    return -1;
	}

    TokenizedString = (char *)malloc(strlen(*FileNameToClean)+ 1);
	if(TokenizedString == NULL)
	{
	    return(0);
	}

    strcpy(TokenizedString, *FileNameToClean);
	TokenizedString[strlen(*FileNameToClean)] = '\0';
    
	/* Get First Token */
	ResultString = strtok(TokenizedString, " ");

    /* search the input string for the actual file name */
	do
	{
	  Iteration++;
	  if((strstr(ResultString, ":") == NULL))
	  {
	      /* No ':' found in the current token, we have either the year or the filename */
		  /* if this is iteration 1, then this is probably the filename so return it.
		   * if this is iteration 2, then the format is probably time/year/filename and
		   * this is the year, skip it
		   * If this is iteration 3, then this is the File name so return it
		   */
		  if((Iteration == 1) || (Iteration == 3))
		  {
		      strcpy(*FileNameToClean, ResultString);
			  FileNameFound = 1;
			  goto EndFNC_Processing;
		  } /* end if Iteration 1 or 3 */
		  		   
	  }
	/* get next token */
	/* for the second to nth token, NULL i sused in place of the variable */
	ResultString = strtok(NULL, " ");
	} while (ResultString != NULL); /* NULL means we hit the end of the string */

EndFNC_Processing:

    free(TokenizedString);
	if (FileNameFound == 1)
	    return 1;
	else
	    return 0;
} 

/*
 * Send the given buffer to the specified socket if there is not enough space
 * for the requested size. If the buffer is sent, it returns the beginning
 * of the buffer. Otherwise, it returns pointer that points to the available 
 * space. The caller is responsible to copy the data in the buffer.
 * 
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param   socket_fd  socket # where data will be sent to
 * param   buffer     pointer to the beginning of the response data which
 *                     will be sent if there is not enough space
 * param   sendPtr    pointer to the response data for the requested size
 * param   availSize  Size left in the buffer
 * param   needSize   Size is required in the buffer
 *                        
 * return  buffer     pointer to the beginning of the response data if
 *                      the response has been successfully send it.
 *
 * return  sendPtr    pointer to the current send data and the response data
 *                      has not been sent yet.
 *
 */

static char * fs_send_response(int socket_fd, char * buffer, char * sendPtr, int availSize, int needSize)
{
    char *thePtr = sendPtr, * buffer_ptr = buffer;
    int rc, error_code, length;

    if (availSize < needSize)
    {
        length = sendPtr - buffer;
        while(length > 0)
        {
            /* resend buffer if error code is EAGAIN */
            rc = send(socket_fd, buffer_ptr, length, 0);
            if (rc < 0)
            {
                error_code = getErrno();
                if (error_code != EAGAIN)
                {
                    printf("fs_send_response send() error %d\n", error_code);
                    thePtr = NULL;
                    goto exit_function;
                }
                tx_thread_sleep(1);
            }
            else
            {
                length -= rc;
                buffer_ptr += rc;
            }
        }
	thePtr = buffer;
    }
	
exit_function:

    return thePtr;
}

/*
 * Callback function which sets the event for the waiting thread
 */
static void fs_io_callback_fn(void * req_data)
{
    TX_EVENT_FLAGS_GROUP *eventPtr = (TX_EVENT_FLAGS_GROUP *)((NAFS_IO_REQUEST_CB *)req_data)->user_data;
    
    if (eventPtr != NULL)
        tx_event_flags_set(eventPtr, FS_IO_REQ_EVENT_BIT, TX_OR);    
}


/*
 * It waits for io request status from the file system.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    io_request       pointer to the file system io hander
 * param    byteTransferred  pointer to memory where number of bytes which has been processed
 *                              by the file system is copied to.
 * param    error_status     pointer to memory where error that is encountered by file 
 *                              system is copied to. 
 *                        
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPS_SUCCESS          No Error
 *
 * see link NAFS_IO_REQUEST_CB
 * see link file_sys_errors
 */
static int fs_wait_io_status(NAFS_IO_REQUEST_CB * io_request, unsigned int * byteTransferred, int * error_status)
{
    unsigned int        requestType, status;
    int                 rc;
    unsigned long       actual_event;
    TX_EVENT_FLAGS_GROUP * eventPtr = (TX_EVENT_FLAGS_GROUP *)io_request->user_data;

    do {    
    
        if (eventPtr != NULL) /* wait until we get an event */
            tx_event_flags_get(eventPtr, FS_IO_REQ_EVENT_BIT, TX_AND_CLEAR, &actual_event, TX_WAIT_FOREVER);
        else
            tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(10));
            
        /* inquire file system io request */    
        rc = NAFSio_request_cb_status(io_request, &requestType, &status, 
                                      error_status, byteTransferred); 

        if (rc == NAFS_SUCCESS && status == NAFS_IO_REQUEST_COMPLETED)
            break;

    } while (rc == NAFS_SUCCESS && (status == NAFS_IO_REQUEST_PENDING || 
            status == NAFS_IO_REQUEST_PROCESSING));

    if (*error_status != NAFS_SUCCESS)
        NA_DBPF("fs_wait_io_status Error: error status %d\n", *error_status);
    
    return  ((rc != NAFS_SUCCESS || *error_status != NAFS_SUCCESS) ? NAFTPS_FILESYSTEMERR : NAFTPS_SUCCESS);
}

/*
 * Parse the given full path into directory path and filename or the last directory 
 * name.
 * 
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param   fullPathName   Pointer to full path name to be parsed
 * param   dirpath        Pointer to memory where directory path will be written to
 * param   dirlen         Pointer to the max size of dirpath and will be set with
 *                          the actual length of dirpath.
 * param   filename       Pointer to memory where filename or last direcotry
 *                          will be written to
 * param   filelen        Pointer to the max size of filename and will be set with
 *                          the actual length of filename.
 *
 *                        
 * return  FILE_OK    directory and dirname have been successfully obtained
 * return  INVALID_FILE_LEN       Invalid filename length
 * return  INVALID_DIRECTORY_LEN   Invalid directory length
 *
 */
static int parse_file_path(char * fullPathName, char * dirpath, int * dirlen, char * filename, int * filelen)
{

    char    * bufferPtr, * namePtr = NULL;
    int     len = 0, rc = FILE_OK;
	int     LoopIdx = 0;

	NA_ASSERT((int)fullPathName);
    NA_ASSERT((int)dirpath);
    NA_ASSERT((int)filename);

    /* search for slash until the last slash */
    bufferPtr = fullPathName;
    while (bufferPtr != NULL) 
    {
        if (strstr(bufferPtr, "\\") != NULL)
            bufferPtr = strstr(bufferPtr, "\\");
        else
            bufferPtr = strstr(bufferPtr, "/");

        if (bufferPtr != NULL)
        {
            bufferPtr++;
            namePtr = bufferPtr;
        }
    }
    if (namePtr != NULL) 
    {   /* got filename */
        len = (namePtr - fullPathName)-1;
    }
    /* no directory is given */
    if (namePtr == NULL) 
        namePtr = fullPathName;

	/* we need to be careful here. The internal file name std says
	* that the first character for a path can not be /. So we do not want
	* the strncpy to begin, until we get to non-/ characters. In the
	* future, we should keep an eye on this code.
	*/
    if (len > 0 && len < *dirlen)
    {
        strncpy(dirpath, fullPathName, len);
        dirpath[len] = '\0';

        /* clean up the directory path */
        bufferPtr = dirpath;
	    for (LoopIdx = 0; LoopIdx < *dirlen; LoopIdx++)
	    {
	        if (!((bufferPtr[LoopIdx] == '/') || (bufferPtr[LoopIdx] == '\\')) )
		    {
		        /* as soon as we find the first non-/, we are done */
		        break;
		    }
		    else
		    {
		        bufferPtr++;
		    }
	    }
	    *dirlen = strlen(bufferPtr);
	    if(*dirlen == 0)
	    {
            rc = INVALID_DIRECTORY_LEN;
	    }

        strncpy(dirpath, bufferPtr, *dirlen);
        dirpath[*dirlen] = '\0';

        if (strstr(dirpath, "/") == NULL && strstr(dirpath, "\\") == NULL)
            strcat(dirpath, "/");  /* root directory requires "/" */

        *dirlen = strlen(dirpath);
    }
    else if (len > *dirlen)
        rc = INVALID_DIRECTORY_LEN;

    if (strlen(namePtr) <= (unsigned int)*filelen)
        strcpy(filename, namePtr);
    else
        rc = INVALID_FILE_LEN;

    *filelen = strlen(namePtr);

    return rc;    
}


/*
 * Set the full directory path. It takes the current directory and new directory
 * and copies into a buffer.
 * 
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param   buffer     pointer to memory where the full directory path will
 *                     be written to. It will contain the full path.
 * param   szbuffer   size of buffer
 * param   cur_dir    pointer to the current directory
 * param   new_dir    pointer to the request directory
 *                        
 * return  FILE_OK                Successfully copy full directory path
 * return  INVALID_DIRECTORY_LEN  Invalid directory length
 */
static int set_request_path(char * buffer, int szbuffer, char * cur_dir, char * new_dir)
{
    char * givenDir;
    char * thisDir;
    int  len;
    int  rc = FILE_OK;

    NA_ASSERT((int)buffer);
    NA_ASSERT((int)new_dir);
    
    /* since the file system require forward slash, change all backward
     * slashes to forward slashes
     */
    givenDir = new_dir;
    while (strstr(givenDir, "\\") != NULL)
    {
        givenDir = strstr(givenDir, "\\");
        *givenDir = '/'; /* change it to forward slash */
        givenDir++;
    }

    givenDir = new_dir;

    if (strcmp(givenDir, ".") == 0)
    {   /* no directory change */
        strcpy(buffer, cur_dir);
    }
    /* see whether it refers back to root directory */
    else if ((*givenDir == '/'))
    {   
        if (strlen(givenDir) >= (unsigned int)szbuffer)
        {
            rc = INVALID_DIRECTORY_LEN;
            goto _ret;
        }
        if (strlen(givenDir) > 1)
        {   /* slash + directory name (/DIR_NAME) is given */
            givenDir++;
            strcpy(buffer, givenDir);
            /* If this is root directory, make sure slash is at the end.
             * The file system requires slash at the end of root directory.
             */ 
            len = strlen(buffer);
            givenDir = strstr(buffer, "/");
            if (givenDir == NULL)
            {
                buffer[len] = '/'; /* must be forward slash */
                buffer[len+1] = '\0';
            }
            else if (givenDir < (buffer + len -1))
            {   /* no slash at the end if not root directory */
                if (buffer[len-1] == '/')
                {
                    buffer[len-1] = '\0';
                }
            }
        }
        else 
        {   /* only slash */
            thisDir = NULL;
            if (strlen(cur_dir) > 0)
            {
                /* Get the current directory and go back to root directory */
                thisDir = strstr(cur_dir, "/");
            }

            if (thisDir != NULL)
            {
                strcpy(buffer, cur_dir); 
                thisDir++;
                buffer[thisDir-cur_dir] = '\0';
            }
            else
            {   /* default root directory */
                strcpy(buffer, gFtpSvrFileRootDir);
            }
        }
    }
    else 
    {
        NA_ASSERT((int)cur_dir);

        if (strlen(cur_dir) >= (unsigned int)szbuffer)
        {
            rc = INVALID_DIRECTORY_LEN;
            goto _ret;
        }

        strcpy(buffer, cur_dir);

        thisDir = buffer;
        givenDir = new_dir;

        while (strstr(givenDir, "..") == givenDir || strstr(givenDir, ".") == givenDir)
        {
            if (strstr(givenDir, "..") == givenDir)
                len = 2;
            else 
                len = 1;

            givenDir += len;

            if ((strlen(givenDir) > 0) && (givenDir[0] != '\\') && (givenDir[0] != '/'))
            {   /* invalid back reference */
                givenDir -= len;
                break;
            }
            givenDir++; /* skip slash */

            /* look for last slash on current directory
             */
            while (strchr(thisDir, '/') != NULL)
            {
                thisDir = strchr(thisDir, '/');
                thisDir++;
            }

            if (thisDir != NULL)
            {   /* remove last directory */
                *(thisDir-1) = '\0';
                thisDir = buffer;
            }
        }

        if (givenDir != NULL)
        {
            len = strlen(buffer);
            if (buffer[len-1] != '/')
            {   /* if no slash at the end, add slash before adding the new request directory */
                if (strlen(givenDir) > 0 && len < (szbuffer-2))
                {
                    len = strlen(buffer);
                    buffer[len] = '/';
                    buffer[len+1] = '\0';
                }
            }

            if (givenDir[0] == '/')
                givenDir++;
            if (strlen(givenDir) >= (szbuffer-strlen(buffer))) 
            {
                rc = INVALID_DIRECTORY_LEN;
                goto _ret;
            }

            strcat(buffer, givenDir);   /* add new directory */
        }

        len = strlen(buffer);
        givenDir = strchr(buffer, '/');
        /* must have at least one slash for root directory */
        if (givenDir == NULL)
        {
            len = strlen(buffer);
            if (len >= (szbuffer-2))
            {
                rc = INVALID_DIRECTORY_LEN;
                goto _ret;
            }

            buffer[len] = '/';  
            buffer[len+1] = '\0';
        }            
        else if (givenDir < (buffer + len -1))
        {   /* no slash at the end if not root directory */
            if (buffer[len-1] == '/')
            {
                buffer[len-1] = '\0';
            }
        }

    }

#if (_DEBUG_PRINTF > 0)
    printf("set_request_path: %s size %d\n", buffer, strlen(buffer));
#endif
_ret:
    return rc;
}


/*
 * Validate the given group id
 * 
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param   permission  access permission 
 *                        
 * return  0          Invalid group id
 * return  1          valid group id
 *
 * see link securityLevels
 */
static BOOLEAN is_valid_group_id(unsigned int permission)
{
    BOOLEAN isValid = 0;
    unsigned int group_id;

    /* we're only interested in WRITE bit */
    group_id = (permission >> _WRITE_PERMISSION_BIT);
	switch (group_id)
	{
	    case NAFS_FS_GROUP1:
	    case NAFS_FS_GROUP2:
	    case NAFS_FS_GROUP3:
	    case NAFS_FS_GROUP4:
	    case NAFS_FS_GROUP5:
	    case NAFS_FS_GROUP6:
	    case NAFS_FS_GROUP7:
	    case NAFS_FS_GROUP8:
	        isValid = 1;
	        break;
	    default:
	        break;
    }
    return isValid;
}

/*
 * Returns the group id for the given directory
 *
 * The given file system handle must have access permission to access the given 
 * directory. It accesses the directory and returns the group id of the directory.
 *
 * It returns a smaller group id for the root directory if the file system handle 
 *  contains multiple accesses.
 * 
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param   pFile       Pointer to the file system handle
 * param   dir_path    Pointer to the directory where the group id will be returned.
 * param   group_id    Pointer to memory where the group id will be returned
 *                        
 * return  NAFTPS_SUCCESS      The group id was successfully obtained.
 * return  NAFTPS_GENERROR     Unable to get the group id of the directory.
 *
 * see link file_sys_errors
 * see link group_masks
 */
int get_group_id(FS_FILESYSTEM_T * pFile, char * dir_path, unsigned int * group_id)
{
    int          rc = NAFTPS_GENERROR, status;    
    unsigned int i, byteTransferred;
    char         prev_dir[NAFS_DIRECTORY_PATH_SIZE];
    char         cur_dir[NAFS_FILE_NAME_SIZE];
    int          prev_dirlen, cur_dirlen;
    unsigned int write_group;
  

    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);

	if (pFile->error_status != NAFS_SUCCESS) goto _ret;

    prev_dirlen = sizeof prev_dir;
    cur_dirlen = sizeof cur_dir;

	if (parse_file_path(dir_path, prev_dir, &prev_dirlen, cur_dir, &cur_dirlen) != FILE_OK)
	    goto _ret;
    

        /* root directory */
    write_group = pFile->permissions >> _WRITE_PERMISSION_BIT;

    for (i=0x01; i <= 0x80; i = i << 1)
    {
        if ((write_group & i) != 0)
        {
            *group_id = i;
            break;
        }
    }
    if (cur_dirlen == 0)
    {
        rc = NAFTPS_SUCCESS;
        goto _ret;
    }
        /* Get number of entry in the directory first.
     * Then allocate memory for the directory info
     * before get the directory listing info.
     * Error if no entry or no such directory.
     */
    pFile->error_status = NAFSdir_entry_count(prev_dir, pFile->permissions, 
                        &pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _ret;

    if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS)
        goto _ret;

    pFile->dir_info_list_items++; /* at least one entry */

    pFile->dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(pFile->dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

    if (pFile->dir_info_list == NULL) goto _ret;
	    
    memset(pFile->dir_info_list, 0, (pFile->dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

    /* Get info on the previous directory and make sure the specified directory is
     * existed.
     */
    pFile->error_status = NAFSlist_dir(prev_dir, pFile->permissions, pFile->dir_info_list, 
                                        pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _clean;

    status = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

    if (status == NAFTPS_SUCCESS && pFile->error_status == NAFS_SUCCESS)
    {
        for (i=0; i < pFile->dir_info_list_items; i++)
        {
            if (pFile->dir_info_list[i].entry_type  == NAFS_FILE_TYPE_DIR &&
                strcmp(pFile->dir_info_list[i].entry_name, cur_dir) == 0)
            {
                *group_id = pFile->dir_info_list[i].group_id;
            #if (_DEBUG_PRINTF > 0)
                printf("get_group_id: %s directory has 0x%x group id.\n", prev_dir, *group_id);
            #endif
                rc = NAFTPS_SUCCESS;
                break;
            }
        }
    }
_clean:
    free(pFile->dir_info_list);
    pFile->dir_info_list = NULL;

_ret:
    return rc;
}
/*
 * Get the file system handle for the given FTP Server handle. It returns
 * the current file system handle for the given FTP server handle.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    handle    handle to access FTP server client connection  
 *
 *                        
 * return  Non-zero    Pointer to the current file system handle 
 * return  NULL        Unable to find the current file system handle for
 *                      the given FTP server handle
 *
 */
static FS_FILESYSTEM_T * get_filesystem_cb(unsigned long handle)
{
    FS_FILESYSTEM_T * pFile = NULL; 
    int             i;
   
    if (tx_mutex_get(&gFtpSvrFileMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
    {
        for (i=0; i < FSGetNumberOfUser(); i++)
        {
            if (gFtpSvrFileHandles[i].fs_handle == handle)
            {
                pFile = &gFtpSvrFileHandles[i];
                break;
            }

        }

        tx_mutex_put(&gFtpSvrFileMutex);
    }
    return pFile;
}

/*
 * Get a new file system handle for the given FTP Server handle. It returns
 * an unused file system handle for a new FTP connection.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    handle    handle to access FTP server client connection  
 *
 *                        
 * return  Non-zero   Pointer to a new file system handle 
 * return  NULL       No new file system handle available
 *
 */
static FS_FILESYSTEM_T * allocate_filesystem_cb(unsigned long handle)
{
    int             i;
    FS_FILESYSTEM_T * pFile = NULL; 
   
    if (tx_mutex_get(&gFtpSvrFileMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
    {
        for (i=0; i < FSGetNumberOfUser(); i++)
        {
            if (gFtpSvrFileHandles[i].fs_handle == 0)
            {
                pFile = &gFtpSvrFileHandles[i];
                pFile->fs_handle = handle;
                strcpy(pFile->directory_path, gFtpSvrFileRootDir);
                pFile->permissions =  FSGetAccessCode(handle);
                memset(&pFile->io_event, 0, sizeof(TX_EVENT_FLAGS_GROUP));
                if (tx_event_flags_create(&pFile->io_event, "FTPSvr File IO") == TX_SUCCESS)
                    pFile->io_request.user_data = (unsigned int)&pFile->io_event;
                else
                    pFile->io_request.user_data = 0;
                                    
                break;
            }

        }
        tx_mutex_put(&gFtpSvrFileMutex);
    }
    return pFile;
}

/*
 * Return the file system handle for the given FTP Server handle. It makes
 * this file system handle available for another connection.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    handle    handle to access FTP server client connection  
 *
 *                        
 * return  NAFTPS_SUCCESS   the file system handle has been successfully freed
 * return  NAFTPS_GENERROR  Unable to free the file system handle
 *
 * see link file_sys_errors
 */
static int free_filesystem_cb(unsigned long handle)
{
    int i;
    int rc = NAFTPS_GENERROR;
   
    if (tx_mutex_get(&gFtpSvrFileMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
    {
        for (i=0; i < FSGetNumberOfUser(); i++)
        {
            if (gFtpSvrFileHandles[i].fs_handle == handle)
            {
                gFtpSvrFileHandles[i].fs_handle = 0;
                if (gFtpSvrFileHandles[i].dir_info_list != NULL)
                {
                    free(gFtpSvrFileHandles[i].dir_info_list);
                    gFtpSvrFileHandles[i].dir_info_list = NULL;
                }
                gFtpSvrFileHandles[i].file_handler = 0;
                gFtpSvrFileHandles[i].file_offset = 0;
                gFtpSvrFileHandles[i].file_size = 0;
                gFtpSvrFileHandles[i].directory_path[0] = '\0';
                tx_event_flags_delete(&gFtpSvrFileHandles[i].io_event);
                gFtpSvrFileHandles[i].io_request.user_data = 0;
            
                rc = NAFTPS_SUCCESS;
                break;
            }

        }
        tx_mutex_put(&gFtpSvrFileMutex);
    }
    return rc;
}


/*
 * This function closes a file if any.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    pFile   pointer to internal FTP structure
 *
 * return  NAFTPS_SUCCESS   No file needs to be closed or a File has been successfully closed
 * return  NAFTPS_GENERROR  Unable to close a file
 *
 * see link file_sys_errors
 */
static int fs_close_file(FS_FILESYSTEM_T * pFile)
{
    int                 rc = NAFTPS_SUCCESS;
    unsigned int        byteTransferred;
    int                 error_status;

    if (pFile != NULL)
    {
	    if ((pFile->file_handler != 0)
	    	&& (pFile->file_handler != (unsigned int)-1))
	    {
	        rc = NAFSclose_file(pFile->file_handler, pFile->permissions, &pFile->io_request); 
	        if (rc != NAFS_SUCCESS)
		    {
                NA_DBPF("fs_close_file Error: NAFSclose_file faild %d\n", rc);
                rc = NAFTPS_GENERROR;
            }
            else if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &error_status) != NAFTPS_SUCCESS)
		    {
                NA_DBPF("fs_close_file Error: NAFSclose_file error status %d\n", error_status);
                rc = NAFTPS_GENERROR;
            }
            else
            {
            #if (_DEBUG_PRINTF > 0)
                printf("fs_close_file: NAFSclose_file 0x%X file size %d\n", 
                                pFile->file_handler, pFile->file_size);
            #endif
    		    pFile->file_handler = 0;
                rc = NAFTPS_SUCCESS;
            }
        }
	}

    return rc;
}

/*
 * This function writes permission in the output buffer.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.2 
 *
 * param    output      pointer to buffer to be written for the permission
 * param    group_id    contains group id 
 * param    permissions contains permission. Read or write permission will be 
 *                      written to the output buffer if this permission has access
 *                      to the given group id.
 * param    entry_type  contains either NAFS_FILE_TYPE_DIR for directory
 *                      or NAFS_FILE_TYPE_FILE for file.
 *
 *                        
 * return  "pointer to end of output buffer"
 *
 */
char * output_permission_format(char * output, unsigned int group_id, unsigned long permissions, unsigned int entry_type)
{
    unsigned long perm;
    char          *destPtr;
    
    destPtr = output;
    
    if (destPtr != NULL)
    {
    
        /* read permission */ 
        perm = group_id << _READ_PERMISSION_BIT;
        if ((permissions & perm))
        {
            strcpy(destPtr, "r");
        }
        else 
        {
            strcpy(destPtr, "-");
        }
        destPtr += 1;

        /* write permission */ 
        perm = group_id << _WRITE_PERMISSION_BIT;
        if ((permissions & perm))
        {
            strcpy(destPtr, "w");
        }
        else 
        {
            strcpy(destPtr, "-");
        }
        destPtr += 1;
        
        /* l for locking if it's a file */
        if (entry_type == NAFS_FILE_TYPE_DIR)
        {
            strcpy(destPtr, "-");

        }
        else
        {
            strcpy(destPtr, "l");
        }
        destPtr += 1;
    }
    
    return destPtr;
}    
/*
 * This function is used to get a directory info and send it to the 
 * FTP client.
 *
 * category Networking:FTP_Server:File_System_Interface
 * since 6.0 
 *
 * param    argp      pointer to a directory name to be retrieved list info
 * param    handle    handle to access FTP server client connection  
 * param    format    indicating whether all listing info includes size & date/time
 *                      or only item name to be sent to FTP client 
 *          <list type="unordered">
 *          <item> LIST_FILE_INFO </item>
 *          <item> LIST_FILE_ONLY </item>
 *          </list>
 *
 *                        
 * return  NAFTPS_SUCCESS   Directory info has been successfully retrieved and sent
 * return  NAFTPS_GENERROR  Unable to get directory listing 
 *
 * see link file_sys_errors
 */
static int list_dir(char * argp, unsigned long handle, int format)
{
    FS_FILESYSTEM_T     * pFile; 
    int                 rc = NAFTPS_GENERROR;
    struct tm           * file_tm;
    unsigned int        i, byteTransferred;
    int		            socket_fd, len;
    char                * sendPtr;
    int                 availSize;
    unsigned long       current_time;
    
	/* changed to mixed upper and lower cases */
    static char const   * month[] =
    {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };

    pFile = get_filesystem_cb(handle);   

    if (pFile == NULL)
    {   /* new request */
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _list_dir_ret;

    /* set the argument path */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _list_dir_ret;        

    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
    if (pFile->error_status != NAFS_SUCCESS) goto _list_dir_ret;

    /* get number of entry in the directory first.
     * Then allocate memory to same the directory info
     * before get the directory listing.
     */
     
#if (_RETRICTED_USER_LISTING > 0)
    pFile->error_status = NAFSdir_entry_count(pFile->buffer, pFile->permissions, 
                        &pFile->dir_info_list_items, &pFile->io_request);
#else
    pFile->error_status = NAFSdir_entry_count(pFile->buffer, NASYSACC_FS_SUPER_USER, 
                        &pFile->dir_info_list_items, &pFile->io_request);
#endif

    if (pFile->error_status != NAFS_SUCCESS) goto _list_dir_ret;

    if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS) 
        goto _list_dir_ret;

    if (pFile->dir_info_list_items == 0) 
    {   /* no entry in the directory */
        socket_fd = FSHandleToSocket(handle);
        strcpy(pFile->buffer, "Empty Directory\n");
        send(socket_fd, pFile->buffer, strlen(pFile->buffer), 0);
        rc = NAFTPS_SUCCESS;
        goto _list_dir_ret;
    }

    /* Now, we know how many entries in the directory so let's
     * allocate memory to store the directory info.
     */
    pFile->dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(pFile->dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

    if (pFile->dir_info_list != NULL) 
    {
        memset(pFile->dir_info_list, 0, (pFile->dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

        /* get directory list */
#if (_RETRICTED_USER_LISTING > 0)
        pFile->error_status = NAFSlist_dir(pFile->buffer, pFile->permissions, pFile->dir_info_list, 
                        pFile->dir_info_list_items, &pFile->io_request);
#else
        pFile->error_status = NAFSlist_dir(pFile->buffer, NASYSACC_FS_SUPER_USER, pFile->dir_info_list, 
                        pFile->dir_info_list_items, &pFile->io_request);
#endif
        if (pFile->error_status != NAFS_SUCCESS) goto _list_dir_clean;

        if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS) 
            goto _list_dir_clean;

        socket_fd = FSHandleToSocket(handle);

        sendPtr = pFile->buffer;
        availSize = 0;
        for (i=0; i < pFile->dir_info_list_items; i++)
        {

            if (format == LIST_FILE_INFO)
            {
                #define _PERMISSION_FORMAT_SIZE_REQUIRED    22
                #define _GROUP_SIZE_REQUIRED                16
                #define _DATE_SIZE_REQUIRED                 13
                
                /* Needs _PERMISSION_FORMAT_SIZE_REQUIRED char for permission format. 
                 * Send response to make room for permission format if the response 
                 * buffer doesn't have enough room for it
                 */
                availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
                sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, (_PERMISSION_FORMAT_SIZE_REQUIRED+1));
                if (sendPtr == NULL) goto _list_dir_ret;

                /* Set 'd' for directory or '-' for a file. */
                if (pFile->dir_info_list[i].entry_type == NAFS_FILE_TYPE_DIR)
                {
                    strcpy(sendPtr, "d");

                }
                else
                {
                    strcpy(sendPtr, "-");
                }
                sendPtr += 1;
               
                /* 1st is the owner's permissions. */ 
                sendPtr = output_permission_format(sendPtr, pFile->dir_info_list[i].group_id, 
                                                            pFile->permissions, 
                                                            pFile->dir_info_list[i].entry_type);
                
                /* 2nd is permissions for the group which is same as owner's permission */
                sendPtr = output_permission_format(sendPtr, pFile->dir_info_list[i].group_id, 
                                                            pFile->permissions, 
                                                            pFile->dir_info_list[i].entry_type);

                /* next is permissions for others which we don't let the others to access
                 * without group's permissions
                 */
                strcpy(sendPtr, "--- 1 noone    "); /* jzw added 3 spaced */
                sendPtr += (_PERMISSION_FORMAT_SIZE_REQUIRED-7); /* changed from 12 to 15, 3 extra spaces */

            
                /* Needs _GROUP_SIZE_REQUIRED chars for group id & size format. 
                 * Send response to make room for this format if the response 
                 * buffer doesn't have enough room for it
                 */
                availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
                sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, _GROUP_SIZE_REQUIRED);
                if (sendPtr == NULL) goto _list_dir_ret;


				/* 
				 * use the new routine to convert the internal format group id into a more
				 * human-readable format
				 */
                len = sprintf(sendPtr, "group%d %-8d ",getGroupNumber(pFile->dir_info_list[i].group_id),
                                        pFile->dir_info_list[i].file_size);
		        sendPtr += len;

                /* Needs _DATE_SIZE_REQUIRED chars for date format. 
                 * Send response to make room for this format if the response 
                 * buffer doesn't have enough room for it
                 */
                availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
                sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, _DATE_SIZE_REQUIRED);
                if (sendPtr == NULL) goto _list_dir_ret;

                file_tm = localtime(&pFile->dir_info_list[i].last_modified_time);
                
                time((time_t *)&current_time);

                /* The format allowed (especially by IE seems to be Month Day and either
                 * Time or Year but not both. 
                 * We are going to send the time if it's with FS_MAX_SECONDS otherwise
                 * it sends the year.
                 */
                if ((current_time - pFile->dir_info_list[i].last_modified_time) > FS_MAX_SECONDS)
                {   /* return year */
                    if (file_tm != NULL)
                        len = sprintf(sendPtr, "%s %02d %04d ", month[file_tm->tm_mon],
                                    file_tm->tm_mday, (file_tm->tm_year + 1900));
                    else
                        len = sprintf(sendPtr, "%s 01 1979 ", month[0]);

                }
                else if (file_tm != NULL)
                {   /* return time */
                    len = sprintf(sendPtr, "%s %02d %02d:%02d ", month[file_tm->tm_mon],
                                file_tm->tm_mday, file_tm->tm_hour, file_tm->tm_min);
                }
                else
                {
                    len = sprintf(sendPtr, "%s 01 00:00 ", month[0]);
                }

                sendPtr += len;
            }

            /* Needs the size for the entry name. 
             * Send response to make room for this format if the response 
             * buffer doesn't have enough room for it
             */
            availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
            sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, 
                            (strlen(pFile->dir_info_list[i].entry_name) + 1));
            if (sendPtr == NULL) goto _list_dir_ret;

            len = sprintf(sendPtr, "%s\r\n", pFile->dir_info_list[i].entry_name);
            sendPtr += len;
        }

        if ((sendPtr-pFile->buffer) > 0)
        {
            availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
            sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, sizeof pFile->buffer);
            if (sendPtr == NULL) goto _list_dir_ret;
        }

        rc = NAFTPS_SUCCESS;

_list_dir_clean:
        free(pFile->dir_info_list);
        pFile->dir_info_list = NULL;
    }
    
_list_dir_ret:
    return rc;                            
}

/*ad*
 * Create and write a file in the NET+OS file system.
 * 
 * User needs to call @link FSRegisterSTOR  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, the user doesn't need to register this function.
 *
 * This function is called multiple time until it finishes all data of a file.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    bufferp   pointer to buffer which contains file data 
 * @param    buflen    length of a buffer
 * @param    argp      pointer to a filename to be created
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   A file has been successfully created and written
 * @return  NAFTPS_GENERROR  Unable to create or write a file 
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_StoreFile(char * bufferp, int buflen, char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    byteTransferred;
	int				sendlen;
	char			* sendPtr;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            filename[NAFS_FILE_NAME_SIZE];
    int             cur_dirlen, filelen;
    unsigned int    group_id;

    NA_ASSERT((int)handle);
    if (buflen > 0)
        NA_ASSERT((int)bufferp);

#if (_DEBUG_PRINTF > 0)
	printf("ftp store %d bytes %s (%x)\n\r", buflen, argp, handle);
#endif
    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _store_file_ret;

	if (pFile->file_handler == 0)
	{
        /* 1st time here so let's get file name */

        /* set the argument path */
        if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
            goto _store_file_ret;        

        cur_dirlen = sizeof cur_directory;
        filelen = sizeof filename;
	    if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK)
		    goto _store_file_ret;

	    /* create a file. */
	    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
	    if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;

						 
		pFile->file_offset = 0;
		pFile->file_size = 0;

    	/* if the group id from the login user is not valid, use default group id */
        group_id = (pFile->permissions >> _WRITE_PERMISSION_BIT);
        if (!is_valid_group_id(pFile->permissions))
        {
            if (get_group_id(pFile, cur_directory, &group_id) != NAFTPS_SUCCESS)
		        goto _store_file_ret;
        }

		pFile->io_request.arg2 = O_CREAT | O_TRUNC | O_RDWR;  /* for yaffs. jhl */
	    pFile->error_status = NAFScreate_file(cur_directory, filename, pFile->permissions,
                            group_id, &pFile->io_request);

		if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;

        /* If this is ok, we need to wait until the file has been successfully created.
         * If not, try open to see whether it can be opened.
         */
		if (pFile->error_status == NAFS_SUCCESS) 
        {
    		fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
        }

#if (_FTPS_RESTRICTED_FILE_CREATION > 0)
		if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;
#endif
        
#if (_FTPS_RESTRICTED_FILE_CREATION == 0)
        do {
#endif
			pFile->io_request.arg2 = O_RDWR;  /* for yaffs. jhl */
    	    pFile->error_status = NAFSopen_file(cur_directory, filename, 
    	                                        &pFile->file_handler, pFile->permissions, 
    	                                        &pFile->io_request);


    		if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;
	
    		rc = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

            if (pFile->error_status == NAFS_FILE_IS_OPEN)
                tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
	    	else if (rc != NAFTPS_SUCCESS ) goto _store_file_ret;
            
#if (_FTPS_RESTRICTED_FILE_CREATION == 0)
        } while (pFile->error_status == NAFS_FILE_IS_OPEN);
#endif
		pFile->io_request.arg2 = 0;  /* clean up */

        pFile->error_status = NAFSwrite_file(pFile->file_handler, pFile->permissions, 
                                    1, 0, filename,
		                            &pFile->io_request, NAFS_ERASE_DATA);
		if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;

		rc = fs_wait_io_status(&pFile->io_request, &byteTransferred,
					   	&pFile->error_status);

		if (rc != NAFTPS_SUCCESS) goto _store_file_ret;
	}


	sendlen = buflen;
	sendPtr = bufferp;

    /* keep writing until it finishes the whole buffer */
	while (sendlen > 0)
	{   
        pFile->error_status = NAFSwrite_file(pFile->file_handler, pFile->permissions, 
                                        sendlen, pFile->file_offset, sendPtr,
		                                //&pFile->io_request, NAFS_ZERO_OFFSET_ERASE);
		                                &pFile->io_request, NAFS_RANDOM_WRITE);

		if (pFile->error_status != NAFS_SUCCESS) goto _store_file_ret;

		rc =fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

		if (rc != NAFTPS_SUCCESS)
                {
                   if ((pFile->error_status == NAFS_NO_FREE_BLOCKS) || (pFile->error_status == NAFS_NO_FREE_INODES))
                   {
                       printf("FS_StoreFile volume full\n");
                       rc = (-2); /* a negative 2 indicates to the FTP server that the volume is full */
                   }
		   goto _store_file_ret;
                }

    #if (_DEBUG_PRINTF > 0)
        printf("fs_store_file: NAFSwrite_file 0x%X size %d\n", pFile->file_handler, byteTransferred);
    #endif
		sendlen -= byteTransferred;
		sendPtr += byteTransferred;
		pFile->file_size += byteTransferred;
        pFile->file_offset += byteTransferred;
	}

    if (sendlen == 0) rc = NAFTPS_SUCCESS;

_store_file_ret:

	return rc;   
}

/*ad*
 * Retrieve and send a file from the NET+OS file system to the FTP client
 *
 * User needs to call @link FSRegisterRETR  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, the user doesn't need to register this function.
 *
 * This function is called multiple time until it finishes all data of a file. On first call 
 * it should return NAFTPS_CONTINUE after it has succsessfully opened the file. This is necessary
 * to give FTP server a chance to send a 150 response on a control connection before file transfer
 * starts on a data connection. On a second call the callback should skip opening the file. 
 * It can block while sending the whole file. 
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a filename to be retrieved and sent to FTP client
 * @param    handle    handle to access FTP server client connection  
 *
 * @return  NAFTPS_CONTINUE  FTP server must call this callback again                 
 * @return  NAFTPS_SUCCESS   A file has been successfully retrieved and sent
 * @return  NAFTPS_GENERROR  Unable to send file to FTP client
 *
 * @see @link NAFTPS
*ad*/
int FS_RetrieveFile(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    int             byteTransferred;
    int	            sendlen;
    char            *sendPtr;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            filename[NAFS_FILE_NAME_SIZE];
    int             cur_dirlen, filelen;
    int	            socket_fd;
    int             error_code;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _retrieve_file_ret;

	if (pFile->error_status == NAFTPS_CONTINUE)
	{ 
		pFile->error_status = NAFS_SUCCESS;
		rc = NAFTPS_GENERROR;
	}
	else
	{
		/* Windows systems return file names in a state that is not useful to the file system
		 * so lets clean up the file name first
		 */
		 CleanUpFileName(&argp);


		if (pFile->file_handler == 0)
		{   /* 1st time here so let's get file name and open it */
			/* set the argument path */
			if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
				goto _retrieve_file_ret;        

			cur_dirlen = sizeof cur_directory;
			filelen = sizeof filename;
			if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK)
				goto _retrieve_file_ret;

			/* open a file. */
			pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
			if (pFile->error_status != NAFS_SUCCESS) 
				goto _retrieve_file_ret;

							 
			pFile->file_offset = 0;
			pFile->file_size = 0;

			do {
    			pFile->error_status = NAFSopen_file(cur_directory, filename, 
    												&pFile->file_handler, pFile->permissions, 
    												&pFile->io_request);


    			if (pFile->error_status != NAFS_SUCCESS) 
					goto _retrieve_file_ret;

		
	    		rc = fs_wait_io_status(&pFile->io_request, (unsigned int *)&byteTransferred, &pFile->error_status);

				if (pFile->error_status == NAFS_FILE_IS_OPEN)
					tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
	    		else if (rc != NAFTPS_SUCCESS ) 
					goto _retrieve_file_ret;

			} while (pFile->error_status == NAFS_FILE_IS_OPEN);
		}
		rc = pFile->error_status = NAFTPS_CONTINUE;
		goto _retrieve_file_ret;
	}
    socket_fd = FSHandleToSocket(handle);

    do {
        /* We keep sending data to ftp client until we finish the whole file */
        pFile->error_status = NAFSread_file(pFile->file_handler, pFile->permissions, 
                                            sizeof pFile->buffer, pFile->file_offset, 
                                            pFile->buffer, &pFile->io_request);

        if (pFile->error_status != NAFS_SUCCESS) 
			goto _retrieve_file_ret;

	    rc = fs_wait_io_status(&pFile->io_request, (unsigned int *)&byteTransferred, &pFile->error_status);

	    if (rc != NAFTPS_SUCCESS && pFile->error_status != NAFS_END_OF_FILE) 
	        goto _retrieve_file_ret;
		
        sendlen = byteTransferred;
        sendPtr = pFile->buffer;
        pFile->file_size += byteTransferred;
        pFile->file_offset += byteTransferred;

    #if (_DEBUG_PRINTF > 0)
        printf("fs_retrieve_file: NAFSread_file 0x%X size %d\n", pFile->file_handler, byteTransferred);
    #endif
        while (sendlen > 0)
        {   /* keep sending data until all data in the buffer is sent */
            byteTransferred = send(socket_fd, sendPtr, sendlen, 0);
            if (byteTransferred < 0)
            {
                error_code = getErrno();
                if (error_code != EAGAIN)
                {
                    printf ("FS_RetrieveFile send() error %d\n", error_code);
                    rc = NAFTPS_GENERROR; 
                    goto _retrieve_file_ret;
                }
                tx_thread_sleep(1);
            }
            else
            {
                sendlen -= byteTransferred;
                sendPtr += byteTransferred;
            }
        }

    } while (pFile->error_status != NAFS_END_OF_FILE);

    if (pFile->error_status) 
        rc = NAFTPS_SUCCESS;    /* done */
    else 
        rc = NAFTPS_CONTINUE;

_retrieve_file_ret:

	return rc;   
}

/*ad*
 * Delete a file in the NET+OS file system
 *
 * User needs to call @link FSRegisterDELE  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 * 
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a file name to be deleted
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   A file has been successfully deleted
 * @return  NAFTPS_GENERROR  Unable to delete the file
 *
 * @see @link NAFTPS
*ad*/
int FS_DeleteFile(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            filename[NAFS_FILE_NAME_SIZE];
    int             cur_dirlen, filelen;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _delete_file_ret;

    /* set the argument path */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _delete_file_ret;        

    cur_dirlen = sizeof cur_directory;
    filelen = sizeof filename;

	if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK)
		goto _delete_file_ret;

	pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
	if (pFile->error_status != NAFS_SUCCESS) goto _delete_file_ret;

    pFile->error_status = NAFSdelete_file(cur_directory, filename, pFile->permissions, &pFile->io_request);
	if (pFile->error_status != NAFS_SUCCESS) goto _delete_file_ret;

    rc = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

_delete_file_ret:
    return rc;
}

/*ad*
 * Rename a file in the NET+OS file system
 * 
 * This function saves the specified filename and makes sure this file 
 * exists. @link FS_RenameTo  is then called to change the filename.
 *
 * User needs to call @link FSRegisterRNFR  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @category Networking:FTP_Server:File_System_Interface
 * @include "ftpsvrfs.h"
 * @since 6.0 
 *
 * @param    argp      pointer to a file that will be renamed
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully obtain the filename
 * @return  NAFTPS_GENERROR  Unable to obtain the file
 *
 * @see @link NAFTPS
 * @see @link FS_RenameTo
 *
*ad*/
int FS_RenameFrom(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    i, byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            filename[NAFS_FILE_NAME_SIZE];
    int             cur_dirlen, filelen;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _rename_from_ret;
    
    /* get the argument into a full path (directory + filename) */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _rename_from_ret;
            
    /* parse the directory & filename */
    cur_dirlen = sizeof cur_directory;
    filelen = sizeof filename;
    if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK)
        goto _rename_from_ret;
    

#if (_DEBUG_PRINTF > 0) 
    printf("FS_RenameFrom: %s (directory = %s filename = %s)\n", pFile->buffer, cur_directory, filename);
#endif


#if 1
    /* This checks whether the given filename is in current directory */
	pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
	if (pFile->error_status != NAFS_SUCCESS) goto _rename_from_ret;

    /* get number of entry in the directory first.
     * Then allocate memory to same the directory info
     * before get the directory listing.
     * Error if no entry or no such directory.
     */
    pFile->error_status = NAFSdir_entry_count(cur_directory, pFile->permissions, 
                        &pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _rename_from_ret;

    if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS ||
        pFile->dir_info_list_items == 0)
        goto _rename_from_ret;

    pFile->dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(pFile->dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

    if (pFile->dir_info_list == NULL) goto _rename_from_ret;
	    
    memset(pFile->dir_info_list, 0, (pFile->dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

    pFile->error_status = NAFSlist_dir(cur_directory, pFile->permissions, pFile->dir_info_list, 
                                        pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _rename_from_clean;

    rc = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
    if (rc == NAFTPS_SUCCESS && pFile->error_status == NAFS_SUCCESS)
    {
        for (i=0; i < pFile->dir_info_list_items; i++)
        {
            if (pFile->dir_info_list[i].entry_type  == NAFS_FILE_TYPE_FILE &&
                strcmp(pFile->dir_info_list[i].entry_name, filename) == 0)
            {
                pFile->dir_info_list_items = pFile->dir_info_list[i].group_id;
                rc = NAFTPS_SUCCESS;
                break;
            }
        }
    }

_rename_from_clean:
    free (pFile->dir_info_list);
    pFile->dir_info_list = NULL;
#else
    rc = NAFTPS_SUCCESS;
#endif

_rename_from_ret:
    return rc;
}

/*ad*
 * Rename a file in the NET+OS file system
 *
 * This function should be called after @link FS_RenameFrom  .
 *
 * User needs to call @link FSRegisterRNTO  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to new filename
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   The file has been successfully renamed to.
 * @return  NAFTPS_GENERROR  Unable to rename a filename to
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_RenameTo(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile, * pSrcFile=NULL; 
    int             rc = NAFTPS_GENERROR, status;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            filename[NAFS_FILE_NAME_SIZE];
    int             cur_dirlen, filelen;
    char            *src_directory=NULL;
    char            *src_filename=NULL;
	int				sendlen;
	char			* sendPtr;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _rename_to_ret;
    
    /* let's allocate memory & save the full filename path from RenameFrom command */
    pSrcFile = malloc(sizeof(FS_FILESYSTEM_T));
    src_directory = malloc(NAFS_DIRECTORY_PATH_SIZE);
    src_filename = malloc(NAFS_FILE_NAME_SIZE);
    
    if (pSrcFile == NULL || src_directory == NULL || src_filename == NULL)
    {
        rc = NAFTPS_NOMEMSNDBUF; 
        goto _rename_to_ret;
    }
        
    /* let get the directory path & filename from the RenameFrom command */
    strcpy(pSrcFile->buffer, pFile->buffer);
    strcpy(pSrcFile->directory_path, pFile->directory_path);
    pSrcFile->permissions = pFile->permissions; /* user's permission */
    pSrcFile->dir_info_list_items = pFile->dir_info_list_items; /* group id */
    pSrcFile->file_size = 0;
    pSrcFile->file_offset = 0;
    pSrcFile->file_handler = 0;
    
    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    filelen = NAFS_DIRECTORY_PATH_SIZE;
	if (parse_file_path(pFile->buffer, src_directory, &cur_dirlen, src_filename, &filelen) != FILE_OK)
        goto _rename_to_ret;
    
    
    /* let get the full path from RenameTo command */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _rename_to_ret;
    
    /* parse the directory & filename */
    cur_dirlen = sizeof cur_directory;
    filelen = sizeof filename;
	if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK)
        goto _rename_to_ret;
    
	pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
	if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;

    /* if RenameFrom & RenameTo commands specify the same directory path, 
     * we just need to rename it. Otherwise, we need to copy the file from
     * RenameFrom directory to RenameTo directory and delete the one in 
     * RenameFrom directory 
     */
    if (strcmp(src_directory, cur_directory) == 0)
    {   /* just rename a file within the same directory */
    #if (_DEBUG_PRINTF > 0)
        printf("FS_RenameTo: just rename a file %s to %s in %s directory\n",
                    src_filename, filename, cur_directory);
    #endif
              
        rc = NAFTPS_FILESYSTEMERR;      
        pFile->error_status = NAFSrename_file(cur_directory, src_filename, filename, pFile->permissions, &pFile->io_request);
        
        if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
        
        if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS)
        {
            pFile->error_status = NAFSrename_dir(cur_directory, src_filename, filename, pFile->permissions, &pFile->io_request);
        
            if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
        
            if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS)
                goto _rename_to_ret;
        }
    }
    else 
    {
        rc = NAFTPS_FILESYSTEMERR;
    #if (_DEBUG_PRINTF > 0)
        printf("FS_RenameTo: rename %s/%s to %s/%s\n", src_directory, src_filename, cur_directory, filename);
    #endif
        /* let open the file from RenameFrom command */
	    pSrcFile->error_status = NAFSinit_io_request_cb(&pSrcFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
    	if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
        
        do {
    	    pSrcFile->error_status = NAFSopen_file(src_directory, src_filename, 
    	                                        &pSrcFile->file_handler, pSrcFile->permissions, 
    	                                        &pSrcFile->io_request);


    		if (pSrcFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;

    		status = fs_wait_io_status(&pSrcFile->io_request, &byteTransferred, &pSrcFile->error_status);

            if (pSrcFile->error_status == NAFS_FILE_IS_OPEN)
                tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
	    	else if (status != NAFTPS_SUCCESS ) goto _rename_to_ret;
        
        } while (pSrcFile->error_status == NAFS_FILE_IS_OPEN);
            
    
        /* let create a new file for RenameTo command */
		pFile->io_request.arg2 = O_CREAT | O_TRUNC | O_RDWR;  /* for yaffs. jhl */
	    pFile->error_status = NAFScreate_file(cur_directory, filename, pFile->permissions,
                            pFile->dir_info_list_items, &pFile->io_request);
    
    #if (_FTPS_RESTRICTED_FILE_CREATION > 0)
		if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
    #endif    
    
        if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS)
            goto _rename_to_ret;
            
    #if (_FTPS_RESTRICTED_FILE_CREATION == 0)
        do {
    #endif
			pFile->io_request.arg2 = O_RDWR;  /* for yaffs. jhl */
    	    pFile->error_status = NAFSopen_file(cur_directory, filename, 
    	                                        &pFile->file_handler, pFile->permissions, 
    	                                        &pFile->io_request);


    		if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
            
    		status = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

            if (pFile->error_status == NAFS_FILE_IS_OPEN)
                tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
	    	else if (status != NAFTPS_SUCCESS ) goto _rename_to_ret;
        
    #if (_FTPS_RESTRICTED_FILE_CREATION == 0)
        } while (pFile->error_status == NAFS_FILE_IS_OPEN);
    #endif
		pFile->io_request.arg2 = 0;  /* clean up */
    
    
        do {
            /* We keep sending data to ftp client until we finish the whole file */
            pSrcFile->error_status = NAFSread_file(pSrcFile->file_handler, pSrcFile->permissions, 
                                                sizeof pSrcFile->buffer, pSrcFile->file_offset, 
                                                pSrcFile->buffer, &pSrcFile->io_request);

            if (pSrcFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;

	        status = fs_wait_io_status(&pSrcFile->io_request, (unsigned int *)&byteTransferred, &pSrcFile->error_status);

	        if (status != NAFTPS_SUCCESS && pSrcFile->error_status != NAFS_END_OF_FILE) 
	            goto _rename_to_ret;

            sendlen = byteTransferred;
            sendPtr = pFile->buffer;
            pSrcFile->file_size += byteTransferred;
            pSrcFile->file_offset += byteTransferred;
        #if (_DEBUG_PRINTF > 0)
            printf("FS_RenameTo: NAFSread_file 0x%X size %d -->\n", pSrcFile->file_handler, byteTransferred);
        #endif
        
	        while (sendlen > 0)
	        {   
                pFile->error_status = NAFSwrite_file(pFile->file_handler, pFile->permissions, 
                                                sendlen, pFile->file_offset, sendPtr,
		                                        &pFile->io_request, NAFS_ZERO_OFFSET_ERASE);

		        if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;

		        status = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);

		        if (status != NAFTPS_SUCCESS)
                {
                    if ((pFile->error_status == NAFS_NO_FREE_BLOCKS) || (pFile->error_status == NAFS_NO_FREE_INODES))
                    {
                        printf("FS_RenameTo volume full\n");
                        rc = (-2); /* a negative 2 indicates to the FTP server that the volume is full */
                    }
		            goto _rename_to_ret;
                }

            #if (_DEBUG_PRINTF > 0)
                printf("FS_RenameTo: NAFSwrite_file 0x%X size %d <--\n", pFile->file_handler, byteTransferred);
            #endif
		        sendlen -= byteTransferred;
		        sendPtr += byteTransferred;
		        pFile->file_size += byteTransferred;
                pFile->file_offset += byteTransferred;
	        }

        } while (pSrcFile->error_status != NAFS_END_OF_FILE);
        
        fs_close_file (pSrcFile);
        pSrcFile->file_handler = 0;
        
        do {
            pSrcFile->error_status = NAFSdelete_file(src_directory, src_filename, pSrcFile->permissions, &pSrcFile->io_request);

    		if (pSrcFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;
            
    		status = fs_wait_io_status(&pSrcFile->io_request, &byteTransferred, &pSrcFile->error_status);

            if (pSrcFile->error_status == NAFS_FILE_IS_OPEN)
                tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
	    	else if (status != NAFTPS_SUCCESS ) goto _rename_to_ret;
        
        } while (pSrcFile->error_status == NAFS_FILE_IS_OPEN);
    
    }


    rc = NAFTPS_SUCCESS; 
        
_rename_to_ret:

#if (_DEBUG_PRINTF > 0)
    if (rc != NAFTPS_SUCCESS || pFile->error_status != NAFS_SUCCESS)
    {
        printf("FS_RenameTo: return error = %d file system error = %d\n", rc, pFile->error_status);
    }
#endif

    if (pSrcFile != NULL) {
#if (_DEBUG_PRINTF > 0)
        if (rc != NAFTPS_SUCCESS || pSrcFile->error_status != NAFS_SUCCESS)
        {
            printf("FS_RenameTo: file system error for opening/reading %s dir %s file = %d\n", 
                            src_directory, src_filename, pFile->error_status);
        }
#endif
        fs_close_file (pSrcFile);
        fs_close_file (pFile);
        free(pSrcFile);
    }
    if (src_filename != NULL) free(src_filename);
    if (src_directory != NULL) free(src_directory);
    
    return rc;
}

/*ad*
 * Return the present working directory
 *
 * User needs to call @link FSRegisterPWD  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      NOT Used
 * @param    handle    handle to access FTP server client connection  
 * @param    buffer    pointer to memory which will be set to the present 
 *                      working directory and returned
 *                      back to FTP client.
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully get present working directory.
 * @return  NAFTPS_GENERROR  Unable to get present working directory
 *
 * @see @link NAFTPS
*ad*/
int FS_Pwd(char * argp, unsigned long handle, char * buffer)
{
    FS_FILESYSTEM_T * pFile; 
    int rc = NAFTPS_GENERROR;
    NA_UNUSED_PARAM (argp);

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }

    if (pFile == NULL) goto _pwd_ret;

    sprintf(buffer, "\"/%s\" is current directory", pFile->directory_path);

    rc = NAFTPS_SUCCESS;

_pwd_ret:
    return rc;
}

/*ad*
 * Change to the specified directory in the file system
 *
 * User needs to call @link FSRegisterCWD  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path which will be changed to
 * @param    handle    handle to access FTP server client connection  
 *
 * @return  NAFTPS_SUCCESS   Successfully change the directory.
 * @return  NAFTPS_GENERROR  Unable to change the directory
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_ChangeDirectory(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR, status;
    unsigned int    i, byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            new_directory[NAFS_DIRECTORY_PATH_SIZE];
    int             cur_dirlen, new_dirlen;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _change_dir_ret;

    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _change_dir_ret;        

    cur_dirlen = sizeof cur_directory;
    new_dirlen = sizeof new_directory;

    if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK)
        goto _change_dir_ret;

    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
    if (pFile->error_status != NAFS_SUCCESS) goto _change_dir_ret;

    /* Get number of entry in the directory first.
     * Then allocate memory for the directory info
     * before get the directory listing info.
     * Error if no entry or no such directory.
     */
    pFile->error_status = NAFSdir_entry_count(cur_directory, pFile->permissions, 
                        &pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _change_dir_ret;

    if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS ||
        (pFile->dir_info_list_items == 0 && new_dirlen > 0))
        goto _change_dir_ret;

    if ((pFile->dir_info_list_items == 0) && (new_dirlen == 0))
    {   /* This must be root directory */
        strcpy(pFile->directory_path, (char *)cur_directory);
    #if (_DEBUG_PRINTF > 0)
        printf("fs_changed_directory: current directory is %s\n", pFile->directory_path);
    #endif
        rc = NAFTPS_SUCCESS;
        goto _change_dir_ret;
    }

    pFile->dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(pFile->dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

    if (pFile->dir_info_list == NULL) goto _change_dir_ret;
	    
    memset(pFile->dir_info_list, 0, (pFile->dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

    /* Get info on the previous directory and make sure the specified directory is
     * existed.
     */
    pFile->error_status = NAFSlist_dir(cur_directory, pFile->permissions, pFile->dir_info_list, 
                                        pFile->dir_info_list_items, &pFile->io_request);

    if (pFile->error_status != NAFS_SUCCESS) goto _change_dir_clean;

    status = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
    if (status == NAFTPS_SUCCESS && pFile->error_status == NAFS_SUCCESS)
    {
        if (new_dirlen > 0)
        {
            for (i=0; i < pFile->dir_info_list_items; i++)
            {
            /* if we have a directory and we find it in the file system , then */
                if ((pFile->dir_info_list[i].entry_type  == NAFS_FILE_TYPE_DIR) &&
		            (strcmp(pFile->dir_info_list[i].entry_name, new_directory) == 0))
                {
					/* 
					 * Since the current contents of new_directory is useless, lets 
					 * update it with something that the file system can use
					 */
                    strcpy(new_directory,pFile->dir_info_list[i].entry_name);
                    strcpy(pFile->directory_path, (char *)cur_directory);
                    if (strstr(pFile->directory_path, "\\") != NULL)
                    {
                       if (pFile->directory_path[strlen(pFile->directory_path)-1] != '\\')
                        strcat(pFile->directory_path, "\\");
                    }
                    else if (strstr(pFile->directory_path, "/") != NULL)
                    {
                       if (pFile->directory_path[strlen(pFile->directory_path)-1] != '/')
                        strcat(pFile->directory_path, "/");
                    }
                    strcat(pFile->directory_path, (char *)new_directory);
                #if (_DEBUG_PRINTF > 0)
                    printf("fs_changed_directory: current directory is %s size %d\n", 
                            pFile->directory_path, strlen(pFile->directory_path));
                #endif
                    rc = NAFTPS_SUCCESS;
                    break;
                }
            }
        }
        else
        {   /* no diectory change */
            strcpy(pFile->directory_path, (char *)cur_directory);
        #if (_DEBUG_PRINTF > 0)
            printf("fs_changed_directory: current directory is %s\n", pFile->directory_path);
        #endif
            rc = NAFTPS_SUCCESS;
        }
    }

_change_dir_clean:
    free (pFile->dir_info_list);
    pFile->dir_info_list = NULL;

_change_dir_ret:
    return rc;
}

/*ad*
 * This function is used to go up one level. 
 *
 * User needs to call @link FSRegisterCDUP  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      Not Used
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Has been successfully go back to root directory
 * @return  NAFTPS_GENERROR  Unable to go back to root directory
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_RootDirectory(char * argp, unsigned long handle)
{
    NA_UNUSED_PARAM (argp);
    return FS_ChangeDirectory ("..", handle);
}

/*ad*
 * List all items with detail info for the specified directory
 *
 * User needs to call @link FSRegisterLIST  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path for which all items will be returned
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   All items are successfully listed for the given dir.
 * @return  NAFTPS_GENERROR  Unable to list the directory
 *
 * @see @link NAFTPS
 * @see @link FS_nListDir
 *
*ad*/
int FS_ListDir(char * argp, unsigned long handle)
{
    int rc;

    NA_ASSERT((int)handle);

    rc = list_dir(argp, handle, LIST_FILE_INFO);
    return rc;                            
}

/*ad*
 * List all items for the specified directory
 *
 * User needs to call @link FSRegisterNLST  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path for which all items will be returned
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   All items are successfully listed for the given dir.
 * @return  NAFTPS_GENERROR  Unable to list the directory
 *
 * @see @link NAFTPS
 * @see @link FS_ListDir
 *
*ad*/
int FS_nListDir(char * argp, unsigned long handle)
{
    int rc;

    NA_ASSERT((int)handle);

    rc = list_dir(argp, handle, LIST_FILE_ONLY);
    return rc;                            
}

/*ad*
 * Creates a new directory. 
 *
 * User needs to call @link FSRegisterMKD  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path which will be created
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   The directory is successfully created
 * @return  NAFTPS_GENERROR  Unable to create the directory
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_MakeDir(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR, status;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            new_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            temp_directory[NAFS_DIRECTORY_PATH_SIZE] = "\0";
    char            * temp_dir, slash;
    int             cur_dirlen, new_dirlen;
    unsigned int    group_id;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _make_dir_ret;

    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
    if (pFile->error_status != NAFS_SUCCESS) goto _make_dir_ret;

    /* set the argument path */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _make_dir_ret;        

    cur_dirlen = sizeof cur_directory;
    new_dirlen = sizeof new_directory;

    if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK ||
        new_dirlen == 0 || (cur_dirlen + new_dirlen) >= (int)sizeof pFile->directory_path)
        goto _make_dir_ret;


    if (strstr(cur_directory, "\\") != NULL)
        slash = '\\';
    else
        slash = '/';

    group_id = pFile->permissions >> _WRITE_PERMISSION_BIT;
    if (!is_valid_group_id(pFile->permissions))
    {
        get_group_id(pFile, cur_directory, &group_id);
    }

    do {
        strcpy(pFile->buffer, cur_directory);

        pFile->error_status = NAFScreate_dir(pFile->buffer, new_directory, pFile->permissions, group_id, &pFile->io_request);

        if (pFile->error_status == NAFS_SUCCESS)
        {
            status = fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
            if (status != NAFTPS_SUCCESS && pFile->error_status != NAFS_DIR_ENTRY_NOT_FOUND) 
                goto _make_dir_ret;
        }

        if (pFile->error_status == NAFS_DIR_ENTRY_NOT_FOUND)
        {
            /* unable to create a new sub-directory in the current directory
             * since the current directory is not exist. So we need to create
             * this current directory before creating another sub-directory.
             */
            if (strlen(temp_directory) > 0)
            {   /* add sub-directory in the list that needs to be created */
                new_dirlen = strlen(new_directory);
                new_directory[new_dirlen] = slash;
                new_directory[new_dirlen+1] = '\0';
                strcat(new_directory, temp_directory);
                strcpy(temp_directory, new_directory);
            }
            else /* 1st sub-directory */
                strcpy(temp_directory, new_directory);

            cur_dirlen = sizeof cur_directory;
            new_dirlen = sizeof new_directory;

            if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK)
                goto _make_dir_ret;

            status = pFile->error_status;

            get_group_id(pFile, cur_directory, &group_id);

            pFile->error_status = status;

        }
        else if (strlen(temp_directory) > 0)
        {
            /* we successfully create a sub-directory. More
             * sub-directories need to be created. Check one at a time.
             */
            cur_dirlen = strlen(pFile->buffer);
            if (cur_directory[cur_dirlen-1] != slash)
	        {
                cur_directory[cur_dirlen] = slash;
                cur_directory[cur_dirlen+1] = '\0';
	        }

            strcat(cur_directory, new_directory);

            /* get a new directory for directory creation.
             * we only want one sub-directory at a time. 
             * i.e., DIR1/DIR2/DIR3 1st one is DIR1 then DIR2 then DIR3
             */
            temp_dir = strchr(temp_directory, slash);
            if (temp_dir != NULL)
            {
                /* get one sub-directory in the list */
                strcpy(new_directory, temp_directory);
                new_dirlen = (temp_dir - temp_directory);
                new_directory[new_dirlen] = '\0';
                temp_dir++;
                strcpy(temp_directory, temp_dir);
            }
            else
            {   /* only one sub-directory */
                strcpy(new_directory, temp_directory);
                temp_directory[0] = '\0';
            }
            pFile->error_status = NAFS_DIR_ENTRY_NOT_FOUND;
        }
      } while (pFile->error_status == NAFS_DIR_ENTRY_NOT_FOUND);

     if (pFile->error_status == NAFS_SUCCESS)
     {
        rc = NAFTPS_SUCCESS;
     }

_make_dir_ret:
     return rc;
}


/*ad*
 * Remove a directory
 *
 * User needs to call @link FSRegisterRMD  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory name to be removed 
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully remove the directory 
 * @return  NAFTPS_GENERROR  Unable to remove the directory
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_RemoveDir(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            new_directory[NAFS_DIRECTORY_PATH_SIZE];
    int             cur_dirlen, new_dirlen;

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);   
    if (pFile == NULL)
    {
        pFile = allocate_filesystem_cb(handle);
    }
    if (pFile == NULL) goto _remove_dir_ret;

    /* no way to delete these */
    if (strcmp(argp, ".") == 0 || strcmp(argp, "..") == 0) goto _remove_dir_ret;

    /* set the argument path */
    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, argp) != FILE_OK)
        goto _remove_dir_ret;        

    cur_dirlen = sizeof cur_directory;
    new_dirlen = sizeof new_directory;

    /* if unable to parse directory name or no sub-directory is specified (only root directory),
     * return error.
     */
    if (parse_file_path(pFile->buffer, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK ||
        new_dirlen == 0)
        goto _remove_dir_ret;

    pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, fs_io_callback_fn, pFile->io_request.user_data);
    if (pFile->error_status != NAFS_SUCCESS) goto _remove_dir_ret;

    /* delete directory */
    pFile->error_status = NAFSdelete_dir(cur_directory, new_directory, pFile->permissions, &pFile->io_request);

    if (pFile->error_status == NAFS_SUCCESS)
    {
        if (fs_wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status) != NAFTPS_SUCCESS)
            goto _remove_dir_ret;

        if (pFile->error_status == NAFS_SUCCESS)
            rc = NAFTPS_SUCCESS;
    }

_remove_dir_ret:
     return rc;
   
}

/*ad*
 * Close and terminate the current ftp session
 *
 * User needs to call @link FSRegisterControlClose  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a string containing the login username 
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully close and clean all file system 
 * @return  NAFTPS_GENERROR  Unable close and clean all file system 
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_SessionClosed(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_SUCCESS;
    NA_UNUSED_PARAM (argp);

    NA_ASSERT((int)handle);

    pFile = get_filesystem_cb(handle);
    if (pFile != NULL)
    {
	    if ((pFile->file_handler != 0)
	    	&& (pFile->file_handler != (unsigned int)-1))
        {
            rc = fs_close_file (pFile);
        }
        free_filesystem_cb(handle);
    }

	return rc;
}
/*ad*
 * Close and terminate data socket
 *
 * User needs to call @link FSRegisterDataClose  to register this function for FTP server.
 * If @link FSInstallFileSystem  is called, user doesn't need to register this function.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a string containing the login username 
 * @param    handle    handle to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully close a file
 * @return  NAFTPS_GENERROR  Unable close a file 
 *
 * @see @link NAFTPS
 *
*ad*/
int FS_DataSessionClosed(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_SUCCESS;
    NA_UNUSED_PARAM (argp);

    pFile = get_filesystem_cb(handle);   
    if (pFile != NULL)
    {
	    if ((pFile->file_handler != 0)
	    	&& (pFile->file_handler != (unsigned int)-1))
        {
#if (_DEBUG_PRINTF > 0)
            printf("Data session closed handle 0x%x\n\r", pFile->file_handler);
#endif
            rc = fs_close_file (pFile);
        }
    }
	return rc;
}

static int genericFtpSyst (char * fsParameters, char * fsUserData, unsigned long handle)
{
    NA_UNUSED_PARAM (handle);
    NA_UNUSED_PARAM (fsParameters);

    sprintf (fsUserData, "NET+OS FTP for the %s", NABspPlatform);
    return NAFTPS_SUCCESS;
}


/*ad*
 * Enable NET+OS file system interface for FTP Server.
 *
 * It allows the user to store, retrieve, delete, and rename files. It also allows the user
 * to change, list, remove, and create directories
 *.
 * This function should be called after @link FSInitialize  and before @link FSStartServer  .
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category Networking:FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    root_directory         Pointer to root directory when Ftp client login
 *                        
 * @return  NAFTPS_SUCCESS          Successfully intall file system into ftp client
 * @return  NAFTPS_GENERROR         Unable to intall file system.
 *
 * @see @link NAFTPS
*ad*/
int FSInstallFileSystem(char * root_directory)
{
    int rc, nUsers;

    NA_ASSERT((int)root_directory);
	
    /* Already initialized */
    if (gFtpSvrFileHandles != NULL)
    {
        rc = NAFTPS_GENERROR;
        goto _fsinstallfilesystem_ret;
    }

    /* store file */
    rc = FSRegisterSTOR(FS_StoreFile);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* retrieve file */
    rc = FSRegisterRETR(FS_RetrieveFile);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* delete file */
    rc = FSRegisterDELE(FS_DeleteFile);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* rename file from */
    rc = FSRegisterRNFR(FS_RenameFrom);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* rename file to */
    rc = FSRegisterRNTO(FS_RenameTo);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* display current directory */
    rc = FSRegisterPWD(FS_Pwd);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* change to root directory */
    rc = FSRegisterCDUP(FS_RootDirectory);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* change directory */
    rc = FSRegisterCWD(FS_ChangeDirectory);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* list with file name only */
    rc = FSRegisterNLST(FS_nListDir);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* list directory info */
    rc = FSRegisterLIST(FS_ListDir);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* make directory */
    rc = FSRegisterMKD(FS_MakeDir);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* remove directory */
    rc = FSRegisterRMD(FS_RemoveDir);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* close data socket */    
    rc = FSRegisterDataClose(FS_DataSessionClosed);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* SYST command */
    rc = FSRegisterSYST (genericFtpSyst);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    /* close ftp server session */   
    rc = FSRegisterControlClose(FS_SessionClosed);
    if (rc != NAFTPS_SUCCESS)
    {
        goto _fsinstallfilesystem_ret;
    }

    nUsers = FSGetNumberOfUser();
    /* root directory */
    strcpy(gFtpSvrFileRootDir, root_directory);

    /* allocate number of users */
    gFtpSvrFileHandles = (FS_FILESYSTEM_T *)calloc(nUsers, sizeof(FS_FILESYSTEM_T));
    if (gFtpSvrFileHandles == NULL)
    {
        rc = NAFTPS_GENERROR;
        goto _fsinstallfilesystem_ret;
    }

    memset(&gFtpSvrFileMutex, 0, sizeof gFtpSvrFileMutex); /* GNU */
    if (tx_mutex_create(&gFtpSvrFileMutex, "FtpSvrFileMutex", TX_NO_INHERIT) != TX_SUCCESS)
    {
        rc = NAFTPS_GENERROR;
    }

_fsinstallfilesystem_ret:
   return rc;
}

/*ad*
 *
 * User can call this function to initiate the FTP server which allows FTP
 * clients to download firmware images and also to access file system at the
 * same time. When user downloads application image like image.bin or 
 * backup.bin then the FTP server stores the entire image in dynamically 
 * allocated memory, checks the CRC and signature and finally writes the 
 * image to the flash.
 *
 * Unit reboots 2 seconds after client closes its connection or firmware 
 * update completes whichever comes last. It is possible to download multiple 
 * files in one FTP session. That means user can download both rom.bin 
 * (bootloader file) and image.bin (application file) in one session.
 *
 * If there is not enough memory to store the entire application image, then 
 * the integrity check (CRC) will be dropped and the received data will be 
 * written to flash to clear the dynamically allocated memory. From that point 
 * onwards data will be written to flash sector by sector as we received. 
 *
 * The integrity check mechanism is added only to the application images not
 * for the bootloader images. 
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  rootdir File system root directory name
 * @param  nusers  number of clients that can login at a time
 *
 * @return  NAFTPS_SUCCESS  FTP server is initialized and started and is
 *                          ready to receive firmware file.
 * @return  NAFTPS_GENERROR  Either failed to initialize or failed to start
 *                           the server. 
 *
 * @see @link NAFTPS
 * @see @link NAFlashWrite
 * @see @link NAFlashSectors
 * @see @link NAFlashSectorSizes
 * @see @link naFtpDlInit
 *
 * @note Use @link naFtpDlInit if you are just looking for FTP download 
 *       capability. User can see the update status on the std IO terminal. 
 *       If the terminal is not connected, then user should allow enough time
 *       (normally one minute per mega bytes of data) for the firmware update 
 *       to complete. 
 *
 * @see @link naFtpDlInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWStoreImage
 * @see @link naFWDataClose
 * @see @link naFWControlClose
 *
*ad*/
int naFtpFSInit(char * rootdir, int nusers)
{
    int result;
    
    /* initialize ftp server with number of users */
    if ((result = FSInitialize(nusers)) != NAFTPS_SUCCESS)
    {
        printf("FSInitialize failed\n");
        return result;
    }

    if ((result = FSInstallFileSystem(rootdir)) != NAFTPS_SUCCESS)
    {
        printf("FSInstallFileSystem failed\n");
        return result;
    }

    /* start ftp server */
    if ((result = FSStartServer()) != NAFTPS_SUCCESS) 
        printf("FSStartServer failed\n");
    
    printf ("FTP server is ready.\n");        
    return result;    
}



