/*
 *            Copyright (c) 1998-2003 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 304                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSalenetsilicon.com
 *
 *************************************************************************/

/*********************************************************
 * Edit History
 * Date			Initials		Change Description
 * 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
 */

/**
 * This is the interface between NETOS FTP Server and NETOS file system.
 * It allows user to store, retrieve, delete, and rename a file and
 * list, change, remove and display a directory in the file system. 
 *
 * The 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>
 *
 * This FSInstallFileSystem() must be called after FSInitialize() to initalize 
 * FTP server and before FSStartServer() to start FTP server. If multiple volumes
 * are created, use slash \ before the volume name "\VOL1" to change to 
 * different volume.
 *
 * This interface requires FTP server and the file system libraries.
 *
 * The following definition is used for file creation in the file system:
 *      <list type="unordered">
 *          <item> _FTPS_RESTRICTED_FILE_CREATION - Disabled by default that allows
 *                  a file can be rewritten. </item>
 *      </list>
 *
 * @external
 * @overview FTP Server & File System Interface
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 * @see @link _FTPS_RESTRICTED_FILE_CREATION
 * @see @link FTP_Server
 * @see @link fileSystem
 */
#include <string.h>
#include <npttypes.h>
#include <fservapi.h>
#include <fclntapi.h>
#include <fs_api.h>
#include <fs.h>
#include <errno.h>
#include <string.h>

#define _DEBUG_PRINTF           0

/**
 * this _FTPS_RESTRICTED_FILE_CREATION definition only allows users to create a 
 * non-existing file. A file can only be created if the file is not existed.
 * If this definition is 0, data will over write into a file if a file is 
 * already existed.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 *
 */
#define _FTPS_RESTRICTED_FILE_CREATION   0

/**
 * Parsing directory or file return status
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 *
 * @name fs_parsing_status   "Parsing File Return Status"
 *
 * @param FILE_OK                 Directory or file was successfully parsed
 * @param INVALID_FILE_LEN        Unable to parse the file 
 * @param INVALID_DIRECTORY_LEN   Unable to parse the directory
 */
#define FILE_OK                 0
#define INVALID_FILE_LEN        -1
#define INVALID_DIRECTORY_LEN   -2

#define LIST_FILE_ONLY          0
#define LIST_FILE_INFO          1


/**
 * FTP Server Response Buffer Size
 *
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 *
 */
#define FS_BUFFER_SIZE          2048  

/**
 * FTP server to file system interface control block.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 *
 * @param fs_handle             The FTP server login user's handle.
 * @param directory_path        The array to store the directory path.
 * @param buffer                A general purpose buffer used as needed.
 * @param io_request            The I/O request control block.
 * @param dir_info_list         The pointer to a directory information array.
 * @param dir_info_list_items   The number of entries in the directory information array.
 * @param file_handler          The handle to a file.
 * @param file_offset           The offset in bytes from the start of a file.
 * @param file_size             The size of a file in bytes.
 * @param permissions           The users read and write permissions mask.
 * @param error_status          The error status after a file I/O request is completed or terminated.
 * 
 */

typedef struct {
    unsigned long fs_handle;
    char          directory_path[NAFS_DIRECTORY_PATH_SIZE];
    char          buffer[FS_BUFFER_SIZE];

    NAFS_IO_REQUEST_CB  io_request;
    NAFS_DIR_ENTRY_INFO *dir_info_list;
    unsigned int        dir_info_list_items;
    unsigned int        file_handler;
    unsigned int        file_offset;
    unsigned int        file_size;
    unsigned int        permissions;
    int                 error_status;    
} FS_FILESYSTEM_T;    

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


extern int FSGetNumberOfUser(void);
extern unsigned int FSGetAccessCode(unsigned long handle);

/* 
 * Functional prototype for new routine to convert file system internal group code
 * to a more human readable form
 */
static int getGroupNumber(unsigned int groupid);

/**
 * Tries to match a directory name to the unix style directory names returned by the file system.
 * Breaks the directory name into tokens and compares each token with the name provided 
 *
 * @category FTP_Server:File_System_Interface
 * @since 6.1 
 *
 * @param   StringToMatch  pointer to char directory we are trying to match
 * @param   StringWithTokens     Pointer to char containing a string of directory and time and
 *                               date info
 *                        
 * @return  ResultString     string that matched StringToMatch
 *                     
 *
 * @return  NULL    No match was found (Failure mode)
 *
 */
static char * MatchUsingToken(char * StringToMatch, char * StringWithTokens)
{
	char * ResultString;
	char * TokenizedString;


    /* if either string is a pointer to NULL, exit now */
    if(StringToMatch == NULL)
	{
	    return(NULL);
	}
	if(StringWithTokens == NULL)
	{
	    return(NULL);
	}
	TokenizedString = (char *)malloc(strlen(StringWithTokens)+ 1);
	if(TokenizedString == NULL)
	{
	    return(NULL);
	}

    strcpy(TokenizedString, StringWithTokens);
	TokenizedString[strlen(StringWithTokens)] = '\0';

    /* first do one strtok using the string with the tokens as input. All other iterations
	 * use NULL
	 */
	ResultString = strtok(TokenizedString, " ");
	do
	{
	    /* if a match is found, return and tell the upper layer */
		if (strcmp(ResultString, StringToMatch) == 0)
		{
		    free(TokenizedString);
		    return ResultString; /* match found */
		}
	    /* no match found on this iteration, look at the next token */
		ResultString = strtok(NULL, " ");
	} while (ResultString != NULL); /* result string NULL means end of string */
	free(TokenizedString);
	return NULL; /* no match found */
}

/**
 * Tries to remove the file name from a file name and date string object returned from
 * the file system. 
 * @category 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
 *
 */

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.
 * 
 * @external
 * @category 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;
}

/**
 * It waits for io request status from the file system.
 *
 * @external
 * @category 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;

    do {    
        /* 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;
        tx_thread_sleep(5);

    } 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.
 * 
 * @external
 * @category 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, rc = FILE_OK;

    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;


    if (len > 0 && len < *dirlen)
    {
        strncpy(dirpath, fullPathName, len);
        dirpath[len] = '\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) <= *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.
 * 
 * @external
 * @category 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) >= 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) >= 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
 * 
 * @external
 * @category 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 >> 16);
	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.
 * 
 * @external
 * @category 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, i;
    unsigned int 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, NULL, 0);

	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 >> 16;

    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.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    handle    handler 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.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    handle    handler 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);
                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.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    handle    handler 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';
            
                rc = NAFTPS_SUCCESS;
                break;
            }

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


/**
 * This function closes a file if any.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    handle    handler to access FTP server client connection  
 *
 *                        
 * @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(unsigned long handle)
{
    int                 rc = NAFTPS_SUCCESS;
    FS_FILESYSTEM_T     * pFile; 
    unsigned int        byteTransferred;
    int                 error_status;

    pFile = get_filesystem_cb(handle);   

    if (pFile != NULL)
    {
	    if (pFile->file_handler != 0)
	    {
	        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 is used to get a directory info and send it to the 
 * FTP client.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a directory name to be retrieved list info
 * @param    handle    handler 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;
    unsigned int        byteTransferred, perm;
    struct tm           * file_tm;
    int		            socket_fd, i, len;
    char                * sendPtr;
    int                 availSize;
    

    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, NULL, 0);
    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 0
    pFile->error_status = NAFSdir_entry_count(pFile->buffer, NASYSACC_FS_SUPER_USER, 
                        &pFile->dir_info_list_items, &pFile->io_request);
#else
    pFile->error_status = NAFSdir_entry_count(pFile->buffer, pFile->permissions, 
                        &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;
    }

    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)));

#if 0
        pFile->error_status = NAFSlist_dir(pFile->buffer, NASYSACC_FS_SUPER_USER, pFile->dir_info_list, 
                        pFile->dir_info_list_items, &pFile->io_request);
#else
        pFile->error_status = NAFSlist_dir(pFile->buffer, pFile->permissions, 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)
            {
                availSize = (sizeof pFile->buffer - (sendPtr - pFile->buffer));
                sendPtr = fs_send_response(socket_fd, pFile->buffer, sendPtr, availSize, 20);
                if (sendPtr == NULL) goto _list_dir_ret;

                /* Set 'd' for directory or '-' for a file. Then, the next 
                 * 3 characters are the permissions of the files owner.
                 * Since we don't have info on file's owner, set to rw 
                 */
                if (pFile->dir_info_list[i].entry_type == NAFS_FILE_TYPE_DIR)
                {
                    strcpy(sendPtr, "drwx");

                }
                else
                {
                    strcpy(sendPtr, "-rw-");
                }
                sendPtr += 4;

                /* permission on user's group */
                /*perm = NASYSACC_FS_GROUP1_READ << (pFile->dir_info_list[i].group_id - 1);*/
                /* do not shift the guy's group id, it is already shifted */
                perm = pFile->dir_info_list[i].group_id;

                if ((pFile->permissions & perm))
                {
                    strcpy(sendPtr, "r");
                }
                else 
                {
                    strcpy(sendPtr, "-");
                }
                sendPtr += 1;

                /*perm = NASYSACC_FS_GROUP1_WRITE << (pFile->dir_info_list[i].group_id - 1);*/
				/* do not shift the guys group id, it is already shifted */
				perm = pFile->dir_info_list[i].group_id;

                if ((pFile->permissions & perm))
                {
                    strcpy(sendPtr, "w");
                }
                else 
                {
                    strcpy(sendPtr, "-");
                }
                sendPtr += 1;

                if (pFile->dir_info_list[i].entry_type == NAFS_FILE_TYPE_DIR)
                {
                    strcpy(sendPtr, "x");

                }
                else
                {
                    strcpy(sendPtr, "-");
                }
                sendPtr += 1;

                /* permissions for everyone which we don't let the world to access
                 * without permissions
                 */
                strcpy(sendPtr, "--- 1 noone ");
                sendPtr += 12;

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

                /*len = sprintf(sendPtr, "group %d %-8d ", pFile->dir_info_list[i].group_id,
                                        pFile->dir_info_list[i].file_size);*/
				/* 
				 * 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;

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

                file_tm = localtime(&pFile->dir_info_list[i].last_modified_time);
                if (file_tm != NULL)
                    len = sprintf(sendPtr, "%s %02d %02d:%02d:%02d %04d ", month[file_tm->tm_mon],
                                file_tm->tm_mday, file_tm->tm_hour, file_tm->tm_min,
                                file_tm->tm_sec, (1900 + file_tm->tm_year));
                else
                    len = sprintf(sendPtr, "%s 01 00:00:00 1979 ", month[0]);

                sendPtr += len;
            }

            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;                            
}

/**
 * Create and write a file in the file system
 * This function should be called multiple time until it finishes all data of a file.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category 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    handler 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
 */
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);

    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, NULL, 0);
	    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 >> 16);
        if (!is_valid_group_id(pFile->permissions))
        {
            if (get_group_id(pFile, cur_directory, &group_id) != NAFTPS_SUCCESS)
		        goto _store_file_ret;
        }

	    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->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(10);
	    	else if (rc != NAFTPS_SUCCESS ) goto _store_file_ret;
            
#if (_FTPS_RESTRICTED_FILE_CREATION > 0)
        } while (pFile->error_status == NAFS_FILE_IS_OPEN);
#endif
	}


	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);

		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;   
}

/**
 * Retrieve and send a file from the local file system to the FTP client
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a filename to be retrieved and sent to FTP client
 * @param    handle    handler to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   A file has been successfully retrieved and sent
 * @return  NAFTPS_GENERROR  Unable to send file to FTP client
 *
 * @see @link NAFTPS
 */
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;

	/* 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, NULL, 0);
	    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(10);
	    	else if (rc != NAFTPS_SUCCESS ) goto _retrieve_file_ret;

        } while (pFile->error_status == NAFS_FILE_IS_OPEN);

	}

    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;   
}

/**
 * Delete a file in the file system
 * 
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a file name to be deleted
 * @param    handle    handler 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
 */
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, NULL, 0);
	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;
}

/**
 * Rename a file in the file system 
 * This function saves the specified filename and makes sure this file 
 * is exist. FS_RenameTo function is then called to change this file
 * to a different name.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @include "ftpsvrfs.h"
 * @since 6.0 
 *
 * @param    argp      pointer to a file that will be renamed
 * @param    handle    handler 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
 *
 */
int FS_RenameFrom(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    byteTransferred;
    int             i;

    NA_ASSERT((int)handle);

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

    /* We only allow a file to be renamed in the current directory.
     * Make sure the argument doesn't refer to different directory.
     */
     if ((strstr(argp, "\\") != NULL) || (strstr(argp,"/") != NULL) || strlen(argp) == 0)
        goto _rename_from_ret;

    
    strcpy(pFile->buffer, argp);

#if (_DEBUG_PRINTF > 0)
    printf("fs_rename_form: r%s\n", pFile->buffer);
#endif

#if 1
    /* This checks whether the given filename is in current directory */
	pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, NULL, 0);
	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(pFile->directory_path, 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(pFile->directory_path, 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, pFile->buffer) == 0)
            {
                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;
}

/**
 * Rename a file in the file system
 * This function should be called after FS_RenameFrom.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to new filename
 * @param    handle    handler 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
 * @see @link FS_RenameFrom
 *
 */
int FS_RenameTo(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;
    unsigned int    byteTransferred;

    NA_ASSERT((int)handle);

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

    /* We only allow a file to be renamed to in the current directory.
     * Make sure the argument doesn't refer to different directory
     * and file name must be specified.
     */
     if ((strstr(argp, "\\") != NULL) || (strstr(argp,"/") != NULL) || 
         (strlen(pFile->buffer) == 0) || (strlen(argp) == 0))
        goto _rename_to_ret;
    
	pFile->error_status = NAFSinit_io_request_cb(&pFile->io_request, NULL, 0);
	if (pFile->error_status != NAFS_SUCCESS) goto _rename_to_ret;

    pFile->error_status = NAFSrename_file(pFile->directory_path, pFile->buffer, argp, pFile->permissions, &pFile->io_request);

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

#if (_DEBUG_PRINTF > 0)
    printf("fs_rename_to: rename %s to %s\n", pFile->buffer, argp);
#endif
    rc = NAFTPS_SUCCESS;
        
_rename_to_ret:
    return rc;
}

/**
 * Return the present working directory
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      NOT Used
 * @param    handle    handler 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
 */
int FS_Pwd(char * argp, unsigned long handle, char * buffer)
{
    FS_FILESYSTEM_T * pFile; 
    int rc = NAFTPS_GENERROR, length, j;

    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);

    /* convert forward slash (file system syntax) to backward slash (FTP server syntax)*/
    length = strlen(buffer);
    for (j = 0; j < length; j++)
    {
       if (buffer[j] == '/')
           buffer[j] = '\\';
    }
    rc = NAFTPS_SUCCESS;

_pwd_ret:
    return rc;
}

/**
 * This function is used to go back to root directory. 
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      Not Used
 * @param    handle    handler 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
 *
 */
int FS_RootDirectory(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR;

    NA_ASSERT((int)handle);

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

    if (set_request_path(pFile->buffer, sizeof pFile->buffer, pFile->directory_path, "//") != FILE_OK)
        goto _root_dir_ret;        

    strcpy(pFile->directory_path, pFile->buffer);

    rc = NAFTPS_SUCCESS;

_root_dir_ret:
    return rc;

}

/**
 * Change to the specified directory in the file system
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path which will be changed to
 * @param    handle    handler to access FTP server client connection  
 *
 * @return  NAFTPS_SUCCESS   Successfully change the directory.
 * @return  NAFTPS_GENERROR  Unable to change the directory
 *
 * @see @link NAFTPS
 *
 */
int FS_ChangeDirectory(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_GENERROR, i, status;
    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 _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, NULL, 0);
    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) &&
		(MatchUsingToken(pFile->dir_info_list[i].entry_name,new_directory) != NULL)) 
                {
		    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;
}

/**
 * List all items with detail info for the specified directory
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path for which all items will be returned
 * @param    handle    handler 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
 *
 */
int FS_ListDir(char * argp, unsigned long handle)
{
    int rc;

    NA_ASSERT((int)handle);

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

/**
 * List all items for the specified directory
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path for which all items will be returned
 * @param    handle    handler 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
 *
 */
int FS_nListDir(char * argp, unsigned long handle)
{
    int rc;

    NA_ASSERT((int)handle);

    rc = list_dir(argp, handle, LIST_FILE_ONLY);
    return rc;                            
}
/**
 * Creates a new directory. 
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory path which will be created
 * @param    handle    handler 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
 *
 */
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, NULL, 0);
    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) >= sizeof pFile->directory_path)
        goto _make_dir_ret;


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

    group_id = pFile->permissions >> 16;
    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;
}


/**
 * Remove a directory
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to directory name to be removed 
 * @param    handle    handler to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully remove the directory 
 * @return  NAFTPS_GENERROR  Unable to remove the directory
 *
 * @see @link NAFTPS
 *
 */
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, NULL, 0);
    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;
   
}

/**
 * Close and terminate the current ftp session
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a string containing the login username 
 * @param    handle    handler 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
 *
 */
int FS_SessionClosed(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_SUCCESS;

    NA_ASSERT((int)handle);

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

	return rc;
}
/**
 * Close and terminate data socket
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    argp      pointer to a string containing the login username 
 * @param    handle    handler to access FTP server client connection  
 *
 *                        
 * @return  NAFTPS_SUCCESS   Successfully close a file
 * @return  NAFTPS_GENERROR  Unable close a file 
 *
 * @see @link NAFTPS
 *
 */
int FS_DataSessionClosed(char * argp, unsigned long handle)
{
    FS_FILESYSTEM_T * pFile; 
    int             rc = NAFTPS_SUCCESS;

    pFile = get_filesystem_cb(handle);   
    if (pFile != NULL)
    {
        if (pFile->file_handler != 0)
        {
            rc = fs_close_file (handle);
        }
    }
	return rc;
}

/**
 * Register all functions to handle file system for FTP Server
 * This function should be called after FSInitialize and before FSStartServer.
 *
 * @external
 * @include "ftpsvrfs.h"
 * @category 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 FSInitialize
 * @see @link FSStartServer
 * @see @link NAFTPS
 */
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;
    }

    /* 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;
    }

    if (tx_mutex_create(&gFtpSvrFileMutex, "FtpSvrFileMutex", TX_NO_INHERIT) != TX_SUCCESS)
    {
        rc = NAFTPS_GENERROR;
    }

_fsinstallfilesystem_ret:
   return rc;
}

/**
 * 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)
 *
 * @internal
 * @include "ftpsvrfs.h"
 * @category FTP_Server:File_System_Interface
 * @since 6.0 
 *
 * @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
 *
 */
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;
}


