/*
 *            Copyright (c) 1998-2003 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 304                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSalenetsilicon.com
 *
 *************************************************************************/
 /* 
  * Edit History
  *
  * 11/17/04        JZW       In function  FileSystem_CreateDirectory in call to
  *                           GET_PERMISSIONS() use full privs when creating test 
  *                           file system. This allows the creation of sub directories
  *                           in directory structures with multiple group ids
  */

#include <stdio.h>
#include <string.h>
#include <tx_api.h>
#include <Npttypes.h>
#include <sysAccess.h>

#include "fs.h"
#include "rss.h"
#include "fs_api.h"
#include "fileinit.h"
#include "bsp_api.h"
 
#if (DEBUG_PRINTF > 0)
#define _DEBUG_PRINTF(a, b)		printf("ERROR: %s fails %d\n", (a), (b));
#else
#define _DEBUG_PRINTF(a, b)
#endif

#define GET_PERMISSIONS(a)      (((a)<< READ_BIT_OFFSET) | ((a) << WRITE_BIT_OFFSET))

typedef struct name_list_t {
	char				 *name;
	struct name_list_t *next;
}NAME_LIST_T;


static TX_MUTEX 	*gNameMutex = NULL;		
static NAME_LIST_T  *gVolumeList = NULL;	
static NAME_LIST_T  *gUsernameList = NULL;
static int          gMediaType = 0; 


static int Add_NameList(NAME_LIST_T **name_list, char *name)
{
	int				rc = NAFS_MALLOC_FAILED;
	NAME_LIST_T		*pList;
	char			*pName;

	if (gNameMutex == NULL)
	{	/* create mutex for locking (signle access) */
		gNameMutex = (TX_MUTEX *)malloc(sizeof(TX_MUTEX));
		if (gNameMutex  == NULL) goto _add_nameList;
        /* gnu require this */
        memset(gNameMutex, 0, sizeof(TX_MUTEX));
		if (tx_mutex_create(gNameMutex, "gNameMutex", TX_INHERIT) != TX_SUCCESS)
			goto _add_nameList;
	}

	if (tx_mutex_get(gNameMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
	{
		/* allocates NAME_LIST_T memory */
		pList = (NAME_LIST_T *)malloc(sizeof(NAME_LIST_T) + strlen(name) + 2);
		if (pList != NULL)
		{
			pName = (char *)pList;
			pName += sizeof(NAME_LIST_T);

			strcpy(pName, name);
			pList->name = pName;

			/* put the newly allocated NAME_LIST_T in the HEAD */
			if (*name_list != NULL)
			{
				pList->next = *name_list;
			}
			else
			{
				pList->next = NULL;
			}			
			*name_list = pList;
			rc = NAFS_SUCCESS;
		}

		tx_mutex_put(gNameMutex);
	}
	else
		rc = NAFS_SEMAPHORE_CREATE_FAILED;
_add_nameList:
	if (rc != NAFS_SUCCESS && gNameMutex != NULL)
	{
		free(gNameMutex);
		gNameMutex = NULL;
	}
		
	return rc;
}

static unsigned Get_NumberOfNameList(NAME_LIST_T *name_list)
{
	NAME_LIST_T		*pList;
	unsigned 		listCount = 0;

	/* Gets number of items in the given name list */
	if (tx_mutex_get(gNameMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
	{
		for (pList = name_list; pList != NULL; pList = pList->next)
		{
			listCount++;
		}

		tx_mutex_put(gNameMutex);
	}
	return listCount;

}


static unsigned Get_NameList(NAME_LIST_T *name_list, char *volume_table, unsigned table_size)
{
	unsigned		i = 0;
	NAME_LIST_T		*pList;
	char			**table;

	/* copy specified number of name list into the given table */
	if (tx_mutex_get(gNameMutex, TX_WAIT_FOREVER) == TX_SUCCESS)
	{
		table = (char **)volume_table;

		for (i=0, pList = name_list; pList != NULL && i < table_size; i++, pList = pList->next)
		{
			table[i] = pList->name;
		}
		tx_mutex_put(gNameMutex);
	}
	return i;
}

unsigned FileSystem_GetNumberOfDirUser(void)
{
	return Get_NumberOfNameList(gUsernameList);
}


unsigned FileSystem_GetDirUserList(char *name_table, unsigned table_size)
{
	NA_ASSERT((int) name_table);
	NA_ASSERT((int)(table_size != 0));

	return Get_NameList(gUsernameList, name_table, table_size);
}


unsigned FileSystem_GetNumberOfVolumes(void)
{
	return Get_NumberOfNameList(gVolumeList);
}


unsigned FileSystem_GetVolumeList(char *volume_table, unsigned table_size)
{
	NA_ASSERT((int) volume_table);
	NA_ASSERT((int)(table_size != 0));

	return Get_NameList(gVolumeList, volume_table, table_size);
}

int FileSystem_CreateRamVolume (char *volume_name, char *volume_area, unsigned volume_size)
{
#define STACK_SIZE                   8 * 1024
#define QUEUE_SIZE                   512
#define BUFFERS_IN_POOL              20

#define RAM_INODE_BLOCK_LIMIT       100
#define RAM_OPEN_DIRS               10
#define RAM_OPEN_FILES_PER_DIR      10
#define MEDIA_BLOCK_SIZE            NAFS_BLOCK_SIZE_1K

    NAFS_MEDIA_CB    * media_cb;
    TX_THREAD        * media_thread;
    TX_QUEUE         * media_queue;
    NAFS_BUFFER_POOL * buffer_pool;
    char             * media_stack_addr;
    char             * media_queue_addr;
    NAFS_IO_INTF     * ram_intf;
    NAFS_VOLUME_CB   * ram_cb;

    int rc;
	char name[NAFS_MAX_OPEN_DIRS];
    
    if (NABspFilesystemForCLib == 0)
    {
    NA_ASSERT((int)volume_name);
    NA_ASSERT((int)volume_area);

    media_cb = (NAFS_MEDIA_CB *)malloc(sizeof(NAFS_MEDIA_CB));
    if (media_cb == NULL) goto _createRamVolume_ret;
    memset(media_cb, 0, sizeof(NAFS_MEDIA_CB));

    media_thread = (TX_THREAD *)malloc(sizeof(TX_THREAD));
    if (media_thread == NULL) goto _createRamVolume_ret;
    memset(media_thread, 0, sizeof(TX_THREAD));

    media_queue = (TX_QUEUE *)malloc(sizeof(TX_QUEUE));
    if (media_queue == NULL) goto _createRamVolume_ret;
    memset(media_queue, 0, sizeof(TX_QUEUE));

    media_thread = (TX_THREAD *)malloc(sizeof(TX_THREAD));
    if (media_thread == NULL) goto _createRamVolume_ret;
    memset(media_thread, 0, sizeof(TX_THREAD));

    buffer_pool = (NAFS_BUFFER_POOL *)malloc(sizeof(NAFS_BUFFER_POOL));
    if (buffer_pool == NULL) goto _createRamVolume_ret;
    memset(buffer_pool, 0, sizeof(NAFS_BUFFER_POOL));

    media_stack_addr = (char *)malloc(STACK_SIZE);
    if (media_stack_addr == NULL) goto _createRamVolume_ret;
    memset(media_stack_addr, 0, sizeof(STACK_SIZE));

    media_queue_addr = (char *)malloc(QUEUE_SIZE);
    if (media_queue_addr == NULL) goto _createRamVolume_ret;
    memset(media_queue_addr, 0, sizeof(QUEUE_SIZE));

    ram_intf = (NAFS_IO_INTF *)malloc(sizeof(NAFS_IO_INTF));
    if (ram_intf == NULL) goto _createRamVolume_ret;
    memset(ram_intf, 0, sizeof(NAFS_IO_INTF));

    ram_cb = (NAFS_VOLUME_CB *)malloc(sizeof(NAFS_VOLUME_CB));
    if (ram_cb == NULL) goto _createRamVolume_ret;
    memset(ram_cb, 0, sizeof(NAFS_VOLUME_CB));

	/* initialized the file system */
    if (gMediaType == 0)
    {
        rc = NAFSinit();
        if (rc)
        {
           _DEBUG_PRINTF("NAFSinit", rc);
           goto _createRamVolume_ret;
        }
    }
    
    /* initialize media control block for the file system */
    rc = NAFSinstall_media_cb(media_cb, gMediaType, MEDIA_BLOCK_SIZE, media_thread, 
                media_stack_addr, STACK_SIZE, media_queue, media_queue_addr, 
                QUEUE_SIZE, buffer_pool, BUFFERS_IN_POOL);
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinit_media_cb", rc);
       goto _createRamVolume_ret;
    }
    /* initialize io interface */
    rc = NAFSinit_io_intf(ram_intf, volume_name, gMediaType, RSScreate, RSSremove, 
                          RSSwrite_block, RSSread_block, RSSdelete_block, 
                          RSSget_block_count);
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinit_io_intf", rc);
       goto _createRamVolume_ret;
    }
   
	/* initialize volume control block */
    rc = NAFSinit_volume_cb(ram_cb, volume_name, gMediaType, RAM_INODE_BLOCK_LIMIT, 
                        RAM_OPEN_DIRS, RAM_OPEN_FILES_PER_DIR, 0, 0, volume_size, 
                        (unsigned int)volume_area, 0, 0, 0, 0, 0, 0);
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinit_volume_cb", rc);
       goto _createRamVolume_ret;
    }

	/* install RAM services */
    rc = NAFSinstall_services(media_cb, ram_intf, ram_cb);
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinstall_services", rc);
       goto _createRamVolume_ret;
    }
   
	/* Create volume */
    rc = NAFScreate_volume(volume_name);
    if (rc)
    {
       _DEBUG_PRINTF("NAFScreate_volume", rc);
       goto _createRamVolume_ret;
    }
    }
        
	/* Add the volume in the list */
	sprintf(name, "%s/\0", volume_name);
	rc = Add_NameList(&gVolumeList, name);
    if (rc)
    {
       _DEBUG_PRINTF(volume_name, rc);
    }

    gMediaType++;

_createRamVolume_ret:
    return rc;
}
int FileSystem_CreateDirectory(char *dir_path, unsigned int group_id, 
							char *username, char *password)
{

    int                 rc, error_code, len;
    int                  status;
    char                volume_name[NAFS_DIR_NAME_SIZE], *new_dir_path;
    unsigned int        permission;
    unsigned int        request_type;
    unsigned int        bytes_transferred;
    NAFS_IO_REQUEST_CB  io_request;
    
        
    NA_ASSERT((int)username);
    NA_ASSERT((int)password);
    NA_ASSERT((int)dir_path);
    NA_ASSERT((group_id >= NAFS_FS_GROUP1) && (group_id <= NAFS_FS_GROUP8));

	/* get the volume name from the dir path first */
    new_dir_path = strstr(dir_path, "/");
    if (new_dir_path == NULL)
        new_dir_path = strstr(dir_path, "\\");
    if (new_dir_path != NULL) new_dir_path++;

    len = (new_dir_path - dir_path);

    while (strstr(new_dir_path, "/") != 0 || strstr(new_dir_path, "\\") != 0)
    {
        if (strstr(new_dir_path, "/") != NULL)
            new_dir_path = strstr(new_dir_path, "/");
        else
            new_dir_path = strstr(new_dir_path, "\\");

        new_dir_path++;
        len = (new_dir_path - dir_path) - 1;
    }
        
    if (new_dir_path == NULL || strlen(new_dir_path) == 1)
    {
        rc = NAFS_INVALID_VOLUME_NAME;
        goto _createDirectory_ret;
    }        
    
    strncpy(volume_name, dir_path, (new_dir_path - dir_path));
    volume_name[len] = '\0';
    

    rc = NAFSinit_io_request_cb(&io_request, NULL, 0);
    
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinit_io_request_cb", rc);
       goto _createDirectory_ret;
    }

	/* since this is the application and not the running system, we can take liberties with the
	 * permission used. Used full privs to allow writing of subdirectories when creating the file
	 * system
	 */
    permission = GET_PERMISSIONS(0xFFFF);
        
    rc = NAFScreate_dir(volume_name, new_dir_path, permission, group_id, &io_request);
    if (rc)
    {
       _DEBUG_PRINTF("NAFScreate_dir", rc);
        goto _createDirectory_ret;
    }
   
    do {
        tx_thread_sleep(5);
        rc = NAFSio_request_cb_status(&io_request, &request_type, (unsigned int *)&status, &error_code, &bytes_transferred);
        if (rc != NAFS_SUCCESS) 
            break;
            
        rc = error_code;
    } while ((status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING));

    if (rc != NAFS_SUCCESS || status == NAFS_IO_REQUEST_TERMINATED || status == NAFS_IO_REQUEST_FREE) {
		_DEBUG_PRINTF("NAFSio_request_cb_status", rc);
        goto _createDirectory_ret;
	}

    status = NAsetSysAccess (NASYSACC_ADD, username, password, (group_id << READ_BIT_OFFSET) |
    				   (group_id << WRITE_BIT_OFFSET) | NASYSACC_LEVEL_RW, NULL);
    if (status != 0)
	{
        if (status == NASYSACC_DUPLICATE_USERNAME)
            rc = NAFS_SUCCESS;
        else
        {
            printf("NASetSysAccess Error: unsuccessfully set %s username %s password %d\n", 
                                   username, password);
	    	rc = NAFS_INVALID_USER_PERMISSIONS;
        }
	}
	else
		rc = Add_NameList(&gUsernameList, username);

_createDirectory_ret:
    return rc;
}

int FileSystem_CreateTestFile(char *dir_path, unsigned int group_id, 
                              char **test_file_name, unsigned long *file_size)
{
	/* these definitions are used to create a testFile */
	#define _NLINES         5  
	#define _FILE_DATA      "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890AB\
CDEFGHIJKLMNOPQRSTUVWXYZ1234567890\r\n"

    unsigned int request_type, status, permission;
    unsigned int file_offset, bytes_to_write;
    unsigned     file_handler, bytes_transferred;
    int          error_code;
    int          total_size;
    
    NAFS_IO_REQUEST_CB  io_request;
    char                *wbuffer;
    int                 rc;
    char 				*file_name = NULL;
    
    NA_ASSERT((int)file_size);	
    NA_ASSERT((int)dir_path);  

    *test_file_name = NULL;
    *file_size = 0;    
    
    memset(&io_request, 0, sizeof io_request);
    /* set up io request for file system */
    rc = NAFSinit_io_request_cb(&io_request, NULL, 0);
    
    if (rc)
    {
       _DEBUG_PRINTF("NAFSinit_io_request_cb", rc);
       goto _createTestFile_ret;
    }
    
    permission = NASYSACC_FS_SUPER_USER;

	/* create an unique file name */
	file_name  = (char *)malloc(20);
	if (file_name == NULL)
    {
       _DEBUG_PRINTF("malloc", NAFS_MALLOC_FAILED);
       goto _createTestFile_ret;
    }
	sprintf(file_name, "t%d.txt\0", (unsigned) file_name);	    
    /* create test file */    
    rc = NAFScreate_file(dir_path, file_name, permission, group_id, &io_request);
    if (rc)
    {
       _DEBUG_PRINTF("NAFScreate_file", rc);
       goto _createTestFile_ret;
    }
                                        
                                        
    do {
        tx_thread_sleep(5);
        rc = NAFSio_request_cb_status(&io_request, &request_type, &status, &error_code, &bytes_transferred);
        if (rc != NAFS_SUCCESS) 
            break;
            
        rc = error_code;
    } while ((status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING));

    if (rc != NAFS_SUCCESS || status == NAFS_IO_REQUEST_TERMINATED || status == NAFS_IO_REQUEST_FREE) {
        goto _createTestFile_ret;
	}

     
    /* open the test file */        
    rc = NAFSopen_file(dir_path, file_name, &file_handler, permission,
                         &io_request);
    if (rc)
    {
       _DEBUG_PRINTF("NAFSopen_file", rc);
       goto _createTestFile_ret;
    }
    
                                        
    do {
        tx_thread_sleep(5);
        rc = NAFSio_request_cb_status(&io_request, &request_type, &status, &error_code, &bytes_transferred);
        if (rc != NAFS_SUCCESS) 
            break;
            
        rc = error_code;
    } while ((status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING));

    if (rc != NAFS_SUCCESS || status == NAFS_IO_REQUEST_TERMINATED || status == NAFS_IO_REQUEST_FREE) {
        goto _createTestFile_close;
	}

    
	/* write data */
    wbuffer = _FILE_DATA;        
    file_offset = 0;
    total_size = strlen(_FILE_DATA) * _NLINES;
    while (file_offset < total_size)
    {
        bytes_to_write = strlen(_FILE_DATA);
        
        if ((total_size - file_offset) < bytes_to_write)
            bytes_to_write = total_size - file_offset;
            
        rc = NAFSwrite_file(file_handler, permission, bytes_to_write, 
                            file_offset, wbuffer, &io_request, NAFS_RANDOM_WRITE);
                            
        if (rc)
        {
       		_DEBUG_PRINTF("NAFSwrite_file", rc);
           	goto _createTestFile_close;
        }
        
                                        
        do {
            tx_thread_sleep(5);
            rc = NAFSio_request_cb_status(&io_request, &request_type, &status, &error_code, &bytes_transferred);
            if (rc != NAFS_SUCCESS) 
                break;
            
            rc = error_code;
        } while ((status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING));

        if (rc != NAFS_SUCCESS || status == NAFS_IO_REQUEST_TERMINATED || status == NAFS_IO_REQUEST_FREE) {
            goto _createTestFile_close;
	    }

        
        file_offset += bytes_to_write;
    }
    /* done writing the test file */  
    *file_size = file_offset;    

    /* since the test file is already created, must setup the return values */
    *test_file_name = file_name;
    
_createTestFile_close:
    rc = NAFSclose_file(file_handler, permission, &io_request);
                                        
    do {
        tx_thread_sleep(5);
        rc = NAFSio_request_cb_status(&io_request, &request_type, &status, &error_code, &bytes_transferred);
        if (rc != NAFS_SUCCESS) 
            break;
            
        rc = error_code;
    } while ((status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING));

_createTestFile_ret:
	if (rc != NAFS_SUCCESS && file_name != NULL)
	{
		free(file_name);
		file_name = NULL;
	}
    return rc;
}
