FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / firmware / common / file.c
blob3d7722f687ef633770208b51168b0ffba2a8a245
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 "system.h"
32 These functions provide a roughly POSIX-compatible file IO API.
34 Since the fat32 driver only manages sectors, we maintain a one-sector
35 cache for each open file. This way we can provide byte access without
36 having to re-read the sector each time.
37 The penalty is the RAM used for the cache and slightly more complex code.
40 struct filedesc {
41 unsigned char cache[SECTOR_SIZE];
42 int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
43 long fileoffset;
44 long size;
45 int attr;
46 struct fat_file fatfile;
47 bool busy;
48 bool write;
49 bool dirty;
50 bool trunc;
53 static struct filedesc openfiles[MAX_OPEN_FILES];
55 static int flush_cache(int fd);
57 int creat(const char *pathname)
59 return open(pathname, O_WRONLY|O_CREAT|O_TRUNC);
62 static int open_internal(const char* pathname, int flags, bool use_cache)
64 DIR_UNCACHED* dir;
65 struct dirent_uncached* entry;
66 int fd;
67 char pathnamecopy[MAX_PATH];
68 char* name;
69 struct filedesc* file = NULL;
70 int rc;
71 #ifndef HAVE_DIRCACHE
72 (void)use_cache;
73 #endif
75 LDEBUGF("open(\"%s\",%d)\n",pathname,flags);
77 if ( pathname[0] != '/' ) {
78 DEBUGF("'%s' is not an absolute path.\n",pathname);
79 DEBUGF("Only absolute pathnames supported at the moment\n");
80 errno = EINVAL;
81 return -1;
84 /* find a free file descriptor */
85 for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
86 if ( !openfiles[fd].busy )
87 break;
89 if ( fd == MAX_OPEN_FILES ) {
90 DEBUGF("Too many files open\n");
91 errno = EMFILE;
92 return -2;
95 file = &openfiles[fd];
96 memset(file, 0, sizeof(struct filedesc));
98 if (flags & (O_RDWR | O_WRONLY)) {
99 file->write = true;
101 if (flags & O_TRUNC)
102 file->trunc = true;
104 file->busy = true;
106 #ifdef HAVE_DIRCACHE
107 if (dircache_is_enabled() && !file->write && use_cache)
109 const struct dircache_entry *ce;
110 # ifdef HAVE_MULTIVOLUME
111 int volume = strip_volume(pathname, pathnamecopy);
112 # endif
114 ce = dircache_get_entry_ptr(pathname);
115 if (!ce)
117 errno = ENOENT;
118 file->busy = false;
119 return -7;
122 fat_open(IF_MV2(volume,)
123 ce->startcluster,
124 &(file->fatfile),
125 NULL);
126 file->size = ce->size;
127 file->attr = ce->attribute;
128 file->cacheoffset = -1;
129 file->fileoffset = 0;
131 return fd;
133 #endif
135 strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy));
137 /* locate filename */
138 name=strrchr(pathnamecopy+1,'/');
139 if ( name ) {
140 *name = 0;
141 dir = opendir_uncached(pathnamecopy);
142 *name = '/';
143 name++;
145 else {
146 dir = opendir_uncached("/");
147 name = pathnamecopy+1;
149 if (!dir) {
150 DEBUGF("Failed opening dir\n");
151 errno = EIO;
152 file->busy = false;
153 return -4;
156 if(name[0] == 0) {
157 DEBUGF("Empty file name\n");
158 errno = EINVAL;
159 file->busy = false;
160 closedir_uncached(dir);
161 return -5;
164 /* scan dir for name */
165 while ((entry = readdir_uncached(dir))) {
166 if ( !strcasecmp(name, entry->d_name) ) {
167 fat_open(IF_MV2(dir->fatdir.file.volume,)
168 entry->startcluster,
169 &(file->fatfile),
170 &(dir->fatdir));
171 file->size = file->trunc ? 0 : entry->size;
172 file->attr = entry->attribute;
173 break;
177 if ( !entry ) {
178 LDEBUGF("Didn't find file %s\n",name);
179 if ( file->write && (flags & O_CREAT) ) {
180 rc = fat_create_file(name,
181 &(file->fatfile),
182 &(dir->fatdir));
183 if (rc < 0) {
184 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
185 errno = EIO;
186 file->busy = false;
187 closedir_uncached(dir);
188 return rc * 10 - 6;
190 #ifdef HAVE_DIRCACHE
191 dircache_add_file(pathname, file->fatfile.firstcluster);
192 #endif
193 file->size = 0;
194 file->attr = 0;
196 else {
197 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
198 errno = ENOENT;
199 file->busy = false;
200 closedir_uncached(dir);
201 return -7;
203 } else {
204 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
205 errno = EISDIR;
206 file->busy = false;
207 closedir_uncached(dir);
208 return -8;
211 closedir_uncached(dir);
213 file->cacheoffset = -1;
214 file->fileoffset = 0;
216 if (file->write && (flags & O_APPEND)) {
217 rc = lseek(fd,0,SEEK_END);
218 if (rc < 0 )
219 return rc * 10 - 9;
222 #ifdef HAVE_DIRCACHE
223 if (file->write)
224 dircache_bind(fd, pathname);
225 #endif
227 return fd;
230 int open(const char* pathname, int flags)
232 /* By default, use the dircache if available. */
233 return open_internal(pathname, flags, true);
236 int close(int fd)
238 struct filedesc* file = &openfiles[fd];
239 int rc = 0;
241 LDEBUGF("close(%d)\n", fd);
243 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
244 errno = EINVAL;
245 return -1;
247 if (!file->busy) {
248 errno = EBADF;
249 return -2;
251 if (file->write) {
252 rc = fsync(fd);
253 if (rc < 0)
254 return rc * 10 - 3;
255 #ifdef HAVE_DIRCACHE
256 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
257 dircache_update_filetime(fd);
258 #endif
261 file->busy = false;
262 return 0;
265 int fsync(int fd)
267 struct filedesc* file = &openfiles[fd];
268 int rc = 0;
270 LDEBUGF("fsync(%d)\n", fd);
272 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
273 errno = EINVAL;
274 return -1;
276 if (!file->busy) {
277 errno = EBADF;
278 return -2;
280 if (file->write) {
281 /* flush sector cache */
282 if ( file->dirty ) {
283 rc = flush_cache(fd);
284 if (rc < 0)
286 /* when failing, try to close the file anyway */
287 fat_closewrite(&(file->fatfile), file->size, file->attr);
288 return rc * 10 - 3;
292 /* truncate? */
293 if (file->trunc) {
294 rc = ftruncate(fd, file->size);
295 if (rc < 0)
297 /* when failing, try to close the file anyway */
298 fat_closewrite(&(file->fatfile), file->size, file->attr);
299 return rc * 10 - 4;
303 /* tie up all loose ends */
304 rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
305 if (rc < 0)
306 return rc * 10 - 5;
308 return 0;
311 int remove(const char* name)
313 int rc;
314 struct filedesc* file;
315 /* Can't use dircache now, because we need to access the fat structures. */
316 int fd = open_internal(name, O_WRONLY, false);
317 if ( fd < 0 )
318 return fd * 10 - 1;
320 file = &openfiles[fd];
321 #ifdef HAVE_DIRCACHE
322 dircache_remove(name);
323 #endif
324 rc = fat_remove(&(file->fatfile));
325 if ( rc < 0 ) {
326 DEBUGF("Failed removing file: %d\n", rc);
327 errno = EIO;
328 return rc * 10 - 3;
331 file->size = 0;
333 rc = close(fd);
334 if (rc<0)
335 return rc * 10 - 4;
337 return 0;
340 int rename(const char* path, const char* newpath)
342 int rc, fd;
343 DIR_UNCACHED* dir;
344 char* nameptr;
345 char* dirptr;
346 struct filedesc* file;
347 char newpath2[MAX_PATH];
349 /* verify new path does not already exist */
350 /* If it is a directory, errno == EISDIR if the name exists */
351 fd = open(newpath, O_RDONLY);
352 if ( fd >= 0 || errno == EISDIR) {
353 close(fd);
354 errno = EBUSY;
355 return -1;
357 close(fd);
359 fd = open_internal(path, O_RDONLY, false);
360 if ( fd < 0 ) {
361 errno = EIO;
362 return fd * 10 - 2;
365 /* extract new file name */
366 nameptr = strrchr(newpath,'/');
367 if (nameptr)
368 nameptr++;
369 else
370 return - 3;
372 /* Extract new path */
373 strcpy(newpath2, newpath);
375 dirptr = strrchr(newpath2,'/');
376 if(dirptr)
377 *dirptr = 0;
378 else
379 return - 4;
381 dirptr = newpath2;
383 if(strlen(dirptr) == 0) {
384 dirptr = "/";
387 dir = opendir_uncached(dirptr);
388 if(!dir)
389 return - 5;
391 file = &openfiles[fd];
393 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
394 file->size, file->attr);
395 #ifdef HAVE_MULTIVOLUME
396 if ( rc == -1) {
397 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
398 errno = EXDEV;
399 return -6;
401 #endif
402 if ( rc < 0 ) {
403 DEBUGF("Failed renaming file: %d\n", rc);
404 errno = EIO;
405 return rc * 10 - 7;
408 #ifdef HAVE_DIRCACHE
409 dircache_rename(path, newpath);
410 #endif
412 rc = close(fd);
413 if (rc<0) {
414 errno = EIO;
415 return rc * 10 - 8;
418 rc = closedir_uncached(dir);
419 if (rc<0) {
420 errno = EIO;
421 return rc * 10 - 9;
424 return 0;
427 int ftruncate(int fd, off_t size)
429 int rc, sector;
430 struct filedesc* file = &openfiles[fd];
432 sector = size / SECTOR_SIZE;
433 if (size % SECTOR_SIZE)
434 sector++;
436 rc = fat_seek(&(file->fatfile), sector);
437 if (rc < 0) {
438 errno = EIO;
439 return rc * 10 - 1;
442 rc = fat_truncate(&(file->fatfile));
443 if (rc < 0) {
444 errno = EIO;
445 return rc * 10 - 2;
448 file->size = size;
449 #ifdef HAVE_DIRCACHE
450 dircache_update_filesize(fd, size, file->fatfile.firstcluster);
451 #endif
453 return 0;
456 static int flush_cache(int fd)
458 int rc;
459 struct filedesc* file = &openfiles[fd];
460 long sector = file->fileoffset / SECTOR_SIZE;
462 DEBUGF("Flushing dirty sector cache\n");
464 /* make sure we are on correct sector */
465 rc = fat_seek(&(file->fatfile), sector);
466 if ( rc < 0 )
467 return rc * 10 - 3;
469 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
471 if ( rc < 0 ) {
472 if(file->fatfile.eof)
473 errno = ENOSPC;
475 return rc * 10 - 2;
478 file->dirty = false;
480 return 0;
483 static int readwrite(int fd, void* buf, long count, bool write)
485 long sectors;
486 long nread=0;
487 struct filedesc* file = &openfiles[fd];
488 int rc;
490 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
491 errno = EINVAL;
492 return -1;
494 if ( !file->busy ) {
495 errno = EBADF;
496 return -1;
499 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
500 fd,(long)buf,count,write?"write":"read");
502 /* attempt to read past EOF? */
503 if (!write && count > file->size - file->fileoffset)
504 count = file->size - file->fileoffset;
506 /* any head bytes? */
507 if ( file->cacheoffset != -1 ) {
508 int offs = file->cacheoffset;
509 int headbytes = MIN(count, SECTOR_SIZE - offs);
511 if (write) {
512 memcpy( file->cache + offs, buf, headbytes );
513 file->dirty = true;
515 else {
516 memcpy( buf, file->cache + offs, headbytes );
519 if (offs + headbytes == SECTOR_SIZE) {
520 if (file->dirty) {
521 rc = flush_cache(fd);
522 if ( rc < 0 ) {
523 errno = EIO;
524 return rc * 10 - 2;
527 file->cacheoffset = -1;
529 else {
530 file->cacheoffset += headbytes;
533 nread = headbytes;
534 count -= headbytes;
537 /* If the buffer has been modified, either it has been flushed already
538 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
539 * more data to follow in this call). Do NOT flush here. */
541 /* read/write whole sectors right into/from the supplied buffer */
542 sectors = count / SECTOR_SIZE;
543 if ( sectors ) {
544 rc = fat_readwrite(&(file->fatfile), sectors,
545 (unsigned char*)buf+nread, write );
546 if ( rc < 0 ) {
547 DEBUGF("Failed read/writing %ld sectors\n",sectors);
548 errno = EIO;
549 if(write && file->fatfile.eof) {
550 DEBUGF("No space left on device\n");
551 errno = ENOSPC;
552 } else {
553 file->fileoffset += nread;
555 file->cacheoffset = -1;
556 /* adjust file size to length written */
557 if ( write && file->fileoffset > file->size )
559 file->size = file->fileoffset;
560 #ifdef HAVE_DIRCACHE
561 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
562 #endif
564 return nread ? nread : rc * 10 - 4;
566 else {
567 if ( rc > 0 ) {
568 nread += rc * SECTOR_SIZE;
569 count -= sectors * SECTOR_SIZE;
571 /* if eof, skip tail bytes */
572 if ( rc < sectors )
573 count = 0;
575 else {
576 /* eof */
577 count=0;
580 file->cacheoffset = -1;
584 /* any tail bytes? */
585 if ( count ) {
586 if (write) {
587 if ( file->fileoffset + nread < file->size ) {
588 /* sector is only partially filled. copy-back from disk */
589 LDEBUGF("Copy-back tail cache\n");
590 rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
591 if ( rc < 0 ) {
592 DEBUGF("Failed writing\n");
593 errno = EIO;
594 file->fileoffset += nread;
595 file->cacheoffset = -1;
596 /* adjust file size to length written */
597 if ( file->fileoffset > file->size )
599 file->size = file->fileoffset;
600 #ifdef HAVE_DIRCACHE
601 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
602 #endif
604 return nread ? nread : rc * 10 - 5;
606 /* seek back one sector to put file position right */
607 rc = fat_seek(&(file->fatfile),
608 (file->fileoffset + nread) /
609 SECTOR_SIZE);
610 if ( rc < 0 ) {
611 DEBUGF("fat_seek() failed\n");
612 errno = EIO;
613 file->fileoffset += nread;
614 file->cacheoffset = -1;
615 /* adjust file size to length written */
616 if ( file->fileoffset > file->size )
618 file->size = file->fileoffset;
619 #ifdef HAVE_DIRCACHE
620 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
621 #endif
623 return nread ? nread : rc * 10 - 6;
626 memcpy( file->cache, (unsigned char*)buf + nread, count );
627 file->dirty = true;
629 else {
630 rc = fat_readwrite(&(file->fatfile), 1, &(file->cache),false);
631 if (rc < 1 ) {
632 DEBUGF("Failed caching sector\n");
633 errno = EIO;
634 file->fileoffset += nread;
635 file->cacheoffset = -1;
636 return nread ? nread : rc * 10 - 7;
638 memcpy( (unsigned char*)buf + nread, file->cache, count );
641 nread += count;
642 file->cacheoffset = count;
645 file->fileoffset += nread;
646 LDEBUGF("fileoffset: %ld\n", file->fileoffset);
648 /* adjust file size to length written */
649 if ( write && file->fileoffset > file->size )
651 file->size = file->fileoffset;
652 #ifdef HAVE_DIRCACHE
653 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
654 #endif
657 return nread;
660 ssize_t write(int fd, const void* buf, size_t count)
662 if (!openfiles[fd].write) {
663 errno = EACCES;
664 return -1;
666 return readwrite(fd, (void *)buf, count, true);
669 ssize_t read(int fd, void* buf, size_t count)
671 return readwrite(fd, buf, count, false);
675 off_t lseek(int fd, off_t offset, int whence)
677 off_t pos;
678 long newsector;
679 long oldsector;
680 int sectoroffset;
681 int rc;
682 struct filedesc* file = &openfiles[fd];
684 LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence);
686 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
687 errno = EINVAL;
688 return -1;
690 if ( !file->busy ) {
691 errno = EBADF;
692 return -1;
695 switch ( whence ) {
696 case SEEK_SET:
697 pos = offset;
698 break;
700 case SEEK_CUR:
701 pos = file->fileoffset + offset;
702 break;
704 case SEEK_END:
705 pos = file->size + offset;
706 break;
708 default:
709 errno = EINVAL;
710 return -2;
712 if ((pos < 0) || (pos > file->size)) {
713 errno = EINVAL;
714 return -3;
717 /* new sector? */
718 newsector = pos / SECTOR_SIZE;
719 oldsector = file->fileoffset / SECTOR_SIZE;
720 sectoroffset = pos % SECTOR_SIZE;
722 if ( (newsector != oldsector) ||
723 ((file->cacheoffset==-1) && sectoroffset) ) {
725 if ( newsector != oldsector ) {
726 if (file->dirty) {
727 rc = flush_cache(fd);
728 if (rc < 0)
729 return rc * 10 - 5;
732 rc = fat_seek(&(file->fatfile), newsector);
733 if ( rc < 0 ) {
734 errno = EIO;
735 return rc * 10 - 4;
738 if ( sectoroffset ) {
739 rc = fat_readwrite(&(file->fatfile), 1,
740 &(file->cache),false);
741 if ( rc < 0 ) {
742 errno = EIO;
743 return rc * 10 - 6;
745 file->cacheoffset = sectoroffset;
747 else
748 file->cacheoffset = -1;
750 else
751 if ( file->cacheoffset != -1 )
752 file->cacheoffset = sectoroffset;
754 file->fileoffset = pos;
756 return pos;
759 off_t filesize(int fd)
761 struct filedesc* file = &openfiles[fd];
763 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
764 errno = EINVAL;
765 return -1;
767 if ( !file->busy ) {
768 errno = EBADF;
769 return -1;
772 return file->size;
776 #ifdef HAVE_HOTSWAP
777 /* release all file handles on a given volume "by force", to avoid leaks */
778 int release_files(int volume)
780 struct filedesc* pfile = openfiles;
781 int fd;
782 int closed = 0;
783 for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
785 if (pfile->fatfile.volume == volume)
787 pfile->busy = false; /* mark as available, no further action */
788 closed++;
791 return closed; /* return how many we did */
793 #endif /* #ifdef HAVE_HOTSWAP */