/*
 *
 *     Module Name:  nafs_intf.c
 *         Version: 1.00
 *        Language: Ansi C
 * Compile Options:
 * Compile defines:
 *       Libraries:
 *    Link Options:
 *
 *    Entry Points:
 *
 *  Copyright (c) 1996-2005 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
 *
 */


/*
 * This file contains the implementation of the native file system interface
 * to the C library to file system interface in startfilesystem.c.
 */
#include <errno.h>
#ifdef BSP_ARM9
#include "bsp_refs.h"
#else
#include "bsp_arm7refs.h"
#endif
#include "bsp_fs.h"
#include "bsp_drivers.h"
#include "Npttypes.h"

#include "sysAccess.h"
#include "fs.h"
#include "fss.h"
#include "rss.h"
#include "fs_api.h"
#include "flash.h"
#include "naflash.h"
#include "netosIo.h"
#include "naclibfs.h"
#include "clib_fs.h"
#include "bootldr.h"
#include "boardParams.h"
#include "bsp_api.h"
#include "minmax.h"

/* #define _DEBUG_PRINTF */

/* #define _MEM_PRINTF */

/* external references */
int yaffs_StartUp(void);
int yaffs_mount(const char *path);

#ifdef NETOS_DEBUG
#define _DEBUG_PRINTF
#endif

/* status for file handler */
#define _FILE_NOT_USED  0
#define _FILE_OPENED    0x1
#define _FILE_CREATED   (0x01 << 1)
#define _FILE_END       (0x01 << 2)
    
    
#define FILE_OK                 0
#define INVALID_FILE_LEN        -1
#define INVALID_DIRECTORY_LEN   -2

#define IO_REQ_EVENT_BIT    0x01

/* operation enum */
enum {
    _FS_OP_INTERFACE,
    _FS_OP_MKDIR,
    _FS_OP_OPEN,
    _FS_OP_CLOSE,
    _FS_OP_WRITE,
    _FS_OP_READ,
    _FS_OP_UNLINK,
    _FS_OP_RMDIR,
    _FS_OP_RENAME,
};

static int native_fs_initialized = 0;
static fs_config_t * gNativeVolumeList = NULL;  /* Native volume list */
static unsigned int gNativeVolumeCount = 0;     /* Number of native volumes */
static unsigned int gNativeDefaultGroupId = NAFS_FS_GROUP1;

static NaClibHandleFs_t     * gClibHandleList = NULL;   /* list of active file handlers */
/* static int                  gNaClibMaxFiles = 0; */  /* max of active file handler */

#ifdef _DEBUG_PRINTF
void get_application_addr(WORD32 * app_start, WORD32 * app_end)
{
        blImageHeaderType *imageInfo = (blImageHeaderType *) NABspApplicationFlashAddress;
        
        *app_start = NABspApplicationFlashAddress + imageInfo->headerSize;
        *app_end = *app_start + imageInfo->size;
        
        printf("get_application_addr: imageInfo 0x%X\n", (unsigned)imageInfo);
        printf("get_application_addr: Rom App starts 0x%X\n", (unsigned)*app_start);
        printf("get_application_addr: Rom App ends 0x%X\n", (unsigned)*app_end);
        printf("get_application_addr: Rom App size 0x%X\n", (unsigned)imageInfo->size);

        return;
}
#endif

/**
 * Allocates memory for file handler
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @return  NULL        Out of memory
 * @return  "Not Null"  Pointer to a new file handler
 *
**/
NaClibHandleFs_t * allocate_file_buffer(void)
{
    NaClibHandleFs_t *filePtr;

    GET_SEMAPHORE    
    filePtr = (NaClibHandleFs_t *)malloc(sizeof(NaClibHandleFs_t));
    if (filePtr != NULL)
    {
    #ifdef _MEM_PRINTF
        printf("malloc ---> 0x%X\n", (unsigned) filePtr);
    #endif
        if (gClibHandleList == NULL)
        {
            gClibHandleList = filePtr;
            filePtr->next = NULL;
        }
        else
        {
            filePtr->next = gClibHandleList;
            gClibHandleList = filePtr;
        }
        filePtr->file_handler = 0;
        filePtr->offset = 0;
        filePtr->mode = 0;
        filePtr->status = _FILE_NOT_USED;
        filePtr->permissions = NASYSACC_FS_SUPER_USER;
        filePtr->file_size = 0;
    }
    PUT_SEMAPHORE    
    
    return filePtr;
}

/**
 * Frees file handler memory that is previously allocated.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   filePtr     Pointer to the file handler to be freed.
 *
**/
void free_file_buffer(NaClibHandleFs_t * filePtr)
{
    NaClibHandleFs_t * thePtr, * thePtr1;
    
    if (filePtr != NULL)
    {
        GET_SEMAPHORE    
        if (gClibHandleList == filePtr)
        {
            gClibHandleList = filePtr->next;
        #ifdef _MEM_PRINTF
            printf("free <--- 0x%X\n", (unsigned) filePtr);
        #endif
            free((void *)filePtr);
        }
        else
        {
            thePtr1 = gClibHandleList;
            for (thePtr = gClibHandleList->next; thePtr != NULL; thePtr = thePtr->next)
            {
                if (thePtr == filePtr)
                {   /* found it.
                     * Before freeing it, let's break this link to next item 
                     */
                    thePtr1->next = thePtr->next;
                #ifdef _MEM_PRINTF
                    printf("free <--- 0x%X\n", (unsigned) filePtr);
                #endif
                    free((void *)filePtr);
                    break;
                }
                thePtr1 = thePtr;
            }
        }
        PUT_SEMAPHORE    
    }    
    return;
}

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

/*
 * Deletes the event flag if it is created
 */
static void cleanup_io_request(NAFS_IO_REQUEST_CB * io_request)
{
    TX_EVENT_FLAGS_GROUP * eventPtr = (TX_EVENT_FLAGS_GROUP *)io_request->user_data;
    
    if (eventPtr != NULL)
    {
        tx_event_flags_delete(eventPtr);
        io_request->user_data = 0;    
    }        
}

/*
 * Creates an event flag and sets up the IO request callback
 */
static int setup_io_request(NAFS_IO_REQUEST_CB * io_request, TX_EVENT_FLAGS_GROUP * eventPtr)
{
    int rc = NAFS_SUCCESS;

    io_request->user_data = 0;    
    if (eventPtr != NULL)
    {
        memset(eventPtr, 0, sizeof(TX_EVENT_FLAGS_GROUP));    
        if (tx_event_flags_create(eventPtr, "Native File IO") != TX_SUCCESS)
        {
            rc = NAFS_IO_REQUEST_CB_NOT_INITIALIZED;
            goto doneSetupIoReq; 
        }
    }

    rc = NAFSinit_io_request_cb (io_request, io_callback_fn, (unsigned int)eventPtr);
    doneSetupIoReq:
        return rc;    
}

/**
 * Returns for file handler for the given handle.
 * This looks for native file handler for the given CLIB file handler.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @return  NULL        Out of memory
 * @return  "Not Null"  Pointer to file handler
 *
**/
static NaClibHandleFs_t * get_file_buffer(unsigned int clib_handle)
{
    NaClibHandleFs_t * filePtr;

    GET_SEMAPHORE    
    for (filePtr = gClibHandleList; filePtr != NULL; filePtr = filePtr->next)
    {
        if (filePtr == (NaClibHandleFs_t *)clib_handle)
        {
            break;
        }
    }
    PUT_SEMAPHORE    
    
    return filePtr;
}

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

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

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

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

        if (bufferPtr != NULL)
        {
            bufferPtr++;
            namePtr = bufferPtr;
        }
    }
    len = strlen(fullPathName);
    
    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';
        if (strstr(dirpath, "/") == NULL && strstr(dirpath, "\\") == NULL)
            strcat(dirpath, "/");  /* root directory requires "/" */

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

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

    *filelen = strlen(namePtr);

    return rc;    
}

/*
 * It waits for io request status from the file system.
 *
 * @category clib_FileSystemInterface:native
 * 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  NAFS_IO_REQUEST_TERMINATED    File System Error. Check error_status.
 * return  NAFS_SUCCESS          No Error
 *
 */
static int wait_io_status(NAFS_IO_REQUEST_CB * io_request, unsigned int * byteTransferred, int * error_status)
{
    unsigned int        requestType, status;
    int                 rc;
    unsigned long       actual_event;
    TX_EVENT_FLAGS_GROUP * eventPtr = (TX_EVENT_FLAGS_GROUP *)io_request->user_data;

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

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

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

#ifdef _DEBUG_PRINTF
    if (*error_status != NAFS_SUCCESS)
        printf("fs_wait_io_status Error: rc = %d status %d error status %d\n", rc, status, *error_status);
#endif
    
    return  ((rc != NAFS_SUCCESS || *error_status != NAFS_SUCCESS) ? NAFS_IO_REQUEST_TERMINATED: NAFS_SUCCESS);
}

/**
 * Returns group_id and permission used to access the given directory or file.
 * This looks for the volume in the given directory or file. If the volume is 
 * given (initialized) in BSP's table, it returns the group id and 
 * permission from the table. If the volume is not given in the BSP's table,
 * this function calls native file system API to get information about the
 * given directory or file.  If the given directory or file is under the volume
 * (VOLUME/DIR), default group id and NASYSACC_FS_SUPER_USER will be used for
 * group id and permission.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   io_request     Pointer to the file system io hander
 * @param   dir_path       Pointer to full directory or file path
 * @param   group_id       Pointer to memory where group id be returned.
 * @param   permission       Pointer to memory where permission be returned.
 *
 * @return  -1      Unable to get the group id & permission.
 * @return  0       It successfully obtains group id & permission.
 *
**/
static int get_group_id(NAFS_IO_REQUEST_CB * io_request, char * dir_path, 
                            unsigned int * group_id, unsigned int * permission)
{
    int          rc = -1, status, error_status;
    unsigned int i;
    unsigned int byteTransferred;
    unsigned int            dir_info_list_items;
    NAFS_DIR_ENTRY_INFO     * dir_info_list = NULL;
    char                    * slash;
    char                    * cur_directory = NULL, * new_directory = NULL;
    int                     new_dirlen, cur_dirlen;

    /* let check the volume in BSP's table if any */
    for (i=0; i < gNativeVolumeCount; i++)
    {
        slash = strstr(dir_path, "\\");
        if (slash == NULL)
        {
            slash = strstr(dir_path, "/");
        }
        
        if (slash != NULL)
        {
            /* if this is the one listed in the table, we must use the permission
             * & group id given in this table.
             */
            if (memcmp(gNativeVolumeList[i].volume_name, dir_path, (slash - dir_path)) == 0)
            {
                *group_id = (unsigned int) gNativeVolumeList[i].group_id;
                *permission = (unsigned int) gNativeVolumeList[i].user_permissions;
                rc = 0;
                goto _ret;
            }
        }
    }
    /* we don't find it in the BSP's table.
     * let's try to get info from the file system API.
     */
         
    cur_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    new_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    
#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) cur_directory);
    printf("malloc ---> 0x%X\n", (unsigned) new_directory);
#endif    
    if (cur_directory == NULL || new_directory == NULL)
        goto _ret;
    
    /* parse the directory path. Separate the name of the 
     * new directory and the path of the directory where
     * the new diretory is going to be created.
     */
    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    new_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    
    if (parse_file_path(dir_path, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK)
        goto _ret;
    
    *group_id = gNativeDefaultGroupId;
    /* root directory */
    if (new_dirlen == 0)
    {
        *permission = NASYSACC_FS_SUPER_USER;
        rc = 0;
        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.
     */
    status = NAFSdir_entry_count(cur_directory, NASYSACC_FS_SUPER_USER, 
                        &dir_info_list_items, io_request);

    if (status != NAFS_SUCCESS) goto _ret;

    if (wait_io_status(io_request, &byteTransferred, &status) != NAFS_SUCCESS)
        goto _ret;

    dir_info_list_items++; /* at least one entry */

    dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

    if (dir_info_list == NULL) goto _ret;
    
#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) dir_info_list);
#endif    
	    
    memset(dir_info_list, 0, (dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

    /* Get info on the previous directory and make sure the specified directory is
     * existed.
     */
    status = NAFSlist_dir(cur_directory, NASYSACC_FS_SUPER_USER, dir_info_list, 
                                        dir_info_list_items, io_request);

    if (status != NAFS_SUCCESS) goto _clean;

    status = wait_io_status(io_request, &byteTransferred, &error_status);

    if (status == NAFS_SUCCESS && error_status == NAFS_SUCCESS)
    {
        for (i=0; i < dir_info_list_items; i++)
        {
        #ifdef _DEBUG_PRINTF
            printf("%d - %s entry type %d\n", i+1, dir_info_list[i].entry_name, dir_info_list[i].entry_type);
        #endif  
            /* get the right entry */
            if (dir_info_list[i].entry_type  == NAFS_FILE_TYPE_DIR &&
                strcmp(dir_info_list[i].entry_name, new_directory) == 0)
            {
                *group_id = dir_info_list[i].group_id;
                *permission = NASYSACC_FS_SUPER_USER;
            #ifdef _DEBUG_PRINTF
                printf("get_group_id: %s directory has 0x%x group id.\n", new_directory, *group_id);
            #endif
                rc = 0;
                break;
            }
        }
    }
_clean:
#ifdef _MEM_PRINTF
    printf("free <--- 0x%X\n", (unsigned) dir_info_list);
#endif    
    free(dir_info_list);
_ret:
    if (cur_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_directory);
    #endif    
        free(cur_directory);
    }
    if (new_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) new_directory);
    #endif    
        free(new_directory);
    }
    return rc;
}

/**
 * Get the file size for the given file handler. 
 * It calls native file system API to obtain the file size for the given file.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   file_handle     File handler for file system API
 * @param   file_size       Pointer to memory where file size will be written.
 * @param   permissions     Permissions for access file system API
 *
 * @return  -1      Unable to get the file size
 * @return  0       It successfully obtains the file size
 *
**/
int get_file_size (int file_handle, unsigned int * file_size, unsigned int permissions)
{
    int                 rc = -1, error_status;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;
    unsigned int        bytes_transferred;

    io_request.user_data = 0;
    if (file_handle == -1)
        goto _ret;

    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
        goto _ret;

/* file system has problem with NASYSACC_FS_SUPER_USER permissions.
 * permissions must be given exactly same as the file handle during open.
 */
/*    error_status = NAFSopen_file_size (file_handle, NASYSACC_FS_SUPER_USER, file_size, &io_request); */
    error_status = NAFSopen_file_size (file_handle, permissions, file_size, &io_request);
    if (error_status != NAFS_SUCCESS)
        goto _ret;

    if (wait_io_status(&io_request, &bytes_transferred, &error_status) == NAFS_SUCCESS)
        rc = 0;
        
_ret:
    cleanup_io_request(&io_request);
    return rc;
}

/**
 * This function is used for debugging. It prints the file system API error
 * and its operation.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   fs_errcode      File System API error code
 * @param   op              Operation enum
 *
**/
static void print_fs_error (int fs_errcode, int op)
{
#ifdef _DEBUG_PRINTF
    char *errstring[] = {
        "_FS_OP_INTERFACE",
        "_FS_OP_MKDIR",
        "_FS_OP_OPEN",
        "_FS_OP_CLOSE",
        "_FS_OP_WRITE",
        "_FS_OP_READ",
        "_FS_OP_UNLINK",
        "_FS_OP_RMDIR",
        "_FS_OP_RENAME"
    };

    if (fs_errcode != NAFS_SUCCESS)
    {
        printf("fs_error: %d from %s operation\n", fs_errcode, errstring[op]);
    }
    
#else

    NA_UNUSED_PARAM(fs_errcode);
    NA_UNUSED_PARAM(op);
    
#endif
    
}

#ifdef _DEBUG_PRINTF
/**
 * This function is used for debugging. It prints the open modes.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   mode            modes 
 *
**/
static void print_mode(int mode)
{
    printf("MODE: 0x%X = ", mode);
    
    if ( mode == O_RDONLY) printf(" O_RDONLY,");
    if ( mode & O_WRONLY) printf(" O_WRONLY,");
    if ( mode & O_RDWR) printf(" O_RDWR,");
    if ( mode & O_APPEND) printf(" O_APPEND,");
    if ( mode & O_TRUNC) printf(" O_TRUNC,");
    if ( mode & O_CREAT) printf(" O_CREAT,");
    if ( mode & O_EXCL) printf(" O_EXCL,");
    printf("\n");
}
#endif

/**
 * This routine checks the configuration data used for native file system initialization.
 *
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 *                        
 * @return  TRUE         The configuration data is ok
 * @return  FALSE        The configuration data is bad
 *
 **/
static BOOLEAN validate_volumes_config(fs_config_t * fs_table, int fs_count)
{
    BOOLEAN rc = TRUE;
    
    unsigned int group_id;
    int i, j;

    for (i = 0; i < fs_count; i++)
    {
        if (fs_table[i].media_cb != NULL || fs_table[i].media_thread != NULL ||
            fs_table[i].media_queue != NULL || fs_table[i].buffer_pool != NULL ||
            fs_table[i].media_stack_addr != NULL || fs_table[i].io_intf != NULL ||
            fs_table[i].volume_cb != NULL || fs_table[i].volume != NULL || fs_table[i].media_queue_addr != NULL)
        {
            NA_DBPF ("wrong file system configuration data\n");
            rc = FALSE;
            goto _ret;
        }

        if (fs_table[i].volume_type != FLASH && fs_table[i].volume_type != RAM
        	&& fs_table[i].volume_type != NAND)
        {
            NA_DBPF ("wrong file system volume_type\n");
            rc = FALSE;
            goto _ret;
        }
/*
        if (fs_table[i].volume_type == FLASH && (!fs_table[i].flash_start_sector || !fs_table[i].flash_end_sector
                                                || !fs_table[i].flash_option || !fs_table[i].flash_compacting_threshold ||
                                                fs_table[i].flash_end_sector <= fs_table[i].flash_start_sector))
        {
            NA_DBPF ("wrong flash parameters\n");
            rc = FALSE;
            goto validateConfiguration_ret;
        }
*/

        for (group_id = NAFS_FS_GROUP1, j = 0; group_id <= NAFS_FS_GROUP8; j++, group_id = NAFS_FS_GROUP1 << j)
        {
            if ((fs_table[i].user_permissions & (NASYSACC_FS_GROUP1_READ << j)) != 0 &&
                (fs_table[i].user_permissions & (NASYSACC_FS_GROUP1_WRITE << j)) != 0)
            {
                if (fs_table[i].group_id == group_id)
                {
                    break;
                }
            }
        }

        if (fs_table[i].group_id != group_id)
        {
            NA_DBPF ("wrong user permission or group id\n");
            rc = FALSE;
            goto _ret;
        }

    }

_ret:

    return rc;
}


/**
 * This function allocates all neccessary memory for setting up the given
 * volume.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   fsPtr           Pointer to volume configuration
 * @param   stack_size      Stack size for file system API to handle this volume
 * @param   queue_size      Queue size for file system API to handle this volume
 *
 * @return  0         Successfully allocated all memory
 * @return  -1        Out of memory
**/
static int allocate_memory(fs_config_t * fsPtr, int stack_size, int queue_size)
{
    int rc = -1;
    
    fsPtr->media_cb = (NAFS_MEDIA_CB *) malloc (sizeof (NAFS_MEDIA_CB));
    fsPtr->media_thread = (TX_THREAD *) malloc (sizeof (TX_THREAD));
    fsPtr->media_queue = (TX_QUEUE *) malloc (sizeof (TX_QUEUE));
    fsPtr->buffer_pool = (NAFS_BUFFER_POOL *) malloc (sizeof (NAFS_BUFFER_POOL));
    fsPtr->media_stack_addr = (void *) malloc (stack_size);
    fsPtr->media_queue_addr = (void *) malloc (queue_size);
    fsPtr->io_intf = (NAFS_IO_INTF *) malloc (sizeof (NAFS_IO_INTF));
    fsPtr->volume_cb = (NAFS_VOLUME_CB *) malloc (sizeof (NAFS_VOLUME_CB));
    if (fsPtr->volume_type != FLASH)
    {
        fsPtr->volume = (char *) malloc (fsPtr->volume_size);
        if (!fsPtr->volume) goto _ret;
    }
                
    if (!fsPtr->volume_cb || !fsPtr->io_intf || !fsPtr->media_queue_addr || 
        !fsPtr->media_stack_addr || !fsPtr->buffer_pool || !fsPtr->media_queue ||
        !fsPtr->media_thread || !fsPtr->media_cb)
    {
        NA_DBPF ("malloc failed\n");
        goto _ret;
    }
    memset (fsPtr->media_cb, 0, sizeof (NAFS_MEDIA_CB));
    memset (fsPtr->media_thread, 0, sizeof (TX_THREAD));
    memset (fsPtr->media_queue, 0, sizeof (TX_QUEUE));
    memset (fsPtr->buffer_pool, 0, sizeof (NAFS_BUFFER_POOL));
    memset (fsPtr->media_stack_addr, 0, stack_size);
    memset (fsPtr->media_queue_addr, 0, queue_size);
    memset (fsPtr->io_intf, 0, sizeof (NAFS_IO_INTF));
    memset (fsPtr->volume_cb, 0, sizeof (NAFS_VOLUME_CB));

    if (fsPtr->volume_type != FLASH)
    {
        memset (fsPtr->volume, 0, fsPtr->volume_size);
    }
    rc = 0;
    
_ret:                
    return rc;
}                


/**
 * Gets available sectors. 
 * This function returns the start and end sectors that is free to use.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   start_sector    Pointer to memory where start sector value will be written.
 * @param   end_sector      Pointer to memory where end sector value will be written.
 *
 * @return  0         Successfully obtain start and end sector 
 * @return  -1        Unable to get start and end sector
**/
int get_flash_size(int * start_sector, int * end_sector)
{
    extern unsigned int _NAFlashSizeUsedTop, _NAFlashSizeUsedEnd, _NABspFileSystemSize, _NAFlashSizeUnusedMiddle;
    int         rc = -1;
    int         i;
    int             flash0_start_sector = -1, flash0_end_sector = -1;
    int             flash_sectors;
    unsigned long   sector_offset_array[MAX_SECTORS], sector_size_array[MAX_SECTORS];
    unsigned long   total_size, linker_flash_size, actual_flash_size;
/*    int             flash_fs_start_offset, flash_fs_end_offset; */
    unsigned long   app_size;
    
#ifdef _DEBUG_PRINTF    
    unsigned long   app_start, app_end;

    get_application_addr(&app_start, &app_end);
#endif    
    
    /* get flash memory sector count */
    flash_sectors = NAFlashSectors();
    if (flash_sectors < 0)
    {
        NA_DBPF("NAFlashSector() error\n");
        goto _ret;
    }

    /* get flash memory sector sizes */
    rc = NAFlashSectorSizes(sector_size_array);
    if (rc)
    {
        NA_DBPF("NAFlashSectorSizes() error\n");
        goto _ret;
    }
    /* get flash memory sector offsets */
    rc = NAFlashSectorOffsets(sector_offset_array); /* retrieve sector offsets data */
    if (rc)
    {
        NA_DBPF("NAFlashSectorOffsets() error\n");
        goto _ret;
    }

    linker_flash_size = (unsigned long)&_NABspFlashSize;
    actual_flash_size = NAFlashSize();
    if (!actual_flash_size)
    {
        NA_DBPF("NAFlashSectorSize() error\n");
        goto _ret;
    }

    /* get flash file system start offset */

    total_size = 0;
    if (linker_flash_size != actual_flash_size)
    {
        /* recompute reserved flash area (including unused area if any) prior to file system area */
        unsigned long flash_size_unused_middle = actual_flash_size - ( (unsigned long)&_NAFlashSizeUsedTop + (unsigned long)&_NABspFileSystemSize + (unsigned long)&_NAFlashSizeUsedEnd );
        app_size = (unsigned long)&_NAFlashSizeUsedTop + flash_size_unused_middle;
    }
    else
    {
        
        app_size = (unsigned long)&_NAFlashSizeUsedTop + (unsigned long)&_NAFlashSizeUnusedMiddle;
    }
    
    for (i=0; i<flash_sectors; i++)
    {
        if (total_size >= app_size) break;
        total_size += sector_size_array[i];
    }
    
/*    flash_fs_start_offset = sector_offset_array[i]; */
    flash0_start_sector = i;
    
#ifdef _DEBUG_PRINTF    
    printf("get_flash_size: start[%d] offset 0x%X\n", i, (unsigned)sector_offset_array[i]);
#endif    
    
    /* get flash file system end offset */
    total_size = 0;
    app_size = (unsigned long)&_NAFlashSizeUsedEnd;

    for (i=flash_sectors-1; i>0; i--)
    {
        if (total_size >= app_size) break;
        total_size += sector_size_array[i];
    }
/*    flash_fs_end_offset = sector_offset_array[i]; */
    flash0_end_sector = i;
    
#ifdef _DEBUG_PRINTF    
    printf("get_flash_size: ends[%d] offset 0x%X\n", i, (unsigned) sector_offset_array[i]);
#endif
    
    /* if last sector used as NVRAM, make sure _NAFlashSizeUsedEnd is enough for the NVRAM storage */
    if ( BSP_NVRAM_DRIVER == BSP_NVRAM_LAST_FLASH_SECTOR )
    {
        if (app_size < START_OF_FREE_NVRAM) 
        {
#ifdef _DEBUG_PRINTF    
            printf("get_flash_size: Area reseved at the end of flash 0x%X\n", (unsigned)app_size);
            printf("get_flash_size: START_OF_FREE_NVRAM at flash 0x%X\n", (unsigned)START_OF_FREE_NVRAM);
#endif            
            printf("get_flash_size: Need more memory defined in _NAFlashSizeUsedEnd in linker file\n");
            goto _ret;
         }
    }
    
    

    /* check for equal size sectors */
    for (i = flash0_start_sector; i < flash0_end_sector; i++)
    {
        if ( sector_size_array[i] != sector_size_array[i+1])
        {
            NA_DBPF("Flash file system sectors not of equal size\n");
            goto _ret;
        }
    }

    NABspFileSystemSize = sector_offset_array[flash0_end_sector] - 
                          sector_offset_array[flash0_start_sector] +
                          sector_size_array[flash0_end_sector];
    *start_sector = flash0_start_sector;
    *end_sector = flash0_end_sector;
    
#ifdef _DEBUG_PRINTF    
    printf("get_flash_size: file system flash starts %d ends %d\n", flash0_start_sector, flash0_end_sector);
#endif    
    rc = 0;
        
_ret:
    return rc;
}        

void prependRoot(unsigned char *rooted, unsigned char *rootless, unsigned int len)
{
	unsigned int ndx;
	*rooted++ = 0x2f;	/* '/' */
	/* MAXPATH */
	for (ndx = 0; ndx < min(len, NAFS_VOLUME_NAME_SIZE+1); ndx++)
		*rooted++ = *rootless++;
    *rooted = '\0';

}

/**
 * Initializes and sets up the native file system.
 * It sets up all volumes specified in fs_conf table.
 *
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 *                        
 * @return  0  Native file system is successfully set up.
 * @return  -1  Native file system is unsuccessfully set up.
 *
 **/
int init_volume_table(fs_config_t * fs_table, fs_param_t * fs_param, int fs_count)
{
    int         rc = -1;

    int         fs_status;
    int         i;
    unsigned    flash_volume_avail = (unsigned)-1; 
    
    if (native_fs_initialized)
    {
        rc = -2;
        NA_DBPF ("filesystem already initialized!\n");
        goto _ret;
    }
    
    
    native_fs_initialized = 1;
    
    if (fs_table == NULL || fs_param == NULL || !validate_volumes_config(fs_table, fs_count))
        goto _ret;


    fs_status = NAFSinit();
    if (fs_status != NAFS_SUCCESS)
    {
        NA_DBPF ("NAFSinit error %d\n", rc);
        goto _ret;
    }
    
    NA_DBPF ("File System initialization start!\n\n");
    
    for (i = 0; i < fs_count; i++)
    {
#if	BSP_YAFFS_DRIVER_ENABLE
        if (fs_table[i].volume_type == NAND)
		{
			/* nothing really required here. These parameters are set
			 * up in yaffs_StartUp() */
		}
		else
#endif
        if (fs_table[i].volume_type == FLASH)
        {
            if (flash_volume_avail == 0)
            {   /* we already max the size of the flash */ 
                goto _ret;
            }
            /* no start sector and end sector are given, we must try to 
             * fully use all available space in the flash.
             * 
             */
            if (fs_table[i].flash_start_sector == 0 && fs_table[i].flash_end_sector == 0)
            {
                if (flash_volume_avail == 1) 
                {   /* we did have FLASH volume already.
                     * we can't initialize another FLASH volume for all space in the flash.
                     */
                    goto _ret;
                }
                /* Get the available flash space that is not used.
                 * If we fully use the available flash, make sure no other flash volume is
                 * initialized.
                 */
                if (get_flash_size(&fs_table[i].flash_start_sector, &fs_table[i].flash_end_sector) == -1)
                    goto _ret;
                flash_volume_avail = 0; 
            }
            else
            {   /* mark this so that we know we already have a FLASH volume */
                flash_volume_avail = 1; 
            }

            /* check for the minimum 2 sector requirement */
            if ( (fs_table[i].flash_end_sector - fs_table[i].flash_start_sector + 1) < fs_param[i].min_flash_sectors)
            {
                NA_DBPF("Flash file system requires a minimum of 2 sectors\n");
                goto _ret;
            }

        }    


        if (allocate_memory(&fs_table[i], fs_param[i].media_stack_size, fs_param[i].media_queue_size) != 0)
        {
            goto _ret;
        }
#if	BSP_YAFFS_DRIVER_ENABLE
		/* yaffs will just follow the main path here. Using the same media_cb
		 * and io_request_cb to send to the thread allows for using the 
		 * NAFS<io functions>. This requires filling the fs_table with
		 * the correct (same) values from nandDev */
#endif
        fs_status = NAFSinstall_media_cb (fs_table[i].media_cb,
					   	                  fs_table[i].media_type, 
                                          fs_param[i].block_size,
							              fs_table[i].media_thread,
                                          fs_table[i].media_stack_addr, 
                                          fs_param[i].media_stack_size, 
                                          fs_table[i].media_queue, 
                                          fs_table[i].media_queue_addr, 
                                          fs_param[i].media_queue_size, 
                                          fs_table[i].buffer_pool, 
                                          fs_param[i].number_buffers_pool);
        if (fs_status != NAFS_SUCCESS)
        {
            NA_DBPF ("NAFSinit_media_cb error: %d\n", fs_status);
            goto _ret;
        }
#if	BSP_YAFFS_DRIVER_ENABLE
        if (fs_table[i].volume_type == NAND)
		{
			/* the IO functions are hidden inside the thread, so we 
			 * won't need to do the init_io_intf for yaffs. */
            fs_status = NAFSinit_io_intf (fs_table[i].io_intf,
						fs_table[i].volume_name, fs_table[i].media_type,
                        NULL, NULL, NULL, NULL, NULL, NULL);
			if (fs_status != NAFS_SUCCESS)
			{
				NA_DBPF ("NAFSinit_io_intf error: %d\n", fs_status);
				goto _ret;
			}

        	fs_status = (yaffs_StartUp() == 0) ? NAFS_SUCCESS : -1;
			if (fs_status != NAFS_SUCCESS)
			{
				NA_DBPF ("yaffs_StartUp error: %d\n", fs_status);
				goto _ret;
			}
		}
		else
#endif
        if (fs_table[i].volume_type == FLASH)
            fs_status = NAFSinit_io_intf (fs_table[i].io_intf, fs_table[i].volume_name, fs_table[i].media_type, 
                                            FSScreate, FSSremove, FSSwrite_block, FSSread_block, 
                                            FSSdelete_block, FSSget_block_count);
        else
            fs_status = NAFSinit_io_intf (fs_table[i].io_intf, fs_table[i].volume_name, fs_table[i].media_type, 
                                            RSScreate, RSSremove, RSSwrite_block, RSSread_block, 
                                            RSSdelete_block, RSSget_block_count);
        if (fs_status != NAFS_SUCCESS)
        {
            NA_DBPF ("NAFSinit_io_intf error: %d\n", fs_status);
            goto _ret;
        }

#if	BSP_YAFFS_DRIVER_ENABLE
        if (fs_table[i].volume_type == NAND)
		{
			/* yaffs sets up these parameters in yaffs_StartUp() */
				/* IS THIS A PROBLEM? volume_name checks fail in
				 * install_services for volume_cb and io_intf. */
            fs_status = NAFSinit_volume_cb (fs_table[i].volume_cb,
				fs_table[i].volume_name, fs_table[i].media_type,
                fs_param[i].inode_block_max, fs_param[i].open_dirs_max,
                fs_param[i].open_files_max, fs_param[i].volume_option,
                0,
                fs_table[i].flash_start_sector, fs_table[i].flash_end_sector, 
                fs_table[i].flash_option,
			   	fs_table[i].flash_compacting_threshold, 
                0, 0, 0, 0);
		}
		else
#endif
        if (fs_table[i].volume_type == FLASH)
            fs_status = NAFSinit_volume_cb (fs_table[i].volume_cb, fs_table[i].volume_name, fs_table[i].media_type,
                                         fs_param[i].inode_block_max, fs_param[i].open_dirs_max,
                                         fs_param[i].open_files_max, fs_param[i].volume_option,
                                         0,
                                         fs_table[i].flash_start_sector, fs_table[i].flash_end_sector, 
                                         fs_table[i].flash_option, fs_table[i].flash_compacting_threshold, 
                                         0, 0, 0, 0);
        else
            fs_status = NAFSinit_volume_cb (fs_table[i].volume_cb, fs_table[i].volume_name, fs_table[i].media_type,
                                         fs_param[i].inode_block_max, fs_param[i].open_dirs_max,
                                         fs_param[i].open_files_max, 0,
                                         0,
                                         fs_table[i].volume_size, (unsigned int) fs_table[i].volume, 
                                         0, 0, 0, 0, 0, 0);

        if (fs_status != NAFS_SUCCESS)
        {
            NA_DBPF ("NAFSinit_volume_cb error: %d\n", fs_status);
            goto _ret;
        }
        fs_status = NAFSinstall_services (fs_table[i].media_cb, fs_table[i].io_intf, fs_table[i].volume_cb);
        if (fs_status != NAFS_SUCCESS)
        {
            NA_DBPF ("NAFSinstall_services error: %d\n", fs_status);
            goto _ret;
        }

#if	BSP_YAFFS_DRIVER_ENABLE
		/* finally, mount the volume, done by NAFScreate_volume */
        if (fs_table[i].volume_type == NAND)
		{
			unsigned char yaffs_rootedName[32];

			prependRoot(yaffs_rootedName, (unsigned char *)fs_table[i].volume_name,
							strlen(fs_table[i].volume_name));
			/* yaffs sets up these parameters in yaffs_StartUp() */
			fs_status = yaffs_mount((char *)yaffs_rootedName);
		}
		else
#endif
        fs_status = NAFScreate_volume (fs_table[i].volume_name);
        if ((fs_status == NAFS_BLOCK_NOT_MAPPED || fs_status == NAFS_FLASH_VOLUME_SECTORS_MISMATCH || fs_status == NAFS_BLOCK_COUNT_MISMATCH) &&
             fs_table[i].volume_type == FLASH)
        {
            NA_DBPF("init_volume_table: fs_status %d Erase the flash\n", fs_status);
            fs_status = NAFlashErase (fs_table[i].flash_start_sector, fs_table[i].flash_end_sector);
            if (fs_status != NAFS_SUCCESS)
            {
                NA_DBPF ("flash1 NAFlashErase error: %d\n", fs_status);
                goto _ret;
            }

            fs_status = NAFSinit_volume_cb (fs_table[i].volume_cb, fs_table[i].volume_name, fs_table[i].media_type,
                                             fs_param[i].inode_block_max, fs_param[i].open_dirs_max,
                                             fs_param[i].open_files_max, fs_param[i].volume_option & (~NAFS_INIT_STATUS),
                                             0,
                                             fs_table[i].flash_start_sector, fs_table[i].flash_end_sector, 
                                             fs_table[i].flash_option, fs_table[i].flash_compacting_threshold, 
                                             0, 0, 0, 0);
            if (fs_status != NAFS_SUCCESS)
            {
                NA_DBPF ("NAFSinit_volume_cb error: %d\n", fs_status);
                goto _ret;
            }

            fs_status = NAFScreate_volume (fs_table[i].volume_name);
        }
        
        if (fs_status != NAFS_SUCCESS)
        {
            NA_DBPF ("NAFScreate_volume error: %d\n", fs_status);
            goto _ret;
        }
    }

    gNativeVolumeList = (fs_config_t *)fs_table;
    gNativeVolumeCount = fs_count;
    rc = 0;
        
_ret:
    if (rc)
    {
        native_fs_initialized = 0;
        for (i = 0; i < fs_count; i++)
        {
            if (fs_table[i].media_cb) free (fs_table[i].media_cb);
            if (fs_table[i].media_thread) free (fs_table[i].media_thread);
            if (fs_table[i].media_queue) free (fs_table[i].media_queue);
            if (fs_table[i].buffer_pool) free (fs_table[i].buffer_pool);
            if (fs_table[i].media_stack_addr) free (fs_table[i].media_stack_addr);
            if (fs_table[i].media_queue_addr) free (fs_table[i].media_queue_addr);
            if (fs_table[i].io_intf) free (fs_table[i].io_intf);
            if (fs_table[i].volume_cb) free (fs_table[i].volume_cb);
            if (fs_table[i].volume) free (fs_table[i].volume);
        }
    }
    return rc;
}

/**
 * Initializes native file system interface.
 * This function intializes the given volume table. 
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   max_files       Maximum file handler is used
 * @param   data            Pointer to the volume table configuration
 *
 * @return  0         Successfully initialized native file system interface.
 * @return  -1        Unsuccessfully initialized native file system interface.
**/
int nat_fs_init(int max_files, void * data)
{
    int         rc = 0;
    NafsClib_t  * volume_config = (NafsClib_t *)data;

    NA_UNUSED_PARAM(max_files);

    if (volume_config != NULL)
    {   /* intialize the volume table */
        rc = init_volume_table(volume_config->fs_volumes, 
                                volume_config->fs_volume_param, 
                                volume_config->fs_number_volumes);
    }
        
    return rc;
}

/**
 * Determine whether this entry is native file system and return native file
 * system interface type value.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   entry_name      Pointer to the entry name which will be determined
 *                          whether it's native volume.
 *
 * @return  NA_CLIB_FS_TYPE_NONE    It's not native file system entry.
 * @return  NA_CLIB_FS_TYPE_NAT     It's native file system entry.
**/
int nat_fs_interface(char * entry_name)
{
    char    * thePath = NULL;
    char    volume_name[NAFS_DIRECTORY_PATH_SIZE];
    
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    unsigned int        bytes_transferred;    
    unsigned int    dir_info_list_items;
    int             rc = NA_CLIB_FS_TYPE_NONE;
    int             error_status; 
    
    if (entry_name != NULL)
    {    
        strcpy(volume_name, entry_name);
        
        if (strstr(volume_name, "\\") != NULL)
            thePath = strstr(volume_name, "\\");
        else
            thePath = strstr(volume_name, "/");
         
        /* got a slash. We just want the volume name.
         * Try to access the volume. If we can access the volume, 
         * then we know this is NAT file system. 
         */
        if (thePath != NULL)
        {
            *(thePath+1) = '\0';
        }
        
        error_status = setup_io_request (&io_request, &io_event);
        if (error_status != NAFS_SUCCESS)
            goto _ret;

        error_status = NAFSdir_entry_count(volume_name, NASYSACC_FS_SUPER_USER, 
                            &dir_info_list_items, &io_request);

        if (error_status != NAFS_SUCCESS) goto _ret;
        
        if (wait_io_status(&io_request, &bytes_transferred, &error_status) == NAFS_SUCCESS)
        {
            rc = NA_CLIB_FS_TYPE_NAT;
        }

        _ret:
        cleanup_io_request(&io_request);                
    }

    print_fs_error(error_status, _FS_OP_INTERFACE);
    return rc;
}
/**
 * Native file system interface for mkdir CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   dirname     Pointer to new directory to be created.
 *
 * @return  0           The given directory was successfully created.
 * @return  -1          The given directory was unsuccessfully created.
**/
int nat_fs_mkdir(const char * dirname)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int                 error_code, error_no = EFAULT;
    unsigned int        bytes_transferred;
    
    char                * cur_directory = NULL;
    char                * new_directory = NULL;
    int                 cur_dirlen, new_dirlen;
    unsigned int        group_id, permissions;

    io_request.user_data = 0;
    if (dirname == NULL)
    {
        goto _ret;
    }

    cur_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    new_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    
#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) cur_directory);
    printf("malloc ---> 0x%X\n", (unsigned) new_directory);
#endif    
    if (cur_directory == NULL || new_directory == NULL)
    {
        error_no = ENOMEM;
        goto _ret;
    }
    
    /* parse the directory path. Separate the name of the 
     * new directory and the path of the directory where
     * the new diretory is going to be created.
     */
    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    new_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    
    /* fail if we can't get the sub directory or if only volume name is given.
     * (we can't create volume)
     */
    if (parse_file_path((char *)dirname, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK ||
        new_dirlen == 0)
    {
        goto _ret;
    }
        
    error_code = setup_io_request (&io_request, &io_event);
    if (error_code != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }
    
    /* let's get the group id */
    if (get_group_id(&io_request, cur_directory, &group_id, &permissions) != 0)
    {
        goto _ret;
    }
        
#ifdef _DEBUG_PRINTF
    printf("mkdir: group id = %d path = %s new_dir = %s\n", group_id, cur_directory, new_directory);
#endif        
    error_code = NAFScreate_dir (cur_directory, new_directory, permissions, group_id, &io_request);

    if (error_code == NAFS_SUCCESS)
    {        
        wait_io_status(&io_request, &bytes_transferred, &error_code);
    }
           
    switch (error_code)
    {
        case NAFS_DIR_ENTRY_NOT_FOUND:
            error_no = ENOENT;
            break;
            
        case NAFS_DUPLICATE_DIR_ENTRY:
            error_no = EEXIST;
            break;
            
        case NAFS_NO_FREE_INODES:
        case NAFS_NO_FREE_BLOCKS:
            error_no = ENOSPC;
            break;
            
        case NAFS_NO_READ_PERMISSION:
        case NAFS_NO_WRITE_PERMISSION:
            error_no = EACCES;
            break;
            
        case NAFS_SHUTDOWN_MODE:
            error_no = ENXIO;
            break;
    }


    if (error_code == NAFS_SUCCESS)
    {
        error_no = 0;
        rc = 0;
    }
    
_ret:
    cleanup_io_request(&io_request);                
    
    if (cur_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_directory);
    #endif    
        free (cur_directory);
    }
    if (new_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) new_directory);
    #endif    
        free (new_directory);
    }
    print_fs_error(error_code, _FS_OP_MKDIR);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for open & fopen CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   filename    Pointer to file name to be opened.
 * @param   mode        Mode for opening the file
 * @param   perm        Permission (currently not used)
 * @param   handler     Pointer to memory which native file handle will be written
 *
 * @return  0           The given file was successfully opened.
 * @return  -1          The given file was unsuccessfully opened.
**/
int nat_fs_open(const char * filename, int mode, int perm, int * handler)
{
#define RDWR_MASK   0xfff
#define _FILE_OPEN    0x01
#define _FILE_CREATE  (0x01 << 1)

    int                 rc = -1;
    int                 error_no = EIO;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int             error_status, status;
    unsigned int    file_handle = 0, bytes_transferred;
    char            * cur_directory = NULL;
    char            * cur_filename = NULL;
    int             cur_dirlen, filelen;
    unsigned int    group_id, permissions;
    int             o_mode = mode & RDWR_MASK;
    int             open_flag;
    char            buffer; 

    NaClibHandleFs_t * filePtr = NULL;
    
    NA_UNUSED_PARAM(perm);
    
    io_request.user_data = 0;    
    if (filename == NULL)
    {   
        error_no = ENOENT;
        goto _ret;
    }

#ifdef _DEBUG_PRINTF
    print_mode(mode);
#endif

    /* check all valid modes */
    if ( mode & ~(O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_TRUNC|O_CREAT|O_EXCL|O_BINARY|O_TEXT)) 
    {
    #ifdef _DEBUG_PRINTF
        printf("open: mode = 0x%X we only allow 0x%X\n", mode, (O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_TRUNC|O_CREAT|O_EXCL) ); 
    #endif
        error_no = EINVAL;
        goto _ret;
    }

    cur_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    cur_filename = (char *)malloc(NAFS_FILE_NAME_SIZE);
    
#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) cur_directory);
    printf("malloc ---> 0x%X\n", (unsigned) cur_filename);
#endif    
    if (cur_directory == NULL || cur_filename == NULL)
    {
        error_no = ENOMEM;
        goto _ret;
    }
    
    filePtr = allocate_file_buffer();
    
    if (filePtr == NULL)
    {
        error_no = EMFILE; /* max file open */
        goto _ret;
    }

    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    filelen = NAFS_FILE_NAME_SIZE;
    
    if (parse_file_path((char *)filename, cur_directory, &cur_dirlen, cur_filename, &filelen) != FILE_OK ||
        filelen == 0)
    {
        error_no = ENOENT;
        goto _ret;
    }

    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }
    
    /* get the group id & permission  */
    if (get_group_id(&io_request, cur_directory, &group_id, &permissions) != 0)
    {
        error_no = ENOENT;
        goto _ret;
    }
     
    /* let set to create a file if create a non-existing file modes are set */    
    if ((mode & O_CREAT) == O_CREAT && (mode & O_EXCL) == O_EXCL)
    {
        open_flag = _FILE_CREATE;
    }
    else
    {   /* otherwise, let try open file first */
        open_flag = _FILE_OPEN;
    }

	io_request.arg2 = mode;	/* for yaffs. */

    /* wait until we can open the file if no error */
    do {
    
        if (open_flag == _FILE_OPEN)
        {
                    
      	    error_status = NAFSopen_file(cur_directory, cur_filename, &file_handle, 
                                    permissions, &io_request);
                                    
        #ifdef _DEBUG_PRINTF
            printf("open: dir = %s file = %s ---->\n", cur_directory, filename);
        #endif    
        }
        else
        {                       
            
            error_status = NAFScreate_file (cur_directory, cur_filename, permissions, 
                                    group_id, &io_request);
                                    
        #ifdef _DEBUG_PRINTF
            printf("create: dir = %s file = %s ---->\n", cur_directory, cur_filename);
        #endif    
        }
        
		status = 0;	/* for yaffs */
        if (error_status == NAFS_SUCCESS)
        {        
       	    status = wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
        }
        status = error_status;
                                        
        switch (error_status)
        {
            case NAFS_DIR_ENTRY_NOT_FOUND:
                error_no = ENOENT;
                break;
                
            case NAFS_FILE_IS_OPEN:
                error_no = EAGAIN;
                break;
                
            case NAFS_FILE_TABLE_FULL:
            case NAFS_DIR_TABLE_FULL:
                error_no = ENFILE;
                break;
                
            case NAFS_DUPLICATE_DIR_ENTRY:
                error_no = EEXIST;
                break;
                
            case NAFS_NO_FREE_INODES:
            case NAFS_NO_FREE_BLOCKS:
                error_no = ENOMEM;
                break;
                
            case NAFS_NO_READ_PERMISSION:
            case NAFS_NO_WRITE_PERMISSION:
                error_no = EACCES;
                break;
                
            case NAFS_SHUTDOWN_MODE:
                error_no = ENXIO;
                break;
        }
        

        if (error_status == NAFS_FILE_IS_OPEN)
        {
            /* the file is opened by other module so exit */
            break;
        }
        else
        {
            if (error_status == NAFS_DIR_ENTRY_NOT_FOUND) 
            {
                /* file doesn't exist. Let's check whether the mode.
                 * Error if mode is read only or read/write file 
                 * must exist before open.
                 * Error if got error when creating a file. 
                 */
                if ((o_mode == O_RDONLY) || (o_mode == O_RDWR) || (open_flag == _FILE_CREATE))
                {
                #ifdef _DEBUG_PRINTF
                    printf("open: file not exist (mode = 0x%x)\n", o_mode);
                #endif
                    goto _ret;
                }
            }
   	        else if (status != NAFS_SUCCESS ) 
            {
                /* error so let's return from this function */
                goto _ret;
            }
            else if (open_flag != _FILE_OPEN)
            {
                /* We must be creating a file, let open a file
                 * if no error. 
                 */
                error_status = NAFS_FILE_IS_OPEN;            
            }
                
            /* open change to create  OR
             * create change to open
             */
            open_flag = (open_flag == _FILE_OPEN) ? _FILE_CREATE : _FILE_OPEN;
        }
        
      } while (error_status == NAFS_FILE_IS_OPEN || error_status == NAFS_DIR_ENTRY_NOT_FOUND);
/*    } while (error_status == NAFS_DIR_ENTRY_NOT_FOUND); */


    if (status == NAFS_SUCCESS)
    {
    #ifdef _DEBUG_PRINTF
        printf("open: handle = 0x%X file handle = 0x%X ---->\n", (unsigned)filePtr, file_handle);
    #endif
        status = get_file_size (file_handle, (unsigned int *)&filelen, permissions);
        if (status != 0)
        {
            error_no = EIO;
            rc = -1;  
            goto _ret;
        }

        error_no = 0;  /* no error */
        rc = 0;
    
        GET_SEMAPHORE
        filePtr->file_handler = file_handle;
        filePtr->permissions = permissions;
        if (mode & O_APPEND)
            filePtr->offset = filelen;
        else
            filePtr->offset = 0;    
        filePtr->status = _FILE_OPENED;
        /* we only trunc the file once, so let's mask it off.
         * we don't need to do it anymore */
        filePtr->mode = (mode & ~O_TRUNC); 
        *handler = (int) filePtr;
    
        PUT_SEMAPHORE
                
        /* if O_TRUNC mode, we must erase/truncates existing contents */
        if (mode & O_TRUNC)
        {
            /* See whether we have any data in the file */
            status = get_file_size (file_handle, (unsigned int *)&filelen, permissions);
        #ifdef _DEBUG_PRINTF
            printf("open: mode = O_TRUNC get_file_size returns %d with file size = %d\n",
                                status, filelen);
        #endif                        
            if (status != 0)
            {
                error_no = EIO;
                rc = -1;  
                goto _ret;
            }
            else if(filelen > 0)
            {
                /* truncate existing data */
                error_status = NAFSwrite_file (file_handle, permissions, 1, 0, 
                                        (char *) &buffer, &io_request, NAFS_ERASE_DATA);
                                        
                if (error_status == NAFS_SUCCESS) 
                {
                    wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
                }
                
                if (error_status != NAFS_SUCCESS)
                {  
                    error_no = EIO;
                    rc = -1;  
                    goto _ret;
                }
            #ifdef _DEBUG_PRINTF
                /* just checking */
                status = get_file_size (file_handle, (unsigned int *)&filelen, permissions);
                printf("open: mode = O_TRUNC get_file_size returns %d with file size = %d\n",
                                    status, filelen);
            #endif
                GET_SEMAPHORE
                filePtr->offset = 0;
                PUT_SEMAPHORE
            }
        }
    }  /* status = NAFS_SUCCESS */
    

_ret:
    cleanup_io_request(&io_request);                
    
    if (cur_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_directory);
    #endif    
        free(cur_directory);
    }
    if (cur_filename != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_filename);
    #endif    
        free(cur_filename);
    }
    
    if (rc == -1 && filePtr != NULL)
    {
        if (nat_fs_close((int)filePtr) != 0)
            free_file_buffer(filePtr); /* free it anyway. */
            
    }
    
    setErrno(error_no);
    print_fs_error(error_status, _FS_OP_OPEN);
    
    return rc;
}

/**
 * Native file system interface for close & fclose CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param   handler     Pointer file handle returned by nat_fs_open
 *
 * @return  0           The file was successfully closed.
 * @return  -1          The file was unsuccessfully closed.
**/
int nat_fs_close(int handle)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int             error_status, error_no = EBADF;
    unsigned int    bytes_transferred;
    unsigned int    file_handler, permissions;
    NaClibHandleFs_t * filePtr;

    NA_ASSERT (handle != 0);
    
    io_request.user_data = 0;    
    filePtr = (NaClibHandleFs_t *)get_file_buffer(handle);
    
    if (handle == 0 || filePtr == NULL) goto _ret;

    GET_SEMAPHORE    
    file_handler = filePtr->file_handler;
    permissions = filePtr->permissions;    
    PUT_SEMAPHORE    

    if (file_handler != 0)
    {
        error_status = setup_io_request (&io_request, &io_event);
        if (error_status != NAFS_SUCCESS)
        {
            error_no = EIO;
            goto _ret;
        }
        
    #ifdef _DEBUG_PRINTF
        printf("close: handle = 0x%X <-----\n\n", file_handler);
    #endif
        error_status = NAFSclose_file (file_handler, permissions, &io_request);
    
        if (error_status == NAFS_SUCCESS)
        {
            wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
        }
    
        if (error_status == NAFS_SHUTDOWN_MODE)
        {
            error_no = ENXIO;
        }
        else if (error_status == NAFS_SUCCESS)
        {
            free_file_buffer(filePtr); /* free it anyway. */
            error_no = 0;
            rc = 0;
        }
        
    }
    
_ret:
    cleanup_io_request(&io_request);
    print_fs_error(error_status, _FS_OP_CLOSE);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for write & fwrite CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param handler     Pointer to file handle returned by nat_fs_open
 * @param buf         Pointer to data to be written to the file.
 * @param size        Number of bytes in buf to be written to the file.
 *
 * @return  >= 0      Number of bytes is written.
 * @return  -1        Fails to write data to the file.
**/
int nat_fs_write(int handle, void * buf, int size)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int                 error_status, error_no = EBADF;
    unsigned int        file_size, file_offset;          
    unsigned int        bytes_transferred, mode, permissions;
    unsigned int        file_handle;
    unsigned char       write_option;
    NaClibHandleFs_t    * filePtr;
    char zerobuf = '\0';

    NA_ASSERT (handle != 0);
    
    io_request.user_data = 0;
    filePtr = (NaClibHandleFs_t *)get_file_buffer(handle);
    
    if (handle == 0 || filePtr == NULL)
    {
#ifdef _DEBUG_PRINTF
        printf("nat_fs_write: handle or file pointer is NULL\n");
#endif
	goto _ret;
    }
    
    GET_SEMAPHORE    
    file_handle         = filePtr->file_handler;
    file_offset         = filePtr->offset;
    file_size           = filePtr->file_size;
    mode                = filePtr->mode;
    permissions         = filePtr->permissions;
    PUT_SEMAPHORE    

    if (mode == O_RDONLY)
    {   /* only read access */
        error_no = EACCES;
        goto _ret;
    }
    
    write_option = NAFS_RANDOM_WRITE;

    if (get_file_size(file_handle, &file_size, permissions) == -1)
    {
#ifdef _DEBUG_PRINTF
        printf("nat_fs_write: get_file_size() failed\n");
#endif
        goto _ret;
    }

    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }
    
    /* if offset is > than the actual file size, we need to write
     * 0 at the EOF until it reaches the offset
     */
    if (file_offset > file_size)
    {
        /* write 0 from the EOF until the offset */
    #ifdef _DEBUG_PRINTF
        printf("nat_fs_write: writes 0 to handle 0x%X size = %d\n", file_handle, (file_offset - file_size));
    #endif
        while (file_offset > file_size)
        {
            error_status = NAFSwrite_file (file_handle, permissions, sizeof zerobuf, 
                                            file_size, (char *) &zerobuf, &io_request, write_option);
            if (error_status == NAFS_SUCCESS) 
            {
                wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
            }
            else
            {
                switch (error_status)
                {
                    case NAFS_NO_FREE_BLOCKS:
                        error_no = EFBIG;
                        break;
                
                    case NAFS_NO_WRITE_PERMISSION:
                        error_no = EACCES;
                        break;
                
                    case NAFS_SHUTDOWN_MODE:
                        error_no = ENXIO;
                        break;
                    case NAFS_IO_REQUEST_CB_BUSY:
                        error_no = EAGAIN;
                        break;
                    default:		
                        printf("nat_fs_write: error_status is %d\n", error_status);
                        break;
			
                }
                goto _ret;
            }
            
            file_size += bytes_transferred;
            filePtr->file_size = file_size;
        }
    #ifdef _DEBUG_PRINTF
        printf("nat_fs_write: handle 0x%X file size = %d\n", file_handle, file_size);
    #endif
    }

        
    /* write actual data to the file */
    error_status = NAFSwrite_file (file_handle, permissions, size, file_offset, 
                                        (char *) buf, &io_request, write_option);
                                        
    if (error_status == NAFS_SUCCESS) 
    {
        wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
    }
        
#ifdef _DEBUG_PRINTF
    printf("nat_fs_write: writes data to handle 0x%X offset = 0x%X size = %d\n", file_handle, file_offset, bytes_transferred);
#endif
        
    if (error_status == NAFS_SUCCESS)
    {
        error_no = 0;
        GET_SEMAPHORE    
        if (write_option != NAFS_ERASE_DATA)
        {
            filePtr->offset += bytes_transferred;
            if ((file_offset + bytes_transferred) > file_size)
                filePtr->file_size += bytes_transferred;
        }
        PUT_SEMAPHORE    
        rc = bytes_transferred;
    }
    else
    {
        switch (error_status)
        {
            case NAFS_NO_FREE_BLOCKS:
                error_no = ENOSPC;
                break;
        
            case NAFS_NO_WRITE_PERMISSION:
                error_no = EACCES;
                break;
        
            case NAFS_SHUTDOWN_MODE:
                error_no = ENXIO;
                break;
            case NAFS_IO_REQUEST_CB_BUSY:
                error_no = EAGAIN;
                break;
            default:
                printf("nat_fs_write: error_status is %d\n", error_status);
                break;
        }
            
    }

_ret:
    cleanup_io_request(&io_request);
    print_fs_error(error_status, _FS_OP_WRITE);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for read & fread CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param handler     Pointer to file handle returned by nat_fs_open
 * @param buf         Pointer to memory where file data to be written.
 * @param size        Number of bytes to be read.
 *
 * @return  >= 0      Actually number of bytes is read.
 * @return  -1        Fails to read the file.
**/
int nat_fs_read(int handle, void * buf, int size) 
{
    int                     rc = -1;
    unsigned int            file_handle, user_permissions;
    NAFS_IO_REQUEST_CB      io_request;
    TX_EVENT_FLAGS_GROUP    io_event;    
    int                     error_status, status, error_no = EBADF, mode;
    unsigned int            file_offset, bytes_transferred;
    NaClibHandleFs_t        * filePtr;

    NA_ASSERT (handle != 0);

    io_request.user_data = 0;
    filePtr = (NaClibHandleFs_t *)get_file_buffer(handle);
    if (handle == 0 || filePtr == NULL) goto _ret;

    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }

    GET_SEMAPHORE    
    file_handle = filePtr->file_handler;
    file_offset = filePtr->offset;
    user_permissions = filePtr->permissions;
    status = filePtr->status;
    mode = filePtr->mode;
    PUT_SEMAPHORE    

    if ((mode & O_RDWR) == 0 && mode != O_RDONLY && (mode & O_WRONLY) == O_WRONLY)
    {   /* we don't have read access */
        error_no = EACCES;
        goto _ret;
    }
    
    error_status = NAFSread_file (file_handle, user_permissions, size, file_offset, (char *) buf, &io_request);
    
    if (error_status == NAFS_SUCCESS) 
    {
        status = wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
    }
    
    if (error_status != NAFS_SUCCESS && error_status != NAFS_END_OF_FILE) 
    {
    
        switch (error_status)
        {
            case NAFS_INVALID_FILE_OFFSET:
                error_no = EBADF; /* EOVERFLOW; */
                break;
                
            case NAFS_INVALID_BYTES_TO_READ:
                error_no = 0;
                rc = 0;
                break;
        
            case NAFS_NO_READ_PERMISSION:
                error_no = EACCES;
                break;
        
            case NAFS_SHUTDOWN_MODE:
                error_no = ENXIO;
                break;

            case NAFS_IO_REQUEST_CB_BUSY:
                error_no = EAGAIN;
                break;

            default:
                printf("nat_fs_read: error_status is %d\n", error_status);
                break;
        }
        goto _ret;    
    }

    GET_SEMAPHORE    
    filePtr->offset += bytes_transferred;
    if (filePtr->offset > filePtr->file_size)
    {
        /* this happens if the file was opened once for write */
        /* and opened once (or more times) for read           */
        filePtr->file_size = filePtr->offset;
    }

    PUT_SEMAPHORE    
    
#ifdef _DEBUG_PRINTF
    printf("nat_fs_read: bytes read = %d\n", bytes_transferred);
#endif
    rc = bytes_transferred;

    if (error_status == NAFS_END_OF_FILE)
    {
        error_no = EOF;
        goto _ret;
    }

    error_no = 0;

_ret:
    cleanup_io_request(&io_request);
    print_fs_error(error_status, _FS_OP_READ);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for unlink which CLIB calls unlink for remove.
 * If unlink is used to remove a directory, it deletes a directory if no error.
 * If unlink is used to remove a file, it deletes the file if no one accesses 
 * the file. Otherwise, it deletes the file when it becomes inactive.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param filepath    Full file path or directory path to be deleted.
 *
 * @return  0         The file or directory was successfully unlinked.
 * @return  -1        The file or directory was unsuccessfully unlinked.
**/
int nat_fs_unlink (const char * filepath)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB  * io_request = NULL;
    TX_EVENT_FLAGS_GROUP io_event;    
    int             error_status, status, error_no = EFAULT;
    unsigned int    bytes_transferred, group_id, permissions;
    char            * cur_directory = NULL;
    char            * filename = NULL;
    int             cur_dirlen, filelen;

    if (filepath == NULL)
        goto _ret;

    cur_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    filename = (char *)malloc(NAFS_FILE_NAME_SIZE);
    if (cur_directory == NULL || filename == NULL)
    {
        error_no = ENOMEM;
        goto _ret;
    }
    
#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) cur_directory);
    printf("malloc ---> 0x%X\n", (unsigned) filename);
#endif
    
    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    filelen = NAFS_FILE_NAME_SIZE;

	if (parse_file_path((char *)filepath, cur_directory, &cur_dirlen, filename, &filelen) != FILE_OK ||
        filelen == 0)
    {
		goto _ret;
    }
    
    io_request = (NAFS_IO_REQUEST_CB *)malloc(sizeof(NAFS_IO_REQUEST_CB));
    if (io_request == NULL)
    {
        error_no = ENOMEM;
        goto _ret;
    }
        
    #ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) io_request);
    #endif    

    error_status = setup_io_request (io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
		goto _ret;
    }

    /* let get the group id  & permissions */
    if (get_group_id(io_request, cur_directory, &group_id, &permissions) != 0)
        goto _ret;
            
#ifdef _DEBUG_PRINTF
    printf("unlink: directory = %s   remove = %s\n", cur_directory, filename);
#endif    
    error_status = NAFSdelete_file (cur_directory, filename, permissions, io_request);
    if (error_status == NAFS_SUCCESS)
    {
        status = wait_io_status(io_request, (unsigned int *)&bytes_transferred, &error_status);
        
        /* error from the file system. See whether file system tells us
         * that the file is being used. If it's not, try delete a directory.
         */
        if (status != NAFS_SUCCESS && error_status != NAFS_FILE_IS_OPEN)
        {
            /* Maybe it's a directory */
            error_status = NAFSdelete_dir (cur_directory, filename, permissions, io_request);
            if (error_status == NAFS_SUCCESS)
            {
                status = wait_io_status(io_request, (unsigned int *)&bytes_transferred, &error_status);
            }
        }
    }

    switch (error_status)
    {
        case NAFS_INVALID_DIR_PATH_LENGTH:
        case NAFS_INVALID_FILE_NAME_LENGTH:
            error_no = ENAMETOOLONG;
            break;
    
        case NAFS_SHUTDOWN_MODE:
            error_no = ENXIO;
            break;
            
        case NAFS_IO_REQUEST_CB_BUSY:
            error_no = EAGAIN;
            break;
            
        case NAFS_DIR_ENTRY_NOT_FOUND:
            error_no = ENOENT;
            break;
            
        case NAFS_NO_WRITE_PERMISSION:
        case NAFS_NO_READ_PERMISSION:
            error_no = EACCES;
            break;
            
        case NAFS_DIR_NOT_EMPTY:
            error_no = ENOTEMPTY /* EEXIST */;
            break;
            
        case NAFS_FILE_IS_OPEN:
            /* let the callback to delete the file when becomes available */
        #ifdef _DEBUG_PRINTF
            printf("unlink: %s %s is being opened\n", cur_directory, filename);
        #endif
            error_no = 0;
            rc = 0;
            break;
        case NAFS_SUCCESS:
        #ifdef _DEBUG_PRINTF
            printf("unlink: unlink %s\\%s successfully\n", cur_directory, filename);
        #endif
            error_no = 0;
            rc = 0;
            break;
    }
    
_ret:    
    
    if (cur_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_directory);
    #endif    
        free(cur_directory);
    }
    if (filename != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) filename);
    #endif    
        free(filename);
    }
    if (io_request != NULL)
    {
        cleanup_io_request(io_request);
    
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) io_request);
    #endif    
        free (io_request);
    }
    
    print_fs_error(error_status, _FS_OP_UNLINK);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for rmdir CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param dirname   Pointer to the full directory name to be removed.
 *
 * @return  0         The directory was successfully removed.
 * @return  -1        The directory was unsuccessfully removed.
**/
int nat_fs_rmdir (const char * dirname)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int             error_status, error_no = EFAULT;
    unsigned int    bytes_transferred, group_id, permissions;
    char            * cur_directory = NULL;
    char            * new_directory = NULL;
    int             cur_dirlen, new_dirlen;

    io_request.user_data = 0;
    if (dirname == NULL)
    {
        error_no = ENOENT;
        goto _ret;
    }


    cur_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    new_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    
    if (cur_directory == NULL || new_directory == NULL)
    {
        error_no = ENOMEM;
        goto _ret;
    }    

#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) cur_directory);
    printf("malloc ---> 0x%X\n", (unsigned) new_directory);
#endif

    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }

    cur_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    new_dirlen = NAFS_DIRECTORY_PATH_SIZE;
    
	if (parse_file_path((char *)dirname, cur_directory, &cur_dirlen, new_directory, &new_dirlen) != FILE_OK ||
        new_dirlen == 0)
    {
		goto _ret;
    }
        
    /* let get the group id  & permissions */
    if (get_group_id(&io_request, cur_directory, &group_id, &permissions) != 0)
        goto _ret;
    
#ifdef _DEBUG_PRINTF
    printf("rmdir: %s  sub-directory = %s\n", cur_directory, new_directory);
#endif    
    error_status = NAFSdelete_dir (cur_directory, new_directory, permissions, &io_request);
    if (error_status == NAFS_SUCCESS)
    {
        wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
    }
    
    switch (error_status)
    {
        case NAFS_DIR_ENTRY_NOT_FOUND:
            break;
        case NAFS_NO_WRITE_PERMISSION:
        case NAFS_NO_READ_PERMISSION:
            error_no = EACCES;
            break;
        case NAFS_DIR_NOT_EMPTY:
            error_no = ENOTEMPTY /* EEXIST */;
            break;
            
        case NAFS_INVALID_DIR_PATH_LENGTH:
        case NAFS_INVALID_FILE_NAME_LENGTH:
            error_no = ENAMETOOLONG;
            break;
    
        case NAFS_SHUTDOWN_MODE:
            error_no = ENXIO;
            break;
            
        case NAFS_IO_REQUEST_CB_BUSY:
            error_no = EAGAIN;
            break;
        case NAFS_SUCCESS:
            error_no = 0;
            rc = 0;
            break;
    }
    
_ret:
    cleanup_io_request(&io_request);

    if (cur_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) cur_directory);
    #endif
        free(cur_directory);
    }
    if (new_directory != NULL) 
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) new_directory);
    #endif
        free(new_directory);
    }
    print_fs_error(error_status, _FS_OP_RMDIR);
    setErrno(error_no);
    return rc;
}

/**
 * Native file system interface for rename CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param oldfilename   Pointer to the full file name to be renamed.
 * @param newfilename   Pointer to the new file name.
 *
 * @return  0         The file was successfully renamed.
 * @return  -1        The file was unsuccessfully renamed.
**/
int nat_fs_rename (const char * oldfilename, const char * newfilename)
{
    int                 rc = -1;
    NAFS_IO_REQUEST_CB  io_request;
    TX_EVENT_FLAGS_GROUP io_event;    
    int                 error_status, error_no = ENOENT;
    unsigned int        bytes_transferred;
    char            * src_directory = NULL, * src_filename = NULL;
    char            * dest_directory = NULL, * dest_filename = NULL;
    int             dirlen, filelen;
    unsigned int    group_id, permissions;

    io_request.user_data = 0;

    /* make sure it renames under the same volume/drive */
    src_directory = strstr(oldfilename, "\\");
    if (src_directory == NULL) 
        src_directory = strstr(oldfilename, "/");
    if (src_directory == NULL)
    {
        goto _ret;   /* not slash - it means no volume is specified */
    }

    dirlen = (int)(src_directory - oldfilename);
    if ((memcmp(oldfilename, newfilename, dirlen) != 0) ||
        ((*(newfilename+dirlen) != '/') &&  (*(newfilename+dirlen) != '\\')) )
    {
        /* volume name is not the same */    
        error_no = EXDEV;
        src_directory = NULL;
        goto _ret;   
    }

    src_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    src_filename = (char *)malloc(NAFS_FILE_NAME_SIZE);
    
    dest_directory = (char *)malloc(NAFS_DIRECTORY_PATH_SIZE);
    dest_filename = (char *)malloc(NAFS_FILE_NAME_SIZE);

#ifdef _MEM_PRINTF
    printf("malloc ---> 0x%X\n", (unsigned) src_directory);
    printf("malloc ---> 0x%X\n", (unsigned) src_filename);
    
    printf("malloc ---> 0x%X\n", (unsigned) dest_directory);
    printf("malloc ---> 0x%X\n", (unsigned) dest_filename);
#endif    

    if (src_directory == NULL || src_filename == NULL || 
        dest_directory == NULL || dest_filename == NULL)
    {
        error_status = ENOMEM;
        goto _ret;
    }

    /* parse the old filename path */
    dirlen = NAFS_DIRECTORY_PATH_SIZE;
    filelen = NAFS_FILE_NAME_SIZE;
	if (parse_file_path((char *)oldfilename, src_directory, &dirlen, src_filename, &filelen) != FILE_OK ||
        filelen == 0)
    {
		goto _ret;
    }    
    /* parse the new filename path */
    dirlen = NAFS_DIRECTORY_PATH_SIZE;
    filelen = NAFS_FILE_NAME_SIZE;
	if (parse_file_path((char *)newfilename, dest_directory, &dirlen, dest_filename, &filelen) != FILE_OK ||
        filelen == 0)
    {
        goto _ret;
    }
    
    /* Current file system only can rename a file in the same directory */
    if (strcmp(dest_directory, src_directory) != 0)
    {
        /* volume name is not the same */    
        error_no = EXDEV;
        goto _ret;   
    }
    
    error_status = setup_io_request (&io_request, &io_event);
    if (error_status != NAFS_SUCCESS)
    {
        error_no = EIO;
        goto _ret;
    }
    
    /* let get the group id  & permissions */
    if (get_group_id(&io_request, src_directory, &group_id, &permissions) != 0)
        goto _ret;
        
    /* rename filename */
    error_status = NAFSrename_file (src_directory, src_filename, 
                                    (char *) dest_filename, permissions, &io_request);
    if (error_status == NAFS_SUCCESS)
    {
        wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
    }
    
    if (error_status == NAFS_SUCCESS)
    {
        error_no = 0;
        rc = 0;
    }
    else                       
    {
        /* fail to rename the filename.
         * Let's try rename the directory name.
         */
        error_status = NAFSrename_dir (src_directory, src_filename, (char *) dest_filename, 
                                        permissions, &io_request);
        if (error_status == NAFS_SUCCESS)
        {
            wait_io_status(&io_request, (unsigned int *)&bytes_transferred, &error_status);
        }
        
        switch (error_status)
        {
            case NAFS_DIR_ENTRY_NOT_FOUND:
                break;
                
            case NAFS_NO_WRITE_PERMISSION:
            case NAFS_NO_READ_PERMISSION:
                error_no = EACCES;
                break;
        
            case NAFS_INVALID_DIR_PATH_LENGTH:
            case NAFS_INVALID_FILE_NAME_LENGTH:
                error_no = ENAMETOOLONG;
                break;

            case NAFS_SHUTDOWN_MODE:
                error_no = ENXIO;
                break;
        
            case NAFS_IO_REQUEST_CB_BUSY:
                error_no = EAGAIN;
                break;
            case NAFS_DUPLICATE_DIR_ENTRY: 
            case NAFS_DIR_HAS_OPEN_FILES:
            case NAFS_FILE_IS_OPEN:
                error_no = EEXIST;
                break;
            case NAFS_SUCCESS:
                error_no = 0;
                rc = 0;
                break;
        }
    }
    
_ret:
    cleanup_io_request(&io_request);

    if (src_directory != NULL)
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) src_directory);
    #endif
        free (src_directory);
    } 
    if (src_filename != NULL)
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) src_filename);
    #endif
        free (src_filename);
    }
    if (dest_directory != NULL)
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) dest_directory);
    #endif
        free (dest_directory);
    } 
    if (dest_filename != NULL)
    {
    #ifdef _MEM_PRINTF
        printf("free <--- 0x%X\n", (unsigned) dest_filename);
    #endif
        free (dest_filename);
    }
    print_fs_error(error_status, _FS_OP_RENAME);
    setErrno(error_no);
    
    return rc;
}

/**
 * Native file system interface for lseek & fseek CLIB.
 *
 * @external
 * @category clib_FileSystemInterface:native
 * @since 6.3 
 *
 * @param handler     Pointer to file handle returned by nat_fs_open
 * @param ptr         Number of bytes to offset the file handle from 
 *                    the specified starting position
 * @param dir         The starting position
 *                    <list type="unordered">
 *                      <item> SEEK_SET - offsets from start of file. </item>
 *                      <item> SEEK_CUR - offsets from current position </item>
 *                      <item> SEEK_END - offsets from end-of-file </item>
 *                    </list>
 *
 * @return  >=0       File current position
 * @return  -1        Unable to set the offset.
**/
int nat_fs_seek (int handle, int ptr, int dir)
{
    int rc = -1, get_file_size_ok = 0;
    int error_no = EBADF;
    
    unsigned int file_handle;
    unsigned int file_size;
    unsigned int cursor;
    unsigned int permissions;
    NaClibHandleFs_t * filePtr;

    NA_ASSERT (handle != 0);
    
    filePtr = (NaClibHandleFs_t *)get_file_buffer(handle);
    if (handle == 0 || filePtr == NULL) goto _ret;

    
    /* only allow these SEEK position values */
    if ((dir != SEEK_SET) && (dir != SEEK_CUR) && (dir != SEEK_END))
    {
        error_no = EINVAL;
        goto _ret;
    }

    GET_SEMAPHORE    
    file_handle = filePtr->file_handler;
    cursor      = filePtr->offset;
    permissions = filePtr->permissions;
    PUT_SEMAPHORE    

    if (get_file_size(file_handle, &file_size, permissions) == 0)
    {
        get_file_size_ok = 1;
        if (!filePtr->file_size && (filePtr->file_size != file_size))
        {
            /* internal file size info is 0 so initialize with file size retrieved from file system */
            filePtr->file_size = file_size;
        }
    }
    else
    {
        error_no = EBADF;
        goto _ret;
    }

    switch (dir)
    {
        case SEEK_SET:
            /* not allow SEEK before the beginning of a file */
            if (ptr < 0)
            {
                error_no = EINVAL; /* EOVERFLOW;*/
                goto _ret;
            }
            rc = ptr;
            break;
        case SEEK_CUR:
            if ((ptr + (int)cursor) < 0)
            {
                error_no = EINVAL; /* EOVERFLOW; */
                goto _ret;
            }
            rc = cursor + ptr;
            break;
        case SEEK_END:
    
            if (get_file_size_ok)
            {
                /* not allow SEEK before the beginning of a file */
                if ((ptr + (int)file_size) < 0)
                {
                    error_no = EINVAL; /* EOVERFLOW; */
                    goto _ret;
                }
    
                rc = ptr + file_size;
            }
            else
            {
                error_no = EBADF;
                goto _ret;
            }
            break;
    }

    GET_SEMAPHORE    
    filePtr->offset = rc;  /* current offset */
    filePtr->status &= ~_FILE_END; /* clear the EOF */
    PUT_SEMAPHORE    
    
_ret:
    setErrno(error_no);
    return rc;
}

