/*
 *  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 FTP_Server:File_System_Interface
 * @since 6.0
 * @see @link FTP_Server
 * @see @link 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"


#define _DEBUG_PRINTF           0

#define _READ_PERMISSION_BIT    2
#define _WRITE_PERMISSION_BIT   16      /* bit number for write permisstion */

extern int getErrno (void);

static int blocks                  = 0;
static int pageSize                = 0;
static unsigned int  remnantCount = 0;
char os_boot_file[64];
char u_boot_file[64];

static int ftp_nand_stor (char *bufferp, int buflen, char *argp, unsigned long handle);
int yaffs_FindNetOSPartition(unsigned int *fsStart, unsigned int *fsSize);
int yaffs_FindUBootPartition(unsigned int *fsStart, unsigned int *fsSize);

/*ad*
 * When defined to 1, it allows users to list all files and directories which
 * the user has access permission to. It's defined to 0 by default which will
 * displays all files and directories. 
 * 
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.2
 *
*ad*/
#define _RETRICTED_USER_LISTING     0

/*ad*
 * When defined, _FTPS_RESTRICTED_FILE_CREATION allows users to create a file
 * only if it does not already exist. If this definition is 0, data will overwrite
 * an existing file.
 *
 * @external
 * @category FTP_Server:File_System_Interface
 * @since 6.0
 *
*ad*/
#define _FTPS_RESTRICTED_FILE_CREATION   0

/*ad*
 * 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
*ad*/
#define FILE_OK                 0
#define INVALID_FILE_LEN        -1
#define INVALID_DIRECTORY_LEN   -2

#define LIST_FILE_ONLY          0
#define LIST_FILE_INFO          1

/*
 * This is used for listing files. If the date of a file exceeds this max
 * time (in seconds),  year will be returned. 
 * 
 */
#define FS_MAX_SECONDS      (180 * 24 * 60 * 60)  /* ~6 months */

/*
 * 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.
 *
 * 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*2+1];
    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);

static int isBootFile(char * fname)
{
    if ((strcmp (fname, "rom.bin") == 0)     || 
        (strcmp (fname, "spi_rom.bin") == 0) || 
        (strcmp (fname, "romzip.bin") == 0)  || 
        (strcmp (fname, "u-boot.bin") == 0) || 
		(strcmp (fname, u_boot_file) == 0) ||
		(strcmp (fname, os_boot_file) == 0) ||
        (strcmp (fname, "image.bin") == 0))
			return TRUE;
	else
			return FALSE;
}

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

/******************************************************************************
 *
 *      Definitions and Declarations for Flash Image Downloads. 
 *
 *      BUFFERSIZE of the block of memory malloced to store FTP fragments 
 *
 *      DELAY_BEFORE_RESET is the number of seconds to wait prior to
 *      wait prior to reseting after an image was downloaded  
 */
#define BUFFERSIZE              128 * 1024
#define DELAY_BEFORE_RESET      (2 * NABspTicksPerSecond)
#define NOT_USED                0

static char *sectorDatap = NULL;
static int  successful = 0;
static int  currentLength = 0;
static unsigned int  currentSector = 0;
static unsigned int  writeIndex = 0;
static int sectors = 0;
static unsigned long *sectorSizes = NULL;
static unsigned int  romsize = 0;
static int flashFull = 0;


/*ad*
 * Application convenience function for the FTP server API used to hook the FTP server 
 * calls via the @link FSRegisterSTOR and used to handle storing executable images to 
 * flash.  This function takes FTP receive data from the client and places it in flash 
 * so NET+OS will boot from Flash.
 *
 * This function calls @link NAFlashSectors and @link NAFlashSectorSizes to determine the
 * number of sectors and size of the sectors for downloading.  This function is then 
 * called multiple times until it holds a full flash sector frame and then makes a call to 
 * the @link NAFlashWrite function.
 *
 * This function should be used in conjunction with the @link FSCloseWithFlashCleanUp and 
 * @link FSRegisterControlClose calls so the end image fragment gets stored once
 * the FTP client connection closes.
 *
 * This function is also used within a @link FSInstallFileSystem call.
 *
 * @external
 * @include "fservapi.h"
 * @location src/fs_intf/ftpsvrfs.c
 * @library filesys,ftpsvr
 * @platform all
 * @category FTP_Server
 * @since 7.2
 *
 * @param    bufferp   pointer to the buffer holding FTP receive data 
 * @param    buflen    length of a buffer
 * @param    argp      pointer to FTP 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 
 * @return  NAFTPS_FILESYSTEMERR  Flash error, due to image too large or non-recognized flash. 
 *
 * @see @link NAFTPS
 * @see @link FSRegisterSTOR
 * @see @link FSInstallFileSystem
 * @see @link FSCloseWithFlashCleanUp
 * @see @link NAFlashWrite
 * @see @link NAFlashSectors
 * @see @link NAFlashSectorSizes
 *
 * @note    To use this function, this file must be included as part of your 
 *          application project.
 *
 *          This function is not capable of processing simultaneous FTP downloads.
 *
 *
*ad*/
int FSStoreImageToFlash(char * bufferp, int buflen, char * argp, unsigned long handle)
{
    int templen, k;
    int result;

    if (NABspIsFlashNand() == TRUE)
    {
        return ftp_nand_stor(bufferp, buflen, argp, handle);
    }

    if (romsize == 0)
    {
        sectorDatap = malloc (BUFFERSIZE);
        if (sectorDatap == NULL)
        {
            printf ("FTP Server could not allocate memory for Flash write.\n");
            return NAFTPS_GENERROR;
        }

        /*
         * Initialize the FTP server internal data structures. The following
         * allocates control data structures for two concurrent users.
         */
#ifdef BSP_ARM9
        romsize = customizeGetCSSize ((WORD32 *)NA_SCM_CS1_BASE_ADDR, (WORD32 *)NA_SCM_CS1_BASE_ADDR);
#else
        romsize = customizeGetCSSize ((WORD32 *)NARM_CSAR0_ADDR, (WORD32 *)NARM_CSOR0_ADDR);
#endif

        /* determine the flash physical sectors and sector sizes */
        sectors = NAFlashSectors();
        sectorSizes = (unsigned long *)malloc(sizeof(unsigned long) * sectors);
        if (NAFlashSectorSizes(sectorSizes))
        {
            printf ("FTP Server not started\n");
            printf ("Unable to get Flash sector size\n");
            printf ("Check if flash is enabled\n");
            free (sectorSizes);
            free (sectorDatap);
            sectorDatap = NULL;
            romsize = 0;
            return NAFTPS_FILESYSTEMERR;
        }
    }

    /*  our protocol is to verify they are trying to download rom.bin   */
	if (isBootFile(argp) == TRUE)
    {
        if ((strcmp (argp, "image.bin") == 0) && (currentSector == 0))
        {
           writeIndex = NAAppOffsetInFlash;
           for (k = currentSector; k < sectors; k++)
           {
               if (sectorSizes[k] <= writeIndex)
               {

                    writeIndex -= sectorSizes[k];
                    if (k == (sectors - 1))
                    {
                        /* No more flash memory to write, so set flag */
                        flashFull = 1;
                    }
                }
                else
                {
                    currentSector = k;
                    break;
                }
            }
            if (writeIndex != 0)
            {
                printf ("_NAAppOffsetInFlash in the linker customizatino file is set incorrectly.  \n");
                printf ("The application image must start on a sector boundary.\n");
                successful = 0;
                return NAFTPS_FILESYSTEMERR;
            }
        }

        if (flashFull)
        {
            /* If all flash memory has been written, clear flag and return -2 */
            printf("Image size exceeds flash memory size, and flash memory has been overwritten.\n");
            successful = 0;
            return NAFTPS_FILESYSTEMERR;
        }

        if ((currentLength + buflen) > romsize)
        {
            /* If data received exceeds available flash memory, clear flag and return -2 */
            printf("Image size exceeds flash memory size, but flash memory has not been overwritten.\n");
            successful = 0;
            return NAFTPS_FILESYSTEMERR;
        }

        /*  copy this file into our buffer  */
        if (currentLength + buflen > BUFFERSIZE)
        {
            templen = BUFFERSIZE - currentLength;
        } 
        else
        {
            templen = buflen;
        }

        memcpy ((char *)&sectorDatap[currentLength], bufferp, templen);

        currentLength += templen;

        /* Write the data to Flash in 128K chunks */
        /* Smaller data chunks decrease performance but require a smaller buffer */
        while (currentLength == BUFFERSIZE)
        {
            /* Write to a valid Flash sector */
            if (currentSector < sectors)
            {
                result = NAFlashWrite(currentSector, writeIndex, BUFFERSIZE, sectorDatap, ERASE_AS_NEEDED);
                if (result)
                {
                    printf ("FSStoreImageToFlash NAFlashWrite() error %d.\n", result);
                    successful = 0;
                    return result;
                }

                /*  save the extra data not written to flash -- for the next NAFlashWrite() */
                currentLength = 0;
                if (buflen > templen)
                {
                    if ((buflen - templen) >= BUFFERSIZE)
                    {
                        memcpy ((char *)&sectorDatap[currentLength], (char *)&bufferp[templen], BUFFERSIZE);
                        templen += BUFFERSIZE;
                        currentLength = BUFFERSIZE;
                    }
                    else
                    {
                        memcpy ((char *)&sectorDatap[currentLength], (char *)&bufferp[templen], buflen - templen);
                        currentLength = buflen - templen;
                    }
                }
                writeIndex += BUFFERSIZE;

                /* Compute the sector and sector offset to start writing next */
                for (k = currentSector; k < sectors; k++)
                {
                    if (sectorSizes[k] <= writeIndex)
                    {
                        writeIndex -= sectorSizes[k];
                        if (k == (sectors - 1))
                        {
                            /* No more flash memory to write, so set flag */
                            flashFull = 1;
                        }
                    }
                    else
                    {
                        currentSector = k;
                        break;
                    }
                }
            }
        }

        successful = 1;
    }

    return NAFTPS_SUCCESS;
}

static void resetAfterImageDownload (ULONG notUsed)
{
    NA_UNUSED_PARAM(notUsed);
    customizeReset();   /* reset*/
}

int ftp_nand_erase_partition(char *devName, int startBlock, int count)
{
    return yaffs_ErasePartition(devName, startBlock, count);
}

/*
 * ftp_nand_stor
 *
 * This routine is the nand flash handler used by the FTP server STOR command
 * to write a u-boot or kernel boot image into the boot sector (non file system)
 * partition.
 * This routine collects data in g_flashbuf and then
 * writes it to nand flash memory at the current sector in the boot sector.
 * If the file is u-boot.bin, we write in blocks 0-15 (<= 256 KB). 
 * If the file is image.bin, we write in blocks 16-4095 (<= 2.75 MB)
 *
 * INPUT
 *
 *    bufferp    Contains data read by the FTP server.
 *    buflen     Specifies length of bufferp data.
 *    argp       Specifies file name being read.
 *    handle     Handle to FTP server client connection object. 
 */ 

static int ftp_nand_stor (char *bufferp, int buflen, char *argp, unsigned long handle)
{
    int uboot_image;
    int os_image;
    int blocksize;
    int pagesize;
    int firstBlock;
    int endBlock;
    int startBlock;
    int chunksperblock;
    char *devName = "/root";
    unsigned int fsStart, fsSize;
    int dataLeft;
    unsigned char *buf;

    uboot_image = ((strcmp (argp, u_boot_file) == 0) || 
        (strcmp (argp, "u-boot.bin") == 0));
    os_image = ((strcmp (argp, os_boot_file) == 0) ||
        (strcmp (argp, "image.bin") == 0));

    if (uboot_image || os_image)
    {
        if (romsize == 0)
        {
            printf("starting nand flash boot sector download\n\r");
            /*
             * This end block is for the whole "/root" partition.
             * Find the specific sections within the partition used
             * for the U-Boot or NET+OS image below. 
             */
            if (yaffs_ReadNandParms(devName, &pagesize, &blocksize,
                        &chunksperblock, &firstBlock, &endBlock) != 0)
            {
                return NAFTPS_FILESYSTEMERR;
            }

            sectorDatap = malloc (pagesize);    /* for final partial page */
            if (sectorDatap == NULL)
            {
                printf ("FTP Svr could not allocate memory for Flash write.\n");
                return NAFTPS_GENERROR;
            }
            /* blocks means 'eraseable blocks" here, not pages */
            if (uboot_image)
            {
                if (yaffs_FindUBootPartition(&fsStart, &fsSize) != 0)
                {
                    return NAFTPS_GENERROR;
                }
            
                blocks = fsSize / (pagesize * chunksperblock);
                startBlock = fsStart / (pagesize * chunksperblock);
            }
            else    /* OS image */
            {
                if (yaffs_FindNetOSPartition(&fsStart, &fsSize) != 0)
                {
                    return NAFTPS_GENERROR;
                }

                blocks = fsSize / (pagesize * chunksperblock);
                startBlock = fsStart / (pagesize * chunksperblock);
            }

            romsize = blocks * blocksize;   /* this is how much we can write */
            /* The global var currentSector is running page (chunk) offset */
            currentSector = startBlock * chunksperblock;
            pageSize = pagesize;
            writeIndex = 0;
            currentLength = 0;
            remnantCount = 0;
            ftp_nand_erase_partition(devName, startBlock, blocks);
        }

        /*
         * global vars keep track of
         * how many bytes we've written to flash, (currentLength)
         * next page to write, (currentSector)
         * how many are in our orphan buffer, (remnantCount)
         * and how big the file can be. (romsize)
         */ 

        if ((currentLength + buflen) > romsize)
        {
            /* If data received exceeds available flash memory, clear
             * flag and return -2 */
            printf("Image size would exceed partition size. Not writing.\n");
            successful = 0;
            return NAFTPS_FILESYSTEMERR;
        }

        /* Write the data to nand in pageSize chunks */
        dataLeft = buflen;
        buf = (unsigned char *)bufferp;
        /* take care of any data left from last time */
        if (remnantCount)
        {
            /* make a full page in the remnant buffer */
            if (dataLeft >= pageSize - remnantCount)
            {
                memcpy(&sectorDatap[remnantCount], buf,
                                pageSize - remnantCount);
                yaffs_writeChunkAPI(devName, currentSector,
                                (unsigned char *)sectorDatap);
                dataLeft -= (pageSize - remnantCount);
                buf += (pageSize - remnantCount);
                currentSector++;
                currentLength += pageSize;
                remnantCount = 0;   /* nothing hanging around now */
            }
            else    /* not a full chunk -- save, don't send */
            {
                memcpy(&sectorDatap[remnantCount], buf, dataLeft);
                remnantCount += dataLeft;
                dataLeft -= dataLeft;
            }
        }

        /* then write out the rest of the receive buffer */
        while (dataLeft >= pageSize)
        {
            yaffs_writeChunkAPI(devName, currentSector, buf);
            dataLeft -= pageSize;
            buf += pageSize;
            currentSector++;
            currentLength += pageSize;
        }

        if (dataLeft > 0)
        {
            /* save data not written to flash for the next time around. */
            memset(sectorDatap, 0xff, pageSize);
            memcpy(sectorDatap, buf, dataLeft);
            remnantCount = dataLeft;	/* save in global */
        }

        /* we get the final remnant on the socket close (see ftp_nand_run) */
        successful = 1;
    }   /* os_image or uboot_image */
    return NAFTPS_SUCCESS;
}

static void ftp_ResetSystem(void)
{
    static TX_TIMER resetTimer = {0};

    /*  We did an image download -- reset the unit  */
    printf ("FTP: Flash download complete.\n" );
    if (tx_timer_create (&resetTimer, "Reset Timer", resetAfterImageDownload,
			NOT_USED, (2 * NABspTicksPerSecond), NOT_USED,
		   	TX_AUTO_ACTIVATE) == TX_SUCCESS)
    {
        printf ("Resetting system in 2 seconds.\n" );
    }
}

static int ftp_nand_run (char *argp, unsigned long handle)
{
    char *devName = "/root";

    if (successful)  /* the whole rom.bin is downloaded successfully */
    {
        /* we already checked that the received data will fit in 
         * our partition. */
        /* take care of any data left from last time. We have to write
         * the partial page. */
        if (remnantCount)
        {
            yaffs_writeChunkAPI(devName, currentSector,
                            (unsigned char *) sectorDatap);
            printf ("FTP: final partial page %d. %d bytes\n\r",
                            currentSector, remnantCount);
            currentSector++;
            currentLength += (remnantCount);
        }

		ftp_ResetSystem();
    }

    /* Reset variables to allow downloading again */
    currentSector = 0;
    writeIndex = 0;
    currentLength = 0;
    flashFull = 0;
    successful = 0;
    romsize = 0;
    return NAFTPS_SUCCESS;
}

/*ad*
 * FTP server API application convenience function for @link FSRegisterControlClose used to
 * complete the end flash sector fragment during FTP downloads.  This function should be 
 * used in conjunction with the other FTP convenience function @link FSStoreImageToFlash to 
 * write application images to Flash.
 * 
 * Since the last portion of a flash download will rarely end up along a Flash sector 
 * boundary, it's necessary to trigger the last flash write call @link NAFlashWrite on 
 * the FTP quit event.
 *
 * Applications need to call @link FSRegisterControlClose to register this function.
 * When @link FSInstallFileSystem is called, this function will be registered and 
 * used without any application calls.
 *
 * @external
 * @include "fservapi.h"
 * @location src/fs_intf/ftpsvrfs.c
 * @library filesys,ftpsvr
 * @platform all
 * @category FTP_Server
 * @since 7.2 
 *
 * @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
 * @see @link FSRegisterControlClose
 * @see @link FSInstallFileSystem
 * @see @link FSStoreImageToFlash
 * @see @link NAFlashWrite
 *
 * @note    To use this function, this file must be included as part of your 
 *          application project.
 *
 *          This function is not capable of processing simultaneous FTP downloads.
 *
*ad*/
int FSCloseWithFlashCleanUp(char * argp, unsigned long handle)
{
    int result = NAFTPS_SUCCESS;

    if (NABspIsFlashNand() == TRUE)
    {
        return ftp_nand_run(argp, handle);
    }

    if (successful)  /* the whole rom.bin is downloaded successfully */
    {
        if (currentLength)
        {
            /* Write to a valid Flash sector */
            if (currentSector < sectors)
            {
               result = NAFlashWrite(currentSector, writeIndex, currentLength, sectorDatap, ERASE_AS_NEEDED);
               if (result)
               {
                   printf ("FSCloseWithFlashCleanUp NAFlashWrite() error %d.\n", result);
               }
            }
        }
    }

    if (sectorDatap != NULL)
    {
		ftp_ResetSystem();

        /* Reset variables to allow downloading again */
        currentSector = 0;
        writeIndex = 0;
        currentLength = 0;
        flashFull = 0;
        successful = 0;
        romsize = 0;

        free (sectorSizes);
        free (sectorDatap);
        sectorDatap = NULL;
    }

    return result;
}



/*ad*
 * 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
 *
*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 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.
 *
 * 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.
 * 
 * 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 = 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) <= *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 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
 * 
 * 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 >> _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 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 >> _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 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 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);
                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 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';
            
                rc = NAFTPS_SUCCESS;
                break;
            }

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


/*
 * This function closes a file if any.
 *
 * category 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 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 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        byteTransferred;
    int		            socket_fd, i, 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, 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 (_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;                            
}

static unsigned long firmwareDownloadHandle = 0;

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

    /*  redirect firmware upload files to dedicated routines    */
	if (isBootFile(argp) == TRUE)
    {
        firmwareDownloadHandle = handle;
        return FSStoreImageToFlash (bufferp, buflen, argp, handle);
    }

#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, 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 >> _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(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
		pFile->io_request.arg2 = 0;  /* clean up */

        pFile->error_status = NAFSwrite_file(pFile->file_handler, pFile->permissions, 
                                    1, 0, bufferp,
		                            &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) 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.
 *
 * @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    handle 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
*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;

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

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

/*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 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    byteTransferred;
    int             i;
    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, 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(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 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, NULL, 0);
	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, NULL, 0);
    	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(10);
	    	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(10);
	    	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(10);
	    	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 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 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, 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) &&
		            (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 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)
{
    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 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 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 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, 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 >> _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 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, 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;
   
}

/*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 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_ASSERT((int)handle);

    /*  check to see if this was a firmware download    */
    if (firmwareDownloadHandle == handle)
    {
        /*  if so, complete the last sector and write to Flash. */
        return FSCloseWithFlashCleanUp (argp, 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 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;

    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 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);
	
	sprintf(os_boot_file, "image-%s.bin", NABspPlatform);
	sprintf(u_boot_file, "u-boot-%s.bin", NABspPlatform);
    
    /* 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;
}




