1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
24 #include "dir_uncached.h"
30 These functions provide a roughly POSIX-compatible file IO API.
32 Since the fat32 driver only manages sectors, we maintain a one-sector
33 cache for each open file. This way we can provide byte access without
34 having to re-read the sector each time.
35 The penalty is the RAM used for the cache and slightly more complex code.
39 unsigned char cache
[SECTOR_SIZE
];
40 int cacheoffset
; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
44 struct fat_file fatfile
;
51 static struct filedesc openfiles
[MAX_OPEN_FILES
];
53 static int flush_cache(int fd
);
55 int creat(const char *pathname
)
57 return open(pathname
, O_WRONLY
|O_CREAT
|O_TRUNC
);
60 static int open_internal(const char* pathname
, int flags
, bool use_cache
)
63 struct dirent_uncached
* entry
;
65 char pathnamecopy
[MAX_PATH
];
67 struct filedesc
* file
= NULL
;
73 LDEBUGF("open(\"%s\",%d)\n",pathname
,flags
);
75 if ( pathname
[0] != '/' ) {
76 DEBUGF("'%s' is not an absolute path.\n",pathname
);
77 DEBUGF("Only absolute pathnames supported at the moment\n");
82 /* find a free file descriptor */
83 for ( fd
=0; fd
<MAX_OPEN_FILES
; fd
++ )
84 if ( !openfiles
[fd
].busy
)
87 if ( fd
== MAX_OPEN_FILES
) {
88 DEBUGF("Too many files open\n");
93 file
= &openfiles
[fd
];
94 memset(file
, 0, sizeof(struct filedesc
));
96 if (flags
& (O_RDWR
| O_WRONLY
)) {
105 if (dircache_is_enabled() && !file
->write
&& use_cache
)
107 const struct dircache_entry
*ce
;
109 ce
= dircache_get_entry_ptr(pathname
);
117 fat_open(IF_MV2(unsupported at the moment
,)
121 file
->size
= ce
->size
;
122 file
->attr
= ce
->attribute
;
123 file
->cacheoffset
= -1;
124 file
->fileoffset
= 0;
130 strncpy(pathnamecopy
,pathname
,sizeof(pathnamecopy
));
131 pathnamecopy
[sizeof(pathnamecopy
)-1] = 0;
133 /* locate filename */
134 name
=strrchr(pathnamecopy
+1,'/');
137 dir
= opendir_uncached(pathnamecopy
);
142 dir
= opendir_uncached("/");
143 name
= pathnamecopy
+1;
146 DEBUGF("Failed opening dir\n");
153 DEBUGF("Empty file name\n");
156 closedir_uncached(dir
);
160 /* scan dir for name */
161 while ((entry
= readdir_uncached(dir
))) {
162 if ( !strcasecmp(name
, entry
->d_name
) ) {
163 fat_open(IF_MV2(dir
->fatdir
.file
.volume
,)
167 file
->size
= file
->trunc
? 0 : entry
->size
;
168 file
->attr
= entry
->attribute
;
174 LDEBUGF("Didn't find file %s\n",name
);
175 if ( file
->write
&& (flags
& O_CREAT
) ) {
176 rc
= fat_create_file(name
,
180 DEBUGF("Couldn't create %s in %s\n",name
,pathnamecopy
);
183 closedir_uncached(dir
);
187 dircache_add_file(pathname
, file
->fatfile
.firstcluster
);
193 DEBUGF("Couldn't find %s in %s\n",name
,pathnamecopy
);
196 closedir_uncached(dir
);
200 if(file
->write
&& (file
->attr
& FAT_ATTR_DIRECTORY
)) {
203 closedir_uncached(dir
);
207 closedir_uncached(dir
);
209 file
->cacheoffset
= -1;
210 file
->fileoffset
= 0;
212 if (file
->write
&& (flags
& O_APPEND
)) {
213 rc
= lseek(fd
,0,SEEK_END
);
220 dircache_bind(fd
, pathname
);
226 int open(const char* pathname
, int flags
)
228 /* By default, use the dircache if available. */
229 return open_internal(pathname
, flags
, true);
234 struct filedesc
* file
= &openfiles
[fd
];
237 LDEBUGF("close(%d)\n", fd
);
239 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
252 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
253 dircache_update_filetime(fd
);
263 struct filedesc
* file
= &openfiles
[fd
];
266 LDEBUGF("fsync(%d)\n", fd
);
268 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
277 /* flush sector cache */
279 rc
= flush_cache(fd
);
282 /* when failing, try to close the file anyway */
283 fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
290 rc
= ftruncate(fd
, file
->size
);
293 /* when failing, try to close the file anyway */
294 fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
299 /* tie up all loose ends */
300 rc
= fat_closewrite(&(file
->fatfile
), file
->size
, file
->attr
);
307 int remove(const char* name
)
310 struct filedesc
* file
;
311 /* Can't use dircache now, because we need to access the fat structures. */
312 int fd
= open_internal(name
, O_WRONLY
, false);
316 file
= &openfiles
[fd
];
318 dircache_remove(name
);
320 rc
= fat_remove(&(file
->fatfile
));
322 DEBUGF("Failed removing file: %d\n", rc
);
336 int rename(const char* path
, const char* newpath
)
342 struct filedesc
* file
;
343 char newpath2
[MAX_PATH
];
345 /* verify new path does not already exist */
346 /* If it is a directory, errno == EISDIR if the name exists */
347 fd
= open(newpath
, O_RDONLY
);
348 if ( fd
>= 0 || errno
== EISDIR
) {
355 fd
= open_internal(path
, O_RDONLY
, false);
361 /* extract new file name */
362 nameptr
= strrchr(newpath
,'/');
368 /* Extract new path */
369 strcpy(newpath2
, newpath
);
371 dirptr
= strrchr(newpath2
,'/');
379 if(strlen(dirptr
) == 0) {
383 dir
= opendir_uncached(dirptr
);
387 file
= &openfiles
[fd
];
389 rc
= fat_rename(&file
->fatfile
, &dir
->fatdir
, nameptr
,
390 file
->size
, file
->attr
);
391 #ifdef HAVE_MULTIVOLUME
393 DEBUGF("Failed renaming file across volumnes: %d\n", rc
);
399 DEBUGF("Failed renaming file: %d\n", rc
);
405 dircache_rename(path
, newpath
);
414 rc
= closedir_uncached(dir
);
423 int ftruncate(int fd
, off_t size
)
426 struct filedesc
* file
= &openfiles
[fd
];
428 sector
= size
/ SECTOR_SIZE
;
429 if (size
% SECTOR_SIZE
)
432 rc
= fat_seek(&(file
->fatfile
), sector
);
438 rc
= fat_truncate(&(file
->fatfile
));
446 dircache_update_filesize(fd
, size
, file
->fatfile
.firstcluster
);
452 static int flush_cache(int fd
)
455 struct filedesc
* file
= &openfiles
[fd
];
456 long sector
= file
->fileoffset
/ SECTOR_SIZE
;
458 DEBUGF("Flushing dirty sector cache\n");
460 /* make sure we are on correct sector */
461 rc
= fat_seek(&(file
->fatfile
), sector
);
465 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
, true );
468 if(file
->fatfile
.eof
)
479 static int readwrite(int fd
, void* buf
, long count
, bool write
)
483 struct filedesc
* file
= &openfiles
[fd
];
486 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
495 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
496 fd
,(long)buf
,count
,write
?"write":"read");
498 /* attempt to read past EOF? */
499 if (!write
&& count
> file
->size
- file
->fileoffset
)
500 count
= file
->size
- file
->fileoffset
;
502 /* any head bytes? */
503 if ( file
->cacheoffset
!= -1 ) {
504 int offs
= file
->cacheoffset
;
505 int headbytes
= MIN(count
, SECTOR_SIZE
- offs
);
508 memcpy( file
->cache
+ offs
, buf
, headbytes
);
512 memcpy( buf
, file
->cache
+ offs
, headbytes
);
515 if (offs
+ headbytes
== SECTOR_SIZE
) {
517 rc
= flush_cache(fd
);
523 file
->cacheoffset
= -1;
526 file
->cacheoffset
+= headbytes
;
533 /* If the buffer has been modified, either it has been flushed already
534 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
535 * more data to follow in this call). Do NOT flush here. */
537 /* read/write whole sectors right into/from the supplied buffer */
538 sectors
= count
/ SECTOR_SIZE
;
540 rc
= fat_readwrite(&(file
->fatfile
), sectors
,
541 (unsigned char*)buf
+nread
, write
);
543 DEBUGF("Failed read/writing %ld sectors\n",sectors
);
545 if(write
&& file
->fatfile
.eof
) {
546 DEBUGF("No space left on device\n");
549 file
->fileoffset
+= nread
;
551 file
->cacheoffset
= -1;
552 /* adjust file size to length written */
553 if ( write
&& file
->fileoffset
> file
->size
)
555 file
->size
= file
->fileoffset
;
557 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
560 return nread
? nread
: rc
* 10 - 4;
564 nread
+= rc
* SECTOR_SIZE
;
565 count
-= sectors
* SECTOR_SIZE
;
567 /* if eof, skip tail bytes */
576 file
->cacheoffset
= -1;
580 /* any tail bytes? */
583 if ( file
->fileoffset
+ nread
< file
->size
) {
584 /* sector is only partially filled. copy-back from disk */
585 LDEBUGF("Copy-back tail cache\n");
586 rc
= fat_readwrite(&(file
->fatfile
), 1, file
->cache
, false );
588 DEBUGF("Failed writing\n");
590 file
->fileoffset
+= nread
;
591 file
->cacheoffset
= -1;
592 /* adjust file size to length written */
593 if ( file
->fileoffset
> file
->size
)
595 file
->size
= file
->fileoffset
;
597 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
600 return nread
? nread
: rc
* 10 - 5;
602 /* seek back one sector to put file position right */
603 rc
= fat_seek(&(file
->fatfile
),
604 (file
->fileoffset
+ nread
) /
607 DEBUGF("fat_seek() failed\n");
609 file
->fileoffset
+= nread
;
610 file
->cacheoffset
= -1;
611 /* adjust file size to length written */
612 if ( file
->fileoffset
> file
->size
)
614 file
->size
= file
->fileoffset
;
616 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
619 return nread
? nread
: rc
* 10 - 6;
622 memcpy( file
->cache
, (unsigned char*)buf
+ nread
, count
);
626 rc
= fat_readwrite(&(file
->fatfile
), 1, &(file
->cache
),false);
628 DEBUGF("Failed caching sector\n");
630 file
->fileoffset
+= nread
;
631 file
->cacheoffset
= -1;
632 return nread
? nread
: rc
* 10 - 7;
634 memcpy( (unsigned char*)buf
+ nread
, file
->cache
, count
);
638 file
->cacheoffset
= count
;
641 file
->fileoffset
+= nread
;
642 LDEBUGF("fileoffset: %ld\n", file
->fileoffset
);
644 /* adjust file size to length written */
645 if ( write
&& file
->fileoffset
> file
->size
)
647 file
->size
= file
->fileoffset
;
649 dircache_update_filesize(fd
, file
->size
, file
->fatfile
.firstcluster
);
656 ssize_t
write(int fd
, const void* buf
, size_t count
)
658 if (!openfiles
[fd
].write
) {
662 return readwrite(fd
, (void *)buf
, count
, true);
665 ssize_t
read(int fd
, void* buf
, size_t count
)
667 return readwrite(fd
, buf
, count
, false);
671 off_t
lseek(int fd
, off_t offset
, int whence
)
678 struct filedesc
* file
= &openfiles
[fd
];
680 LDEBUGF("lseek(%d,%ld,%d)\n",fd
,offset
,whence
);
682 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
697 pos
= file
->fileoffset
+ offset
;
701 pos
= file
->size
+ offset
;
708 if ((pos
< 0) || (pos
> file
->size
)) {
714 newsector
= pos
/ SECTOR_SIZE
;
715 oldsector
= file
->fileoffset
/ SECTOR_SIZE
;
716 sectoroffset
= pos
% SECTOR_SIZE
;
718 if ( (newsector
!= oldsector
) ||
719 ((file
->cacheoffset
==-1) && sectoroffset
) ) {
721 if ( newsector
!= oldsector
) {
723 rc
= flush_cache(fd
);
728 rc
= fat_seek(&(file
->fatfile
), newsector
);
734 if ( sectoroffset
) {
735 rc
= fat_readwrite(&(file
->fatfile
), 1,
736 &(file
->cache
),false);
741 file
->cacheoffset
= sectoroffset
;
744 file
->cacheoffset
= -1;
747 if ( file
->cacheoffset
!= -1 )
748 file
->cacheoffset
= sectoroffset
;
750 file
->fileoffset
= pos
;
755 off_t
filesize(int fd
)
757 struct filedesc
* file
= &openfiles
[fd
];
759 if (fd
< 0 || fd
> MAX_OPEN_FILES
-1) {
773 /* release all file handles on a given volume "by force", to avoid leaks */
774 int release_files(int volume
)
776 struct filedesc
* pfile
= openfiles
;
779 for ( fd
=0; fd
<MAX_OPEN_FILES
; fd
++, pfile
++)
781 if (pfile
->fatfile
.volume
== volume
)
783 pfile
->busy
= false; /* mark as available, no further action */
787 return closed
; /* return how many we did */
789 #endif /* #ifdef HAVE_HOTSWAP */