1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Björn Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
26 #include "dir_uncached.h"
29 #include "filefuncs.h"
33 These functions provide a roughly POSIX-compatible file IO API.
35 Since the fat32 driver only manages sectors, we maintain a one-sector
36 cache for each open file. This way we can provide byte access without
37 having to re-read the sector each time.
38 The penalty is the RAM used for the cache and slightly more complex code.
42 unsigned char cache
[SECTOR_SIZE
] CACHEALIGN_ATTR
;
43 int cacheoffset
; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
47 struct fat_file fatfile
;
54 static struct filedesc openfiles
[MAX_OPEN_FILES
] CACHEALIGN_ATTR
;
56 static int flush_cache(int fd
);
58 int file_creat(const char *pathname
)
60 return open(pathname
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
63 static int open_internal(const char* pathname
, int flags
, bool use_cache
)
66 struct dirent_uncached
* entry
;
68 int pathnamesize
= strlen(pathname
) + 1;
69 char pathnamecopy
[pathnamesize
];
71 struct filedesc
* file
= NULL
;
77 LDEBUGF("open(\"%s\",%d)\n",pathname
,flags
);
79 if ( pathname
[0] != '/' ) {
80 DEBUGF("'%s' is not an absolute path.\n",pathname
);
81 DEBUGF("Only absolute pathnames supported at the moment\n");
86 /* find a free file descriptor */
87 for ( fd
=0; fd
<MAX_OPEN_FILES
; fd
++ )
88 if ( !openfiles
[fd
].busy
)
91 if ( fd
== MAX_OPEN_FILES
) {
92 DEBUGF("Too many files open\n");
97 file
= &openfiles
[fd
];
98 memset(file
, 0, sizeof(struct filedesc
));
100 if (flags
& (O_RDWR
| O_WRONLY
)) {
109 if (dircache_is_enabled() && !file
->write
&& use_cache
)
111 # ifdef HAVE_MULTIVOLUME
112 int volume
= strip_volume(pathname
, pathnamecopy
);
115 int ce
= dircache_get_entry_id(pathname
);
123 long startcluster
= _dircache_get_entry_startcluster(ce
);
124 fat_open(IF_MV2(volume
,)
128 struct dirinfo
*info
= _dircache_get_entry_dirinfo(ce
);
129 file
->size
= info
->size
;
130 file
->attr
= info
->attribute
;
131 file
->cacheoffset
= -1;
132 file
->fileoffset
= 0;
138 strlcpy(pathnamecopy
, pathname
, pathnamesize
);
140 /* locate filename */
141 name
=strrchr(pathnamecopy
+1,'/');
144 dir
= opendir_uncached(pathnamecopy
);
149 dir
= opendir_uncached("/");
150 name
= pathnamecopy
+1;
153 DEBUGF("Failed opening dir\n");
160 DEBUGF("Empty file name\n");
163 closedir_uncached(dir
);
167 /* scan dir for name */
168 while ((entry
= readdir_uncached(dir
))) {
169 if ( !strcasecmp(name
, entry
->d_name
) ) {
170 fat_open(IF_MV2(dir
->fatdir
.file
.volume
,)
174 file
->size
= file
->trunc
? 0 : entry
->info
.size
;
175 file
->attr
= entry
->info
.attribute
;
181 LDEBUGF("Didn't find file %s\n",name
);
182 if ( file
->write
&& (flags
& O_CREAT
) ) {
183 rc
= fat_create_file(name
,
187 DEBUGF("Couldn't create %s in %s\n",name
,pathnamecopy
);
190 closedir_uncached(dir
);
194 dircache_add_file(pathname
, file
->fatfile
.firstcluster
);
200 DEBUGF("Couldn't find %s in %s\n",name
,pathnamecopy
);
203 closedir_uncached(dir
);
207 if(file
->write
&& (file
->attr
& FAT_ATTR_DIRECTORY
)) {
210 closedir_uncached(dir
);
214 closedir_uncached(dir
);
216 file
->cacheoffset
= -1;
217 file
->fileoffset
= 0;
219 if (file
->write
&& (flags
& O_APPEND
)) {
220 rc
= lseek(fd
,0,SEEK_END
);
227 dircache_bind(fd
, pathname
);
233 int file_open(const char* pathname
, int flags
)
235 /* By default, use the dircache if available. */
236 return open_internal(pathname
, flags
, true);
241 struct filedesc
* file
= &openfiles
[fd
];
244 LDEBUGF("close(%d)\n", fd
);
246 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
259 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
260 dircache_update_filetime(fd
);
270 struct filedesc
* file
= &openfiles
[fd
];
273 LDEBUGF("fsync(%d)\n", fd
);
275 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
284 /* flush sector cache */
286 rc
= flush_cache(fd
);
289 /* when failing, try to close the file anyway */
290 fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
297 rc
= ftruncate(fd
, file
->size
);
300 /* when failing, try to close the file anyway */
301 fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
306 /* tie up all loose ends */
307 rc
= fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
314 int remove(const char* name
)
317 struct filedesc
* file
;
318 /* Can't use dircache now, because we need to access the fat structures. */
319 int fd
= open_internal(name
, O_WRONLY
, false);
323 file
= &openfiles
[fd
];
325 dircache_remove(name
);
327 rc
= fat_remove(&(file
->fatfile
));
329 DEBUGF("Failed removing file: %d\n", rc
);
343 int rename(const char* path
, const char* newpath
)
349 struct filedesc
* file
;
350 char newpath2
[MAX_PATH
];
352 /* verify new path does not already exist */
353 /* If it is a directory, errno == EISDIR if the name exists */
354 fd
= open(newpath
, O_RDONLY
);
355 if ( fd
>= 0 || errno
== EISDIR
) {
362 fd
= open_internal(path
, O_RDONLY
, false);
368 /* extract new file name */
369 nameptr
= strrchr(newpath
,'/');
377 /* Extract new path */
378 strcpy(newpath2
, newpath
);
380 dirptr
= strrchr(newpath2
,'/');
390 if(strlen(dirptr
) == 0) {
394 dir
= opendir_uncached(dirptr
);
400 file
= &openfiles
[fd
];
402 rc
= fat_rename(&file
->fatfile
, &dir
->fatdir
, nameptr
,
403 file
->size
, file
->attr
);
404 #ifdef HAVE_MULTIVOLUME
407 closedir_uncached(dir
);
408 DEBUGF("Failed renaming file across volumnes: %d\n", rc
);
415 closedir_uncached(dir
);
416 DEBUGF("Failed renaming file: %d\n", rc
);
422 dircache_rename(path
, newpath
);
427 closedir_uncached(dir
);
432 rc
= closedir_uncached(dir
);
441 int ftruncate(int fd
, off_t size
)
444 struct filedesc
* file
= &openfiles
[fd
];
446 sector
= size
/ SECTOR_SIZE
;
447 if (size
% SECTOR_SIZE
)
450 rc
= fat_seek(&(file
->fatfile
), sector
);
456 rc
= fat_truncate(&(file
->fatfile
));
464 dircache_update_filesize(fd
, size
, file
->fatfile
.firstcluster
);
470 static int flush_cache(int fd
)
473 struct filedesc
* file
= &openfiles
[fd
];
474 long sector
= file
->fileoffset
/ SECTOR_SIZE
;
476 DEBUGF("Flushing dirty sector cache\n");
478 /* make sure we are on correct sector */
479 rc
= fat_seek(&(file
->fatfile
), sector
);
483 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
, true );
486 if(file
->fatfile
.eof
)
497 static int readwrite(int fd
, void* buf
, long count
, bool write
)
501 struct filedesc
* file
;
503 #ifdef STORAGE_NEEDS_ALIGN
508 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
513 file
= &openfiles
[fd
];
520 if(file
->attr
& FAT_ATTR_DIRECTORY
) {
525 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
526 fd
,(long)buf
,count
,write
?"write":"read");
528 /* attempt to read past EOF? */
529 if (!write
&& count
> file
->size
- file
->fileoffset
)
530 count
= file
->size
- file
->fileoffset
;
532 /* any head bytes? */
533 if ( file
->cacheoffset
!= -1 ) {
534 int offs
= file
->cacheoffset
;
535 int headbytes
= MIN(count
, SECTOR_SIZE
- offs
);
538 memcpy( file
->cache
+ offs
, buf
, headbytes
);
542 memcpy( buf
, file
->cache
+ offs
, headbytes
);
545 if (offs
+ headbytes
== SECTOR_SIZE
) {
547 rc
= flush_cache(fd
);
553 file
->cacheoffset
= -1;
556 file
->cacheoffset
+= headbytes
;
563 /* If the buffer has been modified, either it has been flushed already
564 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
565 * more data to follow in this call). Do NOT flush here. */
567 /* read/write whole sectors right into/from the supplied buffer */
568 sectors
= count
/ SECTOR_SIZE
;
571 #ifdef STORAGE_NEEDS_ALIGN
572 if (((uint32_t)buf
+ nread
) & (CACHEALIGN_SIZE
- 1))
573 for (i
= 0; i
< sectors
; i
++)
575 if (write
) memcpy(file
->cache
, buf
+nread
+i
*SECTOR_SIZE
, SECTOR_SIZE
);
576 rc2
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
, write
);
583 if (!write
) memcpy(buf
+nread
+i
*SECTOR_SIZE
, file
->cache
, SECTOR_SIZE
);
587 rc
= fat_readwrite(&(file
->fatfile
), sectors
, (unsigned char*)buf
+nread
, write
);
589 DEBUGF("Failed read/writing %ld sectors\n",sectors
);
591 if(write
&& file
->fatfile
.eof
) {
592 DEBUGF("No space left on device\n");
595 file
->fileoffset
+= nread
;
597 file
->cacheoffset
= -1;
598 /* adjust file size to length written */
599 if ( write
&& file
->fileoffset
> file
->size
)
601 file
->size
= file
->fileoffset
;
603 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
606 return nread
? nread
: rc
* 10 - 4;
610 nread
+= rc
* SECTOR_SIZE
;
611 count
-= sectors
* SECTOR_SIZE
;
613 /* if eof, skip tail bytes */
622 file
->cacheoffset
= -1;
626 /* any tail bytes? */
629 if ( file
->fileoffset
+ nread
< file
->size
) {
630 /* sector is only partially filled. copy-back from disk */
631 LDEBUGF("Copy-back tail cache\n");
632 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
, false );
634 DEBUGF("Failed writing\n");
636 file
->fileoffset
+= nread
;
637 file
->cacheoffset
= -1;
638 /* adjust file size to length written */
639 if ( file
->fileoffset
> file
->size
)
641 file
->size
= file
->fileoffset
;
643 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
646 return nread
? nread
: rc
* 10 - 5;
648 /* seek back one sector to put file position right */
649 rc
= fat_seek(&(file
->fatfile
),
650 (file
->fileoffset
+ nread
) /
653 DEBUGF("fat_seek() failed\n");
655 file
->fileoffset
+= nread
;
656 file
->cacheoffset
= -1;
657 /* adjust file size to length written */
658 if ( file
->fileoffset
> file
->size
)
660 file
->size
= file
->fileoffset
;
662 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
665 return nread
? nread
: rc
* 10 - 6;
668 memcpy( file
->cache
, (unsigned char*)buf
+ nread
, count
);
672 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
,false);
674 DEBUGF("Failed caching sector\n");
676 file
->fileoffset
+= nread
;
677 file
->cacheoffset
= -1;
678 return nread
? nread
: rc
* 10 - 7;
680 memcpy( (unsigned char*)buf
+ nread
, file
->cache
, count
);
684 file
->cacheoffset
= count
;
687 file
->fileoffset
+= nread
;
688 LDEBUGF("fileoffset: %ld\n", file
->fileoffset
);
690 /* adjust file size to length written */
691 if ( write
&& file
->fileoffset
> file
->size
)
693 file
->size
= file
->fileoffset
;
695 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
702 ssize_t
write(int fd
, const void* buf
, size_t count
)
704 if (!openfiles
[fd
].write
) {
708 return readwrite(fd
, (void *)buf
, count
, true);
711 ssize_t
read(int fd
, void* buf
, size_t count
)
713 return readwrite(fd
, buf
, count
, false);
717 off_t
lseek(int fd
, off_t offset
, int whence
)
724 struct filedesc
* file
= &openfiles
[fd
];
726 LDEBUGF("lseek(%d,%ld,%d)\n",fd
,offset
,whence
);
728 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
743 pos
= file
->fileoffset
+ offset
;
747 pos
= file
->size
+ offset
;
754 if ((pos
< 0) || (pos
> file
->size
)) {
760 newsector
= pos
/ SECTOR_SIZE
;
761 oldsector
= file
->fileoffset
/ SECTOR_SIZE
;
762 sectoroffset
= pos
% SECTOR_SIZE
;
764 if ( (newsector
!= oldsector
) ||
765 ((file
->cacheoffset
==-1) && sectoroffset
) ) {
767 if ( newsector
!= oldsector
) {
769 rc
= flush_cache(fd
);
774 rc
= fat_seek(&(file
->fatfile
), newsector
);
780 if ( sectoroffset
) {
781 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
,false);
786 file
->cacheoffset
= sectoroffset
;
789 file
->cacheoffset
= -1;
792 if ( file
->cacheoffset
!= -1 )
793 file
->cacheoffset
= sectoroffset
;
795 file
->fileoffset
= pos
;
800 off_t
filesize(int fd
)
802 struct filedesc
* file
= &openfiles
[fd
];
804 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
817 /* release all file handles on a given volume "by force", to avoid leaks */
818 int release_files(int volume
)
820 struct filedesc
* pfile
= openfiles
;
823 for ( fd
=0; fd
<MAX_OPEN_FILES
; fd
++, pfile
++)
825 #ifdef HAVE_MULTIVOLUME
826 if (pfile
->fatfile
.volume
== volume
)
831 pfile
->busy
= false; /* mark as available, no further action */
835 return closed
; /* return how many we did */