1 //==========================================================================
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: nickg
46 // Purpose: Test file system
47 // Description: This is a very simple implementation of a RAM file system.
48 // This implementation is not "industrial strength" or suitable
49 // for production use, it is too wasteful of both memory and time.
50 // Its primary purpose is to support testing of the fileio
51 // infrastructure and API. It can, however, serve as a model
52 // and source of code fragments for the implementation
53 // of further filesystems.
56 //####DESCRIPTIONEND####
58 //==========================================================================
60 #include <pkgconf/hal.h>
61 #include <pkgconf/io_fileio.h>
63 #include <cyg/infra/cyg_trac.h> // tracing macros
64 #include <cyg/infra/cyg_ass.h> // assertion macros
72 #include <cyg/fileio/fileio.h>
74 #include <cyg/infra/diag.h>
76 //==========================================================================
77 // Configuration parameters
79 #define TESTFS_NFILE 10 // Max number of files/directories
80 #define TESTFS_NBLOCK 20 // Number of data blocks available
81 #define TESTFS_BLOCKSIZE 128 // Bytes stored per block
82 #define TESTFS_FILEBLOCKS 8 // Max blocks per file
83 #define TESTFS_NAMESIZE 32 // Length of file names in bytes
86 // Maximum file size is blocksize*blocks
87 #define TESTFS_FILESIZE_MAX (TESTFS_BLOCKSIZE*TESTFS_FILEBLOCKS)
89 //==========================================================================
93 typedef struct testfs_node testfs_node;
96 typedef struct testfs_block testfs_block;
101 testfs_node *next; // next node in list
102 testfs_node *parent; // Back pointer to parent
103 int refcnt; // reference count
104 char name[TESTFS_NAMESIZE]; // file name
105 struct stat status; // status data
110 testfs_block *data[TESTFS_FILEBLOCKS]; // array of blocks
115 testfs_node *nodes[TESTFS_FILEBLOCKS]; // array of nodes
124 testfs_block *next; // next block in free list
125 testfs_node *file; // back pointer to file
127 off_t pos; // position in file of first byte
128 size_t size; // number of bytes in buffer
129 char data[TESTFS_BLOCKSIZE]; // the data
132 //==========================================================================
136 static testfs_node node[TESTFS_NFILE];
139 static testfs_node *free_node = NULL;
141 // Array of data blocks
142 static testfs_block block[TESTFS_NBLOCK];
145 static testfs_block *free_block = NULL;
148 cyg_bool testfs_initialized = false;
150 //==========================================================================
151 // Forward definitions
153 // Filesystem operations
154 static int testfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte );
155 static int testfs_umount ( cyg_mtab_entry *mte );
156 static int testfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
157 int mode, cyg_file *fte );
158 static int testfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
159 static int testfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
160 static int testfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name );
161 static int testfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
162 cyg_dir dir2, const char *name2 );
163 static int testfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
164 cyg_dir dir2, const char *name2, int type );
165 static int testfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
167 static int testfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
169 static int testfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
171 static int testfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
172 int key, void *buf, int len );
173 static int testfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
174 int key, void *buf, int len );
177 static int testfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
178 static int testfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
179 static int testfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
180 static int testfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
182 //static int testfs_fo_select (struct CYG_FILE_TAG *fp, int which, CYG_ADDRWORD info);
183 static int testfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode );
184 static int testfs_fo_close (struct CYG_FILE_TAG *fp);
185 static int testfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf );
186 static int testfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
187 static int testfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len );
189 // Directory operations
190 static int testfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
191 static int testfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
193 //==========================================================================
194 // Filesystem table entries
196 FSTAB_ENTRY( testfs_fste, "testfs", 0,
197 CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
212 MTAB_ENTRY( testfs_mte1,
219 MTAB_ENTRY( testfs_mte2,
226 static cyg_fileops testfs_fileops =
240 static cyg_fileops testfs_dirops =
243 (cyg_fileop_write *)cyg_fileio_enosys,
245 (cyg_fileop_ioctl *)cyg_fileio_enosys,
247 (cyg_fileop_fsync *)cyg_fileio_enosys,
249 (cyg_fileop_fstat *)cyg_fileio_enosys,
250 (cyg_fileop_getinfo *)cyg_fileio_enosys,
251 (cyg_fileop_setinfo *)cyg_fileio_enosys
254 //==========================================================================
257 // -------------------------------------------------------------------------
258 // Local strcmp() and strcpy()
260 static int mystrcmp( const char *s1, const char *s2 )
262 while( *s1 == *s2 && *s1 != '\0' && *s2 != '\0' )
268 static char *mystrcpy( char *s1, const char *s2 )
271 while( (*s1++ = *s2++) != 0);
275 // -------------------------------------------------------------------------
276 // Follow a path through the directory structure
278 static int testfs_find( testfs_node *dir, // dir to start search in
279 const char *path, // path to follow
280 testfs_node **found, // return node found
281 testfs_node **parent, // return last dir searched
282 char *name, // name fragment buffer
283 cyg_bool *lastp) // last name in path ?
285 testfs_node *nd = dir;
290 while( *path != '\0' )
292 const char *p = path;
297 // check nd is a directory
298 if( !S_ISDIR(nd->status.st_mode) ) return ENOTDIR;
300 // Isolate the next element of the path name.
301 while( *p != '\0' && *p != '/' && (n-&name[0]) < TESTFS_NAMESIZE)
304 if( (n-&name[0]) >= TESTFS_NAMESIZE )
307 // Step path on past the separator
308 // If this is the last name element in the path,
309 // set *lastp to indicate this.
310 if( *(path=p) == '/' ) path++;
316 // name now contains the next path element, search the node
322 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
324 testfs_node *n = nd->u.dir.nodes[i];
328 if( mystrcmp( name, n->name ) == 0 )
335 if( nd1 == NULL ) return ENOENT;
340 // Return what we have found
346 // -------------------------------------------------------------------------
347 // Get current time since epoch
349 static time_t testfs_time(void)
352 return cyg_current_time();
358 // -------------------------------------------------------------------------
360 static int testfs_delnode( testfs_node *nd )
366 // Non-unitary ref count means this node is either open
367 // or is a dir with entries in it.
371 // Remove from parent's node list.
375 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
376 if( parent->u.dir.nodes[i] == nd )
378 parent->u.dir.nodes[i] = NULL;
384 if( S_ISREG(nd->status.st_mode) )
386 // for a file, return blocks to free list
387 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
389 testfs_block *b = nd->u.file.data[i];
392 b->u.next = free_block;
399 // and finally return nd to free node list
401 nd->next = free_node;
408 //==========================================================================
409 // Filesystem operations
411 // -------------------------------------------------------------------------
413 static int testfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte )
418 if( !testfs_initialized )
422 for( i = 0; i < TESTFS_NFILE; i++ )
424 node[i].next = free_node;
426 free_node = &node[i];
429 for( i = 0; i < TESTFS_NBLOCK; i++ )
431 block[i].u.next = free_block;
433 free_block = &block[i];
436 testfs_initialized = true;
439 // Allocate a node to be the root of this filesystem and
444 if( root == NULL ) return ENOSPC;
446 free_node = root->next;
448 root->next = root; // form circular list
449 root->parent = root; // I'm my own parent!
450 root->refcnt = 1; // don't want to ever lose root
451 mystrcpy( root->name, "root");
452 root->status.st_mode = __stat_mode_DIR;
453 root->status.st_ino = root-&node[0];
454 root->status.st_dev = 0;
455 root->status.st_nlink = 1;
456 root->status.st_uid = 0;
457 root->status.st_gid = 0;
458 root->status.st_size = 0;
459 root->status.st_atime = testfs_time();
460 root->status.st_mtime = testfs_time();
461 root->status.st_ctime = testfs_time();
463 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
464 root->u.dir.nodes[i] = NULL;
466 mte->root = (cyg_dir)root;
471 // -------------------------------------------------------------------------
473 static int testfs_umount ( cyg_mtab_entry *mte )
475 testfs_node *root = (testfs_node *)mte->root;
477 // Non-empty filesystem, do not unmount
478 if( root->refcnt != 1 )
481 // Otherwise just return it to the free pool
482 root->next = free_node;
486 // Clear root pointer
487 mte->root = CYG_DIR_NULL;
493 // -------------------------------------------------------------------------
495 static int testfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
496 int mode, cyg_file *file )
498 testfs_node *nd, *parent;
500 char name[TESTFS_NAMESIZE];
503 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
505 if( lastp && err == ENOENT && (mode & O_CREAT) )
508 // No node there, if the O_CREAT bit is set then we must
509 // create a new one. The parent and name results will have been filled
510 // in, so we know where to put it.
512 // first check that there is space for it
513 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
514 if( parent->u.dir.nodes[i] == NULL )
517 if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
519 // Allocate a new node
521 if( nd == NULL ) return ENOSPC;
522 free_node = nd->next;
524 // Add to directory list
525 parent->u.dir.nodes[i] = nd;
531 nd->refcnt = 1; // 1 for directory reference
532 mystrcpy( nd->name, name);
533 nd->status.st_mode = __stat_mode_REG;
534 nd->status.st_ino = nd-&node[0];
535 nd->status.st_dev = 0;
536 nd->status.st_nlink = 1;
537 nd->status.st_uid = 0;
538 nd->status.st_gid = 0;
539 nd->status.st_size = 0;
540 nd->status.st_atime = testfs_time();
541 nd->status.st_mtime = testfs_time();
542 nd->status.st_ctime = testfs_time();
544 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
545 nd->u.file.data[i] = NULL;
550 if( err == ENOERR && (mode & O_TRUNC ) )
552 // Clean out any blocks in the file...
556 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
558 testfs_block *b = nd->u.file.data[i];
561 b->u.next = free_block;
564 nd->u.file.data[i] = NULL;
568 nd->status.st_size = 0;
571 if( err != ENOERR ) return err;
573 if( S_ISDIR(nd->status.st_mode) ) return EISDIR;
575 nd->refcnt++; // Count successful open as a ref
577 // Initialize the file object
579 file->f_flag |= mode & CYG_FILE_MODE_MASK;
580 file->f_type = CYG_FILE_TYPE_FILE;
581 file->f_ops = &testfs_fileops;
583 file->f_data = (CYG_ADDRWORD)nd;
589 // -------------------------------------------------------------------------
591 static int testfs_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
593 testfs_node *nd, *parent;
595 char name[TESTFS_NAMESIZE];
598 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
600 if( err != ENOERR ) return err;
602 // Cannot unlink directories, use rmdir() instead
603 if( S_ISDIR(nd->status.st_mode) )
606 err = testfs_delnode( nd );
611 // -------------------------------------------------------------------------
613 static int testfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
616 testfs_node *nd, *parent;
618 char name[TESTFS_NAMESIZE];
621 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
623 if( lastp && err == ENOENT )
626 // No node there, create a new one. The parent and name
627 // results will have been filled in, so we know where to put
630 // first check that there is space for it
631 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
632 if( parent->u.dir.nodes[i] == NULL )
635 if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
637 // Allocate a new node
639 if( nd == NULL ) return ENOSPC;
640 free_node = nd->next;
642 // Add to directory list
643 parent->u.dir.nodes[i] = nd;
649 nd->refcnt = 1; // 1 for directory reference
650 mystrcpy( nd->name, name);
651 nd->status.st_mode = __stat_mode_DIR;
652 nd->status.st_ino = nd-&node[0];
653 nd->status.st_dev = 0;
654 nd->status.st_nlink = 1;
655 nd->status.st_uid = 0;
656 nd->status.st_gid = 0;
657 nd->status.st_size = 0;
658 nd->status.st_atime = testfs_time();
659 nd->status.st_mtime = testfs_time();
660 nd->status.st_ctime = testfs_time();
662 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
663 nd->u.dir.nodes[i] = NULL;
671 // -------------------------------------------------------------------------
673 static int testfs_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
675 testfs_node *nd, *parent;
677 char name[TESTFS_NAMESIZE];
680 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
682 if( err != ENOERR ) return err;
684 // Check that it is a directory
685 if( !S_ISDIR(nd->status.st_mode) )
688 err = testfs_delnode( nd );
693 // -------------------------------------------------------------------------
695 static int testfs_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1,
696 cyg_dir dir2, const char *path2 )
698 testfs_node *nd1, *parent1;
699 testfs_node *nd2, *parent2;
701 char name1[TESTFS_NAMESIZE];
702 char name2[TESTFS_NAMESIZE];
706 err = testfs_find( (testfs_node *)dir1, path1, &nd1, &parent1, name1, &lastp );
708 if( err != ENOERR ) return err;
710 err = testfs_find( (testfs_node *)dir2, path2, &nd2, &parent2, name2, &lastp );
712 // Allow through renames to non-existent objects.
713 if( lastp && err == ENOENT )
716 if( err != ENOERR ) return err;
718 // Null rename, just return
723 // First deal with any node that is at the destination
726 // Check that we are renaming like-for-like
728 if( !S_ISDIR(nd1->status.st_mode) && S_ISDIR(nd2->status.st_mode) )
731 if( S_ISDIR(nd1->status.st_mode) && !S_ISDIR(nd2->status.st_mode) )
734 // Now delete the destination node.
735 err = testfs_delnode( nd2 );
736 if( err != ENOERR ) return err;
739 // Now we know that there is no clashing node at the destination.
740 // Move the node over and change its name.
742 // first check that there is space for it
743 for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
744 if( parent2->u.dir.nodes[i] == NULL )
747 if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
749 // Now remove node from old parent.
750 for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
751 if( parent1->u.dir.nodes[j] == nd1 )
753 parent1->u.dir.nodes[j] = NULL;
759 // Add to directory list
760 parent2->u.dir.nodes[i] = nd1;
762 nd1->parent = parent2;
764 // And give it a new name.
765 mystrcpy( nd1->name, name2 );
770 // -------------------------------------------------------------------------
772 static int testfs_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1,
773 cyg_dir dir2, const char *path2, int type )
775 // The data structures of this file system do not support the
776 // creation of links.
781 // -------------------------------------------------------------------------
783 static int testfs_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
786 testfs_node *nd, *parent;
788 char name[TESTFS_NAMESIZE];
791 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
793 if( err != ENOERR ) return err;
795 if( !S_ISDIR(nd->status.st_mode) )
798 nd->refcnt++; // Count successful open as a ref
800 // Initialize the file object
802 file->f_type = CYG_FILE_TYPE_FILE;
803 file->f_ops = &testfs_dirops;
805 file->f_data = (CYG_ADDRWORD)nd;
811 // -------------------------------------------------------------------------
813 static int testfs_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
816 if( dir_out != NULL )
818 // This is a request to get a new directory pointer in
821 testfs_node *nd, *parent;
823 char name[TESTFS_NAMESIZE];
826 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
828 if( err != ENOERR ) return err;
830 if( !S_ISDIR(nd->status.st_mode) )
833 // Increment ref count to keep this directory in existent
834 // while it is the current cdir.
838 *dir_out = (cyg_dir)nd;
842 // If no output dir is required, this means that the mte and
843 // dir arguments are the current cdir setting and we should
846 testfs_node *nd = (testfs_node *)dir;
848 // Just decrement reference count.
855 // -------------------------------------------------------------------------
857 static int testfs_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
860 testfs_node *nd, *parent;
862 char name[TESTFS_NAMESIZE];
865 err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
867 if( err != ENOERR ) return err;
874 // -------------------------------------------------------------------------
876 static int testfs_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
877 int key, void *buf, int len )
882 // -------------------------------------------------------------------------
884 static int testfs_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
885 int key, void *buf, int len )
891 //==========================================================================
895 // -------------------------------------------------------------------------
897 static int testfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
899 testfs_node *nd = (testfs_node *)fp->f_data;
901 off_t pos = fp->f_offset;
903 for( i = 0; i < uio->uio_iovcnt; i++ )
905 cyg_iovec *iov = &uio->uio_iov[i];
906 char *buf = (char *)iov->iov_base;
907 off_t len = iov->iov_len;
909 while( len > 0 && pos < nd->status.st_size )
911 testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE];
913 off_t bpos = pos%TESTFS_BLOCKSIZE;
915 // If there is no block in that pos, we have reached
916 // the end of the file.
917 if( b == NULL ) return ENOERR;
919 // adjust size to this block
920 if( l > (b->size-bpos) )
924 memcpy( buf, &b->data[bpos], l );
931 // keep offset up to date incase of errors
939 // -------------------------------------------------------------------------
941 static int testfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
943 testfs_node *nd = (testfs_node *)fp->f_data;
945 off_t pos = fp->f_offset;
947 // Check we are not at end of allowed max file size
948 if( pos >= TESTFS_FILESIZE_MAX )
951 // Check that pos is within current file size, or at the very end.
952 if( pos < 0 || pos > nd->status.st_size )
955 // Now loop over the iovecs until they are all done, or
957 for( i = 0; i < uio->uio_iovcnt; i++ )
959 cyg_iovec *iov = &uio->uio_iov[i];
960 char *buf = (char *)iov->iov_base;
961 off_t len = iov->iov_len;
965 testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE];
967 off_t bpos = pos%TESTFS_BLOCKSIZE;
969 // If there is no block in that pos, allocate one
974 if( b == NULL ) return ENOSPC;
975 free_block = b->u.next;
976 nd->u.file.data[pos/TESTFS_BLOCKSIZE] = b;
982 // adjust size to this block
983 if( l > (TESTFS_BLOCKSIZE-bpos) )
984 l = (TESTFS_BLOCKSIZE-bpos);
987 memcpy( &b->data[bpos], buf, l );
989 // adjust buffer info
990 if( b->size < bpos+l )
998 // keep node size and file offset up to date
999 //in case of an error.
1000 if( pos > nd->status.st_size )
1001 nd->status.st_size = pos;
1004 if( pos >= TESTFS_FILESIZE_MAX )
1012 // -------------------------------------------------------------------------
1014 static int testfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence )
1016 testfs_node *nd = (testfs_node *)fp->f_data;
1022 // we are already where we want to be.
1026 pos += fp->f_offset;
1030 pos += nd->status.st_size;
1037 // Check that pos is within current file size, or at the very end.
1038 if( pos < 0 || pos > nd->status.st_size )
1041 // All OK, set fp offset.
1042 *apos = fp->f_offset = pos;
1047 // -------------------------------------------------------------------------
1049 static int testfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
1055 // -------------------------------------------------------------------------
1057 static int testfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode )
1063 // -------------------------------------------------------------------------
1065 static int testfs_fo_close (struct CYG_FILE_TAG *fp)
1067 testfs_node *nd = (testfs_node *)fp->f_data;
1069 nd->refcnt--; // remove open count
1071 fp->f_data = 0; // clear data pointer
1076 // -------------------------------------------------------------------------
1078 static int testfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf )
1080 testfs_node *nd = (testfs_node *)fp->f_data;
1087 // -------------------------------------------------------------------------
1089 static int testfs_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
1094 // -------------------------------------------------------------------------
1096 static int testfs_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
1102 //==========================================================================
1103 // Directory operations
1105 static int testfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
1107 testfs_node *nd = (testfs_node *)fp->f_data;
1108 off_t pos = fp->f_offset;
1109 cyg_iovec *iov = &uio->uio_iov[0];
1110 char *buf = (char *)iov->iov_base;
1111 off_t len = iov->iov_len;
1114 if( pos >= TESTFS_FILEBLOCKS )
1117 if( len < sizeof(struct dirent) )
1120 for( ; pos < TESTFS_FILEBLOCKS; pos++ )
1121 if( nd->u.dir.nodes[pos] != NULL )
1123 struct dirent *ent = (struct dirent *)buf;
1124 mystrcpy( ent->d_name, nd->u.dir.nodes[pos]->name );
1125 uio->uio_resid -= sizeof(struct dirent);
1129 fp->f_offset = pos+1;
1134 // -------------------------------------------------------------------------
1136 static int testfs_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence )
1138 if( whence != SEEK_SET || *pos != 0)
1141 *pos = fp->f_offset = 0;
1146 //==========================================================================
1148 // Dumps out the node and block arrays in a readable format, and does
1149 // a little consistency checking as it goes.
1151 void testfs_dump(void)
1155 char *indent = "\n |";
1157 diag_printf("Nodes:\n");
1158 for( i = 0; i < TESTFS_NFILE; i++ )
1160 testfs_node *nd = &node[i];
1162 diag_printf("%3d : ",i);
1163 if( nd->refcnt < 0 )
1164 diag_printf("<free>");
1165 else if( !S_ISDIR(nd->status.st_mode) )
1169 diag_printf("f %8s %4ld |",nd->name,
1170 (unsigned long)nd->status.st_size);
1171 for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
1173 testfs_block *b = nd->u.file.data[j];
1176 if( j > 0 && (j%4) == 0 )
1177 diag_printf(indent);
1178 diag_printf(" %3d[%3ld,%3zd]",(int) (b-block),
1179 (unsigned long)b->pos,b->size);
1180 if( b->u.file != nd )
1193 diag_printf("d %8s |",nd->name);
1195 for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
1197 testfs_node *n = nd->u.dir.nodes[j];
1200 if( j > 0 && (j%4) == 0 )
1201 diag_printf(indent);
1202 diag_printf(" %3d[%7s]",(int) (n-node),n->name);
1207 if( nd->refcnt != rc )
1209 diag_printf("%s refcount is %d should be %d",indent,nd->refcnt,rc);
1210 if( nd->refcnt == rc+1 )
1211 diag_printf(" (but may be current dir)");
1218 diag_printf("Blocks:\n");
1220 for( i = 0; i < TESTFS_NBLOCK; i++ )
1222 testfs_block *b = &block[i];
1224 diag_printf("%3d :",i);
1226 diag_printf(" <free>");
1230 testfs_node *nd = b->u.file;
1231 diag_printf(" %3ld %3zd %d[%7s]",b->pos,b->size,(int) (nd-node),nd->name);
1232 for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
1234 if( nd->u.file.data[j] == b )
1237 if( j == TESTFS_FILEBLOCKS )
1240 diag_printf(" block not in file!");
1247 diag_printf("%d errors detected\n",errors);
1250 // -------------------------------------------------------------------------