Make encoder name conversion functions static to the base class.
[Rockbox.git] / firmware / common / file.c
blob613eba97df684c1a7696036e60ea14483253009f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
19 #include <string.h>
20 #include <errno.h>
21 #include <stdbool.h>
22 #include "file.h"
23 #include "fat.h"
24 #include "dir_uncached.h"
25 #include "debug.h"
26 #include "dircache.h"
27 #include "system.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.
38 struct filedesc {
39 unsigned char cache[SECTOR_SIZE];
40 int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
41 long fileoffset;
42 long size;
43 int attr;
44 struct fat_file fatfile;
45 bool busy;
46 bool write;
47 bool dirty;
48 bool trunc;
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)
62 DIR_UNCACHED* dir;
63 struct dirent_uncached* entry;
64 int fd;
65 char pathnamecopy[MAX_PATH];
66 char* name;
67 struct filedesc* file = NULL;
68 int rc;
69 #ifndef HAVE_DIRCACHE
70 (void)use_cache;
71 #endif
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");
78 errno = EINVAL;
79 return -1;
82 /* find a free file descriptor */
83 for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
84 if ( !openfiles[fd].busy )
85 break;
87 if ( fd == MAX_OPEN_FILES ) {
88 DEBUGF("Too many files open\n");
89 errno = EMFILE;
90 return -2;
93 file = &openfiles[fd];
94 memset(file, 0, sizeof(struct filedesc));
96 if (flags & (O_RDWR | O_WRONLY)) {
97 file->write = true;
99 if (flags & O_TRUNC)
100 file->trunc = true;
102 file->busy = true;
104 #ifdef HAVE_DIRCACHE
105 if (dircache_is_enabled() && !file->write && use_cache)
107 const struct dircache_entry *ce;
109 ce = dircache_get_entry_ptr(pathname);
110 if (!ce)
112 errno = ENOENT;
113 file->busy = false;
114 return -7;
117 fat_open(IF_MV2(unsupported at the moment,)
118 ce->startcluster,
119 &(file->fatfile),
120 NULL);
121 file->size = ce->size;
122 file->attr = ce->attribute;
123 file->cacheoffset = -1;
124 file->fileoffset = 0;
126 return fd;
128 #endif
130 strncpy(pathnamecopy,pathname,sizeof(pathnamecopy));
131 pathnamecopy[sizeof(pathnamecopy)-1] = 0;
133 /* locate filename */
134 name=strrchr(pathnamecopy+1,'/');
135 if ( name ) {
136 *name = 0;
137 dir = opendir_uncached(pathnamecopy);
138 *name = '/';
139 name++;
141 else {
142 dir = opendir_uncached("/");
143 name = pathnamecopy+1;
145 if (!dir) {
146 DEBUGF("Failed opening dir\n");
147 errno = EIO;
148 file->busy = false;
149 return -4;
152 if(name[0] == 0) {
153 DEBUGF("Empty file name\n");
154 errno = EINVAL;
155 file->busy = false;
156 closedir_uncached(dir);
157 return -5;
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,)
164 entry->startcluster,
165 &(file->fatfile),
166 &(dir->fatdir));
167 file->size = file->trunc ? 0 : entry->size;
168 file->attr = entry->attribute;
169 break;
173 if ( !entry ) {
174 LDEBUGF("Didn't find file %s\n",name);
175 if ( file->write && (flags & O_CREAT) ) {
176 rc = fat_create_file(name,
177 &(file->fatfile),
178 &(dir->fatdir));
179 if (rc < 0) {
180 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
181 errno = EIO;
182 file->busy = false;
183 closedir_uncached(dir);
184 return rc * 10 - 6;
186 #ifdef HAVE_DIRCACHE
187 dircache_add_file(pathname, file->fatfile.firstcluster);
188 #endif
189 file->size = 0;
190 file->attr = 0;
192 else {
193 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
194 errno = ENOENT;
195 file->busy = false;
196 closedir_uncached(dir);
197 return -7;
199 } else {
200 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
201 errno = EISDIR;
202 file->busy = false;
203 closedir_uncached(dir);
204 return -8;
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);
214 if (rc < 0 )
215 return rc * 10 - 9;
218 #ifdef HAVE_DIRCACHE
219 if (file->write)
220 dircache_bind(fd, pathname);
221 #endif
223 return fd;
226 int open(const char* pathname, int flags)
228 /* By default, use the dircache if available. */
229 return open_internal(pathname, flags, true);
232 int close(int fd)
234 struct filedesc* file = &openfiles[fd];
235 int rc = 0;
237 LDEBUGF("close(%d)\n", fd);
239 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
240 errno = EINVAL;
241 return -1;
243 if (!file->busy) {
244 errno = EBADF;
245 return -2;
247 if (file->write) {
248 rc = fsync(fd);
249 if (rc < 0)
250 return rc * 10 - 3;
251 #ifdef HAVE_DIRCACHE
252 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
253 dircache_update_filetime(fd);
254 #endif
257 file->busy = false;
258 return 0;
261 int fsync(int fd)
263 struct filedesc* file = &openfiles[fd];
264 int rc = 0;
266 LDEBUGF("fsync(%d)\n", fd);
268 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
269 errno = EINVAL;
270 return -1;
272 if (!file->busy) {
273 errno = EBADF;
274 return -2;
276 if (file->write) {
277 /* flush sector cache */
278 if ( file->dirty ) {
279 rc = flush_cache(fd);
280 if (rc < 0)
282 /* when failing, try to close the file anyway */
283 fat_closewrite(&(file->fatfile), file->size, file->attr);
284 return rc * 10 - 3;
288 /* truncate? */
289 if (file->trunc) {
290 rc = ftruncate(fd, file->size);
291 if (rc < 0)
293 /* when failing, try to close the file anyway */
294 fat_closewrite(&(file->fatfile), file->size, file->attr);
295 return rc * 10 - 4;
299 /* tie up all loose ends */
300 rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
301 if (rc < 0)
302 return rc * 10 - 5;
304 return 0;
307 int remove(const char* name)
309 int rc;
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);
313 if ( fd < 0 )
314 return fd * 10 - 1;
316 file = &openfiles[fd];
317 #ifdef HAVE_DIRCACHE
318 dircache_remove(name);
319 #endif
320 rc = fat_remove(&(file->fatfile));
321 if ( rc < 0 ) {
322 DEBUGF("Failed removing file: %d\n", rc);
323 errno = EIO;
324 return rc * 10 - 3;
327 file->size = 0;
329 rc = close(fd);
330 if (rc<0)
331 return rc * 10 - 4;
333 return 0;
336 int rename(const char* path, const char* newpath)
338 int rc, fd;
339 DIR_UNCACHED* dir;
340 char* nameptr;
341 char* dirptr;
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) {
349 close(fd);
350 errno = EBUSY;
351 return -1;
353 close(fd);
355 fd = open_internal(path, O_RDONLY, false);
356 if ( fd < 0 ) {
357 errno = EIO;
358 return fd * 10 - 2;
361 /* extract new file name */
362 nameptr = strrchr(newpath,'/');
363 if (nameptr)
364 nameptr++;
365 else
366 return - 3;
368 /* Extract new path */
369 strcpy(newpath2, newpath);
371 dirptr = strrchr(newpath2,'/');
372 if(dirptr)
373 *dirptr = 0;
374 else
375 return - 4;
377 dirptr = newpath2;
379 if(strlen(dirptr) == 0) {
380 dirptr = "/";
383 dir = opendir_uncached(dirptr);
384 if(!dir)
385 return - 5;
387 file = &openfiles[fd];
389 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
390 file->size, file->attr);
391 #ifdef HAVE_MULTIVOLUME
392 if ( rc == -1) {
393 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
394 errno = EXDEV;
395 return -6;
397 #endif
398 if ( rc < 0 ) {
399 DEBUGF("Failed renaming file: %d\n", rc);
400 errno = EIO;
401 return rc * 10 - 7;
404 #ifdef HAVE_DIRCACHE
405 dircache_rename(path, newpath);
406 #endif
408 rc = close(fd);
409 if (rc<0) {
410 errno = EIO;
411 return rc * 10 - 8;
414 rc = closedir_uncached(dir);
415 if (rc<0) {
416 errno = EIO;
417 return rc * 10 - 9;
420 return 0;
423 int ftruncate(int fd, off_t size)
425 int rc, sector;
426 struct filedesc* file = &openfiles[fd];
428 sector = size / SECTOR_SIZE;
429 if (size % SECTOR_SIZE)
430 sector++;
432 rc = fat_seek(&(file->fatfile), sector);
433 if (rc < 0) {
434 errno = EIO;
435 return rc * 10 - 1;
438 rc = fat_truncate(&(file->fatfile));
439 if (rc < 0) {
440 errno = EIO;
441 return rc * 10 - 2;
444 file->size = size;
445 #ifdef HAVE_DIRCACHE
446 dircache_update_filesize(fd, size, file->fatfile.firstcluster);
447 #endif
449 return 0;
452 static int flush_cache(int fd)
454 int rc;
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);
462 if ( rc < 0 )
463 return rc * 10 - 3;
465 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
467 if ( rc < 0 ) {
468 if(file->fatfile.eof)
469 errno = ENOSPC;
471 return rc * 10 - 2;
474 file->dirty = false;
476 return 0;
479 static int readwrite(int fd, void* buf, long count, bool write)
481 long sectors;
482 long nread=0;
483 struct filedesc* file = &openfiles[fd];
484 int rc;
486 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
487 errno = EINVAL;
488 return -1;
490 if ( !file->busy ) {
491 errno = EBADF;
492 return -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);
507 if (write) {
508 memcpy( file->cache + offs, buf, headbytes );
509 file->dirty = true;
511 else {
512 memcpy( buf, file->cache + offs, headbytes );
515 if (offs + headbytes == SECTOR_SIZE) {
516 if (file->dirty) {
517 rc = flush_cache(fd);
518 if ( rc < 0 ) {
519 errno = EIO;
520 return rc * 10 - 2;
523 file->cacheoffset = -1;
525 else {
526 file->cacheoffset += headbytes;
529 nread = headbytes;
530 count -= 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;
539 if ( sectors ) {
540 rc = fat_readwrite(&(file->fatfile), sectors,
541 (unsigned char*)buf+nread, write );
542 if ( rc < 0 ) {
543 DEBUGF("Failed read/writing %ld sectors\n",sectors);
544 errno = EIO;
545 if(write && file->fatfile.eof) {
546 DEBUGF("No space left on device\n");
547 errno = ENOSPC;
548 } else {
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;
556 #ifdef HAVE_DIRCACHE
557 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
558 #endif
560 return nread ? nread : rc * 10 - 4;
562 else {
563 if ( rc > 0 ) {
564 nread += rc * SECTOR_SIZE;
565 count -= sectors * SECTOR_SIZE;
567 /* if eof, skip tail bytes */
568 if ( rc < sectors )
569 count = 0;
571 else {
572 /* eof */
573 count=0;
576 file->cacheoffset = -1;
580 /* any tail bytes? */
581 if ( count ) {
582 if (write) {
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 );
587 if ( rc < 0 ) {
588 DEBUGF("Failed writing\n");
589 errno = EIO;
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;
596 #ifdef HAVE_DIRCACHE
597 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
598 #endif
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) /
605 SECTOR_SIZE);
606 if ( rc < 0 ) {
607 DEBUGF("fat_seek() failed\n");
608 errno = EIO;
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;
615 #ifdef HAVE_DIRCACHE
616 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
617 #endif
619 return nread ? nread : rc * 10 - 6;
622 memcpy( file->cache, (unsigned char*)buf + nread, count );
623 file->dirty = true;
625 else {
626 rc = fat_readwrite(&(file->fatfile), 1, &(file->cache),false);
627 if (rc < 1 ) {
628 DEBUGF("Failed caching sector\n");
629 errno = EIO;
630 file->fileoffset += nread;
631 file->cacheoffset = -1;
632 return nread ? nread : rc * 10 - 7;
634 memcpy( (unsigned char*)buf + nread, file->cache, count );
637 nread += 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;
648 #ifdef HAVE_DIRCACHE
649 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
650 #endif
653 return nread;
656 ssize_t write(int fd, const void* buf, size_t count)
658 if (!openfiles[fd].write) {
659 errno = EACCES;
660 return -1;
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)
673 off_t pos;
674 long newsector;
675 long oldsector;
676 int sectoroffset;
677 int rc;
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) {
683 errno = EINVAL;
684 return -1;
686 if ( !file->busy ) {
687 errno = EBADF;
688 return -1;
691 switch ( whence ) {
692 case SEEK_SET:
693 pos = offset;
694 break;
696 case SEEK_CUR:
697 pos = file->fileoffset + offset;
698 break;
700 case SEEK_END:
701 pos = file->size + offset;
702 break;
704 default:
705 errno = EINVAL;
706 return -2;
708 if ((pos < 0) || (pos > file->size)) {
709 errno = EINVAL;
710 return -3;
713 /* new sector? */
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 ) {
722 if (file->dirty) {
723 rc = flush_cache(fd);
724 if (rc < 0)
725 return rc * 10 - 5;
728 rc = fat_seek(&(file->fatfile), newsector);
729 if ( rc < 0 ) {
730 errno = EIO;
731 return rc * 10 - 4;
734 if ( sectoroffset ) {
735 rc = fat_readwrite(&(file->fatfile), 1,
736 &(file->cache),false);
737 if ( rc < 0 ) {
738 errno = EIO;
739 return rc * 10 - 6;
741 file->cacheoffset = sectoroffset;
743 else
744 file->cacheoffset = -1;
746 else
747 if ( file->cacheoffset != -1 )
748 file->cacheoffset = sectoroffset;
750 file->fileoffset = pos;
752 return pos;
755 off_t filesize(int fd)
757 struct filedesc* file = &openfiles[fd];
759 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
760 errno = EINVAL;
761 return -1;
763 if ( !file->busy ) {
764 errno = EBADF;
765 return -1;
768 return file->size;
772 #ifdef HAVE_HOTSWAP
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;
777 int fd;
778 int closed = 0;
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 */
784 closed++;
787 return closed; /* return how many we did */
789 #endif /* #ifdef HAVE_HOTSWAP */