Fix a warning.
[kugel-rb.git] / firmware / common / file.c
blob8f6bfa051b51ca2425b987f19c7f250f5c27773c
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] CACHEALIGN_ATTR;
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;
52 } CACHEALIGN_ATTR;
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)
65 DIR_UNCACHED* dir;
66 struct dirent_uncached* entry;
67 int fd;
68 int pathnamesize = strlen(pathname) + 1;
69 char pathnamecopy[pathnamesize];
70 char* name;
71 struct filedesc* file = NULL;
72 int rc;
73 #ifndef HAVE_DIRCACHE
74 (void)use_cache;
75 #endif
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");
82 errno = EINVAL;
83 return -1;
86 /* find a free file descriptor */
87 for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
88 if ( !openfiles[fd].busy )
89 break;
91 if ( fd == MAX_OPEN_FILES ) {
92 DEBUGF("Too many files open\n");
93 errno = EMFILE;
94 return -2;
97 file = &openfiles[fd];
98 memset(file, 0, sizeof(struct filedesc));
100 if (flags & (O_RDWR | O_WRONLY)) {
101 file->write = true;
103 if (flags & O_TRUNC)
104 file->trunc = true;
106 file->busy = true;
108 #ifdef HAVE_DIRCACHE
109 if (dircache_is_enabled() && !file->write && use_cache)
111 const struct dircache_entry *ce;
112 # ifdef HAVE_MULTIVOLUME
113 int volume = strip_volume(pathname, pathnamecopy);
114 # endif
116 ce = dircache_get_entry_ptr(pathname);
117 if (!ce)
119 errno = ENOENT;
120 file->busy = false;
121 return -7;
124 fat_open(IF_MV2(volume,)
125 ce->startcluster,
126 &(file->fatfile),
127 NULL);
128 file->size = ce->info.size;
129 file->attr = ce->info.attribute;
130 file->cacheoffset = -1;
131 file->fileoffset = 0;
133 return fd;
135 #endif
137 strlcpy(pathnamecopy, pathname, pathnamesize);
139 /* locate filename */
140 name=strrchr(pathnamecopy+1,'/');
141 if ( name ) {
142 *name = 0;
143 dir = opendir_uncached(pathnamecopy);
144 *name = '/';
145 name++;
147 else {
148 dir = opendir_uncached("/");
149 name = pathnamecopy+1;
151 if (!dir) {
152 DEBUGF("Failed opening dir\n");
153 errno = EIO;
154 file->busy = false;
155 return -4;
158 if(name[0] == 0) {
159 DEBUGF("Empty file name\n");
160 errno = EINVAL;
161 file->busy = false;
162 closedir_uncached(dir);
163 return -5;
166 /* scan dir for name */
167 while ((entry = readdir_uncached(dir))) {
168 if ( !strcasecmp(name, entry->d_name) ) {
169 fat_open(IF_MV2(dir->fatdir.file.volume,)
170 entry->startcluster,
171 &(file->fatfile),
172 &(dir->fatdir));
173 file->size = file->trunc ? 0 : entry->info.size;
174 file->attr = entry->info.attribute;
175 break;
179 if ( !entry ) {
180 LDEBUGF("Didn't find file %s\n",name);
181 if ( file->write && (flags & O_CREAT) ) {
182 rc = fat_create_file(name,
183 &(file->fatfile),
184 &(dir->fatdir));
185 if (rc < 0) {
186 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
187 errno = EIO;
188 file->busy = false;
189 closedir_uncached(dir);
190 return rc * 10 - 6;
192 #ifdef HAVE_DIRCACHE
193 dircache_add_file(pathname, file->fatfile.firstcluster);
194 #endif
195 file->size = 0;
196 file->attr = 0;
198 else {
199 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
200 errno = ENOENT;
201 file->busy = false;
202 closedir_uncached(dir);
203 return -7;
205 } else {
206 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
207 errno = EISDIR;
208 file->busy = false;
209 closedir_uncached(dir);
210 return -8;
213 closedir_uncached(dir);
215 file->cacheoffset = -1;
216 file->fileoffset = 0;
218 if (file->write && (flags & O_APPEND)) {
219 rc = lseek(fd,0,SEEK_END);
220 if (rc < 0 )
221 return rc * 10 - 9;
224 #ifdef HAVE_DIRCACHE
225 if (file->write)
226 dircache_bind(fd, pathname);
227 #endif
229 return fd;
232 int file_open(const char* pathname, int flags)
234 /* By default, use the dircache if available. */
235 return open_internal(pathname, flags, true);
238 int close(int fd)
240 struct filedesc* file = &openfiles[fd];
241 int rc = 0;
243 LDEBUGF("close(%d)\n", fd);
245 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
246 errno = EINVAL;
247 return -1;
249 if (!file->busy) {
250 errno = EBADF;
251 return -2;
253 if (file->write) {
254 rc = fsync(fd);
255 if (rc < 0)
256 return rc * 10 - 3;
257 #ifdef HAVE_DIRCACHE
258 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
259 dircache_update_filetime(fd);
260 #endif
263 file->busy = false;
264 return 0;
267 int fsync(int fd)
269 struct filedesc* file = &openfiles[fd];
270 int rc = 0;
272 LDEBUGF("fsync(%d)\n", fd);
274 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
275 errno = EINVAL;
276 return -1;
278 if (!file->busy) {
279 errno = EBADF;
280 return -2;
282 if (file->write) {
283 /* flush sector cache */
284 if ( file->dirty ) {
285 rc = flush_cache(fd);
286 if (rc < 0)
288 /* when failing, try to close the file anyway */
289 fat_closewrite(&(file->fatfile), file->size, file->attr);
290 return rc * 10 - 3;
294 /* truncate? */
295 if (file->trunc) {
296 rc = ftruncate(fd, file->size);
297 if (rc < 0)
299 /* when failing, try to close the file anyway */
300 fat_closewrite(&(file->fatfile), file->size, file->attr);
301 return rc * 10 - 4;
305 /* tie up all loose ends */
306 rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
307 if (rc < 0)
308 return rc * 10 - 5;
310 return 0;
313 int remove(const char* name)
315 int rc;
316 struct filedesc* file;
317 /* Can't use dircache now, because we need to access the fat structures. */
318 int fd = open_internal(name, O_WRONLY, false);
319 if ( fd < 0 )
320 return fd * 10 - 1;
322 file = &openfiles[fd];
323 #ifdef HAVE_DIRCACHE
324 dircache_remove(name);
325 #endif
326 rc = fat_remove(&(file->fatfile));
327 if ( rc < 0 ) {
328 DEBUGF("Failed removing file: %d\n", rc);
329 errno = EIO;
330 return rc * 10 - 3;
333 file->size = 0;
335 rc = close(fd);
336 if (rc<0)
337 return rc * 10 - 4;
339 return 0;
342 int rename(const char* path, const char* newpath)
344 int rc, fd;
345 DIR_UNCACHED* dir;
346 char* nameptr;
347 char* dirptr;
348 struct filedesc* file;
349 char newpath2[MAX_PATH];
351 /* verify new path does not already exist */
352 /* If it is a directory, errno == EISDIR if the name exists */
353 fd = open(newpath, O_RDONLY);
354 if ( fd >= 0 || errno == EISDIR) {
355 close(fd);
356 errno = EBUSY;
357 return -1;
359 close(fd);
361 fd = open_internal(path, O_RDONLY, false);
362 if ( fd < 0 ) {
363 errno = EIO;
364 return fd * 10 - 2;
367 /* extract new file name */
368 nameptr = strrchr(newpath,'/');
369 if (nameptr)
370 nameptr++;
371 else {
372 close(fd);
373 return - 3;
376 /* Extract new path */
377 strcpy(newpath2, newpath);
379 dirptr = strrchr(newpath2,'/');
380 if(dirptr)
381 *dirptr = 0;
382 else {
383 close(fd);
384 return - 4;
387 dirptr = newpath2;
389 if(strlen(dirptr) == 0) {
390 dirptr = "/";
393 dir = opendir_uncached(dirptr);
394 if(!dir) {
395 close(fd);
396 return - 5;
399 file = &openfiles[fd];
401 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
402 file->size, file->attr);
403 #ifdef HAVE_MULTIVOLUME
404 if ( rc == -1) {
405 close(fd);
406 closedir_uncached(dir);
407 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
408 errno = EXDEV;
409 return -6;
411 #endif
412 if ( rc < 0 ) {
413 close(fd);
414 closedir_uncached(dir);
415 DEBUGF("Failed renaming file: %d\n", rc);
416 errno = EIO;
417 return rc * 10 - 7;
420 #ifdef HAVE_DIRCACHE
421 dircache_rename(path, newpath);
422 #endif
424 rc = close(fd);
425 if (rc<0) {
426 closedir_uncached(dir);
427 errno = EIO;
428 return rc * 10 - 8;
431 rc = closedir_uncached(dir);
432 if (rc<0) {
433 errno = EIO;
434 return rc * 10 - 9;
437 return 0;
440 int ftruncate(int fd, off_t size)
442 int rc, sector;
443 struct filedesc* file = &openfiles[fd];
445 sector = size / SECTOR_SIZE;
446 if (size % SECTOR_SIZE)
447 sector++;
449 rc = fat_seek(&(file->fatfile), sector);
450 if (rc < 0) {
451 errno = EIO;
452 return rc * 10 - 1;
455 rc = fat_truncate(&(file->fatfile));
456 if (rc < 0) {
457 errno = EIO;
458 return rc * 10 - 2;
461 file->size = size;
462 #ifdef HAVE_DIRCACHE
463 dircache_update_filesize(fd, size, file->fatfile.firstcluster);
464 #endif
466 return 0;
469 static int flush_cache(int fd)
471 int rc;
472 struct filedesc* file = &openfiles[fd];
473 long sector = file->fileoffset / SECTOR_SIZE;
475 DEBUGF("Flushing dirty sector cache\n");
477 /* make sure we are on correct sector */
478 rc = fat_seek(&(file->fatfile), sector);
479 if ( rc < 0 )
480 return rc * 10 - 3;
482 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
484 if ( rc < 0 ) {
485 if(file->fatfile.eof)
486 errno = ENOSPC;
488 return rc * 10 - 2;
491 file->dirty = false;
493 return 0;
496 static int readwrite(int fd, void* buf, long count, bool write)
498 long sectors;
499 long nread=0;
500 struct filedesc* file;
501 int rc;
502 #ifdef STORAGE_NEEDS_ALIGN
503 long i;
504 int rc2;
505 #endif
507 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
508 errno = EINVAL;
509 return -1;
512 file = &openfiles[fd];
514 if ( !file->busy ) {
515 errno = EBADF;
516 return -1;
519 if(file->attr & FAT_ATTR_DIRECTORY) {
520 errno = EISDIR;
521 return -1;
524 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
525 fd,(long)buf,count,write?"write":"read");
527 /* attempt to read past EOF? */
528 if (!write && count > file->size - file->fileoffset)
529 count = file->size - file->fileoffset;
531 /* any head bytes? */
532 if ( file->cacheoffset != -1 ) {
533 int offs = file->cacheoffset;
534 int headbytes = MIN(count, SECTOR_SIZE - offs);
536 if (write) {
537 memcpy( file->cache + offs, buf, headbytes );
538 file->dirty = true;
540 else {
541 memcpy( buf, file->cache + offs, headbytes );
544 if (offs + headbytes == SECTOR_SIZE) {
545 if (file->dirty) {
546 rc = flush_cache(fd);
547 if ( rc < 0 ) {
548 errno = EIO;
549 return rc * 10 - 2;
552 file->cacheoffset = -1;
554 else {
555 file->cacheoffset += headbytes;
558 nread = headbytes;
559 count -= headbytes;
562 /* If the buffer has been modified, either it has been flushed already
563 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
564 * more data to follow in this call). Do NOT flush here. */
566 /* read/write whole sectors right into/from the supplied buffer */
567 sectors = count / SECTOR_SIZE;
568 rc = 0;
569 if ( sectors ) {
570 #ifdef STORAGE_NEEDS_ALIGN
571 if (((uint32_t)buf + nread) & (CACHEALIGN_SIZE - 1))
572 for (i = 0; i < sectors; i++)
574 if (write) memcpy(file->cache, buf+nread+i*SECTOR_SIZE, SECTOR_SIZE);
575 rc2 = fat_readwrite(&(file->fatfile), 1, file->cache, write );
576 if (rc2 < 0)
578 rc = rc2;
579 break;
581 else rc += rc2;
582 if (!write) memcpy(buf+nread+i*SECTOR_SIZE, file->cache, SECTOR_SIZE);
584 else
585 #endif
586 rc = fat_readwrite(&(file->fatfile), sectors, (unsigned char*)buf+nread, write );
587 if ( rc < 0 ) {
588 DEBUGF("Failed read/writing %ld sectors\n",sectors);
589 errno = EIO;
590 if(write && file->fatfile.eof) {
591 DEBUGF("No space left on device\n");
592 errno = ENOSPC;
593 } else {
594 file->fileoffset += nread;
596 file->cacheoffset = -1;
597 /* adjust file size to length written */
598 if ( write && file->fileoffset > file->size )
600 file->size = file->fileoffset;
601 #ifdef HAVE_DIRCACHE
602 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
603 #endif
605 return nread ? nread : rc * 10 - 4;
607 else {
608 if ( rc > 0 ) {
609 nread += rc * SECTOR_SIZE;
610 count -= sectors * SECTOR_SIZE;
612 /* if eof, skip tail bytes */
613 if ( rc < sectors )
614 count = 0;
616 else {
617 /* eof */
618 count=0;
621 file->cacheoffset = -1;
625 /* any tail bytes? */
626 if ( count ) {
627 if (write) {
628 if ( file->fileoffset + nread < file->size ) {
629 /* sector is only partially filled. copy-back from disk */
630 LDEBUGF("Copy-back tail cache\n");
631 rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
632 if ( rc < 0 ) {
633 DEBUGF("Failed writing\n");
634 errno = EIO;
635 file->fileoffset += nread;
636 file->cacheoffset = -1;
637 /* adjust file size to length written */
638 if ( file->fileoffset > file->size )
640 file->size = file->fileoffset;
641 #ifdef HAVE_DIRCACHE
642 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
643 #endif
645 return nread ? nread : rc * 10 - 5;
647 /* seek back one sector to put file position right */
648 rc = fat_seek(&(file->fatfile),
649 (file->fileoffset + nread) /
650 SECTOR_SIZE);
651 if ( rc < 0 ) {
652 DEBUGF("fat_seek() failed\n");
653 errno = EIO;
654 file->fileoffset += nread;
655 file->cacheoffset = -1;
656 /* adjust file size to length written */
657 if ( file->fileoffset > file->size )
659 file->size = file->fileoffset;
660 #ifdef HAVE_DIRCACHE
661 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
662 #endif
664 return nread ? nread : rc * 10 - 6;
667 memcpy( file->cache, (unsigned char*)buf + nread, count );
668 file->dirty = true;
670 else {
671 rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
672 if (rc < 1 ) {
673 DEBUGF("Failed caching sector\n");
674 errno = EIO;
675 file->fileoffset += nread;
676 file->cacheoffset = -1;
677 return nread ? nread : rc * 10 - 7;
679 memcpy( (unsigned char*)buf + nread, file->cache, count );
682 nread += count;
683 file->cacheoffset = count;
686 file->fileoffset += nread;
687 LDEBUGF("fileoffset: %ld\n", file->fileoffset);
689 /* adjust file size to length written */
690 if ( write && file->fileoffset > file->size )
692 file->size = file->fileoffset;
693 #ifdef HAVE_DIRCACHE
694 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
695 #endif
698 return nread;
701 ssize_t write(int fd, const void* buf, size_t count)
703 if (!openfiles[fd].write) {
704 errno = EACCES;
705 return -1;
707 return readwrite(fd, (void *)buf, count, true);
710 ssize_t read(int fd, void* buf, size_t count)
712 return readwrite(fd, buf, count, false);
716 off_t lseek(int fd, off_t offset, int whence)
718 off_t pos;
719 long newsector;
720 long oldsector;
721 int sectoroffset;
722 int rc;
723 struct filedesc* file = &openfiles[fd];
725 LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence);
727 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
728 errno = EINVAL;
729 return -1;
731 if ( !file->busy ) {
732 errno = EBADF;
733 return -1;
736 switch ( whence ) {
737 case SEEK_SET:
738 pos = offset;
739 break;
741 case SEEK_CUR:
742 pos = file->fileoffset + offset;
743 break;
745 case SEEK_END:
746 pos = file->size + offset;
747 break;
749 default:
750 errno = EINVAL;
751 return -2;
753 if ((pos < 0) || (pos > file->size)) {
754 errno = EINVAL;
755 return -3;
758 /* new sector? */
759 newsector = pos / SECTOR_SIZE;
760 oldsector = file->fileoffset / SECTOR_SIZE;
761 sectoroffset = pos % SECTOR_SIZE;
763 if ( (newsector != oldsector) ||
764 ((file->cacheoffset==-1) && sectoroffset) ) {
766 if ( newsector != oldsector ) {
767 if (file->dirty) {
768 rc = flush_cache(fd);
769 if (rc < 0)
770 return rc * 10 - 5;
773 rc = fat_seek(&(file->fatfile), newsector);
774 if ( rc < 0 ) {
775 errno = EIO;
776 return rc * 10 - 4;
779 if ( sectoroffset ) {
780 rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
781 if ( rc < 0 ) {
782 errno = EIO;
783 return rc * 10 - 6;
785 file->cacheoffset = sectoroffset;
787 else
788 file->cacheoffset = -1;
790 else
791 if ( file->cacheoffset != -1 )
792 file->cacheoffset = sectoroffset;
794 file->fileoffset = pos;
796 return pos;
799 off_t filesize(int fd)
801 struct filedesc* file = &openfiles[fd];
803 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
804 errno = EINVAL;
805 return -1;
807 if ( !file->busy ) {
808 errno = EBADF;
809 return -1;
812 return file->size;
816 /* release all file handles on a given volume "by force", to avoid leaks */
817 int release_files(int volume)
819 struct filedesc* pfile = openfiles;
820 int fd;
821 int closed = 0;
822 for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
824 #ifdef HAVE_MULTIVOLUME
825 if (pfile->fatfile.volume == volume)
826 #else
827 (void)volume;
828 #endif
830 pfile->busy = false; /* mark as available, no further action */
831 closed++;
834 return closed; /* return how many we did */