]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/fileio/v2_0/tests/testfs.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / io / fileio / v2_0 / tests / testfs.c
1 //==========================================================================
2 //
3 //      testfs.c
4 //
5 //      Test file system
6 //
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.
12 //
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.
16 //
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
20 // for more details.
21 //
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.
25 //
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.
32 //
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.
35 //
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####
42 //
43 // Author(s):           nickg
44 // Contributors:        nickg
45 // Date:                2000-05-25
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.
54 //              
55 //
56 //####DESCRIPTIONEND####
57 //
58 //==========================================================================
59
60 #include <pkgconf/hal.h>
61 #include <pkgconf/io_fileio.h>
62
63 #include <cyg/infra/cyg_trac.h>        // tracing macros
64 #include <cyg/infra/cyg_ass.h>         // assertion macros
65
66 #include <unistd.h>
67 #include <fcntl.h>
68 #include <sys/stat.h>
69 #include <errno.h>
70 #include <dirent.h>
71
72 #include <cyg/fileio/fileio.h>
73
74 #include <cyg/infra/diag.h>
75
76 //==========================================================================
77 // Configuration  parameters
78
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
84
85
86 // Maximum file size is blocksize*blocks
87 #define TESTFS_FILESIZE_MAX     (TESTFS_BLOCKSIZE*TESTFS_FILEBLOCKS)
88
89 //==========================================================================
90 // Data structures
91
92 struct testfs_node;
93 typedef struct testfs_node testfs_node;
94
95 struct testfs_block;
96 typedef struct testfs_block testfs_block;
97
98
99 struct testfs_node
100 {
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
106     union
107     {
108         struct
109         {
110             testfs_block        *data[TESTFS_FILEBLOCKS];  // array of blocks
111         } file;
112
113         struct
114         {
115             testfs_node         *nodes[TESTFS_FILEBLOCKS]; // array of nodes
116         } dir;
117     } u;
118 };
119
120 struct testfs_block
121 {
122     union
123     {
124         testfs_block    *next;          // next block in free list
125         testfs_node     *file;          // back pointer to file
126     } u;
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
130 };
131
132 //==========================================================================
133 // Local data
134
135 // Array of nodes
136 static testfs_node node[TESTFS_NFILE];
137
138 // node free list.
139 static testfs_node *free_node = NULL;
140
141 // Array of data blocks
142 static testfs_block block[TESTFS_NBLOCK];
143
144 // block free list.
145 static testfs_block *free_block = NULL;
146
147 // Init flag
148 cyg_bool testfs_initialized = false;
149
150 //==========================================================================
151 // Forward definitions
152
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,
166                              cyg_file *fte );
167 static int testfs_chdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
168                              cyg_dir *dir_out );
169 static int testfs_stat     ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,
170                              struct stat *buf);
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 );
175
176 // File operations
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,
181                                 CYG_ADDRWORD data);
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 );
188
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 );
192
193 //==========================================================================
194 // Filesystem table entries
195
196 FSTAB_ENTRY( testfs_fste, "testfs", 0,
197              CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
198              testfs_mount,
199              testfs_umount,
200              testfs_open,
201              testfs_unlink,
202              testfs_mkdir,
203              testfs_rmdir,
204              testfs_rename,
205              testfs_link,
206              testfs_opendir,
207              testfs_chdir,
208              testfs_stat,
209              testfs_getinfo,
210              testfs_setinfo);
211
212 MTAB_ENTRY( testfs_mte1,
213                    "/",
214                    "testfs",
215                    "",
216                    0);
217
218 #if 0
219 MTAB_ENTRY( testfs_mte2,
220                    "/ram",
221                    "testfs",
222                    "",
223                    0);
224 #endif
225
226 static cyg_fileops testfs_fileops =
227 {
228     testfs_fo_read,
229     testfs_fo_write,
230     testfs_fo_lseek,
231     testfs_fo_ioctl,
232     cyg_fileio_seltrue,
233     testfs_fo_fsync,
234     testfs_fo_close,
235     testfs_fo_fstat,
236     testfs_fo_getinfo,
237     testfs_fo_setinfo
238 };
239
240 static cyg_fileops testfs_dirops =
241 {
242     testfs_fo_dirread,
243     (cyg_fileop_write *)cyg_fileio_enosys,
244     testfs_fo_dirlseek,
245     (cyg_fileop_ioctl *)cyg_fileio_enosys,
246     cyg_fileio_seltrue,
247     (cyg_fileop_fsync *)cyg_fileio_enosys,
248     testfs_fo_close,
249     (cyg_fileop_fstat *)cyg_fileio_enosys,
250     (cyg_fileop_getinfo *)cyg_fileio_enosys,
251     (cyg_fileop_setinfo *)cyg_fileio_enosys
252 };
253
254 //==========================================================================
255 // Support routines
256
257 // -------------------------------------------------------------------------
258 // Local strcmp() and strcpy()
259
260 static int mystrcmp( const char *s1, const char *s2 )
261 {
262     while( *s1 == *s2 && *s1 != '\0' && *s2 != '\0' )
263         s1++, s2++;
264
265     return (*s2)-(*s1);
266 }
267
268 static char *mystrcpy( char *s1, const char *s2 )
269 {
270     char *s = s1;
271     while( (*s1++ = *s2++) != 0);
272     return s;
273 }
274
275 // -------------------------------------------------------------------------
276 // Follow a path through the directory structure
277
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 ?
284 {
285     testfs_node *nd = dir;
286
287     *lastp = false;
288     *found = NULL;
289     
290     while( *path != '\0' )
291     {
292         const char *p = path;
293         char *n = name;
294         testfs_node *nd1;
295         int i;
296
297         // check nd is a directory
298         if( !S_ISDIR(nd->status.st_mode) ) return ENOTDIR;        
299
300         // Isolate the next element of the path name. 
301         while( *p != '\0' && *p != '/' && (n-&name[0]) < TESTFS_NAMESIZE)
302             *n++ = *p++;
303
304         if( (n-&name[0]) >= TESTFS_NAMESIZE )
305             return ENAMETOOLONG;
306
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++;
311         else *lastp = true;
312
313         // teminate name
314         *n = '\0';
315
316         // name now contains the next path element, search the node
317         // in nd for it.
318
319         *parent = nd;
320         nd1 = NULL;
321         
322         for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
323         {
324             testfs_node *n = nd->u.dir.nodes[i];
325             if( n == NULL )
326                 continue;
327
328             if( mystrcmp( name, n->name ) == 0 )
329             {
330                 nd1 = n;
331                 break;
332             }
333         }
334         
335         if( nd1 == NULL ) return ENOENT;
336
337         nd = nd1;
338     }
339
340     // Return what we have found
341     *found = nd;
342     
343     return ENOERR;
344 }
345
346 // -------------------------------------------------------------------------
347 // Get current time since epoch
348
349 static time_t testfs_time(void)
350 {
351 #ifdef CYGPKG_KERNEL
352     return cyg_current_time();
353 #else
354     return 0;
355 #endif
356 }
357
358 // -------------------------------------------------------------------------
359
360 static int testfs_delnode( testfs_node *nd )
361 {
362
363     testfs_node *parent;
364     int i;
365
366     // Non-unitary ref count means this node is either open    
367     // or is a dir with entries in it.
368     if( nd->refcnt > 1 )
369         return EBUSY;
370
371     // Remove from parent's node list.
372     
373     parent = nd->parent;
374
375     for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
376         if( parent->u.dir.nodes[i] == nd )
377         {
378             parent->u.dir.nodes[i] = NULL;
379             break;
380         }
381
382     parent->refcnt--;
383
384     if( S_ISREG(nd->status.st_mode) )
385     {
386         // for a file, return blocks to free list
387         for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
388         {
389             testfs_block *b = nd->u.file.data[i];
390             if( b != NULL )
391             {
392                 b->u.next = free_block;
393                 b->pos = -1;
394                 free_block = b;
395             }
396         }
397     }
398
399     // and finally return nd to free node list
400
401     nd->next = free_node;
402     nd->refcnt = -1;
403     free_node = nd;
404
405     return ENOERR;
406 }
407
408 //==========================================================================
409 // Filesystem operations
410
411 // -------------------------------------------------------------------------
412
413 static int testfs_mount    ( cyg_fstab_entry *fste, cyg_mtab_entry *mte )
414 {
415     testfs_node *root;
416     int i;
417     
418     if( !testfs_initialized )
419     {
420         int i;
421
422         for( i = 0; i < TESTFS_NFILE; i++ )
423         {
424             node[i].next = free_node;
425             node[i].refcnt = -1;
426             free_node = &node[i];
427         }
428
429         for( i = 0; i < TESTFS_NBLOCK; i++ )
430         {
431             block[i].u.next = free_block;
432             block[i].pos = -1;
433             free_block = &block[i];
434         }
435
436         testfs_initialized = true;
437     }
438
439     // Allocate a node to be the root of this filesystem and
440     // initialize it.
441
442     root = free_node;
443     
444     if( root == NULL ) return ENOSPC;
445
446     free_node = root->next;
447
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();
462
463     for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
464         root->u.dir.nodes[i]    = NULL;
465     
466     mte->root = (cyg_dir)root;
467     
468     return 0;
469 }
470
471 // -------------------------------------------------------------------------
472
473 static int testfs_umount    ( cyg_mtab_entry *mte )
474 {
475     testfs_node *root = (testfs_node *)mte->root;
476
477     // Non-empty filesystem, do not unmount
478     if( root->refcnt != 1 )
479         return EBUSY;
480
481     // Otherwise just return it to the free pool
482     root->next = free_node;
483     root->refcnt = -1;
484     free_node = root;
485
486     // Clear root pointer
487     mte->root = CYG_DIR_NULL;
488     
489     // That's all folks.
490     
491     return ENOERR;
492 }
493 // -------------------------------------------------------------------------
494
495 static int testfs_open     ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
496                           int mode,  cyg_file *file )
497 {
498     testfs_node *nd, *parent;
499     int err;
500     char name[TESTFS_NAMESIZE];
501     cyg_bool lastp;
502     
503     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
504
505     if( lastp && err == ENOENT && (mode & O_CREAT) )
506     {
507         int i;
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.
511
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 )
515                 break;
516
517         if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
518         
519         // Allocate a new node
520         nd = free_node;
521         if( nd == NULL ) return ENOSPC;
522         free_node = nd->next;
523
524         // Add to directory list
525         parent->u.dir.nodes[i] = nd;
526
527         parent->refcnt++;
528         
529         // Fill in details
530         nd->parent              = parent;
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();
543
544         for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
545             nd->u.file.data[i]  = NULL;
546
547         err = ENOERR;
548     }
549
550     if( err == ENOERR && (mode & O_TRUNC ) )
551     {
552         // Clean out any blocks in the file...
553
554         int i;
555         
556         for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
557         {
558             testfs_block *b = nd->u.file.data[i];
559             if( b != NULL )
560             {
561                 b->u.next = free_block;
562                 b->pos = -1;
563                 free_block = b;
564                 nd->u.file.data[i] = NULL;
565             }
566         }
567
568         nd->status.st_size = 0;
569     }
570     
571     if( err != ENOERR ) return err;
572
573     if( S_ISDIR(nd->status.st_mode) ) return EISDIR;
574
575     nd->refcnt++;       // Count successful open as a ref
576     
577     // Initialize the file object
578     
579     file->f_flag        |= mode & CYG_FILE_MODE_MASK;
580     file->f_type        = CYG_FILE_TYPE_FILE;
581     file->f_ops         = &testfs_fileops;
582     file->f_offset      = 0;
583     file->f_data        = (CYG_ADDRWORD)nd;
584     file->f_xops        = 0;
585     
586     return ENOERR;
587 }
588
589 // -------------------------------------------------------------------------
590
591 static int testfs_unlink   ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
592 {
593     testfs_node *nd, *parent;
594     int err;
595     char name[TESTFS_NAMESIZE];
596     cyg_bool lastp;
597    
598     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
599
600     if( err != ENOERR ) return err;
601
602     // Cannot unlink directories, use rmdir() instead
603     if( S_ISDIR(nd->status.st_mode) )
604         return EPERM;
605
606     err = testfs_delnode( nd );
607     
608     return err;
609 }
610
611 // -------------------------------------------------------------------------
612
613 static int testfs_mkdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
614 {
615     
616     testfs_node *nd, *parent;
617     int err;
618     char name[TESTFS_NAMESIZE];
619     cyg_bool lastp;
620     
621     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
622
623     if( lastp && err == ENOENT )
624     {
625         int i;
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
628         // it.
629
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 )
633                 break;
634
635         if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
636         
637         // Allocate a new node
638         nd = free_node;
639         if( nd == NULL ) return ENOSPC;
640         free_node = nd->next;
641
642         // Add to directory list
643         parent->u.dir.nodes[i] = nd;
644
645         parent->refcnt++;
646  
647         // Fill in details
648         nd->parent              = parent;
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();
661
662         for( i = 0; i < TESTFS_FILEBLOCKS; i++ )
663             nd->u.dir.nodes[i]  = NULL;
664         
665         err = ENOERR;
666     }
667
668     return err;
669 }
670
671 // -------------------------------------------------------------------------
672
673 static int testfs_rmdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
674 {
675     testfs_node *nd, *parent;
676     int err;
677     char name[TESTFS_NAMESIZE];
678     cyg_bool lastp;
679    
680     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
681
682     if( err != ENOERR ) return err;
683
684     // Check that it is a directory
685     if( !S_ISDIR(nd->status.st_mode) )
686         return EPERM;
687
688     err = testfs_delnode( nd );
689
690     return err;
691 }
692
693 // -------------------------------------------------------------------------
694
695 static int testfs_rename   ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1,
696                           cyg_dir dir2, const char *path2 )
697 {
698     testfs_node *nd1, *parent1;
699     testfs_node *nd2, *parent2;
700     int err;
701     char name1[TESTFS_NAMESIZE];
702     char name2[TESTFS_NAMESIZE];
703     cyg_bool lastp;
704     int i,j;
705     
706     err = testfs_find( (testfs_node *)dir1, path1, &nd1, &parent1, name1, &lastp );
707
708     if( err != ENOERR ) return err;
709
710     err = testfs_find( (testfs_node *)dir2, path2, &nd2, &parent2, name2, &lastp );
711
712     // Allow through renames to non-existent objects.
713     if( lastp && err == ENOENT )
714         err = ENOERR;
715     
716     if( err != ENOERR ) return err;
717
718     // Null rename, just return
719     if( nd1 == nd2 )
720         return ENOERR;
721     
722
723     // First deal with any node that is at the destination
724     if( nd2 )
725     {
726         // Check that we are renaming like-for-like
727
728         if( !S_ISDIR(nd1->status.st_mode) && S_ISDIR(nd2->status.st_mode) )
729             return EISDIR;
730
731         if( S_ISDIR(nd1->status.st_mode) && !S_ISDIR(nd2->status.st_mode) )
732             return ENOTDIR;
733
734         // Now delete the destination node.
735         err = testfs_delnode( nd2 );
736         if( err != ENOERR ) return err;
737     }
738
739     // Now we know that there is no clashing node at the destination.
740     // Move the node over and change its name.
741
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 )
745             break;
746
747     if( i == TESTFS_FILEBLOCKS ) return ENOSPC;
748
749     // Now remove node from old parent.
750     for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
751         if( parent1->u.dir.nodes[j] == nd1 )
752         {
753             parent1->u.dir.nodes[j] = NULL;
754             break;
755         }
756
757     parent1->refcnt--;
758
759     // Add to directory list
760     parent2->u.dir.nodes[i] = nd1;
761     parent2->refcnt++;
762     nd1->parent = parent2;
763     
764     // And give it a new name.
765     mystrcpy( nd1->name, name2 );
766     
767     return err;
768 }
769
770 // -------------------------------------------------------------------------
771
772 static int testfs_link   ( cyg_mtab_entry *mte, cyg_dir dir1, const char *path1,
773                           cyg_dir dir2, const char *path2, int type )
774 {
775     // The data structures of this file system do not support the
776     // creation of links.
777     
778     return ENOSYS;
779 }
780
781 // -------------------------------------------------------------------------
782
783 static int testfs_opendir  ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
784                           cyg_file *file )
785 {
786     testfs_node *nd, *parent;
787     int err;
788     char name[TESTFS_NAMESIZE];
789     cyg_bool lastp;
790     
791     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
792
793     if( err != ENOERR ) return err;
794
795     if( !S_ISDIR(nd->status.st_mode) )
796         return ENOTDIR;
797
798     nd->refcnt++;       // Count successful open as a ref
799     
800     // Initialize the file object
801     
802     file->f_type        = CYG_FILE_TYPE_FILE;
803     file->f_ops         = &testfs_dirops;
804     file->f_offset      = 0;
805     file->f_data        = (CYG_ADDRWORD)nd;
806     file->f_xops        = 0;
807     
808     return ENOERR;
809 }
810
811 // -------------------------------------------------------------------------
812
813 static int testfs_chdir    ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
814                              cyg_dir *dir_out )
815 {
816     if( dir_out != NULL )
817     {
818         // This is a request to get a new directory pointer in
819         // *dir_out.
820
821         testfs_node *nd, *parent;
822         int err;
823         char name[TESTFS_NAMESIZE];
824         cyg_bool lastp;
825     
826         err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
827
828         if( err != ENOERR ) return err;
829
830         if( !S_ISDIR(nd->status.st_mode) )
831             return ENOTDIR;
832         
833         // Increment ref count to keep this directory in existent
834         // while it is the current cdir.
835         nd->refcnt++;
836
837         // Pass it out
838         *dir_out = (cyg_dir)nd;
839     }
840     else
841     {
842         // If no output dir is required, this means that the mte and
843         // dir arguments are the current cdir setting and we should
844         // forget this fact.
845
846         testfs_node *nd = (testfs_node *)dir;
847
848         // Just decrement reference count.
849         nd->refcnt--;
850     }
851         
852     return ENOERR;
853 }
854
855 // -------------------------------------------------------------------------
856
857 static int testfs_stat     ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
858                           struct stat *buf)
859 {
860     testfs_node *nd, *parent;
861     int err;
862     char name[TESTFS_NAMESIZE];
863     cyg_bool lastp;
864     
865     err = testfs_find( (testfs_node *)dir, path, &nd, &parent, name, &lastp );
866
867     if( err != ENOERR ) return err;
868
869     *buf = nd->status;
870     
871     return err;
872 }
873
874 // -------------------------------------------------------------------------
875
876 static int testfs_getinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
877                           int key, void *buf, int len )
878 {
879     return ENOSYS;
880 }
881
882 // -------------------------------------------------------------------------
883
884 static int testfs_setinfo  ( cyg_mtab_entry *mte, cyg_dir dir, const char *path,
885                           int key, void *buf, int len )
886 {
887     return ENOSYS;
888 }
889
890
891 //==========================================================================
892 // File operations
893
894
895 // -------------------------------------------------------------------------
896
897 static int testfs_fo_read      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
898 {
899     testfs_node *nd = (testfs_node *)fp->f_data;
900     int i;
901     off_t pos = fp->f_offset;
902
903     for( i = 0; i < uio->uio_iovcnt; i++ )
904     {
905         cyg_iovec *iov = &uio->uio_iov[i];
906         char *buf = (char *)iov->iov_base;
907         off_t len = iov->iov_len;
908
909         while( len > 0 && pos < nd->status.st_size )
910         {
911             testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE];
912             off_t l = len;
913             off_t bpos = pos%TESTFS_BLOCKSIZE;
914             
915             // If there is no block in that pos, we have reached
916             // the end of the file.
917             if( b == NULL ) return ENOERR;
918
919             // adjust size to this block
920             if( l > (b->size-bpos) )
921                 l = (b->size-bpos);
922
923             // copy data out
924             memcpy( buf, &b->data[bpos], l );
925
926             uio->uio_resid -= l;
927             len -= l;
928             buf += l;
929             pos += l;
930
931             // keep offset up to date incase of errors
932             fp->f_offset = pos;
933         }
934     }
935     
936     return ENOERR;
937 }
938
939 // -------------------------------------------------------------------------
940
941 static int testfs_fo_write     (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
942 {
943     testfs_node *nd = (testfs_node *)fp->f_data;
944     int i;
945     off_t pos = fp->f_offset;
946     
947     // Check we are not at end of allowed max file size
948     if( pos >= TESTFS_FILESIZE_MAX )
949         return EFBIG;
950
951     // Check that pos is within current file size, or at the very end.
952     if( pos < 0 || pos > nd->status.st_size )
953         return EINVAL;
954
955     // Now loop over the iovecs until they are all done, or
956     // we get an error.
957     for( i = 0; i < uio->uio_iovcnt; i++ )
958     {
959         cyg_iovec *iov = &uio->uio_iov[i];
960         char *buf = (char *)iov->iov_base;
961         off_t len = iov->iov_len;
962
963         while( len > 0 )
964         {
965             testfs_block *b = nd->u.file.data[pos/TESTFS_BLOCKSIZE];
966             off_t l = len;
967             off_t bpos = pos%TESTFS_BLOCKSIZE;
968             
969             // If there is no block in that pos, allocate one
970             // and initialize it
971             if( b == NULL )
972             {
973                 b = free_block;
974                 if( b == NULL ) return ENOSPC;
975                 free_block = b->u.next;
976                 nd->u.file.data[pos/TESTFS_BLOCKSIZE] = b;
977                 b->u.file = nd;
978                 b->pos = pos;
979                 b->size = 0;
980             }
981
982             // adjust size to this block
983             if( l > (TESTFS_BLOCKSIZE-bpos) )
984                 l = (TESTFS_BLOCKSIZE-bpos);
985
986             // copy data in
987             memcpy( &b->data[bpos], buf, l );
988
989             // adjust buffer info
990             if( b->size < bpos+l )
991                 b->size = bpos+l;
992             
993             uio->uio_resid -= l;
994             len -= l;
995             buf += l;
996             pos += l;
997
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;
1002             fp->f_offset = pos;
1003
1004             if( pos >= TESTFS_FILESIZE_MAX )
1005                 return EFBIG;
1006         }
1007     }
1008     
1009     return ENOERR;
1010 }
1011
1012 // -------------------------------------------------------------------------
1013
1014 static int testfs_fo_lseek     (struct CYG_FILE_TAG *fp, off_t *apos, int whence )
1015 {
1016     testfs_node *nd = (testfs_node *)fp->f_data;
1017     off_t pos = *apos;
1018
1019     switch( whence )
1020     {
1021     case SEEK_SET:
1022         // we are already where we want to be.
1023         break;
1024
1025     case SEEK_CUR:
1026         pos += fp->f_offset;
1027         break;
1028
1029     case SEEK_END:
1030         pos += nd->status.st_size;
1031         break;
1032
1033     default:
1034         return EINVAL;
1035     }
1036     
1037     // Check that pos is within current file size, or at the very end.
1038     if( pos < 0 || pos > nd->status.st_size )
1039         return EINVAL;
1040
1041     // All OK, set fp offset.
1042     *apos = fp->f_offset = pos;
1043     
1044     return ENOERR;
1045 }
1046
1047 // -------------------------------------------------------------------------
1048
1049 static int testfs_fo_ioctl     (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
1050                              CYG_ADDRWORD data)
1051 {
1052     return ENOSYS;
1053 }
1054
1055 // -------------------------------------------------------------------------
1056
1057 static int testfs_fo_fsync     (struct CYG_FILE_TAG *fp, int mode )
1058 {
1059     // Nothing to do
1060     return ENOERR;
1061 }
1062
1063 // -------------------------------------------------------------------------
1064
1065 static int testfs_fo_close     (struct CYG_FILE_TAG *fp)
1066 {
1067     testfs_node *nd = (testfs_node *)fp->f_data;
1068
1069     nd->refcnt--;       // remove open count
1070
1071     fp->f_data = 0;     // clear data pointer
1072     
1073     return ENOERR;
1074 }
1075
1076 // -------------------------------------------------------------------------
1077
1078 static int testfs_fo_fstat     (struct CYG_FILE_TAG *fp, struct stat *buf )
1079 {
1080     testfs_node *nd = (testfs_node *)fp->f_data;
1081
1082     *buf = nd->status;    
1083     
1084     return ENOERR;
1085 }
1086
1087 // -------------------------------------------------------------------------
1088
1089 static int testfs_fo_getinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
1090 {
1091     return ENOERR;
1092 }
1093
1094 // -------------------------------------------------------------------------
1095
1096 static int testfs_fo_setinfo   (struct CYG_FILE_TAG *fp, int key, void *buf, int len )
1097 {
1098     return ENOERR;
1099 }
1100
1101
1102 //==========================================================================
1103 // Directory operations
1104
1105 static int testfs_fo_dirread      (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
1106 {
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;
1112
1113     // End of directory
1114     if( pos >= TESTFS_FILEBLOCKS )
1115         return ENOERR;
1116
1117     if( len < sizeof(struct dirent) )
1118         return EINVAL;
1119     
1120     for( ; pos < TESTFS_FILEBLOCKS; pos++ )
1121         if( nd->u.dir.nodes[pos] != NULL )
1122         {
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);
1126             break;
1127         }
1128
1129     fp->f_offset = pos+1;
1130     
1131     return ENOERR;
1132 }
1133
1134 // -------------------------------------------------------------------------
1135
1136 static int testfs_fo_dirlseek     (struct CYG_FILE_TAG *fp, off_t *pos, int whence )
1137 {
1138     if( whence != SEEK_SET || *pos != 0)
1139         return EINVAL;
1140
1141     *pos = fp->f_offset = 0;
1142     
1143     return ENOERR;
1144 }
1145
1146 //==========================================================================
1147 // Filesystem dump
1148 // Dumps out the node and block arrays in a readable format, and does
1149 // a little consistency checking as it goes.
1150
1151 void testfs_dump(void)
1152 {
1153     int errors = 0;
1154     int i;
1155     char *indent = "\n                      |";
1156
1157     diag_printf("Nodes:\n");
1158     for( i = 0; i < TESTFS_NFILE; i++ )
1159     {
1160         testfs_node *nd = &node[i];
1161
1162         diag_printf("%3d : ",i);
1163         if( nd->refcnt < 0 )
1164             diag_printf("<free>");
1165         else if( !S_ISDIR(nd->status.st_mode) )
1166         {
1167             // Regular file
1168             int j;
1169             diag_printf("f %8s %4ld |",nd->name,
1170                         (unsigned long)nd->status.st_size);
1171             for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
1172             {
1173                 testfs_block *b = nd->u.file.data[j];
1174                 if( b != NULL )
1175                 {
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 )
1181                     {
1182                         errors++;
1183                         diag_printf("!");
1184                     }
1185                 }
1186             }
1187         }
1188         else
1189         {
1190             // Directory
1191             int j;
1192             int rc = 1;
1193             diag_printf("d %8s      |",nd->name);
1194
1195             for( j = 0; j < TESTFS_FILEBLOCKS; j++ )
1196             {
1197                 testfs_node *n = nd->u.dir.nodes[j];
1198                 if( n != NULL )
1199                 {
1200                     if( j > 0 && (j%4) == 0 )
1201                         diag_printf(indent);
1202                     diag_printf(" %3d[%7s]",(int) (n-node),n->name);
1203                     rc++;
1204                 }
1205             }
1206
1207             if( nd->refcnt != rc )
1208             {
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)");
1212             }
1213         }
1214
1215         diag_printf("\n");
1216     }
1217
1218     diag_printf("Blocks:\n");
1219
1220     for( i = 0; i < TESTFS_NBLOCK; i++ )
1221     {
1222         testfs_block *b = &block[i];
1223
1224         diag_printf("%3d :",i);
1225         if( b->pos == -1 )
1226             diag_printf(" <free>");
1227         else
1228         {
1229             int j;
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++ )
1233             {
1234                 if( nd->u.file.data[j] == b )
1235                     break;
1236             }
1237             if( j == TESTFS_FILEBLOCKS )
1238             {
1239                 errors++;
1240                 diag_printf(" block not in file!");
1241             }
1242         }
1243         diag_printf("\n");
1244     }
1245
1246     if( errors != 0 )
1247         diag_printf("%d errors detected\n",errors);
1248 }
1249
1250 // -------------------------------------------------------------------------
1251 // EOF testfs.c