Mercurial > flash_v2
diff packages/io/fileio/current/tests/testfs.c @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/io/fileio/current/tests/testfs.c @@ -0,0 +1,1240 @@ +//========================================================================== +// +// testfs.c +// +// Test file system +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2000-05-25 +// Purpose: Test file system +// Description: This is a very simple implementation of a RAM file system. +// This implementation is not "industrial strength" or suitable +// for production use, it is too wasteful of both memory and time. +// Its primary purpose is to support testing of the fileio +// infrastructure and API. It can, however, serve as a model +// and source of code fragments for the implementation +// of further filesystems. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <pkgconf/io_fileio.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/kernel/kapi.h> +#include <cyg/infra/diag.h> + +//========================================================================== +// Configuration parameters + +#define TESTFS_NFILE 10 // Max number of files/directories +#define TESTFS_NBLOCK 20 // Number of data blocks available +#define TESTFS_BLOCKSIZE 128 // Bytes stored per block +#define TESTFS_FILEBLOCKS 8 // Max blocks per file +#define TESTFS_NAMESIZE 32 // Length of file names in bytes + + +// Maximum file size is blocksize*blocks +#define TESTFS_FILESIZE_MAX (TESTFS_BLOCKSIZE*TESTFS_FILEBLOCKS) + +//========================================================================== +// Data structures + +struct testfs_node; +typedef struct testfs_node testfs_node; + +struct testfs_block; +typedef struct testfs_block testfs_block; + + +struct testfs_node +{ + testfs_node *next; // next node in list + testfs_node *parent; // Back pointer to parent + int refcnt; // reference count + char name[TESTFS_NAMESIZE]; // file name + struct stat status; // status data + union + { + struct + { + testfs_block *data[TESTFS_FILEBLOCKS]; // array of blocks + } file; + + struct + { + testfs_node *nodes[TESTFS_FILEBLOCKS]; // array of nodes + } dir; + } u; +}; + +struct testfs_block +{ + union + { + testfs_block *next; // next block in free list + testfs_node *file; // back pointer to file + } u; + off_t pos; // position in file of first byte + size_t size; // number of bytes in buffer + char data[TESTFS_BLOCKSIZE]; // the data +}; + +//========================================================================== +// Local data + +// Array of nodes +static testfs_node node[TESTFS_NFILE]; + +// node free list. +static testfs_node *free_node = NULL; + +// Array of data blocks +static testfs_block block[TESTFS_NBLOCK]; + +// block free list. +static testfs_block *free_block = NULL; + +// Init flag +cyg_bool testfs_initialized = false; + +//========================================================================== +// Forward definitions + +// Filesystem operations +static int testfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ); +static int testfs_umount ( cyg_mtab_entry *mte ); +static int testfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte ); +static int testfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int testfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int testfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); +static int testfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2 ); +static int testfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type ); +static int testfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int testfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int testfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int testfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int testfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int testfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int testfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int testfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int testfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +//static int testfs_fo_select (struct CYG_FILE_TAG *fp, int which, CYG_ADDRWORD info); +static int testfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int testfs_fo_close (struct CYG_FILE_TAG *fp); +static int testfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int testfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); +static int testfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); + +// Directory operations +static int testfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int testfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); + +//========================================================================== +// Filesystem table entries + +FSTAB_ENTRY( testfs_fste, "testfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + testfs_mount, + testfs_umount, + testfs_open, + testfs_unlink, + testfs_mkdir, + testfs_rmdir, + testfs_rename, + testfs_link, + testfs_opendir, + testfs_chdir, + testfs_stat, + testfs_getinfo, + testfs_setinfo); + +MTAB_ENTRY( testfs_mte1, + "/", + "testfs", + "", + 0); + +#if 0 +MTAB_ENTRY( testfs_mte2, + "/ram", + "testfs", + "", + 0); +#endif + +static cyg_fileops testfs_fileops = +{ + testfs_fo_read, + testfs_fo_write, + testfs_fo_lseek, + testfs_fo_ioctl, + cyg_fileio_seltrue, + testfs_fo_fsync, + testfs_fo_close, + testfs_fo_fstat, + testfs_fo_getinfo, + testfs_fo_setinfo +}; + +static cyg_fileops testfs_dirops = +{ + testfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + testfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + testfs_fo_close, + (cyg_fileop_fstat *)cyg_fileio_enosys, + (cyg_fileop_getinfo *)cyg_fileio_enosys, + (cyg_fileop_setinfo *)cyg_fileio_enosys +}; + +//========================================================================== +// Support routines + +// ------------------------------------------------------------------------- +// Local strcmp() and strcpy() + +static int strcmp( const char *s1, const char *s2 ) +{ + while( *s1 == *s2 && *s1 != '\0' && *s2 != '\0' ) + s1++, s2++; + + return (*s2)-(*s1); +} + +static char *strcpy( char *s1, const char *s2 ) +{ + char *s = s1; + while( (*s1++ = *s2++) != 0); + return s; +} + +// ------------------------------------------------------------------------- +// Follow a path through the directory structure + +static int testfs_find( testfs_node *dir, // dir to start search in + const char *path, // path to follow + testfs_node **found, // return node found + testfs_node **parent, // return last dir searched + char *name, // name fragment buffer + cyg_bool *lastp) // last name in path ? +{ + testfs_node *nd = dir; + + *lastp = false; + *found = NULL; + + while( *path != '\0' ) + { + const char *p = path; + char *n = name; + testfs_node *nd1; + int i; + + // check nd is a directory + if( !S_ISDIR(nd->status.st_mode) ) return ENOTDIR; + + // Isolate the next element of the path name. + while( *p != '\0' && *p != '/' && (n-&name[0]) < TESTFS_NAMESIZE) + *n++ = *p++; + + if( (n-&name[0]) >= TESTFS_NAMESIZE ) + return ENAMETOOLONG; + + // Step path on past the separator + // If this is the last name element in the path, + // set *lastp to indicate this. + if( *(path=p) == '/' ) path++; + else *lastp = true; + + // teminate name + *n = '\0'; + + // name now contains the next path element, search the node + // in nd for it. + + *parent = nd; + nd1 = NULL; + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + { + testfs_node *n = nd->u.dir.nodes[i]; + if( n == NULL ) + continue; + + if( strcmp( name, n->name ) == 0 ) + { + nd1 = n; + break; + } + } + + if( nd1 == NULL ) return ENOENT; + + nd = nd1; + } + + // Return what we have found + *found = nd; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// Get current time since epoch + +static time_t testfs_time(void) +{ + // FIXME: !!!!Temporary!!!! + return cyg_current_time(); +} + +// ------------------------------------------------------------------------- + +static int testfs_delnode( testfs_node *nd ) +{ + + testfs_node *parent; + int i; + + // Non-unitary ref count means this node is either open + // or is a dir with entries in it. + if( nd->refcnt > 1 ) + return EBUSY; + + // Remove from parent's node list. + + parent = nd->parent; + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + if( parent->u.dir.nodes[i] == nd ) + { + parent->u.dir.nodes[i] = NULL; + break; + } + + parent->refcnt--; + + if( S_ISREG(nd->status.st_mode) ) + { + // for a file, return blocks to free list + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + { + testfs_block *b = nd->u.file.data[i]; + if( b != NULL ) + { + b->u.next = free_block; + b->pos = -1; + free_block = b; + } + } + } + + // and finally return nd to free node list + + nd->next = free_node; + nd->refcnt = -1; + free_node = nd; + + return ENOERR; +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- + +static int testfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ) +{ + testfs_node *root; + int i; + + if( !testfs_initialized ) + { + int i; + + for( i = 0; i < TESTFS_NFILE; i++ ) + { + node[i].next = free_node; + node[i].refcnt = -1; + free_node = &node[i]; + } + + for( i = 0; i < TESTFS_NBLOCK; i++ ) + { + block[i].u.next = free_block; + block[i].pos = -1; + free_block = &block[i]; + } + + testfs_initialized = true; + } + + // Allocate a node to be the root of this filesystem and + // initialize it. + + root = free_node; + + if( root == NULL ) return ENOSPC; + + free_node = root->next; + + root->next = root; // form circular list + root->parent = root; // I'm my own parent! + root->refcnt = 1; // don't want to ever lose root + strcpy( root->name, "root"); + root->status.st_mode = __stat_mode_DIR; + root->status.st_ino = root-&node[0]; + root->status.st_dev = 0; + root->status.st_nlink = 1; + root->status.st_uid = 0; + root->status.st_gid = 0; + root->status.st_size = 0; + root->status.st_atime = testfs_time(); + root->status.st_mtime = testfs_time(); + root->status.st_ctime = testfs_time(); + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + root->u.dir.nodes[i] = NULL; + + mte->root = (cyg_dir)root; + + return 0; +} + +// ------------------------------------------------------------------------- + +static int testfs_umount ( cyg_mtab_entry *mte ) +{ + testfs_node *root = (testfs_node *)mte->root; + + // Non-empty filesystem, do not unmount + if( root->refcnt != 1 ) + return EBUSY; + + // Otherwise just return it to the free pool + root->next = free_node; + root->refcnt = -1; + free_node = root; + + // Clear root pointer + mte->root = CYG_DIR_NULL; + + // That's all folks. + + return ENOERR; +} +// ------------------------------------------------------------------------- + +static int testfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + int mode, cyg_file *file ) +{ + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( lastp && err == ENOENT && (mode & O_CREAT) ) + { + int i; + // No node there, if the O_CREAT bit is set then we must + // create a new one. The parent and name results will have been filled + // in, so we know where to put it. + + // first check that there is space for it + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + if( parent->u.dir.nodes[i] == NULL ) + break; + + if( i == TESTFS_FILEBLOCKS ) return ENOSPC; + + // Allocate a new node + nd = free_node; + if( nd == NULL ) return ENOSPC; + free_node = nd->next; + + // Add to directory list + parent->u.dir.nodes[i] = nd; + + parent->refcnt++; + + // Fill in details + nd->parent = parent; + nd->refcnt = 1; // 1 for directory reference + strcpy( nd->name, name); + nd->status.st_mode = __stat_mode_REG; + nd->status.st_ino = nd-&node[0]; + nd->status.st_dev = 0; + nd->status.st_nlink = 1; + nd->status.st_uid = 0; + nd->status.st_gid = 0; + nd->status.st_size = 0; + nd->status.st_atime = testfs_time(); + nd->status.st_mtime = testfs_time(); + nd->status.st_ctime = testfs_time(); + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + nd->u.file.data[i] = NULL; + + err = ENOERR; + } + + if( err == ENOERR && (mode & O_TRUNC ) ) + { + // Clean out any blocks in the file... + + int i; + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + { + testfs_block *b = nd->u.file.data[i]; + if( b != NULL ) + { + b->u.next = free_block; + b->pos = -1; + free_block = b; + nd->u.file.data[i] = NULL; + } + } + + nd->status.st_size = 0; + } + + if( err != ENOERR ) return err; + + if( S_ISDIR(nd->status.st_mode) ) return EISDIR; + + nd->refcnt++; // Count successful open as a ref + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &testfs_fileops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)nd; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *path ) +{ + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( err != ENOERR ) return err; + + // Cannot unlink directories, use rmdir() instead + if( S_ISDIR(nd->status.st_mode) ) + return EPERM; + + err = testfs_delnode( nd ); + + return err; +} + +// ------------------------------------------------------------------------- + +static int testfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path ) +{ + + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( lastp && err == ENOENT ) + { + int i; + // No node there, create a new one. The parent and name + // results will have been filled in, so we know where to put + // it. + + // first check that there is space for it + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + if( parent->u.dir.nodes[i] == NULL ) + break; + + if( i == TESTFS_FILEBLOCKS ) return ENOSPC; + + // Allocate a new node + nd = free_node; + if( nd == NULL ) return ENOSPC; + free_node = nd->next; + + // Add to directory list + parent->u.dir.nodes[i] = nd; + + parent->refcnt++; + + // Fill in details + nd->parent = parent; + nd->refcnt = 1; // 1 for directory reference + strcpy( nd->name, name); + nd->status.st_mode = __stat_mode_DIR; + nd->status.st_ino = nd-&node[0]; + nd->status.st_dev = 0; + nd->status.st_nlink = 1; + nd->status.st_uid = 0; + nd->status.st_gid = 0; + nd->status.st_size = 0; + nd->status.st_atime = testfs_time(); + nd->status.st_mtime = testfs_time(); + nd->status.st_ctime = testfs_time(); + + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + nd->u.dir.nodes[i] = NULL; + + err = ENOERR; + } + + return err; +} + +// ------------------------------------------------------------------------- + +static int testfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path ) +{ + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( err != ENOERR ) return err; + + // Check that it is a directory + if( !S_ISDIR(nd->status.st_mode) ) + return EPERM; + + err = testfs_delnode( nd ); + + return err; +} + +// ------------------------------------------------------------------------- + +static int testfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1, + cyg_dir dir2, const char *path2 ) +{ + testfs_node *nd1, *parent1; + testfs_node *nd2, *parent2; + int err; + char name1[TESTFS_NAMESIZE]; + char name2[TESTFS_NAMESIZE]; + cyg_bool lastp; + int i,j; + + err = testfs_find( (testfs_node *)dir1, path1, &nd1, &parent1, name1, &lastp ); + + if( err != ENOERR ) return err; + + err = testfs_find( (testfs_node *)dir2, path2, &nd2, &parent2, name2, &lastp ); + + // Allow through renames to non-existent objects. + if( lastp && err == ENOENT ) + err = ENOERR; + + if( err != ENOERR ) return err; + + // Null rename, just return + if( nd1 == nd2 ) + return ENOERR; + + + // First deal with any node that is at the destination + if( nd2 ) + { + // Check that we are renaming like-for-like + + if( !S_ISDIR(nd1->status.st_mode) && S_ISDIR(nd2->status.st_mode) ) + return EISDIR; + + if( S_ISDIR(nd1->status.st_mode) && !S_ISDIR(nd2->status.st_mode) ) + return ENOTDIR; + + // Now delete the destination node. + err = testfs_delnode( nd2 ); + if( err != ENOERR ) return err; + } + + // Now we know that there is no clashing node at the destination. + // Move the node over and change its name. + + // first check that there is space for it + for( i = 0; i < TESTFS_FILEBLOCKS; i++ ) + if( parent2->u.dir.nodes[i] == NULL ) + break; + + if( i == TESTFS_FILEBLOCKS ) return ENOSPC; + + // Now remove node from old parent. + for( j = 0; j < TESTFS_FILEBLOCKS; j++ ) + if( parent1->u.dir.nodes[j] == nd1 ) + { + parent1->u.dir.nodes[j] = NULL; + break; + } + + parent1->refcnt--; + + // Add to directory list + parent2->u.dir.nodes[i] = nd1; + parent2->refcnt++; + nd1->parent = parent2; + + // And give it a new name. + strcpy( nd1->name, name2 ); + + return err; +} + +// ------------------------------------------------------------------------- + +static int testfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1, + cyg_dir dir2, const char *path2, int type ) +{ + // The data structures of this file system do not support the + // creation of links. + + return ENOSYS; +} + +// ------------------------------------------------------------------------- + +static int testfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + cyg_file *file ) +{ + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( err != ENOERR ) return err; + + if( !S_ISDIR(nd->status.st_mode) ) + return ENOTDIR; + + nd->refcnt++; // Count successful open as a ref + + // Initialize the file object + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &testfs_dirops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD)nd; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + cyg_dir *dir_out ) +{ + if( dir_out != NULL ) + { + // This is a request to get a new directory pointer in + // *dir_out. + + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( err != ENOERR ) return err; + + if( !S_ISDIR(nd->status.st_mode) ) + return ENOTDIR; + + // Increment ref count to keep this directory in existent + // while it is the current cdir. + nd->refcnt++; + + // Pass it out + *dir_out = (cyg_dir)nd; + } + else + { + // If no output dir is required, this means that the mte and + // dir arguments are the current cdir setting and we should + // forget this fact. + + testfs_node *nd = (testfs_node *)dir; + + // Just decrement reference count. + nd->refcnt--; + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + struct stat *buf) +{ + testfs_node *nd, *parent; + int err; + char name[TESTFS_NAMESIZE]; + cyg_bool lastp; + + err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp ); + + if( err != ENOERR ) return err; + + *buf = nd->status; + + return err; +} + +// ------------------------------------------------------------------------- + +static int testfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + int key, void *buf, int len ) +{ + return ENOSYS; +} + +// ------------------------------------------------------------------------- + +static int testfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *path, + int key, void *buf, int len ) +{ + return ENOSYS; +} + + +//========================================================================== +// File operations + + +// ------------------------------------------------------------------------- + +static int testfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + int i; + off_t pos = fp->f_offset; + + for( i = 0; i < uio->uio_iovcnt; i++ ) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + while( len > 0 && pos < nd->status.st_size ) + { + testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE]; + off_t l = len; + off_t bpos = pos%TESTFS_BLOCKSIZE; + + // If there is no block in that pos, we have reached + // the end of the file. + if( b == NULL ) return ENOERR; + + // adjust size to this block + if( l > (b->size-bpos) ) + l = (b->size-bpos); + + // copy data out + memcpy( buf, &b->data[bpos], l ); + + uio->uio_resid -= l; + len -= l; + buf += l; + pos += l; + + // keep offset up to date incase of errors + fp->f_offset = pos; + } + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + int i; + off_t pos = fp->f_offset; + + // Check we are not at end of allowed max file size + if( pos >= TESTFS_FILESIZE_MAX ) + return EFBIG; + + // Check that pos is within current file size, or at the very end. + if( pos < 0 || pos > nd->status.st_size ) + return EINVAL; + + // Now loop over the iovecs until they are all done, or + // we get an error. + for( i = 0; i < uio->uio_iovcnt; i++ ) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + while( len > 0 ) + { + testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE]; + off_t l = len; + off_t bpos = pos%TESTFS_BLOCKSIZE; + + // If there is no block in that pos, allocate one + // and initialize it + if( b == NULL ) + { + b = free_block; + if( b == NULL ) return ENOSPC; + free_block = b->u.next; + nd->u.file.data[pos/TESTFS_BLOCKSIZE] = b; + b->u.file = nd; + b->pos = pos; + b->size = 0; + } + + // adjust size to this block + if( l > (TESTFS_BLOCKSIZE-bpos) ) + l = (TESTFS_BLOCKSIZE-bpos); + + // copy data in + memcpy( &b->data[bpos], buf, l ); + + // adjust buffer info + if( b->size < bpos+l ) + b->size = bpos+l; + + uio->uio_resid -= l; + len -= l; + buf += l; + pos += l; + + // keep node size and file offset up to date + //in case of an error. + if( pos > nd->status.st_size ) + nd->status.st_size = pos; + fp->f_offset = pos; + + if( pos >= TESTFS_FILESIZE_MAX ) + return EFBIG; + } + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence ) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + off_t pos = *apos; + + switch( whence ) + { + case SEEK_SET: + // we are already where we want to be. + break; + + case SEEK_CUR: + pos += fp->f_offset; + break; + + case SEEK_END: + pos += nd->status.st_size; + break; + + default: + return EINVAL; + } + + // Check that pos is within current file size, or at the very end. + if( pos < 0 || pos > nd->status.st_size ) + return EINVAL; + + // All OK, set fp offset. + *apos = fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data) +{ + return ENOSYS; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ) +{ + // Nothing to do + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_close (struct CYG_FILE_TAG *fp) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + + nd->refcnt--; // remove open count + + fp->f_data = 0; // clear data pointer + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + + *buf = nd->status; + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) +{ + return ENOERR; +} + + +//========================================================================== +// Directory operations + +static int testfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + testfs_node *nd = (testfs_node *)fp->f_data; + off_t pos = fp->f_offset; + cyg_iovec *iov = &uio->uio_iov[0]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // End of directory + if( pos >= TESTFS_FILEBLOCKS ) + return ENOERR; + + if( len < sizeof(struct dirent) ) + return EINVAL; + + for( ; pos < TESTFS_FILEBLOCKS; pos++ ) + if( nd->u.dir.nodes[pos] != NULL ) + { + struct dirent *ent = (struct dirent *)buf; + strcpy( ent->d_name, nd->u.dir.nodes[pos]->name ); + uio->uio_resid -= sizeof(struct dirent); + break; + } + + fp->f_offset = pos+1; + + return ENOERR; +} + +// ------------------------------------------------------------------------- + +static int testfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ) +{ + if( whence != SEEK_SET || *pos != 0) + return EINVAL; + + *pos = fp->f_offset = 0; + + return ENOERR; +} + +//========================================================================== +// Filesystem dump +// Dumps out the node and block arrays in a readable format, and does +// a little consistency checking as it goes. + +void testfs_dump(void) +{ + int errors = 0; + int i; + char *indent = "\n |"; + + diag_printf("Nodes:\n"); + for( i = 0; i < TESTFS_NFILE; i++ ) + { + testfs_node *nd = &node[i]; + + diag_printf("%3d : ",i); + if( nd->refcnt < 0 ) + diag_printf("<free>"); + else if( !S_ISDIR(nd->status.st_mode) ) + { + // Regular file + int j; + diag_printf("f %8s %4d |",nd->name,nd->status.st_size); + for( j = 0; j < TESTFS_FILEBLOCKS; j++ ) + { + testfs_block *b = nd->u.file.data[j]; + if( b != NULL ) + { + if( j > 0 && (j%4) == 0 ) + diag_printf(indent); + diag_printf(" %3d[%3d,%3d]",b-block,b->pos,b->size); + if( b->u.file != nd ) + { + errors++; + diag_printf("!"); + } + } + } + } + else + { + // Directory + int j; + int rc = 1; + diag_printf("d %8s |",nd->name); + + for( j = 0; j < TESTFS_FILEBLOCKS; j++ ) + { + testfs_node *n = nd->u.dir.nodes[j]; + if( n != NULL ) + { + if( j > 0 && (j%4) == 0 ) + diag_printf(indent); + diag_printf(" %3d[%7s]",n-node,n->name); + rc++; + } + } + + if( nd->refcnt != rc ) + { + diag_printf("%s refcount is %d should be %d",indent,nd->refcnt,rc); + if( nd->refcnt == rc+1 ) + diag_printf(" (but may be current dir)"); + } + } + + diag_printf("\n"); + } + + diag_printf("Blocks:\n"); + + for( i = 0; i < TESTFS_NBLOCK; i++ ) + { + testfs_block *b = &block[i]; + + diag_printf("%3d :",i); + if( b->pos == -1 ) + diag_printf(" <free>"); + else + { + int j; + testfs_node *nd = b->u.file; + diag_printf(" %3d %3d %d[%7s]",b->pos,b->size,nd-node,nd->name); + for( j = 0; j < TESTFS_FILEBLOCKS; j++ ) + { + if( nd->u.file.data[j] == b ) + break; + } + if( j == TESTFS_FILEBLOCKS ) + { + errors++; + diag_printf(" block not in file!"); + } + } + diag_printf("\n"); + } + + if( errors != 0 ) + diag_printf("%d errors detected\n",errors); +} + +// ------------------------------------------------------------------------- +// EOF testfs.c
