]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/fs/fat/v2_0/src/fatfs.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / fs / fat / v2_0 / src / fatfs.c
1 //==========================================================================
2 //
3 //      fatfs.c
4 //
5 //      FAT 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, 2003, 2004 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):           Savin Zlobec <savin@elatec.si> (based on ramfs.c)
44 // Original data:       nickg
45 // Date:                2003-06-29
46 // Purpose:             FAT file system
47 // Description:         This is a FAT filesystem for eCos. 
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <pkgconf/system.h>
54 #include <pkgconf/hal.h>
55 #include <pkgconf/io_fileio.h>
56 #include <pkgconf/fs_fat.h>
57
58 #include <cyg/infra/cyg_type.h>   
59 #include <cyg/infra/cyg_trac.h>        // tracing macros
60 #include <cyg/infra/cyg_ass.h>         // assertion macros
61
62 #include <unistd.h>
63 #include <sys/types.h>
64 #include <fcntl.h>
65 #include <sys/stat.h>
66 #include <errno.h>
67 #include <dirent.h>
68
69 #include <stdlib.h>
70 #include <string.h>
71
72 #include <cyg/infra/diag.h>
73 #include <cyg/fileio/fileio.h>
74 #include <cyg/io/io.h>
75 #include <blib/blib.h>
76 #include <cyg/fs/fatfs.h>
77
78 #include "fatfs.h"
79
80 //==========================================================================
81 // Tracing support defines 
82
83 #ifdef FATFS_TRACE_FS_OP
84 # define TFS 1
85 #else
86 # define TFS 0
87 #endif
88
89 #ifdef FATFS_TRACE_FILE_OP
90 # define TFO 1
91 #else
92 # define TFO 0
93 #endif
94
95 //==========================================================================
96 // Forward definitions
97
98 // Filesystem operations
99 static int fatfs_mount  (cyg_fstab_entry *fste, cyg_mtab_entry *mte);
100 static int fatfs_umount (cyg_mtab_entry *mte);
101 static int fatfs_open   (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
102                          int mode, cyg_file *fte);
103 static int fatfs_unlink (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
104 static int fatfs_mkdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
105 static int fatfs_rmdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
106 static int fatfs_rename (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
107                          cyg_dir dir2, const char *name2 );
108 static int fatfs_link   (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
109                          cyg_dir dir2, const char *name2, int type );
110 static int fatfs_opendir(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
111                          cyg_file *fte );
112 static int fatfs_chdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
113                          cyg_dir *dir_out );
114 static int fatfs_stat   (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
115                          struct stat *buf);
116 static int fatfs_getinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
117                          int key, void *buf, int len );
118 static int fatfs_setinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
119                          int key, void *buf, int len );
120
121 // File operations
122 static int fatfs_fo_read   (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
123 static int fatfs_fo_write  (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
124 static int fatfs_fo_lseek  (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
125 static int fatfs_fo_ioctl  (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
126                             CYG_ADDRWORD data);
127 static int fatfs_fo_fsync  (struct CYG_FILE_TAG *fp, int mode );        
128 static int fatfs_fo_close  (struct CYG_FILE_TAG *fp);
129 static int fatfs_fo_fstat  (struct CYG_FILE_TAG *fp, struct stat *buf );
130 static int fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, 
131                             void *buf, int len );
132 static int fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, 
133                             void *buf, int len );
134
135 // Directory operations
136 static int fatfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
137 static int fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence);
138
139 //==========================================================================
140 // Filesystem table entries
141
142 // -------------------------------------------------------------------------
143 // Fstab entry.
144
145 FSTAB_ENTRY(fatfs_fste, "fatfs", 0,
146             CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
147             fatfs_mount,
148             fatfs_umount,
149             fatfs_open,
150             fatfs_unlink,
151             fatfs_mkdir,
152             fatfs_rmdir,
153             fatfs_rename,
154             fatfs_link,
155             fatfs_opendir,
156             fatfs_chdir,
157             fatfs_stat,
158             fatfs_getinfo,
159             fatfs_setinfo);
160
161 // -------------------------------------------------------------------------
162 // File operations.
163
164 static cyg_fileops fatfs_fileops =
165 {
166     fatfs_fo_read,
167     fatfs_fo_write,
168     fatfs_fo_lseek,
169     fatfs_fo_ioctl,
170     cyg_fileio_seltrue,
171     fatfs_fo_fsync,
172     fatfs_fo_close,
173     fatfs_fo_fstat,
174     fatfs_fo_getinfo,
175     fatfs_fo_setinfo
176 };
177
178 // -------------------------------------------------------------------------
179 // Directory file operations.
180
181 static cyg_fileops fatfs_dirops =
182 {
183     fatfs_fo_dirread,
184     (cyg_fileop_write *)cyg_fileio_enosys,
185     fatfs_fo_dirlseek,
186     (cyg_fileop_ioctl *)cyg_fileio_enosys,
187     cyg_fileio_seltrue,
188     (cyg_fileop_fsync *)cyg_fileio_enosys,
189     fatfs_fo_close,
190     (cyg_fileop_fstat *)cyg_fileio_enosys,
191     (cyg_fileop_getinfo *)cyg_fileio_enosys,
192     (cyg_fileop_setinfo *)cyg_fileio_enosys
193 };
194
195 // -------------------------------------------------------------------------
196 // Directory search data
197 // Parameters for a directory search. The fields of this structure are
198 // updated as we follow a pathname through the directory tree.
199
200 typedef struct fatfs_dirsearch_s
201 {
202     fatfs_disk_t        *disk;     // Disk info 
203     fatfs_node_t        *dir;      // Directory to search
204     const char          *path;     // Path to follow
205     fatfs_node_t        *node;     // Node found
206     const char          *name;     // Last name fragment used
207     int                  namelen;  // Name fragment length
208     cyg_bool             last;     // Last name in path?
209 } fatfs_dirsearch_t;
210
211 // -------------------------------------------------------------------------
212 // FATFS file descriptor data
213
214 typedef struct fatfs_fd_s
215 {
216     fatfs_node_t      *node;
217     fatfs_data_pos_t   pos;
218 } fatfs_fd_t;
219
220 static fatfs_fd_t  fatfs_fds_base[CYGNUM_FILEIO_NFD];
221 static fatfs_fd_t *fatfs_fds_pool[CYGNUM_FILEIO_NFD];
222 static cyg_uint32  fatfs_fds_free_cnt;
223
224 //==========================================================================
225
226 #if TFS
227 static void 
228 print_disk_info(fatfs_disk_t *disk)
229 {
230     diag_printf("FAT: sector size:        %u\n", disk->sector_size);
231     diag_printf("FAT: cluster size:       %u\n", disk->cluster_size);
232     diag_printf("FAT: FAT table position: %u\n", disk->fat_tbl_pos);
233     diag_printf("FAT: FAT table num ent:  %u\n", disk->fat_tbl_nents);
234     diag_printf("FAT: FAT table size:     %u\n", disk->fat_tbl_size);
235     diag_printf("FAT: FAT tables num:     %u\n", disk->fat_tbls_num);
236     diag_printf("FAT: FAT root dir pos:   %u\n", disk->fat_root_dir_pos);
237     diag_printf("FAT: FAT root dir nents: %u\n", disk->fat_root_dir_nents);
238     diag_printf("FAT: FAT root dir size:  %u\n", disk->fat_root_dir_size);
239     diag_printf("FAT: FAT data position:  %u\n", disk->fat_data_pos);
240 }
241 #endif
242
243 static void
244 init_fatfs_fds(void)
245 {
246     static bool initialized = false;
247
248     int i;
249
250     if (initialized)
251         return;
252     
253     initialized = true;
254     
255     for (i = 0; i < CYGNUM_FILEIO_NFD; i++)
256     {
257         fatfs_fds_pool[i] = &fatfs_fds_base[i];    
258     }
259     fatfs_fds_free_cnt = i;
260 }
261
262 static fatfs_fd_t *
263 alloc_fatfs_fd(fatfs_disk_t *disk, fatfs_node_t *node)
264 {
265     fatfs_fd_t *fd = NULL;
266
267     if (fatfs_fds_free_cnt > 0)
268     {
269         fd = fatfs_fds_pool[--fatfs_fds_free_cnt];
270
271         fd->node = node;
272         fatfs_initpos(disk, &node->dentry, &fd->pos);
273     }
274     
275     return fd;
276 }
277
278 static void
279 free_fatfs_fd(fatfs_fd_t *fd)
280 {
281     fatfs_fds_pool[fatfs_fds_free_cnt++] = fd;
282 }
283
284 static void
285 init_dirsearch(fatfs_dirsearch_t *ds,
286                fatfs_disk_t      *disk, 
287                fatfs_node_t      *dir,
288                const char        *name)
289 {
290     ds->disk = disk;
291     
292     if (NULL == dir)
293         ds->dir = disk->root;
294     else
295         ds->dir = dir;
296     
297     ds->path    = name;
298     ds->node    = ds->dir;
299     ds->namelen = 0;
300     ds->last    = false;
301 }
302
303 static int
304 find_direntry(fatfs_dirsearch_t *ds)
305 {
306     fatfs_dir_entry_t  dentry;
307     fatfs_data_pos_t   pos;
308     int                err;
309
310     CYG_TRACE1(TFS, "searching for dir entry '%s'", ds->name);
311
312     // Check for '.' 
313
314     if (!strncmp(".", ds->name, ds->namelen))
315     {
316         ds->node = ds->dir;
317         return ENOERR;
318     }
319    
320     // Check the cache
321     
322     ds->node = fatfs_node_find(ds->disk, 
323                                ds->name, 
324                                ds->namelen, 
325                                ds->dir->dentry.cluster);
326
327     if (ds->node != NULL)
328     {
329         // Dir entry found in cache
330         
331         CYG_TRACE0(TFS, "dir entry found in cache");
332
333         fatfs_node_touch(ds->disk, ds->node);
334         return ENOERR;
335     }
336
337     // Dir entry not in cache - search the current dir
338
339     fatfs_initpos(ds->disk, &ds->dir->dentry, &pos);
340
341     while (true)
342     {  
343         // Read next dir entry 
344         
345         err = fatfs_read_dir_entry(ds->disk, &ds->dir->dentry, &pos, &dentry);
346         if (err != ENOERR)
347             return (err == EEOF ? ENOERR : err);
348
349         // Compare filenames
350     
351         if ('\0' == dentry.filename[ds->namelen] &&
352                0 == strncasecmp(dentry.filename, ds->name, ds->namelen))
353         {
354             // Dir entry found - allocate new node and return
355
356             CYG_TRACE0(TFS, "dir entry found");
357             
358             if (0 == strncmp(ds->name, "..", ds->namelen)) 
359             {
360                 fatfs_dir_entry_t _dentry;
361                 fatfs_data_pos_t  _pos;
362  
363                 if (0 == dentry.cluster) 
364                 {
365                     ds->node = ds->disk->root;
366                     return ENOERR;
367                 }
368              
369                 fatfs_initpos(ds->disk, &dentry, &_pos);
370                 while (true)
371                 {
372                     err = fatfs_read_dir_entry(ds->disk, &dentry, &_pos, &_dentry);
373                     if (err != ENOERR)
374                         return err;
375                     if (0 == strcmp(".", _dentry.filename))
376                         break;
377                 }
378  
379                 ds->node = fatfs_node_find(ds->disk, 
380                                            _dentry.filename, 
381                                            strlen(_dentry.filename),
382                                            _dentry.parent_cluster); 
383                 
384                 if (NULL != ds->node)
385                     fatfs_node_touch(ds->disk, ds->node);
386                 else 
387                     ds->node = fatfs_node_alloc(ds->disk, &_dentry);
388                                 
389                 if (NULL == ds->node)
390                     return EMFILE;
391                                 
392                 return ENOERR;
393             }
394             else 
395                 ds->node = fatfs_node_alloc(ds->disk, &dentry);
396
397             if (NULL == ds->node)
398                 return EMFILE;
399
400             return ENOERR;
401         }
402     }
403 }
404
405 static int 
406 find_entry(fatfs_dirsearch_t *ds)
407 {
408     const char  *name     = ds->path;
409     const char  *n        = name;
410     char         namelen  = 0;
411     int          err;
412     
413     if( !S_ISDIR(ds->dir->dentry.mode) )
414     {
415         CYG_TRACE1(TFS, "entry '%s' not dir", ds->dir->dentry.filename);
416         return ENOTDIR;
417     }
418     
419     // Isolate the next element of the path name
420     while (*n != '\0' && *n != '/')
421         n++, namelen++;
422
423     // If we terminated on a NUL, set last flag
424     if (*n == '\0')
425         ds->last = true;
426
427     // Update name in dirsearch object
428     ds->name    = name;
429     ds->namelen = namelen;
430
431     err = find_direntry(ds);
432     if (err != ENOERR)
433         return err;
434
435     CYG_TRACE2(TFS, "entry '%s' %s", name, (ds->node ? "found" : "not found"));
436     
437     if (ds->node != NULL)
438        return ENOERR;
439     else
440        return ENOENT; 
441 }
442
443 static int
444 fatfs_find(fatfs_dirsearch_t *ds)
445 {
446     int err;
447
448     CYG_TRACE1(TFS, "find path='%s'", ds->path);
449     
450     // Short circuit empty paths
451     if (*(ds->path) == '\0')
452         return ENOERR;
453     
454     // Iterate down directory tree until we find the object we want
455     for(;;)
456     {
457         err = find_entry(ds);
458         
459         if (err != ENOERR)
460             return err;
461         
462         if (ds->last)
463         {
464             CYG_TRACE0(TFS, "entry found");
465             return ENOERR;
466         }
467
468         // Update dirsearch object to search next directory
469         ds->dir   = ds->node;
470         ds->path += ds->namelen;
471         
472         // Skip dirname separators
473         if (*(ds->path) == '/') ds->path++;
474         
475         CYG_TRACE1(TFS, "find path to go='%s'", ds->path);
476     }
477 }
478
479 //==========================================================================
480 // Filesystem operations
481
482 // -------------------------------------------------------------------------
483 // fatfs_mount()
484 // Process a mount request. This mainly creates a root for the
485 // filesystem.
486
487 static int 
488 fatfs_mount(cyg_fstab_entry *fste, cyg_mtab_entry *mte)
489 {
490     cyg_io_handle_t     dev_h;
491     fatfs_disk_t       *disk;
492     fatfs_dir_entry_t   root_dentry;
493     Cyg_ErrNo           err;
494
495     CYG_TRACE2(TFS, "mount fste=%p mte=%p", fste, mte);
496
497     init_fatfs_fds();
498     
499     CYG_TRACE1(TFS, "looking up disk device '%s'", mte->devname);
500     
501     err = cyg_io_lookup(mte->devname, &dev_h);
502     if (err != ENOERR)
503         return err;
504
505     disk = (fatfs_disk_t *)malloc(sizeof(fatfs_disk_t));
506     if (NULL == disk)
507         return ENOMEM;
508         
509     CYG_TRACE0(TFS, "initializing block cache"); 
510
511     disk->bcache_mem = (cyg_uint8 *)malloc(CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE);
512     if (NULL == disk->bcache_mem)
513     {
514         free(disk);
515         return ENOMEM;
516     }
517     // FIXME: get block size from disk device
518     err = cyg_blib_io_create(dev_h, disk->bcache_mem, 
519             CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE, 512, &disk->blib);
520     if (err != ENOERR)
521     {
522         free(disk->bcache_mem);
523         free(disk);
524         return err;
525     }
526     
527     disk->dev_h = dev_h;
528
529     CYG_TRACE0(TFS, "initializing disk");
530     
531     err = fatfs_init(disk);
532     if (err != ENOERR)
533     {
534         cyg_blib_delete(&disk->blib);
535         free(disk->bcache_mem);
536         free(disk);
537         return err;
538     }
539    
540 #if TFS    
541     print_disk_info(disk);
542 #endif
543
544     CYG_TRACE0(TFS, "initializing node cache");
545
546     fatfs_node_cache_init(disk);
547     
548     CYG_TRACE0(TFS, "initializing root node");
549     
550     fatfs_get_root_dir_entry(disk, &root_dentry);
551     
552     disk->root = fatfs_node_alloc(disk, &root_dentry);
553
554     fatfs_node_ref(disk, disk->root);
555     
556     mte->root = (cyg_dir)disk->root;
557     mte->data = (CYG_ADDRWORD)disk;
558
559     CYG_TRACE0(TFS, "disk mounted");
560
561     return ENOERR;
562 }
563
564 // -------------------------------------------------------------------------
565 // fatfs_umount()
566 // Unmount the filesystem. This will currently only succeed if the
567 // filesystem is empty.
568
569 static int
570 fatfs_umount(cyg_mtab_entry *mte)
571 {
572     fatfs_disk_t  *disk  = (fatfs_disk_t *) mte->data;
573     fatfs_node_t  *root  = (fatfs_node_t *) mte->root;
574
575     CYG_TRACE3(TFS, "umount mte=%p %d live nodes %d dead nodes", 
576                     mte, fatfs_get_live_node_count(disk), 
577                     fatfs_get_dead_node_count(disk));
578
579     if (root->refcnt > 1)
580         return EBUSY;
581     
582     if (fatfs_get_live_node_count(disk) != 1)
583         return EBUSY;
584
585     fatfs_node_unref(disk, root);
586     fatfs_node_cache_flush(disk);
587     // FIXME: cache delete can fail if cache can't be synced
588     cyg_blib_delete(&disk->blib); 
589     free(disk->bcache_mem);
590     free(disk);
591     
592     mte->root = CYG_DIR_NULL;
593     mte->data = (CYG_ADDRWORD) NULL;
594     
595     CYG_TRACE0(TFS, "disk umounted");
596     
597     return ENOERR;
598 }
599
600 // -------------------------------------------------------------------------
601 // fatfs_open()
602 // Open a file for reading or writing.
603
604 static int 
605 fatfs_open(cyg_mtab_entry *mte,
606            cyg_dir         dir, 
607            const char     *name,
608            int             mode,  
609            cyg_file       *file)
610 {
611     fatfs_disk_t       *disk = (fatfs_disk_t *) mte->data;
612     fatfs_node_t       *node = NULL;
613     fatfs_fd_t         *fd;
614     fatfs_dirsearch_t   ds;
615     int                 err;
616
617     CYG_TRACE5(TFS, "open mte=%p dir=%p name='%s' mode=%d file=%p", 
618                     mte, dir, name, mode, file);
619
620     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
621
622     err = fatfs_find(&ds);
623
624     if (err == ENOENT)
625     {
626         if (ds.last && (mode & O_CREAT))
627         {
628             fatfs_dir_entry_t new_file_dentry;
629             
630             // No node there, if the O_CREAT bit is set then we must
631             // create a new one. The dir and name fields of the dirsearch
632             // object will have been updated so we know where to put it.
633
634             CYG_TRACE1(TFS, "creating new file '%s'", name); 
635
636             err = fatfs_create_file(disk, 
637                                     &ds.dir->dentry, 
638                                     ds.name, 
639                                     ds.namelen, 
640                                     &new_file_dentry);
641             if (err != ENOERR)
642                 return err;
643
644             node = fatfs_node_alloc(disk, &new_file_dentry);
645             if (NULL == node)
646                 return EMFILE;
647            
648             // Update directory times
649             ds.dir->dentry.atime =
650             ds.dir->dentry.mtime = cyg_timestamp();
651             
652             err = ENOERR;
653         }
654     }
655     else if (err == ENOERR)
656     {
657         // The node exists. If the O_CREAT and O_EXCL bits are set, we
658         // must fail the open
659
660         if ((mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
661             err = EEXIST;
662         else
663             node = ds.node;
664     }
665
666     if (err == ENOERR && (mode & O_TRUNC))
667     {
668         // If the O_TRUNC bit is set we must clean out the file data
669         CYG_TRACE0(TFS, "truncating file"); 
670         fatfs_trunc_file(disk, &node->dentry);
671     }
672
673     if (err != ENOERR)
674         return err;
675     
676     if (S_ISDIR(node->dentry.mode))
677         return EISDIR;
678
679 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
680     // if the file is read only and is opened for writing
681     // fail with permission error
682     if (S_FATFS_ISRDONLY(node->dentry.attrib) && (mode & O_WRONLY))
683         return EACCES;
684 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
685
686     // Allocate file object private data and
687     // make a reference to this file node
688
689     fd = alloc_fatfs_fd(disk, node);
690     if (NULL == fd)
691         return EMFILE;
692
693     fatfs_node_ref(disk, node);
694
695     // Initialize the file object
696
697     if (mode & O_APPEND)
698         fatfs_setpos(disk, &node->dentry, &fd->pos, node->dentry.size);  
699     
700     file->f_flag   |= mode & CYG_FILE_MODE_MASK;
701     file->f_type    = CYG_FILE_TYPE_FILE;
702     file->f_ops     = &fatfs_fileops;
703     file->f_offset  = (mode & O_APPEND) ? node->dentry.size : 0;
704     file->f_data    = (CYG_ADDRWORD) fd;
705     file->f_xops    = 0;
706
707     return ENOERR;
708 }
709
710 // -------------------------------------------------------------------------
711 // fatfs_unlink()
712 // Remove a file link from its directory.
713
714 static int 
715 fatfs_unlink(cyg_mtab_entry *mte, 
716              cyg_dir         dir, 
717              const char     *name)
718 {
719     fatfs_disk_t       *disk = (fatfs_disk_t *) mte->data;
720     fatfs_dirsearch_t   ds;
721     int                 err;
722
723     CYG_TRACE3(TFS, "unlink mte=%p dir=%p name='%s'", mte, dir, name);
724
725     init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
726
727     err = fatfs_find(&ds);
728
729     if (err != ENOERR)
730         return err;
731
732     if (ds.node->refcnt > 0)
733         return EBUSY;
734     
735 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
736     // if the file is read only fail with permission error
737     if (S_FATFS_ISRDONLY(ds.node->dentry.attrib))
738         return EPERM;
739 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
740
741     err = fatfs_delete_file(disk, &ds.node->dentry);
742     if (err == ENOERR)
743         fatfs_node_free(disk, ds.node);
744     
745     return err;
746 }
747
748 // -------------------------------------------------------------------------
749 // fatfs_mkdir()
750 // Create a new directory.
751
752 static int
753 fatfs_mkdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name)
754 {
755     fatfs_disk_t       *disk = (fatfs_disk_t *) mte->data;
756     fatfs_dirsearch_t   ds;
757     int                 err;
758     
759     CYG_TRACE3(TFS, "mkdir mte=%p dir=%p name='%s'", mte, dir, name);
760
761     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
762
763     err = fatfs_find(&ds);
764
765     if (err == ENOENT)
766     {
767         if (ds.last)
768         {
769             fatfs_dir_entry_t new_dir_dentry;
770             
771             // The entry does not exist, and it is the last element in
772             // the pathname, so we can create it here
773
774             err = fatfs_create_dir(disk, 
775                                    &ds.dir->dentry, 
776                                    ds.name, 
777                                    ds.namelen, 
778                                    &new_dir_dentry);
779             if (err != ENOERR)
780                 return err;
781             
782             fatfs_node_alloc(disk, &new_dir_dentry);
783
784             return ENOERR;
785         }
786     } 
787     else if (err == ENOERR)
788     {
789         return EEXIST;
790     }
791     
792     return err;
793 }
794
795 // -------------------------------------------------------------------------
796 // fatfs_rmdir()
797 // Remove a directory.
798
799 static int 
800 fatfs_rmdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name)
801 {
802     fatfs_disk_t      *disk = (fatfs_disk_t *) mte->data;
803     fatfs_dirsearch_t  ds;
804     int                err;
805     fatfs_node_t      *node;
806
807     CYG_TRACE3(TFS, "rmdir mte=%p dir=%p name='%s'", mte, dir, name);
808
809     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
810
811     err = fatfs_find(&ds);
812
813     if (err != ENOERR)
814         return err;
815   
816     if (!S_ISDIR(ds.node->dentry.mode))
817         return EPERM;
818  
819     if (ds.node->refcnt > 0)
820         return EBUSY;
821     
822     err = fatfs_delete_file(disk, &ds.node->dentry);
823     if (err == ENOERR)
824     {
825         node = fatfs_node_find( disk, ".", 1, ds.node->dentry.cluster );
826         if (node != NULL)
827              fatfs_node_free(disk, node);
828
829         node = fatfs_node_find( disk, "..", 2, ds.node->dentry.cluster );
830         if (node != NULL)
831             fatfs_node_free(disk, node);
832
833         fatfs_node_free(disk, ds.node);
834     }
835     return err;
836 }
837
838 // -------------------------------------------------------------------------
839 // fatfs_rename()
840 // Rename a file/dir.
841
842 static int 
843 fatfs_rename(cyg_mtab_entry *mte, 
844              cyg_dir         dir1, 
845              const char     *name1,
846              cyg_dir         dir2, 
847              const char     *name2)
848 {
849     fatfs_disk_t       *disk = (fatfs_disk_t *) mte->data;
850     fatfs_dirsearch_t   ds1, ds2;
851     int                 err;
852  
853     CYG_TRACE5(TFS, "rename mte=%p dir1=%p name1='%s' dir2=%p name2='%s'", 
854                     mte, dir1, name1, dir2, name2);
855
856     init_dirsearch(&ds1, disk, (fatfs_node_t *)dir1, name1);
857
858     err = fatfs_find(&ds1);
859     if (err != ENOERR)
860         return err;
861
862 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
863     // if the file is read only fail with permission error
864     if (S_FATFS_ISRDONLY(ds1.node->dentry.attrib))
865         return EPERM;
866 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
867
868     // Protect the found nodes from being reused 
869     // by the search for the ds2 dir/node pair 
870     fatfs_node_ref(disk, ds1.dir);
871     fatfs_node_ref(disk, ds1.node);
872     
873     init_dirsearch(&ds2, disk, (fatfs_node_t *) dir2, name2);
874    
875     err = fatfs_find(&ds2);
876
877     // Check if the target name already exists
878     if (err == ENOERR && ds2.last)
879     {
880         err = EEXIST;  
881         goto out;  
882     }
883     
884     // Check if the target dir doesn't exist
885     if (err == ENOENT && !ds2.last)
886         goto out;
887     
888     // Check if the target and the source are the same
889     if (ds1.node == ds2.node)
890     {
891         err = ENOERR;
892         goto out;
893     }
894     
895     err = fatfs_rename_file(disk, 
896                             &ds1.dir->dentry, 
897                             &ds1.node->dentry, 
898                             &ds2.dir->dentry, 
899                             ds2.name, 
900                             ds2.namelen);  
901
902     fatfs_node_rehash(disk, ds1.node);
903
904 out:
905     // Unreference previousely protected nodes
906     fatfs_node_unref(disk, ds1.dir);
907     fatfs_node_unref(disk, ds1.node);
908    
909     if (err == ENOERR)
910     {
911         ds1.dir->dentry.atime =
912         ds1.dir->dentry.mtime = 
913         ds2.dir->dentry.atime = 
914         ds2.dir->dentry.mtime = cyg_timestamp();    
915     } 
916     return err;
917 }
918
919 // -------------------------------------------------------------------------
920 // fatfs_link()
921 // Make a new directory entry for a file.
922
923 static int 
924 fatfs_link(cyg_mtab_entry *mte, 
925            cyg_dir         dir1, 
926            const char     *name1,
927            cyg_dir         dir2, 
928            const char     *name2, 
929            int             type)
930 {
931     CYG_TRACE6(TFS, "link mte=%p dir1=%p name1='%s' dir2=%p name2='%s' type=%d",
932                     mte, dir1, name1, dir2, name2, type);
933
934     // Linking not supported
935     return EINVAL;
936 }
937
938 // -------------------------------------------------------------------------
939 // fatfs_opendir()
940 // Open a directory for reading.
941
942 static int
943 fatfs_opendir(cyg_mtab_entry *mte,
944               cyg_dir         dir,
945               const char     *name,
946               cyg_file       *file)
947 {
948     fatfs_disk_t       *disk = (fatfs_disk_t *) mte->data;
949     fatfs_fd_t         *fd;
950     fatfs_dirsearch_t   ds;
951     int                 err;
952
953     CYG_TRACE4(TFS, "opendir mte=%p dir=%p name='%s' file=%p", 
954                     mte, dir, name, file);
955
956     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
957
958     err = fatfs_find(&ds);
959     if (err != ENOERR)
960         return err;
961
962     if (!S_ISDIR(ds.node->dentry.mode)) 
963         return ENOTDIR;
964
965     // Allocate file object private data and
966     // make a reference to this file node
967
968     fd = alloc_fatfs_fd(disk, ds.node);
969     if (NULL == fd)
970         return EMFILE;
971     
972     fatfs_node_ref(disk, ds.node);
973     
974     // Initialize the file object
975
976     file->f_type    = CYG_FILE_TYPE_FILE;
977     file->f_ops     = &fatfs_dirops;
978     file->f_data    = (CYG_ADDRWORD) fd;
979     file->f_xops    = 0;
980     file->f_offset  = 0;
981
982     return ENOERR;
983 }
984
985 // -------------------------------------------------------------------------
986 // fatfs_chdir()
987 // Change directory support.
988
989 static int
990 fatfs_chdir(cyg_mtab_entry *mte,
991             cyg_dir         dir,
992             const char     *name,
993             cyg_dir        *dir_out)
994 {
995     fatfs_disk_t *disk = (fatfs_disk_t *) mte->data;
996
997     CYG_TRACE4(TFS, "chdir mte=%p dir=%p dir_out=%p name=%d", 
998                     mte, dir, dir_out, name);
999
1000     if (dir_out != NULL)
1001     {
1002         // This is a request to get a new directory pointer in *dir_out
1003         
1004         fatfs_dirsearch_t ds;
1005         int               err;
1006
1007         init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
1008
1009         err = fatfs_find(&ds);
1010         if (err != ENOERR)
1011             return err;
1012
1013         if (!S_ISDIR(ds.node->dentry.mode))
1014             return ENOTDIR;
1015
1016         if (ds.node != disk->root)
1017             fatfs_node_ref(disk, ds.node);
1018         
1019         *dir_out = (cyg_dir) ds.node;
1020     }
1021     else
1022     {
1023         // If no output dir is required, this means that the mte and
1024         // dir arguments are the current cdir setting and we should
1025         // forget this fact.
1026
1027         fatfs_node_t *node = (fatfs_node_t *) dir;
1028
1029         if (node != disk->root)
1030             fatfs_node_unref(disk, node);
1031     }
1032
1033     return ENOERR;
1034 }
1035
1036 // -------------------------------------------------------------------------
1037 // fatfs_stat()
1038 // Get struct stat info for named object.
1039
1040 static int
1041 fatfs_stat(cyg_mtab_entry *mte,
1042            cyg_dir         dir,
1043            const char     *name,
1044            struct stat    *buf)
1045 {
1046     fatfs_disk_t      *disk = (fatfs_disk_t *) mte->data;
1047     fatfs_dirsearch_t  ds;
1048     int                err;
1049
1050     CYG_TRACE4(TFS, "stat mte=%p dir=%p name='%s' buf=%p", 
1051                     mte, dir, name, buf);
1052
1053     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
1054
1055     err = fatfs_find(&ds);
1056     if (err != ENOERR)
1057         return err;
1058
1059     // Fill in the status
1060     
1061     buf->st_mode   = ds.node->dentry.mode;
1062 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
1063     if (!S_FATFS_ISRDONLY(ds.node->dentry.attrib))
1064         buf->st_mode |= (S_IWUSR | S_IWGRP | S_IWOTH);
1065 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
1066     buf->st_ino    = (ino_t) ds.node->dentry.cluster;
1067     buf->st_dev    = 0;
1068     buf->st_nlink  = 1;
1069     buf->st_uid    = 0;
1070     buf->st_gid    = 0;
1071     buf->st_size   = ds.node->dentry.size;
1072     buf->st_atime  = ds.node->dentry.atime;
1073     buf->st_mtime  = ds.node->dentry.mtime;
1074     buf->st_ctime  = ds.node->dentry.ctime;
1075
1076     return ENOERR;
1077 }
1078
1079 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
1080 // -------------------------------------------------------------------------
1081 // fatfs_set_attrib()
1082 // Set FAT file system attributes for specified file
1083
1084 static int
1085 fatfs_set_attrib(cyg_mtab_entry        *mte,
1086                  cyg_dir                dir,
1087                  const char            *name,
1088                  const cyg_fs_attrib_t  new_attrib)
1089 {
1090     fatfs_disk_t      *disk = (fatfs_disk_t *) mte->data;
1091     fatfs_dirsearch_t  ds;
1092     int                err;
1093
1094     CYG_TRACE4(TFS, "set_attrib mte=%p dir=%p name='%s' buf=%x", 
1095                     mte, dir, name, new_attrib);
1096
1097     // Verify new_mode is valid
1098     if ((new_attrib & S_FATFS_ATTRIB) != new_attrib)
1099         return EINVAL;
1100     
1101     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
1102
1103     err = fatfs_find(&ds);
1104     if (err != ENOERR)
1105         return err;
1106
1107     // Change the "changeable" mode bits for the file.
1108     ds.node->dentry.attrib = 
1109       (ds.node->dentry.attrib & (~S_FATFS_ATTRIB)) | new_attrib;
1110
1111     return fatfs_write_dir_entry(disk,&ds.node->dentry);
1112 }
1113
1114 // -------------------------------------------------------------------------
1115 // fatfs_get_attrib()
1116 // Set FAT file system attributes for specified file
1117
1118 static int
1119 fatfs_get_attrib(cyg_mtab_entry  *mte,
1120                  cyg_dir          dir,
1121                  const char      *name,
1122                  cyg_fs_attrib_t * const file_attrib)
1123 {
1124     fatfs_disk_t      *disk = (fatfs_disk_t *) mte->data;
1125     fatfs_dirsearch_t  ds;
1126     int                err;
1127
1128     CYG_TRACE4(TFS, "get_attrib mte=%p dir=%p name='%s' buf=%x", 
1129                     mte, dir, name, file_attrib);
1130
1131     init_dirsearch(&ds, disk, (fatfs_node_t *) dir, name);
1132
1133     err = fatfs_find(&ds);
1134     if (err != ENOERR)
1135         return err;
1136
1137     // Get the attribute field
1138     CYG_CHECK_DATA_PTR(file_attrib,"Invalid destination attribute pointer");
1139     *file_attrib = ds.node->dentry.attrib;
1140
1141     return ENOERR;
1142 }
1143 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
1144
1145 // -------------------------------------------------------------------------
1146 // fatfs_getinfo()
1147 // Getinfo. Support for attrib
1148
1149 static int 
1150 fatfs_getinfo(cyg_mtab_entry *mte, 
1151               cyg_dir         dir, 
1152               const char     *name,
1153               int             key, 
1154               void           *buf, 
1155               int             len)
1156 {
1157     int err = EINVAL;
1158
1159     CYG_TRACE6(TFS, "getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d",
1160                     mte, dir, name, key, buf, len);
1161     switch( key )
1162     {
1163 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
1164         case FS_INFO_ATTRIB:
1165             err = fatfs_get_attrib(mte, dir, name, (cyg_fs_attrib_t*)buf);
1166             break;
1167 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
1168 #if defined(CYGSEM_FILEIO_BLOCK_USAGE)
1169         case FS_INFO_BLOCK_USAGE: {
1170           cyg_uint32 total_clusters;
1171           cyg_uint32 free_clusters;
1172           struct cyg_fs_block_usage *usage = (struct cyg_fs_block_usage *) buf;
1173           fatfs_disk_t  *disk   = (fatfs_disk_t *) mte->data;
1174
1175           err = fatfs_get_disk_usage(disk, &total_clusters, &free_clusters);
1176           if (err)
1177             return err;
1178           usage->total_blocks = total_clusters; 
1179           usage->free_blocks = free_clusters;
1180           usage->block_size = disk->cluster_size;
1181           break;
1182         }
1183 #endif
1184         default:
1185             err = EINVAL;
1186             break;
1187     }
1188     return err;
1189 }
1190
1191 // -------------------------------------------------------------------------
1192 // fatfs_setinfo()
1193 // Setinfo. Support for fssync and attrib
1194
1195 static int 
1196 fatfs_setinfo(cyg_mtab_entry *mte, 
1197               cyg_dir         dir, 
1198               const char     *name,
1199               int             key, 
1200               void           *buf, 
1201               int             len)
1202 {
1203     int err = EINVAL;
1204
1205     CYG_TRACE6(TFS, "setinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d",
1206                     mte, dir, name, key, buf, len);
1207
1208     switch( key )
1209     {
1210         case FS_INFO_SYNC:
1211             err = cyg_blib_sync(&(((fatfs_disk_t *) mte->data)->blib));
1212             break;
1213 #ifdef CYGCFG_FS_FAT_USE_ATTRIBUTES
1214         case FS_INFO_ATTRIB:
1215             err = fatfs_set_attrib(mte, dir, name, *(cyg_fs_attrib_t *)buf);
1216             break;
1217 #endif // CYGCFG_FS_FAT_USE_ATTRIBUTES
1218         default:
1219             err = EINVAL;
1220             break;
1221     }
1222     return err;
1223 }
1224
1225 //==========================================================================
1226 // File operations
1227
1228 // -------------------------------------------------------------------------
1229 // fatfs_fo_read()
1230 // Read data from the file.
1231
1232 static int 
1233 fatfs_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
1234 {
1235     fatfs_disk_t  *disk   = (fatfs_disk_t *) fp->f_mte->data;
1236     fatfs_fd_t    *fd     = (fatfs_fd_t *)   fp->f_data;
1237     fatfs_node_t  *node   = fd->node;
1238     cyg_uint32     pos    = fp->f_offset;
1239     ssize_t        resid  = uio->uio_resid;
1240     int            i;
1241
1242     CYG_TRACE3(TFO, "read fp=%p uio=%p pos=%d", fp, uio, pos);
1243
1244     // Loop over the io vectors until there are none left
1245
1246     for (i = 0; i < uio->uio_iovcnt; i++)
1247     {
1248         cyg_iovec  *iov  = &uio->uio_iov[i];
1249         char       *buf  = (char *) iov->iov_base;
1250         off_t       len  = iov->iov_len;
1251
1252         // Loop over each vector filling it with data from the file
1253         
1254         while (len > 0 && pos < node->dentry.size)
1255         {
1256             cyg_uint32 l = len;
1257             int        err;
1258
1259             // Adjust size to end of file if necessary
1260             if (l > node->dentry.size-pos)
1261                 l = node->dentry.size-pos;
1262
1263             err = fatfs_read_data(disk, &node->dentry, &fd->pos, buf, &l);
1264             if (err != ENOERR)
1265                 return err;
1266
1267             // Update working vars
1268
1269             len   -= l;
1270             buf   += l;
1271             pos   += l;
1272             resid -= l;
1273         }
1274     }
1275
1276     // We successfully read some data, update the access time, 
1277     // file offset and transfer residue
1278
1279     node->dentry.atime = cyg_timestamp(); 
1280     uio->uio_resid     = resid;
1281     fp->f_offset       = (off_t) pos;
1282
1283     return ENOERR;
1284 }
1285
1286 // -------------------------------------------------------------------------
1287 // fatfs_fo_write()
1288 // Write data to file.
1289
1290 static int 
1291 fatfs_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
1292 {
1293     fatfs_disk_t  *disk   = (fatfs_disk_t *) fp->f_mte->data;
1294     fatfs_fd_t    *fd     = (fatfs_fd_t *)   fp->f_data;
1295     fatfs_node_t  *node   = fd->node;
1296     cyg_uint32     pos    = fp->f_offset;
1297     ssize_t        resid  = uio->uio_resid;
1298     int            err    = ENOERR;
1299     int            i;
1300
1301     CYG_TRACE3(TFO, "write fp=%p uio=%p pos=%d", fp, uio, pos);
1302     
1303     // If the APPEND mode bit was supplied, force all writes to
1304     // the end of the file
1305     if (fp->f_flag & CYG_FAPPEND)
1306     {
1307         fatfs_setpos(disk, &node->dentry, &fd->pos, node->dentry.size);
1308         pos = fp->f_offset = node->dentry.size;
1309     }
1310     
1311     // Check that pos is within current file size, or at the very end
1312     if (pos < 0 || pos > node->dentry.size)
1313         return EINVAL;
1314    
1315     // Now loop over the iovecs until they are all done, or we get an error
1316     
1317     for (i = 0; i < uio->uio_iovcnt; i++)
1318     {
1319         cyg_iovec  *iov  = &uio->uio_iov[i];
1320         char       *buf  = (char *) iov->iov_base;
1321         off_t       len  = iov->iov_len;
1322  
1323         // Loop over the vector writing it to the file 
1324         // until it has all been done
1325
1326         while (len > 0)
1327         {
1328             cyg_uint32 l = len;
1329
1330             err = fatfs_write_data(disk, &node->dentry, &fd->pos, buf, &l);
1331
1332             // Update working vars
1333
1334             len   -= l;
1335             buf   += l;
1336             pos   += l;
1337             resid -= l;
1338
1339             // Stop writing if there is no more space in the file
1340             if (err == ENOSPC)
1341                 break;
1342             
1343             if (err != ENOERR)
1344                 return err;
1345         }
1346     }        
1347
1348     // We wrote some data successfully, update the modified and access
1349     // times of the node, increase its size appropriately, and update
1350     // the file offset and transfer residue.
1351
1352     node->dentry.mtime = 
1353     node->dentry.atime = cyg_timestamp();
1354
1355     if (pos > node->dentry.size)
1356         node->dentry.size = pos;
1357
1358     uio->uio_resid = resid;
1359     fp->f_offset   = (off_t) pos;
1360
1361     return err;
1362 }
1363
1364 // -------------------------------------------------------------------------
1365 // fatfs_fo_lseek()
1366 // Seek to a new file position.
1367
1368 static int 
1369 fatfs_fo_lseek(struct CYG_FILE_TAG *fp, off_t *apos, int whence)
1370 {
1371     fatfs_disk_t  *disk  = (fatfs_disk_t *) fp->f_mte->data;
1372     fatfs_fd_t    *fd    = (fatfs_fd_t *)   fp->f_data;
1373     off_t          pos   = *apos;
1374     int            err;
1375     
1376     CYG_TRACE3(TFO, "lseek fp=%p pos=%d whence=%d", fp, fp->f_offset, whence);
1377
1378     switch (whence)
1379     {
1380         case SEEK_SET:
1381             // Pos is already where we want to be
1382             break;
1383          case SEEK_CUR:
1384             // Add pos to current offset
1385             pos += fp->f_offset;
1386             break;
1387          case SEEK_END:
1388             // Add pos to file size
1389             pos += fd->node->dentry.size;
1390             break;
1391          default:
1392             return EINVAL;
1393     }
1394
1395     // Check that pos is still within current file size, 
1396     // or at the very end
1397     if (pos < 0 || pos > fd->node->dentry.size)
1398         return EINVAL;
1399   
1400     // All OK, set fp offset and return new position
1401     
1402     err = fatfs_setpos(disk, &fd->node->dentry, &fd->pos, pos);
1403
1404     if (ENOERR == err)
1405         *apos = fp->f_offset = pos;
1406     
1407     CYG_TRACE2(TFO, "lseek fp=%p new pos=%d", fp, *apos);
1408
1409     return err;
1410 }
1411
1412 // -------------------------------------------------------------------------
1413 // fatfs_fo_ioctl()
1414 // Handle ioctls. Currently none are defined.
1415
1416 static int 
1417 fatfs_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, CYG_ADDRWORD data)
1418 {
1419     CYG_TRACE3(TFO, "ioctl fp=%p com=%x data=%x", fp, com, data);
1420     return EINVAL;
1421 }
1422
1423 // -------------------------------------------------------------------------
1424 // fatfs_fo_fsync().
1425 // Force the file out to data storage.
1426
1427 static int 
1428 fatfs_fo_fsync(struct CYG_FILE_TAG *fp, int mode)
1429 {
1430     fatfs_disk_t  *disk  = (fatfs_disk_t *) fp->f_mte->data;
1431     fatfs_fd_t    *fd    = (fatfs_fd_t *)   fp->f_data;
1432     fatfs_node_t  *node  = fd->node;
1433     int            err;
1434     
1435     CYG_TRACE2(TFO, "fsync fp=%p mode=%d", fp, mode);
1436
1437     err = fatfs_write_dir_entry(disk, &node->dentry);
1438
1439     if (ENOERR == err)
1440         err = cyg_blib_sync(&disk->blib);
1441     
1442     return err;
1443 }
1444
1445 // -------------------------------------------------------------------------
1446 // fatfs_fo_close()
1447 // Close a file.
1448
1449 static int
1450 fatfs_fo_close(struct CYG_FILE_TAG *fp)
1451 {    
1452     fatfs_disk_t  *disk  = (fatfs_disk_t *) fp->f_mte->data;
1453     fatfs_fd_t    *fd    = (fatfs_fd_t *)   fp->f_data;
1454     fatfs_node_t  *node  = fd->node;
1455     int            err   = ENOERR;
1456
1457     CYG_TRACE1(TFO, "close fp=%p", fp);
1458
1459     // Write file attributes to disk, unreference 
1460     // the file node and free its private data
1461
1462     if (node != disk->root)
1463         err = fatfs_write_dir_entry(disk, &node->dentry);
1464
1465     fatfs_node_unref(disk, node);    
1466
1467     free_fatfs_fd(fd);
1468     
1469     return err;
1470 }
1471
1472 // -------------------------------------------------------------------------
1473 // fatfs_fo_fstat()
1474 // Get file status.
1475
1476 static int
1477 fatfs_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf)
1478 {
1479     fatfs_fd_t    *fd    = (fatfs_fd_t *) fp->f_data;
1480     fatfs_node_t  *node  = fd->node;
1481     
1482     CYG_TRACE2(TFO, "fstat fp=%p buf=%p", fp, buf);
1483
1484     // Fill in the status
1485
1486     buf->st_mode   = node->dentry.mode;
1487     buf->st_ino    = (ino_t) node->dentry.cluster;
1488     buf->st_dev    = 0;
1489     buf->st_nlink  = 1;
1490     buf->st_uid    = 0;
1491     buf->st_gid    = 0;
1492     buf->st_size   = node->dentry.size;
1493     buf->st_atime  = node->dentry.atime;
1494     buf->st_mtime  = node->dentry.mtime;
1495     buf->st_ctime  = node->dentry.ctime;
1496
1497     return ENOERR;
1498 }
1499
1500 // -------------------------------------------------------------------------
1501 // fatfs_fo_getinfo()
1502 // Get info.
1503
1504 static int
1505 fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len)
1506 {
1507     CYG_TRACE4(TFO, "getinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len);
1508     return EINVAL;
1509 }
1510
1511 // -------------------------------------------------------------------------
1512 // fatfs_fo_setinfo()
1513 // Set info.
1514
1515 static int
1516 fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len)
1517 {
1518     CYG_TRACE4(TFO, "setinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len);
1519     return EINVAL;
1520 }
1521
1522 //==========================================================================
1523 // Directory operations
1524
1525 // -------------------------------------------------------------------------
1526 // fatfs_fo_dirread()
1527 // Read a single directory entry from a file.
1528
1529 static int 
1530 fatfs_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
1531 {
1532     fatfs_disk_t      *disk    = (fatfs_disk_t *)  fp->f_mte->data;
1533     fatfs_fd_t        *fd      = (fatfs_fd_t *)    fp->f_data;
1534     struct dirent     *ent     = (struct dirent *) uio->uio_iov[0].iov_base;
1535     char              *nbuf    = ent->d_name;
1536     off_t              len     = uio->uio_iov[0].iov_len;
1537     fatfs_dir_entry_t  dentry;
1538     int                err;
1539     
1540     CYG_TRACE3(TFO, "dirread fp=%p uio=%p pos=%d", fp, uio, fp->f_offset);
1541
1542     if (len < sizeof(struct dirent))
1543         return EINVAL;
1544
1545     err = fatfs_read_dir_entry(disk, &fd->node->dentry, &fd->pos, &dentry);
1546
1547     if (err != ENOERR)
1548         return (err == EEOF ? ENOERR : err);
1549
1550     strcpy(nbuf, dentry.filename);
1551 #ifdef CYGPKG_FS_FAT_RET_DIRENT_DTYPE
1552     ent->d_type = dentry.mode;
1553 #endif
1554     fd->node->dentry.atime  = cyg_timestamp();
1555     uio->uio_resid         -= sizeof(struct dirent);
1556     fp->f_offset++;
1557     
1558     return ENOERR;
1559 }
1560
1561 // -------------------------------------------------------------------------
1562 // fatfs_fo_dirlseek()
1563 // Seek directory to start.
1564
1565 static int 
1566 fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence)
1567 {
1568     fatfs_disk_t  *disk  = (fatfs_disk_t *) fp->f_mte->data;
1569     fatfs_fd_t    *fd    = (fatfs_fd_t *)   fp->f_data;
1570     int            err;
1571     
1572     CYG_TRACE2(TFO, "dirlseek fp=%p whence=%d", fp, whence);
1573
1574     // Only allow SEEK_SET to zero
1575     
1576     if (whence != SEEK_SET || *pos != 0)
1577         return EINVAL;
1578    
1579     err = fatfs_setpos(disk, &fd->node->dentry, &fd->pos, 0);
1580
1581     if (ENOERR == err)
1582         *pos = fp->f_offset = 0;
1583     
1584     return err;
1585 }
1586
1587 // -------------------------------------------------------------------------
1588 // EOF fatfs.c