Theme Editor: Added extern C declarations to header files
[kugel-rb.git] / firmware / common / file.c
blob6beec9f606ea68e9e9705848e4197b8d56a02405
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include <string.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include "file.h"
25 #include "fat.h"
26 #include "dir_uncached.h"
27 #include "debug.h"
28 #include "dircache.h"
29 #include "filefuncs.h"
30 #include "system.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.
41 struct filedesc {
42 unsigned char cache[SECTOR_SIZE];
43 int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
44 long fileoffset;
45 long size;
46 int attr;
47 struct fat_file fatfile;
48 bool busy;
49 bool write;
50 bool dirty;
51 bool trunc;
54 static struct filedesc openfiles[MAX_OPEN_FILES];
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)
65 DIR_UNCACHED* dir;
66 struct dirent_uncached* entry;
67 int fd;
68 char pathnamecopy[MAX_PATH];
69 char* name;
70 struct filedesc* file = NULL;
71 int rc;
72 #ifndef HAVE_DIRCACHE
73 (void)use_cache;
74 #endif
76 LDEBUGF("open(\"%s\",%d)\n",pathname,flags);
78 if ( pathname[0] != '/' ) {
79 DEBUGF("'%s' is not an absolute path.\n",pathname);
80 DEBUGF("Only absolute pathnames supported at the moment\n");
81 errno = EINVAL;
82 return -1;
85 /* find a free file descriptor */
86 for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
87 if ( !openfiles[fd].busy )
88 break;
90 if ( fd == MAX_OPEN_FILES ) {
91 DEBUGF("Too many files open\n");
92 errno = EMFILE;
93 return -2;
96 file = &openfiles[fd];
97 memset(file, 0, sizeof(struct filedesc));
99 if (flags & (O_RDWR | O_WRONLY)) {
100 file->write = true;
102 if (flags & O_TRUNC)
103 file->trunc = true;
105 file->busy = true;
107 #ifdef HAVE_DIRCACHE
108 if (dircache_is_enabled() && !file->write && use_cache)
110 const struct dircache_entry *ce;
111 # ifdef HAVE_MULTIVOLUME
112 int volume = strip_volume(pathname, pathnamecopy);
113 # endif
115 ce = dircache_get_entry_ptr(pathname);
116 if (!ce)
118 errno = ENOENT;
119 file->busy = false;
120 return -7;
123 fat_open(IF_MV2(volume,)
124 ce->startcluster,
125 &(file->fatfile),
126 NULL);
127 file->size = ce->size;
128 file->attr = ce->attribute;
129 file->cacheoffset = -1;
130 file->fileoffset = 0;
132 return fd;
134 #endif
136 strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy));
138 /* locate filename */
139 name=strrchr(pathnamecopy+1,'/');
140 if ( name ) {
141 *name = 0;
142 dir = opendir_uncached(pathnamecopy);
143 *name = '/';
144 name++;
146 else {
147 dir = opendir_uncached("/");
148 name = pathnamecopy+1;
150 if (!dir) {
151 DEBUGF("Failed opening dir\n");
152 errno = EIO;
153 file->busy = false;
154 return -4;
157 if(name[0] == 0) {
158 DEBUGF("Empty file name\n");
159 errno = EINVAL;
160 file->busy = false;
161 closedir_uncached(dir);
162 return -5;
165 /* scan dir for name */
166 while ((entry = readdir_uncached(dir))) {
167 if ( !strcasecmp(name, entry->d_name) ) {
168 fat_open(IF_MV2(dir->fatdir.file.volume,)
169 entry->startcluster,
170 &(file->fatfile),
171 &(dir->fatdir));
172 file->size = file->trunc ? 0 : entry->size;
173 file->attr = entry->attribute;
174 break;
178 if ( !entry ) {
179 LDEBUGF("Didn't find file %s\n",name);
180 if ( file->write && (flags & O_CREAT) ) {
181 rc = fat_create_file(name,
182 &(file->fatfile),
183 &(dir->fatdir));
184 if (rc < 0) {
185 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
186 errno = EIO;
187 file->busy = false;
188 closedir_uncached(dir);
189 return rc * 10 - 6;
191 #ifdef HAVE_DIRCACHE
192 dircache_add_file(pathname, file->fatfile.firstcluster);
193 #endif
194 file->size = 0;
195 file->attr = 0;
197 else {
198 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
199 errno = ENOENT;
200 file->busy = false;
201 closedir_uncached(dir);
202 return -7;
204 } else {
205 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
206 errno = EISDIR;
207 file->busy = false;
208 closedir_uncached(dir);
209 return -8;
212 closedir_uncached(dir);
214 file->cacheoffset = -1;
215 file->fileoffset = 0;
217 if (file->write && (flags & O_APPEND)) {
218 rc = lseek(fd,0,SEEK_END);
219 if (rc < 0 )
220 return rc * 10 - 9;
223 #ifdef HAVE_DIRCACHE
224 if (file->write)
225 dircache_bind(fd, pathname);
226 #endif
228 return fd;
231 int file_open(const char* pathname, int flags)
233 /* By default, use the dircache if available. */
234 return open_internal(pathname, flags, true);
237 int close(int fd)
239 struct filedesc* file = &openfiles[fd];
240 int rc = 0;
242 LDEBUGF("close(%d)\n", fd);
244 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
245 errno = EINVAL;
246 return -1;
248 if (!file->busy) {
249 errno = EBADF;
250 return -2;
252 if (file->write) {
253 rc = fsync(fd);
254 if (rc < 0)
255 return rc * 10 - 3;
256 #ifdef HAVE_DIRCACHE
257 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
258 dircache_update_filetime(fd);
259 #endif
262 file->busy = false;
263 return 0;
266 int fsync(int fd)
268 struct filedesc* file = &openfiles[fd];
269 int rc = 0;
271 LDEBUGF("fsync(%d)\n", fd);
273 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
274 errno = EINVAL;
275 return -1;
277 if (!file->busy) {
278 errno = EBADF;
279 return -2;
281 if (file->write) {
282 /* flush sector cache */
283 if ( file->dirty ) {
284 rc = flush_cache(fd);
285 if (rc < 0)
287 /* when failing, try to close the file anyway */
288 fat_closewrite(&(file->fatfile), file->size, file->attr);
289 return rc * 10 - 3;
293 /* truncate? */
294 if (file->trunc) {
295 rc = ftruncate(fd, file->size);
296 if (rc < 0)
298 /* when failing, try to close the file anyway */
299 fat_closewrite(&(file->fatfile), file->size, file->attr);
300 return rc * 10 - 4;
304 /* tie up all loose ends */
305 rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
306 if (rc < 0)
307 return rc * 10 - 5;
309 return 0;
312 int remove(const char* name)
314 int rc;
315 struct filedesc* file;
316 /* Can't use dircache now, because we need to access the fat structures. */
317 int fd = open_internal(name, O_WRONLY, false);
318 if ( fd < 0 )
319 return fd * 10 - 1;
321 file = &openfiles[fd];
322 #ifdef HAVE_DIRCACHE
323 dircache_remove(name);
324 #endif
325 rc = fat_remove(&(file->fatfile));
326 if ( rc < 0 ) {
327 DEBUGF("Failed removing file: %d\n", rc);
328 errno = EIO;
329 return rc * 10 - 3;
332 file->size = 0;
334 rc = close(fd);
335 if (rc<0)
336 return rc * 10 - 4;
338 return 0;
341 int rename(const char* path, const char* newpath)
343 int rc, fd;
344 DIR_UNCACHED* dir;
345 char* nameptr;
346 char* dirptr;
347 struct filedesc* file;
348 char newpath2[MAX_PATH];
350 /* verify new path does not already exist */
351 /* If it is a directory, errno == EISDIR if the name exists */
352 fd = open(newpath, O_RDONLY);
353 if ( fd >= 0 || errno == EISDIR) {
354 close(fd);
355 errno = EBUSY;
356 return -1;
358 close(fd);
360 fd = open_internal(path, O_RDONLY, false);
361 if ( fd < 0 ) {
362 errno = EIO;
363 return fd * 10 - 2;
366 /* extract new file name */
367 nameptr = strrchr(newpath,'/');
368 if (nameptr)
369 nameptr++;
370 else {
371 close(fd);
372 return - 3;
375 /* Extract new path */
376 strcpy(newpath2, newpath);
378 dirptr = strrchr(newpath2,'/');
379 if(dirptr)
380 *dirptr = 0;
381 else {
382 close(fd);
383 return - 4;
386 dirptr = newpath2;
388 if(strlen(dirptr) == 0) {
389 dirptr = "/";
392 dir = opendir_uncached(dirptr);
393 if(!dir) {
394 close(fd);
395 return - 5;
398 file = &openfiles[fd];
400 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
401 file->size, file->attr);
402 #ifdef HAVE_MULTIVOLUME
403 if ( rc == -1) {
404 close(fd);
405 closedir_uncached(dir);
406 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
407 errno = EXDEV;
408 return -6;
410 #endif
411 if ( rc < 0 ) {
412 close(fd);
413 closedir_uncached(dir);
414 DEBUGF("Failed renaming file: %d\n", rc);
415 errno = EIO;
416 return rc * 10 - 7;
419 #ifdef HAVE_DIRCACHE
420 dircache_rename(path, newpath);
421 #endif
423 rc = close(fd);
424 if (rc<0) {
425 closedir_uncached(dir);
426 errno = EIO;
427 return rc * 10 - 8;
430 rc = closedir_uncached(dir);
431 if (rc<0) {
432 errno = EIO;
433 return rc * 10 - 9;
436 return 0;
439 int ftruncate(int fd, off_t size)
441 int rc, sector;
442 struct filedesc* file = &openfiles[fd];
444 sector = size / SECTOR_SIZE;
445 if (size % SECTOR_SIZE)
446 sector++;
448 rc = fat_seek(&(file->fatfile), sector);
449 if (rc < 0) {
450 errno = EIO;
451 return rc * 10 - 1;
454 rc = fat_truncate(&(file->fatfile));
455 if (rc < 0) {
456 errno = EIO;
457 return rc * 10 - 2;
460 file->size = size;
461 #ifdef HAVE_DIRCACHE
462 dircache_update_filesize(fd, size, file->fatfile.firstcluster);
463 #endif
465 return 0;
468 static int flush_cache(int fd)
470 int rc;
471 struct filedesc* file = &openfiles[fd];
472 long sector = file->fileoffset / SECTOR_SIZE;
474 DEBUGF("Flushing dirty sector cache\n");
476 /* make sure we are on correct sector */
477 rc = fat_seek(&(file->fatfile), sector);
478 if ( rc < 0 )
479 return rc * 10 - 3;
481 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
483 if ( rc < 0 ) {
484 if(file->fatfile.eof)
485 errno = ENOSPC;
487 return rc * 10 - 2;
490 file->dirty = false;
492 return 0;
495 static int readwrite(int fd, void* buf, long count, bool write)
497 long sectors;
498 long nread=0;
499 struct filedesc* file;
500 int rc;
502 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
503 errno = EINVAL;
504 return -1;
507 file = &openfiles[fd];
509 if ( !file->busy ) {
510 errno = EBADF;
511 return -1;
514 if(file->attr & FAT_ATTR_DIRECTORY) {
515 errno = EISDIR;
516 return -1;
519 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
520 fd,(long)buf,count,write?"write":"read");
522 /* attempt to read past EOF? */
523 if (!write && count > file->size - file->fileoffset)
524 count = file->size - file->fileoffset;
526 /* any head bytes? */
527 if ( file->cacheoffset != -1 ) {
528 int offs = file->cacheoffset;
529 int headbytes = MIN(count, SECTOR_SIZE - offs);
531 if (write) {
532 memcpy( file->cache + offs, buf, headbytes );
533 file->dirty = true;
535 else {
536 memcpy( buf, file->cache + offs, headbytes );
539 if (offs + headbytes == SECTOR_SIZE) {
540 if (file->dirty) {
541 rc = flush_cache(fd);
542 if ( rc < 0 ) {
543 errno = EIO;
544 return rc * 10 - 2;
547 file->cacheoffset = -1;
549 else {
550 file->cacheoffset += headbytes;
553 nread = headbytes;
554 count -= headbytes;
557 /* If the buffer has been modified, either it has been flushed already
558 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
559 * more data to follow in this call). Do NOT flush here. */
561 /* read/write whole sectors right into/from the supplied buffer */
562 sectors = count / SECTOR_SIZE;
563 if ( sectors ) {
564 rc = fat_readwrite(&(file->fatfile), sectors,
565 (unsigned char*)buf+nread, write );
566 if ( rc < 0 ) {
567 DEBUGF("Failed read/writing %ld sectors\n",sectors);
568 errno = EIO;
569 if(write && file->fatfile.eof) {
570 DEBUGF("No space left on device\n");
571 errno = ENOSPC;
572 } else {
573 file->fileoffset += nread;
575 file->cacheoffset = -1;
576 /* adjust file size to length written */
577 if ( write && file->fileoffset > file->size )
579 file->size = file->fileoffset;
580 #ifdef HAVE_DIRCACHE
581 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
582 #endif
584 return nread ? nread : rc * 10 - 4;
586 else {
587 if ( rc > 0 ) {
588 nread += rc * SECTOR_SIZE;
589 count -= sectors * SECTOR_SIZE;
591 /* if eof, skip tail bytes */
592 if ( rc < sectors )
593 count = 0;
595 else {
596 /* eof */
597 count=0;
600 file->cacheoffset = -1;
604 /* any tail bytes? */
605 if ( count ) {
606 if (write) {
607 if ( file->fileoffset + nread < file->size ) {
608 /* sector is only partially filled. copy-back from disk */
609 LDEBUGF("Copy-back tail cache\n");
610 rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
611 if ( rc < 0 ) {
612 DEBUGF("Failed writing\n");
613 errno = EIO;
614 file->fileoffset += nread;
615 file->cacheoffset = -1;
616 /* adjust file size to length written */
617 if ( file->fileoffset > file->size )
619 file->size = file->fileoffset;
620 #ifdef HAVE_DIRCACHE
621 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
622 #endif
624 return nread ? nread : rc * 10 - 5;
626 /* seek back one sector to put file position right */
627 rc = fat_seek(&(file->fatfile),
628 (file->fileoffset + nread) /
629 SECTOR_SIZE);
630 if ( rc < 0 ) {
631 DEBUGF("fat_seek() failed\n");
632 errno = EIO;
633 file->fileoffset += nread;
634 file->cacheoffset = -1;
635 /* adjust file size to length written */
636 if ( file->fileoffset > file->size )
638 file->size = file->fileoffset;
639 #ifdef HAVE_DIRCACHE
640 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
641 #endif
643 return nread ? nread : rc * 10 - 6;
646 memcpy( file->cache, (unsigned char*)buf + nread, count );
647 file->dirty = true;
649 else {
650 rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
651 if (rc < 1 ) {
652 DEBUGF("Failed caching sector\n");
653 errno = EIO;
654 file->fileoffset += nread;
655 file->cacheoffset = -1;
656 return nread ? nread : rc * 10 - 7;
658 memcpy( (unsigned char*)buf + nread, file->cache, count );
661 nread += count;
662 file->cacheoffset = count;
665 file->fileoffset += nread;
666 LDEBUGF("fileoffset: %ld\n", file->fileoffset);
668 /* adjust file size to length written */
669 if ( write && file->fileoffset > file->size )
671 file->size = file->fileoffset;
672 #ifdef HAVE_DIRCACHE
673 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
674 #endif
677 return nread;
680 ssize_t write(int fd, const void* buf, size_t count)
682 if (!openfiles[fd].write) {
683 errno = EACCES;
684 return -1;
686 return readwrite(fd, (void *)buf, count, true);
689 ssize_t read(int fd, void* buf, size_t count)
691 return readwrite(fd, buf, count, false);
695 off_t lseek(int fd, off_t offset, int whence)
697 off_t pos;
698 long newsector;
699 long oldsector;
700 int sectoroffset;
701 int rc;
702 struct filedesc* file = &openfiles[fd];
704 LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence);
706 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
707 errno = EINVAL;
708 return -1;
710 if ( !file->busy ) {
711 errno = EBADF;
712 return -1;
715 switch ( whence ) {
716 case SEEK_SET:
717 pos = offset;
718 break;
720 case SEEK_CUR:
721 pos = file->fileoffset + offset;
722 break;
724 case SEEK_END:
725 pos = file->size + offset;
726 break;
728 default:
729 errno = EINVAL;
730 return -2;
732 if ((pos < 0) || (pos > file->size)) {
733 errno = EINVAL;
734 return -3;
737 /* new sector? */
738 newsector = pos / SECTOR_SIZE;
739 oldsector = file->fileoffset / SECTOR_SIZE;
740 sectoroffset = pos % SECTOR_SIZE;
742 if ( (newsector != oldsector) ||
743 ((file->cacheoffset==-1) && sectoroffset) ) {
745 if ( newsector != oldsector ) {
746 if (file->dirty) {
747 rc = flush_cache(fd);
748 if (rc < 0)
749 return rc * 10 - 5;
752 rc = fat_seek(&(file->fatfile), newsector);
753 if ( rc < 0 ) {
754 errno = EIO;
755 return rc * 10 - 4;
758 if ( sectoroffset ) {
759 rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
760 if ( rc < 0 ) {
761 errno = EIO;
762 return rc * 10 - 6;
764 file->cacheoffset = sectoroffset;
766 else
767 file->cacheoffset = -1;
769 else
770 if ( file->cacheoffset != -1 )
771 file->cacheoffset = sectoroffset;
773 file->fileoffset = pos;
775 return pos;
778 off_t filesize(int fd)
780 struct filedesc* file = &openfiles[fd];
782 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
783 errno = EINVAL;
784 return -1;
786 if ( !file->busy ) {
787 errno = EBADF;
788 return -1;
791 return file->size;
795 #ifdef HAVE_HOTSWAP
796 /* release all file handles on a given volume "by force", to avoid leaks */
797 int release_files(int volume)
799 struct filedesc* pfile = openfiles;
800 int fd;
801 int closed = 0;
802 for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
804 #ifdef HAVE_MULTIVOLUME
805 if (pfile->fatfile.volume == volume)
806 #else
807 (void)volume;
808 #endif
810 pfile->busy = false; /* mark as available, no further action */
811 closed++;
814 return closed; /* return how many we did */
816 #endif /* #ifdef HAVE_HOTSWAP */